diff options
author | Dave Barach <dave@barachs.net> | 2016-06-14 18:38:02 -0400 |
---|---|---|
committer | Keith Burns <alagalah@gmail.com> | 2016-06-17 16:24:16 +0000 |
commit | d65346098daf8967e882d0299679a131769c9be9 (patch) | |
tree | 50a9bb16a3f2c1ef8a9ef6b5e2623564a8453a5d | |
parent | 378893a485d0e0dd331cebcb2ebefdeca073b1ab (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.h | 14 | ||||
-rw-r--r-- | vnet/Makefile.am | 2 | ||||
-rw-r--r-- | vnet/vnet/buffer.h | 3 | ||||
-rw-r--r-- | vnet/vnet/classify/input_acl.c | 4 | ||||
-rw-r--r-- | vnet/vnet/ip/ip.h | 1 | ||||
-rw-r--r-- | vnet/vnet/ip/ip4.h | 66 | ||||
-rw-r--r-- | vnet/vnet/ip/ip4_forward.c | 121 | ||||
-rw-r--r-- | vnet/vnet/ip/ip4_input.c | 12 | ||||
-rw-r--r-- | vnet/vnet/ip/ip4_source_check.c | 12 | ||||
-rw-r--r-- | vnet/vnet/ip/ip6.h | 66 | ||||
-rw-r--r-- | vnet/vnet/ip/ip6_forward.c | 97 | ||||
-rw-r--r-- | vnet/vnet/ip/ip6_input.c | 12 | ||||
-rw-r--r-- | vnet/vnet/ip/ip_feature_registration.c | 207 | ||||
-rw-r--r-- | vnet/vnet/ip/ip_feature_registration.h | 35 | ||||
-rw-r--r-- | vnet/vnet/ip/ip_input_acl.c | 2 | ||||
-rw-r--r-- | vnet/vnet/ipsec/ipsec.c | 4 | ||||
-rw-r--r-- | vnet/vnet/ipsec/ipsec_input.c | 4 | ||||
-rw-r--r-- | vnet/vnet/l2tp/decap.c | 2 | ||||
-rw-r--r-- | vnet/vnet/l2tp/l2tp.c | 6 | ||||
-rw-r--r-- | vnet/vnet/misc.c | 9 | ||||
-rw-r--r-- | vpp/api/api.c | 34 | ||||
-rw-r--r-- | vppinfra/Makefile.am | 9 | ||||
-rw-r--r-- | vppinfra/vppinfra/ptclosure.c | 113 | ||||
-rw-r--r-- | vppinfra/vppinfra/ptclosure.h | 32 | ||||
-rw-r--r-- | vppinfra/vppinfra/test_ptclosure.c | 198 |
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 */ |