From: Keith Burns (alagalah) Date: Fri, 25 Mar 2016 16:38:50 +0000 (-0700) Subject: IP6 SR multicast replicator X-Git-Tag: v16.06-rc1~67 X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=52fc44d61bcebb898dc19ab818ff60e617055694;p=vpp.git IP6 SR multicast replicator - adds ability to name tunnel - creates policy as a collection of tunnel names - map ip6 multicast address to policy and replicate packet - adds zero memcpy for invariant portion of packet Change-Id: Icd2fe6a2cf65c09906e82ed1afbb0eae8df79452 Signed-off-by: Keith Burns (alagalah) --- diff --git a/vnet/Makefile.am b/vnet/Makefile.am index d8778eb6a10..a69734d1530 100644 --- a/vnet/Makefile.am +++ b/vnet/Makefile.am @@ -527,7 +527,8 @@ nobase_include_HEADERS += \ if WITH_IPV6SR libvnet_la_SOURCES += \ - vnet/sr/sr.c + vnet/sr/sr.c \ + vnet/sr/sr_replicate.c endif nobase_include_HEADERS += \ diff --git a/vnet/vnet/api_errno.h b/vnet/vnet/api_errno.h index 04779f0bf1f..6d3dbbac7bd 100644 --- a/vnet/vnet/api_errno.h +++ b/vnet/vnet/api_errno.h @@ -72,7 +72,9 @@ _(NOT_CONNECTED, -78, "Not connected to the data plane") \ _(IF_ALREADY_EXISTS, -79, "Interface already exists") \ _(BOND_SLAVE_NOT_ALLOWED, -80, "Operation not allowed on slave of BondEthernet") \ _(VALUE_EXIST, -81, "Value already exists") \ -_(SAME_SRC_DST, -82, "Source and destination are the same") +_(SAME_SRC_DST, -82, "Source and destination are the same") \ +_(IP6_MULTICAST_ADDRESS_NOT_PRESENT, -83, "IP6 multicast address required") \ +_(SR_POLICY_NAME_NOT_PRESENT, -84, "Segement routing policy name required") typedef enum { #define _(a,b,c) VNET_API_ERROR_##a = (b), diff --git a/vnet/vnet/devices/dpdk/device.c b/vnet/vnet/devices/dpdk/device.c index 011ec75e18b..20c8b8f77cc 100644 --- a/vnet/vnet/devices/dpdk/device.c +++ b/vnet/vnet/devices/dpdk/device.c @@ -77,7 +77,7 @@ dpdk_set_mc_filter (vnet_hw_interface_t * hi, } } -static struct rte_mbuf * dpdk_replicate_packet_mb (vlib_buffer_t * b) +struct rte_mbuf * dpdk_replicate_packet_mb (vlib_buffer_t * b) { vlib_main_t * vm = vlib_get_main(); vlib_buffer_main_t * bm = vm->buffer_main; @@ -147,6 +147,74 @@ static struct rte_mbuf * dpdk_replicate_packet_mb (vlib_buffer_t * b) return first_mb; } +struct rte_mbuf * dpdk_zerocopy_replicate_packet_mb (vlib_buffer_t * b) +{ + vlib_main_t * vm = vlib_get_main(); + vlib_buffer_main_t * bm = vm->buffer_main; + struct rte_mbuf * first_mb = 0, * new_mb, * pkt_mb, ** prev_mb_next = 0; + u8 nb_segs, nb_segs_left; + unsigned socket_id = rte_socket_id(); + + ASSERT (bm->pktmbuf_pools[socket_id]); + pkt_mb = rte_mbuf_from_vlib_buffer(b); + nb_segs = pkt_mb->nb_segs; + for (nb_segs_left = nb_segs; nb_segs_left; nb_segs_left--) + { + if (PREDICT_FALSE(pkt_mb == 0)) + { + clib_warning ("Missing %d mbuf chain segment(s): " + "(nb_segs = %d, nb_segs_left = %d)!", + nb_segs - nb_segs_left, nb_segs, nb_segs_left); + if (first_mb) + rte_pktmbuf_free(first_mb); + return NULL; + } + new_mb = rte_pktmbuf_clone(pkt_mb, bm->pktmbuf_pools[socket_id]); + if (PREDICT_FALSE(new_mb == 0)) + { + if (first_mb) + rte_pktmbuf_free(first_mb); + return NULL; + } + + /* + * Copy packet info into 1st segment. + */ + if (first_mb == 0) + { + first_mb = new_mb; + rte_pktmbuf_pkt_len (first_mb) = pkt_mb->pkt_len; + first_mb->nb_segs = pkt_mb->nb_segs; + first_mb->port = pkt_mb->port; +#ifdef DAW_FIXME // TX Offload support TBD + first_mb->vlan_macip = pkt_mb->vlan_macip; + first_mb->hash = pkt_mb->hash; + first_mb->ol_flags = pkt_mb->ol_flags +#endif + } + else + { + ASSERT(prev_mb_next != 0); + *prev_mb_next = new_mb; + } + + /* + * Copy packet segment data into new mbuf segment. + */ + rte_pktmbuf_data_len (new_mb) = pkt_mb->data_len; + + prev_mb_next = &new_mb->next; + pkt_mb = pkt_mb->next; + } + + ASSERT(pkt_mb == 0); + __rte_mbuf_sanity_check(first_mb, 1); + + return first_mb; + + +} + static void dpdk_tx_trace_buffer (dpdk_main_t * dm, vlib_node_runtime_t * node, @@ -686,7 +754,7 @@ dpdk_interface_tx (vlib_main_t * vm, i++; } } - + n_left -= 2; } while (n_left > 0) diff --git a/vnet/vnet/devices/dpdk/dpdk.h b/vnet/vnet/devices/dpdk/dpdk.h index c6b0711d446..019d83f797a 100644 --- a/vnet/vnet/devices/dpdk/dpdk.h +++ b/vnet/vnet/devices/dpdk/dpdk.h @@ -468,6 +468,9 @@ u32 dpdk_get_handoff_node_index (void); void set_efd_bitmap (u8 *bitmap, u32 value, u32 op); +struct rte_mbuf * dpdk_replicate_packet_mb (vlib_buffer_t * b); +struct rte_mbuf * dpdk_zerocopy_replicate_packet_mb (vlib_buffer_t * b); + #define foreach_dpdk_error \ _(NONE, "no error") \ _(RX_PACKET_ERROR, "Rx packet errors") \ diff --git a/vnet/vnet/devices/virtio/vhost-user.c b/vnet/vnet/devices/virtio/vhost-user.c index f52de25416f..8486135dd69 100644 --- a/vnet/vnet/devices/virtio/vhost-user.c +++ b/vnet/vnet/devices/virtio/vhost-user.c @@ -1031,7 +1031,7 @@ static u32 vhost_user_if_input ( vlib_main_t * vm, error = VHOST_USER_INPUT_FUNC_ERROR_UNDERSIZED_FRAME; } - VLIB_BUFFER_TRACE_TRAJECTORY_INIT(b); + VLIB_BUFFER_TRACE_TRAJECTORY_INIT(b_head); vnet_buffer (b_head)->sw_if_index[VLIB_RX] = vui->sw_if_index; vnet_buffer (b_head)->sw_if_index[VLIB_TX] = (u32)~0; diff --git a/vnet/vnet/ip/lookup.h b/vnet/vnet/ip/lookup.h index e9a49124105..9c88b85c285 100644 --- a/vnet/vnet/ip/lookup.h +++ b/vnet/vnet/ip/lookup.h @@ -471,9 +471,13 @@ ip_update_adjacency (ip_lookup_main_t * lm, static inline int ip_adjacency_is_multipath(ip_lookup_main_t * lm, u32 adj_index) { + if (!vec_len(lm->multipath_adjacencies)) + return 0; + if (vec_len(lm->multipath_adjacencies) < adj_index - 1) return 0; + return (lm->multipath_adjacencies[adj_index].adj_index == adj_index && lm->multipath_adjacencies[adj_index].n_adj_in_block > 0); } diff --git a/vnet/vnet/sr/examples/sr_multicastmap.script b/vnet/vnet/sr/examples/sr_multicastmap.script new file mode 100644 index 00000000000..20bf7dc0eb7 --- /dev/null +++ b/vnet/vnet/sr/examples/sr_multicastmap.script @@ -0,0 +1,4 @@ +sr_tunnel_add_del name sr2 src ::a:1:1:0:6 dst ff15::2/128 next ::a:1:1:0:f next ::a:1:1:0:1a next ff15::1 tag ::a:1:1:0:7 clean +sr_tunnel_add_del name sr3 src ::b:1:1:0:6 dst ff16::2/128 next ::a:1:1:0:13 next ::a:1:1:0:1a next ff15::1 tag ::a:1:1:0:7 clean +sr_policy_add_del name pol1 tunnel sr2 tunnel sr3 +sr_multicast_map_add_del address ff15::1 sr-policy pol1 diff --git a/vnet/vnet/sr/sr.c b/vnet/vnet/sr/sr.c index 699df992d15..e17b474e9e7 100644 --- a/vnet/vnet/sr/sr.c +++ b/vnet/vnet/sr/sr.c @@ -23,7 +23,7 @@ ip6_sr_main_t sr_main; static vlib_node_registration_t sr_local_node; -static void sr_fix_hmac (ip6_sr_main_t * sm, ip6_header_t * ip, +void sr_fix_hmac (ip6_sr_main_t * sm, ip6_header_t * ip, ip6_sr_header_t * sr) { u32 key_index; @@ -204,7 +204,8 @@ u8 * format_ip6_sr_header_with_length (u8 * s, va_list * args) #define foreach_sr_rewrite_next \ _(ERROR, "error-drop") \ _(IP6_LOOKUP, "ip6-lookup") \ -_(SR_LOCAL, "sr-local") +_(SR_LOCAL, "sr-local") \ +_(SR_REPLICATE,"sr-replicate") typedef enum { #define _(s,n) SR_REWRITE_NEXT_##s, @@ -289,7 +290,8 @@ sr_rewrite (vlib_main_t * vm, vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - while (n_left_from >= 4 && n_left_to_next >= 2) + /* Note 2x loop disabled */ + while (0 && n_left_from >= 4 && n_left_to_next >= 2) { u32 bi0, bi1; vlib_buffer_t * b0, * b1; @@ -301,7 +303,8 @@ sr_rewrite (vlib_main_t * vm, u64 * copy_src1, * copy_dst1; u32 next0 = SR_REWRITE_NEXT_IP6_LOOKUP; u32 next1 = SR_REWRITE_NEXT_IP6_LOOKUP; - u16 new_l0, new_l1; + u16 new_l0 = 0; + u16 new_l1 = 0; /* Prefetch next iteration. */ { @@ -490,13 +493,13 @@ sr_rewrite (vlib_main_t * vm, { u32 bi0; vlib_buffer_t * b0; - ip6_header_t * ip0; + ip6_header_t * ip0 = 0; ip_adjacency_t * adj0; - ip6_sr_header_t * sr0; + ip6_sr_header_t * sr0 = 0; ip6_sr_tunnel_t * t0; u64 * copy_src0, * copy_dst0; u32 next0 = SR_REWRITE_NEXT_IP6_LOOKUP; - u16 new_l0; + u16 new_l0 = 0; bi0 = from[0]; to_next[0] = bi0; @@ -516,6 +519,14 @@ sr_rewrite (vlib_main_t * vm, t0 = pool_elt_at_index (sm->tunnels, adj0->rewrite_header.sw_if_index); + /* add a replication node */ + if(PREDICT_FALSE(t0->policy_index != ~0)) + { + vnet_buffer(b0)->ip.save_protocol = t0->policy_index; + next0=SR_REWRITE_NEXT_SR_REPLICATE; + goto trace0; + } + ASSERT (VLIB_BUFFER_PRE_DATA_SIZE >= ((word) vec_len (t0->rewrite)) + b0->current_data); @@ -582,15 +593,19 @@ sr_rewrite (vlib_main_t * vm, } } + trace0: if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) { sr_rewrite_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); tr->tunnel_index = t0 - sm->tunnels; - clib_memcpy (tr->src.as_u8, ip0->src_address.as_u8, + if (ip0) + { + memcpy (tr->src.as_u8, ip0->src_address.as_u8, sizeof (tr->src.as_u8)); - clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8, + memcpy (tr->dst.as_u8, ip0->dst_address.as_u8, sizeof (tr->dst.as_u8)); + } tr->length = new_l0; tr->next_index = next0; clib_memcpy (tr->sr, sr0, sizeof (tr->sr)); @@ -715,6 +730,7 @@ find_or_add_shared_secret (ip6_sr_main_t * sm, u8 * secret, u32 * indexp) return (key); } + int ip6_sr_add_del_tunnel (ip6_sr_add_del_tunnel_args_t * a) { ip6_main_t * im = &ip6_main; @@ -733,6 +749,8 @@ int ip6_sr_add_del_tunnel (ip6_sr_add_del_tunnel_args_t * a) ip6_add_del_route_args_t aa; u32 hmac_key_index_u32; u8 hmac_key_index = 0; + ip6_sr_policy_t * pt; + int i; /* Make sure that the rx FIB exists */ p = hash_get (im->fib_index_by_table_id, a->rx_table_id); @@ -755,7 +773,11 @@ int ip6_sr_add_del_tunnel (ip6_sr_add_del_tunnel_args_t * a) clib_memcpy (key.src.as_u8, a->src_address->as_u8, sizeof (key.src)); clib_memcpy (key.dst.as_u8, a->dst_address->as_u8, sizeof (key.dst)); - p = hash_get_mem (sm->tunnel_index_by_key, &key); + /* If the name exists, find the tunnel by name else... */ + if (a->name) + p = hash_get_mem(sm->tunnel_index_by_name, a->name); + else if (p==0) + p = hash_get_mem (sm->tunnel_index_by_key, &key); if (p) { @@ -769,6 +791,36 @@ int ip6_sr_add_del_tunnel (ip6_sr_add_del_tunnel_args_t * a) ip6_delete_route_no_next_hop (&t->key.dst, t->dst_mask_width, a->rx_table_id); vec_free (t->rewrite); + /* Remove tunnel from any policy if associated */ + if (t->policy_index != ~0) + { + pt=pool_elt_at_index (sm->policies, t->policy_index); + for (i=0; i< vec_len (pt->tunnel_indices); i++) + { + if (pt->tunnel_indices[i] == t - sm->tunnels) + { + vec_delete (pt->tunnel_indices, 1, i); + goto found; + } + } + clib_warning ("Tunnel index %d not found in policy_index %d", + t - sm->tunnels, pt - sm->policies); + found: + /* If this is last tunnel in the policy, clean up the policy too */ + if (vec_len (pt->tunnel_indices) == 0) + { + hash_unset_mem (sm->policy_index_by_policy_name, pt->name); + vec_free (pt->name); + pool_put (sm->policies, pt); + } + } + + /* Clean up the tunnel by name */ + if (t->name) + { + hash_unset_mem (sm->tunnel_index_by_name, t->name); + vec_free (t->name); + } pool_put (sm->tunnels, t); hp = hash_get_pair (sm->tunnel_index_by_key, &key); key_copy = (void *)(hp->key); @@ -789,6 +841,7 @@ int ip6_sr_add_del_tunnel (ip6_sr_add_del_tunnel_args_t * a) /* create a new tunnel */ pool_get (sm->tunnels, t); memset (t, 0, sizeof (*t)); + t->policy_index = ~0; clib_memcpy (&t->key, &key, sizeof (t->key)); t->dst_mask_width = a->dst_mask_width; @@ -902,6 +955,31 @@ int ip6_sr_add_del_tunnel (ip6_sr_add_del_tunnel_args_t * a) ip6_add_del_route (im, &aa); vec_free (add_adj); + if (a->policy_name) + { + p=hash_get_mem (sm->policy_index_by_policy_name, a->policy_name); + if (p) + { + pt = pool_elt_at_index (sm->policies, p[0]); + } + else /* no policy, lets create one */ + { + pool_get (sm->policies, pt); + memset (pt, 0, sizeof(*pt)); + pt->name = format (0, "%s%c", a->policy_name, 0); + hash_set_mem (sm->policy_index_by_policy_name, pt->name, pt - sm->policies); + p=hash_get_mem (sm->policy_index_by_policy_name, a->policy_name); + } + vec_add1 (pt->tunnel_indices, t - sm->tunnels); + t->policy_index = p[0]; /* equiv. to (pt - sm->policies) */ + } + + if (a->name) + { + t->name = format (0, "%s%c", a->name, 0); + hash_set_mem(sm->tunnel_index_by_name, t->name, t - sm->tunnels); + } + return 0; } @@ -918,6 +996,8 @@ sr_add_del_tunnel_command_fn (vlib_main_t * vm, int dst_address_set = 0; u16 flags = 0; u8 *shared_secret = 0; + u8 *name = 0; + u8 *policy_name = 0; u32 rx_table_id = 0; u32 tx_table_id = 0; ip6_address_t * segments = 0; @@ -939,6 +1019,10 @@ sr_add_del_tunnel_command_fn (vlib_main_t * vm, ; else if (unformat (input, "src %U", unformat_ip6_address, &src_address)) src_address_set = 1; + else if (unformat (input, "name %s", &name)) + ; + else if (unformat (input, "policy %s", &policy_name)) + ; else if (unformat (input, "dst %U/%d", unformat_ip6_address, &dst_address, &dst_mask_width)) @@ -1013,6 +1097,16 @@ sr_add_del_tunnel_command_fn (vlib_main_t * vm, a->tx_table_id = tx_table_id; a->shared_secret = shared_secret; + if (vec_len(name)) + a->name = name; + else + a->name = 0; + + if (vec_len(policy_name)) + a->policy_name = policy_name; + else + a->policy_name = 0; + rv = ip6_sr_add_del_tunnel (a); vec_free (segments); @@ -1051,10 +1145,46 @@ sr_add_del_tunnel_command_fn (vlib_main_t * vm, VLIB_CLI_COMMAND (sr_tunnel_command, static) = { .path = "sr tunnel", .short_help = - "sr tunnel [del] [next ] [cleanup] [reroute] [key %s]", + "sr tunnel [del] [name ] src dst [next ] [cleanup] [reroute] [key %s] [policy ", .function = sr_add_del_tunnel_command_fn, }; +void +ip6_sr_tunnel_display (vlib_main_t * vm, + ip6_sr_tunnel_t * t) +{ + ip6_main_t * im = &ip6_main; + ip6_sr_main_t * sm = &sr_main; + ip6_fib_t * rx_fib, * tx_fib; + ip6_sr_policy_t * pt; + + rx_fib = find_ip6_fib_by_table_index_or_id (im, t->rx_fib_index, + IP6_ROUTE_FLAG_FIB_INDEX); + + tx_fib = find_ip6_fib_by_table_index_or_id (im, t->tx_fib_index, + IP6_ROUTE_FLAG_FIB_INDEX); + + if (t->name) + vlib_cli_output (vm,"sr tunnel name: %s", (char *)t->name); + + vlib_cli_output (vm, "src %U dst %U first hop %U", + format_ip6_address, &t->key.src, + format_ip6_address, &t->key.dst, + format_ip6_address, &t->first_hop); + vlib_cli_output (vm, " rx-fib-id %d tx-fib-id %d", + rx_fib->table_id, tx_fib->table_id); + vlib_cli_output (vm, " sr: %U", format_ip6_sr_header, t->rewrite, + 0 /* print_hmac */); + + if (t->policy_index != ~0) + { + pt=pool_elt_at_index(sm->policies, t->policy_index); + vlib_cli_output (vm,"sr policy: %s", (char *)pt->name); + } + vlib_cli_output (vm, "-------"); + + return; +} static clib_error_t * show_sr_tunnel_fn (vlib_main_t * vm, @@ -1064,39 +1194,41 @@ show_sr_tunnel_fn (vlib_main_t * vm, static ip6_sr_tunnel_t ** tunnels; ip6_sr_tunnel_t * t; ip6_sr_main_t * sm = &sr_main; - ip6_main_t * im = &ip6_main; - ip6_fib_t * rx_fib, * tx_fib; int i; + uword * p = 0; + u8 *name = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "name %s", &name)) + { + p=hash_get_mem (sm->tunnel_index_by_name, name); + if(!p) + vlib_cli_output (vm, "No SR tunnel with name: %s. Showing all.", name); + } + else + break; + } vec_reset_length (tunnels); + if(!p) /* Either name parm not passed or no tunnel with that name found, show all */ + { pool_foreach (t, sm->tunnels, ({ vec_add1 (tunnels, t); })); + } + else /* Just show the one tunnel by name */ + vec_add1 (tunnels, &sm->tunnels[p[0]]); if (vec_len (tunnels) == 0) vlib_cli_output (vm, "No SR tunnels configured"); for (i = 0; i < vec_len (tunnels); i++) { - t = tunnels [i]; - - rx_fib = find_ip6_fib_by_table_index_or_id (im, t->rx_fib_index, - IP6_ROUTE_FLAG_FIB_INDEX); - - tx_fib = find_ip6_fib_by_table_index_or_id (im, t->tx_fib_index, - IP6_ROUTE_FLAG_FIB_INDEX); - - vlib_cli_output (vm, "src %U dst %U first hop %U", - format_ip6_address, &t->key.src, - format_ip6_address, &t->key.dst, - format_ip6_address, &t->first_hop); - vlib_cli_output (vm, " rx-fib-id %d tx-fib-id %d", - rx_fib->table_id, tx_fib->table_id); - vlib_cli_output (vm, " sr: %U", format_ip6_sr_header, t->rewrite, - 0 /* print_hmac */); - vlib_cli_output (vm, "-------"); + t = tunnels[i]; + ip6_sr_tunnel_display (vm, t); } return 0; @@ -1104,10 +1236,434 @@ show_sr_tunnel_fn (vlib_main_t * vm, VLIB_CLI_COMMAND (show_sr_tunnel_command, static) = { .path = "show sr tunnel", - .short_help = "show sr tunnel", + .short_help = "show sr tunnel [name ]", .function = show_sr_tunnel_fn, }; +int ip6_sr_add_del_policy (ip6_sr_add_del_policy_args_t * a) +{ + ip6_sr_main_t * sm = &sr_main; + uword * p; + ip6_sr_tunnel_t * t = 0; + ip6_sr_policy_t * policy; + u32 * tunnel_indices = 0; + int i; + + + + if (a->is_del) + { + p=hash_get_mem (sm->policy_index_by_policy_name, a->name); + if (!p) + return -6; /* policy name not found */ + + policy = pool_elt_at_index(sm->policies, p[0]); + + vec_foreach_index (i, policy->tunnel_indices) + { + t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[i]); + t->policy_index = ~0; + } + hash_unset_mem (sm->policy_index_by_policy_name, a->name); + pool_put (sm->policies, policy); + return 0; + } + + + if (!vec_len(a->tunnel_names)) + return -3; /*tunnel name is required case */ + + vec_reset_length (tunnel_indices); + /* Check tunnel names, add tunnel_index to policy */ + for (i=0; i < vec_len (a->tunnel_names); i++) + { + p = hash_get_mem (sm->tunnel_index_by_name, a->tunnel_names[i]); + if (!p) + return -4; /* tunnel name not found case */ + + t = pool_elt_at_index (sm->tunnels, p[0]); + /* + No need to check t==0. -3 condition above ensures name + */ + if (t->policy_index != ~0) + return -5; /* tunnel name already associated with a policy */ + + /* Add to tunnel indicies */ + vec_add1 (tunnel_indices, p[0]); + } + + /* Add policy to ip6_sr_main_t */ + pool_get (sm->policies, policy); + policy->name = a->name; + policy->tunnel_indices = tunnel_indices; + hash_set_mem (sm->policy_index_by_policy_name, policy->name, policy - sm->policies); + + /* Yes, this could be construed as overkill but the last thing you should do is set + the policy_index on the tunnel after everything is set in ip6_sr_main_t. + If this is deemed overly cautious, could set this in the vec_len(tunnel_names) loop. + */ + for (i=0; i < vec_len(policy->tunnel_indices); i++) + { + t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[i]); + t->policy_index = policy - sm->policies; + } + + return 0; +} + + +static clib_error_t * +sr_add_del_policy_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + int is_del = 0; + u8 ** tunnel_names = 0; + u8 * tunnel_name = 0; + u8 * name = 0; + ip6_sr_add_del_policy_args_t _a, *a=&_a; + int rv; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "del")) + is_del = 1; + else if (unformat (input, "name %s", &name)) + ; + else if (unformat (input, "tunnel %s", &tunnel_name)) + { + if (tunnel_name) + { + vec_add1 (tunnel_names, tunnel_name); + tunnel_name = 0; + } + } + else + break; + } + + if (!name) + return clib_error_return (0, "name of SR policy required"); + + + memset(a, 0, sizeof(*a)); + + a->is_del = is_del; + a->name = name; + a->tunnel_names = tunnel_names; + + rv = ip6_sr_add_del_policy (a); + + vec_free(tunnel_names); + + switch (rv) + { + case 0: + break; + + case -3: + return clib_error_return (0, "tunnel name to associate to SR policy is required"); + + case -4: + return clib_error_return (0, "tunnel name not found"); + + case -5: + return clib_error_return (0, "tunnel already associated with policy"); + + case -6: + return clib_error_return (0, "policy name %s not found", name); + + case -7: + return clib_error_return (0, "TODO: deleting policy name %s", name); + + default: + return clib_error_return (0, "BUG: ip6_sr_add_del_policy returns %d", rv); + + } + return 0; +} + +VLIB_CLI_COMMAND (sr_policy_command, static) = { + .path = "sr policy", + .short_help = + "sr policy [del] name tunnel [tunnel ]*", + .function = sr_add_del_policy_command_fn, +}; + +static clib_error_t * +show_sr_policy_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + static ip6_sr_policy_t ** policies; + ip6_sr_policy_t * policy; + ip6_sr_tunnel_t * t; + ip6_sr_main_t * sm = &sr_main; + int i, j; + uword * p = 0; + u8 * name = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "name %s", &name)) + { + p=hash_get_mem (sm->policy_index_by_policy_name, name); + if(!p) + vlib_cli_output (vm, "policy with name %s not found. Showing all.", name); + } + else + break; + } + + vec_reset_length (policies); + + if(!p) /* Either name parm not passed or no policy with that name found, show all */ + { + pool_foreach (policy, sm->policies, + ({ + vec_add1 (policies, policy); + })); + } + else /* Just show the one policy by name and a summary of tunnel names */ + { + policy = pool_elt_at_index(sm->policies, p[0]); + vec_add1 (policies, policy); + } + + if (vec_len (policies) == 0) + vlib_cli_output (vm, "No SR policies configured"); + + for (i = 0; i < vec_len (policies); i++) + { + policy = policies [i]; + + if(policy->name) + vlib_cli_output (vm,"SR policy name: %s", (char *)policy->name); + for(j = 0; j < vec_len (policy->tunnel_indices); j++) + { + t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[j]); + ip6_sr_tunnel_display (vm, t); + } + } + + return 0; + +} + +VLIB_CLI_COMMAND (show_sr_policy_command, static) = { + .path = "show sr policy", + .short_help = "show sr policy [name ]", + .function = show_sr_policy_fn, +}; + +int ip6_sr_add_del_multicastmap (ip6_sr_add_del_multicastmap_args_t * a) +{ + uword * p; + ip6_main_t * im = &ip6_main; + ip_lookup_main_t * lm = &im->lookup_main; + ip6_sr_tunnel_t * t; + ip_adjacency_t adj, * ap, * add_adj = 0; + u32 adj_index; + ip6_sr_main_t * sm = &sr_main; + ip6_add_del_route_args_t aa; + ip6_sr_policy_t * pt; + + if (a->is_del) + { + /* clean up the adjacency */ + p = hash_get_mem (sm->policy_index_by_multicast_address, a->multicast_address); + } + else + { + /* Get our policy by policy_name */ + p = hash_get_mem (sm->policy_index_by_policy_name, a->policy_name); + + } + if (!p) + return -1; + + pt=pool_elt_at_index (sm->policies, p[0]); + + /* + Get the first tunnel associated with policy populate the fib adjacency. + From there, since this tunnel will have it's policy_index != ~0 it will + be the trigger in the dual_loop to pull up the policy and make a copy-rewrite + for each tunnel in the policy + */ + + t = pool_elt_at_index (sm->tunnels, pt->tunnel_indices[0]); + + /* Construct a FIB entry for multicast using the rx/tx fib from the first tunnel */ + memset(&adj, 0, sizeof (adj)); + + /* Create an adjacency and add to v6 fib */ + adj.lookup_next_index = sm->ip6_lookup_sr_replicate_index; + adj.explicit_fib_index = ~0; + + ap = ip_add_adjacency (lm, &adj, 1 /* one adj */, + &adj_index); + + /* + * Stick the tunnel index into the rewrite header. + * + * Unfortunately, inserting an SR header according to the various + * RFC's requires parsing through the ip6 header, perhaps consing a + * buffer onto the head of the vlib_buffer_t, etc. We don't use the + * normal reverse bcopy rewrite code. + * + * We don't handle ugly RFC-related cases yet, but I'm sure PL will complain + * at some point... + */ + ap->rewrite_header.sw_if_index = t - sm->tunnels; + + vec_add1 (add_adj, ap[0]); + + memcpy (aa.dst_address.as_u8, a->multicast_address, sizeof (aa.dst_address.as_u8)); + aa.dst_address_length = 128; + + aa.flags = (a->is_del ? IP6_ROUTE_FLAG_DEL : IP6_ROUTE_FLAG_ADD); + aa.flags |= IP6_ROUTE_FLAG_FIB_INDEX; + aa.table_index_or_table_id = t->rx_fib_index; + aa.add_adj = add_adj; + aa.adj_index = adj_index; + aa.n_add_adj = 1; + ip6_add_del_route (im, &aa); + vec_free (add_adj); + + u8 * mcast_copy = 0; + mcast_copy = vec_new (ip6_address_t, 1); + memcpy (mcast_copy, a->multicast_address, sizeof (ip6_address_t)); + + if (a->is_del) + { + hash_unset_mem (sm->policy_index_by_multicast_address, mcast_copy); + vec_free (mcast_copy); + return 0; + } + /* else */ + + hash_set_mem (sm->policy_index_by_multicast_address, mcast_copy, pt - sm->policies); + + + return 0; +} + +static clib_error_t * +sr_add_del_multicast_map_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + int is_del = 0; + ip6_address_t multicast_address; + u8 * policy_name = 0; + int multicast_address_set = 0; + ip6_sr_add_del_multicastmap_args_t _a, *a=&_a; + int rv; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "del")) + is_del = 1; + else if (unformat (input, "address %U", unformat_ip6_address, &multicast_address)) + multicast_address_set = 1; + else if (unformat (input, "sr-policy %s", &policy_name)) + ; + else + break; + } + + if (!is_del && !policy_name) + return clib_error_return (0, "name of sr policy required"); + + if (!multicast_address_set) + return clib_error_return (0, "multicast address required"); + + memset(a, 0, sizeof(*a)); + + a->is_del = is_del; + a->multicast_address = &multicast_address; + a->policy_name = policy_name; + + rv = ip6_sr_add_del_multicastmap (a); + + switch (rv) + { + case 0: + break; + case -1: + return clib_error_return (0, "no policy with name: %s", policy_name); + + case -2: + return clib_error_return (0, "multicast map someting "); + + case -3: + return clib_error_return (0, "tunnel name to associate to SR policy is required"); + + case -7: + return clib_error_return (0, "TODO: deleting policy name %s", policy_name); + + default: + return clib_error_return (0, "BUG: ip6_sr_add_del_policy returns %d", rv); + + } + return 0; + +} + + +VLIB_CLI_COMMAND (sr_multicast_map_command, static) = { + .path = "sr multicast-map", + .short_help = + "sr multicast-map address sr-policy [del]", + .function = sr_add_del_multicast_map_command_fn, +}; + +static clib_error_t * +show_sr_multicast_map_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + ip6_sr_main_t * sm = &sr_main; + u8 * key = 0; + u32 value; + ip6_address_t multicast_address; + ip6_sr_policy_t * pt ; + + /* pull all entries from the hash table into vector for display */ + + hash_foreach_mem (key, value, sm->policy_index_by_multicast_address, + ({ + if (!key) + vlib_cli_output (vm, "no multicast maps configured"); + else + { + multicast_address = *((ip6_address_t *)key); + pt = pool_elt_at_index (sm->policies, value); + if (pt) + { + vlib_cli_output (vm, "address: %U policy: %s", + format_ip6_address, &multicast_address, + pt->name); + } + else + vlib_cli_output (vm, "BUG: policy not found for address: %U with policy index %d", + format_ip6_address, &multicast_address, + value); + + } + + })); + + return 0; +} + +VLIB_CLI_COMMAND (show_sr_multicast_map_command, static) = { + .path = "show sr multicast-map", + .short_help = "show sr multicast-map", + .function = show_sr_multicast_map_fn, +}; + + #define foreach_sr_fix_dst_addr_next \ _(DROP, "error-drop") @@ -1363,6 +1919,15 @@ static clib_error_t * sr_init (vlib_main_t * vm) sm->tunnel_index_by_key = hash_create_mem (0, sizeof (ip6_sr_tunnel_key_t), sizeof (uword)); + sm->tunnel_index_by_name = + hash_create_string (0, sizeof (uword)); + + sm->policy_index_by_policy_name = + hash_create_string(0, sizeof (uword)); + + sm->policy_index_by_multicast_address = + hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword)); + sm->hmac_key_by_shared_secret = hash_create_string (0, sizeof(uword)); ip6_register_protocol (43, sr_local_node.index); @@ -1381,6 +1946,10 @@ static clib_error_t * sr_init (vlib_main_t * vm) sm->ip6_lookup_sr_next_index = vlib_node_add_next (vm, ip6_lookup_node->index, sr_rewrite_node.index); + /* Add a disposition to sr_replicate for the sr multicast replicate node */ + sm->ip6_lookup_sr_replicate_index = + vlib_node_add_next (vm, ip6_lookup_node->index, sr_replicate_node.index); + /* Add a disposition to ip6_rewrite for the sr dst address hack node */ sm->ip6_rewrite_sr_next_index = vlib_node_add_next (vm, ip6_rewrite_node->index, @@ -1824,7 +2393,7 @@ sr_local (vlib_main_t * vm, { u32 bi0; vlib_buffer_t * b0; - ip6_header_t * ip0; + ip6_header_t * ip0 = 0; ip6_sr_header_t * sr0; ip6_address_t * new_dst0; u32 next0 = SR_LOCAL_NEXT_IP6_LOOKUP; diff --git a/vnet/vnet/sr/sr.h b/vnet/vnet/sr/sr.h index 3c6d981d17e..023ca243d7d 100644 --- a/vnet/vnet/sr/sr.h +++ b/vnet/vnet/sr/sr.h @@ -37,6 +37,9 @@ typedef struct { /* src, dst address */ ip6_sr_tunnel_key_t key; + /* optional tunnel name */ + u8 * name; + /* mask width for FIB entry */ u32 dst_mask_width; @@ -49,6 +52,10 @@ typedef struct { /* The actual ip6 sr header */ u8 * rewrite; + + /* Indicates that this tunnel is part of a policy comprising + of multiple tunnels. */ + u32 policy_index; } ip6_sr_tunnel_t; typedef struct { @@ -63,6 +70,12 @@ typedef struct { u32 rx_table_id; u32 tx_table_id; + /* optional name argument - for referencing SR tunnel/policy by name */ + u8 * name; + + /* optional policy name */ + u8 * policy_name; + /* segment list, when inserting an ip6 SR header*/ ip6_address_t *segments; @@ -82,13 +95,58 @@ typedef struct { u8 is_del; } ip6_sr_add_del_tunnel_args_t; +typedef struct { + /* policy name */ + u8 * name; + + /* tunnel names */ + u8 ** tunnel_names; + + /* Delete the policy? */ + u8 is_del; +} ip6_sr_add_del_policy_args_t; + + +typedef struct { + /* name of policy */ + u8 * name; + + /* vector to SR tunnel index */ + u32 * tunnel_indices; + +} ip6_sr_policy_t; + +typedef struct { + /* multicast IP6 address */ + ip6_address_t *multicast_address; + + /* name of policy to map to */ + u8 * policy_name; + + /* Delete the mapping */ + u8 is_del; + +} ip6_sr_add_del_multicastmap_args_t; + typedef struct { /* pool of tunnel instances, sr entry only */ ip6_sr_tunnel_t *tunnels; /* find an sr "tunnel" by its outer-IP src/dst */ uword * tunnel_index_by_key; - + + /* find an sr "tunnel" by its name */ + uword * tunnel_index_by_name; + + /* policy pool */ + ip6_sr_policy_t * policies; + + /* find a policy by name */ + uword * policy_index_by_policy_name; + + /* multicast address to policy mapping */ + uword * policy_index_by_multicast_address; + /* ip6-lookup next index for imposition FIB entries */ u32 ip6_lookup_sr_next_index; @@ -98,6 +156,9 @@ typedef struct { /* ip6-rewrite next index for reinstalling the original dst address */ u32 ip6_rewrite_sr_next_index; + /* ip6-replicate next index for multicast tunnel */ + u32 ip6_lookup_sr_replicate_index; + /* application API callback */ void *sr_local_cb; @@ -126,7 +187,15 @@ format_function_t format_ip6_sr_header_with_length; vlib_node_registration_t ip6_sr_input_node; +vlib_node_registration_t sr_replicate_node; + int ip6_sr_add_del_tunnel (ip6_sr_add_del_tunnel_args_t * a); +int ip6_sr_add_del_policy (ip6_sr_add_del_policy_args_t * a); +int ip6_sr_add_del_multicastmap (ip6_sr_add_del_multicastmap_args_t * a); + void vnet_register_sr_app_callback (void *cb); +void sr_fix_hmac (ip6_sr_main_t * sm, ip6_header_t * ip, + ip6_sr_header_t * sr); + #endif /* included_vnet_sr_h */ diff --git a/vnet/vnet/sr/sr_replicate.c b/vnet/vnet/sr/sr_replicate.c new file mode 100644 index 00000000000..e60b95624e1 --- /dev/null +++ b/vnet/vnet/sr/sr_replicate.c @@ -0,0 +1,366 @@ +/* + * sr_replicate.c: ipv6 segment routing replicator for multicast + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +typedef struct { + /* convenience */ + vlib_main_t * vlib_main; + vnet_main_t * vnet_main; +} sr_replicate_main_t; + +sr_replicate_main_t sr_replicate_main; + +vlib_node_registration_t sr_replicate_node; + + +typedef struct { + ip6_address_t src, dst; + u16 length; + u32 next_index; + u32 tunnel_index; + u8 sr[256]; +} sr_replicate_trace_t; + +/* packet trace format function */ +static u8 * format_sr_replicate_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + sr_replicate_trace_t * t = va_arg (*args, sr_replicate_trace_t *); + ip6_main_t * im = &ip6_main; + ip6_sr_main_t * sm = &sr_main; + ip6_sr_tunnel_t *tun = pool_elt_at_index (sm->tunnels, t->tunnel_index); + ip6_fib_t * rx_fib, * tx_fib; + + rx_fib = find_ip6_fib_by_table_index_or_id (im, tun->rx_fib_index, + IP6_ROUTE_FLAG_FIB_INDEX); + + tx_fib = find_ip6_fib_by_table_index_or_id (im, tun->tx_fib_index, + IP6_ROUTE_FLAG_FIB_INDEX); + + s = format + (s, "SR-REPLICATE: next %s ip6 src %U dst %U len %u\n" + " rx-fib-id %d tx-fib-id %d\n%U", + "ip6-lookup", + format_ip6_address, &t->src, + format_ip6_address, &t->dst, t->length, + rx_fib->table_id, tx_fib->table_id, + format_ip6_sr_header, t->sr, 0 /* print_hmac */); + return s; + +} + +vlib_node_registration_t sr_replicate_node; + +#define foreach_sr_replicate_error \ +_(REPLICATED, "sr packets replicated") \ +_(NO_BUFFERS, "error allocating buffers for replicas") \ +_(NO_REPLICAS, "no replicas were needed") \ +_(NO_BUFFER_DROPS, "sr no buffer drops") + +typedef enum { +#define _(sym,str) SR_REPLICATE_ERROR_##sym, + foreach_sr_replicate_error +#undef _ + SR_REPLICATE_N_ERROR, +} sr_replicate_error_t; + +static char * sr_replicate_error_strings[] = { +#define _(sym,string) string, + foreach_sr_replicate_error +#undef _ +}; + +typedef enum { + SR_REPLICATE_NEXT_IP6_LOOKUP, + SR_REPLICATE_N_NEXT, +} sr_replicate_next_t; + +static uword +sr_replicate_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, * from, * to_next; + sr_replicate_next_t next_index; + int pkts_replicated = 0; + ip6_sr_main_t * sm = &sr_main; + int no_buffer_drops = 0; + vlib_buffer_free_list_t * fl; + unsigned socket_id = rte_socket_id(); + vlib_buffer_main_t * bm = vm->buffer_main; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + fl = vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX); + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, + to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0, hdr_bi0; + vlib_buffer_t * b0, * orig_b0; + struct rte_mbuf * orig_mb0 = 0, * hdr_mb0 = 0, * clone0 = 0; + struct rte_mbuf ** hdr_vec = 0, ** rte_mbuf_vec = 0; + ip6_sr_policy_t * pol0 = 0; + ip6_sr_tunnel_t * t0 = 0; + ip6_sr_header_t * hdr_sr0 = 0; + ip6_header_t * ip0 = 0, * hdr_ip0 = 0; + int num_replicas = 0; + int i; + + bi0 = from[0]; + + b0 = vlib_get_buffer (vm, bi0); + orig_b0 = b0; + + pol0 = pool_elt_at_index (sm->policies, + vnet_buffer(b0)->ip.save_protocol); + + ip0 = vlib_buffer_get_current (b0); + /* Skip forward to the punch-in point */ + vlib_buffer_advance (b0, sizeof(*ip0)); + + orig_mb0 = rte_mbuf_from_vlib_buffer (b0); + + i16 delta0 = vlib_buffer_length_in_chain (vm, orig_b0) + - (i16) orig_mb0->pkt_len; + + u16 new_data_len0 = (u16)((i16) orig_mb0->data_len + delta0); + u16 new_pkt_len0 = (u16)((i16) orig_mb0->pkt_len + delta0); + + orig_mb0->data_len = new_data_len0; + orig_mb0->pkt_len = new_pkt_len0; + orig_mb0->data_off = (u16)(RTE_PKTMBUF_HEADROOM + b0->current_data); + + /* + Before entering loop determine if we can allocate: + - all the new HEADER RTE_MBUFs and assign them to a vector + - all the clones + + if successful, then iterate over vectors of resources + + */ + num_replicas = vec_len (pol0->tunnel_indices); + + if (PREDICT_FALSE(num_replicas == 0)) + { + b0->error = node->errors[SR_REPLICATE_ERROR_NO_REPLICAS]; + goto do_trace0; + } + + vec_reset_length (hdr_vec); + vec_reset_length (rte_mbuf_vec); + + for (i=0; i < num_replicas; i++) + { + hdr_mb0 = rte_pktmbuf_alloc(bm->pktmbuf_pools[socket_id]); + + if (i < (num_replicas - 1) ) + /* Not the last tunnel to process */ + clone0 = rte_pktmbuf_clone + (orig_mb0, bm->pktmbuf_pools[socket_id]); + else + /* Last tunnel to process, use original MB */ + clone0 = orig_mb0; + + + if (PREDICT_FALSE( !clone0 || !hdr_mb0 )) + { + b0->error = node->errors[SR_REPLICATE_ERROR_NO_BUFFERS]; + + vec_foreach_index (i, rte_mbuf_vec) + { + rte_pktmbuf_free(rte_mbuf_vec[i]); + } + vec_free (rte_mbuf_vec); + + vec_foreach_index (i, hdr_vec) + { + rte_pktmbuf_free(hdr_vec[i]); + } + vec_free (hdr_vec); + + goto do_trace0; + } + + vec_add1 (hdr_vec, hdr_mb0); + vec_add1 (rte_mbuf_vec, clone0); + + } + + for (i=0; i < num_replicas; i++) + { + vlib_buffer_t * hdr_b0; + + t0 = vec_elt_at_index (sm->tunnels, pol0->tunnel_indices[i]); + + /* Our replicas */ + hdr_mb0 = hdr_vec[i]; + clone0 = rte_mbuf_vec[i]; + + hdr_mb0->data_len = sizeof (*ip0) + vec_len (t0->rewrite); + hdr_mb0->pkt_len = hdr_mb0->data_len + + vlib_buffer_length_in_chain (vm, orig_b0); + + hdr_b0 = vlib_buffer_from_rte_mbuf (hdr_mb0); + + vlib_buffer_init_for_free_list (hdr_b0, fl); + + memcpy (hdr_b0->data, ip0, sizeof (*ip0)); + memcpy (hdr_b0->data + sizeof (*ip0), t0->rewrite, + vec_len (t0->rewrite)); + + hdr_b0->current_data = 0; + hdr_b0->current_length = sizeof (*ip0) + vec_len (t0->rewrite); + hdr_b0->flags = orig_b0->flags | VLIB_BUFFER_NEXT_PRESENT; + + + hdr_b0->total_length_not_including_first_buffer = + hdr_mb0->pkt_len - hdr_b0->current_length; + + hdr_ip0 = (ip6_header_t *) hdr_b0->data; + hdr_ip0->payload_length = clib_host_to_net_u16(hdr_mb0->data_len); + hdr_sr0 = (ip6_sr_header_t *) (hdr_ip0+1); + hdr_sr0->protocol = hdr_ip0->protocol; + hdr_ip0->protocol = 43; + + /* Rewrite the ip6 dst address */ + hdr_ip0->dst_address.as_u64[0] = t0->first_hop.as_u64[0]; + hdr_ip0->dst_address.as_u64[1] = t0->first_hop.as_u64[1]; + + sr_fix_hmac (sm, hdr_ip0, hdr_sr0); + + /* prepend new header to invariant piece */ + hdr_mb0->next = clone0; + hdr_b0->next_buffer = vlib_get_buffer_index (vm, vlib_buffer_from_rte_mbuf (clone0)); + + /* update header's fields */ + hdr_mb0->pkt_len = (uint16_t)(hdr_mb0->data_len + clone0->pkt_len); + hdr_mb0->nb_segs = (uint8_t)(clone0->nb_segs + 1); + + /* copy metadata from source packet*/ + hdr_mb0->port = clone0->port; + hdr_mb0->vlan_tci = clone0->vlan_tci; + hdr_mb0->vlan_tci_outer = clone0->vlan_tci_outer; + hdr_mb0->tx_offload = clone0->tx_offload; + hdr_mb0->hash = clone0->hash; + + hdr_mb0->ol_flags = clone0->ol_flags; + + __rte_mbuf_sanity_check(hdr_mb0, 1); + + hdr_bi0 = vlib_get_buffer_index (vm, hdr_b0); + + to_next[0] = hdr_bi0; + to_next += 1; + n_left_to_next -= 1; + + if (n_left_to_next == 0) + { + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + vlib_get_next_frame (vm, node, next_index, + to_next, n_left_to_next); + + } + pkts_replicated++; + } + + from += 1; + n_left_from -= 1; + + do_trace0: + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + sr_replicate_trace_t *tr = vlib_add_trace (vm, node, + b0, sizeof (*tr)); + tr->tunnel_index = t0 - sm->tunnels; + if (hdr_ip0) + { + memcpy (tr->src.as_u8, hdr_ip0->src_address.as_u8, + sizeof (tr->src.as_u8)); + memcpy (tr->dst.as_u8, hdr_ip0->dst_address.as_u8, + sizeof (tr->dst.as_u8)); + } + if (hdr_ip0->payload_length) + tr->length = clib_net_to_host_u16(hdr_ip0->payload_length); + else + tr->length = 0; + tr->next_index = next_index; + memcpy (tr->sr, hdr_sr0, sizeof (tr->sr)); + } + + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, sr_replicate_node.index, + SR_REPLICATE_ERROR_REPLICATED, pkts_replicated); + + vlib_node_increment_counter (vm, sr_replicate_node.index, + SR_REPLICATE_ERROR_NO_BUFFER_DROPS, no_buffer_drops); + + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (sr_replicate_node) = { + .function = sr_replicate_node_fn, + .name = "sr-replicate", + .vector_size = sizeof (u32), + .format_trace = format_sr_replicate_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(sr_replicate_error_strings), + .error_strings = sr_replicate_error_strings, + + .n_next_nodes = SR_REPLICATE_N_NEXT, + + .next_nodes = { + [SR_REPLICATE_NEXT_IP6_LOOKUP] = "ip6-lookup", + }, +}; + +clib_error_t *sr_replicate_init (vlib_main_t *vm) +{ + sr_replicate_main_t *msm = &sr_replicate_main; + + msm->vlib_main = vm; + msm->vnet_main = vnet_get_main(); + + return 0; +} + +VLIB_INIT_FUNCTION(sr_replicate_init); diff --git a/vpp-api-test/vat/api_format.c b/vpp-api-test/vat/api_format.c index bbb3e252c38..4a57d4cb018 100644 --- a/vpp-api-test/vat/api_format.c +++ b/vpp-api-test/vat/api_format.c @@ -2159,6 +2159,8 @@ _(sw_interface_ip6nd_ra_config_reply) \ _(set_arp_neighbor_limit_reply) \ _(l2_patch_add_del_reply) \ _(sr_tunnel_add_del_reply) \ +_(sr_policy_add_del_reply) \ +_(sr_multicast_map_add_del_reply) \ _(classify_add_del_session_reply) \ _(classify_set_interface_ip_table_reply) \ _(classify_set_interface_l2_tables_reply) \ @@ -2298,6 +2300,8 @@ _(SW_INTERFACE_IP6ND_RA_CONFIG_REPLY, \ _(SET_ARP_NEIGHBOR_LIMIT_REPLY, set_arp_neighbor_limit_reply) \ _(L2_PATCH_ADD_DEL_REPLY, l2_patch_add_del_reply) \ _(SR_TUNNEL_ADD_DEL_REPLY, sr_tunnel_add_del_reply) \ +_(SR_POLICY_ADD_DEL_REPLY, sr_policy_add_del_reply) \ +_(SR_MULTICAST_MAP_ADD_DEL_REPLY, sr_multicast_map_add_del_reply) \ _(CLASSIFY_ADD_DEL_TABLE_REPLY, classify_add_del_table_reply) \ _(CLASSIFY_ADD_DEL_SESSION_REPLY, classify_add_del_session_reply) \ _(CLASSIFY_SET_INTERFACE_IP_TABLE_REPLY, \ @@ -5537,6 +5541,7 @@ static int api_trace_profile_del (vat_main_t *vam) S; W; return 0; } + static int api_sr_tunnel_add_del (vat_main_t * vam) { unformat_input_t * i = vam->input; @@ -5557,11 +5562,17 @@ static int api_sr_tunnel_add_del (vat_main_t * vam) ip6_address_t * tags = 0; ip6_address_t * this_tag; ip6_address_t next_address, tag; + u8 * name = 0; + u8 * policy_name = 0; while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { if (unformat (i, "del")) is_del = 1; + else if (unformat (i, "name %s", &name)) + ; + else if (unformat (i, "policy %s", &policy_name)) + ; else if (unformat (i, "rx_fib_id %d", &rx_table_id)) ; else if (unformat (i, "tx_fib_id %d", &tx_table_id)) @@ -5650,6 +5661,8 @@ static int api_sr_tunnel_add_del (vat_main_t * vam) mp->outer_vrf_id = ntohl (rx_table_id); mp->inner_vrf_id = ntohl (tx_table_id); + memcpy (mp->name, name, vec_len(name)); + memcpy (mp->policy_name, policy_name, vec_len(policy_name)); vec_free (segments); vec_free (tags); @@ -5658,6 +5671,136 @@ static int api_sr_tunnel_add_del (vat_main_t * vam) /* NOTREACHED */ } +static int api_sr_policy_add_del (vat_main_t * vam) +{ + unformat_input_t * input = vam->input; + vl_api_sr_policy_add_del_t *mp; + f64 timeout; + int is_del = 0; + u8 * name = 0; + u8 * tunnel_name = 0; + u8 ** tunnel_names = 0; + + int name_set = 0 ; + int tunnel_set = 0; + int j = 0; + int tunnel_names_length = 1; // Init to 1 to offset the #tunnel_names counter byte + int tun_name_len = 0; // Different naming convention used as confusing these would be "bad" (TM) + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "del")) + is_del = 1; + else if (unformat (input, "name %s", &name)) + name_set = 1; + else if (unformat (input, "tunnel %s", &tunnel_name)) + { + if (tunnel_name) + { + vec_add1 (tunnel_names, tunnel_name); + /* For serializer: + - length = #bytes to store in serial vector + - +1 = byte to store that length + */ + tunnel_names_length += (vec_len (tunnel_name) + 1); + tunnel_set = 1; + tunnel_name = 0; + } + } + else + break; + } + + if (!name_set) + { + errmsg ("policy name required\n"); + return -99; + } + + if ((!tunnel_set) && (!is_del)) + { + errmsg ("tunnel name required\n"); + return -99; + } + + M2(SR_POLICY_ADD_DEL, sr_policy_add_del, tunnel_names_length); + + + + mp->is_add = !is_del; + + memcpy (mp->name, name, vec_len(name)); + // Since mp->tunnel_names is of type u8[0] and not a u8 *, u8 ** needs to be serialized + u8 * serial_orig = 0; + vec_validate (serial_orig, tunnel_names_length); + *serial_orig = vec_len(tunnel_names); // Store the number of tunnels as length in first byte of serialized vector + serial_orig += 1; // Move along one byte to store the length of first tunnel_name + + for (j=0; j < vec_len(tunnel_names); j++) + { + tun_name_len = vec_len (tunnel_names[j]); + *serial_orig = tun_name_len; // Store length of tunnel name in first byte of Length/Value pair + serial_orig += 1; // Move along one byte to store the actual tunnel name + memcpy (serial_orig, tunnel_names[j], tun_name_len); + serial_orig += tun_name_len; // Advance past the copy + } + memcpy (mp->tunnel_names, serial_orig - tunnel_names_length, tunnel_names_length); // Regress serial_orig to head then copy fwd + + vec_free (tunnel_names); + vec_free (tunnel_name); + + S; W; + /* NOTREACHED */ +} + +static int api_sr_multicast_map_add_del (vat_main_t * vam) +{ + unformat_input_t * input = vam->input; + vl_api_sr_multicast_map_add_del_t *mp; + f64 timeout; + int is_del = 0; + ip6_address_t multicast_address; + u8 * policy_name = 0; + int multicast_address_set = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "del")) + is_del = 1; + else if (unformat (input, "address %U", unformat_ip6_address, &multicast_address)) + multicast_address_set = 1; + else if (unformat (input, "sr-policy %s", &policy_name)) + ; + else + break; + } + + if (!is_del && !policy_name) + { + errmsg ("sr-policy name required\n"); + return -99; + } + + + if (!multicast_address_set) + { + errmsg ("address required\n"); + return -99; + } + + M(SR_MULTICAST_MAP_ADD_DEL, sr_multicast_map_add_del); + + mp->is_add = !is_del; + memcpy (mp->policy_name, policy_name, vec_len(policy_name)); + clib_memcpy (mp->multicast_address, &multicast_address, sizeof (mp->multicast_address)); + + + vec_free (policy_name); + + S; W; + /* NOTREACHED */ +} + #define foreach_ip4_proto_field \ _(src_address) \ @@ -10508,8 +10651,13 @@ _(mpls_ethernet_add_del_tunnel_2, \ "inner_vrf_id outer_vrf_id next-hop \n" \ "resolve-attempts resolve-if-needed 0 | 1 [del]") \ _(sr_tunnel_add_del, \ - "src dst / (next )+\n" \ - " [tag ]* [clean] [reroute]") \ + "[name ] src dst / \n" \ + "(next )+ [tag ]* [clean] [reroute] \n" \ + "[policy ]") \ +_(sr_policy_add_del, \ + "name tunnel [tunnel ]* [del]") \ +_(sr_multicast_map_add_del, \ + "address [ip6 multicast address] sr-policy [policy name] [del]") \ _(classify_add_del_table, \ "buckets [skip ] [match ] [memory_size ]\n" \ "[del] mask \n" \ diff --git a/vpp/api/api.c b/vpp/api/api.c index eaae51a5eac..41f57602550 100644 --- a/vpp/api/api.c +++ b/vpp/api/api.c @@ -42,6 +42,7 @@ #include #include +#include // alagalah TODO : committers please pay note, is this ok? #include #include #include @@ -329,7 +330,8 @@ _(LISP_GPE_ADD_DEL_IFACE, lisp_gpe_add_del_iface) \ _(LISP_LOCATOR_SET_DUMP, lisp_locator_set_dump) \ _(LISP_LOCAL_EID_TABLE_DUMP, lisp_local_eid_table_dump) \ _(LISP_GPE_TUNNEL_DUMP, lisp_gpe_tunnel_dump) \ -_(LISP_MAP_RESOLVER_DUMP, lisp_map_resolver_dump) +_(LISP_MAP_RESOLVER_DUMP, lisp_map_resolver_dump) \ +_(SR_MULTICAST_MAP_ADD_DEL, sr_multicast_map_add_del) #define QUOTE_(x) #x #define QUOTE(x) QUOTE_(x) @@ -3389,7 +3391,15 @@ static void vl_api_sr_tunnel_add_del_t_handler a->is_del = (mp->is_add == 0); a->rx_table_id = ntohl(mp->outer_vrf_id); a->tx_table_id = ntohl(mp->inner_vrf_id); - + + a->name = format(0, "%s", mp->name); + if (!(vec_len(a->name))) + a->name = 0; + + a->policy_name = format(0, "%s", mp->policy_name); + if (!(vec_len(a->policy_name))) + a->policy_name = 0; + /* Yank segments and tags out of the API message */ this_address = (ip6_address_t *)mp->segs_and_tags; for (i = 0; i < mp->n_segments; i++) { @@ -3414,6 +3424,96 @@ out: #endif } +static void vl_api_sr_policy_add_del_t_handler +(vl_api_sr_policy_add_del_t *mp) +{ +#if IPV6SR == 0 + clib_warning ("unimplemented"); +#else + ip6_sr_add_del_policy_args_t _a, *a=&_a; + int rv = 0; + vl_api_sr_policy_add_del_reply_t * rmp; + int i; + + memset (a, 0, sizeof (*a)); + a->is_del = (mp->is_add == 0); + + a->name = format(0, "%s", mp->name); + if (!(vec_len(a->name))) + { + rv = VNET_API_ERROR_NO_SUCH_NODE2; + goto out; + } + + if (!(mp->tunnel_names)) + { + rv = VNET_API_ERROR_NO_SUCH_NODE2; + goto out; + } + + // start deserializing tunnel_names + int num_tunnels = mp->tunnel_names[0]; //number of tunnels + u8 * deser_tun_names = mp->tunnel_names; + deser_tun_names += 1; //moving along + + u8 * tun_name = 0; + int tun_name_len = 0; + + for (i=0; i < num_tunnels; i++) + { + tun_name_len= *deser_tun_names; + deser_tun_names += 1; + vec_resize (tun_name, tun_name_len); + memcpy(tun_name, deser_tun_names, tun_name_len); + vec_add1 (a->tunnel_names, tun_name); + deser_tun_names += tun_name_len; + tun_name = 0; + } + + rv = ip6_sr_add_del_policy (a); + +out: + + REPLY_MACRO(VL_API_SR_POLICY_ADD_DEL_REPLY); +#endif +} + +static void vl_api_sr_multicast_map_add_del_t_handler +(vl_api_sr_multicast_map_add_del_t *mp) +{ +#if IPV6SR == 0 + clib_warning ("unimplemented"); +#else + ip6_sr_add_del_multicastmap_args_t _a, *a=&_a; + int rv = 0; + vl_api_sr_multicast_map_add_del_reply_t * rmp; + + memset (a, 0, sizeof (*a)); + a->is_del = (mp->is_add == 0); + + a->multicast_address = (ip6_address_t *)&mp->multicast_address; + a->policy_name = format(0, "%s", mp->policy_name); + + if (a->multicast_address == 0) + { + rv = -1 ; + goto out; + } + + if (!(a->policy_name)) + { + rv = -2 ; + goto out; + } + + rv = ip6_sr_add_del_multicastmap (a); + +out: + + REPLY_MACRO(VL_API_SR_MULTICAST_MAP_ADD_DEL_REPLY); +#endif +} + #define foreach_classify_add_del_table_field \ _(table_index) \ _(nbuckets) \ @@ -5827,6 +5927,19 @@ vpe_api_hookup (vlib_main_t *vm) vl_api_sr_tunnel_add_del_t_print, 256, 1); + + /* + * Manually register the sr policy add del msg, so we trace + * enough bytes to capture a typical tunnel name list + */ + vl_msg_api_set_handlers (VL_API_SR_POLICY_ADD_DEL, + "sr_policy_add_del", + vl_api_sr_policy_add_del_t_handler, + vl_noop_handler, + vl_api_sr_policy_add_del_t_endian, + vl_api_sr_policy_add_del_t_print, + 256, 1); + /* * Trace space for 8 MPLS encap labels, classifier mask+match */ diff --git a/vpp/api/custom_dump.c b/vpp/api/custom_dump.c index cd17328edb4..6ae8c76dd82 100644 --- a/vpp/api/custom_dump.c +++ b/vpp/api/custom_dump.c @@ -1014,6 +1014,9 @@ static void *vl_api_sr_tunnel_add_del_t_print s = format (0, "SCRIPT: sr_tunnel_add_del "); + if (mp->name) + s = format (s, "name %s ", mp->name); + s = format (s, "src %U dst %U/%d ", format_ip6_address, (ip6_address_t *) mp->src_address, format_ip6_address, @@ -1062,12 +1065,78 @@ static void *vl_api_sr_tunnel_add_del_t_print } } + if (mp->policy_name) + s = format (s, "policy_name %s ", mp->policy_name); + if (mp->is_add == 0) s = format (s, "del "); FINISH; } +static void *vl_api_sr_policy_add_del_t_print +(vl_api_sr_policy_add_del_t * mp, void *handle) +{ + u8 * s; + int i; + + s = format (0, "SCRIPT: sr_policy_add_del "); + + if (mp->name) + s = format (s, "name %s ", mp->name); + + + if (mp->tunnel_names) + { + // start deserializing tunnel_names + int num_tunnels = mp->tunnel_names[0]; //number of tunnels + u8 * deser_tun_names = mp->tunnel_names; + deser_tun_names += 1; //moving along + + u8 * tun_name = 0; + int tun_name_len = 0; + + for (i=0; i < num_tunnels; i++) + { + tun_name_len= *deser_tun_names; + deser_tun_names += 1; + vec_resize (tun_name, tun_name_len); + memcpy(tun_name, deser_tun_names, tun_name_len); + s = format (s, "tunnel %s ", tun_name); + deser_tun_names += tun_name_len; + tun_name = 0; + } + } + + if (mp->is_add == 0) + s = format (s, "del "); + + FINISH; +} + +static void *vl_api_sr_multicast_map_add_del_t_print +(vl_api_sr_multicast_map_add_del_t * mp, void *handle) +{ + + u8 * s = 0; + /* int i; */ + + s = format (0, "SCRIPT: sr_multicast_map_add_del "); + + if (mp->multicast_address) + s = format (s, "address %U ", format_ip6_address, &mp->multicast_address); + + if (mp->policy_name) + s = format (s, "sr-policy %s ", &mp->policy_name); + + + if (mp->is_add == 0) + s = format (s, "del "); + + FINISH; +} + + static void *vl_api_classify_add_del_table_t_print (vl_api_classify_add_del_table_t * mp, void *handle) { @@ -1808,6 +1877,8 @@ _(SW_INTERFACE_IP6ND_RA_CONFIG, sw_interface_ip6nd_ra_config) \ _(SET_ARP_NEIGHBOR_LIMIT, set_arp_neighbor_limit) \ _(L2_PATCH_ADD_DEL, l2_patch_add_del) \ _(SR_TUNNEL_ADD_DEL, sr_tunnel_add_del) \ +_(SR_POLICY_ADD_DEL, sr_policy_add_del) \ +_(SR_MULTICAST_MAP_ADD_DEL, sr_multicast_map_add_del) \ _(SW_INTERFACE_SET_L2_XCONNECT, sw_interface_set_l2_xconnect) \ _(L2FIB_ADD_DEL, l2fib_add_del) \ _(L2_FLAGS, l2_flags) \ @@ -1816,7 +1887,7 @@ _(CLASSIFY_ADD_DEL_TABLE, classify_add_del_table) \ _(CLASSIFY_ADD_DEL_SESSION, classify_add_del_session) \ _(SW_INTERFACE_SET_L2_BRIDGE, sw_interface_set_l2_bridge) \ _(BRIDGE_DOMAIN_ADD_DEL, bridge_domain_add_del) \ -_(BRIDGE_DOMAIN_DUMP, bridge_domain_dump) \ +_(BRIDGE_DOMAIN_DUMP, bridge_domain_dump) \ _(CLASSIFY_SET_INTERFACE_IP_TABLE, classify_set_interface_ip_table) \ _(CLASSIFY_SET_INTERFACE_L2_TABLES, classify_set_interface_l2_tables) \ _(ADD_NODE_NEXT, add_node_next) \ diff --git a/vpp/api/vpe.api b/vpp/api/vpe.api index 88b6070083d..9d0f6b7f469 100644 --- a/vpp/api/vpe.api +++ b/vpp/api/vpe.api @@ -1145,6 +1145,7 @@ define l2_patch_add_del_reply { @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @param is_add - add the tunnel if non-zero, else delete it + @param name[] - tunnel name (len. 64) @param src_address[] - @param dst_address[] - @param dst_mask_width - @@ -1154,11 +1155,13 @@ define l2_patch_add_del_reply { @param n_segments - @param n_tags - @param segs_and_tags[] - + @param policy_name[] - name of policy to associate this tunnel to (len. 64) */ define sr_tunnel_add_del { u32 client_index; u32 context; u8 is_add; + u8 name[64]; u8 src_address[16]; u8 dst_address[16]; u8 dst_mask_width; @@ -1168,6 +1171,7 @@ define sr_tunnel_add_del { u8 n_segments; u8 n_tags; u8 segs_and_tags[0]; + u8 policy_name[64]; }; /** \brief IPv6 segment routing tunnel add / del response @@ -1179,6 +1183,54 @@ define sr_tunnel_add_del_reply { i32 retval; }; +/** \brief IPv6 segment routing policy add / del request + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - add the tunnel if non-zero, else delete it + @param name[] - policy name (len. 64) + @param tunnel_names[] - +*/ +define sr_policy_add_del { + u32 client_index; + u32 context; + u8 is_add; + u8 name[64]; + u8 tunnel_names[0]; +}; + +/** \brief IPv6 segment routing policy add / del response + @param context - sender context, to match reply w/ request + @param retval - return value for request +*/ +define sr_policy_add_del_reply { + u32 context; + i32 retval; +}; + +/** \brief IPv6 segment routing multicast map to policy add / del request + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - add the tunnel if non-zero, else delete it + @param multicast_address[] - IP6 multicast address + @param policy_name[] = policy name (len.64) +*/ +define sr_multicast_map_add_del { + u32 client_index; + u32 context; + u8 is_add; + u8 multicast_address[16]; + u8 policy_name[64]; +}; + +/** \brief IPv6 segment routing multicast map to policy add / del response + @param context - sender context, to match reply w/ request + @param retval - return value for request +*/ +define sr_multicast_map_add_del_reply { + u32 context; + i32 retval; +}; + /** \brief Interface set vpath request @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request