df2da5c66fcd220855aceb44d42832c37201807f
[vpp.git] / src / vnet / bonding / node.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2017 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17
18 #define _GNU_SOURCE
19 #include <stdint.h>
20 #include <vnet/llc/llc.h>
21 #include <vnet/snap/snap.h>
22 #include <vnet/bonding/node.h>
23
24 bond_main_t bond_main;
25
26 #define foreach_bond_input_error \
27   _(NONE, "no error")            \
28   _(IF_DOWN, "interface down")   \
29   _(PASS_THRU, "pass through (CDP, LLDP, slow protocols)")
30
31 typedef enum
32 {
33 #define _(f,s) BOND_INPUT_ERROR_##f,
34   foreach_bond_input_error
35 #undef _
36     BOND_INPUT_N_ERROR,
37 } bond_input_error_t;
38
39 #ifndef CLIB_MULTIARCH_VARIANT
40 static char *bond_input_error_strings[] = {
41 #define _(n,s) s,
42   foreach_bond_input_error
43 #undef _
44 };
45
46 static u8 *
47 format_bond_input_trace (u8 * s, va_list * args)
48 {
49   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
50   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
51   bond_packet_trace_t *t = va_arg (*args, bond_packet_trace_t *);
52
53   s = format (s, "src %U, dst %U, %U -> %U",
54               format_ethernet_address, t->ethernet.src_address,
55               format_ethernet_address, t->ethernet.dst_address,
56               format_vnet_sw_if_index_name, vnet_get_main (),
57               t->sw_if_index,
58               format_vnet_sw_if_index_name, vnet_get_main (),
59               t->bond_sw_if_index);
60
61   return s;
62 }
63 #endif
64
65
66 typedef enum
67 {
68   BOND_INPUT_NEXT_DROP,
69   BOND_INPUT_N_NEXT,
70 } l2output_next_t;
71
72 static_always_inline u8
73 packet_is_cdp (ethernet_header_t * eth)
74 {
75   llc_header_t *llc;
76   snap_header_t *snap;
77
78   llc = (llc_header_t *) (eth + 1);
79   snap = (snap_header_t *) (llc + 1);
80
81   return ((eth->type == htons (ETHERNET_TYPE_CDP)) ||
82           ((llc->src_sap == 0xAA) && (llc->control == 0x03) &&
83            (snap->protocol == htons (0x2000)) &&
84            (snap->oui[0] == 0) && (snap->oui[1] == 0) &&
85            (snap->oui[2] == 0x0C)));
86 }
87
88 static inline u32
89 bond_sw_if_idx_rewrite (vlib_main_t * vm, vlib_node_runtime_t * node,
90                         vlib_buffer_t * b, u32 bond_sw_if_index)
91 {
92   u16 *ethertype_p, ethertype;
93   ethernet_vlan_header_t *vlan;
94   ethernet_header_t *eth = (ethernet_header_t *) vlib_buffer_get_current (b);
95
96   ethertype = clib_mem_unaligned (&eth->type, u16);
97   if (!ethernet_frame_is_tagged (ntohs (ethertype)))
98     {
99       // Let some layer2 packets pass through.
100       if (PREDICT_TRUE ((ethertype != htons (ETHERNET_TYPE_SLOW_PROTOCOLS))
101                         && !packet_is_cdp (eth)
102                         && (ethertype != htons (ETHERNET_TYPE_802_1_LLDP))))
103         {
104           /* Change the physical interface to bond interface */
105           vnet_buffer (b)->sw_if_index[VLIB_RX] = bond_sw_if_index;
106           return 1;
107         }
108     }
109   else
110     {
111       vlan = (void *) (eth + 1);
112       ethertype_p = &vlan->type;
113       ethertype = clib_mem_unaligned (ethertype_p, u16);
114       if (ethertype == ntohs (ETHERNET_TYPE_VLAN))
115         {
116           vlan++;
117           ethertype_p = &vlan->type;
118         }
119       ethertype = clib_mem_unaligned (ethertype_p, u16);
120       if (PREDICT_TRUE ((ethertype != htons (ETHERNET_TYPE_SLOW_PROTOCOLS))
121                         && (ethertype != htons (ETHERNET_TYPE_CDP))
122                         && (ethertype != htons (ETHERNET_TYPE_802_1_LLDP))))
123         {
124           /* Change the physical interface to bond interface */
125           vnet_buffer (b)->sw_if_index[VLIB_RX] = bond_sw_if_index;
126           return 1;
127         }
128     }
129
130   vlib_error_count (vm, node->node_index, BOND_INPUT_ERROR_PASS_THRU, 1);
131   return 0;
132 }
133
134 static inline void
135 bond_update_next (vlib_main_t * vm, vlib_node_runtime_t * node,
136                   u32 * last_slave_sw_if_index, u32 slave_sw_if_index,
137                   u32 packet_count,
138                   u32 * bond_sw_if_index, vlib_buffer_t * b,
139                   u32 * next_index, vlib_error_t * error)
140 {
141   u16 thread_index = vlib_get_thread_index ();
142   slave_if_t *sif;
143   bond_if_t *bif;
144
145   if (PREDICT_TRUE (*last_slave_sw_if_index == slave_sw_if_index))
146     return;
147
148   if (packet_count)
149     vlib_increment_simple_counter (vnet_main.interface_main.sw_if_counters +
150                                    VNET_INTERFACE_COUNTER_RX, thread_index,
151                                    *last_slave_sw_if_index, packet_count);
152
153   *last_slave_sw_if_index = slave_sw_if_index;
154   *next_index = BOND_INPUT_NEXT_DROP;
155
156   sif = bond_get_slave_by_sw_if_index (slave_sw_if_index);
157   ASSERT (sif);
158
159   bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
160
161   ASSERT (bif);
162   ASSERT (vec_len (bif->slaves));
163
164   if (PREDICT_TRUE (bif->admin_up == 0))
165     {
166       *bond_sw_if_index = slave_sw_if_index;
167       *error = node->errors[BOND_INPUT_ERROR_IF_DOWN];
168     }
169
170   *bond_sw_if_index = bif->sw_if_index;
171   *error = 0;
172   vnet_feature_next ( /* not used */ 0, next_index, b);
173 }
174
175 uword CLIB_CPU_OPTIMIZED
176 CLIB_MULTIARCH_FN (bond_input_fn) (vlib_main_t * vm,
177                                    vlib_node_runtime_t * node,
178                                    vlib_frame_t * frame)
179 {
180   u16 thread_index = vlib_get_thread_index ();
181   u32 *from, n_left;
182   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
183   u32 sw_if_indices[VLIB_FRAME_SIZE], *sw_if_index;
184   u16 nexts[VLIB_FRAME_SIZE], *next;
185   u32 last_slave_sw_if_index = ~0;
186   u32 bond_sw_if_index = 0;
187   vlib_error_t error = 0;
188   u32 next_index = 0;
189   u32 cnt = 0;
190
191   /* Vector of buffer / pkt indices we're supposed to process */
192   from = vlib_frame_vector_args (frame);
193
194   /* Number of buffers / pkts */
195   n_left = frame->n_vectors;
196
197   vlib_get_buffers (vm, from, bufs, n_left);
198
199   b = bufs;
200   next = nexts;
201   sw_if_index = sw_if_indices;
202
203   while (n_left >= 4)
204     {
205       u32 x = 0;
206       /* Prefetch next iteration */
207       if (PREDICT_TRUE (n_left >= 16))
208         {
209           CLIB_PREFETCH (vlib_buffer_get_current (b[8]),
210                          CLIB_CACHE_LINE_BYTES, LOAD);
211           CLIB_PREFETCH (vlib_buffer_get_current (b[9]),
212                          CLIB_CACHE_LINE_BYTES, LOAD);
213           CLIB_PREFETCH (vlib_buffer_get_current (b[10]),
214                          CLIB_CACHE_LINE_BYTES, LOAD);
215           CLIB_PREFETCH (vlib_buffer_get_current (b[11]),
216                          CLIB_CACHE_LINE_BYTES, LOAD);
217
218           vlib_prefetch_buffer_header (b[12], LOAD);
219           vlib_prefetch_buffer_header (b[13], LOAD);
220           vlib_prefetch_buffer_header (b[14], LOAD);
221           vlib_prefetch_buffer_header (b[15], LOAD);
222         }
223
224       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
225       sw_if_index[1] = vnet_buffer (b[1])->sw_if_index[VLIB_RX];
226       sw_if_index[2] = vnet_buffer (b[2])->sw_if_index[VLIB_RX];
227       sw_if_index[3] = vnet_buffer (b[3])->sw_if_index[VLIB_RX];
228
229       x |= sw_if_index[0] ^ last_slave_sw_if_index;
230       x |= sw_if_index[1] ^ last_slave_sw_if_index;
231       x |= sw_if_index[2] ^ last_slave_sw_if_index;
232       x |= sw_if_index[3] ^ last_slave_sw_if_index;
233
234       if (PREDICT_TRUE (x == 0))
235         {
236           next[0] = next[1] = next[2] = next[3] = next_index;
237           if (next_index == BOND_INPUT_NEXT_DROP)
238             {
239               b[0]->error = error;
240               b[1]->error = error;
241               b[2]->error = error;
242               b[3]->error = error;
243             }
244           else
245             {
246               cnt +=
247                 bond_sw_if_idx_rewrite (vm, node, b[0], bond_sw_if_index);
248               cnt +=
249                 bond_sw_if_idx_rewrite (vm, node, b[1], bond_sw_if_index);
250               cnt +=
251                 bond_sw_if_idx_rewrite (vm, node, b[2], bond_sw_if_index);
252               cnt +=
253                 bond_sw_if_idx_rewrite (vm, node, b[3], bond_sw_if_index);
254             }
255         }
256       else
257         {
258
259           bond_update_next (vm, node, &last_slave_sw_if_index, sw_if_index[0],
260                             cnt, &bond_sw_if_index, b[0], &next_index,
261                             &error);
262           next[0] = next_index;
263           if (next_index == BOND_INPUT_NEXT_DROP)
264             b[0]->error = error;
265           else
266             cnt += bond_sw_if_idx_rewrite (vm, node, b[0], bond_sw_if_index);
267
268           bond_update_next (vm, node, &last_slave_sw_if_index, sw_if_index[1],
269                             cnt, &bond_sw_if_index, b[1], &next_index,
270                             &error);
271           next[1] = next_index;
272           if (next_index == BOND_INPUT_NEXT_DROP)
273             b[1]->error = error;
274           else
275             cnt += bond_sw_if_idx_rewrite (vm, node, b[1], bond_sw_if_index);
276
277           bond_update_next (vm, node, &last_slave_sw_if_index, sw_if_index[2],
278                             cnt, &bond_sw_if_index, b[2], &next_index,
279                             &error);
280           next[2] = next_index;
281           if (next_index == BOND_INPUT_NEXT_DROP)
282             b[2]->error = error;
283           else
284             cnt += bond_sw_if_idx_rewrite (vm, node, b[2], bond_sw_if_index);
285
286           bond_update_next (vm, node, &last_slave_sw_if_index, sw_if_index[3],
287                             cnt, &bond_sw_if_index, b[3], &next_index,
288                             &error);
289           next[3] = next_index;
290           if (next_index == BOND_INPUT_NEXT_DROP)
291             b[3]->error = error;
292           else
293             cnt += bond_sw_if_idx_rewrite (vm, node, b[3], bond_sw_if_index);
294         }
295
296       VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[0]);
297       VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[1]);
298       VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[2]);
299       VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[3]);
300
301       /* next */
302       n_left -= 4;
303       b += 4;
304       sw_if_index += 4;
305       next += 4;
306     }
307
308   while (n_left)
309     {
310       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
311       bond_update_next (vm, node, &last_slave_sw_if_index, sw_if_index[0],
312                         cnt, &bond_sw_if_index, b[0], &next_index, &error);
313       next[0] = next_index;
314       if (next_index == BOND_INPUT_NEXT_DROP)
315         b[0]->error = error;
316       else
317         bond_sw_if_idx_rewrite (vm, node, b[0], bond_sw_if_index);
318
319       VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[0]);
320
321       /* next */
322       n_left -= 1;
323       b += 1;
324       sw_if_index += 1;
325       next += 1;
326     }
327
328   if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
329     {
330       n_left = frame->n_vectors;        /* number of packets to process */
331       b = bufs;
332       sw_if_index = sw_if_indices;
333       next = nexts;
334       bond_packet_trace_t *t0;
335       uword n_trace = vlib_get_trace_count (vm, node);
336
337       while (n_left && n_trace)
338         {
339           if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
340             {
341               vlib_trace_buffer (vm, node, next[0], b[0],
342                                  0 /* follow_chain */ );
343               vlib_set_trace_count (vm, node, --n_trace);
344               t0 = vlib_add_trace (vm, node, b[0], sizeof (*t0));
345               t0->sw_if_index = sw_if_index[0];
346               clib_memcpy (&t0->ethernet, vlib_buffer_get_current (b[0]),
347                            sizeof (ethernet_header_t));
348               t0->bond_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
349             }
350           /* next */
351           n_left--;
352           b++;
353           sw_if_index++;
354           next++;
355         }
356     }
357
358   /* increase rx counters */
359   vlib_increment_simple_counter
360     (vnet_main.interface_main.sw_if_counters +
361      VNET_INTERFACE_COUNTER_RX, thread_index, bond_sw_if_index, cnt);
362
363   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
364   vlib_node_increment_counter (vm, bond_input_node.index,
365                                BOND_INPUT_ERROR_NONE, frame->n_vectors);
366
367   return frame->n_vectors;
368 }
369
370 #ifndef CLIB_MULTIARCH_VARIANT
371 static clib_error_t *
372 bond_input_init (vlib_main_t * vm)
373 {
374   return 0;
375 }
376
377 /* *INDENT-OFF* */
378 VLIB_REGISTER_NODE (bond_input_node) = {
379   .function = bond_input_fn,
380   .name = "bond-input",
381   .vector_size = sizeof (u32),
382   .format_buffer = format_ethernet_header_with_length,
383   .format_trace = format_bond_input_trace,
384   .type = VLIB_NODE_TYPE_INTERNAL,
385   .n_errors = BOND_INPUT_N_ERROR,
386   .error_strings = bond_input_error_strings,
387   .n_next_nodes = BOND_INPUT_N_NEXT,
388   .next_nodes =
389   {
390     [BOND_INPUT_NEXT_DROP] = "error-drop"
391   }
392 };
393
394 #if __x86_64__
395 vlib_node_function_t __clib_weak bond_input_fn_avx512;
396 vlib_node_function_t __clib_weak bond_input_fn_avx2;
397 static void __clib_constructor
398 bond_input_multiarch_select (void)
399 {
400   if (bond_input_fn_avx512 && clib_cpu_supports_avx512f ())
401     bond_input_node.function = bond_input_fn_avx512;
402   else if (bond_input_fn_avx2 && clib_cpu_supports_avx2 ())
403     bond_input_node.function = bond_input_fn_avx2;
404 }
405 #endif
406
407
408 VLIB_INIT_FUNCTION (bond_input_init);
409
410 VNET_FEATURE_INIT (bond_input, static) =
411 {
412   .arc_name = "device-input",
413   .node_name = "bond-input",
414   .runs_before = VNET_FEATURES ("ethernet-input"),
415 };
416 /* *INDENT-ON* */
417
418 static clib_error_t *
419 bond_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
420 {
421   bond_main_t *bm = &bond_main;
422   slave_if_t *sif;
423   vlib_main_t *vm = bm->vlib_main;
424
425   sif = bond_get_slave_by_sw_if_index (sw_if_index);
426   if (sif)
427     {
428       sif->port_enabled = flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP;
429       if (sif->port_enabled == 0)
430         {
431           if (sif->lacp_enabled == 0)
432             {
433               bond_disable_collecting_distributing (vm, sif);
434             }
435         }
436       else
437         {
438           if (sif->lacp_enabled == 0)
439             {
440               bond_enable_collecting_distributing (vm, sif);
441             }
442         }
443     }
444
445   return 0;
446 }
447
448 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (bond_sw_interface_up_down);
449
450 static clib_error_t *
451 bond_hw_interface_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
452 {
453   bond_main_t *bm = &bond_main;
454   slave_if_t *sif;
455   vnet_sw_interface_t *sw;
456   vlib_main_t *vm = bm->vlib_main;
457
458   sw = vnet_get_hw_sw_interface (vnm, hw_if_index);
459   sif = bond_get_slave_by_sw_if_index (sw->sw_if_index);
460   if (sif)
461     {
462       if (!(flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
463         {
464           if (sif->lacp_enabled == 0)
465             {
466               bond_disable_collecting_distributing (vm, sif);
467             }
468         }
469       else
470         {
471           if (sif->lacp_enabled == 0)
472             {
473               bond_enable_collecting_distributing (vm, sif);
474             }
475         }
476     }
477
478   return 0;
479 }
480
481 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (bond_hw_interface_up_down);
482 #endif
483
484 /*
485  * fd.io coding-style-patch-verification: ON
486  *
487  * Local Variables:
488  * eval: (c-set-style "gnu")
489  * End:
490  */