Dynamically compute ip feature subgraph order 53/1553/6
authorDave Barach <dave@barachs.net>
Tue, 14 Jun 2016 22:38:02 +0000 (18:38 -0400)
committerKeith Burns <alagalah@gmail.com>
Fri, 17 Jun 2016 16:24:16 +0000 (16:24 +0000)
This change-set enables plugins to add themselves to the ip4/ip6
feature subgraphs without having to modify core vpp engine code
at all. Add VNET_IP4/IP6_UNICAST/MULTICAST_FEATURE_INIT macros
which express the required ordering constraints, and off you go.

Along the way, added an implementation of Warshall's algorithm to
vppinfra; to compute the positive transitive closure of a relation. In
this case, the relation is "feature A runs before feature B."

With that in hand, ip_feature_init_cast(...) computes a partial order
across the set of configured feature subgraph nodes.

In unit-testing, we discovered VPP-145 - ip4/6 inacl wiped out
vnet_buffer(b)->ip>current_config_index, which exists in main. So, we
fixed that by moving b->trace_index, adding b->current_config_index,
and removing the ip opaque union current_config_index.

Change-Id: Iff132116f66413dc6b31ac3377198c7a32d51f48
Signed-off-by: Dave Barach <dave@barachs.net>
25 files changed:
vlib/vlib/buffer.h
vnet/Makefile.am
vnet/vnet/buffer.h
vnet/vnet/classify/input_acl.c
vnet/vnet/ip/ip.h
vnet/vnet/ip/ip4.h
vnet/vnet/ip/ip4_forward.c
vnet/vnet/ip/ip4_input.c
vnet/vnet/ip/ip4_source_check.c
vnet/vnet/ip/ip6.h
vnet/vnet/ip/ip6_forward.c
vnet/vnet/ip/ip6_input.c
vnet/vnet/ip/ip_feature_registration.c [new file with mode: 0644]
vnet/vnet/ip/ip_feature_registration.h [new file with mode: 0644]
vnet/vnet/ip/ip_input_acl.c
vnet/vnet/ipsec/ipsec.c
vnet/vnet/ipsec/ipsec_input.c
vnet/vnet/l2tp/decap.c
vnet/vnet/l2tp/l2tp.c
vnet/vnet/misc.c
vpp/api/api.c
vppinfra/Makefile.am
vppinfra/vppinfra/ptclosure.c [new file with mode: 0644]
vppinfra/vppinfra/ptclosure.h [new file with mode: 0644]
vppinfra/vppinfra/test_ptclosure.c [new file with mode: 0644]

