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/mfib/mfib_table.h>
27 #include <vnet/dpo/dpo.h>
28 #include <vnet/dpo/replicate_dpo.h>
30 #include <openssl/hmac.h>
32 ip6_sr_main_t sr_main;
33 static vlib_node_registration_t sr_local_node;
36 * @brief Dynamically added SR DPO type
38 static dpo_type_t sr_dpo_type;
41 * @brief Dynamically added SR FIB Node type
43 static fib_node_type_t sr_fib_node_type;
46 * @brief Use passed HMAC key in ip6_sr_header_t in OpenSSL HMAC routines
48 * @param sm ip6_sr_main_t *
49 * @param ip ip6_header_t *
50 * @param sr ip6_sr_header_t *
53 sr_fix_hmac (ip6_sr_main_t * sm, ip6_header_t * ip, ip6_sr_header_t * sr)
61 ip6_sr_hmac_key_t *hmac_key;
64 key_index = sr->hmac_key;
66 /* No signature? Pass... */
70 /* We don't know about this key? Fail... */
71 if (key_index >= vec_len (sm->hmac_keys))
74 hmac_key = sm->hmac_keys + key_index;
76 vec_reset_length (keybuf);
78 /* pkt ip6 src address */
79 vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
80 clib_memcpy (copy_target, ip->src_address.as_u8, sizeof (ip6_address_t));
83 vec_add2 (keybuf, copy_target, 1);
84 copy_target[0] = sr->first_segment;
86 /* octet w/ bit 0 = "clean" flag */
87 vec_add2 (keybuf, copy_target, 1);
89 = (sr->flags & clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))
93 vec_add2 (keybuf, copy_target, 1);
94 copy_target[0] = sr->hmac_key;
96 first_segment = sr->first_segment;
101 for (i = 0; i <= first_segment; i++)
103 vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
104 clib_memcpy (copy_target, addrp->as_u8, sizeof (ip6_address_t));
110 HMAC_CTX_init (sm->hmac_ctx);
111 if (!HMAC_Init (sm->hmac_ctx, hmac_key->shared_secret,
112 vec_len (hmac_key->shared_secret), sm->md))
113 clib_warning ("barf1");
114 if (!HMAC_Update (sm->hmac_ctx, keybuf, vec_len (keybuf)))
115 clib_warning ("barf2");
116 if (!HMAC_Final (sm->hmac_ctx, (unsigned char *) addrp, &sig_len))
117 clib_warning ("barf3");
118 HMAC_CTX_cleanup (sm->hmac_ctx);
122 * @brief Format function for decoding various SR flags
124 * @param s u8 * - formatted string
125 * @param args va_list * - u16 flags
127 * @return formatted output string u8 *
130 format_ip6_sr_header_flags (u8 * s, va_list * args)
132 u16 flags = (u16) va_arg (*args, int);
134 int bswap_needed = va_arg (*args, int);
138 flags = clib_host_to_net_u16 (flags);
140 if (flags & IP6_SR_HEADER_FLAG_CLEANUP)
141 s = format (s, "cleanup ");
143 if (flags & IP6_SR_HEADER_FLAG_PROTECTED)
144 s = format (s, "reroute ");
146 s = format (s, "pl: ");
147 for (i = 1; i <= 4; i++)
149 pl_flag = ip6_sr_policy_list_flags (flags, i);
150 s = format (s, "[%d] ", i);
154 case IP6_SR_HEADER_FLAG_PL_ELT_NOT_PRESENT:
155 s = format (s, "NotPr ");
157 case IP6_SR_HEADER_FLAG_PL_ELT_INGRESS_PE:
158 s = format (s, "InPE ");
160 case IP6_SR_HEADER_FLAG_PL_ELT_EGRESS_PE:
161 s = format (s, "EgPE ");
164 case IP6_SR_HEADER_FLAG_PL_ELT_ORIG_SRC_ADDR:
165 s = format (s, "OrgSrc ");
173 * @brief Format function for decoding ip6_sr_header_t
175 * @param s u8 * - formatted string
176 * @param args va_list * - ip6_sr_header_t
178 * @return formatted output string u8 *
181 format_ip6_sr_header (u8 * s, va_list * args)
183 ip6_sr_header_t *h = va_arg (*args, ip6_sr_header_t *);
184 ip6_address_t placeholder_addr =
185 { {254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
188 int print_hmac = va_arg (*args, int);
189 int i, pl_index, max_segs;
190 int flags_host_byte_order = clib_net_to_host_u16 (h->flags);
192 s = format (s, "next proto %d, len %d, type %d",
193 h->protocol, (h->length << 3) + 8, h->type);
194 s = format (s, "\n segs left %d, first_segment %d, hmac key %d",
195 h->segments_left, h->first_segment, h->hmac_key);
196 s = format (s, "\n flags %U", format_ip6_sr_header_flags,
197 flags_host_byte_order, 0 /* bswap needed */ );
200 * Header length is in 8-byte units (minus one), so
201 * divide by 2 to ascertain the number of ip6 addresses in the
204 max_segs = (h->length >> 1);
206 if (!print_hmac && h->hmac_key)
209 s = format (s, "\n Segments (in processing order):");
211 for (i = h->first_segment; i >= 1; i--)
212 s = format (s, "\n %U", format_ip6_address, h->segments + i);
213 if (ip6_address_is_equal (&placeholder_addr, h->segments))
214 s = format (s, "\n (empty placeholder)");
216 s = format (s, "\n %U", format_ip6_address, h->segments);
218 s = format (s, "\n Policy List:");
220 pl_index = 1; /* to match the RFC text */
221 for (i = (h->first_segment + 1); i < max_segs; i++, pl_index++)
224 char *tags[] = { " ", "InPE: ", "EgPE: ", "OrgSrc: " };
227 if (pl_index >= 1 && pl_index <= 4)
229 int this_pl_flag = ip6_sr_policy_list_flags
230 (flags_host_byte_order, pl_index);
231 tag = tags[this_pl_flag];
234 s = format (s, "\n %s%U", tag, format_ip6_address, h->segments + i);
241 * @brief Format function for decoding ip6_sr_header_t with length
243 * @param s u8 * - formatted string
244 * @param args va_list * - ip6_header_t + ip6_sr_header_t
246 * @return formatted output string u8 *
249 format_ip6_sr_header_with_length (u8 * s, va_list * args)
251 ip6_header_t *h = va_arg (*args, ip6_header_t *);
252 u32 max_header_bytes = va_arg (*args, u32);
255 header_bytes = sizeof (h[0]) + sizeof (ip6_sr_header_t);
256 if (max_header_bytes != 0 && header_bytes > max_header_bytes)
257 return format (s, "ip6_sr header truncated");
259 s = format (s, "IP6: %U\n", format_ip6_header, h, max_header_bytes);
261 format (s, "SR: %U\n", format_ip6_sr_header, (ip6_sr_header_t *) (h + 1),
262 0 /* print_hmac */ , max_header_bytes);
267 * @brief Defined valid next nodes
269 #define foreach_sr_rewrite_next \
270 _(ERROR, "error-drop") \
271 _(SR_LOCAL, "sr-local")
274 * @brief Struct for defined valid next nodes
278 #define _(s,n) SR_REWRITE_NEXT_##s,
279 foreach_sr_rewrite_next
285 * @brief Struct for data for SR rewrite packet trace
289 ip6_address_t src, dst;
294 } sr_rewrite_trace_t;
297 * @brief Error strings for SR rewrite
299 static char *sr_rewrite_error_strings[] = {
300 #define sr_error(n,s) s,
301 #include "sr_error.def"
306 * @brief Struct for SR rewrite error strings
310 #define sr_error(n,s) SR_REWRITE_ERROR_##n,
311 #include "sr_error.def"
314 } sr_rewrite_error_t;
318 * @brief Format function for SR rewrite trace.
321 format_sr_rewrite_trace (u8 * s, va_list * args)
323 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
324 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
325 sr_rewrite_trace_t *t = va_arg (*args, sr_rewrite_trace_t *);
326 ip6_sr_main_t *sm = &sr_main;
327 ip6_sr_tunnel_t *tun = pool_elt_at_index (sm->tunnels, t->tunnel_index);
328 ip6_fib_t *rx_fib, *tx_fib;
330 rx_fib = ip6_fib_get (tun->rx_fib_index);
331 tx_fib = ip6_fib_get (tun->tx_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_sr_main_t *sm = &sr_main;
361 u32 (*sr_local_cb) (vlib_main_t *, vlib_node_runtime_t *,
362 vlib_buffer_t *, ip6_header_t *, ip6_sr_header_t *);
363 sr_local_cb = sm->sr_local_cb;
365 from = vlib_frame_vector_args (from_frame);
366 n_left_from = from_frame->n_vectors;
368 next_index = node->cached_next_index;
370 while (n_left_from > 0)
374 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
376 /* Note 2x loop disabled */
377 while (0 && n_left_from >= 4 && n_left_to_next >= 2)
380 vlib_buffer_t *b0, *b1;
381 ip6_header_t *ip0, *ip1;
382 ip6_sr_header_t *sr0, *sr1;
383 ip6_sr_tunnel_t *t0, *t1;
389 /* Prefetch next iteration. */
391 vlib_buffer_t *p2, *p3;
393 p2 = vlib_get_buffer (vm, from[2]);
394 p3 = vlib_get_buffer (vm, from[3]);
396 vlib_prefetch_buffer_header (p2, LOAD);
397 vlib_prefetch_buffer_header (p3, LOAD);
409 b0 = vlib_get_buffer (vm, bi0);
410 b1 = vlib_get_buffer (vm, bi1);
413 * $$$ parse through header(s) to pick the point
414 * where we punch in the SR extention header
417 pool_elt_at_index (sm->tunnels,
418 vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
420 pool_elt_at_index (sm->tunnels,
421 vnet_buffer (b1)->ip.adj_index[VLIB_TX]);
423 ASSERT (VLIB_BUFFER_PRE_DATA_SIZE
424 >= ((word) vec_len (t0->rewrite)) + b0->current_data);
425 ASSERT (VLIB_BUFFER_PRE_DATA_SIZE
426 >= ((word) vec_len (t1->rewrite)) + b1->current_data);
428 vnet_buffer (b0)->sw_if_index[VLIB_TX] = t0->tx_fib_index;
429 vnet_buffer (b1)->sw_if_index[VLIB_TX] = t1->tx_fib_index;
431 ip0 = vlib_buffer_get_current (b0);
432 ip1 = vlib_buffer_get_current (b1);
435 * SR-unaware service chaining case: pkt coming back from
436 * service has the original dst address, and will already
437 * have an SR header. If so, send it to sr-local
439 if (PREDICT_FALSE (ip0->protocol == IPPROTO_IPV6_ROUTE))
441 vlib_buffer_advance (b0, sizeof (ip0));
442 sr0 = (ip6_sr_header_t *) (ip0 + 1);
443 new_l0 = clib_net_to_host_u16 (ip0->payload_length);
444 next0 = SR_REWRITE_NEXT_SR_LOCAL;
448 u32 len_bytes = sizeof (ip6_header_t);
449 u8 next_hdr = ip0->protocol;
451 /* HBH must immediately follow ipv6 header */
453 (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
455 ip6_hop_by_hop_ext_t *ext_hdr =
456 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
458 ip6_ext_header_len ((ip6_ext_header_t *) ext_hdr);
459 /* Ignoring the sr_local for now, if RH follows HBH here */
460 next_hdr = ext_hdr->next_hdr;
461 ext_hdr->next_hdr = IPPROTO_IPV6_ROUTE;
465 ip0->protocol = IPPROTO_IPV6_ROUTE; /* routing extension header */
468 * Copy data before the punch-in point left by the
469 * required amount. Assume (for the moment) that only
470 * the main packet header needs to be copied.
472 clib_memcpy (((u8 *) ip0) - vec_len (t0->rewrite),
474 vlib_buffer_advance (b0, -(word) vec_len (t0->rewrite));
475 ip0 = vlib_buffer_get_current (b0);
476 sr0 = (ip6_sr_header_t *) ((u8 *) ip0 + len_bytes);
478 clib_memcpy (sr0, t0->rewrite, vec_len (t0->rewrite));
480 /* Fix the next header chain */
481 sr0->protocol = next_hdr;
483 new_l0 = clib_net_to_host_u16 (ip0->payload_length) +
484 vec_len (t0->rewrite);
485 ip0->payload_length = clib_host_to_net_u16 (new_l0);
487 /* Copy dst address into the DA slot in the segment list */
488 clib_memcpy (sr0->segments, ip0->dst_address.as_u64,
489 sizeof (ip6_address_t));
490 /* Rewrite the ip6 dst address with the first hop */
491 clib_memcpy (ip0->dst_address.as_u64, t0->first_hop.as_u64,
492 sizeof (ip6_address_t));
494 sr_fix_hmac (sm, ip0, sr0);
496 vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
497 t0->first_hop_dpo.dpoi_index;
498 next0 = t0->first_hop_dpo.dpoi_next_node;
499 next0 = (sr_local_cb ?
500 sr_local_cb (vm, node, b0, ip0, sr0) : next0);
503 * Ignore "do not rewrite" shtik in this path
505 if (PREDICT_FALSE (next0 & 0x80000000))
508 if (PREDICT_FALSE (next0 == SR_REWRITE_NEXT_ERROR))
509 b0->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK];
513 if (PREDICT_FALSE (ip1->protocol == IPPROTO_IPV6_ROUTE))
515 vlib_buffer_advance (b1, sizeof (ip1));
516 sr1 = (ip6_sr_header_t *) (ip1 + 1);
517 new_l1 = clib_net_to_host_u16 (ip1->payload_length);
518 next1 = SR_REWRITE_NEXT_SR_LOCAL;
522 u32 len_bytes = sizeof (ip6_header_t);
523 u8 next_hdr = ip1->protocol;
525 /* HBH must immediately follow ipv6 header */
527 (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
529 ip6_hop_by_hop_ext_t *ext_hdr =
530 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1);
532 ip6_ext_header_len ((ip6_ext_header_t *) ext_hdr);
533 /* Ignoring the sr_local for now, if RH follows HBH here */
534 next_hdr = ext_hdr->next_hdr;
535 ext_hdr->next_hdr = IPPROTO_IPV6_ROUTE;
539 ip1->protocol = IPPROTO_IPV6_ROUTE;
542 * Copy data before the punch-in point left by the
543 * required amount. Assume (for the moment) that only
544 * the main packet header needs to be copied.
546 clib_memcpy (((u8 *) ip1) - vec_len (t1->rewrite),
548 vlib_buffer_advance (b1, -(word) vec_len (t1->rewrite));
549 ip1 = vlib_buffer_get_current (b1);
550 sr1 = (ip6_sr_header_t *) ((u8 *) ip1 + len_bytes);
551 clib_memcpy (sr1, t1->rewrite, vec_len (t1->rewrite));
553 sr1->protocol = next_hdr;
554 new_l1 = clib_net_to_host_u16 (ip1->payload_length) +
555 vec_len (t1->rewrite);
556 ip1->payload_length = clib_host_to_net_u16 (new_l1);
558 /* Copy dst address into the DA slot in the segment list */
559 clib_memcpy (sr1->segments, ip1->dst_address.as_u64,
560 sizeof (ip6_address_t));
561 /* Rewrite the ip6 dst address with the first hop */
562 clib_memcpy (ip1->dst_address.as_u64, t1->first_hop.as_u64,
563 sizeof (ip6_address_t));
565 sr_fix_hmac (sm, ip1, sr1);
567 vnet_buffer (b1)->ip.adj_index[VLIB_TX] =
568 t1->first_hop_dpo.dpoi_index;
569 next1 = t1->first_hop_dpo.dpoi_next_node;
570 next1 = (sr_local_cb ?
571 sr_local_cb (vm, node, b1, ip1, sr1) : next1);
574 * Ignore "do not rewrite" shtik in this path
576 if (PREDICT_FALSE (next1 & 0x80000000))
579 if (PREDICT_FALSE (next1 == SR_REWRITE_NEXT_ERROR))
580 b1->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK];
584 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
586 sr_rewrite_trace_t *tr = vlib_add_trace (vm, node,
588 tr->tunnel_index = t0 - sm->tunnels;
589 clib_memcpy (tr->src.as_u8, ip0->src_address.as_u8,
590 sizeof (tr->src.as_u8));
591 clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
592 sizeof (tr->dst.as_u8));
594 tr->next_index = next0;
596 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
598 if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
600 sr_rewrite_trace_t *tr = vlib_add_trace (vm, node,
602 tr->tunnel_index = t1 - sm->tunnels;
603 clib_memcpy (tr->src.as_u8, ip1->src_address.as_u8,
604 sizeof (tr->src.as_u8));
605 clib_memcpy (tr->dst.as_u8, ip1->dst_address.as_u8,
606 sizeof (tr->dst.as_u8));
608 tr->next_index = next1;
610 clib_memcpy (tr->sr, sr1, sizeof (tr->sr));
612 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
613 to_next, n_left_to_next,
614 bi0, bi1, next0, next1);
617 while (n_left_from > 0 && n_left_to_next > 0)
621 ip6_header_t *ip0 = 0;
622 ip6_sr_header_t *sr0 = 0;
634 b0 = vlib_get_buffer (vm, bi0);
638 * $$$ parse through header(s) to pick the point
639 * where we punch in the SR extention header
642 pool_elt_at_index (sm->tunnels,
643 vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
645 ASSERT (VLIB_BUFFER_PRE_DATA_SIZE
646 >= ((word) vec_len (t0->rewrite)) + b0->current_data);
648 vnet_buffer (b0)->sw_if_index[VLIB_TX] = t0->tx_fib_index;
650 ip0 = vlib_buffer_get_current (b0);
653 * SR-unaware service chaining case: pkt coming back from
654 * service has the original dst address, and will already
655 * have an SR header. If so, send it to sr-local
657 if (PREDICT_FALSE (ip0->protocol == IPPROTO_IPV6_ROUTE))
659 vlib_buffer_advance (b0, sizeof (ip0));
660 sr0 = (ip6_sr_header_t *) (ip0 + 1);
661 new_l0 = clib_net_to_host_u16 (ip0->payload_length);
662 next0 = SR_REWRITE_NEXT_SR_LOCAL;
666 u32 len_bytes = sizeof (ip6_header_t);
667 u8 next_hdr = ip0->protocol;
669 /* HBH must immediately follow ipv6 header */
671 (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
673 ip6_hop_by_hop_ext_t *ext_hdr =
674 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
676 ip6_ext_header_len ((ip6_ext_header_t *) ext_hdr);
677 next_hdr = ext_hdr->next_hdr;
678 ext_hdr->next_hdr = IPPROTO_IPV6_ROUTE;
679 /* Ignoring the sr_local for now, if RH follows HBH here */
683 ip0->protocol = IPPROTO_IPV6_ROUTE; /* routing extension header */
686 * Copy data before the punch-in point left by the
687 * required amount. Assume (for the moment) that only
688 * the main packet header needs to be copied.
690 clib_memcpy (((u8 *) ip0) - vec_len (t0->rewrite),
692 vlib_buffer_advance (b0, -(word) vec_len (t0->rewrite));
693 ip0 = vlib_buffer_get_current (b0);
694 sr0 = (ip6_sr_header_t *) ((u8 *) ip0 + len_bytes);
696 clib_memcpy (sr0, t0->rewrite, vec_len (t0->rewrite));
698 /* Fix the next header chain */
699 sr0->protocol = next_hdr;
700 new_l0 = clib_net_to_host_u16 (ip0->payload_length) +
701 vec_len (t0->rewrite);
702 ip0->payload_length = clib_host_to_net_u16 (new_l0);
704 /* Copy dst address into the DA slot in the segment list */
705 clib_memcpy (sr0->segments, ip0->dst_address.as_u64,
706 sizeof (ip6_address_t));
707 /* Rewrite the ip6 dst address with the first hop */
708 clib_memcpy (ip0->dst_address.as_u64, t0->first_hop.as_u64,
709 sizeof (ip6_address_t));
711 sr_fix_hmac (sm, ip0, sr0);
713 vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
714 t0->first_hop_dpo.dpoi_index;
715 next0 = t0->first_hop_dpo.dpoi_next_node;
716 next0 = (sr_local_cb ?
717 sr_local_cb (vm, node, b0, ip0, sr0) : next0);
720 * Ignore "do not rewrite" shtik in this path
722 if (PREDICT_FALSE (next0 & 0x80000000))
725 if (PREDICT_FALSE (next0 == SR_REWRITE_NEXT_ERROR))
726 b0->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK];
730 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
732 sr_rewrite_trace_t *tr = vlib_add_trace (vm, node,
734 tr->tunnel_index = t0 - sm->tunnels;
737 memcpy (tr->src.as_u8, ip0->src_address.as_u8,
738 sizeof (tr->src.as_u8));
739 memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
740 sizeof (tr->dst.as_u8));
743 tr->next_index = next0;
745 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
747 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
748 to_next, n_left_to_next,
751 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
753 return from_frame->n_vectors;
757 VLIB_REGISTER_NODE (sr_rewrite_node) = {
758 .function = sr_rewrite,
759 .name = "sr-rewrite",
760 /* Takes a vector of packets. */
761 .vector_size = sizeof (u32),
762 .format_trace = format_sr_rewrite_trace,
763 .format_buffer = format_ip6_sr_header_with_length,
765 .n_errors = SR_REWRITE_N_ERROR,
766 .error_strings = sr_rewrite_error_strings,
768 .runtime_data_bytes = 0,
770 .n_next_nodes = SR_REWRITE_N_NEXT,
772 #define _(s,n) [SR_REWRITE_NEXT_##s] = n,
773 foreach_sr_rewrite_next
778 VLIB_NODE_FUNCTION_MULTIARCH (sr_rewrite_node, sr_rewrite)
782 ip6_routes_add_del (ip6_sr_tunnel_t * t, int is_del)
784 ip6_sr_main_t *sm = &sr_main;
787 * the prefix for the tunnel's destination
791 .fp_proto = FIB_PROTOCOL_IP6,
792 .fp_len = t->dst_mask_width,
801 fib_table_entry_delete (t->rx_fib_index, &pfx, FIB_SOURCE_SR);
805 dpo_id_t dpo = DPO_INVALID;
807 dpo_set (&dpo, sr_dpo_type, DPO_PROTO_IP6, t - sm->tunnels);
808 fib_table_entry_special_dpo_add (t->rx_fib_index,
811 FIB_ENTRY_FLAG_EXCLUSIVE, &dpo);
816 * Track the first hop address so we don't need to perform an extra
817 * lookup in the data-path
820 const fib_prefix_t first_hop_pfx = {
822 .fp_proto = FIB_PROTOCOL_IP6,
831 fib_entry_child_remove (t->fib_entry_index, t->sibling_index);
832 fib_table_entry_delete_index (t->fib_entry_index, FIB_SOURCE_RR);
837 fib_table_entry_special_add (t->rx_fib_index,
840 FIB_ENTRY_FLAG_NONE, ADJ_INDEX_INVALID);
842 fib_entry_child_add (t->fib_entry_index,
843 sr_fib_node_type, t - sm->tunnels);
850 * @brief Find or add if not found - HMAC shared secret
852 * @param sm ip6_sr_main_t *
854 * @param indexp u32 *
856 * @return ip6_sr_hmac_key_t *
858 static ip6_sr_hmac_key_t *
859 find_or_add_shared_secret (ip6_sr_main_t * sm, u8 * secret, u32 * indexp)
862 ip6_sr_hmac_key_t *key = 0;
865 p = hash_get_mem (sm->hmac_key_by_shared_secret, secret);
869 key = vec_elt_at_index (sm->hmac_keys, p[0]);
875 /* Specific key ID? */
876 if (indexp && *indexp)
878 vec_validate (sm->hmac_keys, *indexp);
879 key = sm->hmac_keys + *indexp;
883 for (i = 0; i < vec_len (sm->hmac_keys); i++)
885 if (sm->hmac_keys[i].shared_secret == 0)
887 key = sm->hmac_keys + i;
891 vec_validate (sm->hmac_keys, i);
892 key = sm->hmac_keys + i;
897 key->shared_secret = vec_dup (secret);
899 hash_set_mem (sm->hmac_key_by_shared_secret, key->shared_secret,
900 key - sm->hmac_keys);
903 *indexp = key - sm->hmac_keys;
908 * @brief Stack a tunnel on the forwarding chain of the first-hop
911 sr_tunnel_stack (ip6_sr_tunnel_t * st)
913 dpo_stack (sr_dpo_type,
916 fib_entry_contribute_ip_forwarding (st->fib_entry_index));
920 * @brief Add or Delete a Segment Routing tunnel.
922 * @param a ip6_sr_add_del_tunnel_args_t *
927 ip6_sr_add_del_tunnel (ip6_sr_add_del_tunnel_args_t * a)
929 ip6_main_t *im = &ip6_main;
930 ip6_sr_tunnel_key_t key;
933 ip6_sr_header_t *h = 0;
935 ip6_address_t *addrp, *this_address;
936 ip6_sr_main_t *sm = &sr_main;
938 u32 rx_fib_index, tx_fib_index;
939 u32 hmac_key_index_u32;
940 u8 hmac_key_index = 0;
944 /* Make sure that the rx FIB exists */
945 p = hash_get (im->fib_index_by_table_id, a->rx_table_id);
950 /* remember the FIB index */
953 /* Make sure that the supplied FIB exists */
954 p = hash_get (im->fib_index_by_table_id, a->tx_table_id);
959 /* remember the FIB index */
962 clib_memcpy (key.src.as_u8, a->src_address->as_u8, sizeof (key.src));
963 clib_memcpy (key.dst.as_u8, a->dst_address->as_u8, sizeof (key.dst));
965 /* When adding a tunnel:
966 * - If a "name" is given, it must not exist.
967 * - The "key" is always checked, and must not exist.
968 * When deleting a tunnel:
969 * - If the "name" is given, and it exists, then use it.
970 * - If the "name" is not given, use the "key".
971 * - If the "name" and the "key" are given, then both must point to the same
976 p = hash_get_mem (sm->tunnel_index_by_key, &key);
978 /* If the name is given, look it up */
980 n = hash_get_mem (sm->tunnel_index_by_name, a->name);
984 /* validate key/name parameters */
985 if (!a->is_del) /* adding a tunnel */
987 if (a->name && n) /* name given & exists already */
989 if (p) /* key exists already */
992 else /* deleting a tunnel */
994 if (!p) /* key doesn't exist */
996 if (a->name && !n) /* name given & it doesn't exist */
999 if (n) /* name given & found */
1001 if (n[0] != p[0]) /* name and key do not point to the same thing */
1007 if (a->is_del) /* delete the tunnel */
1011 /* Delete existing tunnel */
1012 t = pool_elt_at_index (sm->tunnels, p[0]);
1014 ip6_routes_add_del (t, 1);
1016 vec_free (t->rewrite);
1017 /* Remove tunnel from any policy if associated */
1018 if (t->policy_index != ~0)
1020 pt = pool_elt_at_index (sm->policies, t->policy_index);
1021 for (i = 0; i < vec_len (pt->tunnel_indices); i++)
1023 if (pt->tunnel_indices[i] == t - sm->tunnels)
1025 vec_delete (pt->tunnel_indices, 1, i);
1029 clib_warning ("Tunnel index %d not found in policy_index %d",
1030 t - sm->tunnels, pt - sm->policies);
1032 /* If this is last tunnel in the policy, clean up the policy too */
1033 if (vec_len (pt->tunnel_indices) == 0)
1035 hash_unset_mem (sm->policy_index_by_policy_name, pt->name);
1036 vec_free (pt->name);
1037 pool_put (sm->policies, pt);
1041 /* Clean up the tunnel by name */
1044 hash_unset_mem (sm->tunnel_index_by_name, t->name);
1047 dpo_reset (&t->first_hop_dpo);
1048 pool_put (sm->tunnels, t);
1049 hp = hash_get_pair (sm->tunnel_index_by_key, &key);
1050 key_copy = (void *) (hp->key);
1051 hash_unset_mem (sm->tunnel_index_by_key, &key);
1052 vec_free (key_copy);
1056 /* create a new tunnel */
1057 pool_get (sm->tunnels, t);
1058 memset (t, 0, sizeof (*t));
1059 t->policy_index = ~0;
1060 fib_node_init (&t->node, sr_fib_node_type);
1062 clib_memcpy (&t->key, &key, sizeof (t->key));
1063 t->dst_mask_width = a->dst_mask_width;
1064 t->rx_fib_index = rx_fib_index;
1065 t->tx_fib_index = tx_fib_index;
1067 if (!vec_len (a->segments))
1068 /* there must be at least one segment... */
1071 /* The first specified hop goes right into the dst address */
1072 clib_memcpy (&t->first_hop, &a->segments[0], sizeof (ip6_address_t));
1075 * Create the sr header rewrite string
1076 * The list of segments needs an extra slot for the ultimate destination
1077 * which is taken from the packet we add the SRH to.
1079 header_length = sizeof (*h) +
1080 sizeof (ip6_address_t) * (vec_len (a->segments) + 1 + vec_len (a->tags));
1082 if (a->shared_secret)
1084 /* Allocate a new key slot if we don't find the secret key */
1085 hmac_key_index_u32 = 0;
1086 (void) find_or_add_shared_secret (sm, a->shared_secret,
1087 &hmac_key_index_u32);
1089 /* Hey Vinz Clortho: Gozzer is pissed.. you're out of keys! */
1090 if (hmac_key_index_u32 >= 256)
1092 hmac_key_index = hmac_key_index_u32;
1093 header_length += SHA256_DIGEST_LENGTH;
1096 vec_validate (t->rewrite, header_length - 1);
1098 h = (ip6_sr_header_t *) t->rewrite;
1100 h->protocol = 0xFF; /* we don't know yet */
1102 h->length = (header_length / 8) - 1;
1103 h->type = ROUTING_HEADER_TYPE_SR;
1105 /* first_segment and segments_left need to have the index of the last
1106 * element in the list; a->segments has one element less than ends up
1107 * in the header (it does not have the DA in it), so vec_len(a->segments)
1108 * is the value we want.
1110 h->first_segment = h->segments_left = vec_len (a->segments);
1112 if (a->shared_secret)
1113 h->hmac_key = hmac_key_index & 0xFF;
1115 h->flags = a->flags_net_byte_order;
1117 /* Paint on the segment list, in reverse.
1118 * This is offset by one to leave room at the start for the ultimate
1121 addrp = h->segments + vec_len (a->segments);
1123 vec_foreach (this_address, a->segments)
1125 clib_memcpy (addrp->as_u8, this_address->as_u8, sizeof (ip6_address_t));
1130 * Since the ultimate destination address is not yet known, set that slot
1131 * to a value we will instantly recognize as bogus.
1133 memset (h->segments, 0xfe, sizeof (ip6_address_t));
1135 /* Paint on the tag list, not reversed */
1136 addrp = h->segments + vec_len (a->segments);
1138 vec_foreach (this_address, a->tags)
1140 clib_memcpy (addrp->as_u8, this_address->as_u8, sizeof (ip6_address_t));
1144 key_copy = vec_new (ip6_sr_tunnel_key_t, 1);
1145 clib_memcpy (key_copy, &key, sizeof (ip6_sr_tunnel_key_t));
1146 hash_set_mem (sm->tunnel_index_by_key, key_copy, t - sm->tunnels);
1149 * Stick the tunnel index into the rewrite header.
1151 * Unfortunately, inserting an SR header according to the various
1152 * RFC's requires parsing through the ip6 header, perhaps consing a
1153 * buffer onto the head of the vlib_buffer_t, etc. We don't use the
1154 * normal reverse bcopy rewrite code.
1156 * We don't handle ugly RFC-related cases yet, but I'm sure PL will complain
1161 * Add the routes for the tunnel destination and first-hop, then stack
1162 * the tunnel on the appropriate forwarding DPOs.
1164 ip6_routes_add_del (t, 0);
1165 sr_tunnel_stack (t);
1169 p = hash_get_mem (sm->policy_index_by_policy_name, a->policy_name);
1172 pt = pool_elt_at_index (sm->policies, p[0]);
1174 else /* no policy, lets create one */
1176 pool_get (sm->policies, pt);
1177 memset (pt, 0, sizeof (*pt));
1178 pt->name = format (0, "%s%c", a->policy_name, 0);
1179 hash_set_mem (sm->policy_index_by_policy_name, pt->name,
1181 p = hash_get_mem (sm->policy_index_by_policy_name, a->policy_name);
1183 vec_add1 (pt->tunnel_indices, t - sm->tunnels);
1185 clib_warning ("p is NULL!");
1186 t->policy_index = p ? p[0] : ~0; /* equiv. to (pt - sm->policies) */
1191 t->name = format (0, "%s%c", a->name, 0);
1192 hash_set_mem (sm->tunnel_index_by_name, t->name, t - sm->tunnels);
1199 * @brief no-op lock function.
1200 * The lifetime of the SR entry is managed by the control plane
1203 sr_dpo_lock (dpo_id_t * dpo)
1208 * @brief no-op unlock function.
1209 * The lifetime of the SR entry is managed by the control plane
1212 sr_dpo_unlock (dpo_id_t * dpo)
1217 format_sr_dpo (u8 * s, va_list * args)
1219 index_t index = va_arg (*args, index_t);
1220 CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
1222 return (format (s, "SR: tunnel:[%d]", index));
1225 const static dpo_vft_t sr_dpo_vft = {
1226 .dv_lock = sr_dpo_lock,
1227 .dv_unlock = sr_dpo_unlock,
1228 .dv_format = format_sr_dpo,
1231 const static char *const sr_ip6_nodes[] = {
1236 const static char *const *const sr_nodes[DPO_PROTO_NUM] = {
1237 [DPO_PROTO_IP6] = sr_ip6_nodes,
1240 static ip6_sr_tunnel_t *
1241 sr_tunnel_from_fib_node (fib_node_t * node)
1243 #if (CLIB_DEBUG > 0)
1244 ASSERT (sr_fib_node_type == node->fn_type);
1246 return ((ip6_sr_tunnel_t *) (((char *) node) -
1247 STRUCT_OFFSET_OF (ip6_sr_tunnel_t, node)));
1251 * Function definition to backwalk a FIB node
1253 static fib_node_back_walk_rc_t
1254 sr_tunnel_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
1256 sr_tunnel_stack (sr_tunnel_from_fib_node (node));
1258 return (FIB_NODE_BACK_WALK_CONTINUE);
1262 * Function definition to get a FIB node from its index
1265 sr_tunnel_fib_node_get (fib_node_index_t index)
1267 ip6_sr_tunnel_t *st;
1271 st = pool_elt_at_index (sm->tunnels, index);
1277 * Function definition to inform the FIB node that its last lock has gone.
1280 sr_tunnel_last_lock_gone (fib_node_t * node)
1283 * The SR tunnel is a root of the graph. As such
1284 * it never has children and thus is never locked.
1290 * Virtual function table registered by SR tunnels
1291 * for participation in the FIB object graph.
1293 const static fib_node_vft_t sr_fib_vft = {
1294 .fnv_get = sr_tunnel_fib_node_get,
1295 .fnv_last_lock = sr_tunnel_last_lock_gone,
1296 .fnv_back_walk = sr_tunnel_back_walk,
1300 * @brief CLI parser for Add or Delete a Segment Routing tunnel.
1302 * @param vm vlib_main_t *
1303 * @param input unformat_input_t *
1304 * @param cmd vlib_cli_command_t *
1306 * @return error clib_error_t *
1308 static clib_error_t *
1309 sr_add_del_tunnel_command_fn (vlib_main_t * vm,
1310 unformat_input_t * input,
1311 vlib_cli_command_t * cmd)
1314 ip6_address_t src_address;
1315 int src_address_set = 0;
1316 ip6_address_t dst_address;
1318 int dst_address_set = 0;
1320 u8 *shared_secret = 0;
1322 u8 *policy_name = 0;
1323 u32 rx_table_id = 0;
1324 u32 tx_table_id = 0;
1325 ip6_address_t *segments = 0;
1326 ip6_address_t *this_seg;
1327 ip6_address_t *tags = 0;
1328 ip6_address_t *this_tag;
1329 ip6_sr_add_del_tunnel_args_t _a, *a = &_a;
1330 ip6_address_t next_address, tag;
1334 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1336 if (unformat (input, "del"))
1338 else if (unformat (input, "rx-fib-id %d", &rx_table_id))
1340 else if (unformat (input, "tx-fib-id %d", &tx_table_id))
1342 else if (unformat (input, "src %U", unformat_ip6_address, &src_address))
1343 src_address_set = 1;
1344 else if (unformat (input, "name %s", &name))
1346 else if (unformat (input, "policy %s", &policy_name))
1348 else if (unformat (input, "dst %U/%d",
1349 unformat_ip6_address, &dst_address, &dst_mask_width))
1350 dst_address_set = 1;
1351 else if (unformat (input, "next %U", unformat_ip6_address,
1354 vec_add2 (segments, this_seg, 1);
1355 clib_memcpy (this_seg->as_u8, next_address.as_u8,
1356 sizeof (*this_seg));
1358 else if (unformat (input, "tag %U", unformat_ip6_address, &tag))
1360 vec_add2 (tags, this_tag, 1);
1361 clib_memcpy (this_tag->as_u8, tag.as_u8, sizeof (*this_tag));
1363 else if (unformat (input, "clean"))
1364 flags |= IP6_SR_HEADER_FLAG_CLEANUP;
1365 else if (unformat (input, "protected"))
1366 flags |= IP6_SR_HEADER_FLAG_PROTECTED;
1367 else if (unformat (input, "key %s", &shared_secret))
1368 /* Do not include the trailing NULL byte. Guaranteed interop issue */
1369 _vec_len (shared_secret) -= 1;
1370 else if (unformat (input, "InPE %d", &pl_index))
1372 if (pl_index <= 0 || pl_index > 4)
1374 pl_index_range_error:
1375 return clib_error_return
1376 (0, "Policy List Element Index %d out of range (1-4)",
1380 flags |= IP6_SR_HEADER_FLAG_PL_ELT_INGRESS_PE
1381 << ip6_sr_policy_list_shift_from_index (pl_index);
1383 else if (unformat (input, "EgPE %d", &pl_index))
1385 if (pl_index <= 0 || pl_index > 4)
1386 goto pl_index_range_error;
1387 flags |= IP6_SR_HEADER_FLAG_PL_ELT_EGRESS_PE
1388 << ip6_sr_policy_list_shift_from_index (pl_index);
1390 else if (unformat (input, "OrgSrc %d", &pl_index))
1392 if (pl_index <= 0 || pl_index > 4)
1393 goto pl_index_range_error;
1394 flags |= IP6_SR_HEADER_FLAG_PL_ELT_ORIG_SRC_ADDR
1395 << ip6_sr_policy_list_shift_from_index (pl_index);
1401 if (!src_address_set)
1402 return clib_error_return (0, "src address required");
1404 if (!dst_address_set)
1405 return clib_error_return (0, "dst address required");
1408 return clib_error_return (0, "at least one sr segment required");
1410 memset (a, 0, sizeof (*a));
1411 a->src_address = &src_address;
1412 a->dst_address = &dst_address;
1413 a->dst_mask_width = dst_mask_width;
1414 a->segments = segments;
1416 a->flags_net_byte_order = clib_host_to_net_u16 (flags);
1418 a->rx_table_id = rx_table_id;
1419 a->tx_table_id = tx_table_id;
1420 a->shared_secret = shared_secret;
1427 if (vec_len (policy_name))
1428 a->policy_name = policy_name;
1432 rv = ip6_sr_add_del_tunnel (a);
1434 vec_free (segments);
1436 vec_free (shared_secret);
1444 return clib_error_return (0, "SR tunnel src %U dst %U already exists",
1445 format_ip6_address, &src_address,
1446 format_ip6_address, &dst_address);
1449 return clib_error_return (0, "SR tunnel src %U dst %U does not exist",
1450 format_ip6_address, &src_address,
1451 format_ip6_address, &dst_address);
1454 return clib_error_return (0, "FIB table %d does not exist",
1458 return clib_error_return (0, "At least one segment is required");
1461 return clib_error_return (0, "BUG: ip6_sr_add_del_tunnel returns %d",
1469 VLIB_CLI_COMMAND (sr_tunnel_command, static) = {
1470 .path = "sr tunnel",
1472 "sr tunnel [del] [name <name>] src <addr> dst <addr> [next <addr>] "
1473 "[clean] [reroute] [key <secret>] [policy <policy_name>]"
1474 "[rx-fib-id <fib_id>] [tx-fib-id <fib_id>]",
1475 .function = sr_add_del_tunnel_command_fn,
1480 * @brief Display Segment Routing tunnel
1482 * @param vm vlib_main_t *
1483 * @param t ip6_sr_tunnel_t *
1487 ip6_sr_tunnel_display (vlib_main_t * vm, ip6_sr_tunnel_t * t)
1489 ip6_sr_main_t *sm = &sr_main;
1490 ip6_fib_t *rx_fib, *tx_fib;
1491 ip6_sr_policy_t *pt;
1493 rx_fib = ip6_fib_get (t->rx_fib_index);
1494 tx_fib = ip6_fib_get (t->tx_fib_index);
1497 vlib_cli_output (vm, "sr tunnel name: %s", (char *) t->name);
1499 vlib_cli_output (vm, "src %U dst %U first hop %U",
1500 format_ip6_address, &t->key.src,
1501 format_ip6_address, &t->key.dst,
1502 format_ip6_address, &t->first_hop);
1503 vlib_cli_output (vm, " rx-fib-id %d tx-fib-id %d",
1504 rx_fib->table_id, tx_fib->table_id);
1505 vlib_cli_output (vm, " sr: %U", format_ip6_sr_header, t->rewrite,
1506 0 /* print_hmac */ );
1508 if (t->policy_index != ~0)
1510 pt = pool_elt_at_index (sm->policies, t->policy_index);
1511 vlib_cli_output (vm, "sr policy: %s", (char *) pt->name);
1513 vlib_cli_output (vm, "-------");
1519 * @brief CLI Parser for Display Segment Routing tunnel
1521 * @param vm vlib_main_t *
1522 * @param input unformat_input_t *
1523 * @param cmd vlib_cli_command_t *
1525 * @return error clib_error_t *
1527 static clib_error_t *
1528 show_sr_tunnel_fn (vlib_main_t * vm,
1529 unformat_input_t * input, vlib_cli_command_t * cmd)
1531 static ip6_sr_tunnel_t **tunnels;
1533 ip6_sr_main_t *sm = &sr_main;
1538 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1540 if (unformat (input, "name %s", &name))
1542 p = hash_get_mem (sm->tunnel_index_by_name, name);
1544 vlib_cli_output (vm, "No SR tunnel with name: %s. Showing all.",
1551 vec_reset_length (tunnels);
1553 if (!p) /* Either name parm not passed or no tunnel with that name found, show all */
1556 pool_foreach (t, sm->tunnels,
1558 vec_add1 (tunnels, t);
1562 else /* Just show the one tunnel by name */
1563 vec_add1 (tunnels, &sm->tunnels[p[0]]);
1565 if (vec_len (tunnels) == 0)
1566 vlib_cli_output (vm, "No SR tunnels configured");
1568 for (i = 0; i < vec_len (tunnels); i++)
1571 ip6_sr_tunnel_display (vm, t);
1578 VLIB_CLI_COMMAND (show_sr_tunnel_command, static) = {
1579 .path = "show sr tunnel",
1580 .short_help = "show sr tunnel [name <sr-tunnel-name>]",
1581 .function = show_sr_tunnel_fn,
1586 * @brief Add or Delete a Segment Routing policy
1588 * @param a ip6_sr_add_del_policy_args_t *
1590 * @return retval int
1593 ip6_sr_add_del_policy (ip6_sr_add_del_policy_args_t * a)
1595 ip6_sr_main_t *sm = &sr_main;
1597 ip6_sr_tunnel_t *t = 0;
1598 ip6_sr_policy_t *policy;
1599 u32 *tunnel_indices = 0;
1606 p = hash_get_mem (sm->policy_index_by_policy_name, a->name);
1608 return -6; /* policy name not found */
1610 policy = pool_elt_at_index (sm->policies, p[0]);
1612 vec_foreach_index (i, policy->tunnel_indices)
1614 t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[i]);
1615 t->policy_index = ~0;
1617 hash_unset_mem (sm->policy_index_by_policy_name, a->name);
1618 pool_put (sm->policies, policy);
1623 if (!vec_len (a->tunnel_names))
1624 return -3; /*tunnel name is required case */
1626 vec_reset_length (tunnel_indices);
1627 /* Check tunnel names, add tunnel_index to policy */
1628 for (i = 0; i < vec_len (a->tunnel_names); i++)
1630 p = hash_get_mem (sm->tunnel_index_by_name, a->tunnel_names[i]);
1632 return -4; /* tunnel name not found case */
1634 t = pool_elt_at_index (sm->tunnels, p[0]);
1636 No need to check t==0. -3 condition above ensures name
1638 if (t->policy_index != ~0)
1639 return -5; /* tunnel name already associated with a policy */
1641 /* Add to tunnel indicies */
1642 vec_add1 (tunnel_indices, p[0]);
1645 /* Add policy to ip6_sr_main_t */
1646 pool_get (sm->policies, policy);
1647 policy->name = a->name;
1648 policy->tunnel_indices = tunnel_indices;
1649 hash_set_mem (sm->policy_index_by_policy_name, policy->name,
1650 policy - sm->policies);
1652 /* Yes, this could be construed as overkill but the last thing you should do is set
1653 the policy_index on the tunnel after everything is set in ip6_sr_main_t.
1654 If this is deemed overly cautious, could set this in the vec_len(tunnel_names) loop.
1656 for (i = 0; i < vec_len (policy->tunnel_indices); i++)
1658 t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[i]);
1659 t->policy_index = policy - sm->policies;
1666 * @brief CLI Parser for Add or Delete a Segment Routing policy
1668 * @param vm vlib_main_t *
1669 * @param input unformat_input_t *
1670 * @param cmd vlib_cli_command_t *
1672 * @return error clib_error_t *
1674 static clib_error_t *
1675 sr_add_del_policy_command_fn (vlib_main_t * vm,
1676 unformat_input_t * input,
1677 vlib_cli_command_t * cmd)
1680 u8 **tunnel_names = 0;
1681 u8 *tunnel_name = 0;
1683 ip6_sr_add_del_policy_args_t _a, *a = &_a;
1686 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1688 if (unformat (input, "del"))
1690 else if (unformat (input, "name %s", &name))
1692 else if (unformat (input, "tunnel %s", &tunnel_name))
1696 vec_add1 (tunnel_names, tunnel_name);
1705 return clib_error_return (0, "name of SR policy required");
1708 memset (a, 0, sizeof (*a));
1712 a->tunnel_names = tunnel_names;
1714 rv = ip6_sr_add_del_policy (a);
1716 vec_free (tunnel_names);
1724 return clib_error_return (0,
1725 "tunnel name to associate to SR policy is required");
1728 return clib_error_return (0, "tunnel name not found");
1731 return clib_error_return (0, "tunnel already associated with policy");
1734 return clib_error_return (0, "policy name %s not found", name);
1737 return clib_error_return (0, "TODO: deleting policy name %s", name);
1740 return clib_error_return (0, "BUG: ip6_sr_add_del_policy returns %d",
1748 VLIB_CLI_COMMAND (sr_policy_command, static) = {
1749 .path = "sr policy",
1751 "sr policy [del] name <policy-name> tunnel <sr-tunnel-name> [tunnel <sr-tunnel-name>]*",
1752 .function = sr_add_del_policy_command_fn,
1757 * @brief CLI Parser for Displaying Segment Routing policy
1759 * @param vm vlib_main_t *
1760 * @param input unformat_input_t *
1761 * @param cmd vlib_cli_command_t *
1763 * @return error clib_error_t *
1765 static clib_error_t *
1766 show_sr_policy_fn (vlib_main_t * vm,
1767 unformat_input_t * input, vlib_cli_command_t * cmd)
1769 static ip6_sr_policy_t **policies;
1770 ip6_sr_policy_t *policy;
1772 ip6_sr_main_t *sm = &sr_main;
1777 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1779 if (unformat (input, "name %s", &name))
1781 p = hash_get_mem (sm->policy_index_by_policy_name, name);
1783 vlib_cli_output (vm,
1784 "policy with name %s not found. Showing all.",
1791 vec_reset_length (policies);
1793 if (!p) /* Either name parm not passed or no policy with that name found, show all */
1796 pool_foreach (policy, sm->policies,
1798 vec_add1 (policies, policy);
1802 else /* Just show the one policy by name and a summary of tunnel names */
1804 policy = pool_elt_at_index (sm->policies, p[0]);
1805 vec_add1 (policies, policy);
1808 if (vec_len (policies) == 0)
1809 vlib_cli_output (vm, "No SR policies configured");
1811 for (i = 0; i < vec_len (policies); i++)
1813 policy = policies[i];
1816 vlib_cli_output (vm, "SR policy name: %s", (char *) policy->name);
1817 for (j = 0; j < vec_len (policy->tunnel_indices); j++)
1819 t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[j]);
1820 ip6_sr_tunnel_display (vm, t);
1829 VLIB_CLI_COMMAND (show_sr_policy_command, static) = {
1830 .path = "show sr policy",
1831 .short_help = "show sr policy [name <sr-policy-name>]",
1832 .function = show_sr_policy_fn,
1837 * @brief Add or Delete a mapping of IP6 multicast address
1838 * to Segment Routing policy.
1840 * @param a ip6_sr_add_del_multicastmap_args_t *
1842 * @return retval int
1845 ip6_sr_add_del_multicastmap (ip6_sr_add_del_multicastmap_args_t * a)
1849 ip6_sr_main_t *sm = &sr_main;
1850 ip6_sr_policy_t *pt;
1856 /* clean up the adjacency */
1858 hash_get_mem (sm->policy_index_by_multicast_address,
1859 a->multicast_address);
1863 /* Get our policy by policy_name */
1864 p = hash_get_mem (sm->policy_index_by_policy_name, a->policy_name);
1870 pt = pool_elt_at_index (sm->policies, p[0]);
1873 Get the first tunnel associated with policy populate the fib adjacency.
1874 From there, since this tunnel will have it's policy_index != ~0 it will
1875 be the trigger in the dual_loop to pull up the policy and make a copy-rewrite
1876 for each tunnel in the policy
1879 t = pool_elt_at_index (sm->tunnels, pt->tunnel_indices[0]);
1882 * Stick the tunnel index into the rewrite header.
1884 * Unfortunately, inserting an SR header according to the various
1885 * RFC's requires parsing through the ip6 header, perhaps consing a
1886 * buffer onto the head of the vlib_buffer_t, etc. We don't use the
1887 * normal reverse bcopy rewrite code.
1889 * We don't handle ugly RFC-related cases yet, but I'm sure PL will complain
1894 * Construct an mFIB entry for the multicast address,
1895 * using the rx/tx fib from the first tunnel.
1896 * There is no RPF information for this address (I need to discuss this with
1897 * Pablo), so for now accept from anywhere...
1900 mfib_prefix_t pfx = {
1901 .fp_proto = FIB_PROTOCOL_IP6,
1904 .ip6 = *a->multicast_address,
1910 mfib_table_entry_delete (t->rx_fib_index, &pfx, MFIB_SOURCE_SRv6);
1914 * Construct a replicate DPO that will replicate received packets over
1915 * each tunnel in the policy
1917 dpo_id_t dpo = DPO_INVALID;
1919 rep = replicate_create (vec_len (pt->tunnel_indices), DPO_PROTO_IP6);
1921 vec_foreach_index (ii, pt->tunnel_indices)
1923 dpo_set (&dpo, sr_dpo_type, DPO_PROTO_IP6, pt->tunnel_indices[ii]);
1925 replicate_set_bucket (rep, ii, &dpo);
1928 mfib_table_entry_special_add (t->rx_fib_index,
1931 MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF, rep);
1937 mcast_copy = vec_new (ip6_address_t, 1);
1938 memcpy (mcast_copy, a->multicast_address, sizeof (ip6_address_t));
1942 hash_unset_mem (sm->policy_index_by_multicast_address, mcast_copy);
1943 vec_free (mcast_copy);
1947 hash_set_mem (sm->policy_index_by_multicast_address, mcast_copy,
1955 * @brief CLI Parser for Adding or Delete a mapping of IP6 multicast address
1956 * to Segment Routing policy.
1958 * @param vm vlib_main_t *
1959 * @param input unformat_input_t *
1960 * @param cmd vlib_cli_command_t *
1962 * @return error clib_error_t *
1964 static clib_error_t *
1965 sr_add_del_multicast_map_command_fn (vlib_main_t * vm,
1966 unformat_input_t * input,
1967 vlib_cli_command_t * cmd)
1970 ip6_address_t multicast_address;
1971 u8 *policy_name = 0;
1972 int multicast_address_set = 0;
1973 ip6_sr_add_del_multicastmap_args_t _a, *a = &_a;
1976 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1978 if (unformat (input, "del"))
1982 (input, "address %U", unformat_ip6_address, &multicast_address))
1983 multicast_address_set = 1;
1984 else if (unformat (input, "sr-policy %s", &policy_name))
1990 if (!is_del && !policy_name)
1991 return clib_error_return (0, "name of sr policy required");
1993 if (!multicast_address_set)
1994 return clib_error_return (0, "multicast address required");
1996 memset (a, 0, sizeof (*a));
1999 a->multicast_address = &multicast_address;
2000 a->policy_name = policy_name;
2002 rv = ip6_sr_add_del_multicastmap (a);
2009 return clib_error_return (0, "no policy with name: %s", policy_name);
2012 return clib_error_return (0, "multicast map someting ");
2015 return clib_error_return (0,
2016 "tunnel name to associate to SR policy is required");
2019 return clib_error_return (0, "TODO: deleting policy name %s",
2023 return clib_error_return (0, "BUG: ip6_sr_add_del_policy returns %d",
2033 VLIB_CLI_COMMAND (sr_multicast_map_command, static) = {
2034 .path = "sr multicast-map",
2036 "sr multicast-map address <multicast-ip6-address> sr-policy <sr-policy-name> [del]",
2037 .function = sr_add_del_multicast_map_command_fn,
2042 * @brief CLI Parser for Displaying a mapping of IP6 multicast address
2043 * to Segment Routing policy.
2045 * @param vm vlib_main_t *
2046 * @param input unformat_input_t *
2047 * @param cmd vlib_cli_command_t *
2049 * @return error clib_error_t *
2051 static clib_error_t *
2052 show_sr_multicast_map_fn (vlib_main_t * vm,
2053 unformat_input_t * input, vlib_cli_command_t * cmd)
2055 ip6_sr_main_t *sm = &sr_main;
2058 ip6_address_t multicast_address;
2059 ip6_sr_policy_t *pt;
2061 /* pull all entries from the hash table into vector for display */
2064 hash_foreach_mem (key, value, sm->policy_index_by_multicast_address,
2067 vlib_cli_output (vm, "no multicast maps configured");
2070 multicast_address = *((ip6_address_t *)key);
2071 pt = pool_elt_at_index (sm->policies, value);
2074 vlib_cli_output (vm, "address: %U policy: %s",
2075 format_ip6_address, &multicast_address,
2079 vlib_cli_output (vm, "BUG: policy not found for address: %U with policy index %d",
2080 format_ip6_address, &multicast_address,
2092 VLIB_CLI_COMMAND (show_sr_multicast_map_command, static) = {
2093 .path = "show sr multicast-map",
2094 .short_help = "show sr multicast-map",
2095 .function = show_sr_multicast_map_fn,
2100 #define foreach_sr_fix_dst_addr_next \
2101 _(DROP, "error-drop")
2104 * @brief Struct for valid next-nodes for SR fix destination address node
2108 #define _(s,n) SR_FIX_DST_ADDR_NEXT_##s,
2109 foreach_sr_fix_dst_addr_next
2111 SR_FIX_DST_ADDR_N_NEXT,
2112 } sr_fix_dst_addr_next_t;
2115 * @brief Error strings for SR Fix Destination rewrite
2117 static char *sr_fix_dst_error_strings[] = {
2118 #define sr_fix_dst_error(n,s) s,
2119 #include "sr_fix_dst_error.def"
2120 #undef sr_fix_dst_error
2124 * @brief Struct for errors for SR Fix Destination rewrite
2128 #define sr_fix_dst_error(n,s) SR_FIX_DST_ERROR_##n,
2129 #include "sr_fix_dst_error.def"
2130 #undef sr_fix_dst_error
2132 } sr_fix_dst_error_t;
2135 * @brief Information for fix address trace
2139 ip6_address_t src, dst;
2143 } sr_fix_addr_trace_t;
2146 * @brief Formatter for fix address trace
2149 format_sr_fix_addr_trace (u8 * s, va_list * args)
2151 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
2152 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
2153 sr_fix_addr_trace_t *t = va_arg (*args, sr_fix_addr_trace_t *);
2154 vnet_hw_interface_t *hi = 0;
2155 ip_adjacency_t *adj;
2156 ip6_main_t *im = &ip6_main;
2157 ip_lookup_main_t *lm = &im->lookup_main;
2158 vnet_main_t *vnm = vnet_get_main ();
2160 if (t->adj_index != ~0)
2162 adj = ip_get_adjacency (lm, t->adj_index);
2163 hi = vnet_get_sup_hw_interface (vnm, adj->rewrite_header.sw_if_index);
2166 s = format (s, "SR-FIX_ADDR: next %s ip6 src %U dst %U\n",
2167 (t->next_index == SR_FIX_DST_ADDR_NEXT_DROP)
2168 ? "drop" : "output",
2169 format_ip6_address, &t->src, format_ip6_address, &t->dst);
2170 if (t->next_index != SR_FIX_DST_ADDR_NEXT_DROP)
2173 format (s, "%U\n", format_ip6_sr_header, t->sr, 1 /* print_hmac */ );
2175 format (s, " output via %s",
2176 hi ? (char *) (hi->name) : "Invalid adj");
2182 * @brief Fix SR destination address - dual-loop
2184 * @node sr-fix-dst-addr
2185 * @param vm vlib_main_t *
2186 * @param node vlib_node_runtime_t *
2187 * @param from_frame vlib_frame_t *
2189 * @return from_frame->n_vectors uword
2192 sr_fix_dst_addr (vlib_main_t * vm,
2193 vlib_node_runtime_t * node, vlib_frame_t * from_frame)
2195 u32 n_left_from, next_index, *from, *to_next;
2196 ip6_main_t *im = &ip6_main;
2197 ip_lookup_main_t *lm = &im->lookup_main;
2199 from = vlib_frame_vector_args (from_frame);
2200 n_left_from = from_frame->n_vectors;
2202 next_index = node->cached_next_index;
2204 while (n_left_from > 0)
2208 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
2211 while (0 && n_left_from >= 4 && n_left_to_next >= 2)
2214 __attribute__ ((unused)) vlib_buffer_t *b0, *b1;
2215 u32 next0 = SR_FIX_DST_ADDR_NEXT_DROP;
2216 u32 next1 = SR_FIX_DST_ADDR_NEXT_DROP;
2218 /* Prefetch next iteration. */
2220 vlib_buffer_t *p2, *p3;
2222 p2 = vlib_get_buffer (vm, from[2]);
2223 p3 = vlib_get_buffer (vm, from[3]);
2225 vlib_prefetch_buffer_header (p2, LOAD);
2226 vlib_prefetch_buffer_header (p3, LOAD);
2235 n_left_to_next -= 2;
2238 b0 = vlib_get_buffer (vm, bi0);
2239 b1 = vlib_get_buffer (vm, bi1);
2242 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
2243 to_next, n_left_to_next,
2244 bi0, bi1, next0, next1);
2248 while (n_left_from > 0 && n_left_to_next > 0)
2253 ip_adjacency_t *adj0;
2254 ip6_sr_header_t *sr0;
2255 u32 next0 = SR_FIX_DST_ADDR_NEXT_DROP;
2256 ip6_address_t *new_dst0;
2257 ethernet_header_t *eh0;
2264 n_left_to_next -= 1;
2266 b0 = vlib_get_buffer (vm, bi0);
2269 ip_get_adjacency (lm, vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
2270 next0 = adj0->if_address_index;
2272 /* We should be pointing at an Ethernet header... */
2273 eh0 = vlib_buffer_get_current (b0);
2274 ip0 = (ip6_header_t *) (eh0 + 1);
2275 sr0 = (ip6_sr_header_t *) (ip0 + 1);
2277 /* We'd better find an SR header... */
2278 if (PREDICT_FALSE (ip0->protocol != IPPROTO_IPV6_ROUTE))
2280 b0->error = node->errors[SR_FIX_DST_ERROR_NO_SR_HEADER];
2286 * We get here from sr_rewrite or sr_local, with
2287 * sr->segments_left pointing at the (copy of the original) dst
2288 * address. Use it, then increment sr0->segments_left.
2291 /* Out of segments? Turf the packet */
2292 if (PREDICT_FALSE (sr0->segments_left == 0))
2294 b0->error = node->errors[SR_FIX_DST_ERROR_NO_MORE_SEGMENTS];
2299 * Rewrite the packet with the original dst address
2300 * We assume that the last segment (in processing order) contains
2301 * the original dst address. The list is reversed, so sr0->segments
2302 * contains the original dst address.
2304 new_dst0 = sr0->segments;
2305 ip0->dst_address.as_u64[0] = new_dst0->as_u64[0];
2306 ip0->dst_address.as_u64[1] = new_dst0->as_u64[1];
2311 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
2313 sr_fix_addr_trace_t *t = vlib_add_trace (vm, node,
2315 t->next_index = next0;
2318 if (next0 != SR_FIX_DST_ADDR_NEXT_DROP)
2320 t->adj_index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
2321 clib_memcpy (t->src.as_u8, ip0->src_address.as_u8,
2322 sizeof (t->src.as_u8));
2323 clib_memcpy (t->dst.as_u8, ip0->dst_address.as_u8,
2324 sizeof (t->dst.as_u8));
2325 clib_memcpy (t->sr, sr0, sizeof (t->sr));
2329 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2330 to_next, n_left_to_next,
2334 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2336 return from_frame->n_vectors;
2341 VLIB_REGISTER_NODE (sr_fix_dst_addr_node) = {
2342 .function = sr_fix_dst_addr,
2343 .name = "sr-fix-dst-addr",
2344 /* Takes a vector of packets. */
2345 .vector_size = sizeof (u32),
2346 .format_trace = format_sr_fix_addr_trace,
2347 .format_buffer = format_ip6_sr_header_with_length,
2349 .runtime_data_bytes = 0,
2351 .n_errors = SR_FIX_DST_N_ERROR,
2352 .error_strings = sr_fix_dst_error_strings,
2354 .n_next_nodes = SR_FIX_DST_ADDR_N_NEXT,
2356 #define _(s,n) [SR_FIX_DST_ADDR_NEXT_##s] = n,
2357 foreach_sr_fix_dst_addr_next
2362 VLIB_NODE_FUNCTION_MULTIARCH (sr_fix_dst_addr_node, sr_fix_dst_addr)
2365 static clib_error_t *
2366 sr_init (vlib_main_t * vm)
2368 ip6_sr_main_t *sm = &sr_main;
2369 clib_error_t *error = 0;
2370 vlib_node_t *ip6_lookup_node, *ip6_rewrite_node;
2372 if ((error = vlib_call_init_function (vm, ip_main_init)))
2375 if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
2379 sm->vnet_main = vnet_get_main ();
2381 vec_validate (sm->hmac_keys, 0);
2382 sm->hmac_keys[0].shared_secret = (u8 *) 0xdeadbeef;
2384 sm->tunnel_index_by_key =
2385 hash_create_mem (0, sizeof (ip6_sr_tunnel_key_t), sizeof (uword));
2387 sm->tunnel_index_by_name = hash_create_string (0, sizeof (uword));
2389 sm->policy_index_by_policy_name = hash_create_string (0, sizeof (uword));
2391 sm->policy_index_by_multicast_address =
2392 hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword));
2394 sm->hmac_key_by_shared_secret = hash_create_string (0, sizeof (uword));
2396 ip6_register_protocol (IPPROTO_IPV6_ROUTE, sr_local_node.index);
2398 ip6_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip6-lookup");
2399 ASSERT (ip6_lookup_node);
2401 ip6_rewrite_node = vlib_get_node_by_name (vm, (u8 *) "ip6-rewrite");
2402 ASSERT (ip6_rewrite_node);
2404 /* Add a disposition to ip6_rewrite for the sr dst address hack node */
2405 sm->ip6_rewrite_sr_next_index =
2406 vlib_node_add_next (vm, ip6_rewrite_node->index,
2407 sr_fix_dst_addr_node.index);
2409 OpenSSL_add_all_digests ();
2411 sm->md = (void *) EVP_get_digestbyname ("sha1");
2412 sm->hmac_ctx = clib_mem_alloc (sizeof (HMAC_CTX));
2414 sr_dpo_type = dpo_register_new_type (&sr_dpo_vft, sr_nodes);
2415 sr_fib_node_type = fib_node_register_new_type (&sr_fib_vft);
2420 VLIB_INIT_FUNCTION (sr_init);
2423 * @brief Definition of next-nodes for SR local
2425 #define foreach_sr_local_next \
2426 _ (ERROR, "error-drop") \
2427 _ (IP6_LOOKUP, "ip6-lookup")
2430 * @brief Struct for definition of next-nodes for SR local
2434 #define _(s,n) SR_LOCAL_NEXT_##s,
2435 foreach_sr_local_next
2441 * @brief Struct for packet trace of SR local
2447 ip6_address_t src, dst;
2453 * @brief Definition of SR local error-strings
2455 static char *sr_local_error_strings[] = {
2456 #define sr_error(n,s) s,
2457 #include "sr_error.def"
2462 * @brief Struct for definition of SR local error-strings
2466 #define sr_error(n,s) SR_LOCAL_ERROR_##n,
2467 #include "sr_error.def"
2473 * @brief Format SR local trace
2476 * @param args va_list *
2481 format_sr_local_trace (u8 * s, va_list * args)
2483 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
2484 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
2485 sr_local_trace_t *t = va_arg (*args, sr_local_trace_t *);
2487 s = format (s, "SR-LOCAL: src %U dst %U len %u next_index %d",
2488 format_ip6_address, &t->src,
2489 format_ip6_address, &t->dst, t->length, t->next_index);
2492 format (s, "\n %U", format_ip6_sr_header, t->sr, 1 /* print_hmac */ );
2494 s = format (s, "\n popped SR header");
2500 /* $$$$ fixme: smp, don't copy data, cache input, output (maybe) */
2502 * @brief Validate the SR HMAC
2504 * @param sm ip6_sr_main_t *
2505 * @param ip ip6_header_t *
2506 * @param sr ip6_sr_header_t *
2508 * @return retval int
2511 sr_validate_hmac (ip6_sr_main_t * sm, ip6_header_t * ip, ip6_sr_header_t * sr)
2517 ip6_address_t *addrp;
2519 ip6_sr_hmac_key_t *hmac_key;
2520 static u8 *signature;
2523 key_index = sr->hmac_key;
2525 /* No signature? Pass... */
2529 /* We don't know about this key? Fail... */
2530 if (key_index >= vec_len (sm->hmac_keys))
2533 vec_validate (signature, SHA256_DIGEST_LENGTH - 1);
2535 hmac_key = sm->hmac_keys + key_index;
2537 vec_reset_length (keybuf);
2539 /* pkt ip6 src address */
2540 vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
2541 clib_memcpy (copy_target, ip->src_address.as_u8, sizeof (ip6_address_t));
2544 vec_add2 (keybuf, copy_target, 1);
2545 copy_target[0] = sr->first_segment;
2547 /* octet w/ bit 0 = "clean" flag */
2548 vec_add2 (keybuf, copy_target, 1);
2550 = (sr->flags & clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))
2554 vec_add2 (keybuf, copy_target, 1);
2555 copy_target[0] = sr->hmac_key;
2557 first_segment = sr->first_segment;
2559 addrp = sr->segments;
2562 for (i = 0; i <= first_segment; i++)
2564 vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
2565 clib_memcpy (copy_target, addrp->as_u8, sizeof (ip6_address_t));
2570 clib_warning ("verify key index %d keybuf: %U", key_index,
2571 format_hex_bytes, keybuf, vec_len (keybuf));
2575 /* SHA1 is shorter than SHA-256 */
2576 memset (signature, 0, vec_len (signature));
2578 HMAC_CTX_init (sm->hmac_ctx);
2579 if (!HMAC_Init (sm->hmac_ctx, hmac_key->shared_secret,
2580 vec_len (hmac_key->shared_secret), sm->md))
2581 clib_warning ("barf1");
2582 if (!HMAC_Update (sm->hmac_ctx, keybuf, vec_len (keybuf)))
2583 clib_warning ("barf2");
2584 if (!HMAC_Final (sm->hmac_ctx, signature, &sig_len))
2585 clib_warning ("barf3");
2586 HMAC_CTX_cleanup (sm->hmac_ctx);
2589 clib_warning ("computed signature len %d, value %U", sig_len,
2590 format_hex_bytes, signature, vec_len (signature));
2592 /* Point at the SHA signature in the packet */
2595 clib_warning ("read signature %U", format_hex_bytes, addrp,
2596 SHA256_DIGEST_LENGTH);
2598 return memcmp (signature, addrp, SHA256_DIGEST_LENGTH);
2602 * @brief SR local node
2605 * @param vm vlib_main_t *
2606 * @param node vlib_node_runtime_t *
2607 * @param from_frame vlib_frame_t *
2609 * @return from_frame->n_vectors uword
2612 sr_local (vlib_main_t * vm,
2613 vlib_node_runtime_t * node, vlib_frame_t * from_frame)
2615 u32 n_left_from, next_index, *from, *to_next;
2616 ip6_sr_main_t *sm = &sr_main;
2617 u32 (*sr_local_cb) (vlib_main_t *, vlib_node_runtime_t *,
2618 vlib_buffer_t *, ip6_header_t *, ip6_sr_header_t *);
2619 sr_local_cb = sm->sr_local_cb;
2621 from = vlib_frame_vector_args (from_frame);
2622 n_left_from = from_frame->n_vectors;
2624 next_index = node->cached_next_index;
2626 while (n_left_from > 0)
2630 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
2632 while (n_left_from >= 4 && n_left_to_next >= 2)
2635 vlib_buffer_t *b0, *b1;
2636 ip6_header_t *ip0, *ip1;
2637 ip6_sr_header_t *sr0, *sr1;
2638 ip6_address_t *new_dst0, *new_dst1;
2639 u32 next0 = SR_LOCAL_NEXT_IP6_LOOKUP;
2640 u32 next1 = SR_LOCAL_NEXT_IP6_LOOKUP;
2642 /* Prefetch next iteration. */
2644 vlib_buffer_t *p2, *p3;
2646 p2 = vlib_get_buffer (vm, from[2]);
2647 p3 = vlib_get_buffer (vm, from[3]);
2649 vlib_prefetch_buffer_header (p2, LOAD);
2650 vlib_prefetch_buffer_header (p3, LOAD);
2652 CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
2653 CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
2662 n_left_to_next -= 2;
2666 b0 = vlib_get_buffer (vm, bi0);
2667 ip0 = vlib_buffer_get_current (b0);
2668 sr0 = (ip6_sr_header_t *) (ip0 + 1);
2670 (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2672 ip6_hop_by_hop_ext_t *ext_hdr =
2673 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
2675 (ip6_sr_header_t *) ip6_ext_next_header ((ip6_ext_header_t *)
2679 if (PREDICT_FALSE (sr0->type != ROUTING_HEADER_TYPE_SR))
2681 next0 = SR_LOCAL_NEXT_ERROR;
2683 node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE];
2687 /* Out of segments? Turf the packet */
2688 if (PREDICT_FALSE (sr0->segments_left == 0))
2690 next0 = SR_LOCAL_NEXT_ERROR;
2691 b0->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS];
2695 if (PREDICT_FALSE (sm->validate_hmac))
2697 if (sr_validate_hmac (sm, ip0, sr0))
2699 next0 = SR_LOCAL_NEXT_ERROR;
2700 b0->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID];
2705 next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) : next0;
2708 * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx
2710 if (PREDICT_FALSE (next0 & 0x80000000))
2712 next0 ^= 0xFFFFFFFF;
2713 if (PREDICT_FALSE (next0 == SR_LOCAL_NEXT_ERROR))
2714 b0->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK];
2720 segment_index0 = sr0->segments_left - 1;
2722 /* Rewrite the packet */
2723 new_dst0 = (ip6_address_t *) (sr0->segments + segment_index0);
2724 ip0->dst_address.as_u64[0] = new_dst0->as_u64[0];
2725 ip0->dst_address.as_u64[1] = new_dst0->as_u64[1];
2727 if (PREDICT_TRUE (sr0->segments_left > 0))
2728 sr0->segments_left -= 1;
2731 /* End of the path. Clean up the SR header, or not */
2733 (sr0->segments_left == 0 &&
2735 clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))))
2737 u64 *copy_dst0, *copy_src0;
2739 u32 copy_len_u64s0 = 0;
2743 * Copy the ip6 header right by the (real) length of the
2747 (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2749 ip6_hop_by_hop_ext_t *ext_hdr =
2750 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
2752 (((ip6_ext_header_t *) ext_hdr)->n_data_u64s) + 1;
2753 ext_hdr->next_hdr = sr0->protocol;
2757 ip0->protocol = sr0->protocol;
2759 vlib_buffer_advance (b0, (sr0->length + 1) * 8);
2761 new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
2762 (sr0->length + 1) * 8;
2763 ip0->payload_length = clib_host_to_net_u16 (new_l0);
2765 copy_src0 = (u64 *) ip0;
2766 copy_dst0 = copy_src0 + (sr0->length + 1);
2768 copy_dst0[4 + copy_len_u64s0] = copy_src0[4 + copy_len_u64s0];
2769 copy_dst0[3 + copy_len_u64s0] = copy_src0[3 + copy_len_u64s0];
2770 copy_dst0[2 + copy_len_u64s0] = copy_src0[2 + copy_len_u64s0];
2771 copy_dst0[1 + copy_len_u64s0] = copy_src0[1 + copy_len_u64s0];
2772 copy_dst0[0 + copy_len_u64s0] = copy_src0[0 + copy_len_u64s0];
2774 for (i = copy_len_u64s0 - 1; i >= 0; i--)
2776 copy_dst0[i] = copy_src0[i];
2783 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
2785 sr_local_trace_t *tr = vlib_add_trace (vm, node,
2787 clib_memcpy (tr->src.as_u8, ip0->src_address.as_u8,
2788 sizeof (tr->src.as_u8));
2789 clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
2790 sizeof (tr->dst.as_u8));
2791 tr->length = vlib_buffer_length_in_chain (vm, b0);
2792 tr->next_index = next0;
2793 tr->sr_valid = sr0 != 0;
2795 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
2798 b1 = vlib_get_buffer (vm, bi1);
2799 ip1 = vlib_buffer_get_current (b1);
2800 sr1 = (ip6_sr_header_t *) (ip1 + 1);
2802 (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2805 ip6_hop_by_hop_ext_t *ext_hdr =
2806 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1);
2808 (ip6_sr_header_t *) ip6_ext_next_header ((ip6_ext_header_t *)
2812 if (PREDICT_FALSE (sr1->type != ROUTING_HEADER_TYPE_SR))
2814 next1 = SR_LOCAL_NEXT_ERROR;
2816 node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE];
2820 /* Out of segments? Turf the packet */
2821 if (PREDICT_FALSE (sr1->segments_left == 0))
2823 next1 = SR_LOCAL_NEXT_ERROR;
2824 b1->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS];
2828 if (PREDICT_FALSE (sm->validate_hmac))
2830 if (sr_validate_hmac (sm, ip1, sr1))
2832 next1 = SR_LOCAL_NEXT_ERROR;
2833 b1->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID];
2838 next1 = sr_local_cb ? sr_local_cb (vm, node, b1, ip1, sr1) : next1;
2841 * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx
2843 if (PREDICT_FALSE (next1 & 0x80000000))
2845 next1 ^= 0xFFFFFFFF;
2846 if (PREDICT_FALSE (next1 == SR_LOCAL_NEXT_ERROR))
2847 b1->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK];
2853 segment_index1 = sr1->segments_left - 1;
2855 /* Rewrite the packet */
2856 new_dst1 = (ip6_address_t *) (sr1->segments + segment_index1);
2857 ip1->dst_address.as_u64[0] = new_dst1->as_u64[0];
2858 ip1->dst_address.as_u64[1] = new_dst1->as_u64[1];
2860 if (PREDICT_TRUE (sr1->segments_left > 0))
2861 sr1->segments_left -= 1;
2864 /* End of the path. Clean up the SR header, or not */
2866 (sr1->segments_left == 0 &&
2868 clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))))
2870 u64 *copy_dst1, *copy_src1;
2872 u32 copy_len_u64s1 = 0;
2876 * Copy the ip6 header right by the (real) length of the
2880 (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2882 ip6_hop_by_hop_ext_t *ext_hdr =
2883 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1);
2885 (((ip6_ext_header_t *) ext_hdr)->n_data_u64s) + 1;
2886 ext_hdr->next_hdr = sr1->protocol;
2890 ip1->protocol = sr1->protocol;
2892 vlib_buffer_advance (b1, (sr1->length + 1) * 8);
2894 new_l1 = clib_net_to_host_u16 (ip1->payload_length) -
2895 (sr1->length + 1) * 8;
2896 ip1->payload_length = clib_host_to_net_u16 (new_l1);
2898 copy_src1 = (u64 *) ip1;
2899 copy_dst1 = copy_src1 + (sr1->length + 1);
2901 copy_dst1[4 + copy_len_u64s1] = copy_src1[4 + copy_len_u64s1];
2902 copy_dst1[3 + copy_len_u64s1] = copy_src1[3 + copy_len_u64s1];
2903 copy_dst1[2 + copy_len_u64s1] = copy_src1[2 + copy_len_u64s1];
2904 copy_dst1[1 + copy_len_u64s1] = copy_src1[1 + copy_len_u64s1];
2905 copy_dst1[0 + copy_len_u64s1] = copy_src1[0 + copy_len_u64s1];
2907 for (i = copy_len_u64s1 - 1; i >= 0; i--)
2909 copy_dst1[i] = copy_src1[i];
2916 if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
2918 sr_local_trace_t *tr = vlib_add_trace (vm, node,
2920 clib_memcpy (tr->src.as_u8, ip1->src_address.as_u8,
2921 sizeof (tr->src.as_u8));
2922 clib_memcpy (tr->dst.as_u8, ip1->dst_address.as_u8,
2923 sizeof (tr->dst.as_u8));
2924 tr->length = vlib_buffer_length_in_chain (vm, b1);
2925 tr->next_index = next1;
2926 tr->sr_valid = sr1 != 0;
2928 clib_memcpy (tr->sr, sr1, sizeof (tr->sr));
2931 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
2932 to_next, n_left_to_next,
2933 bi0, bi1, next0, next1);
2936 while (n_left_from > 0 && n_left_to_next > 0)
2940 ip6_header_t *ip0 = 0;
2941 ip6_sr_header_t *sr0;
2942 ip6_address_t *new_dst0;
2943 u32 next0 = SR_LOCAL_NEXT_IP6_LOOKUP;
2950 n_left_to_next -= 1;
2952 b0 = vlib_get_buffer (vm, bi0);
2953 ip0 = vlib_buffer_get_current (b0);
2954 sr0 = (ip6_sr_header_t *) (ip0 + 1);
2957 (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2959 ip6_hop_by_hop_ext_t *ext_hdr =
2960 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
2962 (ip6_sr_header_t *) ip6_ext_next_header ((ip6_ext_header_t *)
2965 if (PREDICT_FALSE (sr0->type != ROUTING_HEADER_TYPE_SR))
2967 next0 = SR_LOCAL_NEXT_ERROR;
2969 node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE];
2973 /* Out of segments? Turf the packet */
2974 if (PREDICT_FALSE (sr0->segments_left == 0))
2976 next0 = SR_LOCAL_NEXT_ERROR;
2977 b0->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS];
2981 if (PREDICT_FALSE (sm->validate_hmac))
2983 if (sr_validate_hmac (sm, ip0, sr0))
2985 next0 = SR_LOCAL_NEXT_ERROR;
2986 b0->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID];
2991 next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) : next0;
2994 * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx
2996 if (PREDICT_FALSE (next0 & 0x80000000))
2998 next0 ^= 0xFFFFFFFF;
2999 if (PREDICT_FALSE (next0 == SR_LOCAL_NEXT_ERROR))
3000 b0->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK];
3006 segment_index0 = sr0->segments_left - 1;
3008 /* Rewrite the packet */
3009 new_dst0 = (ip6_address_t *) (sr0->segments + segment_index0);
3010 ip0->dst_address.as_u64[0] = new_dst0->as_u64[0];
3011 ip0->dst_address.as_u64[1] = new_dst0->as_u64[1];
3013 if (PREDICT_TRUE (sr0->segments_left > 0))
3014 sr0->segments_left -= 1;
3017 /* End of the path. Clean up the SR header, or not */
3019 (sr0->segments_left == 0 &&
3021 clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))))
3023 u64 *copy_dst0, *copy_src0;
3025 u32 copy_len_u64s0 = 0;
3029 * Copy the ip6 header right by the (real) length of the
3033 (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
3035 ip6_hop_by_hop_ext_t *ext_hdr =
3036 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
3038 (((ip6_ext_header_t *) ext_hdr)->n_data_u64s) + 1;
3039 ext_hdr->next_hdr = sr0->protocol;
3043 ip0->protocol = sr0->protocol;
3046 vlib_buffer_advance (b0, (sr0->length + 1) * 8);
3048 new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
3049 (sr0->length + 1) * 8;
3050 ip0->payload_length = clib_host_to_net_u16 (new_l0);
3052 copy_src0 = (u64 *) ip0;
3053 copy_dst0 = copy_src0 + (sr0->length + 1);
3054 copy_dst0[4 + copy_len_u64s0] = copy_src0[4 + copy_len_u64s0];
3055 copy_dst0[3 + copy_len_u64s0] = copy_src0[3 + copy_len_u64s0];
3056 copy_dst0[2 + copy_len_u64s0] = copy_src0[2 + copy_len_u64s0];
3057 copy_dst0[1 + copy_len_u64s0] = copy_src0[1 + copy_len_u64s0];
3058 copy_dst0[0 + copy_len_u64s0] = copy_src0[0 + copy_len_u64s0];
3060 for (i = copy_len_u64s0 - 1; i >= 0; i--)
3062 copy_dst0[i] = copy_src0[i];
3069 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
3071 sr_local_trace_t *tr = vlib_add_trace (vm, node,
3073 clib_memcpy (tr->src.as_u8, ip0->src_address.as_u8,
3074 sizeof (tr->src.as_u8));
3075 clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
3076 sizeof (tr->dst.as_u8));
3077 tr->length = vlib_buffer_length_in_chain (vm, b0);
3078 tr->next_index = next0;
3079 tr->sr_valid = sr0 != 0;
3081 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
3084 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
3085 to_next, n_left_to_next,
3089 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
3091 vlib_node_increment_counter (vm, sr_local_node.index,
3092 SR_LOCAL_ERROR_PKTS_PROCESSED,
3093 from_frame->n_vectors);
3094 return from_frame->n_vectors;
3098 VLIB_REGISTER_NODE (sr_local_node, static) = {
3099 .function = sr_local,
3101 /* Takes a vector of packets. */
3102 .vector_size = sizeof (u32),
3103 .format_trace = format_sr_local_trace,
3105 .runtime_data_bytes = 0,
3107 .n_errors = SR_LOCAL_N_ERROR,
3108 .error_strings = sr_local_error_strings,
3110 .n_next_nodes = SR_LOCAL_N_NEXT,
3112 #define _(s,n) [SR_LOCAL_NEXT_##s] = n,
3113 foreach_sr_local_next
3118 VLIB_NODE_FUNCTION_MULTIARCH (sr_local_node, sr_local)
3122 sr_get_main (vlib_main_t * vm)
3124 vlib_call_init_function (vm, sr_init);
3125 ASSERT (sr_local_node.index);
3130 * @brief CLI parser for SR fix destination rewrite node
3132 * @param vm vlib_main_t *
3133 * @param input unformat_input_t *
3134 * @param cmd vlib_cli_command_t *
3136 * @return error clib_error_t *
3138 static clib_error_t *
3139 set_ip6_sr_rewrite_fn (vlib_main_t * vm,
3140 unformat_input_t * input, vlib_cli_command_t * cmd)
3142 fib_prefix_t pfx = {
3143 .fp_proto = FIB_PROTOCOL_IP6,
3149 ip_adjacency_t *adj;
3150 vnet_hw_interface_t *hi;
3152 ip6_sr_main_t *sm = &sr_main;
3153 vnet_main_t *vnm = vnet_get_main ();
3154 fib_node_index_t fei;
3156 if (!unformat (input, "%U", unformat_ip6_address, &pfx.fp_addr.ip6))
3157 return clib_error_return (0, "ip6 address missing in '%U'",
3158 format_unformat_error, input);
3160 if (unformat (input, "rx-table-id %d", &fib_id))
3162 fib_index = fib_table_id_find_fib_index (FIB_PROTOCOL_IP6, fib_id);
3163 if (fib_index == ~0)
3164 return clib_error_return (0, "fib-id %d not found", fib_id);
3167 fei = fib_table_lookup_exact_match (fib_index, &pfx);
3169 if (FIB_NODE_INDEX_INVALID == fei)
3170 return clib_error_return (0, "no match for %U",
3171 format_ip6_address, &pfx.fp_addr.ip6);
3173 adj_index = fib_entry_get_adj_for_source (fei, FIB_SOURCE_SR);
3175 if (ADJ_INDEX_INVALID == adj_index)
3176 return clib_error_return (0, "%U not SR sourced",
3177 format_ip6_address, &pfx.fp_addr.ip6);
3179 adj = adj_get (adj_index);
3181 if (adj->lookup_next_index != IP_LOOKUP_NEXT_REWRITE)
3182 return clib_error_return (0, "%U unresolved (not a rewrite adj)",
3183 format_ip6_address, &pfx.fp_addr.ip6);
3185 adj->rewrite_header.next_index = sm->ip6_rewrite_sr_next_index;
3187 sw_if_index = adj->rewrite_header.sw_if_index;
3188 hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
3189 adj->rewrite_header.node_index = sr_fix_dst_addr_node.index;
3191 /* $$$$$ hack... steal the interface address index */
3192 adj->if_address_index =
3193 vlib_node_add_next (vm, sr_fix_dst_addr_node.index,
3194 hi->output_node_index);
3200 VLIB_CLI_COMMAND (set_ip6_sr_rewrite, static) = {
3201 .path = "set ip6 sr rewrite",
3202 .short_help = "set ip6 sr rewrite <ip6-address> [fib-id <id>]",
3203 .function = set_ip6_sr_rewrite_fn,
3208 * @brief Register a callback routine to set next0 in sr_local
3213 vnet_register_sr_app_callback (void *cb)
3215 ip6_sr_main_t *sm = &sr_main;
3217 sm->sr_local_cb = cb;
3221 * @brief Test routine for validation of HMAC
3223 static clib_error_t *
3224 test_sr_hmac_validate_fn (vlib_main_t * vm,
3225 unformat_input_t * input, vlib_cli_command_t * cmd)
3227 ip6_sr_main_t *sm = &sr_main;
3229 if (unformat (input, "validate on"))
3230 sm->validate_hmac = 1;
3231 else if (unformat (input, "chunk-offset off"))
3232 sm->validate_hmac = 0;
3234 return clib_error_return (0, "expected validate on|off in '%U'",
3235 format_unformat_error, input);
3237 vlib_cli_output (vm, "hmac signature validation %s",
3238 sm->validate_hmac ? "on" : "off");
3243 VLIB_CLI_COMMAND (test_sr_hmac_validate, static) = {
3244 .path = "test sr hmac",
3245 .short_help = "test sr hmac validate [on|off]",
3246 .function = test_sr_hmac_validate_fn,
3251 * @brief Add or Delete HMAC key
3253 * @param sm ip6_sr_main_t *
3255 * @param shared_secret u8 *
3258 * @return retval i32
3260 // $$$ fixme shouldn't return i32
3262 sr_hmac_add_del_key (ip6_sr_main_t * sm, u32 key_id, u8 * shared_secret,
3266 ip6_sr_hmac_key_t *key;
3270 /* Specific key in use? Fail. */
3271 if (key_id && vec_len (sm->hmac_keys) > key_id
3272 && sm->hmac_keys[key_id].shared_secret)
3276 key = find_or_add_shared_secret (sm, shared_secret, &index);
3277 ASSERT (index == key_id);
3283 if (key_id) /* delete by key ID */
3285 if (vec_len (sm->hmac_keys) <= key_id)
3288 key = sm->hmac_keys + key_id;
3290 hash_unset_mem (sm->hmac_key_by_shared_secret, key->shared_secret);
3291 vec_free (key->shared_secret);
3296 key = find_or_add_shared_secret (sm, shared_secret, &index);
3297 hash_unset_mem (sm->hmac_key_by_shared_secret, key->shared_secret);
3298 vec_free (key->shared_secret);
3303 static clib_error_t *
3304 sr_hmac_add_del_key_fn (vlib_main_t * vm,
3305 unformat_input_t * input, vlib_cli_command_t * cmd)
3307 ip6_sr_main_t *sm = &sr_main;
3311 u8 *shared_secret = 0;
3314 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3316 if (unformat (input, "del"))
3318 else if (unformat (input, "id %d", &key_id))
3320 else if (unformat (input, "key %s", &shared_secret))
3322 /* Do not include the trailing NULL byte. Guaranteed interop issue */
3323 _vec_len (shared_secret) -= 1;
3329 if (is_del == 0 && shared_secret == 0)
3330 return clib_error_return (0, "shared secret must be set to add a key");
3332 if (shared_secret == 0 && key_id_set == 0)
3333 return clib_error_return (0, "shared secret and key id both unset");
3335 rv = sr_hmac_add_del_key (sm, key_id, shared_secret, is_del);
3337 vec_free (shared_secret);
3345 return clib_error_return (0, "sr_hmac_add_del_key returned %d", rv);
3352 VLIB_CLI_COMMAND (sr_hmac, static) = {
3354 .short_help = "sr hmac [del] id <nn> key <str>",
3355 .function = sr_hmac_add_del_key_fn,
3360 * @brief CLI parser for show HMAC key shared secrets
3362 * @param vm vlib_main_t *
3363 * @param input unformat_input_t *
3364 * @param cmd vlib_cli_command_t *
3366 * @return error clib_error_t *
3368 static clib_error_t *
3369 show_sr_hmac_fn (vlib_main_t * vm,
3370 unformat_input_t * input, vlib_cli_command_t * cmd)
3372 ip6_sr_main_t *sm = &sr_main;
3375 for (i = 1; i < vec_len (sm->hmac_keys); i++)
3377 if (sm->hmac_keys[i].shared_secret)
3378 vlib_cli_output (vm, "[%d]: %v", i, sm->hmac_keys[i].shared_secret);
3385 VLIB_CLI_COMMAND (show_sr_hmac, static) = {
3386 .path = "show sr hmac",
3387 .short_help = "show sr hmac",
3388 .function = show_sr_hmac_fn,
3393 * @brief Test for SR debug flag
3395 * @param vm vlib_main_t *
3396 * @param input unformat_input_t *
3397 * @param cmd vlib_cli_command_t *
3399 * @return error clib_error_t *
3401 static clib_error_t *
3402 test_sr_debug_fn (vlib_main_t * vm,
3403 unformat_input_t * input, vlib_cli_command_t * cmd)
3405 ip6_sr_main_t *sm = &sr_main;
3407 if (unformat (input, "on"))
3409 else if (unformat (input, "off"))
3412 return clib_error_return (0, "expected on|off in '%U'",
3413 format_unformat_error, input);
3415 vlib_cli_output (vm, "debug trace now %s", sm->is_debug ? "on" : "off");
3421 VLIB_CLI_COMMAND (test_sr_debug, static) = {
3422 .path = "test sr debug",
3423 .short_help = "test sr debug on|off",
3424 .function = test_sr_debug_fn,
3429 * fd.io coding-style-patch-verification: ON
3432 * eval: (c-set-style "gnu")