5d0275d992a991621d98a850e37a5d0fc9d5091b
[vpp.git] / vnet / 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/dpo/dpo.h>
27
28 #include <openssl/hmac.h>
29
30 ip6_sr_main_t sr_main;
31 static vlib_node_registration_t sr_local_node;
32
33 /**
34  * @brief Dynamically added SR DPO type
35  */
36 static dpo_type_t sr_dpo_type;
37
38 /**
39  * @brief Use passed HMAC key in ip6_sr_header_t in OpenSSL HMAC routines
40  *
41  * @param sm ip6_sr_main_t *
42  * @param ip ip6_header_t *
43  * @param sr ip6_sr_header_t *
44  */
45 void
46 sr_fix_hmac (ip6_sr_main_t * sm, ip6_header_t * ip, ip6_sr_header_t * sr)
47 {
48   u32 key_index;
49   static u8 *keybuf;
50   u8 *copy_target;
51   int first_segment;
52   ip6_address_t *addrp;
53   int i;
54   ip6_sr_hmac_key_t *hmac_key;
55   u32 sig_len;
56
57   key_index = sr->hmac_key;
58
59   /* No signature? Pass... */
60   if (key_index == 0)
61     return;
62
63   /* We don't know about this key? Fail... */
64   if (key_index >= vec_len (sm->hmac_keys))
65     return;
66
67   hmac_key = sm->hmac_keys + key_index;
68
69   vec_reset_length (keybuf);
70
71   /* pkt ip6 src address */
72   vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
73   clib_memcpy (copy_target, ip->src_address.as_u8, sizeof (ip6_address_t));
74
75   /* first segment */
76   vec_add2 (keybuf, copy_target, 1);
77   copy_target[0] = sr->first_segment;
78
79   /* octet w/ bit 0 = "clean" flag */
80   vec_add2 (keybuf, copy_target, 1);
81   copy_target[0]
82     = (sr->flags & clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))
83     ? 0x80 : 0;
84
85   /* hmac key id */
86   vec_add2 (keybuf, copy_target, 1);
87   copy_target[0] = sr->hmac_key;
88
89   first_segment = sr->first_segment;
90
91   addrp = sr->segments;
92
93   /* segments */
94   for (i = 0; i <= first_segment; i++)
95     {
96       vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
97       clib_memcpy (copy_target, addrp->as_u8, sizeof (ip6_address_t));
98       addrp++;
99     }
100
101   addrp++;
102
103   HMAC_CTX_init (sm->hmac_ctx);
104   if (!HMAC_Init (sm->hmac_ctx, hmac_key->shared_secret,
105                   vec_len (hmac_key->shared_secret), sm->md))
106     clib_warning ("barf1");
107   if (!HMAC_Update (sm->hmac_ctx, keybuf, vec_len (keybuf)))
108     clib_warning ("barf2");
109   if (!HMAC_Final (sm->hmac_ctx, (unsigned char *) addrp, &sig_len))
110     clib_warning ("barf3");
111   HMAC_CTX_cleanup (sm->hmac_ctx);
112 }
113
114 /**
115  * @brief Format function for decoding various SR flags
116  *
117  * @param s u8 * - formatted string
118  * @param args va_list * - u16 flags
119  *
120  * @return formatted output string u8 *
121  */
122 u8 *
123 format_ip6_sr_header_flags (u8 * s, va_list * args)
124 {
125   u16 flags = (u16) va_arg (*args, int);
126   u8 pl_flag;
127   int bswap_needed = va_arg (*args, int);
128   int i;
129
130   if (bswap_needed)
131     flags = clib_host_to_net_u16 (flags);
132
133   if (flags & IP6_SR_HEADER_FLAG_CLEANUP)
134     s = format (s, "cleanup ");
135
136   if (flags & IP6_SR_HEADER_FLAG_PROTECTED)
137     s = format (s, "reroute ");
138
139   s = format (s, "pl: ");
140   for (i = 1; i <= 4; i++)
141     {
142       pl_flag = ip6_sr_policy_list_flags (flags, i);
143       s = format (s, "[%d] ", i);
144
145       switch (pl_flag)
146         {
147         case IP6_SR_HEADER_FLAG_PL_ELT_NOT_PRESENT:
148           s = format (s, "NotPr ");
149           break;
150         case IP6_SR_HEADER_FLAG_PL_ELT_INGRESS_PE:
151           s = format (s, "InPE ");
152           break;
153         case IP6_SR_HEADER_FLAG_PL_ELT_EGRESS_PE:
154           s = format (s, "EgPE ");
155           break;
156
157         case IP6_SR_HEADER_FLAG_PL_ELT_ORIG_SRC_ADDR:
158           s = format (s, "OrgSrc ");
159           break;
160         }
161     }
162   return s;
163 }
164
165 /**
166  * @brief Format function for decoding ip6_sr_header_t
167  *
168  * @param s u8 * - formatted string
169  * @param args va_list * - ip6_sr_header_t
170  *
171  * @return formatted output string u8 *
172  */
173 u8 *
174 format_ip6_sr_header (u8 * s, va_list * args)
175 {
176   ip6_sr_header_t *h = va_arg (*args, ip6_sr_header_t *);
177   ip6_address_t placeholder_addr =
178     { {254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
179        254, 254}
180   };
181   int print_hmac = va_arg (*args, int);
182   int i, pl_index, max_segs;
183   int flags_host_byte_order = clib_net_to_host_u16 (h->flags);
184
185   s = format (s, "next proto %d, len %d, type %d",
186               h->protocol, (h->length << 3) + 8, h->type);
187   s = format (s, "\n      segs left %d, first_segment %d, hmac key %d",
188               h->segments_left, h->first_segment, h->hmac_key);
189   s = format (s, "\n      flags %U", format_ip6_sr_header_flags,
190               flags_host_byte_order, 0 /* bswap needed */ );
191
192   /*
193    * Header length is in 8-byte units (minus one), so
194    * divide by 2 to ascertain the number of ip6 addresses in the
195    * segment list
196    */
197   max_segs = (h->length >> 1);
198
199   if (!print_hmac && h->hmac_key)
200     max_segs -= 2;
201
202   s = format (s, "\n  Segments (in processing order):");
203
204   for (i = h->first_segment; i >= 1; i--)
205     s = format (s, "\n  %U", format_ip6_address, h->segments + i);
206   if (ip6_address_is_equal (&placeholder_addr, h->segments))
207     s = format (s, "\n  (empty placeholder)");
208   else
209     s = format (s, "\n  %U", format_ip6_address, h->segments);
210
211   s = format (s, "\n  Policy List:");
212
213   pl_index = 1;                 /* to match the RFC text */
214   for (i = (h->first_segment + 1); i < max_segs; i++, pl_index++)
215     {
216       char *tag;
217       char *tags[] = { " ", "InPE: ", "EgPE: ", "OrgSrc: " };
218
219       tag = tags[0];
220       if (pl_index >= 1 && pl_index <= 4)
221         {
222           int this_pl_flag = ip6_sr_policy_list_flags
223             (flags_host_byte_order, pl_index);
224           tag = tags[this_pl_flag];
225         }
226
227       s = format (s, "\n  %s%U", tag, format_ip6_address, h->segments + i);
228     }
229
230   return s;
231 }
232
233 /**
234  * @brief Format function for decoding ip6_sr_header_t with length
235  *
236  * @param s u8 * - formatted string
237  * @param args va_list * - ip6_header_t + ip6_sr_header_t
238  *
239  * @return formatted output string u8 *
240  */
241 u8 *
242 format_ip6_sr_header_with_length (u8 * s, va_list * args)
243 {
244   ip6_header_t *h = va_arg (*args, ip6_header_t *);
245   u32 max_header_bytes = va_arg (*args, u32);
246   uword header_bytes;
247
248   header_bytes = sizeof (h[0]) + sizeof (ip6_sr_header_t);
249   if (max_header_bytes != 0 && header_bytes > max_header_bytes)
250     return format (s, "ip6_sr header truncated");
251
252   s = format (s, "IP6: %U\n", format_ip6_header, h, max_header_bytes);
253   s =
254     format (s, "SR: %U\n", format_ip6_sr_header, (ip6_sr_header_t *) (h + 1),
255             0 /* print_hmac */ , max_header_bytes);
256   return s;
257 }
258
259 /**
260  * @brief Defined valid next nodes
261  * @note Cannot call replicate yet without DPDK
262 */
263 #if DPDK > 0
264 #define foreach_sr_rewrite_next                 \
265 _(ERROR, "error-drop")                          \
266 _(IP6_LOOKUP, "ip6-lookup")                     \
267 _(SR_LOCAL, "sr-local")                         \
268 _(SR_REPLICATE,"sr-replicate")
269 #else
270 #define foreach_sr_rewrite_next                 \
271 _(ERROR, "error-drop")                          \
272 _(IP6_LOOKUP, "ip6-lookup")                     \
273 _(SR_LOCAL, "sr-local")
274 #endif /* DPDK */
275
276 /**
277  * @brief Struct for defined valid next nodes
278 */
279 typedef enum
280 {
281 #define _(s,n) SR_REWRITE_NEXT_##s,
282   foreach_sr_rewrite_next
283 #undef _
284     SR_REWRITE_N_NEXT,
285 } sr_rewrite_next_t;
286
287 /**
288  * @brief Struct for data for SR rewrite packet trace
289  */
290 typedef struct
291 {
292   ip6_address_t src, dst;
293   u16 length;
294   u32 next_index;
295   u32 tunnel_index;
296   u8 sr[256];
297 } sr_rewrite_trace_t;
298
299 /**
300  * @brief Error strings for SR rewrite
301  */
302 static char *sr_rewrite_error_strings[] = {
303 #define sr_error(n,s) s,
304 #include "sr_error.def"
305 #undef sr_error
306 };
307
308 /**
309  * @brief Struct for SR rewrite error strings
310  */
311 typedef enum
312 {
313 #define sr_error(n,s) SR_REWRITE_ERROR_##n,
314 #include "sr_error.def"
315 #undef sr_error
316   SR_REWRITE_N_ERROR,
317 } sr_rewrite_error_t;
318
319
320 /**
321  * @brief Format function for SR rewrite trace.
322  */
323 u8 *
324 format_sr_rewrite_trace (u8 * s, va_list * args)
325 {
326   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
327   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
328   sr_rewrite_trace_t *t = va_arg (*args, sr_rewrite_trace_t *);
329   ip6_sr_main_t *sm = &sr_main;
330   ip6_sr_tunnel_t *tun = pool_elt_at_index (sm->tunnels, t->tunnel_index);
331   ip6_fib_t *rx_fib, *tx_fib;
332
333   rx_fib = ip6_fib_get (tun->rx_fib_index);
334   tx_fib = ip6_fib_get (tun->tx_fib_index);
335
336   s = format
337     (s, "SR-REWRITE: next %s ip6 src %U dst %U len %u\n"
338      "           rx-fib-id %d tx-fib-id %d\n%U",
339      (t->next_index == SR_REWRITE_NEXT_SR_LOCAL)
340      ? "sr-local" : "ip6-lookup",
341      format_ip6_address, &t->src,
342      format_ip6_address, &t->dst, t->length,
343      rx_fib->table_id, tx_fib->table_id,
344      format_ip6_sr_header, t->sr, 0 /* print_hmac */ );
345   return s;
346 }
347
348 /**
349  * @brief Main processing dual-loop for Segment Routing Rewrite
350  * @node sr-rewrite
351  *
352  * @param vm vlib_main_t *
353  * @param node vlib_node_runtime_t *
354  * @param from_frame vlib_frame_t *
355  *
356  * @return from_frame->n_vectors uword
357  */
358 static uword
359 sr_rewrite (vlib_main_t * vm,
360             vlib_node_runtime_t * node, vlib_frame_t * from_frame)
361 {
362   u32 n_left_from, next_index, *from, *to_next;
363   ip6_sr_main_t *sm = &sr_main;
364   u32 (*sr_local_cb) (vlib_main_t *, vlib_node_runtime_t *,
365                       vlib_buffer_t *, ip6_header_t *, ip6_sr_header_t *);
366   sr_local_cb = sm->sr_local_cb;
367
368   from = vlib_frame_vector_args (from_frame);
369   n_left_from = from_frame->n_vectors;
370
371   next_index = node->cached_next_index;
372
373   while (n_left_from > 0)
374     {
375       u32 n_left_to_next;
376
377       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
378
379       /* Note 2x loop disabled */
380       while (0 && n_left_from >= 4 && n_left_to_next >= 2)
381         {
382           u32 bi0, bi1;
383           vlib_buffer_t *b0, *b1;
384           ip6_header_t *ip0, *ip1;
385           ip6_sr_header_t *sr0, *sr1;
386           ip6_sr_tunnel_t *t0, *t1;
387           u32 next0 = SR_REWRITE_NEXT_IP6_LOOKUP;
388           u32 next1 = SR_REWRITE_NEXT_IP6_LOOKUP;
389           u16 new_l0 = 0;
390           u16 new_l1 = 0;
391
392           /* Prefetch next iteration. */
393           {
394             vlib_buffer_t *p2, *p3;
395
396             p2 = vlib_get_buffer (vm, from[2]);
397             p3 = vlib_get_buffer (vm, from[3]);
398
399             vlib_prefetch_buffer_header (p2, LOAD);
400             vlib_prefetch_buffer_header (p3, LOAD);
401           }
402
403           bi0 = from[0];
404           bi1 = from[1];
405           to_next[0] = bi0;
406           to_next[1] = bi1;
407           from += 2;
408           to_next += 2;
409           n_left_to_next -= 2;
410           n_left_from -= 2;
411
412           b0 = vlib_get_buffer (vm, bi0);
413           b1 = vlib_get_buffer (vm, bi1);
414
415           /*
416            * $$$ parse through header(s) to pick the point
417            * where we punch in the SR extention header
418            */
419           t0 =
420             pool_elt_at_index (sm->tunnels,
421                                vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
422           t1 =
423             pool_elt_at_index (sm->tunnels,
424                                vnet_buffer (b1)->ip.adj_index[VLIB_TX]);
425
426           ASSERT (VLIB_BUFFER_PRE_DATA_SIZE
427                   >= ((word) vec_len (t0->rewrite)) + b0->current_data);
428           ASSERT (VLIB_BUFFER_PRE_DATA_SIZE
429                   >= ((word) vec_len (t1->rewrite)) + b1->current_data);
430
431           vnet_buffer (b0)->sw_if_index[VLIB_TX] = t0->tx_fib_index;
432           vnet_buffer (b1)->sw_if_index[VLIB_TX] = t1->tx_fib_index;
433
434           ip0 = vlib_buffer_get_current (b0);
435           ip1 = vlib_buffer_get_current (b1);
436 #if DPDK > 0                    /* Cannot call replication node yet without DPDK */
437           /* add a replication node */
438           if (PREDICT_FALSE (t0->policy_index != ~0))
439             {
440               vnet_buffer (b0)->ip.save_protocol = t0->policy_index;
441               next0 = SR_REWRITE_NEXT_SR_REPLICATE;
442               sr0 = (ip6_sr_header_t *) (t0->rewrite);
443               goto processnext;
444             }
445 #endif /* DPDK */
446
447           /*
448            * SR-unaware service chaining case: pkt coming back from
449            * service has the original dst address, and will already
450            * have an SR header. If so, send it to sr-local
451            */
452           if (PREDICT_FALSE (ip0->protocol == IPPROTO_IPV6_ROUTE))
453             {
454               vlib_buffer_advance (b0, sizeof (ip0));
455               sr0 = (ip6_sr_header_t *) (ip0 + 1);
456               new_l0 = clib_net_to_host_u16 (ip0->payload_length);
457               next0 = SR_REWRITE_NEXT_SR_LOCAL;
458             }
459           else
460             {
461               u32 len_bytes = sizeof (ip6_header_t);
462               u8 next_hdr = ip0->protocol;
463
464               /* HBH must immediately follow ipv6 header */
465               if (PREDICT_FALSE
466                   (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
467                 {
468                   ip6_hop_by_hop_ext_t *ext_hdr =
469                     (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
470                   len_bytes +=
471                     ip6_ext_header_len ((ip6_ext_header_t *) ext_hdr);
472                   /* Ignoring the sr_local for now, if RH follows HBH here */
473                   next_hdr = ext_hdr->next_hdr;
474                   ext_hdr->next_hdr = IPPROTO_IPV6_ROUTE;
475                 }
476               else
477                 {
478                   ip0->protocol = IPPROTO_IPV6_ROUTE;   /* routing extension header */
479                 }
480               /*
481                * Copy data before the punch-in point left by the
482                * required amount. Assume (for the moment) that only
483                * the main packet header needs to be copied.
484                */
485               clib_memcpy (((u8 *) ip0) - vec_len (t0->rewrite),
486                            ip0, len_bytes);
487               vlib_buffer_advance (b0, -(word) vec_len (t0->rewrite));
488               ip0 = vlib_buffer_get_current (b0);
489               sr0 = (ip6_sr_header_t *) ((u8 *) ip0 + len_bytes);
490               /* $$$ tune */
491               clib_memcpy (sr0, t0->rewrite, vec_len (t0->rewrite));
492
493               /* Fix the next header chain */
494               sr0->protocol = next_hdr;
495
496               new_l0 = clib_net_to_host_u16 (ip0->payload_length) +
497                 vec_len (t0->rewrite);
498               ip0->payload_length = clib_host_to_net_u16 (new_l0);
499
500               /* Copy dst address into the DA slot in the segment list */
501               clib_memcpy (sr0->segments, ip0->dst_address.as_u64,
502                            sizeof (ip6_address_t));
503               /* Rewrite the ip6 dst address with the first hop */
504               clib_memcpy (ip0->dst_address.as_u64, t0->first_hop.as_u64,
505                            sizeof (ip6_address_t));
506
507               sr_fix_hmac (sm, ip0, sr0);
508
509               next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) :
510                 next0;
511
512               /*
513                * Ignore "do not rewrite" shtik in this path
514                */
515               if (PREDICT_FALSE (next0 & 0x80000000))
516                 {
517                   next0 ^= 0xFFFFFFFF;
518                   if (PREDICT_FALSE (next0 == SR_REWRITE_NEXT_ERROR))
519                     b0->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK];
520                 }
521             }
522 #if DPDK > 0                    /* Cannot call replication node yet without DPDK */
523         processnext:
524           /* add a replication node */
525           if (PREDICT_FALSE (t1->policy_index != ~0))
526             {
527               vnet_buffer (b1)->ip.save_protocol = t1->policy_index;
528               next1 = SR_REWRITE_NEXT_SR_REPLICATE;
529               sr1 = (ip6_sr_header_t *) (t1->rewrite);
530               goto trace00;
531             }
532 #endif /* DPDK */
533           if (PREDICT_FALSE (ip1->protocol == IPPROTO_IPV6_ROUTE))
534             {
535               vlib_buffer_advance (b1, sizeof (ip1));
536               sr1 = (ip6_sr_header_t *) (ip1 + 1);
537               new_l1 = clib_net_to_host_u16 (ip1->payload_length);
538               next1 = SR_REWRITE_NEXT_SR_LOCAL;
539             }
540           else
541             {
542               u32 len_bytes = sizeof (ip6_header_t);
543               u8 next_hdr = ip1->protocol;
544
545               /* HBH must immediately follow ipv6 header */
546               if (PREDICT_FALSE
547                   (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
548                 {
549                   ip6_hop_by_hop_ext_t *ext_hdr =
550                     (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1);
551                   len_bytes +=
552                     ip6_ext_header_len ((ip6_ext_header_t *) ext_hdr);
553                   /* Ignoring the sr_local for now, if RH follows HBH here */
554                   next_hdr = ext_hdr->next_hdr;
555                   ext_hdr->next_hdr = IPPROTO_IPV6_ROUTE;
556                 }
557               else
558                 {
559                   ip1->protocol = IPPROTO_IPV6_ROUTE;
560                 }
561               /*
562                * Copy data before the punch-in point left by the
563                * required amount. Assume (for the moment) that only
564                * the main packet header needs to be copied.
565                */
566               clib_memcpy (((u8 *) ip1) - vec_len (t1->rewrite),
567                            ip1, len_bytes);
568               vlib_buffer_advance (b1, -(word) vec_len (t1->rewrite));
569               ip1 = vlib_buffer_get_current (b1);
570               sr1 = (ip6_sr_header_t *) ((u8 *) ip1 + len_bytes);
571               clib_memcpy (sr1, t1->rewrite, vec_len (t1->rewrite));
572
573               sr1->protocol = next_hdr;
574               new_l1 = clib_net_to_host_u16 (ip1->payload_length) +
575                 vec_len (t1->rewrite);
576               ip1->payload_length = clib_host_to_net_u16 (new_l1);
577
578               /* Copy dst address into the DA slot in the segment list */
579               clib_memcpy (sr1->segments, ip1->dst_address.as_u64,
580                            sizeof (ip6_address_t));
581               /* Rewrite the ip6 dst address with the first hop */
582               clib_memcpy (ip1->dst_address.as_u64, t1->first_hop.as_u64,
583                            sizeof (ip6_address_t));
584
585               sr_fix_hmac (sm, ip1, sr1);
586
587               next1 = sr_local_cb ? sr_local_cb (vm, node, b1, ip1, sr1) :
588                 next1;
589
590               /*
591                * Ignore "do not rewrite" shtik in this path
592                */
593               if (PREDICT_FALSE (next1 & 0x80000000))
594                 {
595                   next1 ^= 0xFFFFFFFF;
596                   if (PREDICT_FALSE (next1 == SR_REWRITE_NEXT_ERROR))
597                     b1->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK];
598                 }
599             }
600 #if DPDK > 0                    /* Cannot run replicate without DPDK and only replicate uses this label */
601         trace00:
602 #endif /* DPDK */
603
604           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
605             {
606               sr_rewrite_trace_t *tr = vlib_add_trace (vm, node,
607                                                        b0, sizeof (*tr));
608               tr->tunnel_index = t0 - sm->tunnels;
609               clib_memcpy (tr->src.as_u8, ip0->src_address.as_u8,
610                            sizeof (tr->src.as_u8));
611               clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
612                            sizeof (tr->dst.as_u8));
613               tr->length = new_l0;
614               tr->next_index = next0;
615               if (sr0)
616                 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
617             }
618           if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
619             {
620               sr_rewrite_trace_t *tr = vlib_add_trace (vm, node,
621                                                        b1, sizeof (*tr));
622               tr->tunnel_index = t1 - sm->tunnels;
623               clib_memcpy (tr->src.as_u8, ip1->src_address.as_u8,
624                            sizeof (tr->src.as_u8));
625               clib_memcpy (tr->dst.as_u8, ip1->dst_address.as_u8,
626                            sizeof (tr->dst.as_u8));
627               tr->length = new_l1;
628               tr->next_index = next1;
629               if (sr1)
630                 clib_memcpy (tr->sr, sr1, sizeof (tr->sr));
631             }
632           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
633                                            to_next, n_left_to_next,
634                                            bi0, bi1, next0, next1);
635         }
636
637       while (n_left_from > 0 && n_left_to_next > 0)
638         {
639           u32 bi0;
640           vlib_buffer_t *b0;
641           ip6_header_t *ip0 = 0;
642           ip6_sr_header_t *sr0 = 0;
643           ip6_sr_tunnel_t *t0;
644           u32 next0 = SR_REWRITE_NEXT_IP6_LOOKUP;
645           u16 new_l0 = 0;
646
647           bi0 = from[0];
648           to_next[0] = bi0;
649           from += 1;
650           to_next += 1;
651           n_left_from -= 1;
652           n_left_to_next -= 1;
653
654           b0 = vlib_get_buffer (vm, bi0);
655
656
657           /*
658            * $$$ parse through header(s) to pick the point
659            * where we punch in the SR extention header
660            */
661           t0 =
662             pool_elt_at_index (sm->tunnels,
663                                vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
664 #if DPDK > 0                    /* Cannot call replication node yet without DPDK */
665           /* add a replication node */
666           if (PREDICT_FALSE (t0->policy_index != ~0))
667             {
668               vnet_buffer (b0)->ip.save_protocol = t0->policy_index;
669               next0 = SR_REWRITE_NEXT_SR_REPLICATE;
670               sr0 = (ip6_sr_header_t *) (t0->rewrite);
671               goto trace0;
672             }
673 #endif /* DPDK */
674
675           ASSERT (VLIB_BUFFER_PRE_DATA_SIZE
676                   >= ((word) vec_len (t0->rewrite)) + b0->current_data);
677
678           vnet_buffer (b0)->sw_if_index[VLIB_TX] = t0->tx_fib_index;
679
680           ip0 = vlib_buffer_get_current (b0);
681
682           /*
683            * SR-unaware service chaining case: pkt coming back from
684            * service has the original dst address, and will already
685            * have an SR header. If so, send it to sr-local
686            */
687           if (PREDICT_FALSE (ip0->protocol == IPPROTO_IPV6_ROUTE))
688             {
689               vlib_buffer_advance (b0, sizeof (ip0));
690               sr0 = (ip6_sr_header_t *) (ip0 + 1);
691               new_l0 = clib_net_to_host_u16 (ip0->payload_length);
692               next0 = SR_REWRITE_NEXT_SR_LOCAL;
693             }
694           else
695             {
696               u32 len_bytes = sizeof (ip6_header_t);
697               u8 next_hdr = ip0->protocol;
698
699               /* HBH must immediately follow ipv6 header */
700               if (PREDICT_FALSE
701                   (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
702                 {
703                   ip6_hop_by_hop_ext_t *ext_hdr =
704                     (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
705                   len_bytes +=
706                     ip6_ext_header_len ((ip6_ext_header_t *) ext_hdr);
707                   next_hdr = ext_hdr->next_hdr;
708                   ext_hdr->next_hdr = IPPROTO_IPV6_ROUTE;
709                   /* Ignoring the sr_local for now, if RH follows HBH here */
710                 }
711               else
712                 {
713                   ip0->protocol = IPPROTO_IPV6_ROUTE;   /* routing extension header */
714                 }
715               /*
716                * Copy data before the punch-in point left by the
717                * required amount. Assume (for the moment) that only
718                * the main packet header needs to be copied.
719                */
720               clib_memcpy (((u8 *) ip0) - vec_len (t0->rewrite),
721                            ip0, len_bytes);
722               vlib_buffer_advance (b0, -(word) vec_len (t0->rewrite));
723               ip0 = vlib_buffer_get_current (b0);
724               sr0 = (ip6_sr_header_t *) ((u8 *) ip0 + len_bytes);
725               /* $$$ tune */
726               clib_memcpy (sr0, t0->rewrite, vec_len (t0->rewrite));
727
728               /* Fix the next header chain */
729               sr0->protocol = next_hdr;
730               new_l0 = clib_net_to_host_u16 (ip0->payload_length) +
731                 vec_len (t0->rewrite);
732               ip0->payload_length = clib_host_to_net_u16 (new_l0);
733
734               /* Copy dst address into the DA slot in the segment list */
735               clib_memcpy (sr0->segments, ip0->dst_address.as_u64,
736                            sizeof (ip6_address_t));
737               /* Rewrite the ip6 dst address with the first hop */
738               clib_memcpy (ip0->dst_address.as_u64, t0->first_hop.as_u64,
739                            sizeof (ip6_address_t));
740
741               sr_fix_hmac (sm, ip0, sr0);
742
743               next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) :
744                 next0;
745
746               /*
747                * Ignore "do not rewrite" shtik in this path
748                */
749               if (PREDICT_FALSE (next0 & 0x80000000))
750                 {
751                   next0 ^= 0xFFFFFFFF;
752                   if (PREDICT_FALSE (next0 == SR_REWRITE_NEXT_ERROR))
753                     b0->error = node->errors[SR_REWRITE_ERROR_APP_CALLBACK];
754                 }
755             }
756 #if DPDK > 0                    /* Cannot run replicate without DPDK and only replicate uses this label */
757         trace0:
758 #endif /* DPDK */
759
760           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
761             {
762               sr_rewrite_trace_t *tr = vlib_add_trace (vm, node,
763                                                        b0, sizeof (*tr));
764               tr->tunnel_index = t0 - sm->tunnels;
765               if (ip0)
766                 {
767                   memcpy (tr->src.as_u8, ip0->src_address.as_u8,
768                           sizeof (tr->src.as_u8));
769                   memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
770                           sizeof (tr->dst.as_u8));
771                 }
772               tr->length = new_l0;
773               tr->next_index = next0;
774               if (sr0)
775                 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
776             }
777           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
778                                            to_next, n_left_to_next,
779                                            bi0, next0);
780         }
781       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
782     }
783   return from_frame->n_vectors;
784 }
785
786 /* *INDENT-OFF* */
787 VLIB_REGISTER_NODE (sr_rewrite_node) = {
788   .function = sr_rewrite,
789   .name = "sr-rewrite",
790   /* Takes a vector of packets. */
791   .vector_size = sizeof (u32),
792   .format_trace = format_sr_rewrite_trace,
793   .format_buffer = format_ip6_sr_header_with_length,
794
795   .n_errors = SR_REWRITE_N_ERROR,
796   .error_strings = sr_rewrite_error_strings,
797
798   .runtime_data_bytes = 0,
799
800   .n_next_nodes = SR_REWRITE_N_NEXT,
801   .next_nodes = {
802 #define _(s,n) [SR_REWRITE_NEXT_##s] = n,
803     foreach_sr_rewrite_next
804 #undef _
805   },
806 };
807
808 VLIB_NODE_FUNCTION_MULTIARCH (sr_rewrite_node, sr_rewrite)
809 /* *INDENT-ON* */
810
811 static int
812 ip6_delete_route_no_next_hop (ip6_address_t * dst_address_arg,
813                               u32 dst_address_length, u32 rx_table_id)
814 {
815   fib_prefix_t pfx = {
816     .fp_len = dst_address_length,
817     .fp_proto = FIB_PROTOCOL_IP6,
818     .fp_addr = {
819                 .ip6 = *dst_address_arg,
820                 }
821   };
822
823   fib_table_entry_delete (fib_table_id_find_fib_index (FIB_PROTOCOL_IP6,
824                                                        rx_table_id),
825                           &pfx, FIB_SOURCE_SR);
826
827   return 0;
828 }
829
830 /**
831  * @brief Find or add if not found - HMAC shared secret
832  *
833  * @param sm ip6_sr_main_t *
834  * @param secret u8 *
835  * @param indexp u32 *
836  *
837  * @return ip6_sr_hmac_key_t *
838  */
839 static ip6_sr_hmac_key_t *
840 find_or_add_shared_secret (ip6_sr_main_t * sm, u8 * secret, u32 * indexp)
841 {
842   uword *p;
843   ip6_sr_hmac_key_t *key = 0;
844   int i;
845
846   p = hash_get_mem (sm->hmac_key_by_shared_secret, secret);
847
848   if (p)
849     {
850       key = vec_elt_at_index (sm->hmac_keys, p[0]);
851       if (indexp)
852         *indexp = p[0];
853       return (key);
854     }
855
856   /* Specific key ID? */
857   if (indexp && *indexp)
858     {
859       vec_validate (sm->hmac_keys, *indexp);
860       key = sm->hmac_keys + *indexp;
861     }
862   else
863     {
864       for (i = 0; i < vec_len (sm->hmac_keys); i++)
865         {
866           if (sm->hmac_keys[i].shared_secret == 0)
867             {
868               key = sm->hmac_keys + i;
869               goto found;
870             }
871         }
872       vec_validate (sm->hmac_keys, i);
873       key = sm->hmac_keys + i;
874     found:
875       ;
876     }
877
878   key->shared_secret = vec_dup (secret);
879
880   hash_set_mem (sm->hmac_key_by_shared_secret, key->shared_secret,
881                 key - sm->hmac_keys);
882
883   if (indexp)
884     *indexp = key - sm->hmac_keys;
885   return (key);
886 }
887
888 /**
889  * @brief Add or Delete a Segment Routing tunnel.
890  *
891  * @param a ip6_sr_add_del_tunnel_args_t *
892  *
893  * @return retval int
894  */
895 int
896 ip6_sr_add_del_tunnel (ip6_sr_add_del_tunnel_args_t * a)
897 {
898   ip6_main_t *im = &ip6_main;
899   ip6_sr_tunnel_key_t key;
900   ip6_sr_tunnel_t *t;
901   uword *p, *n;
902   ip6_sr_header_t *h = 0;
903   u32 header_length;
904   ip6_address_t *addrp, *this_address;
905   ip6_sr_main_t *sm = &sr_main;
906   u8 *key_copy;
907   u32 rx_fib_index, tx_fib_index;
908   u32 hmac_key_index_u32;
909   u8 hmac_key_index = 0;
910   ip6_sr_policy_t *pt;
911   int i;
912   dpo_id_t dpo = DPO_INVALID;
913
914   /* Make sure that the rx FIB exists */
915   p = hash_get (im->fib_index_by_table_id, a->rx_table_id);
916
917   if (p == 0)
918     return -3;
919
920   /* remember the FIB index */
921   rx_fib_index = p[0];
922
923   /* Make sure that the supplied FIB exists */
924   p = hash_get (im->fib_index_by_table_id, a->tx_table_id);
925
926   if (p == 0)
927     return -4;
928
929   /* remember the FIB index */
930   tx_fib_index = p[0];
931
932   clib_memcpy (key.src.as_u8, a->src_address->as_u8, sizeof (key.src));
933   clib_memcpy (key.dst.as_u8, a->dst_address->as_u8, sizeof (key.dst));
934
935   /* When adding a tunnel:
936    * - If a "name" is given, it must not exist.
937    * - The "key" is always checked, and must not exist.
938    * When deleting a tunnel:
939    * - If the "name" is given, and it exists, then use it.
940    * - If the "name" is not given, use the "key".
941    * - If the "name" and the "key" are given, then both must point to the same
942    *   thing.
943    */
944
945   /* Lookup the key */
946   p = hash_get_mem (sm->tunnel_index_by_key, &key);
947
948   /* If the name is given, look it up */
949   if (a->name)
950     n = hash_get_mem (sm->tunnel_index_by_name, a->name);
951   else
952     n = 0;
953
954   /* validate key/name parameters */
955   if (!a->is_del)               /* adding a tunnel */
956     {
957       if (a->name && n)         /* name given & exists already */
958         return -1;
959       if (p)                    /* key exists already */
960         return -1;
961     }
962   else                          /* deleting a tunnel */
963     {
964       if (!p)                   /* key doesn't exist */
965         return -2;
966       if (a->name && !n)        /* name given & it doesn't exist */
967         return -2;
968
969       if (n)                    /* name given & found */
970         {
971           if (n[0] != p[0])     /* name and key do not point to the same thing */
972             return -2;
973         }
974     }
975
976
977   if (a->is_del)                /* delete the tunnel */
978     {
979       hash_pair_t *hp;
980
981       /* Delete existing tunnel */
982       t = pool_elt_at_index (sm->tunnels, p[0]);
983
984       ip6_delete_route_no_next_hop (&t->key.dst, t->dst_mask_width,
985                                     a->rx_table_id);
986       vec_free (t->rewrite);
987       /* Remove tunnel from any policy if associated */
988       if (t->policy_index != ~0)
989         {
990           pt = pool_elt_at_index (sm->policies, t->policy_index);
991           for (i = 0; i < vec_len (pt->tunnel_indices); i++)
992             {
993               if (pt->tunnel_indices[i] == t - sm->tunnels)
994                 {
995                   vec_delete (pt->tunnel_indices, 1, i);
996                   goto found;
997                 }
998             }
999           clib_warning ("Tunnel index %d not found in policy_index %d",
1000                         t - sm->tunnels, pt - sm->policies);
1001         found:
1002           /* If this is last tunnel in the  policy, clean up the policy too */
1003           if (vec_len (pt->tunnel_indices) == 0)
1004             {
1005               hash_unset_mem (sm->policy_index_by_policy_name, pt->name);
1006               vec_free (pt->name);
1007               pool_put (sm->policies, pt);
1008             }
1009         }
1010
1011       /* Clean up the tunnel by name */
1012       if (t->name)
1013         {
1014           hash_unset_mem (sm->tunnel_index_by_name, t->name);
1015           vec_free (t->name);
1016         }
1017       pool_put (sm->tunnels, t);
1018       hp = hash_get_pair (sm->tunnel_index_by_key, &key);
1019       key_copy = (void *) (hp->key);
1020       hash_unset_mem (sm->tunnel_index_by_key, &key);
1021       vec_free (key_copy);
1022       return 0;
1023     }
1024
1025   /* create a new tunnel */
1026   pool_get (sm->tunnels, t);
1027   memset (t, 0, sizeof (*t));
1028   t->policy_index = ~0;
1029
1030   clib_memcpy (&t->key, &key, sizeof (t->key));
1031   t->dst_mask_width = a->dst_mask_width;
1032   t->rx_fib_index = rx_fib_index;
1033   t->tx_fib_index = tx_fib_index;
1034
1035   if (!vec_len (a->segments))
1036     /* there must be at least one segment... */
1037     return -4;
1038
1039   /* The first specified hop goes right into the dst address */
1040   clib_memcpy (&t->first_hop, &a->segments[0], sizeof (ip6_address_t));
1041
1042   /*
1043    * Create the sr header rewrite string
1044    * The list of segments needs an extra slot for the ultimate destination
1045    * which is taken from the packet we add the SRH to.
1046    */
1047   header_length = sizeof (*h) +
1048     sizeof (ip6_address_t) * (vec_len (a->segments) + 1 + vec_len (a->tags));
1049
1050   if (a->shared_secret)
1051     {
1052       /* Allocate a new key slot if we don't find the secret key */
1053       hmac_key_index_u32 = 0;
1054       (void) find_or_add_shared_secret (sm, a->shared_secret,
1055                                         &hmac_key_index_u32);
1056
1057       /* Hey Vinz Clortho: Gozzer is pissed.. you're out of keys! */
1058       if (hmac_key_index_u32 >= 256)
1059         return -5;
1060       hmac_key_index = hmac_key_index_u32;
1061       header_length += SHA256_DIGEST_LENGTH;
1062     }
1063
1064   vec_validate (t->rewrite, header_length - 1);
1065
1066   h = (ip6_sr_header_t *) t->rewrite;
1067
1068   h->protocol = 0xFF;           /* we don't know yet */
1069
1070   h->length = (header_length / 8) - 1;
1071   h->type = ROUTING_HEADER_TYPE_SR;
1072
1073   /* first_segment and segments_left need to have the index of the last
1074    * element in the list; a->segments has one element less than ends up
1075    * in the header (it does not have the DA in it), so vec_len(a->segments)
1076    * is the value we want.
1077    */
1078   h->first_segment = h->segments_left = vec_len (a->segments);
1079
1080   if (a->shared_secret)
1081     h->hmac_key = hmac_key_index & 0xFF;
1082
1083   h->flags = a->flags_net_byte_order;
1084
1085   /* Paint on the segment list, in reverse.
1086    * This is offset by one to leave room at the start for the ultimate
1087    * destination.
1088    */
1089   addrp = h->segments + vec_len (a->segments);
1090
1091   vec_foreach (this_address, a->segments)
1092   {
1093     clib_memcpy (addrp->as_u8, this_address->as_u8, sizeof (ip6_address_t));
1094     addrp--;
1095   }
1096
1097   /*
1098    * Since the ultimate destination address is not yet known, set that slot
1099    * to a value we will instantly recognize as bogus.
1100    */
1101   memset (h->segments, 0xfe, sizeof (ip6_address_t));
1102
1103   /* Paint on the tag list, not reversed */
1104   addrp = h->segments + vec_len (a->segments);
1105
1106   vec_foreach (this_address, a->tags)
1107   {
1108     clib_memcpy (addrp->as_u8, this_address->as_u8, sizeof (ip6_address_t));
1109     addrp++;
1110   }
1111
1112   key_copy = vec_new (ip6_sr_tunnel_key_t, 1);
1113   clib_memcpy (key_copy, &key, sizeof (ip6_sr_tunnel_key_t));
1114   hash_set_mem (sm->tunnel_index_by_key, key_copy, t - sm->tunnels);
1115
1116   /*
1117    * Stick the tunnel index into the rewrite header.
1118    *
1119    * Unfortunately, inserting an SR header according to the various
1120    * RFC's requires parsing through the ip6 header, perhaps consing a
1121    * buffer onto the head of the vlib_buffer_t, etc. We don't use the
1122    * normal reverse bcopy rewrite code.
1123    *
1124    * We don't handle ugly RFC-related cases yet, but I'm sure PL will complain
1125    * at some point...
1126    */
1127   dpo_set (&dpo, sr_dpo_type, DPO_PROTO_IP6, t - sm->tunnels);
1128
1129   fib_prefix_t pfx = {
1130     .fp_proto = FIB_PROTOCOL_IP6,
1131     .fp_len = a->dst_mask_width,
1132     .fp_addr = {
1133                 .ip6 = *a->dst_address,
1134                 }
1135   };
1136   fib_table_entry_special_dpo_add (rx_fib_index,
1137                                    &pfx,
1138                                    FIB_SOURCE_SR,
1139                                    FIB_ENTRY_FLAG_EXCLUSIVE, &dpo);
1140   dpo_reset (&dpo);
1141
1142   if (a->policy_name)
1143     {
1144       p = hash_get_mem (sm->policy_index_by_policy_name, a->policy_name);
1145       if (p)
1146         {
1147           pt = pool_elt_at_index (sm->policies, p[0]);
1148         }
1149       else                      /* no policy, lets create one */
1150         {
1151           pool_get (sm->policies, pt);
1152           memset (pt, 0, sizeof (*pt));
1153           pt->name = format (0, "%s%c", a->policy_name, 0);
1154           hash_set_mem (sm->policy_index_by_policy_name, pt->name,
1155                         pt - sm->policies);
1156           p = hash_get_mem (sm->policy_index_by_policy_name, a->policy_name);
1157         }
1158       vec_add1 (pt->tunnel_indices, t - sm->tunnels);
1159       if (p == 0)
1160         clib_warning ("p is NULL!");
1161       t->policy_index = p ? p[0] : ~0;  /* equiv. to (pt - sm->policies) */
1162     }
1163
1164   if (a->name)
1165     {
1166       t->name = format (0, "%s%c", a->name, 0);
1167       hash_set_mem (sm->tunnel_index_by_name, t->name, t - sm->tunnels);
1168     }
1169
1170   return 0;
1171 }
1172
1173 /**
1174  * @brief no-op lock function.
1175  * The lifetime of the SR entry is managed by the control plane
1176  */
1177 static void
1178 sr_dpo_lock (dpo_id_t * dpo)
1179 {
1180 }
1181
1182 /**
1183  * @brief no-op unlock function.
1184  * The lifetime of the SR entry is managed by the control plane
1185  */
1186 static void
1187 sr_dpo_unlock (dpo_id_t * dpo)
1188 {
1189 }
1190
1191 u8 *
1192 format_sr_dpo (u8 * s, va_list * args)
1193 {
1194   index_t index = va_arg (*args, index_t);
1195   CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
1196
1197   return (format (s, "SR: tunnel:[%d]", index));
1198 }
1199
1200 const static dpo_vft_t sr_vft = {
1201   .dv_lock = sr_dpo_lock,
1202   .dv_unlock = sr_dpo_unlock,
1203   .dv_format = format_sr_dpo,
1204 };
1205
1206 const static char *const sr_ip6_nodes[] = {
1207   "sr-rewrite",
1208   NULL,
1209 };
1210
1211 const static char *const *const sr_nodes[DPO_PROTO_NUM] = {
1212   [DPO_PROTO_IP6] = sr_ip6_nodes,
1213 };
1214
1215 /**
1216  * @brief CLI parser for Add or Delete a Segment Routing tunnel.
1217  *
1218  * @param vm vlib_main_t *
1219  * @param input unformat_input_t *
1220  * @param cmd vlib_cli_command_t *
1221  *
1222  * @return error clib_error_t *
1223  */
1224 static clib_error_t *
1225 sr_add_del_tunnel_command_fn (vlib_main_t * vm,
1226                               unformat_input_t * input,
1227                               vlib_cli_command_t * cmd)
1228 {
1229   int is_del = 0;
1230   ip6_address_t src_address;
1231   int src_address_set = 0;
1232   ip6_address_t dst_address;
1233   u32 dst_mask_width;
1234   int dst_address_set = 0;
1235   u16 flags = 0;
1236   u8 *shared_secret = 0;
1237   u8 *name = 0;
1238   u8 *policy_name = 0;
1239   u32 rx_table_id = 0;
1240   u32 tx_table_id = 0;
1241   ip6_address_t *segments = 0;
1242   ip6_address_t *this_seg;
1243   ip6_address_t *tags = 0;
1244   ip6_address_t *this_tag;
1245   ip6_sr_add_del_tunnel_args_t _a, *a = &_a;
1246   ip6_address_t next_address, tag;
1247   int pl_index;
1248   int rv;
1249
1250   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1251     {
1252       if (unformat (input, "del"))
1253         is_del = 1;
1254       else if (unformat (input, "rx-fib-id %d", &rx_table_id))
1255         ;
1256       else if (unformat (input, "tx-fib-id %d", &tx_table_id))
1257         ;
1258       else if (unformat (input, "src %U", unformat_ip6_address, &src_address))
1259         src_address_set = 1;
1260       else if (unformat (input, "name %s", &name))
1261         ;
1262       else if (unformat (input, "policy %s", &policy_name))
1263         ;
1264       else if (unformat (input, "dst %U/%d",
1265                          unformat_ip6_address, &dst_address, &dst_mask_width))
1266         dst_address_set = 1;
1267       else if (unformat (input, "next %U", unformat_ip6_address,
1268                          &next_address))
1269         {
1270           vec_add2 (segments, this_seg, 1);
1271           clib_memcpy (this_seg->as_u8, next_address.as_u8,
1272                        sizeof (*this_seg));
1273         }
1274       else if (unformat (input, "tag %U", unformat_ip6_address, &tag))
1275         {
1276           vec_add2 (tags, this_tag, 1);
1277           clib_memcpy (this_tag->as_u8, tag.as_u8, sizeof (*this_tag));
1278         }
1279       else if (unformat (input, "clean"))
1280         flags |= IP6_SR_HEADER_FLAG_CLEANUP;
1281       else if (unformat (input, "protected"))
1282         flags |= IP6_SR_HEADER_FLAG_PROTECTED;
1283       else if (unformat (input, "key %s", &shared_secret))
1284         /* Do not include the trailing NULL byte. Guaranteed interop issue */
1285         _vec_len (shared_secret) -= 1;
1286       else if (unformat (input, "InPE %d", &pl_index))
1287         {
1288           if (pl_index <= 0 || pl_index > 4)
1289             {
1290             pl_index_range_error:
1291               return clib_error_return
1292                 (0, "Policy List Element Index %d out of range (1-4)",
1293                  pl_index);
1294
1295             }
1296           flags |= IP6_SR_HEADER_FLAG_PL_ELT_INGRESS_PE
1297             << ip6_sr_policy_list_shift_from_index (pl_index);
1298         }
1299       else if (unformat (input, "EgPE %d", &pl_index))
1300         {
1301           if (pl_index <= 0 || pl_index > 4)
1302             goto pl_index_range_error;
1303           flags |= IP6_SR_HEADER_FLAG_PL_ELT_EGRESS_PE
1304             << ip6_sr_policy_list_shift_from_index (pl_index);
1305         }
1306       else if (unformat (input, "OrgSrc %d", &pl_index))
1307         {
1308           if (pl_index <= 0 || pl_index > 4)
1309             goto pl_index_range_error;
1310           flags |= IP6_SR_HEADER_FLAG_PL_ELT_ORIG_SRC_ADDR
1311             << ip6_sr_policy_list_shift_from_index (pl_index);
1312         }
1313       else
1314         break;
1315     }
1316
1317   if (!src_address_set)
1318     return clib_error_return (0, "src address required");
1319
1320   if (!dst_address_set)
1321     return clib_error_return (0, "dst address required");
1322
1323   if (!segments)
1324     return clib_error_return (0, "at least one sr segment required");
1325
1326   memset (a, 0, sizeof (*a));
1327   a->src_address = &src_address;
1328   a->dst_address = &dst_address;
1329   a->dst_mask_width = dst_mask_width;
1330   a->segments = segments;
1331   a->tags = tags;
1332   a->flags_net_byte_order = clib_host_to_net_u16 (flags);
1333   a->is_del = is_del;
1334   a->rx_table_id = rx_table_id;
1335   a->tx_table_id = tx_table_id;
1336   a->shared_secret = shared_secret;
1337
1338   if (vec_len (name))
1339     a->name = name;
1340   else
1341     a->name = 0;
1342
1343   if (vec_len (policy_name))
1344     a->policy_name = policy_name;
1345   else
1346     a->policy_name = 0;
1347
1348   rv = ip6_sr_add_del_tunnel (a);
1349
1350   vec_free (segments);
1351   vec_free (tags);
1352   vec_free (shared_secret);
1353
1354   switch (rv)
1355     {
1356     case 0:
1357       break;
1358
1359     case -1:
1360       return clib_error_return (0, "SR tunnel src %U dst %U already exists",
1361                                 format_ip6_address, &src_address,
1362                                 format_ip6_address, &dst_address);
1363
1364     case -2:
1365       return clib_error_return (0, "SR tunnel src %U dst %U does not exist",
1366                                 format_ip6_address, &src_address,
1367                                 format_ip6_address, &dst_address);
1368
1369     case -3:
1370       return clib_error_return (0, "FIB table %d does not exist",
1371                                 rx_table_id);
1372
1373     case -4:
1374       return clib_error_return (0, "At least one segment is required");
1375
1376     default:
1377       return clib_error_return (0, "BUG: ip6_sr_add_del_tunnel returns %d",
1378                                 rv);
1379     }
1380
1381   return 0;
1382 }
1383
1384 /* *INDENT-OFF* */
1385 VLIB_CLI_COMMAND (sr_tunnel_command, static) = {
1386     .path = "sr tunnel",
1387     .short_help =
1388       "sr tunnel [del] [name <name>] src <addr> dst <addr> [next <addr>] "
1389       "[clean] [reroute] [key <secret>] [policy <policy_name>]"
1390       "[rx-fib-id <fib_id>] [tx-fib-id <fib_id>]",
1391     .function = sr_add_del_tunnel_command_fn,
1392 };
1393 /* *INDENT-ON* */
1394
1395 /**
1396  * @brief Display Segment Routing tunnel
1397  *
1398  * @param vm vlib_main_t *
1399  * @param t ip6_sr_tunnel_t *
1400  *
1401  */
1402 void
1403 ip6_sr_tunnel_display (vlib_main_t * vm, ip6_sr_tunnel_t * t)
1404 {
1405   ip6_sr_main_t *sm = &sr_main;
1406   ip6_fib_t *rx_fib, *tx_fib;
1407   ip6_sr_policy_t *pt;
1408
1409   rx_fib = ip6_fib_get (t->rx_fib_index);
1410   tx_fib = ip6_fib_get (t->tx_fib_index);
1411
1412   if (t->name)
1413     vlib_cli_output (vm, "sr tunnel name: %s", (char *) t->name);
1414
1415   vlib_cli_output (vm, "src %U dst %U first hop %U",
1416                    format_ip6_address, &t->key.src,
1417                    format_ip6_address, &t->key.dst,
1418                    format_ip6_address, &t->first_hop);
1419   vlib_cli_output (vm, "    rx-fib-id %d tx-fib-id %d",
1420                    rx_fib->table_id, tx_fib->table_id);
1421   vlib_cli_output (vm, "  sr: %U", format_ip6_sr_header, t->rewrite,
1422                    0 /* print_hmac */ );
1423
1424   if (t->policy_index != ~0)
1425     {
1426       pt = pool_elt_at_index (sm->policies, t->policy_index);
1427       vlib_cli_output (vm, "sr policy: %s", (char *) pt->name);
1428     }
1429   vlib_cli_output (vm, "-------");
1430
1431   return;
1432 }
1433
1434 /**
1435  * @brief CLI Parser for Display Segment Routing tunnel
1436  *
1437  * @param vm vlib_main_t *
1438  * @param input unformat_input_t *
1439  * @param cmd vlib_cli_command_t *
1440  *
1441  * @return error clib_error_t *
1442  */
1443 static clib_error_t *
1444 show_sr_tunnel_fn (vlib_main_t * vm,
1445                    unformat_input_t * input, vlib_cli_command_t * cmd)
1446 {
1447   static ip6_sr_tunnel_t **tunnels;
1448   ip6_sr_tunnel_t *t;
1449   ip6_sr_main_t *sm = &sr_main;
1450   int i;
1451   uword *p = 0;
1452   u8 *name = 0;
1453
1454   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1455     {
1456       if (unformat (input, "name %s", &name))
1457         {
1458           p = hash_get_mem (sm->tunnel_index_by_name, name);
1459           if (!p)
1460             vlib_cli_output (vm, "No SR tunnel with name: %s. Showing all.",
1461                              name);
1462         }
1463       else
1464         break;
1465     }
1466
1467   vec_reset_length (tunnels);
1468
1469   if (!p)                       /* Either name parm not passed or no tunnel with that name found, show all */
1470     {
1471       /* *INDENT-OFF* */
1472   pool_foreach (t, sm->tunnels,
1473   ({
1474     vec_add1 (tunnels, t);
1475   }));
1476   /* *INDENT-ON* */
1477     }
1478   else                          /* Just show the one tunnel by name */
1479     vec_add1 (tunnels, &sm->tunnels[p[0]]);
1480
1481   if (vec_len (tunnels) == 0)
1482     vlib_cli_output (vm, "No SR tunnels configured");
1483
1484   for (i = 0; i < vec_len (tunnels); i++)
1485     {
1486       t = tunnels[i];
1487       ip6_sr_tunnel_display (vm, t);
1488     }
1489
1490   return 0;
1491 }
1492
1493 /* *INDENT-OFF* */
1494 VLIB_CLI_COMMAND (show_sr_tunnel_command, static) = {
1495     .path = "show sr tunnel",
1496     .short_help = "show sr tunnel [name <sr-tunnel-name>]",
1497     .function = show_sr_tunnel_fn,
1498 };
1499 /* *INDENT-ON* */
1500
1501 /**
1502  * @brief Add or Delete a Segment Routing policy
1503  *
1504  * @param a ip6_sr_add_del_policy_args_t *
1505  *
1506  * @return retval int
1507  */
1508 int
1509 ip6_sr_add_del_policy (ip6_sr_add_del_policy_args_t * a)
1510 {
1511   ip6_sr_main_t *sm = &sr_main;
1512   uword *p;
1513   ip6_sr_tunnel_t *t = 0;
1514   ip6_sr_policy_t *policy;
1515   u32 *tunnel_indices = 0;
1516   int i;
1517
1518
1519
1520   if (a->is_del)
1521     {
1522       p = hash_get_mem (sm->policy_index_by_policy_name, a->name);
1523       if (!p)
1524         return -6;              /* policy name not found */
1525
1526       policy = pool_elt_at_index (sm->policies, p[0]);
1527
1528       vec_foreach_index (i, policy->tunnel_indices)
1529       {
1530         t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[i]);
1531         t->policy_index = ~0;
1532       }
1533       hash_unset_mem (sm->policy_index_by_policy_name, a->name);
1534       pool_put (sm->policies, policy);
1535       return 0;
1536     }
1537
1538
1539   if (!vec_len (a->tunnel_names))
1540     return -3;                  /*tunnel name is required case */
1541
1542   vec_reset_length (tunnel_indices);
1543   /* Check tunnel names, add tunnel_index to policy */
1544   for (i = 0; i < vec_len (a->tunnel_names); i++)
1545     {
1546       p = hash_get_mem (sm->tunnel_index_by_name, a->tunnel_names[i]);
1547       if (!p)
1548         return -4;              /* tunnel name not found case */
1549
1550       t = pool_elt_at_index (sm->tunnels, p[0]);
1551       /*
1552          No need to check t==0. -3 condition above ensures name
1553        */
1554       if (t->policy_index != ~0)
1555         return -5;              /* tunnel name already associated with a policy */
1556
1557       /* Add to tunnel indicies */
1558       vec_add1 (tunnel_indices, p[0]);
1559     }
1560
1561   /* Add policy to ip6_sr_main_t */
1562   pool_get (sm->policies, policy);
1563   policy->name = a->name;
1564   policy->tunnel_indices = tunnel_indices;
1565   hash_set_mem (sm->policy_index_by_policy_name, policy->name,
1566                 policy - sm->policies);
1567
1568   /* Yes, this could be construed as overkill but the last thing you should do is set
1569      the policy_index on the tunnel after everything is set in ip6_sr_main_t.
1570      If this is deemed overly cautious, could set this in the vec_len(tunnel_names) loop.
1571    */
1572   for (i = 0; i < vec_len (policy->tunnel_indices); i++)
1573     {
1574       t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[i]);
1575       t->policy_index = policy - sm->policies;
1576     }
1577
1578   return 0;
1579 }
1580
1581 /**
1582  * @brief CLI Parser for Add or Delete a Segment Routing policy
1583  *
1584  * @param vm vlib_main_t *
1585  * @param input unformat_input_t *
1586  * @param cmd vlib_cli_command_t *
1587  *
1588  * @return error clib_error_t *
1589  */
1590 static clib_error_t *
1591 sr_add_del_policy_command_fn (vlib_main_t * vm,
1592                               unformat_input_t * input,
1593                               vlib_cli_command_t * cmd)
1594 {
1595   int is_del = 0;
1596   u8 **tunnel_names = 0;
1597   u8 *tunnel_name = 0;
1598   u8 *name = 0;
1599   ip6_sr_add_del_policy_args_t _a, *a = &_a;
1600   int rv;
1601
1602   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1603     {
1604       if (unformat (input, "del"))
1605         is_del = 1;
1606       else if (unformat (input, "name %s", &name))
1607         ;
1608       else if (unformat (input, "tunnel %s", &tunnel_name))
1609         {
1610           if (tunnel_name)
1611             {
1612               vec_add1 (tunnel_names, tunnel_name);
1613               tunnel_name = 0;
1614             }
1615         }
1616       else
1617         break;
1618     }
1619
1620   if (!name)
1621     return clib_error_return (0, "name of SR policy required");
1622
1623
1624   memset (a, 0, sizeof (*a));
1625
1626   a->is_del = is_del;
1627   a->name = name;
1628   a->tunnel_names = tunnel_names;
1629
1630   rv = ip6_sr_add_del_policy (a);
1631
1632   vec_free (tunnel_names);
1633
1634   switch (rv)
1635     {
1636     case 0:
1637       break;
1638
1639     case -3:
1640       return clib_error_return (0,
1641                                 "tunnel name to associate to SR policy is required");
1642
1643     case -4:
1644       return clib_error_return (0, "tunnel name not found");
1645
1646     case -5:
1647       return clib_error_return (0, "tunnel already associated with policy");
1648
1649     case -6:
1650       return clib_error_return (0, "policy name %s not found", name);
1651
1652     case -7:
1653       return clib_error_return (0, "TODO: deleting policy name %s", name);
1654
1655     default:
1656       return clib_error_return (0, "BUG: ip6_sr_add_del_policy returns %d",
1657                                 rv);
1658
1659     }
1660   return 0;
1661 }
1662
1663 /* *INDENT-OFF* */
1664 VLIB_CLI_COMMAND (sr_policy_command, static) = {
1665     .path = "sr policy",
1666     .short_help =
1667     "sr policy [del] name <policy-name> tunnel <sr-tunnel-name> [tunnel <sr-tunnel-name>]*",
1668     .function = sr_add_del_policy_command_fn,
1669 };
1670 /* *INDENT-ON* */
1671
1672 /**
1673  * @brief CLI Parser for Displaying Segment Routing policy
1674  *
1675  * @param vm vlib_main_t *
1676  * @param input unformat_input_t *
1677  * @param cmd vlib_cli_command_t *
1678  *
1679  * @return error clib_error_t *
1680  */
1681 static clib_error_t *
1682 show_sr_policy_fn (vlib_main_t * vm,
1683                    unformat_input_t * input, vlib_cli_command_t * cmd)
1684 {
1685   static ip6_sr_policy_t **policies;
1686   ip6_sr_policy_t *policy;
1687   ip6_sr_tunnel_t *t;
1688   ip6_sr_main_t *sm = &sr_main;
1689   int i, j;
1690   uword *p = 0;
1691   u8 *name = 0;
1692
1693   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1694     {
1695       if (unformat (input, "name %s", &name))
1696         {
1697           p = hash_get_mem (sm->policy_index_by_policy_name, name);
1698           if (!p)
1699             vlib_cli_output (vm,
1700                              "policy with name %s not found. Showing all.",
1701                              name);
1702         }
1703       else
1704         break;
1705     }
1706
1707   vec_reset_length (policies);
1708
1709   if (!p)                       /* Either name parm not passed or no policy with that name found, show all */
1710     {
1711       /* *INDENT-OFF* */
1712   pool_foreach (policy, sm->policies,
1713   ({
1714     vec_add1 (policies, policy);
1715   }));
1716   /* *INDENT-ON* */
1717     }
1718   else                          /* Just show the one policy by name and a summary of tunnel names */
1719     {
1720       policy = pool_elt_at_index (sm->policies, p[0]);
1721       vec_add1 (policies, policy);
1722     }
1723
1724   if (vec_len (policies) == 0)
1725     vlib_cli_output (vm, "No SR policies configured");
1726
1727   for (i = 0; i < vec_len (policies); i++)
1728     {
1729       policy = policies[i];
1730
1731       if (policy->name)
1732         vlib_cli_output (vm, "SR policy name: %s", (char *) policy->name);
1733       for (j = 0; j < vec_len (policy->tunnel_indices); j++)
1734         {
1735           t = pool_elt_at_index (sm->tunnels, policy->tunnel_indices[j]);
1736           ip6_sr_tunnel_display (vm, t);
1737         }
1738     }
1739
1740   return 0;
1741
1742 }
1743
1744 /* *INDENT-OFF* */
1745 VLIB_CLI_COMMAND (show_sr_policy_command, static) = {
1746     .path = "show sr policy",
1747     .short_help = "show sr policy [name <sr-policy-name>]",
1748     .function = show_sr_policy_fn,
1749 };
1750 /* *INDENT-ON* */
1751
1752 /**
1753  * @brief Add or Delete a mapping of IP6 multicast address
1754  * to Segment Routing policy.
1755  *
1756  * @param a ip6_sr_add_del_multicastmap_args_t *
1757  *
1758  * @return retval int
1759  */
1760 int
1761 ip6_sr_add_del_multicastmap (ip6_sr_add_del_multicastmap_args_t * a)
1762 {
1763   uword *p;
1764   ip6_sr_tunnel_t *t;
1765   ip6_sr_main_t *sm = &sr_main;
1766   ip6_sr_policy_t *pt;
1767
1768   if (a->is_del)
1769     {
1770       /* clean up the adjacency */
1771       p =
1772         hash_get_mem (sm->policy_index_by_multicast_address,
1773                       a->multicast_address);
1774     }
1775   else
1776     {
1777       /* Get our policy by policy_name */
1778       p = hash_get_mem (sm->policy_index_by_policy_name, a->policy_name);
1779
1780     }
1781   if (!p)
1782     return -1;
1783
1784   pt = pool_elt_at_index (sm->policies, p[0]);
1785
1786   /*
1787      Get the first tunnel associated with policy populate the fib adjacency.
1788      From there, since this tunnel will have it's policy_index != ~0 it will
1789      be the trigger in the dual_loop to pull up the policy and make a copy-rewrite
1790      for each tunnel in the policy
1791    */
1792
1793   t = pool_elt_at_index (sm->tunnels, pt->tunnel_indices[0]);
1794
1795   /*
1796    * Stick the tunnel index into the rewrite header.
1797    *
1798    * Unfortunately, inserting an SR header according to the various
1799    * RFC's requires parsing through the ip6 header, perhaps consing a
1800    * buffer onto the head of the vlib_buffer_t, etc. We don't use the
1801    * normal reverse bcopy rewrite code.
1802    *
1803    * We don't handle ugly RFC-related cases yet, but I'm sure PL will complain
1804    * at some point...
1805    */
1806   dpo_id_t dpo = DPO_INVALID;
1807
1808   dpo_set (&dpo, sr_dpo_type, DPO_PROTO_IP6, t - sm->tunnels);
1809
1810   /* Construct a FIB entry for multicast using the rx/tx fib from the first tunnel */
1811   fib_prefix_t pfx = {
1812     .fp_proto = FIB_PROTOCOL_IP6,
1813     .fp_len = 128,
1814     .fp_addr = {
1815                 .ip6 = *a->multicast_address,
1816                 }
1817   };
1818   fib_table_entry_special_dpo_add (t->rx_fib_index,
1819                                    &pfx,
1820                                    FIB_SOURCE_SR,
1821                                    FIB_ENTRY_FLAG_EXCLUSIVE, &dpo);
1822   dpo_reset (&dpo);
1823
1824   u8 *mcast_copy = 0;
1825   mcast_copy = vec_new (ip6_address_t, 1);
1826   memcpy (mcast_copy, a->multicast_address, sizeof (ip6_address_t));
1827
1828   if (a->is_del)
1829     {
1830       hash_unset_mem (sm->policy_index_by_multicast_address, mcast_copy);
1831       vec_free (mcast_copy);
1832       return 0;
1833     }
1834   /* else */
1835
1836   hash_set_mem (sm->policy_index_by_multicast_address, mcast_copy,
1837                 pt - sm->policies);
1838
1839
1840   return 0;
1841 }
1842
1843 /**
1844  * @brief CLI Parser for Adding or Delete a mapping of IP6 multicast address
1845  * to Segment Routing policy.
1846  *
1847  * @param vm vlib_main_t *
1848  * @param input unformat_input_t *
1849  * @param cmd vlib_cli_command_t *
1850  *
1851  * @return error clib_error_t *
1852  */
1853 static clib_error_t *
1854 sr_add_del_multicast_map_command_fn (vlib_main_t * vm,
1855                                      unformat_input_t * input,
1856                                      vlib_cli_command_t * cmd)
1857 {
1858   int is_del = 0;
1859   ip6_address_t multicast_address;
1860   u8 *policy_name = 0;
1861   int multicast_address_set = 0;
1862   ip6_sr_add_del_multicastmap_args_t _a, *a = &_a;
1863   int rv;
1864
1865   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1866     {
1867       if (unformat (input, "del"))
1868         is_del = 1;
1869       else
1870         if (unformat
1871             (input, "address %U", unformat_ip6_address, &multicast_address))
1872         multicast_address_set = 1;
1873       else if (unformat (input, "sr-policy %s", &policy_name))
1874         ;
1875       else
1876         break;
1877     }
1878
1879   if (!is_del && !policy_name)
1880     return clib_error_return (0, "name of sr policy required");
1881
1882   if (!multicast_address_set)
1883     return clib_error_return (0, "multicast address required");
1884
1885   memset (a, 0, sizeof (*a));
1886
1887   a->is_del = is_del;
1888   a->multicast_address = &multicast_address;
1889   a->policy_name = policy_name;
1890
1891 #if DPDK > 0                    /*Cannot call replicate or configure multicast map yet without DPDK */
1892   rv = ip6_sr_add_del_multicastmap (a);
1893 #else
1894   return clib_error_return (0,
1895                             "cannot use multicast replicate spray case without DPDK installed");
1896 #endif /* DPDK */
1897
1898   switch (rv)
1899     {
1900     case 0:
1901       break;
1902     case -1:
1903       return clib_error_return (0, "no policy with name: %s", policy_name);
1904
1905     case -2:
1906       return clib_error_return (0, "multicast map someting ");
1907
1908     case -3:
1909       return clib_error_return (0,
1910                                 "tunnel name to associate to SR policy is required");
1911
1912     case -7:
1913       return clib_error_return (0, "TODO: deleting policy name %s",
1914                                 policy_name);
1915
1916     default:
1917       return clib_error_return (0, "BUG: ip6_sr_add_del_policy returns %d",
1918                                 rv);
1919
1920     }
1921   return 0;
1922
1923 }
1924
1925
1926 /* *INDENT-OFF* */
1927 VLIB_CLI_COMMAND (sr_multicast_map_command, static) = {
1928     .path = "sr multicast-map",
1929     .short_help =
1930     "sr multicast-map address <multicast-ip6-address> sr-policy <sr-policy-name> [del]",
1931     .function = sr_add_del_multicast_map_command_fn,
1932 };
1933 /* *INDENT-ON* */
1934
1935 /**
1936  * @brief CLI Parser for Displaying a mapping of IP6 multicast address
1937  * to Segment Routing policy.
1938  *
1939  * @param vm vlib_main_t *
1940  * @param input unformat_input_t *
1941  * @param cmd vlib_cli_command_t *
1942  *
1943  * @return error clib_error_t *
1944  */
1945 static clib_error_t *
1946 show_sr_multicast_map_fn (vlib_main_t * vm,
1947                           unformat_input_t * input, vlib_cli_command_t * cmd)
1948 {
1949   ip6_sr_main_t *sm = &sr_main;
1950   u8 *key = 0;
1951   u32 value;
1952   ip6_address_t multicast_address;
1953   ip6_sr_policy_t *pt;
1954
1955   /* pull all entries from the hash table into vector for display */
1956
1957   /* *INDENT-OFF* */
1958   hash_foreach_mem (key, value, sm->policy_index_by_multicast_address,
1959   ({
1960     if (!key)
1961         vlib_cli_output (vm, "no multicast maps configured");
1962     else
1963       {
1964         multicast_address = *((ip6_address_t *)key);
1965         pt = pool_elt_at_index (sm->policies, value);
1966         if (pt)
1967           {
1968             vlib_cli_output (vm, "address: %U policy: %s",
1969                              format_ip6_address, &multicast_address,
1970                              pt->name);
1971           }
1972         else
1973           vlib_cli_output (vm, "BUG: policy not found for address: %U with policy index %d",
1974                              format_ip6_address, &multicast_address,
1975                              value);
1976
1977       }
1978
1979   }));
1980   /* *INDENT-ON* */
1981
1982   return 0;
1983 }
1984
1985 /* *INDENT-OFF* */
1986 VLIB_CLI_COMMAND (show_sr_multicast_map_command, static) = {
1987     .path = "show sr multicast-map",
1988     .short_help = "show sr multicast-map",
1989     .function = show_sr_multicast_map_fn,
1990 };
1991 /* *INDENT-ON* */
1992
1993
1994 #define foreach_sr_fix_dst_addr_next            \
1995 _(DROP, "error-drop")
1996
1997 /**
1998  * @brief Struct for valid next-nodes for SR fix destination address node
1999  */
2000 typedef enum
2001 {
2002 #define _(s,n) SR_FIX_DST_ADDR_NEXT_##s,
2003   foreach_sr_fix_dst_addr_next
2004 #undef _
2005     SR_FIX_DST_ADDR_N_NEXT,
2006 } sr_fix_dst_addr_next_t;
2007
2008 /**
2009  * @brief Error strings for SR Fix Destination rewrite
2010  */
2011 static char *sr_fix_dst_error_strings[] = {
2012 #define sr_fix_dst_error(n,s) s,
2013 #include "sr_fix_dst_error.def"
2014 #undef sr_fix_dst_error
2015 };
2016
2017 /**
2018  * @brief Struct for errors for SR Fix Destination rewrite
2019  */
2020 typedef enum
2021 {
2022 #define sr_fix_dst_error(n,s) SR_FIX_DST_ERROR_##n,
2023 #include "sr_fix_dst_error.def"
2024 #undef sr_fix_dst_error
2025   SR_FIX_DST_N_ERROR,
2026 } sr_fix_dst_error_t;
2027
2028 /**
2029  * @brief Information for fix address trace
2030  */
2031 typedef struct
2032 {
2033   ip6_address_t src, dst;
2034   u32 next_index;
2035   u32 adj_index;
2036   u8 sr[256];
2037 } sr_fix_addr_trace_t;
2038
2039 /**
2040  * @brief Formatter for fix address trace
2041  */
2042 u8 *
2043 format_sr_fix_addr_trace (u8 * s, va_list * args)
2044 {
2045   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
2046   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
2047   sr_fix_addr_trace_t *t = va_arg (*args, sr_fix_addr_trace_t *);
2048   vnet_hw_interface_t *hi = 0;
2049   ip_adjacency_t *adj;
2050   ip6_main_t *im = &ip6_main;
2051   ip_lookup_main_t *lm = &im->lookup_main;
2052   vnet_main_t *vnm = vnet_get_main ();
2053
2054   if (t->adj_index != ~0)
2055     {
2056       adj = ip_get_adjacency (lm, t->adj_index);
2057       hi = vnet_get_sup_hw_interface (vnm, adj->rewrite_header.sw_if_index);
2058     }
2059
2060   s = format (s, "SR-FIX_ADDR: next %s ip6 src %U dst %U\n",
2061               (t->next_index == SR_FIX_DST_ADDR_NEXT_DROP)
2062               ? "drop" : "output",
2063               format_ip6_address, &t->src, format_ip6_address, &t->dst);
2064   if (t->next_index != SR_FIX_DST_ADDR_NEXT_DROP)
2065     {
2066       s =
2067         format (s, "%U\n", format_ip6_sr_header, t->sr, 1 /* print_hmac */ );
2068       s =
2069         format (s, "   output via %s",
2070                 hi ? (char *) (hi->name) : "Invalid adj");
2071     }
2072   return s;
2073 }
2074
2075 /**
2076  * @brief Fix SR destination address - dual-loop
2077  *
2078  * @node sr-fix-dst-addr
2079  * @param vm vlib_main_t *
2080  * @param node vlib_node_runtime_t *
2081  * @param from_frame vlib_frame_t *
2082  *
2083  * @return from_frame->n_vectors uword
2084  */
2085 static uword
2086 sr_fix_dst_addr (vlib_main_t * vm,
2087                  vlib_node_runtime_t * node, vlib_frame_t * from_frame)
2088 {
2089   u32 n_left_from, next_index, *from, *to_next;
2090   ip6_main_t *im = &ip6_main;
2091   ip_lookup_main_t *lm = &im->lookup_main;
2092
2093   from = vlib_frame_vector_args (from_frame);
2094   n_left_from = from_frame->n_vectors;
2095
2096   next_index = node->cached_next_index;
2097
2098   while (n_left_from > 0)
2099     {
2100       u32 n_left_to_next;
2101
2102       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
2103
2104 #if 0
2105       while (0 && n_left_from >= 4 && n_left_to_next >= 2)
2106         {
2107           u32 bi0, bi1;
2108           __attribute__ ((unused)) vlib_buffer_t *b0, *b1;
2109           u32 next0 = SR_FIX_DST_ADDR_NEXT_DROP;
2110           u32 next1 = SR_FIX_DST_ADDR_NEXT_DROP;
2111
2112           /* Prefetch next iteration. */
2113           {
2114             vlib_buffer_t *p2, *p3;
2115
2116             p2 = vlib_get_buffer (vm, from[2]);
2117             p3 = vlib_get_buffer (vm, from[3]);
2118
2119             vlib_prefetch_buffer_header (p2, LOAD);
2120             vlib_prefetch_buffer_header (p3, LOAD);
2121           }
2122
2123           bi0 = from[0];
2124           bi1 = from[1];
2125           to_next[0] = bi0;
2126           to_next[1] = bi1;
2127           from += 2;
2128           to_next += 2;
2129           n_left_to_next -= 2;
2130           n_left_from -= 2;
2131
2132           b0 = vlib_get_buffer (vm, bi0);
2133           b1 = vlib_get_buffer (vm, bi1);
2134
2135
2136           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
2137                                            to_next, n_left_to_next,
2138                                            bi0, bi1, next0, next1);
2139         }
2140 #endif
2141
2142       while (n_left_from > 0 && n_left_to_next > 0)
2143         {
2144           u32 bi0;
2145           vlib_buffer_t *b0;
2146           ip6_header_t *ip0;
2147           ip_adjacency_t *adj0;
2148           ip6_sr_header_t *sr0;
2149           u32 next0 = SR_FIX_DST_ADDR_NEXT_DROP;
2150           ip6_address_t *new_dst0;
2151           ethernet_header_t *eh0;
2152
2153           bi0 = from[0];
2154           to_next[0] = bi0;
2155           from += 1;
2156           to_next += 1;
2157           n_left_from -= 1;
2158           n_left_to_next -= 1;
2159
2160           b0 = vlib_get_buffer (vm, bi0);
2161
2162           adj0 =
2163             ip_get_adjacency (lm, vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
2164           next0 = adj0->mcast_group_index;
2165
2166           /* We should be pointing at an Ethernet header... */
2167           eh0 = vlib_buffer_get_current (b0);
2168           ip0 = (ip6_header_t *) (eh0 + 1);
2169           sr0 = (ip6_sr_header_t *) (ip0 + 1);
2170
2171           /* We'd better find an SR header... */
2172           if (PREDICT_FALSE (ip0->protocol != IPPROTO_IPV6_ROUTE))
2173             {
2174               b0->error = node->errors[SR_FIX_DST_ERROR_NO_SR_HEADER];
2175               goto do_trace0;
2176             }
2177           else
2178             {
2179               /*
2180                * We get here from sr_rewrite or sr_local, with
2181                * sr->segments_left pointing at the (copy of the original) dst
2182                * address. Use it, then increment sr0->segments_left.
2183                */
2184
2185               /* Out of segments? Turf the packet */
2186               if (PREDICT_FALSE (sr0->segments_left == 0))
2187                 {
2188                   b0->error = node->errors[SR_FIX_DST_ERROR_NO_MORE_SEGMENTS];
2189                   goto do_trace0;
2190                 }
2191
2192               /*
2193                * Rewrite the packet with the original dst address
2194                * We assume that the last segment (in processing order) contains
2195                * the original dst address. The list is reversed, so sr0->segments
2196                * contains the original dst address.
2197                */
2198               new_dst0 = sr0->segments;
2199               ip0->dst_address.as_u64[0] = new_dst0->as_u64[0];
2200               ip0->dst_address.as_u64[1] = new_dst0->as_u64[1];
2201             }
2202
2203         do_trace0:
2204
2205           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
2206             {
2207               sr_fix_addr_trace_t *t = vlib_add_trace (vm, node,
2208                                                        b0, sizeof (*t));
2209               t->next_index = next0;
2210               t->adj_index = ~0;
2211
2212               if (next0 != SR_FIX_DST_ADDR_NEXT_DROP)
2213                 {
2214                   t->adj_index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
2215                   clib_memcpy (t->src.as_u8, ip0->src_address.as_u8,
2216                                sizeof (t->src.as_u8));
2217                   clib_memcpy (t->dst.as_u8, ip0->dst_address.as_u8,
2218                                sizeof (t->dst.as_u8));
2219                   clib_memcpy (t->sr, sr0, sizeof (t->sr));
2220                 }
2221             }
2222
2223           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2224                                            to_next, n_left_to_next,
2225                                            bi0, next0);
2226         }
2227
2228       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2229     }
2230   return from_frame->n_vectors;
2231 }
2232
2233
2234 /* *INDENT-OFF* */
2235 VLIB_REGISTER_NODE (sr_fix_dst_addr_node) = {
2236   .function = sr_fix_dst_addr,
2237   .name = "sr-fix-dst-addr",
2238   /* Takes a vector of packets. */
2239   .vector_size = sizeof (u32),
2240   .format_trace = format_sr_fix_addr_trace,
2241   .format_buffer = format_ip6_sr_header_with_length,
2242
2243   .runtime_data_bytes = 0,
2244
2245   .n_errors = SR_FIX_DST_N_ERROR,
2246   .error_strings = sr_fix_dst_error_strings,
2247
2248   .n_next_nodes = SR_FIX_DST_ADDR_N_NEXT,
2249   .next_nodes = {
2250 #define _(s,n) [SR_FIX_DST_ADDR_NEXT_##s] = n,
2251     foreach_sr_fix_dst_addr_next
2252 #undef _
2253   },
2254 };
2255
2256 VLIB_NODE_FUNCTION_MULTIARCH (sr_fix_dst_addr_node, sr_fix_dst_addr)
2257 /* *INDENT-ON* */
2258
2259 static clib_error_t *
2260 sr_init (vlib_main_t * vm)
2261 {
2262   ip6_sr_main_t *sm = &sr_main;
2263   clib_error_t *error = 0;
2264   vlib_node_t *ip6_lookup_node, *ip6_rewrite_node;
2265
2266   if ((error = vlib_call_init_function (vm, ip_main_init)))
2267     return error;
2268
2269   if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
2270     return error;
2271
2272   sm->vlib_main = vm;
2273   sm->vnet_main = vnet_get_main ();
2274
2275   vec_validate (sm->hmac_keys, 0);
2276   sm->hmac_keys[0].shared_secret = (u8 *) 0xdeadbeef;
2277
2278   sm->tunnel_index_by_key =
2279     hash_create_mem (0, sizeof (ip6_sr_tunnel_key_t), sizeof (uword));
2280
2281   sm->tunnel_index_by_name = hash_create_string (0, sizeof (uword));
2282
2283   sm->policy_index_by_policy_name = hash_create_string (0, sizeof (uword));
2284
2285   sm->policy_index_by_multicast_address =
2286     hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword));
2287
2288   sm->hmac_key_by_shared_secret = hash_create_string (0, sizeof (uword));
2289
2290   ip6_register_protocol (IPPROTO_IPV6_ROUTE, sr_local_node.index);
2291
2292   ip6_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip6-lookup");
2293   ASSERT (ip6_lookup_node);
2294
2295   ip6_rewrite_node = vlib_get_node_by_name (vm, (u8 *) "ip6-rewrite");
2296   ASSERT (ip6_rewrite_node);
2297
2298 #if DPDK > 0                    /* Cannot run replicate without DPDK */
2299   /* Add a disposition to sr_replicate for the sr multicast replicate node */
2300   sm->ip6_lookup_sr_replicate_index =
2301     vlib_node_add_next (vm, ip6_lookup_node->index, sr_replicate_node.index);
2302 #endif /* DPDK */
2303
2304   /* Add a disposition to ip6_rewrite for the sr dst address hack node */
2305   sm->ip6_rewrite_sr_next_index =
2306     vlib_node_add_next (vm, ip6_rewrite_node->index,
2307                         sr_fix_dst_addr_node.index);
2308
2309   OpenSSL_add_all_digests ();
2310
2311   sm->md = (void *) EVP_get_digestbyname ("sha1");
2312   sm->hmac_ctx = clib_mem_alloc (sizeof (HMAC_CTX));
2313
2314   sr_dpo_type = dpo_register_new_type (&sr_vft, sr_nodes);
2315
2316   return error;
2317 }
2318
2319 VLIB_INIT_FUNCTION (sr_init);
2320
2321 /**
2322  * @brief Definition of next-nodes for SR local
2323  */
2324 #define foreach_sr_local_next                   \
2325   _ (ERROR, "error-drop")                       \
2326   _ (IP6_LOOKUP, "ip6-lookup")
2327
2328 /**
2329  * @brief Struct for definition of next-nodes for SR local
2330  */
2331 typedef enum
2332 {
2333 #define _(s,n) SR_LOCAL_NEXT_##s,
2334   foreach_sr_local_next
2335 #undef _
2336     SR_LOCAL_N_NEXT,
2337 } sr_local_next_t;
2338
2339 /**
2340  * @brief Struct for packet trace of SR local
2341  */
2342 typedef struct
2343 {
2344   u8 next_index;
2345   u8 sr_valid;
2346   ip6_address_t src, dst;
2347   u16 length;
2348   u8 sr[256];
2349 } sr_local_trace_t;
2350
2351 /**
2352  * @brief Definition of SR local error-strings
2353  */
2354 static char *sr_local_error_strings[] = {
2355 #define sr_error(n,s) s,
2356 #include "sr_error.def"
2357 #undef sr_error
2358 };
2359
2360 /**
2361  * @brief Struct for definition of SR local error-strings
2362  */
2363 typedef enum
2364 {
2365 #define sr_error(n,s) SR_LOCAL_ERROR_##n,
2366 #include "sr_error.def"
2367 #undef sr_error
2368   SR_LOCAL_N_ERROR,
2369 } sr_local_error_t;
2370
2371 /**
2372  * @brief Format SR local trace
2373  *
2374  * @param s u8 *
2375  * @param args va_list *
2376  *
2377  * @return s u8 *
2378  */
2379 u8 *
2380 format_sr_local_trace (u8 * s, va_list * args)
2381 {
2382   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
2383   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
2384   sr_local_trace_t *t = va_arg (*args, sr_local_trace_t *);
2385
2386   s = format (s, "SR-LOCAL: src %U dst %U len %u next_index %d",
2387               format_ip6_address, &t->src,
2388               format_ip6_address, &t->dst, t->length, t->next_index);
2389   if (t->sr_valid)
2390     s =
2391       format (s, "\n  %U", format_ip6_sr_header, t->sr, 1 /* print_hmac */ );
2392   else
2393     s = format (s, "\n  popped SR header");
2394
2395   return s;
2396 }
2397
2398
2399 /* $$$$ fixme: smp, don't copy data, cache input, output (maybe) */
2400 /**
2401  * @brief Validate the SR HMAC
2402  *
2403  * @param sm ip6_sr_main_t *
2404  * @param ip ip6_header_t *
2405  * @param sr ip6_sr_header_t *
2406  *
2407  * @return retval int
2408  */
2409 static int
2410 sr_validate_hmac (ip6_sr_main_t * sm, ip6_header_t * ip, ip6_sr_header_t * sr)
2411 {
2412   u32 key_index;
2413   static u8 *keybuf;
2414   u8 *copy_target;
2415   int first_segment;
2416   ip6_address_t *addrp;
2417   int i;
2418   ip6_sr_hmac_key_t *hmac_key;
2419   static u8 *signature;
2420   u32 sig_len;
2421
2422   key_index = sr->hmac_key;
2423
2424   /* No signature? Pass... */
2425   if (key_index == 0)
2426     return 0;
2427
2428   /* We don't know about this key? Fail... */
2429   if (key_index >= vec_len (sm->hmac_keys))
2430     return 1;
2431
2432   vec_validate (signature, SHA256_DIGEST_LENGTH - 1);
2433
2434   hmac_key = sm->hmac_keys + key_index;
2435
2436   vec_reset_length (keybuf);
2437
2438   /* pkt ip6 src address */
2439   vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
2440   clib_memcpy (copy_target, ip->src_address.as_u8, sizeof (ip6_address_t));
2441
2442   /* last segment */
2443   vec_add2 (keybuf, copy_target, 1);
2444   copy_target[0] = sr->first_segment;
2445
2446   /* octet w/ bit 0 = "clean" flag */
2447   vec_add2 (keybuf, copy_target, 1);
2448   copy_target[0]
2449     = (sr->flags & clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))
2450     ? 0x80 : 0;
2451
2452   /* hmac key id */
2453   vec_add2 (keybuf, copy_target, 1);
2454   copy_target[0] = sr->hmac_key;
2455
2456   first_segment = sr->first_segment;
2457
2458   addrp = sr->segments;
2459
2460   /* segments */
2461   for (i = 0; i <= first_segment; i++)
2462     {
2463       vec_add2 (keybuf, copy_target, sizeof (ip6_address_t));
2464       clib_memcpy (copy_target, addrp->as_u8, sizeof (ip6_address_t));
2465       addrp++;
2466     }
2467
2468   if (sm->is_debug)
2469     clib_warning ("verify key index %d keybuf: %U", key_index,
2470                   format_hex_bytes, keybuf, vec_len (keybuf));
2471
2472   /* shared secret */
2473
2474   /* SHA1 is shorter than SHA-256 */
2475   memset (signature, 0, vec_len (signature));
2476
2477   HMAC_CTX_init (sm->hmac_ctx);
2478   if (!HMAC_Init (sm->hmac_ctx, hmac_key->shared_secret,
2479                   vec_len (hmac_key->shared_secret), sm->md))
2480     clib_warning ("barf1");
2481   if (!HMAC_Update (sm->hmac_ctx, keybuf, vec_len (keybuf)))
2482     clib_warning ("barf2");
2483   if (!HMAC_Final (sm->hmac_ctx, signature, &sig_len))
2484     clib_warning ("barf3");
2485   HMAC_CTX_cleanup (sm->hmac_ctx);
2486
2487   if (sm->is_debug)
2488     clib_warning ("computed signature len %d, value %U", sig_len,
2489                   format_hex_bytes, signature, vec_len (signature));
2490
2491   /* Point at the SHA signature in the packet */
2492   addrp++;
2493   if (sm->is_debug)
2494     clib_warning ("read signature %U", format_hex_bytes, addrp,
2495                   SHA256_DIGEST_LENGTH);
2496
2497   return memcmp (signature, addrp, SHA256_DIGEST_LENGTH);
2498 }
2499
2500 /**
2501  * @brief SR local node
2502  * @node sr-local
2503  *
2504  * @param vm vlib_main_t *
2505  * @param node vlib_node_runtime_t *
2506  * @param from_frame vlib_frame_t *
2507  *
2508  * @return from_frame->n_vectors uword
2509  */
2510 static uword
2511 sr_local (vlib_main_t * vm,
2512           vlib_node_runtime_t * node, vlib_frame_t * from_frame)
2513 {
2514   u32 n_left_from, next_index, *from, *to_next;
2515   ip6_sr_main_t *sm = &sr_main;
2516   u32 (*sr_local_cb) (vlib_main_t *, vlib_node_runtime_t *,
2517                       vlib_buffer_t *, ip6_header_t *, ip6_sr_header_t *);
2518   sr_local_cb = sm->sr_local_cb;
2519
2520   from = vlib_frame_vector_args (from_frame);
2521   n_left_from = from_frame->n_vectors;
2522
2523   next_index = node->cached_next_index;
2524
2525   while (n_left_from > 0)
2526     {
2527       u32 n_left_to_next;
2528
2529       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
2530
2531       while (n_left_from >= 4 && n_left_to_next >= 2)
2532         {
2533           u32 bi0, bi1;
2534           vlib_buffer_t *b0, *b1;
2535           ip6_header_t *ip0, *ip1;
2536           ip6_sr_header_t *sr0, *sr1;
2537           ip6_address_t *new_dst0, *new_dst1;
2538           u32 next0 = SR_LOCAL_NEXT_IP6_LOOKUP;
2539           u32 next1 = SR_LOCAL_NEXT_IP6_LOOKUP;
2540
2541           /* Prefetch next iteration. */
2542           {
2543             vlib_buffer_t *p2, *p3;
2544
2545             p2 = vlib_get_buffer (vm, from[2]);
2546             p3 = vlib_get_buffer (vm, from[3]);
2547
2548             vlib_prefetch_buffer_header (p2, LOAD);
2549             vlib_prefetch_buffer_header (p3, LOAD);
2550
2551             CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
2552             CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
2553           }
2554
2555           bi0 = from[0];
2556           bi1 = from[1];
2557           to_next[0] = bi0;
2558           to_next[1] = bi1;
2559           from += 2;
2560           to_next += 2;
2561           n_left_to_next -= 2;
2562           n_left_from -= 2;
2563
2564
2565           b0 = vlib_get_buffer (vm, bi0);
2566           ip0 = vlib_buffer_get_current (b0);
2567           sr0 = (ip6_sr_header_t *) (ip0 + 1);
2568           if (PREDICT_FALSE
2569               (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2570             {
2571               ip6_hop_by_hop_ext_t *ext_hdr =
2572                 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
2573               sr0 =
2574                 (ip6_sr_header_t *) ip6_ext_next_header ((ip6_ext_header_t *)
2575                                                          ext_hdr);
2576             }
2577
2578           if (PREDICT_FALSE (sr0->type != ROUTING_HEADER_TYPE_SR))
2579             {
2580               next0 = SR_LOCAL_NEXT_ERROR;
2581               b0->error =
2582                 node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE];
2583               goto do_trace0;
2584             }
2585
2586           /* Out of segments? Turf the packet */
2587           if (PREDICT_FALSE (sr0->segments_left == 0))
2588             {
2589               next0 = SR_LOCAL_NEXT_ERROR;
2590               b0->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS];
2591               goto do_trace0;
2592             }
2593
2594           if (PREDICT_FALSE (sm->validate_hmac))
2595             {
2596               if (sr_validate_hmac (sm, ip0, sr0))
2597                 {
2598                   next0 = SR_LOCAL_NEXT_ERROR;
2599                   b0->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID];
2600                   goto do_trace0;
2601                 }
2602             }
2603
2604           next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) : next0;
2605
2606           /*
2607            * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx
2608            */
2609           if (PREDICT_FALSE (next0 & 0x80000000))
2610             {
2611               next0 ^= 0xFFFFFFFF;
2612               if (PREDICT_FALSE (next0 == SR_LOCAL_NEXT_ERROR))
2613                 b0->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK];
2614             }
2615           else
2616             {
2617               u32 segment_index0;
2618
2619               segment_index0 = sr0->segments_left - 1;
2620
2621               /* Rewrite the packet */
2622               new_dst0 = (ip6_address_t *) (sr0->segments + segment_index0);
2623               ip0->dst_address.as_u64[0] = new_dst0->as_u64[0];
2624               ip0->dst_address.as_u64[1] = new_dst0->as_u64[1];
2625
2626               if (PREDICT_TRUE (sr0->segments_left > 0))
2627                 sr0->segments_left -= 1;
2628             }
2629
2630           /* End of the path. Clean up the SR header, or not */
2631           if (PREDICT_FALSE
2632               (sr0->segments_left == 0 &&
2633                (sr0->flags &
2634                 clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))))
2635             {
2636               u64 *copy_dst0, *copy_src0;
2637               u16 new_l0;
2638               u32 copy_len_u64s0 = 0;
2639               int i;
2640
2641               /*
2642                * Copy the ip6 header right by the (real) length of the
2643                * sr header.
2644                */
2645               if (PREDICT_FALSE
2646                   (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2647                 {
2648                   ip6_hop_by_hop_ext_t *ext_hdr =
2649                     (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
2650                   copy_len_u64s0 =
2651                     (((ip6_ext_header_t *) ext_hdr)->n_data_u64s) + 1;
2652                   ext_hdr->next_hdr = sr0->protocol;
2653                 }
2654               else
2655                 {
2656                   ip0->protocol = sr0->protocol;
2657                 }
2658               vlib_buffer_advance (b0, (sr0->length + 1) * 8);
2659
2660               new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
2661                 (sr0->length + 1) * 8;
2662               ip0->payload_length = clib_host_to_net_u16 (new_l0);
2663
2664               copy_src0 = (u64 *) ip0;
2665               copy_dst0 = copy_src0 + (sr0->length + 1);
2666
2667               copy_dst0[4 + copy_len_u64s0] = copy_src0[4 + copy_len_u64s0];
2668               copy_dst0[3 + copy_len_u64s0] = copy_src0[3 + copy_len_u64s0];
2669               copy_dst0[2 + copy_len_u64s0] = copy_src0[2 + copy_len_u64s0];
2670               copy_dst0[1 + copy_len_u64s0] = copy_src0[1 + copy_len_u64s0];
2671               copy_dst0[0 + copy_len_u64s0] = copy_src0[0 + copy_len_u64s0];
2672
2673               for (i = copy_len_u64s0 - 1; i >= 0; i--)
2674                 {
2675                   copy_dst0[i] = copy_src0[i];
2676                 }
2677
2678               sr0 = 0;
2679             }
2680
2681         do_trace0:
2682           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
2683             {
2684               sr_local_trace_t *tr = vlib_add_trace (vm, node,
2685                                                      b0, sizeof (*tr));
2686               clib_memcpy (tr->src.as_u8, ip0->src_address.as_u8,
2687                            sizeof (tr->src.as_u8));
2688               clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
2689                            sizeof (tr->dst.as_u8));
2690               tr->length = vlib_buffer_length_in_chain (vm, b0);
2691               tr->next_index = next0;
2692               tr->sr_valid = sr0 != 0;
2693               if (tr->sr_valid)
2694                 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
2695             }
2696
2697           b1 = vlib_get_buffer (vm, bi1);
2698           ip1 = vlib_buffer_get_current (b1);
2699           sr1 = (ip6_sr_header_t *) (ip1 + 1);
2700           if (PREDICT_FALSE
2701               (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2702             {
2703
2704               ip6_hop_by_hop_ext_t *ext_hdr =
2705                 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1);
2706               sr1 =
2707                 (ip6_sr_header_t *) ip6_ext_next_header ((ip6_ext_header_t *)
2708                                                          ext_hdr);
2709             }
2710
2711           if (PREDICT_FALSE (sr1->type != ROUTING_HEADER_TYPE_SR))
2712             {
2713               next1 = SR_LOCAL_NEXT_ERROR;
2714               b1->error =
2715                 node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE];
2716               goto do_trace1;
2717             }
2718
2719           /* Out of segments? Turf the packet */
2720           if (PREDICT_FALSE (sr1->segments_left == 0))
2721             {
2722               next1 = SR_LOCAL_NEXT_ERROR;
2723               b1->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS];
2724               goto do_trace1;
2725             }
2726
2727           if (PREDICT_FALSE (sm->validate_hmac))
2728             {
2729               if (sr_validate_hmac (sm, ip1, sr1))
2730                 {
2731                   next1 = SR_LOCAL_NEXT_ERROR;
2732                   b1->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID];
2733                   goto do_trace1;
2734                 }
2735             }
2736
2737           next1 = sr_local_cb ? sr_local_cb (vm, node, b1, ip1, sr1) : next1;
2738
2739           /*
2740            * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx
2741            */
2742           if (PREDICT_FALSE (next1 & 0x80000000))
2743             {
2744               next1 ^= 0xFFFFFFFF;
2745               if (PREDICT_FALSE (next1 == SR_LOCAL_NEXT_ERROR))
2746                 b1->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK];
2747             }
2748           else
2749             {
2750               u32 segment_index1;
2751
2752               segment_index1 = sr1->segments_left - 1;
2753
2754               /* Rewrite the packet */
2755               new_dst1 = (ip6_address_t *) (sr1->segments + segment_index1);
2756               ip1->dst_address.as_u64[0] = new_dst1->as_u64[0];
2757               ip1->dst_address.as_u64[1] = new_dst1->as_u64[1];
2758
2759               if (PREDICT_TRUE (sr1->segments_left > 0))
2760                 sr1->segments_left -= 1;
2761             }
2762
2763           /* End of the path. Clean up the SR header, or not */
2764           if (PREDICT_FALSE
2765               (sr1->segments_left == 0 &&
2766                (sr1->flags &
2767                 clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))))
2768             {
2769               u64 *copy_dst1, *copy_src1;
2770               u16 new_l1;
2771               u32 copy_len_u64s1 = 0;
2772               int i;
2773
2774               /*
2775                * Copy the ip6 header right by the (real) length of the
2776                * sr header.
2777                */
2778               if (PREDICT_FALSE
2779                   (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2780                 {
2781                   ip6_hop_by_hop_ext_t *ext_hdr =
2782                     (ip6_hop_by_hop_ext_t *) ip6_next_header (ip1);
2783                   copy_len_u64s1 =
2784                     (((ip6_ext_header_t *) ext_hdr)->n_data_u64s) + 1;
2785                   ext_hdr->next_hdr = sr1->protocol;
2786                 }
2787               else
2788                 {
2789                   ip1->protocol = sr1->protocol;
2790                 }
2791               vlib_buffer_advance (b1, (sr1->length + 1) * 8);
2792
2793               new_l1 = clib_net_to_host_u16 (ip1->payload_length) -
2794                 (sr1->length + 1) * 8;
2795               ip1->payload_length = clib_host_to_net_u16 (new_l1);
2796
2797               copy_src1 = (u64 *) ip1;
2798               copy_dst1 = copy_src1 + (sr1->length + 1);
2799
2800               copy_dst1[4 + copy_len_u64s1] = copy_src1[4 + copy_len_u64s1];
2801               copy_dst1[3 + copy_len_u64s1] = copy_src1[3 + copy_len_u64s1];
2802               copy_dst1[2 + copy_len_u64s1] = copy_src1[2 + copy_len_u64s1];
2803               copy_dst1[1 + copy_len_u64s1] = copy_src1[1 + copy_len_u64s1];
2804               copy_dst1[0 + copy_len_u64s1] = copy_src1[0 + copy_len_u64s1];
2805
2806               for (i = copy_len_u64s1 - 1; i >= 0; i--)
2807                 {
2808                   copy_dst1[i] = copy_src1[i];
2809                 }
2810
2811               sr1 = 0;
2812             }
2813
2814         do_trace1:
2815           if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
2816             {
2817               sr_local_trace_t *tr = vlib_add_trace (vm, node,
2818                                                      b1, sizeof (*tr));
2819               clib_memcpy (tr->src.as_u8, ip1->src_address.as_u8,
2820                            sizeof (tr->src.as_u8));
2821               clib_memcpy (tr->dst.as_u8, ip1->dst_address.as_u8,
2822                            sizeof (tr->dst.as_u8));
2823               tr->length = vlib_buffer_length_in_chain (vm, b1);
2824               tr->next_index = next1;
2825               tr->sr_valid = sr1 != 0;
2826               if (tr->sr_valid)
2827                 clib_memcpy (tr->sr, sr1, sizeof (tr->sr));
2828             }
2829
2830           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
2831                                            to_next, n_left_to_next,
2832                                            bi0, bi1, next0, next1);
2833         }
2834
2835       while (n_left_from > 0 && n_left_to_next > 0)
2836         {
2837           u32 bi0;
2838           vlib_buffer_t *b0;
2839           ip6_header_t *ip0 = 0;
2840           ip6_sr_header_t *sr0;
2841           ip6_address_t *new_dst0;
2842           u32 next0 = SR_LOCAL_NEXT_IP6_LOOKUP;
2843
2844           bi0 = from[0];
2845           to_next[0] = bi0;
2846           from += 1;
2847           to_next += 1;
2848           n_left_from -= 1;
2849           n_left_to_next -= 1;
2850
2851           b0 = vlib_get_buffer (vm, bi0);
2852           ip0 = vlib_buffer_get_current (b0);
2853           sr0 = (ip6_sr_header_t *) (ip0 + 1);
2854
2855           if (PREDICT_FALSE
2856               (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2857             {
2858               ip6_hop_by_hop_ext_t *ext_hdr =
2859                 (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
2860               sr0 =
2861                 (ip6_sr_header_t *) ip6_ext_next_header ((ip6_ext_header_t *)
2862                                                          ext_hdr);
2863             }
2864           if (PREDICT_FALSE (sr0->type != ROUTING_HEADER_TYPE_SR))
2865             {
2866               next0 = SR_LOCAL_NEXT_ERROR;
2867               b0->error =
2868                 node->errors[SR_LOCAL_ERROR_BAD_ROUTING_HEADER_TYPE];
2869               goto do_trace;
2870             }
2871
2872           /* Out of segments? Turf the packet */
2873           if (PREDICT_FALSE (sr0->segments_left == 0))
2874             {
2875               next0 = SR_LOCAL_NEXT_ERROR;
2876               b0->error = node->errors[SR_LOCAL_ERROR_NO_MORE_SEGMENTS];
2877               goto do_trace;
2878             }
2879
2880           if (PREDICT_FALSE (sm->validate_hmac))
2881             {
2882               if (sr_validate_hmac (sm, ip0, sr0))
2883                 {
2884                   next0 = SR_LOCAL_NEXT_ERROR;
2885                   b0->error = node->errors[SR_LOCAL_ERROR_HMAC_INVALID];
2886                   goto do_trace;
2887                 }
2888             }
2889
2890           next0 = sr_local_cb ? sr_local_cb (vm, node, b0, ip0, sr0) : next0;
2891
2892           /*
2893            * To suppress rewrite, return ~SR_LOCAL_NEXT_xxx
2894            */
2895           if (PREDICT_FALSE (next0 & 0x80000000))
2896             {
2897               next0 ^= 0xFFFFFFFF;
2898               if (PREDICT_FALSE (next0 == SR_LOCAL_NEXT_ERROR))
2899                 b0->error = node->errors[SR_LOCAL_ERROR_APP_CALLBACK];
2900             }
2901           else
2902             {
2903               u32 segment_index0;
2904
2905               segment_index0 = sr0->segments_left - 1;
2906
2907               /* Rewrite the packet */
2908               new_dst0 = (ip6_address_t *) (sr0->segments + segment_index0);
2909               ip0->dst_address.as_u64[0] = new_dst0->as_u64[0];
2910               ip0->dst_address.as_u64[1] = new_dst0->as_u64[1];
2911
2912               if (PREDICT_TRUE (sr0->segments_left > 0))
2913                 sr0->segments_left -= 1;
2914             }
2915
2916           /* End of the path. Clean up the SR header, or not */
2917           if (PREDICT_FALSE
2918               (sr0->segments_left == 0 &&
2919                (sr0->flags &
2920                 clib_host_to_net_u16 (IP6_SR_HEADER_FLAG_CLEANUP))))
2921             {
2922               u64 *copy_dst0, *copy_src0;
2923               u16 new_l0;
2924               u32 copy_len_u64s0 = 0;
2925               int i;
2926
2927               /*
2928                * Copy the ip6 header right by the (real) length of the
2929                * sr header.
2930                */
2931               if (PREDICT_FALSE
2932                   (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
2933                 {
2934                   ip6_hop_by_hop_ext_t *ext_hdr =
2935                     (ip6_hop_by_hop_ext_t *) ip6_next_header (ip0);
2936                   copy_len_u64s0 =
2937                     (((ip6_ext_header_t *) ext_hdr)->n_data_u64s) + 1;
2938                   ext_hdr->next_hdr = sr0->protocol;
2939                 }
2940               else
2941                 {
2942                   ip0->protocol = sr0->protocol;
2943                 }
2944
2945               vlib_buffer_advance (b0, (sr0->length + 1) * 8);
2946
2947               new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
2948                 (sr0->length + 1) * 8;
2949               ip0->payload_length = clib_host_to_net_u16 (new_l0);
2950
2951               copy_src0 = (u64 *) ip0;
2952               copy_dst0 = copy_src0 + (sr0->length + 1);
2953               copy_dst0[4 + copy_len_u64s0] = copy_src0[4 + copy_len_u64s0];
2954               copy_dst0[3 + copy_len_u64s0] = copy_src0[3 + copy_len_u64s0];
2955               copy_dst0[2 + copy_len_u64s0] = copy_src0[2 + copy_len_u64s0];
2956               copy_dst0[1 + copy_len_u64s0] = copy_src0[1 + copy_len_u64s0];
2957               copy_dst0[0 + copy_len_u64s0] = copy_src0[0 + copy_len_u64s0];
2958
2959               for (i = copy_len_u64s0 - 1; i >= 0; i--)
2960                 {
2961                   copy_dst0[i] = copy_src0[i];
2962                 }
2963
2964               sr0 = 0;
2965             }
2966
2967         do_trace:
2968           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
2969             {
2970               sr_local_trace_t *tr = vlib_add_trace (vm, node,
2971                                                      b0, sizeof (*tr));
2972               clib_memcpy (tr->src.as_u8, ip0->src_address.as_u8,
2973                            sizeof (tr->src.as_u8));
2974               clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
2975                            sizeof (tr->dst.as_u8));
2976               tr->length = vlib_buffer_length_in_chain (vm, b0);
2977               tr->next_index = next0;
2978               tr->sr_valid = sr0 != 0;
2979               if (tr->sr_valid)
2980                 clib_memcpy (tr->sr, sr0, sizeof (tr->sr));
2981             }
2982
2983           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2984                                            to_next, n_left_to_next,
2985                                            bi0, next0);
2986         }
2987
2988       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2989     }
2990   vlib_node_increment_counter (vm, sr_local_node.index,
2991                                SR_LOCAL_ERROR_PKTS_PROCESSED,
2992                                from_frame->n_vectors);
2993   return from_frame->n_vectors;
2994 }
2995
2996 /* *INDENT-OFF* */
2997 VLIB_REGISTER_NODE (sr_local_node, static) = {
2998   .function = sr_local,
2999   .name = "sr-local",
3000   /* Takes a vector of packets. */
3001   .vector_size = sizeof (u32),
3002   .format_trace = format_sr_local_trace,
3003
3004   .runtime_data_bytes = 0,
3005
3006   .n_errors = SR_LOCAL_N_ERROR,
3007   .error_strings = sr_local_error_strings,
3008
3009   .n_next_nodes = SR_LOCAL_N_NEXT,
3010   .next_nodes = {
3011 #define _(s,n) [SR_LOCAL_NEXT_##s] = n,
3012     foreach_sr_local_next
3013 #undef _
3014   },
3015 };
3016
3017 VLIB_NODE_FUNCTION_MULTIARCH (sr_local_node, sr_local)
3018 /* *INDENT-ON* */
3019
3020 ip6_sr_main_t *
3021 sr_get_main (vlib_main_t * vm)
3022 {
3023   vlib_call_init_function (vm, sr_init);
3024   ASSERT (sr_local_node.index);
3025   return &sr_main;
3026 }
3027
3028 /**
3029  * @brief CLI parser for SR fix destination rewrite node
3030  *
3031  * @param vm vlib_main_t *
3032  * @param input unformat_input_t *
3033  * @param cmd vlib_cli_command_t *
3034  *
3035  * @return error clib_error_t *
3036  */
3037 static clib_error_t *
3038 set_ip6_sr_rewrite_fn (vlib_main_t * vm,
3039                        unformat_input_t * input, vlib_cli_command_t * cmd)
3040 {
3041   fib_prefix_t pfx = {
3042     .fp_proto = FIB_PROTOCOL_IP6,
3043     .fp_len = 128,
3044   };
3045   u32 fib_index = 0;
3046   u32 fib_id = 0;
3047   u32 adj_index;
3048   ip_adjacency_t *adj;
3049   vnet_hw_interface_t *hi;
3050   u32 sw_if_index;
3051   ip6_sr_main_t *sm = &sr_main;
3052   vnet_main_t *vnm = vnet_get_main ();
3053   fib_node_index_t fei;
3054
3055   if (!unformat (input, "%U", unformat_ip6_address, &pfx.fp_addr.ip6))
3056     return clib_error_return (0, "ip6 address missing in '%U'",
3057                               format_unformat_error, input);
3058
3059   if (unformat (input, "rx-table-id %d", &fib_id))
3060     {
3061       fib_index = fib_table_id_find_fib_index (FIB_PROTOCOL_IP6, fib_id);
3062       if (fib_index == ~0)
3063         return clib_error_return (0, "fib-id %d not found", fib_id);
3064     }
3065
3066   fei = fib_table_lookup_exact_match (fib_index, &pfx);
3067
3068   if (FIB_NODE_INDEX_INVALID == fei)
3069     return clib_error_return (0, "no match for %U",
3070                               format_ip6_address, &pfx.fp_addr.ip6);
3071
3072   adj_index = fib_entry_get_adj_for_source (fei, FIB_SOURCE_SR);
3073
3074   if (ADJ_INDEX_INVALID == adj_index)
3075     return clib_error_return (0, "%U not SR sourced",
3076                               format_ip6_address, &pfx.fp_addr.ip6);
3077
3078   adj = adj_get (adj_index);
3079
3080   if (adj->lookup_next_index != IP_LOOKUP_NEXT_REWRITE)
3081     return clib_error_return (0, "%U unresolved (not a rewrite adj)",
3082                               format_ip6_address, &pfx.fp_addr.ip6);
3083
3084   adj->rewrite_header.next_index = sm->ip6_rewrite_sr_next_index;
3085
3086   sw_if_index = adj->rewrite_header.sw_if_index;
3087   hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
3088   adj->rewrite_header.node_index = sr_fix_dst_addr_node.index;
3089
3090   /* $$$$$ hack... steal the mcast group index */
3091   adj->mcast_group_index =
3092     vlib_node_add_next (vm, sr_fix_dst_addr_node.index,
3093                         hi->output_node_index);
3094
3095   return 0;
3096 }
3097
3098 /* *INDENT-OFF* */
3099 VLIB_CLI_COMMAND (set_ip6_sr_rewrite, static) = {
3100     .path = "set ip6 sr rewrite",
3101     .short_help = "set ip6 sr rewrite <ip6-address> [fib-id <id>]",
3102     .function = set_ip6_sr_rewrite_fn,
3103 };
3104 /* *INDENT-ON* */
3105
3106 /**
3107  * @brief Register a callback routine to set next0 in sr_local
3108  *
3109  * @param cb void *
3110  */
3111 void
3112 vnet_register_sr_app_callback (void *cb)
3113 {
3114   ip6_sr_main_t *sm = &sr_main;
3115
3116   sm->sr_local_cb = cb;
3117 }
3118
3119 /**
3120  * @brief Test routine for validation of HMAC
3121  */
3122 static clib_error_t *
3123 test_sr_hmac_validate_fn (vlib_main_t * vm,
3124                           unformat_input_t * input, vlib_cli_command_t * cmd)
3125 {
3126   ip6_sr_main_t *sm = &sr_main;
3127
3128   if (unformat (input, "validate on"))
3129     sm->validate_hmac = 1;
3130   else if (unformat (input, "chunk-offset off"))
3131     sm->validate_hmac = 0;
3132   else
3133     return clib_error_return (0, "expected validate on|off in '%U'",
3134                               format_unformat_error, input);
3135
3136   vlib_cli_output (vm, "hmac signature validation %s",
3137                    sm->validate_hmac ? "on" : "off");
3138   return 0;
3139 }
3140
3141 /* *INDENT-OFF* */
3142 VLIB_CLI_COMMAND (test_sr_hmac_validate, static) = {
3143     .path = "test sr hmac",
3144     .short_help = "test sr hmac validate [on|off]",
3145     .function = test_sr_hmac_validate_fn,
3146 };
3147 /* *INDENT-ON* */
3148
3149 /**
3150  * @brief Add or Delete HMAC key
3151  *
3152  * @param sm ip6_sr_main_t *
3153  * @param key_id u32
3154  * @param shared_secret u8 *
3155  * @param is_del u8
3156  *
3157  * @return retval i32
3158  */
3159 // $$$ fixme shouldn't return i32
3160 i32
3161 sr_hmac_add_del_key (ip6_sr_main_t * sm, u32 key_id, u8 * shared_secret,
3162                      u8 is_del)
3163 {
3164   u32 index;
3165   ip6_sr_hmac_key_t *key;
3166
3167   if (is_del == 0)
3168     {
3169       /* Specific key in use? Fail. */
3170       if (key_id && vec_len (sm->hmac_keys) > key_id
3171           && sm->hmac_keys[key_id].shared_secret)
3172         return -2;
3173
3174       index = key_id;
3175       key = find_or_add_shared_secret (sm, shared_secret, &index);
3176       ASSERT (index == key_id);
3177       return 0;
3178     }
3179
3180   /* delete */
3181
3182   if (key_id)                   /* delete by key ID */
3183     {
3184       if (vec_len (sm->hmac_keys) <= key_id)
3185         return -3;
3186
3187       key = sm->hmac_keys + key_id;
3188
3189       hash_unset_mem (sm->hmac_key_by_shared_secret, key->shared_secret);
3190       vec_free (key->shared_secret);
3191       return 0;
3192     }
3193
3194   index = 0;
3195   key = find_or_add_shared_secret (sm, shared_secret, &index);
3196   hash_unset_mem (sm->hmac_key_by_shared_secret, key->shared_secret);
3197   vec_free (key->shared_secret);
3198   return 0;
3199 }
3200
3201
3202 static clib_error_t *
3203 sr_hmac_add_del_key_fn (vlib_main_t * vm,
3204                         unformat_input_t * input, vlib_cli_command_t * cmd)
3205 {
3206   ip6_sr_main_t *sm = &sr_main;
3207   u8 is_del = 0;
3208   u32 key_id = 0;
3209   u8 key_id_set = 0;
3210   u8 *shared_secret = 0;
3211   i32 rv;
3212
3213   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3214     {
3215       if (unformat (input, "del"))
3216         is_del = 1;
3217       else if (unformat (input, "id %d", &key_id))
3218         key_id_set = 1;
3219       else if (unformat (input, "key %s", &shared_secret))
3220         {
3221           /* Do not include the trailing NULL byte. Guaranteed interop issue */
3222           _vec_len (shared_secret) -= 1;
3223         }
3224       else
3225         break;
3226     }
3227
3228   if (is_del == 0 && shared_secret == 0)
3229     return clib_error_return (0, "shared secret must be set to add a key");
3230
3231   if (shared_secret == 0 && key_id_set == 0)
3232     return clib_error_return (0, "shared secret and key id both unset");
3233
3234   rv = sr_hmac_add_del_key (sm, key_id, shared_secret, is_del);
3235
3236   vec_free (shared_secret);
3237
3238   switch (rv)
3239     {
3240     case 0:
3241       break;
3242
3243     default:
3244       return clib_error_return (0, "sr_hmac_add_del_key returned %d", rv);
3245     }
3246
3247   return 0;
3248 }
3249
3250 /* *INDENT-OFF* */
3251 VLIB_CLI_COMMAND (sr_hmac, static) = {
3252     .path = "sr hmac",
3253     .short_help = "sr hmac [del] id <nn> key <str>",
3254     .function = sr_hmac_add_del_key_fn,
3255 };
3256 /* *INDENT-ON* */
3257
3258 /**
3259  * @brief CLI parser for show HMAC key shared secrets
3260  *
3261  * @param vm vlib_main_t *
3262  * @param input unformat_input_t *
3263  * @param cmd vlib_cli_command_t *
3264  *
3265  * @return error clib_error_t *
3266  */
3267 static clib_error_t *
3268 show_sr_hmac_fn (vlib_main_t * vm,
3269                  unformat_input_t * input, vlib_cli_command_t * cmd)
3270 {
3271   ip6_sr_main_t *sm = &sr_main;
3272   int i;
3273
3274   for (i = 1; i < vec_len (sm->hmac_keys); i++)
3275     {
3276       if (sm->hmac_keys[i].shared_secret)
3277         vlib_cli_output (vm, "[%d]: %v", i, sm->hmac_keys[i].shared_secret);
3278     }
3279
3280   return 0;
3281 }
3282
3283 /* *INDENT-OFF* */
3284 VLIB_CLI_COMMAND (show_sr_hmac, static) = {
3285     .path = "show sr hmac",
3286     .short_help = "show sr hmac",
3287     .function = show_sr_hmac_fn,
3288 };
3289 /* *INDENT-ON* */
3290
3291 /**
3292  * @brief Test for SR debug flag
3293  *
3294  * @param vm vlib_main_t *
3295  * @param input unformat_input_t *
3296  * @param cmd vlib_cli_command_t *
3297  *
3298  * @return error clib_error_t *
3299  */
3300 static clib_error_t *
3301 test_sr_debug_fn (vlib_main_t * vm,
3302                   unformat_input_t * input, vlib_cli_command_t * cmd)
3303 {
3304   ip6_sr_main_t *sm = &sr_main;
3305
3306   if (unformat (input, "on"))
3307     sm->is_debug = 1;
3308   else if (unformat (input, "off"))
3309     sm->is_debug = 0;
3310   else
3311     return clib_error_return (0, "expected on|off in '%U'",
3312                               format_unformat_error, input);
3313
3314   vlib_cli_output (vm, "debug trace now %s", sm->is_debug ? "on" : "off");
3315
3316   return 0;
3317 }
3318
3319 /* *INDENT-OFF* */
3320 VLIB_CLI_COMMAND (test_sr_debug, static) = {
3321     .path = "test sr debug",
3322     .short_help = "test sr debug on|off",
3323     .function = test_sr_debug_fn,
3324 };
3325 /* *INDENT-ON* */
3326
3327 /*
3328  * fd.io coding-style-patch-verification: ON
3329  *
3330  * Local Variables:
3331  * eval: (c-set-style "gnu")
3332  * End:
3333  */