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>
25 #include <vnet/fib/ip6_fib.h>
26 #include <vnet/dpo/dpo.h>
28 #include <openssl/hmac.h>
30 ip6_sr_main_t sr_main;
31 static vlib_node_registration_t sr_local_node;
34 * @brief Dynamically added SR DPO type
36 static dpo_type_t sr_dpo_type;
39 * @brief Use passed HMAC key in ip6_sr_header_t in OpenSSL HMAC routines
41 * @param sm ip6_sr_main_t *
42 * @param ip ip6_header_t *
43 * @param sr ip6_sr_header_t *
46 sr_fix_hmac (ip6_sr_main_t * sm, ip6_header_t * ip, ip6_sr_header_t * sr)
54 ip6_sr_hmac_key_t *hmac_key;
57 key_index = sr->hmac_key;
59 /* No signature? Pass... */
63 /* We don't know about this key? Fail... */
64 if (key_index >= vec_len (sm->hmac_keys))
67 hmac_key = sm->hmac_keys + key_index;
69 vec_reset_length (keybuf);
71 /* pkt ip6 src address */
72 vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
73 clib_memcpy (copy_target, ip->src_address.as_u8, sizeof (ip6_address_t));
76 vec_add2 (keybuf, copy_target, 1);
77 copy_target[0] = sr->first_segment;
79 /* octet w/ bit 0 = "clean" flag */
80 vec_add2 (keybuf, copy_target, 1);
82 = (sr->flags & clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))
86 vec_add2 (keybuf, copy_target, 1);
87 copy_target[0] = sr->hmac_key;
89 first_segment = sr->first_segment;
94 for (i = 0; i <= first_segment; i++)
96 vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
97 clib_memcpy (copy_target, addrp->as_u8, sizeof (ip6_address_t));
103 HMAC_CTX_init (sm->hmac_ctx);
104 if (!HMAC_Init (sm->hmac_ctx, hmac_key->shared_secret,
105 vec_len (hmac_key->shared_secret), sm->md))
106 clib_warning ("barf1");
107 if (!HMAC_Update (sm->hmac_ctx, keybuf, vec_len (keybuf)))
108 clib_warning ("barf2");
109 if (!HMAC_Final (sm->hmac_ctx, (unsigned char *) addrp, &sig_len))
110 clib_warning ("barf3");
111 HMAC_CTX_cleanup (sm->hmac_ctx);
115 * @brief Format function for decoding various SR flags
117 * @param s u8 * - formatted string
118 * @param args va_list * - u16 flags
120 * @return formatted output string u8 *
123 format_ip6_sr_header_flags (u8 * s, va_list * args)
125 u16 flags = (u16) va_arg (*args, int);
127 int bswap_needed = va_arg (*args, int);
131 flags = clib_host_to_net_u16 (flags);
133 if (flags & IP6_SR_HEADER_FLAG_CLEANUP)
134 s = format (s, "cleanup ");
136 if (flags & IP6_SR_HEADER_FLAG_PROTECTED)
137 s = format (s, "reroute ");
139 s = format (s, "pl: ");
140 for (i = 1; i <= 4; i++)
142 pl_flag = ip6_sr_policy_list_flags (flags, i);
143 s = format (s, "[%d] ", i);
147 case IP6_SR_HEADER_FLAG_PL_ELT_NOT_PRESENT:
148 s = format (s, "NotPr ");
150 case IP6_SR_HEADER_FLAG_PL_ELT_INGRESS_PE:
151 s = format (s, "InPE ");
153 case IP6_SR_HEADER_FLAG_PL_ELT_EGRESS_PE:
154 s = format (s, "EgPE ");
157 case IP6_SR_HEADER_FLAG_PL_ELT_ORIG_SRC_ADDR:
158 s = format (s, "OrgSrc ");
166 * @brief Format function for decoding ip6_sr_header_t
168 * @param s u8 * - formatted string
169 * @param args va_list * - ip6_sr_header_t
171 * @return formatted output string u8 *
174 format_ip6_sr_header (u8 * s, va_list * args)
176 ip6_sr_header_t *h = va_arg (*args, ip6_sr_header_t *);
177 ip6_address_t placeholder_addr =
178 { {254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
181 int print_hmac = va_arg (*args, int);
182 int i, pl_index, max_segs;
183 int flags_host_byte_order = clib_net_to_host_u16 (h->flags);
185 s = format (s, "next proto %d, len %d, type %d",
186 h->protocol, (h->length << 3) + 8, h->type);
187 s = format (s, "\n segs left %d, first_segment %d, hmac key %d",
188 h->segments_left, h->first_segment, h->hmac_key);
189 s = format (s, "\n flags %U", format_ip6_sr_header_flags,
190 flags_host_byte_order, 0 /* bswap needed */ );
193 * Header length is in 8-byte units (minus one), so
194 * divide by 2 to ascertain the number of ip6 addresses in the
197 max_segs = (h->length >> 1);
199 if (!print_hmac && h->hmac_key)
202 s = format (s, "\n Segments (in processing order):");
204 for (i = h->first_segment; i >= 1; i--)
205 s = format (s, "\n %U", format_ip6_address, h->segments + i);
206 if (ip6_address_is_equal (&placeholder_addr, h->segments))
207 s = format (s, "\n (empty placeholder)");
209 s = format (s, "\n %U", format_ip6_address, h->segments);
211 s = format (s, "\n Policy List:");
213 pl_index = 1; /* to match the RFC text */
214 for (i = (h->first_segment + 1); i < max_segs; i++, pl_index++)
217 char *tags[] = { " ", "InPE: ", "EgPE: ", "OrgSrc: " };
220 if (pl_index >= 1 && pl_index <= 4)
222 int this_pl_flag = ip6_sr_policy_list_flags
223 (flags_host_byte_order, pl_index);
224 tag = tags[this_pl_flag];
227 s = format (s, "\n %s%U", tag, format_ip6_address, h->segments + i);
234 * @brief Format function for decoding ip6_sr_header_t with length
236 * @param s u8 * - formatted string
237 * @param args va_list * - ip6_header_t + ip6_sr_header_t
239 * @return formatted output string u8 *
242 format_ip6_sr_header_with_length (u8 * s, va_list * args)
244 ip6_header_t *h = va_arg (*args, ip6_header_t *);
245 u32 max_header_bytes = va_arg (*args, u32);
248 header_bytes = sizeof (h[0]) + sizeof (ip6_sr_header_t);
249 if (max_header_bytes != 0 && header_bytes > max_header_bytes)
250 return format (s, "ip6_sr header truncated");
252 s = format (s, "IP6: %U\n", format_ip6_header, h, max_header_bytes);
254 format (s, "SR: %U\n", format_ip6_sr_header, (ip6_sr_header_t *) (h + 1),
255 0 /* print_hmac */ , max_header_bytes);
260 * @brief Defined valid next nodes
261 * @note Cannot call replicate yet without DPDK
264 #define foreach_sr_rewrite_next \
265 _(ERROR, "error-drop") \
266 _(IP6_LOOKUP, "ip6-lookup") \
267 _(SR_LOCAL, "sr-local") \
268 _(SR_REPLICATE,"sr-replicate")
270 #define foreach_sr_rewrite_next \
271 _(ERROR, "error-drop") \
272 _(IP6_LOOKUP, "ip6-lookup") \
273 _(SR_LOCAL, "sr-local")
277 * @brief Struct for defined valid next nodes
281 #define _(s,n) SR_REWRITE_NEXT_##s,
282 foreach_sr_rewrite_next
288 * @brief Struct for data for SR rewrite packet trace
292 ip6_address_t src, dst;
297 } sr_rewrite_trace_t;
300 * @brief Error strings for SR rewrite
302 static char *sr_rewrite_error_strings[] = {
303 #define sr_error(n,s) s,
304 #include "sr_error.def"
309 * @brief Struct for SR rewrite error strings
313 #define sr_error(n,s) SR_REWRITE_ERROR_##n,
314 #include "sr_error.def"
317 } sr_rewrite_error_t;
321 * @brief Format function for SR rewrite trace.
324 format_sr_rewrite_trace (u8 * s, va_list * args)
326 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
327 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
328 sr_rewrite_trace_t *t = va_arg (*args, sr_rewrite_trace_t *);
329 ip6_sr_main_t *sm = &sr_main;
330 ip6_sr_tunnel_t *tun = pool_elt_at_index (sm->tunnels, t->tunnel_index);
331 ip6_fib_t *rx_fib, *tx_fib;
333 rx_fib = ip6_fib_get (tun->rx_fib_index);
334 tx_fib = ip6_fib_get (tun->tx_fib_index);
337 (s, "SR-REWRITE: next %s ip6 src %U dst %U len %u\n"
338 " rx-fib-id %d tx-fib-id %d\n%U",
339 (t->next_index == SR_REWRITE_NEXT_SR_LOCAL)
340 ? "sr-local" : "ip6-lookup",
341 format_ip6_address, &t->src,
342 format_ip6_address, &t->dst, t->length,
343 rx_fib->table_id, tx_fib->table_id,
344 format_ip6_sr_header, t->sr, 0 /* print_hmac */ );
349 * @brief Main processing dual-loop for Segment Routing Rewrite
352 * @param vm vlib_main_t *
353 * @param node vlib_node_runtime_t *
354 * @param from_frame vlib_frame_t *
356 * @return from_frame->n_vectors uword
359 sr_rewrite (vlib_main_t * vm,
360 vlib_node_runtime_t * node, vlib_frame_t * from_frame)
362 u32 n_left_from, next_index, *from, *to_next;
363 ip6_sr_main_t *sm = &sr_main;
364 u32 (*sr_local_cb) (vlib_main_t *, vlib_node_runtime_t *,
365 vlib_buffer_t *, ip6_header_t *, ip6_sr_header_t *);
366 sr_local_cb = sm->sr_local_cb;
368 from = vlib_frame_vector_args (from_frame);
369 n_left_from = from_frame->n_vectors;
371 next_index = node->cached_next_index;
373 while (n_left_from > 0)
377 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
379 /* Note 2x loop disabled */
380 while (0 && n_left_from >= 4 && n_left_to_next >= 2)
383 vlib_buffer_t *b0, *b1;
384 ip6_header_t *ip0, *ip1;
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
420 pool_elt_at_index (sm->tunnels,
421 vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
423 pool_elt_at_index (sm->tunnels,
424 vnet_buffer (b1)->ip.adj_index[VLIB_TX]);
426 ASSERT (VLIB_BUFFER_PRE_DATA_SIZE
427 >= ((word) vec_len (t0->rewrite)) + b0->current_data);
428 ASSERT (VLIB_BUFFER_PRE_DATA_SIZE
429 >= ((word) vec_len (t1->rewrite)) + b1->current_data);
431 vnet_buffer (b0)->sw_if_index[VLIB_TX] = t0->tx_fib_index;
432 vnet_buffer (b1)->sw_if_index[VLIB_TX] = t1->tx_fib_index;
434 ip0 = vlib_buffer_get_current (b0);
435 ip1 = vlib_buffer_get_current (b1);
436 #if DPDK > 0 /* Cannot call replication node yet without DPDK */
437 /* add a replication node */
438 if (PREDICT_FALSE (t0->policy_index != ~0))
440 vnet_buffer (b0)->ip.save_protocol = t0->policy_index;
441 next0 = SR_REWRITE_NEXT_SR_REPLICATE;
442 sr0 = (ip6_sr_header_t *) (t0->rewrite);
448 * SR-unaware service chaining case: pkt coming back from
449 * service has the original dst address, and will already
450 * have an SR header. If so, send it to sr-local
452 if (PREDICT_FALSE (ip0->protocol == IPPROTO_IPV6_ROUTE))
454 vlib_buffer_advance (b0, sizeof (ip0));
455 sr0 = (ip6_sr_header_t *) (ip0 + 1);
456 new_l0 = clib_net_to_host_u16 (ip0->payload_length);
457 next0 = SR_REWRITE_NEXT_SR_LOCAL;
461 u32 len_bytes = sizeof (ip6_header_t);
462 u8 next_hdr = ip0->protocol;
464 /* HBH must immediately follow ipv6 header */
466 (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
468 ip6_hop_by_hop_ext_t *ext_hdr =
469 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
471 ip6_ext_header_len ((ip6_ext_header_t *) ext_hdr);
472 /* Ignoring the sr_local for now, if RH follows HBH here */
473 next_hdr = ext_hdr->next_hdr;
474 ext_hdr->next_hdr = IPPROTO_IPV6_ROUTE;
478 ip0->protocol = IPPROTO_IPV6_ROUTE; /* routing extension header */
481 * Copy data before the punch-in point left by the
482 * required amount. Assume (for the moment) that only
483 * the main packet header needs to be copied.
485 clib_memcpy (((u8 *) ip0) - vec_len (t0->rewrite),
487 vlib_buffer_advance (b0, -(word) vec_len (t0->rewrite));
488 ip0 = vlib_buffer_get_current (b0);
489 sr0 = (ip6_sr_header_t *) ((u8 *) ip0 + len_bytes);
491 clib_memcpy (sr0, t0->rewrite, vec_len (t0->rewrite));
493 /* Fix the next header chain */
494 sr0->protocol = next_hdr;
496 new_l0 = clib_net_to_host_u16 (ip0->payload_length) +
497 vec_len (t0->rewrite);
498 ip0->payload_length = clib_host_to_net_u16 (new_l0);
500 /* Copy dst address into the DA slot in the segment list */
501 clib_memcpy (sr0->segments, ip0->dst_address.as_u64,
502 sizeof (ip6_address_t));
503 /* Rewrite the ip6 dst address with the first hop */
504 clib_memcpy (ip0->dst_address.as_u64, t0->first_hop.as_u64,
505 sizeof (ip6_address_t));
507 sr_fix_hmac (sm, ip0, sr0);
509 next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) :
513 * Ignore "do not rewrite" shtik in this path
515 if (PREDICT_FALSE (next0 & 0x80000000))
518 if (PREDICT_FALSE (next0 == SR_REWRITE_NEXT_ERROR))
519 b0->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK];
522 #if DPDK > 0 /* Cannot call replication node yet without DPDK */
524 /* add a replication node */
525 if (PREDICT_FALSE (t1->policy_index != ~0))
527 vnet_buffer (b1)->ip.save_protocol = t1->policy_index;
528 next1 = SR_REWRITE_NEXT_SR_REPLICATE;
529 sr1 = (ip6_sr_header_t *) (t1->rewrite);
533 if (PREDICT_FALSE (ip1->protocol == IPPROTO_IPV6_ROUTE))
535 vlib_buffer_advance (b1, sizeof (ip1));
536 sr1 = (ip6_sr_header_t *) (ip1 + 1);
537 new_l1 = clib_net_to_host_u16 (ip1->payload_length);
538 next1 = SR_REWRITE_NEXT_SR_LOCAL;
542 u32 len_bytes = sizeof (ip6_header_t);
543 u8 next_hdr = ip1->protocol;
545 /* HBH must immediately follow ipv6 header */
547 (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
549 ip6_hop_by_hop_ext_t *ext_hdr =
550 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1);
552 ip6_ext_header_len ((ip6_ext_header_t *) ext_hdr);
553 /* Ignoring the sr_local for now, if RH follows HBH here */
554 next_hdr = ext_hdr->next_hdr;
555 ext_hdr->next_hdr = IPPROTO_IPV6_ROUTE;
559 ip1->protocol = IPPROTO_IPV6_ROUTE;
562 * Copy data before the punch-in point left by the
563 * required amount. Assume (for the moment) that only
564 * the main packet header needs to be copied.
566 clib_memcpy (((u8 *) ip1) - vec_len (t1->rewrite),
568 vlib_buffer_advance (b1, -(word) vec_len (t1->rewrite));
569 ip1 = vlib_buffer_get_current (b1);
570 sr1 = (ip6_sr_header_t *) ((u8 *) ip1 + len_bytes);
571 clib_memcpy (sr1, t1->rewrite, vec_len (t1->rewrite));
573 sr1->protocol = next_hdr;
574 new_l1 = clib_net_to_host_u16 (ip1->payload_length) +
575 vec_len (t1->rewrite);
576 ip1->payload_length = clib_host_to_net_u16 (new_l1);
578 /* Copy dst address into the DA slot in the segment list */
579 clib_memcpy (sr1->segments, ip1->dst_address.as_u64,
580 sizeof (ip6_address_t));
581 /* Rewrite the ip6 dst address with the first hop */
582 clib_memcpy (ip1->dst_address.as_u64, t1->first_hop.as_u64,
583 sizeof (ip6_address_t));
585 sr_fix_hmac (sm, ip1, sr1);
587 next1 = sr_local_cb ? sr_local_cb (vm, node, b1, ip1, sr1) :
591 * Ignore "do not rewrite" shtik in this path
593 if (PREDICT_FALSE (next1 & 0x80000000))
596 if (PREDICT_FALSE (next1 == SR_REWRITE_NEXT_ERROR))
597 b1->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK];
600 #if DPDK > 0 /* Cannot run replicate without DPDK and only replicate uses this label */
604 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
606 sr_rewrite_trace_t *tr = vlib_add_trace (vm, node,
608 tr->tunnel_index = t0 - sm->tunnels;
609 clib_memcpy (tr->src.as_u8, ip0->src_address.as_u8,
610 sizeof (tr->src.as_u8));
611 clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
612 sizeof (tr->dst.as_u8));
614 tr->next_index = next0;
616 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
618 if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
620 sr_rewrite_trace_t *tr = vlib_add_trace (vm, node,
622 tr->tunnel_index = t1 - sm->tunnels;
623 clib_memcpy (tr->src.as_u8, ip1->src_address.as_u8,
624 sizeof (tr->src.as_u8));
625 clib_memcpy (tr->dst.as_u8, ip1->dst_address.as_u8,
626 sizeof (tr->dst.as_u8));
628 tr->next_index = next1;
630 clib_memcpy (tr->sr, sr1, sizeof (tr->sr));
632 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
633 to_next, n_left_to_next,
634 bi0, bi1, next0, next1);
637 while (n_left_from > 0 && n_left_to_next > 0)
641 ip6_header_t *ip0 = 0;
642 ip6_sr_header_t *sr0 = 0;
644 u32 next0 = SR_REWRITE_NEXT_IP6_LOOKUP;
654 b0 = vlib_get_buffer (vm, bi0);
658 * $$$ parse through header(s) to pick the point
659 * where we punch in the SR extention header
662 pool_elt_at_index (sm->tunnels,
663 vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
664 #if DPDK > 0 /* Cannot call replication node yet without DPDK */
665 /* add a replication node */
666 if (PREDICT_FALSE (t0->policy_index != ~0))
668 vnet_buffer (b0)->ip.save_protocol = t0->policy_index;
669 next0 = SR_REWRITE_NEXT_SR_REPLICATE;
670 sr0 = (ip6_sr_header_t *) (t0->rewrite);
675 ASSERT (VLIB_BUFFER_PRE_DATA_SIZE
676 >= ((word) vec_len (t0->rewrite)) + b0->current_data);
678 vnet_buffer (b0)->sw_if_index[VLIB_TX] = t0->tx_fib_index;
680 ip0 = vlib_buffer_get_current (b0);
683 * SR-unaware service chaining case: pkt coming back from
684 * service has the original dst address, and will already
685 * have an SR header. If so, send it to sr-local
687 if (PREDICT_FALSE (ip0->protocol == IPPROTO_IPV6_ROUTE))
689 vlib_buffer_advance (b0, sizeof (ip0));
690 sr0 = (ip6_sr_header_t *) (ip0 + 1);
691 new_l0 = clib_net_to_host_u16 (ip0->payload_length);
692 next0 = SR_REWRITE_NEXT_SR_LOCAL;
696 u32 len_bytes = sizeof (ip6_header_t);
697 u8 next_hdr = ip0->protocol;
699 /* HBH must immediately follow ipv6 header */
701 (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
703 ip6_hop_by_hop_ext_t *ext_hdr =
704 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
706 ip6_ext_header_len ((ip6_ext_header_t *) ext_hdr);
707 next_hdr = ext_hdr->next_hdr;
708 ext_hdr->next_hdr = IPPROTO_IPV6_ROUTE;
709 /* Ignoring the sr_local for now, if RH follows HBH here */
713 ip0->protocol = IPPROTO_IPV6_ROUTE; /* routing extension header */
716 * Copy data before the punch-in point left by the
717 * required amount. Assume (for the moment) that only
718 * the main packet header needs to be copied.
720 clib_memcpy (((u8 *) ip0) - vec_len (t0->rewrite),
722 vlib_buffer_advance (b0, -(word) vec_len (t0->rewrite));
723 ip0 = vlib_buffer_get_current (b0);
724 sr0 = (ip6_sr_header_t *) ((u8 *) ip0 + len_bytes);
726 clib_memcpy (sr0, t0->rewrite, vec_len (t0->rewrite));
728 /* Fix the next header chain */
729 sr0->protocol = next_hdr;
730 new_l0 = clib_net_to_host_u16 (ip0->payload_length) +
731 vec_len (t0->rewrite);
732 ip0->payload_length = clib_host_to_net_u16 (new_l0);
734 /* Copy dst address into the DA slot in the segment list */
735 clib_memcpy (sr0->segments, ip0->dst_address.as_u64,
736 sizeof (ip6_address_t));
737 /* Rewrite the ip6 dst address with the first hop */
738 clib_memcpy (ip0->dst_address.as_u64, t0->first_hop.as_u64,
739 sizeof (ip6_address_t));
741 sr_fix_hmac (sm, ip0, sr0);
743 next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) :
747 * Ignore "do not rewrite" shtik in this path
749 if (PREDICT_FALSE (next0 & 0x80000000))
752 if (PREDICT_FALSE (next0 == SR_REWRITE_NEXT_ERROR))
753 b0->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK];
756 #if DPDK > 0 /* Cannot run replicate without DPDK and only replicate uses this label */
760 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
762 sr_rewrite_trace_t *tr = vlib_add_trace (vm, node,
764 tr->tunnel_index = t0 - sm->tunnels;
767 memcpy (tr->src.as_u8, ip0->src_address.as_u8,
768 sizeof (tr->src.as_u8));
769 memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
770 sizeof (tr->dst.as_u8));
773 tr->next_index = next0;
775 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
777 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
778 to_next, n_left_to_next,
781 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
783 return from_frame->n_vectors;
787 VLIB_REGISTER_NODE (sr_rewrite_node) = {
788 .function = sr_rewrite,
789 .name = "sr-rewrite",
790 /* Takes a vector of packets. */
791 .vector_size = sizeof (u32),
792 .format_trace = format_sr_rewrite_trace,
793 .format_buffer = format_ip6_sr_header_with_length,
795 .n_errors = SR_REWRITE_N_ERROR,
796 .error_strings = sr_rewrite_error_strings,
798 .runtime_data_bytes = 0,
800 .n_next_nodes = SR_REWRITE_N_NEXT,
802 #define _(s,n) [SR_REWRITE_NEXT_##s] = n,
803 foreach_sr_rewrite_next
808 VLIB_NODE_FUNCTION_MULTIARCH (sr_rewrite_node, sr_rewrite)
812 ip6_delete_route_no_next_hop (ip6_address_t * dst_address_arg,
813 u32 dst_address_length, u32 rx_table_id)
816 .fp_len = dst_address_length,
817 .fp_proto = FIB_PROTOCOL_IP6,
819 .ip6 = *dst_address_arg,
823 fib_table_entry_delete (fib_table_id_find_fib_index (FIB_PROTOCOL_IP6,
825 &pfx, FIB_SOURCE_SR);
831 * @brief Find or add if not found - HMAC shared secret
833 * @param sm ip6_sr_main_t *
835 * @param indexp u32 *
837 * @return ip6_sr_hmac_key_t *
839 static ip6_sr_hmac_key_t *
840 find_or_add_shared_secret (ip6_sr_main_t * sm, u8 * secret, u32 * indexp)
843 ip6_sr_hmac_key_t *key = 0;
846 p = hash_get_mem (sm->hmac_key_by_shared_secret, secret);
850 key = vec_elt_at_index (sm->hmac_keys, p[0]);
856 /* Specific key ID? */
857 if (indexp && *indexp)
859 vec_validate (sm->hmac_keys, *indexp);
860 key = sm->hmac_keys + *indexp;
864 for (i = 0; i < vec_len (sm->hmac_keys); i++)
866 if (sm->hmac_keys[i].shared_secret == 0)
868 key = sm->hmac_keys + i;
872 vec_validate (sm->hmac_keys, i);
873 key = sm->hmac_keys + i;
878 key->shared_secret = vec_dup (secret);
880 hash_set_mem (sm->hmac_key_by_shared_secret, key->shared_secret,
881 key - sm->hmac_keys);
884 *indexp = key - sm->hmac_keys;
889 * @brief Add or Delete a Segment Routing tunnel.
891 * @param a ip6_sr_add_del_tunnel_args_t *
896 ip6_sr_add_del_tunnel (ip6_sr_add_del_tunnel_args_t * a)
898 ip6_main_t *im = &ip6_main;
899 ip6_sr_tunnel_key_t key;
902 ip6_sr_header_t *h = 0;
904 ip6_address_t *addrp, *this_address;
905 ip6_sr_main_t *sm = &sr_main;
907 u32 rx_fib_index, tx_fib_index;
908 u32 hmac_key_index_u32;
909 u8 hmac_key_index = 0;
912 dpo_id_t dpo = DPO_INVALID;
914 /* Make sure that the rx FIB exists */
915 p = hash_get (im->fib_index_by_table_id, a->rx_table_id);
920 /* remember the FIB index */
923 /* Make sure that the supplied FIB exists */
924 p = hash_get (im->fib_index_by_table_id, a->tx_table_id);
929 /* remember the FIB index */
932 clib_memcpy (key.src.as_u8, a->src_address->as_u8, sizeof (key.src));
933 clib_memcpy (key.dst.as_u8, a->dst_address->as_u8, sizeof (key.dst));
935 /* When adding a tunnel:
936 * - If a "name" is given, it must not exist.
937 * - The "key" is always checked, and must not exist.
938 * When deleting a tunnel:
939 * - If the "name" is given, and it exists, then use it.
940 * - If the "name" is not given, use the "key".
941 * - If the "name" and the "key" are given, then both must point to the same
946 p = hash_get_mem (sm->tunnel_index_by_key, &key);
948 /* If the name is given, look it up */
950 n = hash_get_mem (sm->tunnel_index_by_name, a->name);
954 /* validate key/name parameters */
955 if (!a->is_del) /* adding a tunnel */
957 if (a->name && n) /* name given & exists already */
959 if (p) /* key exists already */
962 else /* deleting a tunnel */
964 if (!p) /* key doesn't exist */
966 if (a->name && !n) /* name given & it doesn't exist */
969 if (n) /* name given & found */
971 if (n[0] != p[0]) /* name and key do not point to the same thing */
977 if (a->is_del) /* delete the tunnel */
981 /* Delete existing tunnel */
982 t = pool_elt_at_index (sm->tunnels, p[0]);
984 ip6_delete_route_no_next_hop (&t->key.dst, t->dst_mask_width,
986 vec_free (t->rewrite);
987 /* Remove tunnel from any policy if associated */
988 if (t->policy_index != ~0)
990 pt = pool_elt_at_index (sm->policies, t->policy_index);
991 for (i = 0; i < vec_len (pt->tunnel_indices); i++)
993 if (pt->tunnel_indices[i] == t - sm->tunnels)
995 vec_delete (pt->tunnel_indices, 1, i);
999 clib_warning ("Tunnel index %d not found in policy_index %d",
1000 t - sm->tunnels, pt - sm->policies);
1002 /* If this is last tunnel in the policy, clean up the policy too */
1003 if (vec_len (pt->tunnel_indices) == 0)
1005 hash_unset_mem (sm->policy_index_by_policy_name, pt->name);
1006 vec_free (pt->name);
1007 pool_put (sm->policies, pt);
1011 /* Clean up the tunnel by name */
1014 hash_unset_mem (sm->tunnel_index_by_name, t->name);
1017 pool_put (sm->tunnels, t);
1018 hp = hash_get_pair (sm->tunnel_index_by_key, &key);
1019 key_copy = (void *) (hp->key);
1020 hash_unset_mem (sm->tunnel_index_by_key, &key);
1021 vec_free (key_copy);
1025 /* create a new tunnel */
1026 pool_get (sm->tunnels, t);
1027 memset (t, 0, sizeof (*t));
1028 t->policy_index = ~0;
1030 clib_memcpy (&t->key, &key, sizeof (t->key));
1031 t->dst_mask_width = a->dst_mask_width;
1032 t->rx_fib_index = rx_fib_index;
1033 t->tx_fib_index = tx_fib_index;
1035 if (!vec_len (a->segments))
1036 /* there must be at least one segment... */
1039 /* The first specified hop goes right into the dst address */
1040 clib_memcpy (&t->first_hop, &a->segments[0], sizeof (ip6_address_t));
1043 * Create the sr header rewrite string
1044 * The list of segments needs an extra slot for the ultimate destination
1045 * which is taken from the packet we add the SRH to.
1047 header_length = sizeof (*h) +
1048 sizeof (ip6_address_t) * (vec_len (a->segments) + 1 + vec_len (a->tags));
1050 if (a->shared_secret)
1052 /* Allocate a new key slot if we don't find the secret key */
1053 hmac_key_index_u32 = 0;
1054 (void) find_or_add_shared_secret (sm, a->shared_secret,
1055 &hmac_key_index_u32);
1057 /* Hey Vinz Clortho: Gozzer is pissed.. you're out of keys! */
1058 if (hmac_key_index_u32 >= 256)
1060 hmac_key_index = hmac_key_index_u32;
1061 header_length += SHA256_DIGEST_LENGTH;
1064 vec_validate (t->rewrite, header_length - 1);
1066 h = (ip6_sr_header_t *) t->rewrite;
1068 h->protocol = 0xFF; /* we don't know yet */
1070 h->length = (header_length / 8) - 1;
1071 h->type = ROUTING_HEADER_TYPE_SR;
1073 /* first_segment and segments_left need to have the index of the last
1074 * element in the list; a->segments has one element less than ends up
1075 * in the header (it does not have the DA in it), so vec_len(a->segments)
1076 * is the value we want.
1078 h->first_segment = h->segments_left = vec_len (a->segments);
1080 if (a->shared_secret)
1081 h->hmac_key = hmac_key_index & 0xFF;
1083 h->flags = a->flags_net_byte_order;
1085 /* Paint on the segment list, in reverse.
1086 * This is offset by one to leave room at the start for the ultimate
1089 addrp = h->segments + vec_len (a->segments);
1091 vec_foreach (this_address, a->segments)
1093 clib_memcpy (addrp->as_u8, this_address->as_u8, sizeof (ip6_address_t));
1098 * Since the ultimate destination address is not yet known, set that slot
1099 * to a value we will instantly recognize as bogus.
1101 memset (h->segments, 0xfe, sizeof (ip6_address_t));
1103 /* Paint on the tag list, not reversed */
1104 addrp = h->segments + vec_len (a->segments);
1106 vec_foreach (this_address, a->tags)
1108 clib_memcpy (addrp->as_u8, this_address->as_u8, sizeof (ip6_address_t));
1112 key_copy = vec_new (ip6_sr_tunnel_key_t, 1);
1113 clib_memcpy (key_copy, &key, sizeof (ip6_sr_tunnel_key_t));
1114 hash_set_mem (sm->tunnel_index_by_key, key_copy, t - sm->tunnels);
1117 * Stick the tunnel index into the rewrite header.
1119 * Unfortunately, inserting an SR header according to the various
1120 * RFC's requires parsing through the ip6 header, perhaps consing a
1121 * buffer onto the head of the vlib_buffer_t, etc. We don't use the
1122 * normal reverse bcopy rewrite code.
1124 * We don't handle ugly RFC-related cases yet, but I'm sure PL will complain
1127 dpo_set (&dpo, sr_dpo_type, DPO_PROTO_IP6, t - sm->tunnels);
1129 fib_prefix_t pfx = {
1130 .fp_proto = FIB_PROTOCOL_IP6,
1131 .fp_len = a->dst_mask_width,
1133 .ip6 = *a->dst_address,
1136 fib_table_entry_special_dpo_add (rx_fib_index,
1139 FIB_ENTRY_FLAG_EXCLUSIVE, &dpo);
1144 p = hash_get_mem (sm->policy_index_by_policy_name, a->policy_name);
1147 pt = pool_elt_at_index (sm->policies, p[0]);
1149 else /* no policy, lets create one */
1151 pool_get (sm->policies, pt);
1152 memset (pt, 0, sizeof (*pt));
1153 pt->name = format (0, "%s%c", a->policy_name, 0);
1154 hash_set_mem (sm->policy_index_by_policy_name, pt->name,
1156 p = hash_get_mem (sm->policy_index_by_policy_name, a->policy_name);
1158 vec_add1 (pt->tunnel_indices, t - sm->tunnels);
1160 clib_warning ("p is NULL!");
1161 t->policy_index = p ? p[0] : ~0; /* equiv. to (pt - sm->policies) */
1166 t->name = format (0, "%s%c", a->name, 0);
1167 hash_set_mem (sm->tunnel_index_by_name, t->name, t - sm->tunnels);
1174 * @brief no-op lock function.
1175 * The lifetime of the SR entry is managed by the control plane
1178 sr_dpo_lock (dpo_id_t * dpo)
1183 * @brief no-op unlock function.
1184 * The lifetime of the SR entry is managed by the control plane
1187 sr_dpo_unlock (dpo_id_t * dpo)
1192 format_sr_dpo (u8 * s, va_list * args)
1194 index_t index = va_arg (*args, index_t);
1195 CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
1197 return (format (s, "SR: tunnel:[%d]", index));
1200 const static dpo_vft_t sr_vft = {
1201 .dv_lock = sr_dpo_lock,
1202 .dv_unlock = sr_dpo_unlock,
1203 .dv_format = format_sr_dpo,
1206 const static char *const sr_ip6_nodes[] = {
1211 const static char *const *const sr_nodes[DPO_PROTO_NUM] = {
1212 [DPO_PROTO_IP6] = sr_ip6_nodes,
1216 * @brief CLI parser for Add or Delete a Segment Routing tunnel.
1218 * @param vm vlib_main_t *
1219 * @param input unformat_input_t *
1220 * @param cmd vlib_cli_command_t *
1222 * @return error clib_error_t *
1224 static clib_error_t *
1225 sr_add_del_tunnel_command_fn (vlib_main_t * vm,
1226 unformat_input_t * input,
1227 vlib_cli_command_t * cmd)
1230 ip6_address_t src_address;
1231 int src_address_set = 0;
1232 ip6_address_t dst_address;
1234 int dst_address_set = 0;
1236 u8 *shared_secret = 0;
1238 u8 *policy_name = 0;
1239 u32 rx_table_id = 0;
1240 u32 tx_table_id = 0;
1241 ip6_address_t *segments = 0;
1242 ip6_address_t *this_seg;
1243 ip6_address_t *tags = 0;
1244 ip6_address_t *this_tag;
1245 ip6_sr_add_del_tunnel_args_t _a, *a = &_a;
1246 ip6_address_t next_address, tag;
1250 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1252 if (unformat (input, "del"))
1254 else if (unformat (input, "rx-fib-id %d", &rx_table_id))
1256 else if (unformat (input, "tx-fib-id %d", &tx_table_id))
1258 else if (unformat (input, "src %U", unformat_ip6_address, &src_address))
1259 src_address_set = 1;
1260 else if (unformat (input, "name %s", &name))
1262 else if (unformat (input, "policy %s", &policy_name))
1264 else if (unformat (input, "dst %U/%d",
1265 unformat_ip6_address, &dst_address, &dst_mask_width))
1266 dst_address_set = 1;
1267 else if (unformat (input, "next %U", unformat_ip6_address,
1270 vec_add2 (segments, this_seg, 1);
1271 clib_memcpy (this_seg->as_u8, next_address.as_u8,
1272 sizeof (*this_seg));
1274 else if (unformat (input, "tag %U", unformat_ip6_address, &tag))
1276 vec_add2 (tags, this_tag, 1);
1277 clib_memcpy (this_tag->as_u8, tag.as_u8, sizeof (*this_tag));
1279 else if (unformat (input, "clean"))
1280 flags |= IP6_SR_HEADER_FLAG_CLEANUP;
1281 else if (unformat (input, "protected"))
1282 flags |= IP6_SR_HEADER_FLAG_PROTECTED;
1283 else if (unformat (input, "key %s", &shared_secret))
1284 /* Do not include the trailing NULL byte. Guaranteed interop issue */
1285 _vec_len (shared_secret) -= 1;
1286 else if (unformat (input, "InPE %d", &pl_index))
1288 if (pl_index <= 0 || pl_index > 4)
1290 pl_index_range_error:
1291 return clib_error_return
1292 (0, "Policy List Element Index %d out of range (1-4)",
1296 flags |= IP6_SR_HEADER_FLAG_PL_ELT_INGRESS_PE
1297 << ip6_sr_policy_list_shift_from_index (pl_index);
1299 else if (unformat (input, "EgPE %d", &pl_index))
1301 if (pl_index <= 0 || pl_index > 4)
1302 goto pl_index_range_error;
1303 flags |= IP6_SR_HEADER_FLAG_PL_ELT_EGRESS_PE
1304 << ip6_sr_policy_list_shift_from_index (pl_index);
1306 else if (unformat (input, "OrgSrc %d", &pl_index))
1308 if (pl_index <= 0 || pl_index > 4)
1309 goto pl_index_range_error;
1310 flags |= IP6_SR_HEADER_FLAG_PL_ELT_ORIG_SRC_ADDR
1311 << ip6_sr_policy_list_shift_from_index (pl_index);
1317 if (!src_address_set)
1318 return clib_error_return (0, "src address required");
1320 if (!dst_address_set)
1321 return clib_error_return (0, "dst address required");
1324 return clib_error_return (0, "at least one sr segment required");
1326 memset (a, 0, sizeof (*a));
1327 a->src_address = &src_address;
1328 a->dst_address = &dst_address;
1329 a->dst_mask_width = dst_mask_width;
1330 a->segments = segments;
1332 a->flags_net_byte_order = clib_host_to_net_u16 (flags);
1334 a->rx_table_id = rx_table_id;
1335 a->tx_table_id = tx_table_id;
1336 a->shared_secret = shared_secret;
1343 if (vec_len (policy_name))
1344 a->policy_name = policy_name;
1348 rv = ip6_sr_add_del_tunnel (a);
1350 vec_free (segments);
1352 vec_free (shared_secret);
1360 return clib_error_return (0, "SR tunnel src %U dst %U already exists",
1361 format_ip6_address, &src_address,
1362 format_ip6_address, &dst_address);
1365 return clib_error_return (0, "SR tunnel src %U dst %U does not exist",
1366 format_ip6_address, &src_address,
1367 format_ip6_address, &dst_address);
1370 return clib_error_return (0, "FIB table %d does not exist",
1374 return clib_error_return (0, "At least one segment is required");
1377 return clib_error_return (0, "BUG: ip6_sr_add_del_tunnel returns %d",
1385 VLIB_CLI_COMMAND (sr_tunnel_command, static) = {
1386 .path = "sr tunnel",
1388 "sr tunnel [del] [name <name>] src <addr> dst <addr> [next <addr>] "
1389 "[clean] [reroute] [key <secret>] [policy <policy_name>]"
1390 "[rx-fib-id <fib_id>] [tx-fib-id <fib_id>]",
1391 .function = sr_add_del_tunnel_command_fn,
1396 * @brief Display Segment Routing tunnel
1398 * @param vm vlib_main_t *
1399 * @param t ip6_sr_tunnel_t *
1403 ip6_sr_tunnel_display (vlib_main_t * vm, ip6_sr_tunnel_t * t)
1405 ip6_sr_main_t *sm = &sr_main;
1406 ip6_fib_t *rx_fib, *tx_fib;
1407 ip6_sr_policy_t *pt;
1409 rx_fib = ip6_fib_get (t->rx_fib_index);
1410 tx_fib = ip6_fib_get (t->tx_fib_index);
1413 vlib_cli_output (vm, "sr tunnel name: %s", (char *) t->name);
1415 vlib_cli_output (vm, "src %U dst %U first hop %U",
1416 format_ip6_address, &t->key.src,
1417 format_ip6_address, &t->key.dst,
1418 format_ip6_address, &t->first_hop);
1419 vlib_cli_output (vm, " rx-fib-id %d tx-fib-id %d",
1420 rx_fib->table_id, tx_fib->table_id);
1421 vlib_cli_output (vm, " sr: %U", format_ip6_sr_header, t->rewrite,
1422 0 /* print_hmac */ );
1424 if (t->policy_index != ~0)
1426 pt = pool_elt_at_index (sm->policies, t->policy_index);
1427 vlib_cli_output (vm, "sr policy: %s", (char *) pt->name);
1429 vlib_cli_output (vm, "-------");
1435 * @brief CLI Parser for Display Segment Routing tunnel
1437 * @param vm vlib_main_t *
1438 * @param input unformat_input_t *
1439 * @param cmd vlib_cli_command_t *
1441 * @return error clib_error_t *
1443 static clib_error_t *
1444 show_sr_tunnel_fn (vlib_main_t * vm,
1445 unformat_input_t * input, vlib_cli_command_t * cmd)
1447 static ip6_sr_tunnel_t **tunnels;
1449 ip6_sr_main_t *sm = &sr_main;
1454 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1456 if (unformat (input, "name %s", &name))
1458 p = hash_get_mem (sm->tunnel_index_by_name, name);
1460 vlib_cli_output (vm, "No SR tunnel with name: %s. Showing all.",
1467 vec_reset_length (tunnels);
1469 if (!p) /* Either name parm not passed or no tunnel with that name found, show all */
1472 pool_foreach (t, sm->tunnels,
1474 vec_add1 (tunnels, t);
1478 else /* Just show the one tunnel by name */
1479 vec_add1 (tunnels, &sm->tunnels[p[0]]);
1481 if (vec_len (tunnels) == 0)
1482 vlib_cli_output (vm, "No SR tunnels configured");
1484 for (i = 0; i < vec_len (tunnels); i++)
1487 ip6_sr_tunnel_display (vm, t);
1494 VLIB_CLI_COMMAND (show_sr_tunnel_command, static) = {
1495 .path = "show sr tunnel",
1496 .short_help = "show sr tunnel [name <sr-tunnel-name>]",
1497 .function = show_sr_tunnel_fn,
1502 * @brief Add or Delete a Segment Routing policy
1504 * @param a ip6_sr_add_del_policy_args_t *
1506 * @return retval int
1509 ip6_sr_add_del_policy (ip6_sr_add_del_policy_args_t * a)
1511 ip6_sr_main_t *sm = &sr_main;
1513 ip6_sr_tunnel_t *t = 0;
1514 ip6_sr_policy_t *policy;
1515 u32 *tunnel_indices = 0;
1522 p = hash_get_mem (sm->policy_index_by_policy_name, a->name);
1524 return -6; /* policy name not found */
1526 policy = pool_elt_at_index (sm->policies, p[0]);
1528 vec_foreach_index (i, policy->tunnel_indices)
1530 t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[i]);
1531 t->policy_index = ~0;
1533 hash_unset_mem (sm->policy_index_by_policy_name, a->name);
1534 pool_put (sm->policies, policy);
1539 if (!vec_len (a->tunnel_names))
1540 return -3; /*tunnel name is required case */
1542 vec_reset_length (tunnel_indices);
1543 /* Check tunnel names, add tunnel_index to policy */
1544 for (i = 0; i < vec_len (a->tunnel_names); i++)
1546 p = hash_get_mem (sm->tunnel_index_by_name, a->tunnel_names[i]);
1548 return -4; /* tunnel name not found case */
1550 t = pool_elt_at_index (sm->tunnels, p[0]);
1552 No need to check t==0. -3 condition above ensures name
1554 if (t->policy_index != ~0)
1555 return -5; /* tunnel name already associated with a policy */
1557 /* Add to tunnel indicies */
1558 vec_add1 (tunnel_indices, p[0]);
1561 /* Add policy to ip6_sr_main_t */
1562 pool_get (sm->policies, policy);
1563 policy->name = a->name;
1564 policy->tunnel_indices = tunnel_indices;
1565 hash_set_mem (sm->policy_index_by_policy_name, policy->name,
1566 policy - sm->policies);
1568 /* Yes, this could be construed as overkill but the last thing you should do is set
1569 the policy_index on the tunnel after everything is set in ip6_sr_main_t.
1570 If this is deemed overly cautious, could set this in the vec_len(tunnel_names) loop.
1572 for (i = 0; i < vec_len (policy->tunnel_indices); i++)
1574 t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[i]);
1575 t->policy_index = policy - sm->policies;
1582 * @brief CLI Parser for Add or Delete a Segment Routing policy
1584 * @param vm vlib_main_t *
1585 * @param input unformat_input_t *
1586 * @param cmd vlib_cli_command_t *
1588 * @return error clib_error_t *
1590 static clib_error_t *
1591 sr_add_del_policy_command_fn (vlib_main_t * vm,
1592 unformat_input_t * input,
1593 vlib_cli_command_t * cmd)
1596 u8 **tunnel_names = 0;
1597 u8 *tunnel_name = 0;
1599 ip6_sr_add_del_policy_args_t _a, *a = &_a;
1602 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1604 if (unformat (input, "del"))
1606 else if (unformat (input, "name %s", &name))
1608 else if (unformat (input, "tunnel %s", &tunnel_name))
1612 vec_add1 (tunnel_names, tunnel_name);
1621 return clib_error_return (0, "name of SR policy required");
1624 memset (a, 0, sizeof (*a));
1628 a->tunnel_names = tunnel_names;
1630 rv = ip6_sr_add_del_policy (a);
1632 vec_free (tunnel_names);
1640 return clib_error_return (0,
1641 "tunnel name to associate to SR policy is required");
1644 return clib_error_return (0, "tunnel name not found");
1647 return clib_error_return (0, "tunnel already associated with policy");
1650 return clib_error_return (0, "policy name %s not found", name);
1653 return clib_error_return (0, "TODO: deleting policy name %s", name);
1656 return clib_error_return (0, "BUG: ip6_sr_add_del_policy returns %d",
1664 VLIB_CLI_COMMAND (sr_policy_command, static) = {
1665 .path = "sr policy",
1667 "sr policy [del] name <policy-name> tunnel <sr-tunnel-name> [tunnel <sr-tunnel-name>]*",
1668 .function = sr_add_del_policy_command_fn,
1673 * @brief CLI Parser for Displaying Segment Routing policy
1675 * @param vm vlib_main_t *
1676 * @param input unformat_input_t *
1677 * @param cmd vlib_cli_command_t *
1679 * @return error clib_error_t *
1681 static clib_error_t *
1682 show_sr_policy_fn (vlib_main_t * vm,
1683 unformat_input_t * input, vlib_cli_command_t * cmd)
1685 static ip6_sr_policy_t **policies;
1686 ip6_sr_policy_t *policy;
1688 ip6_sr_main_t *sm = &sr_main;
1693 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1695 if (unformat (input, "name %s", &name))
1697 p = hash_get_mem (sm->policy_index_by_policy_name, name);
1699 vlib_cli_output (vm,
1700 "policy with name %s not found. Showing all.",
1707 vec_reset_length (policies);
1709 if (!p) /* Either name parm not passed or no policy with that name found, show all */
1712 pool_foreach (policy, sm->policies,
1714 vec_add1 (policies, policy);
1718 else /* Just show the one policy by name and a summary of tunnel names */
1720 policy = pool_elt_at_index (sm->policies, p[0]);
1721 vec_add1 (policies, policy);
1724 if (vec_len (policies) == 0)
1725 vlib_cli_output (vm, "No SR policies configured");
1727 for (i = 0; i < vec_len (policies); i++)
1729 policy = policies[i];
1732 vlib_cli_output (vm, "SR policy name: %s", (char *) policy->name);
1733 for (j = 0; j < vec_len (policy->tunnel_indices); j++)
1735 t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[j]);
1736 ip6_sr_tunnel_display (vm, t);
1745 VLIB_CLI_COMMAND (show_sr_policy_command, static) = {
1746 .path = "show sr policy",
1747 .short_help = "show sr policy [name <sr-policy-name>]",
1748 .function = show_sr_policy_fn,
1753 * @brief Add or Delete a mapping of IP6 multicast address
1754 * to Segment Routing policy.
1756 * @param a ip6_sr_add_del_multicastmap_args_t *
1758 * @return retval int
1761 ip6_sr_add_del_multicastmap (ip6_sr_add_del_multicastmap_args_t * a)
1765 ip6_sr_main_t *sm = &sr_main;
1766 ip6_sr_policy_t *pt;
1770 /* clean up the adjacency */
1772 hash_get_mem (sm->policy_index_by_multicast_address,
1773 a->multicast_address);
1777 /* Get our policy by policy_name */
1778 p = hash_get_mem (sm->policy_index_by_policy_name, a->policy_name);
1784 pt = pool_elt_at_index (sm->policies, p[0]);
1787 Get the first tunnel associated with policy populate the fib adjacency.
1788 From there, since this tunnel will have it's policy_index != ~0 it will
1789 be the trigger in the dual_loop to pull up the policy and make a copy-rewrite
1790 for each tunnel in the policy
1793 t = pool_elt_at_index (sm->tunnels, pt->tunnel_indices[0]);
1796 * Stick the tunnel index into the rewrite header.
1798 * Unfortunately, inserting an SR header according to the various
1799 * RFC's requires parsing through the ip6 header, perhaps consing a
1800 * buffer onto the head of the vlib_buffer_t, etc. We don't use the
1801 * normal reverse bcopy rewrite code.
1803 * We don't handle ugly RFC-related cases yet, but I'm sure PL will complain
1806 dpo_id_t dpo = DPO_INVALID;
1808 dpo_set (&dpo, sr_dpo_type, DPO_PROTO_IP6, t - sm->tunnels);
1810 /* Construct a FIB entry for multicast using the rx/tx fib from the first tunnel */
1811 fib_prefix_t pfx = {
1812 .fp_proto = FIB_PROTOCOL_IP6,
1815 .ip6 = *a->multicast_address,
1818 fib_table_entry_special_dpo_add (t->rx_fib_index,
1821 FIB_ENTRY_FLAG_EXCLUSIVE, &dpo);
1825 mcast_copy = vec_new (ip6_address_t, 1);
1826 memcpy (mcast_copy, a->multicast_address, sizeof (ip6_address_t));
1830 hash_unset_mem (sm->policy_index_by_multicast_address, mcast_copy);
1831 vec_free (mcast_copy);
1836 hash_set_mem (sm->policy_index_by_multicast_address, mcast_copy,
1844 * @brief CLI Parser for Adding or Delete a mapping of IP6 multicast address
1845 * to Segment Routing policy.
1847 * @param vm vlib_main_t *
1848 * @param input unformat_input_t *
1849 * @param cmd vlib_cli_command_t *
1851 * @return error clib_error_t *
1853 static clib_error_t *
1854 sr_add_del_multicast_map_command_fn (vlib_main_t * vm,
1855 unformat_input_t * input,
1856 vlib_cli_command_t * cmd)
1859 ip6_address_t multicast_address;
1860 u8 *policy_name = 0;
1861 int multicast_address_set = 0;
1862 ip6_sr_add_del_multicastmap_args_t _a, *a = &_a;
1865 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1867 if (unformat (input, "del"))
1871 (input, "address %U", unformat_ip6_address, &multicast_address))
1872 multicast_address_set = 1;
1873 else if (unformat (input, "sr-policy %s", &policy_name))
1879 if (!is_del && !policy_name)
1880 return clib_error_return (0, "name of sr policy required");
1882 if (!multicast_address_set)
1883 return clib_error_return (0, "multicast address required");
1885 memset (a, 0, sizeof (*a));
1888 a->multicast_address = &multicast_address;
1889 a->policy_name = policy_name;
1891 #if DPDK > 0 /*Cannot call replicate or configure multicast map yet without DPDK */
1892 rv = ip6_sr_add_del_multicastmap (a);
1894 return clib_error_return (0,
1895 "cannot use multicast replicate spray case without DPDK installed");
1903 return clib_error_return (0, "no policy with name: %s", policy_name);
1906 return clib_error_return (0, "multicast map someting ");
1909 return clib_error_return (0,
1910 "tunnel name to associate to SR policy is required");
1913 return clib_error_return (0, "TODO: deleting policy name %s",
1917 return clib_error_return (0, "BUG: ip6_sr_add_del_policy returns %d",
1927 VLIB_CLI_COMMAND (sr_multicast_map_command, static) = {
1928 .path = "sr multicast-map",
1930 "sr multicast-map address <multicast-ip6-address> sr-policy <sr-policy-name> [del]",
1931 .function = sr_add_del_multicast_map_command_fn,
1936 * @brief CLI Parser for Displaying a mapping of IP6 multicast address
1937 * to Segment Routing policy.
1939 * @param vm vlib_main_t *
1940 * @param input unformat_input_t *
1941 * @param cmd vlib_cli_command_t *
1943 * @return error clib_error_t *
1945 static clib_error_t *
1946 show_sr_multicast_map_fn (vlib_main_t * vm,
1947 unformat_input_t * input, vlib_cli_command_t * cmd)
1949 ip6_sr_main_t *sm = &sr_main;
1952 ip6_address_t multicast_address;
1953 ip6_sr_policy_t *pt;
1955 /* pull all entries from the hash table into vector for display */
1958 hash_foreach_mem (key, value, sm->policy_index_by_multicast_address,
1961 vlib_cli_output (vm, "no multicast maps configured");
1964 multicast_address = *((ip6_address_t *)key);
1965 pt = pool_elt_at_index (sm->policies, value);
1968 vlib_cli_output (vm, "address: %U policy: %s",
1969 format_ip6_address, &multicast_address,
1973 vlib_cli_output (vm, "BUG: policy not found for address: %U with policy index %d",
1974 format_ip6_address, &multicast_address,
1986 VLIB_CLI_COMMAND (show_sr_multicast_map_command, static) = {
1987 .path = "show sr multicast-map",
1988 .short_help = "show sr multicast-map",
1989 .function = show_sr_multicast_map_fn,
1994 #define foreach_sr_fix_dst_addr_next \
1995 _(DROP, "error-drop")
1998 * @brief Struct for valid next-nodes for SR fix destination address node
2002 #define _(s,n) SR_FIX_DST_ADDR_NEXT_##s,
2003 foreach_sr_fix_dst_addr_next
2005 SR_FIX_DST_ADDR_N_NEXT,
2006 } sr_fix_dst_addr_next_t;
2009 * @brief Error strings for SR Fix Destination rewrite
2011 static char *sr_fix_dst_error_strings[] = {
2012 #define sr_fix_dst_error(n,s) s,
2013 #include "sr_fix_dst_error.def"
2014 #undef sr_fix_dst_error
2018 * @brief Struct for errors for SR Fix Destination rewrite
2022 #define sr_fix_dst_error(n,s) SR_FIX_DST_ERROR_##n,
2023 #include "sr_fix_dst_error.def"
2024 #undef sr_fix_dst_error
2026 } sr_fix_dst_error_t;
2029 * @brief Information for fix address trace
2033 ip6_address_t src, dst;
2037 } sr_fix_addr_trace_t;
2040 * @brief Formatter for fix address trace
2043 format_sr_fix_addr_trace (u8 * s, va_list * args)
2045 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
2046 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
2047 sr_fix_addr_trace_t *t = va_arg (*args, sr_fix_addr_trace_t *);
2048 vnet_hw_interface_t *hi = 0;
2049 ip_adjacency_t *adj;
2050 ip6_main_t *im = &ip6_main;
2051 ip_lookup_main_t *lm = &im->lookup_main;
2052 vnet_main_t *vnm = vnet_get_main ();
2054 if (t->adj_index != ~0)
2056 adj = ip_get_adjacency (lm, t->adj_index);
2057 hi = vnet_get_sup_hw_interface (vnm, adj->rewrite_header.sw_if_index);
2060 s = format (s, "SR-FIX_ADDR: next %s ip6 src %U dst %U\n",
2061 (t->next_index == SR_FIX_DST_ADDR_NEXT_DROP)
2062 ? "drop" : "output",
2063 format_ip6_address, &t->src, format_ip6_address, &t->dst);
2064 if (t->next_index != SR_FIX_DST_ADDR_NEXT_DROP)
2067 format (s, "%U\n", format_ip6_sr_header, t->sr, 1 /* print_hmac */ );
2069 format (s, " output via %s",
2070 hi ? (char *) (hi->name) : "Invalid adj");
2076 * @brief Fix SR destination address - dual-loop
2078 * @node sr-fix-dst-addr
2079 * @param vm vlib_main_t *
2080 * @param node vlib_node_runtime_t *
2081 * @param from_frame vlib_frame_t *
2083 * @return from_frame->n_vectors uword
2086 sr_fix_dst_addr (vlib_main_t * vm,
2087 vlib_node_runtime_t * node, vlib_frame_t * from_frame)
2089 u32 n_left_from, next_index, *from, *to_next;
2090 ip6_main_t *im = &ip6_main;
2091 ip_lookup_main_t *lm = &im->lookup_main;
2093 from = vlib_frame_vector_args (from_frame);
2094 n_left_from = from_frame->n_vectors;
2096 next_index = node->cached_next_index;
2098 while (n_left_from > 0)
2102 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
2105 while (0 && n_left_from >= 4 && n_left_to_next >= 2)
2108 __attribute__ ((unused)) vlib_buffer_t *b0, *b1;
2109 u32 next0 = SR_FIX_DST_ADDR_NEXT_DROP;
2110 u32 next1 = SR_FIX_DST_ADDR_NEXT_DROP;
2112 /* Prefetch next iteration. */
2114 vlib_buffer_t *p2, *p3;
2116 p2 = vlib_get_buffer (vm, from[2]);
2117 p3 = vlib_get_buffer (vm, from[3]);
2119 vlib_prefetch_buffer_header (p2, LOAD);
2120 vlib_prefetch_buffer_header (p3, LOAD);
2129 n_left_to_next -= 2;
2132 b0 = vlib_get_buffer (vm, bi0);
2133 b1 = vlib_get_buffer (vm, bi1);
2136 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
2137 to_next, n_left_to_next,
2138 bi0, bi1, next0, next1);
2142 while (n_left_from > 0 && n_left_to_next > 0)
2147 ip_adjacency_t *adj0;
2148 ip6_sr_header_t *sr0;
2149 u32 next0 = SR_FIX_DST_ADDR_NEXT_DROP;
2150 ip6_address_t *new_dst0;
2151 ethernet_header_t *eh0;
2158 n_left_to_next -= 1;
2160 b0 = vlib_get_buffer (vm, bi0);
2163 ip_get_adjacency (lm, vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
2164 next0 = adj0->mcast_group_index;
2166 /* We should be pointing at an Ethernet header... */
2167 eh0 = vlib_buffer_get_current (b0);
2168 ip0 = (ip6_header_t *) (eh0 + 1);
2169 sr0 = (ip6_sr_header_t *) (ip0 + 1);
2171 /* We'd better find an SR header... */
2172 if (PREDICT_FALSE (ip0->protocol != IPPROTO_IPV6_ROUTE))
2174 b0->error = node->errors[SR_FIX_DST_ERROR_NO_SR_HEADER];
2180 * We get here from sr_rewrite or sr_local, with
2181 * sr->segments_left pointing at the (copy of the original) dst
2182 * address. Use it, then increment sr0->segments_left.
2185 /* Out of segments? Turf the packet */
2186 if (PREDICT_FALSE (sr0->segments_left == 0))
2188 b0->error = node->errors[SR_FIX_DST_ERROR_NO_MORE_SEGMENTS];
2193 * Rewrite the packet with the original dst address
2194 * We assume that the last segment (in processing order) contains
2195 * the original dst address. The list is reversed, so sr0->segments
2196 * contains the original dst address.
2198 new_dst0 = sr0->segments;
2199 ip0->dst_address.as_u64[0] = new_dst0->as_u64[0];
2200 ip0->dst_address.as_u64[1] = new_dst0->as_u64[1];
2205 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
2207 sr_fix_addr_trace_t *t = vlib_add_trace (vm, node,
2209 t->next_index = next0;
2212 if (next0 != SR_FIX_DST_ADDR_NEXT_DROP)
2214 t->adj_index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
2215 clib_memcpy (t->src.as_u8, ip0->src_address.as_u8,
2216 sizeof (t->src.as_u8));
2217 clib_memcpy (t->dst.as_u8, ip0->dst_address.as_u8,
2218 sizeof (t->dst.as_u8));
2219 clib_memcpy (t->sr, sr0, sizeof (t->sr));
2223 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2224 to_next, n_left_to_next,
2228 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2230 return from_frame->n_vectors;
2235 VLIB_REGISTER_NODE (sr_fix_dst_addr_node) = {
2236 .function = sr_fix_dst_addr,
2237 .name = "sr-fix-dst-addr",
2238 /* Takes a vector of packets. */
2239 .vector_size = sizeof (u32),
2240 .format_trace = format_sr_fix_addr_trace,
2241 .format_buffer = format_ip6_sr_header_with_length,
2243 .runtime_data_bytes = 0,
2245 .n_errors = SR_FIX_DST_N_ERROR,
2246 .error_strings = sr_fix_dst_error_strings,
2248 .n_next_nodes = SR_FIX_DST_ADDR_N_NEXT,
2250 #define _(s,n) [SR_FIX_DST_ADDR_NEXT_##s] = n,
2251 foreach_sr_fix_dst_addr_next
2256 VLIB_NODE_FUNCTION_MULTIARCH (sr_fix_dst_addr_node, sr_fix_dst_addr)
2259 static clib_error_t *
2260 sr_init (vlib_main_t * vm)
2262 ip6_sr_main_t *sm = &sr_main;
2263 clib_error_t *error = 0;
2264 vlib_node_t *ip6_lookup_node, *ip6_rewrite_node;
2266 if ((error = vlib_call_init_function (vm, ip_main_init)))
2269 if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
2273 sm->vnet_main = vnet_get_main ();
2275 vec_validate (sm->hmac_keys, 0);
2276 sm->hmac_keys[0].shared_secret = (u8 *) 0xdeadbeef;
2278 sm->tunnel_index_by_key =
2279 hash_create_mem (0, sizeof (ip6_sr_tunnel_key_t), sizeof (uword));
2281 sm->tunnel_index_by_name = hash_create_string (0, sizeof (uword));
2283 sm->policy_index_by_policy_name = hash_create_string (0, sizeof (uword));
2285 sm->policy_index_by_multicast_address =
2286 hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword));
2288 sm->hmac_key_by_shared_secret = hash_create_string (0, sizeof (uword));
2290 ip6_register_protocol (IPPROTO_IPV6_ROUTE, sr_local_node.index);
2292 ip6_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip6-lookup");
2293 ASSERT (ip6_lookup_node);
2295 ip6_rewrite_node = vlib_get_node_by_name (vm, (u8 *) "ip6-rewrite");
2296 ASSERT (ip6_rewrite_node);
2298 #if DPDK > 0 /* Cannot run replicate without DPDK */
2299 /* Add a disposition to sr_replicate for the sr multicast replicate node */
2300 sm->ip6_lookup_sr_replicate_index =
2301 vlib_node_add_next (vm, ip6_lookup_node->index, sr_replicate_node.index);
2304 /* Add a disposition to ip6_rewrite for the sr dst address hack node */
2305 sm->ip6_rewrite_sr_next_index =
2306 vlib_node_add_next (vm, ip6_rewrite_node->index,
2307 sr_fix_dst_addr_node.index);
2309 OpenSSL_add_all_digests ();
2311 sm->md = (void *) EVP_get_digestbyname ("sha1");
2312 sm->hmac_ctx = clib_mem_alloc (sizeof (HMAC_CTX));
2314 sr_dpo_type = dpo_register_new_type (&sr_vft, sr_nodes);
2319 VLIB_INIT_FUNCTION (sr_init);
2322 * @brief Definition of next-nodes for SR local
2324 #define foreach_sr_local_next \
2325 _ (ERROR, "error-drop") \
2326 _ (IP6_LOOKUP, "ip6-lookup")
2329 * @brief Struct for definition of next-nodes for SR local
2333 #define _(s,n) SR_LOCAL_NEXT_##s,
2334 foreach_sr_local_next
2340 * @brief Struct for packet trace of SR local
2346 ip6_address_t src, dst;
2352 * @brief Definition of SR local error-strings
2354 static char *sr_local_error_strings[] = {
2355 #define sr_error(n,s) s,
2356 #include "sr_error.def"
2361 * @brief Struct for definition of SR local error-strings
2365 #define sr_error(n,s) SR_LOCAL_ERROR_##n,
2366 #include "sr_error.def"
2372 * @brief Format SR local trace
2375 * @param args va_list *
2380 format_sr_local_trace (u8 * s, va_list * args)
2382 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
2383 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
2384 sr_local_trace_t *t = va_arg (*args, sr_local_trace_t *);
2386 s = format (s, "SR-LOCAL: src %U dst %U len %u next_index %d",
2387 format_ip6_address, &t->src,
2388 format_ip6_address, &t->dst, t->length, t->next_index);
2391 format (s, "\n %U", format_ip6_sr_header, t->sr, 1 /* print_hmac */ );
2393 s = format (s, "\n popped SR header");
2399 /* $$$$ fixme: smp, don't copy data, cache input, output (maybe) */
2401 * @brief Validate the SR HMAC
2403 * @param sm ip6_sr_main_t *
2404 * @param ip ip6_header_t *
2405 * @param sr ip6_sr_header_t *
2407 * @return retval int
2410 sr_validate_hmac (ip6_sr_main_t * sm, ip6_header_t * ip, ip6_sr_header_t * sr)
2416 ip6_address_t *addrp;
2418 ip6_sr_hmac_key_t *hmac_key;
2419 static u8 *signature;
2422 key_index = sr->hmac_key;
2424 /* No signature? Pass... */
2428 /* We don't know about this key? Fail... */
2429 if (key_index >= vec_len (sm->hmac_keys))
2432 vec_validate (signature, SHA256_DIGEST_LENGTH - 1);
2434 hmac_key = sm->hmac_keys + key_index;
2436 vec_reset_length (keybuf);
2438 /* pkt ip6 src address */
2439 vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
2440 clib_memcpy (copy_target, ip->src_address.as_u8, sizeof (ip6_address_t));
2443 vec_add2 (keybuf, copy_target, 1);
2444 copy_target[0] = sr->first_segment;
2446 /* octet w/ bit 0 = "clean" flag */
2447 vec_add2 (keybuf, copy_target, 1);
2449 = (sr->flags & clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))
2453 vec_add2 (keybuf, copy_target, 1);
2454 copy_target[0] = sr->hmac_key;
2456 first_segment = sr->first_segment;
2458 addrp = sr->segments;
2461 for (i = 0; i <= first_segment; i++)
2463 vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
2464 clib_memcpy (copy_target, addrp->as_u8, sizeof (ip6_address_t));
2469 clib_warning ("verify key index %d keybuf: %U", key_index,
2470 format_hex_bytes, keybuf, vec_len (keybuf));
2474 /* SHA1 is shorter than SHA-256 */
2475 memset (signature, 0, vec_len (signature));
2477 HMAC_CTX_init (sm->hmac_ctx);
2478 if (!HMAC_Init (sm->hmac_ctx, hmac_key->shared_secret,
2479 vec_len (hmac_key->shared_secret), sm->md))
2480 clib_warning ("barf1");
2481 if (!HMAC_Update (sm->hmac_ctx, keybuf, vec_len (keybuf)))
2482 clib_warning ("barf2");
2483 if (!HMAC_Final (sm->hmac_ctx, signature, &sig_len))
2484 clib_warning ("barf3");
2485 HMAC_CTX_cleanup (sm->hmac_ctx);
2488 clib_warning ("computed signature len %d, value %U", sig_len,
2489 format_hex_bytes, signature, vec_len (signature));
2491 /* Point at the SHA signature in the packet */
2494 clib_warning ("read signature %U", format_hex_bytes, addrp,
2495 SHA256_DIGEST_LENGTH);
2497 return memcmp (signature, addrp, SHA256_DIGEST_LENGTH);
2501 * @brief SR local node
2504 * @param vm vlib_main_t *
2505 * @param node vlib_node_runtime_t *
2506 * @param from_frame vlib_frame_t *
2508 * @return from_frame->n_vectors uword
2511 sr_local (vlib_main_t * vm,
2512 vlib_node_runtime_t * node, vlib_frame_t * from_frame)
2514 u32 n_left_from, next_index, *from, *to_next;
2515 ip6_sr_main_t *sm = &sr_main;
2516 u32 (*sr_local_cb) (vlib_main_t *, vlib_node_runtime_t *,
2517 vlib_buffer_t *, ip6_header_t *, ip6_sr_header_t *);
2518 sr_local_cb = sm->sr_local_cb;
2520 from = vlib_frame_vector_args (from_frame);
2521 n_left_from = from_frame->n_vectors;
2523 next_index = node->cached_next_index;
2525 while (n_left_from > 0)
2529 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
2531 while (n_left_from >= 4 && n_left_to_next >= 2)
2534 vlib_buffer_t *b0, *b1;
2535 ip6_header_t *ip0, *ip1;
2536 ip6_sr_header_t *sr0, *sr1;
2537 ip6_address_t *new_dst0, *new_dst1;
2538 u32 next0 = SR_LOCAL_NEXT_IP6_LOOKUP;
2539 u32 next1 = SR_LOCAL_NEXT_IP6_LOOKUP;
2541 /* Prefetch next iteration. */
2543 vlib_buffer_t *p2, *p3;
2545 p2 = vlib_get_buffer (vm, from[2]);
2546 p3 = vlib_get_buffer (vm, from[3]);
2548 vlib_prefetch_buffer_header (p2, LOAD);
2549 vlib_prefetch_buffer_header (p3, LOAD);
2551 CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
2552 CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
2561 n_left_to_next -= 2;
2565 b0 = vlib_get_buffer (vm, bi0);
2566 ip0 = vlib_buffer_get_current (b0);
2567 sr0 = (ip6_sr_header_t *) (ip0 + 1);
2569 (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2571 ip6_hop_by_hop_ext_t *ext_hdr =
2572 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
2574 (ip6_sr_header_t *) ip6_ext_next_header ((ip6_ext_header_t *)
2578 if (PREDICT_FALSE (sr0->type != ROUTING_HEADER_TYPE_SR))
2580 next0 = SR_LOCAL_NEXT_ERROR;
2582 node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE];
2586 /* Out of segments? Turf the packet */
2587 if (PREDICT_FALSE (sr0->segments_left == 0))
2589 next0 = SR_LOCAL_NEXT_ERROR;
2590 b0->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS];
2594 if (PREDICT_FALSE (sm->validate_hmac))
2596 if (sr_validate_hmac (sm, ip0, sr0))
2598 next0 = SR_LOCAL_NEXT_ERROR;
2599 b0->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID];
2604 next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) : next0;
2607 * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx
2609 if (PREDICT_FALSE (next0 & 0x80000000))
2611 next0 ^= 0xFFFFFFFF;
2612 if (PREDICT_FALSE (next0 == SR_LOCAL_NEXT_ERROR))
2613 b0->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK];
2619 segment_index0 = sr0->segments_left - 1;
2621 /* Rewrite the packet */
2622 new_dst0 = (ip6_address_t *) (sr0->segments + segment_index0);
2623 ip0->dst_address.as_u64[0] = new_dst0->as_u64[0];
2624 ip0->dst_address.as_u64[1] = new_dst0->as_u64[1];
2626 if (PREDICT_TRUE (sr0->segments_left > 0))
2627 sr0->segments_left -= 1;
2630 /* End of the path. Clean up the SR header, or not */
2632 (sr0->segments_left == 0 &&
2634 clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))))
2636 u64 *copy_dst0, *copy_src0;
2638 u32 copy_len_u64s0 = 0;
2642 * Copy the ip6 header right by the (real) length of the
2646 (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2648 ip6_hop_by_hop_ext_t *ext_hdr =
2649 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
2651 (((ip6_ext_header_t *) ext_hdr)->n_data_u64s) + 1;
2652 ext_hdr->next_hdr = sr0->protocol;
2656 ip0->protocol = sr0->protocol;
2658 vlib_buffer_advance (b0, (sr0->length + 1) * 8);
2660 new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
2661 (sr0->length + 1) * 8;
2662 ip0->payload_length = clib_host_to_net_u16 (new_l0);
2664 copy_src0 = (u64 *) ip0;
2665 copy_dst0 = copy_src0 + (sr0->length + 1);
2667 copy_dst0[4 + copy_len_u64s0] = copy_src0[4 + copy_len_u64s0];
2668 copy_dst0[3 + copy_len_u64s0] = copy_src0[3 + copy_len_u64s0];
2669 copy_dst0[2 + copy_len_u64s0] = copy_src0[2 + copy_len_u64s0];
2670 copy_dst0[1 + copy_len_u64s0] = copy_src0[1 + copy_len_u64s0];
2671 copy_dst0[0 + copy_len_u64s0] = copy_src0[0 + copy_len_u64s0];
2673 for (i = copy_len_u64s0 - 1; i >= 0; i--)
2675 copy_dst0[i] = copy_src0[i];
2682 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
2684 sr_local_trace_t *tr = vlib_add_trace (vm, node,
2686 clib_memcpy (tr->src.as_u8, ip0->src_address.as_u8,
2687 sizeof (tr->src.as_u8));
2688 clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
2689 sizeof (tr->dst.as_u8));
2690 tr->length = vlib_buffer_length_in_chain (vm, b0);
2691 tr->next_index = next0;
2692 tr->sr_valid = sr0 != 0;
2694 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
2697 b1 = vlib_get_buffer (vm, bi1);
2698 ip1 = vlib_buffer_get_current (b1);
2699 sr1 = (ip6_sr_header_t *) (ip1 + 1);
2701 (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2704 ip6_hop_by_hop_ext_t *ext_hdr =
2705 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1);
2707 (ip6_sr_header_t *) ip6_ext_next_header ((ip6_ext_header_t *)
2711 if (PREDICT_FALSE (sr1->type != ROUTING_HEADER_TYPE_SR))
2713 next1 = SR_LOCAL_NEXT_ERROR;
2715 node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE];
2719 /* Out of segments? Turf the packet */
2720 if (PREDICT_FALSE (sr1->segments_left == 0))
2722 next1 = SR_LOCAL_NEXT_ERROR;
2723 b1->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS];
2727 if (PREDICT_FALSE (sm->validate_hmac))
2729 if (sr_validate_hmac (sm, ip1, sr1))
2731 next1 = SR_LOCAL_NEXT_ERROR;
2732 b1->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID];
2737 next1 = sr_local_cb ? sr_local_cb (vm, node, b1, ip1, sr1) : next1;
2740 * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx
2742 if (PREDICT_FALSE (next1 & 0x80000000))
2744 next1 ^= 0xFFFFFFFF;
2745 if (PREDICT_FALSE (next1 == SR_LOCAL_NEXT_ERROR))
2746 b1->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK];
2752 segment_index1 = sr1->segments_left - 1;
2754 /* Rewrite the packet */
2755 new_dst1 = (ip6_address_t *) (sr1->segments + segment_index1);
2756 ip1->dst_address.as_u64[0] = new_dst1->as_u64[0];
2757 ip1->dst_address.as_u64[1] = new_dst1->as_u64[1];
2759 if (PREDICT_TRUE (sr1->segments_left > 0))
2760 sr1->segments_left -= 1;
2763 /* End of the path. Clean up the SR header, or not */
2765 (sr1->segments_left == 0 &&
2767 clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))))
2769 u64 *copy_dst1, *copy_src1;
2771 u32 copy_len_u64s1 = 0;
2775 * Copy the ip6 header right by the (real) length of the
2779 (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2781 ip6_hop_by_hop_ext_t *ext_hdr =
2782 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1);
2784 (((ip6_ext_header_t *) ext_hdr)->n_data_u64s) + 1;
2785 ext_hdr->next_hdr = sr1->protocol;
2789 ip1->protocol = sr1->protocol;
2791 vlib_buffer_advance (b1, (sr1->length + 1) * 8);
2793 new_l1 = clib_net_to_host_u16 (ip1->payload_length) -
2794 (sr1->length + 1) * 8;
2795 ip1->payload_length = clib_host_to_net_u16 (new_l1);
2797 copy_src1 = (u64 *) ip1;
2798 copy_dst1 = copy_src1 + (sr1->length + 1);
2800 copy_dst1[4 + copy_len_u64s1] = copy_src1[4 + copy_len_u64s1];
2801 copy_dst1[3 + copy_len_u64s1] = copy_src1[3 + copy_len_u64s1];
2802 copy_dst1[2 + copy_len_u64s1] = copy_src1[2 + copy_len_u64s1];
2803 copy_dst1[1 + copy_len_u64s1] = copy_src1[1 + copy_len_u64s1];
2804 copy_dst1[0 + copy_len_u64s1] = copy_src1[0 + copy_len_u64s1];
2806 for (i = copy_len_u64s1 - 1; i >= 0; i--)
2808 copy_dst1[i] = copy_src1[i];
2815 if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
2817 sr_local_trace_t *tr = vlib_add_trace (vm, node,
2819 clib_memcpy (tr->src.as_u8, ip1->src_address.as_u8,
2820 sizeof (tr->src.as_u8));
2821 clib_memcpy (tr->dst.as_u8, ip1->dst_address.as_u8,
2822 sizeof (tr->dst.as_u8));
2823 tr->length = vlib_buffer_length_in_chain (vm, b1);
2824 tr->next_index = next1;
2825 tr->sr_valid = sr1 != 0;
2827 clib_memcpy (tr->sr, sr1, sizeof (tr->sr));
2830 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
2831 to_next, n_left_to_next,
2832 bi0, bi1, next0, next1);
2835 while (n_left_from > 0 && n_left_to_next > 0)
2839 ip6_header_t *ip0 = 0;
2840 ip6_sr_header_t *sr0;
2841 ip6_address_t *new_dst0;
2842 u32 next0 = SR_LOCAL_NEXT_IP6_LOOKUP;
2849 n_left_to_next -= 1;
2851 b0 = vlib_get_buffer (vm, bi0);
2852 ip0 = vlib_buffer_get_current (b0);
2853 sr0 = (ip6_sr_header_t *) (ip0 + 1);
2856 (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2858 ip6_hop_by_hop_ext_t *ext_hdr =
2859 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
2861 (ip6_sr_header_t *) ip6_ext_next_header ((ip6_ext_header_t *)
2864 if (PREDICT_FALSE (sr0->type != ROUTING_HEADER_TYPE_SR))
2866 next0 = SR_LOCAL_NEXT_ERROR;
2868 node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE];
2872 /* Out of segments? Turf the packet */
2873 if (PREDICT_FALSE (sr0->segments_left == 0))
2875 next0 = SR_LOCAL_NEXT_ERROR;
2876 b0->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS];
2880 if (PREDICT_FALSE (sm->validate_hmac))
2882 if (sr_validate_hmac (sm, ip0, sr0))
2884 next0 = SR_LOCAL_NEXT_ERROR;
2885 b0->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID];
2890 next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) : next0;
2893 * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx
2895 if (PREDICT_FALSE (next0 & 0x80000000))
2897 next0 ^= 0xFFFFFFFF;
2898 if (PREDICT_FALSE (next0 == SR_LOCAL_NEXT_ERROR))
2899 b0->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK];
2905 segment_index0 = sr0->segments_left - 1;
2907 /* Rewrite the packet */
2908 new_dst0 = (ip6_address_t *) (sr0->segments + segment_index0);
2909 ip0->dst_address.as_u64[0] = new_dst0->as_u64[0];
2910 ip0->dst_address.as_u64[1] = new_dst0->as_u64[1];
2912 if (PREDICT_TRUE (sr0->segments_left > 0))
2913 sr0->segments_left -= 1;
2916 /* End of the path. Clean up the SR header, or not */
2918 (sr0->segments_left == 0 &&
2920 clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))))
2922 u64 *copy_dst0, *copy_src0;
2924 u32 copy_len_u64s0 = 0;
2928 * Copy the ip6 header right by the (real) length of the
2932 (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2934 ip6_hop_by_hop_ext_t *ext_hdr =
2935 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
2937 (((ip6_ext_header_t *) ext_hdr)->n_data_u64s) + 1;
2938 ext_hdr->next_hdr = sr0->protocol;
2942 ip0->protocol = sr0->protocol;
2945 vlib_buffer_advance (b0, (sr0->length + 1) * 8);
2947 new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
2948 (sr0->length + 1) * 8;
2949 ip0->payload_length = clib_host_to_net_u16 (new_l0);
2951 copy_src0 = (u64 *) ip0;
2952 copy_dst0 = copy_src0 + (sr0->length + 1);
2953 copy_dst0[4 + copy_len_u64s0] = copy_src0[4 + copy_len_u64s0];
2954 copy_dst0[3 + copy_len_u64s0] = copy_src0[3 + copy_len_u64s0];
2955 copy_dst0[2 + copy_len_u64s0] = copy_src0[2 + copy_len_u64s0];
2956 copy_dst0[1 + copy_len_u64s0] = copy_src0[1 + copy_len_u64s0];
2957 copy_dst0[0 + copy_len_u64s0] = copy_src0[0 + copy_len_u64s0];
2959 for (i = copy_len_u64s0 - 1; i >= 0; i--)
2961 copy_dst0[i] = copy_src0[i];
2968 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
2970 sr_local_trace_t *tr = vlib_add_trace (vm, node,
2972 clib_memcpy (tr->src.as_u8, ip0->src_address.as_u8,
2973 sizeof (tr->src.as_u8));
2974 clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
2975 sizeof (tr->dst.as_u8));
2976 tr->length = vlib_buffer_length_in_chain (vm, b0);
2977 tr->next_index = next0;
2978 tr->sr_valid = sr0 != 0;
2980 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
2983 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2984 to_next, n_left_to_next,
2988 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2990 vlib_node_increment_counter (vm, sr_local_node.index,
2991 SR_LOCAL_ERROR_PKTS_PROCESSED,
2992 from_frame->n_vectors);
2993 return from_frame->n_vectors;
2997 VLIB_REGISTER_NODE (sr_local_node, static) = {
2998 .function = sr_local,
3000 /* Takes a vector of packets. */
3001 .vector_size = sizeof (u32),
3002 .format_trace = format_sr_local_trace,
3004 .runtime_data_bytes = 0,
3006 .n_errors = SR_LOCAL_N_ERROR,
3007 .error_strings = sr_local_error_strings,
3009 .n_next_nodes = SR_LOCAL_N_NEXT,
3011 #define _(s,n) [SR_LOCAL_NEXT_##s] = n,
3012 foreach_sr_local_next
3017 VLIB_NODE_FUNCTION_MULTIARCH (sr_local_node, sr_local)
3021 sr_get_main (vlib_main_t * vm)
3023 vlib_call_init_function (vm, sr_init);
3024 ASSERT (sr_local_node.index);
3029 * @brief CLI parser for SR fix destination rewrite node
3031 * @param vm vlib_main_t *
3032 * @param input unformat_input_t *
3033 * @param cmd vlib_cli_command_t *
3035 * @return error clib_error_t *
3037 static clib_error_t *
3038 set_ip6_sr_rewrite_fn (vlib_main_t * vm,
3039 unformat_input_t * input, vlib_cli_command_t * cmd)
3041 fib_prefix_t pfx = {
3042 .fp_proto = FIB_PROTOCOL_IP6,
3048 ip_adjacency_t *adj;
3049 vnet_hw_interface_t *hi;
3051 ip6_sr_main_t *sm = &sr_main;
3052 vnet_main_t *vnm = vnet_get_main ();
3053 fib_node_index_t fei;
3055 if (!unformat (input, "%U", unformat_ip6_address, &pfx.fp_addr.ip6))
3056 return clib_error_return (0, "ip6 address missing in '%U'",
3057 format_unformat_error, input);
3059 if (unformat (input, "rx-table-id %d", &fib_id))
3061 fib_index = fib_table_id_find_fib_index (FIB_PROTOCOL_IP6, fib_id);
3062 if (fib_index == ~0)
3063 return clib_error_return (0, "fib-id %d not found", fib_id);
3066 fei = fib_table_lookup_exact_match (fib_index, &pfx);
3068 if (FIB_NODE_INDEX_INVALID == fei)
3069 return clib_error_return (0, "no match for %U",
3070 format_ip6_address, &pfx.fp_addr.ip6);
3072 adj_index = fib_entry_get_adj_for_source (fei, FIB_SOURCE_SR);
3074 if (ADJ_INDEX_INVALID == adj_index)
3075 return clib_error_return (0, "%U not SR sourced",
3076 format_ip6_address, &pfx.fp_addr.ip6);
3078 adj = adj_get (adj_index);
3080 if (adj->lookup_next_index != IP_LOOKUP_NEXT_REWRITE)
3081 return clib_error_return (0, "%U unresolved (not a rewrite adj)",
3082 format_ip6_address, &pfx.fp_addr.ip6);
3084 adj->rewrite_header.next_index = sm->ip6_rewrite_sr_next_index;
3086 sw_if_index = adj->rewrite_header.sw_if_index;
3087 hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
3088 adj->rewrite_header.node_index = sr_fix_dst_addr_node.index;
3090 /* $$$$$ hack... steal the mcast group index */
3091 adj->mcast_group_index =
3092 vlib_node_add_next (vm, sr_fix_dst_addr_node.index,
3093 hi->output_node_index);
3099 VLIB_CLI_COMMAND (set_ip6_sr_rewrite, static) = {
3100 .path = "set ip6 sr rewrite",
3101 .short_help = "set ip6 sr rewrite <ip6-address> [fib-id <id>]",
3102 .function = set_ip6_sr_rewrite_fn,
3107 * @brief Register a callback routine to set next0 in sr_local
3112 vnet_register_sr_app_callback (void *cb)
3114 ip6_sr_main_t *sm = &sr_main;
3116 sm->sr_local_cb = cb;
3120 * @brief Test routine for validation of HMAC
3122 static clib_error_t *
3123 test_sr_hmac_validate_fn (vlib_main_t * vm,
3124 unformat_input_t * input, vlib_cli_command_t * cmd)
3126 ip6_sr_main_t *sm = &sr_main;
3128 if (unformat (input, "validate on"))
3129 sm->validate_hmac = 1;
3130 else if (unformat (input, "chunk-offset off"))
3131 sm->validate_hmac = 0;
3133 return clib_error_return (0, "expected validate on|off in '%U'",
3134 format_unformat_error, input);
3136 vlib_cli_output (vm, "hmac signature validation %s",
3137 sm->validate_hmac ? "on" : "off");
3142 VLIB_CLI_COMMAND (test_sr_hmac_validate, static) = {
3143 .path = "test sr hmac",
3144 .short_help = "test sr hmac validate [on|off]",
3145 .function = test_sr_hmac_validate_fn,
3150 * @brief Add or Delete HMAC key
3152 * @param sm ip6_sr_main_t *
3154 * @param shared_secret u8 *
3157 * @return retval i32
3159 // $$$ fixme shouldn't return i32
3161 sr_hmac_add_del_key (ip6_sr_main_t * sm, u32 key_id, u8 * shared_secret,
3165 ip6_sr_hmac_key_t *key;
3169 /* Specific key in use? Fail. */
3170 if (key_id && vec_len (sm->hmac_keys) > key_id
3171 && sm->hmac_keys[key_id].shared_secret)
3175 key = find_or_add_shared_secret (sm, shared_secret, &index);
3176 ASSERT (index == key_id);
3182 if (key_id) /* delete by key ID */
3184 if (vec_len (sm->hmac_keys) <= key_id)
3187 key = sm->hmac_keys + key_id;
3189 hash_unset_mem (sm->hmac_key_by_shared_secret, key->shared_secret);
3190 vec_free (key->shared_secret);
3195 key = find_or_add_shared_secret (sm, shared_secret, &index);
3196 hash_unset_mem (sm->hmac_key_by_shared_secret, key->shared_secret);
3197 vec_free (key->shared_secret);
3202 static clib_error_t *
3203 sr_hmac_add_del_key_fn (vlib_main_t * vm,
3204 unformat_input_t * input, vlib_cli_command_t * cmd)
3206 ip6_sr_main_t *sm = &sr_main;
3210 u8 *shared_secret = 0;
3213 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3215 if (unformat (input, "del"))
3217 else if (unformat (input, "id %d", &key_id))
3219 else if (unformat (input, "key %s", &shared_secret))
3221 /* Do not include the trailing NULL byte. Guaranteed interop issue */
3222 _vec_len (shared_secret) -= 1;
3228 if (is_del == 0 && shared_secret == 0)
3229 return clib_error_return (0, "shared secret must be set to add a key");
3231 if (shared_secret == 0 && key_id_set == 0)
3232 return clib_error_return (0, "shared secret and key id both unset");
3234 rv = sr_hmac_add_del_key (sm, key_id, shared_secret, is_del);
3236 vec_free (shared_secret);
3244 return clib_error_return (0, "sr_hmac_add_del_key returned %d", rv);
3251 VLIB_CLI_COMMAND (sr_hmac, static) = {
3253 .short_help = "sr hmac [del] id <nn> key <str>",
3254 .function = sr_hmac_add_del_key_fn,
3259 * @brief CLI parser for show HMAC key shared secrets
3261 * @param vm vlib_main_t *
3262 * @param input unformat_input_t *
3263 * @param cmd vlib_cli_command_t *
3265 * @return error clib_error_t *
3267 static clib_error_t *
3268 show_sr_hmac_fn (vlib_main_t * vm,
3269 unformat_input_t * input, vlib_cli_command_t * cmd)
3271 ip6_sr_main_t *sm = &sr_main;
3274 for (i = 1; i < vec_len (sm->hmac_keys); i++)
3276 if (sm->hmac_keys[i].shared_secret)
3277 vlib_cli_output (vm, "[%d]: %v", i, sm->hmac_keys[i].shared_secret);
3284 VLIB_CLI_COMMAND (show_sr_hmac, static) = {
3285 .path = "show sr hmac",
3286 .short_help = "show sr hmac",
3287 .function = show_sr_hmac_fn,
3292 * @brief Test for SR debug flag
3294 * @param vm vlib_main_t *
3295 * @param input unformat_input_t *
3296 * @param cmd vlib_cli_command_t *
3298 * @return error clib_error_t *
3300 static clib_error_t *
3301 test_sr_debug_fn (vlib_main_t * vm,
3302 unformat_input_t * input, vlib_cli_command_t * cmd)
3304 ip6_sr_main_t *sm = &sr_main;
3306 if (unformat (input, "on"))
3308 else if (unformat (input, "off"))
3311 return clib_error_return (0, "expected on|off in '%U'",
3312 format_unformat_error, input);
3314 vlib_cli_output (vm, "debug trace now %s", sm->is_debug ? "on" : "off");
3320 VLIB_CLI_COMMAND (test_sr_debug, static) = {
3321 .path = "test sr debug",
3322 .short_help = "test sr debug on|off",
3323 .function = test_sr_debug_fn,
3328 * fd.io coding-style-patch-verification: ON
3331 * eval: (c-set-style "gnu")