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