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