diff options
Diffstat (limited to 'src/vnet/sr/sr.c')
-rwxr-xr-x[-rw-r--r--] | src/vnet/sr/sr.c | 3397 |
1 files changed, 10 insertions, 3387 deletions
diff --git a/src/vnet/sr/sr.c b/src/vnet/sr/sr.c index 012d45428ec..34344fcebd2 100644..100755 --- a/src/vnet/sr/sr.c +++ b/src/vnet/sr/sr.c @@ -17,9 +17,10 @@ /** * @file - * @brief Segment Routing main functions + * @brief Segment Routing initialization * */ + #include <vnet/vnet.h> #include <vnet/sr/sr.h> #include <vnet/fib/ip6_fib.h> @@ -27,1179 +28,13 @@ #include <vnet/dpo/dpo.h> #include <vnet/dpo/replicate_dpo.h> -#include <openssl/hmac.h> - ip6_sr_main_t sr_main; -static vlib_node_registration_t sr_local_node; - -/** - * @brief Dynamically added SR DPO type - */ -static dpo_type_t sr_dpo_type; - -/** - * @brief Dynamically added SR FIB Node type - */ -static fib_node_type_t sr_fib_node_type; - -/** - * @brief Use passed HMAC key in ip6_sr_header_t in OpenSSL HMAC routines - * - * @param sm ip6_sr_main_t * - * @param ip ip6_header_t * - * @param sr ip6_sr_header_t * - */ -void -sr_fix_hmac (ip6_sr_main_t * sm, ip6_header_t * ip, ip6_sr_header_t * sr) -{ - u32 key_index; - static u8 *keybuf; - u8 *copy_target; - int first_segment; - ip6_address_t *addrp; - int i; - ip6_sr_hmac_key_t *hmac_key; - u32 sig_len; - - key_index = sr->hmac_key; - - /* No signature? Pass... */ - if (key_index == 0) - return; - - /* We don't know about this key? Fail... */ - if (key_index >= vec_len (sm->hmac_keys)) - return; - - hmac_key = sm->hmac_keys + key_index; - - vec_reset_length (keybuf); - - /* pkt ip6 src address */ - vec_add2 (keybuf, copy_target, sizeof (ip6_address_t)); - clib_memcpy (copy_target, ip->src_address.as_u8, sizeof (ip6_address_t)); - - /* first segment */ - vec_add2 (keybuf, copy_target, 1); - copy_target[0] = sr->first_segment; - - /* octet w/ bit 0 = "clean" flag */ - vec_add2 (keybuf, copy_target, 1); - copy_target[0] - = (sr->flags & clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP)) - ? 0x80 : 0; - - /* hmac key id */ - vec_add2 (keybuf, copy_target, 1); - copy_target[0] = sr->hmac_key; - - first_segment = sr->first_segment; - - addrp = sr->segments; - - /* segments */ - for (i = 0; i <= first_segment; i++) - { - vec_add2 (keybuf, copy_target, sizeof (ip6_address_t)); - clib_memcpy (copy_target, addrp->as_u8, sizeof (ip6_address_t)); - addrp++; - } - - addrp++; - - HMAC_CTX_init (sm->hmac_ctx); - if (!HMAC_Init (sm->hmac_ctx, hmac_key->shared_secret, - vec_len (hmac_key->shared_secret), sm->md)) - clib_warning ("barf1"); - if (!HMAC_Update (sm->hmac_ctx, keybuf, vec_len (keybuf))) - clib_warning ("barf2"); - if (!HMAC_Final (sm->hmac_ctx, (unsigned char *) addrp, &sig_len)) - clib_warning ("barf3"); - HMAC_CTX_cleanup (sm->hmac_ctx); -} - -/** - * @brief Format function for decoding various SR flags - * - * @param s u8 * - formatted string - * @param args va_list * - u16 flags - * - * @return formatted output string u8 * - */ -u8 * -format_ip6_sr_header_flags (u8 * s, va_list * args) -{ - u16 flags = (u16) va_arg (*args, int); - u8 pl_flag; - int bswap_needed = va_arg (*args, int); - int i; - - if (bswap_needed) - flags = clib_host_to_net_u16 (flags); - - if (flags & IP6_SR_HEADER_FLAG_CLEANUP) - s = format (s, "cleanup "); - - if (flags & IP6_SR_HEADER_FLAG_PROTECTED) - s = format (s, "reroute "); - - s = format (s, "pl: "); - for (i = 1; i <= 4; i++) - { - pl_flag = ip6_sr_policy_list_flags (flags, i); - s = format (s, "[%d] ", i); - - switch (pl_flag) - { - case IP6_SR_HEADER_FLAG_PL_ELT_NOT_PRESENT: - s = format (s, "NotPr "); - break; - case IP6_SR_HEADER_FLAG_PL_ELT_INGRESS_PE: - s = format (s, "InPE "); - break; - case IP6_SR_HEADER_FLAG_PL_ELT_EGRESS_PE: - s = format (s, "EgPE "); - break; - - case IP6_SR_HEADER_FLAG_PL_ELT_ORIG_SRC_ADDR: - s = format (s, "OrgSrc "); - break; - } - } - return s; -} - -/** - * @brief Format function for decoding ip6_sr_header_t - * - * @param s u8 * - formatted string - * @param args va_list * - ip6_sr_header_t - * - * @return formatted output string u8 * - */ -u8 * -format_ip6_sr_header (u8 * s, va_list * args) -{ - ip6_sr_header_t *h = va_arg (*args, ip6_sr_header_t *); - ip6_address_t placeholder_addr = - { {254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254} - }; - int print_hmac = va_arg (*args, int); - int i, pl_index, max_segs; - int flags_host_byte_order = clib_net_to_host_u16 (h->flags); - - s = format (s, "next proto %d, len %d, type %d", - h->protocol, (h->length << 3) + 8, h->type); - s = format (s, "\n segs left %d, first_segment %d, hmac key %d", - h->segments_left, h->first_segment, h->hmac_key); - s = format (s, "\n flags %U", format_ip6_sr_header_flags, - flags_host_byte_order, 0 /* bswap needed */ ); - - /* - * Header length is in 8-byte units (minus one), so - * divide by 2 to ascertain the number of ip6 addresses in the - * segment list - */ - max_segs = (h->length >> 1); - - if (!print_hmac && h->hmac_key) - max_segs -= 2; - - s = format (s, "\n Segments (in processing order):"); - - for (i = h->first_segment; i >= 1; i--) - s = format (s, "\n %U", format_ip6_address, h->segments + i); - if (ip6_address_is_equal (&placeholder_addr, h->segments)) - s = format (s, "\n (empty placeholder)"); - else - s = format (s, "\n %U", format_ip6_address, h->segments); - - s = format (s, "\n Policy List:"); - - pl_index = 1; /* to match the RFC text */ - for (i = (h->first_segment + 1); i < max_segs; i++, pl_index++) - { - char *tag; - char *tags[] = { " ", "InPE: ", "EgPE: ", "OrgSrc: " }; - - tag = tags[0]; - if (pl_index >= 1 && pl_index <= 4) - { - int this_pl_flag = ip6_sr_policy_list_flags - (flags_host_byte_order, pl_index); - tag = tags[this_pl_flag]; - } - - s = format (s, "\n %s%U", tag, format_ip6_address, h->segments + i); - } - - return s; -} - -/** - * @brief Format function for decoding ip6_sr_header_t with length - * - * @param s u8 * - formatted string - * @param args va_list * - ip6_header_t + ip6_sr_header_t - * - * @return formatted output string u8 * - */ -u8 * -format_ip6_sr_header_with_length (u8 * s, va_list * args) -{ - ip6_header_t *h = va_arg (*args, ip6_header_t *); - u32 max_header_bytes = va_arg (*args, u32); - uword header_bytes; - - header_bytes = sizeof (h[0]) + sizeof (ip6_sr_header_t); - if (max_header_bytes != 0 && header_bytes > max_header_bytes) - return format (s, "ip6_sr header truncated"); - - s = format (s, "IP6: %U\n", format_ip6_header, h, max_header_bytes); - s = - format (s, "SR: %U\n", format_ip6_sr_header, (ip6_sr_header_t *) (h + 1), - 0 /* print_hmac */ , max_header_bytes); - return s; -} - -/** - * @brief Defined valid next nodes -*/ -#define foreach_sr_rewrite_next \ -_(ERROR, "error-drop") \ -_(SR_LOCAL, "sr-local") - -/** - * @brief Struct for defined valid next nodes -*/ -typedef enum -{ -#define _(s,n) SR_REWRITE_NEXT_##s, - foreach_sr_rewrite_next -#undef _ - SR_REWRITE_N_NEXT, -} sr_rewrite_next_t; - -/** - * @brief Struct for data for SR rewrite packet trace - */ -typedef struct -{ - ip6_address_t src, dst; - u16 length; - u32 next_index; - u32 tunnel_index; - u8 sr[256]; -} sr_rewrite_trace_t; - -/** - * @brief Error strings for SR rewrite - */ -static char *sr_rewrite_error_strings[] = { -#define sr_error(n,s) s, -#include "sr_error.def" -#undef sr_error -}; - -/** - * @brief Struct for SR rewrite error strings - */ -typedef enum -{ -#define sr_error(n,s) SR_REWRITE_ERROR_##n, -#include "sr_error.def" -#undef sr_error - SR_REWRITE_N_ERROR, -} sr_rewrite_error_t; - - -/** - * @brief Format function for SR rewrite trace. - */ -u8 * -format_sr_rewrite_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_rewrite_trace_t *t = va_arg (*args, sr_rewrite_trace_t *); - 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 = ip6_fib_get (tun->rx_fib_index); - tx_fib = ip6_fib_get (tun->tx_fib_index); - - s = format - (s, "SR-REWRITE: next %s ip6 src %U dst %U len %u\n" - " rx-fib-id %d tx-fib-id %d\n%U", - (t->next_index == SR_REWRITE_NEXT_SR_LOCAL) - ? "sr-local" : "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; -} - -/** - * @brief Main processing dual-loop for Segment Routing Rewrite - * @node sr-rewrite - * - * @param vm vlib_main_t * - * @param node vlib_node_runtime_t * - * @param from_frame vlib_frame_t * - * - * @return from_frame->n_vectors uword - */ -static uword -sr_rewrite (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * from_frame) -{ - u32 n_left_from, next_index, *from, *to_next; - ip6_sr_main_t *sm = &sr_main; - u32 (*sr_local_cb) (vlib_main_t *, vlib_node_runtime_t *, - vlib_buffer_t *, ip6_header_t *, ip6_sr_header_t *); - sr_local_cb = sm->sr_local_cb; - - from = vlib_frame_vector_args (from_frame); - n_left_from = from_frame->n_vectors; - - next_index = node->cached_next_index; - - while (n_left_from > 0) - { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - /* Note 2x loop disabled */ - while (0 && n_left_from >= 4 && n_left_to_next >= 2) - { - u32 bi0, bi1; - vlib_buffer_t *b0, *b1; - ip6_header_t *ip0, *ip1; - ip6_sr_header_t *sr0, *sr1; - ip6_sr_tunnel_t *t0, *t1; - u32 next0; - u32 next1; - u16 new_l0 = 0; - u16 new_l1 = 0; - - /* Prefetch next iteration. */ - { - vlib_buffer_t *p2, *p3; - - p2 = vlib_get_buffer (vm, from[2]); - p3 = vlib_get_buffer (vm, from[3]); - - vlib_prefetch_buffer_header (p2, LOAD); - vlib_prefetch_buffer_header (p3, LOAD); - } - - bi0 = from[0]; - bi1 = from[1]; - to_next[0] = bi0; - to_next[1] = bi1; - from += 2; - to_next += 2; - n_left_to_next -= 2; - n_left_from -= 2; - - b0 = vlib_get_buffer (vm, bi0); - b1 = vlib_get_buffer (vm, bi1); - - /* - * $$$ parse through header(s) to pick the point - * where we punch in the SR extention header - */ - t0 = - pool_elt_at_index (sm->tunnels, - vnet_buffer (b0)->ip.adj_index[VLIB_TX]); - t1 = - pool_elt_at_index (sm->tunnels, - vnet_buffer (b1)->ip.adj_index[VLIB_TX]); - - ASSERT (VLIB_BUFFER_PRE_DATA_SIZE - >= ((word) vec_len (t0->rewrite)) + b0->current_data); - ASSERT (VLIB_BUFFER_PRE_DATA_SIZE - >= ((word) vec_len (t1->rewrite)) + b1->current_data); - - vnet_buffer (b0)->sw_if_index[VLIB_TX] = t0->tx_fib_index; - vnet_buffer (b1)->sw_if_index[VLIB_TX] = t1->tx_fib_index; - - ip0 = vlib_buffer_get_current (b0); - ip1 = vlib_buffer_get_current (b1); - - /* - * SR-unaware service chaining case: pkt coming back from - * service has the original dst address, and will already - * have an SR header. If so, send it to sr-local - */ - if (PREDICT_FALSE (ip0->protocol == IPPROTO_IPV6_ROUTE)) - { - vlib_buffer_advance (b0, sizeof (ip0)); - sr0 = (ip6_sr_header_t *) (ip0 + 1); - new_l0 = clib_net_to_host_u16 (ip0->payload_length); - next0 = SR_REWRITE_NEXT_SR_LOCAL; - } - else - { - u32 len_bytes = sizeof (ip6_header_t); - u8 next_hdr = ip0->protocol; - - /* HBH must immediately follow ipv6 header */ - if (PREDICT_FALSE - (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS)) - { - ip6_hop_by_hop_ext_t *ext_hdr = - (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0); - len_bytes += - ip6_ext_header_len ((ip6_ext_header_t *) ext_hdr); - /* Ignoring the sr_local for now, if RH follows HBH here */ - next_hdr = ext_hdr->next_hdr; - ext_hdr->next_hdr = IPPROTO_IPV6_ROUTE; - } - else - { - ip0->protocol = IPPROTO_IPV6_ROUTE; /* routing extension header */ - } - /* - * Copy data before the punch-in point left by the - * required amount. Assume (for the moment) that only - * the main packet header needs to be copied. - */ - clib_memcpy (((u8 *) ip0) - vec_len (t0->rewrite), - ip0, len_bytes); - vlib_buffer_advance (b0, -(word) vec_len (t0->rewrite)); - ip0 = vlib_buffer_get_current (b0); - sr0 = (ip6_sr_header_t *) ((u8 *) ip0 + len_bytes); - /* $$$ tune */ - clib_memcpy (sr0, t0->rewrite, vec_len (t0->rewrite)); - - /* Fix the next header chain */ - sr0->protocol = next_hdr; - - new_l0 = clib_net_to_host_u16 (ip0->payload_length) + - vec_len (t0->rewrite); - ip0->payload_length = clib_host_to_net_u16 (new_l0); - - /* Copy dst address into the DA slot in the segment list */ - clib_memcpy (sr0->segments, ip0->dst_address.as_u64, - sizeof (ip6_address_t)); - /* Rewrite the ip6 dst address with the first hop */ - clib_memcpy (ip0->dst_address.as_u64, t0->first_hop.as_u64, - sizeof (ip6_address_t)); - - sr_fix_hmac (sm, ip0, sr0); - - vnet_buffer (b0)->ip.adj_index[VLIB_TX] = - t0->first_hop_dpo.dpoi_index; - next0 = t0->first_hop_dpo.dpoi_next_node; - next0 = (sr_local_cb ? - sr_local_cb (vm, node, b0, ip0, sr0) : next0); - - /* - * Ignore "do not rewrite" shtik in this path - */ - if (PREDICT_FALSE (next0 & 0x80000000)) - { - next0 ^= 0xFFFFFFFF; - if (PREDICT_FALSE (next0 == SR_REWRITE_NEXT_ERROR)) - b0->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK]; - } - } - - if (PREDICT_FALSE (ip1->protocol == IPPROTO_IPV6_ROUTE)) - { - vlib_buffer_advance (b1, sizeof (ip1)); - sr1 = (ip6_sr_header_t *) (ip1 + 1); - new_l1 = clib_net_to_host_u16 (ip1->payload_length); - next1 = SR_REWRITE_NEXT_SR_LOCAL; - } - else - { - u32 len_bytes = sizeof (ip6_header_t); - u8 next_hdr = ip1->protocol; - - /* HBH must immediately follow ipv6 header */ - if (PREDICT_FALSE - (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS)) - { - ip6_hop_by_hop_ext_t *ext_hdr = - (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1); - len_bytes += - ip6_ext_header_len ((ip6_ext_header_t *) ext_hdr); - /* Ignoring the sr_local for now, if RH follows HBH here */ - next_hdr = ext_hdr->next_hdr; - ext_hdr->next_hdr = IPPROTO_IPV6_ROUTE; - } - else - { - ip1->protocol = IPPROTO_IPV6_ROUTE; - } - /* - * Copy data before the punch-in point left by the - * required amount. Assume (for the moment) that only - * the main packet header needs to be copied. - */ - clib_memcpy (((u8 *) ip1) - vec_len (t1->rewrite), - ip1, len_bytes); - vlib_buffer_advance (b1, -(word) vec_len (t1->rewrite)); - ip1 = vlib_buffer_get_current (b1); - sr1 = (ip6_sr_header_t *) ((u8 *) ip1 + len_bytes); - clib_memcpy (sr1, t1->rewrite, vec_len (t1->rewrite)); - - sr1->protocol = next_hdr; - new_l1 = clib_net_to_host_u16 (ip1->payload_length) + - vec_len (t1->rewrite); - ip1->payload_length = clib_host_to_net_u16 (new_l1); - - /* Copy dst address into the DA slot in the segment list */ - clib_memcpy (sr1->segments, ip1->dst_address.as_u64, - sizeof (ip6_address_t)); - /* Rewrite the ip6 dst address with the first hop */ - clib_memcpy (ip1->dst_address.as_u64, t1->first_hop.as_u64, - sizeof (ip6_address_t)); - - sr_fix_hmac (sm, ip1, sr1); - - vnet_buffer (b1)->ip.adj_index[VLIB_TX] = - t1->first_hop_dpo.dpoi_index; - next1 = t1->first_hop_dpo.dpoi_next_node; - next1 = (sr_local_cb ? - sr_local_cb (vm, node, b1, ip1, sr1) : next1); - - /* - * Ignore "do not rewrite" shtik in this path - */ - if (PREDICT_FALSE (next1 & 0x80000000)) - { - next1 ^= 0xFFFFFFFF; - if (PREDICT_FALSE (next1 == SR_REWRITE_NEXT_ERROR)) - b1->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK]; - } - } - - 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, - sizeof (tr->src.as_u8)); - clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8, - sizeof (tr->dst.as_u8)); - tr->length = new_l0; - tr->next_index = next0; - if (sr0) - clib_memcpy (tr->sr, sr0, sizeof (tr->sr)); - } - if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED)) - { - sr_rewrite_trace_t *tr = vlib_add_trace (vm, node, - b1, sizeof (*tr)); - tr->tunnel_index = t1 - sm->tunnels; - clib_memcpy (tr->src.as_u8, ip1->src_address.as_u8, - sizeof (tr->src.as_u8)); - clib_memcpy (tr->dst.as_u8, ip1->dst_address.as_u8, - sizeof (tr->dst.as_u8)); - tr->length = new_l1; - tr->next_index = next1; - if (sr1) - clib_memcpy (tr->sr, sr1, sizeof (tr->sr)); - } - vlib_validate_buffer_enqueue_x2 (vm, node, next_index, - to_next, n_left_to_next, - bi0, bi1, next0, next1); - } - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t *b0; - ip6_header_t *ip0 = 0; - ip6_sr_header_t *sr0 = 0; - ip6_sr_tunnel_t *t0; - u32 next0; - u16 new_l0 = 0; - - bi0 = from[0]; - to_next[0] = bi0; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - b0 = vlib_get_buffer (vm, bi0); - - - /* - * $$$ parse through header(s) to pick the point - * where we punch in the SR extention header - */ - t0 = - pool_elt_at_index (sm->tunnels, - vnet_buffer (b0)->ip.adj_index[VLIB_TX]); - - ASSERT (VLIB_BUFFER_PRE_DATA_SIZE - >= ((word) vec_len (t0->rewrite)) + b0->current_data); - - vnet_buffer (b0)->sw_if_index[VLIB_TX] = t0->tx_fib_index; - - ip0 = vlib_buffer_get_current (b0); - - /* - * SR-unaware service chaining case: pkt coming back from - * service has the original dst address, and will already - * have an SR header. If so, send it to sr-local - */ - if (PREDICT_FALSE (ip0->protocol == IPPROTO_IPV6_ROUTE)) - { - vlib_buffer_advance (b0, sizeof (ip0)); - sr0 = (ip6_sr_header_t *) (ip0 + 1); - new_l0 = clib_net_to_host_u16 (ip0->payload_length); - next0 = SR_REWRITE_NEXT_SR_LOCAL; - } - else - { - u32 len_bytes = sizeof (ip6_header_t); - u8 next_hdr = ip0->protocol; - - /* HBH must immediately follow ipv6 header */ - if (PREDICT_FALSE - (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS)) - { - ip6_hop_by_hop_ext_t *ext_hdr = - (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0); - len_bytes += - ip6_ext_header_len ((ip6_ext_header_t *) ext_hdr); - next_hdr = ext_hdr->next_hdr; - ext_hdr->next_hdr = IPPROTO_IPV6_ROUTE; - /* Ignoring the sr_local for now, if RH follows HBH here */ - } - else - { - ip0->protocol = IPPROTO_IPV6_ROUTE; /* routing extension header */ - } - /* - * Copy data before the punch-in point left by the - * required amount. Assume (for the moment) that only - * the main packet header needs to be copied. - */ - clib_memcpy (((u8 *) ip0) - vec_len (t0->rewrite), - ip0, len_bytes); - vlib_buffer_advance (b0, -(word) vec_len (t0->rewrite)); - ip0 = vlib_buffer_get_current (b0); - sr0 = (ip6_sr_header_t *) ((u8 *) ip0 + len_bytes); - /* $$$ tune */ - clib_memcpy (sr0, t0->rewrite, vec_len (t0->rewrite)); - - /* Fix the next header chain */ - sr0->protocol = next_hdr; - new_l0 = clib_net_to_host_u16 (ip0->payload_length) + - vec_len (t0->rewrite); - ip0->payload_length = clib_host_to_net_u16 (new_l0); - - /* Copy dst address into the DA slot in the segment list */ - clib_memcpy (sr0->segments, ip0->dst_address.as_u64, - sizeof (ip6_address_t)); - /* Rewrite the ip6 dst address with the first hop */ - clib_memcpy (ip0->dst_address.as_u64, t0->first_hop.as_u64, - sizeof (ip6_address_t)); - - sr_fix_hmac (sm, ip0, sr0); - - vnet_buffer (b0)->ip.adj_index[VLIB_TX] = - t0->first_hop_dpo.dpoi_index; - next0 = t0->first_hop_dpo.dpoi_next_node; - next0 = (sr_local_cb ? - sr_local_cb (vm, node, b0, ip0, sr0) : next0); - - /* - * Ignore "do not rewrite" shtik in this path - */ - if (PREDICT_FALSE (next0 & 0x80000000)) - { - next0 ^= 0xFFFFFFFF; - if (PREDICT_FALSE (next0 == SR_REWRITE_NEXT_ERROR)) - b0->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK]; - } - } - - 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; - if (ip0) - { - memcpy (tr->src.as_u8, ip0->src_address.as_u8, - sizeof (tr->src.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; - if (sr0) - clib_memcpy (tr->sr, sr0, sizeof (tr->sr)); - } - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, - to_next, n_left_to_next, - bi0, next0); - } - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - return from_frame->n_vectors; -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (sr_rewrite_node) = { - .function = sr_rewrite, - .name = "sr-rewrite", - /* Takes a vector of packets. */ - .vector_size = sizeof (u32), - .format_trace = format_sr_rewrite_trace, - .format_buffer = format_ip6_sr_header_with_length, - - .n_errors = SR_REWRITE_N_ERROR, - .error_strings = sr_rewrite_error_strings, - - .runtime_data_bytes = 0, - - .n_next_nodes = SR_REWRITE_N_NEXT, - .next_nodes = { -#define _(s,n) [SR_REWRITE_NEXT_##s] = n, - foreach_sr_rewrite_next -#undef _ - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (sr_rewrite_node, sr_rewrite) -/* *INDENT-ON* */ - -static int -ip6_routes_add_del (ip6_sr_tunnel_t * t, int is_del) -{ - ip6_sr_main_t *sm = &sr_main; - - /* - * the prefix for the tunnel's destination - */ - /* *INDENT-OFF* */ - fib_prefix_t pfx = { - .fp_proto = FIB_PROTOCOL_IP6, - .fp_len = t->dst_mask_width, - .fp_addr = { - .ip6 = t->key.dst, - } - }; - /* *INDENT-ON* */ - - if (is_del) - { - fib_table_entry_delete (t->rx_fib_index, &pfx, FIB_SOURCE_SR); - } - else - { - dpo_id_t dpo = DPO_INVALID; - - dpo_set (&dpo, sr_dpo_type, DPO_PROTO_IP6, t - sm->tunnels); - fib_table_entry_special_dpo_add (t->rx_fib_index, - &pfx, - FIB_SOURCE_SR, - FIB_ENTRY_FLAG_EXCLUSIVE, &dpo); - dpo_reset (&dpo); - } - - /* - * Track the first hop address so we don't need to perform an extra - * lookup in the data-path - */ - /* *INDENT-OFF* */ - const fib_prefix_t first_hop_pfx = { - .fp_len = 128, - .fp_proto = FIB_PROTOCOL_IP6, - .fp_addr = { - .ip6 = t->first_hop, - } - }; - /* *INDENT-ON* */ - - if (is_del) - { - fib_entry_child_remove (t->fib_entry_index, t->sibling_index); - fib_table_entry_delete_index (t->fib_entry_index, FIB_SOURCE_RR); - } - else - { - t->fib_entry_index = - fib_table_entry_special_add (t->rx_fib_index, - &first_hop_pfx, - FIB_SOURCE_RR, - FIB_ENTRY_FLAG_NONE, ADJ_INDEX_INVALID); - t->sibling_index = - fib_entry_child_add (t->fib_entry_index, - sr_fib_node_type, t - sm->tunnels); - } - - return 0; -} - -/** - * @brief Find or add if not found - HMAC shared secret - * - * @param sm ip6_sr_main_t * - * @param secret u8 * - * @param indexp u32 * - * - * @return ip6_sr_hmac_key_t * - */ -static ip6_sr_hmac_key_t * -find_or_add_shared_secret (ip6_sr_main_t * sm, u8 * secret, u32 * indexp) -{ - uword *p; - ip6_sr_hmac_key_t *key = 0; - int i; - - p = hash_get_mem (sm->hmac_key_by_shared_secret, secret); - - if (p) - { - key = vec_elt_at_index (sm->hmac_keys, p[0]); - if (indexp) - *indexp = p[0]; - return (key); - } - - /* Specific key ID? */ - if (indexp && *indexp) - { - vec_validate (sm->hmac_keys, *indexp); - key = sm->hmac_keys + *indexp; - } - else - { - for (i = 0; i < vec_len (sm->hmac_keys); i++) - { - if (sm->hmac_keys[i].shared_secret == 0) - { - key = sm->hmac_keys + i; - goto found; - } - } - vec_validate (sm->hmac_keys, i); - key = sm->hmac_keys + i; - found: - ; - } - - key->shared_secret = vec_dup (secret); - - hash_set_mem (sm->hmac_key_by_shared_secret, key->shared_secret, - key - sm->hmac_keys); - - if (indexp) - *indexp = key - sm->hmac_keys; - return (key); -} - -/** - * @brief Stack a tunnel on the forwarding chain of the first-hop - */ -static void -sr_tunnel_stack (ip6_sr_tunnel_t * st) -{ - dpo_stack (sr_dpo_type, - DPO_PROTO_IP6, - &st->first_hop_dpo, - fib_entry_contribute_ip_forwarding (st->fib_entry_index)); -} - -/** - * @brief Add or Delete a Segment Routing tunnel. - * - * @param a ip6_sr_add_del_tunnel_args_t * - * - * @return retval int - */ -int -ip6_sr_add_del_tunnel (ip6_sr_add_del_tunnel_args_t * a) -{ - ip6_main_t *im = &ip6_main; - ip6_sr_tunnel_key_t key; - ip6_sr_tunnel_t *t; - uword *p, *n; - ip6_sr_header_t *h = 0; - u32 header_length; - ip6_address_t *addrp, *this_address; - ip6_sr_main_t *sm = &sr_main; - u8 *key_copy; - u32 rx_fib_index, tx_fib_index; - 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); - - if (p == 0) - return -3; - - /* remember the FIB index */ - rx_fib_index = p[0]; - - /* Make sure that the supplied FIB exists */ - p = hash_get (im->fib_index_by_table_id, a->tx_table_id); - - if (p == 0) - return -4; - - /* remember the FIB index */ - tx_fib_index = p[0]; - - 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)); - - /* When adding a tunnel: - * - If a "name" is given, it must not exist. - * - The "key" is always checked, and must not exist. - * When deleting a tunnel: - * - If the "name" is given, and it exists, then use it. - * - If the "name" is not given, use the "key". - * - If the "name" and the "key" are given, then both must point to the same - * thing. - */ - - /* Lookup the key */ - p = hash_get_mem (sm->tunnel_index_by_key, &key); - - /* If the name is given, look it up */ - if (a->name) - n = hash_get_mem (sm->tunnel_index_by_name, a->name); - else - n = 0; - - /* validate key/name parameters */ - if (!a->is_del) /* adding a tunnel */ - { - if (a->name && n) /* name given & exists already */ - return -1; - if (p) /* key exists already */ - return -1; - } - else /* deleting a tunnel */ - { - if (!p) /* key doesn't exist */ - return -2; - if (a->name && !n) /* name given & it doesn't exist */ - return -2; - - if (n) /* name given & found */ - { - if (n[0] != p[0]) /* name and key do not point to the same thing */ - return -2; - } - } - - - if (a->is_del) /* delete the tunnel */ - { - hash_pair_t *hp; - - /* Delete existing tunnel */ - t = pool_elt_at_index (sm->tunnels, p[0]); - - ip6_routes_add_del (t, 1); - - 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); - } - dpo_reset (&t->first_hop_dpo); - pool_put (sm->tunnels, t); - hp = hash_get_pair (sm->tunnel_index_by_key, &key); - key_copy = (void *) (hp->key); - hash_unset_mem (sm->tunnel_index_by_key, &key); - vec_free (key_copy); - return 0; - } - - /* create a new tunnel */ - pool_get (sm->tunnels, t); - memset (t, 0, sizeof (*t)); - t->policy_index = ~0; - fib_node_init (&t->node, sr_fib_node_type); - - clib_memcpy (&t->key, &key, sizeof (t->key)); - t->dst_mask_width = a->dst_mask_width; - t->rx_fib_index = rx_fib_index; - t->tx_fib_index = tx_fib_index; - - if (!vec_len (a->segments)) - /* there must be at least one segment... */ - return -4; - - /* The first specified hop goes right into the dst address */ - clib_memcpy (&t->first_hop, &a->segments[0], sizeof (ip6_address_t)); - - /* - * Create the sr header rewrite string - * The list of segments needs an extra slot for the ultimate destination - * which is taken from the packet we add the SRH to. - */ - header_length = sizeof (*h) + - sizeof (ip6_address_t) * (vec_len (a->segments) + 1 + vec_len (a->tags)); - - if (a->shared_secret) - { - /* Allocate a new key slot if we don't find the secret key */ - hmac_key_index_u32 = 0; - (void) find_or_add_shared_secret (sm, a->shared_secret, - &hmac_key_index_u32); - - /* Hey Vinz Clortho: Gozzer is pissed.. you're out of keys! */ - if (hmac_key_index_u32 >= 256) - return -5; - hmac_key_index = hmac_key_index_u32; - header_length += SHA256_DIGEST_LENGTH; - } - - vec_validate (t->rewrite, header_length - 1); - - h = (ip6_sr_header_t *) t->rewrite; - - h->protocol = 0xFF; /* we don't know yet */ - - h->length = (header_length / 8) - 1; - h->type = ROUTING_HEADER_TYPE_SR; - - /* first_segment and segments_left need to have the index of the last - * element in the list; a->segments has one element less than ends up - * in the header (it does not have the DA in it), so vec_len(a->segments) - * is the value we want. - */ - h->first_segment = h->segments_left = vec_len (a->segments); - - if (a->shared_secret) - h->hmac_key = hmac_key_index & 0xFF; - - h->flags = a->flags_net_byte_order; - - /* Paint on the segment list, in reverse. - * This is offset by one to leave room at the start for the ultimate - * destination. - */ - addrp = h->segments + vec_len (a->segments); - - vec_foreach (this_address, a->segments) - { - clib_memcpy (addrp->as_u8, this_address->as_u8, sizeof (ip6_address_t)); - addrp--; - } - - /* - * Since the ultimate destination address is not yet known, set that slot - * to a value we will instantly recognize as bogus. - */ - memset (h->segments, 0xfe, sizeof (ip6_address_t)); - - /* Paint on the tag list, not reversed */ - addrp = h->segments + vec_len (a->segments); - - vec_foreach (this_address, a->tags) - { - clib_memcpy (addrp->as_u8, this_address->as_u8, sizeof (ip6_address_t)); - addrp++; - } - - key_copy = vec_new (ip6_sr_tunnel_key_t, 1); - clib_memcpy (key_copy, &key, sizeof (ip6_sr_tunnel_key_t)); - hash_set_mem (sm->tunnel_index_by_key, key_copy, t - sm->tunnels); - - /* - * 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... - */ - - /* - * Add the routes for the tunnel destination and first-hop, then stack - * the tunnel on the appropriate forwarding DPOs. - */ - ip6_routes_add_del (t, 0); - sr_tunnel_stack (t); - - 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); - if (p == 0) - clib_warning ("p is NULL!"); - t->policy_index = p ? p[0] : ~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; -} /** * @brief no-op lock function. * The lifetime of the SR entry is managed by the control plane */ -static void +void sr_dpo_lock (dpo_id_t * dpo) { } @@ -1208,2227 +43,15 @@ sr_dpo_lock (dpo_id_t * dpo) * @brief no-op unlock function. * The lifetime of the SR entry is managed by the control plane */ -static void -sr_dpo_unlock (dpo_id_t * dpo) -{ -} - -u8 * -format_sr_dpo (u8 * s, va_list * args) -{ - index_t index = va_arg (*args, index_t); - CLIB_UNUSED (u32 indent) = va_arg (*args, u32); - - return (format (s, "SR: tunnel:[%d]", index)); -} - -const static dpo_vft_t sr_dpo_vft = { - .dv_lock = sr_dpo_lock, - .dv_unlock = sr_dpo_unlock, - .dv_format = format_sr_dpo, -}; - -const static char *const sr_ip6_nodes[] = { - "sr-rewrite", - NULL, -}; - -const static char *const *const sr_nodes[DPO_PROTO_NUM] = { - [DPO_PROTO_IP6] = sr_ip6_nodes, -}; - -static ip6_sr_tunnel_t * -sr_tunnel_from_fib_node (fib_node_t * node) -{ -#if (CLIB_DEBUG > 0) - ASSERT (sr_fib_node_type == node->fn_type); -#endif - return ((ip6_sr_tunnel_t *) (((char *) node) - - STRUCT_OFFSET_OF (ip6_sr_tunnel_t, node))); -} - -/** - * Function definition to backwalk a FIB node - */ -static fib_node_back_walk_rc_t -sr_tunnel_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx) -{ - sr_tunnel_stack (sr_tunnel_from_fib_node (node)); - - return (FIB_NODE_BACK_WALK_CONTINUE); -} - -/** - * Function definition to get a FIB node from its index - */ -static fib_node_t * -sr_tunnel_fib_node_get (fib_node_index_t index) -{ - ip6_sr_tunnel_t *st; - ip6_sr_main_t *sm; - - sm = &sr_main; - st = pool_elt_at_index (sm->tunnels, index); - - return (&st->node); -} - -/** - * Function definition to inform the FIB node that its last lock has gone. - */ -static void -sr_tunnel_last_lock_gone (fib_node_t * node) -{ - /* - * The SR tunnel is a root of the graph. As such - * it never has children and thus is never locked. - */ - ASSERT (0); -} - -/* - * Virtual function table registered by SR tunnels - * for participation in the FIB object graph. - */ -const static fib_node_vft_t sr_fib_vft = { - .fnv_get = sr_tunnel_fib_node_get, - .fnv_last_lock = sr_tunnel_last_lock_gone, - .fnv_back_walk = sr_tunnel_back_walk, -}; - -/** - * @brief CLI parser for Add or Delete a Segment Routing tunnel. - * - * @param vm vlib_main_t * - * @param input unformat_input_t * - * @param cmd vlib_cli_command_t * - * - * @return error clib_error_t * - */ -static clib_error_t * -sr_add_del_tunnel_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - int is_del = 0; - ip6_address_t src_address; - int src_address_set = 0; - ip6_address_t dst_address; - u32 dst_mask_width; - 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; - ip6_address_t *this_seg; - ip6_address_t *tags = 0; - ip6_address_t *this_tag; - ip6_sr_add_del_tunnel_args_t _a, *a = &_a; - ip6_address_t next_address, tag; - int pl_index; - int rv; - - while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (input, "del")) - is_del = 1; - else if (unformat (input, "rx-fib-id %d", &rx_table_id)) - ; - else if (unformat (input, "tx-fib-id %d", &tx_table_id)) - ; - 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)) - dst_address_set = 1; - else if (unformat (input, "next %U", unformat_ip6_address, - &next_address)) - { - vec_add2 (segments, this_seg, 1); - clib_memcpy (this_seg->as_u8, next_address.as_u8, - sizeof (*this_seg)); - } - else if (unformat (input, "tag %U", unformat_ip6_address, &tag)) - { - vec_add2 (tags, this_tag, 1); - clib_memcpy (this_tag->as_u8, tag.as_u8, sizeof (*this_tag)); - } - else if (unformat (input, "clean")) - flags |= IP6_SR_HEADER_FLAG_CLEANUP; - else if (unformat (input, "protected")) - flags |= IP6_SR_HEADER_FLAG_PROTECTED; - else if (unformat (input, "key %s", &shared_secret)) - /* Do not include the trailing NULL byte. Guaranteed interop issue */ - _vec_len (shared_secret) -= 1; - else if (unformat (input, "InPE %d", &pl_index)) - { - if (pl_index <= 0 || pl_index > 4) - { - pl_index_range_error: - return clib_error_return - (0, "Policy List Element Index %d out of range (1-4)", - pl_index); - - } - flags |= IP6_SR_HEADER_FLAG_PL_ELT_INGRESS_PE - << ip6_sr_policy_list_shift_from_index (pl_index); - } - else if (unformat (input, "EgPE %d", &pl_index)) - { - if (pl_index <= 0 || pl_index > 4) - goto pl_index_range_error; - flags |= IP6_SR_HEADER_FLAG_PL_ELT_EGRESS_PE - << ip6_sr_policy_list_shift_from_index (pl_index); - } - else if (unformat (input, "OrgSrc %d", &pl_index)) - { - if (pl_index <= 0 || pl_index > 4) - goto pl_index_range_error; - flags |= IP6_SR_HEADER_FLAG_PL_ELT_ORIG_SRC_ADDR - << ip6_sr_policy_list_shift_from_index (pl_index); - } - else - break; - } - - if (!src_address_set) - return clib_error_return (0, "src address required"); - - if (!dst_address_set) - return clib_error_return (0, "dst address required"); - - if (!segments) - return clib_error_return (0, "at least one sr segment required"); - - memset (a, 0, sizeof (*a)); - a->src_address = &src_address; - a->dst_address = &dst_address; - a->dst_mask_width = dst_mask_width; - a->segments = segments; - a->tags = tags; - a->flags_net_byte_order = clib_host_to_net_u16 (flags); - a->is_del = is_del; - a->rx_table_id = rx_table_id; - 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); - vec_free (tags); - vec_free (shared_secret); - - switch (rv) - { - case 0: - break; - - case -1: - return clib_error_return (0, "SR tunnel src %U dst %U already exists", - format_ip6_address, &src_address, - format_ip6_address, &dst_address); - - case -2: - return clib_error_return (0, "SR tunnel src %U dst %U does not exist", - format_ip6_address, &src_address, - format_ip6_address, &dst_address); - - case -3: - return clib_error_return (0, "FIB table %d does not exist", - rx_table_id); - - case -4: - return clib_error_return (0, "At least one segment is required"); - - default: - return clib_error_return (0, "BUG: ip6_sr_add_del_tunnel returns %d", - rv); - } - - return 0; -} - -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (sr_tunnel_command, static) = { - .path = "sr tunnel", - .short_help = - "sr tunnel [del] [name <name>] src <addr> dst <addr> [next <addr>] " - "[clean] [reroute] [key <secret>] [policy <policy_name>]" - "[rx-fib-id <fib_id>] [tx-fib-id <fib_id>]", - .function = sr_add_del_tunnel_command_fn, -}; -/* *INDENT-ON* */ - -/** - * @brief Display Segment Routing tunnel - * - * @param vm vlib_main_t * - * @param t ip6_sr_tunnel_t * - * - */ void -ip6_sr_tunnel_display (vlib_main_t * vm, ip6_sr_tunnel_t * t) -{ - ip6_sr_main_t *sm = &sr_main; - ip6_fib_t *rx_fib, *tx_fib; - ip6_sr_policy_t *pt; - - rx_fib = ip6_fib_get (t->rx_fib_index); - tx_fib = ip6_fib_get (t->tx_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; -} - -/** - * @brief CLI Parser for Display Segment Routing tunnel - * - * @param vm vlib_main_t * - * @param input unformat_input_t * - * @param cmd vlib_cli_command_t * - * - * @return error clib_error_t * - */ -static clib_error_t * -show_sr_tunnel_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - static ip6_sr_tunnel_t **tunnels; - ip6_sr_tunnel_t *t; - ip6_sr_main_t *sm = &sr_main; - 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 */ - { - /* *INDENT-OFF* */ - pool_foreach (t, sm->tunnels, - ({ - vec_add1 (tunnels, t); - })); - /* *INDENT-ON* */ - } - 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]; - ip6_sr_tunnel_display (vm, t); - } - - return 0; -} - -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (show_sr_tunnel_command, static) = { - .path = "show sr tunnel", - .short_help = "show sr tunnel [name <sr-tunnel-name>]", - .function = show_sr_tunnel_fn, -}; -/* *INDENT-ON* */ - -/** - * @brief Add or Delete a Segment Routing policy - * - * @param a ip6_sr_add_del_policy_args_t * - * - * @return retval int - */ -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; -} - -/** - * @brief CLI Parser for Add or Delete a Segment Routing policy - * - * @param vm vlib_main_t * - * @param input unformat_input_t * - * @param cmd vlib_cli_command_t * - * - * @return error clib_error_t * - */ -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; -} - -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (sr_policy_command, static) = { - .path = "sr policy", - .short_help = - "sr policy [del] name <policy-name> tunnel <sr-tunnel-name> [tunnel <sr-tunnel-name>]*", - .function = sr_add_del_policy_command_fn, -}; -/* *INDENT-ON* */ - -/** - * @brief CLI Parser for Displaying Segment Routing policy - * - * @param vm vlib_main_t * - * @param input unformat_input_t * - * @param cmd vlib_cli_command_t * - * - * @return error clib_error_t * - */ -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 */ - { - /* *INDENT-OFF* */ - pool_foreach (policy, sm->policies, - ({ - vec_add1 (policies, policy); - })); - /* *INDENT-ON* */ - } - 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; - -} - -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (show_sr_policy_command, static) = { - .path = "show sr policy", - .short_help = "show sr policy [name <sr-policy-name>]", - .function = show_sr_policy_fn, -}; -/* *INDENT-ON* */ - -/** - * @brief Add or Delete a mapping of IP6 multicast address - * to Segment Routing policy. - * - * @param a ip6_sr_add_del_multicastmap_args_t * - * - * @return retval int - */ -int -ip6_sr_add_del_multicastmap (ip6_sr_add_del_multicastmap_args_t * a) -{ - uword *p; - ip6_sr_tunnel_t *t; - ip6_sr_main_t *sm = &sr_main; - ip6_sr_policy_t *pt; - index_t rep; - u32 ii; - - 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]); - - /* - * 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... - */ - - /* - * Construct an mFIB entry for the multicast address, - * using the rx/tx fib from the first tunnel. - * There is no RPF information for this address (I need to discuss this with - * Pablo), so for now accept from anywhere... - */ - /* *INDENT-OFF* */ - mfib_prefix_t pfx = { - .fp_proto = FIB_PROTOCOL_IP6, - .fp_len = 128, - .fp_grp_addr = { - .ip6 = *a->multicast_address, - } - }; - /* *INDENT-ON* */ - - if (a->is_del) - mfib_table_entry_delete (t->rx_fib_index, &pfx, MFIB_SOURCE_SRv6); - else - { - /* - * Construct a replicate DPO that will replicate received packets over - * each tunnel in the policy - */ - dpo_id_t dpo = DPO_INVALID; - - rep = replicate_create (vec_len (pt->tunnel_indices), DPO_PROTO_IP6); - - vec_foreach_index (ii, pt->tunnel_indices) - { - dpo_set (&dpo, sr_dpo_type, DPO_PROTO_IP6, pt->tunnel_indices[ii]); - - replicate_set_bucket (rep, ii, &dpo); - } - - mfib_table_entry_special_add (t->rx_fib_index, - &pfx, - MFIB_SOURCE_SRv6, - MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF, rep); - - dpo_reset (&dpo); - } - - 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); - } - else - { - hash_set_mem (sm->policy_index_by_multicast_address, mcast_copy, - pt - sm->policies); - } - - return 0; -} - -/** - * @brief CLI Parser for Adding or Delete a mapping of IP6 multicast address - * to Segment Routing policy. - * - * @param vm vlib_main_t * - * @param input unformat_input_t * - * @param cmd vlib_cli_command_t * - * - * @return error clib_error_t * - */ -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; - -} - - -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (sr_multicast_map_command, static) = { - .path = "sr multicast-map", - .short_help = - "sr multicast-map address <multicast-ip6-address> sr-policy <sr-policy-name> [del]", - .function = sr_add_del_multicast_map_command_fn, -}; -/* *INDENT-ON* */ - -/** - * @brief CLI Parser for Displaying a mapping of IP6 multicast address - * to Segment Routing policy. - * - * @param vm vlib_main_t * - * @param input unformat_input_t * - * @param cmd vlib_cli_command_t * - * - * @return error clib_error_t * - */ -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 */ - - /* *INDENT-OFF* */ - 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); - - } - - })); - /* *INDENT-ON* */ - - return 0; -} - -/* *INDENT-OFF* */ -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, -}; -/* *INDENT-ON* */ - - -#define foreach_sr_fix_dst_addr_next \ -_(DROP, "error-drop") - -/** - * @brief Struct for valid next-nodes for SR fix destination address node - */ -typedef enum -{ -#define _(s,n) SR_FIX_DST_ADDR_NEXT_##s, - foreach_sr_fix_dst_addr_next -#undef _ - SR_FIX_DST_ADDR_N_NEXT, -} sr_fix_dst_addr_next_t; - -/** - * @brief Error strings for SR Fix Destination rewrite - */ -static char *sr_fix_dst_error_strings[] = { -#define sr_fix_dst_error(n,s) s, -#include "sr_fix_dst_error.def" -#undef sr_fix_dst_error -}; - -/** - * @brief Struct for errors for SR Fix Destination rewrite - */ -typedef enum -{ -#define sr_fix_dst_error(n,s) SR_FIX_DST_ERROR_##n, -#include "sr_fix_dst_error.def" -#undef sr_fix_dst_error - SR_FIX_DST_N_ERROR, -} sr_fix_dst_error_t; - -/** - * @brief Information for fix address trace - */ -typedef struct -{ - ip6_address_t src, dst; - u32 next_index; - u32 adj_index; - u8 sr[256]; -} sr_fix_addr_trace_t; - -/** - * @brief Formatter for fix address trace - */ -u8 * -format_sr_fix_addr_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_fix_addr_trace_t *t = va_arg (*args, sr_fix_addr_trace_t *); - vnet_hw_interface_t *hi = 0; - ip_adjacency_t *adj; - ip6_main_t *im = &ip6_main; - ip_lookup_main_t *lm = &im->lookup_main; - vnet_main_t *vnm = vnet_get_main (); - - if (t->adj_index != ~0) - { - adj = ip_get_adjacency (lm, t->adj_index); - hi = vnet_get_sup_hw_interface (vnm, adj->rewrite_header.sw_if_index); - } - - s = format (s, "SR-FIX_ADDR: next %s ip6 src %U dst %U\n", - (t->next_index == SR_FIX_DST_ADDR_NEXT_DROP) - ? "drop" : "output", - format_ip6_address, &t->src, format_ip6_address, &t->dst); - if (t->next_index != SR_FIX_DST_ADDR_NEXT_DROP) - { - s = - format (s, "%U\n", format_ip6_sr_header, t->sr, 1 /* print_hmac */ ); - s = - format (s, " output via %s", - hi ? (char *) (hi->name) : "Invalid adj"); - } - return s; -} - -/** - * @brief Fix SR destination address - dual-loop - * - * @node sr-fix-dst-addr - * @param vm vlib_main_t * - * @param node vlib_node_runtime_t * - * @param from_frame vlib_frame_t * - * - * @return from_frame->n_vectors uword - */ -static uword -sr_fix_dst_addr (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * from_frame) -{ - u32 n_left_from, next_index, *from, *to_next; - ip6_main_t *im = &ip6_main; - ip_lookup_main_t *lm = &im->lookup_main; - - from = vlib_frame_vector_args (from_frame); - n_left_from = from_frame->n_vectors; - - next_index = node->cached_next_index; - - while (n_left_from > 0) - { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - -#if 0 - while (0 && n_left_from >= 4 && n_left_to_next >= 2) - { - u32 bi0, bi1; - __attribute__ ((unused)) vlib_buffer_t *b0, *b1; - u32 next0 = SR_FIX_DST_ADDR_NEXT_DROP; - u32 next1 = SR_FIX_DST_ADDR_NEXT_DROP; - - /* Prefetch next iteration. */ - { - vlib_buffer_t *p2, *p3; - - p2 = vlib_get_buffer (vm, from[2]); - p3 = vlib_get_buffer (vm, from[3]); - - vlib_prefetch_buffer_header (p2, LOAD); - vlib_prefetch_buffer_header (p3, LOAD); - } - - bi0 = from[0]; - bi1 = from[1]; - to_next[0] = bi0; - to_next[1] = bi1; - from += 2; - to_next += 2; - n_left_to_next -= 2; - n_left_from -= 2; - - b0 = vlib_get_buffer (vm, bi0); - b1 = vlib_get_buffer (vm, bi1); - - - vlib_validate_buffer_enqueue_x2 (vm, node, next_index, - to_next, n_left_to_next, - bi0, bi1, next0, next1); - } -#endif - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t *b0; - ip6_header_t *ip0; - ip_adjacency_t *adj0; - ip6_sr_header_t *sr0; - u32 next0 = SR_FIX_DST_ADDR_NEXT_DROP; - ip6_address_t *new_dst0; - ethernet_header_t *eh0; - - bi0 = from[0]; - to_next[0] = bi0; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - b0 = vlib_get_buffer (vm, bi0); - - adj0 = - ip_get_adjacency (lm, vnet_buffer (b0)->ip.adj_index[VLIB_TX]); - next0 = adj0->if_address_index; - - /* We should be pointing at an Ethernet header... */ - eh0 = vlib_buffer_get_current (b0); - ip0 = (ip6_header_t *) (eh0 + 1); - sr0 = (ip6_sr_header_t *) (ip0 + 1); - - /* We'd better find an SR header... */ - if (PREDICT_FALSE (ip0->protocol != IPPROTO_IPV6_ROUTE)) - { - b0->error = node->errors[SR_FIX_DST_ERROR_NO_SR_HEADER]; - goto do_trace0; - } - else - { - /* - * We get here from sr_rewrite or sr_local, with - * sr->segments_left pointing at the (copy of the original) dst - * address. Use it, then increment sr0->segments_left. - */ - - /* Out of segments? Turf the packet */ - if (PREDICT_FALSE (sr0->segments_left == 0)) - { - b0->error = node->errors[SR_FIX_DST_ERROR_NO_MORE_SEGMENTS]; - goto do_trace0; - } - - /* - * Rewrite the packet with the original dst address - * We assume that the last segment (in processing order) contains - * the original dst address. The list is reversed, so sr0->segments - * contains the original dst address. - */ - new_dst0 = sr0->segments; - ip0->dst_address.as_u64[0] = new_dst0->as_u64[0]; - ip0->dst_address.as_u64[1] = new_dst0->as_u64[1]; - } - - do_trace0: - - if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) - { - sr_fix_addr_trace_t *t = vlib_add_trace (vm, node, - b0, sizeof (*t)); - t->next_index = next0; - t->adj_index = ~0; - - if (next0 != SR_FIX_DST_ADDR_NEXT_DROP) - { - t->adj_index = vnet_buffer (b0)->ip.adj_index[VLIB_TX]; - clib_memcpy (t->src.as_u8, ip0->src_address.as_u8, - sizeof (t->src.as_u8)); - clib_memcpy (t->dst.as_u8, ip0->dst_address.as_u8, - sizeof (t->dst.as_u8)); - clib_memcpy (t->sr, sr0, sizeof (t->sr)); - } - } - - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, - to_next, n_left_to_next, - bi0, next0); - } - - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - return from_frame->n_vectors; -} - - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (sr_fix_dst_addr_node) = { - .function = sr_fix_dst_addr, - .name = "sr-fix-dst-addr", - /* Takes a vector of packets. */ - .vector_size = sizeof (u32), - .format_trace = format_sr_fix_addr_trace, - .format_buffer = format_ip6_sr_header_with_length, - - .runtime_data_bytes = 0, - - .n_errors = SR_FIX_DST_N_ERROR, - .error_strings = sr_fix_dst_error_strings, - - .n_next_nodes = SR_FIX_DST_ADDR_N_NEXT, - .next_nodes = { -#define _(s,n) [SR_FIX_DST_ADDR_NEXT_##s] = n, - foreach_sr_fix_dst_addr_next -#undef _ - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (sr_fix_dst_addr_node, sr_fix_dst_addr) -/* *INDENT-ON* */ - -static clib_error_t * -sr_init (vlib_main_t * vm) -{ - ip6_sr_main_t *sm = &sr_main; - clib_error_t *error = 0; - vlib_node_t *ip6_lookup_node, *ip6_rewrite_node; - - if ((error = vlib_call_init_function (vm, ip_main_init))) - return error; - - if ((error = vlib_call_init_function (vm, ip6_lookup_init))) - return error; - - sm->vlib_main = vm; - sm->vnet_main = vnet_get_main (); - - vec_validate (sm->hmac_keys, 0); - sm->hmac_keys[0].shared_secret = (u8 *) 0xdeadbeef; - - 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 (IPPROTO_IPV6_ROUTE, sr_local_node.index); - - ip6_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip6-lookup"); - ASSERT (ip6_lookup_node); - - ip6_rewrite_node = vlib_get_node_by_name (vm, (u8 *) "ip6-rewrite"); - ASSERT (ip6_rewrite_node); - - /* 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, - sr_fix_dst_addr_node.index); - - OpenSSL_add_all_digests (); - - sm->md = (void *) EVP_get_digestbyname ("sha1"); - sm->hmac_ctx = clib_mem_alloc (sizeof (HMAC_CTX)); - - sr_dpo_type = dpo_register_new_type (&sr_dpo_vft, sr_nodes); - sr_fib_node_type = fib_node_register_new_type (&sr_fib_vft); - - return error; -} - -VLIB_INIT_FUNCTION (sr_init); - -/** - * @brief Definition of next-nodes for SR local - */ -#define foreach_sr_local_next \ - _ (ERROR, "error-drop") \ - _ (IP6_LOOKUP, "ip6-lookup") - -/** - * @brief Struct for definition of next-nodes for SR local - */ -typedef enum -{ -#define _(s,n) SR_LOCAL_NEXT_##s, - foreach_sr_local_next -#undef _ - SR_LOCAL_N_NEXT, -} sr_local_next_t; - -/** - * @brief Struct for packet trace of SR local - */ -typedef struct -{ - u8 next_index; - u8 sr_valid; - ip6_address_t src, dst; - u16 length; - u8 sr[256]; -} sr_local_trace_t; - -/** - * @brief Definition of SR local error-strings - */ -static char *sr_local_error_strings[] = { -#define sr_error(n,s) s, -#include "sr_error.def" -#undef sr_error -}; - -/** - * @brief Struct for definition of SR local error-strings - */ -typedef enum -{ -#define sr_error(n,s) SR_LOCAL_ERROR_##n, -#include "sr_error.def" -#undef sr_error - SR_LOCAL_N_ERROR, -} sr_local_error_t; - -/** - * @brief Format SR local trace - * - * @param s u8 * - * @param args va_list * - * - * @return s u8 * - */ -u8 * -format_sr_local_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_local_trace_t *t = va_arg (*args, sr_local_trace_t *); - - s = format (s, "SR-LOCAL: src %U dst %U len %u next_index %d", - format_ip6_address, &t->src, - format_ip6_address, &t->dst, t->length, t->next_index); - if (t->sr_valid) - s = - format (s, "\n %U", format_ip6_sr_header, t->sr, 1 /* print_hmac */ ); - else - s = format (s, "\n popped SR header"); - - return s; -} - - -/* $$$$ fixme: smp, don't copy data, cache input, output (maybe) */ -/** - * @brief Validate the SR HMAC - * - * @param sm ip6_sr_main_t * - * @param ip ip6_header_t * - * @param sr ip6_sr_header_t * - * - * @return retval int - */ -static int -sr_validate_hmac (ip6_sr_main_t * sm, ip6_header_t * ip, ip6_sr_header_t * sr) -{ - u32 key_index; - static u8 *keybuf; - u8 *copy_target; - int first_segment; - ip6_address_t *addrp; - int i; - ip6_sr_hmac_key_t *hmac_key; - static u8 *signature; - u32 sig_len; - - key_index = sr->hmac_key; - - /* No signature? Pass... */ - if (key_index == 0) - return 0; - - /* We don't know about this key? Fail... */ - if (key_index >= vec_len (sm->hmac_keys)) - return 1; - - vec_validate (signature, SHA256_DIGEST_LENGTH - 1); - - hmac_key = sm->hmac_keys + key_index; - - vec_reset_length (keybuf); - - /* pkt ip6 src address */ - vec_add2 (keybuf, copy_target, sizeof (ip6_address_t)); - clib_memcpy (copy_target, ip->src_address.as_u8, sizeof (ip6_address_t)); - - /* last segment */ - vec_add2 (keybuf, copy_target, 1); - copy_target[0] = sr->first_segment; - - /* octet w/ bit 0 = "clean" flag */ - vec_add2 (keybuf, copy_target, 1); - copy_target[0] - = (sr->flags & clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP)) - ? 0x80 : 0; - - /* hmac key id */ - vec_add2 (keybuf, copy_target, 1); - copy_target[0] = sr->hmac_key; - - first_segment = sr->first_segment; - - addrp = sr->segments; - - /* segments */ - for (i = 0; i <= first_segment; i++) - { - vec_add2 (keybuf, copy_target, sizeof (ip6_address_t)); - clib_memcpy (copy_target, addrp->as_u8, sizeof (ip6_address_t)); - addrp++; - } - - if (sm->is_debug) - clib_warning ("verify key index %d keybuf: %U", key_index, - format_hex_bytes, keybuf, vec_len (keybuf)); - - /* shared secret */ - - /* SHA1 is shorter than SHA-256 */ - memset (signature, 0, vec_len (signature)); - - HMAC_CTX_init (sm->hmac_ctx); - if (!HMAC_Init (sm->hmac_ctx, hmac_key->shared_secret, - vec_len (hmac_key->shared_secret), sm->md)) - clib_warning ("barf1"); - if (!HMAC_Update (sm->hmac_ctx, keybuf, vec_len (keybuf))) - clib_warning ("barf2"); - if (!HMAC_Final (sm->hmac_ctx, signature, &sig_len)) - clib_warning ("barf3"); - HMAC_CTX_cleanup (sm->hmac_ctx); - - if (sm->is_debug) - clib_warning ("computed signature len %d, value %U", sig_len, - format_hex_bytes, signature, vec_len (signature)); - - /* Point at the SHA signature in the packet */ - addrp++; - if (sm->is_debug) - clib_warning ("read signature %U", format_hex_bytes, addrp, - SHA256_DIGEST_LENGTH); - - return memcmp (signature, addrp, SHA256_DIGEST_LENGTH); -} - -/** - * @brief SR local node - * @node sr-local - * - * @param vm vlib_main_t * - * @param node vlib_node_runtime_t * - * @param from_frame vlib_frame_t * - * - * @return from_frame->n_vectors uword - */ -static uword -sr_local (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * from_frame) -{ - u32 n_left_from, next_index, *from, *to_next; - ip6_sr_main_t *sm = &sr_main; - u32 (*sr_local_cb) (vlib_main_t *, vlib_node_runtime_t *, - vlib_buffer_t *, ip6_header_t *, ip6_sr_header_t *); - sr_local_cb = sm->sr_local_cb; - - from = vlib_frame_vector_args (from_frame); - n_left_from = from_frame->n_vectors; - - next_index = node->cached_next_index; - - while (n_left_from > 0) - { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - while (n_left_from >= 4 && n_left_to_next >= 2) - { - u32 bi0, bi1; - vlib_buffer_t *b0, *b1; - ip6_header_t *ip0, *ip1; - ip6_sr_header_t *sr0, *sr1; - ip6_address_t *new_dst0, *new_dst1; - u32 next0 = SR_LOCAL_NEXT_IP6_LOOKUP; - u32 next1 = SR_LOCAL_NEXT_IP6_LOOKUP; - - /* Prefetch next iteration. */ - { - vlib_buffer_t *p2, *p3; - - p2 = vlib_get_buffer (vm, from[2]); - p3 = vlib_get_buffer (vm, from[3]); - - vlib_prefetch_buffer_header (p2, LOAD); - vlib_prefetch_buffer_header (p3, LOAD); - - CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD); - CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD); - } - - bi0 = from[0]; - bi1 = from[1]; - to_next[0] = bi0; - to_next[1] = bi1; - from += 2; - to_next += 2; - n_left_to_next -= 2; - n_left_from -= 2; - - - b0 = vlib_get_buffer (vm, bi0); - ip0 = vlib_buffer_get_current (b0); - sr0 = (ip6_sr_header_t *) (ip0 + 1); - if (PREDICT_FALSE - (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS)) - { - ip6_hop_by_hop_ext_t *ext_hdr = - (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0); - sr0 = - (ip6_sr_header_t *) ip6_ext_next_header ((ip6_ext_header_t *) - ext_hdr); - } - - if (PREDICT_FALSE (sr0->type != ROUTING_HEADER_TYPE_SR)) - { - next0 = SR_LOCAL_NEXT_ERROR; - b0->error = - node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE]; - goto do_trace0; - } - - /* Out of segments? Turf the packet */ - if (PREDICT_FALSE (sr0->segments_left == 0)) - { - next0 = SR_LOCAL_NEXT_ERROR; - b0->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS]; - goto do_trace0; - } - - if (PREDICT_FALSE (sm->validate_hmac)) - { - if (sr_validate_hmac (sm, ip0, sr0)) - { - next0 = SR_LOCAL_NEXT_ERROR; - b0->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID]; - goto do_trace0; - } - } - - next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) : next0; - - /* - * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx - */ - if (PREDICT_FALSE (next0 & 0x80000000)) - { - next0 ^= 0xFFFFFFFF; - if (PREDICT_FALSE (next0 == SR_LOCAL_NEXT_ERROR)) - b0->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK]; - } - else - { - u32 segment_index0; - - segment_index0 = sr0->segments_left - 1; - - /* Rewrite the packet */ - new_dst0 = (ip6_address_t *) (sr0->segments + segment_index0); - ip0->dst_address.as_u64[0] = new_dst0->as_u64[0]; - ip0->dst_address.as_u64[1] = new_dst0->as_u64[1]; - - if (PREDICT_TRUE (sr0->segments_left > 0)) - sr0->segments_left -= 1; - } - - /* End of the path. Clean up the SR header, or not */ - if (PREDICT_FALSE - (sr0->segments_left == 0 && - (sr0->flags & - clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP)))) - { - u64 *copy_dst0, *copy_src0; - u16 new_l0; - u32 copy_len_u64s0 = 0; - int i; - - /* - * Copy the ip6 header right by the (real) length of the - * sr header. - */ - if (PREDICT_FALSE - (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS)) - { - ip6_hop_by_hop_ext_t *ext_hdr = - (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0); - copy_len_u64s0 = - (((ip6_ext_header_t *) ext_hdr)->n_data_u64s) + 1; - ext_hdr->next_hdr = sr0->protocol; - } - else - { - ip0->protocol = sr0->protocol; - } - vlib_buffer_advance (b0, (sr0->length + 1) * 8); - - new_l0 = clib_net_to_host_u16 (ip0->payload_length) - - (sr0->length + 1) * 8; - ip0->payload_length = clib_host_to_net_u16 (new_l0); - - copy_src0 = (u64 *) ip0; - copy_dst0 = copy_src0 + (sr0->length + 1); - - copy_dst0[4 + copy_len_u64s0] = copy_src0[4 + copy_len_u64s0]; - copy_dst0[3 + copy_len_u64s0] = copy_src0[3 + copy_len_u64s0]; - copy_dst0[2 + copy_len_u64s0] = copy_src0[2 + copy_len_u64s0]; - copy_dst0[1 + copy_len_u64s0] = copy_src0[1 + copy_len_u64s0]; - copy_dst0[0 + copy_len_u64s0] = copy_src0[0 + copy_len_u64s0]; - - for (i = copy_len_u64s0 - 1; i >= 0; i--) - { - copy_dst0[i] = copy_src0[i]; - } - - sr0 = 0; - } - - do_trace0: - if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) - { - sr_local_trace_t *tr = vlib_add_trace (vm, node, - b0, sizeof (*tr)); - clib_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, - sizeof (tr->dst.as_u8)); - tr->length = vlib_buffer_length_in_chain (vm, b0); - tr->next_index = next0; - tr->sr_valid = sr0 != 0; - if (tr->sr_valid) - clib_memcpy (tr->sr, sr0, sizeof (tr->sr)); - } - - b1 = vlib_get_buffer (vm, bi1); - ip1 = vlib_buffer_get_current (b1); - sr1 = (ip6_sr_header_t *) (ip1 + 1); - if (PREDICT_FALSE - (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS)) - { - - ip6_hop_by_hop_ext_t *ext_hdr = - (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1); - sr1 = - (ip6_sr_header_t *) ip6_ext_next_header ((ip6_ext_header_t *) - ext_hdr); - } - - if (PREDICT_FALSE (sr1->type != ROUTING_HEADER_TYPE_SR)) - { - next1 = SR_LOCAL_NEXT_ERROR; - b1->error = - node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE]; - goto do_trace1; - } - - /* Out of segments? Turf the packet */ - if (PREDICT_FALSE (sr1->segments_left == 0)) - { - next1 = SR_LOCAL_NEXT_ERROR; - b1->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS]; - goto do_trace1; - } - - if (PREDICT_FALSE (sm->validate_hmac)) - { - if (sr_validate_hmac (sm, ip1, sr1)) - { - next1 = SR_LOCAL_NEXT_ERROR; - b1->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID]; - goto do_trace1; - } - } - - next1 = sr_local_cb ? sr_local_cb (vm, node, b1, ip1, sr1) : next1; - - /* - * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx - */ - if (PREDICT_FALSE (next1 & 0x80000000)) - { - next1 ^= 0xFFFFFFFF; - if (PREDICT_FALSE (next1 == SR_LOCAL_NEXT_ERROR)) - b1->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK]; - } - else - { - u32 segment_index1; - - segment_index1 = sr1->segments_left - 1; - - /* Rewrite the packet */ - new_dst1 = (ip6_address_t *) (sr1->segments + segment_index1); - ip1->dst_address.as_u64[0] = new_dst1->as_u64[0]; - ip1->dst_address.as_u64[1] = new_dst1->as_u64[1]; - - if (PREDICT_TRUE (sr1->segments_left > 0)) - sr1->segments_left -= 1; - } - - /* End of the path. Clean up the SR header, or not */ - if (PREDICT_FALSE - (sr1->segments_left == 0 && - (sr1->flags & - clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP)))) - { - u64 *copy_dst1, *copy_src1; - u16 new_l1; - u32 copy_len_u64s1 = 0; - int i; - - /* - * Copy the ip6 header right by the (real) length of the - * sr header. - */ - if (PREDICT_FALSE - (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS)) - { - ip6_hop_by_hop_ext_t *ext_hdr = - (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1); - copy_len_u64s1 = - (((ip6_ext_header_t *) ext_hdr)->n_data_u64s) + 1; - ext_hdr->next_hdr = sr1->protocol; - } - else - { - ip1->protocol = sr1->protocol; - } - vlib_buffer_advance (b1, (sr1->length + 1) * 8); - - new_l1 = clib_net_to_host_u16 (ip1->payload_length) - - (sr1->length + 1) * 8; - ip1->payload_length = clib_host_to_net_u16 (new_l1); - - copy_src1 = (u64 *) ip1; - copy_dst1 = copy_src1 + (sr1->length + 1); - - copy_dst1[4 + copy_len_u64s1] = copy_src1[4 + copy_len_u64s1]; - copy_dst1[3 + copy_len_u64s1] = copy_src1[3 + copy_len_u64s1]; - copy_dst1[2 + copy_len_u64s1] = copy_src1[2 + copy_len_u64s1]; - copy_dst1[1 + copy_len_u64s1] = copy_src1[1 + copy_len_u64s1]; - copy_dst1[0 + copy_len_u64s1] = copy_src1[0 + copy_len_u64s1]; - - for (i = copy_len_u64s1 - 1; i >= 0; i--) - { - copy_dst1[i] = copy_src1[i]; - } - - sr1 = 0; - } - - do_trace1: - if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED)) - { - sr_local_trace_t *tr = vlib_add_trace (vm, node, - b1, sizeof (*tr)); - clib_memcpy (tr->src.as_u8, ip1->src_address.as_u8, - sizeof (tr->src.as_u8)); - clib_memcpy (tr->dst.as_u8, ip1->dst_address.as_u8, - sizeof (tr->dst.as_u8)); - tr->length = vlib_buffer_length_in_chain (vm, b1); - tr->next_index = next1; - tr->sr_valid = sr1 != 0; - if (tr->sr_valid) - clib_memcpy (tr->sr, sr1, sizeof (tr->sr)); - } - - vlib_validate_buffer_enqueue_x2 (vm, node, next_index, - to_next, n_left_to_next, - bi0, bi1, next0, next1); - } - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t *b0; - ip6_header_t *ip0 = 0; - ip6_sr_header_t *sr0; - ip6_address_t *new_dst0; - u32 next0 = SR_LOCAL_NEXT_IP6_LOOKUP; - - bi0 = from[0]; - to_next[0] = bi0; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - b0 = vlib_get_buffer (vm, bi0); - ip0 = vlib_buffer_get_current (b0); - sr0 = (ip6_sr_header_t *) (ip0 + 1); - - if (PREDICT_FALSE - (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS)) - { - ip6_hop_by_hop_ext_t *ext_hdr = - (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0); - sr0 = - (ip6_sr_header_t *) ip6_ext_next_header ((ip6_ext_header_t *) - ext_hdr); - } - if (PREDICT_FALSE (sr0->type != ROUTING_HEADER_TYPE_SR)) - { - next0 = SR_LOCAL_NEXT_ERROR; - b0->error = - node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE]; - goto do_trace; - } - - /* Out of segments? Turf the packet */ - if (PREDICT_FALSE (sr0->segments_left == 0)) - { - next0 = SR_LOCAL_NEXT_ERROR; - b0->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS]; - goto do_trace; - } - - if (PREDICT_FALSE (sm->validate_hmac)) - { - if (sr_validate_hmac (sm, ip0, sr0)) - { - next0 = SR_LOCAL_NEXT_ERROR; - b0->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID]; - goto do_trace; - } - } - - next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) : next0; - - /* - * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx - */ - if (PREDICT_FALSE (next0 & 0x80000000)) - { - next0 ^= 0xFFFFFFFF; - if (PREDICT_FALSE (next0 == SR_LOCAL_NEXT_ERROR)) - b0->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK]; - } - else - { - u32 segment_index0; - - segment_index0 = sr0->segments_left - 1; - - /* Rewrite the packet */ - new_dst0 = (ip6_address_t *) (sr0->segments + segment_index0); - ip0->dst_address.as_u64[0] = new_dst0->as_u64[0]; - ip0->dst_address.as_u64[1] = new_dst0->as_u64[1]; - - if (PREDICT_TRUE (sr0->segments_left > 0)) - sr0->segments_left -= 1; - } - - /* End of the path. Clean up the SR header, or not */ - if (PREDICT_FALSE - (sr0->segments_left == 0 && - (sr0->flags & - clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP)))) - { - u64 *copy_dst0, *copy_src0; - u16 new_l0; - u32 copy_len_u64s0 = 0; - int i; - - /* - * Copy the ip6 header right by the (real) length of the - * sr header. - */ - if (PREDICT_FALSE - (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS)) - { - ip6_hop_by_hop_ext_t *ext_hdr = - (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0); - copy_len_u64s0 = - (((ip6_ext_header_t *) ext_hdr)->n_data_u64s) + 1; - ext_hdr->next_hdr = sr0->protocol; - } - else - { - ip0->protocol = sr0->protocol; - } - - vlib_buffer_advance (b0, (sr0->length + 1) * 8); - - new_l0 = clib_net_to_host_u16 (ip0->payload_length) - - (sr0->length + 1) * 8; - ip0->payload_length = clib_host_to_net_u16 (new_l0); - - copy_src0 = (u64 *) ip0; - copy_dst0 = copy_src0 + (sr0->length + 1); - copy_dst0[4 + copy_len_u64s0] = copy_src0[4 + copy_len_u64s0]; - copy_dst0[3 + copy_len_u64s0] = copy_src0[3 + copy_len_u64s0]; - copy_dst0[2 + copy_len_u64s0] = copy_src0[2 + copy_len_u64s0]; - copy_dst0[1 + copy_len_u64s0] = copy_src0[1 + copy_len_u64s0]; - copy_dst0[0 + copy_len_u64s0] = copy_src0[0 + copy_len_u64s0]; - - for (i = copy_len_u64s0 - 1; i >= 0; i--) - { - copy_dst0[i] = copy_src0[i]; - } - - sr0 = 0; - } - - do_trace: - if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) - { - sr_local_trace_t *tr = vlib_add_trace (vm, node, - b0, sizeof (*tr)); - clib_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, - sizeof (tr->dst.as_u8)); - tr->length = vlib_buffer_length_in_chain (vm, b0); - tr->next_index = next0; - tr->sr_valid = sr0 != 0; - if (tr->sr_valid) - clib_memcpy (tr->sr, sr0, sizeof (tr->sr)); - } - - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, - to_next, n_left_to_next, - bi0, next0); - } - - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - vlib_node_increment_counter (vm, sr_local_node.index, - SR_LOCAL_ERROR_PKTS_PROCESSED, - from_frame->n_vectors); - return from_frame->n_vectors; -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (sr_local_node, static) = { - .function = sr_local, - .name = "sr-local", - /* Takes a vector of packets. */ - .vector_size = sizeof (u32), - .format_trace = format_sr_local_trace, - - .runtime_data_bytes = 0, - - .n_errors = SR_LOCAL_N_ERROR, - .error_strings = sr_local_error_strings, - - .n_next_nodes = SR_LOCAL_N_NEXT, - .next_nodes = { -#define _(s,n) [SR_LOCAL_NEXT_##s] = n, - foreach_sr_local_next -#undef _ - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (sr_local_node, sr_local) -/* *INDENT-ON* */ - -ip6_sr_main_t * -sr_get_main (vlib_main_t * vm) -{ - vlib_call_init_function (vm, sr_init); - ASSERT (sr_local_node.index); - return &sr_main; -} - -/** - * @brief CLI parser for SR fix destination rewrite node - * - * @param vm vlib_main_t * - * @param input unformat_input_t * - * @param cmd vlib_cli_command_t * - * - * @return error clib_error_t * - */ -static clib_error_t * -set_ip6_sr_rewrite_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - fib_prefix_t pfx = { - .fp_proto = FIB_PROTOCOL_IP6, - .fp_len = 128, - }; - u32 fib_index = 0; - u32 fib_id = 0; - u32 adj_index; - ip_adjacency_t *adj; - vnet_hw_interface_t *hi; - u32 sw_if_index; - ip6_sr_main_t *sm = &sr_main; - vnet_main_t *vnm = vnet_get_main (); - fib_node_index_t fei; - - if (!unformat (input, "%U", unformat_ip6_address, &pfx.fp_addr.ip6)) - return clib_error_return (0, "ip6 address missing in '%U'", - format_unformat_error, input); - - if (unformat (input, "rx-table-id %d", &fib_id)) - { - fib_index = fib_table_id_find_fib_index (FIB_PROTOCOL_IP6, fib_id); - if (fib_index == ~0) - return clib_error_return (0, "fib-id %d not found", fib_id); - } - - fei = fib_table_lookup_exact_match (fib_index, &pfx); - - if (FIB_NODE_INDEX_INVALID == fei) - return clib_error_return (0, "no match for %U", - format_ip6_address, &pfx.fp_addr.ip6); - - adj_index = fib_entry_get_adj_for_source (fei, FIB_SOURCE_SR); - - if (ADJ_INDEX_INVALID == adj_index) - return clib_error_return (0, "%U not SR sourced", - format_ip6_address, &pfx.fp_addr.ip6); - - adj = adj_get (adj_index); - - if (adj->lookup_next_index != IP_LOOKUP_NEXT_REWRITE) - return clib_error_return (0, "%U unresolved (not a rewrite adj)", - format_ip6_address, &pfx.fp_addr.ip6); - - adj->rewrite_header.next_index = sm->ip6_rewrite_sr_next_index; - - sw_if_index = adj->rewrite_header.sw_if_index; - hi = vnet_get_sup_hw_interface (vnm, sw_if_index); - adj->rewrite_header.node_index = sr_fix_dst_addr_node.index; - - /* $$$$$ hack... steal the interface address index */ - adj->if_address_index = - vlib_node_add_next (vm, sr_fix_dst_addr_node.index, - hi->output_node_index); - - return 0; -} - -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (set_ip6_sr_rewrite, static) = { - .path = "set ip6 sr rewrite", - .short_help = "set ip6 sr rewrite <ip6-address> [fib-id <id>]", - .function = set_ip6_sr_rewrite_fn, -}; -/* *INDENT-ON* */ - -/** - * @brief Register a callback routine to set next0 in sr_local - * - * @param cb void * - */ -void -vnet_register_sr_app_callback (void *cb) -{ - ip6_sr_main_t *sm = &sr_main; - - sm->sr_local_cb = cb; -} - -/** - * @brief Test routine for validation of HMAC - */ -static clib_error_t * -test_sr_hmac_validate_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - ip6_sr_main_t *sm = &sr_main; - - if (unformat (input, "validate on")) - sm->validate_hmac = 1; - else if (unformat (input, "chunk-offset off")) - sm->validate_hmac = 0; - else - return clib_error_return (0, "expected validate on|off in '%U'", - format_unformat_error, input); - - vlib_cli_output (vm, "hmac signature validation %s", - sm->validate_hmac ? "on" : "off"); - return 0; -} - -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (test_sr_hmac_validate, static) = { - .path = "test sr hmac", - .short_help = "test sr hmac validate [on|off]", - .function = test_sr_hmac_validate_fn, -}; -/* *INDENT-ON* */ - -/** - * @brief Add or Delete HMAC key - * - * @param sm ip6_sr_main_t * - * @param key_id u32 - * @param shared_secret u8 * - * @param is_del u8 - * - * @return retval i32 - */ -// $$$ fixme shouldn't return i32 -i32 -sr_hmac_add_del_key (ip6_sr_main_t * sm, u32 key_id, u8 * shared_secret, - u8 is_del) -{ - u32 index; - ip6_sr_hmac_key_t *key; - - if (is_del == 0) - { - /* Specific key in use? Fail. */ - if (key_id && vec_len (sm->hmac_keys) > key_id - && sm->hmac_keys[key_id].shared_secret) - return -2; - - index = key_id; - key = find_or_add_shared_secret (sm, shared_secret, &index); - ASSERT (index == key_id); - return 0; - } - - /* delete */ - - if (key_id) /* delete by key ID */ - { - if (vec_len (sm->hmac_keys) <= key_id) - return -3; - - key = sm->hmac_keys + key_id; - - hash_unset_mem (sm->hmac_key_by_shared_secret, key->shared_secret); - vec_free (key->shared_secret); - return 0; - } - - index = 0; - key = find_or_add_shared_secret (sm, shared_secret, &index); - hash_unset_mem (sm->hmac_key_by_shared_secret, key->shared_secret); - vec_free (key->shared_secret); - return 0; -} - - -static clib_error_t * -sr_hmac_add_del_key_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - ip6_sr_main_t *sm = &sr_main; - u8 is_del = 0; - u32 key_id = 0; - u8 key_id_set = 0; - u8 *shared_secret = 0; - i32 rv; - - while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (input, "del")) - is_del = 1; - else if (unformat (input, "id %d", &key_id)) - key_id_set = 1; - else if (unformat (input, "key %s", &shared_secret)) - { - /* Do not include the trailing NULL byte. Guaranteed interop issue */ - _vec_len (shared_secret) -= 1; - } - else - break; - } - - if (is_del == 0 && shared_secret == 0) - return clib_error_return (0, "shared secret must be set to add a key"); - - if (shared_secret == 0 && key_id_set == 0) - return clib_error_return (0, "shared secret and key id both unset"); - - rv = sr_hmac_add_del_key (sm, key_id, shared_secret, is_del); - - vec_free (shared_secret); - - switch (rv) - { - case 0: - break; - - default: - return clib_error_return (0, "sr_hmac_add_del_key returned %d", rv); - } - - return 0; -} - -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (sr_hmac, static) = { - .path = "sr hmac", - .short_help = "sr hmac [del] id <nn> key <str>", - .function = sr_hmac_add_del_key_fn, -}; -/* *INDENT-ON* */ - -/** - * @brief CLI parser for show HMAC key shared secrets - * - * @param vm vlib_main_t * - * @param input unformat_input_t * - * @param cmd vlib_cli_command_t * - * - * @return error clib_error_t * - */ -static clib_error_t * -show_sr_hmac_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - ip6_sr_main_t *sm = &sr_main; - int i; - - for (i = 1; i < vec_len (sm->hmac_keys); i++) - { - if (sm->hmac_keys[i].shared_secret) - vlib_cli_output (vm, "[%d]: %v", i, sm->hmac_keys[i].shared_secret); - } - - return 0; -} - -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (show_sr_hmac, static) = { - .path = "show sr hmac", - .short_help = "show sr hmac", - .function = show_sr_hmac_fn, -}; -/* *INDENT-ON* */ - -/** - * @brief Test for SR debug flag - * - * @param vm vlib_main_t * - * @param input unformat_input_t * - * @param cmd vlib_cli_command_t * - * - * @return error clib_error_t * - */ -static clib_error_t * -test_sr_debug_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) +sr_dpo_unlock (dpo_id_t * dpo) { - ip6_sr_main_t *sm = &sr_main; - - if (unformat (input, "on")) - sm->is_debug = 1; - else if (unformat (input, "off")) - sm->is_debug = 0; - else - return clib_error_return (0, "expected on|off in '%U'", - format_unformat_error, input); - - vlib_cli_output (vm, "debug trace now %s", sm->is_debug ? "on" : "off"); - - return 0; } -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (test_sr_debug, static) = { - .path = "test sr debug", - .short_help = "test sr debug on|off", - .function = test_sr_debug_fn, -}; -/* *INDENT-ON* */ - /* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ +* fd.io coding-style-patch-verification: ON +* +* Local Variables: +* eval: (c-set-style "gnu") +* End: +*/ |