blob: 286ec3c0e78de5a3ff66a8e164f57ba3efbdd431 [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
Damjan Marion650223c2018-11-14 16:55:53 +01002 * Copyright (c) 2018 Cisco and/or its affiliates.
Ed Warnickecb9cada2015-12-08 15:45:58 -07003 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15/*
16 * ethernet_node.c: ethernet packet processing
17 *
18 * Copyright (c) 2008 Eliot Dresselhaus
19 *
20 * Permission is hereby granted, free of charge, to any person obtaining
21 * a copy of this software and associated documentation files (the
22 * "Software"), to deal in the Software without restriction, including
23 * without limitation the rights to use, copy, modify, merge, publish,
24 * distribute, sublicense, and/or sell copies of the Software, and to
25 * permit persons to whom the Software is furnished to do so, subject to
26 * the following conditions:
27 *
28 * The above copyright notice and this permission notice shall be
29 * included in all copies or substantial portions of the Software.
30 *
31 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 */
39
40#include <vlib/vlib.h>
41#include <vnet/pg/pg.h>
42#include <vnet/ethernet/ethernet.h>
Pavel Kotucek15ac81c2017-06-20 14:00:26 +020043#include <vnet/ethernet/p2p_ethernet.h>
Neale Ranns17ff3c12018-07-04 10:24:24 -070044#include <vnet/devices/pipe/pipe.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070045#include <vppinfra/sparse_vec.h>
46#include <vnet/l2/l2_bvi.h>
47
Ed Warnickecb9cada2015-12-08 15:45:58 -070048#define foreach_ethernet_input_next \
49 _ (PUNT, "error-punt") \
50 _ (DROP, "error-drop") \
Damjan Marion650223c2018-11-14 16:55:53 +010051 _ (LLC, "llc-input") \
52 _ (IP4_INPUT, "ip4-input") \
53 _ (IP4_INPUT_NCS, "ip4-input-no-checksum")
Ed Warnickecb9cada2015-12-08 15:45:58 -070054
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -070055typedef enum
56{
Ed Warnickecb9cada2015-12-08 15:45:58 -070057#define _(s,n) ETHERNET_INPUT_NEXT_##s,
58 foreach_ethernet_input_next
59#undef _
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -070060 ETHERNET_INPUT_N_NEXT,
Ed Warnickecb9cada2015-12-08 15:45:58 -070061} ethernet_input_next_t;
62
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -070063typedef struct
64{
Ed Warnickecb9cada2015-12-08 15:45:58 -070065 u8 packet_data[32];
Damjan Marion650223c2018-11-14 16:55:53 +010066 u16 frame_flags;
67 ethernet_input_frame_t frame_data;
Ed Warnickecb9cada2015-12-08 15:45:58 -070068} ethernet_input_trace_t;
69
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -070070static u8 *
71format_ethernet_input_trace (u8 * s, va_list * va)
Ed Warnickecb9cada2015-12-08 15:45:58 -070072{
73 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
74 CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -070075 ethernet_input_trace_t *t = va_arg (*va, ethernet_input_trace_t *);
Damjan Marion650223c2018-11-14 16:55:53 +010076 u32 indent = format_get_indent (s);
Ed Warnickecb9cada2015-12-08 15:45:58 -070077
Damjan Marion650223c2018-11-14 16:55:53 +010078 if (t->frame_flags)
79 {
80 s = format (s, "frame: flags 0x%x", t->frame_flags);
81 if (t->frame_flags & ETH_INPUT_FRAME_F_SINGLE_SW_IF_IDX)
82 s = format (s, ", hw-if-index %u, sw-if-index %u",
83 t->frame_data.hw_if_index, t->frame_data.sw_if_index);
84 s = format (s, "\n%U", format_white_space, indent);
85 }
Ed Warnickecb9cada2015-12-08 15:45:58 -070086 s = format (s, "%U", format_ethernet_header, t->packet_data);
87
88 return s;
89}
90
Damjan Marione849da22018-09-12 13:32:01 +020091extern vlib_node_registration_t ethernet_input_node;
Ed Warnickecb9cada2015-12-08 15:45:58 -070092
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -070093typedef enum
94{
Ed Warnickecb9cada2015-12-08 15:45:58 -070095 ETHERNET_INPUT_VARIANT_ETHERNET,
96 ETHERNET_INPUT_VARIANT_ETHERNET_TYPE,
Ed Warnickecb9cada2015-12-08 15:45:58 -070097 ETHERNET_INPUT_VARIANT_NOT_L2,
98} ethernet_input_variant_t;
99
100
Ed Warnickecb9cada2015-12-08 15:45:58 -0700101// Parse the ethernet header to extract vlan tags and innermost ethertype
102static_always_inline void
103parse_header (ethernet_input_variant_t variant,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700104 vlib_buffer_t * b0,
105 u16 * type,
106 u16 * orig_type,
107 u16 * outer_id, u16 * inner_id, u32 * match_flags)
108{
Chris Luke194ebc52016-04-25 14:26:55 -0400109 u8 vlan_count;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700110
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700111 if (variant == ETHERNET_INPUT_VARIANT_ETHERNET
112 || variant == ETHERNET_INPUT_VARIANT_NOT_L2)
113 {
114 ethernet_header_t *e0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700115
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700116 e0 = (void *) (b0->data + b0->current_data);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700117
Damjan Marion072401e2017-07-13 18:53:27 +0200118 vnet_buffer (b0)->l2_hdr_offset = b0->current_data;
Steven35de3b32017-12-03 23:40:54 -0800119 b0->flags |= VNET_BUFFER_F_L2_HDR_OFFSET_VALID;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700120
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700121 vlib_buffer_advance (b0, sizeof (e0[0]));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700122
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700123 *type = clib_net_to_host_u16 (e0->type);
124 }
125 else if (variant == ETHERNET_INPUT_VARIANT_ETHERNET_TYPE)
126 {
127 // here when prior node was LLC/SNAP processing
128 u16 *e0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700129
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700130 e0 = (void *) (b0->data + b0->current_data);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700131
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700132 vlib_buffer_advance (b0, sizeof (e0[0]));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700133
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700134 *type = clib_net_to_host_u16 (e0[0]);
135 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700136
137 // save for distinguishing between dot1q and dot1ad later
138 *orig_type = *type;
139
140 // default the tags to 0 (used if there is no corresponding tag)
141 *outer_id = 0;
142 *inner_id = 0;
143
144 *match_flags = SUBINT_CONFIG_VALID | SUBINT_CONFIG_MATCH_0_TAG;
Chris Luke194ebc52016-04-25 14:26:55 -0400145 vlan_count = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700146
147 // check for vlan encaps
Damjan Marionb94bdad2016-09-19 11:32:03 +0200148 if (ethernet_frame_is_tagged (*type))
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700149 {
150 ethernet_vlan_header_t *h0;
151 u16 tag;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700152
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700153 *match_flags = SUBINT_CONFIG_VALID | SUBINT_CONFIG_MATCH_1_TAG;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700154
155 h0 = (void *) (b0->data + b0->current_data);
156
157 tag = clib_net_to_host_u16 (h0->priority_cfi_and_id);
158
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700159 *outer_id = tag & 0xfff;
Neale Ranns30d0fd42017-05-30 07:30:04 -0700160 if (0 == *outer_id)
161 *match_flags &= ~SUBINT_CONFIG_MATCH_1_TAG;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700162
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700163 *type = clib_net_to_host_u16 (h0->type);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700164
165 vlib_buffer_advance (b0, sizeof (h0[0]));
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700166 vlan_count = 1;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700167
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700168 if (*type == ETHERNET_TYPE_VLAN)
169 {
170 // Double tagged packet
171 *match_flags = SUBINT_CONFIG_VALID | SUBINT_CONFIG_MATCH_2_TAG;
172
173 h0 = (void *) (b0->data + b0->current_data);
174
175 tag = clib_net_to_host_u16 (h0->priority_cfi_and_id);
176
177 *inner_id = tag & 0xfff;
178
179 *type = clib_net_to_host_u16 (h0->type);
180
181 vlib_buffer_advance (b0, sizeof (h0[0]));
182 vlan_count = 2;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700183 if (*type == ETHERNET_TYPE_VLAN)
184 {
185 // More than double tagged packet
186 *match_flags = SUBINT_CONFIG_VALID | SUBINT_CONFIG_MATCH_3_TAG;
Eyal Bari6f7ebf92017-06-13 12:09:37 +0300187
188 vlib_buffer_advance (b0, sizeof (h0[0]));
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700189 vlan_count = 3; // "unknown" number, aka, 3-or-more
190 }
191 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700192 }
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700193 ethernet_buffer_set_vlan_count (b0, vlan_count);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700194}
195
196// Determine the subinterface for this packet, given the result of the
197// vlan table lookups and vlan header parsing. Check the most specific
198// matches first.
199static_always_inline void
200identify_subint (vnet_hw_interface_t * hi,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700201 vlib_buffer_t * b0,
202 u32 match_flags,
203 main_intf_t * main_intf,
204 vlan_intf_t * vlan_intf,
205 qinq_intf_t * qinq_intf,
206 u32 * new_sw_if_index, u8 * error0, u32 * is_l2)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700207{
208 u32 matched;
209
Damjan Marionddf6e082018-11-26 16:05:07 +0100210 matched = eth_identify_subint (hi, match_flags, main_intf, vlan_intf,
211 qinq_intf, new_sw_if_index, error0, is_l2);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700212
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700213 if (matched)
214 {
Ed Warnickecb9cada2015-12-08 15:45:58 -0700215
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700216 // Perform L3 my-mac filter
217 // A unicast packet arriving on an L3 interface must have a dmac matching the interface mac.
218 // This is required for promiscuous mode, else we will forward packets we aren't supposed to.
219 if (!(*is_l2))
220 {
221 ethernet_header_t *e0;
Damjan Marion072401e2017-07-13 18:53:27 +0200222 e0 = (void *) (b0->data + vnet_buffer (b0)->l2_hdr_offset);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700223
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700224 if (!(ethernet_address_cast (e0->dst_address)))
225 {
Neale Ranns37029302018-08-10 05:30:06 -0700226 if (!ethernet_mac_address_equal ((u8 *) e0, hi->hw_address))
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700227 {
228 *error0 = ETHERNET_ERROR_L3_MAC_MISMATCH;
229 }
230 }
231 }
232
233 // Check for down subinterface
234 *error0 = (*new_sw_if_index) != ~0 ? (*error0) : ETHERNET_ERROR_DOWN;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700235 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700236}
237
238static_always_inline void
239determine_next_node (ethernet_main_t * em,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700240 ethernet_input_variant_t variant,
241 u32 is_l20,
242 u32 type0, vlib_buffer_t * b0, u8 * error0, u8 * next0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700243{
Andrew Yourtchenko20e6d362018-10-05 20:36:03 +0200244 vnet_buffer (b0)->l3_hdr_offset = b0->current_data;
245 b0->flags |= VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
246
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700247 if (PREDICT_FALSE (*error0 != ETHERNET_ERROR_NONE))
248 {
249 // some error occurred
250 *next0 = ETHERNET_INPUT_NEXT_DROP;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700251 }
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700252 else if (is_l20)
253 {
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700254 // record the L2 len and reset the buffer so the L2 header is preserved
John Lob14826e2018-04-18 15:52:23 -0400255 u32 eth_start = vnet_buffer (b0)->l2_hdr_offset;
256 vnet_buffer (b0)->l2.l2_len = b0->current_data - eth_start;
257 *next0 = em->l2_next;
Eyal Bari6f7ebf92017-06-13 12:09:37 +0300258 ASSERT (vnet_buffer (b0)->l2.l2_len ==
259 ethernet_buffer_header_size (b0));
Andrew Yourtchenko20e6d362018-10-05 20:36:03 +0200260 vlib_buffer_advance (b0, -(vnet_buffer (b0)->l2.l2_len));
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700261
262 // check for common IP/MPLS ethertypes
263 }
264 else if (type0 == ETHERNET_TYPE_IP4)
265 {
266 *next0 = em->l3_next.input_next_ip4;
267 }
268 else if (type0 == ETHERNET_TYPE_IP6)
269 {
270 *next0 = em->l3_next.input_next_ip6;
271 }
Neale Ranns0f26c5a2017-03-01 15:12:11 -0800272 else if (type0 == ETHERNET_TYPE_MPLS)
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700273 {
274 *next0 = em->l3_next.input_next_mpls;
275
276 }
277 else if (em->redirect_l3)
278 {
279 // L3 Redirect is on, the cached common next nodes will be
280 // pointing to the redirect node, catch the uncommon types here
281 *next0 = em->redirect_l3_next;
282 }
283 else
284 {
285 // uncommon ethertype, check table
286 u32 i0;
287 i0 = sparse_vec_index (em->l3_next.input_next_by_type, type0);
288 *next0 = vec_elt (em->l3_next.input_next_by_type, i0);
289 *error0 =
290 i0 ==
291 SPARSE_VEC_INVALID_INDEX ? ETHERNET_ERROR_UNKNOWN_TYPE : *error0;
292
293 // The table is not populated with LLC values, so check that now.
Damjan Marion607de1a2016-08-16 22:53:54 +0200294 // If variant is variant_ethernet then we came from LLC processing. Don't
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -0700295 // go back there; drop instead using by keeping the drop/bad table result.
296 if ((type0 < 0x600) && (variant == ETHERNET_INPUT_VARIANT_ETHERNET))
297 {
298 *next0 = ETHERNET_INPUT_NEXT_LLC;
299 }
300 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700301}
302
Damjan Marion650223c2018-11-14 16:55:53 +0100303
304/* following vector code relies on following assumptions */
305STATIC_ASSERT_OFFSET_OF (vlib_buffer_t, current_data, 0);
306STATIC_ASSERT_OFFSET_OF (vlib_buffer_t, current_length, 2);
307STATIC_ASSERT_OFFSET_OF (vlib_buffer_t, flags, 4);
308STATIC_ASSERT (STRUCT_OFFSET_OF (vnet_buffer_opaque_t, l2_hdr_offset) ==
309 STRUCT_OFFSET_OF (vnet_buffer_opaque_t, l3_hdr_offset) - 2,
310 "l3_hdr_offset must follow l2_hdr_offset");
311
312static_always_inline void
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100313eth_input_adv_and_flags_x4 (vlib_buffer_t ** b, int is_l3)
Damjan Marion650223c2018-11-14 16:55:53 +0100314{
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100315 i16 adv = sizeof (ethernet_header_t);
316 u32 flags = VNET_BUFFER_F_L2_HDR_OFFSET_VALID |
317 VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
318
Damjan Marion650223c2018-11-14 16:55:53 +0100319#ifdef CLIB_HAVE_VEC256
320 /* to reduce number of small loads/stores we are loading first 64 bits
321 of each buffer metadata into 256-bit register so we can advance
322 current_data, current_length and flags.
323 Observed saving of this code is ~2 clocks per packet */
324 u64x4 r, radv;
325
326 /* vector if signed 16 bit integers used in signed vector add operation
327 to advnce current_data and current_length */
328 u32x8 flags4 = { 0, flags, 0, flags, 0, flags, 0, flags };
329 i16x16 adv4 = {
330 adv, -adv, 0, 0, adv, -adv, 0, 0,
331 adv, -adv, 0, 0, adv, -adv, 0, 0
332 };
333
334 /* load 4 x 64 bits */
335 r = u64x4_gather (b[0], b[1], b[2], b[3]);
336
337 /* set flags */
338 r |= (u64x4) flags4;
339
340 /* advance buffer */
341 radv = (u64x4) ((i16x16) r + adv4);
342
343 /* write 4 x 64 bits */
344 u64x4_scatter (is_l3 ? radv : r, b[0], b[1], b[2], b[3]);
345
346 /* use old current_data as l2_hdr_offset and new current_data as
347 l3_hdr_offset */
348 r = (u64x4) u16x16_blend (r, radv << 16, 0xaa);
349
350 /* store both l2_hdr_offset and l3_hdr_offset in single store operation */
351 u32x8_scatter_one ((u32x8) r, 0, &vnet_buffer (b[0])->l2_hdr_offset);
352 u32x8_scatter_one ((u32x8) r, 2, &vnet_buffer (b[1])->l2_hdr_offset);
353 u32x8_scatter_one ((u32x8) r, 4, &vnet_buffer (b[2])->l2_hdr_offset);
354 u32x8_scatter_one ((u32x8) r, 6, &vnet_buffer (b[3])->l2_hdr_offset);
355
Damjan Marione9cebdf2018-11-21 00:47:42 +0100356 if (is_l3)
357 {
358 ASSERT (b[0]->current_data == vnet_buffer (b[0])->l3_hdr_offset);
359 ASSERT (b[1]->current_data == vnet_buffer (b[1])->l3_hdr_offset);
360 ASSERT (b[2]->current_data == vnet_buffer (b[2])->l3_hdr_offset);
361 ASSERT (b[3]->current_data == vnet_buffer (b[3])->l3_hdr_offset);
Damjan Marion650223c2018-11-14 16:55:53 +0100362
Damjan Marione9cebdf2018-11-21 00:47:42 +0100363 ASSERT (b[0]->current_data - vnet_buffer (b[0])->l2_hdr_offset == adv);
364 ASSERT (b[1]->current_data - vnet_buffer (b[1])->l2_hdr_offset == adv);
365 ASSERT (b[2]->current_data - vnet_buffer (b[2])->l2_hdr_offset == adv);
366 ASSERT (b[3]->current_data - vnet_buffer (b[3])->l2_hdr_offset == adv);
367 }
368 else
369 {
370 ASSERT (b[0]->current_data == vnet_buffer (b[0])->l2_hdr_offset);
371 ASSERT (b[1]->current_data == vnet_buffer (b[1])->l2_hdr_offset);
372 ASSERT (b[2]->current_data == vnet_buffer (b[2])->l2_hdr_offset);
373 ASSERT (b[3]->current_data == vnet_buffer (b[3])->l2_hdr_offset);
374
375 ASSERT (b[0]->current_data - vnet_buffer (b[0])->l3_hdr_offset == -adv);
376 ASSERT (b[1]->current_data - vnet_buffer (b[1])->l3_hdr_offset == -adv);
377 ASSERT (b[2]->current_data - vnet_buffer (b[2])->l3_hdr_offset == -adv);
378 ASSERT (b[3]->current_data - vnet_buffer (b[3])->l3_hdr_offset == -adv);
379 }
Damjan Marion650223c2018-11-14 16:55:53 +0100380
381#else
382 vnet_buffer (b[0])->l2_hdr_offset = b[0]->current_data;
383 vnet_buffer (b[1])->l2_hdr_offset = b[1]->current_data;
384 vnet_buffer (b[2])->l2_hdr_offset = b[2]->current_data;
385 vnet_buffer (b[3])->l2_hdr_offset = b[3]->current_data;
386 vnet_buffer (b[0])->l3_hdr_offset = b[0]->current_data + adv;
387 vnet_buffer (b[1])->l3_hdr_offset = b[1]->current_data + adv;
388 vnet_buffer (b[2])->l3_hdr_offset = b[2]->current_data + adv;
389 vnet_buffer (b[3])->l3_hdr_offset = b[3]->current_data + adv;
390
391 if (is_l3)
392 {
393 vlib_buffer_advance (b[0], adv);
394 vlib_buffer_advance (b[1], adv);
395 vlib_buffer_advance (b[2], adv);
396 vlib_buffer_advance (b[3], adv);
397 }
398
399 b[0]->flags |= flags;
400 b[1]->flags |= flags;
401 b[2]->flags |= flags;
402 b[3]->flags |= flags;
403#endif
404
405 if (!is_l3)
406 {
407 vnet_buffer (b[0])->l2.l2_len = adv;
408 vnet_buffer (b[1])->l2.l2_len = adv;
409 vnet_buffer (b[2])->l2.l2_len = adv;
410 vnet_buffer (b[3])->l2.l2_len = adv;
411 }
412}
413
414static_always_inline void
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100415eth_input_adv_and_flags_x1 (vlib_buffer_t ** b, int is_l3)
Damjan Marion650223c2018-11-14 16:55:53 +0100416{
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100417 i16 adv = sizeof (ethernet_header_t);
418 u32 flags = VNET_BUFFER_F_L2_HDR_OFFSET_VALID |
419 VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
420
Damjan Marion650223c2018-11-14 16:55:53 +0100421 vnet_buffer (b[0])->l2_hdr_offset = b[0]->current_data;
422 vnet_buffer (b[0])->l3_hdr_offset = b[0]->current_data + adv;
423
424 if (is_l3)
425 vlib_buffer_advance (b[0], adv);
426 b[0]->flags |= flags;
427 if (!is_l3)
428 vnet_buffer (b[0])->l2.l2_len = adv;
429}
430
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100431
Damjan Marion650223c2018-11-14 16:55:53 +0100432static_always_inline void
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100433eth_input_get_etype_and_tags (vlib_buffer_t ** b, u16 * etype, u64 * tags,
434 u64 * dmacs, int offset, int dmac_check)
Damjan Marion650223c2018-11-14 16:55:53 +0100435{
Damjan Marion650223c2018-11-14 16:55:53 +0100436 ethernet_header_t *e;
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100437 e = vlib_buffer_get_current (b[offset]);
438#ifdef CLIB_HAVE_VEC128
439 u64x2 r = u64x2_load_unaligned (((u8 *) & e->type) - 6);
440 etype[offset] = ((u16x8) r)[3];
441 tags[offset] = r[1];
442#else
443 etype[offset] = e->type;
444 tags[offset] = *(u64 *) (e + 1);
445#endif
Damjan Marion650223c2018-11-14 16:55:53 +0100446
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100447 if (dmac_check)
448 dmacs[offset] = *(u64 *) e;
449}
Damjan Marion650223c2018-11-14 16:55:53 +0100450
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100451static_always_inline u16
452eth_input_next_by_type (u16 etype)
453{
454 ethernet_main_t *em = &ethernet_main;
455
456 return (etype < 0x600) ? ETHERNET_INPUT_NEXT_LLC :
457 vec_elt (em->l3_next.input_next_by_type,
458 sparse_vec_index (em->l3_next.input_next_by_type, etype));
459}
460
461typedef struct
462{
463 u64 tag, mask;
464 u32 sw_if_index;
465 u16 type, len, next;
466 i16 adv;
467 u8 err, n_tags;
468 u64 n_packets, n_bytes;
469} eth_input_tag_lookup_t;
470
471static_always_inline void
472eth_input_update_if_counters (vlib_main_t * vm, vnet_main_t * vnm,
473 eth_input_tag_lookup_t * l)
474{
475 if (l->n_packets == 0 || l->sw_if_index == ~0)
476 return;
477
478 if (l->adv > 0)
479 l->n_bytes += l->n_packets * l->len;
480
481 vlib_increment_combined_counter
482 (vnm->interface_main.combined_sw_if_counters +
483 VNET_INTERFACE_COUNTER_RX, vm->thread_index, l->sw_if_index,
484 l->n_packets, l->n_bytes);
485}
486
487static_always_inline void
488eth_input_tag_lookup (vlib_main_t * vm, vnet_main_t * vnm,
489 vlib_node_runtime_t * node, vnet_hw_interface_t * hi,
490 u64 tag, u16 * next, vlib_buffer_t * b,
491 eth_input_tag_lookup_t * l, u8 dmac_bad, int is_dot1ad,
492 int main_is_l3, int check_dmac)
493{
494 ethernet_main_t *em = &ethernet_main;
495
496 if ((tag ^ l->tag) & l->mask)
Damjan Marion650223c2018-11-14 16:55:53 +0100497 {
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100498 main_intf_t *mif = vec_elt_at_index (em->main_intfs, hi->hw_if_index);
499 vlan_intf_t *vif;
500 qinq_intf_t *qif;
501 vlan_table_t *vlan_table;
502 qinq_table_t *qinq_table;
503 u16 *t = (u16 *) & tag;
504 u16 vlan1 = clib_net_to_host_u16 (t[0]) & 0xFFF;
505 u16 vlan2 = clib_net_to_host_u16 (t[2]) & 0xFFF;
506 u32 matched, is_l2, new_sw_if_index;
507
508 vlan_table = vec_elt_at_index (em->vlan_pool, is_dot1ad ?
509 mif->dot1ad_vlans : mif->dot1q_vlans);
510 vif = &vlan_table->vlans[vlan1];
511 qinq_table = vec_elt_at_index (em->qinq_pool, vif->qinqs);
512 qif = &qinq_table->vlans[vlan2];
513 l->err = ETHERNET_ERROR_NONE;
514 l->type = clib_net_to_host_u16 (t[1]);
515
516 if (l->type == ETHERNET_TYPE_VLAN)
517 {
518 l->type = clib_net_to_host_u16 (t[3]);
519 l->n_tags = 2;
520 matched = eth_identify_subint (hi, SUBINT_CONFIG_VALID |
521 SUBINT_CONFIG_MATCH_2_TAG, mif, vif,
522 qif, &new_sw_if_index, &l->err,
523 &is_l2);
524 }
525 else
526 {
527 l->n_tags = 1;
528 if (vlan1 == 0)
529 {
530 new_sw_if_index = hi->sw_if_index;
531 l->err = ETHERNET_ERROR_NONE;
532 matched = 1;
533 is_l2 = main_is_l3 == 0;
534 }
535 else
536 matched = eth_identify_subint (hi, SUBINT_CONFIG_VALID |
537 SUBINT_CONFIG_MATCH_1_TAG, mif,
538 vif, qif, &new_sw_if_index,
539 &l->err, &is_l2);
540 }
541
542 if (l->sw_if_index != new_sw_if_index)
543 {
544 eth_input_update_if_counters (vm, vnm, l);
545 l->n_packets = 0;
546 l->n_bytes = 0;
547 l->sw_if_index = new_sw_if_index;
548 }
549 l->tag = tag;
550 l->mask = (l->n_tags == 2) ?
551 clib_net_to_host_u64 (0xffffffffffffffff) :
552 clib_net_to_host_u64 (0xffffffff00000000);
553
554 if (matched && l->sw_if_index == ~0)
555 l->err = ETHERNET_ERROR_DOWN;
556
557 l->len = sizeof (ethernet_header_t) +
558 l->n_tags * sizeof (ethernet_vlan_header_t);
559 if (main_is_l3)
560 l->adv = is_l2 ? -(int) sizeof (ethernet_header_t) :
561 l->n_tags * sizeof (ethernet_vlan_header_t);
562 else
563 l->adv = is_l2 ? 0 : l->len;
564
565 if (PREDICT_FALSE (l->err != ETHERNET_ERROR_NONE))
566 l->next = ETHERNET_INPUT_NEXT_DROP;
567 else if (is_l2)
568 l->next = em->l2_next;
569 else if (l->type == ETHERNET_TYPE_IP4)
570 l->next = em->l3_next.input_next_ip4;
571 else if (l->type == ETHERNET_TYPE_IP6)
572 l->next = em->l3_next.input_next_ip6;
573 else if (l->type == ETHERNET_TYPE_MPLS)
574 l->next = em->l3_next.input_next_mpls;
575 else if (em->redirect_l3)
576 l->next = em->redirect_l3_next;
577 else
578 {
579 l->next = eth_input_next_by_type (l->type);
580 if (l->next == ETHERNET_INPUT_NEXT_PUNT)
581 l->err = ETHERNET_ERROR_UNKNOWN_TYPE;
582 }
583 }
584
585 if (check_dmac && l->adv > 0 && dmac_bad)
586 {
587 l->err = ETHERNET_ERROR_L3_MAC_MISMATCH;
588 next[0] = ETHERNET_INPUT_NEXT_PUNT;
589 }
590 else
591 next[0] = l->next;
592
593 vlib_buffer_advance (b, l->adv);
594 vnet_buffer (b)->l2.l2_len = l->len;
595 vnet_buffer (b)->l3_hdr_offset = vnet_buffer (b)->l2_hdr_offset + l->len;
596
597 if (l->err == ETHERNET_ERROR_NONE)
598 {
599 vnet_buffer (b)->sw_if_index[VLIB_RX] = l->sw_if_index;
600 ethernet_buffer_set_vlan_count (b, l->n_tags);
601 }
602 else
603 b->error = node->errors[l->err];
604
605 /* update counters */
606 l->n_packets += 1;
607 l->n_bytes += vlib_buffer_length_in_chain (vm, b);
608}
609
610/* process frame of buffers, store ethertype into array and update
611 buffer metadata fields depending on interface being l2 or l3 assuming that
612 packets are untagged. For tagged packets those fields are updated later.
613 Optionally store Destionation MAC address and tag data into arrays
614 for further processing */
615
616STATIC_ASSERT (VLIB_FRAME_SIZE % 8 == 0,
617 "VLIB_FRAME_SIZE must be power of 8");
618static_always_inline void
619eth_input_process_frame (vlib_main_t * vm, vlib_node_runtime_t * node,
620 vnet_hw_interface_t * hi,
621 u32 * buffer_indices, u32 n_packets, int main_is_l3,
622 int ip4_cksum_ok, int dmac_check)
623{
624 ethernet_main_t *em = &ethernet_main;
625 u16 nexts[VLIB_FRAME_SIZE], *next;
626 u16 etypes[VLIB_FRAME_SIZE], *etype = etypes;
627 u64 dmacs[VLIB_FRAME_SIZE], *dmac = dmacs;
628 u8 dmacs_bad[VLIB_FRAME_SIZE];
629 u64 tags[VLIB_FRAME_SIZE], *tag = tags;
630 u16 slowpath_indices[VLIB_FRAME_SIZE];
631 u16 n_slowpath, i;
632 u16 next_ip4, next_ip6, next_mpls, next_l2;
633 u16 et_ip4 = clib_host_to_net_u16 (ETHERNET_TYPE_IP4);
634 u16 et_ip6 = clib_host_to_net_u16 (ETHERNET_TYPE_IP6);
635 u16 et_mpls = clib_host_to_net_u16 (ETHERNET_TYPE_MPLS);
636 u16 et_vlan = clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
637 u16 et_dot1ad = clib_host_to_net_u16 (ETHERNET_TYPE_DOT1AD);
638 i32 n_left = n_packets;
639 vlib_buffer_t *b[20];
640 u32 *from;
641
642 from = buffer_indices;
643
644 while (n_left >= 20)
645 {
646 vlib_buffer_t **ph = b + 16, **pd = b + 8;
Damjan Marion650223c2018-11-14 16:55:53 +0100647 vlib_get_buffers (vm, from, b, 4);
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100648 vlib_get_buffers (vm, from + 8, pd, 4);
649 vlib_get_buffers (vm, from + 16, ph, 4);
Damjan Marion650223c2018-11-14 16:55:53 +0100650
651 vlib_prefetch_buffer_header (ph[0], LOAD);
652 vlib_prefetch_buffer_data (pd[0], LOAD);
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100653 eth_input_get_etype_and_tags (b, etype, tag, dmac, 0, dmac_check);
Damjan Marion650223c2018-11-14 16:55:53 +0100654
655 vlib_prefetch_buffer_header (ph[1], LOAD);
656 vlib_prefetch_buffer_data (pd[1], LOAD);
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100657 eth_input_get_etype_and_tags (b, etype, tag, dmac, 1, dmac_check);
Damjan Marion650223c2018-11-14 16:55:53 +0100658
659 vlib_prefetch_buffer_header (ph[2], LOAD);
660 vlib_prefetch_buffer_data (pd[2], LOAD);
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100661 eth_input_get_etype_and_tags (b, etype, tag, dmac, 2, dmac_check);
Damjan Marion650223c2018-11-14 16:55:53 +0100662
663 vlib_prefetch_buffer_header (ph[3], LOAD);
664 vlib_prefetch_buffer_data (pd[3], LOAD);
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100665 eth_input_get_etype_and_tags (b, etype, tag, dmac, 3, dmac_check);
Damjan Marion650223c2018-11-14 16:55:53 +0100666
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100667 eth_input_adv_and_flags_x4 (b, main_is_l3);
Damjan Marion650223c2018-11-14 16:55:53 +0100668
669 /* next */
670 n_left -= 4;
671 etype += 4;
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100672 tag += 4;
673 dmac += 4;
Damjan Marion650223c2018-11-14 16:55:53 +0100674 from += 4;
675 }
676 while (n_left >= 4)
677 {
678 vlib_get_buffers (vm, from, b, 4);
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100679 eth_input_get_etype_and_tags (b, etype, tag, dmac, 0, dmac_check);
680 eth_input_get_etype_and_tags (b, etype, tag, dmac, 1, dmac_check);
681 eth_input_get_etype_and_tags (b, etype, tag, dmac, 2, dmac_check);
682 eth_input_get_etype_and_tags (b, etype, tag, dmac, 3, dmac_check);
683 eth_input_adv_and_flags_x4 (b, main_is_l3);
Damjan Marion650223c2018-11-14 16:55:53 +0100684
685 /* next */
686 n_left -= 4;
687 etype += 4;
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100688 tag += 4;
689 dmac += 4;
Damjan Marion650223c2018-11-14 16:55:53 +0100690 from += 4;
691 }
692 while (n_left)
693 {
694 vlib_get_buffers (vm, from, b, 1);
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100695 eth_input_get_etype_and_tags (b, etype, tag, dmac, 0, dmac_check);
696 eth_input_adv_and_flags_x1 (b, main_is_l3);
Damjan Marion650223c2018-11-14 16:55:53 +0100697
698 /* next */
699 n_left -= 1;
700 etype += 1;
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100701 tag += 1;
702 dmac += 4;
Damjan Marion650223c2018-11-14 16:55:53 +0100703 from += 1;
704 }
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100705
706 if (dmac_check)
707 {
708 u64 mask = clib_net_to_host_u64 (0xFFFFFFFFFFFF0000);
709 u64 igbit = clib_net_to_host_u64 (0x0100000000000000);
710 u64 hwaddr = (*(u64 *) hi->hw_address) & mask;
711 u64 *dmac = dmacs;
712 u8 *dmac_bad = dmacs_bad;
713
714 n_left = n_packets;
715
716#ifdef CLIB_HAVE_VEC256
717 u64x4 igbit4 = u64x4_splat (igbit);
718 u64x4 mask4 = u64x4_splat (mask);
719 u64x4 hwaddr4 = u64x4_splat (hwaddr);
720 while (n_left >= 0)
721 {
722 u64x4 r0, r1;
723 r0 = u64x4_load_unaligned (dmac + 0) & mask4;
724 r1 = u64x4_load_unaligned (dmac + 4) & mask4;
725
726 r0 = (r0 != hwaddr4) & ((r0 & igbit4) == 0);
727 r1 = (r1 != hwaddr4) & ((r1 & igbit4) == 0);
728
729 *(u32 *) (dmac_bad + 0) = u8x32_msb_mask ((u8x32) (r0));
730 *(u32 *) (dmac_bad + 4) = u8x32_msb_mask ((u8x32) (r1));
731
732 /* next */
733 dmac += 8;
734 dmac_bad += 8;
735 n_left -= 8;
736 }
737#else
738 while (n_left > 0)
739 {
740 u64 r0, r1, r2, r3;
741 r0 = dmac[0] & mask;
742 r1 = dmac[1] & mask;
743 r2 = dmac[2] & mask;
744 r3 = dmac[3] & mask;
745
746 r0 = (r0 != hwaddr) && ((r0 & igbit) == 0);
747 r1 = (r1 != hwaddr) && ((r1 & igbit) == 0);
748 r2 = (r2 != hwaddr) && ((r2 & igbit) == 0);
749 r3 = (r3 != hwaddr) && ((r3 & igbit) == 0);
750
751 dmac_bad[0] = r0;
752 dmac_bad[1] = r1;
753 dmac_bad[2] = r2;
754 dmac_bad[3] = r3;
755
756 /* next */
757 dmac += 4;
758 dmac_bad += 4;
759 n_left -= 4;
760 }
761#endif
762 }
763
764 next_ip4 = em->l3_next.input_next_ip4;
765 next_ip6 = em->l3_next.input_next_ip6;
766 next_mpls = em->l3_next.input_next_mpls;
767 next_l2 = em->l2_next;
768
769 if (next_ip4 == ETHERNET_INPUT_NEXT_IP4_INPUT && ip4_cksum_ok)
770 next_ip4 = ETHERNET_INPUT_NEXT_IP4_INPUT_NCS;
771
772#ifdef CLIB_HAVE_VEC256
773 u16x16 et16_ip4 = u16x16_splat (et_ip4);
774 u16x16 et16_ip6 = u16x16_splat (et_ip6);
775 u16x16 et16_mpls = u16x16_splat (et_mpls);
776 u16x16 et16_vlan = u16x16_splat (et_vlan);
777 u16x16 et16_dot1ad = u16x16_splat (et_dot1ad);
778 u16x16 next16_ip4 = u16x16_splat (next_ip4);
779 u16x16 next16_ip6 = u16x16_splat (next_ip6);
780 u16x16 next16_mpls = u16x16_splat (next_mpls);
781 u16x16 next16_l2 = u16x16_splat (next_l2);
782 u16x16 zero = { 0 };
783 u16x16 stairs = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
784#endif
785
786 etype = etypes;
787 n_left = n_packets;
788 next = nexts;
789 n_slowpath = 0;
790 i = 0;
791
792 /* fastpath - in l3 mode hadles ip4, ip6 and mpls packets, other packets
793 are considered as slowpath, in l2 mode all untagged packets are
794 considered as fastpath */
795 while (n_left > 0)
796 {
797#ifdef CLIB_HAVE_VEC256
798 if (n_left >= 16)
799 {
800 u16x16 r = zero;
801 u16x16 e16 = u16x16_load_unaligned (etype);
802 if (main_is_l3)
803 {
804 r += (e16 == et16_ip4) & next16_ip4;
805 r += (e16 == et16_ip6) & next16_ip6;
806 r += (e16 == et16_mpls) & next16_mpls;
807 }
808 else
809 r = ((e16 != et16_vlan) & (e16 != et16_dot1ad)) & next16_l2;
810 u16x16_store_unaligned (r, next);
811
812 if (!u16x16_is_all_zero (r == zero))
813 {
814 if (u16x16_is_all_zero (r))
815 {
816 u16x16_store_unaligned (u16x16_splat (i) + stairs,
817 slowpath_indices + n_slowpath);
818 n_slowpath += 16;
819 }
820 else
821 {
822 for (int j = 0; j < 16; j++)
823 if (next[j] == 0)
824 slowpath_indices[n_slowpath++] = i + j;
825 }
826 }
827
828 etype += 16;
829 next += 16;
830 n_left -= 16;
831 i += 16;
832 continue;
833 }
834#endif
835 if (main_is_l3 && etype[0] == et_ip4)
836 next[0] = next_ip4;
837 else if (main_is_l3 && etype[0] == et_ip6)
838 next[0] = next_ip6;
839 else if (main_is_l3 && etype[0] == et_mpls)
840 next[0] = next_mpls;
841 else if (main_is_l3 == 0 &&
842 etype[0] != et_vlan && etype[0] != et_dot1ad)
843 next[0] = next_l2;
844 else
845 {
846 next[0] = 0;
847 slowpath_indices[n_slowpath++] = i;
848 }
849
850 etype += 1;
851 next += 1;
852 n_left -= 1;
853 i += 1;
854 }
855
856 if (n_slowpath)
857 {
858 vnet_main_t *vnm = vnet_get_main ();
859 n_left = n_slowpath;
860 u16 *si = slowpath_indices;
861 u32 last_unknown_etype = ~0;
862 u32 last_unknown_next = ~0;
863 eth_input_tag_lookup_t dot1ad_lookup, dot1q_lookup = {
864 .mask = -1LL,
865 .tag = tags[si[0]] ^ -1LL,
866 .sw_if_index = ~0
867 };
868
869 clib_memcpy_fast (&dot1ad_lookup, &dot1q_lookup, sizeof (dot1q_lookup));
870
871 while (n_left)
872 {
873 i = si[0];
874 u16 etype = etypes[i];
875
876 if (etype == et_vlan)
877 {
878 vlib_buffer_t *b = vlib_get_buffer (vm, buffer_indices[i]);
879 eth_input_tag_lookup (vm, vnm, node, hi, tags[i], nexts + i, b,
880 &dot1q_lookup, dmacs_bad[i], 0,
881 main_is_l3, dmac_check);
882
883 }
884 else if (etype == et_dot1ad)
885 {
886 vlib_buffer_t *b = vlib_get_buffer (vm, buffer_indices[i]);
887 eth_input_tag_lookup (vm, vnm, node, hi, tags[i], nexts + i, b,
888 &dot1ad_lookup, dmacs_bad[i], 1,
889 main_is_l3, dmac_check);
890 }
891 else
892 {
893 /* untagged packet with not well known etyertype */
894 if (last_unknown_etype != etype)
895 {
896 last_unknown_etype = etype;
897 etype = clib_host_to_net_u16 (etype);
898 last_unknown_next = eth_input_next_by_type (etype);
899 }
900 if (dmac_check && main_is_l3 && dmacs_bad[i])
901 {
902 vlib_buffer_t *b = vlib_get_buffer (vm, buffer_indices[i]);
903 b->error = node->errors[ETHERNET_ERROR_L3_MAC_MISMATCH];
904 nexts[i] = ETHERNET_INPUT_NEXT_PUNT;
905 }
906 else
907 nexts[i] = last_unknown_next;
908 }
909
910 /* next */
911 n_left--;
912 si++;
913 }
914
915 eth_input_update_if_counters (vm, vnm, &dot1q_lookup);
916 eth_input_update_if_counters (vm, vnm, &dot1ad_lookup);
917 }
918
919 vlib_buffer_enqueue_to_next (vm, node, buffer_indices, nexts, n_packets);
Damjan Marion650223c2018-11-14 16:55:53 +0100920}
921
922static_always_inline void
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100923eth_input_single_int (vlib_main_t * vm, vlib_node_runtime_t * node,
924 vnet_hw_interface_t * hi, u32 * from, u32 n_pkts,
925 int ip4_cksum_ok)
Damjan Marion650223c2018-11-14 16:55:53 +0100926{
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100927 ethernet_main_t *em = &ethernet_main;
928 ethernet_interface_t *ei;
929 ei = pool_elt_at_index (em->interfaces, hi->hw_instance);
930 main_intf_t *intf0 = vec_elt_at_index (em->main_intfs, hi->hw_if_index);
931 subint_config_t *subint0 = &intf0->untagged_subint;
Damjan Marion650223c2018-11-14 16:55:53 +0100932
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100933 int main_is_l3 = (subint0->flags & SUBINT_CONFIG_L2) == 0;
934 int promisc = (ei->flags & ETHERNET_INTERFACE_FLAG_ACCEPT_ALL) != 0;
Damjan Marion650223c2018-11-14 16:55:53 +0100935
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100936 if (main_is_l3)
Damjan Marion650223c2018-11-14 16:55:53 +0100937 {
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100938 /* main interface is L3, we dont expect tagged packets and interface
939 is not in promisc node, so we dont't need to check DMAC */
940 int is_l3 = 1;
Damjan Marion650223c2018-11-14 16:55:53 +0100941
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100942 if (promisc == 0)
943 eth_input_process_frame (vm, node, hi, from, n_pkts, is_l3,
944 ip4_cksum_ok, 0);
Damjan Marion650223c2018-11-14 16:55:53 +0100945 else
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100946 /* subinterfaces and promisc mode so DMAC check is needed */
947 eth_input_process_frame (vm, node, hi, from, n_pkts, is_l3,
948 ip4_cksum_ok, 1);
949 return;
Damjan Marion650223c2018-11-14 16:55:53 +0100950 }
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100951 else
Damjan Marion650223c2018-11-14 16:55:53 +0100952 {
Damjan Marion8d6f34e2018-11-25 21:19:13 +0100953 /* untagged packets are treated as L2 */
954 int is_l3 = 0;
955 eth_input_process_frame (vm, node, hi, from, n_pkts, is_l3,
956 ip4_cksum_ok, 1);
957 return;
Damjan Marion650223c2018-11-14 16:55:53 +0100958 }
959}
960
961static_always_inline void
962ethernet_input_trace (vlib_main_t * vm, vlib_node_runtime_t * node,
963 vlib_frame_t * from_frame)
964{
965 u32 *from, n_left;
Benoît Ganne98477922019-04-10 14:21:11 +0200966 if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
Damjan Marion650223c2018-11-14 16:55:53 +0100967 {
Dave Barach5ecd5a52019-02-25 15:27:28 -0500968 from = vlib_frame_vector_args (from_frame);
969 n_left = from_frame->n_vectors;
Damjan Marion650223c2018-11-14 16:55:53 +0100970
Dave Barach5ecd5a52019-02-25 15:27:28 -0500971 while (n_left)
Damjan Marion650223c2018-11-14 16:55:53 +0100972 {
Dave Barach5ecd5a52019-02-25 15:27:28 -0500973 ethernet_input_trace_t *t0;
974 vlib_buffer_t *b0 = vlib_get_buffer (vm, from[0]);
975
976 if (b0->flags & VLIB_BUFFER_IS_TRACED)
977 {
978 t0 = vlib_add_trace (vm, node, b0,
979 sizeof (ethernet_input_trace_t));
980 clib_memcpy_fast (t0->packet_data, b0->data + b0->current_data,
981 sizeof (t0->packet_data));
982 t0->frame_flags = from_frame->flags;
983 clib_memcpy_fast (&t0->frame_data,
984 vlib_frame_scalar_args (from_frame),
985 sizeof (ethernet_input_frame_t));
986 }
987 from += 1;
988 n_left -= 1;
Damjan Marion650223c2018-11-14 16:55:53 +0100989 }
Dave Barach5ecd5a52019-02-25 15:27:28 -0500990 }
991
992 /* rx pcap capture if enabled */
993 if (PREDICT_FALSE (vm->pcap[VLIB_RX].pcap_enable))
994 {
995 u32 bi0;
996
997 from = vlib_frame_vector_args (from_frame);
998 n_left = from_frame->n_vectors;
999 while (n_left > 0)
1000 {
1001 vlib_buffer_t *b0;
1002 bi0 = from[0];
1003 from++;
1004 b0 = vlib_get_buffer (vm, bi0);
1005
1006 if (vm->pcap[VLIB_RX].pcap_sw_if_index == 0 ||
1007 vm->pcap[VLIB_RX].pcap_sw_if_index
1008 == vnet_buffer (b0)->sw_if_index[VLIB_RX])
1009 {
1010 pcap_add_buffer (&vm->pcap[VLIB_RX].pcap_main, vm, bi0, 512);
1011 }
1012 n_left--;
1013 }
Damjan Marion650223c2018-11-14 16:55:53 +01001014 }
1015}
1016
1017static_always_inline void
Ed Warnickecb9cada2015-12-08 15:45:58 -07001018ethernet_input_inline (vlib_main_t * vm,
1019 vlib_node_runtime_t * node,
Damjan Marion650223c2018-11-14 16:55:53 +01001020 u32 * from, u32 n_packets,
Ed Warnickecb9cada2015-12-08 15:45:58 -07001021 ethernet_input_variant_t variant)
1022{
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001023 vnet_main_t *vnm = vnet_get_main ();
1024 ethernet_main_t *em = &ethernet_main;
1025 vlib_node_runtime_t *error_node;
Damjan Marion650223c2018-11-14 16:55:53 +01001026 u32 n_left_from, next_index, *to_next;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001027 u32 stats_sw_if_index, stats_n_packets, stats_n_bytes;
Damjan Marion067cd622018-07-11 12:47:43 +02001028 u32 thread_index = vm->thread_index;
Dave Barachcfba1e22016-11-16 10:23:50 -05001029 u32 cached_sw_if_index = ~0;
1030 u32 cached_is_l2 = 0; /* shut up gcc */
John Lo1904c472017-03-10 17:15:22 -05001031 vnet_hw_interface_t *hi = NULL; /* used for main interface only */
Zhiyong Yangb3ca33f2019-04-24 04:13:27 -04001032 vlib_buffer_t *bufs[VLIB_FRAME_SIZE];
1033 vlib_buffer_t **b = bufs;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001034
1035 if (variant != ETHERNET_INPUT_VARIANT_ETHERNET)
1036 error_node = vlib_node_get_runtime (vm, ethernet_input_node.index);
1037 else
1038 error_node = node;
1039
Damjan Marion650223c2018-11-14 16:55:53 +01001040 n_left_from = n_packets;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001041
1042 next_index = node->cached_next_index;
1043 stats_sw_if_index = node->runtime_data[0];
1044 stats_n_packets = stats_n_bytes = 0;
Zhiyong Yangb3ca33f2019-04-24 04:13:27 -04001045 vlib_get_buffers (vm, from, bufs, n_left_from);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001046
1047 while (n_left_from > 0)
1048 {
1049 u32 n_left_to_next;
1050
1051 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
1052
1053 while (n_left_from >= 4 && n_left_to_next >= 2)
1054 {
1055 u32 bi0, bi1;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001056 vlib_buffer_t *b0, *b1;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001057 u8 next0, next1, error0, error1;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001058 u16 type0, orig_type0, type1, orig_type1;
1059 u16 outer_id0, inner_id0, outer_id1, inner_id1;
1060 u32 match_flags0, match_flags1;
1061 u32 old_sw_if_index0, new_sw_if_index0, len0, old_sw_if_index1,
1062 new_sw_if_index1, len1;
1063 vnet_hw_interface_t *hi0, *hi1;
1064 main_intf_t *main_intf0, *main_intf1;
1065 vlan_intf_t *vlan_intf0, *vlan_intf1;
1066 qinq_intf_t *qinq_intf0, *qinq_intf1;
1067 u32 is_l20, is_l21;
Dave Barachcfba1e22016-11-16 10:23:50 -05001068 ethernet_header_t *e0, *e1;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001069
1070 /* Prefetch next iteration. */
1071 {
Zhiyong Yangb3ca33f2019-04-24 04:13:27 -04001072 vlib_prefetch_buffer_header (b[2], STORE);
1073 vlib_prefetch_buffer_header (b[3], STORE);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001074
Zhiyong Yangb3ca33f2019-04-24 04:13:27 -04001075 CLIB_PREFETCH (b[2]->data, sizeof (ethernet_header_t), LOAD);
1076 CLIB_PREFETCH (b[3]->data, sizeof (ethernet_header_t), LOAD);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001077 }
1078
1079 bi0 = from[0];
1080 bi1 = from[1];
1081 to_next[0] = bi0;
1082 to_next[1] = bi1;
1083 from += 2;
1084 to_next += 2;
1085 n_left_to_next -= 2;
1086 n_left_from -= 2;
1087
Zhiyong Yangb3ca33f2019-04-24 04:13:27 -04001088 b0 = b[0];
1089 b1 = b[1];
1090 b += 2;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001091
1092 error0 = error1 = ETHERNET_ERROR_NONE;
Dave Barachcfba1e22016-11-16 10:23:50 -05001093 e0 = vlib_buffer_get_current (b0);
1094 type0 = clib_net_to_host_u16 (e0->type);
1095 e1 = vlib_buffer_get_current (b1);
1096 type1 = clib_net_to_host_u16 (e1->type);
1097
Andrew Yourtchenko20e6d362018-10-05 20:36:03 +02001098 /* Set the L2 header offset for all packets */
1099 vnet_buffer (b0)->l2_hdr_offset = b0->current_data;
1100 vnet_buffer (b1)->l2_hdr_offset = b1->current_data;
1101 b0->flags |= VNET_BUFFER_F_L2_HDR_OFFSET_VALID;
1102 b1->flags |= VNET_BUFFER_F_L2_HDR_OFFSET_VALID;
1103
John Locc532852016-12-14 15:42:45 -05001104 /* Speed-path for the untagged case */
Dave Barachcfba1e22016-11-16 10:23:50 -05001105 if (PREDICT_TRUE (variant == ETHERNET_INPUT_VARIANT_ETHERNET
Damjan Marionc6969b52018-02-19 12:14:06 +01001106 && !ethernet_frame_is_any_tagged_x2 (type0,
1107 type1)))
Dave Barachcfba1e22016-11-16 10:23:50 -05001108 {
1109 main_intf_t *intf0;
1110 subint_config_t *subint0;
1111 u32 sw_if_index0, sw_if_index1;
1112
1113 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
1114 sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
1115 is_l20 = cached_is_l2;
1116
1117 /* This is probably wholly unnecessary */
1118 if (PREDICT_FALSE (sw_if_index0 != sw_if_index1))
1119 goto slowpath;
1120
John Lo1904c472017-03-10 17:15:22 -05001121 /* Now sw_if_index0 == sw_if_index1 */
Dave Barachcfba1e22016-11-16 10:23:50 -05001122 if (PREDICT_FALSE (cached_sw_if_index != sw_if_index0))
1123 {
1124 cached_sw_if_index = sw_if_index0;
John Lo1904c472017-03-10 17:15:22 -05001125 hi = vnet_get_sup_hw_interface (vnm, sw_if_index0);
1126 intf0 = vec_elt_at_index (em->main_intfs, hi->hw_if_index);
Dave Barachcfba1e22016-11-16 10:23:50 -05001127 subint0 = &intf0->untagged_subint;
1128 cached_is_l2 = is_l20 = subint0->flags & SUBINT_CONFIG_L2;
1129 }
John Lo7714b302016-12-20 16:59:02 -05001130
Dave Barachcfba1e22016-11-16 10:23:50 -05001131 if (PREDICT_TRUE (is_l20 != 0))
1132 {
Andrew Yourtchenko20e6d362018-10-05 20:36:03 +02001133 vnet_buffer (b0)->l3_hdr_offset =
1134 vnet_buffer (b0)->l2_hdr_offset +
1135 sizeof (ethernet_header_t);
1136 vnet_buffer (b1)->l3_hdr_offset =
1137 vnet_buffer (b1)->l2_hdr_offset +
1138 sizeof (ethernet_header_t);
1139 b0->flags |= VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
1140 b1->flags |= VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
Dave Barachcfba1e22016-11-16 10:23:50 -05001141 next0 = em->l2_next;
1142 vnet_buffer (b0)->l2.l2_len = sizeof (ethernet_header_t);
Dave Barachcfba1e22016-11-16 10:23:50 -05001143 next1 = em->l2_next;
1144 vnet_buffer (b1)->l2.l2_len = sizeof (ethernet_header_t);
Dave Barachcfba1e22016-11-16 10:23:50 -05001145 }
John Locc532852016-12-14 15:42:45 -05001146 else
1147 {
John Lo1904c472017-03-10 17:15:22 -05001148 if (!ethernet_address_cast (e0->dst_address) &&
Hongjun Ni9e3e3612017-04-26 18:40:55 +08001149 (hi->hw_address != 0) &&
Neale Ranns37029302018-08-10 05:30:06 -07001150 !ethernet_mac_address_equal ((u8 *) e0, hi->hw_address))
John Lo1904c472017-03-10 17:15:22 -05001151 error0 = ETHERNET_ERROR_L3_MAC_MISMATCH;
1152 if (!ethernet_address_cast (e1->dst_address) &&
Hongjun Ni9e3e3612017-04-26 18:40:55 +08001153 (hi->hw_address != 0) &&
Neale Ranns37029302018-08-10 05:30:06 -07001154 !ethernet_mac_address_equal ((u8 *) e1, hi->hw_address))
John Lo1904c472017-03-10 17:15:22 -05001155 error1 = ETHERNET_ERROR_L3_MAC_MISMATCH;
John Lob14826e2018-04-18 15:52:23 -04001156 vlib_buffer_advance (b0, sizeof (ethernet_header_t));
John Locc532852016-12-14 15:42:45 -05001157 determine_next_node (em, variant, 0, type0, b0,
1158 &error0, &next0);
John Lob14826e2018-04-18 15:52:23 -04001159 vlib_buffer_advance (b1, sizeof (ethernet_header_t));
John Locc532852016-12-14 15:42:45 -05001160 determine_next_node (em, variant, 0, type1, b1,
1161 &error1, &next1);
John Locc532852016-12-14 15:42:45 -05001162 }
1163 goto ship_it01;
Dave Barachcfba1e22016-11-16 10:23:50 -05001164 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001165
John Locc532852016-12-14 15:42:45 -05001166 /* Slow-path for the tagged case */
1167 slowpath:
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001168 parse_header (variant,
1169 b0,
1170 &type0,
1171 &orig_type0, &outer_id0, &inner_id0, &match_flags0);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001172
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001173 parse_header (variant,
1174 b1,
1175 &type1,
1176 &orig_type1, &outer_id1, &inner_id1, &match_flags1);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001177
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001178 old_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
1179 old_sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
Ed Warnickecb9cada2015-12-08 15:45:58 -07001180
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001181 eth_vlan_table_lookups (em,
1182 vnm,
1183 old_sw_if_index0,
1184 orig_type0,
1185 outer_id0,
1186 inner_id0,
1187 &hi0,
1188 &main_intf0, &vlan_intf0, &qinq_intf0);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001189
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001190 eth_vlan_table_lookups (em,
1191 vnm,
1192 old_sw_if_index1,
1193 orig_type1,
1194 outer_id1,
1195 inner_id1,
1196 &hi1,
1197 &main_intf1, &vlan_intf1, &qinq_intf1);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001198
1199 identify_subint (hi0,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001200 b0,
1201 match_flags0,
1202 main_intf0,
1203 vlan_intf0,
1204 qinq_intf0, &new_sw_if_index0, &error0, &is_l20);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001205
1206 identify_subint (hi1,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001207 b1,
1208 match_flags1,
1209 main_intf1,
1210 vlan_intf1,
1211 qinq_intf1, &new_sw_if_index1, &error1, &is_l21);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001212
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001213 // Save RX sw_if_index for later nodes
1214 vnet_buffer (b0)->sw_if_index[VLIB_RX] =
1215 error0 !=
1216 ETHERNET_ERROR_NONE ? old_sw_if_index0 : new_sw_if_index0;
1217 vnet_buffer (b1)->sw_if_index[VLIB_RX] =
1218 error1 !=
1219 ETHERNET_ERROR_NONE ? old_sw_if_index1 : new_sw_if_index1;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001220
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001221 // Check if there is a stat to take (valid and non-main sw_if_index for pkt 0 or pkt 1)
1222 if (((new_sw_if_index0 != ~0)
1223 && (new_sw_if_index0 != old_sw_if_index0))
1224 || ((new_sw_if_index1 != ~0)
1225 && (new_sw_if_index1 != old_sw_if_index1)))
1226 {
Ed Warnickecb9cada2015-12-08 15:45:58 -07001227
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001228 len0 = vlib_buffer_length_in_chain (vm, b0) + b0->current_data
Damjan Marion072401e2017-07-13 18:53:27 +02001229 - vnet_buffer (b0)->l2_hdr_offset;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001230 len1 = vlib_buffer_length_in_chain (vm, b1) + b1->current_data
Damjan Marion072401e2017-07-13 18:53:27 +02001231 - vnet_buffer (b1)->l2_hdr_offset;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001232
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001233 stats_n_packets += 2;
1234 stats_n_bytes += len0 + len1;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001235
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001236 if (PREDICT_FALSE
1237 (!(new_sw_if_index0 == stats_sw_if_index
1238 && new_sw_if_index1 == stats_sw_if_index)))
Ed Warnickecb9cada2015-12-08 15:45:58 -07001239 {
1240 stats_n_packets -= 2;
1241 stats_n_bytes -= len0 + len1;
1242
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001243 if (new_sw_if_index0 != old_sw_if_index0
1244 && new_sw_if_index0 != ~0)
1245 vlib_increment_combined_counter (vnm->
1246 interface_main.combined_sw_if_counters
1247 +
1248 VNET_INTERFACE_COUNTER_RX,
Damjan Marion586afd72017-04-05 19:18:20 +02001249 thread_index,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001250 new_sw_if_index0, 1,
1251 len0);
1252 if (new_sw_if_index1 != old_sw_if_index1
1253 && new_sw_if_index1 != ~0)
1254 vlib_increment_combined_counter (vnm->
1255 interface_main.combined_sw_if_counters
1256 +
1257 VNET_INTERFACE_COUNTER_RX,
Damjan Marion586afd72017-04-05 19:18:20 +02001258 thread_index,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001259 new_sw_if_index1, 1,
1260 len1);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001261
1262 if (new_sw_if_index0 == new_sw_if_index1)
1263 {
1264 if (stats_n_packets > 0)
1265 {
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001266 vlib_increment_combined_counter
1267 (vnm->interface_main.combined_sw_if_counters
1268 + VNET_INTERFACE_COUNTER_RX,
Damjan Marion586afd72017-04-05 19:18:20 +02001269 thread_index,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001270 stats_sw_if_index,
1271 stats_n_packets, stats_n_bytes);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001272 stats_n_packets = stats_n_bytes = 0;
1273 }
1274 stats_sw_if_index = new_sw_if_index0;
1275 }
1276 }
1277 }
1278
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001279 if (variant == ETHERNET_INPUT_VARIANT_NOT_L2)
1280 is_l20 = is_l21 = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001281
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001282 determine_next_node (em, variant, is_l20, type0, b0, &error0,
1283 &next0);
1284 determine_next_node (em, variant, is_l21, type1, b1, &error1,
1285 &next1);
1286
John Lo1904c472017-03-10 17:15:22 -05001287 ship_it01:
Ed Warnickecb9cada2015-12-08 15:45:58 -07001288 b0->error = error_node->errors[error0];
1289 b1->error = error_node->errors[error1];
1290
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001291 // verify speculative enqueue
1292 vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
1293 n_left_to_next, bi0, bi1, next0,
1294 next1);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001295 }
1296
1297 while (n_left_from > 0 && n_left_to_next > 0)
1298 {
1299 u32 bi0;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001300 vlib_buffer_t *b0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001301 u8 error0, next0;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001302 u16 type0, orig_type0;
1303 u16 outer_id0, inner_id0;
1304 u32 match_flags0;
1305 u32 old_sw_if_index0, new_sw_if_index0, len0;
1306 vnet_hw_interface_t *hi0;
1307 main_intf_t *main_intf0;
1308 vlan_intf_t *vlan_intf0;
1309 qinq_intf_t *qinq_intf0;
Dave Barachcfba1e22016-11-16 10:23:50 -05001310 ethernet_header_t *e0;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001311 u32 is_l20;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001312
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001313 // Prefetch next iteration
1314 if (n_left_from > 1)
1315 {
Zhiyong Yangb3ca33f2019-04-24 04:13:27 -04001316 vlib_prefetch_buffer_header (b[1], STORE);
1317 CLIB_PREFETCH (b[1]->data, CLIB_CACHE_LINE_BYTES, LOAD);
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001318 }
1319
1320 bi0 = from[0];
Ed Warnickecb9cada2015-12-08 15:45:58 -07001321 to_next[0] = bi0;
1322 from += 1;
1323 to_next += 1;
1324 n_left_from -= 1;
1325 n_left_to_next -= 1;
1326
Zhiyong Yangb3ca33f2019-04-24 04:13:27 -04001327 b0 = b[0];
1328 b += 1;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001329
1330 error0 = ETHERNET_ERROR_NONE;
Dave Barachcfba1e22016-11-16 10:23:50 -05001331 e0 = vlib_buffer_get_current (b0);
1332 type0 = clib_net_to_host_u16 (e0->type);
1333
Andrew Yourtchenko20e6d362018-10-05 20:36:03 +02001334 /* Set the L2 header offset for all packets */
1335 vnet_buffer (b0)->l2_hdr_offset = b0->current_data;
1336 b0->flags |= VNET_BUFFER_F_L2_HDR_OFFSET_VALID;
1337
John Locc532852016-12-14 15:42:45 -05001338 /* Speed-path for the untagged case */
Dave Barachcfba1e22016-11-16 10:23:50 -05001339 if (PREDICT_TRUE (variant == ETHERNET_INPUT_VARIANT_ETHERNET
1340 && !ethernet_frame_is_tagged (type0)))
1341 {
1342 main_intf_t *intf0;
1343 subint_config_t *subint0;
1344 u32 sw_if_index0;
1345
1346 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
1347 is_l20 = cached_is_l2;
1348
1349 if (PREDICT_FALSE (cached_sw_if_index != sw_if_index0))
1350 {
1351 cached_sw_if_index = sw_if_index0;
John Lo1904c472017-03-10 17:15:22 -05001352 hi = vnet_get_sup_hw_interface (vnm, sw_if_index0);
1353 intf0 = vec_elt_at_index (em->main_intfs, hi->hw_if_index);
Dave Barachcfba1e22016-11-16 10:23:50 -05001354 subint0 = &intf0->untagged_subint;
1355 cached_is_l2 = is_l20 = subint0->flags & SUBINT_CONFIG_L2;
1356 }
John Lo7714b302016-12-20 16:59:02 -05001357
John Lo7714b302016-12-20 16:59:02 -05001358
Dave Barachcfba1e22016-11-16 10:23:50 -05001359 if (PREDICT_TRUE (is_l20 != 0))
1360 {
Andrew Yourtchenko20e6d362018-10-05 20:36:03 +02001361 vnet_buffer (b0)->l3_hdr_offset =
1362 vnet_buffer (b0)->l2_hdr_offset +
1363 sizeof (ethernet_header_t);
1364 b0->flags |= VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
Dave Barachcfba1e22016-11-16 10:23:50 -05001365 next0 = em->l2_next;
1366 vnet_buffer (b0)->l2.l2_len = sizeof (ethernet_header_t);
Dave Barachcfba1e22016-11-16 10:23:50 -05001367 }
John Locc532852016-12-14 15:42:45 -05001368 else
1369 {
John Lo1904c472017-03-10 17:15:22 -05001370 if (!ethernet_address_cast (e0->dst_address) &&
Hongjun Ni9e3e3612017-04-26 18:40:55 +08001371 (hi->hw_address != 0) &&
Neale Ranns37029302018-08-10 05:30:06 -07001372 !ethernet_mac_address_equal ((u8 *) e0, hi->hw_address))
John Lo1904c472017-03-10 17:15:22 -05001373 error0 = ETHERNET_ERROR_L3_MAC_MISMATCH;
Andrew Yourtchenkoe78bca12018-10-10 16:15:55 +02001374 vlib_buffer_advance (b0, sizeof (ethernet_header_t));
John Locc532852016-12-14 15:42:45 -05001375 determine_next_node (em, variant, 0, type0, b0,
1376 &error0, &next0);
John Locc532852016-12-14 15:42:45 -05001377 }
1378 goto ship_it0;
Dave Barachcfba1e22016-11-16 10:23:50 -05001379 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001380
John Locc532852016-12-14 15:42:45 -05001381 /* Slow-path for the tagged case */
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001382 parse_header (variant,
1383 b0,
1384 &type0,
1385 &orig_type0, &outer_id0, &inner_id0, &match_flags0);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001386
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001387 old_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
Ed Warnickecb9cada2015-12-08 15:45:58 -07001388
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001389 eth_vlan_table_lookups (em,
1390 vnm,
1391 old_sw_if_index0,
1392 orig_type0,
1393 outer_id0,
1394 inner_id0,
1395 &hi0,
1396 &main_intf0, &vlan_intf0, &qinq_intf0);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001397
1398 identify_subint (hi0,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001399 b0,
1400 match_flags0,
1401 main_intf0,
1402 vlan_intf0,
1403 qinq_intf0, &new_sw_if_index0, &error0, &is_l20);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001404
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001405 // Save RX sw_if_index for later nodes
1406 vnet_buffer (b0)->sw_if_index[VLIB_RX] =
1407 error0 !=
1408 ETHERNET_ERROR_NONE ? old_sw_if_index0 : new_sw_if_index0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001409
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001410 // Increment subinterface stats
1411 // Note that interface-level counters have already been incremented
1412 // prior to calling this function. Thus only subinterface counters
1413 // are incremented here.
1414 //
Damjan Marion607de1a2016-08-16 22:53:54 +02001415 // Interface level counters include packets received on the main
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001416 // interface and all subinterfaces. Subinterface level counters
1417 // include only those packets received on that subinterface
Ed Warnickecb9cada2015-12-08 15:45:58 -07001418 // Increment stats if the subint is valid and it is not the main intf
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001419 if ((new_sw_if_index0 != ~0)
1420 && (new_sw_if_index0 != old_sw_if_index0))
1421 {
Ed Warnickecb9cada2015-12-08 15:45:58 -07001422
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001423 len0 = vlib_buffer_length_in_chain (vm, b0) + b0->current_data
Damjan Marion072401e2017-07-13 18:53:27 +02001424 - vnet_buffer (b0)->l2_hdr_offset;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001425
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001426 stats_n_packets += 1;
1427 stats_n_bytes += len0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001428
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001429 // Batch stat increments from the same subinterface so counters
Damjan Marion607de1a2016-08-16 22:53:54 +02001430 // don't need to be incremented for every packet.
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001431 if (PREDICT_FALSE (new_sw_if_index0 != stats_sw_if_index))
1432 {
1433 stats_n_packets -= 1;
1434 stats_n_bytes -= len0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001435
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001436 if (new_sw_if_index0 != ~0)
1437 vlib_increment_combined_counter
1438 (vnm->interface_main.combined_sw_if_counters
1439 + VNET_INTERFACE_COUNTER_RX,
Damjan Marion586afd72017-04-05 19:18:20 +02001440 thread_index, new_sw_if_index0, 1, len0);
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001441 if (stats_n_packets > 0)
1442 {
1443 vlib_increment_combined_counter
1444 (vnm->interface_main.combined_sw_if_counters
1445 + VNET_INTERFACE_COUNTER_RX,
Damjan Marion586afd72017-04-05 19:18:20 +02001446 thread_index,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001447 stats_sw_if_index, stats_n_packets, stats_n_bytes);
1448 stats_n_packets = stats_n_bytes = 0;
1449 }
1450 stats_sw_if_index = new_sw_if_index0;
1451 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001452 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001453
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001454 if (variant == ETHERNET_INPUT_VARIANT_NOT_L2)
1455 is_l20 = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001456
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001457 determine_next_node (em, variant, is_l20, type0, b0, &error0,
1458 &next0);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001459
John Lo1904c472017-03-10 17:15:22 -05001460 ship_it0:
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001461 b0->error = error_node->errors[error0];
1462
1463 // verify speculative enqueue
Ed Warnickecb9cada2015-12-08 15:45:58 -07001464 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1465 to_next, n_left_to_next,
1466 bi0, next0);
1467 }
1468
1469 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1470 }
1471
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001472 // Increment any remaining batched stats
1473 if (stats_n_packets > 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001474 {
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001475 vlib_increment_combined_counter
1476 (vnm->interface_main.combined_sw_if_counters
1477 + VNET_INTERFACE_COUNTER_RX,
Damjan Marion586afd72017-04-05 19:18:20 +02001478 thread_index, stats_sw_if_index, stats_n_packets, stats_n_bytes);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001479 node->runtime_data[0] = stats_sw_if_index;
1480 }
Damjan Marion650223c2018-11-14 16:55:53 +01001481}
Ed Warnickecb9cada2015-12-08 15:45:58 -07001482
Damjan Marion5beecec2018-09-10 13:09:21 +02001483VLIB_NODE_FN (ethernet_input_node) (vlib_main_t * vm,
1484 vlib_node_runtime_t * node,
Damjan Marion650223c2018-11-14 16:55:53 +01001485 vlib_frame_t * frame)
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001486{
Damjan Marion650223c2018-11-14 16:55:53 +01001487 vnet_main_t *vnm = vnet_get_main ();
Damjan Marion650223c2018-11-14 16:55:53 +01001488 u32 *from = vlib_frame_vector_args (frame);
1489 u32 n_packets = frame->n_vectors;
1490
1491 ethernet_input_trace (vm, node, frame);
1492
1493 if (frame->flags & ETH_INPUT_FRAME_F_SINGLE_SW_IF_IDX)
1494 {
Damjan Marion650223c2018-11-14 16:55:53 +01001495 ethernet_input_frame_t *ef = vlib_frame_scalar_args (frame);
Damjan Marion650223c2018-11-14 16:55:53 +01001496 int ip4_cksum_ok = (frame->flags & ETH_INPUT_FRAME_F_IP4_CKSUM_OK) != 0;
Damjan Marion8d6f34e2018-11-25 21:19:13 +01001497 vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, ef->hw_if_index);
1498 eth_input_single_int (vm, node, hi, from, n_packets, ip4_cksum_ok);
Damjan Marion650223c2018-11-14 16:55:53 +01001499 }
Damjan Marion8d6f34e2018-11-25 21:19:13 +01001500 else
1501 ethernet_input_inline (vm, node, from, n_packets,
1502 ETHERNET_INPUT_VARIANT_ETHERNET);
Damjan Marion650223c2018-11-14 16:55:53 +01001503 return n_packets;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001504}
Ed Warnickecb9cada2015-12-08 15:45:58 -07001505
Damjan Marion5beecec2018-09-10 13:09:21 +02001506VLIB_NODE_FN (ethernet_input_type_node) (vlib_main_t * vm,
1507 vlib_node_runtime_t * node,
1508 vlib_frame_t * from_frame)
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001509{
Damjan Marion650223c2018-11-14 16:55:53 +01001510 u32 *from = vlib_frame_vector_args (from_frame);
1511 u32 n_packets = from_frame->n_vectors;
1512 ethernet_input_trace (vm, node, from_frame);
1513 ethernet_input_inline (vm, node, from, n_packets,
1514 ETHERNET_INPUT_VARIANT_ETHERNET_TYPE);
1515 return n_packets;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001516}
Ed Warnickecb9cada2015-12-08 15:45:58 -07001517
Damjan Marion5beecec2018-09-10 13:09:21 +02001518VLIB_NODE_FN (ethernet_input_not_l2_node) (vlib_main_t * vm,
1519 vlib_node_runtime_t * node,
1520 vlib_frame_t * from_frame)
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001521{
Damjan Marion650223c2018-11-14 16:55:53 +01001522 u32 *from = vlib_frame_vector_args (from_frame);
1523 u32 n_packets = from_frame->n_vectors;
1524 ethernet_input_trace (vm, node, from_frame);
1525 ethernet_input_inline (vm, node, from, n_packets,
1526 ETHERNET_INPUT_VARIANT_NOT_L2);
1527 return n_packets;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001528}
Ed Warnickecb9cada2015-12-08 15:45:58 -07001529
1530
1531// Return the subinterface config struct for the given sw_if_index
1532// Also return via parameter the appropriate match flags for the
1533// configured number of tags.
1534// On error (unsupported or not ethernet) return 0.
1535static subint_config_t *
1536ethernet_sw_interface_get_config (vnet_main_t * vnm,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001537 u32 sw_if_index,
1538 u32 * flags, u32 * unsupported)
1539{
1540 ethernet_main_t *em = &ethernet_main;
1541 vnet_hw_interface_t *hi;
1542 vnet_sw_interface_t *si;
1543 main_intf_t *main_intf;
1544 vlan_table_t *vlan_table;
1545 qinq_table_t *qinq_table;
1546 subint_config_t *subint = 0;
1547
Ed Warnickecb9cada2015-12-08 15:45:58 -07001548 hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1549
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001550 if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index))
1551 {
1552 *unsupported = 0;
1553 goto done; // non-ethernet interface
1554 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001555
1556 // ensure there's an entry for the main intf (shouldn't really be necessary)
1557 vec_validate (em->main_intfs, hi->hw_if_index);
1558 main_intf = vec_elt_at_index (em->main_intfs, hi->hw_if_index);
1559
1560 // Locate the subint for the given ethernet config
1561 si = vnet_get_sw_interface (vnm, sw_if_index);
1562
Pavel Kotucek15ac81c2017-06-20 14:00:26 +02001563 if (si->type == VNET_SW_INTERFACE_TYPE_P2P)
1564 {
1565 p2p_ethernet_main_t *p2pm = &p2p_main;
1566 u32 p2pe_sw_if_index =
1567 p2p_ethernet_lookup (hi->hw_if_index, si->p2p.client_mac);
1568 if (p2pe_sw_if_index == ~0)
1569 {
1570 pool_get (p2pm->p2p_subif_pool, subint);
1571 si->p2p.pool_index = subint - p2pm->p2p_subif_pool;
1572 }
1573 else
1574 subint = vec_elt_at_index (p2pm->p2p_subif_pool, si->p2p.pool_index);
1575 *flags = SUBINT_CONFIG_P2P;
1576 }
Neale Ranns17ff3c12018-07-04 10:24:24 -07001577 else if (si->type == VNET_SW_INTERFACE_TYPE_PIPE)
1578 {
1579 pipe_t *pipe;
1580
1581 pipe = pipe_get (sw_if_index);
1582 subint = &pipe->subint;
1583 *flags = SUBINT_CONFIG_P2P;
1584 }
Pavel Kotucek15ac81c2017-06-20 14:00:26 +02001585 else if (si->sub.eth.flags.default_sub)
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001586 {
1587 subint = &main_intf->default_subint;
Mike Bly88076742018-09-24 10:13:06 -07001588 *flags = SUBINT_CONFIG_MATCH_1_TAG |
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001589 SUBINT_CONFIG_MATCH_2_TAG | SUBINT_CONFIG_MATCH_3_TAG;
1590 }
1591 else if ((si->sub.eth.flags.no_tags) || (si->sub.eth.raw_flags == 0))
1592 {
1593 // if no flags are set then this is a main interface
1594 // so treat as untagged
1595 subint = &main_intf->untagged_subint;
1596 *flags = SUBINT_CONFIG_MATCH_0_TAG;
1597 }
1598 else
1599 {
1600 // one or two tags
1601 // first get the vlan table
1602 if (si->sub.eth.flags.dot1ad)
1603 {
1604 if (main_intf->dot1ad_vlans == 0)
1605 {
1606 // Allocate a vlan table from the pool
1607 pool_get (em->vlan_pool, vlan_table);
1608 main_intf->dot1ad_vlans = vlan_table - em->vlan_pool;
1609 }
1610 else
1611 {
1612 // Get ptr to existing vlan table
1613 vlan_table =
1614 vec_elt_at_index (em->vlan_pool, main_intf->dot1ad_vlans);
1615 }
1616 }
1617 else
1618 { // dot1q
1619 if (main_intf->dot1q_vlans == 0)
1620 {
1621 // Allocate a vlan table from the pool
1622 pool_get (em->vlan_pool, vlan_table);
1623 main_intf->dot1q_vlans = vlan_table - em->vlan_pool;
1624 }
1625 else
1626 {
1627 // Get ptr to existing vlan table
1628 vlan_table =
1629 vec_elt_at_index (em->vlan_pool, main_intf->dot1q_vlans);
1630 }
1631 }
1632
1633 if (si->sub.eth.flags.one_tag)
1634 {
1635 *flags = si->sub.eth.flags.exact_match ?
1636 SUBINT_CONFIG_MATCH_1_TAG :
1637 (SUBINT_CONFIG_MATCH_1_TAG |
1638 SUBINT_CONFIG_MATCH_2_TAG | SUBINT_CONFIG_MATCH_3_TAG);
1639
1640 if (si->sub.eth.flags.outer_vlan_id_any)
1641 {
1642 // not implemented yet
1643 *unsupported = 1;
1644 goto done;
1645 }
1646 else
1647 {
1648 // a single vlan, a common case
1649 subint =
1650 &vlan_table->vlans[si->sub.eth.
1651 outer_vlan_id].single_tag_subint;
1652 }
1653
1654 }
1655 else
1656 {
1657 // Two tags
1658 *flags = si->sub.eth.flags.exact_match ?
1659 SUBINT_CONFIG_MATCH_2_TAG :
1660 (SUBINT_CONFIG_MATCH_2_TAG | SUBINT_CONFIG_MATCH_3_TAG);
1661
1662 if (si->sub.eth.flags.outer_vlan_id_any
1663 && si->sub.eth.flags.inner_vlan_id_any)
1664 {
1665 // not implemented yet
1666 *unsupported = 1;
1667 goto done;
1668 }
1669
1670 if (si->sub.eth.flags.inner_vlan_id_any)
1671 {
1672 // a specific outer and "any" inner
1673 // don't need a qinq table for this
1674 subint =
1675 &vlan_table->vlans[si->sub.eth.
1676 outer_vlan_id].inner_any_subint;
1677 if (si->sub.eth.flags.exact_match)
1678 {
1679 *flags = SUBINT_CONFIG_MATCH_2_TAG;
1680 }
1681 else
1682 {
1683 *flags = SUBINT_CONFIG_MATCH_2_TAG |
1684 SUBINT_CONFIG_MATCH_3_TAG;
1685 }
1686 }
1687 else
1688 {
1689 // a specific outer + specifc innner vlan id, a common case
1690
1691 // get the qinq table
1692 if (vlan_table->vlans[si->sub.eth.outer_vlan_id].qinqs == 0)
1693 {
1694 // Allocate a qinq table from the pool
1695 pool_get (em->qinq_pool, qinq_table);
1696 vlan_table->vlans[si->sub.eth.outer_vlan_id].qinqs =
1697 qinq_table - em->qinq_pool;
1698 }
1699 else
1700 {
1701 // Get ptr to existing qinq table
1702 qinq_table =
1703 vec_elt_at_index (em->qinq_pool,
1704 vlan_table->vlans[si->sub.
1705 eth.outer_vlan_id].
1706 qinqs);
1707 }
1708 subint = &qinq_table->vlans[si->sub.eth.inner_vlan_id].subint;
1709 }
1710 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001711 }
1712
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001713done:
Ed Warnickecb9cada2015-12-08 15:45:58 -07001714 return subint;
1715}
1716
Damjan Marion5beecec2018-09-10 13:09:21 +02001717static clib_error_t *
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001718ethernet_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001719{
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001720 subint_config_t *subint;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001721 u32 dummy_flags;
1722 u32 dummy_unsup;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001723 clib_error_t *error = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001724
1725 // Find the config for this subinterface
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001726 subint =
1727 ethernet_sw_interface_get_config (vnm, sw_if_index, &dummy_flags,
1728 &dummy_unsup);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001729
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001730 if (subint == 0)
1731 {
1732 // not implemented yet or not ethernet
1733 goto done;
1734 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001735
1736 subint->sw_if_index =
1737 ((flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ? sw_if_index : ~0);
1738
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001739done:
Ed Warnickecb9cada2015-12-08 15:45:58 -07001740 return error;
1741}
1742
1743VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ethernet_sw_interface_up_down);
1744
1745
Damjan Marion5beecec2018-09-10 13:09:21 +02001746#ifndef CLIB_MARCH_VARIANT
Ed Warnickecb9cada2015-12-08 15:45:58 -07001747// Set the L2/L3 mode for the subinterface
1748void
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001749ethernet_sw_interface_set_l2_mode (vnet_main_t * vnm, u32 sw_if_index, u32 l2)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001750{
1751 subint_config_t *subint;
1752 u32 dummy_flags;
1753 u32 dummy_unsup;
1754 int is_port;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001755 vnet_sw_interface_t *sw = vnet_get_sw_interface (vnm, sw_if_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001756
1757 is_port = !(sw->type == VNET_SW_INTERFACE_TYPE_SUB);
1758
1759 // Find the config for this subinterface
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001760 subint =
1761 ethernet_sw_interface_get_config (vnm, sw_if_index, &dummy_flags,
1762 &dummy_unsup);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001763
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001764 if (subint == 0)
1765 {
1766 // unimplemented or not ethernet
1767 goto done;
1768 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001769
1770 // Double check that the config we found is for our interface (or the interface is down)
1771 ASSERT ((subint->sw_if_index == sw_if_index) | (subint->sw_if_index == ~0));
1772
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001773 if (l2)
1774 {
1775 subint->flags |= SUBINT_CONFIG_L2;
1776 if (is_port)
1777 subint->flags |=
1778 SUBINT_CONFIG_MATCH_0_TAG | SUBINT_CONFIG_MATCH_1_TAG
1779 | SUBINT_CONFIG_MATCH_2_TAG | SUBINT_CONFIG_MATCH_3_TAG;
1780 }
1781 else
1782 {
1783 subint->flags &= ~SUBINT_CONFIG_L2;
1784 if (is_port)
1785 subint->flags &=
1786 ~(SUBINT_CONFIG_MATCH_1_TAG | SUBINT_CONFIG_MATCH_2_TAG
1787 | SUBINT_CONFIG_MATCH_3_TAG);
1788 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001789
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001790done:
Ed Warnickecb9cada2015-12-08 15:45:58 -07001791 return;
1792}
1793
Christian Dechamplain (cdechamp)07aecbb2016-04-05 10:40:38 -04001794/*
1795 * Set the L2/L3 mode for the subinterface regardless of port
1796 */
1797void
1798ethernet_sw_interface_set_l2_mode_noport (vnet_main_t * vnm,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001799 u32 sw_if_index, u32 l2)
Christian Dechamplain (cdechamp)07aecbb2016-04-05 10:40:38 -04001800{
1801 subint_config_t *subint;
1802 u32 dummy_flags;
1803 u32 dummy_unsup;
1804
1805 /* Find the config for this subinterface */
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001806 subint =
1807 ethernet_sw_interface_get_config (vnm, sw_if_index, &dummy_flags,
1808 &dummy_unsup);
Christian Dechamplain (cdechamp)07aecbb2016-04-05 10:40:38 -04001809
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001810 if (subint == 0)
1811 {
1812 /* unimplemented or not ethernet */
1813 goto done;
1814 }
Christian Dechamplain (cdechamp)07aecbb2016-04-05 10:40:38 -04001815
1816 /*
1817 * Double check that the config we found is for our interface (or the
1818 * interface is down)
1819 */
1820 ASSERT ((subint->sw_if_index == sw_if_index) | (subint->sw_if_index == ~0));
1821
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001822 if (l2)
1823 {
1824 subint->flags |= SUBINT_CONFIG_L2;
1825 }
1826 else
1827 {
1828 subint->flags &= ~SUBINT_CONFIG_L2;
1829 }
Christian Dechamplain (cdechamp)07aecbb2016-04-05 10:40:38 -04001830
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001831done:
Christian Dechamplain (cdechamp)07aecbb2016-04-05 10:40:38 -04001832 return;
1833}
Damjan Marion5beecec2018-09-10 13:09:21 +02001834#endif
Ed Warnickecb9cada2015-12-08 15:45:58 -07001835
1836static clib_error_t *
1837ethernet_sw_interface_add_del (vnet_main_t * vnm,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001838 u32 sw_if_index, u32 is_create)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001839{
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001840 clib_error_t *error = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001841 subint_config_t *subint;
1842 u32 match_flags;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001843 u32 unsupported = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001844
1845 // Find the config for this subinterface
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001846 subint =
1847 ethernet_sw_interface_get_config (vnm, sw_if_index, &match_flags,
1848 &unsupported);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001849
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001850 if (subint == 0)
1851 {
1852 // not implemented yet or not ethernet
1853 if (unsupported)
1854 {
Damjan Marion607de1a2016-08-16 22:53:54 +02001855 // this is the NYI case
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001856 error = clib_error_return (0, "not implemented yet");
1857 }
1858 goto done;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001859 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001860
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001861 if (!is_create)
1862 {
1863 subint->flags = 0;
1864 return error;
1865 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001866
1867 // Initialize the subint
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001868 if (subint->flags & SUBINT_CONFIG_VALID)
1869 {
1870 // Error vlan already in use
1871 error = clib_error_return (0, "vlan is already in use");
1872 }
1873 else
1874 {
Neale Ranns17ff3c12018-07-04 10:24:24 -07001875 // Note that config is L3 by default
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001876 subint->flags = SUBINT_CONFIG_VALID | match_flags;
1877 subint->sw_if_index = ~0; // because interfaces are initially down
1878 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001879
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001880done:
Ed Warnickecb9cada2015-12-08 15:45:58 -07001881 return error;
1882}
1883
1884VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ethernet_sw_interface_add_del);
1885
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001886static char *ethernet_error_strings[] = {
Ed Warnickecb9cada2015-12-08 15:45:58 -07001887#define ethernet_error(n,c,s) s,
1888#include "error.def"
1889#undef ethernet_error
1890};
1891
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001892/* *INDENT-OFF* */
Ed Warnickecb9cada2015-12-08 15:45:58 -07001893VLIB_REGISTER_NODE (ethernet_input_node) = {
Ed Warnickecb9cada2015-12-08 15:45:58 -07001894 .name = "ethernet-input",
1895 /* Takes a vector of packets. */
1896 .vector_size = sizeof (u32),
Damjan Marion650223c2018-11-14 16:55:53 +01001897 .scalar_size = sizeof (ethernet_input_frame_t),
Ed Warnickecb9cada2015-12-08 15:45:58 -07001898 .n_errors = ETHERNET_N_ERROR,
1899 .error_strings = ethernet_error_strings,
Ed Warnickecb9cada2015-12-08 15:45:58 -07001900 .n_next_nodes = ETHERNET_INPUT_N_NEXT,
1901 .next_nodes = {
1902#define _(s,n) [ETHERNET_INPUT_NEXT_##s] = n,
1903 foreach_ethernet_input_next
1904#undef _
1905 },
Ed Warnickecb9cada2015-12-08 15:45:58 -07001906 .format_buffer = format_ethernet_header_with_length,
1907 .format_trace = format_ethernet_input_trace,
1908 .unformat_buffer = unformat_ethernet_header,
1909};
1910
Damjan Marion5beecec2018-09-10 13:09:21 +02001911VLIB_REGISTER_NODE (ethernet_input_type_node) = {
Ed Warnickecb9cada2015-12-08 15:45:58 -07001912 .name = "ethernet-input-type",
1913 /* Takes a vector of packets. */
1914 .vector_size = sizeof (u32),
Ed Warnickecb9cada2015-12-08 15:45:58 -07001915 .n_next_nodes = ETHERNET_INPUT_N_NEXT,
1916 .next_nodes = {
1917#define _(s,n) [ETHERNET_INPUT_NEXT_##s] = n,
1918 foreach_ethernet_input_next
1919#undef _
1920 },
1921};
1922
Damjan Marion5beecec2018-09-10 13:09:21 +02001923VLIB_REGISTER_NODE (ethernet_input_not_l2_node) = {
Ed Warnickecb9cada2015-12-08 15:45:58 -07001924 .name = "ethernet-input-not-l2",
1925 /* Takes a vector of packets. */
1926 .vector_size = sizeof (u32),
Ed Warnickecb9cada2015-12-08 15:45:58 -07001927 .n_next_nodes = ETHERNET_INPUT_N_NEXT,
1928 .next_nodes = {
1929#define _(s,n) [ETHERNET_INPUT_NEXT_##s] = n,
1930 foreach_ethernet_input_next
1931#undef _
1932 },
1933};
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001934/* *INDENT-ON* */
Ed Warnickecb9cada2015-12-08 15:45:58 -07001935
Damjan Marion5beecec2018-09-10 13:09:21 +02001936#ifndef CLIB_MARCH_VARIANT
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001937void
1938ethernet_set_rx_redirect (vnet_main_t * vnm,
1939 vnet_hw_interface_t * hi, u32 enable)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001940{
1941 // Insure all packets go to ethernet-input (i.e. untagged ipv4 packets
1942 // don't go directly to ip4-input)
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001943 vnet_hw_interface_rx_redirect_to_node
1944 (vnm, hi->hw_if_index, enable ? ethernet_input_node.index : ~0);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001945}
1946
1947
1948/*
1949 * Initialization and registration for the next_by_ethernet structure
1950 */
1951
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001952clib_error_t *
1953next_by_ethertype_init (next_by_ethertype_t * l3_next)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001954{
1955 l3_next->input_next_by_type = sparse_vec_new
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001956 ( /* elt bytes */ sizeof (l3_next->input_next_by_type[0]),
Ed Warnickecb9cada2015-12-08 15:45:58 -07001957 /* bits in index */ BITS (((ethernet_header_t *) 0)->type));
1958
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001959 vec_validate (l3_next->sparse_index_by_input_next_index,
1960 ETHERNET_INPUT_NEXT_DROP);
1961 vec_validate (l3_next->sparse_index_by_input_next_index,
1962 ETHERNET_INPUT_NEXT_PUNT);
1963 l3_next->sparse_index_by_input_next_index[ETHERNET_INPUT_NEXT_DROP] =
1964 SPARSE_VEC_INVALID_INDEX;
1965 l3_next->sparse_index_by_input_next_index[ETHERNET_INPUT_NEXT_PUNT] =
1966 SPARSE_VEC_INVALID_INDEX;
1967
Damjan Marion607de1a2016-08-16 22:53:54 +02001968 /*
1969 * Make sure we don't wipe out an ethernet registration by mistake
Dave Barach1f49ed62016-02-24 11:29:06 -05001970 * Can happen if init function ordering constraints are missing.
1971 */
1972 if (CLIB_DEBUG > 0)
1973 {
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001974 ethernet_main_t *em = &ethernet_main;
1975 ASSERT (em->next_by_ethertype_register_called == 0);
Dave Barach1f49ed62016-02-24 11:29:06 -05001976 }
1977
Ed Warnickecb9cada2015-12-08 15:45:58 -07001978 return 0;
1979}
1980
1981// Add an ethertype -> next index mapping to the structure
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001982clib_error_t *
1983next_by_ethertype_register (next_by_ethertype_t * l3_next,
1984 u32 ethertype, u32 next_index)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001985{
1986 u32 i;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001987 u16 *n;
1988 ethernet_main_t *em = &ethernet_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001989
Dave Barach1f49ed62016-02-24 11:29:06 -05001990 if (CLIB_DEBUG > 0)
1991 {
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001992 ethernet_main_t *em = &ethernet_main;
Dave Barach1f49ed62016-02-24 11:29:06 -05001993 em->next_by_ethertype_register_called = 1;
1994 }
1995
Ed Warnickecb9cada2015-12-08 15:45:58 -07001996 /* Setup ethernet type -> next index sparse vector mapping. */
1997 n = sparse_vec_validate (l3_next->input_next_by_type, ethertype);
1998 n[0] = next_index;
1999
2000 /* Rebuild next index -> sparse index inverse mapping when sparse vector
2001 is updated. */
2002 vec_validate (l3_next->sparse_index_by_input_next_index, next_index);
2003 for (i = 1; i < vec_len (l3_next->input_next_by_type); i++)
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002004 l3_next->
2005 sparse_index_by_input_next_index[l3_next->input_next_by_type[i]] = i;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002006
2007 // do not allow the cached next index's to be updated if L3
2008 // redirect is enabled, as it will have overwritten them
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002009 if (!em->redirect_l3)
2010 {
2011 // Cache common ethertypes directly
2012 if (ethertype == ETHERNET_TYPE_IP4)
2013 {
2014 l3_next->input_next_ip4 = next_index;
2015 }
2016 else if (ethertype == ETHERNET_TYPE_IP6)
2017 {
2018 l3_next->input_next_ip6 = next_index;
2019 }
Neale Ranns0f26c5a2017-03-01 15:12:11 -08002020 else if (ethertype == ETHERNET_TYPE_MPLS)
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002021 {
2022 l3_next->input_next_mpls = next_index;
2023 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07002024 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07002025 return 0;
2026}
2027
Dave Barachf8d50682019-05-14 18:01:44 -04002028void
2029ethernet_input_init (vlib_main_t * vm, ethernet_main_t * em)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002030{
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002031 __attribute__ ((unused)) vlan_table_t *invalid_vlan_table;
2032 __attribute__ ((unused)) qinq_table_t *invalid_qinq_table;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002033
2034 ethernet_setup_node (vm, ethernet_input_node.index);
2035 ethernet_setup_node (vm, ethernet_input_type_node.index);
2036 ethernet_setup_node (vm, ethernet_input_not_l2_node.index);
2037
2038 next_by_ethertype_init (&em->l3_next);
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002039
Ed Warnickecb9cada2015-12-08 15:45:58 -07002040 // Initialize pools and vector for vlan parsing
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002041 vec_validate (em->main_intfs, 10); // 10 main interfaces
2042 pool_alloc (em->vlan_pool, 10);
2043 pool_alloc (em->qinq_pool, 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002044
2045 // The first vlan pool will always be reserved for an invalid table
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002046 pool_get (em->vlan_pool, invalid_vlan_table); // first id = 0
Ed Warnickecb9cada2015-12-08 15:45:58 -07002047 // The first qinq pool will always be reserved for an invalid table
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002048 pool_get (em->qinq_pool, invalid_qinq_table); // first id = 0
Ed Warnickecb9cada2015-12-08 15:45:58 -07002049}
2050
Ed Warnickecb9cada2015-12-08 15:45:58 -07002051void
2052ethernet_register_input_type (vlib_main_t * vm,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002053 ethernet_type_t type, u32 node_index)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002054{
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002055 ethernet_main_t *em = &ethernet_main;
2056 ethernet_type_info_t *ti;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002057 u32 i;
2058
2059 {
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002060 clib_error_t *error = vlib_call_init_function (vm, ethernet_init);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002061 if (error)
2062 clib_error_report (error);
2063 }
2064
2065 ti = ethernet_get_type_info (em, type);
2066 ti->node_index = node_index;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002067 ti->next_index = vlib_node_add_next (vm,
2068 ethernet_input_node.index, node_index);
2069 i = vlib_node_add_next (vm, ethernet_input_type_node.index, node_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002070 ASSERT (i == ti->next_index);
2071
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002072 i = vlib_node_add_next (vm, ethernet_input_not_l2_node.index, node_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002073 ASSERT (i == ti->next_index);
2074
2075 // Add the L3 node for this ethertype to the next nodes structure
2076 next_by_ethertype_register (&em->l3_next, type, ti->next_index);
2077
2078 // Call the registration functions for other nodes that want a mapping
2079 l2bvi_register_input_type (vm, type, node_index);
2080}
2081
2082void
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002083ethernet_register_l2_input (vlib_main_t * vm, u32 node_index)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002084{
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002085 ethernet_main_t *em = &ethernet_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002086 u32 i;
2087
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002088 em->l2_next =
2089 vlib_node_add_next (vm, ethernet_input_node.index, node_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002090
Damjan Marion607de1a2016-08-16 22:53:54 +02002091 /*
Ed Warnickecb9cada2015-12-08 15:45:58 -07002092 * Even if we never use these arcs, we have to align the next indices...
2093 */
2094 i = vlib_node_add_next (vm, ethernet_input_type_node.index, node_index);
2095
2096 ASSERT (i == em->l2_next);
2097
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002098 i = vlib_node_add_next (vm, ethernet_input_not_l2_node.index, node_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002099 ASSERT (i == em->l2_next);
2100}
2101
2102// Register a next node for L3 redirect, and enable L3 redirect
2103void
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002104ethernet_register_l3_redirect (vlib_main_t * vm, u32 node_index)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002105{
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002106 ethernet_main_t *em = &ethernet_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002107 u32 i;
2108
2109 em->redirect_l3 = 1;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002110 em->redirect_l3_next = vlib_node_add_next (vm,
2111 ethernet_input_node.index,
2112 node_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002113 /*
2114 * Change the cached next nodes to the redirect node
2115 */
2116 em->l3_next.input_next_ip4 = em->redirect_l3_next;
2117 em->l3_next.input_next_ip6 = em->redirect_l3_next;
2118 em->l3_next.input_next_mpls = em->redirect_l3_next;
2119
2120 /*
2121 * Even if we never use these arcs, we have to align the next indices...
2122 */
2123 i = vlib_node_add_next (vm, ethernet_input_type_node.index, node_index);
2124
2125 ASSERT (i == em->redirect_l3_next);
jerryianff82ed62016-12-05 17:13:00 +08002126
2127 i = vlib_node_add_next (vm, ethernet_input_not_l2_node.index, node_index);
2128
2129 ASSERT (i == em->redirect_l3_next);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002130}
Damjan Marion5beecec2018-09-10 13:09:21 +02002131#endif
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002132
2133/*
2134 * fd.io coding-style-patch-verification: ON
2135 *
2136 * Local Variables:
2137 * eval: (c-set-style "gnu")
2138 * End:
2139 */