LCOV - code coverage report
Current view: top level - plugins/vrrp - vrrp.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 352 581 60.6 %
Date: 2023-10-26 01:39:38 Functions: 39 47 83.0 %

          Line data    Source code
       1             : /*
       2             :  * vrrp.c - vrrp plugin action functions
       3             :  *
       4             :  * Copyright 2019-2020 Rubicon Communications, LLC (Netgate)
       5             :  *
       6             :  * SPDX-License-Identifier: Apache-2.0
       7             :  *
       8             :  */
       9             : 
      10             : #include <vnet/vnet.h>
      11             : #include <vnet/plugin/plugin.h>
      12             : #include <vnet/mfib/mfib_entry.h>
      13             : #include <vnet/mfib/mfib_table.h>
      14             : #include <vnet/adj/adj.h>
      15             : #include <vnet/adj/adj_mcast.h>
      16             : #include <vnet/fib/fib_table.h>
      17             : #include <vnet/ip/igmp_packet.h>
      18             : #include <vnet/ip/ip6_link.h>
      19             : 
      20             : #include <vrrp/vrrp.h>
      21             : #include <vrrp/vrrp_packet.h>
      22             : 
      23             : #include <vpp/app/version.h>
      24             : 
      25             : vrrp_main_t vrrp_main;
      26             : 
      27             : static const mac_address_t ipv4_vmac = {
      28             :   .bytes = {0x00, 0x00, 0x5e, 0x00, 0x01, 0x00}
      29             : };
      30             : 
      31             : static const mac_address_t ipv6_vmac = {
      32             :   .bytes = {0x00, 0x00, 0x5e, 0x00, 0x02, 0x00}
      33             : };
      34             : 
      35             : vlib_simple_counter_main_t vrrp_errs[] = {
      36             :   /* Total number of VRRP packets received with invalid checksum */
      37             :   {
      38             :     .name = "CHKSUM_ERRS",
      39             :     .stat_segment_name = "/net/vrrp/chksum-errs",
      40             :   },
      41             :   /* Total number of VRRP packets received with unknown or unsupported version
      42             :    */
      43             :   {
      44             :     .name = "VERSION_ERRS",
      45             :     .stat_segment_name = "/net/vrrp/version-errs",
      46             :   },
      47             :   /* Total number of VRRP packets received with invalid VRID */
      48             :   {
      49             :     .name = "VRID_ERRS",
      50             :     .stat_segment_name = "/net/vrrp/vrid-errs",
      51             :   },
      52             :   /* Total number of VRRP packets received with TTL/Hop limit != 255 */
      53             :   {
      54             :     .name = "TTL_ERRS",
      55             :     .stat_segment_name = "/net/vrrp/ttl-errs",
      56             :   },
      57             :   /* Number of packets received with an address list not matching the locally
      58             :      configured one */
      59             :   {
      60             :     .name = "ADDR_LIST_ERRS",
      61             :     .stat_segment_name = "/net/vrrp/addr-list-errs",
      62             :   },
      63             :   /* Number of packets received with a length less than the VRRP header */
      64             :   {
      65             :     .name = "PACKET_LEN_ERRS",
      66             :     .stat_segment_name = "/net/vrrp/packet-len-errs",
      67             :   },
      68             : };
      69             : 
      70             : void
      71           0 : vrrp_incr_err_counter (vrrp_err_counter_t err_type)
      72             : {
      73           0 :   if (err_type >= VRRP_ERR_COUNTER_MAX)
      74             :     {
      75           0 :       clib_warning ("Attempt to increse error counter of unknown type %u",
      76             :                     err_type);
      77           0 :       return;
      78             :     }
      79           0 :   vlib_increment_simple_counter (&vrrp_errs[err_type],
      80           0 :                                  vlib_get_main ()->thread_index, 0, 1);
      81             : }
      82             : 
      83             : // per-VRRP statistics
      84             : 
      85             : /* Number of times a VRRP instance has transitioned to master */
      86             : vlib_simple_counter_main_t vrrp_stats[] = {
      87             :   {
      88             :     .name = "MASTER_TRANS",
      89             :     .stat_segment_name = "/net/vrrp/master-trans",
      90             :   },
      91             :   /* Number of VRRP advertisements sent by a VRRP instance */
      92             :   {
      93             :     .name = "ADV_SENT",
      94             :     .stat_segment_name = "/net/vrrp/adv-sent",
      95             :   },
      96             :   /* Number of VRRP advertisements received by a VRRP instance */
      97             :   {
      98             :     .name = "ADV_RCVD",
      99             :     .stat_segment_name = "/net/vrrp/adv-rcvd",
     100             :   },
     101             :   /* Number of VRRP priority-0 packets sent by a VRRP instance */
     102             :   {
     103             :     .name = "PRIO0_SENT",
     104             :     .stat_segment_name = "/net/vrrp/prio0-sent",
     105             :   },
     106             :   /* Number of VRRP priority-0 packets received by a VRRP instance */
     107             :   {
     108             :     .name = "PRIO0_RCVD",
     109             :     .stat_segment_name = "/net/vrrp/prio0-rcvd",
     110             :   },
     111             : };
     112             : 
     113             : void
     114           8 : vrrp_incr_stat_counter (vrrp_stat_counter_t stat_type, u32 stat_index)
     115             : {
     116           8 :   if (stat_type >= VRRP_STAT_COUNTER_MAX)
     117             :     {
     118           0 :       clib_warning ("Attempt to increse stat counter of unknown type %u",
     119             :                     stat_type);
     120           0 :       return;
     121             :     }
     122           8 :   vlib_increment_simple_counter (
     123           8 :     &vrrp_stats[stat_type], vlib_get_main ()->thread_index, stat_index, 1);
     124             : }
     125             : 
     126             : typedef struct
     127             : {
     128             :   vrrp_vr_key_t key;
     129             :   u32 count;
     130             : } vrrp_hwif_vr_count_t;
     131             : 
     132             : typedef enum
     133             : {
     134             :   VRRP_IF_UPDATE_IP,
     135             :   VRRP_IF_UPDATE_HW_LINK,
     136             :   VRRP_IF_UPDATE_SW_ADMIN,
     137             : } vrrp_intf_update_type_t;
     138             : 
     139             : typedef struct
     140             : {
     141             :   vrrp_intf_update_type_t type;
     142             :   u32 sw_if_index;
     143             :   u32 hw_if_index;
     144             :   int intf_up;
     145             : } vrrp_intf_update_t;
     146             : 
     147             : static int vrrp_intf_is_up (u32 sw_if_index, u8 is_ipv6,
     148             :                             vrrp_intf_update_t * pending);
     149             : 
     150             : static walk_rc_t
     151           4 : vrrp_hwif_master_count_walk (vnet_main_t * vnm, u32 sw_if_index, void *arg)
     152             : {
     153           4 :   vrrp_hwif_vr_count_t *vr_count = arg;
     154             :   vrrp_vr_t *vr;
     155             : 
     156           4 :   vr = vrrp_vr_lookup (sw_if_index, vr_count->key.vr_id,
     157           4 :                        vr_count->key.is_ipv6);
     158             : 
     159           4 :   if (vr && (vr->runtime.state == VRRP_VR_STATE_MASTER))
     160           2 :     vr_count->count++;
     161             : 
     162           4 :   return WALK_CONTINUE;
     163             : }
     164             : 
     165             : /*
     166             :  * Get a count of VRs in master state on a given hardware interface with
     167             :  * the provided VR ID and AF.
     168             :  */
     169             : static u32
     170           4 : vrrp_vr_hwif_master_vrs_by_vrid (u32 hw_if_index, u8 vr_id, u8 is_ipv6)
     171             : {
     172           4 :   vnet_main_t *vnm = vnet_get_main ();
     173             :   vrrp_hwif_vr_count_t vr_count;
     174             : 
     175           4 :   clib_memset (&vr_count, 0, sizeof (vr_count));
     176             : 
     177           4 :   vr_count.key.vr_id = vr_id;
     178           4 :   vr_count.key.is_ipv6 = is_ipv6;
     179             : 
     180           4 :   vnet_hw_interface_walk_sw (vnm, hw_if_index,
     181             :                              vrrp_hwif_master_count_walk, &vr_count);
     182             : 
     183           4 :   return vr_count.count;
     184             : }
     185             : 
     186             : /*
     187             :  * Add or delete the VR virtual MAC address on the hardware interface
     188             :  * when a VR enters or leaves the master state.
     189             :  *
     190             :  * Multiple subinterfaces may host the same VR ID. We should only add or
     191             :  * delete the virtual MAC if this is the first VR being enabled on the
     192             :  * hardware interface or the last one being disabled, respectively.
     193             :  */
     194             : void
     195           4 : vrrp_vr_transition_vmac (vrrp_vr_t * vr, vrrp_vr_state_t new_state)
     196             : {
     197           4 :   vnet_main_t *vnm = vnet_get_main ();
     198           4 :   clib_error_t *error = 0;
     199             :   vnet_hw_interface_t *hw;
     200           4 :   u8 enable = (new_state == VRRP_VR_STATE_MASTER);
     201             :   u32 n_master_vrs;
     202             : 
     203           4 :   hw = vnet_get_sup_hw_interface (vnm, vr->config.sw_if_index);
     204             :   n_master_vrs =
     205           4 :     vrrp_vr_hwif_master_vrs_by_vrid (hw->hw_if_index, vr->config.vr_id,
     206           4 :                                      vrrp_vr_is_ipv6 (vr));
     207             : 
     208             :   /* enable only if current master vrs is 0, disable only if 0 or 1 */
     209           4 :   if ((enable && !n_master_vrs) || (!enable && (n_master_vrs < 2)))
     210             :     {
     211           4 :       clib_warning ("%s virtual MAC address %U on hardware interface %u",
     212             :                     (enable) ? "Adding" : "Deleting",
     213             :                     format_ethernet_address, vr->runtime.mac.bytes,
     214             :                     hw->hw_if_index);
     215             : 
     216           4 :       error = vnet_hw_interface_add_del_mac_address
     217           4 :         (vnm, hw->hw_if_index, vr->runtime.mac.bytes, enable);
     218             :     }
     219             : 
     220           4 :   if (error)
     221           0 :     clib_error_report (error);
     222           4 : }
     223             : 
     224             : /*
     225             :  * Manage VR interface data on transition to/from master:
     226             :  *  - enable or disable ARP/ND input feature if appropriate
     227             :  *  - update count of VRs in master state
     228             :  */
     229             : static void
     230           4 : vrrp_vr_transition_intf (vrrp_vr_t * vr, vrrp_vr_state_t new_state)
     231             : {
     232             :   vrrp_intf_t *intf;
     233           4 :   const char *arc_name = 0, *node_name = 0;
     234           4 :   const char *mc_arc_name = 0, *mc_node_name = 0;
     235           4 :   u8 is_ipv6 = vrrp_vr_is_ipv6 (vr);
     236             :   u32 *vr_index;
     237           4 :   int n_master_accept = 0;
     238           4 :   int n_started = 0;
     239             : 
     240           4 :   if (is_ipv6)
     241             :     {
     242           2 :       arc_name = "ip6-local";
     243           2 :       node_name = "vrrp6-nd-input";
     244           2 :       mc_arc_name = "ip6-multicast";
     245           2 :       mc_node_name = "vrrp6-accept-owner-input";
     246             :     }
     247             :   else
     248             :     {
     249           2 :       arc_name = "arp";
     250           2 :       node_name = "vrrp4-arp-input";
     251           2 :       mc_arc_name = "ip4-multicast";
     252           2 :       mc_node_name = "vrrp4-accept-owner-input";
     253             :     }
     254             : 
     255           4 :   intf = vrrp_intf_get (vr->config.sw_if_index);
     256             : 
     257             :   /* Check other VRs on this intf to see if features need to be toggled */
     258           8 :   vec_foreach (vr_index, intf->vr_indices[is_ipv6])
     259             :   {
     260           4 :     vrrp_vr_t *intf_vr = vrrp_vr_lookup_index (*vr_index);
     261             : 
     262           4 :     if (intf_vr == vr)
     263           4 :       continue;
     264             : 
     265           0 :     if (intf_vr->runtime.state == VRRP_VR_STATE_INIT)
     266           0 :       continue;
     267             : 
     268           0 :     n_started++;
     269             : 
     270           0 :     if ((intf_vr->runtime.state == VRRP_VR_STATE_MASTER) &&
     271           0 :         vrrp_vr_accept_mode_enabled (intf_vr))
     272           0 :       n_master_accept++;
     273             :   }
     274             : 
     275             :   /* If entering/leaving init state, start/stop ARP or ND feature if no other
     276             :    * VRs are active on the interface.
     277             :    */
     278           4 :   if (((vr->runtime.state == VRRP_VR_STATE_INIT) ||
     279           4 :        (new_state == VRRP_VR_STATE_INIT)) && (n_started == 0))
     280           4 :     vnet_feature_enable_disable (arc_name, node_name,
     281             :                                  vr->config.sw_if_index,
     282             :                                  (new_state != VRRP_VR_STATE_INIT), NULL, 0);
     283             : 
     284             :   /* Special housekeeping when entering/leaving master mode */
     285           4 :   if ((vr->runtime.state == VRRP_VR_STATE_MASTER) ||
     286             :       (new_state == VRRP_VR_STATE_MASTER))
     287             :     {
     288             :       /* Maintain count of master state VRs on interface */
     289           4 :       if (new_state == VRRP_VR_STATE_MASTER)
     290           2 :         intf->n_master_vrs[is_ipv6]++;
     291           2 :       else if (intf->n_master_vrs[is_ipv6] > 0)
     292           2 :         intf->n_master_vrs[is_ipv6]--;
     293             : 
     294             :       /* If accept mode is enabled and no other master on intf has accept
     295             :        * mode enabled, enable/disable feature node to avoid spurious drops by
     296             :        * spoofing check.
     297             :        */
     298           4 :       if (vrrp_vr_accept_mode_enabled (vr) && !n_master_accept)
     299           0 :         vnet_feature_enable_disable (mc_arc_name, mc_node_name,
     300             :                                      vr->config.sw_if_index,
     301             :                                      (new_state == VRRP_VR_STATE_MASTER),
     302             :                                      NULL, 0);
     303             :     }
     304           4 : }
     305             : 
     306             : /* If accept mode enabled, add/remove VR addresses from interface */
     307             : static void
     308           4 : vrrp_vr_transition_addrs (vrrp_vr_t * vr, vrrp_vr_state_t new_state)
     309             : {
     310           4 :   vlib_main_t *vm = vlib_get_main ();
     311             :   u8 is_del;
     312             :   ip46_address_t *vr_addr;
     313             : 
     314           4 :   if (!vrrp_vr_accept_mode_enabled (vr))
     315           4 :     return;
     316             : 
     317             :   /* owner always has VR addresses configured, should never remove them */
     318           0 :   if (vrrp_vr_is_owner (vr))
     319           0 :     return;
     320             : 
     321             :   /* only need to do something if entering or leaving master state */
     322           0 :   if ((vr->runtime.state != VRRP_VR_STATE_MASTER) &&
     323             :       (new_state != VRRP_VR_STATE_MASTER))
     324           0 :     return;
     325             : 
     326           0 :   is_del = (new_state != VRRP_VR_STATE_MASTER);
     327             : 
     328           0 :   clib_warning ("%s VR addresses on sw_if_index %u",
     329             :                 (is_del) ? "Deleting" : "Adding", vr->config.sw_if_index);
     330             : 
     331           0 :   vec_foreach (vr_addr, vr->config.vr_addrs)
     332             :   {
     333           0 :     ip_interface_address_t *ia = NULL;
     334             : 
     335             :     /* We need to know the address length to use, find it from another
     336             :      * address on the interface. Or use a default (/24, /64).
     337             :      */
     338           0 :     if (!vrrp_vr_is_ipv6 (vr))
     339             :       {
     340           0 :         ip4_main_t *im = &ip4_main;
     341             :         ip4_address_t *intf4;
     342             : 
     343             :         intf4 =
     344           0 :           ip4_interface_address_matching_destination
     345           0 :           (im, &vr_addr->ip4, vr->config.sw_if_index, &ia);
     346             : 
     347           0 :         ip4_add_del_interface_address (vm, vr->config.sw_if_index,
     348             :                                        &vr_addr->ip4,
     349           0 :                                        (intf4 ? ia->address_length : 24),
     350             :                                        is_del);
     351             :       }
     352             :     else
     353             :       {
     354           0 :         ip6_main_t *im = &ip6_main;
     355             :         ip6_address_t *intf6;
     356             : 
     357             :         intf6 =
     358           0 :           ip6_interface_address_matching_destination
     359           0 :           (im, &vr_addr->ip6, vr->config.sw_if_index, &ia);
     360             : 
     361           0 :         ip6_add_del_interface_address (vm, vr->config.sw_if_index,
     362             :                                        &vr_addr->ip6,
     363           0 :                                        (intf6 ? ia->address_length : 64),
     364             :                                        is_del);
     365             :       }
     366             :   }
     367             : }
     368             : 
     369             : void
     370           4 : vrrp_vr_transition (vrrp_vr_t * vr, vrrp_vr_state_t new_state, void *data)
     371             : {
     372             : 
     373           4 :   clib_warning ("VR %U transitioning to %U", format_vrrp_vr_key, vr,
     374             :                 format_vrrp_vr_state, new_state);
     375             : 
     376             :   /* Don't do anything if transitioning to the state VR is already in.
     377             :    * This should never happen, just covering our bases.
     378             :    */
     379           4 :   if (new_state == vr->runtime.state)
     380           0 :     return;
     381             : 
     382           4 :   if (new_state == VRRP_VR_STATE_MASTER)
     383             :     {
     384           2 :       vrrp_incr_stat_counter (VRRP_STAT_COUNTER_MASTER_TRANS, vr->stat_index);
     385             :       /* RFC 5798 sec 6.4.1 (105) - startup event for VR with priority 255
     386             :        *          sec 6.4.2 (365) - master down timer fires on backup VR
     387             :        */
     388             : 
     389           2 :       vrrp_vr_multicast_group_join (vr);
     390           2 :       vrrp_adv_send (vr, 0);
     391           2 :       vrrp_garp_or_na_send (vr);
     392             : 
     393           2 :       vrrp_vr_timer_set (vr, VRRP_VR_TIMER_ADV);
     394             :     }
     395           2 :   else if (new_state == VRRP_VR_STATE_BACKUP)
     396             :     {
     397             :       /* RFC 5798 sec 6.4.1 (150) - startup event for VR with priority < 255
     398             :        *          sec 6.4.3 (735) - master preempted by higher priority VR
     399             :        */
     400             : 
     401           0 :       vrrp_vr_multicast_group_join (vr);
     402             : 
     403           0 :       if (vr->runtime.state == VRRP_VR_STATE_MASTER)
     404             :         {
     405           0 :           vrrp_input_process_args_t *args = data;
     406             : 
     407           0 :           if (args)
     408           0 :             vr->runtime.master_adv_int = args->max_adv_int;
     409             :         }
     410             :       else                      /* INIT, INTF_DOWN */
     411           0 :         vr->runtime.master_adv_int = vr->config.adv_interval;
     412             : 
     413           0 :       vrrp_vr_skew_compute (vr);
     414           0 :       vrrp_vr_master_down_compute (vr);
     415           0 :       vrrp_vr_timer_set (vr, VRRP_VR_TIMER_MASTER_DOWN);
     416             : 
     417             :     }
     418           2 :   else if (new_state == VRRP_VR_STATE_INIT)
     419             :     {
     420             :       /* RFC 5798 sec 6.4.2 (345) - shutdown event for backup VR
     421             :        *          sec 6.4.3 (655) - shutdown event for master VR
     422             :        */
     423             : 
     424           2 :       vrrp_vr_timer_cancel (vr);
     425           2 :       if (vr->runtime.state == VRRP_VR_STATE_MASTER)
     426           2 :         vrrp_adv_send (vr, 1);
     427             :     }
     428           0 :   else if (new_state == VRRP_VR_STATE_INTF_DOWN)
     429             :     /* State is not specified by RFC. This is to avoid attempting to
     430             :      * send packets on an interface that's down and to avoid having a
     431             :      * VR believe it is already the master when an interface is brought up
     432             :      */
     433           0 :     vrrp_vr_timer_cancel (vr);
     434             : 
     435             :   /* add/delete virtual IP addrs if accept_mode is true */
     436           4 :   vrrp_vr_transition_addrs (vr, new_state);
     437             : 
     438             :   /* enable/disable input features if necessary */
     439           4 :   vrrp_vr_transition_intf (vr, new_state);
     440             : 
     441             :   /* add/delete virtual MAC address on NIC if necessary */
     442           4 :   vrrp_vr_transition_vmac (vr, new_state);
     443             : 
     444           4 :   vrrp_vr_event (vr, new_state);
     445             : 
     446           4 :   vr->runtime.state = new_state;
     447             : }
     448             : 
     449             : #define VRRP4_MCAST_ADDR_AS_U8 { 224, 0, 0, 18 }
     450             : #define VRRP6_MCAST_ADDR_AS_U8 \
     451             : { 0xff, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x12 }
     452             : 
     453             : static const mfib_prefix_t all_vrrp4_routers = {
     454             :   .fp_proto = FIB_PROTOCOL_IP4,
     455             :   .fp_len = 32,
     456             :   .fp_grp_addr = {
     457             :                   .ip4 = {
     458             :                           .as_u8 = VRRP4_MCAST_ADDR_AS_U8,
     459             :                           },
     460             :                   },
     461             : };
     462             : 
     463             : static const mfib_prefix_t all_vrrp6_routers = {
     464             :   .fp_proto = FIB_PROTOCOL_IP6,
     465             :   .fp_len = 128,
     466             :   .fp_grp_addr = {
     467             :                   .ip6 = {
     468             :                           .as_u8 = VRRP6_MCAST_ADDR_AS_U8,
     469             :                           },
     470             :                   },
     471             : };
     472             : 
     473             : static int
     474           4 : vrrp_intf_enable_disable_mcast (u8 enable, u32 sw_if_index, u8 is_ipv6)
     475             : {
     476           4 :   vrrp_main_t *vrm = &vrrp_main;
     477             :   vrrp_intf_t *intf;
     478             :   u32 fib_index, i;
     479           4 :   u32 n_vrs_in_fib = 0;
     480             :   const mfib_prefix_t *vrrp_prefix;
     481             :   fib_protocol_t proto;
     482             :   vnet_link_t link_type;
     483           4 :   fib_route_path_t for_us = {
     484             :     .frp_sw_if_index = 0xffffffff,
     485             :     .frp_weight = 1,
     486             :     .frp_flags = FIB_ROUTE_PATH_LOCAL,
     487             :     .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD,
     488             :   };
     489           4 :   fib_route_path_t via_itf = {
     490             :     .frp_sw_if_index = sw_if_index,
     491             :     .frp_weight = 1,
     492             :     .frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT,
     493             :   };
     494             : 
     495           4 :   intf = vrrp_intf_get (sw_if_index);
     496             : 
     497           4 :   if (is_ipv6)
     498             :     {
     499           2 :       proto = FIB_PROTOCOL_IP6;
     500           2 :       link_type = VNET_LINK_IP6;
     501           2 :       vrrp_prefix = &all_vrrp6_routers;
     502             :     }
     503             :   else
     504             :     {
     505           2 :       proto = FIB_PROTOCOL_IP4;
     506           2 :       link_type = VNET_LINK_IP4;
     507           2 :       vrrp_prefix = &all_vrrp4_routers;
     508             :     }
     509             : 
     510           4 :   for_us.frp_proto = fib_proto_to_dpo (proto);
     511           4 :   via_itf.frp_proto = fib_proto_to_dpo (proto);
     512           4 :   fib_index = mfib_table_get_index_for_sw_if_index (proto, sw_if_index);
     513             : 
     514          16 :   vec_foreach_index (i, vrm->vrrp_intfs)
     515             :     {
     516          12 :       if (mfib_table_get_index_for_sw_if_index (proto, i) != fib_index)
     517           0 :         continue;
     518             : 
     519          12 :       n_vrs_in_fib += vrrp_intf_num_vrs (i, is_ipv6);
     520             :     }
     521             : 
     522           4 :   if (enable)
     523             :     {
     524             :       /* ensure that the local mcast route exists */
     525           2 :       mfib_table_entry_path_update (fib_index, vrrp_prefix, MFIB_SOURCE_API,
     526             :                                     MFIB_ENTRY_FLAG_NONE, &for_us);
     527             : 
     528           2 :       mfib_table_entry_path_update (fib_index, vrrp_prefix, MFIB_SOURCE_API,
     529             :                                     MFIB_ENTRY_FLAG_NONE, &via_itf);
     530           2 :       intf->mcast_adj_index[! !is_ipv6] =
     531           2 :         adj_mcast_add_or_lock (proto, link_type, sw_if_index);
     532             :     }
     533             :   else
     534             :     {
     535             :       /* Remove mcast local routes if this is the last VR being deleted */
     536           2 :       if (n_vrs_in_fib == 0)
     537           2 :         mfib_table_entry_path_remove (fib_index, vrrp_prefix, MFIB_SOURCE_API,
     538             :                                       &for_us);
     539             : 
     540           2 :       mfib_table_entry_path_remove (fib_index, vrrp_prefix, MFIB_SOURCE_API,
     541             :                                     &via_itf);
     542             :     }
     543             : 
     544           4 :   return 0;
     545             : }
     546             : 
     547             : static int
     548           4 : vrrp_intf_vr_add_del (u8 is_add, u32 sw_if_index, u32 vr_index, u8 is_ipv6)
     549             : {
     550             :   vrrp_intf_t *vr_intf;
     551             : 
     552           4 :   vr_intf = vrrp_intf_get (sw_if_index);
     553           4 :   if (!vr_intf)
     554           0 :     return -1;
     555             : 
     556           4 :   if (is_add)
     557             :     {
     558           2 :       if (!vec_len (vr_intf->vr_indices[is_ipv6]))
     559           2 :         vrrp_intf_enable_disable_mcast (1, sw_if_index, is_ipv6);
     560             : 
     561           2 :       vec_add1 (vr_intf->vr_indices[is_ipv6], vr_index);
     562             :     }
     563             :   else
     564             :     {
     565           2 :       u32 per_intf_index =
     566           2 :         vec_search (vr_intf->vr_indices[is_ipv6], vr_index);
     567             : 
     568           2 :       if (per_intf_index != ~0)
     569           2 :         vec_del1 (vr_intf->vr_indices[is_ipv6], per_intf_index);
     570             : 
     571             :       /* no more VRs on this interface, disable multicast */
     572           2 :       if (!vec_len (vr_intf->vr_indices[is_ipv6]))
     573           2 :         vrrp_intf_enable_disable_mcast (0, sw_if_index, is_ipv6);
     574             :     }
     575             : 
     576           4 :   return 0;
     577             : }
     578             : 
     579             : /* RFC 5798 section 8.3.2 says to take care not to configure more than
     580             :  * one VRRP router as the "IPvX address owner" of a VRID. Make sure that
     581             :  * all of the addresses configured for this VR are configured on the
     582             :  * interface.
     583             :  */
     584             : static int
     585           2 : vrrp_vr_valid_addrs_owner (vrrp_vr_config_t * vr_conf)
     586             : {
     587             :   ip46_address_t *addr;
     588           2 :   u8 is_ipv6 = (vr_conf->flags & VRRP_VR_IPV6) != 0;
     589             : 
     590           4 :   vec_foreach (addr, vr_conf->vr_addrs)
     591             :   {
     592           2 :     if (!ip_interface_has_address (vr_conf->sw_if_index, addr, !is_ipv6))
     593           0 :       return VNET_API_ERROR_ADDRESS_NOT_FOUND_FOR_INTERFACE;
     594             :   }
     595             : 
     596           2 :   return 0;
     597             : }
     598             : 
     599             : static int
     600           2 : vrrp_vr_valid_addrs_unused (vrrp_vr_config_t *vr_conf, index_t vrrp_index)
     601             : {
     602             :   ip46_address_t *vr_addr;
     603           2 :   u8 is_ipv6 = (vr_conf->flags & VRRP_VR_IPV6) != 0;
     604             : 
     605           4 :   vec_foreach (vr_addr, vr_conf->vr_addrs)
     606             :   {
     607             :     u32 vr_index;
     608             :     void *addr;
     609             : 
     610           2 :     addr = (is_ipv6) ? (void *) &vr_addr->ip6 : (void *) &vr_addr->ip4;
     611           2 :     vr_index = vrrp_vr_lookup_address (vr_conf->sw_if_index, is_ipv6, addr);
     612           2 :     if (vr_index != ~0 && vrrp_index != vr_index)
     613           0 :       return VNET_API_ERROR_ADDRESS_IN_USE;
     614             :   }
     615             : 
     616           2 :   return 0;
     617             : }
     618             : 
     619             : static int
     620           2 : vrrp_vr_valid_addrs (vrrp_vr_config_t *vr_conf, index_t vrrp_index)
     621             : {
     622           2 :   int ret = 0;
     623             : 
     624             :   /* If the VR owns the addresses, make sure they are configured */
     625           4 :   if (vr_conf->priority == 255 &&
     626           2 :       (ret = vrrp_vr_valid_addrs_owner (vr_conf)) < 0)
     627           0 :     return ret;
     628             : 
     629             :   /* make sure no other VR has already configured any of the VR addresses */
     630           2 :   ret = vrrp_vr_valid_addrs_unused (vr_conf, vrrp_index);
     631             : 
     632           2 :   return ret;
     633             : }
     634             : 
     635             : int
     636           4 : vrrp_vr_addr_add_del (vrrp_vr_t * vr, u8 is_add, ip46_address_t * vr_addr)
     637             : {
     638           4 :   vrrp_main_t *vmp = &vrrp_main;
     639             :   u32 vr_index;
     640             :   vrrp4_arp_key_t key4;
     641             :   vrrp6_nd_key_t key6;
     642             :   ip46_address_t *addr;
     643             : 
     644           4 :   if (!vr || !vr_addr)
     645           0 :     return VNET_API_ERROR_INVALID_ARGUMENT;
     646             : 
     647           4 :   vr_index = vr - vmp->vrs;
     648             : 
     649           4 :   if (vrrp_vr_is_ipv6 (vr))
     650             :     {
     651           2 :       key6.sw_if_index = vr->config.sw_if_index;
     652           2 :       key6.addr = vr_addr->ip6;
     653           2 :       if (is_add)
     654             :         {
     655           1 :           hash_set_mem_alloc (&vmp->vrrp6_nd_lookup, &key6, vr_index);
     656           1 :           vec_add1 (vr->config.vr_addrs, vr_addr[0]);
     657             :         }
     658             :       else
     659             :         {
     660           1 :           hash_unset_mem_free (&vmp->vrrp6_nd_lookup, &key6);
     661           1 :           vec_foreach (addr, vr->config.vr_addrs)
     662             :           {
     663           1 :             if (!ip46_address_cmp (addr, vr_addr))
     664             :               {
     665           1 :                 vec_del1 (vr->config.vr_addrs, addr - vr->config.vr_addrs);
     666           1 :                 break;
     667             :               }
     668             :           }
     669             :         }
     670             :     }
     671             :   else
     672             :     {
     673           2 :       key4.sw_if_index = vr->config.sw_if_index;
     674           2 :       key4.addr = vr_addr->ip4;
     675           2 :       if (is_add)
     676             :         {
     677           1 :           hash_set (vmp->vrrp4_arp_lookup, key4.as_u64, vr_index);
     678           1 :           vec_add1 (vr->config.vr_addrs, vr_addr[0]);
     679             :         }
     680             :       else
     681             :         {
     682           1 :           hash_unset (vmp->vrrp4_arp_lookup, key4.as_u64);
     683           1 :           vec_foreach (addr, vr->config.vr_addrs)
     684             :           {
     685           1 :             if (!ip46_address_cmp (addr, vr_addr))
     686             :               {
     687           1 :                 vec_del1 (vr->config.vr_addrs, addr - vr->config.vr_addrs);
     688           1 :                 break;
     689             :               }
     690             :           }
     691             :         }
     692             :     }
     693             : 
     694           4 :   return 0;
     695             : }
     696             : 
     697             : static void
     698           4 : vrrp_vr_addrs_add_del (vrrp_vr_t * vr, u8 is_add, ip46_address_t * vr_addrs)
     699             : {
     700             :   ip46_address_t *vr_addr;
     701             : 
     702           8 :   vec_foreach (vr_addr, vr_addrs)
     703             :   {
     704           4 :     vrrp_vr_addr_add_del (vr, is_add, vr_addr);
     705             :   }
     706           4 : }
     707             : 
     708             : int
     709           0 : vrrp_vr_update (index_t *vrrp_index, vrrp_vr_config_t *vr_conf)
     710             : {
     711           0 :   index_t index = *vrrp_index;
     712           0 :   vrrp_main_t *vrm = &vrrp_main;
     713           0 :   vrrp_vr_t *vr = NULL;
     714           0 :   vrrp_vr_key_t key = { 0 };
     715           0 :   uint8_t must_restart = 0;
     716           0 :   int ret = 0;
     717             : 
     718             :   /* no valid index -> create and return allocated index */
     719           0 :   if (index == INDEX_INVALID)
     720             :     {
     721           0 :       return vrrp_vr_add_del (1, vr_conf, vrrp_index);
     722             :     }
     723             :   /* update: lookup vrrp instance */
     724           0 :   if (pool_is_free_index (vrm->vrs, index))
     725           0 :     return (VNET_API_ERROR_NO_SUCH_ENTRY);
     726             : 
     727             :   /* fetch existing VR */
     728           0 :   vr = pool_elt_at_index (vrm->vrs, index);
     729             : 
     730             :   /* populate key */
     731           0 :   key.vr_id = vr->config.vr_id;
     732           0 :   key.is_ipv6 = !!(vr->config.flags & VRRP_VR_IPV6);
     733             :   ;
     734           0 :   key.sw_if_index = vr->config.sw_if_index;
     735             : 
     736             :   /* Do not allow changes to the keys of the VRRP instance */
     737           0 :   if (vr_conf->vr_id != key.vr_id || vr_conf->sw_if_index != key.sw_if_index ||
     738           0 :       !!(vr_conf->flags & VRRP_VR_IPV6) != key.is_ipv6)
     739             :     {
     740           0 :       clib_warning ("Attempt to change VR ID, IP version or interface index "
     741             :                     "for VRRP instance with index %u",
     742             :                     index);
     743           0 :       return VNET_API_ERROR_INVALID_ARGUMENT;
     744             :     }
     745             : 
     746             :   /* were IPvX addresses included ? */
     747           0 :   if (!vec_len (vr_conf->vr_addrs))
     748             :     {
     749           0 :       clib_warning ("Conf of VR %u for IPv%d on sw_if_index %u "
     750             :                     " does not contain IP addresses",
     751             :                     key.vr_id, key.is_ipv6 ? 6 : 4, key.sw_if_index);
     752           0 :       return VNET_API_ERROR_INVALID_SRC_ADDRESS;
     753             :     }
     754             : 
     755             :   /* Make sure the addresses are ok to use */
     756           0 :   if ((ret = vrrp_vr_valid_addrs (vr_conf, index)) < 0)
     757           0 :     return ret;
     758             : 
     759             :   /* stop it if needed */
     760           0 :   must_restart = (vr->runtime.state != VRRP_VR_STATE_INIT);
     761           0 :   if (must_restart)
     762           0 :     vrrp_vr_start_stop (0, &key);
     763             : 
     764             :   /* overwrite new config */
     765           0 :   vr->config.priority = vr_conf->priority;
     766           0 :   vr->config.adv_interval = vr_conf->adv_interval;
     767           0 :   vr->config.flags = vr_conf->flags;
     768             : 
     769             :   /* check if any address has changed */
     770             :   ip46_address_t *vr_addr, *conf_addr;
     771             :   uint8_t found;
     772           0 :   vec_foreach (vr_addr, vr->config.vr_addrs)
     773             :     {
     774           0 :       found = 0;
     775           0 :       vec_foreach (conf_addr, vr_conf->vr_addrs)
     776             :         {
     777           0 :           if (ip46_address_is_equal (vr_addr, conf_addr))
     778             :             {
     779           0 :               found = 1;
     780           0 :               break;
     781             :             }
     782             :         }
     783           0 :       if (!found)
     784             :         {
     785           0 :           vrrp_vr_addr_add_del (vr, 0, vr_addr);
     786             :         }
     787             :     }
     788           0 :   vec_foreach (conf_addr, vr_conf->vr_addrs)
     789             :     {
     790           0 :       found = 0;
     791           0 :       vec_foreach (vr_addr, vr->config.vr_addrs)
     792             :         {
     793           0 :           if (ip46_address_is_equal (vr_addr, conf_addr))
     794             :             {
     795           0 :               found = 1;
     796           0 :               break;
     797             :             }
     798             :         }
     799           0 :       if (!found)
     800             :         {
     801           0 :           vrrp_vr_addr_add_del (vr, 1, conf_addr);
     802             :         }
     803             :     }
     804             : 
     805             :   /* restart it if needed */
     806           0 :   if (must_restart)
     807           0 :     vrrp_vr_start_stop (1, &key);
     808             : 
     809           0 :   return 0;
     810             : }
     811             : 
     812             : static void
     813           2 : vrrp_vr_del_common (vrrp_vr_t *vr, vrrp_vr_key_t *key)
     814             : {
     815           2 :   vrrp_main_t *vrm = &vrrp_main;
     816             :   ip46_address_t *vr_addrs_del_copy;
     817             : 
     818           2 :   vrrp_vr_timer_cancel (vr);
     819           2 :   vrrp_vr_tracking_ifs_add_del (vr, vr->tracking.interfaces, 0);
     820           2 :   vr_addrs_del_copy = vec_dup (vr->config.vr_addrs);
     821           2 :   vrrp_vr_addrs_add_del (vr, 0, vr_addrs_del_copy);
     822           2 :   mhash_unset (&vrm->vr_index_by_key, key, 0);
     823           2 :   vec_free (vr_addrs_del_copy);
     824           2 :   vec_free (vr->config.peer_addrs);
     825           2 :   vec_free (vr->config.vr_addrs);
     826           2 :   vec_free (vr->tracking.interfaces);
     827           2 :   pool_put (vrm->vrs, vr);
     828           2 : }
     829             : 
     830             : int
     831           0 : vrrp_vr_del (index_t vrrp_index)
     832             : {
     833           0 :   vrrp_main_t *vrm = &vrrp_main;
     834             :   vrrp_vr_key_t key;
     835           0 :   vrrp_vr_t *vr = 0;
     836             : 
     837           0 :   if (pool_is_free_index (vrm->vrs, vrrp_index))
     838             :     {
     839           0 :       return (VNET_API_ERROR_NO_SUCH_ENTRY);
     840             :     }
     841             :   else
     842             :     {
     843           0 :       vr = pool_elt_at_index (vrm->vrs, vrrp_index);
     844           0 :       key.sw_if_index = vr->config.sw_if_index;
     845           0 :       key.vr_id = vr->config.vr_id;
     846           0 :       key.is_ipv6 = vrrp_vr_is_ipv6 (vr);
     847           0 :       vrrp_vr_del_common (vr, &key);
     848           0 :       return 0;
     849             :     }
     850             : }
     851             : 
     852             : /* Action function shared between message handler and debug CLI */
     853             : int
     854           4 : vrrp_vr_add_del (u8 is_add, vrrp_vr_config_t *vr_conf, index_t *ret_index)
     855             : {
     856           4 :   vrrp_main_t *vrm = &vrrp_main;
     857           4 :   vnet_main_t *vnm = vnet_get_main ();
     858             :   vrrp_vr_key_t key;
     859             :   uword *p;
     860             :   u32 vr_index;
     861           4 :   vrrp_vr_t *vr = 0;
     862             :   int ret;
     863             : 
     864           8 :   if (vr_conf->sw_if_index == ~0 ||
     865           4 :       !vnet_sw_interface_is_valid (vnm, vr_conf->sw_if_index))
     866           0 :     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
     867             : 
     868           4 :   clib_memset (&key, 0, sizeof (key));
     869             : 
     870           4 :   key.sw_if_index = vr_conf->sw_if_index;
     871           4 :   key.vr_id = vr_conf->vr_id;
     872           4 :   key.is_ipv6 = ((vr_conf->flags & VRRP_VR_IPV6) != 0);
     873             : 
     874           4 :   p = mhash_get (&vrm->vr_index_by_key, &key);
     875             : 
     876           4 :   if (is_add)
     877             :     {
     878             :       /* does a VR matching this key already exist ? */
     879           2 :       if (p)
     880             :         {
     881           0 :           clib_warning ("VR %u for IPv%d already exists on sw_if_index %u",
     882             :                         key.vr_id, (key.is_ipv6) ? 6 : 4, key.sw_if_index);
     883           0 :           return VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
     884             :         }
     885             : 
     886             :       /* were IPvX addresses included ? */
     887           2 :       if (!vec_len (vr_conf->vr_addrs))
     888             :         {
     889           0 :           clib_warning ("Conf of VR %u for IPv%d on sw_if_index %u "
     890             :                         " does not contain IP addresses",
     891             :                         key.vr_id, (key.is_ipv6) ? 6 : 4, key.sw_if_index);
     892           0 :           return VNET_API_ERROR_INVALID_SRC_ADDRESS;
     893             :         }
     894             : 
     895             :       /* Make sure the addresses are ok to use */
     896           2 :       if ((ret = vrrp_vr_valid_addrs (vr_conf, INDEX_INVALID)) < 0)
     897           0 :         return ret;
     898             : 
     899           2 :       pool_get_zero (vrm->vrs, vr);
     900           2 :       vr_index = vr - vrm->vrs;
     901             : 
     902           2 :       clib_memcpy (&vr->config, vr_conf, sizeof (vrrp_vr_config_t));
     903             : 
     904           2 :       vr->config.vr_addrs = 0;       /* allocate our own memory */
     905           2 :       vrrp_vr_addrs_add_del (vr, is_add, vr_conf->vr_addrs);
     906             : 
     907           2 :       vr->runtime.state = VRRP_VR_STATE_INIT;
     908           2 :       vr->runtime.timer_index = ~0;
     909             : 
     910             :       /* set virtual MAC based on IP version and VR ID */
     911           2 :       vr->runtime.mac = (key.is_ipv6) ? ipv6_vmac : ipv4_vmac;
     912           2 :       vr->runtime.mac.bytes[5] = vr_conf->vr_id;
     913             : 
     914             :       /* recall pool index for stats */
     915           2 :       vr->stat_index = vr_index;
     916             :       /* and return it if we were asked to */
     917           2 :       if (ret_index != NULL)
     918             :         {
     919           0 :           *ret_index = vr_index;
     920             :         }
     921             :       /* allocate & reset stats */
     922          12 :       for (int i = 0; i < VRRP_STAT_COUNTER_MAX; i++)
     923             :         {
     924          10 :           vlib_validate_simple_counter (&vrrp_stats[i], vr_index);
     925          10 :           vlib_zero_simple_counter (&vrrp_stats[i], vr_index);
     926             :         }
     927             : 
     928           2 :       mhash_set (&vrm->vr_index_by_key, &key, vr_index, 0);
     929             :     }
     930             :   else
     931             :     {
     932           2 :       if (!p)
     933             :         {
     934           0 :           clib_warning ("No VR %u for IPv%d exists on sw_if_index %u",
     935             :                         key.vr_id, (key.is_ipv6) ? 6 : 4, key.sw_if_index);
     936           0 :           return VNET_API_ERROR_NO_SUCH_ENTRY;
     937             :         }
     938             : 
     939           2 :       vr_index = p[0];
     940           2 :       vr = pool_elt_at_index (vrm->vrs, vr_index);
     941           2 :       vrrp_vr_del_common (vr, &key);
     942             :     }
     943             : 
     944           4 :   vrrp_intf_vr_add_del (is_add, vr_conf->sw_if_index, vr_index, key.is_ipv6);
     945             : 
     946           4 :   return 0;
     947             : }
     948             : 
     949             : int
     950           4 : vrrp_vr_start_stop (u8 is_start, vrrp_vr_key_t * vr_key)
     951             : {
     952           4 :   vrrp_main_t *vmp = &vrrp_main;
     953             :   uword *p;
     954           4 :   vrrp_vr_t *vr = 0;
     955             : 
     956           4 :   p = mhash_get (&vmp->vr_index_by_key, vr_key);
     957           4 :   if (!p)
     958           0 :     return VNET_API_ERROR_NO_SUCH_ENTRY;
     959             : 
     960           4 :   vr = pool_elt_at_index (vmp->vrs, p[0]);
     961             : 
     962             :   /* return success if already in the desired state */
     963           4 :   switch (vr->runtime.state)
     964             :     {
     965           2 :     case VRRP_VR_STATE_INIT:
     966           2 :       if (!is_start)
     967             :         {
     968           0 :           clib_warning ("Attempting to stop already stopped VR (%U)",
     969             :                         format_vrrp_vr_key, vr);
     970           0 :           return 0;
     971             :         }
     972           2 :       break;
     973           2 :     default:
     974           2 :       if (is_start)
     975             :         {
     976           0 :           clib_warning ("Attempting to start already started VR (%U)",
     977             :                         format_vrrp_vr_key, vr);
     978           0 :           return 0;
     979             :         }
     980           2 :       break;
     981             :     }
     982             : 
     983           4 :   if (is_start)
     984             :     {
     985           2 :       if (vrrp_vr_is_unicast (vr) && vec_len (vr->config.peer_addrs) == 0)
     986             :         {
     987           0 :           clib_warning ("Cannot start unicast VR without peers");
     988           0 :           return VNET_API_ERROR_INIT_FAILED;
     989             :         }
     990             : 
     991           2 :       vmp->n_vrs_started++;
     992             : 
     993           2 :       if (!vrrp_intf_is_up (vr->config.sw_if_index, vrrp_vr_is_ipv6 (vr),
     994             :                             NULL))
     995             :         {
     996           0 :           clib_warning ("VRRP VR started on down interface (%U)",
     997             :                         format_vrrp_vr_key, vr);
     998           0 :           vrrp_vr_transition (vr, VRRP_VR_STATE_INTF_DOWN, NULL);
     999             :         }
    1000           2 :       else if (vrrp_vr_is_owner (vr))
    1001           2 :         vrrp_vr_transition (vr, VRRP_VR_STATE_MASTER, NULL);
    1002             :       else
    1003           0 :         vrrp_vr_transition (vr, VRRP_VR_STATE_BACKUP, NULL);
    1004             :     }
    1005             :   else
    1006             :     {
    1007           2 :       vmp->n_vrs_started--;
    1008             : 
    1009           2 :       vrrp_vr_transition (vr, VRRP_VR_STATE_INIT, NULL);
    1010             :     }
    1011             : 
    1012           4 :   clib_warning ("%d VRs configured, %d VRs running",
    1013             :                 pool_elts (vmp->vrs), vmp->n_vrs_started);
    1014             : 
    1015           4 :   return 0;
    1016             : }
    1017             : 
    1018             : static int
    1019           0 : vrrp_vr_set_peers_validate (vrrp_vr_t * vr, ip46_address_t * peers)
    1020             : {
    1021           0 :   if (!vrrp_vr_is_unicast (vr))
    1022             :     {
    1023           0 :       clib_warning ("Peers can only be set on a unicast VR");
    1024           0 :       return VNET_API_ERROR_INVALID_ARGUMENT;
    1025             :     }
    1026             : 
    1027           0 :   if (vr->runtime.state != VRRP_VR_STATE_INIT)
    1028             :     {
    1029           0 :       clib_warning ("Cannot set peers on a running VR");
    1030           0 :       return VNET_API_ERROR_RSRC_IN_USE;
    1031             :     }
    1032             : 
    1033           0 :   if (vec_len (peers) == 0)
    1034             :     {
    1035           0 :       clib_warning ("No peer addresses provided");
    1036           0 :       return VNET_API_ERROR_INVALID_DST_ADDRESS;
    1037             :     }
    1038             : 
    1039           0 :   return 0;
    1040             : }
    1041             : 
    1042             : int
    1043           0 : vrrp_vr_set_peers (vrrp_vr_key_t * vr_key, ip46_address_t * peers)
    1044             : {
    1045           0 :   vrrp_main_t *vmp = &vrrp_main;
    1046             :   uword *p;
    1047           0 :   vrrp_vr_t *vr = 0;
    1048           0 :   int ret = 0;
    1049             : 
    1050           0 :   p = mhash_get (&vmp->vr_index_by_key, vr_key);
    1051           0 :   if (!p)
    1052           0 :     return VNET_API_ERROR_NO_SUCH_ENTRY;
    1053             : 
    1054           0 :   vr = pool_elt_at_index (vmp->vrs, p[0]);
    1055             : 
    1056           0 :   ret = vrrp_vr_set_peers_validate (vr, peers);
    1057           0 :   if (ret < 0)
    1058           0 :     return ret;
    1059             : 
    1060           0 :   if (vr->config.peer_addrs)
    1061           0 :     vec_free (vr->config.peer_addrs);
    1062             : 
    1063           0 :   vr->config.peer_addrs = vec_dup (peers);
    1064             : 
    1065           0 :   return 0;
    1066             : }
    1067             : 
    1068             : /* Manage reference on the interface to the VRs which track that interface */
    1069             : static void
    1070           0 : vrrp_intf_tracking_vr_add_del (u32 sw_if_index, vrrp_vr_t * vr, u8 is_add)
    1071             : {
    1072             :   vrrp_intf_t *intf;
    1073             :   u32 vr_index;
    1074           0 :   u8 is_ipv6 = vrrp_vr_is_ipv6 (vr);
    1075             :   int i;
    1076             : 
    1077           0 :   intf = vrrp_intf_get (sw_if_index);
    1078           0 :   vr_index = vrrp_vr_index (vr);
    1079             : 
    1080             :   /* Try to find the VR index in the list of tracking VRs */
    1081           0 :   vec_foreach_index (i, intf->tracking_vrs[is_ipv6])
    1082             :   {
    1083           0 :     if (vec_elt (intf->tracking_vrs[is_ipv6], i) != vr_index)
    1084           0 :       continue;
    1085             : 
    1086             :     /* Current index matches VR index */
    1087           0 :     if (!is_add)
    1088           0 :       vec_delete (intf->tracking_vrs[is_ipv6], 1, i);
    1089             : 
    1090             :     /* If deleting, the job is done. If adding, it's already here */
    1091           0 :     return;
    1092             :   }
    1093             : 
    1094             :   /* vr index was not found. */
    1095           0 :   if (is_add)
    1096           0 :     vec_add1 (intf->tracking_vrs[is_ipv6], vr_index);
    1097             : }
    1098             : 
    1099             : /* Check if sw intf admin state is up or in the process of coming up */
    1100             : static int
    1101           6 : vrrp_intf_sw_admin_up (u32 sw_if_index, vrrp_intf_update_t * pending)
    1102             : {
    1103           6 :   vnet_main_t *vnm = vnet_get_main ();
    1104             :   int admin_up;
    1105             : 
    1106           6 :   if (pending && (pending->type == VRRP_IF_UPDATE_SW_ADMIN))
    1107           2 :     admin_up = pending->intf_up;
    1108             :   else
    1109           4 :     admin_up = vnet_sw_interface_is_admin_up (vnm, sw_if_index);
    1110             : 
    1111           6 :   return admin_up;
    1112             : }
    1113             : 
    1114             : /* Check if hw intf link state is up or int the process of coming up */
    1115             : static int
    1116           6 : vrrp_intf_hw_link_up (u32 sw_if_index, vrrp_intf_update_t * pending)
    1117             : {
    1118           6 :   vnet_main_t *vnm = vnet_get_main ();
    1119             :   vnet_sw_interface_t *sup_sw;
    1120             :   int link_up;
    1121             : 
    1122           6 :   sup_sw = vnet_get_sup_sw_interface (vnm, sw_if_index);
    1123             : 
    1124           6 :   if (pending && (pending->type == VRRP_IF_UPDATE_HW_LINK) &&
    1125           2 :       (pending->hw_if_index == sup_sw->hw_if_index))
    1126           2 :     link_up = pending->intf_up;
    1127             :   else
    1128           4 :     link_up = vnet_hw_interface_is_link_up (vnm, sup_sw->hw_if_index);
    1129             : 
    1130           6 :   return link_up;
    1131             : }
    1132             : 
    1133             : /* Check if interface has ability to send IP packets. */
    1134             : static int
    1135           6 : vrrp_intf_ip_up (u32 sw_if_index, u8 is_ipv6, vrrp_intf_update_t * pending)
    1136             : {
    1137             :   int ip_up;
    1138             : 
    1139           6 :   if (pending && pending->type == VRRP_IF_UPDATE_IP)
    1140           0 :     ip_up = pending->intf_up;
    1141             :   else
    1142             :     /* Either a unicast address has to be explicitly assigned, or
    1143             :      * for IPv6 only, a link local assigned and multicast/ND enabled
    1144             :      */
    1145           6 :     ip_up =
    1146           6 :       ((ip_interface_get_first_ip (sw_if_index, !is_ipv6) != 0) ||
    1147           0 :        (is_ipv6 && ip6_link_is_enabled (sw_if_index)));
    1148             : 
    1149           6 :   return ip_up;
    1150             : }
    1151             : 
    1152             : static int
    1153           6 : vrrp_intf_is_up (u32 sw_if_index, u8 is_ipv6, vrrp_intf_update_t * pending)
    1154             : {
    1155             :   int admin_up, link_up, ip_up;
    1156             : 
    1157           6 :   admin_up = vrrp_intf_sw_admin_up (sw_if_index, pending);
    1158           6 :   link_up = vrrp_intf_hw_link_up (sw_if_index, pending);
    1159           6 :   ip_up = vrrp_intf_ip_up (sw_if_index, is_ipv6, pending);
    1160             : 
    1161           6 :   return (admin_up && link_up && ip_up);
    1162             : }
    1163             : 
    1164             : /* Examine the state of interfaces tracked by a VR and compute the priority
    1165             :  * adjustment that should be applied to the VR. If this is being called
    1166             :  * by the hw_link_up_down callback, the pending new flags on the sup hw
    1167             :  * interface have not been updated yet, so accept those as an optional
    1168             :  * argument.
    1169             :  */
    1170             : void
    1171           2 : vrrp_vr_tracking_ifs_compute (vrrp_vr_t * vr, vrrp_intf_update_t * pending)
    1172             : {
    1173             :   vrrp_vr_tracking_if_t *intf;
    1174           2 :   u32 total_priority = 0;
    1175             : 
    1176           2 :   vec_foreach (intf, vr->tracking.interfaces)
    1177             :   {
    1178           0 :     if (vrrp_intf_is_up (intf->sw_if_index, vrrp_vr_is_ipv6 (vr), pending))
    1179           0 :       continue;
    1180             : 
    1181           0 :     total_priority += intf->priority;
    1182             :   }
    1183             : 
    1184           2 :   if (total_priority != vr->tracking.interfaces_dec)
    1185             :     {
    1186           0 :       clib_warning ("VR %U interface track adjustment change from %u to %u",
    1187             :                     format_vrrp_vr_key, vr, vr->tracking.interfaces_dec,
    1188             :                     total_priority);
    1189           0 :       vr->tracking.interfaces_dec = total_priority;
    1190             :     }
    1191           2 : }
    1192             : 
    1193             : /* Manage tracked interfaces on a VR */
    1194             : int
    1195           0 : vrrp_vr_tracking_if_add_del (vrrp_vr_t * vr, u32 sw_if_index, u8 prio,
    1196             :                              u8 is_add)
    1197             : {
    1198           0 :   vnet_main_t *vnm = vnet_get_main ();
    1199             :   vrrp_vr_tracking_if_t *track_intf;
    1200             : 
    1201             :   /* VR can't track non-existent interface */
    1202           0 :   if (!vnet_sw_interface_is_valid (vnm, sw_if_index))
    1203           0 :     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
    1204             : 
    1205             :   /* VR can't track it's own interface */
    1206           0 :   if (sw_if_index == vr->config.sw_if_index)
    1207           0 :     return VNET_API_ERROR_INVALID_SW_IF_INDEX_2;
    1208             : 
    1209             :   /* update intf vector of tracking VRs */
    1210           0 :   vrrp_intf_tracking_vr_add_del (sw_if_index, vr, is_add);
    1211             : 
    1212             :   /* update VR vector of tracked interfaces */
    1213           0 :   vec_foreach (track_intf, vr->tracking.interfaces)
    1214             :   {
    1215           0 :     if (track_intf->sw_if_index != sw_if_index)
    1216           0 :       continue;
    1217             : 
    1218             :     /* found it */
    1219           0 :     if (!is_add)
    1220           0 :       vec_delete
    1221             :         (vr->tracking.interfaces, 1, track_intf - vr->tracking.interfaces);
    1222             : 
    1223           0 :     return 0;
    1224             :   }
    1225             : 
    1226           0 :   if (is_add)
    1227             :     {
    1228           0 :       vec_add2 (vr->tracking.interfaces, track_intf, 1);
    1229             : 
    1230           0 :       track_intf->sw_if_index = sw_if_index;
    1231           0 :       track_intf->priority = prio;
    1232             :     }
    1233             : 
    1234           0 :   return 0;
    1235             : }
    1236             : 
    1237             : int
    1238           2 : vrrp_vr_tracking_ifs_add_del (vrrp_vr_t * vr,
    1239             :                               vrrp_vr_tracking_if_t * track_ifs, u8 is_add)
    1240             : {
    1241             :   vrrp_vr_tracking_if_t *track_if, *ifs_copy;
    1242           2 :   int rv = 0;
    1243             : 
    1244             :   /* if deleting & track_ifs points to the VR list of tracked intfs, the
    1245             :    * vector could be modified as we iterate it. make a copy instead */
    1246           2 :   ifs_copy = vec_dup (track_ifs);
    1247             : 
    1248             :   /* add each tracked interface in the vector */
    1249           2 :   vec_foreach (track_if, ifs_copy)
    1250             :   {
    1251           0 :     rv = vrrp_vr_tracking_if_add_del (vr, track_if->sw_if_index,
    1252           0 :                                       track_if->priority, (is_add != 0));
    1253             : 
    1254             :     /* if operation failed, undo the previous changes */
    1255           0 :     if (rv)
    1256             :       {
    1257             :         vrrp_vr_tracking_if_t *rb_if;
    1258             : 
    1259           0 :         for (rb_if = track_if - 1; rb_if >= track_ifs; rb_if -= 1)
    1260           0 :           vrrp_vr_tracking_if_add_del (vr, rb_if->sw_if_index,
    1261           0 :                                        rb_if->priority, !(is_add != 0));
    1262           0 :         break;
    1263             :       }
    1264             :   }
    1265             : 
    1266           2 :   vec_free (ifs_copy);
    1267             : 
    1268           2 :   vrrp_vr_tracking_ifs_compute (vr, 0);
    1269             : 
    1270           2 :   return rv;
    1271             : }
    1272             : 
    1273             : /* Compute priority to advertise on all VRs which track the given interface
    1274             :  * and address family. The flags on an HW interface are not updated until
    1275             :  * after link up/down callbacks are invoked, so if this function is called
    1276             :  * by the link up/down callback, the flags about to be set will be passed
    1277             :  * via the 'pending' argument. Otherwise, pending will be NULL.
    1278             :  */
    1279             : static void
    1280       62772 : vrrp_intf_tracking_vrs_compute (u32 sw_if_index,
    1281             :                                 vrrp_intf_update_t * pending, u8 is_ipv6)
    1282             : {
    1283             :   u32 *vr_index;
    1284             :   vrrp_vr_t *vr;
    1285       62772 :   vrrp_intf_t *intf = vrrp_intf_get (sw_if_index);
    1286             : 
    1287       62772 :   vec_foreach (vr_index, intf->tracking_vrs[is_ipv6])
    1288             :   {
    1289           0 :     vr = vrrp_vr_lookup_index (*vr_index);
    1290           0 :     if (vr)
    1291           0 :       vrrp_vr_tracking_ifs_compute (vr, pending);
    1292             :   }
    1293       62772 : }
    1294             : 
    1295             : /* Interface being brought up/down is a quasi-{startup/shutdown} event.
    1296             :  * Execute an appropriate state transition for all VRs on the interface.
    1297             :  * This function may be invoked by:
    1298             :  *    sw interface admin up/down event
    1299             :  *    hw interface link up/down event
    1300             :  */
    1301             : clib_error_t *
    1302       26940 : vrrp_sw_interface_up_down (vrrp_intf_update_t * pending)
    1303             : {
    1304             :   vrrp_intf_t *intf;
    1305             :   int i;
    1306             :   u32 *vr_index;
    1307             :   vrrp_vr_t *vr;
    1308             : 
    1309       26940 :   intf = vrrp_intf_get (pending->sw_if_index);
    1310       26940 :   if (!intf)
    1311           0 :     return 0;
    1312             : 
    1313             :   /* adjust state of VR's configured on this interface */
    1314       80820 :   for (i = 0; i < 2; i++)
    1315             :     {
    1316             :       int is_up;
    1317             : 
    1318       53880 :       if (!intf->vr_indices[i])
    1319       53876 :         continue;
    1320             : 
    1321           4 :       is_up = vrrp_intf_is_up (pending->sw_if_index, i, pending);
    1322             : 
    1323           4 :       vec_foreach (vr_index, intf->vr_indices[i])
    1324             :       {
    1325             :         vrrp_vr_state_t vr_state;
    1326             : 
    1327           0 :         vr = vrrp_vr_lookup_index (*vr_index);
    1328           0 :         if (!vr)
    1329           0 :           continue;
    1330             : 
    1331           0 :         if (vr->runtime.state == VRRP_VR_STATE_INIT)
    1332           0 :           continue;             /* VR not started yet, no transition */
    1333             : 
    1334           0 :         if (!is_up)
    1335           0 :           vr_state = VRRP_VR_STATE_INTF_DOWN;
    1336             :         else
    1337             :           {
    1338           0 :             if (vr->runtime.state != VRRP_VR_STATE_INTF_DOWN)
    1339           0 :               continue;         /* shouldn't happen */
    1340             : 
    1341           0 :             vr_state = (vrrp_vr_is_owner (vr)) ?
    1342           0 :               VRRP_VR_STATE_MASTER : VRRP_VR_STATE_BACKUP;
    1343             :           }
    1344             : 
    1345           0 :         vrrp_vr_transition (vr, vr_state, NULL);
    1346             :       }
    1347             :     }
    1348             : 
    1349             :   /* compute adjustments on any VR's tracking this interface */
    1350       26940 :   vrrp_intf_tracking_vrs_compute (pending->sw_if_index, pending,
    1351             :                                   0 /* is_ipv6 */ );
    1352       26940 :   vrrp_intf_tracking_vrs_compute (pending->sw_if_index, pending,
    1353             :                                   1 /* is_ipv6 */ );
    1354             : 
    1355       26940 :   return 0;
    1356             : }
    1357             : 
    1358             : /* Process change in admin status on an interface */
    1359             : clib_error_t *
    1360       13514 : vrrp_sw_interface_admin_up_down (vnet_main_t * vnm, u32 sw_if_index,
    1361             :                                  u32 flags)
    1362             : {
    1363       13514 :   vrrp_intf_update_t pending = {
    1364             :     .type = VRRP_IF_UPDATE_SW_ADMIN,
    1365             :     .sw_if_index = sw_if_index,
    1366       13514 :     .intf_up = ((flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) != 0),
    1367             :   };
    1368             : 
    1369       13514 :   return vrrp_sw_interface_up_down (&pending);
    1370             : }
    1371             : 
    1372        1151 : VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (vrrp_sw_interface_admin_up_down);
    1373             : 
    1374             : static walk_rc_t
    1375       13426 : vrrp_hw_interface_link_up_down_walk (vnet_main_t * vnm,
    1376             :                                      u32 sw_if_index, void *arg)
    1377             : {
    1378       13426 :   vrrp_intf_update_t *pending = arg;
    1379             : 
    1380       13426 :   pending->sw_if_index = sw_if_index;
    1381       13426 :   vrrp_sw_interface_up_down (pending);
    1382             : 
    1383       13426 :   return WALK_CONTINUE;
    1384             : }
    1385             : 
    1386             : static clib_error_t *
    1387       13336 : vrrp_hw_interface_link_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
    1388             : {
    1389       13336 :   vrrp_intf_update_t pending = {
    1390             :     .type = VRRP_IF_UPDATE_HW_LINK,
    1391             :     .hw_if_index = hw_if_index,
    1392       13336 :     .intf_up = ((flags & VNET_HW_INTERFACE_FLAG_LINK_UP) != 0),
    1393             :   };
    1394             : 
    1395             :   /* walk the sw interface and sub interfaces to adjust interface tracking */
    1396       13336 :   vnet_hw_interface_walk_sw (vnm, hw_if_index,
    1397             :                              vrrp_hw_interface_link_up_down_walk, &pending);
    1398             : 
    1399       13336 :   return 0;
    1400             : }
    1401             : 
    1402        1151 : VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (vrrp_hw_interface_link_up_down);
    1403             : 
    1404             : static void
    1405        4838 : vrrp_ip4_add_del_interface_addr (ip4_main_t * im,
    1406             :                                  uword opaque,
    1407             :                                  u32 sw_if_index,
    1408             :                                  ip4_address_t * address,
    1409             :                                  u32 address_length,
    1410             :                                  u32 if_address_index, u32 is_del)
    1411             : {
    1412        4838 :   vrrp_intf_tracking_vrs_compute (sw_if_index, NULL, 0 /* is_ipv6 */ );
    1413        4838 : }
    1414             : 
    1415             : static ip6_link_delegate_id_t vrrp_ip6_delegate_id;
    1416             : 
    1417             : static u8 *
    1418           0 : format_vrrp_ip6_link (u8 * s, va_list * args)
    1419             : {
    1420           0 :   index_t indi = va_arg (*args, index_t);
    1421           0 :   u32 indent = va_arg (*args, u32);
    1422             :   vrrp_intf_t *intf;
    1423             :   u32 *vr_index;
    1424             : 
    1425           0 :   intf = vrrp_intf_get ((u32) indi);
    1426             : 
    1427           0 :   s = format (s, "%UVRRP VRs monitoring this link:\n",
    1428             :               format_white_space, indent);
    1429             : 
    1430           0 :   vec_foreach (vr_index, intf->tracking_vrs[1])
    1431             :   {
    1432           0 :     vrrp_vr_t *vr = vrrp_vr_lookup_index (*vr_index);
    1433             : 
    1434           0 :     s = format (s, "%U%U\n", format_white_space, indent + 2,
    1435             :                 format_vrrp_vr_key, vr);
    1436             :   }
    1437             : 
    1438           0 :   return s;
    1439             : }
    1440             : 
    1441             : static void
    1442        4054 : vrrp_intf_ip6_enable_disable (u32 sw_if_index, int enable)
    1443             : {
    1444        4054 :   vrrp_intf_update_t pending = {
    1445             :     .type = VRRP_IF_UPDATE_IP,
    1446             :     .sw_if_index = sw_if_index,
    1447             :     .intf_up = enable,
    1448             :   };
    1449             : 
    1450        4054 :   vrrp_intf_tracking_vrs_compute (sw_if_index, &pending, 1 /* is_ipv6 */ );
    1451        4054 : }
    1452             : 
    1453             : static void
    1454        2113 : vrrp_intf_ip6_enable (u32 sw_if_index)
    1455             : {
    1456        2113 :   vrrp_intf_ip6_enable_disable (sw_if_index, 1 /* enable */ );
    1457        2113 :   ip6_link_delegate_update (sw_if_index, vrrp_ip6_delegate_id, sw_if_index);
    1458        2113 : }
    1459             : 
    1460             : static void
    1461        1941 : vrrp_intf_ip6_disable (index_t indi)
    1462             : {
    1463        1941 :   vrrp_intf_ip6_enable_disable (indi, 0 /* enable */ );
    1464        1941 : }
    1465             : 
    1466             : const static ip6_link_delegate_vft_t vrrp_ip6_delegate_vft = {
    1467             :   .ildv_enable = vrrp_intf_ip6_enable,
    1468             :   .ildv_disable = vrrp_intf_ip6_disable,
    1469             :   .ildv_format = format_vrrp_ip6_link,
    1470             : };
    1471             : 
    1472             : static clib_error_t *
    1473         575 : vrrp_init (vlib_main_t * vm)
    1474             : {
    1475         575 :   vrrp_main_t *vmp = &vrrp_main;
    1476         575 :   clib_error_t *error = 0;
    1477         575 :   ip4_main_t *im4 = &ip4_main;
    1478             :   ip4_add_del_interface_address_callback_t cb4;
    1479             :   vlib_node_t *intf_output_node;
    1480             : 
    1481         575 :   clib_memset (vmp, 0, sizeof (*vmp));
    1482             : 
    1483         575 :   if ((error = vlib_call_init_function (vm, ip4_lookup_init)) ||
    1484         575 :       (error = vlib_call_init_function (vm, ip6_lookup_init)))
    1485           0 :     return error;
    1486             : 
    1487         575 :   vmp->vlib_main = vm;
    1488         575 :   vmp->vnet_main = vnet_get_main ();
    1489             : 
    1490         575 :   intf_output_node = vlib_get_node_by_name (vm, (u8 *) "interface-output");
    1491         575 :   vmp->intf_output_node_idx = intf_output_node->index;
    1492             : 
    1493         575 :   error = vrrp_plugin_api_hookup (vm);
    1494             : 
    1495         575 :   if (error)
    1496           0 :     return error;
    1497             : 
    1498         575 :   mhash_init (&vmp->vr_index_by_key, sizeof (u32), sizeof (vrrp_vr_key_t));
    1499         575 :   vmp->vrrp4_arp_lookup = hash_create (0, sizeof (uword));
    1500         575 :   vmp->vrrp6_nd_lookup = hash_create_mem (0, sizeof (vrrp6_nd_key_t),
    1501             :                                           sizeof (uword));
    1502             : 
    1503         575 :   cb4.function = vrrp_ip4_add_del_interface_addr;
    1504         575 :   cb4.function_opaque = 0;
    1505         575 :   vec_add1 (im4->add_del_interface_address_callbacks, cb4);
    1506             : 
    1507         575 :   vrrp_ip6_delegate_id = ip6_link_delegate_register (&vrrp_ip6_delegate_vft);
    1508             : 
    1509             :   /* allocate & reset error counters */
    1510        4025 :   for (int i = 0; i < VRRP_ERR_COUNTER_MAX; i++)
    1511             :     {
    1512        3450 :       vlib_validate_simple_counter (&vrrp_errs[i], 0);
    1513        3450 :       vlib_zero_simple_counter (&vrrp_errs[i], 0);
    1514             :     }
    1515             : 
    1516         575 :   return error;
    1517             : }
    1518             : 
    1519        1151 : VLIB_INIT_FUNCTION (vrrp_init);
    1520             : 
    1521             : 
    1522             : /* *INDENT-OFF* */
    1523             : VLIB_PLUGIN_REGISTER () =
    1524             : {
    1525             :   .version = VPP_BUILD_VER,
    1526             :   .description = "VRRP v3 (RFC 5798)",
    1527             : };
    1528             : /* *INDENT-ON* */
    1529             : 
    1530             : /*
    1531             :  * fd.io coding-style-patch-verification: ON
    1532             :  *
    1533             :  * Local Variables:
    1534             :  * eval: (c-set-style "gnu")
    1535             :  * End:
    1536             :  */

Generated by: LCOV version 1.14