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