/* * sr.c: ipv6 segment routing * * Copyright (c) 2013 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. */ /** * @file * @brief Segment Routing main functions * */ #include #include #include #include #include #include #include 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 = ha }
/*
 * Copyright (c) 2015 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
  Copyright (c) 2001, 2002, 2003, 2005 Eliot Dresselhaus

  Permission is hereby granted, free of charge, to any person obtaining
  a copy of this software and associated documentation files (the
  "Software"), to deal in the Software without restriction, including
  without limitation the rights to use, copy, modify, merge, publish,
  distribute, sublicense, and/or sell copies of the Software, and to
  permit persons to whom the Software is furnished to do so, subject to
  the following conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include <vppinfra/bitmap.h>
#include <vppinfra/bitops.h>	/* for next_with_same_number_of_set_bits */
#include <vppinfra/error.h>	/* for ASSERT */
#include <vppinfra/mem.h>
#include <vppinfra/os.h>	/* for os_panic */
#include <vppinfra/vec.h>
#include <vppinfra/zvec.h>

/* Consider coding as bitmap, coding = 2^c_0 + 2^c_1 + ... + 2^c_n
   With c_0 < c_1 < ... < c_n.  coding == 0 represents c_n = BITS (uword).

   Unsigned integers i = 0 ... are represented as follows:

       0 <= i < 2^c_0       	(i << 1) | (1 << 0) binary:   i 1
   2^c_0 <= i < 2^c_0 + 2^c_1   (i << 2) | (1 << 1) binary: i 1 0
   ...                                              binary: i 0 ... 0

   Smaller numbers use less bits.  Coding is chosen so that encoding
   of given histogram of typical values gives smallest number of bits.
   The number and position of coding bits c_i are used to best fit the
   histogram of typical values.
*/

/* Decode given compressed data.  Return number of compressed data
   bits used. */
uword
zvec_decode (uword coding, uword zdata, uword * n_zdata_bits)
{
  uword c, d, result, n_bits;
  uword explicit_end, implicit_end;

  result = 0;
  n_bits = 0;
  while (1)
    {
      c = first_set (coding);
      implicit_end = c == coding;
      explicit_end = (zdata & 1) & ~implicit_end;
      d = (zdata >> explicit_end) & (c - 1);
      if (explicit_end | implicit_end)
	{
	  result += d;
	  n_bits += min_log2 (c) + explicit_end;
	  break;
	}
      n_bits += 1;
      result += c;
      coding ^= c;
      zdata >>= 1;
    }

  if (coding == 0)
    n_bits = BITS (uword);

  *n_zdata_bits = n_bits;
  return result;
}

uword
zvec_encode (uword coding, uword data, uword * n_result_bits)
{
  uword c, shift, result;
  uword explicit_end, implicit_end;

  /* Data must be in range.  Note special coding == 0
     would break for data - 1 <= coding. */
  ASSERT (data <= coding - 1);

  shift = 0;
  while (1)
    {
      c = first_set (coding);
      implicit_end = c == coding;
      explicit_end = ((data & (c - 1)) == data);
      if (explicit_end | implicit_end)
	{
	  uword t = explicit_end & ~implicit_end;
	  result = ((data << t) | t) << shift;
	  *n_result_bits =
	    /* data bits */ (c == 0 ? BITS (uword) : min_log2 (c))
	    /* shift bits */  + shift + t;
	  return result;
	}
      data -= c;
      coding ^= c;
      shift++;
    }

  /* Never reached. */
  ASSERT (0);
  return ~0;
}

always_inline uword
get_data (void *data, uword data_bytes, uword is_signed)
{
  if (data_bytes == 1)
    return is_signed ? zvec_signed_to_unsigned (*(i8 *) data) : *(u8 *) data;
  else if (data_bytes == 2)
    return is_signed ? zvec_signed_to_unsigned (*(i16 *) data) : *(u16 *)
      data;
  else if (data_bytes == 4)
    return is_signed ? zvec_signed_to_unsigned (*(i32 *) data) : *(u32 *)
      data;
  else if (data_bytes == 8)
    return is_signed ? zvec_signed_to_unsigned (*(i64 *) data) : *(u64 *)
      data;
  else
    {
      os_panic ();
      return ~0;
    }
}

