Fix SR multicast post mfib commit
[vpp.git] / src / vnet / sr / sr.c
1 /*
2  * sr.c: ipv6 segment routing
3  *
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:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17
18 /**
19  * @file
20  * @brief Segment Routing main functions
21  *
22  */
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>
29
30 #include <openssl/hmac.h>
31
32 ip6_sr_main_t sr_main;
33 static vlib_node_registration_t sr_local_node;
34
35 /**
36  * @brief Dynamically added SR DPO type
37  */
38 static dpo_type_t sr_dpo_type;
39
40 /**
41  * @brief Dynamically added SR FIB Node type
42  */
43 static fib_node_type_t sr_fib_node_type;
44
45 /**
46  * @brief Use passed HMAC key in ip6_sr_header_t in OpenSSL HMAC routines
47  *
48  * @param sm ip6_sr_main_t *
49  * @param ip ip6_header_t *
50  * @param sr ip6_sr_header_t *
51  */
52 void
53 sr_fix_hmac (ip6_sr_main_t * sm, ip6_header_t * ip, ip6_sr_header_t * sr)
54 {
55   u32 key_index;
56   static u8 *keybuf;
57   u8 *copy_target;
58   int first_segment;
59   ip6_address_t *addrp;
60   int i;
61   ip6_sr_hmac_key_t *hmac_key;
62   u32 sig_len;
63
64   key_index = sr->hmac_key;
65
66   /* No signature? Pass... */
67   if (key_index == 0)
68     return;
69
70   /* We don't know about this key? Fail... */
71   if (key_index >= vec_len (sm->hmac_keys))
72     return;
73
74   hmac_key = sm->hmac_keys + key_index;
75
76   vec_reset_length (keybuf);
77
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));
81
82   /* first segment */
83   vec_add2 (keybuf, copy_target, 1);
84   copy_target[0] = sr->first_segment;
85
86   /* octet w/ bit 0 = "clean" flag */
87   vec_add2 (keybuf, copy_target, 1);
88   copy_target[0]
89     = (sr->flags & clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))
90     ? 0x80 : 0;
91
92   /* hmac key id */
93   vec_add2 (keybuf, copy_target, 1);
94   copy_target[0] = sr->hmac_key;
95
96   first_segment = sr->first_segment;
97
98   addrp = sr->segments;
99
100   /* segments */
101   for (i = 0; i <= first_segment; i++)
102     {
103       vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
104       clib_memcpy (copy_target, addrp->as_u8, sizeof (ip6_address_t));
105       addrp++;
106     }
107
108   addrp++;
109
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);
119 }
120
121 /**
122  * @brief Format function for decoding various SR flags
123  *
124  * @param s u8 * - formatted string
125  * @param args va_list * - u16 flags
126  *
127  * @return formatted output string u8 *
128  */
129 u8 *
130 format_ip6_sr_header_flags (u8 * s, va_list * args)
131 {
132   u16 flags = (u16) va_arg (*args, int);
133   u8 pl_flag;
134   int bswap_needed = va_arg (*args, int);
135   int i;
136
137   if (bswap_needed)
138     flags = clib_host_to_net_u16 (flags);
139
140   if (flags & IP6_SR_HEADER_FLAG_CLEANUP)
141     s = format (s, "cleanup ");
142
143   if (flags & IP6_SR_HEADER_FLAG_PROTECTED)
144     s = format (s, "reroute ");
145
146   s = format (s, "pl: ");
147   for (i = 1; i <= 4; i++)
148     {
149       pl_flag = ip6_sr_policy_list_flags (flags, i);
150       s = format (s, "[%d] ", i);
151
152       switch (pl_flag)
153         {
154         case IP6_SR_HEADER_FLAG_PL_ELT_NOT_PRESENT:
155           s = format (s, "NotPr ");
156           break;
157         case IP6_SR_HEADER_FLAG_PL_ELT_INGRESS_PE:
158           s = format (s, "InPE ");
159           break;
160         case IP6_SR_HEADER_FLAG_PL_ELT_EGRESS_PE:
161           s = format (s, "EgPE ");
162           break;
163
164         case IP6_SR_HEADER_FLAG_PL_ELT_ORIG_SRC_ADDR:
165           s = format (s, "OrgSrc ");
166           break;
167         }
168     }
169   return s;
170 }
171
172 /**
173  * @brief Format function for decoding ip6_sr_header_t
174  *
175  * @param s u8 * - formatted string
176  * @param args va_list * - ip6_sr_header_t
177  *
178  * @return formatted output string u8 *
179  */
180 u8 *
181 format_ip6_sr_header (u8 * s, va_list * args)
182 {
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,
186        254, 254}
187   };
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);
191
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 */ );
198
199   /*
200    * Header length is in 8-byte units (minus one), so
201    * divide by 2 to ascertain the number of ip6 addresses in the
202    * segment list
203    */
204   max_segs = (h->length >> 1);
205
206   if (!print_hmac && h->hmac_key)
207     max_segs -= 2;
208
209   s = format (s, "\n  Segments (in processing order):");
210
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)");
215   else
216     s = format (s, "\n  %U", format_ip6_address, h->segments);
217
218   s = format (s, "\n  Policy List:");
219
220   pl_index = 1;                 /* to match the RFC text */
221   for (i = (h->first_segment + 1); i < max_segs; i++, pl_index++)
222     {
223       char *tag;
224       char *tags[] = { " ", "InPE: ", "EgPE: ", "OrgSrc: " };
225
226       tag = tags[0];
227       if (pl_index >= 1 && pl_index <= 4)
228         {
229           int this_pl_flag = ip6_sr_policy_list_flags
230             (flags_host_byte_order, pl_index);
231           tag = tags[this_pl_flag];
232         }
233
234       s = format (s, "\n  %s%U", tag, format_ip6_address, h->segments + i);
235     }
236
237   return s;
238 }
239
240 /**
241  * @brief Format function for decoding ip6_sr_header_t with length
242  *
243  * @param s u8 * - formatted string
244  * @param args va_list * - ip6_header_t + ip6_sr_header_t
245  *
246  * @return formatted output string u8 *
247  */
248 u8 *
249 format_ip6_sr_header_with_length (u8 * s, va_list * args)
250 {
251   ip6_header_t *h = va_arg (*args, ip6_header_t *);
252   u32 max_header_bytes = va_arg (*args, u32);
253   uword header_bytes;
254
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");
258
259   s = format (s, "IP6: %U\n", format_ip6_header, h, max_header_bytes);
260   s =
261     format (s, "SR: %U\n", format_ip6_sr_header, (ip6_sr_header_t *) (h + 1),
262             0 /* print_hmac */ , max_header_bytes);
263   return s;
264 }
265
266 /**
267  * @brief Defined valid next nodes
268 */
269 #define foreach_sr_rewrite_next                 \
270 _(ERROR, "error-drop")                          \
271 _(SR_LOCAL, "sr-local")
272
273 /**
274  * @brief Struct for defined valid next nodes
275 */
276 typedef enum
277 {
278 #define _(s,n) SR_REWRITE_NEXT_##s,
279   foreach_sr_rewrite_next
280 #undef _
281     SR_REWRITE_N_NEXT,
282 } sr_rewrite_next_t;
283
284 /**
285  * @brief Struct for data for SR rewrite packet trace
286  */
287 typedef struct
288 {
289   ip6_address_t src, dst;
290   u16 length;
291   u32 next_index;
292   u32 tunnel_index;
293   u8 sr[256];
294 } sr_rewrite_trace_t;
295
296 /**
297  * @brief Error strings for SR rewrite
298  */
299 static char *sr_rewrite_error_strings[] = {
300 #define sr_error(n,s) s,
301 #include "sr_error.def"
302 #undef sr_error
303 };
304
305 /**
306  * @brief Struct for SR rewrite error strings
307  */
308 typedef enum
309 {
310 #define sr_error(n,s) SR_REWRITE_ERROR_##n,
311 #include "sr_error.def"
312 #undef sr_error
313   SR_REWRITE_N_ERROR,
314 } sr_rewrite_error_t;
315
316
317 /**
318  * @brief Format function for SR rewrite trace.
319  */
320 u8 *
321 format_sr_rewrite_trace (u8 * s, va_list * args)
322 {
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;
329
330   rx_fib = ip6_fib_get (tun->rx_fib_index);
331   tx_fib = ip6_fib_get (tun->tx_fib_index);
332
333   s = format
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 */ );
342   return s;
343 }
344
345 /**
346  * @brief Main processing dual-loop for Segment Routing Rewrite
347  * @node sr-rewrite
348  *
349  * @param vm vlib_main_t *
350  * @param node vlib_node_runtime_t *
351  * @param from_frame vlib_frame_t *
352  *
353  * @return from_frame->n_vectors uword
354  */
355 static uword
356 sr_rewrite (vlib_main_t * vm,
357             vlib_node_runtime_t * node, vlib_frame_t * from_frame)
358 {
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;
364
365   from = vlib_frame_vector_args (from_frame);
366   n_left_from = from_frame->n_vectors;
367
368   next_index = node->cached_next_index;
369
370   while (n_left_from > 0)
371     {
372       u32 n_left_to_next;
373
374       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
375
376       /* Note 2x loop disabled */
377       while (0 && n_left_from >= 4 && n_left_to_next >= 2)
378         {
379           u32 bi0, bi1;
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;
384           u32 next0;
385           u32 next1;
386           u16 new_l0 = 0;
387           u16 new_l1 = 0;
388
389           /* Prefetch next iteration. */
390           {
391             vlib_buffer_t *p2, *p3;
392
393             p2 = vlib_get_buffer (vm, from[2]);
394             p3 = vlib_get_buffer (vm, from[3]);
395
396             vlib_prefetch_buffer_header (p2, LOAD);
397             vlib_prefetch_buffer_header (p3, LOAD);
398           }
399
400           bi0 = from[0];
401           bi1 = from[1];
402           to_next[0] = bi0;
403           to_next[1] = bi1;
404           from += 2;
405           to_next += 2;
406           n_left_to_next -= 2;
407           n_left_from -= 2;
408
409           b0 = vlib_get_buffer (vm, bi0);
410           b1 = vlib_get_buffer (vm, bi1);
411
412           /*
413            * $$$ parse through header(s) to pick the point
414            * where we punch in the SR extention header
415            */
416           t0 =
417             pool_elt_at_index (sm->tunnels,
418                                vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
419           t1 =
420             pool_elt_at_index (sm->tunnels,
421                                vnet_buffer (b1)->ip.adj_index[VLIB_TX]);
422
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);
427
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;
430
431           ip0 = vlib_buffer_get_current (b0);
432           ip1 = vlib_buffer_get_current (b1);
433
434           /*
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
438            */
439           if (PREDICT_FALSE (ip0->protocol == IPPROTO_IPV6_ROUTE))
440             {
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;
445             }
446           else
447             {
448               u32 len_bytes = sizeof (ip6_header_t);
449               u8 next_hdr = ip0->protocol;
450
451               /* HBH must immediately follow ipv6 header */
452               if (PREDICT_FALSE
453                   (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
454                 {
455                   ip6_hop_by_hop_ext_t *ext_hdr =
456                     (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
457                   len_bytes +=
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;
462                 }
463               else
464                 {
465                   ip0->protocol = IPPROTO_IPV6_ROUTE;   /* routing extension header */
466                 }
467               /*
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.
471                */
472               clib_memcpy (((u8 *) ip0) - vec_len (t0->rewrite),
473                            ip0, len_bytes);
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);
477               /* $$$ tune */
478               clib_memcpy (sr0, t0->rewrite, vec_len (t0->rewrite));
479
480               /* Fix the next header chain */
481               sr0->protocol = next_hdr;
482
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);
486
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));
493
494               sr_fix_hmac (sm, ip0, sr0);
495
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);
501
502               /*
503                * Ignore "do not rewrite" shtik in this path
504                */
505               if (PREDICT_FALSE (next0 & 0x80000000))
506                 {
507                   next0 ^= 0xFFFFFFFF;
508                   if (PREDICT_FALSE (next0 == SR_REWRITE_NEXT_ERROR))
509                     b0->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK];
510                 }
511             }
512
513           if (PREDICT_FALSE (ip1->protocol == IPPROTO_IPV6_ROUTE))
514             {
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;
519             }
520           else
521             {
522               u32 len_bytes = sizeof (ip6_header_t);
523               u8 next_hdr = ip1->protocol;
524
525               /* HBH must immediately follow ipv6 header */
526               if (PREDICT_FALSE
527                   (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
528                 {
529                   ip6_hop_by_hop_ext_t *ext_hdr =
530                     (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1);
531                   len_bytes +=
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;
536                 }
537               else
538                 {
539                   ip1->protocol = IPPROTO_IPV6_ROUTE;
540                 }
541               /*
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.
545                */
546               clib_memcpy (((u8 *) ip1) - vec_len (t1->rewrite),
547                            ip1, len_bytes);
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));
552
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);
557
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));
564
565               sr_fix_hmac (sm, ip1, sr1);
566
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);
572
573               /*
574                * Ignore "do not rewrite" shtik in this path
575                */
576               if (PREDICT_FALSE (next1 & 0x80000000))
577                 {
578                   next1 ^= 0xFFFFFFFF;
579                   if (PREDICT_FALSE (next1 == SR_REWRITE_NEXT_ERROR))
580                     b1->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK];
581                 }
582             }
583
584           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
585             {
586               sr_rewrite_trace_t *tr = vlib_add_trace (vm, node,
587                                                        b0, sizeof (*tr));
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));
593               tr->length = new_l0;
594               tr->next_index = next0;
595               if (sr0)
596                 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
597             }
598           if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
599             {
600               sr_rewrite_trace_t *tr = vlib_add_trace (vm, node,
601                                                        b1, sizeof (*tr));
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));
607               tr->length = new_l1;
608               tr->next_index = next1;
609               if (sr1)
610                 clib_memcpy (tr->sr, sr1, sizeof (tr->sr));
611             }
612           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
613                                            to_next, n_left_to_next,
614                                            bi0, bi1, next0, next1);
615         }
616
617       while (n_left_from > 0 && n_left_to_next > 0)
618         {
619           u32 bi0;
620           vlib_buffer_t *b0;
621           ip6_header_t *ip0 = 0;
622           ip6_sr_header_t *sr0 = 0;
623           ip6_sr_tunnel_t *t0;
624           u32 next0;
625           u16 new_l0 = 0;
626
627           bi0 = from[0];
628           to_next[0] = bi0;
629           from += 1;
630           to_next += 1;
631           n_left_from -= 1;
632           n_left_to_next -= 1;
633
634           b0 = vlib_get_buffer (vm, bi0);
635
636
637           /*
638            * $$$ parse through header(s) to pick the point
639            * where we punch in the SR extention header
640            */
641           t0 =
642             pool_elt_at_index (sm->tunnels,
643                                vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
644
645           ASSERT (VLIB_BUFFER_PRE_DATA_SIZE
646                   >= ((word) vec_len (t0->rewrite)) + b0->current_data);
647
648           vnet_buffer (b0)->sw_if_index[VLIB_TX] = t0->tx_fib_index;
649
650           ip0 = vlib_buffer_get_current (b0);
651
652           /*
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
656            */
657           if (PREDICT_FALSE (ip0->protocol == IPPROTO_IPV6_ROUTE))
658             {
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;
663             }
664           else
665             {
666               u32 len_bytes = sizeof (ip6_header_t);
667               u8 next_hdr = ip0->protocol;
668
669               /* HBH must immediately follow ipv6 header */
670               if (PREDICT_FALSE
671                   (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
672                 {
673                   ip6_hop_by_hop_ext_t *ext_hdr =
674                     (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
675                   len_bytes +=
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 */
680                 }
681               else
682                 {
683                   ip0->protocol = IPPROTO_IPV6_ROUTE;   /* routing extension header */
684                 }
685               /*
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.
689                */
690               clib_memcpy (((u8 *) ip0) - vec_len (t0->rewrite),
691                            ip0, len_bytes);
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);
695               /* $$$ tune */
696               clib_memcpy (sr0, t0->rewrite, vec_len (t0->rewrite));
697
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);
703
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));
710
711               sr_fix_hmac (sm, ip0, sr0);
712
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);
718
719               /*
720                * Ignore "do not rewrite" shtik in this path
721                */
722               if (PREDICT_FALSE (next0 & 0x80000000))
723                 {
724                   next0 ^= 0xFFFFFFFF;
725                   if (PREDICT_FALSE (next0 == SR_REWRITE_NEXT_ERROR))
726                     b0->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK];
727                 }
728             }
729
730           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
731             {
732               sr_rewrite_trace_t *tr = vlib_add_trace (vm, node,
733                                                        b0, sizeof (*tr));
734               tr->tunnel_index = t0 - sm->tunnels;
735               if (ip0)
736                 {
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));
741                 }
742               tr->length = new_l0;
743               tr->next_index = next0;
744               if (sr0)
745                 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
746             }
747           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
748                                            to_next, n_left_to_next,
749                                            bi0, next0);
750         }
751       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
752     }
753   return from_frame->n_vectors;
754 }
755
756 /* *INDENT-OFF* */
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,
764
765   .n_errors = SR_REWRITE_N_ERROR,
766   .error_strings = sr_rewrite_error_strings,
767
768   .runtime_data_bytes = 0,
769
770   .n_next_nodes = SR_REWRITE_N_NEXT,
771   .next_nodes = {
772 #define _(s,n) [SR_REWRITE_NEXT_##s] = n,
773     foreach_sr_rewrite_next
774 #undef _
775   },
776 };
777
778 VLIB_NODE_FUNCTION_MULTIARCH (sr_rewrite_node, sr_rewrite)
779 /* *INDENT-ON* */
780
781 static int
782 ip6_routes_add_del (ip6_sr_tunnel_t * t, int is_del)
783 {
784   ip6_sr_main_t *sm = &sr_main;
785
786   /*
787    * the prefix for the tunnel's destination
788    */
789   /* *INDENT-OFF* */
790   fib_prefix_t pfx = {
791     .fp_proto = FIB_PROTOCOL_IP6,
792     .fp_len = t->dst_mask_width,
793     .fp_addr = {
794       .ip6 = t->key.dst,
795     }
796   };
797   /* *INDENT-ON* */
798
799   if (is_del)
800     {
801       fib_table_entry_delete (t->rx_fib_index, &pfx, FIB_SOURCE_SR);
802     }
803   else
804     {
805       dpo_id_t dpo = DPO_INVALID;
806
807       dpo_set (&dpo, sr_dpo_type, DPO_PROTO_IP6, t - sm->tunnels);
808       fib_table_entry_special_dpo_add (t->rx_fib_index,
809                                        &pfx,
810                                        FIB_SOURCE_SR,
811                                        FIB_ENTRY_FLAG_EXCLUSIVE, &dpo);
812       dpo_reset (&dpo);
813     }
814
815   /*
816    * Track the first hop address so we don't need to perform an extra
817    * lookup in the data-path
818    */
819   /* *INDENT-OFF* */
820   const fib_prefix_t first_hop_pfx = {
821     .fp_len = 128,
822     .fp_proto = FIB_PROTOCOL_IP6,
823     .fp_addr = {
824       .ip6 = t->first_hop,
825     }
826   };
827   /* *INDENT-ON* */
828
829   if (is_del)
830     {
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);
833     }
834   else
835     {
836       t->fib_entry_index =
837         fib_table_entry_special_add (t->rx_fib_index,
838                                      &first_hop_pfx,
839                                      FIB_SOURCE_RR,
840                                      FIB_ENTRY_FLAG_NONE, ADJ_INDEX_INVALID);
841       t->sibling_index =
842         fib_entry_child_add (t->fib_entry_index,
843                              sr_fib_node_type, t - sm->tunnels);
844     }
845
846   return 0;
847 }
848
849 /**
850  * @brief Find or add if not found - HMAC shared secret
851  *
852  * @param sm ip6_sr_main_t *
853  * @param secret u8 *
854  * @param indexp u32 *
855  *
856  * @return ip6_sr_hmac_key_t *
857  */
858 static ip6_sr_hmac_key_t *
859 find_or_add_shared_secret (ip6_sr_main_t * sm, u8 * secret, u32 * indexp)
860 {
861   uword *p;
862   ip6_sr_hmac_key_t *key = 0;
863   int i;
864
865   p = hash_get_mem (sm->hmac_key_by_shared_secret, secret);
866
867   if (p)
868     {
869       key = vec_elt_at_index (sm->hmac_keys, p[0]);
870       if (indexp)
871         *indexp = p[0];
872       return (key);
873     }
874
875   /* Specific key ID? */
876   if (indexp && *indexp)
877     {
878       vec_validate (sm->hmac_keys, *indexp);
879       key = sm->hmac_keys + *indexp;
880     }
881   else
882     {
883       for (i = 0; i < vec_len (sm->hmac_keys); i++)
884         {
885           if (sm->hmac_keys[i].shared_secret == 0)
886             {
887               key = sm->hmac_keys + i;
888               goto found;
889             }
890         }
891       vec_validate (sm->hmac_keys, i);
892       key = sm->hmac_keys + i;
893     found:
894       ;
895     }
896
897   key->shared_secret = vec_dup (secret);
898
899   hash_set_mem (sm->hmac_key_by_shared_secret, key->shared_secret,
900                 key - sm->hmac_keys);
901
902   if (indexp)
903     *indexp = key - sm->hmac_keys;
904   return (key);
905 }
906
907 /**
908  * @brief Stack a tunnel on the forwarding chain of the first-hop
909  */
910 static void
911 sr_tunnel_stack (ip6_sr_tunnel_t * st)
912 {
913   dpo_stack (sr_dpo_type,
914              DPO_PROTO_IP6,
915              &st->first_hop_dpo,
916              fib_entry_contribute_ip_forwarding (st->fib_entry_index));
917 }
918
919 /**
920  * @brief Add or Delete a Segment Routing tunnel.
921  *
922  * @param a ip6_sr_add_del_tunnel_args_t *
923  *
924  * @return retval int
925  */
926 int
927 ip6_sr_add_del_tunnel (ip6_sr_add_del_tunnel_args_t * a)
928 {
929   ip6_main_t *im = &ip6_main;
930   ip6_sr_tunnel_key_t key;
931   ip6_sr_tunnel_t *t;
932   uword *p, *n;
933   ip6_sr_header_t *h = 0;
934   u32 header_length;
935   ip6_address_t *addrp, *this_address;
936   ip6_sr_main_t *sm = &sr_main;
937   u8 *key_copy;
938   u32 rx_fib_index, tx_fib_index;
939   u32 hmac_key_index_u32;
940   u8 hmac_key_index = 0;
941   ip6_sr_policy_t *pt;
942   int i;
943
944   /* Make sure that the rx FIB exists */
945   p = hash_get (im->fib_index_by_table_id, a->rx_table_id);
946
947   if (p == 0)
948     return -3;
949
950   /* remember the FIB index */
951   rx_fib_index = p[0];
952
953   /* Make sure that the supplied FIB exists */
954   p = hash_get (im->fib_index_by_table_id, a->tx_table_id);
955
956   if (p == 0)
957     return -4;
958
959   /* remember the FIB index */
960   tx_fib_index = p[0];
961
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));
964
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
972    *   thing.
973    */
974
975   /* Lookup the key */
976   p = hash_get_mem (sm->tunnel_index_by_key, &key);
977
978   /* If the name is given, look it up */
979   if (a->name)
980     n = hash_get_mem (sm->tunnel_index_by_name, a->name);
981   else
982     n = 0;
983
984   /* validate key/name parameters */
985   if (!a->is_del)               /* adding a tunnel */
986     {
987       if (a->name && n)         /* name given & exists already */
988         return -1;
989       if (p)                    /* key exists already */
990         return -1;
991     }
992   else                          /* deleting a tunnel */
993     {
994       if (!p)                   /* key doesn't exist */
995         return -2;
996       if (a->name && !n)        /* name given & it doesn't exist */
997         return -2;
998
999       if (n)                    /* name given & found */
1000         {
1001           if (n[0] != p[0])     /* name and key do not point to the same thing */
1002             return -2;
1003         }
1004     }
1005
1006
1007   if (a->is_del)                /* delete the tunnel */
1008     {
1009       hash_pair_t *hp;
1010
1011       /* Delete existing tunnel */
1012       t = pool_elt_at_index (sm->tunnels, p[0]);
1013
1014       ip6_routes_add_del (t, 1);
1015
1016       vec_free (t->rewrite);
1017       /* Remove tunnel from any policy if associated */
1018       if (t->policy_index != ~0)
1019         {
1020           pt = pool_elt_at_index (sm->policies, t->policy_index);
1021           for (i = 0; i < vec_len (pt->tunnel_indices); i++)
1022             {
1023               if (pt->tunnel_indices[i] == t - sm->tunnels)
1024                 {
1025                   vec_delete (pt->tunnel_indices, 1, i);
1026                   goto found;
1027                 }
1028             }
1029           clib_warning ("Tunnel index %d not found in policy_index %d",
1030                         t - sm->tunnels, pt - sm->policies);
1031         found:
1032           /* If this is last tunnel in the  policy, clean up the policy too */
1033           if (vec_len (pt->tunnel_indices) == 0)
1034             {
1035               hash_unset_mem (sm->policy_index_by_policy_name, pt->name);
1036               vec_free (pt->name);
1037               pool_put (sm->policies, pt);
1038             }
1039         }
1040
1041       /* Clean up the tunnel by name */
1042       if (t->name)
1043         {
1044           hash_unset_mem (sm->tunnel_index_by_name, t->name);
1045           vec_free (t->name);
1046         }
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);
1053       return 0;
1054     }
1055
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);
1061
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;
1066
1067   if (!vec_len (a->segments))
1068     /* there must be at least one segment... */
1069     return -4;
1070
1071   /* The first specified hop goes right into the dst address */
1072   clib_memcpy (&t->first_hop, &a->segments[0], sizeof (ip6_address_t));
1073
1074   /*
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.
1078    */
1079   header_length = sizeof (*h) +
1080     sizeof (ip6_address_t) * (vec_len (a->segments) + 1 + vec_len (a->tags));
1081
1082   if (a->shared_secret)
1083     {
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);
1088
1089       /* Hey Vinz Clortho: Gozzer is pissed.. you're out of keys! */
1090       if (hmac_key_index_u32 >= 256)
1091         return -5;
1092       hmac_key_index = hmac_key_index_u32;
1093       header_length += SHA256_DIGEST_LENGTH;
1094     }
1095
1096   vec_validate (t->rewrite, header_length - 1);
1097
1098   h = (ip6_sr_header_t *) t->rewrite;
1099
1100   h->protocol = 0xFF;           /* we don't know yet */
1101
1102   h->length = (header_length / 8) - 1;
1103   h->type = ROUTING_HEADER_TYPE_SR;
1104
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.
1109    */
1110   h->first_segment = h->segments_left = vec_len (a->segments);
1111
1112   if (a->shared_secret)
1113     h->hmac_key = hmac_key_index & 0xFF;
1114
1115   h->flags = a->flags_net_byte_order;
1116
1117   /* Paint on the segment list, in reverse.
1118    * This is offset by one to leave room at the start for the ultimate
1119    * destination.
1120    */
1121   addrp = h->segments + vec_len (a->segments);
1122
1123   vec_foreach (this_address, a->segments)
1124   {
1125     clib_memcpy (addrp->as_u8, this_address->as_u8, sizeof (ip6_address_t));
1126     addrp--;
1127   }
1128
1129   /*
1130    * Since the ultimate destination address is not yet known, set that slot
1131    * to a value we will instantly recognize as bogus.
1132    */
1133   memset (h->segments, 0xfe, sizeof (ip6_address_t));
1134
1135   /* Paint on the tag list, not reversed */
1136   addrp = h->segments + vec_len (a->segments);
1137
1138   vec_foreach (this_address, a->tags)
1139   {
1140     clib_memcpy (addrp->as_u8, this_address->as_u8, sizeof (ip6_address_t));
1141     addrp++;
1142   }
1143
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);
1147
1148   /*
1149    * Stick the tunnel index into the rewrite header.
1150    *
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.
1155    *
1156    * We don't handle ugly RFC-related cases yet, but I'm sure PL will complain
1157    * at some point...
1158    */
1159
1160   /*
1161    * Add the routes for the tunnel destination and first-hop, then stack
1162    * the tunnel on the appropriate forwarding DPOs.
1163    */
1164   ip6_routes_add_del (t, 0);
1165   sr_tunnel_stack (t);
1166
1167   if (a->policy_name)
1168     {
1169       p = hash_get_mem (sm->policy_index_by_policy_name, a->policy_name);
1170       if (p)
1171         {
1172           pt = pool_elt_at_index (sm->policies, p[0]);
1173         }
1174       else                      /* no policy, lets create one */
1175         {
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,
1180                         pt - sm->policies);
1181           p = hash_get_mem (sm->policy_index_by_policy_name, a->policy_name);
1182         }
1183       vec_add1 (pt->tunnel_indices, t - sm->tunnels);
1184       if (p == 0)
1185         clib_warning ("p is NULL!");
1186       t->policy_index = p ? p[0] : ~0;  /* equiv. to (pt - sm->policies) */
1187     }
1188
1189   if (a->name)
1190     {
1191       t->name = format (0, "%s%c", a->name, 0);
1192       hash_set_mem (sm->tunnel_index_by_name, t->name, t - sm->tunnels);
1193     }
1194
1195   return 0;
1196 }
1197
1198 /**
1199  * @brief no-op lock function.
1200  * The lifetime of the SR entry is managed by the control plane
1201  */
1202 static void
1203 sr_dpo_lock (dpo_id_t * dpo)
1204 {
1205 }
1206
1207 /**
1208  * @brief no-op unlock function.
1209  * The lifetime of the SR entry is managed by the control plane
1210  */
1211 static void
1212 sr_dpo_unlock (dpo_id_t * dpo)
1213 {
1214 }
1215
1216 u8 *
1217 format_sr_dpo (u8 * s, va_list * args)
1218 {
1219   index_t index = va_arg (*args, index_t);
1220   CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
1221
1222   return (format (s, "SR: tunnel:[%d]", index));
1223 }
1224
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,
1229 };
1230
1231 const static char *const sr_ip6_nodes[] = {
1232   "sr-rewrite",
1233   NULL,
1234 };
1235
1236 const static char *const *const sr_nodes[DPO_PROTO_NUM] = {
1237   [DPO_PROTO_IP6] = sr_ip6_nodes,
1238 };
1239
1240 static ip6_sr_tunnel_t *
1241 sr_tunnel_from_fib_node (fib_node_t * node)
1242 {
1243 #if (CLIB_DEBUG > 0)
1244   ASSERT (sr_fib_node_type == node->fn_type);
1245 #endif
1246   return ((ip6_sr_tunnel_t *) (((char *) node) -
1247                                STRUCT_OFFSET_OF (ip6_sr_tunnel_t, node)));
1248 }
1249
1250 /**
1251  * Function definition to backwalk a FIB node
1252  */
1253 static fib_node_back_walk_rc_t
1254 sr_tunnel_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
1255 {
1256   sr_tunnel_stack (sr_tunnel_from_fib_node (node));
1257
1258   return (FIB_NODE_BACK_WALK_CONTINUE);
1259 }
1260
1261 /**
1262  * Function definition to get a FIB node from its index
1263  */
1264 static fib_node_t *
1265 sr_tunnel_fib_node_get (fib_node_index_t index)
1266 {
1267   ip6_sr_tunnel_t *st;
1268   ip6_sr_main_t *sm;
1269
1270   sm = &sr_main;
1271   st = pool_elt_at_index (sm->tunnels, index);
1272
1273   return (&st->node);
1274 }
1275
1276 /**
1277  * Function definition to inform the FIB node that its last lock has gone.
1278  */
1279 static void
1280 sr_tunnel_last_lock_gone (fib_node_t * node)
1281 {
1282   /*
1283    * The SR tunnel is a root of the graph. As such
1284    * it never has children and thus is never locked.
1285    */
1286   ASSERT (0);
1287 }
1288
1289 /*
1290  * Virtual function table registered by SR tunnels
1291  * for participation in the FIB object graph.
1292  */
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,
1297 };
1298
1299 /**
1300  * @brief CLI parser for Add or Delete a Segment Routing tunnel.
1301  *
1302  * @param vm vlib_main_t *
1303  * @param input unformat_input_t *
1304  * @param cmd vlib_cli_command_t *
1305  *
1306  * @return error clib_error_t *
1307  */
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)
1312 {
1313   int is_del = 0;
1314   ip6_address_t src_address;
1315   int src_address_set = 0;
1316   ip6_address_t dst_address;
1317   u32 dst_mask_width;
1318   int dst_address_set = 0;
1319   u16 flags = 0;
1320   u8 *shared_secret = 0;
1321   u8 *name = 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;
1331   int pl_index;
1332   int rv;
1333
1334   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1335     {
1336       if (unformat (input, "del"))
1337         is_del = 1;
1338       else if (unformat (input, "rx-fib-id %d", &rx_table_id))
1339         ;
1340       else if (unformat (input, "tx-fib-id %d", &tx_table_id))
1341         ;
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))
1345         ;
1346       else if (unformat (input, "policy %s", &policy_name))
1347         ;
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,
1352                          &next_address))
1353         {
1354           vec_add2 (segments, this_seg, 1);
1355           clib_memcpy (this_seg->as_u8, next_address.as_u8,
1356                        sizeof (*this_seg));
1357         }
1358       else if (unformat (input, "tag %U", unformat_ip6_address, &tag))
1359         {
1360           vec_add2 (tags, this_tag, 1);
1361           clib_memcpy (this_tag->as_u8, tag.as_u8, sizeof (*this_tag));
1362         }
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))
1371         {
1372           if (pl_index <= 0 || pl_index > 4)
1373             {
1374             pl_index_range_error:
1375               return clib_error_return
1376                 (0, "Policy List Element Index %d out of range (1-4)",
1377                  pl_index);
1378
1379             }
1380           flags |= IP6_SR_HEADER_FLAG_PL_ELT_INGRESS_PE
1381             << ip6_sr_policy_list_shift_from_index (pl_index);
1382         }
1383       else if (unformat (input, "EgPE %d", &pl_index))
1384         {
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);
1389         }
1390       else if (unformat (input, "OrgSrc %d", &pl_index))
1391         {
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);
1396         }
1397       else
1398         break;
1399     }
1400
1401   if (!src_address_set)
1402     return clib_error_return (0, "src address required");
1403
1404   if (!dst_address_set)
1405     return clib_error_return (0, "dst address required");
1406
1407   if (!segments)
1408     return clib_error_return (0, "at least one sr segment required");
1409
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;
1415   a->tags = tags;
1416   a->flags_net_byte_order = clib_host_to_net_u16 (flags);
1417   a->is_del = is_del;
1418   a->rx_table_id = rx_table_id;
1419   a->tx_table_id = tx_table_id;
1420   a->shared_secret = shared_secret;
1421
1422   if (vec_len (name))
1423     a->name = name;
1424   else
1425     a->name = 0;
1426
1427   if (vec_len (policy_name))
1428     a->policy_name = policy_name;
1429   else
1430     a->policy_name = 0;
1431
1432   rv = ip6_sr_add_del_tunnel (a);
1433
1434   vec_free (segments);
1435   vec_free (tags);
1436   vec_free (shared_secret);
1437
1438   switch (rv)
1439     {
1440     case 0:
1441       break;
1442
1443     case -1:
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);
1447
1448     case -2:
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);
1452
1453     case -3:
1454       return clib_error_return (0, "FIB table %d does not exist",
1455                                 rx_table_id);
1456
1457     case -4:
1458       return clib_error_return (0, "At least one segment is required");
1459
1460     default:
1461       return clib_error_return (0, "BUG: ip6_sr_add_del_tunnel returns %d",
1462                                 rv);
1463     }
1464
1465   return 0;
1466 }
1467
1468 /* *INDENT-OFF* */
1469 VLIB_CLI_COMMAND (sr_tunnel_command, static) = {
1470     .path = "sr tunnel",
1471     .short_help =
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,
1476 };
1477 /* *INDENT-ON* */
1478
1479 /**
1480  * @brief Display Segment Routing tunnel
1481  *
1482  * @param vm vlib_main_t *
1483  * @param t ip6_sr_tunnel_t *
1484  *
1485  */
1486 void
1487 ip6_sr_tunnel_display (vlib_main_t * vm, ip6_sr_tunnel_t * t)
1488 {
1489   ip6_sr_main_t *sm = &sr_main;
1490   ip6_fib_t *rx_fib, *tx_fib;
1491   ip6_sr_policy_t *pt;
1492
1493   rx_fib = ip6_fib_get (t->rx_fib_index);
1494   tx_fib = ip6_fib_get (t->tx_fib_index);
1495
1496   if (t->name)
1497     vlib_cli_output (vm, "sr tunnel name: %s", (char *) t->name);
1498
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 */ );
1507
1508   if (t->policy_index != ~0)
1509     {
1510       pt = pool_elt_at_index (sm->policies, t->policy_index);
1511       vlib_cli_output (vm, "sr policy: %s", (char *) pt->name);
1512     }
1513   vlib_cli_output (vm, "-------");
1514
1515   return;
1516 }
1517
1518 /**
1519  * @brief CLI Parser for Display Segment Routing tunnel
1520  *
1521  * @param vm vlib_main_t *
1522  * @param input unformat_input_t *
1523  * @param cmd vlib_cli_command_t *
1524  *
1525  * @return error clib_error_t *
1526  */
1527 static clib_error_t *
1528 show_sr_tunnel_fn (vlib_main_t * vm,
1529                    unformat_input_t * input, vlib_cli_command_t * cmd)
1530 {
1531   static ip6_sr_tunnel_t **tunnels;
1532   ip6_sr_tunnel_t *t;
1533   ip6_sr_main_t *sm = &sr_main;
1534   int i;
1535   uword *p = 0;
1536   u8 *name = 0;
1537
1538   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1539     {
1540       if (unformat (input, "name %s", &name))
1541         {
1542           p = hash_get_mem (sm->tunnel_index_by_name, name);
1543           if (!p)
1544             vlib_cli_output (vm, "No SR tunnel with name: %s. Showing all.",
1545                              name);
1546         }
1547       else
1548         break;
1549     }
1550
1551   vec_reset_length (tunnels);
1552
1553   if (!p)                       /* Either name parm not passed or no tunnel with that name found, show all */
1554     {
1555       /* *INDENT-OFF* */
1556   pool_foreach (t, sm->tunnels,
1557   ({
1558     vec_add1 (tunnels, t);
1559   }));
1560   /* *INDENT-ON* */
1561     }
1562   else                          /* Just show the one tunnel by name */
1563     vec_add1 (tunnels, &sm->tunnels[p[0]]);
1564
1565   if (vec_len (tunnels) == 0)
1566     vlib_cli_output (vm, "No SR tunnels configured");
1567
1568   for (i = 0; i < vec_len (tunnels); i++)
1569     {
1570       t = tunnels[i];
1571       ip6_sr_tunnel_display (vm, t);
1572     }
1573
1574   return 0;
1575 }
1576
1577 /* *INDENT-OFF* */
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,
1582 };
1583 /* *INDENT-ON* */
1584
1585 /**
1586  * @brief Add or Delete a Segment Routing policy
1587  *
1588  * @param a ip6_sr_add_del_policy_args_t *
1589  *
1590  * @return retval int
1591  */
1592 int
1593 ip6_sr_add_del_policy (ip6_sr_add_del_policy_args_t * a)
1594 {
1595   ip6_sr_main_t *sm = &sr_main;
1596   uword *p;
1597   ip6_sr_tunnel_t *t = 0;
1598   ip6_sr_policy_t *policy;
1599   u32 *tunnel_indices = 0;
1600   int i;
1601
1602
1603
1604   if (a->is_del)
1605     {
1606       p = hash_get_mem (sm->policy_index_by_policy_name, a->name);
1607       if (!p)
1608         return -6;              /* policy name not found */
1609
1610       policy = pool_elt_at_index (sm->policies, p[0]);
1611
1612       vec_foreach_index (i, policy->tunnel_indices)
1613       {
1614         t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[i]);
1615         t->policy_index = ~0;
1616       }
1617       hash_unset_mem (sm->policy_index_by_policy_name, a->name);
1618       pool_put (sm->policies, policy);
1619       return 0;
1620     }
1621
1622
1623   if (!vec_len (a->tunnel_names))
1624     return -3;                  /*tunnel name is required case */
1625
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++)
1629     {
1630       p = hash_get_mem (sm->tunnel_index_by_name, a->tunnel_names[i]);
1631       if (!p)
1632         return -4;              /* tunnel name not found case */
1633
1634       t = pool_elt_at_index (sm->tunnels, p[0]);
1635       /*
1636          No need to check t==0. -3 condition above ensures name
1637        */
1638       if (t->policy_index != ~0)
1639         return -5;              /* tunnel name already associated with a policy */
1640
1641       /* Add to tunnel indicies */
1642       vec_add1 (tunnel_indices, p[0]);
1643     }
1644
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);
1651
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.
1655    */
1656   for (i = 0; i < vec_len (policy->tunnel_indices); i++)
1657     {
1658       t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[i]);
1659       t->policy_index = policy - sm->policies;
1660     }
1661
1662   return 0;
1663 }
1664
1665 /**
1666  * @brief CLI Parser for Add or Delete a Segment Routing policy
1667  *
1668  * @param vm vlib_main_t *
1669  * @param input unformat_input_t *
1670  * @param cmd vlib_cli_command_t *
1671  *
1672  * @return error clib_error_t *
1673  */
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)
1678 {
1679   int is_del = 0;
1680   u8 **tunnel_names = 0;
1681   u8 *tunnel_name = 0;
1682   u8 *name = 0;
1683   ip6_sr_add_del_policy_args_t _a, *a = &_a;
1684   int rv;
1685
1686   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1687     {
1688       if (unformat (input, "del"))
1689         is_del = 1;
1690       else if (unformat (input, "name %s", &name))
1691         ;
1692       else if (unformat (input, "tunnel %s", &tunnel_name))
1693         {
1694           if (tunnel_name)
1695             {
1696               vec_add1 (tunnel_names, tunnel_name);
1697               tunnel_name = 0;
1698             }
1699         }
1700       else
1701         break;
1702     }
1703
1704   if (!name)
1705     return clib_error_return (0, "name of SR policy required");
1706
1707
1708   memset (a, 0, sizeof (*a));
1709
1710   a->is_del = is_del;
1711   a->name = name;
1712   a->tunnel_names = tunnel_names;
1713
1714   rv = ip6_sr_add_del_policy (a);
1715
1716   vec_free (tunnel_names);
1717
1718   switch (rv)
1719     {
1720     case 0:
1721       break;
1722
1723     case -3:
1724       return clib_error_return (0,
1725                                 "tunnel name to associate to SR policy is required");
1726
1727     case -4:
1728       return clib_error_return (0, "tunnel name not found");
1729
1730     case -5:
1731       return clib_error_return (0, "tunnel already associated with policy");
1732
1733     case -6:
1734       return clib_error_return (0, "policy name %s not found", name);
1735
1736     case -7:
1737       return clib_error_return (0, "TODO: deleting policy name %s", name);
1738
1739     default:
1740       return clib_error_return (0, "BUG: ip6_sr_add_del_policy returns %d",
1741                                 rv);
1742
1743     }
1744   return 0;
1745 }
1746
1747 /* *INDENT-OFF* */
1748 VLIB_CLI_COMMAND (sr_policy_command, static) = {
1749     .path = "sr policy",
1750     .short_help =
1751     "sr policy [del] name <policy-name> tunnel <sr-tunnel-name> [tunnel <sr-tunnel-name>]*",
1752     .function = sr_add_del_policy_command_fn,
1753 };
1754 /* *INDENT-ON* */
1755
1756 /**
1757  * @brief CLI Parser for Displaying Segment Routing policy
1758  *
1759  * @param vm vlib_main_t *
1760  * @param input unformat_input_t *
1761  * @param cmd vlib_cli_command_t *
1762  *
1763  * @return error clib_error_t *
1764  */
1765 static clib_error_t *
1766 show_sr_policy_fn (vlib_main_t * vm,
1767                    unformat_input_t * input, vlib_cli_command_t * cmd)
1768 {
1769   static ip6_sr_policy_t **policies;
1770   ip6_sr_policy_t *policy;
1771   ip6_sr_tunnel_t *t;
1772   ip6_sr_main_t *sm = &sr_main;
1773   int i, j;
1774   uword *p = 0;
1775   u8 *name = 0;
1776
1777   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1778     {
1779       if (unformat (input, "name %s", &name))
1780         {
1781           p = hash_get_mem (sm->policy_index_by_policy_name, name);
1782           if (!p)
1783             vlib_cli_output (vm,
1784                              "policy with name %s not found. Showing all.",
1785                              name);
1786         }
1787       else
1788         break;
1789     }
1790
1791   vec_reset_length (policies);
1792
1793   if (!p)                       /* Either name parm not passed or no policy with that name found, show all */
1794     {
1795       /* *INDENT-OFF* */
1796   pool_foreach (policy, sm->policies,
1797   ({
1798     vec_add1 (policies, policy);
1799   }));
1800   /* *INDENT-ON* */
1801     }
1802   else                          /* Just show the one policy by name and a summary of tunnel names */
1803     {
1804       policy = pool_elt_at_index (sm->policies, p[0]);
1805       vec_add1 (policies, policy);
1806     }
1807
1808   if (vec_len (policies) == 0)
1809     vlib_cli_output (vm, "No SR policies configured");
1810
1811   for (i = 0; i < vec_len (policies); i++)
1812     {
1813       policy = policies[i];
1814
1815       if (policy->name)
1816         vlib_cli_output (vm, "SR policy name: %s", (char *) policy->name);
1817       for (j = 0; j < vec_len (policy->tunnel_indices); j++)
1818         {
1819           t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[j]);
1820           ip6_sr_tunnel_display (vm, t);
1821         }
1822     }
1823
1824   return 0;
1825
1826 }
1827
1828 /* *INDENT-OFF* */
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,
1833 };
1834 /* *INDENT-ON* */
1835
1836 /**
1837  * @brief Add or Delete a mapping of IP6 multicast address
1838  * to Segment Routing policy.
1839  *
1840  * @param a ip6_sr_add_del_multicastmap_args_t *
1841  *
1842  * @return retval int
1843  */
1844 int
1845 ip6_sr_add_del_multicastmap (ip6_sr_add_del_multicastmap_args_t * a)
1846 {
1847   uword *p;
1848   ip6_sr_tunnel_t *t;
1849   ip6_sr_main_t *sm = &sr_main;
1850   ip6_sr_policy_t *pt;
1851   index_t rep;
1852   u32 ii;
1853
1854   if (a->is_del)
1855     {
1856       /* clean up the adjacency */
1857       p =
1858         hash_get_mem (sm->policy_index_by_multicast_address,
1859                       a->multicast_address);
1860     }
1861   else
1862     {
1863       /* Get our policy by policy_name */
1864       p = hash_get_mem (sm->policy_index_by_policy_name, a->policy_name);
1865
1866     }
1867   if (!p)
1868     return -1;
1869
1870   pt = pool_elt_at_index (sm->policies, p[0]);
1871
1872   /*
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
1877    */
1878
1879   t = pool_elt_at_index (sm->tunnels, pt->tunnel_indices[0]);
1880
1881   /*
1882    * Stick the tunnel index into the rewrite header.
1883    *
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.
1888    *
1889    * We don't handle ugly RFC-related cases yet, but I'm sure PL will complain
1890    * at some point...
1891    */
1892
1893   /*
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...
1898    */
1899   /* *INDENT-OFF* */
1900   mfib_prefix_t pfx = {
1901     .fp_proto = FIB_PROTOCOL_IP6,
1902     .fp_len = 128,
1903     .fp_grp_addr = {
1904       .ip6 = *a->multicast_address,
1905     }
1906   };
1907   /* *INDENT-ON* */
1908
1909   if (a->is_del)
1910     mfib_table_entry_delete (t->rx_fib_index, &pfx, MFIB_SOURCE_SRv6);
1911   else
1912     {
1913       /*
1914        * Construct a replicate DPO that will replicate received packets over
1915        * each tunnel in the policy
1916        */
1917       dpo_id_t dpo = DPO_INVALID;
1918
1919       rep = replicate_create (vec_len (pt->tunnel_indices), DPO_PROTO_IP6);
1920
1921       vec_foreach_index (ii, pt->tunnel_indices)
1922       {
1923         dpo_set (&dpo, sr_dpo_type, DPO_PROTO_IP6, pt->tunnel_indices[ii]);
1924
1925         replicate_set_bucket (rep, ii, &dpo);
1926       }
1927
1928       mfib_table_entry_special_add (t->rx_fib_index,
1929                                     &pfx,
1930                                     MFIB_SOURCE_SRv6,
1931                                     MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF, rep);
1932
1933       dpo_reset (&dpo);
1934     }
1935
1936   u8 *mcast_copy = 0;
1937   mcast_copy = vec_new (ip6_address_t, 1);
1938   memcpy (mcast_copy, a->multicast_address, sizeof (ip6_address_t));
1939
1940   if (a->is_del)
1941     {
1942       hash_unset_mem (sm->policy_index_by_multicast_address, mcast_copy);
1943       vec_free (mcast_copy);
1944     }
1945   else
1946     {
1947       hash_set_mem (sm->policy_index_by_multicast_address, mcast_copy,
1948                     pt - sm->policies);
1949     }
1950
1951   return 0;
1952 }
1953
1954 /**
1955  * @brief CLI Parser for Adding or Delete a mapping of IP6 multicast address
1956  * to Segment Routing policy.
1957  *
1958  * @param vm vlib_main_t *
1959  * @param input unformat_input_t *
1960  * @param cmd vlib_cli_command_t *
1961  *
1962  * @return error clib_error_t *
1963  */
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)
1968 {
1969   int is_del = 0;
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;
1974   int rv;
1975
1976   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1977     {
1978       if (unformat (input, "del"))
1979         is_del = 1;
1980       else
1981         if (unformat
1982             (input, "address %U", unformat_ip6_address, &multicast_address))
1983         multicast_address_set = 1;
1984       else if (unformat (input, "sr-policy %s", &policy_name))
1985         ;
1986       else
1987         break;
1988     }
1989
1990   if (!is_del && !policy_name)
1991     return clib_error_return (0, "name of sr policy required");
1992
1993   if (!multicast_address_set)
1994     return clib_error_return (0, "multicast address required");
1995
1996   memset (a, 0, sizeof (*a));
1997
1998   a->is_del = is_del;
1999   a->multicast_address = &multicast_address;
2000   a->policy_name = policy_name;
2001
2002   rv = ip6_sr_add_del_multicastmap (a);
2003
2004   switch (rv)
2005     {
2006     case 0:
2007       break;
2008     case -1:
2009       return clib_error_return (0, "no policy with name: %s", policy_name);
2010
2011     case -2:
2012       return clib_error_return (0, "multicast map someting ");
2013
2014     case -3:
2015       return clib_error_return (0,
2016                                 "tunnel name to associate to SR policy is required");
2017
2018     case -7:
2019       return clib_error_return (0, "TODO: deleting policy name %s",
2020                                 policy_name);
2021
2022     default:
2023       return clib_error_return (0, "BUG: ip6_sr_add_del_policy returns %d",
2024                                 rv);
2025
2026     }
2027   return 0;
2028
2029 }
2030
2031
2032 /* *INDENT-OFF* */
2033 VLIB_CLI_COMMAND (sr_multicast_map_command, static) = {
2034     .path = "sr multicast-map",
2035     .short_help =
2036     "sr multicast-map address <multicast-ip6-address> sr-policy <sr-policy-name> [del]",
2037     .function = sr_add_del_multicast_map_command_fn,
2038 };
2039 /* *INDENT-ON* */
2040
2041 /**
2042  * @brief CLI Parser for Displaying a mapping of IP6 multicast address
2043  * to Segment Routing policy.
2044  *
2045  * @param vm vlib_main_t *
2046  * @param input unformat_input_t *
2047  * @param cmd vlib_cli_command_t *
2048  *
2049  * @return error clib_error_t *
2050  */
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)
2054 {
2055   ip6_sr_main_t *sm = &sr_main;
2056   u8 *key = 0;
2057   u32 value;
2058   ip6_address_t multicast_address;
2059   ip6_sr_policy_t *pt;
2060
2061   /* pull all entries from the hash table into vector for display */
2062
2063   /* *INDENT-OFF* */
2064   hash_foreach_mem (key, value, sm->policy_index_by_multicast_address,
2065   ({
2066     if (!key)
2067         vlib_cli_output (vm, "no multicast maps configured");
2068     else
2069       {
2070         multicast_address = *((ip6_address_t *)key);
2071         pt = pool_elt_at_index (sm->policies, value);
2072         if (pt)
2073           {
2074             vlib_cli_output (vm, "address: %U policy: %s",
2075                              format_ip6_address, &multicast_address,
2076                              pt->name);
2077           }
2078         else
2079           vlib_cli_output (vm, "BUG: policy not found for address: %U with policy index %d",
2080                              format_ip6_address, &multicast_address,
2081                              value);
2082
2083       }
2084
2085   }));
2086   /* *INDENT-ON* */
2087
2088   return 0;
2089 }
2090
2091 /* *INDENT-OFF* */
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,
2096 };
2097 /* *INDENT-ON* */
2098
2099
2100 #define foreach_sr_fix_dst_addr_next            \
2101 _(DROP, "error-drop")
2102
2103 /**
2104  * @brief Struct for valid next-nodes for SR fix destination address node
2105  */
2106 typedef enum
2107 {
2108 #define _(s,n) SR_FIX_DST_ADDR_NEXT_##s,
2109   foreach_sr_fix_dst_addr_next
2110 #undef _
2111     SR_FIX_DST_ADDR_N_NEXT,
2112 } sr_fix_dst_addr_next_t;
2113
2114 /**
2115  * @brief Error strings for SR Fix Destination rewrite
2116  */
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
2121 };
2122
2123 /**
2124  * @brief Struct for errors for SR Fix Destination rewrite
2125  */
2126 typedef enum
2127 {
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
2131   SR_FIX_DST_N_ERROR,
2132 } sr_fix_dst_error_t;
2133
2134 /**
2135  * @brief Information for fix address trace
2136  */
2137 typedef struct
2138 {
2139   ip6_address_t src, dst;
2140   u32 next_index;
2141   u32 adj_index;
2142   u8 sr[256];
2143 } sr_fix_addr_trace_t;
2144
2145 /**
2146  * @brief Formatter for fix address trace
2147  */
2148 u8 *
2149 format_sr_fix_addr_trace (u8 * s, va_list * args)
2150 {
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 ();
2159
2160   if (t->adj_index != ~0)
2161     {
2162       adj = ip_get_adjacency (lm, t->adj_index);
2163       hi = vnet_get_sup_hw_interface (vnm, adj->rewrite_header.sw_if_index);
2164     }
2165
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)
2171     {
2172       s =
2173         format (s, "%U\n", format_ip6_sr_header, t->sr, 1 /* print_hmac */ );
2174       s =
2175         format (s, "   output via %s",
2176                 hi ? (char *) (hi->name) : "Invalid adj");
2177     }
2178   return s;
2179 }
2180
2181 /**
2182  * @brief Fix SR destination address - dual-loop
2183  *
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 *
2188  *
2189  * @return from_frame->n_vectors uword
2190  */
2191 static uword
2192 sr_fix_dst_addr (vlib_main_t * vm,
2193                  vlib_node_runtime_t * node, vlib_frame_t * from_frame)
2194 {
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;
2198
2199   from = vlib_frame_vector_args (from_frame);
2200   n_left_from = from_frame->n_vectors;
2201
2202   next_index = node->cached_next_index;
2203
2204   while (n_left_from > 0)
2205     {
2206       u32 n_left_to_next;
2207
2208       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
2209
2210 #if 0
2211       while (0 && n_left_from >= 4 && n_left_to_next >= 2)
2212         {
2213           u32 bi0, bi1;
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;
2217
2218           /* Prefetch next iteration. */
2219           {
2220             vlib_buffer_t *p2, *p3;
2221
2222             p2 = vlib_get_buffer (vm, from[2]);
2223             p3 = vlib_get_buffer (vm, from[3]);
2224
2225             vlib_prefetch_buffer_header (p2, LOAD);
2226             vlib_prefetch_buffer_header (p3, LOAD);
2227           }
2228
2229           bi0 = from[0];
2230           bi1 = from[1];
2231           to_next[0] = bi0;
2232           to_next[1] = bi1;
2233           from += 2;
2234           to_next += 2;
2235           n_left_to_next -= 2;
2236           n_left_from -= 2;
2237
2238           b0 = vlib_get_buffer (vm, bi0);
2239           b1 = vlib_get_buffer (vm, bi1);
2240
2241
2242           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
2243                                            to_next, n_left_to_next,
2244                                            bi0, bi1, next0, next1);
2245         }
2246 #endif
2247
2248       while (n_left_from > 0 && n_left_to_next > 0)
2249         {
2250           u32 bi0;
2251           vlib_buffer_t *b0;
2252           ip6_header_t *ip0;
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;
2258
2259           bi0 = from[0];
2260           to_next[0] = bi0;
2261           from += 1;
2262           to_next += 1;
2263           n_left_from -= 1;
2264           n_left_to_next -= 1;
2265
2266           b0 = vlib_get_buffer (vm, bi0);
2267
2268           adj0 =
2269             ip_get_adjacency (lm, vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
2270           next0 = adj0->if_address_index;
2271
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);
2276
2277           /* We'd better find an SR header... */
2278           if (PREDICT_FALSE (ip0->protocol != IPPROTO_IPV6_ROUTE))
2279             {
2280               b0->error = node->errors[SR_FIX_DST_ERROR_NO_SR_HEADER];
2281               goto do_trace0;
2282             }
2283           else
2284             {
2285               /*
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.
2289                */
2290
2291               /* Out of segments? Turf the packet */
2292               if (PREDICT_FALSE (sr0->segments_left == 0))
2293                 {
2294                   b0->error = node->errors[SR_FIX_DST_ERROR_NO_MORE_SEGMENTS];
2295                   goto do_trace0;
2296                 }
2297
2298               /*
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.
2303                */
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];
2307             }
2308
2309         do_trace0:
2310
2311           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
2312             {
2313               sr_fix_addr_trace_t *t = vlib_add_trace (vm, node,
2314                                                        b0, sizeof (*t));
2315               t->next_index = next0;
2316               t->adj_index = ~0;
2317
2318               if (next0 != SR_FIX_DST_ADDR_NEXT_DROP)
2319                 {
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));
2326                 }
2327             }
2328
2329           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2330                                            to_next, n_left_to_next,
2331                                            bi0, next0);
2332         }
2333
2334       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2335     }
2336   return from_frame->n_vectors;
2337 }
2338
2339
2340 /* *INDENT-OFF* */
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,
2348
2349   .runtime_data_bytes = 0,
2350
2351   .n_errors = SR_FIX_DST_N_ERROR,
2352   .error_strings = sr_fix_dst_error_strings,
2353
2354   .n_next_nodes = SR_FIX_DST_ADDR_N_NEXT,
2355   .next_nodes = {
2356 #define _(s,n) [SR_FIX_DST_ADDR_NEXT_##s] = n,
2357     foreach_sr_fix_dst_addr_next
2358 #undef _
2359   },
2360 };
2361
2362 VLIB_NODE_FUNCTION_MULTIARCH (sr_fix_dst_addr_node, sr_fix_dst_addr)
2363 /* *INDENT-ON* */
2364
2365 static clib_error_t *
2366 sr_init (vlib_main_t * vm)
2367 {
2368   ip6_sr_main_t *sm = &sr_main;
2369   clib_error_t *error = 0;
2370   vlib_node_t *ip6_lookup_node, *ip6_rewrite_node;
2371
2372   if ((error = vlib_call_init_function (vm, ip_main_init)))
2373     return error;
2374
2375   if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
2376     return error;
2377
2378   sm->vlib_main = vm;
2379   sm->vnet_main = vnet_get_main ();
2380
2381   vec_validate (sm->hmac_keys, 0);
2382   sm->hmac_keys[0].shared_secret = (u8 *) 0xdeadbeef;
2383
2384   sm->tunnel_index_by_key =
2385     hash_create_mem (0, sizeof (ip6_sr_tunnel_key_t), sizeof (uword));
2386
2387   sm->tunnel_index_by_name = hash_create_string (0, sizeof (uword));
2388
2389   sm->policy_index_by_policy_name = hash_create_string (0, sizeof (uword));
2390
2391   sm->policy_index_by_multicast_address =
2392     hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword));
2393
2394   sm->hmac_key_by_shared_secret = hash_create_string (0, sizeof (uword));
2395
2396   ip6_register_protocol (IPPROTO_IPV6_ROUTE, sr_local_node.index);
2397
2398   ip6_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip6-lookup");
2399   ASSERT (ip6_lookup_node);
2400
2401   ip6_rewrite_node = vlib_get_node_by_name (vm, (u8 *) "ip6-rewrite");
2402   ASSERT (ip6_rewrite_node);
2403
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);
2408
2409   OpenSSL_add_all_digests ();
2410
2411   sm->md = (void *) EVP_get_digestbyname ("sha1");
2412   sm->hmac_ctx = clib_mem_alloc (sizeof (HMAC_CTX));
2413
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);
2416
2417   return error;
2418 }
2419
2420 VLIB_INIT_FUNCTION (sr_init);
2421
2422 /**
2423  * @brief Definition of next-nodes for SR local
2424  */
2425 #define foreach_sr_local_next                   \
2426   _ (ERROR, "error-drop")                       \
2427   _ (IP6_LOOKUP, "ip6-lookup")
2428
2429 /**
2430  * @brief Struct for definition of next-nodes for SR local
2431  */
2432 typedef enum
2433 {
2434 #define _(s,n) SR_LOCAL_NEXT_##s,
2435   foreach_sr_local_next
2436 #undef _
2437     SR_LOCAL_N_NEXT,
2438 } sr_local_next_t;
2439
2440 /**
2441  * @brief Struct for packet trace of SR local
2442  */
2443 typedef struct
2444 {
2445   u8 next_index;
2446   u8 sr_valid;
2447   ip6_address_t src, dst;
2448   u16 length;
2449   u8 sr[256];
2450 } sr_local_trace_t;
2451
2452 /**
2453  * @brief Definition of SR local error-strings
2454  */
2455 static char *sr_local_error_strings[] = {
2456 #define sr_error(n,s) s,
2457 #include "sr_error.def"
2458 #undef sr_error
2459 };
2460
2461 /**
2462  * @brief Struct for definition of SR local error-strings
2463  */
2464 typedef enum
2465 {
2466 #define sr_error(n,s) SR_LOCAL_ERROR_##n,
2467 #include "sr_error.def"
2468 #undef sr_error
2469   SR_LOCAL_N_ERROR,
2470 } sr_local_error_t;
2471
2472 /**
2473  * @brief Format SR local trace
2474  *
2475  * @param s u8 *
2476  * @param args va_list *
2477  *
2478  * @return s u8 *
2479  */
2480 u8 *
2481 format_sr_local_trace (u8 * s, va_list * args)
2482 {
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 *);
2486
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);
2490   if (t->sr_valid)
2491     s =
2492       format (s, "\n  %U", format_ip6_sr_header, t->sr, 1 /* print_hmac */ );
2493   else
2494     s = format (s, "\n  popped SR header");
2495
2496   return s;
2497 }
2498
2499
2500 /* $$$$ fixme: smp, don't copy data, cache input, output (maybe) */
2501 /**
2502  * @brief Validate the SR HMAC
2503  *
2504  * @param sm ip6_sr_main_t *
2505  * @param ip ip6_header_t *
2506  * @param sr ip6_sr_header_t *
2507  *
2508  * @return retval int
2509  */
2510 static int
2511 sr_validate_hmac (ip6_sr_main_t * sm, ip6_header_t * ip, ip6_sr_header_t * sr)
2512 {
2513   u32 key_index;
2514   static u8 *keybuf;
2515   u8 *copy_target;
2516   int first_segment;
2517   ip6_address_t *addrp;
2518   int i;
2519   ip6_sr_hmac_key_t *hmac_key;
2520   static u8 *signature;
2521   u32 sig_len;
2522
2523   key_index = sr->hmac_key;
2524
2525   /* No signature? Pass... */
2526   if (key_index == 0)
2527     return 0;
2528
2529   /* We don't know about this key? Fail... */
2530   if (key_index >= vec_len (sm->hmac_keys))
2531     return 1;
2532
2533   vec_validate (signature, SHA256_DIGEST_LENGTH - 1);
2534
2535   hmac_key = sm->hmac_keys + key_index;
2536
2537   vec_reset_length (keybuf);
2538
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));
2542
2543   /* last segment */
2544   vec_add2 (keybuf, copy_target, 1);
2545   copy_target[0] = sr->first_segment;
2546
2547   /* octet w/ bit 0 = "clean" flag */
2548   vec_add2 (keybuf, copy_target, 1);
2549   copy_target[0]
2550     = (sr->flags & clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))
2551     ? 0x80 : 0;
2552
2553   /* hmac key id */
2554   vec_add2 (keybuf, copy_target, 1);
2555   copy_target[0] = sr->hmac_key;
2556
2557   first_segment = sr->first_segment;
2558
2559   addrp = sr->segments;
2560
2561   /* segments */
2562   for (i = 0; i <= first_segment; i++)
2563     {
2564       vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
2565       clib_memcpy (copy_target, addrp->as_u8, sizeof (ip6_address_t));
2566       addrp++;
2567     }
2568
2569   if (sm->is_debug)
2570     clib_warning ("verify key index %d keybuf: %U", key_index,
2571                   format_hex_bytes, keybuf, vec_len (keybuf));
2572
2573   /* shared secret */
2574
2575   /* SHA1 is shorter than SHA-256 */
2576   memset (signature, 0, vec_len (signature));
2577
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);
2587
2588   if (sm->is_debug)
2589     clib_warning ("computed signature len %d, value %U", sig_len,
2590                   format_hex_bytes, signature, vec_len (signature));
2591
2592   /* Point at the SHA signature in the packet */
2593   addrp++;
2594   if (sm->is_debug)
2595     clib_warning ("read signature %U", format_hex_bytes, addrp,
2596                   SHA256_DIGEST_LENGTH);
2597
2598   return memcmp (signature, addrp, SHA256_DIGEST_LENGTH);
2599 }
2600
2601 /**
2602  * @brief SR local node
2603  * @node sr-local
2604  *
2605  * @param vm vlib_main_t *
2606  * @param node vlib_node_runtime_t *
2607  * @param from_frame vlib_frame_t *
2608  *
2609  * @return from_frame->n_vectors uword
2610  */
2611 static uword
2612 sr_local (vlib_main_t * vm,
2613           vlib_node_runtime_t * node, vlib_frame_t * from_frame)
2614 {
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;
2620
2621   from = vlib_frame_vector_args (from_frame);
2622   n_left_from = from_frame->n_vectors;
2623
2624   next_index = node->cached_next_index;
2625
2626   while (n_left_from > 0)
2627     {
2628       u32 n_left_to_next;
2629
2630       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
2631
2632       while (n_left_from >= 4 && n_left_to_next >= 2)
2633         {
2634           u32 bi0, bi1;
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;
2641
2642           /* Prefetch next iteration. */
2643           {
2644             vlib_buffer_t *p2, *p3;
2645
2646             p2 = vlib_get_buffer (vm, from[2]);
2647             p3 = vlib_get_buffer (vm, from[3]);
2648
2649             vlib_prefetch_buffer_header (p2, LOAD);
2650             vlib_prefetch_buffer_header (p3, LOAD);
2651
2652             CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
2653             CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
2654           }
2655
2656           bi0 = from[0];
2657           bi1 = from[1];
2658           to_next[0] = bi0;
2659           to_next[1] = bi1;
2660           from += 2;
2661           to_next += 2;
2662           n_left_to_next -= 2;
2663           n_left_from -= 2;
2664
2665
2666           b0 = vlib_get_buffer (vm, bi0);
2667           ip0 = vlib_buffer_get_current (b0);
2668           sr0 = (ip6_sr_header_t *) (ip0 + 1);
2669           if (PREDICT_FALSE
2670               (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2671             {
2672               ip6_hop_by_hop_ext_t *ext_hdr =
2673                 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
2674               sr0 =
2675                 (ip6_sr_header_t *) ip6_ext_next_header ((ip6_ext_header_t *)
2676                                                          ext_hdr);
2677             }
2678
2679           if (PREDICT_FALSE (sr0->type != ROUTING_HEADER_TYPE_SR))
2680             {
2681               next0 = SR_LOCAL_NEXT_ERROR;
2682               b0->error =
2683                 node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE];
2684               goto do_trace0;
2685             }
2686
2687           /* Out of segments? Turf the packet */
2688           if (PREDICT_FALSE (sr0->segments_left == 0))
2689             {
2690               next0 = SR_LOCAL_NEXT_ERROR;
2691               b0->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS];
2692               goto do_trace0;
2693             }
2694
2695           if (PREDICT_FALSE (sm->validate_hmac))
2696             {
2697               if (sr_validate_hmac (sm, ip0, sr0))
2698                 {
2699                   next0 = SR_LOCAL_NEXT_ERROR;
2700                   b0->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID];
2701                   goto do_trace0;
2702                 }
2703             }
2704
2705           next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) : next0;
2706
2707           /*
2708            * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx
2709            */
2710           if (PREDICT_FALSE (next0 & 0x80000000))
2711             {
2712               next0 ^= 0xFFFFFFFF;
2713               if (PREDICT_FALSE (next0 == SR_LOCAL_NEXT_ERROR))
2714                 b0->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK];
2715             }
2716           else
2717             {
2718               u32 segment_index0;
2719
2720               segment_index0 = sr0->segments_left - 1;
2721
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];
2726
2727               if (PREDICT_TRUE (sr0->segments_left > 0))
2728                 sr0->segments_left -= 1;
2729             }
2730
2731           /* End of the path. Clean up the SR header, or not */
2732           if (PREDICT_FALSE
2733               (sr0->segments_left == 0 &&
2734                (sr0->flags &
2735                 clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))))
2736             {
2737               u64 *copy_dst0, *copy_src0;
2738               u16 new_l0;
2739               u32 copy_len_u64s0 = 0;
2740               int i;
2741
2742               /*
2743                * Copy the ip6 header right by the (real) length of the
2744                * sr header.
2745                */
2746               if (PREDICT_FALSE
2747                   (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2748                 {
2749                   ip6_hop_by_hop_ext_t *ext_hdr =
2750                     (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
2751                   copy_len_u64s0 =
2752                     (((ip6_ext_header_t *) ext_hdr)->n_data_u64s) + 1;
2753                   ext_hdr->next_hdr = sr0->protocol;
2754                 }
2755               else
2756                 {
2757                   ip0->protocol = sr0->protocol;
2758                 }
2759               vlib_buffer_advance (b0, (sr0->length + 1) * 8);
2760
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);
2764
2765               copy_src0 = (u64 *) ip0;
2766               copy_dst0 = copy_src0 + (sr0->length + 1);
2767
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];
2773
2774               for (i = copy_len_u64s0 - 1; i >= 0; i--)
2775                 {
2776                   copy_dst0[i] = copy_src0[i];
2777                 }
2778
2779               sr0 = 0;
2780             }
2781
2782         do_trace0:
2783           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
2784             {
2785               sr_local_trace_t *tr = vlib_add_trace (vm, node,
2786                                                      b0, sizeof (*tr));
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;
2794               if (tr->sr_valid)
2795                 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
2796             }
2797
2798           b1 = vlib_get_buffer (vm, bi1);
2799           ip1 = vlib_buffer_get_current (b1);
2800           sr1 = (ip6_sr_header_t *) (ip1 + 1);
2801           if (PREDICT_FALSE
2802               (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2803             {
2804
2805               ip6_hop_by_hop_ext_t *ext_hdr =
2806                 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1);
2807               sr1 =
2808                 (ip6_sr_header_t *) ip6_ext_next_header ((ip6_ext_header_t *)
2809                                                          ext_hdr);
2810             }
2811
2812           if (PREDICT_FALSE (sr1->type != ROUTING_HEADER_TYPE_SR))
2813             {
2814               next1 = SR_LOCAL_NEXT_ERROR;
2815               b1->error =
2816                 node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE];
2817               goto do_trace1;
2818             }
2819
2820           /* Out of segments? Turf the packet */
2821           if (PREDICT_FALSE (sr1->segments_left == 0))
2822             {
2823               next1 = SR_LOCAL_NEXT_ERROR;
2824               b1->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS];
2825               goto do_trace1;
2826             }
2827
2828           if (PREDICT_FALSE (sm->validate_hmac))
2829             {
2830               if (sr_validate_hmac (sm, ip1, sr1))
2831                 {
2832                   next1 = SR_LOCAL_NEXT_ERROR;
2833                   b1->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID];
2834                   goto do_trace1;
2835                 }
2836             }
2837
2838           next1 = sr_local_cb ? sr_local_cb (vm, node, b1, ip1, sr1) : next1;
2839
2840           /*
2841            * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx
2842            */
2843           if (PREDICT_FALSE (next1 & 0x80000000))
2844             {
2845               next1 ^= 0xFFFFFFFF;
2846               if (PREDICT_FALSE (next1 == SR_LOCAL_NEXT_ERROR))
2847                 b1->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK];
2848             }
2849           else
2850             {
2851               u32 segment_index1;
2852
2853               segment_index1 = sr1->segments_left - 1;
2854
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];
2859
2860               if (PREDICT_TRUE (sr1->segments_left > 0))
2861                 sr1->segments_left -= 1;
2862             }
2863
2864           /* End of the path. Clean up the SR header, or not */
2865           if (PREDICT_FALSE
2866               (sr1->segments_left == 0 &&
2867                (sr1->flags &
2868                 clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))))
2869             {
2870               u64 *copy_dst1, *copy_src1;
2871               u16 new_l1;
2872               u32 copy_len_u64s1 = 0;
2873               int i;
2874
2875               /*
2876                * Copy the ip6 header right by the (real) length of the
2877                * sr header.
2878                */
2879               if (PREDICT_FALSE
2880                   (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2881                 {
2882                   ip6_hop_by_hop_ext_t *ext_hdr =
2883                     (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1);
2884                   copy_len_u64s1 =
2885                     (((ip6_ext_header_t *) ext_hdr)->n_data_u64s) + 1;
2886                   ext_hdr->next_hdr = sr1->protocol;
2887                 }
2888               else
2889                 {
2890                   ip1->protocol = sr1->protocol;
2891                 }
2892               vlib_buffer_advance (b1, (sr1->length + 1) * 8);
2893
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);
2897
2898               copy_src1 = (u64 *) ip1;
2899               copy_dst1 = copy_src1 + (sr1->length + 1);
2900
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];
2906
2907               for (i = copy_len_u64s1 - 1; i >= 0; i--)
2908                 {
2909                   copy_dst1[i] = copy_src1[i];
2910                 }
2911
2912               sr1 = 0;
2913             }
2914
2915         do_trace1:
2916           if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
2917             {
2918               sr_local_trace_t *tr = vlib_add_trace (vm, node,
2919                                                      b1, sizeof (*tr));
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;
2927               if (tr->sr_valid)
2928                 clib_memcpy (tr->sr, sr1, sizeof (tr->sr));
2929             }
2930
2931           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
2932                                            to_next, n_left_to_next,
2933                                            bi0, bi1, next0, next1);
2934         }
2935
2936       while (n_left_from > 0 && n_left_to_next > 0)
2937         {
2938           u32 bi0;
2939           vlib_buffer_t *b0;
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;
2944
2945           bi0 = from[0];
2946           to_next[0] = bi0;
2947           from += 1;
2948           to_next += 1;
2949           n_left_from -= 1;
2950           n_left_to_next -= 1;
2951
2952           b0 = vlib_get_buffer (vm, bi0);
2953           ip0 = vlib_buffer_get_current (b0);
2954           sr0 = (ip6_sr_header_t *) (ip0 + 1);
2955
2956           if (PREDICT_FALSE
2957               (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2958             {
2959               ip6_hop_by_hop_ext_t *ext_hdr =
2960                 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
2961               sr0 =
2962                 (ip6_sr_header_t *) ip6_ext_next_header ((ip6_ext_header_t *)
2963                                                          ext_hdr);
2964             }
2965           if (PREDICT_FALSE (sr0->type != ROUTING_HEADER_TYPE_SR))
2966             {
2967               next0 = SR_LOCAL_NEXT_ERROR;
2968               b0->error =
2969                 node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE];
2970               goto do_trace;
2971             }
2972
2973           /* Out of segments? Turf the packet */
2974           if (PREDICT_FALSE (sr0->segments_left == 0))
2975             {
2976               next0 = SR_LOCAL_NEXT_ERROR;
2977               b0->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS];
2978               goto do_trace;
2979             }
2980
2981           if (PREDICT_FALSE (sm->validate_hmac))
2982             {
2983               if (sr_validate_hmac (sm, ip0, sr0))
2984                 {
2985                   next0 = SR_LOCAL_NEXT_ERROR;
2986                   b0->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID];
2987                   goto do_trace;
2988                 }
2989             }
2990
2991           next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) : next0;
2992
2993           /*
2994            * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx
2995            */
2996           if (PREDICT_FALSE (next0 & 0x80000000))
2997             {
2998               next0 ^= 0xFFFFFFFF;
2999               if (PREDICT_FALSE (next0 == SR_LOCAL_NEXT_ERROR))
3000                 b0->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK];
3001             }
3002           else
3003             {
3004               u32 segment_index0;
3005
3006               segment_index0 = sr0->segments_left - 1;
3007
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];
3012
3013               if (PREDICT_TRUE (sr0->segments_left > 0))
3014                 sr0->segments_left -= 1;
3015             }
3016
3017           /* End of the path. Clean up the SR header, or not */
3018           if (PREDICT_FALSE
3019               (sr0->segments_left == 0 &&
3020                (sr0->flags &
3021                 clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))))
3022             {
3023               u64 *copy_dst0, *copy_src0;
3024               u16 new_l0;
3025               u32 copy_len_u64s0 = 0;
3026               int i;
3027
3028               /*
3029                * Copy the ip6 header right by the (real) length of the
3030                * sr header.
3031                */
3032               if (PREDICT_FALSE
3033                   (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
3034                 {
3035                   ip6_hop_by_hop_ext_t *ext_hdr =
3036                     (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
3037                   copy_len_u64s0 =
3038                     (((ip6_ext_header_t *) ext_hdr)->n_data_u64s) + 1;
3039                   ext_hdr->next_hdr = sr0->protocol;
3040                 }
3041               else
3042                 {
3043                   ip0->protocol = sr0->protocol;
3044                 }
3045
3046               vlib_buffer_advance (b0, (sr0->length + 1) * 8);
3047
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);
3051
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];
3059
3060               for (i = copy_len_u64s0 - 1; i >= 0; i--)
3061                 {
3062                   copy_dst0[i] = copy_src0[i];
3063                 }
3064
3065               sr0 = 0;
3066             }
3067
3068         do_trace:
3069           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
3070             {
3071               sr_local_trace_t *tr = vlib_add_trace (vm, node,
3072                                                      b0, sizeof (*tr));
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;
3080               if (tr->sr_valid)
3081                 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
3082             }
3083
3084           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
3085                                            to_next, n_left_to_next,
3086                                            bi0, next0);
3087         }
3088
3089       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
3090     }
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;
3095 }
3096
3097 /* *INDENT-OFF* */
3098 VLIB_REGISTER_NODE (sr_local_node, static) = {
3099   .function = sr_local,
3100   .name = "sr-local",
3101   /* Takes a vector of packets. */
3102   .vector_size = sizeof (u32),
3103   .format_trace = format_sr_local_trace,
3104
3105   .runtime_data_bytes = 0,
3106
3107   .n_errors = SR_LOCAL_N_ERROR,
3108   .error_strings = sr_local_error_strings,
3109
3110   .n_next_nodes = SR_LOCAL_N_NEXT,
3111   .next_nodes = {
3112 #define _(s,n) [SR_LOCAL_NEXT_##s] = n,
3113     foreach_sr_local_next
3114 #undef _
3115   },
3116 };
3117
3118 VLIB_NODE_FUNCTION_MULTIARCH (sr_local_node, sr_local)
3119 /* *INDENT-ON* */
3120
3121 ip6_sr_main_t *
3122 sr_get_main (vlib_main_t * vm)
3123 {
3124   vlib_call_init_function (vm, sr_init);
3125   ASSERT (sr_local_node.index);
3126   return &sr_main;
3127 }
3128
3129 /**
3130  * @brief CLI parser for SR fix destination rewrite node
3131  *
3132  * @param vm vlib_main_t *
3133  * @param input unformat_input_t *
3134  * @param cmd vlib_cli_command_t *
3135  *
3136  * @return error clib_error_t *
3137  */
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)
3141 {
3142   fib_prefix_t pfx = {
3143     .fp_proto = FIB_PROTOCOL_IP6,
3144     .fp_len = 128,
3145   };
3146   u32 fib_index = 0;
3147   u32 fib_id = 0;
3148   u32 adj_index;
3149   ip_adjacency_t *adj;
3150   vnet_hw_interface_t *hi;
3151   u32 sw_if_index;
3152   ip6_sr_main_t *sm = &sr_main;
3153   vnet_main_t *vnm = vnet_get_main ();
3154   fib_node_index_t fei;
3155
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);
3159
3160   if (unformat (input, "rx-table-id %d", &fib_id))
3161     {
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);
3165     }
3166
3167   fei = fib_table_lookup_exact_match (fib_index, &pfx);
3168
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);
3172
3173   adj_index = fib_entry_get_adj_for_source (fei, FIB_SOURCE_SR);
3174
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);
3178
3179   adj = adj_get (adj_index);
3180
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);
3184
3185   adj->rewrite_header.next_index = sm->ip6_rewrite_sr_next_index;
3186
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;
3190
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);
3195
3196   return 0;
3197 }
3198
3199 /* *INDENT-OFF* */
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,
3204 };
3205 /* *INDENT-ON* */
3206
3207 /**
3208  * @brief Register a callback routine to set next0 in sr_local
3209  *
3210  * @param cb void *
3211  */
3212 void
3213 vnet_register_sr_app_callback (void *cb)
3214 {
3215   ip6_sr_main_t *sm = &sr_main;
3216
3217   sm->sr_local_cb = cb;
3218 }
3219
3220 /**
3221  * @brief Test routine for validation of HMAC
3222  */
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)
3226 {
3227   ip6_sr_main_t *sm = &sr_main;
3228
3229   if (unformat (input, "validate on"))
3230     sm->validate_hmac = 1;
3231   else if (unformat (input, "chunk-offset off"))
3232     sm->validate_hmac = 0;
3233   else
3234     return clib_error_return (0, "expected validate on|off in '%U'",
3235                               format_unformat_error, input);
3236
3237   vlib_cli_output (vm, "hmac signature validation %s",
3238                    sm->validate_hmac ? "on" : "off");
3239   return 0;
3240 }
3241
3242 /* *INDENT-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,
3247 };
3248 /* *INDENT-ON* */
3249
3250 /**
3251  * @brief Add or Delete HMAC key
3252  *
3253  * @param sm ip6_sr_main_t *
3254  * @param key_id u32
3255  * @param shared_secret u8 *
3256  * @param is_del u8
3257  *
3258  * @return retval i32
3259  */
3260 // $$$ fixme shouldn't return i32
3261 i32
3262 sr_hmac_add_del_key (ip6_sr_main_t * sm, u32 key_id, u8 * shared_secret,
3263                      u8 is_del)
3264 {
3265   u32 index;
3266   ip6_sr_hmac_key_t *key;
3267
3268   if (is_del == 0)
3269     {
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)
3273         return -2;
3274
3275       index = key_id;
3276       key = find_or_add_shared_secret (sm, shared_secret, &index);
3277       ASSERT (index == key_id);
3278       return 0;
3279     }
3280
3281   /* delete */
3282
3283   if (key_id)                   /* delete by key ID */
3284     {
3285       if (vec_len (sm->hmac_keys) <= key_id)
3286         return -3;
3287
3288       key = sm->hmac_keys + key_id;
3289
3290       hash_unset_mem (sm->hmac_key_by_shared_secret, key->shared_secret);
3291       vec_free (key->shared_secret);
3292       return 0;
3293     }
3294
3295   index = 0;
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);
3299   return 0;
3300 }
3301
3302
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)
3306 {
3307   ip6_sr_main_t *sm = &sr_main;
3308   u8 is_del = 0;
3309   u32 key_id = 0;
3310   u8 key_id_set = 0;
3311   u8 *shared_secret = 0;
3312   i32 rv;
3313
3314   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3315     {
3316       if (unformat (input, "del"))
3317         is_del = 1;
3318       else if (unformat (input, "id %d", &key_id))
3319         key_id_set = 1;
3320       else if (unformat (input, "key %s", &shared_secret))
3321         {
3322           /* Do not include the trailing NULL byte. Guaranteed interop issue */
3323           _vec_len (shared_secret) -= 1;
3324         }
3325       else
3326         break;
3327     }
3328
3329   if (is_del == 0 && shared_secret == 0)
3330     return clib_error_return (0, "shared secret must be set to add a key");
3331
3332   if (shared_secret == 0 && key_id_set == 0)
3333     return clib_error_return (0, "shared secret and key id both unset");
3334
3335   rv = sr_hmac_add_del_key (sm, key_id, shared_secret, is_del);
3336
3337   vec_free (shared_secret);
3338
3339   switch (rv)
3340     {
3341     case 0:
3342       break;
3343
3344     default:
3345       return clib_error_return (0, "sr_hmac_add_del_key returned %d", rv);
3346     }
3347
3348   return 0;
3349 }
3350
3351 /* *INDENT-OFF* */
3352 VLIB_CLI_COMMAND (sr_hmac, static) = {
3353     .path = "sr hmac",
3354     .short_help = "sr hmac [del] id <nn> key <str>",
3355     .function = sr_hmac_add_del_key_fn,
3356 };
3357 /* *INDENT-ON* */
3358
3359 /**
3360  * @brief CLI parser for show HMAC key shared secrets
3361  *
3362  * @param vm vlib_main_t *
3363  * @param input unformat_input_t *
3364  * @param cmd vlib_cli_command_t *
3365  *
3366  * @return error clib_error_t *
3367  */
3368 static clib_error_t *
3369 show_sr_hmac_fn (vlib_main_t * vm,
3370                  unformat_input_t * input, vlib_cli_command_t * cmd)
3371 {
3372   ip6_sr_main_t *sm = &sr_main;
3373   int i;
3374
3375   for (i = 1; i < vec_len (sm->hmac_keys); i++)
3376     {
3377       if (sm->hmac_keys[i].shared_secret)
3378         vlib_cli_output (vm, "[%d]: %v", i, sm->hmac_keys[i].shared_secret);
3379     }
3380
3381   return 0;
3382 }
3383
3384 /* *INDENT-OFF* */
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,
3389 };
3390 /* *INDENT-ON* */
3391
3392 /**
3393  * @brief Test for SR debug flag
3394  *
3395  * @param vm vlib_main_t *
3396  * @param input unformat_input_t *
3397  * @param cmd vlib_cli_command_t *
3398  *
3399  * @return error clib_error_t *
3400  */
3401 static clib_error_t *
3402 test_sr_debug_fn (vlib_main_t * vm,
3403                   unformat_input_t * input, vlib_cli_command_t * cmd)
3404 {
3405   ip6_sr_main_t *sm = &sr_main;
3406
3407   if (unformat (input, "on"))
3408     sm->is_debug = 1;
3409   else if (unformat (input, "off"))
3410     sm->is_debug = 0;
3411   else
3412     return clib_error_return (0, "expected on|off in '%U'",
3413                               format_unformat_error, input);
3414
3415   vlib_cli_output (vm, "debug trace now %s", sm->is_debug ? "on" : "off");
3416
3417   return 0;
3418 }
3419
3420 /* *INDENT-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,
3425 };
3426 /* *INDENT-ON* */
3427
3428 /*
3429  * fd.io coding-style-patch-verification: ON
3430  *
3431  * Local Variables:
3432  * eval: (c-set-style "gnu")
3433  * End:
3434  */