sr: fix possible null-pointer dereference
[vpp.git] / src / vnet / srv6 / sr_localsid.c
1 /*
2  * sr_localsid.c: ipv6 segment routing Endpoint behaviors
3  *
4  * Copyright (c) 2016 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 Processing of packets with a SRH
21  *
22  * CLI to define new Segment Routing End processing functions.
23  * Graph node to support such functions.
24  *
25  * Each function associates an SRv6 segment (IPv6 address) with an specific
26  * Segment Routing function.
27  *
28  */
29
30 #include <vlib/vlib.h>
31 #include <vnet/vnet.h>
32 #include <vnet/srv6/sr.h>
33 #include <vnet/ip/ip.h>
34 #include <vnet/srv6/sr_packet.h>
35 #include <vnet/ip/ip6_packet.h>
36 #include <vnet/fib/ip6_fib.h>
37 #include <vnet/dpo/dpo.h>
38 #include <vnet/adj/adj.h>
39
40 #include <vppinfra/error.h>
41 #include <vppinfra/elog.h>
42
43 /**
44  * @brief Dynamically added SR localsid DPO type
45  */
46 static dpo_type_t sr_localsid_dpo_type;
47 static dpo_type_t sr_localsid_d_dpo_type;
48
49 /**
50  * @brief SR localsid add/del
51  *
52  * Function to add or delete SR LocalSIDs.
53  *
54  * @param is_del Boolean of whether its a delete instruction
55  * @param localsid_addr IPv6 address of the localsid
56  * @param is_decap Boolean of whether decapsulation is allowed in this function
57  * @param behavior Type of behavior (function) for this localsid
58  * @param sw_if_index Only for L2/L3 xconnect. OIF. In VRF variant the fib_table.
59  * @param vlan_index Only for L2 xconnect. Outgoing VLAN tag.
60  * @param fib_table  FIB table in which we should install the localsid entry
61  * @param nh_addr Next Hop IPv4/IPv6 address. Only for L2/L3 xconnect.
62  *
63  * @return 0 on success, error otherwise.
64  */
65 int
66 sr_cli_localsid (char is_del, ip6_address_t * localsid_addr, u16 prefixlen,
67                  char end_psp, u8 behavior, u32 sw_if_index, u32 vlan_index,
68                  u32 fib_table, ip46_address_t * nh_addr, void *ls_plugin_mem)
69 {
70   ip6_sr_main_t *sm = &sr_main;
71   uword *p;
72   int rv;
73   u8 pref_length = 128;
74   sr_localsid_fn_registration_t *plugin = 0;
75
76   ip6_sr_localsid_t *ls = 0;
77
78   dpo_id_t dpo = DPO_INVALID;
79
80   /* Search for the item */
81   p = mhash_get (&sm->sr_localsids_index_hash, localsid_addr);
82
83   if (p)
84     {
85       if (is_del)
86         {
87           /* Retrieve localsid */
88           ls = pool_elt_at_index (sm->localsids, p[0]);
89           if (ls->behavior >= SR_BEHAVIOR_LAST)
90             {
91               plugin = pool_elt_at_index (sm->plugin_functions,
92                                           ls->behavior - SR_BEHAVIOR_LAST);
93               pref_length = plugin->prefix_length;
94             }
95
96           if (prefixlen != 0)
97             {
98               pref_length = prefixlen;
99             }
100
101           /* Delete FIB entry */
102           fib_prefix_t pfx = {
103             .fp_proto = FIB_PROTOCOL_IP6,
104             .fp_len = pref_length,
105             .fp_addr = {
106                         .ip6 = *localsid_addr,
107                         }
108           };
109
110           fib_table_entry_delete (fib_table_find
111                                   (FIB_PROTOCOL_IP6, fib_table), &pfx,
112                                   FIB_SOURCE_SR);
113
114           /* In case it is a Xconnect iface remove the (OIF, NHOP) adj */
115           if (ls->behavior == SR_BEHAVIOR_X || ls->behavior == SR_BEHAVIOR_DX6
116               || ls->behavior == SR_BEHAVIOR_DX4)
117             adj_unlock (ls->nh_adj);
118
119           if (ls->behavior >= SR_BEHAVIOR_LAST)
120             {
121               /* Callback plugin removal function */
122               rv = plugin->removal (ls);
123             }
124
125           /* Delete localsid registry */
126           pool_put (sm->localsids, ls);
127           mhash_unset (&sm->sr_localsids_index_hash, localsid_addr, NULL);
128           return 0;
129         }
130       else                      /* create with function already existing; complain */
131         return -1;
132     }
133   else
134     /* delete; localsid does not exist; complain */
135   if (is_del)
136     return -2;
137
138   if (behavior >= SR_BEHAVIOR_LAST)
139     {
140       sr_localsid_fn_registration_t *plugin = 0;
141       plugin =
142         pool_elt_at_index (sm->plugin_functions, behavior - SR_BEHAVIOR_LAST);
143       pref_length = plugin->prefix_length;
144     }
145
146   /* Check whether there exists a FIB entry with such address */
147   fib_prefix_t pfx = {
148     .fp_proto = FIB_PROTOCOL_IP6,
149     .fp_len = pref_length,
150   };
151
152   pfx.fp_addr.as_u64[0] = localsid_addr->as_u64[0];
153   pfx.fp_addr.as_u64[1] = localsid_addr->as_u64[1];
154
155   if (prefixlen != 0)
156     {
157       pfx.fp_len = prefixlen;
158     }
159
160   /* Lookup the FIB index associated to the table id provided */
161   u32 fib_index = fib_table_find (FIB_PROTOCOL_IP6, fib_table);
162   if (fib_index == ~0)
163     return -3;
164
165   /* Lookup the localsid in such FIB table */
166   fib_node_index_t fei = fib_table_lookup_exact_match (fib_index, &pfx);
167   if (FIB_NODE_INDEX_INVALID != fei)
168     return -4;                  //There is an entry for such address (the localsid addr)
169
170   /* Create a new localsid registry */
171   pool_get (sm->localsids, ls);
172   clib_memset (ls, 0, sizeof (*ls));
173
174   clib_memcpy (&ls->localsid, localsid_addr, sizeof (ip6_address_t));
175   ls->end_psp = end_psp;
176   ls->behavior = behavior;
177   ls->nh_adj = (u32) ~ 0;
178   ls->fib_table = fib_table;
179   ls->localsid_len = pfx.fp_len;
180   switch (behavior)
181     {
182     case SR_BEHAVIOR_END:
183       break;
184     case SR_BEHAVIOR_X:
185       ls->sw_if_index = sw_if_index;
186       clib_memcpy (&ls->next_hop.ip6, &nh_addr->ip6, sizeof (ip6_address_t));
187       break;
188     case SR_BEHAVIOR_T:
189       ls->vrf_index = fib_table_find (FIB_PROTOCOL_IP6, sw_if_index);
190       break;
191     case SR_BEHAVIOR_DX4:
192       ls->sw_if_index = sw_if_index;
193       clib_memcpy (&ls->next_hop.ip4, &nh_addr->ip4, sizeof (ip4_address_t));
194       break;
195     case SR_BEHAVIOR_DX6:
196       ls->sw_if_index = sw_if_index;
197       clib_memcpy (&ls->next_hop.ip6, &nh_addr->ip6, sizeof (ip6_address_t));
198       break;
199     case SR_BEHAVIOR_DT6:
200       ls->vrf_index = fib_table_find (FIB_PROTOCOL_IP6, sw_if_index);
201       break;
202     case SR_BEHAVIOR_DT4:
203       ls->vrf_index = fib_table_find (FIB_PROTOCOL_IP4, sw_if_index);
204       break;
205     case SR_BEHAVIOR_DX2:
206       ls->sw_if_index = sw_if_index;
207       ls->vlan_index = vlan_index;
208       break;
209     }
210
211   /* Figure out the adjacency magic for Xconnect variants */
212   if (ls->behavior == SR_BEHAVIOR_X || ls->behavior == SR_BEHAVIOR_DX4
213       || ls->behavior == SR_BEHAVIOR_DX6)
214     {
215       adj_index_t nh_adj_index = ADJ_INDEX_INVALID;
216
217       /* Retrieve the adjacency corresponding to the (OIF, next_hop) */
218       if (ls->behavior == SR_BEHAVIOR_DX6 || ls->behavior == SR_BEHAVIOR_X)
219         nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP6, VNET_LINK_IP6,
220                                             nh_addr, sw_if_index);
221
222       else if (ls->behavior == SR_BEHAVIOR_DX4)
223         nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4,
224                                             nh_addr, sw_if_index);
225
226       /* Check for ADJ creation error. If so panic */
227       if (nh_adj_index == ADJ_INDEX_INVALID)
228         {
229           pool_put (sm->localsids, ls);
230           return -5;
231         }
232
233       ls->nh_adj = nh_adj_index;
234     }
235
236   /* Set DPO */
237   if (ls->behavior == SR_BEHAVIOR_END || ls->behavior == SR_BEHAVIOR_X
238       || ls->behavior == SR_BEHAVIOR_T)
239     dpo_set (&dpo, sr_localsid_dpo_type, DPO_PROTO_IP6, ls - sm->localsids);
240   else if (ls->behavior > SR_BEHAVIOR_D_FIRST
241            && ls->behavior < SR_BEHAVIOR_LAST)
242     dpo_set (&dpo, sr_localsid_d_dpo_type, DPO_PROTO_IP6, ls - sm->localsids);
243   else if (ls->behavior >= SR_BEHAVIOR_LAST)
244     {
245       sr_localsid_fn_registration_t *plugin = 0;
246       plugin = pool_elt_at_index (sm->plugin_functions,
247                                   ls->behavior - SR_BEHAVIOR_LAST);
248       /* Copy the unformat memory result */
249       ls->plugin_mem = ls_plugin_mem;
250       /* Callback plugin creation function */
251       rv = plugin->creation (ls);
252       if (rv)
253         {
254           pool_put (sm->localsids, ls);
255           return -6;
256         }
257       dpo_set (&dpo, plugin->dpo, DPO_PROTO_IP6, ls - sm->localsids);
258     }
259
260   /* Set hash key for searching localsid by address */
261   mhash_set (&sm->sr_localsids_index_hash, localsid_addr, ls - sm->localsids,
262              NULL);
263
264   fib_table_entry_special_dpo_add (fib_index, &pfx, FIB_SOURCE_SR,
265                                    FIB_ENTRY_FLAG_EXCLUSIVE, &dpo);
266   dpo_reset (&dpo);
267
268   /* Set counter to zero */
269   vlib_validate_combined_counter (&(sm->sr_ls_valid_counters),
270                                   ls - sm->localsids);
271   vlib_validate_combined_counter (&(sm->sr_ls_invalid_counters),
272                                   ls - sm->localsids);
273
274   vlib_zero_combined_counter (&(sm->sr_ls_valid_counters),
275                               ls - sm->localsids);
276   vlib_zero_combined_counter (&(sm->sr_ls_invalid_counters),
277                               ls - sm->localsids);
278
279   return 0;
280 }
281
282 /**
283  * @brief SR LocalSID CLI function.
284  *
285  * @see sr_cli_localsid
286  */
287 static clib_error_t *
288 sr_cli_localsid_command_fn (vlib_main_t * vm, unformat_input_t * input,
289                             vlib_cli_command_t * cmd)
290 {
291   vnet_main_t *vnm = vnet_get_main ();
292   ip6_sr_main_t *sm = &sr_main;
293   u32 sw_if_index = (u32) ~ 0, vlan_index = (u32) ~ 0, fib_index = 0;
294   u16 prefix_len = 0;
295   int is_del = 0;
296   int end_psp = 0;
297   ip6_address_t resulting_address;
298   ip46_address_t next_hop;
299   char address_set = 0;
300   char behavior = 0;
301   void *ls_plugin_mem = 0;
302
303   int rv;
304
305   clib_memset (&resulting_address, 0, sizeof (ip6_address_t));
306   ip46_address_reset (&next_hop);
307
308   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
309     {
310       if (unformat (input, "del"))
311         is_del = 1;
312       else if (!address_set
313                && unformat (input, "address %U", unformat_ip6_address,
314                             &resulting_address))
315         address_set = 1;
316       else if (!address_set
317                && unformat (input, "prefix %U/%d", unformat_ip6_address,
318                             &resulting_address, &prefix_len))
319         address_set = 1;
320       else if (!address_set
321                && unformat (input, "addr %U", unformat_ip6_address,
322                             &resulting_address))
323         address_set = 1;
324       else if (unformat (input, "fib-table %u", &fib_index));
325       else if (vlan_index == (u32) ~ 0
326                && unformat (input, "vlan %u", &vlan_index));
327       else if (!behavior && unformat (input, "behavior"))
328         {
329           if (unformat (input, "end.x %U %U",
330                         unformat_vnet_sw_interface, vnm, &sw_if_index,
331                         unformat_ip6_address, &next_hop.ip6))
332             behavior = SR_BEHAVIOR_X;
333           else if (unformat (input, "end.t %u", &sw_if_index))
334             behavior = SR_BEHAVIOR_T;
335           else if (unformat (input, "end.dx6 %U %U",
336                              unformat_vnet_sw_interface, vnm, &sw_if_index,
337                              unformat_ip6_address, &next_hop.ip6))
338             behavior = SR_BEHAVIOR_DX6;
339           else if (unformat (input, "end.dx4 %U %U",
340                              unformat_vnet_sw_interface, vnm, &sw_if_index,
341                              unformat_ip4_address, &next_hop.ip4))
342             behavior = SR_BEHAVIOR_DX4;
343           else if (unformat (input, "end.dx2 %U",
344                              unformat_vnet_sw_interface, vnm, &sw_if_index))
345             behavior = SR_BEHAVIOR_DX2;
346           else if (unformat (input, "end.dt6 %u", &sw_if_index))
347             behavior = SR_BEHAVIOR_DT6;
348           else if (unformat (input, "end.dt4 %u", &sw_if_index))
349             behavior = SR_BEHAVIOR_DT4;
350           else
351             {
352               /* Loop over all the plugin behavior format functions */
353               sr_localsid_fn_registration_t *plugin = 0, **vec_plugins = 0;
354               sr_localsid_fn_registration_t **plugin_it = 0;
355
356               /* Create a vector out of the plugin pool as recommended */
357               /* *INDENT-OFF* */
358               pool_foreach (plugin, sm->plugin_functions,
359                 {
360                   vec_add1 (vec_plugins, plugin);
361                 });
362               /* *INDENT-ON* */
363
364               vec_foreach (plugin_it, vec_plugins)
365               {
366                 if (unformat
367                     (input, "%U", (*plugin_it)->ls_unformat, &ls_plugin_mem))
368                   {
369                     behavior = (*plugin_it)->sr_localsid_function_number;
370                     break;
371                   }
372               }
373             }
374
375           if (!behavior)
376             {
377               if (unformat (input, "end"))
378                 behavior = SR_BEHAVIOR_END;
379               else
380                 break;
381             }
382         }
383       else if (!end_psp && unformat (input, "psp"))
384         end_psp = 1;
385       else
386         break;
387     }
388
389   if (!behavior && end_psp)
390     behavior = SR_BEHAVIOR_END;
391
392   if (!address_set)
393     return clib_error_return (0,
394                               "Error: SRv6 LocalSID address is mandatory.");
395   if (!is_del && !behavior)
396     return clib_error_return (0,
397                               "Error: SRv6 LocalSID behavior is mandatory.");
398   if (vlan_index != (u32) ~ 0)
399     return clib_error_return (0,
400                               "Error: SRv6 End.DX2 with rewrite VLAN tag not supported by now.");
401   if (end_psp && !(behavior == SR_BEHAVIOR_END || behavior == SR_BEHAVIOR_X))
402     return clib_error_return (0,
403                               "Error: SRv6 PSP only compatible with End and End.X");
404
405   rv =
406     sr_cli_localsid (is_del, &resulting_address, prefix_len, end_psp,
407                      behavior, sw_if_index, vlan_index, fib_index, &next_hop,
408                      ls_plugin_mem);
409
410   switch (rv)
411     {
412     case 0:
413       break;
414     case 1:
415       return 0;
416     case -1:
417       return clib_error_return (0,
418                                 "Identical localsid already exists. Requested localsid not created.");
419     case -2:
420       return clib_error_return (0,
421                                 "The requested localsid could not be deleted. SR localsid not found");
422     case -3:
423       return clib_error_return (0, "FIB table %u does not exist", fib_index);
424     case -4:
425       return clib_error_return (0, "There is already one FIB entry for the"
426                                 "requested localsid non segment routing related");
427     case -5:
428       return clib_error_return (0,
429                                 "Could not create ARP/ND entry for such next_hop. Internal error.");
430     case -6:
431       return clib_error_return (0,
432                                 "Error on the plugin based localsid creation.");
433     default:
434       return clib_error_return (0, "BUG: sr localsid returns %d", rv);
435     }
436   return 0;
437 }
438
439 /* *INDENT-OFF* */
440 VLIB_CLI_COMMAND (sr_localsid_command, static) = {
441   .path = "sr localsid",
442   .short_help = "sr localsid (del) address XX:XX::YY:YY"
443       "(fib-table 8) behavior STRING",
444   .long_help =
445     "Create SR LocalSID and binds it to a particular behavior\n"
446     "Arguments:\n"
447     "\tlocalSID IPv6_addr(128b)   LocalSID IPv6 address\n"
448     "\t(fib-table X)              Optional. VRF where to install SRv6 localsid\n"
449     "\tbehavior STRING            Specifies the behavior\n"
450     "\n\tBehaviors:\n"
451     "\tEnd\t-> Endpoint.\n"
452     "\tEnd.X\t-> Endpoint with decapsulation and Layer-3 cross-connect.\n"
453     "\t\tParameters: '<iface> <ip6_next_hop>'\n"
454     "\tEnd.DX2\t-> Endpoint with decapsulation and Layer-2 cross-connect.\n"
455     "\t\tParameters: '<iface>'\n"
456     "\tEnd.DX6\t-> Endpoint with decapsulation and IPv6 cross-connect.\n"
457     "\t\tParameters: '<iface> <ip6_next_hop>'\n"
458     "\tEnd.DX4\t-> Endpoint with decapsulation and IPv4 cross-connect.\n"
459     "\t\tParameters: '<iface> <ip4_next_hop>'\n"
460     "\tEnd.DT6\t-> Endpoint with decapsulation and specific IPv6 table lookup.\n"
461     "\t\tParameters: '<ip6_fib_table>'\n"
462     "\tEnd.DT4\t-> Endpoint with decapsulation and specific IPv4 table lookup.\n"
463     "\t\tParameters: '<ip4_fib_table>'\n",
464   .function = sr_cli_localsid_command_fn,
465 };
466 /* *INDENT-ON* */
467
468 /**
469  * @brief CLI function to 'show' all SR LocalSIDs on console.
470  */
471 static clib_error_t *
472 show_sr_localsid_command_fn (vlib_main_t * vm, unformat_input_t * input,
473                              vlib_cli_command_t * cmd)
474 {
475   vnet_main_t *vnm = vnet_get_main ();
476   ip6_sr_main_t *sm = &sr_main;
477   ip6_sr_localsid_t **localsid_list = 0;
478   ip6_sr_localsid_t *ls;
479   int i;
480
481   vlib_cli_output (vm, "SRv6 - My LocalSID Table:");
482   vlib_cli_output (vm, "=========================");
483   /* *INDENT-OFF* */
484   pool_foreach (ls, sm->localsids, ({ vec_add1 (localsid_list, ls); }));
485   /* *INDENT-ON* */
486   for (i = 0; i < vec_len (localsid_list); i++)
487     {
488       ls = localsid_list[i];
489       switch (ls->behavior)
490         {
491         case SR_BEHAVIOR_END:
492           vlib_cli_output (vm, "\tAddress: \t%U\n\tBehavior: \tEnd",
493                            format_ip6_address, &ls->localsid);
494           break;
495         case SR_BEHAVIOR_X:
496           vlib_cli_output (vm,
497                            "\tAddress: \t%U\n\tBehavior: \tX (Endpoint with Layer-3 cross-connect)"
498                            "\n\tIface:  \t%U\n\tNext hop: \t%U",
499                            format_ip6_address, &ls->localsid,
500                            format_vnet_sw_if_index_name, vnm, ls->sw_if_index,
501                            format_ip6_address, &ls->next_hop.ip6);
502           break;
503         case SR_BEHAVIOR_T:
504           vlib_cli_output (vm,
505                            "\tAddress: \t%U\n\tBehavior: \tT (Endpoint with specific IPv6 table lookup)"
506                            "\n\tTable:  \t%u",
507                            format_ip6_address, &ls->localsid,
508                            fib_table_get_table_id (ls->vrf_index,
509                                                    FIB_PROTOCOL_IP6));
510           break;
511         case SR_BEHAVIOR_DX4:
512           vlib_cli_output (vm,
513                            "\tAddress: \t%U\n\tBehavior: \tDX4 (Endpoint with decapsulation and IPv4 cross-connect)"
514                            "\n\tIface:  \t%U\n\tNext hop: \t%U",
515                            format_ip6_address, &ls->localsid,
516                            format_vnet_sw_if_index_name, vnm, ls->sw_if_index,
517                            format_ip4_address, &ls->next_hop.ip4);
518           break;
519         case SR_BEHAVIOR_DX6:
520           vlib_cli_output (vm,
521                            "\tAddress: \t%U\n\tBehavior: \tDX6 (Endpoint with decapsulation and IPv6 cross-connect)"
522                            "\n\tIface:  \t%U\n\tNext hop: \t%U",
523                            format_ip6_address, &ls->localsid,
524                            format_vnet_sw_if_index_name, vnm, ls->sw_if_index,
525                            format_ip6_address, &ls->next_hop.ip6);
526           break;
527         case SR_BEHAVIOR_DX2:
528           if (ls->vlan_index == (u32) ~ 0)
529             vlib_cli_output (vm,
530                              "\tAddress: \t%U\n\tBehavior: \tDX2 (Endpoint with decapulation and Layer-2 cross-connect)"
531                              "\n\tIface:  \t%U", format_ip6_address,
532                              &ls->localsid, format_vnet_sw_if_index_name, vnm,
533                              ls->sw_if_index);
534           else
535             vlib_cli_output (vm,
536                              "Unsupported yet. (DX2 with egress VLAN rewrite)");
537           break;
538         case SR_BEHAVIOR_DT6:
539           vlib_cli_output (vm,
540                            "\tAddress: \t%U\n\tBehavior: \tDT6 (Endpoint with decapsulation and specific IPv6 table lookup)"
541                            "\n\tTable: %u", format_ip6_address, &ls->localsid,
542                            fib_table_get_table_id (ls->vrf_index,
543                                                    FIB_PROTOCOL_IP6));
544           break;
545         case SR_BEHAVIOR_DT4:
546           vlib_cli_output (vm,
547                            "\tAddress: \t%U\n\tBehavior: \tDT4 (Endpoint with decapsulation and specific IPv4 table lookup)"
548                            "\n\tTable: \t%u", format_ip6_address,
549                            &ls->localsid,
550                            fib_table_get_table_id (ls->vrf_index,
551                                                    FIB_PROTOCOL_IP4));
552           break;
553         default:
554           if (ls->behavior >= SR_BEHAVIOR_LAST)
555             {
556               sr_localsid_fn_registration_t *plugin =
557                 pool_elt_at_index (sm->plugin_functions,
558                                    ls->behavior - SR_BEHAVIOR_LAST);
559
560               vlib_cli_output (vm, "\tAddress: \t%U\n"
561                                "\tBehavior: \t%s (%s)\n\t%U",
562                                format_ip6_address, &ls->localsid,
563                                plugin->keyword_str, plugin->def_str,
564                                plugin->ls_format, ls->plugin_mem);
565             }
566           else
567             //Should never get here...
568             vlib_cli_output (vm, "Internal error");
569           break;
570         }
571       if (ls->end_psp)
572         vlib_cli_output (vm, "\tPSP: \tTrue\n");
573
574       /* Print counters */
575       vlib_counter_t valid, invalid;
576       vlib_get_combined_counter (&(sm->sr_ls_valid_counters), i, &valid);
577       vlib_get_combined_counter (&(sm->sr_ls_invalid_counters), i, &invalid);
578       vlib_cli_output (vm, "\tGood traffic: \t[%Ld packets : %Ld bytes]\n",
579                        valid.packets, valid.bytes);
580       vlib_cli_output (vm, "\tBad traffic:  \t[%Ld packets : %Ld bytes]\n",
581                        invalid.packets, invalid.bytes);
582       vlib_cli_output (vm, "--------------------");
583     }
584   return 0;
585 }
586
587 /* *INDENT-OFF* */
588 VLIB_CLI_COMMAND (show_sr_localsid_command, static) = {
589   .path = "show sr localsids",
590   .short_help = "show sr localsids",
591   .function = show_sr_localsid_command_fn,
592 };
593 /* *INDENT-ON* */
594
595 /**
596  * @brief Function to 'clear' ALL SR localsid counters
597  */
598 static clib_error_t *
599 clear_sr_localsid_counters_command_fn (vlib_main_t * vm,
600                                        unformat_input_t * input,
601                                        vlib_cli_command_t * cmd)
602 {
603   ip6_sr_main_t *sm = &sr_main;
604
605   vlib_clear_combined_counters (&(sm->sr_ls_valid_counters));
606   vlib_clear_combined_counters (&(sm->sr_ls_invalid_counters));
607
608   return 0;
609 }
610
611 /* *INDENT-OFF* */
612 VLIB_CLI_COMMAND (clear_sr_localsid_counters_command, static) = {
613   .path = "clear sr localsid-counters",
614   .short_help = "clear sr localsid-counters",
615   .function = clear_sr_localsid_counters_command_fn,
616 };
617 /* *INDENT-ON* */
618
619 /************************ SR LocalSID graphs node ****************************/
620 /**
621  * @brief SR localsid node trace
622  */
623 typedef struct
624 {
625   ip6_address_t localsid;
626   u16 behavior;
627   u8 sr[256];
628   u8 num_segments;
629   u8 segments_left;
630 } sr_localsid_trace_t;
631
632 #define foreach_sr_localsid_error                                   \
633 _(NO_INNER_HEADER, "(SR-Error) No inner IP header")                 \
634 _(NO_MORE_SEGMENTS, "(SR-Error) No more segments")                  \
635 _(NO_SRH, "(SR-Error) No SR header")                                \
636 _(NO_PSP, "(SR-Error) PSP Not available (segments left > 0)")       \
637 _(NOT_LS, "(SR-Error) Decaps not available (segments left > 0)")    \
638 _(L2, "(SR-Error) SRv6 decapsulated a L2 frame without dest")
639
640 typedef enum
641 {
642 #define _(sym,str) SR_LOCALSID_ERROR_##sym,
643   foreach_sr_localsid_error
644 #undef _
645     SR_LOCALSID_N_ERROR,
646 } sr_localsid_error_t;
647
648 static char *sr_localsid_error_strings[] = {
649 #define _(sym,string) string,
650   foreach_sr_localsid_error
651 #undef _
652 };
653
654 #define foreach_sr_localsid_next        \
655 _(ERROR, "error-drop")                  \
656 _(IP6_LOOKUP, "ip6-lookup")             \
657 _(IP4_LOOKUP, "ip4-lookup")             \
658 _(IP6_REWRITE, "ip6-rewrite")           \
659 _(IP4_REWRITE, "ip4-rewrite")           \
660 _(INTERFACE_OUTPUT, "interface-output")
661
662 typedef enum
663 {
664 #define _(s,n) SR_LOCALSID_NEXT_##s,
665   foreach_sr_localsid_next
666 #undef _
667     SR_LOCALSID_N_NEXT,
668 } sr_localsid_next_t;
669
670 /**
671  * @brief SR LocalSID graph node trace function
672  *
673  * @see sr_localsid
674  */
675 u8 *
676 format_sr_localsid_trace (u8 * s, va_list * args)
677 {
678   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
679   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
680   sr_localsid_trace_t *t = va_arg (*args, sr_localsid_trace_t *);
681
682   s =
683     format (s, "SR-LOCALSID:\n\tLocalsid: %U\n", format_ip6_address,
684             &t->localsid);
685   switch (t->behavior)
686     {
687     case SR_BEHAVIOR_END:
688       s = format (s, "\tBehavior: End\n");
689       break;
690     case SR_BEHAVIOR_DX6:
691       s = format (s, "\tBehavior: Decapsulation with IPv6 L3 xconnect\n");
692       break;
693     case SR_BEHAVIOR_DX4:
694       s = format (s, "\tBehavior: Decapsulation with IPv4 L3 xconnect\n");
695       break;
696     case SR_BEHAVIOR_X:
697       s = format (s, "\tBehavior: IPv6 L3 xconnect\n");
698       break;
699     case SR_BEHAVIOR_T:
700       s = format (s, "\tBehavior: IPv6 specific table lookup\n");
701       break;
702     case SR_BEHAVIOR_DT6:
703       s = format (s, "\tBehavior: Decapsulation with IPv6 Table lookup\n");
704       break;
705     case SR_BEHAVIOR_DT4:
706       s = format (s, "\tBehavior: Decapsulation with IPv4 Table lookup\n");
707       break;
708     case SR_BEHAVIOR_DX2:
709       s = format (s, "\tBehavior: Decapsulation with L2 xconnect\n");
710       break;
711     default:
712       s = format (s, "\tBehavior: defined in plugin\n");        //TODO
713       break;
714     }
715   if (t->num_segments != 0xFF)
716     {
717       if (t->num_segments > 0)
718         {
719           s = format (s, "\tSegments left: %d\n", t->segments_left);
720           s = format (s, "\tSID list: [in ietf order]");
721           int i = 0;
722           for (i = 0; i < t->num_segments; i++)
723             {
724               s = format (s, "\n\t-> %U", format_ip6_address,
725                           (ip6_address_t *) & t->sr[i *
726                                                     sizeof (ip6_address_t)]);
727             }
728         }
729     }
730   return s;
731 }
732
733 /**
734  * @brief Function doing End processing.
735  */
736 static_always_inline void
737 end_srh_processing (vlib_node_runtime_t * node,
738                     vlib_buffer_t * b0,
739                     ip6_header_t * ip0,
740                     ip6_sr_header_t * sr0,
741                     ip6_sr_localsid_t * ls0,
742                     u32 * next0, u8 psp, ip6_ext_header_t * prev0)
743 {
744   ip6_address_t *new_dst0;
745
746   if (PREDICT_TRUE (sr0 && sr0->type == ROUTING_HEADER_TYPE_SR))
747     {
748       if (sr0->segments_left == 1 && psp)
749         {
750           u32 new_l0, sr_len;
751           u64 *copy_dst0, *copy_src0;
752           u32 copy_len_u64s0 = 0;
753
754           ip0->dst_address.as_u64[0] = sr0->segments->as_u64[0];
755           ip0->dst_address.as_u64[1] = sr0->segments->as_u64[1];
756
757           /* Remove the SRH taking care of the rest of IPv6 ext header */
758           if (prev0)
759             prev0->next_hdr = sr0->protocol;
760           else
761             ip0->protocol = sr0->protocol;
762
763           sr_len = ip6_ext_header_len (sr0);
764           vlib_buffer_advance (b0, sr_len);
765           new_l0 = clib_net_to_host_u16 (ip0->payload_length) - sr_len;
766           ip0->payload_length = clib_host_to_net_u16 (new_l0);
767           copy_src0 = (u64 *) ip0;
768           copy_dst0 = copy_src0 + (sr0->length + 1);
769           /* number of 8 octet units to copy
770            * By default in absence of extension headers it is equal to length of ip6 header
771            * With extension headers it number of 8 octet units of ext headers preceding
772            * SR header
773            */
774           copy_len_u64s0 =
775             (((u8 *) sr0 - (u8 *) ip0) - sizeof (ip6_header_t)) >> 3;
776           copy_dst0[4 + copy_len_u64s0] = copy_src0[4 + copy_len_u64s0];
777           copy_dst0[3 + copy_len_u64s0] = copy_src0[3 + copy_len_u64s0];
778           copy_dst0[2 + copy_len_u64s0] = copy_src0[2 + copy_len_u64s0];
779           copy_dst0[1 + copy_len_u64s0] = copy_src0[1 + copy_len_u64s0];
780           copy_dst0[0 + copy_len_u64s0] = copy_src0[0 + copy_len_u64s0];
781
782           int i;
783           for (i = copy_len_u64s0 - 1; i >= 0; i--)
784             {
785               copy_dst0[i] = copy_src0[i];
786             }
787
788           if (ls0->behavior == SR_BEHAVIOR_X)
789             {
790               vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ls0->nh_adj;
791               *next0 = SR_LOCALSID_NEXT_IP6_REWRITE;
792             }
793           else if (ls0->behavior == SR_BEHAVIOR_T)
794             {
795               vnet_buffer (b0)->sw_if_index[VLIB_TX] = ls0->vrf_index;
796             }
797         }
798       else if (PREDICT_TRUE (sr0->segments_left > 0))
799         {
800           sr0->segments_left -= 1;
801           new_dst0 = (ip6_address_t *) (sr0->segments);
802           new_dst0 += sr0->segments_left;
803           ip0->dst_address.as_u64[0] = new_dst0->as_u64[0];
804           ip0->dst_address.as_u64[1] = new_dst0->as_u64[1];
805
806           if (ls0->behavior == SR_BEHAVIOR_X)
807             {
808               vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ls0->nh_adj;
809               *next0 = SR_LOCALSID_NEXT_IP6_REWRITE;
810             }
811           else if (ls0->behavior == SR_BEHAVIOR_T)
812             {
813               vnet_buffer (b0)->sw_if_index[VLIB_TX] = ls0->vrf_index;
814             }
815         }
816       else
817         {
818           *next0 = SR_LOCALSID_NEXT_ERROR;
819           b0->error = node->errors[SR_LOCALSID_ERROR_NO_MORE_SEGMENTS];
820         }
821     }
822   else
823     {
824       /* Error. Routing header of type != SR */
825       *next0 = SR_LOCALSID_NEXT_ERROR;
826       b0->error = node->errors[SR_LOCALSID_ERROR_NO_SRH];
827     }
828 }
829
830 /*
831  * @brief Function doing SRH processing for D* variants
832  */
833 static_always_inline void
834 end_decaps_srh_processing (vlib_node_runtime_t * node,
835                            vlib_buffer_t * b0,
836                            ip6_header_t * ip0,
837                            ip6_sr_header_t * sr0,
838                            ip6_sr_localsid_t * ls0, u32 * next0)
839 {
840   /* Compute the size of the IPv6 header with all Ext. headers */
841   u8 next_proto;
842   ip6_ext_header_t *next_ext_header;
843   u16 total_size = 0;
844
845   next_proto = ip0->protocol;
846   next_ext_header = (void *) (ip0 + 1);
847   total_size = sizeof (ip6_header_t);
848   while (ip6_ext_hdr (next_proto))
849     {
850       total_size += ip6_ext_header_len (next_ext_header);
851       next_proto = next_ext_header->next_hdr;
852       next_ext_header = ip6_ext_next_header (next_ext_header);
853     }
854
855   /* Ensure this is the last segment. Otherwise drop. */
856   if (sr0 && sr0->segments_left != 0)
857     {
858       *next0 = SR_LOCALSID_NEXT_ERROR;
859       b0->error = node->errors[SR_LOCALSID_ERROR_NOT_LS];
860       return;
861     }
862
863   switch (next_proto)
864     {
865     case IP_PROTOCOL_IPV6:
866       /* Encap-End IPv6. Pop outer IPv6 header. */
867       if (ls0->behavior == SR_BEHAVIOR_DX6)
868         {
869           vlib_buffer_advance (b0, total_size);
870           vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ls0->nh_adj;
871           *next0 = SR_LOCALSID_NEXT_IP6_REWRITE;
872           return;
873         }
874       else if (ls0->behavior == SR_BEHAVIOR_DT6)
875         {
876           vlib_buffer_advance (b0, total_size);
877           vnet_buffer (b0)->sw_if_index[VLIB_TX] = ls0->vrf_index;
878           return;
879         }
880       break;
881     case IP_PROTOCOL_IP_IN_IP:
882       /* Encap-End IPv4. Pop outer IPv6 header */
883       if (ls0->behavior == SR_BEHAVIOR_DX4)
884         {
885           vlib_buffer_advance (b0, total_size);
886           vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ls0->nh_adj;
887           *next0 = SR_LOCALSID_NEXT_IP4_REWRITE;
888           return;
889         }
890       else if (ls0->behavior == SR_BEHAVIOR_DT4)
891         {
892           vlib_buffer_advance (b0, total_size);
893           vnet_buffer (b0)->sw_if_index[VLIB_TX] = ls0->vrf_index;
894           *next0 = SR_LOCALSID_NEXT_IP4_LOOKUP;
895           return;
896         }
897       break;
898     case IP_PROTOCOL_IP6_NONXT:
899       /* L2 encaps */
900       if (ls0->behavior == SR_BEHAVIOR_DX2)
901         {
902           vlib_buffer_advance (b0, total_size);
903           vnet_buffer (b0)->sw_if_index[VLIB_TX] = ls0->sw_if_index;
904           *next0 = SR_LOCALSID_NEXT_INTERFACE_OUTPUT;
905           return;
906         }
907       break;
908     }
909   *next0 = SR_LOCALSID_NEXT_ERROR;
910   b0->error = node->errors[SR_LOCALSID_ERROR_NO_INNER_HEADER];
911   return;
912 }
913
914 /**
915  * @brief SR LocalSID graph node. Supports all default SR Endpoint variants with decaps
916  */
917 static uword
918 sr_localsid_d_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
919                   vlib_frame_t * from_frame)
920 {
921   u32 n_left_from, next_index, *from, *to_next;
922   ip6_sr_main_t *sm = &sr_main;
923   from = vlib_frame_vector_args (from_frame);
924   n_left_from = from_frame->n_vectors;
925   next_index = node->cached_next_index;
926   u32 thread_index = vm->thread_index;
927
928   while (n_left_from > 0)
929     {
930       u32 n_left_to_next;
931       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
932
933       /* Quad - Loop */
934       while (n_left_from >= 8 && n_left_to_next >= 4)
935         {
936           u32 bi0, bi1, bi2, bi3;
937           vlib_buffer_t *b0, *b1, *b2, *b3;
938           ip6_header_t *ip0, *ip1, *ip2, *ip3;
939           ip6_sr_header_t *sr0, *sr1, *sr2, *sr3;
940           u32 next0, next1, next2, next3;
941           next0 = next1 = next2 = next3 = SR_LOCALSID_NEXT_IP6_LOOKUP;
942           ip6_sr_localsid_t *ls0, *ls1, *ls2, *ls3;
943
944           /* Prefetch next iteration. */
945           {
946             vlib_buffer_t *p4, *p5, *p6, *p7;
947
948             p4 = vlib_get_buffer (vm, from[4]);
949             p5 = vlib_get_buffer (vm, from[5]);
950             p6 = vlib_get_buffer (vm, from[6]);
951             p7 = vlib_get_buffer (vm, from[7]);
952
953             /* Prefetch the buffer header and packet for the N+4 loop iteration */
954             vlib_prefetch_buffer_header (p4, LOAD);
955             vlib_prefetch_buffer_header (p5, LOAD);
956             vlib_prefetch_buffer_header (p6, LOAD);
957             vlib_prefetch_buffer_header (p7, LOAD);
958
959             CLIB_PREFETCH (p4->data, CLIB_CACHE_LINE_BYTES, STORE);
960             CLIB_PREFETCH (p5->data, CLIB_CACHE_LINE_BYTES, STORE);
961             CLIB_PREFETCH (p6->data, CLIB_CACHE_LINE_BYTES, STORE);
962             CLIB_PREFETCH (p7->data, CLIB_CACHE_LINE_BYTES, STORE);
963           }
964
965           to_next[0] = bi0 = from[0];
966           to_next[1] = bi1 = from[1];
967           to_next[2] = bi2 = from[2];
968           to_next[3] = bi3 = from[3];
969           from += 4;
970           to_next += 4;
971           n_left_from -= 4;
972           n_left_to_next -= 4;
973
974           b0 = vlib_get_buffer (vm, bi0);
975           b1 = vlib_get_buffer (vm, bi1);
976           b2 = vlib_get_buffer (vm, bi2);
977           b3 = vlib_get_buffer (vm, bi3);
978
979           ls0 =
980             pool_elt_at_index (sm->localsids,
981                                vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
982           ls1 =
983             pool_elt_at_index (sm->localsids,
984                                vnet_buffer (b1)->ip.adj_index[VLIB_TX]);
985           ls2 =
986             pool_elt_at_index (sm->localsids,
987                                vnet_buffer (b2)->ip.adj_index[VLIB_TX]);
988           ls3 =
989             pool_elt_at_index (sm->localsids,
990                                vnet_buffer (b3)->ip.adj_index[VLIB_TX]);
991
992           ip0 = vlib_buffer_get_current (b0);
993           ip1 = vlib_buffer_get_current (b1);
994           ip2 = vlib_buffer_get_current (b2);
995           ip3 = vlib_buffer_get_current (b3);
996
997           sr0 =
998             ip6_ext_header_find (vm, b0, ip0, IP_PROTOCOL_IPV6_ROUTE, NULL);
999           sr1 =
1000             ip6_ext_header_find (vm, b1, ip1, IP_PROTOCOL_IPV6_ROUTE, NULL);
1001           sr2 =
1002             ip6_ext_header_find (vm, b2, ip2, IP_PROTOCOL_IPV6_ROUTE, NULL);
1003           sr3 =
1004             ip6_ext_header_find (vm, b3, ip3, IP_PROTOCOL_IPV6_ROUTE, NULL);
1005
1006           end_decaps_srh_processing (node, b0, ip0, sr0, ls0, &next0);
1007           end_decaps_srh_processing (node, b1, ip1, sr1, ls1, &next1);
1008           end_decaps_srh_processing (node, b2, ip2, sr2, ls2, &next2);
1009           end_decaps_srh_processing (node, b3, ip3, sr3, ls3, &next3);
1010
1011           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
1012             {
1013               sr_localsid_trace_t *tr =
1014                 vlib_add_trace (vm, node, b0, sizeof (*tr));
1015               tr->num_segments = 0;
1016               clib_memcpy (tr->localsid.as_u8, ls0->localsid.as_u8,
1017                            sizeof (tr->localsid.as_u8));
1018               tr->behavior = ls0->behavior;
1019               if (ip0 == vlib_buffer_get_current (b0))
1020                 {
1021                   if (ip0->protocol == IP_PROTOCOL_IPV6_ROUTE
1022                       && sr0->type == ROUTING_HEADER_TYPE_SR)
1023                     {
1024                       clib_memcpy (tr->sr, sr0->segments, sr0->length * 8);
1025                       tr->num_segments =
1026                         sr0->length * 8 / sizeof (ip6_address_t);
1027                       tr->segments_left = sr0->segments_left;
1028                     }
1029                 }
1030               else
1031                 tr->num_segments = 0xFF;
1032             }
1033
1034           if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
1035             {
1036               sr_localsid_trace_t *tr =
1037                 vlib_add_trace (vm, node, b1, sizeof (*tr));
1038               tr->num_segments = 0;
1039               clib_memcpy (tr->localsid.as_u8, ls1->localsid.as_u8,
1040                            sizeof (tr->localsid.as_u8));
1041               tr->behavior = ls1->behavior;
1042               if (ip1 == vlib_buffer_get_current (b1))
1043                 {
1044                   if (ip1->protocol == IP_PROTOCOL_IPV6_ROUTE
1045                       && sr1->type == ROUTING_HEADER_TYPE_SR)
1046                     {
1047                       clib_memcpy (tr->sr, sr1->segments, sr1->length * 8);
1048                       tr->num_segments =
1049                         sr1->length * 8 / sizeof (ip6_address_t);
1050                       tr->segments_left = sr1->segments_left;
1051                     }
1052                 }
1053               else
1054                 tr->num_segments = 0xFF;
1055             }
1056
1057           if (PREDICT_FALSE (b2->flags & VLIB_BUFFER_IS_TRACED))
1058             {
1059               sr_localsid_trace_t *tr =
1060                 vlib_add_trace (vm, node, b2, sizeof (*tr));
1061               tr->num_segments = 0;
1062               clib_memcpy (tr->localsid.as_u8, ls2->localsid.as_u8,
1063                            sizeof (tr->localsid.as_u8));
1064               tr->behavior = ls2->behavior;
1065               if (ip2 == vlib_buffer_get_current (b2))
1066                 {
1067                   if (ip2->protocol == IP_PROTOCOL_IPV6_ROUTE
1068                       && sr2->type == ROUTING_HEADER_TYPE_SR)
1069                     {
1070                       clib_memcpy (tr->sr, sr2->segments, sr2->length * 8);
1071                       tr->num_segments =
1072                         sr2->length * 8 / sizeof (ip6_address_t);
1073                       tr->segments_left = sr2->segments_left;
1074                     }
1075                 }
1076               else
1077                 tr->num_segments = 0xFF;
1078             }
1079
1080           if (PREDICT_FALSE (b3->flags & VLIB_BUFFER_IS_TRACED))
1081             {
1082               sr_localsid_trace_t *tr =
1083                 vlib_add_trace (vm, node, b3, sizeof (*tr));
1084               tr->num_segments = 0;
1085               clib_memcpy (tr->localsid.as_u8, ls3->localsid.as_u8,
1086                            sizeof (tr->localsid.as_u8));
1087               tr->behavior = ls3->behavior;
1088               if (ip3 == vlib_buffer_get_current (b3))
1089                 {
1090                   if (ip3->protocol == IP_PROTOCOL_IPV6_ROUTE
1091                       && sr3->type == ROUTING_HEADER_TYPE_SR)
1092                     {
1093                       clib_memcpy (tr->sr, sr3->segments, sr3->length * 8);
1094                       tr->num_segments =
1095                         sr3->length * 8 / sizeof (ip6_address_t);
1096                       tr->segments_left = sr3->segments_left;
1097                     }
1098                 }
1099               else
1100                 tr->num_segments = 0xFF;
1101             }
1102
1103           vlib_increment_combined_counter
1104             (((next0 ==
1105                SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) :
1106               &(sm->sr_ls_valid_counters)), thread_index, ls0 - sm->localsids,
1107              1, vlib_buffer_length_in_chain (vm, b0));
1108
1109           vlib_increment_combined_counter
1110             (((next1 ==
1111                SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) :
1112               &(sm->sr_ls_valid_counters)), thread_index, ls1 - sm->localsids,
1113              1, vlib_buffer_length_in_chain (vm, b1));
1114
1115           vlib_increment_combined_counter
1116             (((next2 ==
1117                SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) :
1118               &(sm->sr_ls_valid_counters)), thread_index, ls2 - sm->localsids,
1119              1, vlib_buffer_length_in_chain (vm, b2));
1120
1121           vlib_increment_combined_counter
1122             (((next3 ==
1123                SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) :
1124               &(sm->sr_ls_valid_counters)), thread_index, ls3 - sm->localsids,
1125              1, vlib_buffer_length_in_chain (vm, b3));
1126
1127           vlib_validate_buffer_enqueue_x4 (vm, node, next_index, to_next,
1128                                            n_left_to_next, bi0, bi1, bi2, bi3,
1129                                            next0, next1, next2, next3);
1130         }
1131
1132       /* Single loop for potentially the last three packets */
1133       while (n_left_from > 0 && n_left_to_next > 0)
1134         {
1135           u32 bi0;
1136           vlib_buffer_t *b0;
1137           ip6_header_t *ip0;
1138           ip6_sr_header_t *sr0;
1139           u32 next0 = SR_LOCALSID_NEXT_IP6_LOOKUP;
1140           ip6_sr_localsid_t *ls0;
1141
1142           bi0 = from[0];
1143           to_next[0] = bi0;
1144           from += 1;
1145           to_next += 1;
1146           n_left_from -= 1;
1147           n_left_to_next -= 1;
1148
1149           b0 = vlib_get_buffer (vm, bi0);
1150           ip0 = vlib_buffer_get_current (b0);
1151
1152           /* Lookup the SR End behavior based on IP DA (adj) */
1153           ls0 =
1154             pool_elt_at_index (sm->localsids,
1155                                vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
1156
1157           /* Find SRH as well as previous header */
1158           sr0 =
1159             ip6_ext_header_find (vm, b0, ip0, IP_PROTOCOL_IPV6_ROUTE, NULL);
1160
1161           /* SRH processing and End variants */
1162           end_decaps_srh_processing (node, b0, ip0, sr0, ls0, &next0);
1163
1164           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
1165             {
1166               sr_localsid_trace_t *tr =
1167                 vlib_add_trace (vm, node, b0, sizeof (*tr));
1168               tr->num_segments = 0;
1169               clib_memcpy (tr->localsid.as_u8, ls0->localsid.as_u8,
1170                            sizeof (tr->localsid.as_u8));
1171               tr->behavior = ls0->behavior;
1172               if (ip0 == vlib_buffer_get_current (b0))
1173                 {
1174                   if (ip0->protocol == IP_PROTOCOL_IPV6_ROUTE
1175                       && sr0->type == ROUTING_HEADER_TYPE_SR)
1176                     {
1177                       clib_memcpy (tr->sr, sr0->segments, sr0->length * 8);
1178                       tr->num_segments =
1179                         sr0->length * 8 / sizeof (ip6_address_t);
1180                       tr->segments_left = sr0->segments_left;
1181                     }
1182                 }
1183               else
1184                 tr->num_segments = 0xFF;
1185             }
1186
1187           /* Increase the counters */
1188           vlib_increment_combined_counter
1189             (((next0 ==
1190                SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) :
1191               &(sm->sr_ls_valid_counters)), thread_index, ls0 - sm->localsids,
1192              1, vlib_buffer_length_in_chain (vm, b0));
1193
1194           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
1195                                            n_left_to_next, bi0, next0);
1196         }
1197       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1198     }
1199   return from_frame->n_vectors;
1200 }
1201
1202 /* *INDENT-OFF* */
1203 VLIB_REGISTER_NODE (sr_localsid_d_node) = {
1204   .function = sr_localsid_d_fn,
1205   .name = "sr-localsid-d",
1206   .vector_size = sizeof (u32),
1207   .format_trace = format_sr_localsid_trace,
1208   .type = VLIB_NODE_TYPE_INTERNAL,
1209   .n_errors = SR_LOCALSID_N_ERROR,
1210   .error_strings = sr_localsid_error_strings,
1211   .n_next_nodes = SR_LOCALSID_N_NEXT,
1212   .next_nodes = {
1213 #define _(s,n) [SR_LOCALSID_NEXT_##s] = n,
1214     foreach_sr_localsid_next
1215 #undef _
1216   },
1217 };
1218 /* *INDENT-ON* */
1219
1220 /**
1221  * @brief SR LocalSID graph node. Supports all default SR Endpoint without decaps
1222  */
1223 static uword
1224 sr_localsid_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1225                 vlib_frame_t * from_frame)
1226 {
1227   u32 n_left_from, next_index, *from, *to_next;
1228   ip6_sr_main_t *sm = &sr_main;
1229   from = vlib_frame_vector_args (from_frame);
1230   n_left_from = from_frame->n_vectors;
1231   next_index = node->cached_next_index;
1232   u32 thread_index = vm->thread_index;
1233
1234   while (n_left_from > 0)
1235     {
1236       u32 n_left_to_next;
1237       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
1238
1239       /* Quad - Loop */
1240       while (n_left_from >= 8 && n_left_to_next >= 4)
1241         {
1242           u32 bi0, bi1, bi2, bi3;
1243           vlib_buffer_t *b0, *b1, *b2, *b3;
1244           ip6_header_t *ip0, *ip1, *ip2, *ip3;
1245           ip6_sr_header_t *sr0, *sr1, *sr2, *sr3;
1246           ip6_ext_header_t *prev0, *prev1, *prev2, *prev3;
1247           u32 next0, next1, next2, next3;
1248           next0 = next1 = next2 = next3 = SR_LOCALSID_NEXT_IP6_LOOKUP;
1249           ip6_sr_localsid_t *ls0, *ls1, *ls2, *ls3;
1250
1251           /* Prefetch next iteration. */
1252           {
1253             vlib_buffer_t *p4, *p5, *p6, *p7;
1254
1255             p4 = vlib_get_buffer (vm, from[4]);
1256             p5 = vlib_get_buffer (vm, from[5]);
1257             p6 = vlib_get_buffer (vm, from[6]);
1258             p7 = vlib_get_buffer (vm, from[7]);
1259
1260             /* Prefetch the buffer header and packet for the N+2 loop iteration */
1261             vlib_prefetch_buffer_header (p4, LOAD);
1262             vlib_prefetch_buffer_header (p5, LOAD);
1263             vlib_prefetch_buffer_header (p6, LOAD);
1264             vlib_prefetch_buffer_header (p7, LOAD);
1265
1266             CLIB_PREFETCH (p4->data, CLIB_CACHE_LINE_BYTES, STORE);
1267             CLIB_PREFETCH (p5->data, CLIB_CACHE_LINE_BYTES, STORE);
1268             CLIB_PREFETCH (p6->data, CLIB_CACHE_LINE_BYTES, STORE);
1269             CLIB_PREFETCH (p7->data, CLIB_CACHE_LINE_BYTES, STORE);
1270           }
1271
1272           to_next[0] = bi0 = from[0];
1273           to_next[1] = bi1 = from[1];
1274           to_next[2] = bi2 = from[2];
1275           to_next[3] = bi3 = from[3];
1276           from += 4;
1277           to_next += 4;
1278           n_left_from -= 4;
1279           n_left_to_next -= 4;
1280
1281           b0 = vlib_get_buffer (vm, bi0);
1282           b1 = vlib_get_buffer (vm, bi1);
1283           b2 = vlib_get_buffer (vm, bi2);
1284           b3 = vlib_get_buffer (vm, bi3);
1285
1286           ip0 = vlib_buffer_get_current (b0);
1287           ip1 = vlib_buffer_get_current (b1);
1288           ip2 = vlib_buffer_get_current (b2);
1289           ip3 = vlib_buffer_get_current (b3);
1290
1291           sr0 =
1292             ip6_ext_header_find (vm, b0, ip0, IP_PROTOCOL_IPV6_ROUTE, &prev0);
1293           sr1 =
1294             ip6_ext_header_find (vm, b1, ip1, IP_PROTOCOL_IPV6_ROUTE, &prev1);
1295           sr2 =
1296             ip6_ext_header_find (vm, b2, ip2, IP_PROTOCOL_IPV6_ROUTE, &prev2);
1297           sr3 =
1298             ip6_ext_header_find (vm, b3, ip3, IP_PROTOCOL_IPV6_ROUTE, &prev3);
1299
1300           ls0 =
1301             pool_elt_at_index (sm->localsids,
1302                                vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
1303           ls1 =
1304             pool_elt_at_index (sm->localsids,
1305                                vnet_buffer (b1)->ip.adj_index[VLIB_TX]);
1306           ls2 =
1307             pool_elt_at_index (sm->localsids,
1308                                vnet_buffer (b2)->ip.adj_index[VLIB_TX]);
1309           ls3 =
1310             pool_elt_at_index (sm->localsids,
1311                                vnet_buffer (b3)->ip.adj_index[VLIB_TX]);
1312
1313           end_srh_processing (node, b0, ip0, sr0, ls0, &next0, ls0->end_psp,
1314                               prev0);
1315           end_srh_processing (node, b1, ip1, sr1, ls1, &next1, ls1->end_psp,
1316                               prev1);
1317           end_srh_processing (node, b2, ip2, sr2, ls2, &next2, ls2->end_psp,
1318                               prev2);
1319           end_srh_processing (node, b3, ip3, sr3, ls3, &next3, ls3->end_psp,
1320                               prev3);
1321
1322           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
1323             {
1324               sr_localsid_trace_t *tr =
1325                 vlib_add_trace (vm, node, b0, sizeof (*tr));
1326               tr->num_segments = 0;
1327               clib_memcpy (tr->localsid.as_u8, ls0->localsid.as_u8,
1328                            sizeof (tr->localsid.as_u8));
1329               tr->behavior = ls0->behavior;
1330               if (ip0 == vlib_buffer_get_current (b0))
1331                 {
1332                   if (ip0->protocol == IP_PROTOCOL_IPV6_ROUTE
1333                       && sr0->type == ROUTING_HEADER_TYPE_SR)
1334                     {
1335                       clib_memcpy (tr->sr, sr0->segments, sr0->length * 8);
1336                       tr->num_segments =
1337                         sr0->length * 8 / sizeof (ip6_address_t);
1338                       tr->segments_left = sr0->segments_left;
1339                     }
1340                 }
1341               else
1342                 tr->num_segments = 0xFF;
1343             }
1344
1345           if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
1346             {
1347               sr_localsid_trace_t *tr =
1348                 vlib_add_trace (vm, node, b1, sizeof (*tr));
1349               tr->num_segments = 0;
1350               clib_memcpy (tr->localsid.as_u8, ls1->localsid.as_u8,
1351                            sizeof (tr->localsid.as_u8));
1352               tr->behavior = ls1->behavior;
1353               if (ip1 == vlib_buffer_get_current (b1))
1354                 {
1355                   if (ip1->protocol == IP_PROTOCOL_IPV6_ROUTE
1356                       && sr1->type == ROUTING_HEADER_TYPE_SR)
1357                     {
1358                       clib_memcpy (tr->sr, sr1->segments, sr1->length * 8);
1359                       tr->num_segments =
1360                         sr1->length * 8 / sizeof (ip6_address_t);
1361                       tr->segments_left = sr1->segments_left;
1362                     }
1363                 }
1364               else
1365                 tr->num_segments = 0xFF;
1366             }
1367
1368           if (PREDICT_FALSE (b2->flags & VLIB_BUFFER_IS_TRACED))
1369             {
1370               sr_localsid_trace_t *tr =
1371                 vlib_add_trace (vm, node, b2, sizeof (*tr));
1372               tr->num_segments = 0;
1373               clib_memcpy (tr->localsid.as_u8, ls2->localsid.as_u8,
1374                            sizeof (tr->localsid.as_u8));
1375               tr->behavior = ls2->behavior;
1376               if (ip2 == vlib_buffer_get_current (b2))
1377                 {
1378                   if (ip2->protocol == IP_PROTOCOL_IPV6_ROUTE
1379                       && sr2->type == ROUTING_HEADER_TYPE_SR)
1380                     {
1381                       clib_memcpy (tr->sr, sr2->segments, sr2->length * 8);
1382                       tr->num_segments =
1383                         sr2->length * 8 / sizeof (ip6_address_t);
1384                       tr->segments_left = sr2->segments_left;
1385                     }
1386                 }
1387               else
1388                 tr->num_segments = 0xFF;
1389             }
1390
1391           if (PREDICT_FALSE (b3->flags & VLIB_BUFFER_IS_TRACED))
1392             {
1393               sr_localsid_trace_t *tr =
1394                 vlib_add_trace (vm, node, b3, sizeof (*tr));
1395               tr->num_segments = 0;
1396               clib_memcpy (tr->localsid.as_u8, ls3->localsid.as_u8,
1397                            sizeof (tr->localsid.as_u8));
1398               tr->behavior = ls3->behavior;
1399               if (ip3 == vlib_buffer_get_current (b3))
1400                 {
1401                   if (ip3->protocol == IP_PROTOCOL_IPV6_ROUTE
1402                       && sr3->type == ROUTING_HEADER_TYPE_SR)
1403                     {
1404                       clib_memcpy (tr->sr, sr3->segments, sr3->length * 8);
1405                       tr->num_segments =
1406                         sr3->length * 8 / sizeof (ip6_address_t);
1407                       tr->segments_left = sr3->segments_left;
1408                     }
1409                 }
1410               else
1411                 tr->num_segments = 0xFF;
1412             }
1413
1414           vlib_increment_combined_counter
1415             (((next0 ==
1416                SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) :
1417               &(sm->sr_ls_valid_counters)), thread_index, ls0 - sm->localsids,
1418              1, vlib_buffer_length_in_chain (vm, b0));
1419
1420           vlib_increment_combined_counter
1421             (((next1 ==
1422                SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) :
1423               &(sm->sr_ls_valid_counters)), thread_index, ls1 - sm->localsids,
1424              1, vlib_buffer_length_in_chain (vm, b1));
1425
1426           vlib_increment_combined_counter
1427             (((next2 ==
1428                SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) :
1429               &(sm->sr_ls_valid_counters)), thread_index, ls2 - sm->localsids,
1430              1, vlib_buffer_length_in_chain (vm, b2));
1431
1432           vlib_increment_combined_counter
1433             (((next3 ==
1434                SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) :
1435               &(sm->sr_ls_valid_counters)), thread_index, ls3 - sm->localsids,
1436              1, vlib_buffer_length_in_chain (vm, b3));
1437
1438           vlib_validate_buffer_enqueue_x4 (vm, node, next_index, to_next,
1439                                            n_left_to_next, bi0, bi1, bi2, bi3,
1440                                            next0, next1, next2, next3);
1441         }
1442
1443       /* Single loop for potentially the last three packets */
1444       while (n_left_from > 0 && n_left_to_next > 0)
1445         {
1446           u32 bi0;
1447           vlib_buffer_t *b0;
1448           ip6_header_t *ip0 = 0;
1449           ip6_ext_header_t *prev0;
1450           ip6_sr_header_t *sr0;
1451           u32 next0 = SR_LOCALSID_NEXT_IP6_LOOKUP;
1452           ip6_sr_localsid_t *ls0;
1453
1454           bi0 = from[0];
1455           to_next[0] = bi0;
1456           from += 1;
1457           to_next += 1;
1458           n_left_from -= 1;
1459           n_left_to_next -= 1;
1460
1461           b0 = vlib_get_buffer (vm, bi0);
1462           ip0 = vlib_buffer_get_current (b0);
1463           sr0 =
1464             ip6_ext_header_find (vm, b0, ip0, IP_PROTOCOL_IPV6_ROUTE, &prev0);
1465
1466           /* Lookup the SR End behavior based on IP DA (adj) */
1467           ls0 =
1468             pool_elt_at_index (sm->localsids,
1469                                vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
1470
1471           /* SRH processing */
1472           end_srh_processing (node, b0, ip0, sr0, ls0, &next0, ls0->end_psp,
1473                               prev0);
1474
1475           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
1476             {
1477               sr_localsid_trace_t *tr =
1478                 vlib_add_trace (vm, node, b0, sizeof (*tr));
1479               tr->num_segments = 0;
1480               clib_memcpy (tr->localsid.as_u8, ls0->localsid.as_u8,
1481                            sizeof (tr->localsid.as_u8));
1482               tr->behavior = ls0->behavior;
1483               if (ip0 == vlib_buffer_get_current (b0))
1484                 {
1485                   if (ip0->protocol == IP_PROTOCOL_IPV6_ROUTE
1486                       && sr0->type == ROUTING_HEADER_TYPE_SR)
1487                     {
1488                       clib_memcpy (tr->sr, sr0->segments, sr0->length * 8);
1489                       tr->num_segments =
1490                         sr0->length * 8 / sizeof (ip6_address_t);
1491                       tr->segments_left = sr0->segments_left;
1492                     }
1493                 }
1494               else
1495                 tr->num_segments = 0xFF;
1496             }
1497
1498           vlib_increment_combined_counter
1499             (((next0 ==
1500                SR_LOCALSID_NEXT_ERROR) ? &(sm->sr_ls_invalid_counters) :
1501               &(sm->sr_ls_valid_counters)), thread_index, ls0 - sm->localsids,
1502              1, vlib_buffer_length_in_chain (vm, b0));
1503
1504           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
1505                                            n_left_to_next, bi0, next0);
1506         }
1507       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1508     }
1509   return from_frame->n_vectors;
1510 }
1511
1512 /* *INDENT-OFF* */
1513 VLIB_REGISTER_NODE (sr_localsid_node) = {
1514   .function = sr_localsid_fn,
1515   .name = "sr-localsid",
1516   .vector_size = sizeof (u32),
1517   .format_trace = format_sr_localsid_trace,
1518   .type = VLIB_NODE_TYPE_INTERNAL,
1519   .n_errors = SR_LOCALSID_N_ERROR,
1520   .error_strings = sr_localsid_error_strings,
1521   .n_next_nodes = SR_LOCALSID_N_NEXT,
1522   .next_nodes = {
1523 #define _(s,n) [SR_LOCALSID_NEXT_##s] = n,
1524     foreach_sr_localsid_next
1525 #undef _
1526   },
1527 };
1528 /* *INDENT-ON* */
1529
1530 static u8 *
1531 format_sr_dpo (u8 * s, va_list * args)
1532 {
1533   index_t index = va_arg (*args, index_t);
1534   CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
1535
1536   return (format (s, "SR: localsid_index:[%d]", index));
1537 }
1538
1539 const static dpo_vft_t sr_loc_vft = {
1540   .dv_lock = sr_dpo_lock,
1541   .dv_unlock = sr_dpo_unlock,
1542   .dv_format = format_sr_dpo,
1543 };
1544
1545 const static char *const sr_loc_ip6_nodes[] = {
1546   "sr-localsid",
1547   NULL,
1548 };
1549
1550 const static char *const *const sr_loc_nodes[DPO_PROTO_NUM] = {
1551   [DPO_PROTO_IP6] = sr_loc_ip6_nodes,
1552 };
1553
1554 const static char *const sr_loc_d_ip6_nodes[] = {
1555   "sr-localsid-d",
1556   NULL,
1557 };
1558
1559 const static char *const *const sr_loc_d_nodes[DPO_PROTO_NUM] = {
1560   [DPO_PROTO_IP6] = sr_loc_d_ip6_nodes,
1561 };
1562
1563
1564 /*************************** SR LocalSID plugins ******************************/
1565 /**
1566  * @brief SR LocalSID plugin registry
1567  */
1568 int
1569 sr_localsid_register_function (vlib_main_t * vm, u8 * fn_name,
1570                                u8 * keyword_str, u8 * def_str,
1571                                u8 * params_str, u8 prefix_length,
1572                                dpo_type_t * dpo,
1573                                format_function_t * ls_format,
1574                                unformat_function_t * ls_unformat,
1575                                sr_plugin_callback_t * creation_fn,
1576                                sr_plugin_callback_t * removal_fn)
1577 {
1578   ip6_sr_main_t *sm = &sr_main;
1579   uword *p;
1580
1581   sr_localsid_fn_registration_t *plugin;
1582
1583   /* Did this function exist? If so update it */
1584   p = hash_get_mem (sm->plugin_functions_by_key, fn_name);
1585   if (p)
1586     {
1587       plugin = pool_elt_at_index (sm->plugin_functions, p[0]);
1588     }
1589   /* Else create a new one and set hash key */
1590   else
1591     {
1592       pool_get (sm->plugin_functions, plugin);
1593       hash_set_mem (sm->plugin_functions_by_key, fn_name,
1594                     plugin - sm->plugin_functions);
1595     }
1596
1597   clib_memset (plugin, 0, sizeof (*plugin));
1598
1599   plugin->sr_localsid_function_number = (plugin - sm->plugin_functions);
1600   plugin->sr_localsid_function_number += SR_BEHAVIOR_LAST;
1601   plugin->prefix_length = prefix_length;
1602   plugin->ls_format = ls_format;
1603   plugin->ls_unformat = ls_unformat;
1604   plugin->creation = creation_fn;
1605   plugin->removal = removal_fn;
1606   clib_memcpy (&plugin->dpo, dpo, sizeof (dpo_type_t));
1607   plugin->function_name = format (0, "%s%c", fn_name, 0);
1608   plugin->keyword_str = format (0, "%s%c", keyword_str, 0);
1609   plugin->def_str = format (0, "%s%c", def_str, 0);
1610   plugin->params_str = format (0, "%s%c", params_str, 0);
1611
1612   return plugin->sr_localsid_function_number;
1613 }
1614
1615 /**
1616  * @brief CLI function to 'show' all available SR LocalSID behaviors
1617  */
1618 static clib_error_t *
1619 show_sr_localsid_behaviors_command_fn (vlib_main_t * vm,
1620                                        unformat_input_t * input,
1621                                        vlib_cli_command_t * cmd)
1622 {
1623   ip6_sr_main_t *sm = &sr_main;
1624   sr_localsid_fn_registration_t *plugin;
1625   sr_localsid_fn_registration_t **plugins_vec = 0;
1626   int i;
1627
1628   vlib_cli_output (vm,
1629                    "SR LocalSIDs behaviors:\n-----------------------\n\n");
1630
1631   /* *INDENT-OFF* */
1632   pool_foreach (plugin, sm->plugin_functions,
1633     ({ vec_add1 (plugins_vec, plugin); }));
1634   /* *INDENT-ON* */
1635
1636   /* Print static behaviors */
1637   vlib_cli_output (vm, "Default behaviors:\n"
1638                    "\tEnd\t-> Endpoint.\n"
1639                    "\tEnd.X\t-> Endpoint with Layer-3 cross-connect.\n"
1640                    "\t\tParameters: '<iface> <ip6_next_hop>'\n"
1641                    "\tEnd.T\t-> Endpoint with specific IPv6 table lookup.\n"
1642                    "\t\tParameters: '<fib_table>'\n"
1643                    "\tEnd.DX2\t-> Endpoint with decapsulation and Layer-2 cross-connect.\n"
1644                    "\t\tParameters: '<iface>'\n"
1645                    "\tEnd.DX6\t-> Endpoint with decapsulation and IPv6 cross-connect.\n"
1646                    "\t\tParameters: '<iface> <ip6_next_hop>'\n"
1647                    "\tEnd.DX4\t-> Endpoint with decapsulation and IPv4 cross-connect.\n"
1648                    "\t\tParameters: '<iface> <ip4_next_hop>'\n"
1649                    "\tEnd.DT6\t-> Endpoint with decapsulation and specific IPv6 table lookup.\n"
1650                    "\t\tParameters: '<ip6_fib_table>'\n"
1651                    "\tEnd.DT4\t-> Endpoint with decapsulation and specific IPv4 table lookup.\n"
1652                    "\t\tParameters: '<ip4_fib_table>'\n");
1653   vlib_cli_output (vm, "Plugin behaviors:\n");
1654   for (i = 0; i < vec_len (plugins_vec); i++)
1655     {
1656       plugin = plugins_vec[i];
1657       vlib_cli_output (vm, "\t%s\t-> %s.\n", plugin->keyword_str,
1658                        plugin->def_str);
1659       vlib_cli_output (vm, "\t\tParameters: '%s'\n", plugin->params_str);
1660     }
1661   return 0;
1662 }
1663
1664 /* *INDENT-OFF* */
1665 VLIB_CLI_COMMAND (show_sr_localsid_behaviors_command, static) = {
1666   .path = "show sr localsids behaviors",
1667   .short_help = "show sr localsids behaviors",
1668   .function = show_sr_localsid_behaviors_command_fn,
1669 };
1670 /* *INDENT-ON* */
1671
1672 /**
1673  * @brief SR LocalSID initialization
1674  */
1675 clib_error_t *
1676 sr_localsids_init (vlib_main_t * vm)
1677 {
1678   /* Init memory for function keys */
1679   ip6_sr_main_t *sm = &sr_main;
1680   mhash_init (&sm->sr_localsids_index_hash, sizeof (uword),
1681               sizeof (ip6_address_t));
1682   /* Init SR behaviors DPO type */
1683   sr_localsid_dpo_type = dpo_register_new_type (&sr_loc_vft, sr_loc_nodes);
1684   /* Init SR behaviors DPO type */
1685   sr_localsid_d_dpo_type =
1686     dpo_register_new_type (&sr_loc_vft, sr_loc_d_nodes);
1687   /* Init memory for localsid plugins */
1688   sm->plugin_functions_by_key = hash_create_string (0, sizeof (uword));
1689   return 0;
1690 }
1691
1692 VLIB_INIT_FUNCTION (sr_localsids_init);
1693 /*
1694 * fd.io coding-style-patch-verification: ON
1695 *
1696 * Local Variables:
1697 * eval: (c-set-style "gnu")
1698 * End:
1699 */