always_inline void
put_data (void *data, uword data_bytes, uword is_signed, uword x)
{
  if (data_bytes == 1)
    {
      if (is_signed)
	*(i8 *) data = zvec_unsigned_to_signed (x);
      else
	*(u8 *) data = x;
    }
  else if (data_bytes == 2)
    {
      if (is_signed)
	*(i16 *) data = zvec_unsigned_to_signed (x);
      else
	*(u16 *) data = x;
    }
  else if (data_bytes == 4)
    {
      if (is_signed)
	*(i32 *) data = zvec_unsigned_to_signed (x);
      else
	*(u32 *) data = x;
    }
  else if (data_bytes == 8)
    {
      if (is_signed)
	*(i64 *) data = zvec_unsigned_to_signed (x);
      else
	*(u64 *) data = x;
    }
  else
    {
      os_panic ();
    }
}

always_inline uword *
zvec_encode_inline (uword * zvec,
		    uword * zvec_n_bits,
		    uword coding,
		    void *data,
		    uword data_stride,
		    uword n_data, uword data_bytes, uword is_signed)
{
  uword i;

  i = *zvec_n_bits;
  while (n_data >= 1)
    {
      uword d0, z0, l0;

      d0 = get_data (data + 0 * data_stride, data_bytes, is_signed);
      data += 1 * data_stride;
      n_data -= 1;

      z0 = zvec_encode (coding, d0, &l0);
      zvec = clib_bitmap_set_multiple (zvec, i, z0, l0);
      i += l0;
    }

  *zvec_n_bits = i;
  return zvec;
}

#define _(TYPE,IS_SIGNED)					\
  uword * zvec_encode_##TYPE (uword * zvec,			\
			      uword * zvec_n_bits,		\
			      uword coding,			\
			      void * data,			\
			      uword data_stride,		\
			      uword n_data)			\
  {								\
    return zvec_encode_inline (zvec, zvec_n_bits,		\
			    coding,				\
			    data, data_stride, n_data,		\
			    /* data_bytes */ sizeof (TYPE),	\
			    /* is_signed */ IS_SIGNED);		\
  }

_(u8, /* is_signed */ 0);
_(u16, /* is_signed */ 0);
_(u32, /* is_signed */ 0);
_(u64, /* is_signed */ 0);
_(i8, /* is_signed */ 1);
_(i16, /* is_signed */ 1);
_(i32, /* is_signed */ 1);
_(i64, /* is_signed */ 1);

#undef _

always_inline uword
coding_max_n_bits (uword coding)
{
  uword n_bits;
  (void) zvec_decode (coding, 0, &n_bits);
  return n_bits;
}

always_inline void
zvec_decode_inline (uword * zvec,
		    uword * zvec_n_bits,
		    uword coding,
		    void *data,
		    uword data_stride,
		    uword n_data, uword data_bytes, uword is_signed)
{
  uword i, n_max;

  i = *zvec_n_bits;
  n_max = coding_max_n_bits (coding);
  while (n_data >= 1)
    {
      uword d0, z0, l0;

      z0 = clib_bitmap_get_multiple (zvec, i, n_max);
      d0 = zvec_decode (coding, z0, &l0);
      i += l0;
      put_data (data + 0 * data_stride, data_bytes, is_signed, d0);
      data += 1 * data_stride;
      n_data -= 1;
    }
  *zvec_n_bits = i;
}

#define _(TYPE,IS_SIGNED)					\
  void zvec_decode_##TYPE (uword * zvec,			\
			   uword * zvec_n_bits,			\
			   uword coding,			\
			   void * data,				\
			   uword data_stride,			\
			   uword n_data)			\
  {								\
    return zvec_decode_inline (zvec, zvec_n_bits,		\
			       coding,				\
			       data, data_stride, n_data,	\
			       /* data_bytes */ sizeof (TYPE),	\
			       /* is_signed */ IS_SIGNED);	\
  }

