3a88c3b0a52b50441aa2e76276b329daefe2388a
[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           const ip_adjacency_t *adj;
343
344           bi0 = to_next[0] = from[0];
345
346           from += 1;
347           to_next += 1;
348           n_left_from -= 1;
349           n_left_to_next -= 1;
350
351           b0 = vlib_get_buffer (vm, bi0);
352
353           lipi =
354             lcp_itf_pair_find_by_host (vnet_buffer (b0)->sw_if_index[VLIB_RX]);
355           lip = lcp_itf_pair_get (lipi);
356
357           vnet_buffer (b0)->sw_if_index[VLIB_TX] = lip->lip_phy_sw_if_index;
358           vlib_buffer_advance (b0, -lip->lip_rewrite_len);
359           eth = vlib_buffer_get_current (b0);
360
361           ai = ADJ_INDEX_INVALID;
362           if (!ethernet_address_cast (eth->dst_address))
363             ai = lcp_adj_lkup ((u8 *) eth, lip->lip_rewrite_len,
364                                vnet_buffer (b0)->sw_if_index[VLIB_TX]);
365           if (ai == ADJ_INDEX_INVALID)
366             ai = lip->lip_phy_adjs.adj_index[af];
367
368           adj = adj_get (ai);
369           vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ai;
370           next0 = adj->rewrite_header.next_index;
371           vnet_buffer (b0)->ip.save_rewrite_length = lip->lip_rewrite_len;
372
373           if (PREDICT_FALSE (adj->rewrite_header.flags &
374                              VNET_REWRITE_HAS_FEATURES))
375             vnet_feature_arc_start_w_cfg_index (
376               lm->output_feature_arc_index,
377               vnet_buffer (b0)->sw_if_index[VLIB_TX], &next0, b0,
378               adj->ia_cfg_index);
379
380           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
381             {
382               lcp_xc_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
383               t->phy_sw_if_index = lip->lip_phy_sw_if_index;
384               t->adj_index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
385             }
386
387           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
388                                            n_left_to_next, bi0, next0);
389         }
390
391       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
392     }
393
394   return frame->n_vectors;
395 }
396
397 VLIB_NODE_FN (lcp_xc_ip4)
398 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
399 {
400   return (lcp_xc_inline (vm, node, frame, AF_IP4));
401 }
402
403 VLIB_NODE_FN (lcp_xc_ip6)
404 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
405 {
406   return (lcp_xc_inline (vm, node, frame, AF_IP6));
407 }
408
409 VLIB_REGISTER_NODE (lcp_xc_ip4) = { .name = "linux-cp-xc-ip4",
410                                     .vector_size = sizeof (u32),
411                                     .format_trace = format_lcp_xc_trace,
412                                     .type = VLIB_NODE_TYPE_INTERNAL,
413                                     .sibling_of = "ip4-rewrite" };
414
415 VNET_FEATURE_INIT (lcp_xc_ip4_ucast_node, static) = {
416   .arc_name = "ip4-unicast",
417   .node_name = "linux-cp-xc-ip4",
418 };
419 VNET_FEATURE_INIT (lcp_xc_ip4_mcast_node, static) = {
420   .arc_name = "ip4-multicast",
421   .node_name = "linux-cp-xc-ip4",
422 };
423
424 VLIB_REGISTER_NODE (lcp_xc_ip6) = { .name = "linux-cp-xc-ip6",
425                                     .vector_size = sizeof (u32),
426                                     .format_trace = format_lcp_xc_trace,
427                                     .type = VLIB_NODE_TYPE_INTERNAL,
428                                     .sibling_of = "ip6-rewrite" };
429
430 VNET_FEATURE_INIT (lcp_xc_ip6_ucast_node, static) = {
431   .arc_name = "ip6-unicast",
432   .node_name = "linux-cp-xc-ip6",
433 };
434 VNET_FEATURE_INIT (lcp_xc_ip6_mcast_node, static) = {
435   .arc_name = "ip6-multicast",
436   .node_name = "linux-cp-xc-ip6",
437 };
438
439 typedef enum
440 {
441   LCP_XC_L3_NEXT_XC,
442   LCP_XC_L3_N_NEXT,
443 } lcp_xc_l3_next_t;
444
445 /**
446  * X-connect all packets from the HOST to the PHY on L3 interfaces
447  *
448  * There's only one adjacency that can be used on thises links.
449  */
450 static_always_inline u32
451 lcp_xc_l3_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
452                   vlib_frame_t *frame, ip_address_family_t af)
453 {
454   u32 n_left_from, *from, *to_next, n_left_to_next;
455   lcp_xc_next_t next_index;
456
457   next_index = 0;
458   n_left_from = frame->n_vectors;
459   from = vlib_frame_vector_args (frame);
460
461   while (n_left_from > 0)
462     {
463       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
464
465       while (n_left_from > 0 && n_left_to_next > 0)
466         {
467           vlib_buffer_t *b0;
468           const lcp_itf_pair_t *lip;
469           u32 next0 = ~0;
470           u32 bi0, lipi;
471
472           bi0 = to_next[0] = from[0];
473
474           from += 1;
475           to_next += 1;
476           n_left_from -= 1;
477           n_left_to_next -= 1;
478
479           b0 = vlib_get_buffer (vm, bi0);
480
481           /* Flag buffers as locally originated. Otherwise their TTL will
482            * be checked & decremented. That would break services like BGP
483            * which set a TTL of 1 by default.
484            */
485           b0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
486
487           lipi =
488             lcp_itf_pair_find_by_host (vnet_buffer (b0)->sw_if_index[VLIB_RX]);
489           lip = lcp_itf_pair_get (lipi);
490
491           vnet_buffer (b0)->sw_if_index[VLIB_TX] = lip->lip_phy_sw_if_index;
492           next0 = LCP_XC_L3_NEXT_XC;
493           vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
494             lip->lip_phy_adjs.adj_index[af];
495
496           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
497             {
498               lcp_xc_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
499               t->phy_sw_if_index = lip->lip_phy_sw_if_index;
500               t->adj_index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
501             }
502
503           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
504                                            n_left_to_next, bi0, next0);
505         }
506
507       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
508     }
509
510   return frame->n_vectors;
511 }
512
513 /**
514  * X-connect all packets from the HOST to the PHY.
515  */
516 VLIB_NODE_FN (lcp_xc_l3_ip4_node)
517 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
518 {
519   return (lcp_xc_l3_inline (vm, node, frame, AF_IP4));
520 }
521
522 VLIB_NODE_FN (lcp_xc_l3_ip6_node)
523 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
524 {
525   return (lcp_xc_l3_inline (vm, node, frame, AF_IP6));
526 }
527
528 VLIB_REGISTER_NODE (lcp_xc_l3_ip4_node) = {
529   .name = "linux-cp-xc-l3-ip4",
530   .vector_size = sizeof (u32),
531   .format_trace = format_lcp_xc_trace,
532   .type = VLIB_NODE_TYPE_INTERNAL,
533
534   .n_next_nodes = LCP_XC_L3_N_NEXT,
535   .next_nodes = {
536     [LCP_XC_L3_NEXT_XC] = "ip4-midchain",
537   },
538 };
539
540 VNET_FEATURE_INIT (lcp_xc_node_l3_ip4_unicast, static) = {
541   .arc_name = "ip4-unicast",
542   .node_name = "linux-cp-xc-l3-ip4",
543 };
544
545 VNET_FEATURE_INIT (lcp_xc_node_l3_ip4_multicaast, static) = {
546   .arc_name = "ip4-multicast",
547   .node_name = "linux-cp-xc-l3-ip4",
548 };
549
550 VLIB_REGISTER_NODE (lcp_xc_l3_ip6_node) = {
551   .name = "linux-cp-xc-l3-ip6",
552   .vector_size = sizeof (u32),
553   .format_trace = format_lcp_xc_trace,
554   .type = VLIB_NODE_TYPE_INTERNAL,
555
556   .n_next_nodes = LCP_XC_L3_N_NEXT,
557   .next_nodes = {
558     [LCP_XC_L3_NEXT_XC] = "ip6-midchain",
559   },
560 };
561
562 VNET_FEATURE_INIT (lcp_xc_node_l3_ip6_unicast, static) = {
563   .arc_name = "ip6-unicast",
564   .node_name = "linux-cp-xc-l3-ip6",
565 };
566
567 VNET_FEATURE_INIT (lcp_xc_node_l3_ip6_multicast, static) = {
568   .arc_name = "ip6-multicast",
569   .node_name = "linux-cp-xc-l3-ip6",
570 };
571
572 #define foreach_lcp_arp                                                       \
573   _ (DROP, "error-drop")                                                      \
574   _ (IO, "interface-output")
575
576 typedef enum
577 {
578 #define _(sym, str) LCP_ARP_NEXT_##sym,
579   foreach_lcp_arp
580 #undef _
581     LCP_ARP_N_NEXT,
582 } lcp_arp_next_t;
583
584 typedef struct lcp_arp_trace_t_
585 {
586   u32 rx_sw_if_index;
587   u16 arp_opcode;
588 } lcp_arp_trace_t;
589
590 /* packet trace format function */
591 static u8 *
592 format_lcp_arp_trace (u8 *s, va_list *args)
593 {
594   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
595   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
596   lcp_arp_trace_t *t = va_arg (*args, lcp_arp_trace_t *);
597
598   s = format (s, "rx-sw-if-index: %u opcode: %u", t->rx_sw_if_index,
599               t->arp_opcode);
600
601   return s;
602 }
603
604 /**
605  * punt ARP replies to the host
606  */
607 VLIB_NODE_FN (lcp_arp_phy_node)
608 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
609 {
610   u32 n_left_from, *from, *to_next, n_left_to_next;
611   lcp_arp_next_t next_index;
612   u32 reply_copies[VLIB_FRAME_SIZE];
613   u32 n_copies = 0;
614
615   next_index = node->cached_next_index;
616   n_left_from = frame->n_vectors;
617   from = vlib_frame_vector_args (frame);
618
619   while (n_left_from > 0)
620     {
621       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
622
623       while (n_left_from >= 2 && n_left_to_next >= 2)
624         {
625           u32 next0, next1, bi0, bi1;
626           vlib_buffer_t *b0, *b1;
627           ethernet_arp_header_t *arp0, *arp1;
628
629           bi0 = to_next[0] = from[0];
630           bi1 = to_next[1] = from[1];
631
632           from += 2;
633           n_left_from -= 2;
634           to_next += 2;
635           n_left_to_next -= 2;
636
637           next0 = next1 = LCP_ARP_NEXT_DROP;
638
639           b0 = vlib_get_buffer (vm, bi0);
640           b1 = vlib_get_buffer (vm, bi1);
641
642           arp0 = vlib_buffer_get_current (b0);
643           arp1 = vlib_buffer_get_current (b1);
644
645           vnet_feature_next (&next0, b0);
646           vnet_feature_next (&next1, b1);
647
648           /*
649            * Replies might need to be received by the host, so we
650            * make a copy of them.
651            */
652           if (arp0->opcode == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))
653             {
654               lcp_itf_pair_t *lip0 = 0;
655               u32 lipi0;
656               vlib_buffer_t *c0;
657               u8 len0;
658
659               lipi0 = lcp_itf_pair_find_by_phy (
660                 vnet_buffer (b0)->sw_if_index[VLIB_RX]);
661               lip0 = lcp_itf_pair_get (lipi0);
662
663               if (lip0)
664                 {
665                   /*
666                    * rewind to eth header, copy, advance back to current
667                    */
668                   len0 = ((u8 *) vlib_buffer_get_current (b0) -
669                           (u8 *) ethernet_buffer_get_header (b0));
670                   vlib_buffer_advance (b0, -len0);
671                   c0 = vlib_buffer_copy (vm, b0);
672                   vlib_buffer_advance (b0, len0);
673
674                   if (c0)
675                     {
676                       /* Send to the host */
677                       vnet_buffer (c0)->sw_if_index[VLIB_TX] =
678                         lip0->lip_host_sw_if_index;
679                       reply_copies[n_copies++] =
680                         vlib_get_buffer_index (vm, c0);
681                     }
682                 }
683             }
684           if (arp1->opcode == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))
685             {
686               lcp_itf_pair_t *lip1 = 0;
687               u32 lipi1;
688               vlib_buffer_t *c1;
689               u8 len1;
690
691               lipi1 = lcp_itf_pair_find_by_phy (
692                 vnet_buffer (b1)->sw_if_index[VLIB_RX]);
693               lip1 = lcp_itf_pair_get (lipi1);
694
695               if (lip1)
696                 {
697                   /*
698                    * rewind to reveal the ethernet header
699                    */
700                   len1 = ((u8 *) vlib_buffer_get_current (b1) -
701                           (u8 *) ethernet_buffer_get_header (b1));
702                   vlib_buffer_advance (b1, -len1);
703                   c1 = vlib_buffer_copy (vm, b1);
704                   vlib_buffer_advance (b1, len1);
705
706                   if (c1)
707                     {
708                       /* Send to the host */
709                       vnet_buffer (c1)->sw_if_index[VLIB_TX] =
710                         lip1->lip_host_sw_if_index;
711                       reply_copies[n_copies++] =
712                         vlib_get_buffer_index (vm, c1);
713                     }
714                 }
715             }
716
717           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
718             {
719               lcp_arp_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
720               t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
721             }
722           if (PREDICT_FALSE ((b1->flags & VLIB_BUFFER_IS_TRACED)))
723             {
724               lcp_arp_trace_t *t = vlib_add_trace (vm, node, b1, sizeof (*t));
725               t->rx_sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];
726             }
727
728           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
729                                            n_left_to_next, bi0, bi1, next0,
730                                            next1);
731         }
732
733       while (n_left_from > 0 && n_left_to_next > 0)
734         {
735           u32 next0, bi0;
736           vlib_buffer_t *b0;
737           ethernet_arp_header_t *arp0;
738           u16 arp_opcode;
739
740           bi0 = to_next[0] = from[0];
741
742           from += 1;
743           n_left_from -= 1;
744           to_next += 1;
745           n_left_to_next -= 1;
746           next0 = LCP_ARP_NEXT_DROP;
747
748           b0 = vlib_get_buffer (vm, bi0);
749           arp0 = vlib_buffer_get_current (b0);
750
751           vnet_feature_next (&next0, b0);
752
753           /*
754            * Replies might need to be received by the host, so we
755            * make a copy of them.
756            */
757           arp_opcode = clib_host_to_net_u16 (arp0->opcode);
758
759           if (arp_opcode == ETHERNET_ARP_OPCODE_reply)
760             {
761               lcp_itf_pair_t *lip0 = 0;
762               vlib_buffer_t *c0;
763               u32 lipi0;
764               u8 len0;
765
766               lipi0 = lcp_itf_pair_find_by_phy (
767                 vnet_buffer (b0)->sw_if_index[VLIB_RX]);
768               lip0 = lcp_itf_pair_get (lipi0);
769
770               if (lip0)
771                 {
772
773                   /*
774                    * rewind to reveal the ethernet header
775                    */
776                   len0 = ((u8 *) vlib_buffer_get_current (b0) -
777                           (u8 *) ethernet_buffer_get_header (b0));
778                   vlib_buffer_advance (b0, -len0);
779                   c0 = vlib_buffer_copy (vm, b0);
780                   vlib_buffer_advance (b0, len0);
781
782                   if (c0)
783                     {
784                       /* Send to the host */
785                       vnet_buffer (c0)->sw_if_index[VLIB_TX] =
786                         lip0->lip_host_sw_if_index;
787                       reply_copies[n_copies++] =
788                         vlib_get_buffer_index (vm, c0);
789                     }
790                 }
791             }
792
793           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
794             {
795               lcp_arp_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
796               t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
797               t->arp_opcode = arp_opcode;
798             }
799
800           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
801                                            n_left_to_next, bi0, next0);
802         }
803
804       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
805     }
806
807   if (n_copies)
808     vlib_buffer_enqueue_to_single_next (vm, node, reply_copies,
809                                         LCP_ARP_NEXT_IO, n_copies);
810
811   return frame->n_vectors;
812 }
813
814 VLIB_REGISTER_NODE (lcp_arp_phy_node) = {
815   .name = "linux-cp-arp-phy",
816   .vector_size = sizeof (u32),
817   .format_trace = format_lcp_arp_trace,
818   .type = VLIB_NODE_TYPE_INTERNAL,
819
820   .n_errors = LINUXCP_N_ERROR,
821   .error_counters = linuxcp_error_counters,
822
823   .n_next_nodes = LCP_ARP_N_NEXT,
824   .next_nodes = {
825     [LCP_ARP_NEXT_DROP] = "error-drop",
826     [LCP_ARP_NEXT_IO] = "interface-output",
827   },
828 };
829
830 VNET_FEATURE_INIT (lcp_arp_phy_arp_feat, static) = {
831   .arc_name = "arp",
832   .node_name = "linux-cp-arp-phy",
833   .runs_before = VNET_FEATURES ("arp-reply"),
834 };
835
836 /**
837  * x-connect ARP packets from the host to the phy
838  */
839 VLIB_NODE_FN (lcp_arp_host_node)
840 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
841 {
842   u32 n_left_from, *from, *to_next, n_left_to_next;
843   lcp_arp_next_t next_index;
844
845   next_index = node->cached_next_index;
846   n_left_from = frame->n_vectors;
847   from = vlib_frame_vector_args (frame);
848
849   while (n_left_from > 0)
850     {
851       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
852
853       while (n_left_from > 0 && n_left_to_next > 0)
854         {
855           const lcp_itf_pair_t *lip0;
856           lcp_arp_next_t next0;
857           vlib_buffer_t *b0;
858           u32 bi0, lipi0;
859           u8 len0;
860
861           bi0 = to_next[0] = from[0];
862
863           from += 1;
864           n_left_from -= 1;
865           to_next += 1;
866           n_left_to_next -= 1;
867           next0 = LCP_ARP_NEXT_IO;
868
869           b0 = vlib_get_buffer (vm, bi0);
870
871           lipi0 =
872             lcp_itf_pair_find_by_host (vnet_buffer (b0)->sw_if_index[VLIB_RX]);
873           lip0 = lcp_itf_pair_get (lipi0);
874
875           /* Send to the phy */
876           vnet_buffer (b0)->sw_if_index[VLIB_TX] = lip0->lip_phy_sw_if_index;
877
878           len0 = ((u8 *) vlib_buffer_get_current (b0) -
879                   (u8 *) ethernet_buffer_get_header (b0));
880           vlib_buffer_advance (b0, -len0);
881
882           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
883             {
884               lcp_arp_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
885               t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
886             }
887
888           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
889                                            n_left_to_next, bi0, next0);
890         }
891
892       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
893     }
894
895   return frame->n_vectors;
896 }
897
898 VLIB_REGISTER_NODE (lcp_arp_host_node) = {
899   .name = "linux-cp-arp-host",
900   .vector_size = sizeof (u32),
901   .format_trace = format_lcp_arp_trace,
902   .type = VLIB_NODE_TYPE_INTERNAL,
903
904   .n_errors = LINUXCP_N_ERROR,
905   .error_counters = linuxcp_error_counters,
906
907   .n_next_nodes = LCP_ARP_N_NEXT,
908   .next_nodes = {
909     [LCP_ARP_NEXT_DROP] = "error-drop",
910     [LCP_ARP_NEXT_IO] = "interface-output",
911   },
912 };
913
914 VNET_FEATURE_INIT (lcp_arp_host_arp_feat, static) = {
915   .arc_name = "arp",
916   .node_name = "linux-cp-arp-host",
917   .runs_before = VNET_FEATURES ("arp-reply"),
918 };
919
920 /*
921  * fd.io coding-style-patch-verification: ON
922  *
923  * Local Variables:
924  * eval: (c-set-style "gnu")
925  * End:
926  */