blob: 950a962217cf2b30f502a8a1b6eb1bed61d58057 [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;
Dave Barach5ecd5a52019-02-25 15:27:28 -0500966 if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) == 0))
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 */
Ed Warnickecb9cada2015-12-08 15:45:58 -07001032
1033 if (variant != ETHERNET_INPUT_VARIANT_ETHERNET)
1034 error_node = vlib_node_get_runtime (vm, ethernet_input_node.index);
1035 else
1036 error_node = node;
1037
Damjan Marion650223c2018-11-14 16:55:53 +01001038 n_left_from = n_packets;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001039
1040 next_index = node->cached_next_index;
1041 stats_sw_if_index = node->runtime_data[0];
1042 stats_n_packets = stats_n_bytes = 0;
1043
1044 while (n_left_from > 0)
1045 {
1046 u32 n_left_to_next;
1047
1048 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
1049
1050 while (n_left_from >= 4 && n_left_to_next >= 2)
1051 {
1052 u32 bi0, bi1;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001053 vlib_buffer_t *b0, *b1;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001054 u8 next0, next1, error0, error1;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001055 u16 type0, orig_type0, type1, orig_type1;
1056 u16 outer_id0, inner_id0, outer_id1, inner_id1;
1057 u32 match_flags0, match_flags1;
1058 u32 old_sw_if_index0, new_sw_if_index0, len0, old_sw_if_index1,
1059 new_sw_if_index1, len1;
1060 vnet_hw_interface_t *hi0, *hi1;
1061 main_intf_t *main_intf0, *main_intf1;
1062 vlan_intf_t *vlan_intf0, *vlan_intf1;
1063 qinq_intf_t *qinq_intf0, *qinq_intf1;
1064 u32 is_l20, is_l21;
Dave Barachcfba1e22016-11-16 10:23:50 -05001065 ethernet_header_t *e0, *e1;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001066
1067 /* Prefetch next iteration. */
1068 {
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001069 vlib_buffer_t *b2, *b3;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001070
1071 b2 = vlib_get_buffer (vm, from[2]);
1072 b3 = vlib_get_buffer (vm, from[3]);
1073
1074 vlib_prefetch_buffer_header (b2, STORE);
1075 vlib_prefetch_buffer_header (b3, STORE);
1076
1077 CLIB_PREFETCH (b2->data, sizeof (ethernet_header_t), LOAD);
1078 CLIB_PREFETCH (b3->data, sizeof (ethernet_header_t), LOAD);
1079 }
1080
1081 bi0 = from[0];
1082 bi1 = from[1];
1083 to_next[0] = bi0;
1084 to_next[1] = bi1;
1085 from += 2;
1086 to_next += 2;
1087 n_left_to_next -= 2;
1088 n_left_from -= 2;
1089
1090 b0 = vlib_get_buffer (vm, bi0);
1091 b1 = vlib_get_buffer (vm, bi1);
1092
1093 error0 = error1 = ETHERNET_ERROR_NONE;
Dave Barachcfba1e22016-11-16 10:23:50 -05001094 e0 = vlib_buffer_get_current (b0);
1095 type0 = clib_net_to_host_u16 (e0->type);
1096 e1 = vlib_buffer_get_current (b1);
1097 type1 = clib_net_to_host_u16 (e1->type);
1098
Andrew Yourtchenko20e6d362018-10-05 20:36:03 +02001099 /* Set the L2 header offset for all packets */
1100 vnet_buffer (b0)->l2_hdr_offset = b0->current_data;
1101 vnet_buffer (b1)->l2_hdr_offset = b1->current_data;
1102 b0->flags |= VNET_BUFFER_F_L2_HDR_OFFSET_VALID;
1103 b1->flags |= VNET_BUFFER_F_L2_HDR_OFFSET_VALID;
1104
John Locc532852016-12-14 15:42:45 -05001105 /* Speed-path for the untagged case */
Dave Barachcfba1e22016-11-16 10:23:50 -05001106 if (PREDICT_TRUE (variant == ETHERNET_INPUT_VARIANT_ETHERNET
Damjan Marionc6969b52018-02-19 12:14:06 +01001107 && !ethernet_frame_is_any_tagged_x2 (type0,
1108 type1)))
Dave Barachcfba1e22016-11-16 10:23:50 -05001109 {
1110 main_intf_t *intf0;
1111 subint_config_t *subint0;
1112 u32 sw_if_index0, sw_if_index1;
1113
1114 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
1115 sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
1116 is_l20 = cached_is_l2;
1117
1118 /* This is probably wholly unnecessary */
1119 if (PREDICT_FALSE (sw_if_index0 != sw_if_index1))
1120 goto slowpath;
1121
John Lo1904c472017-03-10 17:15:22 -05001122 /* Now sw_if_index0 == sw_if_index1 */
Dave Barachcfba1e22016-11-16 10:23:50 -05001123 if (PREDICT_FALSE (cached_sw_if_index != sw_if_index0))
1124 {
1125 cached_sw_if_index = sw_if_index0;
John Lo1904c472017-03-10 17:15:22 -05001126 hi = vnet_get_sup_hw_interface (vnm, sw_if_index0);
1127 intf0 = vec_elt_at_index (em->main_intfs, hi->hw_if_index);
Dave Barachcfba1e22016-11-16 10:23:50 -05001128 subint0 = &intf0->untagged_subint;
1129 cached_is_l2 = is_l20 = subint0->flags & SUBINT_CONFIG_L2;
1130 }
John Lo7714b302016-12-20 16:59:02 -05001131
Dave Barachcfba1e22016-11-16 10:23:50 -05001132 if (PREDICT_TRUE (is_l20 != 0))
1133 {
Andrew Yourtchenko20e6d362018-10-05 20:36:03 +02001134 vnet_buffer (b0)->l3_hdr_offset =
1135 vnet_buffer (b0)->l2_hdr_offset +
1136 sizeof (ethernet_header_t);
1137 vnet_buffer (b1)->l3_hdr_offset =
1138 vnet_buffer (b1)->l2_hdr_offset +
1139 sizeof (ethernet_header_t);
1140 b0->flags |= VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
1141 b1->flags |= VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
Dave Barachcfba1e22016-11-16 10:23:50 -05001142 next0 = em->l2_next;
1143 vnet_buffer (b0)->l2.l2_len = sizeof (ethernet_header_t);
Dave Barachcfba1e22016-11-16 10:23:50 -05001144 next1 = em->l2_next;
1145 vnet_buffer (b1)->l2.l2_len = sizeof (ethernet_header_t);
Dave Barachcfba1e22016-11-16 10:23:50 -05001146 }
John Locc532852016-12-14 15:42:45 -05001147 else
1148 {
John Lo1904c472017-03-10 17:15:22 -05001149 if (!ethernet_address_cast (e0->dst_address) &&
Hongjun Ni9e3e3612017-04-26 18:40:55 +08001150 (hi->hw_address != 0) &&
Neale Ranns37029302018-08-10 05:30:06 -07001151 !ethernet_mac_address_equal ((u8 *) e0, hi->hw_address))
John Lo1904c472017-03-10 17:15:22 -05001152 error0 = ETHERNET_ERROR_L3_MAC_MISMATCH;
1153 if (!ethernet_address_cast (e1->dst_address) &&
Hongjun Ni9e3e3612017-04-26 18:40:55 +08001154 (hi->hw_address != 0) &&
Neale Ranns37029302018-08-10 05:30:06 -07001155 !ethernet_mac_address_equal ((u8 *) e1, hi->hw_address))
John Lo1904c472017-03-10 17:15:22 -05001156 error1 = ETHERNET_ERROR_L3_MAC_MISMATCH;
John Lob14826e2018-04-18 15:52:23 -04001157 vlib_buffer_advance (b0, sizeof (ethernet_header_t));
John Locc532852016-12-14 15:42:45 -05001158 determine_next_node (em, variant, 0, type0, b0,
1159 &error0, &next0);
John Lob14826e2018-04-18 15:52:23 -04001160 vlib_buffer_advance (b1, sizeof (ethernet_header_t));
John Locc532852016-12-14 15:42:45 -05001161 determine_next_node (em, variant, 0, type1, b1,
1162 &error1, &next1);
John Locc532852016-12-14 15:42:45 -05001163 }
1164 goto ship_it01;
Dave Barachcfba1e22016-11-16 10:23:50 -05001165 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001166
John Locc532852016-12-14 15:42:45 -05001167 /* Slow-path for the tagged case */
1168 slowpath:
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001169 parse_header (variant,
1170 b0,
1171 &type0,
1172 &orig_type0, &outer_id0, &inner_id0, &match_flags0);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001173
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001174 parse_header (variant,
1175 b1,
1176 &type1,
1177 &orig_type1, &outer_id1, &inner_id1, &match_flags1);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001178
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001179 old_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
1180 old_sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
Ed Warnickecb9cada2015-12-08 15:45:58 -07001181
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001182 eth_vlan_table_lookups (em,
1183 vnm,
1184 old_sw_if_index0,
1185 orig_type0,
1186 outer_id0,
1187 inner_id0,
1188 &hi0,
1189 &main_intf0, &vlan_intf0, &qinq_intf0);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001190
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001191 eth_vlan_table_lookups (em,
1192 vnm,
1193 old_sw_if_index1,
1194 orig_type1,
1195 outer_id1,
1196 inner_id1,
1197 &hi1,
1198 &main_intf1, &vlan_intf1, &qinq_intf1);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001199
1200 identify_subint (hi0,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001201 b0,
1202 match_flags0,
1203 main_intf0,
1204 vlan_intf0,
1205 qinq_intf0, &new_sw_if_index0, &error0, &is_l20);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001206
1207 identify_subint (hi1,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001208 b1,
1209 match_flags1,
1210 main_intf1,
1211 vlan_intf1,
1212 qinq_intf1, &new_sw_if_index1, &error1, &is_l21);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001213
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001214 // Save RX sw_if_index for later nodes
1215 vnet_buffer (b0)->sw_if_index[VLIB_RX] =
1216 error0 !=
1217 ETHERNET_ERROR_NONE ? old_sw_if_index0 : new_sw_if_index0;
1218 vnet_buffer (b1)->sw_if_index[VLIB_RX] =
1219 error1 !=
1220 ETHERNET_ERROR_NONE ? old_sw_if_index1 : new_sw_if_index1;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001221
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001222 // Check if there is a stat to take (valid and non-main sw_if_index for pkt 0 or pkt 1)
1223 if (((new_sw_if_index0 != ~0)
1224 && (new_sw_if_index0 != old_sw_if_index0))
1225 || ((new_sw_if_index1 != ~0)
1226 && (new_sw_if_index1 != old_sw_if_index1)))
1227 {
Ed Warnickecb9cada2015-12-08 15:45:58 -07001228
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001229 len0 = vlib_buffer_length_in_chain (vm, b0) + b0->current_data
Damjan Marion072401e2017-07-13 18:53:27 +02001230 - vnet_buffer (b0)->l2_hdr_offset;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001231 len1 = vlib_buffer_length_in_chain (vm, b1) + b1->current_data
Damjan Marion072401e2017-07-13 18:53:27 +02001232 - vnet_buffer (b1)->l2_hdr_offset;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001233
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001234 stats_n_packets += 2;
1235 stats_n_bytes += len0 + len1;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001236
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001237 if (PREDICT_FALSE
1238 (!(new_sw_if_index0 == stats_sw_if_index
1239 && new_sw_if_index1 == stats_sw_if_index)))
Ed Warnickecb9cada2015-12-08 15:45:58 -07001240 {
1241 stats_n_packets -= 2;
1242 stats_n_bytes -= len0 + len1;
1243
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001244 if (new_sw_if_index0 != old_sw_if_index0
1245 && new_sw_if_index0 != ~0)
1246 vlib_increment_combined_counter (vnm->
1247 interface_main.combined_sw_if_counters
1248 +
1249 VNET_INTERFACE_COUNTER_RX,
Damjan Marion586afd72017-04-05 19:18:20 +02001250 thread_index,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001251 new_sw_if_index0, 1,
1252 len0);
1253 if (new_sw_if_index1 != old_sw_if_index1
1254 && new_sw_if_index1 != ~0)
1255 vlib_increment_combined_counter (vnm->
1256 interface_main.combined_sw_if_counters
1257 +
1258 VNET_INTERFACE_COUNTER_RX,
Damjan Marion586afd72017-04-05 19:18:20 +02001259 thread_index,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001260 new_sw_if_index1, 1,
1261 len1);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001262
1263 if (new_sw_if_index0 == new_sw_if_index1)
1264 {
1265 if (stats_n_packets > 0)
1266 {
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001267 vlib_increment_combined_counter
1268 (vnm->interface_main.combined_sw_if_counters
1269 + VNET_INTERFACE_COUNTER_RX,
Damjan Marion586afd72017-04-05 19:18:20 +02001270 thread_index,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001271 stats_sw_if_index,
1272 stats_n_packets, stats_n_bytes);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001273 stats_n_packets = stats_n_bytes = 0;
1274 }
1275 stats_sw_if_index = new_sw_if_index0;
1276 }
1277 }
1278 }
1279
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001280 if (variant == ETHERNET_INPUT_VARIANT_NOT_L2)
1281 is_l20 = is_l21 = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001282
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001283 determine_next_node (em, variant, is_l20, type0, b0, &error0,
1284 &next0);
1285 determine_next_node (em, variant, is_l21, type1, b1, &error1,
1286 &next1);
1287
John Lo1904c472017-03-10 17:15:22 -05001288 ship_it01:
Ed Warnickecb9cada2015-12-08 15:45:58 -07001289 b0->error = error_node->errors[error0];
1290 b1->error = error_node->errors[error1];
1291
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001292 // verify speculative enqueue
1293 vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
1294 n_left_to_next, bi0, bi1, next0,
1295 next1);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001296 }
1297
1298 while (n_left_from > 0 && n_left_to_next > 0)
1299 {
1300 u32 bi0;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001301 vlib_buffer_t *b0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001302 u8 error0, next0;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001303 u16 type0, orig_type0;
1304 u16 outer_id0, inner_id0;
1305 u32 match_flags0;
1306 u32 old_sw_if_index0, new_sw_if_index0, len0;
1307 vnet_hw_interface_t *hi0;
1308 main_intf_t *main_intf0;
1309 vlan_intf_t *vlan_intf0;
1310 qinq_intf_t *qinq_intf0;
Dave Barachcfba1e22016-11-16 10:23:50 -05001311 ethernet_header_t *e0;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001312 u32 is_l20;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001313
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001314 // Prefetch next iteration
1315 if (n_left_from > 1)
1316 {
1317 vlib_buffer_t *p2;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001318
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001319 p2 = vlib_get_buffer (vm, from[1]);
1320 vlib_prefetch_buffer_header (p2, STORE);
1321 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD);
1322 }
1323
1324 bi0 = from[0];
Ed Warnickecb9cada2015-12-08 15:45:58 -07001325 to_next[0] = bi0;
1326 from += 1;
1327 to_next += 1;
1328 n_left_from -= 1;
1329 n_left_to_next -= 1;
1330
1331 b0 = vlib_get_buffer (vm, bi0);
1332
1333 error0 = ETHERNET_ERROR_NONE;
Dave Barachcfba1e22016-11-16 10:23:50 -05001334 e0 = vlib_buffer_get_current (b0);
1335 type0 = clib_net_to_host_u16 (e0->type);
1336
Andrew Yourtchenko20e6d362018-10-05 20:36:03 +02001337 /* Set the L2 header offset for all packets */
1338 vnet_buffer (b0)->l2_hdr_offset = b0->current_data;
1339 b0->flags |= VNET_BUFFER_F_L2_HDR_OFFSET_VALID;
1340
John Locc532852016-12-14 15:42:45 -05001341 /* Speed-path for the untagged case */
Dave Barachcfba1e22016-11-16 10:23:50 -05001342 if (PREDICT_TRUE (variant == ETHERNET_INPUT_VARIANT_ETHERNET
1343 && !ethernet_frame_is_tagged (type0)))
1344 {
1345 main_intf_t *intf0;
1346 subint_config_t *subint0;
1347 u32 sw_if_index0;
1348
1349 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
1350 is_l20 = cached_is_l2;
1351
1352 if (PREDICT_FALSE (cached_sw_if_index != sw_if_index0))
1353 {
1354 cached_sw_if_index = sw_if_index0;
John Lo1904c472017-03-10 17:15:22 -05001355 hi = vnet_get_sup_hw_interface (vnm, sw_if_index0);
1356 intf0 = vec_elt_at_index (em->main_intfs, hi->hw_if_index);
Dave Barachcfba1e22016-11-16 10:23:50 -05001357 subint0 = &intf0->untagged_subint;
1358 cached_is_l2 = is_l20 = subint0->flags & SUBINT_CONFIG_L2;
1359 }
John Lo7714b302016-12-20 16:59:02 -05001360
John Lo7714b302016-12-20 16:59:02 -05001361
Dave Barachcfba1e22016-11-16 10:23:50 -05001362 if (PREDICT_TRUE (is_l20 != 0))
1363 {
Andrew Yourtchenko20e6d362018-10-05 20:36:03 +02001364 vnet_buffer (b0)->l3_hdr_offset =
1365 vnet_buffer (b0)->l2_hdr_offset +
1366 sizeof (ethernet_header_t);
1367 b0->flags |= VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
Dave Barachcfba1e22016-11-16 10:23:50 -05001368 next0 = em->l2_next;
1369 vnet_buffer (b0)->l2.l2_len = sizeof (ethernet_header_t);
Dave Barachcfba1e22016-11-16 10:23:50 -05001370 }
John Locc532852016-12-14 15:42:45 -05001371 else
1372 {
John Lo1904c472017-03-10 17:15:22 -05001373 if (!ethernet_address_cast (e0->dst_address) &&
Hongjun Ni9e3e3612017-04-26 18:40:55 +08001374 (hi->hw_address != 0) &&
Neale Ranns37029302018-08-10 05:30:06 -07001375 !ethernet_mac_address_equal ((u8 *) e0, hi->hw_address))
John Lo1904c472017-03-10 17:15:22 -05001376 error0 = ETHERNET_ERROR_L3_MAC_MISMATCH;
Andrew Yourtchenkoe78bca12018-10-10 16:15:55 +02001377 vlib_buffer_advance (b0, sizeof (ethernet_header_t));
John Locc532852016-12-14 15:42:45 -05001378 determine_next_node (em, variant, 0, type0, b0,
1379 &error0, &next0);
John Locc532852016-12-14 15:42:45 -05001380 }
1381 goto ship_it0;
Dave Barachcfba1e22016-11-16 10:23:50 -05001382 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001383
John Locc532852016-12-14 15:42:45 -05001384 /* Slow-path for the tagged case */
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001385 parse_header (variant,
1386 b0,
1387 &type0,
1388 &orig_type0, &outer_id0, &inner_id0, &match_flags0);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001389
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001390 old_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
Ed Warnickecb9cada2015-12-08 15:45:58 -07001391
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001392 eth_vlan_table_lookups (em,
1393 vnm,
1394 old_sw_if_index0,
1395 orig_type0,
1396 outer_id0,
1397 inner_id0,
1398 &hi0,
1399 &main_intf0, &vlan_intf0, &qinq_intf0);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001400
1401 identify_subint (hi0,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001402 b0,
1403 match_flags0,
1404 main_intf0,
1405 vlan_intf0,
1406 qinq_intf0, &new_sw_if_index0, &error0, &is_l20);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001407
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001408 // Save RX sw_if_index for later nodes
1409 vnet_buffer (b0)->sw_if_index[VLIB_RX] =
1410 error0 !=
1411 ETHERNET_ERROR_NONE ? old_sw_if_index0 : new_sw_if_index0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001412
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001413 // Increment subinterface stats
1414 // Note that interface-level counters have already been incremented
1415 // prior to calling this function. Thus only subinterface counters
1416 // are incremented here.
1417 //
Damjan Marion607de1a2016-08-16 22:53:54 +02001418 // Interface level counters include packets received on the main
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001419 // interface and all subinterfaces. Subinterface level counters
1420 // include only those packets received on that subinterface
Ed Warnickecb9cada2015-12-08 15:45:58 -07001421 // Increment stats if the subint is valid and it is not the main intf
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001422 if ((new_sw_if_index0 != ~0)
1423 && (new_sw_if_index0 != old_sw_if_index0))
1424 {
Ed Warnickecb9cada2015-12-08 15:45:58 -07001425
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001426 len0 = vlib_buffer_length_in_chain (vm, b0) + b0->current_data
Damjan Marion072401e2017-07-13 18:53:27 +02001427 - vnet_buffer (b0)->l2_hdr_offset;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001428
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001429 stats_n_packets += 1;
1430 stats_n_bytes += len0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001431
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001432 // Batch stat increments from the same subinterface so counters
Damjan Marion607de1a2016-08-16 22:53:54 +02001433 // don't need to be incremented for every packet.
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001434 if (PREDICT_FALSE (new_sw_if_index0 != stats_sw_if_index))
1435 {
1436 stats_n_packets -= 1;
1437 stats_n_bytes -= len0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001438
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001439 if (new_sw_if_index0 != ~0)
1440 vlib_increment_combined_counter
1441 (vnm->interface_main.combined_sw_if_counters
1442 + VNET_INTERFACE_COUNTER_RX,
Damjan Marion586afd72017-04-05 19:18:20 +02001443 thread_index, new_sw_if_index0, 1, len0);
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001444 if (stats_n_packets > 0)
1445 {
1446 vlib_increment_combined_counter
1447 (vnm->interface_main.combined_sw_if_counters
1448 + VNET_INTERFACE_COUNTER_RX,
Damjan Marion586afd72017-04-05 19:18:20 +02001449 thread_index,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001450 stats_sw_if_index, stats_n_packets, stats_n_bytes);
1451 stats_n_packets = stats_n_bytes = 0;
1452 }
1453 stats_sw_if_index = new_sw_if_index0;
1454 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001455 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001456
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001457 if (variant == ETHERNET_INPUT_VARIANT_NOT_L2)
1458 is_l20 = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001459
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001460 determine_next_node (em, variant, is_l20, type0, b0, &error0,
1461 &next0);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001462
John Lo1904c472017-03-10 17:15:22 -05001463 ship_it0:
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001464 b0->error = error_node->errors[error0];
1465
1466 // verify speculative enqueue
Ed Warnickecb9cada2015-12-08 15:45:58 -07001467 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1468 to_next, n_left_to_next,
1469 bi0, next0);
1470 }
1471
1472 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1473 }
1474
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001475 // Increment any remaining batched stats
1476 if (stats_n_packets > 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001477 {
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001478 vlib_increment_combined_counter
1479 (vnm->interface_main.combined_sw_if_counters
1480 + VNET_INTERFACE_COUNTER_RX,
Damjan Marion586afd72017-04-05 19:18:20 +02001481 thread_index, stats_sw_if_index, stats_n_packets, stats_n_bytes);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001482 node->runtime_data[0] = stats_sw_if_index;
1483 }
Damjan Marion650223c2018-11-14 16:55:53 +01001484}
Ed Warnickecb9cada2015-12-08 15:45:58 -07001485
Damjan Marion5beecec2018-09-10 13:09:21 +02001486VLIB_NODE_FN (ethernet_input_node) (vlib_main_t * vm,
1487 vlib_node_runtime_t * node,
Damjan Marion650223c2018-11-14 16:55:53 +01001488 vlib_frame_t * frame)
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001489{
Damjan Marion650223c2018-11-14 16:55:53 +01001490 vnet_main_t *vnm = vnet_get_main ();
Damjan Marion650223c2018-11-14 16:55:53 +01001491 u32 *from = vlib_frame_vector_args (frame);
1492 u32 n_packets = frame->n_vectors;
1493
1494 ethernet_input_trace (vm, node, frame);
1495
1496 if (frame->flags & ETH_INPUT_FRAME_F_SINGLE_SW_IF_IDX)
1497 {
Damjan Marion650223c2018-11-14 16:55:53 +01001498 ethernet_input_frame_t *ef = vlib_frame_scalar_args (frame);
Damjan Marion650223c2018-11-14 16:55:53 +01001499 int ip4_cksum_ok = (frame->flags & ETH_INPUT_FRAME_F_IP4_CKSUM_OK) != 0;
Damjan Marion8d6f34e2018-11-25 21:19:13 +01001500 vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, ef->hw_if_index);
1501 eth_input_single_int (vm, node, hi, from, n_packets, ip4_cksum_ok);
Damjan Marion650223c2018-11-14 16:55:53 +01001502 }
Damjan Marion8d6f34e2018-11-25 21:19:13 +01001503 else
1504 ethernet_input_inline (vm, node, from, n_packets,
1505 ETHERNET_INPUT_VARIANT_ETHERNET);
Damjan Marion650223c2018-11-14 16:55:53 +01001506 return n_packets;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001507}
Ed Warnickecb9cada2015-12-08 15:45:58 -07001508
Damjan Marion5beecec2018-09-10 13:09:21 +02001509VLIB_NODE_FN (ethernet_input_type_node) (vlib_main_t * vm,
1510 vlib_node_runtime_t * node,
1511 vlib_frame_t * from_frame)
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001512{
Damjan Marion650223c2018-11-14 16:55:53 +01001513 u32 *from = vlib_frame_vector_args (from_frame);
1514 u32 n_packets = from_frame->n_vectors;
1515 ethernet_input_trace (vm, node, from_frame);
1516 ethernet_input_inline (vm, node, from, n_packets,
1517 ETHERNET_INPUT_VARIANT_ETHERNET_TYPE);
1518 return n_packets;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001519}
Ed Warnickecb9cada2015-12-08 15:45:58 -07001520
Damjan Marion5beecec2018-09-10 13:09:21 +02001521VLIB_NODE_FN (ethernet_input_not_l2_node) (vlib_main_t * vm,
1522 vlib_node_runtime_t * node,
1523 vlib_frame_t * from_frame)
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001524{
Damjan Marion650223c2018-11-14 16:55:53 +01001525 u32 *from = vlib_frame_vector_args (from_frame);
1526 u32 n_packets = from_frame->n_vectors;
1527 ethernet_input_trace (vm, node, from_frame);
1528 ethernet_input_inline (vm, node, from, n_packets,
1529 ETHERNET_INPUT_VARIANT_NOT_L2);
1530 return n_packets;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001531}
Ed Warnickecb9cada2015-12-08 15:45:58 -07001532
1533
1534// Return the subinterface config struct for the given sw_if_index
1535// Also return via parameter the appropriate match flags for the
1536// configured number of tags.
1537// On error (unsupported or not ethernet) return 0.
1538static subint_config_t *
1539ethernet_sw_interface_get_config (vnet_main_t * vnm,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001540 u32 sw_if_index,
1541 u32 * flags, u32 * unsupported)
1542{
1543 ethernet_main_t *em = &ethernet_main;
1544 vnet_hw_interface_t *hi;
1545 vnet_sw_interface_t *si;
1546 main_intf_t *main_intf;
1547 vlan_table_t *vlan_table;
1548 qinq_table_t *qinq_table;
1549 subint_config_t *subint = 0;
1550
Ed Warnickecb9cada2015-12-08 15:45:58 -07001551 hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1552
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001553 if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index))
1554 {
1555 *unsupported = 0;
1556 goto done; // non-ethernet interface
1557 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001558
1559 // ensure there's an entry for the main intf (shouldn't really be necessary)
1560 vec_validate (em->main_intfs, hi->hw_if_index);
1561 main_intf = vec_elt_at_index (em->main_intfs, hi->hw_if_index);
1562
1563 // Locate the subint for the given ethernet config
1564 si = vnet_get_sw_interface (vnm, sw_if_index);
1565
Pavel Kotucek15ac81c2017-06-20 14:00:26 +02001566 if (si->type == VNET_SW_INTERFACE_TYPE_P2P)
1567 {
1568 p2p_ethernet_main_t *p2pm = &p2p_main;
1569 u32 p2pe_sw_if_index =
1570 p2p_ethernet_lookup (hi->hw_if_index, si->p2p.client_mac);
1571 if (p2pe_sw_if_index == ~0)
1572 {
1573 pool_get (p2pm->p2p_subif_pool, subint);
1574 si->p2p.pool_index = subint - p2pm->p2p_subif_pool;
1575 }
1576 else
1577 subint = vec_elt_at_index (p2pm->p2p_subif_pool, si->p2p.pool_index);
1578 *flags = SUBINT_CONFIG_P2P;
1579 }
Neale Ranns17ff3c12018-07-04 10:24:24 -07001580 else if (si->type == VNET_SW_INTERFACE_TYPE_PIPE)
1581 {
1582 pipe_t *pipe;
1583
1584 pipe = pipe_get (sw_if_index);
1585 subint = &pipe->subint;
1586 *flags = SUBINT_CONFIG_P2P;
1587 }
Pavel Kotucek15ac81c2017-06-20 14:00:26 +02001588 else if (si->sub.eth.flags.default_sub)
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001589 {
1590 subint = &main_intf->default_subint;
Mike Bly88076742018-09-24 10:13:06 -07001591 *flags = SUBINT_CONFIG_MATCH_1_TAG |
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001592 SUBINT_CONFIG_MATCH_2_TAG | SUBINT_CONFIG_MATCH_3_TAG;
1593 }
1594 else if ((si->sub.eth.flags.no_tags) || (si->sub.eth.raw_flags == 0))
1595 {
1596 // if no flags are set then this is a main interface
1597 // so treat as untagged
1598 subint = &main_intf->untagged_subint;
1599 *flags = SUBINT_CONFIG_MATCH_0_TAG;
1600 }
1601 else
1602 {
1603 // one or two tags
1604 // first get the vlan table
1605 if (si->sub.eth.flags.dot1ad)
1606 {
1607 if (main_intf->dot1ad_vlans == 0)
1608 {
1609 // Allocate a vlan table from the pool
1610 pool_get (em->vlan_pool, vlan_table);
1611 main_intf->dot1ad_vlans = vlan_table - em->vlan_pool;
1612 }
1613 else
1614 {
1615 // Get ptr to existing vlan table
1616 vlan_table =
1617 vec_elt_at_index (em->vlan_pool, main_intf->dot1ad_vlans);
1618 }
1619 }
1620 else
1621 { // dot1q
1622 if (main_intf->dot1q_vlans == 0)
1623 {
1624 // Allocate a vlan table from the pool
1625 pool_get (em->vlan_pool, vlan_table);
1626 main_intf->dot1q_vlans = vlan_table - em->vlan_pool;
1627 }
1628 else
1629 {
1630 // Get ptr to existing vlan table
1631 vlan_table =
1632 vec_elt_at_index (em->vlan_pool, main_intf->dot1q_vlans);
1633 }
1634 }
1635
1636 if (si->sub.eth.flags.one_tag)
1637 {
1638 *flags = si->sub.eth.flags.exact_match ?
1639 SUBINT_CONFIG_MATCH_1_TAG :
1640 (SUBINT_CONFIG_MATCH_1_TAG |
1641 SUBINT_CONFIG_MATCH_2_TAG | SUBINT_CONFIG_MATCH_3_TAG);
1642
1643 if (si->sub.eth.flags.outer_vlan_id_any)
1644 {
1645 // not implemented yet
1646 *unsupported = 1;
1647 goto done;
1648 }
1649 else
1650 {
1651 // a single vlan, a common case
1652 subint =
1653 &vlan_table->vlans[si->sub.eth.
1654 outer_vlan_id].single_tag_subint;
1655 }
1656
1657 }
1658 else
1659 {
1660 // Two tags
1661 *flags = si->sub.eth.flags.exact_match ?
1662 SUBINT_CONFIG_MATCH_2_TAG :
1663 (SUBINT_CONFIG_MATCH_2_TAG | SUBINT_CONFIG_MATCH_3_TAG);
1664
1665 if (si->sub.eth.flags.outer_vlan_id_any
1666 && si->sub.eth.flags.inner_vlan_id_any)
1667 {
1668 // not implemented yet
1669 *unsupported = 1;
1670 goto done;
1671 }
1672
1673 if (si->sub.eth.flags.inner_vlan_id_any)
1674 {
1675 // a specific outer and "any" inner
1676 // don't need a qinq table for this
1677 subint =
1678 &vlan_table->vlans[si->sub.eth.
1679 outer_vlan_id].inner_any_subint;
1680 if (si->sub.eth.flags.exact_match)
1681 {
1682 *flags = SUBINT_CONFIG_MATCH_2_TAG;
1683 }
1684 else
1685 {
1686 *flags = SUBINT_CONFIG_MATCH_2_TAG |
1687 SUBINT_CONFIG_MATCH_3_TAG;
1688 }
1689 }
1690 else
1691 {
1692 // a specific outer + specifc innner vlan id, a common case
1693
1694 // get the qinq table
1695 if (vlan_table->vlans[si->sub.eth.outer_vlan_id].qinqs == 0)
1696 {
1697 // Allocate a qinq table from the pool
1698 pool_get (em->qinq_pool, qinq_table);
1699 vlan_table->vlans[si->sub.eth.outer_vlan_id].qinqs =
1700 qinq_table - em->qinq_pool;
1701 }
1702 else
1703 {
1704 // Get ptr to existing qinq table
1705 qinq_table =
1706 vec_elt_at_index (em->qinq_pool,
1707 vlan_table->vlans[si->sub.
1708 eth.outer_vlan_id].
1709 qinqs);
1710 }
1711 subint = &qinq_table->vlans[si->sub.eth.inner_vlan_id].subint;
1712 }
1713 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001714 }
1715
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001716done:
Ed Warnickecb9cada2015-12-08 15:45:58 -07001717 return subint;
1718}
1719
Damjan Marion5beecec2018-09-10 13:09:21 +02001720static clib_error_t *
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001721ethernet_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001722{
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001723 subint_config_t *subint;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001724 u32 dummy_flags;
1725 u32 dummy_unsup;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001726 clib_error_t *error = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001727
1728 // Find the config for this subinterface
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001729 subint =
1730 ethernet_sw_interface_get_config (vnm, sw_if_index, &dummy_flags,
1731 &dummy_unsup);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001732
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001733 if (subint == 0)
1734 {
1735 // not implemented yet or not ethernet
1736 goto done;
1737 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001738
1739 subint->sw_if_index =
1740 ((flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ? sw_if_index : ~0);
1741
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001742done:
Ed Warnickecb9cada2015-12-08 15:45:58 -07001743 return error;
1744}
1745
1746VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ethernet_sw_interface_up_down);
1747
1748
Damjan Marion5beecec2018-09-10 13:09:21 +02001749#ifndef CLIB_MARCH_VARIANT
Ed Warnickecb9cada2015-12-08 15:45:58 -07001750// Set the L2/L3 mode for the subinterface
1751void
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001752ethernet_sw_interface_set_l2_mode (vnet_main_t * vnm, u32 sw_if_index, u32 l2)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001753{
1754 subint_config_t *subint;
1755 u32 dummy_flags;
1756 u32 dummy_unsup;
1757 int is_port;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001758 vnet_sw_interface_t *sw = vnet_get_sw_interface (vnm, sw_if_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001759
1760 is_port = !(sw->type == VNET_SW_INTERFACE_TYPE_SUB);
1761
1762 // Find the config for this subinterface
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001763 subint =
1764 ethernet_sw_interface_get_config (vnm, sw_if_index, &dummy_flags,
1765 &dummy_unsup);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001766
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001767 if (subint == 0)
1768 {
1769 // unimplemented or not ethernet
1770 goto done;
1771 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001772
1773 // Double check that the config we found is for our interface (or the interface is down)
1774 ASSERT ((subint->sw_if_index == sw_if_index) | (subint->sw_if_index == ~0));
1775
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001776 if (l2)
1777 {
1778 subint->flags |= SUBINT_CONFIG_L2;
1779 if (is_port)
1780 subint->flags |=
1781 SUBINT_CONFIG_MATCH_0_TAG | SUBINT_CONFIG_MATCH_1_TAG
1782 | SUBINT_CONFIG_MATCH_2_TAG | SUBINT_CONFIG_MATCH_3_TAG;
1783 }
1784 else
1785 {
1786 subint->flags &= ~SUBINT_CONFIG_L2;
1787 if (is_port)
1788 subint->flags &=
1789 ~(SUBINT_CONFIG_MATCH_1_TAG | SUBINT_CONFIG_MATCH_2_TAG
1790 | SUBINT_CONFIG_MATCH_3_TAG);
1791 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001792
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001793done:
Ed Warnickecb9cada2015-12-08 15:45:58 -07001794 return;
1795}
1796
Christian Dechamplain (cdechamp)07aecbb2016-04-05 10:40:38 -04001797/*
1798 * Set the L2/L3 mode for the subinterface regardless of port
1799 */
1800void
1801ethernet_sw_interface_set_l2_mode_noport (vnet_main_t * vnm,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001802 u32 sw_if_index, u32 l2)
Christian Dechamplain (cdechamp)07aecbb2016-04-05 10:40:38 -04001803{
1804 subint_config_t *subint;
1805 u32 dummy_flags;
1806 u32 dummy_unsup;
1807
1808 /* Find the config for this subinterface */
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001809 subint =
1810 ethernet_sw_interface_get_config (vnm, sw_if_index, &dummy_flags,
1811 &dummy_unsup);
Christian Dechamplain (cdechamp)07aecbb2016-04-05 10:40:38 -04001812
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001813 if (subint == 0)
1814 {
1815 /* unimplemented or not ethernet */
1816 goto done;
1817 }
Christian Dechamplain (cdechamp)07aecbb2016-04-05 10:40:38 -04001818
1819 /*
1820 * Double check that the config we found is for our interface (or the
1821 * interface is down)
1822 */
1823 ASSERT ((subint->sw_if_index == sw_if_index) | (subint->sw_if_index == ~0));
1824
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001825 if (l2)
1826 {
1827 subint->flags |= SUBINT_CONFIG_L2;
1828 }
1829 else
1830 {
1831 subint->flags &= ~SUBINT_CONFIG_L2;
1832 }
Christian Dechamplain (cdechamp)07aecbb2016-04-05 10:40:38 -04001833
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001834done:
Christian Dechamplain (cdechamp)07aecbb2016-04-05 10:40:38 -04001835 return;
1836}
Damjan Marion5beecec2018-09-10 13:09:21 +02001837#endif
Ed Warnickecb9cada2015-12-08 15:45:58 -07001838
1839static clib_error_t *
1840ethernet_sw_interface_add_del (vnet_main_t * vnm,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001841 u32 sw_if_index, u32 is_create)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001842{
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001843 clib_error_t *error = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001844 subint_config_t *subint;
1845 u32 match_flags;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001846 u32 unsupported = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001847
1848 // Find the config for this subinterface
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001849 subint =
1850 ethernet_sw_interface_get_config (vnm, sw_if_index, &match_flags,
1851 &unsupported);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001852
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001853 if (subint == 0)
1854 {
1855 // not implemented yet or not ethernet
1856 if (unsupported)
1857 {
Damjan Marion607de1a2016-08-16 22:53:54 +02001858 // this is the NYI case
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001859 error = clib_error_return (0, "not implemented yet");
1860 }
1861 goto done;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001862 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001863
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001864 if (!is_create)
1865 {
1866 subint->flags = 0;
1867 return error;
1868 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001869
1870 // Initialize the subint
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001871 if (subint->flags & SUBINT_CONFIG_VALID)
1872 {
1873 // Error vlan already in use
1874 error = clib_error_return (0, "vlan is already in use");
1875 }
1876 else
1877 {
Neale Ranns17ff3c12018-07-04 10:24:24 -07001878 // Note that config is L3 by default
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001879 subint->flags = SUBINT_CONFIG_VALID | match_flags;
1880 subint->sw_if_index = ~0; // because interfaces are initially down
1881 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001882
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001883done:
Ed Warnickecb9cada2015-12-08 15:45:58 -07001884 return error;
1885}
1886
1887VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ethernet_sw_interface_add_del);
1888
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001889static char *ethernet_error_strings[] = {
Ed Warnickecb9cada2015-12-08 15:45:58 -07001890#define ethernet_error(n,c,s) s,
1891#include "error.def"
1892#undef ethernet_error
1893};
1894
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001895/* *INDENT-OFF* */
Ed Warnickecb9cada2015-12-08 15:45:58 -07001896VLIB_REGISTER_NODE (ethernet_input_node) = {
Ed Warnickecb9cada2015-12-08 15:45:58 -07001897 .name = "ethernet-input",
1898 /* Takes a vector of packets. */
1899 .vector_size = sizeof (u32),
Damjan Marion650223c2018-11-14 16:55:53 +01001900 .scalar_size = sizeof (ethernet_input_frame_t),
Ed Warnickecb9cada2015-12-08 15:45:58 -07001901 .n_errors = ETHERNET_N_ERROR,
1902 .error_strings = ethernet_error_strings,
Ed Warnickecb9cada2015-12-08 15:45:58 -07001903 .n_next_nodes = ETHERNET_INPUT_N_NEXT,
1904 .next_nodes = {
1905#define _(s,n) [ETHERNET_INPUT_NEXT_##s] = n,
1906 foreach_ethernet_input_next
1907#undef _
1908 },
Ed Warnickecb9cada2015-12-08 15:45:58 -07001909 .format_buffer = format_ethernet_header_with_length,
1910 .format_trace = format_ethernet_input_trace,
1911 .unformat_buffer = unformat_ethernet_header,
1912};
1913
Damjan Marion5beecec2018-09-10 13:09:21 +02001914VLIB_REGISTER_NODE (ethernet_input_type_node) = {
Ed Warnickecb9cada2015-12-08 15:45:58 -07001915 .name = "ethernet-input-type",
1916 /* Takes a vector of packets. */
1917 .vector_size = sizeof (u32),
Ed Warnickecb9cada2015-12-08 15:45:58 -07001918 .n_next_nodes = ETHERNET_INPUT_N_NEXT,
1919 .next_nodes = {
1920#define _(s,n) [ETHERNET_INPUT_NEXT_##s] = n,
1921 foreach_ethernet_input_next
1922#undef _
1923 },
1924};
1925
Damjan Marion5beecec2018-09-10 13:09:21 +02001926VLIB_REGISTER_NODE (ethernet_input_not_l2_node) = {
Ed Warnickecb9cada2015-12-08 15:45:58 -07001927 .name = "ethernet-input-not-l2",
1928 /* Takes a vector of packets. */
1929 .vector_size = sizeof (u32),
Ed Warnickecb9cada2015-12-08 15:45:58 -07001930 .n_next_nodes = ETHERNET_INPUT_N_NEXT,
1931 .next_nodes = {
1932#define _(s,n) [ETHERNET_INPUT_NEXT_##s] = n,
1933 foreach_ethernet_input_next
1934#undef _
1935 },
1936};
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001937/* *INDENT-ON* */
Ed Warnickecb9cada2015-12-08 15:45:58 -07001938
Damjan Marion5beecec2018-09-10 13:09:21 +02001939#ifndef CLIB_MARCH_VARIANT
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001940void
1941ethernet_set_rx_redirect (vnet_main_t * vnm,
1942 vnet_hw_interface_t * hi, u32 enable)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001943{
1944 // Insure all packets go to ethernet-input (i.e. untagged ipv4 packets
1945 // don't go directly to ip4-input)
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001946 vnet_hw_interface_rx_redirect_to_node
1947 (vnm, hi->hw_if_index, enable ? ethernet_input_node.index : ~0);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001948}
1949
1950
1951/*
1952 * Initialization and registration for the next_by_ethernet structure
1953 */
1954
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001955clib_error_t *
1956next_by_ethertype_init (next_by_ethertype_t * l3_next)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001957{
1958 l3_next->input_next_by_type = sparse_vec_new
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001959 ( /* elt bytes */ sizeof (l3_next->input_next_by_type[0]),
Ed Warnickecb9cada2015-12-08 15:45:58 -07001960 /* bits in index */ BITS (((ethernet_header_t *) 0)->type));
1961
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001962 vec_validate (l3_next->sparse_index_by_input_next_index,
1963 ETHERNET_INPUT_NEXT_DROP);
1964 vec_validate (l3_next->sparse_index_by_input_next_index,
1965 ETHERNET_INPUT_NEXT_PUNT);
1966 l3_next->sparse_index_by_input_next_index[ETHERNET_INPUT_NEXT_DROP] =
1967 SPARSE_VEC_INVALID_INDEX;
1968 l3_next->sparse_index_by_input_next_index[ETHERNET_INPUT_NEXT_PUNT] =
1969 SPARSE_VEC_INVALID_INDEX;
1970
Damjan Marion607de1a2016-08-16 22:53:54 +02001971 /*
1972 * Make sure we don't wipe out an ethernet registration by mistake
Dave Barach1f49ed62016-02-24 11:29:06 -05001973 * Can happen if init function ordering constraints are missing.
1974 */
1975 if (CLIB_DEBUG > 0)
1976 {
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001977 ethernet_main_t *em = &ethernet_main;
1978 ASSERT (em->next_by_ethertype_register_called == 0);
Dave Barach1f49ed62016-02-24 11:29:06 -05001979 }
1980
Ed Warnickecb9cada2015-12-08 15:45:58 -07001981 return 0;
1982}
1983
1984// Add an ethertype -> next index mapping to the structure
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001985clib_error_t *
1986next_by_ethertype_register (next_by_ethertype_t * l3_next,
1987 u32 ethertype, u32 next_index)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001988{
1989 u32 i;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001990 u16 *n;
1991 ethernet_main_t *em = &ethernet_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001992
Dave Barach1f49ed62016-02-24 11:29:06 -05001993 if (CLIB_DEBUG > 0)
1994 {
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07001995 ethernet_main_t *em = &ethernet_main;
Dave Barach1f49ed62016-02-24 11:29:06 -05001996 em->next_by_ethertype_register_called = 1;
1997 }
1998
Ed Warnickecb9cada2015-12-08 15:45:58 -07001999 /* Setup ethernet type -> next index sparse vector mapping. */
2000 n = sparse_vec_validate (l3_next->input_next_by_type, ethertype);
2001 n[0] = next_index;
2002
2003 /* Rebuild next index -> sparse index inverse mapping when sparse vector
2004 is updated. */
2005 vec_validate (l3_next->sparse_index_by_input_next_index, next_index);
2006 for (i = 1; i < vec_len (l3_next->input_next_by_type); i++)
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002007 l3_next->
2008 sparse_index_by_input_next_index[l3_next->input_next_by_type[i]] = i;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002009
2010 // do not allow the cached next index's to be updated if L3
2011 // redirect is enabled, as it will have overwritten them
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002012 if (!em->redirect_l3)
2013 {
2014 // Cache common ethertypes directly
2015 if (ethertype == ETHERNET_TYPE_IP4)
2016 {
2017 l3_next->input_next_ip4 = next_index;
2018 }
2019 else if (ethertype == ETHERNET_TYPE_IP6)
2020 {
2021 l3_next->input_next_ip6 = next_index;
2022 }
Neale Ranns0f26c5a2017-03-01 15:12:11 -08002023 else if (ethertype == ETHERNET_TYPE_MPLS)
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002024 {
2025 l3_next->input_next_mpls = next_index;
2026 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07002027 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07002028 return 0;
2029}
2030
2031
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002032static clib_error_t *
2033ethernet_input_init (vlib_main_t * vm)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002034{
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002035 ethernet_main_t *em = &ethernet_main;
2036 __attribute__ ((unused)) vlan_table_t *invalid_vlan_table;
2037 __attribute__ ((unused)) qinq_table_t *invalid_qinq_table;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002038
2039 ethernet_setup_node (vm, ethernet_input_node.index);
2040 ethernet_setup_node (vm, ethernet_input_type_node.index);
2041 ethernet_setup_node (vm, ethernet_input_not_l2_node.index);
2042
2043 next_by_ethertype_init (&em->l3_next);
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002044
Ed Warnickecb9cada2015-12-08 15:45:58 -07002045 // Initialize pools and vector for vlan parsing
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002046 vec_validate (em->main_intfs, 10); // 10 main interfaces
2047 pool_alloc (em->vlan_pool, 10);
2048 pool_alloc (em->qinq_pool, 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002049
2050 // The first vlan pool will always be reserved for an invalid table
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002051 pool_get (em->vlan_pool, invalid_vlan_table); // first id = 0
Ed Warnickecb9cada2015-12-08 15:45:58 -07002052 // The first qinq pool will always be reserved for an invalid table
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002053 pool_get (em->qinq_pool, invalid_qinq_table); // first id = 0
Ed Warnickecb9cada2015-12-08 15:45:58 -07002054
2055 return 0;
2056}
2057
2058VLIB_INIT_FUNCTION (ethernet_input_init);
2059
2060void
2061ethernet_register_input_type (vlib_main_t * vm,
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002062 ethernet_type_t type, u32 node_index)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002063{
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002064 ethernet_main_t *em = &ethernet_main;
2065 ethernet_type_info_t *ti;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002066 u32 i;
2067
2068 {
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002069 clib_error_t *error = vlib_call_init_function (vm, ethernet_init);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002070 if (error)
2071 clib_error_report (error);
2072 }
2073
2074 ti = ethernet_get_type_info (em, type);
2075 ti->node_index = node_index;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002076 ti->next_index = vlib_node_add_next (vm,
2077 ethernet_input_node.index, node_index);
2078 i = vlib_node_add_next (vm, ethernet_input_type_node.index, node_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002079 ASSERT (i == ti->next_index);
2080
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002081 i = vlib_node_add_next (vm, ethernet_input_not_l2_node.index, node_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002082 ASSERT (i == ti->next_index);
2083
2084 // Add the L3 node for this ethertype to the next nodes structure
2085 next_by_ethertype_register (&em->l3_next, type, ti->next_index);
2086
2087 // Call the registration functions for other nodes that want a mapping
2088 l2bvi_register_input_type (vm, type, node_index);
2089}
2090
2091void
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002092ethernet_register_l2_input (vlib_main_t * vm, u32 node_index)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002093{
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002094 ethernet_main_t *em = &ethernet_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002095 u32 i;
2096
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002097 em->l2_next =
2098 vlib_node_add_next (vm, ethernet_input_node.index, node_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002099
Damjan Marion607de1a2016-08-16 22:53:54 +02002100 /*
Ed Warnickecb9cada2015-12-08 15:45:58 -07002101 * Even if we never use these arcs, we have to align the next indices...
2102 */
2103 i = vlib_node_add_next (vm, ethernet_input_type_node.index, node_index);
2104
2105 ASSERT (i == em->l2_next);
2106
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002107 i = vlib_node_add_next (vm, ethernet_input_not_l2_node.index, node_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002108 ASSERT (i == em->l2_next);
2109}
2110
2111// Register a next node for L3 redirect, and enable L3 redirect
2112void
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002113ethernet_register_l3_redirect (vlib_main_t * vm, u32 node_index)
Ed Warnickecb9cada2015-12-08 15:45:58 -07002114{
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002115 ethernet_main_t *em = &ethernet_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07002116 u32 i;
2117
2118 em->redirect_l3 = 1;
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002119 em->redirect_l3_next = vlib_node_add_next (vm,
2120 ethernet_input_node.index,
2121 node_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002122 /*
2123 * Change the cached next nodes to the redirect node
2124 */
2125 em->l3_next.input_next_ip4 = em->redirect_l3_next;
2126 em->l3_next.input_next_ip6 = em->redirect_l3_next;
2127 em->l3_next.input_next_mpls = em->redirect_l3_next;
2128
2129 /*
2130 * Even if we never use these arcs, we have to align the next indices...
2131 */
2132 i = vlib_node_add_next (vm, ethernet_input_type_node.index, node_index);
2133
2134 ASSERT (i == em->redirect_l3_next);
jerryianff82ed62016-12-05 17:13:00 +08002135
2136 i = vlib_node_add_next (vm, ethernet_input_not_l2_node.index, node_index);
2137
2138 ASSERT (i == em->redirect_l3_next);
Ed Warnickecb9cada2015-12-08 15:45:58 -07002139}
Damjan Marion5beecec2018-09-10 13:09:21 +02002140#endif
Keith Burns (alagalah)e70dcc82016-08-15 18:33:19 -07002141
2142/*
2143 * fd.io coding-style-patch-verification: ON
2144 *
2145 * Local Variables:
2146 * eval: (c-set-style "gnu")
2147 * End:
2148 */