LCOV - code coverage report
Current view: top level - plugins/lacp - lacp.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 179 211 84.8 %
Date: 2023-10-26 01:39:38 Functions: 18 18 100.0 %

          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             :  */

Generated by: LCOV version 1.14