bonding: support custom interface IDs
[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 static char *bond_input_error_strings[] = {
40 #define _(n,s) s,
41   foreach_bond_input_error
42 #undef _
43 };
44
45 static u8 *
46 format_bond_input_trace (u8 * s, va_list * args)
47 {
48   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
49   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
50   bond_packet_trace_t *t = va_arg (*args, bond_packet_trace_t *);
51
52   s = format (s, "src %U, dst %U, %U -> %U",
53               format_ethernet_address, t->ethernet.src_address,
54               format_ethernet_address, t->ethernet.dst_address,
55               format_vnet_sw_if_index_name, vnet_get_main (),
56               t->sw_if_index,
57               format_vnet_sw_if_index_name, vnet_get_main (),
58               t->bond_sw_if_index);
59
60   return s;
61 }
62
63 typedef enum
64 {
65   BOND_INPUT_NEXT_DROP,
66   BOND_INPUT_N_NEXT,
67 } bond_output_next_t;
68
69 static_always_inline u8
70 packet_is_cdp (ethernet_header_t * eth)
71 {
72   llc_header_t *llc;
73   snap_header_t *snap;
74
75   llc = (llc_header_t *) (eth + 1);
76   snap = (snap_header_t *) (llc + 1);
77
78   return ((eth->type == htons (ETHERNET_TYPE_CDP)) ||
79           ((llc->src_sap == 0xAA) && (llc->control == 0x03) &&
80            (snap->protocol == htons (0x2000)) &&
81            (snap->oui[0] == 0) && (snap->oui[1] == 0) &&
82            (snap->oui[2] == 0x0C)));
83 }
84
85 static inline u32
86 bond_sw_if_idx_rewrite (vlib_main_t * vm, vlib_node_runtime_t * node,
87                         vlib_buffer_t * b, u32 bond_sw_if_index)
88 {
89   u16 *ethertype_p, ethertype;
90   ethernet_vlan_header_t *vlan;
91   ethernet_header_t *eth = (ethernet_header_t *) vlib_buffer_get_current (b);
92
93   ethertype = clib_mem_unaligned (&eth->type, u16);
94   if (!ethernet_frame_is_tagged (ntohs (ethertype)))
95     {
96       // Let some layer2 packets pass through.
97       if (PREDICT_TRUE ((ethertype != htons (ETHERNET_TYPE_SLOW_PROTOCOLS))
98                         && !packet_is_cdp (eth)
99                         && (ethertype != htons (ETHERNET_TYPE_802_1_LLDP))))
100         {
101           /* Change the physical interface to bond interface */
102           vnet_buffer (b)->sw_if_index[VLIB_RX] = bond_sw_if_index;
103           return 1;
104         }
105     }
106   else
107     {
108       vlan = (void *) (eth + 1);
109       ethertype_p = &vlan->type;
110       ethertype = clib_mem_unaligned (ethertype_p, u16);
111       if (ethertype == ntohs (ETHERNET_TYPE_VLAN))
112         {
113           vlan++;
114           ethertype_p = &vlan->type;
115         }
116       ethertype = clib_mem_unaligned (ethertype_p, u16);
117       if (PREDICT_TRUE ((ethertype != htons (ETHERNET_TYPE_SLOW_PROTOCOLS))
118                         && (ethertype != htons (ETHERNET_TYPE_CDP))
119                         && (ethertype != htons (ETHERNET_TYPE_802_1_LLDP))))
120         {
121           /* Change the physical interface to bond interface */
122           vnet_buffer (b)->sw_if_index[VLIB_RX] = bond_sw_if_index;
123           return 1;
124         }
125     }
126
127   vlib_error_count (vm, node->node_index, BOND_INPUT_ERROR_PASS_THRU, 1);
128   return 0;
129 }
130
131 static inline void
132 bond_update_next (vlib_main_t * vm, vlib_node_runtime_t * node,
133                   u32 * last_slave_sw_if_index, u32 slave_sw_if_index,
134                   u32 packet_count,
135                   u32 * bond_sw_if_index, vlib_buffer_t * b,
136                   u32 * next_index, vlib_error_t * error)
137 {
138   u16 thread_index = vm->thread_index;
139   slave_if_t *sif;
140   bond_if_t *bif;
141
142   if (PREDICT_TRUE (*last_slave_sw_if_index == slave_sw_if_index))
143     return;
144
145   if (packet_count)
146     vlib_increment_simple_counter (vnet_main.interface_main.sw_if_counters +
147                                    VNET_INTERFACE_COUNTER_RX, thread_index,
148                                    *last_slave_sw_if_index, packet_count);
149
150   *last_slave_sw_if_index = slave_sw_if_index;
151   *next_index = BOND_INPUT_NEXT_DROP;
152
153   sif = bond_get_slave_by_sw_if_index (slave_sw_if_index);
154   ASSERT (sif);
155
156   bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
157
158   ASSERT (bif);
159   ASSERT (vec_len (bif->slaves));
160
161   if (PREDICT_TRUE (bif->admin_up == 0))
162     {
163       *bond_sw_if_index = slave_sw_if_index;
164       *error = node->errors[BOND_INPUT_ERROR_IF_DOWN];
165     }
166
167   *bond_sw_if_index = bif->sw_if_index;
168   *error = 0;
169   vnet_feature_next (next_index, b);
170 }
171
172 VLIB_NODE_FN (bond_input_node) (vlib_main_t * vm,
173                                 vlib_node_runtime_t * node,
174                                 vlib_frame_t * frame)
175 {
176   u16 thread_index = vm->thread_index;
177   u32 *from, n_left;
178   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
179   u32 sw_if_indices[VLIB_FRAME_SIZE], *sw_if_index;
180   u16 nexts[VLIB_FRAME_SIZE], *next;
181   u32 last_slave_sw_if_index = ~0;
182   u32 bond_sw_if_index = 0;
183   vlib_error_t error = 0;
184   u32 next_index = 0;
185   u32 cnt = 0;
186
187   /* Vector of buffer / pkt indices we're supposed to process */
188   from = vlib_frame_vector_args (frame);
189
190   /* Number of buffers / pkts */
191   n_left = frame->n_vectors;
192
193   vlib_get_buffers (vm, from, bufs, n_left);
194
195   b = bufs;
196   next = nexts;
197   sw_if_index = sw_if_indices;
198
199   while (n_left >= 4)
200     {
201       u32 x = 0;
202       /* Prefetch next iteration */
203       if (PREDICT_TRUE (n_left >= 16))
204         {
205           vlib_prefetch_buffer_data (b[8], LOAD);
206           vlib_prefetch_buffer_data (b[9], LOAD);
207           vlib_prefetch_buffer_data (b[10], LOAD);
208           vlib_prefetch_buffer_data (b[11], LOAD);
209
210           vlib_prefetch_buffer_header (b[12], LOAD);
211           vlib_prefetch_buffer_header (b[13], LOAD);
212           vlib_prefetch_buffer_header (b[14], LOAD);
213           vlib_prefetch_buffer_header (b[15], LOAD);
214         }
215
216       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
217       sw_if_index[1] = vnet_buffer (b[1])->sw_if_index[VLIB_RX];
218       sw_if_index[2] = vnet_buffer (b[2])->sw_if_index[VLIB_RX];
219       sw_if_index[3] = vnet_buffer (b[3])->sw_if_index[VLIB_RX];
220
221       x |= sw_if_index[0] ^ last_slave_sw_if_index;
222       x |= sw_if_index[1] ^ last_slave_sw_if_index;
223       x |= sw_if_index[2] ^ last_slave_sw_if_index;
224       x |= sw_if_index[3] ^ last_slave_sw_if_index;
225
226       if (PREDICT_TRUE (x == 0))
227         {
228           next[0] = next[1] = next[2] = next[3] = next_index;
229           if (next_index == BOND_INPUT_NEXT_DROP)
230             {
231               b[0]->error = error;
232               b[1]->error = error;
233               b[2]->error = error;
234               b[3]->error = error;
235             }
236           else
237             {
238               cnt +=
239                 bond_sw_if_idx_rewrite (vm, node, b[0], bond_sw_if_index);
240               cnt +=
241                 bond_sw_if_idx_rewrite (vm, node, b[1], bond_sw_if_index);
242               cnt +=
243                 bond_sw_if_idx_rewrite (vm, node, b[2], bond_sw_if_index);
244               cnt +=
245                 bond_sw_if_idx_rewrite (vm, node, b[3], bond_sw_if_index);
246             }
247         }
248       else
249         {
250
251           bond_update_next (vm, node, &last_slave_sw_if_index, sw_if_index[0],
252                             cnt, &bond_sw_if_index, b[0], &next_index,
253                             &error);
254           next[0] = next_index;
255           if (next_index == BOND_INPUT_NEXT_DROP)
256             b[0]->error = error;
257           else
258             cnt += bond_sw_if_idx_rewrite (vm, node, b[0], bond_sw_if_index);
259
260           bond_update_next (vm, node, &last_slave_sw_if_index, sw_if_index[1],
261                             cnt, &bond_sw_if_index, b[1], &next_index,
262                             &error);
263           next[1] = next_index;
264           if (next_index == BOND_INPUT_NEXT_DROP)
265             b[1]->error = error;
266           else
267             cnt += bond_sw_if_idx_rewrite (vm, node, b[1], bond_sw_if_index);
268
269           bond_update_next (vm, node, &last_slave_sw_if_index, sw_if_index[2],
270                             cnt, &bond_sw_if_index, b[2], &next_index,
271                             &error);
272           next[2] = next_index;
273           if (next_index == BOND_INPUT_NEXT_DROP)
274             b[2]->error = error;
275           else
276             cnt += bond_sw_if_idx_rewrite (vm, node, b[2], bond_sw_if_index);
277
278           bond_update_next (vm, node, &last_slave_sw_if_index, sw_if_index[3],
279                             cnt, &bond_sw_if_index, b[3], &next_index,
280                             &error);
281           next[3] = next_index;
282           if (next_index == BOND_INPUT_NEXT_DROP)
283             b[3]->error = error;
284           else
285             cnt += bond_sw_if_idx_rewrite (vm, node, b[3], bond_sw_if_index);
286         }
287
288       VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[0]);
289       VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[1]);
290       VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[2]);
291       VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[3]);
292
293       /* next */
294       n_left -= 4;
295       b += 4;
296       sw_if_index += 4;
297       next += 4;
298     }
299
300   while (n_left)
301     {
302       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
303       bond_update_next (vm, node, &last_slave_sw_if_index, sw_if_index[0],
304                         cnt, &bond_sw_if_index, b[0], &next_index, &error);
305       next[0] = next_index;
306       if (next_index == BOND_INPUT_NEXT_DROP)
307         b[0]->error = error;
308       else
309         bond_sw_if_idx_rewrite (vm, node, b[0], bond_sw_if_index);
310
311       VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[0]);
312
313       /* next */
314       n_left -= 1;
315       b += 1;
316       sw_if_index += 1;
317       next += 1;
318     }
319
320   if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
321     {
322       n_left = frame->n_vectors;        /* number of packets to process */
323       b = bufs;
324       sw_if_index = sw_if_indices;
325       bond_packet_trace_t *t0;
326
327       while (n_left)
328         {
329           if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
330             {
331               t0 = vlib_add_trace (vm, node, b[0], sizeof (*t0));
332               t0->sw_if_index = sw_if_index[0];
333               clib_memcpy_fast (&t0->ethernet, vlib_buffer_get_current (b[0]),
334                                 sizeof (ethernet_header_t));
335               t0->bond_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
336             }
337           /* next */
338           n_left--;
339           b++;
340           sw_if_index++;
341         }
342     }
343
344   /* increase rx counters */
345   vlib_increment_simple_counter
346     (vnet_main.interface_main.sw_if_counters +
347      VNET_INTERFACE_COUNTER_RX, thread_index, bond_sw_if_index, cnt);
348
349   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
350   vlib_node_increment_counter (vm, bond_input_node.index,
351                                BOND_INPUT_ERROR_NONE, frame->n_vectors);
352
353   return frame->n_vectors;
354 }
355
356 static clib_error_t *
357 bond_input_init (vlib_main_t * vm)
358 {
359   return 0;
360 }
361
362 /* *INDENT-OFF* */
363 VLIB_REGISTER_NODE (bond_input_node) = {
364   .name = "bond-input",
365   .vector_size = sizeof (u32),
366   .format_buffer = format_ethernet_header_with_length,
367   .format_trace = format_bond_input_trace,
368   .type = VLIB_NODE_TYPE_INTERNAL,
369   .n_errors = BOND_INPUT_N_ERROR,
370   .error_strings = bond_input_error_strings,
371   .n_next_nodes = BOND_INPUT_N_NEXT,
372   .next_nodes =
373   {
374     [BOND_INPUT_NEXT_DROP] = "error-drop"
375   }
376 };
377
378 VLIB_INIT_FUNCTION (bond_input_init);
379
380 VNET_FEATURE_INIT (bond_input, static) =
381 {
382   .arc_name = "device-input",
383   .node_name = "bond-input",
384   .runs_before = VNET_FEATURES ("ethernet-input"),
385 };
386 /* *INDENT-ON* */
387
388 static clib_error_t *
389 bond_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
390 {
391   bond_main_t *bm = &bond_main;
392   slave_if_t *sif;
393   vlib_main_t *vm = bm->vlib_main;
394
395   sif = bond_get_slave_by_sw_if_index (sw_if_index);
396   if (sif)
397     {
398       sif->port_enabled = flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP;
399       if (sif->port_enabled == 0)
400         {
401           if (sif->lacp_enabled == 0)
402             {
403               bond_disable_collecting_distributing (vm, sif);
404             }
405         }
406       else
407         {
408           if (sif->lacp_enabled == 0)
409             {
410               bond_enable_collecting_distributing (vm, sif);
411             }
412         }
413     }
414
415   return 0;
416 }
417
418 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (bond_sw_interface_up_down);
419
420 static clib_error_t *
421 bond_hw_interface_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
422 {
423   bond_main_t *bm = &bond_main;
424   slave_if_t *sif;
425   vnet_sw_interface_t *sw;
426   vlib_main_t *vm = bm->vlib_main;
427
428   sw = vnet_get_hw_sw_interface (vnm, hw_if_index);
429   sif = bond_get_slave_by_sw_if_index (sw->sw_if_index);
430   if (sif)
431     {
432       if (!(flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
433         {
434           if (sif->lacp_enabled == 0)
435             {
436               bond_disable_collecting_distributing (vm, sif);
437             }
438         }
439       else
440         {
441           if (sif->lacp_enabled == 0)
442             {
443               bond_enable_collecting_distributing (vm, sif);
444             }
445         }
446     }
447
448   return 0;
449 }
450
451 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (bond_hw_interface_up_down);
452
453 /*
454  * fd.io coding-style-patch-verification: ON
455  *
456  * Local Variables:
457  * eval: (c-set-style "gnu")
458  * End:
459  */