aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Barach <dave@barachs.net>2016-06-14 18:38:02 -0400
committerKeith Burns <alagalah@gmail.com>2016-06-17 16:24:16 +0000
commitd65346098daf8967e882d0299679a131769c9be9 (patch)
tree50a9bb16a3f2c1ef8a9ef6b5e2623564a8453a5d
parent378893a485d0e0dd331cebcb2ebefdeca073b1ab (diff)
Dynamically compute ip feature subgraph order
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>
-rw-r--r--vlib/vlib/buffer.h14
-rw-r--r--vnet/Makefile.am2
-rw-r--r--vnet/vnet/buffer.h3
-rw-r--r--vnet/vnet/classify/input_acl.c4
-rw-r--r--vnet/vnet/ip/ip.h1
-rw-r--r--vnet/vnet/ip/ip4.h66
-rw-r--r--vnet/vnet/ip/ip4_forward.c121
-rw-r--r--vnet/vnet/ip/ip4_input.c12
-rw-r--r--vnet/vnet/ip/ip4_source_check.c12
-rw-r--r--vnet/vnet/ip/ip6.h66
-rw-r--r--vnet/vnet/ip/ip6_forward.c97
-rw-r--r--vnet/vnet/ip/ip6_input.c12
-rw-r--r--vnet/vnet/ip/ip_feature_registration.c207
-rw-r--r--vnet/vnet/ip/ip_feature_registration.h35
-rw-r--r--vnet/vnet/ip/ip_input_acl.c2
-rw-r--r--vnet/vnet/ipsec/ipsec.c4
-rw-r--r--vnet/vnet/ipsec/ipsec_input.c4
-rw-r--r--vnet/vnet/l2tp/decap.c2
-rw-r--r--vnet/vnet/l2tp/l2tp.c6
-rw-r--r--vnet/vnet/misc.c9
-rw-r--r--vpp/api/api.c34
-rw-r--r--vppinfra/Makefile.am9
-rw-r--r--vppinfra/vppinfra/ptclosure.c113
-rw-r--r--vppinfra/vppinfra/ptclosure.h32
-rw-r--r--vppinfra/vppinfra/test_ptclosure.c198
25 files changed, 914 insertions, 151 deletions
diff --git a/vlib/vlib/buffer.h b/vlib/vlib/buffer.h
index e11085b72a0..52749572509 100644
--- a/vlib/vlib/buffer.h
+++ b/vlib/vlib/buffer.h
@@ -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);
diff --git a/vnet/Makefile.am b/vnet/Makefile.am
index 61a1998a6eb..d0a06be8e9e 100644
--- a/vnet/Makefile.am
+++ b/vnet/Makefile.am
@@ -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 \
diff --git a/vnet/vnet/buffer.h b/vnet/vnet/buffer.h
index ea25ad0d286..f74be393df1 100644
--- a/vnet/vnet/buffer.h
+++ b/vnet/vnet/buffer.h
@@ -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;
diff --git a/vnet/vnet/classify/input_acl.c b/vnet/vnet/classify/input_acl.c
index 7e835a697f8..fb9a2a46b08 100644
--- a/vnet/vnet/classify/input_acl.c
+++ b/vnet/vnet/classify/input_acl.c
@@ -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];
diff --git a/vnet/vnet/ip/ip.h b/vnet/vnet/ip/ip.h
index 45062cf7b4d..c9a82930a67 100644
--- a/vnet/vnet/ip/ip.h
+++ b/vnet/vnet/ip/ip.h
@@ -42,6 +42,7 @@
#include <vppinfra/hash.h>
#include <vppinfra/heap.h> /* adjacency heap */
+#include <vppinfra/ptclosure.h>
#include <vnet/vnet.h>
diff --git a/vnet/vnet/ip/ip4.h b/vnet/vnet/ip/ip4.h
index 59ef685bc57..c01006ea590 100644
--- a/vnet/vnet/ip/ip4.h
+++ b/vnet/vnet/ip/ip4.h
@@ -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;
diff --git a/vnet/vnet/ip/ip4_forward.c b/vnet/vnet/ip/ip4_forward.c
index 94c446b1f38..1de0b40eb78 100644
--- a/vnet/vnet/ip/ip4_forward.c
+++ b/vnet/vnet/ip/ip4_forward.c
@@ -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;
}
diff --git a/vnet/vnet/ip/ip4_input.c b/vnet/vnet/ip/ip4_input.c
index 606342501a0..5b2b42ded13 100644
--- a/vnet/vnet/ip/ip4_input.c
+++ b/vnet/vnet/ip/ip4_input.c
@@ -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);
diff --git a/vnet/vnet/ip/ip4_source_check.c b/vnet/vnet/ip/ip4_source_check.c
index 11e6678ed2b..64b1e0abe25 100644
--- a/vnet/vnet/ip/ip4_source_check.c
+++ b/vnet/vnet/ip/ip4_source_check.c
@@ -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;
diff --git a/vnet/vnet/ip/ip6.h b/vnet/vnet/ip/ip6.h
index 69e69f2c9cd..3c27db0455b 100644
--- a/vnet/vnet/ip/ip6.h
+++ b/vnet/vnet/ip/ip6.h
@@ -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;
diff --git a/vnet/vnet/ip/ip6_forward.c b/vnet/vnet/ip/ip6_forward.c
index e49d242e6f3..f77b99ef31b 100644
--- a/vnet/vnet/ip/ip6_forward.c
+++ b/vnet/vnet/ip/ip6_forward.c
@@ -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;
}
diff --git a/vnet/vnet/ip/ip6_input.c b/vnet/vnet/ip/ip6_input.c
index 2042cbd7813..7b5470dc553 100644
--- a/vnet/vnet/ip/ip6_input.c
+++ b/vnet/vnet/ip/ip6_input.c
@@ -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
index 00000000000..02699c4f087
--- /dev/null
+++ b/vnet/vnet/ip/ip_feature_registration.c
@@ -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
index 00000000000..da2a0055b2f
--- /dev/null
+++ b/vnet/vnet/ip/ip_feature_registration.h
@@ -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 */
diff --git a/vnet/vnet/ip/ip_input_acl.c b/vnet/vnet/ip/ip_input_acl.c
index fcf8eeabc7b..eaf7f34eec5 100644
--- a/vnet/vnet/ip/ip_input_acl.c
+++ b/vnet/vnet/ip/ip_input_acl.c
@@ -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);
diff --git a/vnet/vnet/ipsec/ipsec.c b/vnet/vnet/ipsec/ipsec.c
index ea077d0a127..1c9d57b1a24 100644
--- a/vnet/vnet/ipsec/ipsec.c
+++ b/vnet/vnet/ipsec/ipsec.c
@@ -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;
diff --git a/vnet/vnet/ipsec/ipsec_input.c b/vnet/vnet/ipsec/ipsec_input.c
index 09acd106cae..e7011177a33 100644
--- a/vnet/vnet/ipsec/ipsec_input.c
+++ b/vnet/vnet/ipsec/ipsec_input.c
@@ -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);
diff --git a/vnet/vnet/l2tp/decap.c b/vnet/vnet/l2tp/decap.c
index 5f0d05c097f..8b9761a60c1 100644
--- a/vnet/vnet/l2tp/decap.c
+++ b/vnet/vnet/l2tp/decap.c
@@ -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]));
}
diff --git a/vnet/vnet/l2tp/l2tp.c b/vnet/vnet/l2tp/l2tp.c
index 7dfbe156c46..db4b4f5a29b 100644
--- a/vnet/vnet/l2tp/l2tp.c
+++ b/vnet/vnet/l2tp/l2tp.c
@@ -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;
diff --git a/vnet/vnet/misc.c b/vnet/vnet/misc.c
index 6effe6eae3e..9dbed8d8609 100644
--- a/vnet/vnet/misc.c
+++ b/vnet/vnet/misc.c
@@ -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
diff --git a/vpp/api/api.c b/vpp/api/api.c
index f99d9ce28e3..bb9a29636fd 100644
--- a/vpp/api/api.c
+++ b/vpp/api/api.c
@@ -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;
}
diff --git a/vppinfra/Makefile.am b/vppinfra/Makefile.am
index 1b4d627dfb6..b99f3d53716 100644
--- a/vppinfra/Makefile.am
+++ b/vppinfra/Makefile.am
@@ -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
index 00000000000..705af62c18a
--- /dev/null
+++ b/vppinfra/vppinfra/ptclosure.c
@@ -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
index 00000000000..c3b71743703
--- /dev/null
+++ b/vppinfra/vppinfra/ptclosure.h
@@ -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
index 00000000000..b5ac13f7de5
--- /dev/null
+++ b/vppinfra/vppinfra/test_ptclosure.c
@@ -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 */