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