index e11085b..5274957 100644 (file)
@@ -108,16 +108,10 @@ typedef struct {
      total length given here give total number of bytes in buffer chain.
   */
 
-
   u32 next_buffer;   /**< Next buffer for this linked-list of buffers.
                         Only valid if VLIB_BUFFER_NEXT_PRESENT flag is set. 
                      */
 
-  u32 trace_index; /**< Specifies index into trace buffer 
-                      if VLIB_PACKET_IS_TRACED flag is set. 
-                   */
-
-
   u32 clone_count; /**< Specifies whether this buffer should be 
                       reinitialized when freed. It will be reinitialized 
                       if the value is 0. This field can be used
@@ -129,13 +123,19 @@ typedef struct {
   vlib_error_t error;   /**< Error code for buffers to be enqueued 
                            to error handler. 
                         */
+  u32 current_config_index; /**< Used by feature subgraph arcs to
+                               visit enabled feature nodes
+                            */
 
   u32 opaque[8]; /**< Opaque data used by sub-graphs for their own purposes. 
                     See .../vnet/vnet/buffer.h
                  */
   CLIB_CACHE_LINE_ALIGN_MARK(cacheline1);
 
-  u32 opaque2[16];  /**< More opaque data, in its own cache line */
+  u32 trace_index; /**< Specifies index into trace buffer 
+                      if VLIB_PACKET_IS_TRACED flag is set. 
+                   */
+  u32 opaque2[15];  /**< More opaque data, currently unused */
 
   /***** end of second cache line */
   CLIB_CACHE_LINE_ALIGN_MARK(cacheline2);
index 61a1998..d0a06be 100644 (file)
@@ -248,6 +248,7 @@ libvnet_la_SOURCES +=                               \
  vnet/ip/format.c                              \
  vnet/ip/icmp4.c                               \
  vnet/ip/icmp6.c                               \
+ vnet/ip/ip_feature_registration.c             \
  vnet/ip/ip46_cli.c                            \
  vnet/ip/ip4_format.c                          \
  vnet/ip/ip4_forward.c                         \
@@ -280,6 +281,7 @@ nobase_include_HEADERS +=                   \
  vnet/ip/icmp6.h                               \
  vnet/ip/igmp_packet.h                         \
  vnet/ip/ip.h                                  \
+ vnet/ip/ip_feature_registration.h             \
  vnet/ip/ip4.h                                 \
  vnet/ip/ip4_mtrie.h                           \
  vnet/ip/ip4_error.h                           \
index ea25ad0..f74be39 100644 (file)
@@ -113,9 +113,6 @@ typedef struct {
 
       union {
        struct {
-         /* Current configuration index. */
-         u32 current_config_index;
-
          /* Flow hash value for this packet computed from IP src/dst address
             protocol and ports. */
          u32 flow_hash;
index 7e835a6..fb9a2a4 100644 (file)
@@ -41,12 +41,12 @@ vnet_inacl_ip_feature_enable (vlib_main_t * vnm,
       if (tid == INPUT_ACL_TABLE_IP4)
         {
           lm = &ip4_main.lookup_main;
-          ftype = IP4_RX_FEATURE_CHECK_ACCESS;
+          ftype = ip4_main.ip4_unicast_rx_feature_check_access;
         }
       else
         {
           lm = &ip6_main.lookup_main;
-          ftype = IP6_RX_FEATURE_CHECK_ACCESS;
+          ftype = ip6_main.ip6_unicast_rx_feature_check_access;
         }
 
       ipcm = &lm->rx_config_mains[VNET_UNICAST];
index 45062cf..c9a8293 100644 (file)
@@ -42,6 +42,7 @@
 
 #include <vppinfra/hash.h>
 #include <vppinfra/heap.h>             /* adjacency heap */
+#include <vppinfra/ptclosure.h>
 
 #include <vnet/vnet.h>
 
index 59ef685..c01006e 100644 (file)
@@ -43,6 +43,7 @@
 #include <vnet/ip/ip4_mtrie.h>
 #include <vnet/ip/ip4_packet.h>
 #include <vnet/ip/lookup.h>
+#include <vnet/ip/ip_feature_registration.h>
 
 typedef struct ip4_fib_t {
   /* Hash table for each prefix length mapping. */
@@ -101,30 +102,6 @@ typedef struct {
   uword function_opaque;
 } ip4_add_del_interface_address_callback_t;
 
-typedef enum {
-  /* First check access list to either permit or deny this
-     packet based on classification. */
-  IP4_RX_FEATURE_CHECK_ACCESS,
-
-  /* RPF check: verify that source address is reachable via
-     RX interface or via any interface. */
-  IP4_RX_FEATURE_SOURCE_CHECK_REACHABLE_VIA_RX,
-  IP4_RX_FEATURE_SOURCE_CHECK_REACHABLE_VIA_ANY,
-
-  /* IPSec */
-  IP4_RX_FEATURE_IPSEC,
-
-  /* vPath forwarding: won't return to call next feature
-     so any feature needed before vPath forwarding must be prior
-     to this entry */
-  IP4_RX_FEATURE_VPATH,
-
-  /* Must be last: perform forwarding lookup. */
-  IP4_RX_FEATURE_LOOKUP,
-
-  IP4_N_RX_FEATURE,
-} ip4_rx_feature_type_t;
-
 typedef struct ip4_main_t {
   ip_lookup_main_t lookup_main;
 
@@ -152,6 +129,22 @@ typedef struct ip4_main_t {
   /* Template used to generate IP4 ARP packets. */
   vlib_packet_template_t ip4_arp_request_packet_template;
 
+  /* feature path configuration lists */
+  vnet_ip_feature_registration_t * next_uc_feature;
+  vnet_ip_feature_registration_t * next_mc_feature;
+
+  /* Built-in unicast feature path indices, see ip_feature_init_cast(...)  */
+  u32 ip4_unicast_rx_feature_check_access;
+  u32 ip4_unicast_rx_feature_source_reachable_via_rx;
+  u32 ip4_unicast_rx_feature_source_reachable_via_any;
+  u32 ip4_unicast_rx_feature_ipsec;
+  u32 ip4_unicast_rx_feature_vpath;
+  u32 ip4_unicast_rx_feature_lookup;
+
+  /* Built-in multicast feature path indices */
+  u32 ip4_multicast_rx_feature_vpath;
+  u32 ip4_multicast_rx_feature_lookup;
+  
   /* Seed for Jenkins hash used to compute ip4 flow hash. */
   u32 flow_hash_seed;
 
@@ -169,6 +162,31 @@ typedef struct ip4_main_t {
 /* Global ip4 main structure. */
 extern ip4_main_t ip4_main;
 
+#define VNET_IP4_UNICAST_FEATURE_INIT(x,...)                    \
+  __VA_ARGS__ vnet_ip_feature_registration_t uc_##x;            \
+static void __vnet_add_feature_registration_uc_##x (void)       \
+  __attribute__((__constructor__)) ;                            \
+static void __vnet_add_feature_registration_uc_##x (void)       \
+{                                                               \
+  ip4_main_t * im = &ip4_main;                                  \
+  uc_##x.next = im->next_uc_feature;                            \
+  im->next_uc_feature = &uc_##x;                                \
+}                                                               \
+__VA_ARGS__ vnet_ip_feature_registration_t uc_##x 
+
+#define VNET_IP4_MULTICAST_FEATURE_INIT(x,...)                  \
+  __VA_ARGS__ vnet_ip_feature_registration_t mc_##x;            \
+static void __vnet_add_feature_registration_mc_##x (void)       \
+  __attribute__((__constructor__)) ;                            \
+static void __vnet_add_feature_registration_mc_##x (void)       \
+{                                                               \
+  ip4_main_t * im = &ip4_main;                                  \
+  mc_##x.next = im->next_mc_feature;                            \
+  im->next_mc_feature = &mc_##x;                                \
+}                                                               \
+__VA_ARGS__ vnet_ip_feature_registration_t mc_##x 
+
+
 /* Global ip4 input node.  Errors get attached to ip4 input node. */
 extern vlib_node_registration_t ip4_input_node;
 extern vlib_node_registration_t ip4_lookup_node;
index 94c446b..1de0b40 100644 (file)
@@ -1284,6 +1284,83 @@ ip4_sw_interface_admin_up_down (vnet_main_t * vnm,
  
 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ip4_sw_interface_admin_up_down);
 
+/* Built-in ip4 unicast rx feature path definition */
+VNET_IP4_UNICAST_FEATURE_INIT (ip4_inacl, static) = {
+  .node_name = "ip4-inacl", 
+  .runs_before = {"ip4-source-check-via-rx", 0}, 
+  .feature_index = &ip4_main.ip4_unicast_rx_feature_check_access,
+};
+
+VNET_IP4_UNICAST_FEATURE_INIT (ip4_source_check_1, static) = {
+  .node_name = "ip4-source-check-via-rx",
+  .runs_before = {"ip4-source-check-via-any", 0},
+  .feature_index = 
+  &ip4_main.ip4_unicast_rx_feature_source_reachable_via_rx,
+};
+
+VNET_IP4_UNICAST_FEATURE_INIT (ip4_source_check_2, static) = {
+  .node_name = "ip4-source-check-via-any",
+  .runs_before = {"ipsec-input-ip4", 0},
+  .feature_index = 
+  &ip4_main.ip4_unicast_rx_feature_source_reachable_via_any,
+};
+
+VNET_IP4_UNICAST_FEATURE_INIT (ip4_ipsec, static) = {
+  .node_name = "ipsec-input-ip4",
+  .runs_before = {"vpath-input-ip4", 0},
+  .feature_index = &ip4_main.ip4_unicast_rx_feature_ipsec,
+};
+
+VNET_IP4_UNICAST_FEATURE_INIT (ip4_vpath, static) = {
+  .node_name = "vpath-input-ip4",
+  .runs_before = {"ip4-lookup", 0},
+  .feature_index = &ip4_main.ip4_unicast_rx_feature_vpath,
+};
+
+VNET_IP4_UNICAST_FEATURE_INIT (ip4_lookup, static) = {
+  .node_name = "ip4-lookup",
+  .runs_before = {0}, /* not before any other features */
+  .feature_index = &ip4_main.ip4_unicast_rx_feature_lookup,
+};
+
+/* Built-in ip4 multicast rx feature path definition */
+VNET_IP4_MULTICAST_FEATURE_INIT (ip4_vpath_mc, static) = {
+  .node_name = "vpath-input-ip4",
+  .runs_before = {"ip4-lookup-multicast", 0},
+  .feature_index = &ip4_main.ip4_multicast_rx_feature_vpath,
+};
+
+VNET_IP4_MULTICAST_FEATURE_INIT (ip4_lookup_mc, static) = {
+  .node_name = "ip4-lookup-multicast",
+  .runs_before = {0}, /* not before any other features */
+  .feature_index = &ip4_main.ip4_multicast_rx_feature_lookup,
+};
+
+static char * feature_start_nodes[] = 
+  { "ip4-input", "ip4-input-no-checksum"};
+
+static clib_error_t *
+ip4_feature_init (vlib_main_t * vm, ip4_main_t * im)
+{
+  ip_lookup_main_t * lm = &im->lookup_main;
+  clib_error_t * error;
+  vnet_cast_t cast;
+
+  for (cast = 0; cast < VNET_N_CAST; cast++)
+    {
+      ip_config_main_t * cm = &lm->rx_config_mains[cast];
+      vnet_config_main_t * vcm = &cm->config_main;
+
+      if ((error = ip_feature_init_cast (vm, cm, vcm, 
+                                         feature_start_nodes,
+                                         ARRAY_LEN(feature_start_nodes),
+                                         cast,
+                                         1 /* is_ip4 */)))
+        return error;
+    }
+  return 0;
+}
+
 static clib_error_t *
 ip4_sw_interface_add_del (vnet_main_t * vnm,
                          u32 sw_if_index,
@@ -1293,57 +1370,31 @@ ip4_sw_interface_add_del (vnet_main_t * vnm,
   ip4_main_t * im = &ip4_main;
   ip_lookup_main_t * lm = &im->lookup_main;
   u32 ci, cast;
+  u32 feature_index;
 
   for (cast = 0; cast < VNET_N_CAST; cast++)
     {
       ip_config_main_t * cm = &lm->rx_config_mains[cast];
       vnet_config_main_t * vcm = &cm->config_main;
 
-      if (! vcm->node_index_by_feature_index)
-       {
-         if (cast == VNET_UNICAST)
-           {
-             static char * start_nodes[] = { "ip4-input", "ip4-input-no-checksum", };
-             static char * feature_nodes[] = {
-               [IP4_RX_FEATURE_CHECK_ACCESS] = "ip4-inacl",
-               [IP4_RX_FEATURE_SOURCE_CHECK_REACHABLE_VIA_RX] = "ip4-source-check-via-rx",
-               [IP4_RX_FEATURE_SOURCE_CHECK_REACHABLE_VIA_ANY] = "ip4-source-check-via-any",
-               [IP4_RX_FEATURE_IPSEC] = "ipsec-input-ip4",
-               [IP4_RX_FEATURE_VPATH] = "vpath-input-ip4",
-               [IP4_RX_FEATURE_LOOKUP] = "ip4-lookup",
-             };
-
-             vnet_config_init (vm, vcm,
-                               start_nodes, ARRAY_LEN (start_nodes),
-                               feature_nodes, ARRAY_LEN (feature_nodes));
-           }
-         else
-           {
-             static char * start_nodes[] = { "ip4-input", "ip4-input-no-checksum", };
-             static char * feature_nodes[] = {
-               [IP4_RX_FEATURE_VPATH] = "vpath-input-ip4",
-               [IP4_RX_FEATURE_LOOKUP] = "ip4-lookup-multicast",
-             };
-
-             vnet_config_init (vm, vcm,
-                               start_nodes, ARRAY_LEN (start_nodes),
-                               feature_nodes, ARRAY_LEN (feature_nodes));
-           }
-       }
-
       vec_validate_init_empty (cm->config_index_by_sw_if_index, sw_if_index, ~0);
       ci = cm->config_index_by_sw_if_index[sw_if_index];
 
+      if (cast == VNET_UNICAST)
+        feature_index = im->ip4_unicast_rx_feature_lookup;
+      else
+        feature_index = im->ip4_multicast_rx_feature_lookup;
+
       if (is_add)
        ci = vnet_config_add_feature (vm, vcm,
                                      ci,
-                                     IP4_RX_FEATURE_LOOKUP,
+                                      feature_index,
                                      /* config data */ 0,
                                      /* # bytes of config data */ 0);
       else
        ci = vnet_config_del_feature (vm, vcm,
                                      ci,
-                                     IP4_RX_FEATURE_LOOKUP,
+                                      feature_index,
                                      /* config data */ 0,
                                      /* # bytes of config data */ 0);
 
@@ -1450,6 +1501,8 @@ ip4_lookup_init (vlib_main_t * vm)
                               "ip4 arp");
   }
 
+  ip4_feature_init (vm, im);
+
   return 0;
 }
 
index 6063425..5b2b42d 100644 (file)
@@ -150,18 +150,18 @@ ip4_input_inline (vlib_main_t * vm,
          cm0 = lm->rx_config_mains + cast0;
          cm1 = lm->rx_config_mains + cast1;
 
-         vnet_buffer (p0)->ip.current_config_index = vec_elt (cm0->config_index_by_sw_if_index, sw_if_index0);
-         vnet_buffer (p1)->ip.current_config_index = vec_elt (cm1->config_index_by_sw_if_index, sw_if_index1);
+         p0->current_config_index = vec_elt (cm0->config_index_by_sw_if_index, sw_if_index0);
+         p1->current_config_index = vec_elt (cm1->config_index_by_sw_if_index, sw_if_index1);
 
          vnet_buffer (p0)->ip.adj_index[VLIB_RX] = ~0;
          vnet_buffer (p1)->ip.adj_index[VLIB_RX] = ~0;
 
          vnet_get_config_data (&cm0->config_main,
-                               &vnet_buffer (p0)->ip.current_config_index,
+                               &p0->current_config_index,
                                &next0,
                                /* # bytes of config data */ 0);
          vnet_get_config_data (&cm1->config_main,
-                               &vnet_buffer (p1)->ip.current_config_index,
+                               &p1->current_config_index,
                                &next1,
                                /* # bytes of config data */ 0);
 
@@ -264,10 +264,10 @@ ip4_input_inline (vlib_main_t * vm,
 
          cast0 = ip4_address_is_multicast (&ip0->dst_address) ? VNET_MULTICAST : VNET_UNICAST;
          cm0 = lm->rx_config_mains + cast0;
-         vnet_buffer (p0)->ip.current_config_index = vec_elt (cm0->config_index_by_sw_if_index, sw_if_index0);
+         p0->current_config_index = vec_elt (cm0->config_index_by_sw_if_index, sw_if_index0);
          vnet_buffer (p0)->ip.adj_index[VLIB_RX] = ~0;
          vnet_get_config_data (&cm0->config_main,
-                               &vnet_buffer (p0)->ip.current_config_index,
+                               &p0->current_config_index,
                                &next0,
                                /* # bytes of config data */ 0);
 
index 11e6678..64b1e0a 100644 (file)
@@ -142,11 +142,11 @@ ip4_source_check_inline (vlib_main_t * vm,
          ip1 = vlib_buffer_get_current (p1);
 
          c0 = vnet_get_config_data (&cm->config_main,
-                                    &vnet_buffer (p0)->ip.current_config_index,
+                                    &p0->current_config_index,
                                     &next0,
                                     sizeof (c0[0]));
          c1 = vnet_get_config_data (&cm->config_main,
-                                    &vnet_buffer (p1)->ip.current_config_index,
+                                    &p1->current_config_index,
                                     &next1,
                                     sizeof (c1[0]));
 
@@ -223,7 +223,7 @@ ip4_source_check_inline (vlib_main_t * vm,
          ip0 = vlib_buffer_get_current (p0);
 
          c0 = vnet_get_config_data (&cm->config_main,
-                                    &vnet_buffer (p0)->ip.current_config_index,
+                                    &p0->current_config_index,
                                     &next0,
                                     sizeof (c0[0]));
 
@@ -329,7 +329,7 @@ set_ip_source_check (vlib_main_t * vm,
   clib_error_t * error = 0;
   u32 sw_if_index, is_del, ci;
   ip4_source_check_config_t config;
-  ip4_rx_feature_type_t type;
+  u32 feature_index;
 
   sw_if_index = ~0;
 
@@ -343,7 +343,7 @@ set_ip_source_check (vlib_main_t * vm,
   is_del = 0;
   config.no_default_route = 0;
   config.fib_index = im->fib_index_by_sw_if_index[sw_if_index];
-  type = IP4_RX_FEATURE_SOURCE_CHECK_REACHABLE_VIA_RX;
+  feature_index = im->ip4_unicast_rx_feature_source_reachable_via_rx;
   if (unformat (input, "del"))
     is_del = 1;
 
@@ -353,7 +353,7 @@ set_ip_source_check (vlib_main_t * vm,
        : vnet_config_add_feature)
     (vm, &rx_cm->config_main,
      ci,
-     type,
+     feature_index,
      &config,
      sizeof (config));
   rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
index 69e69f2..3c27db0 100644 (file)
@@ -103,33 +103,6 @@ typedef struct {
   uword function_opaque;
 } ip6_add_del_interface_address_callback_t;
 
-typedef enum {
-  /* First check access list to either permit or deny this
-     packet based on classification. */
-  IP6_RX_FEATURE_CHECK_ACCESS,
-
-  /* RPF check: verify that source address is reachable via
-     RX interface or via any interface. */
-  IP6_RX_FEATURE_CHECK_SOURCE_REACHABLE_VIA_RX,
-  IP6_RX_FEATURE_CHECK_SOURCE_REACHABLE_VIA_ANY,
-
-  /* IPSec */
-  IP6_RX_FEATURE_IPSEC,
-
-  /* Intercept and decap L2TPv3 packets. */
-  IP6_RX_FEATURE_L2TPV3,
-
-  /* vPath forwarding: won't return to call next feature
-     so any feature needed before vPath forwarding must be prior
-     to this entry */
-  IP6_RX_FEATURE_VPATH,
-
-  /* Must be last: perform forwarding lookup. */
-  IP6_RX_FEATURE_LOOKUP,
-
-  IP6_N_RX_FEATURE,
-} ip6_rx_feature_type_t;
-
 typedef struct ip6_main_t {
   BVT(clib_bihash) ip6_lookup_table;
 
@@ -168,6 +141,21 @@ typedef struct ip6_main_t {
   u32 lookup_table_nbuckets;
   uword lookup_table_size;
 
+  /* feature path configuration lists */
+  vnet_ip_feature_registration_t * next_uc_feature;
+  vnet_ip_feature_registration_t * next_mc_feature;
+
+  /* Built-in unicast feature path indices, see ip_feature_init_cast(...)  */
+  u32 ip6_unicast_rx_feature_check_access;
+  u32 ip6_unicast_rx_feature_ipsec;
+  u32 ip6_unicast_rx_feature_l2tp_decap;
+  u32 ip6_unicast_rx_feature_vpath;
+  u32 ip6_unicast_rx_feature_lookup;
+
+  /* Built-in multicast feature path indices */
+  u32 ip6_multicast_rx_feature_vpath;
+  u32 ip6_multicast_rx_feature_lookup;
+
   /* Seed for Jenkins hash used to compute ip6 flow hash. */
   u32 flow_hash_seed;
 
@@ -185,6 +173,30 @@ typedef struct ip6_main_t {
 /* Global ip6 main structure. */
 extern ip6_main_t ip6_main;
 
+#define VNET_IP6_UNICAST_FEATURE_INIT(x,...)                    \
+  __VA_ARGS__ vnet_ip_feature_registration_t uc_##x;            \
+static void __vnet_add_feature_registration_uc_##x (void)       \
+  __attribute__((__constructor__)) ;                            \
+static void __vnet_add_feature_registration_uc_##x (void)       \
+{                                                               \
+  ip6_main_t * im = &ip6_main;                                  \
+  uc_##x.next = im->next_uc_feature;                            \
+  im->next_uc_feature = &uc_##x;                                \
+}                                                               \
+__VA_ARGS__ vnet_ip_feature_registration_t uc_##x 
+
+#define VNET_IP6_MULTICAST_FEATURE_INIT(x,...)                  \
+  __VA_ARGS__ vnet_ip_feature_registration_t mc_##x;            \
+static void __vnet_add_feature_registration_mc_##x (void)       \
+  __attribute__((__constructor__)) ;                            \
+static void __vnet_add_feature_registration_mc_##x (void)       \
+{                                                               \
+  ip6_main_t * im = &ip6_main;                                  \
+  mc_##x.next = im->next_mc_feature;                            \
+  im->next_mc_feature = &mc_##x;                                \
+}                                                               \
+__VA_ARGS__ vnet_ip_feature_registration_t mc_##x 
+
 /* Global ip6 input node.  Errors get attached to ip6 input node. */
 extern vlib_node_registration_t ip6_input_node;
 extern vlib_node_registration_t ip6_rewrite_node;
index e49d242..f77b99e 100644 (file)
@@ -1200,6 +1200,75 @@ ip6_sw_interface_admin_up_down (vnet_main_t * vnm,
 
 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ip6_sw_interface_admin_up_down);
 
+/* Built-in ip6 unicast rx feature path definition */
+VNET_IP6_UNICAST_FEATURE_INIT (ip6_inacl, static) = {
+  .node_name = "ip6-inacl", 
+  .runs_before = {"ipsec-input-ip6", 0}, 
+  .feature_index = &ip6_main.ip6_unicast_rx_feature_check_access,
+};
+
+VNET_IP6_UNICAST_FEATURE_INIT (ip6_ipsec, static) = {
+  .node_name = "ipsec-input-ip6",
+  .runs_before = {"l2tp-decap", 0},
+  .feature_index = &ip6_main.ip6_unicast_rx_feature_ipsec,
+};
+
+VNET_IP6_UNICAST_FEATURE_INIT (ip6_l2tp, static) = {
+  .node_name = "l2tp-decap",
+  .runs_before = {"vpath-input-ip6", 0},
+  .feature_index = &ip6_main.ip6_unicast_rx_feature_l2tp_decap,
+};
+
+VNET_IP6_UNICAST_FEATURE_INIT (ip6_vpath, static) = {
+  .node_name = "vpath-input-ip6",
+  .runs_before = {"ip6-lookup", 0},
+  .feature_index = &ip6_main.ip6_unicast_rx_feature_vpath,
+};
+
+VNET_IP6_UNICAST_FEATURE_INIT (ip6_lookup, static) = {
+  .node_name = "ip6-lookup",
+  .runs_before = {0}, /* not before any other features */
+  .feature_index = &ip6_main.ip6_unicast_rx_feature_lookup,
+};
+
+/* Built-in ip6 multicast rx feature path definition (none now) */
+VNET_IP6_MULTICAST_FEATURE_INIT (ip4_vpath_mc, static) = {
+  .node_name = "vpath-input-ip6",
+  .runs_before = {"ip6-lookup", 0},
+  .feature_index = &ip6_main.ip6_multicast_rx_feature_vpath,
+};
+
+VNET_IP6_MULTICAST_FEATURE_INIT (ip6_lookup, static) = {
+  .node_name = "ip6-lookup",
+  .runs_before = {0}, /* not before any other features */
+  .feature_index = &ip6_main.ip6_multicast_rx_feature_lookup,
+};
+
+static char * feature_start_nodes[] = 
+  {"ip6-input"};
+
+static clib_error_t *
+ip6_feature_init (vlib_main_t * vm, ip6_main_t * im)
+{
+  ip_lookup_main_t * lm = &im->lookup_main;
+  clib_error_t * error;
+  vnet_cast_t cast;
+  
+  for (cast = 0; cast < VNET_N_CAST; cast++)
+    {
+      ip_config_main_t * cm = &lm->rx_config_mains[cast];
+      vnet_config_main_t * vcm = &cm->config_main;
+      
+      if ((error = ip_feature_init_cast (vm, cm, vcm, 
+                                         feature_start_nodes,
+                                         ARRAY_LEN(feature_start_nodes),
+                                         cast,
+                                         0 /* is_ip4 */)))
+        return error;
+    }
+  return 0;
+}
+
 clib_error_t *
 ip6_sw_interface_add_del (vnet_main_t * vnm,
                          u32 sw_if_index,
@@ -1209,41 +1278,31 @@ ip6_sw_interface_add_del (vnet_main_t * vnm,
   ip6_main_t * im = &ip6_main;
   ip_lookup_main_t * lm = &im->lookup_main;
   u32 ci, cast;
+  u32 feature_index;
 
   for (cast = 0; cast < VNET_N_CAST; cast++)
     {
       ip_config_main_t * cm = &lm->rx_config_mains[cast];
       vnet_config_main_t * vcm = &cm->config_main;
 
-      /* FIXME multicast. */
-      if (! vcm->node_index_by_feature_index)
-       {
-         char * start_nodes[] = { "ip6-input", };
-         char * feature_nodes[] = {
-           [IP6_RX_FEATURE_CHECK_ACCESS] = "ip6-inacl",
-            [IP6_RX_FEATURE_IPSEC] = "ipsec-input-ip6",
-           [IP6_RX_FEATURE_L2TPV3] = "l2tp-decap",
-           [IP6_RX_FEATURE_VPATH]  = "vpath-input-ip6",
-           [IP6_RX_FEATURE_LOOKUP] = "ip6-lookup",
-         };
-         vnet_config_init (vm, vcm,
-                           start_nodes, ARRAY_LEN (start_nodes),
-                           feature_nodes, ARRAY_LEN (feature_nodes));
-       }
-
       vec_validate_init_empty (cm->config_index_by_sw_if_index, sw_if_index, ~0);
       ci = cm->config_index_by_sw_if_index[sw_if_index];
 
+      if (cast == VNET_UNICAST)
+        feature_index = im->ip6_unicast_rx_feature_lookup;
+      else
+        feature_index = im->ip6_multicast_rx_feature_lookup;
+
       if (is_add)
        ci = vnet_config_add_feature (vm, vcm,
                                      ci,
-                                     IP6_RX_FEATURE_LOOKUP,
+                                      feature_index,
                                      /* config data */ 0,
                                      /* # bytes of config data */ 0);
       else
        ci = vnet_config_del_feature (vm, vcm,
                                      ci,
-                                     IP6_RX_FEATURE_LOOKUP,
+                                      feature_index,
                                      /* config data */ 0,
                                      /* # bytes of config data */ 0);
 
@@ -2857,6 +2916,8 @@ ip6_lookup_init (vlib_main_t * vm)
                               "ip6 neighbor discovery");
   }
 
+  ip6_feature_init (vm, im);
+
   return 0;
 }
 
index 2042cbd..7b5470d 100644 (file)
@@ -149,18 +149,18 @@ ip6_input (vlib_main_t * vm,
          cm0 = lm->rx_config_mains + cast0;
          cm1 = lm->rx_config_mains + cast1;
 
-         vnet_buffer (p0)->ip.current_config_index = vec_elt (cm0->config_index_by_sw_if_index, sw_if_index0);
-         vnet_buffer (p1)->ip.current_config_index = vec_elt (cm1->config_index_by_sw_if_index, sw_if_index1);
+         p0->current_config_index = vec_elt (cm0->config_index_by_sw_if_index, sw_if_index0);
+         p1->current_config_index = vec_elt (cm1->config_index_by_sw_if_index, sw_if_index1);
 
          vnet_buffer (p0)->ip.adj_index[VLIB_RX] = ~0;
          vnet_buffer (p1)->ip.adj_index[VLIB_RX] = ~0;
 
          vnet_get_config_data (&cm0->config_main,
-                               &vnet_buffer (p0)->ip.current_config_index,
+                               &p0->current_config_index,
                                &next0,
                                /* # bytes of config data */ 0);
          vnet_get_config_data (&cm1->config_main,
-                               &vnet_buffer (p1)->ip.current_config_index,
+                               &p1->current_config_index,
                                &next1,
                                /* # bytes of config data */ 0);
 
@@ -234,11 +234,11 @@ ip6_input (vlib_main_t * vm,
          sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
          cast0 = ip6_address_is_multicast (&ip0->dst_address) ? VNET_MULTICAST : VNET_UNICAST;
          cm0 = lm->rx_config_mains + cast0;
-         vnet_buffer (p0)->ip.current_config_index = vec_elt (cm0->config_index_by_sw_if_index, sw_if_index0);
+         p0->current_config_index = vec_elt (cm0->config_index_by_sw_if_index, sw_if_index0);
          vnet_buffer (p0)->ip.adj_index[VLIB_RX] = ~0;
 
          vnet_get_config_data (&cm0->config_main,
-                               &vnet_buffer (p0)->ip.current_config_index,
+                               &p0->current_config_index,
                                &next0,
                                /* # bytes of config data */ 0);
 
diff --git a/vnet/vnet/ip/ip_feature_registration.c b/vnet/vnet/ip/ip_feature_registration.c
new file mode 100644 (file)
index 0000000..02699c4
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2015 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/vnet.h>
+#include <vnet/ip/ip.h>
+
+static int comma_split (u8 *s, u8 **a, u8 **b)
+{
+  *a = s;
+
+  while (*s && *s != ',')
+    s++;
+
+  if (*s == ',')
+    *s = 0;
+  else
+    return 1;
+
+  *b = (u8 *) (s+1);
+  return 0;
+}
+
+clib_error_t *
+ip_feature_init_cast (vlib_main_t * vm,
+                      ip_config_main_t * cm,
+                      vnet_config_main_t * vcm,
+                      char **feature_start_nodes,
+                      int num_feature_start_nodes,
+                      vnet_cast_t cast,
+                      int is_ip4)
+{
+  uword * index_by_name;
+  uword * reg_by_index;
+  u8 ** node_names = 0;
+  u8 * node_name;
+  char ** these_constraints;
+  char * this_constraint_c;
+  u8 ** constraints = 0;
+  u8 * constraint_tuple;
+  u8 * this_constraint;
+  u8 ** orig, ** closure;
+  uword * p;
+  int i, j, k;
+  u8 * a_name, * b_name;
+  int a_index, b_index;
+  int n_features;
+  u32 * result = 0;
+  vnet_ip_feature_registration_t * this_reg, * first_reg;
+  char ** feature_nodes = 0;
+  hash_pair_t * hp;
+  u8 ** keys_to_delete = 0;
+  ip4_main_t * im4 = &ip4_main;
+  ip6_main_t * im6 = &ip6_main;
+
+  index_by_name = hash_create_string (0, sizeof (uword));
+  reg_by_index = hash_create (0, sizeof (uword));
+
+  if (cast == VNET_UNICAST)
+    {
+      if (is_ip4)
+        first_reg = im4->next_uc_feature;
+      else
+        first_reg = im6->next_uc_feature;
+    }
+  else
+    {
+      if (is_ip4)
+        first_reg = im4->next_mc_feature;
+      else
+        first_reg = im6->next_mc_feature;
+    }
+  
+  this_reg = first_reg;
+
+  /* pass 1, collect feature node names, construct a before b pairs */
+  while (this_reg)
+    {
+      node_name = format (0, "%s%c", this_reg->node_name, 0);
+      hash_set (reg_by_index, vec_len(node_names), (uword) this_reg);
+
+      hash_set_mem (index_by_name, node_name, vec_len(node_names));
+
+      vec_add1 (node_names, node_name);
+
+      these_constraints = this_reg->runs_before;
+
+      while (these_constraints [0])
+        {
+          this_constraint_c = these_constraints[0];
+
+          constraint_tuple = format (0, "%s,%s%c", node_name,
+                                     this_constraint_c, 0);
+          vec_add1 (constraints, constraint_tuple);
+          these_constraints++;
+        }
+      this_reg = this_reg->next;
+    }
+
+  n_features = vec_len (node_names);
+  orig = clib_ptclosure_alloc (n_features);
+
+  for (i = 0; i < vec_len (constraints); i++)
+    {
+      this_constraint = constraints[i];
+
+      if (comma_split (this_constraint, &a_name, &b_name))
+        return clib_error_return (0, "comma_split failed!");
+      
+      p = hash_get_mem (index_by_name, a_name);
+      if (p == 0)
+        return clib_error_return (0, "feature node '%s' not found", a_name);
+      a_index = p[0];
+
+      p = hash_get_mem (index_by_name, b_name);
+      if (p == 0)
+        return clib_error_return (0, "feature node '%s' not found", b_name);
+      b_index = p[0];
+
+      /* add a before b to the original set of constraints */
+      orig[a_index][b_index] = 1;
+      vec_free (this_constraint);
+    }
+  
+  /* Compute the positive transitive closure of the original constraints */
+  closure = clib_ptclosure (orig);
+
+  /* Compute a partial order across feature nodes, if one exists. */
+ again:
+  for (i = 0; i < n_features; i++)
+    {
+      for (j = 0; j < n_features; j++)
+        {
+          if (closure[i][j])
+            goto item_constrained;
+        }
+      /* Item i can be output */
+      vec_add1 (result, i);
+      {
+        for (k = 0; k < n_features; k++)
+          closure [k][i] = 0;
+        /* 
+         * Add a "Magic" a before a constraint. 
+         * This means we'll never output it again
+         */
+        closure [i][i] = 1;
+        goto again;
+      }
+    item_constrained:
+      ;
+    }
+
+  /* see if we got a partial order... */
+  if (vec_len (result) != n_features)
+    return clib_error_return (0, "ip4_feature_init_cast (cast=%d), no PO!");
+
+  /* 
+   * We win.
+   * Bind the index variables, and output the feature node name vector
+   * using the partial order we just computed. Result is in stack
+   * order, because the entry with the fewest constraints (e.g. none)
+   * is output first, etc.
+   */
+
+  for (i = n_features-1; i >= 0; i--)
+    {
+      p = hash_get (reg_by_index, result[i]);
+      ASSERT (p != 0);
+      this_reg = (vnet_ip_feature_registration_t *)p[0];
+      *this_reg->feature_index = n_features - (i+1);
+      vec_add1 (feature_nodes, this_reg->node_name);
+    }
+  
+  /* Set up the config infrastructure */
+  vnet_config_init (vm, vcm,
+                    feature_start_nodes, 
+                    num_feature_start_nodes,
+                    feature_nodes, 
+                    vec_len(feature_nodes));
+
+  /* Finally, clean up all the shit we allocated */
+  hash_foreach_pair (hp, index_by_name,
+  ({
+    vec_add1 (keys_to_delete, (u8 *)hp->key);
+  }));
+  hash_free (index_by_name);
+  for (i = 0; i < vec_len(keys_to_delete); i++)
+    vec_free (keys_to_delete[i]);
+  vec_free (keys_to_delete);
+  hash_free (reg_by_index);
+  vec_free (result);
+  clib_ptclosure_free (orig);
+  clib_ptclosure_free (closure);
+  return 0;
+}
+
diff --git a/vnet/vnet/ip/ip_feature_registration.h b/vnet/vnet/ip/ip_feature_registration.h
new file mode 100644 (file)
index 0000000..da2a005
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 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.
+ */
+
+#ifndef included_ip_feature_registration_h
+#define included_ip_feature_registration_h
+
+typedef struct _vnet_ip_feature_registration {
+  struct _vnet_ip_feature_registration * next;
+  char * node_name;
+  u32 * feature_index;
+  char * runs_before[];
+} vnet_ip_feature_registration_t;
+
+clib_error_t *
+ip_feature_init_cast (vlib_main_t * vm,
+                      ip_config_main_t * cm,
+                      vnet_config_main_t * vcm,
+                      char **feature_start_nodes,
+                      int num_feature_start_nodes,
+                      vnet_cast_t cast,
+                      int is_ip4);
+
+#endif /* included_ip_feature_registration_h */
index fcf8eea..eaf7f34 100644 (file)
@@ -232,7 +232,7 @@ ip_inacl_inline (vlib_main_t * vm,
           e0 = 0;
           t0 = 0;
           vnet_get_config_data (am->vnet_config_main[tid],
-                                &vnet_buffer(b0)->ip.current_config_index,
+                                &b0->current_config_index,
                                 &next0,
                                 /* # bytes of config data */ 0);
 
index ea077d0..1c9d57b 100644 (file)
@@ -73,7 +73,7 @@ ipsec_set_interface_spd(vlib_main_t * vm, u32 sw_if_index, u32 spd_id, int is_ad
   ci = (is_add ? vnet_config_add_feature : vnet_config_del_feature)
     (vm, &rx_cm->config_main,
      ci,
-     IP4_RX_FEATURE_IPSEC,
+     ip4_main.ip4_unicast_rx_feature_ipsec,
      &config,
      sizeof (config));
   rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
@@ -87,7 +87,7 @@ ipsec_set_interface_spd(vlib_main_t * vm, u32 sw_if_index, u32 spd_id, int is_ad
   ci = (is_add ? vnet_config_add_feature : vnet_config_del_feature)
     (vm, &rx_cm->config_main,
      ci,
-     IP6_RX_FEATURE_IPSEC,
+     ip6_main.ip6_unicast_rx_feature_ipsec,
      &config,
      sizeof (config));
   rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
index 09acd10..e701117 100644 (file)
@@ -212,7 +212,7 @@ ipsec_input_ip4_node_fn (vlib_main_t * vm,
 
           b0 = vlib_get_buffer (vm, bi0);
           c0 = vnet_get_config_data (&cm->config_main,
-                                     &vnet_buffer (b0)->ip.current_config_index,
+                                     &b0->current_config_index,
                                      &next0, sizeof (c0[0]));
 
           spd0 = pool_elt_at_index(im->spds, c0->spd_index);
@@ -335,7 +335,7 @@ ipsec_input_ip6_node_fn (vlib_main_t * vm,
 
           b0 = vlib_get_buffer (vm, bi0);
           c0 = vnet_get_config_data (&cm->config_main,
-                                     &vnet_buffer (b0)->ip.current_config_index,
+                                     &b0->current_config_index,
                                      &next0, sizeof (c0[0]));
 
           spd0 = pool_elt_at_index(im->spds, c0->spd_index);
index 5f0d05c..8b9761a 100644 (file)
@@ -198,7 +198,7 @@ static inline u32 last_stage (vlib_main_t *vm, vlib_node_runtime_t *node,
       ip6_l2tpv3_config_t * c0;
 
       vnet_get_config_data (&cm->config_main,
-                            &vnet_buffer (b)->ip.current_config_index,
+                            &b->current_config_index,
                             &next_index,
                             sizeof (c0[0]));
     }
index 7dfbe15..db4b4f5 100644 (file)
@@ -577,12 +577,12 @@ int l2tpv3_interface_enable_disable (vnet_main_t * vnm,
   ip_config_main_t * rx_cm = &lm->rx_config_mains[VNET_UNICAST];
   u32 ci;
   ip6_l2tpv3_config_t config;
-  ip6_rx_feature_type_t type;
+  u32 feature_index;
 
   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
 
-  type = IP6_RX_FEATURE_L2TPV3;
+  feature_index = im->ip6_unicast_rx_feature_ipsec;
 
   ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
   ci = (enable_disable
@@ -590,7 +590,7 @@ int l2tpv3_interface_enable_disable (vnet_main_t * vnm,
         : vnet_config_del_feature)
     (vlib_get_main(), &rx_cm->config_main,
      ci,
-     type,
+     feature_index,
      &config,
      sizeof (config));
   rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
index 6effe6e..9dbed8d 100644 (file)
@@ -76,6 +76,15 @@ vnet_main_init (vlib_main_t * vm)
   if ((error = vlib_call_init_function (vm, vnet_interface_init)))
     return error;
 
+  if ((error = vlib_call_init_function (vm, ip_main_init)))
+    return error;
+
+  if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
+    return error;
+
+  if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
+    return error;
+
   vnm->vlib_main = vm;
 
   hw_if_index = vnet_register_interface
index f99d9ce..bb9a296 100644 (file)
@@ -1345,6 +1345,8 @@ static void
 vl_api_sw_interface_set_vpath_t_handler (vl_api_sw_interface_set_vpath_t *mp)
 {
     vlib_main_t *vm = vlib_get_main();
+    ip4_main_t * im4 = &ip4_main;
+    ip6_main_t * im6 = &ip6_main;
     vl_api_sw_interface_set_vpath_reply_t * rmp;
     int rv = 0;
     u32 ci;
@@ -1364,36 +1366,52 @@ vl_api_sw_interface_set_vpath_t_handler (vl_api_sw_interface_set_vpath_t *mp)
     if (mp->enable) {
         ci = rx_cm4u->config_index_by_sw_if_index[sw_if_index]; //IP4 unicast
         ci = vnet_config_add_feature(vm, &rx_cm4u->config_main,
-                                     ci, IP4_RX_FEATURE_VPATH, 0, 0);
+                                     ci, 
+                                     im4->ip4_unicast_rx_feature_vpath,
+                                     0, 0);
         rx_cm4u->config_index_by_sw_if_index[sw_if_index] = ci;
         ci = rx_cm4m->config_index_by_sw_if_index[sw_if_index]; //IP4 mcast
         ci = vnet_config_add_feature(vm, &rx_cm4m->config_main,
-                                     ci, IP4_RX_FEATURE_VPATH, 0, 0);
+                                     ci, 
+                                     im4->ip4_multicast_rx_feature_vpath,
+                                     0, 0);
         rx_cm4m->config_index_by_sw_if_index[sw_if_index] = ci;
         ci = rx_cm6u->config_index_by_sw_if_index[sw_if_index]; //IP6 unicast
         ci = vnet_config_add_feature(vm, &rx_cm6u->config_main,
-                                     ci, IP6_RX_FEATURE_VPATH, 0, 0);
+                                     ci, 
+                                     im6->ip6_unicast_rx_feature_vpath,
+                                     0, 0);
         rx_cm6u->config_index_by_sw_if_index[sw_if_index] = ci;
         ci = rx_cm6m->config_index_by_sw_if_index[sw_if_index]; //IP6 mcast
         ci = vnet_config_add_feature(vm, &rx_cm6m->config_main,
-                                     ci, IP6_RX_FEATURE_VPATH, 0, 0);
+                                     ci, 
+                                     im6->ip6_multicast_rx_feature_vpath,
+                                     0, 0);
         rx_cm6m->config_index_by_sw_if_index[sw_if_index] = ci;
     } else {
         ci = rx_cm4u->config_index_by_sw_if_index[sw_if_index]; //IP4 unicast
         ci = vnet_config_del_feature(vm, &rx_cm4u->config_main,
-                                     ci, IP4_RX_FEATURE_VPATH, 0, 0);
+                                     ci, 
+                                     im4->ip4_unicast_rx_feature_vpath,
+                                     0, 0);
         rx_cm4u->config_index_by_sw_if_index[sw_if_index] = ci;
         ci = rx_cm4m->config_index_by_sw_if_index[sw_if_index]; //IP4 mcast
         ci = vnet_config_del_feature(vm, &rx_cm4m->config_main,
-                                     ci, IP4_RX_FEATURE_VPATH, 0, 0);
+                                     ci, 
+                                     im4->ip4_multicast_rx_feature_vpath,
+                                     0, 0);
         rx_cm4m->config_index_by_sw_if_index[sw_if_index] = ci;
         ci = rx_cm6u->config_index_by_sw_if_index[sw_if_index]; //IP6 unicast
         ci = vnet_config_del_feature(vm, &rx_cm6u->config_main,
-                                     ci, IP6_RX_FEATURE_VPATH, 0, 0);
+                                     ci, 
+                                     im6->ip6_unicast_rx_feature_vpath,
+                                     0, 0);
         rx_cm6u->config_index_by_sw_if_index[sw_if_index] = ci;
         ci = rx_cm6m->config_index_by_sw_if_index[sw_if_index]; //IP6 mcast
         ci = vnet_config_del_feature(vm, &rx_cm6m->config_main,
-                                     ci, IP6_RX_FEATURE_VPATH, 0, 0);
+                                     ci, 
+                                     im6->ip6_multicast_rx_feature_vpath,
+                                     0, 0);
         rx_cm6m->config_index_by_sw_if_index[sw_if_index] = ci;
     }
 
index 1b4d627..b99f3d5 100644 (file)
@@ -20,7 +20,7 @@ endif
 
 lib_LIBRARIES =
 
-TESTS =
+TESTS = 
 
 if ENABLE_TESTS
 TESTS  +=  test_bihash_template \
@@ -37,6 +37,7 @@ TESTS  +=  test_bihash_template \
           test_pfhash \
           test_phash \
           test_pool_iterate \
+          test_ptclosure \
           test_qhash \
           test_random \
           test_random_isaac \
@@ -66,6 +67,7 @@ test_mheap_SOURCES = vppinfra/test_mheap.c
 test_pfhash_SOURCES = vppinfra/test_pfhash.c
 test_phash_SOURCES = vppinfra/test_phash.c
 test_pool_iterate_SOURCES = vppinfra/test_pool_iterate.c
+test_ptclosure_SOURCES = vppinfra/test_ptclosure.c
 test_qhash_SOURCES = vppinfra/test_qhash.c
 test_random_SOURCES = vppinfra/test_random.c
 test_random_isaac_SOURCES = vppinfra/test_random_isaac.c
@@ -93,6 +95,7 @@ test_mheap_CPPFLAGS = $(AM_CPPFLAGS) -DCLIB_DEBUG
 test_pfhash_CPPFLAGS = $(AM_CPPFLAGS) -DCLIB_DEBUG
 test_phash_CPPFLAGS =  $(AM_CPPFLAGS) -DCLIB_DEBUG
 test_pool_iterate_CPPFLAGS =   $(AM_CPPFLAGS) -DCLIB_DEBUG
+test_ptclosure_CPPFLAGS =      $(AM_CPPFLAGS) -DCLIB_DEBUG
 test_qhash_CPPFLAGS =  $(AM_CPPFLAGS) -DCLIB_DEBUG
 test_random_CPPFLAGS = $(AM_CPPFLAGS) -DCLIB_DEBUG
 test_random_isaac_CPPFLAGS = $(AM_CPPFLAGS) -DCLIB_DEBUG
@@ -118,6 +121,7 @@ test_mheap_LDADD =  libvppinfra.la
 test_pfhash_LDADD =    libvppinfra.la
 test_phash_LDADD =     libvppinfra.la
 test_pool_iterate_LDADD =      libvppinfra.la
+test_ptclosure_LDADD = libvppinfra.la
 test_qhash_LDADD =     libvppinfra.la
 test_random_LDADD =    libvppinfra.la
 test_random_isaac_LDADD =      libvppinfra.la
@@ -143,6 +147,7 @@ test_mheap_LDFLAGS = -static
 test_pfhash_LDFLAGS = -static
 test_phash_LDFLAGS = -static
 test_pool_iterate_LDFLAGS = -static
+test_ptclosure_LDFLAGS = -static
 test_qhash_LDFLAGS = -static
 test_random_LDFLAGS = -static
 test_random_isaac_LDFLAGS = -static
@@ -199,6 +204,7 @@ nobase_include_HEADERS = \
   vppinfra/phash.h \
   vppinfra/pipeline.h \
   vppinfra/pool.h \
+  vppinfra/ptclosure.h \
   vppinfra/qhash.h \
   vppinfra/random.h \
   vppinfra/random_buffer.h \
@@ -249,6 +255,7 @@ CLIB_CORE = \
   vppinfra/mem_mheap.c \
   vppinfra/pfhash.c \
   vppinfra/phash.c \
+  vppinfra/ptclosure.c \
   vppinfra/qhash.c \
   vppinfra/random.c \
   vppinfra/random_buffer.c \
diff --git a/vppinfra/vppinfra/ptclosure.c b/vppinfra/vppinfra/ptclosure.c
new file mode 100644 (file)
index 0000000..705af62
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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 <vppinfra/ptclosure.h>
+
+u8 ** clib_ptclosure_alloc (int n)
+{
+  u8 ** rv = 0;
+  u8 * row;
+  int i;
+
+  ASSERT (n > 0);
+
+  vec_validate (rv, n-1);
+  for (i = 0; i < n; i++)
+    {
+      row = 0;
+      vec_validate (row, n-1);
+      
+      rv[i] = row;
+    }
+  return rv;
+}
+
+void clib_ptclosure_free (u8 ** ptc)
+{
+  u8 * row;
+  int n = vec_len (ptc);
+  int i;
+
+  ASSERT (n > 0);
+  
+  for (i = 0; i < n; i++)
+    {
+      row = ptc[i];
+      vec_free (row);
+    }
+  vec_free (ptc);
+}
+
+void clib_ptclosure_copy (u8 ** dst, u8 **src)
+{
+  int i, n;
+  u8 * src_row, * dst_row;
+
+  n = vec_len (dst);
+
+  for (i = 0; i < vec_len(dst); i++)
+    {
+      src_row = src[i];
+      dst_row = dst[i];
+      clib_memcpy (dst_row, src_row, n);
+    }
+}
+
+/*
+ * compute the positive transitive closure
+ * of a relation via Warshall's algorithm. 
+ * 
+ * Ref:
+ * Warshall, Stephen (January 1962). "A theorem on Boolean matrices". 
+ * Journal of the ACM 9 (1): 11–12. 
+ *
+ * foo[i][j] = 1 means that item i 
+ * "bears the relation" to item j.
+ *
+ * For example: "item i must be before item j"
+ *
+ * You could use a bitmap, but since the algorithm is
+ * O(n**3) in the first place, large N is inadvisable...
+ *
+ */
+
+u8 ** clib_ptclosure (u8 ** orig)
+{
+  int i, j, k;
+  int n;
+  u8 ** prev, ** cur;
+
+  n = vec_len (orig);
+  prev = clib_ptclosure_alloc (n);
+  cur = clib_ptclosure_alloc (n);
+
+  clib_ptclosure_copy (prev, orig);
+
+  for (k = 0; k < n; k++)
+    {
+      for (i = 0; i < n; i++)
+        {
+          for (j = 0; j < n; j++)
+            {
+              cur[i][j] = prev[i][j] || (prev[i][k] && prev[k][j]);
+            }
+        }
+      clib_ptclosure_copy (prev, cur);
+    }
+  clib_ptclosure_free (prev);
+  return cur;
+}
+
diff --git a/vppinfra/vppinfra/ptclosure.h b/vppinfra/vppinfra/ptclosure.h
new file mode 100644 (file)
index 0000000..c3b7174
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+#ifndef included_clib_ptclosure_h
+#define included_clib_ptclosure_h
+
+#include <vppinfra/vec.h>
+#include <vppinfra/format.h>
+#include <vppinfra/error.h>
+
+/* 
+ * set r[i][j] if item i "bears the relation to" item j 
+ * 
+ */
+
+u8 ** clib_ptclosure_alloc (int n);
+void clib_ptclosure_free (u8 ** ptc);
+void clib_ptclosure_copy (u8 ** dst, u8 **src);
+u8 ** clib_ptclosure (u8 ** orig);
+
+#endif /* included_clib_ptclosure_h */
diff --git a/vppinfra/vppinfra/test_ptclosure.c b/vppinfra/vppinfra/test_ptclosure.c
new file mode 100644 (file)
index 0000000..b5ac13f
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2015 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 <vppinfra/ptclosure.h>
+#include <vppinfra/hash.h>
+
+typedef struct {
+  uword * index_by_name;
+  u8 * items;
+} test_main_t;
+
+test_main_t test_main;
+
+static char * items [] = {
+    "d",
+    "a",
+    "b",
+    "c",
+};
+
+char * constraints [] = {
+    "a,b",
+    "b,c",
+    "d,b",
+    //    "c,a", /* no partial order possible */
+};
+
+u32 vl(void *p)
+{
+  return vec_len (p);
+}
+
+static void dump_closure (test_main_t * tm, char * s, u8 ** orig)
+{
+  int i, j;
+
+  fformat (stdout, "--------- %s --------------\n", s);
+  for (i = 0; i < vec_len (orig); i++)
+    {
+      for (j = 0; j < vec_len (orig); j++)
+        if (orig[i][j])
+          {
+            fformat (stdout, "%s <before> %s\n", items[i], items[j]);
+          }
+    }
+}
+
+int comma_split (u8 *s, u8 **a, u8 **b)
+{
+  *a = s;
+
+  while (*s && *s != ',')
+    s++;
+
+  if (*s == ',')
+    *s = 0;
+  else
+    return 1;
+
+  *b = (u8 *) (s+1);
+  return 0;
+}
+
+int test_ptclosure_main (unformat_input_t * input)
+{
+  test_main_t * tm = &test_main;
+  u8 * item_name;
+  int i, j;
+  u8 ** orig;
+  u8 ** closure;
+  u8 * a_name, * b_name;
+  int a_index, b_index;
+  uword * p;
+  u8 * this_constraint;
+  int n;
+  u32 * result = 0;
+
+  tm->index_by_name = hash_create_string (0, sizeof (uword));
+
+  n = ARRAY_LEN(items);
+
+  for (i = 0; i < n; i++)
+    {
+      item_name = (u8 *) items[i];
+      hash_set_mem (tm->index_by_name, item_name, i);
+    }
+
+  orig = clib_ptclosure_alloc (n);
+
+  for (i = 0; i < ARRAY_LEN(constraints); i++)
+    {
+      this_constraint = format (0, "%s%c", constraints[i], 0);
+      
+      if (comma_split (this_constraint, &a_name, &b_name))
+        {
+          clib_warning ("couldn't split '%s'", constraints[i]);
+          return 1;
+        }
+      
+      p = hash_get_mem (tm->index_by_name, a_name);
+      if (p == 0)
+        {
+          clib_warning ("couldn't find '%s'", a_name);
+          return 1;
+        }
+      a_index = p[0];
+
+      p = hash_get_mem (tm->index_by_name, b_name);
+      if (p == 0)
+        {
+          clib_warning ("couldn't find '%s'", b_name);
+          return 1;
+        }
+      b_index = p[0];
+
+      orig[a_index][b_index] = 1;
+      vec_free (this_constraint);
+    }
+  
+  dump_closure (tm, "original relation", orig);
+
+  closure = clib_ptclosure (orig);
+
+  dump_closure (tm, "closure", closure);
+
+  /* 
+   * Output partial order
+   */
+
+ again:
+  for (i = 0; i < n; i++)
+    {
+      for (j = 0; j < n; j++)
+        {
+          if (closure[i][j])
+            goto item_constrained;
+        }
+      /* Item i can be output */
+      vec_add1 (result, i);
+      {
+        int k;
+        for (k = 0; k < n; k++)
+          closure [k][i] = 0;
+        /* "Magic" a before a, to keep from ever outputting it again */
+        closure [i][i] = 1;
+        goto again;
+      }
+    item_constrained:
+      ;
+    }
+
+  if (vec_len (result) != n)
+    {
+      clib_warning ("no partial order exists");
+      exit (1);
+    }
+
+  fformat (stdout, "Partial order:\n");
+
+  for (i = vec_len(result)-1; i >= 0; i--)
+    {
+      fformat (stdout, "%s\n", items[result[i]]);
+    }
+
+  vec_free (result);
+  clib_ptclosure_free (orig);
+  clib_ptclosure_free (closure);
+
+  return 0;
+}
+
+#ifdef CLIB_UNIX
+int main (int argc, char * argv[])
+{
+  unformat_input_t i;
+  int ret;
+
+  clib_mem_init (0, 3ULL<<30);
+
+  unformat_init_command_line (&i, argv);
+  ret = test_ptclosure_main (&i);
+  unformat_free (&i);
+
+  return ret;
+}
+#endif /* CLIB_UNIX */