LCOV - code coverage report
Current view: top level - plugins/linux-cp - lcp_node.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 215 350 61.4 %
Date: 2023-10-26 01:39:38 Functions: 64 69 92.8 %

          Line data    Source code
       1             : /*
       2             :  * lcp_enthernet_node.c : linux control plane ethernet node
       3             :  *
       4             :  * Copyright (c) 2021 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             : #include <sys/socket.h>
      19             : #include <linux/if.h>
      20             : 
      21             : #include <plugins/linux-cp/lcp_interface.h>
      22             : #include <plugins/linux-cp/lcp_adj.h>
      23             : #include <linux-cp/lcp.api_enum.h>
      24             : 
      25             : #include <vnet/feature/feature.h>
      26             : #include <vnet/ip/ip4_packet.h>
      27             : #include <vnet/ethernet/arp_packet.h>
      28             : #include <vnet/ethernet/ethernet.h>
      29             : #include <vnet/ip/ip_types.h>
      30             : #include <vnet/ip/lookup.h>
      31             : #include <vnet/ip/ip4.h>
      32             : #include <vnet/ip/ip6.h>
      33             : #include <vnet/l2/l2_input.h>
      34             : #include <vnet/mpls/mpls.h>
      35             : 
      36             : #define foreach_lip_punt                                                      \
      37             :   _ (IO, "punt to host")                                                      \
      38             :   _ (DROP, "unknown input interface")
      39             : 
      40             : typedef enum
      41             : {
      42             : #define _(sym, str) LIP_PUNT_NEXT_##sym,
      43             :   foreach_lip_punt
      44             : #undef _
      45             :     LIP_PUNT_N_NEXT,
      46             : } lip_punt_next_t;
      47             : 
      48             : typedef struct lip_punt_trace_t_
      49             : {
      50             :   u32 phy_sw_if_index;
      51             :   u32 host_sw_if_index;
      52             : } lip_punt_trace_t;
      53             : 
      54             : /* packet trace format function */
      55             : static u8 *
      56           0 : format_lip_punt_trace (u8 *s, va_list *args)
      57             : {
      58           0 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
      59           0 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
      60           0 :   lip_punt_trace_t *t = va_arg (*args, lip_punt_trace_t *);
      61             : 
      62             :   s =
      63           0 :     format (s, "lip-punt: %u -> %u", t->phy_sw_if_index, t->host_sw_if_index);
      64             : 
      65           0 :   return s;
      66             : }
      67             : 
      68             : /**
      69             :  * Pass punted packets from the PHY to the HOST.
      70             :  */
      71           2 : VLIB_NODE_FN (lip_punt_node)
      72             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
      73             : {
      74             :   u32 n_left_from, *from, *to_next, n_left_to_next;
      75             :   lip_punt_next_t next_index;
      76             : 
      77           0 :   next_index = node->cached_next_index;
      78           0 :   n_left_from = frame->n_vectors;
      79           0 :   from = vlib_frame_vector_args (frame);
      80             : 
      81           0 :   while (n_left_from > 0)
      82             :     {
      83           0 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
      84             : 
      85           0 :       while (n_left_from > 0 && n_left_to_next > 0)
      86             :         {
      87             :           vlib_buffer_t *b0;
      88           0 :           const lcp_itf_pair_t *lip0 = NULL;
      89           0 :           u32 next0 = ~0;
      90             :           u32 bi0, lipi0;
      91             :           u32 sw_if_index0;
      92             :           u8 len0;
      93             : 
      94           0 :           bi0 = to_next[0] = from[0];
      95             : 
      96           0 :           from += 1;
      97           0 :           to_next += 1;
      98           0 :           n_left_from -= 1;
      99           0 :           n_left_to_next -= 1;
     100           0 :           next0 = LIP_PUNT_NEXT_DROP;
     101             : 
     102           0 :           b0 = vlib_get_buffer (vm, bi0);
     103             : 
     104           0 :           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
     105           0 :           lipi0 = lcp_itf_pair_find_by_phy (sw_if_index0);
     106           0 :           if (PREDICT_FALSE (lipi0 == INDEX_INVALID))
     107           0 :             goto trace0;
     108             : 
     109           0 :           lip0 = lcp_itf_pair_get (lipi0);
     110           0 :           next0 = LIP_PUNT_NEXT_IO;
     111           0 :           vnet_buffer (b0)->sw_if_index[VLIB_TX] = lip0->lip_host_sw_if_index;
     112             : 
     113           0 :           if (PREDICT_TRUE (lip0->lip_host_type == LCP_ITF_HOST_TAP))
     114             :             {
     115             :               /*
     116             :                * rewind to ethernet header
     117             :                */
     118           0 :               len0 = ((u8 *) vlib_buffer_get_current (b0) -
     119           0 :                       (u8 *) ethernet_buffer_get_header (b0));
     120           0 :               vlib_buffer_advance (b0, -len0);
     121             :             }
     122             :           /* Tun packets don't need any special treatment, just need to
     123             :            * be escorted past the TTL decrement. If we still want to use
     124             :            * ip[46]-punt-redirect with these, we could just set the
     125             :            * VNET_BUFFER_F_LOCALLY_ORIGINATED in an 'else {}' here and
     126             :            * then pass to the next node on the ip[46]-punt feature arc
     127             :            */
     128             : 
     129           0 :         trace0:
     130           0 :           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
     131             :             {
     132           0 :               lip_punt_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
     133           0 :               t->phy_sw_if_index = sw_if_index0;
     134           0 :               t->host_sw_if_index =
     135           0 :                 (lipi0 == INDEX_INVALID) ? ~0 : lip0->lip_host_sw_if_index;
     136             :             }
     137             : 
     138           0 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
     139             :                                            n_left_to_next, bi0, next0);
     140             :         }
     141             : 
     142           0 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     143             :     }
     144             : 
     145           0 :   return frame->n_vectors;
     146             : }
     147             : 
     148         354 : VLIB_REGISTER_NODE (lip_punt_node) = {
     149             :   .name = "linux-cp-punt",
     150             :   .vector_size = sizeof (u32),
     151             :   .format_trace = format_lip_punt_trace,
     152             :   .type = VLIB_NODE_TYPE_INTERNAL,
     153             : 
     154             :   .n_next_nodes = LIP_PUNT_N_NEXT,
     155             :   .next_nodes = {
     156             :     [LIP_PUNT_NEXT_DROP] = "error-drop",
     157             :     [LIP_PUNT_NEXT_IO] = "interface-output",
     158             :   },
     159             : };
     160             : 
     161             : #define foreach_lcp_punt_l3 _ (DROP, "unknown error")
     162             : 
     163             : typedef enum
     164             : {
     165             : #define _(sym, str) LCP_LOCAL_NEXT_##sym,
     166             :   foreach_lcp_punt_l3
     167             : #undef _
     168             :     LCP_LOCAL_N_NEXT,
     169             : } lcp_punt_l3_next_t;
     170             : 
     171             : typedef struct lcp_punt_l3_trace_t_
     172             : {
     173             :   u32 phy_sw_if_index;
     174             : } lcp_punt_l3_trace_t;
     175             : 
     176             : /* packet trace format function */
     177             : static u8 *
     178         155 : format_lcp_punt_l3_trace (u8 *s, va_list *args)
     179             : {
     180         155 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
     181         155 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
     182         155 :   lcp_punt_l3_trace_t *t = va_arg (*args, lcp_punt_l3_trace_t *);
     183             : 
     184         155 :   s = format (s, "linux-cp-punt-l3: %u", t->phy_sw_if_index);
     185             : 
     186         155 :   return s;
     187             : }
     188             : 
     189           5 : VLIB_NODE_FN (lcp_punt_l3_node)
     190             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
     191             : {
     192             :   u32 n_left_from, *from, *to_next, n_left_to_next;
     193             :   lip_punt_next_t next_index;
     194             : 
     195           3 :   next_index = node->cached_next_index;
     196           3 :   n_left_from = frame->n_vectors;
     197           3 :   from = vlib_frame_vector_args (frame);
     198             : 
     199           6 :   while (n_left_from > 0)
     200             :     {
     201           3 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     202             : 
     203          96 :       while (n_left_from > 0 && n_left_to_next > 0)
     204             :         {
     205             :           vlib_buffer_t *b0;
     206          93 :           u32 next0 = LCP_LOCAL_NEXT_DROP;
     207             :           u32 bi0;
     208             :           index_t lipi0;
     209             :           lcp_itf_pair_t *lip0;
     210             : 
     211          93 :           bi0 = to_next[0] = from[0];
     212             : 
     213          93 :           from += 1;
     214          93 :           to_next += 1;
     215          93 :           n_left_from -= 1;
     216          93 :           n_left_to_next -= 1;
     217             : 
     218          93 :           b0 = vlib_get_buffer (vm, bi0);
     219          93 :           vnet_feature_next (&next0, b0);
     220             : 
     221             :           lipi0 =
     222          93 :             lcp_itf_pair_find_by_phy (vnet_buffer (b0)->sw_if_index[VLIB_RX]);
     223          93 :           if (lipi0 != INDEX_INVALID)
     224             :             {
     225             :               /*
     226             :                * Avoid TTL check for packets which arrived on a tunnel and
     227             :                * are being punted to the local host.
     228             :                */
     229          93 :               lip0 = lcp_itf_pair_get (lipi0);
     230          93 :               if (lip0->lip_host_type == LCP_ITF_HOST_TUN)
     231          93 :                 b0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
     232             :             }
     233             : 
     234          93 :           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
     235             :             {
     236             :               lcp_punt_l3_trace_t *t =
     237          93 :                 vlib_add_trace (vm, node, b0, sizeof (*t));
     238          93 :               t->phy_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
     239             :             }
     240             : 
     241          93 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
     242             :                                            n_left_to_next, bi0, next0);
     243             :         }
     244             : 
     245           3 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     246             :     }
     247             : 
     248           3 :   return frame->n_vectors;
     249             : }
     250             : 
     251         354 : VLIB_REGISTER_NODE (lcp_punt_l3_node) = {
     252             :   .name = "linux-cp-punt-l3",
     253             :   .vector_size = sizeof (u32),
     254             :   .format_trace = format_lcp_punt_l3_trace,
     255             :   .type = VLIB_NODE_TYPE_INTERNAL,
     256             : 
     257             :   .n_next_nodes = 1,
     258             :   .next_nodes = {
     259             :     [LCP_LOCAL_NEXT_DROP] = "error-drop",
     260             :   },
     261             : };
     262             : 
     263         174 : VNET_FEATURE_INIT (lcp_punt_l3_ip4, static) = {
     264             :   .arc_name = "ip4-punt",
     265             :   .node_name = "linux-cp-punt-l3",
     266             :   .runs_before = VNET_FEATURES ("ip4-punt-redirect"),
     267             : };
     268             : 
     269         174 : VNET_FEATURE_INIT (lip_punt_l3_ip6, static) = {
     270             :   .arc_name = "ip6-punt",
     271             :   .node_name = "linux-cp-punt-l3",
     272             :   .runs_before = VNET_FEATURES ("ip6-punt-redirect"),
     273             : };
     274             : 
     275             : #define foreach_lcp_xc                                                        \
     276             :   _ (DROP, "drop")                                                            \
     277             :   _ (XC_IP4, "x-connnect-ip4")                                                \
     278             :   _ (XC_IP6, "x-connnect-ip6")
     279             : 
     280             : typedef enum
     281             : {
     282             : #define _(sym, str) LCP_XC_NEXT_##sym,
     283             :   foreach_lcp_xc
     284             : #undef _
     285             :     LCP_XC_N_NEXT,
     286             : } lcp_xc_next_t;
     287             : 
     288             : typedef struct lcp_xc_trace_t_
     289             : {
     290             :   u32 phy_sw_if_index;
     291             :   adj_index_t adj_index;
     292             : } lcp_xc_trace_t;
     293             : 
     294             : /* packet trace format function */
     295             : static u8 *
     296         101 : format_lcp_xc_trace (u8 *s, va_list *args)
     297             : {
     298         101 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
     299         101 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
     300         101 :   lcp_xc_trace_t *t = va_arg (*args, lcp_xc_trace_t *);
     301             : 
     302         101 :   s = format (s, "lcp-xc: itf:%d adj:%d", t->phy_sw_if_index, t->adj_index);
     303             : 
     304         101 :   return s;
     305             : }
     306             : 
     307             : /**
     308             :  * X-connect all packets from the HOST to the PHY.
     309             :  *
     310             :  * This runs in either the IP4 or IP6 path. The MAC rewrite on the received
     311             :  * packet from the host is used as a key to find the adjacency used on the phy.
     312             :  * This allows this code to start the feature arc on that adjacency.
     313             :  * Consequently, all packet sent from the host are also subject to output
     314             :  * features, which is symmetric w.r.t. to input features.
     315             :  */
     316             : static_always_inline u32
     317           8 : lcp_xc_inline (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame,
     318             :                ip_address_family_t af)
     319             : {
     320             :   u32 n_left_from, *from, *to_next, n_left_to_next;
     321             :   lcp_xc_next_t next_index;
     322             :   ip_lookup_main_t *lm;
     323             : 
     324           8 :   next_index = 0;
     325           8 :   n_left_from = frame->n_vectors;
     326           8 :   from = vlib_frame_vector_args (frame);
     327             : 
     328           8 :   if (AF_IP4 == af)
     329           8 :     lm = &ip4_main.lookup_main;
     330             :   else
     331           0 :     lm = &ip6_main.lookup_main;
     332             : 
     333          16 :   while (n_left_from > 0)
     334             :     {
     335           8 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     336             : 
     337          16 :       while (n_left_from > 0 && n_left_to_next > 0)
     338             :         {
     339             :           const ethernet_header_t *eth;
     340             :           const lcp_itf_pair_t *lip;
     341             :           u32 next0, bi0, lipi, ai;
     342             :           vlib_buffer_t *b0;
     343             :           const ip_adjacency_t *adj;
     344             : 
     345           8 :           bi0 = to_next[0] = from[0];
     346             : 
     347           8 :           from += 1;
     348           8 :           to_next += 1;
     349           8 :           n_left_from -= 1;
     350           8 :           n_left_to_next -= 1;
     351             : 
     352           8 :           b0 = vlib_get_buffer (vm, bi0);
     353             : 
     354             :           lipi =
     355           8 :             lcp_itf_pair_find_by_host (vnet_buffer (b0)->sw_if_index[VLIB_RX]);
     356           8 :           lip = lcp_itf_pair_get (lipi);
     357             : 
     358           8 :           vnet_buffer (b0)->sw_if_index[VLIB_TX] = lip->lip_phy_sw_if_index;
     359           8 :           vlib_buffer_advance (b0, -lip->lip_rewrite_len);
     360           8 :           eth = vlib_buffer_get_current (b0);
     361             : 
     362           8 :           ai = ADJ_INDEX_INVALID;
     363           8 :           if (!ethernet_address_cast (eth->dst_address))
     364           8 :             ai = lcp_adj_lkup ((u8 *) eth, lip->lip_rewrite_len,
     365           8 :                                vnet_buffer (b0)->sw_if_index[VLIB_TX]);
     366           8 :           if (ai == ADJ_INDEX_INVALID)
     367           8 :             ai = lip->lip_phy_adjs.adj_index[af];
     368             : 
     369           8 :           adj = adj_get (ai);
     370           8 :           vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ai;
     371           8 :           next0 = adj->rewrite_header.next_index;
     372           8 :           vnet_buffer (b0)->ip.save_rewrite_length = lip->lip_rewrite_len;
     373             : 
     374           8 :           if (PREDICT_FALSE (adj->rewrite_header.flags &
     375             :                              VNET_REWRITE_HAS_FEATURES))
     376           0 :             vnet_feature_arc_start_w_cfg_index (
     377           0 :               lm->output_feature_arc_index,
     378           0 :               vnet_buffer (b0)->sw_if_index[VLIB_TX], &next0, b0,
     379             :               adj->ia_cfg_index);
     380             : 
     381           8 :           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
     382             :             {
     383           8 :               lcp_xc_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
     384           8 :               t->phy_sw_if_index = lip->lip_phy_sw_if_index;
     385           8 :               t->adj_index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
     386             :             }
     387             : 
     388           8 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
     389             :                                            n_left_to_next, bi0, next0);
     390             :         }
     391             : 
     392           8 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     393             :     }
     394             : 
     395           8 :   return frame->n_vectors;
     396             : }
     397             : 
     398          10 : VLIB_NODE_FN (lcp_xc_ip4)
     399             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
     400             : {
     401           8 :   return (lcp_xc_inline (vm, node, frame, AF_IP4));
     402             : }
     403             : 
     404           2 : VLIB_NODE_FN (lcp_xc_ip6)
     405             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
     406             : {
     407           0 :   return (lcp_xc_inline (vm, node, frame, AF_IP6));
     408             : }
     409             : 
     410         354 : VLIB_REGISTER_NODE (lcp_xc_ip4) = { .name = "linux-cp-xc-ip4",
     411             :                                     .vector_size = sizeof (u32),
     412             :                                     .format_trace = format_lcp_xc_trace,
     413             :                                     .type = VLIB_NODE_TYPE_INTERNAL,
     414             :                                     .sibling_of = "ip4-rewrite" };
     415             : 
     416         174 : VNET_FEATURE_INIT (lcp_xc_ip4_ucast_node, static) = {
     417             :   .arc_name = "ip4-unicast",
     418             :   .node_name = "linux-cp-xc-ip4",
     419             : };
     420         174 : VNET_FEATURE_INIT (lcp_xc_ip4_mcast_node, static) = {
     421             :   .arc_name = "ip4-multicast",
     422             :   .node_name = "linux-cp-xc-ip4",
     423             : };
     424             : 
     425         354 : VLIB_REGISTER_NODE (lcp_xc_ip6) = { .name = "linux-cp-xc-ip6",
     426             :                                     .vector_size = sizeof (u32),
     427             :                                     .format_trace = format_lcp_xc_trace,
     428             :                                     .type = VLIB_NODE_TYPE_INTERNAL,
     429             :                                     .sibling_of = "ip6-rewrite" };
     430             : 
     431         174 : VNET_FEATURE_INIT (lcp_xc_ip6_ucast_node, static) = {
     432             :   .arc_name = "ip6-unicast",
     433             :   .node_name = "linux-cp-xc-ip6",
     434             : };
     435         174 : VNET_FEATURE_INIT (lcp_xc_ip6_mcast_node, static) = {
     436             :   .arc_name = "ip6-multicast",
     437             :   .node_name = "linux-cp-xc-ip6",
     438             : };
     439             : 
     440             : typedef enum
     441             : {
     442             :   LCP_XC_MPLS_NEXT_DROP,
     443             :   LCP_XC_MPLS_NEXT_IO,
     444             :   LCP_XC_MPLS_N_NEXT,
     445             : } lcp_xc_mpls_next_t;
     446             : 
     447             : static_always_inline uword
     448           0 : lcp_xc_mpls_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
     449             :                     vlib_frame_t *frame)
     450             : {
     451             :   u32 n_left_from, *from, *to_next, n_left_to_next;
     452             :   lcp_xc_next_t next_index;
     453             : 
     454           0 :   next_index = 0;
     455           0 :   n_left_from = frame->n_vectors;
     456           0 :   from = vlib_frame_vector_args (frame);
     457             : 
     458           0 :   while (n_left_from > 0)
     459             :     {
     460           0 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     461             : 
     462           0 :       while (n_left_from > 0 && n_left_to_next > 0)
     463             :         {
     464             :           const ethernet_header_t *eth;
     465             :           const lcp_itf_pair_t *lip;
     466             :           u32 next0, bi0, lipi, ai;
     467             :           vlib_buffer_t *b0;
     468             :           // const ip_adjacency_t *adj;
     469             : 
     470           0 :           bi0 = to_next[0] = from[0];
     471             : 
     472           0 :           from += 1;
     473           0 :           to_next += 1;
     474           0 :           n_left_from -= 1;
     475           0 :           n_left_to_next -= 1;
     476             : 
     477           0 :           b0 = vlib_get_buffer (vm, bi0);
     478             : 
     479             :           lipi =
     480           0 :             lcp_itf_pair_find_by_host (vnet_buffer (b0)->sw_if_index[VLIB_RX]);
     481           0 :           lip = lcp_itf_pair_get (lipi);
     482             : 
     483           0 :           vnet_buffer (b0)->sw_if_index[VLIB_TX] = lip->lip_phy_sw_if_index;
     484           0 :           vlib_buffer_advance (b0, -lip->lip_rewrite_len);
     485           0 :           eth = vlib_buffer_get_current (b0);
     486             : 
     487           0 :           ai = ADJ_INDEX_INVALID;
     488           0 :           next0 = LCP_XC_MPLS_NEXT_DROP;
     489           0 :           if (!ethernet_address_cast (eth->dst_address))
     490           0 :             ai = lcp_adj_lkup ((u8 *) eth, lip->lip_rewrite_len,
     491           0 :                                vnet_buffer (b0)->sw_if_index[VLIB_TX]);
     492           0 :           if (ai != ADJ_INDEX_INVALID)
     493             :             {
     494           0 :               vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ai;
     495           0 :               next0 = LCP_XC_MPLS_NEXT_IO;
     496             :             }
     497             : 
     498           0 :           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
     499             :             {
     500           0 :               lcp_xc_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
     501           0 :               t->phy_sw_if_index = lip->lip_phy_sw_if_index;
     502           0 :               t->adj_index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
     503             :             }
     504             : 
     505           0 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
     506             :                                            n_left_to_next, bi0, next0);
     507             :         }
     508             : 
     509           0 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     510             :     }
     511             : 
     512           0 :   return frame->n_vectors;
     513             : }
     514             : 
     515           2 : VLIB_NODE_FN (lcp_xc_mpls)
     516             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
     517             : {
     518           0 :   return (lcp_xc_mpls_inline (vm, node, frame));
     519             : }
     520             : 
     521         354 : VLIB_REGISTER_NODE (
     522             :   lcp_xc_mpls) = { .name = "linux-cp-xc-mpls",
     523             :                    .vector_size = sizeof (u32),
     524             :                    .format_trace = format_lcp_xc_trace,
     525             :                    .type = VLIB_NODE_TYPE_INTERNAL,
     526             :                    .n_next_nodes = LCP_XC_MPLS_N_NEXT,
     527             :                    .next_nodes = {
     528             :                      [LCP_XC_MPLS_NEXT_DROP] = "error-drop",
     529             :                      [LCP_XC_MPLS_NEXT_IO] = "interface-output",
     530             :                    } };
     531             : 
     532         174 : VNET_FEATURE_INIT (lcp_xc_mpls_node, static) = {
     533             :   .arc_name = "mpls-input",
     534             :   .node_name = "linux-cp-xc-mpls",
     535             : };
     536             : 
     537             : typedef enum
     538             : {
     539             :   LCP_XC_L3_NEXT_XC,
     540             :   LCP_XC_L3_NEXT_LOOKUP,
     541             :   LCP_XC_L3_N_NEXT,
     542             : } lcp_xc_l3_next_t;
     543             : 
     544             : /**
     545             :  * X-connect all packets from the HOST to the PHY on L3 interfaces
     546             :  *
     547             :  * There's only one adjacency that can be used on these links.
     548             :  */
     549             : static_always_inline u32
     550           3 : lcp_xc_l3_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
     551             :                   vlib_frame_t *frame, ip_address_family_t af)
     552             : {
     553             :   u32 n_left_from, *from, *to_next, n_left_to_next;
     554             :   lcp_xc_next_t next_index;
     555           3 :   vnet_main_t *vnm = vnet_get_main ();
     556             : 
     557           3 :   next_index = 0;
     558           3 :   n_left_from = frame->n_vectors;
     559           3 :   from = vlib_frame_vector_args (frame);
     560             : 
     561           6 :   while (n_left_from > 0)
     562             :     {
     563           3 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     564             : 
     565          96 :       while (n_left_from > 0 && n_left_to_next > 0)
     566             :         {
     567             :           vlib_buffer_t *b0;
     568             :           const lcp_itf_pair_t *lip;
     569          93 :           u32 next0 = ~0;
     570             :           u32 bi0, lipi;
     571             : 
     572          93 :           bi0 = to_next[0] = from[0];
     573             : 
     574          93 :           from += 1;
     575          93 :           to_next += 1;
     576          93 :           n_left_from -= 1;
     577          93 :           n_left_to_next -= 1;
     578             : 
     579          93 :           b0 = vlib_get_buffer (vm, bi0);
     580             : 
     581             :           /* Flag buffers as locally originated. Otherwise their TTL will
     582             :            * be checked & decremented. That would break services like BGP
     583             :            * which set a TTL of 1 by default.
     584             :            */
     585          93 :           b0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
     586             : 
     587             :           lipi =
     588          93 :             lcp_itf_pair_find_by_host (vnet_buffer (b0)->sw_if_index[VLIB_RX]);
     589          93 :           lip = lcp_itf_pair_get (lipi);
     590             : 
     591             :           /* P2P tunnels can use generic adjacency */
     592          93 :           if (PREDICT_TRUE (
     593             :                 vnet_sw_interface_is_p2p (vnm, lip->lip_phy_sw_if_index)))
     594             :             {
     595          93 :               vnet_buffer (b0)->sw_if_index[VLIB_TX] =
     596          93 :                 lip->lip_phy_sw_if_index;
     597          93 :               vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
     598          93 :                 lip->lip_phy_adjs.adj_index[af];
     599          93 :               next0 = LCP_XC_L3_NEXT_XC;
     600             :             }
     601             :           /* P2MP tunnels require a fib lookup to find the right adjacency */
     602             :           else
     603             :             {
     604             :               /* lookup should use FIB table associated with phy interface */
     605           0 :               vnet_buffer (b0)->sw_if_index[VLIB_RX] =
     606           0 :                 lip->lip_phy_sw_if_index;
     607           0 :               next0 = LCP_XC_L3_NEXT_LOOKUP;
     608             :             }
     609             : 
     610          93 :           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
     611             :             {
     612          93 :               lcp_xc_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
     613          93 :               t->phy_sw_if_index = lip->lip_phy_sw_if_index;
     614          93 :               t->adj_index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
     615             :             }
     616             : 
     617          93 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
     618             :                                            n_left_to_next, bi0, next0);
     619             :         }
     620             : 
     621           3 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     622             :     }
     623             : 
     624           3 :   return frame->n_vectors;
     625             : }
     626             : 
     627             : /**
     628             :  * X-connect all packets from the HOST to the PHY.
     629             :  */
     630           4 : VLIB_NODE_FN (lcp_xc_l3_ip4_node)
     631             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
     632             : {
     633           2 :   return (lcp_xc_l3_inline (vm, node, frame, AF_IP4));
     634             : }
     635             : 
     636           3 : VLIB_NODE_FN (lcp_xc_l3_ip6_node)
     637             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
     638             : {
     639           1 :   return (lcp_xc_l3_inline (vm, node, frame, AF_IP6));
     640             : }
     641             : 
     642         354 : VLIB_REGISTER_NODE (lcp_xc_l3_ip4_node) = {
     643             :   .name = "linux-cp-xc-l3-ip4",
     644             :   .vector_size = sizeof (u32),
     645             :   .format_trace = format_lcp_xc_trace,
     646             :   .type = VLIB_NODE_TYPE_INTERNAL,
     647             : 
     648             :   .n_next_nodes = LCP_XC_L3_N_NEXT,
     649             :   .next_nodes = {
     650             :     [LCP_XC_L3_NEXT_XC] = "ip4-midchain",
     651             :     [LCP_XC_L3_NEXT_LOOKUP] = "ip4-lookup",
     652             :   },
     653             : };
     654             : 
     655         174 : VNET_FEATURE_INIT (lcp_xc_node_l3_ip4_unicast, static) = {
     656             :   .arc_name = "ip4-unicast",
     657             :   .node_name = "linux-cp-xc-l3-ip4",
     658             : };
     659             : 
     660         174 : VNET_FEATURE_INIT (lcp_xc_node_l3_ip4_multicaast, static) = {
     661             :   .arc_name = "ip4-multicast",
     662             :   .node_name = "linux-cp-xc-l3-ip4",
     663             : };
     664             : 
     665         354 : VLIB_REGISTER_NODE (lcp_xc_l3_ip6_node) = {
     666             :   .name = "linux-cp-xc-l3-ip6",
     667             :   .vector_size = sizeof (u32),
     668             :   .format_trace = format_lcp_xc_trace,
     669             :   .type = VLIB_NODE_TYPE_INTERNAL,
     670             : 
     671             :   .n_next_nodes = LCP_XC_L3_N_NEXT,
     672             :   .next_nodes = {
     673             :     [LCP_XC_L3_NEXT_XC] = "ip6-midchain",
     674             :     [LCP_XC_L3_NEXT_LOOKUP] = "ip6-lookup",
     675             :   },
     676             : };
     677             : 
     678         174 : VNET_FEATURE_INIT (lcp_xc_node_l3_ip6_unicast, static) = {
     679             :   .arc_name = "ip6-unicast",
     680             :   .node_name = "linux-cp-xc-l3-ip6",
     681             : };
     682             : 
     683         174 : VNET_FEATURE_INIT (lcp_xc_node_l3_ip6_multicast, static) = {
     684             :   .arc_name = "ip6-multicast",
     685             :   .node_name = "linux-cp-xc-l3-ip6",
     686             : };
     687             : 
     688             : #define foreach_lcp_arp                                                       \
     689             :   _ (DROP, "error-drop")                                                      \
     690             :   _ (IO, "interface-output")
     691             : 
     692             : typedef enum
     693             : {
     694             : #define _(sym, str) LCP_ARP_NEXT_##sym,
     695             :   foreach_lcp_arp
     696             : #undef _
     697             :     LCP_ARP_N_NEXT,
     698             : } lcp_arp_next_t;
     699             : 
     700             : typedef struct lcp_arp_trace_t_
     701             : {
     702             :   u32 rx_sw_if_index;
     703             :   u16 arp_opcode;
     704             : } lcp_arp_trace_t;
     705             : 
     706             : /* packet trace format function */
     707             : static u8 *
     708          17 : format_lcp_arp_trace (u8 *s, va_list *args)
     709             : {
     710          17 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
     711          17 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
     712          17 :   lcp_arp_trace_t *t = va_arg (*args, lcp_arp_trace_t *);
     713             : 
     714          17 :   s = format (s, "rx-sw-if-index: %u opcode: %u", t->rx_sw_if_index,
     715          17 :               t->arp_opcode);
     716             : 
     717          17 :   return s;
     718             : }
     719             : 
     720             : /**
     721             :  * punt ARP replies to the host
     722             :  */
     723          10 : VLIB_NODE_FN (lcp_arp_phy_node)
     724             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
     725             : {
     726             :   u32 n_left_from, *from, *to_next, n_left_to_next;
     727             :   lcp_arp_next_t next_index;
     728             :   u32 reply_copies[VLIB_FRAME_SIZE];
     729           8 :   u32 n_copies = 0;
     730             : 
     731           8 :   next_index = node->cached_next_index;
     732           8 :   n_left_from = frame->n_vectors;
     733           8 :   from = vlib_frame_vector_args (frame);
     734             : 
     735          16 :   while (n_left_from > 0)
     736             :     {
     737           8 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     738             : 
     739           8 :       while (n_left_from >= 2 && n_left_to_next >= 2)
     740             :         {
     741             :           u32 next0, next1, bi0, bi1;
     742             :           vlib_buffer_t *b0, *b1;
     743             :           ethernet_arp_header_t *arp0, *arp1;
     744             : 
     745           0 :           bi0 = to_next[0] = from[0];
     746           0 :           bi1 = to_next[1] = from[1];
     747             : 
     748           0 :           from += 2;
     749           0 :           n_left_from -= 2;
     750           0 :           to_next += 2;
     751           0 :           n_left_to_next -= 2;
     752             : 
     753           0 :           next0 = next1 = LCP_ARP_NEXT_DROP;
     754             : 
     755           0 :           b0 = vlib_get_buffer (vm, bi0);
     756           0 :           b1 = vlib_get_buffer (vm, bi1);
     757             : 
     758           0 :           arp0 = vlib_buffer_get_current (b0);
     759           0 :           arp1 = vlib_buffer_get_current (b1);
     760             : 
     761           0 :           vnet_feature_next (&next0, b0);
     762           0 :           vnet_feature_next (&next1, b1);
     763             : 
     764             :           /*
     765             :            * Replies might need to be received by the host, so we
     766             :            * make a copy of them.
     767             :            */
     768           0 :           if (arp0->opcode == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))
     769             :             {
     770           0 :               lcp_itf_pair_t *lip0 = 0;
     771             :               u32 lipi0;
     772             :               vlib_buffer_t *c0;
     773             :               u8 len0;
     774             : 
     775           0 :               lipi0 = lcp_itf_pair_find_by_phy (
     776           0 :                 vnet_buffer (b0)->sw_if_index[VLIB_RX]);
     777           0 :               lip0 = lcp_itf_pair_get (lipi0);
     778             : 
     779           0 :               if (lip0)
     780             :                 {
     781             :                   /*
     782             :                    * rewind to eth header, copy, advance back to current
     783             :                    */
     784           0 :                   len0 = ((u8 *) vlib_buffer_get_current (b0) -
     785           0 :                           (u8 *) ethernet_buffer_get_header (b0));
     786           0 :                   vlib_buffer_advance (b0, -len0);
     787           0 :                   c0 = vlib_buffer_copy (vm, b0);
     788           0 :                   vlib_buffer_advance (b0, len0);
     789             : 
     790           0 :                   if (c0)
     791             :                     {
     792             :                       /* Send to the host */
     793           0 :                       vnet_buffer (c0)->sw_if_index[VLIB_TX] =
     794           0 :                         lip0->lip_host_sw_if_index;
     795           0 :                       reply_copies[n_copies++] =
     796           0 :                         vlib_get_buffer_index (vm, c0);
     797             :                     }
     798             :                 }
     799             :             }
     800           0 :           if (arp1->opcode == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))
     801             :             {
     802           0 :               lcp_itf_pair_t *lip1 = 0;
     803             :               u32 lipi1;
     804             :               vlib_buffer_t *c1;
     805             :               u8 len1;
     806             : 
     807           0 :               lipi1 = lcp_itf_pair_find_by_phy (
     808           0 :                 vnet_buffer (b1)->sw_if_index[VLIB_RX]);
     809           0 :               lip1 = lcp_itf_pair_get (lipi1);
     810             : 
     811           0 :               if (lip1)
     812             :                 {
     813             :                   /*
     814             :                    * rewind to reveal the ethernet header
     815             :                    */
     816           0 :                   len1 = ((u8 *) vlib_buffer_get_current (b1) -
     817           0 :                           (u8 *) ethernet_buffer_get_header (b1));
     818           0 :                   vlib_buffer_advance (b1, -len1);
     819           0 :                   c1 = vlib_buffer_copy (vm, b1);
     820           0 :                   vlib_buffer_advance (b1, len1);
     821             : 
     822           0 :                   if (c1)
     823             :                     {
     824             :                       /* Send to the host */
     825           0 :                       vnet_buffer (c1)->sw_if_index[VLIB_TX] =
     826           0 :                         lip1->lip_host_sw_if_index;
     827           0 :                       reply_copies[n_copies++] =
     828           0 :                         vlib_get_buffer_index (vm, c1);
     829             :                     }
     830             :                 }
     831             :             }
     832             : 
     833           0 :           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
     834             :             {
     835           0 :               lcp_arp_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
     836           0 :               t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
     837             :             }
     838           0 :           if (PREDICT_FALSE ((b1->flags & VLIB_BUFFER_IS_TRACED)))
     839             :             {
     840           0 :               lcp_arp_trace_t *t = vlib_add_trace (vm, node, b1, sizeof (*t));
     841           0 :               t->rx_sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];
     842             :             }
     843             : 
     844           0 :           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
     845             :                                            n_left_to_next, bi0, bi1, next0,
     846             :                                            next1);
     847             :         }
     848             : 
     849          16 :       while (n_left_from > 0 && n_left_to_next > 0)
     850             :         {
     851             :           u32 next0, bi0;
     852             :           vlib_buffer_t *b0;
     853             :           ethernet_arp_header_t *arp0;
     854             :           u16 arp_opcode;
     855             : 
     856           8 :           bi0 = to_next[0] = from[0];
     857             : 
     858           8 :           from += 1;
     859           8 :           n_left_from -= 1;
     860           8 :           to_next += 1;
     861           8 :           n_left_to_next -= 1;
     862           8 :           next0 = LCP_ARP_NEXT_DROP;
     863             : 
     864           8 :           b0 = vlib_get_buffer (vm, bi0);
     865           8 :           arp0 = vlib_buffer_get_current (b0);
     866             : 
     867           8 :           vnet_feature_next (&next0, b0);
     868             : 
     869             :           /*
     870             :            * Replies might need to be received by the host, so we
     871             :            * make a copy of them.
     872             :            */
     873           8 :           arp_opcode = clib_host_to_net_u16 (arp0->opcode);
     874             : 
     875           8 :           if (arp_opcode == ETHERNET_ARP_OPCODE_reply)
     876             :             {
     877           8 :               lcp_itf_pair_t *lip0 = 0;
     878             :               vlib_buffer_t *c0;
     879             :               u32 lipi0;
     880             :               u8 len0;
     881             : 
     882           8 :               lipi0 = lcp_itf_pair_find_by_phy (
     883           8 :                 vnet_buffer (b0)->sw_if_index[VLIB_RX]);
     884           8 :               lip0 = lcp_itf_pair_get (lipi0);
     885             : 
     886           8 :               if (lip0)
     887             :                 {
     888             : 
     889             :                   /*
     890             :                    * rewind to reveal the ethernet header
     891             :                    */
     892           8 :                   len0 = ((u8 *) vlib_buffer_get_current (b0) -
     893           8 :                           (u8 *) ethernet_buffer_get_header (b0));
     894           8 :                   vlib_buffer_advance (b0, -len0);
     895           8 :                   c0 = vlib_buffer_copy (vm, b0);
     896           8 :                   vlib_buffer_advance (b0, len0);
     897             : 
     898           8 :                   if (c0)
     899             :                     {
     900             :                       /* Send to the host */
     901           8 :                       vnet_buffer (c0)->sw_if_index[VLIB_TX] =
     902           8 :                         lip0->lip_host_sw_if_index;
     903           8 :                       reply_copies[n_copies++] =
     904           8 :                         vlib_get_buffer_index (vm, c0);
     905             :                     }
     906             :                 }
     907             :             }
     908             : 
     909           8 :           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
     910             :             {
     911           8 :               lcp_arp_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
     912           8 :               t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
     913           8 :               t->arp_opcode = arp_opcode;
     914             :             }
     915             : 
     916           8 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
     917             :                                            n_left_to_next, bi0, next0);
     918             :         }
     919             : 
     920           8 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     921             :     }
     922             : 
     923           8 :   if (n_copies)
     924           8 :     vlib_buffer_enqueue_to_single_next (vm, node, reply_copies,
     925             :                                         LCP_ARP_NEXT_IO, n_copies);
     926             : 
     927           8 :   return frame->n_vectors;
     928             : }
     929             : 
     930         354 : VLIB_REGISTER_NODE (lcp_arp_phy_node) = {
     931             :   .name = "linux-cp-arp-phy",
     932             :   .vector_size = sizeof (u32),
     933             :   .format_trace = format_lcp_arp_trace,
     934             :   .type = VLIB_NODE_TYPE_INTERNAL,
     935             : 
     936             :   .n_errors = LINUXCP_N_ERROR,
     937             :   .error_counters = linuxcp_error_counters,
     938             : 
     939             :   .n_next_nodes = LCP_ARP_N_NEXT,
     940             :   .next_nodes = {
     941             :     [LCP_ARP_NEXT_DROP] = "error-drop",
     942             :     [LCP_ARP_NEXT_IO] = "interface-output",
     943             :   },
     944             : };
     945             : 
     946         174 : VNET_FEATURE_INIT (lcp_arp_phy_arp_feat, static) = {
     947             :   .arc_name = "arp",
     948             :   .node_name = "linux-cp-arp-phy",
     949             :   .runs_before = VNET_FEATURES ("arp-reply"),
     950             : };
     951             : 
     952             : /**
     953             :  * x-connect ARP packets from the host to the phy
     954             :  */
     955          10 : VLIB_NODE_FN (lcp_arp_host_node)
     956             : (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
     957             : {
     958             :   u32 n_left_from, *from, *to_next, n_left_to_next;
     959             :   lcp_arp_next_t next_index;
     960             : 
     961           8 :   next_index = node->cached_next_index;
     962           8 :   n_left_from = frame->n_vectors;
     963           8 :   from = vlib_frame_vector_args (frame);
     964             : 
     965          16 :   while (n_left_from > 0)
     966             :     {
     967           8 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     968             : 
     969          16 :       while (n_left_from > 0 && n_left_to_next > 0)
     970             :         {
     971             :           const lcp_itf_pair_t *lip0;
     972             :           lcp_arp_next_t next0;
     973             :           vlib_buffer_t *b0;
     974             :           u32 bi0, lipi0;
     975             :           u8 len0;
     976             : 
     977           8 :           bi0 = to_next[0] = from[0];
     978             : 
     979           8 :           from += 1;
     980           8 :           n_left_from -= 1;
     981           8 :           to_next += 1;
     982           8 :           n_left_to_next -= 1;
     983           8 :           next0 = LCP_ARP_NEXT_IO;
     984             : 
     985           8 :           b0 = vlib_get_buffer (vm, bi0);
     986             : 
     987             :           lipi0 =
     988           8 :             lcp_itf_pair_find_by_host (vnet_buffer (b0)->sw_if_index[VLIB_RX]);
     989           8 :           lip0 = lcp_itf_pair_get (lipi0);
     990             : 
     991             :           /* Send to the phy */
     992           8 :           vnet_buffer (b0)->sw_if_index[VLIB_TX] = lip0->lip_phy_sw_if_index;
     993             : 
     994           8 :           len0 = ((u8 *) vlib_buffer_get_current (b0) -
     995           8 :                   (u8 *) ethernet_buffer_get_header (b0));
     996           8 :           vlib_buffer_advance (b0, -len0);
     997             : 
     998           8 :           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
     999             :             {
    1000           8 :               lcp_arp_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
    1001           8 :               t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
    1002             :             }
    1003             : 
    1004           8 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
    1005             :                                            n_left_to_next, bi0, next0);
    1006             :         }
    1007             : 
    1008           8 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
    1009             :     }
    1010             : 
    1011           8 :   return frame->n_vectors;
    1012             : }
    1013             : 
    1014         354 : VLIB_REGISTER_NODE (lcp_arp_host_node) = {
    1015             :   .name = "linux-cp-arp-host",
    1016             :   .vector_size = sizeof (u32),
    1017             :   .format_trace = format_lcp_arp_trace,
    1018             :   .type = VLIB_NODE_TYPE_INTERNAL,
    1019             : 
    1020             :   .n_errors = LINUXCP_N_ERROR,
    1021             :   .error_counters = linuxcp_error_counters,
    1022             : 
    1023             :   .n_next_nodes = LCP_ARP_N_NEXT,
    1024             :   .next_nodes = {
    1025             :     [LCP_ARP_NEXT_DROP] = "error-drop",
    1026             :     [LCP_ARP_NEXT_IO] = "interface-output",
    1027             :   },
    1028             : };
    1029             : 
    1030         174 : VNET_FEATURE_INIT (lcp_arp_host_arp_feat, static) = {
    1031             :   .arc_name = "arp",
    1032             :   .node_name = "linux-cp-arp-host",
    1033             :   .runs_before = VNET_FEATURES ("arp-reply"),
    1034             : };
    1035             : 
    1036             : /*
    1037             :  * fd.io coding-style-patch-verification: ON
    1038             :  *
    1039             :  * Local Variables:
    1040             :  * eval: (c-set-style "gnu")
    1041             :  * End:
    1042             :  */

Generated by: LCOV version 1.14