PPPoE usses a midchain adjacency stack on an interface-tx DPO

1) introduce an interface-tx DPO. This is a simple wrapper around a sw_if_index. enhance DPO stacking functions to allow per-instance next-nodes and hence allow children to stack onto the interface per-instance tx node and not on 'interface-output'.
2) update PPPoE code to use ta midchain stack on a interface-tx DPO of the encap-interface. This remove the need for pppoe_encap node (which is replaced by the adj-midchain-tx) and interface-output node is no longer used (see above). Since PPPoE encap node is no longer needed, the PPPoE seesion does not need to be retrieved in the data-path, hence the cahce misses are removed.

Change-Id: Id8b40f53daa14889a9c51d802e14fed7fba4399a
Signed-off-by: Neale Ranns <nranns@cisco.com>
diff --git a/src/vnet/dpo/dpo.c b/src/vnet/dpo/dpo.c
index aa77083..bd18b66 100644
--- a/src/vnet/dpo/dpo.c
+++ b/src/vnet/dpo/dpo.c
@@ -37,7 +37,8 @@
 #include <vnet/dpo/classify_dpo.h>
 #include <vnet/dpo/ip_null_dpo.h>
 #include <vnet/dpo/replicate_dpo.h>
-#include <vnet/dpo/interface_dpo.h>
+#include <vnet/dpo/interface_rx_dpo.h>
+#include <vnet/dpo/interface_tx_dpo.h>
 #include <vnet/dpo/mpls_disposition.h>
 
 /**
@@ -275,6 +276,29 @@
 	    (dpo->dpoi_type == DPO_ADJACENCY_GLEAN));
 }
 
+static u32 *
+dpo_default_get_next_node (const dpo_id_t *dpo)
+{
+    u32 *node_indices = NULL;
+    const char *node_name;
+    u32 ii = 0;
+
+    node_name = dpo_nodes[dpo->dpoi_type][dpo->dpoi_proto][ii];
+    while (NULL != node_name)
+    {
+        vlib_node_t *node;
+
+        node = vlib_get_node_by_name(vlib_get_main(), (u8*) node_name);
+        ASSERT(NULL != node);
+        vec_add1(node_indices, node->index);
+
+        ++ii;
+        node_name = dpo_nodes[dpo->dpoi_type][dpo->dpoi_proto][ii];
+    }
+
+    return (node_indices);
+}
+
 void
 dpo_register (dpo_type_t type,
 	      const dpo_vft_t *vft,
@@ -282,6 +306,10 @@
 {
     vec_validate(dpo_vfts, type);
     dpo_vfts[type] = *vft;
+    if (NULL == dpo_vfts[type].dv_get_next_node)
+    {
+        dpo_vfts[type].dv_get_next_node = dpo_default_get_next_node;
+    }
 
     vec_validate(dpo_nodes, type);
     dpo_nodes[type] = nodes;
@@ -340,24 +368,25 @@
      */
     if (~0 == dpo_edges[child_type][child_proto][parent_type][parent_proto])
     {
-        vlib_node_t *parent_node, *child_node;
+        vlib_node_t *child_node;
+        u32 *parent_indices;
         vlib_main_t *vm;
-        u32 edge ,pp, cc;
+        u32 edge, *pi, cc;
 
         vm = vlib_get_main();
 
-        vlib_worker_thread_barrier_sync(vm);
-
+        ASSERT(NULL != dpo_vfts[parent_type].dv_get_next_node);
         ASSERT(NULL != dpo_nodes[child_type]);
         ASSERT(NULL != dpo_nodes[child_type][child_proto]);
-        ASSERT(NULL != dpo_nodes[parent_type]);
-        ASSERT(NULL != dpo_nodes[parent_type][parent_proto]);
 
         cc = 0;
+        parent_indices = dpo_vfts[parent_type].dv_get_next_node(parent_dpo);
+
+        vlib_worker_thread_barrier_sync(vm);
 
         /*
-         * create a graph arc from each of the parent's registered node types,
-         * to each of the childs.
+         * create a graph arc from each of the child's registered node types,
+         * to each of the parent's.
          */
         while (NULL != dpo_nodes[child_type][child_proto][cc])
         {
@@ -365,17 +394,9 @@
                 vlib_get_node_by_name(vm,
                                       (u8*) dpo_nodes[child_type][child_proto][cc]);
 
-            pp = 0;
-
-            while (NULL != dpo_nodes[parent_type][parent_proto][pp])
+            vec_foreach(pi, parent_indices)
             {
-                parent_node =
-                    vlib_get_node_by_name(vm,
-                                          (u8*) dpo_nodes[parent_type][parent_proto][pp]);
-
-                edge = vlib_node_add_next(vm,
-                                          child_node->index,
-                                          parent_node->index);
+                edge = vlib_node_add_next(vm, child_node->index, *pi);
 
                 if (~0 == dpo_edges[child_type][child_proto][parent_type][parent_proto])
                 {
@@ -385,12 +406,12 @@
                 {
                     ASSERT(dpo_edges[child_type][child_proto][parent_type][parent_proto] == edge);
                 }
-                pp++;
             }
             cc++;
         }
 
         vlib_worker_thread_barrier_release(vm);
+        vec_free(parent_indices);
     }
 
     return (dpo_edges[child_type][child_proto][parent_type][parent_proto]);
@@ -451,38 +472,39 @@
                      dpo_id_t *dpo,
                      const dpo_id_t *parent)
 {
-    dpo_proto_t parent_proto;
-    vlib_node_t *parent_node;
     dpo_type_t parent_type;
+    u32 *parent_indices;
     vlib_main_t *vm;
-    u32 edge;
+    u32 edge, *pi;
 
+    edge = 0;
     parent_type = parent->dpoi_type;
-    parent_proto = parent->dpoi_proto;
-
     vm = vlib_get_main();
 
-    ASSERT(NULL != dpo_nodes[parent_type]);
-    ASSERT(NULL != dpo_nodes[parent_type][parent_proto]);
+    ASSERT(NULL != dpo_vfts[parent_type].dv_get_next_node);
+    parent_indices = dpo_vfts[parent_type].dv_get_next_node(parent);
+    ASSERT(parent_indices);
 
-    parent_node =
-        vlib_get_node_by_name(vm, (u8*) dpo_nodes[parent_type][parent_proto][0]);
-
-    edge = vlib_node_get_next(vm,
-                              child_node_index,
-                              parent_node->index);
-
-    if (~0 == edge)
+    /*
+     * This loop is purposefully written with the worker thread lock in the
+     * inner loop because;
+     *  1) the likelihood that the edge does not exist is smaller
+     *  2) the likelihood there is more than one node is even smaller
+     * so we are optimising for not need to take the lock
+     */
+    vec_foreach(pi, parent_indices)
     {
-        vlib_worker_thread_barrier_sync(vm);
+        edge = vlib_node_get_next(vm, child_node_index, *pi);
 
-        edge = vlib_node_add_next(vm,
-                                  child_node_index,
-                                  parent_node->index);
+        if (~0 == edge)
+        {
+            vlib_worker_thread_barrier_sync(vm);
 
-        vlib_worker_thread_barrier_release(vm);
+            edge = vlib_node_add_next(vm, child_node_index, *pi);
+
+            vlib_worker_thread_barrier_release(vm);
+        }
     }
-
     dpo_stack_i(edge, dpo, parent);
 }
 
@@ -498,7 +520,8 @@
     lookup_dpo_module_init();
     ip_null_dpo_module_init();
     replicate_module_init();
-    interface_dpo_module_init();
+    interface_rx_dpo_module_init();
+    interface_tx_dpo_module_init();
     mpls_disp_dpo_module_init();
 
     return (NULL);
diff --git a/src/vnet/dpo/dpo.h b/src/vnet/dpo/dpo.h
index 42fc51d..3356296 100644
--- a/src/vnet/dpo/dpo.h
+++ b/src/vnet/dpo/dpo.h
@@ -112,7 +112,8 @@
     DPO_MPLS_LABEL,
     DPO_MPLS_DISPOSITION,
     DPO_MFIB_ENTRY,
-    DPO_INTERFACE,
+    DPO_INTERFACE_RX,
+    DPO_INTERFACE_TX,
     DPO_LAST,
 } __attribute__((packed)) dpo_type_t;
 
@@ -138,7 +139,8 @@
     [DPO_MPLS_LABEL] = "dpo-mpls-label", \
     [DPO_MPLS_DISPOSITION] = "dpo-mpls-diposition", \
     [DPO_MFIB_ENTRY] = "dpo-mfib_entry", \
-    [DPO_INTERFACE] = "dpo-interface"	\
+    [DPO_INTERFACE_RX] = "dpo-interface-rx",	\
+    [DPO_INTERFACE_TX] = "dpo-interface-tx"	\
 }
 
 /**
@@ -332,6 +334,12 @@
 typedef void (*dpo_mem_show_t)(void);
 
 /**
+ * @brief Given a DPO instance return a vector of node indices that
+ * the type/instance will use.
+ */
+typedef u32* (*dpo_get_next_node_t)(const dpo_id_t *dpo);
+
+/**
  * @brief A virtual function table regisitered for a DPO type
  */
 typedef struct dpo_vft_t_
@@ -352,6 +360,13 @@
      * A show memory usage function
      */
     dpo_mem_show_t dv_mem_show;
+    /**
+     * A function to get the next VLIB node given an instance
+     * of the DPO. If this is null, then the node's name MUST be
+     * retreiveable from the nodes names array passed in the register
+     * function
+     */
+    dpo_get_next_node_t dv_get_next_node;
 } dpo_vft_t;
 
 
