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