2 * sr.c: ipv6 segment routing
4 * Copyright (c) 2013 Cisco and/or its affiliates.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 * @brief Segment Routing main functions
23 #include <vnet/vnet.h>
24 #include <vnet/sr/sr.h>
26 #include <openssl/hmac.h>
28 ip6_sr_main_t sr_main;
29 static vlib_node_registration_t sr_local_node;
32 * @brief Use passed HMAC key in ip6_sr_header_t in OpenSSL HMAC routines
34 * @param sm ip6_sr_main_t *
35 * @param ip ip6_header_t *
36 * @param sr ip6_sr_header_t *
39 sr_fix_hmac (ip6_sr_main_t * sm, ip6_header_t * ip, ip6_sr_header_t * sr)
47 ip6_sr_hmac_key_t *hmac_key;
50 key_index = sr->hmac_key;
52 /* No signature? Pass... */
56 /* We don't know about this key? Fail... */
57 if (key_index >= vec_len (sm->hmac_keys))
60 hmac_key = sm->hmac_keys + key_index;
62 vec_reset_length (keybuf);
64 /* pkt ip6 src address */
65 vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
66 clib_memcpy (copy_target, ip->src_address.as_u8, sizeof (ip6_address_t));
69 vec_add2 (keybuf, copy_target, 1);
70 copy_target[0] = sr->first_segment;
72 /* octet w/ bit 0 = "clean" flag */
73 vec_add2 (keybuf, copy_target, 1);
75 = (sr->flags & clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))
79 vec_add2 (keybuf, copy_target, 1);
80 copy_target[0] = sr->hmac_key;
82 first_segment = sr->first_segment;
87 for (i = 0; i <= first_segment; i++)
89 vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
90 clib_memcpy (copy_target, addrp->as_u8, sizeof (ip6_address_t));
96 HMAC_CTX_init (sm->hmac_ctx);
97 if (!HMAC_Init (sm->hmac_ctx, hmac_key->shared_secret,
98 vec_len (hmac_key->shared_secret), sm->md))
99 clib_warning ("barf1");
100 if (!HMAC_Update (sm->hmac_ctx, keybuf, vec_len (keybuf)))
101 clib_warning ("barf2");
102 if (!HMAC_Final (sm->hmac_ctx, (unsigned char *) addrp, &sig_len))
103 clib_warning ("barf3");
104 HMAC_CTX_cleanup (sm->hmac_ctx);
108 * @brief Format function for decoding various SR flags
110 * @param s u8 * - formatted string
111 * @param args va_list * - u16 flags
113 * @return formatted output string u8 *
116 format_ip6_sr_header_flags (u8 * s, va_list * args)
118 u16 flags = (u16) va_arg (*args, int);
120 int bswap_needed = va_arg (*args, int);
124 flags = clib_host_to_net_u16 (flags);
126 if (flags & IP6_SR_HEADER_FLAG_CLEANUP)
127 s = format (s, "cleanup ");
129 if (flags & IP6_SR_HEADER_FLAG_PROTECTED)
130 s = format (s, "reroute ");
132 s = format (s, "pl: ");
133 for (i = 1; i <= 4; i++)
135 pl_flag = ip6_sr_policy_list_flags (flags, i);
136 s = format (s, "[%d] ", i);
140 case IP6_SR_HEADER_FLAG_PL_ELT_NOT_PRESENT:
141 s = format (s, "NotPr ");
143 case IP6_SR_HEADER_FLAG_PL_ELT_INGRESS_PE:
144 s = format (s, "InPE ");
146 case IP6_SR_HEADER_FLAG_PL_ELT_EGRESS_PE:
147 s = format (s, "EgPE ");
150 case IP6_SR_HEADER_FLAG_PL_ELT_ORIG_SRC_ADDR:
151 s = format (s, "OrgSrc ");
159 * @brief Format function for decoding ip6_sr_header_t
161 * @param s u8 * - formatted string
162 * @param args va_list * - ip6_sr_header_t
164 * @return formatted output string u8 *
167 format_ip6_sr_header (u8 * s, va_list * args)
169 ip6_sr_header_t *h = va_arg (*args, ip6_sr_header_t *);
170 ip6_address_t placeholder_addr =
171 { {254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
174 int print_hmac = va_arg (*args, int);
175 int i, pl_index, max_segs;
176 int flags_host_byte_order = clib_net_to_host_u16 (h->flags);
178 s = format (s, "next proto %d, len %d, type %d",
179 h->protocol, (h->length << 3) + 8, h->type);
180 s = format (s, "\n segs left %d, first_segment %d, hmac key %d",
181 h->segments_left, h->first_segment, h->hmac_key);
182 s = format (s, "\n flags %U", format_ip6_sr_header_flags,
183 flags_host_byte_order, 0 /* bswap needed */ );
186 * Header length is in 8-byte units (minus one), so
187 * divide by 2 to ascertain the number of ip6 addresses in the
190 max_segs = (h->length >> 1);
192 if (!print_hmac && h->hmac_key)
195 s = format (s, "\n Segments (in processing order):");
197 for (i = h->first_segment; i >= 1; i--)
198 s = format (s, "\n %U", format_ip6_address, h->segments + i);
199 if (ip6_address_is_equal (&placeholder_addr, h->segments))
200 s = format (s, "\n (empty placeholder)");
202 s = format (s, "\n %U", format_ip6_address, h->segments);
204 s = format (s, "\n Policy List:");
206 pl_index = 1; /* to match the RFC text */
207 for (i = (h->first_segment + 1); i < max_segs; i++, pl_index++)
210 char *tags[] = { " ", "InPE: ", "EgPE: ", "OrgSrc: " };
213 if (pl_index >= 1 && pl_index <= 4)
215 int this_pl_flag = ip6_sr_policy_list_flags
216 (flags_host_byte_order, pl_index);
217 tag = tags[this_pl_flag];
220 s = format (s, "\n %s%U", tag, format_ip6_address, h->segments + i);
227 * @brief Format function for decoding ip6_sr_header_t with length
229 * @param s u8 * - formatted string
230 * @param args va_list * - ip6_header_t + ip6_sr_header_t
232 * @return formatted output string u8 *
235 format_ip6_sr_header_with_length (u8 * s, va_list * args)
237 ip6_header_t *h = va_arg (*args, ip6_header_t *);
238 u32 max_header_bytes = va_arg (*args, u32);
241 header_bytes = sizeof (h[0]) + sizeof (ip6_sr_header_t);
242 if (max_header_bytes != 0 && header_bytes > max_header_bytes)
243 return format (s, "ip6_sr header truncated");
245 s = format (s, "IP6: %U\n", format_ip6_header, h, max_header_bytes);
247 format (s, "SR: %U\n", format_ip6_sr_header, (ip6_sr_header_t *) (h + 1),
248 0 /* print_hmac */ , max_header_bytes);
253 * @brief Defined valid next nodes
254 * @note Cannot call replicate yet without DPDK
257 #define foreach_sr_rewrite_next \
258 _(ERROR, "error-drop") \
259 _(IP6_LOOKUP, "ip6-lookup") \
260 _(SR_LOCAL, "sr-local") \
261 _(SR_REPLICATE,"sr-replicate")
263 #define foreach_sr_rewrite_next \
264 _(ERROR, "error-drop") \
265 _(IP6_LOOKUP, "ip6-lookup") \
266 _(SR_LOCAL, "sr-local")
270 * @brief Struct for defined valid next nodes
274 #define _(s,n) SR_REWRITE_NEXT_##s,
275 foreach_sr_rewrite_next
281 * @brief Struct for data for SR rewrite packet trace
285 ip6_address_t src, dst;
290 } sr_rewrite_trace_t;
293 * @brief Error strings for SR rewrite
295 static char *sr_rewrite_error_strings[] = {
296 #define sr_error(n,s) s,
297 #include "sr_error.def"
302 * @brief Struct for SR rewrite error strings
306 #define sr_error(n,s) SR_REWRITE_ERROR_##n,
307 #include "sr_error.def"
310 } sr_rewrite_error_t;
314 * @brief Format function for SR rewrite trace.
317 format_sr_rewrite_trace (u8 * s, va_list * args)
319 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
320 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
321 sr_rewrite_trace_t *t = va_arg (*args, sr_rewrite_trace_t *);
322 ip6_main_t *im = &ip6_main;
323 ip6_sr_main_t *sm = &sr_main;
324 ip6_sr_tunnel_t *tun = pool_elt_at_index (sm->tunnels, t->tunnel_index);
325 ip6_fib_t *rx_fib, *tx_fib;
327 rx_fib = find_ip6_fib_by_table_index_or_id (im, tun->rx_fib_index,
328 IP6_ROUTE_FLAG_FIB_INDEX);
330 tx_fib = find_ip6_fib_by_table_index_or_id (im, tun->tx_fib_index,
331 IP6_ROUTE_FLAG_FIB_INDEX);
334 (s, "SR-REWRITE: next %s ip6 src %U dst %U len %u\n"
335 " rx-fib-id %d tx-fib-id %d\n%U",
336 (t->next_index == SR_REWRITE_NEXT_SR_LOCAL)
337 ? "sr-local" : "ip6-lookup",
338 format_ip6_address, &t->src,
339 format_ip6_address, &t->dst, t->length,
340 rx_fib->table_id, tx_fib->table_id,
341 format_ip6_sr_header, t->sr, 0 /* print_hmac */ );
346 * @brief Main processing dual-loop for Segment Routing Rewrite
349 * @param vm vlib_main_t *
350 * @param node vlib_node_runtime_t *
351 * @param from_frame vlib_frame_t *
353 * @return from_frame->n_vectors uword
356 sr_rewrite (vlib_main_t * vm,
357 vlib_node_runtime_t * node, vlib_frame_t * from_frame)
359 u32 n_left_from, next_index, *from, *to_next;
360 ip6_main_t *im = &ip6_main;
361 ip_lookup_main_t *lm = &im->lookup_main;
362 ip6_sr_main_t *sm = &sr_main;
363 u32 (*sr_local_cb) (vlib_main_t *, vlib_node_runtime_t *,
364 vlib_buffer_t *, ip6_header_t *, ip6_sr_header_t *);
365 sr_local_cb = sm->sr_local_cb;
367 from = vlib_frame_vector_args (from_frame);
368 n_left_from = from_frame->n_vectors;
370 next_index = node->cached_next_index;
372 while (n_left_from > 0)
376 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
378 /* Note 2x loop disabled */
379 while (0 && n_left_from >= 4 && n_left_to_next >= 2)
382 vlib_buffer_t *b0, *b1;
383 ip6_header_t *ip0, *ip1;
384 ip_adjacency_t *adj0, *adj1;
385 ip6_sr_header_t *sr0, *sr1;
386 ip6_sr_tunnel_t *t0, *t1;
387 u32 next0 = SR_REWRITE_NEXT_IP6_LOOKUP;
388 u32 next1 = SR_REWRITE_NEXT_IP6_LOOKUP;
392 /* Prefetch next iteration. */
394 vlib_buffer_t *p2, *p3;
396 p2 = vlib_get_buffer (vm, from[2]);
397 p3 = vlib_get_buffer (vm, from[3]);
399 vlib_prefetch_buffer_header (p2, LOAD);
400 vlib_prefetch_buffer_header (p3, LOAD);
412 b0 = vlib_get_buffer (vm, bi0);
413 b1 = vlib_get_buffer (vm, bi1);
416 * $$$ parse through header(s) to pick the point
417 * where we punch in the SR extention header
421 ip_get_adjacency (lm, vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
423 ip_get_adjacency (lm, vnet_buffer (b1)->ip.adj_index[VLIB_TX]);
425 pool_elt_at_index (sm->tunnels, adj0->rewrite_header.sw_if_index);
427 pool_elt_at_index (sm->tunnels, adj1->rewrite_header.sw_if_index);
429 ASSERT (VLIB_BUFFER_PRE_DATA_SIZE
430 >= ((word) vec_len (t0->rewrite)) + b0->current_data);
431 ASSERT (VLIB_BUFFER_PRE_DATA_SIZE
432 >= ((word) vec_len (t1->rewrite)) + b1->current_data);
434 vnet_buffer (b0)->sw_if_index[VLIB_TX] = t0->tx_fib_index;
435 vnet_buffer (b1)->sw_if_index[VLIB_TX] = t1->tx_fib_index;
437 ip0 = vlib_buffer_get_current (b0);
438 ip1 = vlib_buffer_get_current (b1);
441 * SR-unaware service chaining case: pkt coming back from
442 * service has the original dst address, and will already
443 * have an SR header. If so, send it to sr-local
445 if (PREDICT_FALSE (ip0->protocol == IPPROTO_IPV6_ROUTE))
447 vlib_buffer_advance (b0, sizeof (ip0));
448 sr0 = (ip6_sr_header_t *) (ip0 + 1);
449 new_l0 = clib_net_to_host_u16 (ip0->payload_length);
450 next0 = SR_REWRITE_NEXT_SR_LOCAL;
455 * Copy data before the punch-in point left by the
456 * required amount. Assume (for the moment) that only
457 * the main packet header needs to be copied.
459 clib_memcpy (((u8 *) ip0) - vec_len (t0->rewrite),
460 ip0, sizeof (ip6_header_t));
461 vlib_buffer_advance (b0, -(word) vec_len (t0->rewrite));
462 ip0 = vlib_buffer_get_current (b0);
463 sr0 = (ip6_sr_header_t *) (ip0 + 1);
465 clib_memcpy (sr0, t0->rewrite, vec_len (t0->rewrite));
467 /* Fix the next header chain */
468 sr0->protocol = ip0->protocol;
469 ip0->protocol = IPPROTO_IPV6_ROUTE; /* routing extension header */
470 new_l0 = clib_net_to_host_u16 (ip0->payload_length) +
471 vec_len (t0->rewrite);
472 ip0->payload_length = clib_host_to_net_u16 (new_l0);
474 /* Copy dst address into the DA slot in the segment list */
475 clib_memcpy (sr0->segments, ip0->dst_address.as_u64,
476 sizeof (ip6_address_t));
477 /* Rewrite the ip6 dst address with the first hop */
478 clib_memcpy (ip0->dst_address.as_u64, t0->first_hop.as_u64,
479 sizeof (ip6_address_t));
481 sr_fix_hmac (sm, ip0, sr0);
483 next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) :
487 * Ignore "do not rewrite" shtik in this path
489 if (PREDICT_FALSE (next0 & 0x80000000))
492 if (PREDICT_FALSE (next0 == SR_REWRITE_NEXT_ERROR))
493 b0->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK];
497 if (PREDICT_FALSE (ip1->protocol == IPPROTO_IPV6_ROUTE))
499 vlib_buffer_advance (b1, sizeof (ip1));
500 sr1 = (ip6_sr_header_t *) (ip1 + 1);
501 new_l1 = clib_net_to_host_u16 (ip1->payload_length);
502 next1 = SR_REWRITE_NEXT_SR_LOCAL;
506 clib_memcpy (((u8 *) ip0) - vec_len (t0->rewrite),
507 ip0, sizeof (ip6_header_t));
508 vlib_buffer_advance (b1, -(word) vec_len (t1->rewrite));
509 ip1 = vlib_buffer_get_current (b1);
510 sr1 = (ip6_sr_header_t *) (ip1 + 1);
511 clib_memcpy (sr1, t1->rewrite, vec_len (t1->rewrite));
513 sr1->protocol = ip1->protocol;
514 ip1->protocol = IPPROTO_IPV6_ROUTE;
515 new_l1 = clib_net_to_host_u16 (ip1->payload_length) +
516 vec_len (t1->rewrite);
517 ip1->payload_length = clib_host_to_net_u16 (new_l1);
519 /* Copy dst address into the DA slot in the segment list */
520 clib_memcpy (sr1->segments, ip1->dst_address.as_u64,
521 sizeof (ip6_address_t));
522 /* Rewrite the ip6 dst address with the first hop */
523 clib_memcpy (ip1->dst_address.as_u64, t1->first_hop.as_u64,
524 sizeof (ip6_address_t));
526 sr_fix_hmac (sm, ip1, sr1);
528 next1 = sr_local_cb ? sr_local_cb (vm, node, b1, ip1, sr1) :
532 * Ignore "do not rewrite" shtik in this path
534 if (PREDICT_FALSE (next1 & 0x80000000))
537 if (PREDICT_FALSE (next1 == SR_REWRITE_NEXT_ERROR))
538 b1->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK];
542 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
544 sr_rewrite_trace_t *tr = vlib_add_trace (vm, node,
546 tr->tunnel_index = t0 - sm->tunnels;
547 clib_memcpy (tr->src.as_u8, ip0->src_address.as_u8,
548 sizeof (tr->src.as_u8));
549 clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
550 sizeof (tr->dst.as_u8));
552 tr->next_index = next0;
553 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
555 if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
557 sr_rewrite_trace_t *tr = vlib_add_trace (vm, node,
559 tr->tunnel_index = t1 - sm->tunnels;
560 clib_memcpy (tr->src.as_u8, ip1->src_address.as_u8,
561 sizeof (tr->src.as_u8));
562 clib_memcpy (tr->dst.as_u8, ip1->dst_address.as_u8,
563 sizeof (tr->dst.as_u8));
565 tr->next_index = next1;
566 clib_memcpy (tr->sr, sr1, sizeof (tr->sr));
569 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
570 to_next, n_left_to_next,
571 bi0, bi1, next0, next1);
574 while (n_left_from > 0 && n_left_to_next > 0)
578 ip6_header_t *ip0 = 0;
579 ip_adjacency_t *adj0;
580 ip6_sr_header_t *sr0 = 0;
582 u32 next0 = SR_REWRITE_NEXT_IP6_LOOKUP;
592 b0 = vlib_get_buffer (vm, bi0);
595 * $$$ parse through header(s) to pick the point
596 * where we punch in the SR extention header
600 ip_get_adjacency (lm, vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
602 pool_elt_at_index (sm->tunnels, adj0->rewrite_header.sw_if_index);
604 #if DPDK > 0 /* Cannot call replication node yet without DPDK */
605 /* add a replication node */
606 if (PREDICT_FALSE (t0->policy_index != ~0))
608 vnet_buffer (b0)->ip.save_protocol = t0->policy_index;
609 next0 = SR_REWRITE_NEXT_SR_REPLICATE;
614 ASSERT (VLIB_BUFFER_PRE_DATA_SIZE
615 >= ((word) vec_len (t0->rewrite)) + b0->current_data);
617 vnet_buffer (b0)->sw_if_index[VLIB_TX] = t0->tx_fib_index;
619 ip0 = vlib_buffer_get_current (b0);
622 * SR-unaware service chaining case: pkt coming back from
623 * service has the original dst address, and will already
624 * have an SR header. If so, send it to sr-local
626 if (PREDICT_FALSE (ip0->protocol == IPPROTO_IPV6_ROUTE))
628 vlib_buffer_advance (b0, sizeof (ip0));
629 sr0 = (ip6_sr_header_t *) (ip0 + 1);
630 new_l0 = clib_net_to_host_u16 (ip0->payload_length);
631 next0 = SR_REWRITE_NEXT_SR_LOCAL;
636 * Copy data before the punch-in point left by the
637 * required amount. Assume (for the moment) that only
638 * the main packet header needs to be copied.
640 clib_memcpy (((u8 *) ip0) - vec_len (t0->rewrite),
641 ip0, sizeof (ip6_header_t));
642 vlib_buffer_advance (b0, -(word) vec_len (t0->rewrite));
643 ip0 = vlib_buffer_get_current (b0);
644 sr0 = (ip6_sr_header_t *) (ip0 + 1);
646 clib_memcpy (sr0, t0->rewrite, vec_len (t0->rewrite));
648 /* Fix the next header chain */
649 sr0->protocol = ip0->protocol;
650 ip0->protocol = IPPROTO_IPV6_ROUTE; /* routing extension header */
651 new_l0 = clib_net_to_host_u16 (ip0->payload_length) +
652 vec_len (t0->rewrite);
653 ip0->payload_length = clib_host_to_net_u16 (new_l0);
655 /* Copy dst address into the DA slot in the segment list */
656 clib_memcpy (sr0->segments, ip0->dst_address.as_u64,
657 sizeof (ip6_address_t));
658 /* Rewrite the ip6 dst address with the first hop */
659 clib_memcpy (ip0->dst_address.as_u64, t0->first_hop.as_u64,
660 sizeof (ip6_address_t));
662 sr_fix_hmac (sm, ip0, sr0);
664 next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) :
668 * Ignore "do not rewrite" shtik in this path
670 if (PREDICT_FALSE (next0 & 0x80000000))
673 if (PREDICT_FALSE (next0 == SR_REWRITE_NEXT_ERROR))
674 b0->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK];
678 #if DPDK > 0 /* Cannot run replicate without DPDK and only replicate uses this label */
681 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
683 sr_rewrite_trace_t *tr = vlib_add_trace (vm, node,
685 tr->tunnel_index = t0 - sm->tunnels;
688 memcpy (tr->src.as_u8, ip0->src_address.as_u8,
689 sizeof (tr->src.as_u8));
690 memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
691 sizeof (tr->dst.as_u8));
694 tr->next_index = next0;
695 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
698 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
699 to_next, n_left_to_next,
703 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
705 return from_frame->n_vectors;
709 VLIB_REGISTER_NODE (sr_rewrite_node) = {
710 .function = sr_rewrite,
711 .name = "sr-rewrite",
712 /* Takes a vector of packets. */
713 .vector_size = sizeof (u32),
714 .format_trace = format_sr_rewrite_trace,
715 .format_buffer = format_ip6_sr_header_with_length,
717 .n_errors = SR_REWRITE_N_ERROR,
718 .error_strings = sr_rewrite_error_strings,
720 .runtime_data_bytes = 0,
722 .n_next_nodes = SR_REWRITE_N_NEXT,
724 #define _(s,n) [SR_REWRITE_NEXT_##s] = n,
725 foreach_sr_rewrite_next
731 VLIB_NODE_FUNCTION_MULTIARCH (sr_rewrite_node, sr_rewrite)
732 static int ip6_delete_route_no_next_hop (ip6_address_t * dst_address_arg,
733 u32 dst_address_length,
736 ip6_add_del_route_args_t a;
737 ip6_address_t dst_address;
739 ip6_main_t *im6 = &ip6_main;
740 BVT (clib_bihash_kv) kv, value;
742 fib = find_ip6_fib_by_table_index_or_id (im6, rx_table_id,
743 IP6_ROUTE_FLAG_TABLE_ID);
744 memset (&a, 0, sizeof (a));
745 a.flags |= IP4_ROUTE_FLAG_DEL;
746 a.dst_address_length = dst_address_length;
748 dst_address = *dst_address_arg;
750 ip6_address_mask (&dst_address, &im6->fib_masks[dst_address_length]);
752 kv.key[0] = dst_address.as_u64[0];
753 kv.key[1] = dst_address.as_u64[1];
754 kv.key[2] = ((u64) ((fib - im6->fibs)) << 32) | dst_address_length;
756 if (BV (clib_bihash_search) (&im6->ip6_lookup_table, &kv, &value) < 0)
758 clib_warning ("%U/%d not in FIB",
759 format_ip6_address, &a.dst_address, a.dst_address_length);
763 a.adj_index = value.value;
764 a.dst_address = dst_address;
766 ip6_add_del_route (im6, &a);
767 ip6_maybe_remap_adjacencies (im6, rx_table_id, IP6_ROUTE_FLAG_TABLE_ID);
772 * @brief Find or add if not found - HMAC shared secret
774 * @param sm ip6_sr_main_t *
776 * @param indexp u32 *
778 * @return ip6_sr_hmac_key_t *
780 static ip6_sr_hmac_key_t *
781 find_or_add_shared_secret (ip6_sr_main_t * sm, u8 * secret, u32 * indexp)
784 ip6_sr_hmac_key_t *key = 0;
787 p = hash_get_mem (sm->hmac_key_by_shared_secret, secret);
791 key = vec_elt_at_index (sm->hmac_keys, p[0]);
797 /* Specific key ID? */
798 if (indexp && *indexp)
800 vec_validate (sm->hmac_keys, *indexp);
801 key = sm->hmac_keys + *indexp;
805 for (i = 0; i < vec_len (sm->hmac_keys); i++)
807 if (sm->hmac_keys[i].shared_secret == 0)
809 key = sm->hmac_keys + i;
813 vec_validate (sm->hmac_keys, i);
814 key = sm->hmac_keys + i;
819 key->shared_secret = vec_dup (secret);
821 hash_set_mem (sm->hmac_key_by_shared_secret, key->shared_secret,
822 key - sm->hmac_keys);
825 *indexp = key - sm->hmac_keys;
830 * @brief Add or Delete a Segment Routing tunnel.
832 * @param a ip6_sr_add_del_tunnel_args_t *
837 ip6_sr_add_del_tunnel (ip6_sr_add_del_tunnel_args_t * a)
839 ip6_main_t *im = &ip6_main;
840 ip_lookup_main_t *lm = &im->lookup_main;
841 ip6_sr_tunnel_key_t key;
844 ip6_sr_header_t *h = 0;
846 ip6_address_t *addrp, *this_address;
847 ip_adjacency_t adj, *ap, *add_adj = 0;
849 ip6_sr_main_t *sm = &sr_main;
851 u32 rx_fib_index, tx_fib_index;
852 ip6_add_del_route_args_t aa;
853 u32 hmac_key_index_u32;
854 u8 hmac_key_index = 0;
858 /* Make sure that the rx FIB exists */
859 p = hash_get (im->fib_index_by_table_id, a->rx_table_id);
864 /* remember the FIB index */
867 /* Make sure that the supplied FIB exists */
868 p = hash_get (im->fib_index_by_table_id, a->tx_table_id);
873 /* remember the FIB index */
876 clib_memcpy (key.src.as_u8, a->src_address->as_u8, sizeof (key.src));
877 clib_memcpy (key.dst.as_u8, a->dst_address->as_u8, sizeof (key.dst));
879 /* When adding a tunnel:
880 * - If a "name" is given, it must not exist.
881 * - The "key" is always checked, and must not exist.
882 * When deleting a tunnel:
883 * - If the "name" is given, and it exists, then use it.
884 * - If the "name" is not given, use the "key".
885 * - If the "name" and the "key" are given, then both must point to the same
890 p = hash_get_mem (sm->tunnel_index_by_key, &key);
892 /* If the name is given, look it up */
894 n = hash_get_mem (sm->tunnel_index_by_name, a->name);
898 /* validate key/name parameters */
899 if (!a->is_del) /* adding a tunnel */
901 if (a->name && n) /* name given & exists already */
903 if (p) /* key exists already */
906 else /* deleting a tunnel */
908 if (!p) /* key doesn't exist */
910 if (a->name && !n) /* name given & it doesn't exist */
913 if (n) /* name given & found */
915 if (n[0] != p[0]) /* name and key do not point to the same thing */
921 if (a->is_del) /* delete the tunnel */
925 /* Delete existing tunnel */
926 t = pool_elt_at_index (sm->tunnels, p[0]);
928 ip6_delete_route_no_next_hop (&t->key.dst, t->dst_mask_width,
930 vec_free (t->rewrite);
931 /* Remove tunnel from any policy if associated */
932 if (t->policy_index != ~0)
934 pt = pool_elt_at_index (sm->policies, t->policy_index);
935 for (i = 0; i < vec_len (pt->tunnel_indices); i++)
937 if (pt->tunnel_indices[i] == t - sm->tunnels)
939 vec_delete (pt->tunnel_indices, 1, i);
943 clib_warning ("Tunnel index %d not found in policy_index %d",
944 t - sm->tunnels, pt - sm->policies);
946 /* If this is last tunnel in the policy, clean up the policy too */
947 if (vec_len (pt->tunnel_indices) == 0)
949 hash_unset_mem (sm->policy_index_by_policy_name, pt->name);
951 pool_put (sm->policies, pt);
955 /* Clean up the tunnel by name */
958 hash_unset_mem (sm->tunnel_index_by_name, t->name);
961 pool_put (sm->tunnels, t);
962 hp = hash_get_pair (sm->tunnel_index_by_key, &key);
963 key_copy = (void *) (hp->key);
964 hash_unset_mem (sm->tunnel_index_by_key, &key);
969 /* create a new tunnel */
970 pool_get (sm->tunnels, t);
971 memset (t, 0, sizeof (*t));
972 t->policy_index = ~0;
974 clib_memcpy (&t->key, &key, sizeof (t->key));
975 t->dst_mask_width = a->dst_mask_width;
976 t->rx_fib_index = rx_fib_index;
977 t->tx_fib_index = tx_fib_index;
979 if (!vec_len (a->segments))
980 /* there must be at least one segment... */
983 /* The first specified hop goes right into the dst address */
984 clib_memcpy (&t->first_hop, &a->segments[0], sizeof (ip6_address_t));
987 * Create the sr header rewrite string
988 * The list of segments needs an extra slot for the ultimate destination
989 * which is taken from the packet we add the SRH to.
991 header_length = sizeof (*h) +
992 sizeof (ip6_address_t) * (vec_len (a->segments) + 1 + vec_len (a->tags));
994 if (a->shared_secret)
996 /* Allocate a new key slot if we don't find the secret key */
997 hmac_key_index_u32 = 0;
998 (void) find_or_add_shared_secret (sm, a->shared_secret,
999 &hmac_key_index_u32);
1001 /* Hey Vinz Clortho: Gozzer is pissed.. you're out of keys! */
1002 if (hmac_key_index_u32 >= 256)
1004 hmac_key_index = hmac_key_index_u32;
1005 header_length += SHA256_DIGEST_LENGTH;
1008 vec_validate (t->rewrite, header_length - 1);
1010 h = (ip6_sr_header_t *) t->rewrite;
1012 h->protocol = 0xFF; /* we don't know yet */
1014 h->length = (header_length / 8) - 1;
1015 h->type = ROUTING_HEADER_TYPE_SR;
1017 /* first_segment and segments_left need to have the index of the last
1018 * element in the list; a->segments has one element less than ends up
1019 * in the header (it does not have the DA in it), so vec_len(a->segments)
1020 * is the value we want.
1022 h->first_segment = h->segments_left = vec_len (a->segments);
1024 if (a->shared_secret)
1025 h->hmac_key = hmac_key_index & 0xFF;
1027 h->flags = a->flags_net_byte_order;
1029 /* Paint on the segment list, in reverse.
1030 * This is offset by one to leave room at the start for the ultimate
1033 addrp = h->segments + vec_len (a->segments);
1035 vec_foreach (this_address, a->segments)
1037 clib_memcpy (addrp->as_u8, this_address->as_u8, sizeof (ip6_address_t));
1042 * Since the ultimate destination address is not yet known, set that slot
1043 * to a value we will instantly recognize as bogus.
1045 memset (h->segments, 0xfe, sizeof (ip6_address_t));
1047 /* Paint on the tag list, not reversed */
1048 addrp = h->segments + vec_len (a->segments);
1050 vec_foreach (this_address, a->tags)
1052 clib_memcpy (addrp->as_u8, this_address->as_u8, sizeof (ip6_address_t));
1056 key_copy = vec_new (ip6_sr_tunnel_key_t, 1);
1057 clib_memcpy (key_copy, &key, sizeof (ip6_sr_tunnel_key_t));
1058 hash_set_mem (sm->tunnel_index_by_key, key_copy, t - sm->tunnels);
1060 memset (&adj, 0, sizeof (adj));
1062 /* Create an adjacency and add to v6 fib */
1063 adj.lookup_next_index = sm->ip6_lookup_sr_next_index;
1064 adj.explicit_fib_index = ~0;
1066 ap = ip_add_adjacency (lm, &adj, 1 /* one adj */ ,
1070 * Stick the tunnel index into the rewrite header.
1072 * Unfortunately, inserting an SR header according to the various
1073 * RFC's requires parsing through the ip6 header, perhaps consing a
1074 * buffer onto the head of the vlib_buffer_t, etc. We don't use the
1075 * normal reverse bcopy rewrite code.
1077 * We don't handle ugly RFC-related cases yet, but I'm sure PL will complain
1080 ap->rewrite_header.sw_if_index = t - sm->tunnels;
1082 vec_add1 (add_adj, ap[0]);
1084 clib_memcpy (aa.dst_address.as_u8, a->dst_address,
1085 sizeof (aa.dst_address.as_u8));
1086 aa.dst_address_length = a->dst_mask_width;
1088 aa.flags = (a->is_del ? IP6_ROUTE_FLAG_DEL : IP6_ROUTE_FLAG_ADD);
1089 aa.flags |= IP6_ROUTE_FLAG_FIB_INDEX;
1090 aa.table_index_or_table_id = rx_fib_index;
1091 aa.add_adj = add_adj;
1092 aa.adj_index = adj_index;
1094 ip6_add_del_route (im, &aa);
1099 p = hash_get_mem (sm->policy_index_by_policy_name, a->policy_name);
1102 pt = pool_elt_at_index (sm->policies, p[0]);
1104 else /* no policy, lets create one */
1106 pool_get (sm->policies, pt);
1107 memset (pt, 0, sizeof (*pt));
1108 pt->name = format (0, "%s%c", a->policy_name, 0);
1109 hash_set_mem (sm->policy_index_by_policy_name, pt->name,
1111 p = hash_get_mem (sm->policy_index_by_policy_name, a->policy_name);
1113 vec_add1 (pt->tunnel_indices, t - sm->tunnels);
1115 clib_warning ("p is NULL!");
1116 t->policy_index = p ? p[0] : ~0; /* equiv. to (pt - sm->policies) */
1121 t->name = format (0, "%s%c", a->name, 0);
1122 hash_set_mem (sm->tunnel_index_by_name, t->name, t - sm->tunnels);
1129 * @brief CLI parser for Add or Delete a Segment Routing tunnel.
1131 * @param vm vlib_main_t *
1132 * @param input unformat_input_t *
1133 * @param cmd vlib_cli_command_t *
1135 * @return error clib_error_t *
1137 static clib_error_t *
1138 sr_add_del_tunnel_command_fn (vlib_main_t * vm,
1139 unformat_input_t * input,
1140 vlib_cli_command_t * cmd)
1143 ip6_address_t src_address;
1144 int src_address_set = 0;
1145 ip6_address_t dst_address;
1147 int dst_address_set = 0;
1149 u8 *shared_secret = 0;
1151 u8 *policy_name = 0;
1152 u32 rx_table_id = 0;
1153 u32 tx_table_id = 0;
1154 ip6_address_t *segments = 0;
1155 ip6_address_t *this_seg;
1156 ip6_address_t *tags = 0;
1157 ip6_address_t *this_tag;
1158 ip6_sr_add_del_tunnel_args_t _a, *a = &_a;
1159 ip6_address_t next_address, tag;
1163 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1165 if (unformat (input, "del"))
1167 else if (unformat (input, "rx-fib-id %d", &rx_table_id))
1169 else if (unformat (input, "tx-fib-id %d", &tx_table_id))
1171 else if (unformat (input, "src %U", unformat_ip6_address, &src_address))
1172 src_address_set = 1;
1173 else if (unformat (input, "name %s", &name))
1175 else if (unformat (input, "policy %s", &policy_name))
1177 else if (unformat (input, "dst %U/%d",
1178 unformat_ip6_address, &dst_address, &dst_mask_width))
1179 dst_address_set = 1;
1180 else if (unformat (input, "next %U", unformat_ip6_address,
1183 vec_add2 (segments, this_seg, 1);
1184 clib_memcpy (this_seg->as_u8, next_address.as_u8,
1185 sizeof (*this_seg));
1187 else if (unformat (input, "tag %U", unformat_ip6_address, &tag))
1189 vec_add2 (tags, this_tag, 1);
1190 clib_memcpy (this_tag->as_u8, tag.as_u8, sizeof (*this_tag));
1192 else if (unformat (input, "clean"))
1193 flags |= IP6_SR_HEADER_FLAG_CLEANUP;
1194 else if (unformat (input, "protected"))
1195 flags |= IP6_SR_HEADER_FLAG_PROTECTED;
1196 else if (unformat (input, "key %s", &shared_secret))
1197 /* Do not include the trailing NULL byte. Guaranteed interop issue */
1198 _vec_len (shared_secret) -= 1;
1199 else if (unformat (input, "InPE %d", &pl_index))
1201 if (pl_index <= 0 || pl_index > 4)
1203 pl_index_range_error:
1204 return clib_error_return
1205 (0, "Policy List Element Index %d out of range (1-4)",
1209 flags |= IP6_SR_HEADER_FLAG_PL_ELT_INGRESS_PE
1210 << ip6_sr_policy_list_shift_from_index (pl_index);
1212 else if (unformat (input, "EgPE %d", &pl_index))
1214 if (pl_index <= 0 || pl_index > 4)
1215 goto pl_index_range_error;
1216 flags |= IP6_SR_HEADER_FLAG_PL_ELT_EGRESS_PE
1217 << ip6_sr_policy_list_shift_from_index (pl_index);
1219 else if (unformat (input, "OrgSrc %d", &pl_index))
1221 if (pl_index <= 0 || pl_index > 4)
1222 goto pl_index_range_error;
1223 flags |= IP6_SR_HEADER_FLAG_PL_ELT_ORIG_SRC_ADDR
1224 << ip6_sr_policy_list_shift_from_index (pl_index);
1230 if (!src_address_set)
1231 return clib_error_return (0, "src address required");
1233 if (!dst_address_set)
1234 return clib_error_return (0, "dst address required");
1237 return clib_error_return (0, "at least one sr segment required");
1239 memset (a, 0, sizeof (*a));
1240 a->src_address = &src_address;
1241 a->dst_address = &dst_address;
1242 a->dst_mask_width = dst_mask_width;
1243 a->segments = segments;
1245 a->flags_net_byte_order = clib_host_to_net_u16 (flags);
1247 a->rx_table_id = rx_table_id;
1248 a->tx_table_id = tx_table_id;
1249 a->shared_secret = shared_secret;
1256 if (vec_len (policy_name))
1257 a->policy_name = policy_name;
1261 rv = ip6_sr_add_del_tunnel (a);
1263 vec_free (segments);
1265 vec_free (shared_secret);
1273 return clib_error_return (0, "SR tunnel src %U dst %U already exists",
1274 format_ip6_address, &src_address,
1275 format_ip6_address, &dst_address);
1278 return clib_error_return (0, "SR tunnel src %U dst %U does not exist",
1279 format_ip6_address, &src_address,
1280 format_ip6_address, &dst_address);
1283 return clib_error_return (0, "FIB table %d does not exist",
1287 return clib_error_return (0, "At least one segment is required");
1290 return clib_error_return (0, "BUG: ip6_sr_add_del_tunnel returns %d",
1298 VLIB_CLI_COMMAND (sr_tunnel_command, static) = {
1299 .path = "sr tunnel",
1301 "sr tunnel [del] [name <name>] src <addr> dst <addr> [next <addr>] "
1302 "[clean] [reroute] [key <secret>] [policy <policy_name>]"
1303 "[rx-fib-id <fib_id>] [tx-fib-id <fib_id>]",
1304 .function = sr_add_del_tunnel_command_fn,
1309 * @brief Display Segment Routing tunnel
1311 * @param vm vlib_main_t *
1312 * @param t ip6_sr_tunnel_t *
1316 ip6_sr_tunnel_display (vlib_main_t * vm, ip6_sr_tunnel_t * t)
1318 ip6_main_t *im = &ip6_main;
1319 ip6_sr_main_t *sm = &sr_main;
1320 ip6_fib_t *rx_fib, *tx_fib;
1321 ip6_sr_policy_t *pt;
1323 rx_fib = find_ip6_fib_by_table_index_or_id (im, t->rx_fib_index,
1324 IP6_ROUTE_FLAG_FIB_INDEX);
1326 tx_fib = find_ip6_fib_by_table_index_or_id (im, t->tx_fib_index,
1327 IP6_ROUTE_FLAG_FIB_INDEX);
1330 vlib_cli_output (vm, "sr tunnel name: %s", (char *) t->name);
1332 vlib_cli_output (vm, "src %U dst %U first hop %U",
1333 format_ip6_address, &t->key.src,
1334 format_ip6_address, &t->key.dst,
1335 format_ip6_address, &t->first_hop);
1336 vlib_cli_output (vm, " rx-fib-id %d tx-fib-id %d",
1337 rx_fib->table_id, tx_fib->table_id);
1338 vlib_cli_output (vm, " sr: %U", format_ip6_sr_header, t->rewrite,
1339 0 /* print_hmac */ );
1341 if (t->policy_index != ~0)
1343 pt = pool_elt_at_index (sm->policies, t->policy_index);
1344 vlib_cli_output (vm, "sr policy: %s", (char *) pt->name);
1346 vlib_cli_output (vm, "-------");
1352 * @brief CLI Parser for Display Segment Routing tunnel
1354 * @param vm vlib_main_t *
1355 * @param input unformat_input_t *
1356 * @param cmd vlib_cli_command_t *
1358 * @return error clib_error_t *
1360 static clib_error_t *
1361 show_sr_tunnel_fn (vlib_main_t * vm,
1362 unformat_input_t * input, vlib_cli_command_t * cmd)
1364 static ip6_sr_tunnel_t **tunnels;
1366 ip6_sr_main_t *sm = &sr_main;
1371 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1373 if (unformat (input, "name %s", &name))
1375 p = hash_get_mem (sm->tunnel_index_by_name, name);
1377 vlib_cli_output (vm, "No SR tunnel with name: %s. Showing all.",
1384 vec_reset_length (tunnels);
1386 if (!p) /* Either name parm not passed or no tunnel with that name found, show all */
1389 pool_foreach (t, sm->tunnels,
1391 vec_add1 (tunnels, t);
1395 else /* Just show the one tunnel by name */
1396 vec_add1 (tunnels, &sm->tunnels[p[0]]);
1398 if (vec_len (tunnels) == 0)
1399 vlib_cli_output (vm, "No SR tunnels configured");
1401 for (i = 0; i < vec_len (tunnels); i++)
1404 ip6_sr_tunnel_display (vm, t);
1411 VLIB_CLI_COMMAND (show_sr_tunnel_command, static) = {
1412 .path = "show sr tunnel",
1413 .short_help = "show sr tunnel [name <sr-tunnel-name>]",
1414 .function = show_sr_tunnel_fn,
1419 * @brief Add or Delete a Segment Routing policy
1421 * @param a ip6_sr_add_del_policy_args_t *
1423 * @return retval int
1426 ip6_sr_add_del_policy (ip6_sr_add_del_policy_args_t * a)
1428 ip6_sr_main_t *sm = &sr_main;
1430 ip6_sr_tunnel_t *t = 0;
1431 ip6_sr_policy_t *policy;
1432 u32 *tunnel_indices = 0;
1439 p = hash_get_mem (sm->policy_index_by_policy_name, a->name);
1441 return -6; /* policy name not found */
1443 policy = pool_elt_at_index (sm->policies, p[0]);
1445 vec_foreach_index (i, policy->tunnel_indices)
1447 t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[i]);
1448 t->policy_index = ~0;
1450 hash_unset_mem (sm->policy_index_by_policy_name, a->name);
1451 pool_put (sm->policies, policy);
1456 if (!vec_len (a->tunnel_names))
1457 return -3; /*tunnel name is required case */
1459 vec_reset_length (tunnel_indices);
1460 /* Check tunnel names, add tunnel_index to policy */
1461 for (i = 0; i < vec_len (a->tunnel_names); i++)
1463 p = hash_get_mem (sm->tunnel_index_by_name, a->tunnel_names[i]);
1465 return -4; /* tunnel name not found case */
1467 t = pool_elt_at_index (sm->tunnels, p[0]);
1469 No need to check t==0. -3 condition above ensures name
1471 if (t->policy_index != ~0)
1472 return -5; /* tunnel name already associated with a policy */
1474 /* Add to tunnel indicies */
1475 vec_add1 (tunnel_indices, p[0]);
1478 /* Add policy to ip6_sr_main_t */
1479 pool_get (sm->policies, policy);
1480 policy->name = a->name;
1481 policy->tunnel_indices = tunnel_indices;
1482 hash_set_mem (sm->policy_index_by_policy_name, policy->name,
1483 policy - sm->policies);
1485 /* Yes, this could be construed as overkill but the last thing you should do is set
1486 the policy_index on the tunnel after everything is set in ip6_sr_main_t.
1487 If this is deemed overly cautious, could set this in the vec_len(tunnel_names) loop.
1489 for (i = 0; i < vec_len (policy->tunnel_indices); i++)
1491 t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[i]);
1492 t->policy_index = policy - sm->policies;
1499 * @brief CLI Parser for Add or Delete a Segment Routing policy
1501 * @param vm vlib_main_t *
1502 * @param input unformat_input_t *
1503 * @param cmd vlib_cli_command_t *
1505 * @return error clib_error_t *
1507 static clib_error_t *
1508 sr_add_del_policy_command_fn (vlib_main_t * vm,
1509 unformat_input_t * input,
1510 vlib_cli_command_t * cmd)
1513 u8 **tunnel_names = 0;
1514 u8 *tunnel_name = 0;
1516 ip6_sr_add_del_policy_args_t _a, *a = &_a;
1519 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1521 if (unformat (input, "del"))
1523 else if (unformat (input, "name %s", &name))
1525 else if (unformat (input, "tunnel %s", &tunnel_name))
1529 vec_add1 (tunnel_names, tunnel_name);
1538 return clib_error_return (0, "name of SR policy required");
1541 memset (a, 0, sizeof (*a));
1545 a->tunnel_names = tunnel_names;
1547 rv = ip6_sr_add_del_policy (a);
1549 vec_free (tunnel_names);
1557 return clib_error_return (0,
1558 "tunnel name to associate to SR policy is required");
1561 return clib_error_return (0, "tunnel name not found");
1564 return clib_error_return (0, "tunnel already associated with policy");
1567 return clib_error_return (0, "policy name %s not found", name);
1570 return clib_error_return (0, "TODO: deleting policy name %s", name);
1573 return clib_error_return (0, "BUG: ip6_sr_add_del_policy returns %d",
1581 VLIB_CLI_COMMAND (sr_policy_command, static) = {
1582 .path = "sr policy",
1584 "sr policy [del] name <policy-name> tunnel <sr-tunnel-name> [tunnel <sr-tunnel-name>]*",
1585 .function = sr_add_del_policy_command_fn,
1590 * @brief CLI Parser for Displaying Segment Routing policy
1592 * @param vm vlib_main_t *
1593 * @param input unformat_input_t *
1594 * @param cmd vlib_cli_command_t *
1596 * @return error clib_error_t *
1598 static clib_error_t *
1599 show_sr_policy_fn (vlib_main_t * vm,
1600 unformat_input_t * input, vlib_cli_command_t * cmd)
1602 static ip6_sr_policy_t **policies;
1603 ip6_sr_policy_t *policy;
1605 ip6_sr_main_t *sm = &sr_main;
1610 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1612 if (unformat (input, "name %s", &name))
1614 p = hash_get_mem (sm->policy_index_by_policy_name, name);
1616 vlib_cli_output (vm,
1617 "policy with name %s not found. Showing all.",
1624 vec_reset_length (policies);
1626 if (!p) /* Either name parm not passed or no policy with that name found, show all */
1629 pool_foreach (policy, sm->policies,
1631 vec_add1 (policies, policy);
1635 else /* Just show the one policy by name and a summary of tunnel names */
1637 policy = pool_elt_at_index (sm->policies, p[0]);
1638 vec_add1 (policies, policy);
1641 if (vec_len (policies) == 0)
1642 vlib_cli_output (vm, "No SR policies configured");
1644 for (i = 0; i < vec_len (policies); i++)
1646 policy = policies[i];
1649 vlib_cli_output (vm, "SR policy name: %s", (char *) policy->name);
1650 for (j = 0; j < vec_len (policy->tunnel_indices); j++)
1652 t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[j]);
1653 ip6_sr_tunnel_display (vm, t);
1662 VLIB_CLI_COMMAND (show_sr_policy_command, static) = {
1663 .path = "show sr policy",
1664 .short_help = "show sr policy [name <sr-policy-name>]",
1665 .function = show_sr_policy_fn,
1670 * @brief Add or Delete a mapping of IP6 multicast address
1671 * to Segment Routing policy.
1673 * @param a ip6_sr_add_del_multicastmap_args_t *
1675 * @return retval int
1678 ip6_sr_add_del_multicastmap (ip6_sr_add_del_multicastmap_args_t * a)
1681 ip6_main_t *im = &ip6_main;
1682 ip_lookup_main_t *lm = &im->lookup_main;
1684 ip_adjacency_t adj, *ap, *add_adj = 0;
1686 ip6_sr_main_t *sm = &sr_main;
1687 ip6_add_del_route_args_t aa;
1688 ip6_sr_policy_t *pt;
1692 /* clean up the adjacency */
1694 hash_get_mem (sm->policy_index_by_multicast_address,
1695 a->multicast_address);
1699 /* Get our policy by policy_name */
1700 p = hash_get_mem (sm->policy_index_by_policy_name, a->policy_name);
1706 pt = pool_elt_at_index (sm->policies, p[0]);
1709 Get the first tunnel associated with policy populate the fib adjacency.
1710 From there, since this tunnel will have it's policy_index != ~0 it will
1711 be the trigger in the dual_loop to pull up the policy and make a copy-rewrite
1712 for each tunnel in the policy
1715 t = pool_elt_at_index (sm->tunnels, pt->tunnel_indices[0]);
1717 /* Construct a FIB entry for multicast using the rx/tx fib from the first tunnel */
1718 memset (&adj, 0, sizeof (adj));
1720 /* Create an adjacency and add to v6 fib */
1721 adj.lookup_next_index = sm->ip6_lookup_sr_replicate_index;
1722 adj.explicit_fib_index = ~0;
1724 ap = ip_add_adjacency (lm, &adj, 1 /* one adj */ ,
1728 * Stick the tunnel index into the rewrite header.
1730 * Unfortunately, inserting an SR header according to the various
1731 * RFC's requires parsing through the ip6 header, perhaps consing a
1732 * buffer onto the head of the vlib_buffer_t, etc. We don't use the
1733 * normal reverse bcopy rewrite code.
1735 * We don't handle ugly RFC-related cases yet, but I'm sure PL will complain
1738 ap->rewrite_header.sw_if_index = t - sm->tunnels;
1740 vec_add1 (add_adj, ap[0]);
1742 memcpy (aa.dst_address.as_u8, a->multicast_address,
1743 sizeof (aa.dst_address.as_u8));
1744 aa.dst_address_length = 128;
1746 aa.flags = (a->is_del ? IP6_ROUTE_FLAG_DEL : IP6_ROUTE_FLAG_ADD);
1747 aa.flags |= IP6_ROUTE_FLAG_FIB_INDEX;
1748 aa.table_index_or_table_id = t->rx_fib_index;
1749 aa.add_adj = add_adj;
1750 aa.adj_index = adj_index;
1752 ip6_add_del_route (im, &aa);
1756 mcast_copy = vec_new (ip6_address_t, 1);
1757 memcpy (mcast_copy, a->multicast_address, sizeof (ip6_address_t));
1761 hash_unset_mem (sm->policy_index_by_multicast_address, mcast_copy);
1762 vec_free (mcast_copy);
1767 hash_set_mem (sm->policy_index_by_multicast_address, mcast_copy,
1775 * @brief CLI Parser for Adding or Delete a mapping of IP6 multicast address
1776 * to Segment Routing policy.
1778 * @param vm vlib_main_t *
1779 * @param input unformat_input_t *
1780 * @param cmd vlib_cli_command_t *
1782 * @return error clib_error_t *
1784 static clib_error_t *
1785 sr_add_del_multicast_map_command_fn (vlib_main_t * vm,
1786 unformat_input_t * input,
1787 vlib_cli_command_t * cmd)
1790 ip6_address_t multicast_address;
1791 u8 *policy_name = 0;
1792 int multicast_address_set = 0;
1793 ip6_sr_add_del_multicastmap_args_t _a, *a = &_a;
1796 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1798 if (unformat (input, "del"))
1802 (input, "address %U", unformat_ip6_address, &multicast_address))
1803 multicast_address_set = 1;
1804 else if (unformat (input, "sr-policy %s", &policy_name))
1810 if (!is_del && !policy_name)
1811 return clib_error_return (0, "name of sr policy required");
1813 if (!multicast_address_set)
1814 return clib_error_return (0, "multicast address required");
1816 memset (a, 0, sizeof (*a));
1819 a->multicast_address = &multicast_address;
1820 a->policy_name = policy_name;
1822 #if DPDK > 0 /*Cannot call replicate or configure multicast map yet without DPDK */
1823 rv = ip6_sr_add_del_multicastmap (a);
1825 return clib_error_return (0,
1826 "cannot use multicast replicate spray case without DPDK installed");
1834 return clib_error_return (0, "no policy with name: %s", policy_name);
1837 return clib_error_return (0, "multicast map someting ");
1840 return clib_error_return (0,
1841 "tunnel name to associate to SR policy is required");
1844 return clib_error_return (0, "TODO: deleting policy name %s",
1848 return clib_error_return (0, "BUG: ip6_sr_add_del_policy returns %d",
1858 VLIB_CLI_COMMAND (sr_multicast_map_command, static) = {
1859 .path = "sr multicast-map",
1861 "sr multicast-map address <multicast-ip6-address> sr-policy <sr-policy-name> [del]",
1862 .function = sr_add_del_multicast_map_command_fn,
1867 * @brief CLI Parser for Displaying a mapping of IP6 multicast address
1868 * to Segment Routing policy.
1870 * @param vm vlib_main_t *
1871 * @param input unformat_input_t *
1872 * @param cmd vlib_cli_command_t *
1874 * @return error clib_error_t *
1876 static clib_error_t *
1877 show_sr_multicast_map_fn (vlib_main_t * vm,
1878 unformat_input_t * input, vlib_cli_command_t * cmd)
1880 ip6_sr_main_t *sm = &sr_main;
1883 ip6_address_t multicast_address;
1884 ip6_sr_policy_t *pt;
1886 /* pull all entries from the hash table into vector for display */
1889 hash_foreach_mem (key, value, sm->policy_index_by_multicast_address,
1892 vlib_cli_output (vm, "no multicast maps configured");
1895 multicast_address = *((ip6_address_t *)key);
1896 pt = pool_elt_at_index (sm->policies, value);
1899 vlib_cli_output (vm, "address: %U policy: %s",
1900 format_ip6_address, &multicast_address,
1904 vlib_cli_output (vm, "BUG: policy not found for address: %U with policy index %d",
1905 format_ip6_address, &multicast_address,
1917 VLIB_CLI_COMMAND (show_sr_multicast_map_command, static) = {
1918 .path = "show sr multicast-map",
1919 .short_help = "show sr multicast-map",
1920 .function = show_sr_multicast_map_fn,
1925 #define foreach_sr_fix_dst_addr_next \
1926 _(DROP, "error-drop")
1929 * @brief Struct for valid next-nodes for SR fix destination address node
1933 #define _(s,n) SR_FIX_DST_ADDR_NEXT_##s,
1934 foreach_sr_fix_dst_addr_next
1936 SR_FIX_DST_ADDR_N_NEXT,
1937 } sr_fix_dst_addr_next_t;
1940 * @brief Error strings for SR Fix Destination rewrite
1942 static char *sr_fix_dst_error_strings[] = {
1943 #define sr_fix_dst_error(n,s) s,
1944 #include "sr_fix_dst_error.def"
1945 #undef sr_fix_dst_error
1949 * @brief Struct for errors for SR Fix Destination rewrite
1953 #define sr_fix_dst_error(n,s) SR_FIX_DST_ERROR_##n,
1954 #include "sr_fix_dst_error.def"
1955 #undef sr_fix_dst_error
1957 } sr_fix_dst_error_t;
1960 * @brief Information for fix address trace
1964 ip6_address_t src, dst;
1968 } sr_fix_addr_trace_t;
1971 * @brief Formatter for fix address trace
1974 format_sr_fix_addr_trace (u8 * s, va_list * args)
1976 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
1977 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
1978 sr_fix_addr_trace_t *t = va_arg (*args, sr_fix_addr_trace_t *);
1979 vnet_hw_interface_t *hi = 0;
1980 ip_adjacency_t *adj;
1981 ip6_main_t *im = &ip6_main;
1982 ip_lookup_main_t *lm = &im->lookup_main;
1983 vnet_main_t *vnm = vnet_get_main ();
1985 if (t->adj_index != ~0)
1987 adj = ip_get_adjacency (lm, t->adj_index);
1988 hi = vnet_get_sup_hw_interface (vnm, adj->rewrite_header.sw_if_index);
1991 s = format (s, "SR-FIX_ADDR: next %s ip6 src %U dst %U\n",
1992 (t->next_index == SR_FIX_DST_ADDR_NEXT_DROP)
1993 ? "drop" : "output",
1994 format_ip6_address, &t->src, format_ip6_address, &t->dst);
1995 if (t->next_index != SR_FIX_DST_ADDR_NEXT_DROP)
1998 format (s, "%U\n", format_ip6_sr_header, t->sr, 1 /* print_hmac */ );
2000 format (s, " output via %s",
2001 hi ? (char *) (hi->name) : "Invalid adj");
2007 * @brief Fix SR destination address - dual-loop
2009 * @node sr-fix-dst-addr
2010 * @param vm vlib_main_t *
2011 * @param node vlib_node_runtime_t *
2012 * @param from_frame vlib_frame_t *
2014 * @return from_frame->n_vectors uword
2017 sr_fix_dst_addr (vlib_main_t * vm,
2018 vlib_node_runtime_t * node, vlib_frame_t * from_frame)
2020 u32 n_left_from, next_index, *from, *to_next;
2021 ip6_main_t *im = &ip6_main;
2022 ip_lookup_main_t *lm = &im->lookup_main;
2024 from = vlib_frame_vector_args (from_frame);
2025 n_left_from = from_frame->n_vectors;
2027 next_index = node->cached_next_index;
2029 while (n_left_from > 0)
2033 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
2036 while (0 && n_left_from >= 4 && n_left_to_next >= 2)
2039 __attribute__ ((unused)) vlib_buffer_t *b0, *b1;
2040 u32 next0 = SR_FIX_DST_ADDR_NEXT_DROP;
2041 u32 next1 = SR_FIX_DST_ADDR_NEXT_DROP;
2043 /* Prefetch next iteration. */
2045 vlib_buffer_t *p2, *p3;
2047 p2 = vlib_get_buffer (vm, from[2]);
2048 p3 = vlib_get_buffer (vm, from[3]);
2050 vlib_prefetch_buffer_header (p2, LOAD);
2051 vlib_prefetch_buffer_header (p3, LOAD);
2060 n_left_to_next -= 2;
2063 b0 = vlib_get_buffer (vm, bi0);
2064 b1 = vlib_get_buffer (vm, bi1);
2067 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
2068 to_next, n_left_to_next,
2069 bi0, bi1, next0, next1);
2073 while (n_left_from > 0 && n_left_to_next > 0)
2078 ip_adjacency_t *adj0;
2079 ip6_sr_header_t *sr0;
2080 u32 next0 = SR_FIX_DST_ADDR_NEXT_DROP;
2081 ip6_address_t *new_dst0;
2082 ethernet_header_t *eh0;
2089 n_left_to_next -= 1;
2091 b0 = vlib_get_buffer (vm, bi0);
2094 ip_get_adjacency (lm, vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
2095 next0 = adj0->mcast_group_index;
2097 /* We should be pointing at an Ethernet header... */
2098 eh0 = vlib_buffer_get_current (b0);
2099 ip0 = (ip6_header_t *) (eh0 + 1);
2100 sr0 = (ip6_sr_header_t *) (ip0 + 1);
2102 /* We'd better find an SR header... */
2103 if (PREDICT_FALSE (ip0->protocol != IPPROTO_IPV6_ROUTE))
2105 b0->error = node->errors[SR_FIX_DST_ERROR_NO_SR_HEADER];
2111 * We get here from sr_rewrite or sr_local, with
2112 * sr->segments_left pointing at the (copy of the original) dst
2113 * address. Use it, then increment sr0->segments_left.
2116 /* Out of segments? Turf the packet */
2117 if (PREDICT_FALSE (sr0->segments_left == 0))
2119 b0->error = node->errors[SR_FIX_DST_ERROR_NO_MORE_SEGMENTS];
2124 * Rewrite the packet with the original dst address
2125 * We assume that the last segment (in processing order) contains
2126 * the original dst address. The list is reversed, so sr0->segments
2127 * contains the original dst address.
2129 new_dst0 = sr0->segments;
2130 ip0->dst_address.as_u64[0] = new_dst0->as_u64[0];
2131 ip0->dst_address.as_u64[1] = new_dst0->as_u64[1];
2136 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
2138 sr_fix_addr_trace_t *t = vlib_add_trace (vm, node,
2140 t->next_index = next0;
2143 if (next0 != SR_FIX_DST_ADDR_NEXT_DROP)
2145 t->adj_index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
2146 clib_memcpy (t->src.as_u8, ip0->src_address.as_u8,
2147 sizeof (t->src.as_u8));
2148 clib_memcpy (t->dst.as_u8, ip0->dst_address.as_u8,
2149 sizeof (t->dst.as_u8));
2150 clib_memcpy (t->sr, sr0, sizeof (t->sr));
2154 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2155 to_next, n_left_to_next,
2159 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2161 return from_frame->n_vectors;
2166 VLIB_REGISTER_NODE (sr_fix_dst_addr_node) = {
2167 .function = sr_fix_dst_addr,
2168 .name = "sr-fix-dst-addr",
2169 /* Takes a vector of packets. */
2170 .vector_size = sizeof (u32),
2171 .format_trace = format_sr_fix_addr_trace,
2172 .format_buffer = format_ip6_sr_header_with_length,
2174 .runtime_data_bytes = 0,
2176 .n_errors = SR_FIX_DST_N_ERROR,
2177 .error_strings = sr_fix_dst_error_strings,
2179 .n_next_nodes = SR_FIX_DST_ADDR_N_NEXT,
2181 #define _(s,n) [SR_FIX_DST_ADDR_NEXT_##s] = n,
2182 foreach_sr_fix_dst_addr_next
2188 VLIB_NODE_FUNCTION_MULTIARCH (sr_fix_dst_addr_node, sr_fix_dst_addr)
2189 static clib_error_t *sr_init (vlib_main_t * vm)
2191 ip6_sr_main_t *sm = &sr_main;
2192 clib_error_t *error = 0;
2193 vlib_node_t *ip6_lookup_node, *ip6_rewrite_node;
2195 if ((error = vlib_call_init_function (vm, ip_main_init)))
2198 if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
2202 sm->vnet_main = vnet_get_main ();
2204 vec_validate (sm->hmac_keys, 0);
2205 sm->hmac_keys[0].shared_secret = (u8 *) 0xdeadbeef;
2207 sm->tunnel_index_by_key =
2208 hash_create_mem (0, sizeof (ip6_sr_tunnel_key_t), sizeof (uword));
2210 sm->tunnel_index_by_name = hash_create_string (0, sizeof (uword));
2212 sm->policy_index_by_policy_name = hash_create_string (0, sizeof (uword));
2214 sm->policy_index_by_multicast_address =
2215 hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword));
2217 sm->hmac_key_by_shared_secret = hash_create_string (0, sizeof (uword));
2219 ip6_register_protocol (IPPROTO_IPV6_ROUTE, sr_local_node.index);
2221 ip6_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip6-lookup");
2222 ASSERT (ip6_lookup_node);
2224 ip6_rewrite_node = vlib_get_node_by_name (vm, (u8 *) "ip6-rewrite");
2225 ASSERT (ip6_rewrite_node);
2227 /* Add a disposition to ip6_lookup for the sr rewrite node */
2228 sm->ip6_lookup_sr_next_index =
2229 vlib_node_add_next (vm, ip6_lookup_node->index, sr_rewrite_node.index);
2231 #if DPDK > 0 /* Cannot run replicate without DPDK */
2232 /* Add a disposition to sr_replicate for the sr multicast replicate node */
2233 sm->ip6_lookup_sr_replicate_index =
2234 vlib_node_add_next (vm, ip6_lookup_node->index, sr_replicate_node.index);
2237 /* Add a disposition to ip6_rewrite for the sr dst address hack node */
2238 sm->ip6_rewrite_sr_next_index =
2239 vlib_node_add_next (vm, ip6_rewrite_node->index,
2240 sr_fix_dst_addr_node.index);
2242 OpenSSL_add_all_digests ();
2244 sm->md = (void *) EVP_get_digestbyname ("sha1");
2245 sm->hmac_ctx = clib_mem_alloc (sizeof (HMAC_CTX));
2250 VLIB_INIT_FUNCTION (sr_init);
2253 * @brief Definition of next-nodes for SR local
2255 #define foreach_sr_local_next \
2256 _ (ERROR, "error-drop") \
2257 _ (IP6_LOOKUP, "ip6-lookup")
2260 * @brief Struct for definition of next-nodes for SR local
2264 #define _(s,n) SR_LOCAL_NEXT_##s,
2265 foreach_sr_local_next
2271 * @brief Struct for packet trace of SR local
2277 ip6_address_t src, dst;
2283 * @brief Definition of SR local error-strings
2285 static char *sr_local_error_strings[] = {
2286 #define sr_error(n,s) s,
2287 #include "sr_error.def"
2292 * @brief Struct for definition of SR local error-strings
2296 #define sr_error(n,s) SR_LOCAL_ERROR_##n,
2297 #include "sr_error.def"
2303 * @brief Format SR local trace
2306 * @param args va_list *
2311 format_sr_local_trace (u8 * s, va_list * args)
2313 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
2314 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
2315 sr_local_trace_t *t = va_arg (*args, sr_local_trace_t *);
2317 s = format (s, "SR-LOCAL: src %U dst %U len %u next_index %d",
2318 format_ip6_address, &t->src,
2319 format_ip6_address, &t->dst, t->length, t->next_index);
2322 format (s, "\n %U", format_ip6_sr_header, t->sr, 1 /* print_hmac */ );
2324 s = format (s, "\n popped SR header");
2330 /* $$$$ fixme: smp, don't copy data, cache input, output (maybe) */
2332 * @brief Validate the SR HMAC
2334 * @param sm ip6_sr_main_t *
2335 * @param ip ip6_header_t *
2336 * @param sr ip6_sr_header_t *
2338 * @return retval int
2341 sr_validate_hmac (ip6_sr_main_t * sm, ip6_header_t * ip, ip6_sr_header_t * sr)
2347 ip6_address_t *addrp;
2349 ip6_sr_hmac_key_t *hmac_key;
2350 static u8 *signature;
2353 key_index = sr->hmac_key;
2355 /* No signature? Pass... */
2359 /* We don't know about this key? Fail... */
2360 if (key_index >= vec_len (sm->hmac_keys))
2363 vec_validate (signature, SHA256_DIGEST_LENGTH - 1);
2365 hmac_key = sm->hmac_keys + key_index;
2367 vec_reset_length (keybuf);
2369 /* pkt ip6 src address */
2370 vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
2371 clib_memcpy (copy_target, ip->src_address.as_u8, sizeof (ip6_address_t));
2374 vec_add2 (keybuf, copy_target, 1);
2375 copy_target[0] = sr->first_segment;
2377 /* octet w/ bit 0 = "clean" flag */
2378 vec_add2 (keybuf, copy_target, 1);
2380 = (sr->flags & clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))
2384 vec_add2 (keybuf, copy_target, 1);
2385 copy_target[0] = sr->hmac_key;
2387 first_segment = sr->first_segment;
2389 addrp = sr->segments;
2392 for (i = 0; i <= first_segment; i++)
2394 vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
2395 clib_memcpy (copy_target, addrp->as_u8, sizeof (ip6_address_t));
2400 clib_warning ("verify key index %d keybuf: %U", key_index,
2401 format_hex_bytes, keybuf, vec_len (keybuf));
2405 /* SHA1 is shorter than SHA-256 */
2406 memset (signature, 0, vec_len (signature));
2408 HMAC_CTX_init (sm->hmac_ctx);
2409 if (!HMAC_Init (sm->hmac_ctx, hmac_key->shared_secret,
2410 vec_len (hmac_key->shared_secret), sm->md))
2411 clib_warning ("barf1");
2412 if (!HMAC_Update (sm->hmac_ctx, keybuf, vec_len (keybuf)))
2413 clib_warning ("barf2");
2414 if (!HMAC_Final (sm->hmac_ctx, signature, &sig_len))
2415 clib_warning ("barf3");
2416 HMAC_CTX_cleanup (sm->hmac_ctx);
2419 clib_warning ("computed signature len %d, value %U", sig_len,
2420 format_hex_bytes, signature, vec_len (signature));
2422 /* Point at the SHA signature in the packet */
2425 clib_warning ("read signature %U", format_hex_bytes, addrp,
2426 SHA256_DIGEST_LENGTH);
2428 return memcmp (signature, addrp, SHA256_DIGEST_LENGTH);
2432 * @brief SR local node
2435 * @param vm vlib_main_t *
2436 * @param node vlib_node_runtime_t *
2437 * @param from_frame vlib_frame_t *
2439 * @return from_frame->n_vectors uword
2442 sr_local (vlib_main_t * vm,
2443 vlib_node_runtime_t * node, vlib_frame_t * from_frame)
2445 u32 n_left_from, next_index, *from, *to_next;
2446 ip6_sr_main_t *sm = &sr_main;
2447 u32 (*sr_local_cb) (vlib_main_t *, vlib_node_runtime_t *,
2448 vlib_buffer_t *, ip6_header_t *, ip6_sr_header_t *);
2449 sr_local_cb = sm->sr_local_cb;
2451 from = vlib_frame_vector_args (from_frame);
2452 n_left_from = from_frame->n_vectors;
2454 next_index = node->cached_next_index;
2456 while (n_left_from > 0)
2460 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
2462 while (n_left_from >= 4 && n_left_to_next >= 2)
2465 vlib_buffer_t *b0, *b1;
2466 ip6_header_t *ip0, *ip1;
2467 ip6_sr_header_t *sr0, *sr1;
2468 ip6_address_t *new_dst0, *new_dst1;
2469 u32 next0 = SR_LOCAL_NEXT_IP6_LOOKUP;
2470 u32 next1 = SR_LOCAL_NEXT_IP6_LOOKUP;
2471 /* Prefetch next iteration. */
2473 vlib_buffer_t *p2, *p3;
2475 p2 = vlib_get_buffer (vm, from[2]);
2476 p3 = vlib_get_buffer (vm, from[3]);
2478 vlib_prefetch_buffer_header (p2, LOAD);
2479 vlib_prefetch_buffer_header (p3, LOAD);
2481 CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
2482 CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
2491 n_left_to_next -= 2;
2495 b0 = vlib_get_buffer (vm, bi0);
2496 ip0 = vlib_buffer_get_current (b0);
2497 sr0 = (ip6_sr_header_t *) (ip0 + 1);
2499 if (PREDICT_FALSE (sr0->type != ROUTING_HEADER_TYPE_SR))
2501 next0 = SR_LOCAL_NEXT_ERROR;
2503 node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE];
2507 /* Out of segments? Turf the packet */
2508 if (PREDICT_FALSE (sr0->segments_left == 0))
2510 next0 = SR_LOCAL_NEXT_ERROR;
2511 b0->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS];
2515 if (PREDICT_FALSE (sm->validate_hmac))
2517 if (sr_validate_hmac (sm, ip0, sr0))
2519 next0 = SR_LOCAL_NEXT_ERROR;
2520 b0->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID];
2525 next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) : next0;
2528 * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx
2530 if (PREDICT_FALSE (next0 & 0x80000000))
2532 next0 ^= 0xFFFFFFFF;
2533 if (PREDICT_FALSE (next0 == SR_LOCAL_NEXT_ERROR))
2534 b0->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK];
2540 segment_index0 = sr0->segments_left - 1;
2542 /* Rewrite the packet */
2543 new_dst0 = (ip6_address_t *) (sr0->segments + segment_index0);
2544 ip0->dst_address.as_u64[0] = new_dst0->as_u64[0];
2545 ip0->dst_address.as_u64[1] = new_dst0->as_u64[1];
2547 if (PREDICT_TRUE (sr0->segments_left > 0))
2548 sr0->segments_left -= 1;
2551 /* End of the path. Clean up the SR header, or not */
2553 (sr0->segments_left == 0 &&
2555 clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))))
2557 u64 *copy_dst0, *copy_src0;
2560 * Copy the ip6 header right by the (real) length of the
2561 * sr header. Here's another place which assumes that
2562 * the sr header is the only extention header.
2565 ip0->protocol = sr0->protocol;
2566 vlib_buffer_advance (b0, (sr0->length + 1) * 8);
2568 new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
2569 (sr0->length + 1) * 8;
2570 ip0->payload_length = clib_host_to_net_u16 (new_l0);
2572 copy_src0 = (u64 *) ip0;
2573 copy_dst0 = copy_src0 + (sr0->length + 1);
2575 copy_dst0[4] = copy_src0[4];
2576 copy_dst0[3] = copy_src0[3];
2577 copy_dst0[2] = copy_src0[2];
2578 copy_dst0[1] = copy_src0[1];
2579 copy_dst0[0] = copy_src0[0];
2585 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
2587 sr_local_trace_t *tr = vlib_add_trace (vm, node,
2589 clib_memcpy (tr->src.as_u8, ip0->src_address.as_u8,
2590 sizeof (tr->src.as_u8));
2591 clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
2592 sizeof (tr->dst.as_u8));
2593 tr->length = vlib_buffer_length_in_chain (vm, b0);
2594 tr->next_index = next0;
2595 tr->sr_valid = sr0 != 0;
2597 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
2600 b1 = vlib_get_buffer (vm, bi1);
2601 ip1 = vlib_buffer_get_current (b1);
2602 sr1 = (ip6_sr_header_t *) (ip1 + 1);
2604 if (PREDICT_FALSE (sr1->type != ROUTING_HEADER_TYPE_SR))
2606 next1 = SR_LOCAL_NEXT_ERROR;
2608 node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE];
2612 /* Out of segments? Turf the packet */
2613 if (PREDICT_FALSE (sr1->segments_left == 0))
2615 next1 = SR_LOCAL_NEXT_ERROR;
2616 b1->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS];
2620 if (PREDICT_FALSE (sm->validate_hmac))
2622 if (sr_validate_hmac (sm, ip1, sr1))
2624 next1 = SR_LOCAL_NEXT_ERROR;
2625 b1->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID];
2630 next1 = sr_local_cb ? sr_local_cb (vm, node, b1, ip1, sr1) : next1;
2633 * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx
2635 if (PREDICT_FALSE (next1 & 0x80000000))
2637 next1 ^= 0xFFFFFFFF;
2638 if (PREDICT_FALSE (next1 == SR_LOCAL_NEXT_ERROR))
2639 b1->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK];
2645 segment_index1 = sr1->segments_left - 1;
2647 /* Rewrite the packet */
2648 new_dst1 = (ip6_address_t *) (sr1->segments + segment_index1);
2649 ip1->dst_address.as_u64[0] = new_dst1->as_u64[0];
2650 ip1->dst_address.as_u64[1] = new_dst1->as_u64[1];
2652 if (PREDICT_TRUE (sr1->segments_left > 0))
2653 sr1->segments_left -= 1;
2656 /* End of the path. Clean up the SR header, or not */
2658 (sr1->segments_left == 0 &&
2660 clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))))
2662 u64 *copy_dst1, *copy_src1;
2665 * Copy the ip6 header right by the (real) length of the
2666 * sr header. Here's another place which assumes that
2667 * the sr header is the only extention header.
2670 ip1->protocol = sr1->protocol;
2671 vlib_buffer_advance (b1, (sr1->length + 1) * 8);
2673 new_l1 = clib_net_to_host_u16 (ip1->payload_length) -
2674 (sr1->length + 1) * 8;
2675 ip1->payload_length = clib_host_to_net_u16 (new_l1);
2677 copy_src1 = (u64 *) ip1;
2678 copy_dst1 = copy_src1 + (sr1->length + 1);
2680 copy_dst1[4] = copy_src1[4];
2681 copy_dst1[3] = copy_src1[3];
2682 copy_dst1[2] = copy_src1[2];
2683 copy_dst1[1] = copy_src1[1];
2684 copy_dst1[0] = copy_src1[0];
2690 if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
2692 sr_local_trace_t *tr = vlib_add_trace (vm, node,
2694 clib_memcpy (tr->src.as_u8, ip1->src_address.as_u8,
2695 sizeof (tr->src.as_u8));
2696 clib_memcpy (tr->dst.as_u8, ip1->dst_address.as_u8,
2697 sizeof (tr->dst.as_u8));
2698 tr->length = vlib_buffer_length_in_chain (vm, b1);
2699 tr->next_index = next1;
2700 tr->sr_valid = sr1 != 0;
2702 clib_memcpy (tr->sr, sr1, sizeof (tr->sr));
2705 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
2706 to_next, n_left_to_next,
2707 bi0, bi1, next0, next1);
2710 while (n_left_from > 0 && n_left_to_next > 0)
2714 ip6_header_t *ip0 = 0;
2715 ip6_sr_header_t *sr0;
2716 ip6_address_t *new_dst0;
2717 u32 next0 = SR_LOCAL_NEXT_IP6_LOOKUP;
2724 n_left_to_next -= 1;
2726 b0 = vlib_get_buffer (vm, bi0);
2727 ip0 = vlib_buffer_get_current (b0);
2728 sr0 = (ip6_sr_header_t *) (ip0 + 1);
2730 if (PREDICT_FALSE (sr0->type != ROUTING_HEADER_TYPE_SR))
2732 next0 = SR_LOCAL_NEXT_ERROR;
2734 node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE];
2738 /* Out of segments? Turf the packet */
2739 if (PREDICT_FALSE (sr0->segments_left == 0))
2741 next0 = SR_LOCAL_NEXT_ERROR;
2742 b0->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS];
2746 if (PREDICT_FALSE (sm->validate_hmac))
2748 if (sr_validate_hmac (sm, ip0, sr0))
2750 next0 = SR_LOCAL_NEXT_ERROR;
2751 b0->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID];
2756 next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) : next0;
2759 * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx
2761 if (PREDICT_FALSE (next0 & 0x80000000))
2763 next0 ^= 0xFFFFFFFF;
2764 if (PREDICT_FALSE (next0 == SR_LOCAL_NEXT_ERROR))
2765 b0->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK];
2771 segment_index0 = sr0->segments_left - 1;
2773 /* Rewrite the packet */
2774 new_dst0 = (ip6_address_t *) (sr0->segments + segment_index0);
2775 ip0->dst_address.as_u64[0] = new_dst0->as_u64[0];
2776 ip0->dst_address.as_u64[1] = new_dst0->as_u64[1];
2778 if (PREDICT_TRUE (sr0->segments_left > 0))
2779 sr0->segments_left -= 1;
2782 /* End of the path. Clean up the SR header, or not */
2784 (sr0->segments_left == 0 &&
2786 clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))))
2788 u64 *copy_dst0, *copy_src0;
2791 * Copy the ip6 header right by the (real) length of the
2792 * sr header. Here's another place which assumes that
2793 * the sr header is the only extention header.
2796 ip0->protocol = sr0->protocol;
2797 vlib_buffer_advance (b0, (sr0->length + 1) * 8);
2799 new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
2800 (sr0->length + 1) * 8;
2801 ip0->payload_length = clib_host_to_net_u16 (new_l0);
2803 copy_src0 = (u64 *) ip0;
2804 copy_dst0 = copy_src0 + (sr0->length + 1);
2806 copy_dst0[4] = copy_src0[4];
2807 copy_dst0[3] = copy_src0[3];
2808 copy_dst0[2] = copy_src0[2];
2809 copy_dst0[1] = copy_src0[1];
2810 copy_dst0[0] = copy_src0[0];
2816 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
2818 sr_local_trace_t *tr = vlib_add_trace (vm, node,
2820 clib_memcpy (tr->src.as_u8, ip0->src_address.as_u8,
2821 sizeof (tr->src.as_u8));
2822 clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
2823 sizeof (tr->dst.as_u8));
2824 tr->length = vlib_buffer_length_in_chain (vm, b0);
2825 tr->next_index = next0;
2826 tr->sr_valid = sr0 != 0;
2828 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
2831 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2832 to_next, n_left_to_next,
2836 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2838 vlib_node_increment_counter (vm, sr_local_node.index,
2839 SR_LOCAL_ERROR_PKTS_PROCESSED,
2840 from_frame->n_vectors);
2841 return from_frame->n_vectors;
2845 VLIB_REGISTER_NODE (sr_local_node, static) = {
2846 .function = sr_local,
2848 /* Takes a vector of packets. */
2849 .vector_size = sizeof (u32),
2850 .format_trace = format_sr_local_trace,
2852 .runtime_data_bytes = 0,
2854 .n_errors = SR_LOCAL_N_ERROR,
2855 .error_strings = sr_local_error_strings,
2857 .n_next_nodes = SR_LOCAL_N_NEXT,
2859 #define _(s,n) [SR_LOCAL_NEXT_##s] = n,
2860 foreach_sr_local_next
2866 VLIB_NODE_FUNCTION_MULTIARCH (sr_local_node, sr_local)
2867 ip6_sr_main_t *sr_get_main (vlib_main_t * vm)
2869 vlib_call_init_function (vm, sr_init);
2870 ASSERT (sr_local_node.index);
2875 * @brief CLI parser for SR fix destination rewrite node
2877 * @param vm vlib_main_t *
2878 * @param input unformat_input_t *
2879 * @param cmd vlib_cli_command_t *
2881 * @return error clib_error_t *
2883 static clib_error_t *
2884 set_ip6_sr_rewrite_fn (vlib_main_t * vm,
2885 unformat_input_t * input, vlib_cli_command_t * cmd)
2888 ip6_main_t *im = &ip6_main;
2889 ip_lookup_main_t *lm = &im->lookup_main;
2894 ip_adjacency_t *adj;
2895 vnet_hw_interface_t *hi;
2897 ip6_sr_main_t *sm = &sr_main;
2898 vnet_main_t *vnm = vnet_get_main ();
2900 if (!unformat (input, "%U", unformat_ip6_address, &a))
2901 return clib_error_return (0, "ip6 address missing in '%U'",
2902 format_unformat_error, input);
2904 if (unformat (input, "rx-table-id %d", &fib_id))
2906 p = hash_get (im->fib_index_by_table_id, fib_id);
2908 return clib_error_return (0, "fib-id %d not found");
2912 adj_index = ip6_fib_lookup_with_table (im, fib_index, &a);
2914 if (adj_index == lm->miss_adj_index)
2915 return clib_error_return (0, "no match for %U", format_ip6_address, &a);
2917 adj = ip_get_adjacency (lm, adj_index);
2919 if (adj->lookup_next_index != IP_LOOKUP_NEXT_REWRITE)
2920 return clib_error_return (0, "%U unresolved (not a rewrite adj)",
2921 format_ip6_address, &a);
2923 adj->rewrite_header.next_index = sm->ip6_rewrite_sr_next_index;
2925 sw_if_index = adj->rewrite_header.sw_if_index;
2926 hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
2927 adj->rewrite_header.node_index = sr_fix_dst_addr_node.index;
2929 /* $$$$$ hack... steal the mcast group index */
2930 adj->mcast_group_index =
2931 vlib_node_add_next (vm, sr_fix_dst_addr_node.index,
2932 hi->output_node_index);
2938 VLIB_CLI_COMMAND (set_ip6_sr_rewrite, static) = {
2939 .path = "set ip6 sr rewrite",
2940 .short_help = "set ip6 sr rewrite <ip6-address> [fib-id <id>]",
2941 .function = set_ip6_sr_rewrite_fn,
2946 * @brief Register a callback routine to set next0 in sr_local
2951 vnet_register_sr_app_callback (void *cb)
2953 ip6_sr_main_t *sm = &sr_main;
2955 sm->sr_local_cb = cb;
2959 * @brief Test routine for validation of HMAC
2961 static clib_error_t *
2962 test_sr_hmac_validate_fn (vlib_main_t * vm,
2963 unformat_input_t * input, vlib_cli_command_t * cmd)
2965 ip6_sr_main_t *sm = &sr_main;
2967 if (unformat (input, "validate on"))
2968 sm->validate_hmac = 1;
2969 else if (unformat (input, "chunk-offset off"))
2970 sm->validate_hmac = 0;
2972 return clib_error_return (0, "expected validate on|off in '%U'",
2973 format_unformat_error, input);
2975 vlib_cli_output (vm, "hmac signature validation %s",
2976 sm->validate_hmac ? "on" : "off");
2981 VLIB_CLI_COMMAND (test_sr_hmac_validate, static) = {
2982 .path = "test sr hmac",
2983 .short_help = "test sr hmac validate [on|off]",
2984 .function = test_sr_hmac_validate_fn,
2989 * @brief Add or Delete HMAC key
2991 * @param sm ip6_sr_main_t *
2993 * @param shared_secret u8 *
2996 * @return retval i32
2998 // $$$ fixme shouldn't return i32
3000 sr_hmac_add_del_key (ip6_sr_main_t * sm, u32 key_id, u8 * shared_secret,
3004 ip6_sr_hmac_key_t *key;
3008 /* Specific key in use? Fail. */
3009 if (key_id && vec_len (sm->hmac_keys) > key_id
3010 && sm->hmac_keys[key_id].shared_secret)
3014 key = find_or_add_shared_secret (sm, shared_secret, &index);
3015 ASSERT (index == key_id);
3021 if (key_id) /* delete by key ID */
3023 if (vec_len (sm->hmac_keys) <= key_id)
3026 key = sm->hmac_keys + key_id;
3028 hash_unset_mem (sm->hmac_key_by_shared_secret, key->shared_secret);
3029 vec_free (key->shared_secret);
3034 key = find_or_add_shared_secret (sm, shared_secret, &index);
3035 hash_unset_mem (sm->hmac_key_by_shared_secret, key->shared_secret);
3036 vec_free (key->shared_secret);
3041 static clib_error_t *
3042 sr_hmac_add_del_key_fn (vlib_main_t * vm,
3043 unformat_input_t * input, vlib_cli_command_t * cmd)
3045 ip6_sr_main_t *sm = &sr_main;
3049 u8 *shared_secret = 0;
3052 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3054 if (unformat (input, "del"))
3056 else if (unformat (input, "id %d", &key_id))
3058 else if (unformat (input, "key %s", &shared_secret))
3060 /* Do not include the trailing NULL byte. Guaranteed interop issue */
3061 _vec_len (shared_secret) -= 1;
3067 if (is_del == 0 && shared_secret == 0)
3068 return clib_error_return (0, "shared secret must be set to add a key");
3070 if (shared_secret == 0 && key_id_set == 0)
3071 return clib_error_return (0, "shared secret and key id both unset");
3073 rv = sr_hmac_add_del_key (sm, key_id, shared_secret, is_del);
3075 vec_free (shared_secret);
3083 return clib_error_return (0, "sr_hmac_add_del_key returned %d", rv);
3090 VLIB_CLI_COMMAND (sr_hmac, static) = {
3092 .short_help = "sr hmac [del] id <nn> key <str>",
3093 .function = sr_hmac_add_del_key_fn,
3098 * @brief CLI parser for show HMAC key shared secrets
3100 * @param vm vlib_main_t *
3101 * @param input unformat_input_t *
3102 * @param cmd vlib_cli_command_t *
3104 * @return error clib_error_t *
3106 static clib_error_t *
3107 show_sr_hmac_fn (vlib_main_t * vm,
3108 unformat_input_t * input, vlib_cli_command_t * cmd)
3110 ip6_sr_main_t *sm = &sr_main;
3113 for (i = 1; i < vec_len (sm->hmac_keys); i++)
3115 if (sm->hmac_keys[i].shared_secret)
3116 vlib_cli_output (vm, "[%d]: %v", i, sm->hmac_keys[i].shared_secret);
3123 VLIB_CLI_COMMAND (show_sr_hmac, static) = {
3124 .path = "show sr hmac",
3125 .short_help = "show sr hmac",
3126 .function = show_sr_hmac_fn,
3131 * @brief Test for SR debug flag
3133 * @param vm vlib_main_t *
3134 * @param input unformat_input_t *
3135 * @param cmd vlib_cli_command_t *
3137 * @return error clib_error_t *
3139 static clib_error_t *
3140 test_sr_debug_fn (vlib_main_t * vm,
3141 unformat_input_t * input, vlib_cli_command_t * cmd)
3143 ip6_sr_main_t *sm = &sr_main;
3145 if (unformat (input, "on"))
3147 else if (unformat (input, "off"))
3150 return clib_error_return (0, "expected on|off in '%U'",
3151 format_unformat_error, input);
3153 vlib_cli_output (vm, "debug trace now %s", sm->is_debug ? "on" : "off");
3159 VLIB_CLI_COMMAND (test_sr_debug, static) = {
3160 .path = "test sr debug",
3161 .short_help = "test sr debug on|off",
3162 .function = test_sr_debug_fn,
3167 * fd.io coding-style-patch-verification: ON
3170 * eval: (c-set-style "gnu")