diff --git a/src/vnet/dpo/interface_dpo.c b/src/vnet/dpo/interface_dpo.c
deleted file mode 100644
index 780bfa2..0000000
--- a/src/vnet/dpo/interface_dpo.c
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <vnet/dpo/interface_dpo.h>
-#include <vnet/fib/fib_node.h>
-
-/*
- * The 'DB' of interface DPOs.
- * There is only one  per-interface per-protocol, so this is a per-interface
- * vector
- */
-static index_t *interface_dpo_db[DPO_PROTO_NUM];
-
-static interface_dpo_t *
-interface_dpo_alloc (void)
-{
-    interface_dpo_t *ido;
-
-    pool_get(interface_dpo_pool, ido);
-
-    return (ido);
-}
-
-static inline interface_dpo_t *
-interface_dpo_get_from_dpo (const dpo_id_t *dpo)
-{
-    ASSERT(DPO_INTERFACE == dpo->dpoi_type);
-
-    return (interface_dpo_get(dpo->dpoi_index));
-}
-
-static inline index_t
-interface_dpo_get_index (interface_dpo_t *ido)
-{
-    return (ido - interface_dpo_pool);
-}
-
-static void
-interface_dpo_lock (dpo_id_t *dpo)
-{
-    interface_dpo_t *ido;
-
-    ido = interface_dpo_get_from_dpo(dpo);
-    ido->ido_locks++;
-}
-
-static void
-interface_dpo_unlock (dpo_id_t *dpo)
-{
-    interface_dpo_t *ido;
-
-    ido = interface_dpo_get_from_dpo(dpo);
-    ido->ido_locks--;
-
-    if (0 == ido->ido_locks)
-    {
-	interface_dpo_db[ido->ido_proto][ido->ido_sw_if_index] =
-            INDEX_INVALID;
-        pool_put(interface_dpo_pool, ido);
-    }
-}
-
-/*
- * interface_dpo_add_or_lock
- *
- * Add/create and lock a new or lock an existing for the interface DPO
- * on the interface and protocol given
- */
-void
-interface_dpo_add_or_lock (dpo_proto_t proto,
-                           u32 sw_if_index,
-                           dpo_id_t *dpo)
-{
-    interface_dpo_t *ido;
-
-    vec_validate_init_empty(interface_dpo_db[proto],
-                            sw_if_index,
-                            INDEX_INVALID);
-
-    if (INDEX_INVALID == interface_dpo_db[proto][sw_if_index])
-    {
-	ido = interface_dpo_alloc();
-
-        ido->ido_sw_if_index = sw_if_index;
-        ido->ido_proto = proto;
-
-	interface_dpo_db[proto][sw_if_index] =
-            interface_dpo_get_index(ido);
-    }
-    else
-    {
-	ido = interface_dpo_get(interface_dpo_db[proto][sw_if_index]);
-    }
-
-    dpo_set(dpo, DPO_INTERFACE, proto, interface_dpo_get_index(ido));
-}
-
-
-static clib_error_t *
-interface_dpo_interface_state_change (vnet_main_t * vnm,
-                                      u32 sw_if_index,
-                                      u32 flags)
-{
-    /*
-     */
-    return (NULL);
-}
-
-VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION(
-    interface_dpo_interface_state_change);
-
-/**
- * @brief Registered callback for HW interface state changes
- */
-static clib_error_t *
-interface_dpo_hw_interface_state_change (vnet_main_t * vnm,
-                                         u32 hw_if_index,
-                                         u32 flags)
-{
-    return (NULL);
-}
-
-VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION(
-    interface_dpo_hw_interface_state_change);
-
-static clib_error_t *
-interface_dpo_interface_delete (vnet_main_t * vnm,
-                                u32 sw_if_index,
-                                u32 is_add)
-{
-    return (NULL);
-}
-
-VNET_SW_INTERFACE_ADD_DEL_FUNCTION(
-    interface_dpo_interface_delete);
-
-u8*
-format_interface_dpo (u8* s, va_list *ap)
-{
-    index_t index = va_arg(*ap, index_t);
-    CLIB_UNUSED(u32 indent) = va_arg(*ap, u32);
-    vnet_main_t * vnm = vnet_get_main();
-    interface_dpo_t *ido = interface_dpo_get(index);
-
-    return (format(s, "%U-dpo: %U",
-                   format_vnet_sw_interface_name,
-                   vnm,
-                   vnet_get_sw_interface(vnm, ido->ido_sw_if_index),
-                   format_dpo_proto, ido->ido_proto));
-}
-
-static void
-interface_dpo_mem_show (void)
-{
-    fib_show_memory_usage("Interface",
-			  pool_elts(interface_dpo_pool),
-			  pool_len(interface_dpo_pool),
-			  sizeof(interface_dpo_t));
-}
-
-
-const static dpo_vft_t interface_dpo_vft = {
-    .dv_lock = interface_dpo_lock,
-    .dv_unlock = interface_dpo_unlock,
-    .dv_format = format_interface_dpo,
-    .dv_mem_show = interface_dpo_mem_show,
-};
-
-/**
- * @brief The per-protocol VLIB graph nodes that are assigned to a glean
- *        object.
- *
- * this means that these graph nodes are ones from which a glean is the
- * parent object in the DPO-graph.
- */
-const static char* const interface_dpo_ip4_nodes[] =
-{
-    "interface-dpo-ip4",
-    NULL,
-};
-const static char* const interface_dpo_ip6_nodes[] =
-{
-    "interface-dpo-ip4",
-    NULL,
-};
-const static char* const interface_dpo_l2_nodes[] =
-{
-    "interface-dpo-l2",
-    NULL,
-};
-
-const static char* const * const interface_dpo_nodes[DPO_PROTO_NUM] =
-{
-    [DPO_PROTO_IP4]  = interface_dpo_ip4_nodes,
-    [DPO_PROTO_IP6]  = interface_dpo_ip6_nodes,
-    [DPO_PROTO_ETHERNET]  = interface_dpo_l2_nodes,
-    [DPO_PROTO_MPLS] = NULL,
-};
-
-void
-interface_dpo_module_init (void)
-{
-    dpo_register(DPO_INTERFACE,
-                 &interface_dpo_vft,
-                 interface_dpo_nodes);
-}
-
-/**
- * @brief Interface DPO trace data
- */
-typedef struct interface_dpo_trace_t_
-{
-    u32 sw_if_index;
-} interface_dpo_trace_t;
-
-typedef enum interface_dpo_next_t_
-{
-    INTERFACE_DPO_DROP = 0,
-    INTERFACE_DPO_INPUT = 1,
-} interface_dpo_next_t;
-
-always_inline uword
-interface_dpo_inline (vlib_main_t * vm,
-                      vlib_node_runtime_t * node,
-                      vlib_frame_t * from_frame)
-{
-    u32 n_left_from, next_index, * from, * to_next;
-    u32 thread_index = vlib_get_thread_index ();
-    vnet_interface_main_t *im;
-
-    im = &vnet_get_main ()->interface_main;
-    from = vlib_frame_vector_args (from_frame);
-    n_left_from = from_frame->n_vectors;
-
-    next_index = node->cached_next_index;
-
-    while (n_left_from > 0)
-    {
-        u32 n_left_to_next;
-
-        vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
-
-	while (n_left_from >= 4 && n_left_to_next > 2)
-	{
-	    const interface_dpo_t *ido0, *ido1;
-	    u32 bi0, idoi0, bi1, idoi1;
-	    vlib_buffer_t *b0, *b1;
-
-	    bi0 = from[0];
-	    to_next[0] = bi0;
-	    bi1 = from[1];
-	    to_next[1] = bi1;
-	    from += 2;
-	    to_next += 2;
-	    n_left_from -= 2;
-	    n_left_to_next -= 2;
-
-	    b0 = vlib_get_buffer (vm, bi0);
-	    b1 = vlib_get_buffer (vm, bi1);
-
-	    idoi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
-	    idoi1 = vnet_buffer(b1)->ip.adj_index[VLIB_TX];
-	    ido0 = interface_dpo_get(idoi0);
-	    ido1 = interface_dpo_get(idoi1);
-
-	    vnet_buffer(b0)->sw_if_index[VLIB_RX] = ido0->ido_sw_if_index;
-	    vnet_buffer(b1)->sw_if_index[VLIB_RX] = ido1->ido_sw_if_index;
-
-            vlib_increment_combined_counter (im->combined_sw_if_counters
-                                             + VNET_INTERFACE_COUNTER_RX,
-                                             thread_index,
-                                             ido0->ido_sw_if_index,
-                                             1,
-                                             vlib_buffer_length_in_chain (vm, b0));
-            vlib_increment_combined_counter (im->combined_sw_if_counters
-                                             + VNET_INTERFACE_COUNTER_RX,
-                                             thread_index,
-                                             ido1->ido_sw_if_index,
-                                             1,
-                                             vlib_buffer_length_in_chain (vm, b1));
-
-	    if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
-	    {
-		interface_dpo_trace_t *tr0;
-
-                tr0 = vlib_add_trace (vm, node, b0, sizeof (*tr0));
-		tr0->sw_if_index = ido0->ido_sw_if_index;
-	    }
-	    if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
-	    {
-		interface_dpo_trace_t *tr1;
-
-                tr1 = vlib_add_trace (vm, node, b1, sizeof (*tr1));
-		tr1->sw_if_index = ido1->ido_sw_if_index;
-	    }
-
-	    vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next,
-					    n_left_to_next, bi0, bi1,
-                                            INTERFACE_DPO_INPUT,
-                                            INTERFACE_DPO_INPUT);
-	}
-
-	while (n_left_from > 0 && n_left_to_next > 0)
-	{
-	    const interface_dpo_t * ido0;
-	    vlib_buffer_t * b0;
-	    u32 bi0, idoi0;
-
-	    bi0 = from[0];
-	    to_next[0] = bi0;
-	    from += 1;
-	    to_next += 1;
-	    n_left_from -= 1;
-	    n_left_to_next -= 1;
-
-	    b0 = vlib_get_buffer (vm, bi0);
-
-	    idoi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
-	    ido0 = interface_dpo_get(idoi0);
-
-            /* Swap the RX interface of the packet to the one the
-             * interface DPR represents */
-	    vnet_buffer(b0)->sw_if_index[VLIB_RX] = ido0->ido_sw_if_index;
-
-            /* Bump the interface's RX coutners */
-            vlib_increment_combined_counter (im->combined_sw_if_counters
-                                             + VNET_INTERFACE_COUNTER_RX,
-                                             thread_index,
-                                             ido0->ido_sw_if_index,
-                                             1,
-                                             vlib_buffer_length_in_chain (vm, b0));
-
-	    if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
-	    {
-		interface_dpo_trace_t *tr;
-
-                tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
-		tr->sw_if_index = ido0->ido_sw_if_index;
-	    }
-
-	    vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
-					    n_left_to_next, bi0,
-                                            INTERFACE_DPO_INPUT);
-	}
-        vlib_put_next_frame (vm, node, next_index, n_left_to_next);
-    }
-    return from_frame->n_vectors;
-}
-
-static u8 *
-format_interface_dpo_trace (u8 * s, va_list * args)
-{
-    CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
-    CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
-    interface_dpo_trace_t * t = va_arg (*args, interface_dpo_trace_t *);
-    uword indent = format_get_indent (s);
-    s = format (s, "%U sw_if_index:%d",
-                format_white_space, indent,
-                t->sw_if_index);
-    return s;
-}
-
-static uword
-interface_dpo_ip4 (vlib_main_t * vm,
-                   vlib_node_runtime_t * node,
-                   vlib_frame_t * from_frame)
-{
-    return (interface_dpo_inline(vm, node, from_frame));
-}
-
-static uword
-interface_dpo_ip6 (vlib_main_t * vm,
-                   vlib_node_runtime_t * node,
-                   vlib_frame_t * from_frame)
-{
-    return (interface_dpo_inline(vm, node, from_frame));
-}
-
-static uword
-interface_dpo_l2 (vlib_main_t * vm,
-                   vlib_node_runtime_t * node,
-                   vlib_frame_t * from_frame)
-{
-    return (interface_dpo_inline(vm, node, from_frame));
-}
-
-VLIB_REGISTER_NODE (interface_dpo_ip4_node) = {
-    .function = interface_dpo_ip4,
-    .name = "interface-dpo-ip4",
-    .vector_size = sizeof (u32),
-    .format_trace = format_interface_dpo_trace,
-
-    .n_next_nodes = 2,
-    .next_nodes = {
-        [INTERFACE_DPO_DROP] = "ip4-drop",
-        [INTERFACE_DPO_INPUT] = "ip4-input",
-    },
-};
-
-VLIB_NODE_FUNCTION_MULTIARCH (interface_dpo_ip4_node,
-                              interface_dpo_ip4)
-
-VLIB_REGISTER_NODE (interface_dpo_ip6_node) = {
-    .function = interface_dpo_ip6,
-    .name = "interface-dpo-ip6",
-    .vector_size = sizeof (u32),
-    .format_trace = format_interface_dpo_trace,
-
-    .n_next_nodes = 2,
-    .next_nodes = {
-        [INTERFACE_DPO_DROP] = "ip6-drop",
-        [INTERFACE_DPO_INPUT] = "ip6-input",
-    },
-};
-
-VLIB_NODE_FUNCTION_MULTIARCH (interface_dpo_ip6_node,
-                              interface_dpo_ip6)
-
-VLIB_REGISTER_NODE (interface_dpo_l2_node) = {
-    .function = interface_dpo_l2,
-    .name = "interface-dpo-l2",
-    .vector_size = sizeof (u32),
-    .format_trace = format_interface_dpo_trace,
-
-    .n_next_nodes = 2,
-    .next_nodes = {
-        [INTERFACE_DPO_DROP] = "error-drop",
-        [INTERFACE_DPO_INPUT] = "l2-input",
-    },
-};
-
-VLIB_NODE_FUNCTION_MULTIARCH (interface_dpo_l2_node,
-                              interface_dpo_l2)
-
diff --git a/src/vnet/dpo/interface_rx_dpo.c b/src/vnet/dpo/interface_rx_dpo.c
new file mode 100644
index 0000000..a624f51
--- /dev/null
+++ b/src/vnet/dpo/interface_rx_dpo.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/dpo/interface_rx_dpo.h>
+#include <vnet/fib/fib_node.h>
+
+/*
+ * The 'DB' of interface DPOs.
+ * There is only one  per-interface per-protocol, so this is a per-interface
+ * vector
+ */
+static index_t *interface_rx_dpo_db[DPO_PROTO_NUM];
+
+static interface_rx_dpo_t *
+interface_rx_dpo_alloc (void)
+{
+    interface_rx_dpo_t *ido;
+
+    pool_get(interface_rx_dpo_pool, ido);
+
+    return (ido);
+}
+
+static inline interface_rx_dpo_t *
+interface_rx_dpo_get_from_dpo (const dpo_id_t *dpo)
+{
+    ASSERT(DPO_INTERFACE_RX == dpo->dpoi_type);
+
+    return (interface_rx_dpo_get(dpo->dpoi_index));
+}
+
+static inline index_t
+interface_rx_dpo_get_index (interface_rx_dpo_t *ido)
+{
+    return (ido - interface_rx_dpo_pool);
+}
+
+static void
+interface_rx_dpo_lock (dpo_id_t *dpo)
+{
+    interface_rx_dpo_t *ido;
+
+    ido = interface_rx_dpo_get_from_dpo(dpo);
+    ido->ido_locks++;
+}
+
+static void
+interface_rx_dpo_unlock (dpo_id_t *dpo)
+{
+    interface_rx_dpo_t *ido;
+
+    ido = interface_rx_dpo_get_from_dpo(dpo);
+    ido->ido_locks--;
+
+    if (0 == ido->ido_locks)
+    {
+        interface_rx_dpo_db[ido->ido_proto][ido->ido_sw_if_index] =
+            INDEX_INVALID;
+        pool_put(interface_rx_dpo_pool, ido);
+    }
+}
+
+/*
+ * interface_rx_dpo_add_or_lock
+ *
+ * Add/create and lock a new or lock an existing for the interface DPO
+ * on the interface and protocol given
+ */
+void
+interface_rx_dpo_add_or_lock (dpo_proto_t proto,
+                              u32 sw_if_index,
+                              dpo_id_t *dpo)
+{
+    interface_rx_dpo_t *ido;
+
+    vec_validate_init_empty(interface_rx_dpo_db[proto],
+                            sw_if_index,
+                            INDEX_INVALID);
+
+    if (INDEX_INVALID == interface_rx_dpo_db[proto][sw_if_index])
+    {
+        ido = interface_rx_dpo_alloc();
+
+        ido->ido_sw_if_index = sw_if_index;
+        ido->ido_proto = proto;
+
+        interface_rx_dpo_db[proto][sw_if_index] =
+            interface_rx_dpo_get_index(ido);
+    }
+    else
+    {
+        ido = interface_rx_dpo_get(interface_rx_dpo_db[proto][sw_if_index]);
+    }
+
+    dpo_set(dpo, DPO_INTERFACE_RX, proto, interface_rx_dpo_get_index(ido));
+}
+
+
+static clib_error_t *
+interface_rx_dpo_interface_state_change (vnet_main_t * vnm,
+                                         u32 sw_if_index,
+                                         u32 flags)
+{
+    /*
+     */
+    return (NULL);
+}
+
+VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION(
+    interface_rx_dpo_interface_state_change);
+
+/**
+ * @brief Registered callback for HW interface state changes
+ */
+static clib_error_t *
+interface_rx_dpo_hw_interface_state_change (vnet_main_t * vnm,
+                                            u32 hw_if_index,
+                                            u32 flags)
+{
+    return (NULL);
+}
+
+VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION(
+    interface_rx_dpo_hw_interface_state_change);
+
+static clib_error_t *
+interface_rx_dpo_interface_delete (vnet_main_t * vnm,
+                                   u32 sw_if_index,
+                                   u32 is_add)
+{
+    return (NULL);
+}
+
+VNET_SW_INTERFACE_ADD_DEL_FUNCTION(
+    interface_rx_dpo_interface_delete);
+
+u8*
+format_interface_rx_dpo (u8* s, va_list *ap)
+{
+    index_t index = va_arg(*ap, index_t);
+    CLIB_UNUSED(u32 indent) = va_arg(*ap, u32);
+    vnet_main_t * vnm = vnet_get_main();
+    interface_rx_dpo_t *ido = interface_rx_dpo_get(index);
+
+    return (format(s, "%U-dpo: %U",
+                   format_vnet_sw_interface_name,
+                   vnm,
+                   vnet_get_sw_interface(vnm, ido->ido_sw_if_index),
+                   format_dpo_proto, ido->ido_proto));
+}
+
+static void
+interface_rx_dpo_mem_show (void)
+{
+    fib_show_memory_usage("Interface",
+                          pool_elts(interface_rx_dpo_pool),
+                          pool_len(interface_rx_dpo_pool),
+                          sizeof(interface_rx_dpo_t));
+}
+
+
+const static dpo_vft_t interface_rx_dpo_vft = {
+    .dv_lock = interface_rx_dpo_lock,
+    .dv_unlock = interface_rx_dpo_unlock,
+    .dv_format = format_interface_rx_dpo,
+    .dv_mem_show = interface_rx_dpo_mem_show,
+};
+
+/**
+ * @brief The per-protocol VLIB graph nodes that are assigned to a glean
+ *        object.
+ *
+ * this means that these graph nodes are ones from which a glean is the
+ * parent object in the DPO-graph.
+ */
+const static char* const interface_rx_dpo_ip4_nodes[] =
+{
+    "interface-rx-dpo-ip4",
+    NULL,
+};
+const static char* const interface_rx_dpo_ip6_nodes[] =
+{
+    "interface-rx-dpo-ip6",
+    NULL,
+};
+const static char* const interface_rx_dpo_l2_nodes[] =
+{
+    "interface-rx-dpo-l2",
+    NULL,
+};
+
+const static char* const * const interface_rx_dpo_nodes[DPO_PROTO_NUM] =
+{
+    [DPO_PROTO_IP4]  = interface_rx_dpo_ip4_nodes,
+    [DPO_PROTO_IP6]  = interface_rx_dpo_ip6_nodes,
+    [DPO_PROTO_ETHERNET]  = interface_rx_dpo_l2_nodes,
+    [DPO_PROTO_MPLS] = NULL,
+};
+
+void
+interface_rx_dpo_module_init (void)
+{
+    dpo_register(DPO_INTERFACE_RX,
+                 &interface_rx_dpo_vft,
+                 interface_rx_dpo_nodes);
+}
+
+/**
+ * @brief Interface DPO trace data
+ */
+typedef struct interface_rx_dpo_trace_t_
+{
+    u32 sw_if_index;
+} interface_rx_dpo_trace_t;
+
+typedef enum interface_rx_dpo_next_t_
+{
+    INTERFACE_RX_DPO_DROP = 0,
+    INTERFACE_RX_DPO_INPUT = 1,
+} interface_rx_dpo_next_t;
+
+always_inline uword
+interface_rx_dpo_inline (vlib_main_t * vm,
+                         vlib_node_runtime_t * node,
+                         vlib_frame_t * from_frame)
+{
+    u32 n_left_from, next_index, * from, * to_next;
+    u32 thread_index = vlib_get_thread_index ();
+    vnet_interface_main_t *im;
+
+    im = &vnet_get_main ()->interface_main;
+    from = vlib_frame_vector_args (from_frame);
+    n_left_from = from_frame->n_vectors;
+
+    next_index = node->cached_next_index;
+
+    while (n_left_from > 0)
+    {
+        u32 n_left_to_next;
+
+        vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
+
+        while (n_left_from >= 4 && n_left_to_next > 2)
+        {
+            const interface_rx_dpo_t *ido0, *ido1;
+            u32 bi0, idoi0, bi1, idoi1;
+            vlib_buffer_t *b0, *b1;
+
+            bi0 = from[0];
+            to_next[0] = bi0;
+            bi1 = from[1];
+            to_next[1] = bi1;
+            from += 2;
+            to_next += 2;
+            n_left_from -= 2;
+            n_left_to_next -= 2;
+
+            b0 = vlib_get_buffer (vm, bi0);
+            b1 = vlib_get_buffer (vm, bi1);
+
+            idoi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
+            idoi1 = vnet_buffer(b1)->ip.adj_index[VLIB_TX];
+            ido0 = interface_rx_dpo_get(idoi0);
+            ido1 = interface_rx_dpo_get(idoi1);
+
+            vnet_buffer(b0)->sw_if_index[VLIB_RX] = ido0->ido_sw_if_index;
+            vnet_buffer(b1)->sw_if_index[VLIB_RX] = ido1->ido_sw_if_index;
+
+            vlib_increment_combined_counter (im->combined_sw_if_counters
+                                             + VNET_INTERFACE_COUNTER_RX,
+                                             thread_index,
+                                             ido0->ido_sw_if_index,
+                                             1,
+                                             vlib_buffer_length_in_chain (vm, b0));
+            vlib_increment_combined_counter (im->combined_sw_if_counters
+                                             + VNET_INTERFACE_COUNTER_RX,
+                                             thread_index,
+                                             ido1->ido_sw_if_index,
+                                             1,
+                                             vlib_buffer_length_in_chain (vm, b1));
+
+            if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
+            {
+                interface_rx_dpo_trace_t *tr0;
+
+                tr0 = vlib_add_trace (vm, node, b0, sizeof (*tr0));
+                tr0->sw_if_index = ido0->ido_sw_if_index;
+            }
+            if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
+            {
+                interface_rx_dpo_trace_t *tr1;
+
+                tr1 = vlib_add_trace (vm, node, b1, sizeof (*tr1));
+                tr1->sw_if_index = ido1->ido_sw_if_index;
+            }
+
+            vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next,
+                                            n_left_to_next, bi0, bi1,
+                                            INTERFACE_RX_DPO_INPUT,
+                                            INTERFACE_RX_DPO_INPUT);
+        }
+
+        while (n_left_from > 0 && n_left_to_next > 0)
+        {
+            const interface_rx_dpo_t * ido0;
+            vlib_buffer_t * b0;
+            u32 bi0, idoi0;
+
+            bi0 = from[0];
+            to_next[0] = bi0;
+            from += 1;
+            to_next += 1;
+            n_left_from -= 1;
+            n_left_to_next -= 1;
+
+            b0 = vlib_get_buffer (vm, bi0);
+
+            idoi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
+            ido0 = interface_rx_dpo_get(idoi0);
+
+            /* Swap the RX interface of the packet to the one the
+             * interface DPR represents */
+            vnet_buffer(b0)->sw_if_index[VLIB_RX] = ido0->ido_sw_if_index;
+
+            /* Bump the interface's RX coutners */
+            vlib_increment_combined_counter (im->combined_sw_if_counters
+                                             + VNET_INTERFACE_COUNTER_RX,
+                                             thread_index,
+                                             ido0->ido_sw_if_index,
+                                             1,
+                                             vlib_buffer_length_in_chain (vm, b0));
+
+            if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
+            {
+                interface_rx_dpo_trace_t *tr;
+
+                tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
+                tr->sw_if_index = ido0->ido_sw_if_index;
+            }
+
+            vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
+                                            n_left_to_next, bi0,
+                                            INTERFACE_RX_DPO_INPUT);
+        }
+        vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+    return from_frame->n_vectors;
+}
+
+static u8 *
+format_interface_rx_dpo_trace (u8 * s, va_list * args)
+{
+    CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+    CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+    interface_rx_dpo_trace_t * t = va_arg (*args, interface_rx_dpo_trace_t *);
+    uword indent = format_get_indent (s);
+    s = format (s, "%U sw_if_index:%d",
+                format_white_space, indent,
+                t->sw_if_index);
+    return s;
+}
+
+static uword
+interface_rx_dpo_ip4 (vlib_main_t * vm,
+                      vlib_node_runtime_t * node,
+                      vlib_frame_t * from_frame)
+{
+    return (interface_rx_dpo_inline(vm, node, from_frame));
+}
+
+static uword
+interface_rx_dpo_ip6 (vlib_main_t * vm,
+                      vlib_node_runtime_t * node,
+                      vlib_frame_t * from_frame)
+{
+    return (interface_rx_dpo_inline(vm, node, from_frame));
+}
+
+static uword
+interface_rx_dpo_l2 (vlib_main_t * vm,
+                     vlib_node_runtime_t * node,
+                     vlib_frame_t * from_frame)
+{
+    return (interface_rx_dpo_inline(vm, node, from_frame));
+}
+
+VLIB_REGISTER_NODE (interface_rx_dpo_ip4_node) = {
+    .function = interface_rx_dpo_ip4,
+    .name = "interface-rx-dpo-ip4",
+    .vector_size = sizeof (u32),
+    .format_trace = format_interface_rx_dpo_trace,
+
+    .n_next_nodes = 2,
+    .next_nodes = {
+        [INTERFACE_RX_DPO_DROP] = "ip4-drop",
+        [INTERFACE_RX_DPO_INPUT] = "ip4-input",
+    },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (interface_rx_dpo_ip4_node,
+                              interface_rx_dpo_ip4)
+
+VLIB_REGISTER_NODE (interface_rx_dpo_ip6_node) = {
+    .function = interface_rx_dpo_ip6,
+    .name = "interface-rx-dpo-ip6",
+    .vector_size = sizeof (u32),
+    .format_trace = format_interface_rx_dpo_trace,
+
+    .n_next_nodes = 2,
+    .next_nodes = {
+        [INTERFACE_RX_DPO_DROP] = "ip6-drop",
+        [INTERFACE_RX_DPO_INPUT] = "ip6-input",
+    },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (interface_rx_dpo_ip6_node,
+                              interface_rx_dpo_ip6)
+
+VLIB_REGISTER_NODE (interface_rx_dpo_l2_node) = {
+    .function = interface_rx_dpo_l2,
+    .name = "interface-rx-dpo-l2",
+    .vector_size = sizeof (u32),
+    .format_trace = format_interface_rx_dpo_trace,
+
+    .n_next_nodes = 2,
+    .next_nodes = {
+        [INTERFACE_RX_DPO_DROP] = "error-drop",
+        [INTERFACE_RX_DPO_INPUT] = "l2-input",
+    },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (interface_rx_dpo_l2_node,
+                              interface_rx_dpo_l2)
diff --git a/src/vnet/dpo/interface_dpo.h b/src/vnet/dpo/interface_rx_dpo.h
similarity index 62%
rename from src/vnet/dpo/interface_dpo.h
rename to src/vnet/dpo/interface_rx_dpo.h
index 1538dfb..edecce0 100644
--- a/src/vnet/dpo/interface_dpo.h
+++ b/src/vnet/dpo/interface_rx_dpo.h
@@ -12,17 +12,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-/**
- * @brief
- * The data-path object representing interfaceing the packet, i.e. it's for-us
- */
 
-#ifndef __INTERFACE_DPO_H__
-#define __INTERFACE_DPO_H__
+#ifndef __INTERFACE_RX_DPO_H__
+#define __INTERFACE_RX_DPO_H__
 
 #include <vnet/dpo/dpo.h>
 
-typedef struct interface_dpo_t_
+/**
+ * @brief
+ * The data-path object representing a change of receive interface.
+ * If a packet encounters an object of this type in the data-path, it's
+ * RX interface is changed.
+ */
+typedef struct interface_rx_dpo_t_
 {
     /**
      * The Software interface index that the packets will be given
@@ -45,23 +47,23 @@
      * number of locks.
      */
     u16 ido_locks;
-} interface_dpo_t;
+} interface_rx_dpo_t;
 
-extern void interface_dpo_add_or_lock (dpo_proto_t proto,
-                                       u32 sw_if_index,
-                                       dpo_id_t *dpo);
+extern void interface_rx_dpo_add_or_lock (dpo_proto_t proto,
+                                          u32 sw_if_index,
+                                          dpo_id_t *dpo);
 
-extern void interface_dpo_module_init(void);
+extern void interface_rx_dpo_module_init(void);
 
 /**
  * @brief pool of all interface DPOs
  */
-interface_dpo_t *interface_dpo_pool;
+interface_rx_dpo_t *interface_rx_dpo_pool;
 
-static inline interface_dpo_t *
-interface_dpo_get (index_t index)
+static inline interface_rx_dpo_t *
+interface_rx_dpo_get (index_t index)
 {
-    return (pool_elt_at_index(interface_dpo_pool, index));
+    return (pool_elt_at_index(interface_rx_dpo_pool, index));
 }
 
 #endif
diff --git a/src/vnet/dpo/interface_tx_dpo.c b/src/vnet/dpo/interface_tx_dpo.c
new file mode 100644
index 0000000..f7c8bfd
--- /dev/null
+++ b/src/vnet/dpo/interface_tx_dpo.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/dpo/interface_tx_dpo.h>
+#include <vnet/adj/rewrite.h>
+
+/*
+ * We do not lock nor unlock these DPOs since there is nothing to lock
+ * all we do is construct DPO object wrappers around a sw_if_index
+ */
+static void
+interface_tx_dpo_lock (dpo_id_t *dpo)
+{
+}
+
+static void
+interface_tx_dpo_unlock (dpo_id_t *dpo)
+{
+}
+
+/*
+ * interface_tx_dpo_add_or_lock
+ *
+ * construct DPO object wrappers around a sw_if_index
+ */
+void
+interface_tx_dpo_add_or_lock (dpo_proto_t proto,
+                              u32 sw_if_index,
+                              dpo_id_t *dpo)
+{
+    dpo_set(dpo, DPO_INTERFACE_TX, proto, sw_if_index);
+}
+
+u8*
+format_interface_tx_dpo (u8* s, va_list *ap)
+{
+    index_t index = va_arg(*ap, index_t);
+    CLIB_UNUSED(u32 indent) = va_arg(*ap, u32);
+    vnet_main_t * vnm = vnet_get_main();
+
+    return (format(s, "%U-dpo:",
+                   format_vnet_sw_interface_name,
+                   vnm,
+                   vnet_get_sw_interface(vnm, index)));
+}
+
+static void
+interface_tx_dpo_mem_show (void)
+{
+}
+
+u32*
+interface_tx_dpo_get_next_node (const dpo_id_t *dpo)
+{
+    u32 *node_indices = NULL;
+
+    /*
+     * return the interface's TX node for the wrapped sw_if_index
+     */
+    vec_add1(node_indices,
+             vnet_tx_node_index_for_sw_interface(vnet_get_main(),
+                                                 dpo->dpoi_index));
+
+    return (node_indices);
+}
+
+const static dpo_vft_t interface_tx_dpo_vft = {
+    .dv_lock = interface_tx_dpo_lock,
+    .dv_unlock = interface_tx_dpo_unlock,
+    .dv_format = format_interface_tx_dpo,
+    .dv_mem_show = interface_tx_dpo_mem_show,
+    .dv_get_next_node = interface_tx_dpo_get_next_node,
+};
+
+void
+interface_tx_dpo_module_init (void)
+{
+    dpo_register(DPO_INTERFACE_TX, &interface_tx_dpo_vft, NULL);
+}
+
diff --git a/src/vnet/dpo/interface_tx_dpo.h b/src/vnet/dpo/interface_tx_dpo.h
new file mode 100644
index 0000000..0c560ad
--- /dev/null
+++ b/src/vnet/dpo/interface_tx_dpo.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @brief
+ * The data-path object representing transmitting the packet on a n interface.
+ * This is a convenient DPO wrapper around a simple interface transmit and thus
+ * allows us to represent direct interface transmit in the DPO model.
+ */
+
+#ifndef __INTERFACE_TX_DPO_H__
+#define __INTERFACE_TX_DPO_H__
+
+#include <vnet/dpo/dpo.h>
+
+extern void interface_tx_dpo_add_or_lock (dpo_proto_t proto,
+                                          u32 sw_if_index,
+                                          dpo_id_t *dpo);
+
+extern void interface_tx_dpo_module_init(void);
+
+#endif