_(u8, /* is_signed */ 0);
_(u16, /* is_signed */ 0);
_(u32, /* is_signed */ 0);
_(u64, /* is_signed */ 0);
_(i8, /* is_signed */ 1);
_(i16, /* is_signed */ 1);
_(i32, /* is_signed */ 1);
_(i64, /* is_signed */ 1);

#undef _

/* Compute number of bits needed to encode given histogram. */
static uword
zvec_coding_bits (uword coding, uword * histogram_counts, uword min_bits)
{
  uword n_type_bits, n_bits;
  uword this_count, last_count, max_count_index;
  uword i, b, l;

  n_bits = 0;
  n_type_bits = 1;
  last_count = 0;
  max_count_index = vec_len (histogram_counts) - 1;

  /* Coding is not large enough to encode given data. */
  if (coding <= max_count_index)
    return ~0;

  i = 0;
  while (coding != 0)
    {
      b = first_set (coding);
      l = min_log2 (b);
      i += b;

      this_count =
	histogram_counts[i > max_count_index ? max_count_index : i - 1];

      /* No more data to encode? */
      if (this_count == last_count)
	break;

      /* Last coding is i 0 ... 0 so we don't need an extra type bit. */
      if (coding == b)
	n_type_bits--;

      n_bits += (this_count - last_count) * (n_type_bits + l);

      /* This coding cannot be minimal: so return. */
      if (n_bits >= min_bits)
	return ~0;

      last_count = this_count;
      coding ^= b;
      n_type_bits++;
    }

  return n_bits;
}

uword
_zvec_coding_from_histogram (void *histogram,
			     uword histogram_len,
			     uword histogram_elt_count_offset,
			     uword histogram_elt_bytes,
			     uword max_value_to_encode,
			     zvec_coding_info_t * coding_return)
{
  uword coding, min_coding;
  uword min_coding_bits, coding_bits;
  uword i, n_bits_set, total_count;
  uword *counts;
  zvec_histogram_count_t *h_count = histogram + histogram_elt_count_offset;

  if (histogram_len < 1)
    {
      coding_return->coding = 0;
      coding_return->min_coding_bits = 0;
      coding_return->n_data = 0;
      coding_return->n_codes = 0;
      coding_return->ave_coding_bits = 0;
      return 0;
    }

  total_count = 0;
  counts = vec_new (uword, histogram_len);
  for (i = 0; i < histogram_len; i++)
    {
      zvec_histogram_count_t this_count = h_count[0];
      total_count += this_count;
      counts[i] = total_count;
      h_count =
	(zvec_histogram_count_t *) ((void *) h_count + histogram_elt_bytes);
    }

  min_coding = 0;
  min_coding_bits = ~0;

  {
    uword base_coding =
      max_value_to_encode !=
      ~0 ? (1 + max_value_to_encode) : vec_len (counts);
    uword max_coding = max_pow2 (2 * base_coding);

    for (n_bits_set = 1; n_bits_set <= 8; n_bits_set++)
      {
	for (coding = pow2_mask (n_bits_set);
	     coding < max_coding;
	     coding = next_with_same_number_of_set_bits (coding))
	  {
	    coding_bits = zvec_coding_bits (coding, counts, min_coding_bits);
	    if (coding_bits >= min_coding_bits)
	      continue;
	    min_coding_bits = coding_bits;
	    min_coding = coding;
	  }
      }
  }

  if (coding_return)
    {
      coding_return->coding = min_coding;
      coding_return->min_coding_bits = min_coding_bits;
      coding_return->n_data = total_count;
      coding_return->n_codes = vec_len (counts);
      coding_return->ave_coding_bits =
	(f64) min_coding_bits / (f64) total_count;
    }

  vec_free (counts);

  return min_coding;
}

u8 *
format_zvec_coding (u8 * s, va_list * args)
{
  zvec_coding_info_t *c = va_arg (*args, zvec_coding_info_t *);
  return format (s,
		 "zvec coding 0x%x, %d elts, %d codes, %d bits total, %.4f ave bits/code",
		 c->coding, c->n_data, c->n_codes, c->min_coding_bits,
		 c->ave_coding_bits);
}

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
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 [fib-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 key ", .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) { 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: */