LCOV - code coverage report
Current view: top level - vnet/ip - ip6_hop_by_hop.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 96 586 16.4 %
Date: 2023-10-26 01:39:38 Functions: 37 65 56.9 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2016 Cisco and/or its affiliates.
       3             :  * Licensed under the Apache License, Version 2.0 (the "License");
       4             :  * you may not use this file except in compliance with the License.
       5             :  * You may obtain a copy of the License at:
       6             :  *
       7             :  *     http://www.apache.org/licenses/LICENSE-2.0
       8             :  *
       9             :  * Unless required by applicable law or agreed to in writing, software
      10             :  * distributed under the License is distributed on an "AS IS" BASIS,
      11             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             :  * See the License for the specific language governing permissions and
      13             :  * limitations under the License.
      14             :  */
      15             : #include <vlib/vlib.h>
      16             : #include <vnet/vnet.h>
      17             : #include <vnet/pg/pg.h>
      18             : #include <vppinfra/error.h>
      19             : 
      20             : #include <vnet/ip/ip.h>
      21             : 
      22             : #include <vppinfra/hash.h>
      23             : #include <vppinfra/error.h>
      24             : #include <vppinfra/elog.h>
      25             : 
      26             : #include <vnet/ip/ip6_hop_by_hop.h>
      27             : #include <vnet/fib/ip6_fib.h>
      28             : #include <vnet/classify/vnet_classify.h>
      29             : 
      30             : /**
      31             :  * @file
      32             :  * @brief In-band OAM (iOAM).
      33             :  *
      34             :  * In-band OAM (iOAM) is an implementation study to record operational
      35             :  * information in the packet while the packet traverses a path between
      36             :  * two points in the network.
      37             :  *
      38             :  * VPP can function as in-band OAM encapsulating, transit and
      39             :  * decapsulating node. In this version of VPP in-band OAM data is
      40             :  * transported as options in an IPv6 hop-by-hop extension header. Hence
      41             :  * in-band OAM can be enabled for IPv6 traffic.
      42             :  */
      43             : 
      44             : #ifndef CLIB_MARCH_VARIANT
      45             : ip6_hop_by_hop_ioam_main_t ip6_hop_by_hop_ioam_main;
      46             : #endif /* CLIB_MARCH_VARIANT */
      47             : 
      48             : #define foreach_ip6_hbyh_ioam_input_next        \
      49             :   _(IP6_REWRITE, "ip6-rewrite")                       \
      50             :   _(IP6_LOOKUP, "ip6-lookup")                 \
      51             :   _(DROP, "ip6-drop")
      52             : 
      53             : typedef enum
      54             : {
      55             : #define _(s,n) IP6_HBYH_IOAM_INPUT_NEXT_##s,
      56             :   foreach_ip6_hbyh_ioam_input_next
      57             : #undef _
      58             :     IP6_HBYH_IOAM_INPUT_N_NEXT,
      59             : } ip6_hbyh_ioam_input_next_t;
      60             : 
      61             : #ifndef CLIB_MARCH_VARIANT
      62             : static uword
      63           0 : unformat_opaque_ioam (unformat_input_t * input, va_list * args)
      64             : {
      65           0 :   u64 *opaquep = va_arg (*args, u64 *);
      66           0 :   u8 *flow_name = NULL;
      67           0 :   uword ret = 0;
      68             : 
      69           0 :   if (unformat (input, "ioam-encap %s", &flow_name))
      70             :     {
      71           0 :       *opaquep = ioam_flow_add (1, flow_name);
      72           0 :       ret = 1;
      73             :     }
      74           0 :   else if (unformat (input, "ioam-decap %s", &flow_name))
      75             :     {
      76           0 :       *opaquep = ioam_flow_add (0, flow_name);
      77           0 :       ret = 1;
      78             :     }
      79             : 
      80           0 :   vec_free (flow_name);
      81           0 :   return ret;
      82             : }
      83             : 
      84             : u8 *
      85           0 : get_flow_name_from_flow_ctx (u32 flow_ctx)
      86             : {
      87           0 :   flow_data_t *flow = NULL;
      88           0 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
      89             :   u32 index;
      90             : 
      91           0 :   index = IOAM_MASK_DECAP_BIT (flow_ctx);
      92             : 
      93           0 :   if (pool_is_free_index (hm->flows, index))
      94           0 :     return NULL;
      95             : 
      96           0 :   flow = pool_elt_at_index (hm->flows, index);
      97           0 :   return (flow->flow_name);
      98             : }
      99             : 
     100             : /* The main h-b-h tracer will be invoked, no need to do much here */
     101             : int
     102        1725 : ip6_hbh_add_register_option (u8 option,
     103             :                              u8 size,
     104             :                              int rewrite_options (u8 * rewrite_string,
     105             :                                                   u8 * rewrite_size))
     106             : {
     107        1725 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
     108             : 
     109             :   ASSERT ((u32) option < ARRAY_LEN (hm->add_options));
     110             : 
     111             :   /* Already registered */
     112        1725 :   if (hm->add_options[option])
     113           0 :     return (-1);
     114             : 
     115        1725 :   hm->add_options[option] = rewrite_options;
     116        1725 :   hm->options_size[option] = size;
     117             : 
     118        1725 :   return (0);
     119             : }
     120             : 
     121             : int
     122           0 : ip6_hbh_add_unregister_option (u8 option)
     123             : {
     124           0 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
     125             : 
     126             :   ASSERT ((u32) option < ARRAY_LEN (hm->add_options));
     127             : 
     128             :   /* Not registered */
     129           0 :   if (!hm->add_options[option])
     130           0 :     return (-1);
     131             : 
     132           0 :   hm->add_options[option] = NULL;
     133           0 :   hm->options_size[option] = 0;
     134           0 :   return (0);
     135             : }
     136             : 
     137             : /* Config handler registration */
     138             : int
     139         575 : ip6_hbh_config_handler_register (u8 option,
     140             :                                  int config_handler (void *data, u8 disable))
     141             : {
     142         575 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
     143             : 
     144             :   ASSERT ((u32) option < ARRAY_LEN (hm->config_handler));
     145             : 
     146             :   /* Already registered  */
     147         575 :   if (hm->config_handler[option])
     148           0 :     return (VNET_API_ERROR_INVALID_REGISTRATION);
     149             : 
     150         575 :   hm->config_handler[option] = config_handler;
     151             : 
     152         575 :   return (0);
     153             : }
     154             : 
     155             : int
     156           0 : ip6_hbh_config_handler_unregister (u8 option)
     157             : {
     158           0 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
     159             : 
     160             :   ASSERT ((u32) option < ARRAY_LEN (hm->config_handler));
     161             : 
     162             :   /* Not registered */
     163           0 :   if (!hm->config_handler[option])
     164           0 :     return (VNET_API_ERROR_INVALID_REGISTRATION);
     165             : 
     166           0 :   hm->config_handler[option] = NULL;
     167           0 :   return (0);
     168             : }
     169             : 
     170             : /* Flow handler registration */
     171             : int
     172         575 : ip6_hbh_flow_handler_register (u8 option,
     173             :                                u32 ioam_flow_handler (u32 flow_ctx, u8 add))
     174             : {
     175         575 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
     176             : 
     177             :   ASSERT ((u32) option < ARRAY_LEN (hm->flow_handler));
     178             : 
     179             :   /* Already registered */
     180         575 :   if (hm->flow_handler[option])
     181           0 :     return (VNET_API_ERROR_INVALID_REGISTRATION);
     182             : 
     183         575 :   hm->flow_handler[option] = ioam_flow_handler;
     184             : 
     185         575 :   return (0);
     186             : }
     187             : 
     188             : int
     189           0 : ip6_hbh_flow_handler_unregister (u8 option)
     190             : {
     191           0 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
     192             : 
     193             :   ASSERT ((u32) option < ARRAY_LEN (hm->flow_handler));
     194             : 
     195             :   /* Not registered */
     196           0 :   if (!hm->flow_handler[option])
     197           0 :     return (VNET_API_ERROR_INVALID_REGISTRATION);
     198             : 
     199           0 :   hm->flow_handler[option] = NULL;
     200           0 :   return (0);
     201             : }
     202             : #endif /* CLIB_MARCH_VARIANT */
     203             : 
     204             : typedef struct
     205             : {
     206             :   u32 next_index;
     207             : } ip6_add_hop_by_hop_trace_t;
     208             : 
     209             : /* packet trace format function */
     210             : static u8 *
     211           0 : format_ip6_add_hop_by_hop_trace (u8 * s, va_list * args)
     212             : {
     213           0 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
     214           0 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
     215           0 :   ip6_add_hop_by_hop_trace_t *t = va_arg (*args,
     216             :                                           ip6_add_hop_by_hop_trace_t *);
     217             : 
     218           0 :   s = format (s, "IP6_ADD_HOP_BY_HOP: next index %d", t->next_index);
     219           0 :   return s;
     220             : }
     221             : 
     222             : extern vlib_node_registration_t ip6_add_hop_by_hop_node;
     223             : 
     224             : #define foreach_ip6_add_hop_by_hop_error \
     225             : _(PROCESSED, "Pkts w/ added ip6 hop-by-hop options")
     226             : 
     227             : typedef enum
     228             : {
     229             : #define _(sym,str) IP6_ADD_HOP_BY_HOP_ERROR_##sym,
     230             :   foreach_ip6_add_hop_by_hop_error
     231             : #undef _
     232             :     IP6_ADD_HOP_BY_HOP_N_ERROR,
     233             : } ip6_add_hop_by_hop_error_t;
     234             : 
     235             : static char *ip6_add_hop_by_hop_error_strings[] = {
     236             : #define _(sym,string) string,
     237             :   foreach_ip6_add_hop_by_hop_error
     238             : #undef _
     239             : };
     240             : 
     241        2300 : VLIB_NODE_FN (ip6_add_hop_by_hop_node) (vlib_main_t * vm,
     242             :                                         vlib_node_runtime_t * node,
     243             :                                         vlib_frame_t * frame)
     244             : {
     245           0 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
     246             :   u32 n_left_from, *from, *to_next;
     247             :   ip_lookup_next_t next_index;
     248           0 :   u32 processed = 0;
     249           0 :   u8 *rewrite = hm->rewrite;
     250           0 :   u32 rewrite_length = vec_len (rewrite);
     251             : 
     252           0 :   from = vlib_frame_vector_args (frame);
     253           0 :   n_left_from = frame->n_vectors;
     254           0 :   next_index = node->cached_next_index;
     255             : 
     256           0 :   while (n_left_from > 0)
     257             :     {
     258             :       u32 n_left_to_next;
     259             : 
     260           0 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     261           0 :       while (n_left_from >= 4 && n_left_to_next >= 2)
     262             :         {
     263             :           u32 bi0, bi1;
     264             :           vlib_buffer_t *b0, *b1;
     265             :           u32 next0, next1;
     266             :           ip6_header_t *ip0, *ip1;
     267             :           ip6_hop_by_hop_header_t *hbh0, *hbh1;
     268             :           u64 *copy_src0, *copy_dst0, *copy_src1, *copy_dst1;
     269             :           u16 new_l0, new_l1;
     270             : 
     271             :           /* Prefetch next iteration. */
     272             :           {
     273             :             vlib_buffer_t *p2, *p3;
     274             : 
     275           0 :             p2 = vlib_get_buffer (vm, from[2]);
     276           0 :             p3 = vlib_get_buffer (vm, from[3]);
     277             : 
     278           0 :             vlib_prefetch_buffer_header (p2, LOAD);
     279           0 :             vlib_prefetch_buffer_header (p3, LOAD);
     280             : 
     281           0 :             CLIB_PREFETCH (p2->data - rewrite_length,
     282             :                            2 * CLIB_CACHE_LINE_BYTES, STORE);
     283           0 :             CLIB_PREFETCH (p3->data - rewrite_length,
     284             :                            2 * CLIB_CACHE_LINE_BYTES, STORE);
     285             :           }
     286             : 
     287             :           /* speculatively enqueue b0 and b1 to the current next frame */
     288           0 :           to_next[0] = bi0 = from[0];
     289           0 :           to_next[1] = bi1 = from[1];
     290           0 :           from += 2;
     291           0 :           to_next += 2;
     292           0 :           n_left_from -= 2;
     293           0 :           n_left_to_next -= 2;
     294             : 
     295           0 :           b0 = vlib_get_buffer (vm, bi0);
     296           0 :           b1 = vlib_get_buffer (vm, bi1);
     297             : 
     298             :           /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
     299           0 :           ip0 = vlib_buffer_get_current (b0);
     300           0 :           ip1 = vlib_buffer_get_current (b1);
     301             : 
     302             :           /* Copy the ip header left by the required amount */
     303           0 :           copy_dst0 = (u64 *) (((u8 *) ip0) - rewrite_length);
     304           0 :           copy_dst1 = (u64 *) (((u8 *) ip1) - rewrite_length);
     305           0 :           copy_src0 = (u64 *) ip0;
     306           0 :           copy_src1 = (u64 *) ip1;
     307             : 
     308           0 :           copy_dst0[0] = copy_src0[0];
     309           0 :           copy_dst0[1] = copy_src0[1];
     310           0 :           copy_dst0[2] = copy_src0[2];
     311           0 :           copy_dst0[3] = copy_src0[3];
     312           0 :           copy_dst0[4] = copy_src0[4];
     313             : 
     314           0 :           copy_dst1[0] = copy_src1[0];
     315           0 :           copy_dst1[1] = copy_src1[1];
     316           0 :           copy_dst1[2] = copy_src1[2];
     317           0 :           copy_dst1[3] = copy_src1[3];
     318           0 :           copy_dst1[4] = copy_src1[4];
     319             : 
     320           0 :           vlib_buffer_advance (b0, -(word) rewrite_length);
     321           0 :           vlib_buffer_advance (b1, -(word) rewrite_length);
     322           0 :           ip0 = vlib_buffer_get_current (b0);
     323           0 :           ip1 = vlib_buffer_get_current (b1);
     324             : 
     325           0 :           hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
     326           0 :           hbh1 = (ip6_hop_by_hop_header_t *) (ip1 + 1);
     327             :           /* $$$ tune, rewrite_length is a multiple of 8 */
     328           0 :           clib_memcpy_fast (hbh0, rewrite, rewrite_length);
     329           0 :           clib_memcpy_fast (hbh1, rewrite, rewrite_length);
     330             :           /* Patch the protocol chain, insert the h-b-h (type 0) header */
     331           0 :           hbh0->protocol = ip0->protocol;
     332           0 :           hbh1->protocol = ip1->protocol;
     333           0 :           ip0->protocol = 0;
     334           0 :           ip1->protocol = 0;
     335           0 :           new_l0 =
     336           0 :             clib_net_to_host_u16 (ip0->payload_length) + rewrite_length;
     337           0 :           new_l1 =
     338           0 :             clib_net_to_host_u16 (ip1->payload_length) + rewrite_length;
     339           0 :           ip0->payload_length = clib_host_to_net_u16 (new_l0);
     340           0 :           ip1->payload_length = clib_host_to_net_u16 (new_l1);
     341             : 
     342             :           /* Populate the (first) h-b-h list elt */
     343           0 :           next0 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
     344           0 :           next1 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
     345             : 
     346             : 
     347             :           /* $$$$$ End of processing 2 x packets $$$$$ */
     348             : 
     349           0 :           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
     350             :             {
     351           0 :               if (b0->flags & VLIB_BUFFER_IS_TRACED)
     352             :                 {
     353             :                   ip6_add_hop_by_hop_trace_t *t =
     354           0 :                     vlib_add_trace (vm, node, b0, sizeof (*t));
     355           0 :                   t->next_index = next0;
     356             :                 }
     357           0 :               if (b1->flags & VLIB_BUFFER_IS_TRACED)
     358             :                 {
     359             :                   ip6_add_hop_by_hop_trace_t *t =
     360           0 :                     vlib_add_trace (vm, node, b1, sizeof (*t));
     361           0 :                   t->next_index = next1;
     362             :                 }
     363             :             }
     364           0 :           processed += 2;
     365             :           /* verify speculative enqueues, maybe switch current next frame */
     366           0 :           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
     367             :                                            to_next, n_left_to_next,
     368             :                                            bi0, bi1, next0, next1);
     369             :         }
     370           0 :       while (n_left_from > 0 && n_left_to_next > 0)
     371             :         {
     372             :           u32 bi0;
     373             :           vlib_buffer_t *b0;
     374             :           u32 next0;
     375             :           ip6_header_t *ip0;
     376             :           ip6_hop_by_hop_header_t *hbh0;
     377             :           u64 *copy_src0, *copy_dst0;
     378             :           u16 new_l0;
     379             : 
     380             :           /* speculatively enqueue b0 to the current next frame */
     381           0 :           bi0 = from[0];
     382           0 :           to_next[0] = bi0;
     383           0 :           from += 1;
     384           0 :           to_next += 1;
     385           0 :           n_left_from -= 1;
     386           0 :           n_left_to_next -= 1;
     387             : 
     388           0 :           b0 = vlib_get_buffer (vm, bi0);
     389             : 
     390           0 :           ip0 = vlib_buffer_get_current (b0);
     391             : 
     392             :           /* Copy the ip header left by the required amount */
     393           0 :           copy_dst0 = (u64 *) (((u8 *) ip0) - rewrite_length);
     394           0 :           copy_src0 = (u64 *) ip0;
     395             : 
     396           0 :           copy_dst0[0] = copy_src0[0];
     397           0 :           copy_dst0[1] = copy_src0[1];
     398           0 :           copy_dst0[2] = copy_src0[2];
     399           0 :           copy_dst0[3] = copy_src0[3];
     400           0 :           copy_dst0[4] = copy_src0[4];
     401           0 :           vlib_buffer_advance (b0, -(word) rewrite_length);
     402           0 :           ip0 = vlib_buffer_get_current (b0);
     403             : 
     404           0 :           hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
     405             :           /* $$$ tune, rewrite_length is a multiple of 8 */
     406           0 :           clib_memcpy_fast (hbh0, rewrite, rewrite_length);
     407             :           /* Patch the protocol chain, insert the h-b-h (type 0) header */
     408           0 :           hbh0->protocol = ip0->protocol;
     409           0 :           ip0->protocol = 0;
     410           0 :           new_l0 =
     411           0 :             clib_net_to_host_u16 (ip0->payload_length) + rewrite_length;
     412           0 :           ip0->payload_length = clib_host_to_net_u16 (new_l0);
     413             : 
     414             :           /* Populate the (first) h-b-h list elt */
     415           0 :           next0 = IP6_HBYH_IOAM_INPUT_NEXT_IP6_LOOKUP;
     416             : 
     417           0 :           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
     418             :                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
     419             :             {
     420             :               ip6_add_hop_by_hop_trace_t *t =
     421           0 :                 vlib_add_trace (vm, node, b0, sizeof (*t));
     422           0 :               t->next_index = next0;
     423             :             }
     424             : 
     425           0 :           processed++;
     426             : 
     427             :           /* verify speculative enqueue, maybe switch current next frame */
     428           0 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
     429             :                                            to_next, n_left_to_next,
     430             :                                            bi0, next0);
     431             :         }
     432             : 
     433           0 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     434             :     }
     435             : 
     436           0 :   vlib_node_increment_counter (vm, ip6_add_hop_by_hop_node.index,
     437             :                                IP6_ADD_HOP_BY_HOP_ERROR_PROCESSED, processed);
     438           0 :   return frame->n_vectors;
     439             : }
     440             : 
     441             : /* *INDENT-OFF* */
     442      183788 : VLIB_REGISTER_NODE (ip6_add_hop_by_hop_node) =  /* *INDENT-OFF* */
     443             : {
     444             :   .name = "ip6-add-hop-by-hop",
     445             :   .vector_size = sizeof (u32),
     446             :   .format_trace = format_ip6_add_hop_by_hop_trace,
     447             :   .type = VLIB_NODE_TYPE_INTERNAL,
     448             :   .n_errors = ARRAY_LEN (ip6_add_hop_by_hop_error_strings),
     449             :   .error_strings = ip6_add_hop_by_hop_error_strings,
     450             :   /* See ip/lookup.h */
     451             :   .n_next_nodes = IP6_HBYH_IOAM_INPUT_N_NEXT,
     452             :   .next_nodes = {
     453             : #define _(s,n) [IP6_HBYH_IOAM_INPUT_NEXT_##s] = n,
     454             :     foreach_ip6_hbyh_ioam_input_next
     455             : #undef _
     456             :   },
     457             : };
     458             : /* *INDENT-ON* */
     459             : 
     460             : /* The main h-b-h tracer was already invoked, no need to do much here */
     461             : typedef struct
     462             : {
     463             :   u32 next_index;
     464             : } ip6_pop_hop_by_hop_trace_t;
     465             : 
     466             : /* packet trace format function */
     467             : static u8 *
     468           0 : format_ip6_pop_hop_by_hop_trace (u8 * s, va_list * args)
     469             : {
     470           0 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
     471           0 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
     472           0 :   ip6_pop_hop_by_hop_trace_t *t =
     473             :     va_arg (*args, ip6_pop_hop_by_hop_trace_t *);
     474             : 
     475           0 :   s = format (s, "IP6_POP_HOP_BY_HOP: next index %d", t->next_index);
     476           0 :   return s;
     477             : }
     478             : 
     479             : #ifndef CLIB_MARCH_VARIANT
     480             : int
     481         575 : ip6_hbh_pop_register_option (u8 option,
     482             :                              int options (vlib_buffer_t * b,
     483             :                                           ip6_header_t * ip,
     484             :                                           ip6_hop_by_hop_option_t * opt))
     485             : {
     486         575 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
     487             : 
     488             :   ASSERT ((u32) option < ARRAY_LEN (hm->pop_options));
     489             : 
     490             :   /* Already registered */
     491         575 :   if (hm->pop_options[option])
     492           0 :     return (-1);
     493             : 
     494         575 :   hm->pop_options[option] = options;
     495             : 
     496         575 :   return (0);
     497             : }
     498             : 
     499             : int
     500           0 : ip6_hbh_pop_unregister_option (u8 option)
     501             : {
     502           0 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
     503             : 
     504             :   ASSERT ((u32) option < ARRAY_LEN (hm->pop_options));
     505             : 
     506             :   /* Not registered */
     507           0 :   if (!hm->pop_options[option])
     508           0 :     return (-1);
     509             : 
     510           0 :   hm->pop_options[option] = NULL;
     511           0 :   return (0);
     512             : }
     513             : #endif /* CLIB_MARCH_VARIANT */
     514             : 
     515             : extern vlib_node_registration_t ip6_pop_hop_by_hop_node;
     516             : 
     517             : #define foreach_ip6_pop_hop_by_hop_error                \
     518             : _(PROCESSED, "Pkts w/ removed ip6 hop-by-hop options")  \
     519             : _(NO_HOHO, "Pkts w/ no ip6 hop-by-hop options")         \
     520             : _(OPTION_FAILED, "ip6 pop hop-by-hop failed to process")
     521             : 
     522             : typedef enum
     523             : {
     524             : #define _(sym,str) IP6_POP_HOP_BY_HOP_ERROR_##sym,
     525             :   foreach_ip6_pop_hop_by_hop_error
     526             : #undef _
     527             :     IP6_POP_HOP_BY_HOP_N_ERROR,
     528             : } ip6_pop_hop_by_hop_error_t;
     529             : 
     530             : static char *ip6_pop_hop_by_hop_error_strings[] = {
     531             : #define _(sym,string) string,
     532             :   foreach_ip6_pop_hop_by_hop_error
     533             : #undef _
     534             : };
     535             : 
     536             : static inline void
     537           0 : ioam_pop_hop_by_hop_processing (vlib_main_t * vm,
     538             :                                 ip6_header_t * ip0,
     539             :                                 ip6_hop_by_hop_header_t * hbh0,
     540             :                                 vlib_buffer_t * b)
     541             : {
     542           0 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
     543             :   ip6_hop_by_hop_option_t *opt0, *limit0;
     544             :   u8 type0;
     545             : 
     546           0 :   if (!hbh0 || !ip0)
     547           0 :     return;
     548             : 
     549           0 :   opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
     550           0 :   limit0 = (ip6_hop_by_hop_option_t *)
     551           0 :     ((u8 *) hbh0 + ((hbh0->length + 1) << 3));
     552             : 
     553             :   /* Scan the set of h-b-h options, process ones that we understand */
     554           0 :   while (opt0 < limit0)
     555             :     {
     556           0 :       type0 = opt0->type;
     557           0 :       switch (type0)
     558             :         {
     559           0 :         case 0:         /* Pad1 */
     560           0 :           opt0 = (ip6_hop_by_hop_option_t *) ((u8 *) opt0) + 1;
     561           0 :           continue;
     562           0 :         case 1:         /* PadN */
     563           0 :           break;
     564           0 :         default:
     565           0 :           if (hm->pop_options[type0])
     566             :             {
     567           0 :               if ((*hm->pop_options[type0]) (b, ip0, opt0) < 0)
     568             :                 {
     569           0 :                   vlib_node_increment_counter (vm,
     570             :                                                ip6_pop_hop_by_hop_node.index,
     571             :                                                IP6_POP_HOP_BY_HOP_ERROR_OPTION_FAILED,
     572             :                                                1);
     573             :                 }
     574             :             }
     575             :         }
     576           0 :       opt0 =
     577           0 :         (ip6_hop_by_hop_option_t *) (((u8 *) opt0) + opt0->length +
     578             :                                      sizeof (ip6_hop_by_hop_option_t));
     579             :     }
     580             : }
     581             : 
     582        2300 : VLIB_NODE_FN (ip6_pop_hop_by_hop_node) (vlib_main_t * vm,
     583             :                                         vlib_node_runtime_t * node,
     584             :                                         vlib_frame_t * frame)
     585             : {
     586             :   u32 n_left_from, *from, *to_next;
     587             :   ip_lookup_next_t next_index;
     588           0 :   u32 processed = 0;
     589           0 :   u32 no_header = 0;
     590             : 
     591           0 :   from = vlib_frame_vector_args (frame);
     592           0 :   n_left_from = frame->n_vectors;
     593           0 :   next_index = node->cached_next_index;
     594             : 
     595           0 :   while (n_left_from > 0)
     596             :     {
     597             :       u32 n_left_to_next;
     598             : 
     599           0 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     600             : 
     601           0 :       while (n_left_from >= 4 && n_left_to_next >= 2)
     602             :         {
     603             :           u32 bi0, bi1;
     604             :           vlib_buffer_t *b0, *b1;
     605             :           u32 next0, next1;
     606             :           u32 adj_index0, adj_index1;
     607             :           ip6_header_t *ip0, *ip1;
     608             :           ip_adjacency_t *adj0, *adj1;
     609             :           ip6_hop_by_hop_header_t *hbh0, *hbh1;
     610             :           u64 *copy_dst0, *copy_src0, *copy_dst1, *copy_src1;
     611             :           u16 new_l0, new_l1;
     612             : 
     613             :           /* Prefetch next iteration. */
     614             :           {
     615             :             vlib_buffer_t *p2, *p3;
     616             : 
     617           0 :             p2 = vlib_get_buffer (vm, from[2]);
     618           0 :             p3 = vlib_get_buffer (vm, from[3]);
     619             : 
     620           0 :             vlib_prefetch_buffer_header (p2, LOAD);
     621           0 :             vlib_prefetch_buffer_header (p3, LOAD);
     622             : 
     623           0 :             clib_prefetch_store (p2->data);
     624           0 :             clib_prefetch_store (p3->data);
     625             :           }
     626             : 
     627             :           /* speculatively enqueue b0 and b1 to the current next frame */
     628           0 :           to_next[0] = bi0 = from[0];
     629           0 :           to_next[1] = bi1 = from[1];
     630           0 :           from += 2;
     631           0 :           to_next += 2;
     632           0 :           n_left_from -= 2;
     633           0 :           n_left_to_next -= 2;
     634             : 
     635           0 :           b0 = vlib_get_buffer (vm, bi0);
     636           0 :           b1 = vlib_get_buffer (vm, bi1);
     637             : 
     638             :           /* $$$$$ Dual loop: process 2 x packets here $$$$$ */
     639           0 :           ip0 = vlib_buffer_get_current (b0);
     640           0 :           ip1 = vlib_buffer_get_current (b1);
     641           0 :           adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
     642           0 :           adj_index1 = vnet_buffer (b1)->ip.adj_index[VLIB_TX];
     643           0 :           adj0 = adj_get (adj_index0);
     644           0 :           adj1 = adj_get (adj_index1);
     645             : 
     646           0 :           next0 = adj0->lookup_next_index;
     647           0 :           next1 = adj1->lookup_next_index;
     648             : 
     649           0 :           hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
     650           0 :           hbh1 = (ip6_hop_by_hop_header_t *) (ip1 + 1);
     651             : 
     652           0 :           ioam_pop_hop_by_hop_processing (vm, ip0, hbh0, b0);
     653           0 :           ioam_pop_hop_by_hop_processing (vm, ip1, hbh1, b1);
     654             : 
     655           0 :           vlib_buffer_advance (b0, (hbh0->length + 1) << 3);
     656           0 :           vlib_buffer_advance (b1, (hbh1->length + 1) << 3);
     657             : 
     658           0 :           new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
     659           0 :             ((hbh0->length + 1) << 3);
     660           0 :           new_l1 = clib_net_to_host_u16 (ip1->payload_length) -
     661           0 :             ((hbh1->length + 1) << 3);
     662             : 
     663           0 :           ip0->payload_length = clib_host_to_net_u16 (new_l0);
     664           0 :           ip1->payload_length = clib_host_to_net_u16 (new_l1);
     665             : 
     666           0 :           ip0->protocol = hbh0->protocol;
     667           0 :           ip1->protocol = hbh1->protocol;
     668             : 
     669           0 :           copy_src0 = (u64 *) ip0;
     670           0 :           copy_src1 = (u64 *) ip1;
     671           0 :           copy_dst0 = copy_src0 + (hbh0->length + 1);
     672           0 :           copy_dst0[4] = copy_src0[4];
     673           0 :           copy_dst0[3] = copy_src0[3];
     674           0 :           copy_dst0[2] = copy_src0[2];
     675           0 :           copy_dst0[1] = copy_src0[1];
     676           0 :           copy_dst0[0] = copy_src0[0];
     677           0 :           copy_dst1 = copy_src1 + (hbh1->length + 1);
     678           0 :           copy_dst1[4] = copy_src1[4];
     679           0 :           copy_dst1[3] = copy_src1[3];
     680           0 :           copy_dst1[2] = copy_src1[2];
     681           0 :           copy_dst1[1] = copy_src1[1];
     682           0 :           copy_dst1[0] = copy_src1[0];
     683           0 :           processed += 2;
     684             :           /* $$$$$ End of processing 2 x packets $$$$$ */
     685             : 
     686           0 :           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
     687             :             {
     688           0 :               if (b0->flags & VLIB_BUFFER_IS_TRACED)
     689             :                 {
     690             :                   ip6_pop_hop_by_hop_trace_t *t =
     691           0 :                     vlib_add_trace (vm, node, b0, sizeof (*t));
     692           0 :                   t->next_index = next0;
     693             :                 }
     694           0 :               if (b1->flags & VLIB_BUFFER_IS_TRACED)
     695             :                 {
     696             :                   ip6_pop_hop_by_hop_trace_t *t =
     697           0 :                     vlib_add_trace (vm, node, b1, sizeof (*t));
     698           0 :                   t->next_index = next1;
     699             :                 }
     700             :             }
     701             : 
     702             :           /* verify speculative enqueues, maybe switch current next frame */
     703           0 :           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
     704             :                                            to_next, n_left_to_next,
     705             :                                            bi0, bi1, next0, next1);
     706             :         }
     707             : 
     708           0 :       while (n_left_from > 0 && n_left_to_next > 0)
     709             :         {
     710             :           u32 bi0;
     711             :           vlib_buffer_t *b0;
     712             :           u32 next0;
     713             :           u32 adj_index0;
     714             :           ip6_header_t *ip0;
     715             :           ip_adjacency_t *adj0;
     716             :           ip6_hop_by_hop_header_t *hbh0;
     717             :           u64 *copy_dst0, *copy_src0;
     718             :           u16 new_l0;
     719             : 
     720             :           /* speculatively enqueue b0 to the current next frame */
     721           0 :           bi0 = from[0];
     722           0 :           to_next[0] = bi0;
     723           0 :           from += 1;
     724           0 :           to_next += 1;
     725           0 :           n_left_from -= 1;
     726           0 :           n_left_to_next -= 1;
     727             : 
     728           0 :           b0 = vlib_get_buffer (vm, bi0);
     729             : 
     730           0 :           ip0 = vlib_buffer_get_current (b0);
     731           0 :           adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
     732           0 :           adj0 = adj_get (adj_index0);
     733             : 
     734             :           /* Default use the next_index from the adjacency. */
     735           0 :           next0 = adj0->lookup_next_index;
     736             : 
     737             :           /* Perfectly normal to end up here w/ out h-b-h header */
     738           0 :           hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
     739             : 
     740             :           /* TODO:Temporarily doing it here.. do this validation in end_of_path_cb */
     741           0 :           ioam_pop_hop_by_hop_processing (vm, ip0, hbh0, b0);
     742             :           /* Pop the trace data */
     743           0 :           vlib_buffer_advance (b0, (hbh0->length + 1) << 3);
     744           0 :           new_l0 = clib_net_to_host_u16 (ip0->payload_length) -
     745           0 :             ((hbh0->length + 1) << 3);
     746           0 :           ip0->payload_length = clib_host_to_net_u16 (new_l0);
     747           0 :           ip0->protocol = hbh0->protocol;
     748           0 :           copy_src0 = (u64 *) ip0;
     749           0 :           copy_dst0 = copy_src0 + (hbh0->length + 1);
     750           0 :           copy_dst0[4] = copy_src0[4];
     751           0 :           copy_dst0[3] = copy_src0[3];
     752           0 :           copy_dst0[2] = copy_src0[2];
     753           0 :           copy_dst0[1] = copy_src0[1];
     754           0 :           copy_dst0[0] = copy_src0[0];
     755           0 :           processed++;
     756             : 
     757           0 :           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
     758             :                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
     759             :             {
     760             :               ip6_pop_hop_by_hop_trace_t *t =
     761           0 :                 vlib_add_trace (vm, node, b0, sizeof (*t));
     762           0 :               t->next_index = next0;
     763             :             }
     764             : 
     765             :           /* verify speculative enqueue, maybe switch current next frame */
     766           0 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
     767             :                                            to_next, n_left_to_next,
     768             :                                            bi0, next0);
     769             :         }
     770             : 
     771           0 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     772             :     }
     773             : 
     774           0 :   vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
     775             :                                IP6_POP_HOP_BY_HOP_ERROR_PROCESSED, processed);
     776           0 :   vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index,
     777             :                                IP6_POP_HOP_BY_HOP_ERROR_NO_HOHO, no_header);
     778           0 :   return frame->n_vectors;
     779             : }
     780             : 
     781             : /* *INDENT-OFF* */
     782      183788 : VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node) =
     783             : {
     784             :   .name = "ip6-pop-hop-by-hop",
     785             :   .vector_size = sizeof (u32),
     786             :   .format_trace = format_ip6_pop_hop_by_hop_trace,
     787             :   .type = VLIB_NODE_TYPE_INTERNAL,
     788             :   .sibling_of = "ip6-lookup",
     789             :   .n_errors = ARRAY_LEN (ip6_pop_hop_by_hop_error_strings),
     790             :   .error_strings = ip6_pop_hop_by_hop_error_strings,
     791             :   /* See ip/lookup.h */
     792             :   .n_next_nodes = 0,
     793             : };
     794             : /* *INDENT-ON* */
     795             : 
     796             : typedef struct
     797             : {
     798             :   u32 protocol;
     799             :   u32 next_index;
     800             : } ip6_local_hop_by_hop_trace_t;
     801             : 
     802             : #ifndef CLIB_MARCH_VARIANT
     803             : 
     804             : /* packet trace format function */
     805             : static u8 *
     806           2 : format_ip6_local_hop_by_hop_trace (u8 * s, va_list * args)
     807             : {
     808           2 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
     809           2 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
     810           2 :   ip6_local_hop_by_hop_trace_t *t =
     811             :     va_arg (*args, ip6_local_hop_by_hop_trace_t *);
     812             : 
     813           2 :   s = format (s, "IP6_LOCAL_HOP_BY_HOP: protocol %d,  next index %d\n",
     814             :               t->protocol, t->next_index);
     815           2 :   return s;
     816             : }
     817             : 
     818             : vlib_node_registration_t ip6_local_hop_by_hop_node;
     819             : 
     820             : #endif /* CLIB_MARCH_VARIANT */
     821             : 
     822             : #define foreach_ip6_local_hop_by_hop_error                      \
     823             : _(UNKNOWN, "Unknown protocol ip6 local h-b-h packets dropped")  \
     824             : _(OK, "Good ip6 local h-b-h packets")
     825             : 
     826             : typedef enum
     827             : {
     828             : #define _(sym,str) IP6_LOCAL_HOP_BY_HOP_ERROR_##sym,
     829             :   foreach_ip6_local_hop_by_hop_error
     830             : #undef _
     831             :     IP6_LOCAL_HOP_BY_HOP_N_ERROR,
     832             : } ip6_local_hop_by_hop_error_t;
     833             : 
     834             : #ifndef CLIB_MARCH_VARIANT
     835             : static char *ip6_local_hop_by_hop_error_strings[] = {
     836             : #define _(sym,string) string,
     837             :   foreach_ip6_local_hop_by_hop_error
     838             : #undef _
     839             : };
     840             : #endif /* CLIB_MARCH_VARIANT */
     841             : 
     842             : typedef enum
     843             : {
     844             :   IP6_LOCAL_HOP_BY_HOP_NEXT_DROP,
     845             :   IP6_LOCAL_HOP_BY_HOP_N_NEXT,
     846             : } ip6_local_hop_by_hop_next_t;
     847             : 
     848             : always_inline uword
     849         172 : ip6_local_hop_by_hop_inline (vlib_main_t * vm,
     850             :                              vlib_node_runtime_t * node, vlib_frame_t * frame,
     851             :                              int is_trace)
     852             : {
     853             :   u32 n_left_from, *from;
     854             :   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
     855             :   u16 nexts[VLIB_FRAME_SIZE], *next;
     856         172 :   u32 ok = 0;
     857         172 :   u32 unknown_proto_error = node->errors[IP6_LOCAL_HOP_BY_HOP_ERROR_UNKNOWN];
     858         172 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
     859             : 
     860             :   /* Note: there is only one of these */
     861         172 :   ip6_local_hop_by_hop_runtime_t *rt = hm->ip6_local_hbh_runtime;
     862             : 
     863         172 :   from = vlib_frame_vector_args (frame);
     864         172 :   n_left_from = frame->n_vectors;
     865             : 
     866         172 :   vlib_get_buffers (vm, from, bufs, n_left_from);
     867         172 :   b = bufs;
     868         172 :   next = nexts;
     869             : 
     870         172 :   while (n_left_from >= 4)
     871             :     {
     872             :       ip6_header_t *ip0, *ip1, *ip2, *ip3;
     873             :       u8 *hbh0, *hbh1, *hbh2, *hbh3;
     874             : 
     875             :       /* Prefetch next iteration. */
     876           0 :       if (PREDICT_TRUE (n_left_from >= 8))
     877             :         {
     878           0 :           vlib_prefetch_buffer_header (b[4], STORE);
     879           0 :           vlib_prefetch_buffer_header (b[5], STORE);
     880           0 :           vlib_prefetch_buffer_header (b[6], STORE);
     881           0 :           vlib_prefetch_buffer_header (b[7], STORE);
     882           0 :           clib_prefetch_store (b[4]->data);
     883           0 :           clib_prefetch_store (b[5]->data);
     884           0 :           clib_prefetch_store (b[6]->data);
     885           0 :           clib_prefetch_store (b[7]->data);
     886             :         }
     887             : 
     888             :       /*
     889             :        * Leave current_data pointing at the IP header.
     890             :        * It's reasonably likely that any registered handler
     891             :        * will want to know where to find the ip6 header.
     892             :        */
     893           0 :       ip0 = vlib_buffer_get_current (b[0]);
     894           0 :       ip1 = vlib_buffer_get_current (b[1]);
     895           0 :       ip2 = vlib_buffer_get_current (b[2]);
     896           0 :       ip3 = vlib_buffer_get_current (b[3]);
     897             : 
     898             :       /* Look at hop-by-hop header */
     899           0 :       hbh0 = ip6_next_header (ip0);
     900           0 :       hbh1 = ip6_next_header (ip1);
     901           0 :       hbh2 = ip6_next_header (ip2);
     902           0 :       hbh3 = ip6_next_header (ip3);
     903             : 
     904             :       /*
     905             :        * ... to find the next header type and see if we
     906             :        * have a handler for it...
     907             :        */
     908           0 :       next[0] = rt->next_index_by_protocol[*hbh0];
     909           0 :       next[1] = rt->next_index_by_protocol[*hbh1];
     910           0 :       next[2] = rt->next_index_by_protocol[*hbh2];
     911           0 :       next[3] = rt->next_index_by_protocol[*hbh3];
     912             : 
     913           0 :       b[0]->error = unknown_proto_error;
     914           0 :       b[1]->error = unknown_proto_error;
     915           0 :       b[2]->error = unknown_proto_error;
     916           0 :       b[3]->error = unknown_proto_error;
     917             : 
     918             :       /* Account for non-drop pkts */
     919           0 :       ok += next[0] != 0;
     920           0 :       ok += next[1] != 0;
     921           0 :       ok += next[2] != 0;
     922           0 :       ok += next[3] != 0;
     923             : 
     924           0 :       if (is_trace)
     925             :         {
     926           0 :           if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
     927             :             {
     928             :               ip6_local_hop_by_hop_trace_t *t =
     929           0 :                 vlib_add_trace (vm, node, b[0], sizeof (*t));
     930           0 :               t->next_index = next[0];
     931           0 :               t->protocol = *hbh0;
     932             :             }
     933           0 :           if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
     934             :             {
     935             :               ip6_local_hop_by_hop_trace_t *t =
     936           0 :                 vlib_add_trace (vm, node, b[1], sizeof (*t));
     937           0 :               t->next_index = next[1];
     938           0 :               t->protocol = *hbh1;
     939             :             }
     940           0 :           if (b[2]->flags & VLIB_BUFFER_IS_TRACED)
     941             :             {
     942             :               ip6_local_hop_by_hop_trace_t *t =
     943           0 :                 vlib_add_trace (vm, node, b[2], sizeof (*t));
     944           0 :               t->next_index = next[2];
     945           0 :               t->protocol = *hbh2;
     946             :             }
     947           0 :           if (b[3]->flags & VLIB_BUFFER_IS_TRACED)
     948             :             {
     949             :               ip6_local_hop_by_hop_trace_t *t =
     950           0 :                 vlib_add_trace (vm, node, b[3], sizeof (*t));
     951           0 :               t->next_index = next[3];
     952           0 :               t->protocol = *hbh3;
     953             :             }
     954             :         }
     955             : 
     956           0 :       b += 4;
     957           0 :       next += 4;
     958           0 :       n_left_from -= 4;
     959             :     }
     960             : 
     961         345 :   while (n_left_from > 0)
     962             :     {
     963             :       ip6_header_t *ip0;
     964             :       u8 *hbh0;
     965             : 
     966         173 :       ip0 = vlib_buffer_get_current (b[0]);
     967             : 
     968         173 :       hbh0 = ip6_next_header (ip0);
     969             : 
     970         173 :       next[0] = rt->next_index_by_protocol[*hbh0];
     971             : 
     972         173 :       b[0]->error = unknown_proto_error;
     973         173 :       ok += next[0] != 0;
     974             : 
     975         173 :       if (is_trace)
     976             :         {
     977           2 :           if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
     978             :             {
     979             :               ip6_local_hop_by_hop_trace_t *t =
     980           2 :                 vlib_add_trace (vm, node, b[0], sizeof (*t));
     981           2 :               t->next_index = next[0];
     982           2 :               t->protocol = *hbh0;
     983             :             }
     984             :         }
     985             : 
     986         173 :       b += 1;
     987         173 :       next += 1;
     988         173 :       n_left_from -= 1;
     989             :     }
     990             : 
     991         172 :   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
     992             : 
     993         172 :   vlib_node_increment_counter (vm, node->node_index,
     994             :                                IP6_LOCAL_HOP_BY_HOP_ERROR_OK, ok);
     995         172 :   return frame->n_vectors;
     996             : }
     997             : 
     998        2472 : VLIB_NODE_FN (ip6_local_hop_by_hop_node) (vlib_main_t * vm,
     999             :                                           vlib_node_runtime_t * node,
    1000             :                                           vlib_frame_t * frame)
    1001             : {
    1002         172 :   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
    1003           2 :     return ip6_local_hop_by_hop_inline (vm, node, frame, 1 /* is_trace */ );
    1004             :   else
    1005         170 :     return ip6_local_hop_by_hop_inline (vm, node, frame, 0 /* is_trace */ );
    1006             : }
    1007             : 
    1008             : #ifndef CLIB_MARCH_VARIANT
    1009             : /* *INDENT-OFF* */
    1010      183788 : VLIB_REGISTER_NODE (ip6_local_hop_by_hop_node) =
    1011             : {
    1012             :   .name = "ip6-local-hop-by-hop",
    1013             :   .vector_size = sizeof (u32),
    1014             :   .format_trace = format_ip6_local_hop_by_hop_trace,
    1015             :   .type = VLIB_NODE_TYPE_INTERNAL,
    1016             : 
    1017             :   .n_errors = ARRAY_LEN(ip6_local_hop_by_hop_error_strings),
    1018             :   .error_strings = ip6_local_hop_by_hop_error_strings,
    1019             : 
    1020             :   .n_next_nodes = IP6_LOCAL_HOP_BY_HOP_N_NEXT,
    1021             : 
    1022             :   /* edit / add dispositions here */
    1023             :   .next_nodes =
    1024             :   {
    1025             :     [IP6_LOCAL_HOP_BY_HOP_NEXT_DROP] = "error-drop",
    1026             :   },
    1027             : };
    1028             : /* *INDENT-ON* */
    1029             : 
    1030             : clib_error_t *
    1031           0 : show_ip6_hbh_command_fn (vlib_main_t * vm,
    1032             :                          unformat_input_t * input, vlib_cli_command_t * cmd)
    1033             : {
    1034             :   int i;
    1035             :   u32 next_index;
    1036           0 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
    1037           0 :   ip6_local_hop_by_hop_runtime_t *rt = hm->ip6_local_hbh_runtime;
    1038           0 :   vlib_node_t *n = vlib_get_node (vm, ip6_local_hop_by_hop_node.index);
    1039             : 
    1040           0 :   vlib_cli_output (vm, "%-6s%s", "Proto", "Node Name");
    1041             : 
    1042           0 :   for (i = 0; i < ARRAY_LEN (rt->next_index_by_protocol); i++)
    1043             :     {
    1044           0 :       if ((next_index = rt->next_index_by_protocol[i]))
    1045             :         {
    1046           0 :           u32 next_node_index = n->next_nodes[next_index];
    1047           0 :           vlib_node_t *next_n = vlib_get_node (vm, next_node_index);
    1048           0 :           vlib_cli_output (vm, "[%3d] %v", i, next_n->name);
    1049             :         }
    1050             :     }
    1051             : 
    1052           0 :   return 0;
    1053             : }
    1054             : 
    1055             : /*?
    1056             :  * Display the set of ip6 local hop-by-hop next protocol handler nodes
    1057             :  *
    1058             :  * @cliexpar
    1059             :  * Display ip6 local hop-by-hop next protocol handler nodes
    1060             :  * @cliexcmd{show ip6 hbh}
    1061             : ?*/
    1062             : /* *INDENT-OFF* */
    1063      285289 : VLIB_CLI_COMMAND (show_ip6_hbh, static) = {
    1064             :   .path = "show ip6 hbh",
    1065             :   .short_help = "show ip6 hbh",
    1066             :   .function = show_ip6_hbh_command_fn,
    1067             : };
    1068             : /* *INDENT-ON* */
    1069             : 
    1070             : 
    1071             : #endif /* CLIB_MARCH_VARIANT */
    1072             : 
    1073             : 
    1074             : #ifndef CLIB_MARCH_VARIANT
    1075             : static clib_error_t *
    1076         575 : ip6_hop_by_hop_ioam_init (vlib_main_t * vm)
    1077             : {
    1078             :   clib_error_t *error;
    1079         575 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
    1080             : 
    1081         575 :   if ((error = vlib_call_init_function (vm, ip_main_init)))
    1082           0 :     return (error);
    1083             : 
    1084         575 :   if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
    1085           0 :     return error;
    1086             : 
    1087         575 :   hm->vlib_main = vm;
    1088         575 :   hm->vnet_main = vnet_get_main ();
    1089         575 :   hm->unix_time_0 = (u32) time (0);  /* Store starting time */
    1090         575 :   hm->vlib_time_0 = vlib_time_now (vm);
    1091         575 :   hm->ioam_flag = IOAM_HBYH_MOD;
    1092         575 :   clib_memset (hm->add_options, 0, sizeof (hm->add_options));
    1093         575 :   clib_memset (hm->pop_options, 0, sizeof (hm->pop_options));
    1094         575 :   clib_memset (hm->options_size, 0, sizeof (hm->options_size));
    1095             : 
    1096         575 :   vnet_classify_register_unformat_opaque_index_fn (unformat_opaque_ioam);
    1097         575 :   hm->ip6_local_hbh_runtime = clib_mem_alloc_aligned
    1098             :     (sizeof (ip6_local_hop_by_hop_runtime_t), CLIB_CACHE_LINE_BYTES);
    1099             : 
    1100         575 :   memset (hm->ip6_local_hbh_runtime, 0,
    1101             :           sizeof (ip6_local_hop_by_hop_runtime_t));
    1102             : 
    1103         575 :   ip6_register_protocol (IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS,
    1104             :                          ip6_local_hop_by_hop_node.index);
    1105         575 :   return (0);
    1106             : }
    1107             : 
    1108             : /* *INDENT-OFF* */
    1109       41471 : VLIB_INIT_FUNCTION (ip6_hop_by_hop_ioam_init) =
    1110             : {
    1111             :   .runs_after = VLIB_INITS("ip_main_init", "ip6_lookup_init"),
    1112             : };
    1113             : /* *INDENT-ON* */
    1114             : 
    1115             : void
    1116         575 : ip6_local_hop_by_hop_register_protocol (u32 protocol, u32 node_index)
    1117             : {
    1118         575 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
    1119         575 :   vlib_main_t *vm = hm->vlib_main;
    1120         575 :   ip6_local_hop_by_hop_runtime_t *local_hbh_runtime
    1121             :     = hm->ip6_local_hbh_runtime;
    1122             :   u32 old_next_index;
    1123             : 
    1124         575 :   ASSERT (protocol < ARRAY_LEN (local_hbh_runtime->next_index_by_protocol));
    1125             : 
    1126         575 :   old_next_index = local_hbh_runtime->next_index_by_protocol[protocol];
    1127             : 
    1128         575 :   local_hbh_runtime->next_index_by_protocol[protocol] =
    1129         575 :     vlib_node_add_next (vm, ip6_local_hop_by_hop_node.index, node_index);
    1130             : 
    1131             :   /* Someone will eventually do this. Trust me. */
    1132         575 :   if (old_next_index &&
    1133           0 :       (old_next_index != local_hbh_runtime->next_index_by_protocol[protocol]))
    1134           0 :     clib_warning ("WARNING: replaced next index for protocol %d", protocol);
    1135         575 : }
    1136             : 
    1137             : int
    1138           0 : ip6_ioam_set_rewrite (u8 ** rwp, int has_trace_option,
    1139             :                       int has_pot_option, int has_seqno_option)
    1140             : {
    1141           0 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
    1142           0 :   u8 *rewrite = NULL;
    1143             :   u32 size, rnd_size;
    1144             :   ip6_hop_by_hop_header_t *hbh;
    1145             :   u8 *current;
    1146           0 :   u8 *trace_data_size = NULL;
    1147           0 :   u8 *pot_data_size = NULL;
    1148             : 
    1149           0 :   vec_free (*rwp);
    1150             : 
    1151           0 :   if (has_trace_option == 0 && has_pot_option == 0)
    1152           0 :     return -1;
    1153             : 
    1154             :   /* Work out how much space we need */
    1155           0 :   size = sizeof (ip6_hop_by_hop_header_t);
    1156             : 
    1157             :   //if (has_trace_option && hm->get_sizeof_options[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] != 0)
    1158           0 :   if (has_trace_option
    1159           0 :       && hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] != 0)
    1160             :     {
    1161           0 :       size += hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST];
    1162             :     }
    1163           0 :   if (has_pot_option
    1164           0 :       && hm->add_options[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] != 0)
    1165             :     {
    1166           0 :       size += hm->options_size[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT];
    1167             :     }
    1168             : 
    1169           0 :   if (has_seqno_option)
    1170             :     {
    1171           0 :       size += hm->options_size[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE];
    1172             :     }
    1173             : 
    1174             :   /* Round to a multiple of 8 octets */
    1175           0 :   rnd_size = (size + 7) & ~7;
    1176             : 
    1177             :   /* allocate it, zero-fill / pad by construction */
    1178           0 :   vec_validate (rewrite, rnd_size - 1);
    1179             : 
    1180           0 :   hbh = (ip6_hop_by_hop_header_t *) rewrite;
    1181             :   /* Length of header in 8 octet units, not incl first 8 octets */
    1182           0 :   hbh->length = (rnd_size >> 3) - 1;
    1183           0 :   current = (u8 *) (hbh + 1);
    1184             : 
    1185           0 :   if (has_trace_option
    1186           0 :       && hm->add_options[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] != 0)
    1187             :     {
    1188           0 :       if (0 != (hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST]))
    1189             :         {
    1190           0 :           trace_data_size =
    1191             :             &hm->options_size[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST];
    1192           0 :           if (0 ==
    1193           0 :               hm->add_options[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] (current,
    1194             :                                                                      trace_data_size))
    1195           0 :             current += *trace_data_size;
    1196             :         }
    1197             :     }
    1198           0 :   if (has_pot_option
    1199           0 :       && hm->add_options[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] != 0)
    1200             :     {
    1201           0 :       pot_data_size =
    1202             :         &hm->options_size[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT];
    1203           0 :       if (0 ==
    1204           0 :           hm->add_options[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] (current,
    1205             :                                                                   pot_data_size))
    1206           0 :         current += *pot_data_size;
    1207             :     }
    1208             : 
    1209           0 :   if (has_seqno_option &&
    1210           0 :       (hm->add_options[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE] != 0))
    1211             :     {
    1212           0 :       if (0 == hm->add_options[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE] (current,
    1213             :                                                                    &
    1214             :                                                                    (hm->options_size
    1215             :                                                                     [HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE])))
    1216           0 :         current += hm->options_size[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE];
    1217             :     }
    1218             : 
    1219           0 :   *rwp = rewrite;
    1220           0 :   return 0;
    1221             : }
    1222             : 
    1223             : clib_error_t *
    1224           0 : clear_ioam_rewrite_fn (void)
    1225             : {
    1226           0 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
    1227             : 
    1228           0 :   vec_free (hm->rewrite);
    1229           0 :   hm->rewrite = 0;
    1230           0 :   hm->has_trace_option = 0;
    1231           0 :   hm->has_pot_option = 0;
    1232           0 :   hm->has_seqno_option = 0;
    1233           0 :   hm->has_analyse_option = 0;
    1234           0 :   if (hm->config_handler[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST])
    1235           0 :     hm->config_handler[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] (NULL, 1);
    1236             : 
    1237           0 :   if (hm->config_handler[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT])
    1238           0 :     hm->config_handler[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] (NULL, 1);
    1239             : 
    1240           0 :   if (hm->config_handler[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE])
    1241             :     {
    1242           0 :       hm->config_handler[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE] ((void *)
    1243           0 :                                                              &hm->has_analyse_option,
    1244             :                                                              1);
    1245             :     }
    1246             : 
    1247           0 :   return 0;
    1248             : }
    1249             : 
    1250             : clib_error_t *
    1251           0 : clear_ioam_rewrite_command_fn (vlib_main_t * vm,
    1252             :                                unformat_input_t * input,
    1253             :                                vlib_cli_command_t * cmd)
    1254             : {
    1255           0 :   return (clear_ioam_rewrite_fn ());
    1256             : }
    1257             : 
    1258             : /*?
    1259             :  * This command clears all the In-band OAM (iOAM) features enabled by
    1260             :  * the '<em>set ioam rewrite</em>' command. Use '<em>show ioam summary</em>' to
    1261             :  * verify the configured settings cleared.
    1262             :  *
    1263             :  * @cliexpar
    1264             :  * Example of how to clear iOAM features:
    1265             :  * @cliexcmd{clear ioam rewrite}
    1266             : ?*/
    1267             : /* *INDENT-OFF* */
    1268      285289 : VLIB_CLI_COMMAND (ip6_clear_ioam_rewrite_cmd, static) = {
    1269             :   .path = "clear ioam rewrite",
    1270             :   .short_help = "clear ioam rewrite",
    1271             :   .function = clear_ioam_rewrite_command_fn,
    1272             : };
    1273             : /* *INDENT-ON* */
    1274             : 
    1275             : clib_error_t *
    1276           0 : ip6_ioam_enable (int has_trace_option, int has_pot_option,
    1277             :                  int has_seqno_option, int has_analyse_option)
    1278             : {
    1279             :   int rv;
    1280           0 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
    1281           0 :   rv = ip6_ioam_set_rewrite (&hm->rewrite, has_trace_option,
    1282             :                              has_pot_option, has_seqno_option);
    1283             : 
    1284           0 :   switch (rv)
    1285             :     {
    1286           0 :     case 0:
    1287           0 :       if (has_trace_option)
    1288             :         {
    1289           0 :           hm->has_trace_option = has_trace_option;
    1290           0 :           if (hm->config_handler[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST])
    1291           0 :             hm->config_handler[HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST] (NULL,
    1292             :                                                                       0);
    1293             :         }
    1294             : 
    1295           0 :       if (has_pot_option)
    1296             :         {
    1297           0 :           hm->has_pot_option = has_pot_option;
    1298           0 :           if (hm->config_handler[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT])
    1299           0 :             hm->config_handler[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] (NULL,
    1300             :                                                                        0);
    1301             :         }
    1302           0 :       hm->has_analyse_option = has_analyse_option;
    1303           0 :       if (has_seqno_option)
    1304             :         {
    1305           0 :           hm->has_seqno_option = has_seqno_option;
    1306           0 :           if (hm->config_handler[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE])
    1307             :             {
    1308           0 :               hm->config_handler[HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE] ((void *)
    1309             :                                                                      &has_analyse_option,
    1310             :                                                                      0);
    1311             :             }
    1312             :         }
    1313           0 :       break;
    1314             : 
    1315           0 :     default:
    1316           0 :       return clib_error_return_code (0, rv, 0,
    1317             :                                      "ip6_ioam_set_rewrite returned %d", rv);
    1318             :     }
    1319             : 
    1320           0 :   return 0;
    1321             : }
    1322             : 
    1323             : 
    1324             : static clib_error_t *
    1325           0 : ip6_set_ioam_rewrite_command_fn (vlib_main_t * vm,
    1326             :                                  unformat_input_t * input,
    1327             :                                  vlib_cli_command_t * cmd)
    1328             : {
    1329           0 :   int has_trace_option = 0;
    1330           0 :   int has_pot_option = 0;
    1331           0 :   int has_seqno_option = 0;
    1332           0 :   int has_analyse_option = 0;
    1333           0 :   clib_error_t *rv = 0;
    1334             : 
    1335           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    1336             :     {
    1337           0 :       if (unformat (input, "trace"))
    1338           0 :         has_trace_option = 1;
    1339           0 :       else if (unformat (input, "pot"))
    1340           0 :         has_pot_option = 1;
    1341           0 :       else if (unformat (input, "seqno"))
    1342           0 :         has_seqno_option = 1;
    1343           0 :       else if (unformat (input, "analyse"))
    1344           0 :         has_analyse_option = 1;
    1345             :       else
    1346           0 :         break;
    1347             :     }
    1348             : 
    1349             : 
    1350           0 :   rv = ip6_ioam_enable (has_trace_option, has_pot_option,
    1351             :                         has_seqno_option, has_analyse_option);
    1352             : 
    1353           0 :   return rv;
    1354             : }
    1355             : 
    1356             : /*?
    1357             :  * This command is used to enable In-band OAM (iOAM) features on IPv6.
    1358             :  * '<em>trace</em>' is used to enable iOAM trace feature. '<em>pot</em>' is used to
    1359             :  * enable the Proof Of Transit feature. '<em>ppc</em>' is used to indicate the
    1360             :  * Per Packet Counter feature for Edge to Edge processing. '<em>ppc</em>' is
    1361             :  * used to indicate if this node is an '<em>encap</em>' node (iOAM edge node
    1362             :  * where packet enters iOAM domain), a '<em>decap</em>' node (iOAM edge node
    1363             :  * where packet leaves iOAM domain) or '<em>none</em>' (iOAM node where packet
    1364             :  * is in-transit through the iOAM domain). '<em>ppc</em>' can only be set if
    1365             :  * '<em>trace</em>' or '<em>pot</em>' is enabled.
    1366             :  *
    1367             :  * Use '<em>clear ioam rewrite</em>' to disable all features enabled by this
    1368             :  * command. Use '<em>show ioam summary</em>' to verify the configured settings.
    1369             :  *
    1370             :  * @cliexpar
    1371             :  * Example of how to enable trace and pot with ppc set to encap:
    1372             :  * @cliexcmd{set ioam rewrite trace pot ppc encap}
    1373             : ?*/
    1374             : /* *INDENT-OFF* */
    1375      285289 : VLIB_CLI_COMMAND (ip6_set_ioam_rewrite_cmd, static) = {
    1376             :   .path = "set ioam rewrite",
    1377             :   .short_help = "set ioam [trace] [pot] [seqno] [analyse]",
    1378             :   .function = ip6_set_ioam_rewrite_command_fn,
    1379             : };
    1380             : /* *INDENT-ON* */
    1381             : 
    1382             : static clib_error_t *
    1383           0 : ip6_show_ioam_summary_cmd_fn (vlib_main_t * vm,
    1384             :                               unformat_input_t * input,
    1385             :                               vlib_cli_command_t * cmd)
    1386             : {
    1387           0 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
    1388           0 :   u8 *s = 0;
    1389             : 
    1390             : 
    1391           0 :   if (!is_zero_ip6_address (&hm->adj))
    1392             :     {
    1393           0 :       s = format (s, "              REWRITE FLOW CONFIGS - \n");
    1394           0 :       s = format (s, "               Destination Address : %U\n",
    1395             :                   format_ip6_address, &hm->adj, sizeof (ip6_address_t));
    1396           0 :       s =
    1397           0 :         format (s, "                    Flow operation : %d (%s)\n",
    1398           0 :                 hm->ioam_flag,
    1399           0 :                 (hm->ioam_flag ==
    1400           0 :                  IOAM_HBYH_ADD) ? "Add" : ((hm->ioam_flag ==
    1401           0 :                                             IOAM_HBYH_MOD) ? "Mod" : "Pop"));
    1402             :     }
    1403             :   else
    1404             :     {
    1405           0 :       s = format (s, "              REWRITE FLOW CONFIGS - Not configured\n");
    1406             :     }
    1407             : 
    1408             : 
    1409           0 :   s = format (s, "                        TRACE OPTION - %d (%s)\n",
    1410           0 :               hm->has_trace_option,
    1411           0 :               (hm->has_trace_option ? "Enabled" : "Disabled"));
    1412           0 :   if (hm->has_trace_option)
    1413           0 :     s =
    1414           0 :       format (s,
    1415             :               "Try 'show ioam trace and show ioam-trace profile' for more information\n");
    1416             : 
    1417             : 
    1418           0 :   s = format (s, "                        POT OPTION - %d (%s)\n",
    1419           0 :               hm->has_pot_option,
    1420           0 :               (hm->has_pot_option ? "Enabled" : "Disabled"));
    1421           0 :   if (hm->has_pot_option)
    1422           0 :     s =
    1423           0 :       format (s,
    1424             :               "Try 'show ioam pot and show pot profile' for more information\n");
    1425             : 
    1426           0 :   s = format (s, "         EDGE TO EDGE - SeqNo OPTION - %d (%s)\n",
    1427           0 :               hm->has_seqno_option,
    1428           0 :               hm->has_seqno_option ? "Enabled" : "Disabled");
    1429           0 :   if (hm->has_seqno_option)
    1430           0 :     s = format (s, "Try 'show ioam e2e' for more information\n");
    1431             : 
    1432           0 :   s = format (s, "         iOAM Analyse OPTION - %d (%s)\n",
    1433           0 :               hm->has_analyse_option,
    1434           0 :               hm->has_analyse_option ? "Enabled" : "Disabled");
    1435             : 
    1436           0 :   vlib_cli_output (vm, "%v", s);
    1437           0 :   vec_free (s);
    1438           0 :   return 0;
    1439             : }
    1440             : 
    1441             : /*?
    1442             :  * This command displays the current configuration data for In-band
    1443             :  * OAM (iOAM).
    1444             :  *
    1445             :  * @cliexpar
    1446             :  * Example to show the iOAM configuration:
    1447             :  * @cliexstart{show ioam summary}
    1448             :  *               REWRITE FLOW CONFIGS -
    1449             :  *                Destination Address : ff02::1
    1450             :  *                     Flow operation : 2 (Pop)
    1451             :  *                         TRACE OPTION - 1 (Enabled)
    1452             :  * Try 'show ioam trace and show ioam-trace profile' for more information
    1453             :  *                         POT OPTION - 1 (Enabled)
    1454             :  * Try 'show ioam pot and show pot profile' for more information
    1455             :  *          EDGE TO EDGE - PPC OPTION - 1 (Encap)
    1456             :  * @cliexend
    1457             : ?*/
    1458             : /* *INDENT-OFF* */
    1459      285289 : VLIB_CLI_COMMAND (ip6_show_ioam_run_cmd, static) = {
    1460             :   .path = "show ioam summary",
    1461             :   .short_help = "show ioam summary",
    1462             :   .function = ip6_show_ioam_summary_cmd_fn,
    1463             : };
    1464             : /* *INDENT-ON* */
    1465             : 
    1466             : void
    1467           0 : vnet_register_ioam_end_of_path_callback (void *cb)
    1468             : {
    1469           0 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
    1470             : 
    1471           0 :   hm->ioam_end_of_path_cb = cb;
    1472           0 : }
    1473             : 
    1474             : #endif /* CLIB_MARCH_VARIANT */
    1475             : /*
    1476             :  * fd.io coding-style-patch-verification: ON
    1477             :  *
    1478             :  * Local Variables:
    1479             :  * eval: (c-set-style "gnu")
    1480             :  * End:
    1481             :  */

Generated by: LCOV version 1.14