/* * Copyright (c) 2017 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define _GNU_SOURCE #include #include /* * LACP State = INITIALIZE */ static lacp_fsm_state_t lacp_rx_state_initialize[] = { {LACP_ACTION_INITIALIZE, LACP_RX_STATE_PORT_DISABLED}, // event 0 BEGIN {LACP_ACTION_INITIALIZE, LACP_RX_STATE_PORT_DISABLED}, // event 1 PORT_DISABLED {LACP_ACTION_INITIALIZE, LACP_RX_STATE_PORT_DISABLED}, // event 2 PORT_MOVED {LACP_NOACTION, LACP_RX_STATE_INITIALIZE}, // event 3 LACP_ENABLED {LACP_NOACTION, LACP_RX_STATE_INITIALIZE}, // event 4 LACP_DISABLED {LACP_NOACTION, LACP_RX_STATE_INITIALIZE}, // event 5 PDU_RECEIVED {LACP_NOACTION, LACP_RX_STATE_INITIALIZE}, // event 6 TIMER_EXPIRED }; /* * LACP State = PORT_DISABLED */ static lacp_fsm_state_t lacp_rx_state_port_disabled[] = { {LACP_ACTION_PORT_DISABLED, LACP_RX_STATE_PORT_DISABLED}, // event 0 BEGIN {LACP_ACTION_PORT_DISABLED, LACP_RX_STATE_PORT_DISABLED}, // event 1 PORT_DISABLED {LACP_ACTION_INITIALIZE, LACP_RX_STATE_INITIALIZE}, // event 2 PORT_MOVED {LACP_ACTION_EXPIRED, LACP_RX_STATE_EXPIRED}, // event 3 LACP_ENABLED {LACP_ACTION_LACP_DISABLED, LACP_RX_STATE_LACP_DISABLED}, // event 4 LACP_DISABLED {LACP_NOACTION, LACP_RX_STATE_PORT_DISABLED}, // event 5 PDU_RECEIVED {LACP_NOACTION, LACP_RX_STATE_PORT_DISABLED}, // event 6 TIMER_EXPIRED }; /* * LACP State = EXPIRED */ static lacp_fsm_state_t lacp_rx_state_expired[] = { {LACP_ACTION_INITIALIZE, LACP_RX_STATE_INITIALIZE}, // event 0 BEGIN {LACP_NOACTION, LACP_RX_STATE_EXPIRED}, // event 1 PORT_DISABLED {LACP_NOACTION, LACP_RX_STATE_EXPIRED}, // event 2 PORT_MOVED {LACP_NOACTION, LACP_RX_STATE_EXPIRED}, // event 3 LACP_ENABLED {LACP_NOACTION, LACP_RX_STATE_EXPIRED}, // event 4 LACP_DISABLED {LACP_ACTION_CURRENT, LACP_RX_STATE_CURRENT}, // event 5 PDU_RECEIVED {LACP_ACTION_DEFAULTED, LACP_RX_STATE_DEFAULTED}, // event 6 TIMER_EXPIRED }; /* * LACP State = LACP_DISABLED */ static lacp_fsm_state_t lacp_rx_state_lacp_disabled[] = { {LACP_ACTION_INITIALIZE, LACP_RX_STATE_INITIALIZE}, // event 0 BEGIN {LACP_NOACTION, LACP_RX_STATE_LACP_DISABLED}, // event 1 PORT_DISABLED {LACP_NOACTION, LACP_RX_STATE_LACP_DISABLED}, // event 2 PORT_MOVED {LACP_ACTION_EXPIRED, LACP_RX_STATE_EXPIRED}, // event 3 LACP_ENABLED XXX {LACP_ACTION_LACP_DISABLED, LACP_RX_STATE_LACP_DISABLED}, // event 4 LACP_DISABLED {LACP_NOACTION, LACP_RX_STATE_LACP_DISABLED}, // event 5 PDU_RECEIVED {LACP_NOACTION, LACP_RX_STATE_LACP_DISABLED}, // event 6 TIMER_EXPIRED }; /* * LACP State = DEFAULTED */ static lacp_fsm_state_t lacp_rx_state_defaulted[] = { {LACP_ACTION_INITIALIZE, LACP_RX_STATE_INITIALIZE}, // event 0 BEGIN {LACP_NOACTION, LACP_RX_STATE_DEFAULTED}, // event 1 PORT_DISABLED {LACP_NOACTION, LACP_RX_STATE_DEFAULTED}, // event 2 PORT_MOVED {LACP_NOACTION, LACP_RX_STATE_DEFAULTED}, // event 3 LACP_ENABLED {LACP_ACTION_LACP_DISABLED, LACP_RX_STATE_LACP_DISABLED}, // event 4 LACP_DISABLED {LACP_ACTION_CURRENT, LACP_RX_STATE_CURRENT}, // event 5 PDU_RECEIVED {LACP_ACTION_DEFAULTED, LACP_RX_STATE_DEFAULTED}, // event 6 TIMER_EXPIRED }; /* * LACP State = CURRENT */ static lacp_fsm_state_t lacp_rx_state_current[] = { {LACP_ACTION_INITIALIZE, LACP_RX_STATE_INITIALIZE}, // event 0 BEGIN {LACP_NOACTION, LACP_RX_STATE_CURRENT}, // event 1 PORT_DISABLED {LACP_NOACTION, LACP_RX_STATE_CURRENT}, // event 1 PORT_MOVED {LACP_NOACTION, LACP_RX_STATE_CURRENT}, // event 2 LACP_ENABLED {LACP_ACTION_LACP_DISABLED, LACP_RX_STATE_LACP_DISABLED}, // event 3 LACP_DISABLED {LACP_ACTION_CURRENT, LACP_RX_STATE_CURRENT}, // event 4 PDU_RECEIVED {LACP_ACTION_EXPIRED, LACP_RX_STATE_EXPIRED}, // event 5 TIMER_EXPIRED }; static lacp_fsm_machine_t lacp_rx_fsm_table[] = { {lacp_rx_state_initialize}, {lacp_rx_state_port_disabled}, {lacp_rx_state_expired}, {lacp_rx_state_lacp_disabled}, {lacp_rx_state_defaulted}, {lacp_rx_state_current}, }; lacp_machine_t lacp_rx_machine = { lacp_rx_fsm_table, lacp_rx_debug_func, }; static void lacp_set_port_unselected (vlib_main_t * vm, member_if_t * mif) { mif->selected = LACP_PORT_UNSELECTED; switch (mif->mux_state) { case LACP_MUX_STATE_DETACHED: break; case LACP_MUX_STATE_WAITING: break; case LACP_MUX_STATE_ATTACHED: return; break; case LACP_MUX_STATE_COLLECTING_DISTRIBUTING: if (mif->partner.state & LACP_STATE_SYNCHRONIZATION) return; break; default: break; } lacp_machine_dispatch (&lacp_mux_machine, vm, mif, LACP_MUX_EVENT_UNSELECTED, &mif->mux_state); } static void lacp_update_default_selected (vlib_main_t * vm, member_if_t * mif) { if ((mif->partner_admin.state & LACP_STATE_AGGREGATION) != (mif->partner.state & LACP_STATE_AGGREGATION) || memcmp (&mif->partner, &mif->partner_admin, sizeof (mif->partner) - sizeof (mif->partner.state))) { lacp_set_port_unselected (vm, mif); } } static void lacp_record_default (member_if_t * mif) { mif->partner = mif->partner_admin; mif->actor.state |= LACP_STATE_DEFAULTED; } static void lacp_update_selected (vlib_main_t * vm, member_if_t * mif) { lacp_pdu_t *lacpdu = (lacp_pdu_t *) mif->last_rx_pkt; if ((lacpdu->actor.port_info.state & LACP_STATE_AGGREGATION) != (mif->partner.state & LACP_STATE_AGGREGATION) || memcmp (&mif->partner, &lacpdu->actor.port_info, sizeof (mif->partner) - sizeof (mif->partner.state))) { lacp_set_port_unselected (vm, mif); } } static void lacp_update_ntt (vlib_main_t * vm, member_if_t * mif) { lacp_pdu_t *lacpdu = (lacp_pdu_t *) mif->last_rx_pkt; u8 states = LACP_STATE_LACP_ACTIVITY | LACP_STATE_LACP_TIMEOUT | LACP_STATE_SYNCHRONIZATION | LACP_STATE_AGGREGATION; if ((states & lacpdu->partner.port_info.state) != (states & mif->actor.state) || memcmp (&mif->actor, &lacpdu->partner.port_info, sizeof (mif->actor) - sizeof (mif->actor.state))) { mif->ntt = 1; lacp_start_periodic_timer (vm, mif, 0); } } /* * compare lacpdu partner info against mif->partner. Return 1 if they match, 0 * otherwise. */ static u8 lacp_compare_partner (member_if_t * mif) { lacp_pdu_t *lacpdu = (lacp_pdu_t *) mif->last_rx_pkt; if ((!memcmp (&mif->partner, &lacpdu->actor.port_info, sizeof (mif->partner) - sizeof (mif->partner.state)) && ((mif->actor.state & LACP_STATE_AGGREGATION) == (lacpdu->partner.port_info.state & LACP_STATE_AGGREGATION))) || ((lacpdu->actor.port_info.state & LACP_STATE_AGGREGATION) == 0)) return 1; return 0; } static void lacp_record_pdu (vlib_main_t * vm, member_if_t * mif) { lacp_pdu_t *lacpdu = (lacp_pdu_t *) mif->last_rx_pkt; u8 match; /* Transition PTX out of NO_PERIODIC if needed */ if (!(mif->partner.state & LACP_STATE_LACP_ACTIVITY) && (lacpdu->actor.port_info.state & LACP_STATE_LACP_ACTIVITY)) lacp_ptx_post_short_timeout_event (vm, mif); match = lacp_compare_partner (mif); mif->partner = lacpdu->actor.port_info; mif->actor.state &= ~LACP_STATE_DEFAULTED; if (match && (lacpdu->actor.port_info.state & LACP_STATE_SYNCHRONIZATION)) mif->partner.state |= LACP_STATE_SYNCHRONIZATION; else mif->partner.state &= ~LACP_STATE_SYNCHRONIZATION; } static void lacp_set_port_moved (vlib_main_t * vm, member_if_t * mif, u8 val) { mif->port_moved = val; if (mif->port_moved) lacp_machine_dispatch (&lacp_rx_machine, vm, mif, LACP_RX_EVENT_PORT_MOVED, &mif->rx_state); else if (!mif->port_enabled) lacp_machine_dispatch (&lacp_rx_machine, vm, mif, LACP_RX_EVENT_PORT_DISABLED, &mif->rx_state); } int lacp_rx_action_initialize (void *p1, void *p2) { vlib_main_t *vm = p1; member_if_t *mif = p2; lacp_set_port_unselected (vm, mif); lacp_record_default (mif); mif->actor.state &= ~LACP_STATE_EXPIRED; lacp_set_port_moved (vm, mif, 0); /* UCT */ lacp_machine_dispatch (&lacp_rx_machine, vm, mif, LACP_RX_EVENT_BEGIN, &mif->rx_state); return 0; } int lacp_rx_action_port_disabled (void *p1, void *p2) { vlib_main_t *vm = p1; member_if_t *mif = p2; mif->partner.state &= ~LACP_STATE_SYNCHRONIZATION; if (mif->port_moved) { lacp_machine_dispatch (&lacp_rx_machine, vm, mif, LACP_RX_EVENT_PORT_MOVED, &mif->rx_state); } if (mif->port_enabled) { if (mif->lacp_enabled) lacp_machine_dispatch (&lacp_rx_machine, vm, mif, LACP_RX_EVENT_LACP_ENABLED, &mif->rx_state); else lacp_machine_dispatch (&lacp_rx_machine, vm, mif, LACP_RX_EVENT_LACP_DISABLED, &mif->rx_state); } return 0; } int lacp_rx_action_expired (void *p1, void *p2) { vlib_main_t *vm = p1; member_if_t *mif = p2; u8 timer_expired; mif->partner.state &= ~LACP_STATE_SYNCHRONIZATION; mif->partner.state |= LACP_STATE_LACP_TIMEOUT; lacp_ptx_post_short_timeout_event (vm, mif); if (lacp_timer_is_running (mif->current_while_timer) && lacp_timer_is_expired (vm, mif->current_while_timer)) timer_expired = 1; else timer_expired = 0; lacp_start_current_while_timer (vm, mif, mif->ttl_in_seconds); mif->actor.state |= LACP_STATE_EXPIRED; if (timer_expired) lacp_machine_dispatch (&lacp_rx_machine, vm, mif, LACP_RX_EVENT_TIMER_EXPIRED, &mif->rx_state); if (mif->last_rx_pkt && vec_len (mif->last_rx_pkt)) lacp_machine_dispatch (&lacp_rx_machine, vm, mif, LACP_RX_EVENT_PDU_RECEIVED, &mif->rx_state); return 0; } int lacp_rx_action_lacp_disabled (void *p1, void *p2) { vlib_main_t *vm = p1; member_if_t *mif = p2; lacp_set_port_unselected (vm, mif); lacp_record_default (mif); mif->partner.state &= ~LACP_STATE_AGGREGATION; mif->actor.state &= ~LACP_STATE_EXPIRED; return 0; } int lacp_rx_action_defaulted (void *p1, void *p2) { vlib_main_t *vm = p1; member_if_t *mif = p2; lacp_stop_timer (&mif->current_while_timer); lacp_update_default_selected (vm, mif); lacp_record_default (mif); mif->actor.state &= ~LACP_STATE_EXPIRED; if (mif->last_rx_pkt && vec_len (mif->last_rx_pkt)) lacp_machine_dispatch (&lacp_rx_machine, vm, mif, LACP_RX_EVENT_PDU_RECEIVED, &mif->rx_state); return 0; } static int lacp_port_is_moved (vlib_main_t * vm, member_if_t * mif) { bond_main_t *bm = &bond_main; member_if_t *mif2; lacp_pdu_t *lacpdu = (lacp_pdu_t *) mif->last_rx_pkt; pool_foreach (mif2, bm->neighbors) { { if ((mif != mif2) && (mif2->rx_state == LACP_RX_STATE_PORT_DISABLED) && !memcmp (mif2->partner.system, lacpdu->partner.port_info.system, 6) && (mif2->partner.port_number == lacpdu->partner.port_info.port_number)) return 1; } } return 0; } int lacp_rx_action_current (void *p1, void *p2) { vlib_main_t *vm = p1; member_if_t *mif = p2; lacp_update_selected (vm, mif); lacp_update_ntt (vm, mif); lacp_record_pdu (vm, mif); lacp_start_current_while_timer (vm, mif, mif->ttl_in_seconds); mif->actor.state &= ~LACP_STATE_EXPIRED; if (lacp_port_is_moved (vm, mif)) lacp_set_port_moved (vm, mif, 1); return lacp_selection_logic (vm, mif); } static u8 * format_rx_event (u8 * s, va_list * args) { static lacp_event_struct lacp_rx_event_array[] = { #define _(b, s, n) {.bit = b, .str = #s, }, foreach_lacp_rx_event #undef _ {.str = NULL} }; int e = va_arg (*args, int); lacp_event_struct *event_entry = lacp_rx_event_array; if (e >= (sizeof (lacp_rx_event_array) / sizeof (*event_entry))) s = format (s, "Bad event %d", e); else s = format (s, "%s", event_entry[e].str); return s; } void lacp_rx_debug_func (member_if_t * mif, int event, int state, lacp_fsm_state_t * transition) { vlib_worker_thread_t *w = vlib_worker_threads + os_get_thread_index (); ELOG_TYPE_DECLARE (e) = { .format = "%s", .format_args = "T4", }; struct { u32 event; } *ed = 0; ed = ELOG_TRACK_DATA (&vlib_global_main.elog_main, e, w->elog_track); ed->event = elog_string (&vlib_global_main.elog_main, "%U-RX: %U, %U->%U%c", format_vnet_sw_if_index_name, vnet_get_main (), mif->sw_if_index, format_rx_event, event, format_rx_sm_state, state, format_rx_sm_state, transition->next_state, 0); } void lacp_init_rx_machine (vlib_main_t * vm, member_if_t * mif) { lacp_machine_dispatch (&lacp_rx_machine, vm, mif, LACP_RX_EVENT_BEGIN, &mif->rx_state); lacp_machine_dispatch (&lacp_rx_machine, vm, mif, LACP_RX_EVENT_LACP_ENABLED, &mif->rx_state); } /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */