Line data Source code
1 : /*
2 : * Copyright (c) 2017 Cisco and/or its affiliates.
3 : * 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 : #include <stdint.h>
17 : #include <vlib/vlib.h>
18 : #include <vlib/unix/unix.h>
19 : #include <vnet/plugin/plugin.h>
20 : #include <vpp/app/version.h>
21 : #include <vppinfra/hash.h>
22 : #include <vnet/bonding/node.h>
23 : #include <lacp/node.h>
24 : #include <vlib/stats/stats.h>
25 :
26 : lacp_main_t lacp_main;
27 :
28 : /*
29 : * Generate lacp pdu
30 : */
31 : static void
32 28 : lacp_fill_pdu (lacp_pdu_t * lacpdu, member_if_t * mif)
33 : {
34 : /* Actor TLV */
35 28 : lacpdu->actor.port_info = mif->actor;
36 :
37 : /* Partner TLV */
38 28 : lacpdu->partner.port_info = mif->partner;
39 28 : }
40 :
41 : /*
42 : * send a lacp pkt on an ethernet interface
43 : */
44 : static void
45 28 : lacp_send_ethernet_lacp_pdu (vlib_main_t * vm, member_if_t * mif)
46 : {
47 28 : lacp_main_t *lm = &lacp_main;
48 : u32 *to_next;
49 : ethernet_lacp_pdu_t *h0;
50 : vnet_hw_interface_t *hw;
51 : u32 bi0;
52 : vlib_buffer_t *b0;
53 : vlib_frame_t *f;
54 28 : vnet_main_t *vnm = lm->vnet_main;
55 :
56 : /*
57 : * see lacp_periodic_init() to understand what's already painted
58 : * into the buffer by the packet template mechanism
59 : */
60 28 : h0 = vlib_packet_template_get_packet
61 28 : (vm, &lm->packet_templates[mif->packet_template_index], &bi0);
62 :
63 28 : if (!h0)
64 0 : return;
65 :
66 : /* Add the interface's ethernet source address */
67 28 : hw = vnet_get_sup_hw_interface (vnm, mif->sw_if_index);
68 :
69 28 : clib_memcpy (h0->ethernet.src_address, hw->hw_address,
70 : vec_len (hw->hw_address));
71 :
72 28 : lacp_fill_pdu (&h0->lacp, mif);
73 :
74 : /* Set the outbound packet length */
75 28 : b0 = vlib_get_buffer (vm, bi0);
76 28 : b0->current_length = sizeof (ethernet_lacp_pdu_t);
77 28 : b0->current_data = 0;
78 28 : b0->total_length_not_including_first_buffer = 0;
79 :
80 : /* And the outbound interface */
81 28 : vnet_buffer (b0)->sw_if_index[VLIB_TX] = hw->sw_if_index;
82 :
83 : /* And output the packet on the correct interface */
84 28 : f = vlib_get_frame_to_node (vm, hw->output_node_index);
85 :
86 28 : to_next = vlib_frame_vector_args (f);
87 28 : to_next[0] = bi0;
88 28 : f->n_vectors = 1;
89 :
90 28 : vlib_put_frame_to_node (vm, hw->output_node_index, f);
91 :
92 28 : mif->last_lacpdu_sent_time = vlib_time_now (vm);
93 28 : mif->pdu_sent++;
94 : }
95 :
96 : /*
97 : * Decide which lacp packet template to use
98 : */
99 : static int
100 11 : lacp_pick_packet_template (member_if_t * mif)
101 : {
102 11 : mif->packet_template_index = LACP_PACKET_TEMPLATE_ETHERNET;
103 :
104 11 : return 0;
105 : }
106 :
107 : void
108 28 : lacp_send_lacp_pdu (vlib_main_t * vm, member_if_t * mif)
109 : {
110 28 : if ((mif->mode != BOND_MODE_LACP) || (mif->port_enabled == 0))
111 : {
112 0 : lacp_stop_timer (&mif->periodic_timer);
113 0 : return;
114 : }
115 :
116 28 : if (mif->packet_template_index == (u8) ~ 0)
117 : {
118 : /* If we don't know how to talk to this peer, don't try again */
119 11 : if (lacp_pick_packet_template (mif))
120 : {
121 0 : lacp_stop_timer (&mif->periodic_timer);
122 0 : return;
123 : }
124 : }
125 :
126 28 : switch (mif->packet_template_index)
127 : {
128 28 : case LACP_PACKET_TEMPLATE_ETHERNET:
129 28 : lacp_send_ethernet_lacp_pdu (vm, mif);
130 28 : break;
131 :
132 0 : default:
133 0 : ASSERT (0);
134 : }
135 : }
136 :
137 : void
138 15 : lacp_periodic (vlib_main_t * vm)
139 : {
140 15 : bond_main_t *bm = &bond_main;
141 : member_if_t *mif;
142 : bond_if_t *bif;
143 : u8 actor_state, partner_state;
144 :
145 : /* *INDENT-OFF* */
146 75 : pool_foreach (mif, bm->neighbors)
147 : {
148 60 : if (mif->port_enabled == 0)
149 0 : continue;
150 :
151 60 : actor_state = mif->actor.state;
152 60 : partner_state = mif->partner.state;
153 120 : if (lacp_timer_is_running (mif->current_while_timer) &&
154 60 : lacp_timer_is_expired (vm, mif->current_while_timer))
155 : {
156 0 : lacp_machine_dispatch (&lacp_rx_machine, vm, mif,
157 : LACP_RX_EVENT_TIMER_EXPIRED, &mif->rx_state);
158 : }
159 :
160 120 : if (lacp_timer_is_running (mif->periodic_timer) &&
161 60 : lacp_timer_is_expired (vm, mif->periodic_timer))
162 : {
163 26 : lacp_machine_dispatch (&lacp_ptx_machine, vm, mif,
164 : LACP_PTX_EVENT_TIMER_EXPIRED, &mif->ptx_state);
165 : }
166 99 : if (lacp_timer_is_running (mif->wait_while_timer) &&
167 39 : lacp_timer_is_expired (vm, mif->wait_while_timer))
168 : {
169 2 : mif->ready_n = 1;
170 2 : lacp_stop_timer (&mif->wait_while_timer);
171 2 : lacp_selection_logic (vm, mif);
172 : }
173 60 : if (actor_state != mif->actor.state)
174 : {
175 0 : bif = bond_get_bond_if_by_dev_instance (mif->bif_dev_instance);
176 0 : vlib_stats_set_gauge (
177 0 : bm->stats[bif->sw_if_index][mif->sw_if_index].actor_state,
178 0 : mif->actor.state);
179 : }
180 60 : if (partner_state != mif->partner.state)
181 : {
182 0 : bif = bond_get_bond_if_by_dev_instance (mif->bif_dev_instance);
183 0 : vlib_stats_set_gauge (
184 0 : bm->stats[bif->sw_if_index][mif->sw_if_index].partner_state,
185 0 : mif->partner.state);
186 : }
187 : }
188 : /* *INDENT-ON* */
189 15 : }
190 :
191 : static void
192 22 : lacp_interface_enable_disable (vlib_main_t * vm, bond_if_t * bif,
193 : member_if_t * mif, u8 enable)
194 : {
195 22 : lacp_main_t *lm = &lacp_main;
196 : uword port_number;
197 :
198 22 : if (enable)
199 : {
200 11 : lacp_create_periodic_process ();
201 11 : port_number = clib_bitmap_first_clear (bif->port_number_bitmap);
202 11 : bif->port_number_bitmap = clib_bitmap_set (bif->port_number_bitmap,
203 : port_number, 1);
204 : // bitmap starts at 0. Our port number starts at 1.
205 11 : lacp_init_neighbor (mif, bif->hw_address, port_number + 1, mif->group);
206 11 : lacp_init_state_machines (vm, mif);
207 11 : lm->lacp_int++;
208 11 : if (lm->lacp_int == 1)
209 : {
210 4 : vlib_process_signal_event (vm, lm->lacp_process_node_index,
211 : LACP_PROCESS_EVENT_START, 0);
212 : }
213 : }
214 : else
215 : {
216 11 : ASSERT (lm->lacp_int >= 1);
217 11 : if (lm->lacp_int == 0)
218 : {
219 : /* *INDENT-OFF* */
220 : ELOG_TYPE_DECLARE (e) =
221 : {
222 : .format = "lacp-int-en-dis: BUG lacp_int == 0",
223 : };
224 : /* *INDENT-ON* */
225 0 : ELOG_DATA (&vlib_global_main.elog_main, e);
226 : }
227 : else
228 : {
229 11 : lm->lacp_int--;
230 11 : if (lm->lacp_int == 0)
231 4 : vlib_process_signal_event (vm, lm->lacp_process_node_index,
232 : LACP_PROCESS_EVENT_STOP, 0);
233 : }
234 : }
235 22 : }
236 :
237 : static clib_error_t *
238 575 : lacp_periodic_init (vlib_main_t * vm)
239 : {
240 575 : lacp_main_t *lm = &lacp_main;
241 : ethernet_lacp_pdu_t h;
242 : ethernet_marker_pdu_t m;
243 575 : u8 dst[] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 };
244 :
245 : /* initialize binary API */
246 575 : lacp_plugin_api_hookup (vm);
247 :
248 : /* Create the ethernet lacp packet template */
249 :
250 575 : clib_memset (&h, 0, sizeof (h));
251 :
252 575 : memcpy (h.ethernet.dst_address, dst, sizeof (h.ethernet.dst_address));
253 :
254 : /* leave src address blank (fill in at send time) */
255 :
256 575 : h.ethernet.type = htons (ETHERNET_TYPE_SLOW_PROTOCOLS);
257 :
258 575 : h.lacp.subtype = LACP_SUBTYPE;
259 575 : h.lacp.version_number = LACP_ACTOR_LACP_VERSION;
260 :
261 : /* Actor TLV */
262 575 : h.lacp.actor.tlv_type = LACP_ACTOR_INFORMATION;
263 575 : h.lacp.actor.tlv_length = sizeof (lacp_actor_partner_t);
264 :
265 : /* Partner TLV */
266 575 : h.lacp.partner.tlv_type = LACP_PARTNER_INFORMATION;
267 575 : h.lacp.partner.tlv_length = sizeof (lacp_actor_partner_t);
268 :
269 : /* Collector TLV */
270 575 : h.lacp.collector.tlv_type = LACP_COLLECTOR_INFORMATION;
271 575 : h.lacp.collector.tlv_length = sizeof (lacp_collector_t);
272 575 : h.lacp.collector.max_delay = 0;
273 :
274 : /* Terminator TLV */
275 575 : h.lacp.terminator.tlv_type = LACP_TERMINATOR_INFORMATION;
276 575 : h.lacp.terminator.tlv_length = 0;
277 :
278 575 : vlib_packet_template_init
279 : (vm, &lm->packet_templates[LACP_PACKET_TEMPLATE_ETHERNET],
280 : /* data */ &h,
281 : sizeof (h),
282 : /* alloc chunk size */ 8,
283 : "lacp-ethernet");
284 :
285 : /* Create the ethernet marker protocol packet template */
286 :
287 575 : clib_memset (&m, 0, sizeof (m));
288 :
289 575 : memcpy (m.ethernet.dst_address, dst, sizeof (m.ethernet.dst_address));
290 :
291 : /* leave src address blank (fill in at send time) */
292 :
293 575 : m.ethernet.type = htons (ETHERNET_TYPE_SLOW_PROTOCOLS);
294 :
295 575 : m.marker.subtype = MARKER_SUBTYPE;
296 575 : m.marker.version_number = MARKER_PROTOCOL_VERSION;
297 :
298 575 : m.marker.marker_info.tlv_length = sizeof (marker_information_t);
299 :
300 : /* Terminator TLV */
301 575 : m.marker.terminator.tlv_type = MARKER_TERMINATOR_INFORMATION;
302 575 : m.marker.terminator.tlv_length = 0;
303 :
304 575 : vlib_packet_template_init
305 : (vm, &lm->marker_packet_templates[MARKER_PACKET_TEMPLATE_ETHERNET],
306 : /* data */ &m,
307 : sizeof (m),
308 : /* alloc chunk size */ 8,
309 : "marker-ethernet");
310 :
311 575 : bond_register_callback (lacp_interface_enable_disable);
312 :
313 575 : return 0;
314 : }
315 :
316 : int
317 268 : lacp_machine_dispatch (lacp_machine_t * machine, vlib_main_t * vm,
318 : member_if_t * mif, int event, int *state)
319 : {
320 : lacp_fsm_state_t *transition;
321 268 : int rc = 0;
322 :
323 268 : transition = &machine->tables[*state].state_table[event];
324 268 : LACP_DBG2 (mif, event, *state, machine, transition);
325 268 : *state = transition->next_state;
326 268 : if (transition->action)
327 221 : rc = (*transition->action) ((void *) vm, (void *) mif);
328 :
329 268 : return rc;
330 : }
331 :
332 : void
333 11 : lacp_init_neighbor (member_if_t * mif, u8 * hw_address, u16 port_number,
334 : u32 group)
335 : {
336 11 : lacp_stop_timer (&mif->wait_while_timer);
337 11 : lacp_stop_timer (&mif->current_while_timer);
338 11 : lacp_stop_timer (&mif->actor_churn_timer);
339 11 : lacp_stop_timer (&mif->partner_churn_timer);
340 11 : lacp_stop_timer (&mif->periodic_timer);
341 11 : lacp_stop_timer (&mif->last_lacpdu_sent_time);
342 11 : lacp_stop_timer (&mif->last_lacpdu_recd_time);
343 11 : lacp_stop_timer (&mif->last_marker_pdu_sent_time);
344 11 : lacp_stop_timer (&mif->last_marker_pdu_recd_time);
345 11 : mif->lacp_enabled = 1;
346 11 : mif->loopback_port = 0;
347 11 : mif->ready = 0;
348 11 : mif->ready_n = 0;
349 11 : mif->port_moved = 0;
350 11 : mif->ntt = 0;
351 11 : mif->selected = LACP_PORT_UNSELECTED;
352 11 : mif->actor.state = LACP_STATE_AGGREGATION;
353 11 : if (mif->ttl_in_seconds == LACP_SHORT_TIMOUT_TIME)
354 11 : mif->actor.state |= LACP_STATE_LACP_TIMEOUT;
355 11 : if (mif->is_passive == 0)
356 11 : mif->actor.state |= LACP_STATE_LACP_ACTIVITY;
357 11 : clib_memcpy (mif->actor.system, hw_address, 6);
358 11 : mif->actor.system_priority = htons (LACP_DEFAULT_SYSTEM_PRIORITY);
359 11 : mif->actor.key = htons (group);
360 11 : mif->actor.port_number = htons (port_number);
361 11 : mif->actor.port_priority = htons (LACP_DEFAULT_PORT_PRIORITY);
362 :
363 11 : mif->partner.system_priority = htons (LACP_DEFAULT_SYSTEM_PRIORITY);
364 11 : mif->partner.key = htons (group);
365 11 : mif->partner.port_number = htons (port_number);
366 11 : mif->partner.port_priority = htons (LACP_DEFAULT_PORT_PRIORITY);
367 11 : mif->partner.state = 0;
368 :
369 11 : mif->actor_admin = mif->actor;
370 11 : mif->partner_admin = mif->partner;
371 11 : }
372 :
373 : void
374 11 : lacp_init_state_machines (vlib_main_t * vm, member_if_t * mif)
375 : {
376 11 : bond_main_t *bm = &bond_main;
377 11 : bond_if_t *bif = bond_get_bond_if_by_dev_instance (mif->bif_dev_instance);
378 :
379 11 : lacp_init_tx_machine (vm, mif);
380 11 : lacp_init_mux_machine (vm, mif);
381 11 : lacp_init_ptx_machine (vm, mif);
382 11 : lacp_init_rx_machine (vm, mif);
383 11 : vlib_stats_set_gauge (
384 11 : bm->stats[bif->sw_if_index][mif->sw_if_index].actor_state,
385 11 : mif->actor.state);
386 11 : vlib_stats_set_gauge (
387 11 : bm->stats[bif->sw_if_index][mif->sw_if_index].partner_state,
388 11 : mif->partner.state);
389 11 : }
390 :
391 1151 : VLIB_INIT_FUNCTION (lacp_periodic_init);
392 :
393 : static clib_error_t *
394 13514 : lacp_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
395 : {
396 13514 : lacp_main_t *lm = &lacp_main;
397 : member_if_t *mif;
398 13514 : vlib_main_t *vm = lm->vlib_main;
399 :
400 13514 : mif = bond_get_member_by_sw_if_index (sw_if_index);
401 13514 : if (mif)
402 : {
403 0 : if (mif->lacp_enabled == 0)
404 0 : return 0;
405 :
406 : /* port_enabled is both admin up and hw link up */
407 0 : mif->port_enabled = ((flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) &&
408 0 : vnet_sw_interface_is_link_up (vnm, sw_if_index));
409 0 : if (mif->port_enabled == 0)
410 : {
411 0 : lacp_init_neighbor (mif, mif->actor_admin.system,
412 0 : ntohs (mif->actor_admin.port_number),
413 0 : ntohs (mif->actor_admin.key));
414 0 : lacp_init_state_machines (vm, mif);
415 : }
416 : }
417 :
418 13514 : return 0;
419 : }
420 :
421 1729 : VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (lacp_sw_interface_up_down);
422 :
423 : static clib_error_t *
424 13336 : lacp_hw_interface_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
425 : {
426 13336 : lacp_main_t *lm = &lacp_main;
427 : member_if_t *mif;
428 : vnet_sw_interface_t *sw;
429 13336 : vlib_main_t *vm = lm->vlib_main;
430 :
431 13336 : sw = vnet_get_hw_sw_interface (vnm, hw_if_index);
432 13336 : mif = bond_get_member_by_sw_if_index (sw->sw_if_index);
433 13336 : if (mif)
434 : {
435 2 : if (mif->lacp_enabled == 0)
436 0 : return 0;
437 :
438 : /* port_enabled is both admin up and hw link up */
439 4 : mif->port_enabled = ((flags & VNET_HW_INTERFACE_FLAG_LINK_UP) &&
440 2 : vnet_sw_interface_is_admin_up (vnm,
441 : sw->sw_if_index));
442 2 : if (mif->port_enabled == 0)
443 : {
444 0 : lacp_init_neighbor (mif, mif->actor_admin.system,
445 0 : ntohs (mif->actor_admin.port_number),
446 0 : ntohs (mif->actor_admin.key));
447 0 : lacp_init_state_machines (vm, mif);
448 : }
449 : }
450 :
451 13336 : return 0;
452 : }
453 :
454 1729 : VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (lacp_hw_interface_up_down);
455 :
456 : /* *INDENT-OFF* */
457 : VLIB_PLUGIN_REGISTER () = {
458 : .version = VPP_BUILD_VER,
459 : .description = "Link Aggregation Control Protocol (LACP)",
460 : };
461 : /* *INDENT-ON* */
462 :
463 : /*
464 : * fd.io coding-style-patch-verification: ON
465 : *
466 : * Local Variables:
467 : * eval: (c-set-style "gnu")
468 : * End:
469 : */
|