]> git.proxmox.com Git - mirror_frr.git/blame - vrrpd/vrrp.c
vrrpd: same VRID, different interface
[mirror_frr.git] / vrrpd / vrrp.c
CommitLineData
5435a2bf 1/*
63d4bd12
QY
2 * VRRP global definitions and state machine.
3 * Copyright (C) 2018-2019 Cumulus Networks, Inc.
4 * Quentin Young
5435a2bf
QY
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
41ee5442
QY
20#include <zebra.h>
21
862f2f37
QY
22#include "lib/hash.h"
23#include "lib/hook.h"
40744000
QY
24#include "lib/if.h"
25#include "lib/linklist.h"
862f2f37 26#include "lib/memory.h"
91188ca6 27#include "lib/network.h"
40744000 28#include "lib/prefix.h"
862f2f37 29#include "lib/sockopt.h"
dad18a2f 30#include "lib/sockunion.h"
40744000 31#include "lib/vrf.h"
5435a2bf
QY
32
33#include "vrrp.h"
40744000 34#include "vrrp_arp.h"
4f52e9a6 35#include "vrrp_ndisc.h"
247aa469 36#include "vrrp_packet.h"
f3fe0047 37#include "vrrp_zebra.h"
5435a2bf 38
4ec94408
QY
39#define VRRP_LOGPFX "[CORE] "
40
41const char *vrrp_state_names[3] = {
42 [VRRP_STATE_INITIALIZE] = "Initialize",
43 [VRRP_STATE_MASTER] = "Master",
44 [VRRP_STATE_BACKUP] = "Backup",
45};
46
47const char *vrrp_event_names[2] = {
48 [VRRP_EVENT_STARTUP] = "Startup",
49 [VRRP_EVENT_SHUTDOWN] = "Shutdown",
50};
51
52
5435a2bf
QY
53/* Utility functions ------------------------------------------------------- */
54
55/*
56 * Sets an ethaddr to RFC-defined Virtual Router MAC address.
57 *
58 * mac
59 * ethaddr to set
60 *
61 * v6
62 * Whether this is a V6 or V4 Virtual Router MAC
63 *
64 * vrid
65 * Virtual Router Identifier
66 */
67static void vrrp_mac_set(struct ethaddr *mac, bool v6, uint8_t vrid)
68{
69 /*
70 * V4: 00-00-5E-00-01-{VRID}
71 * V6: 00-00-5E-00-02-{VRID}
72 */
73 mac->octet[0] = 0x00;
74 mac->octet[1] = 0x00;
75 mac->octet[2] = 0x5E;
76 mac->octet[3] = 0x00;
77 mac->octet[4] = v6 ? 0x02 : 0x01;
78 mac->octet[5] = vrid;
79}
80
1d21789e 81/*
862f2f37 82 * Recalculates and sets skew_time and master_down_interval based
1d21789e
QY
83 * values.
84 *
862f2f37
QY
85 * r
86 * VRRP Router to operate on
1d21789e 87 */
862f2f37 88static void vrrp_recalculate_timers(struct vrrp_router *r)
1d21789e 89{
862f2f37
QY
90 r->skew_time =
91 ((256 - r->vr->priority) * r->master_adver_interval) / 256;
92 r->master_down_interval = (3 * r->master_adver_interval);
93 r->master_down_interval += r->skew_time;
1d21789e
QY
94}
95
5d3730c5
QY
96/*
97 * Determines if a VRRP router is the owner of the specified address.
98 *
dad18a2f
QY
99 * The determining factor for whether an interface is the address owner is
100 * simply whether the address is assigned to the VRRP subinterface by someone
101 * other than vrrpd.
102 *
103 * This function should always return the correct answer regardless of
104 * master/backup status.
105 *
5d3730c5 106 * vr
862f2f37 107 * Virtual Router
5d3730c5
QY
108 *
109 * Returns:
110 * whether or not vr owns the specified address
111 */
7e205b4a 112static bool vrrp_is_owner(struct interface *ifp, struct ipaddr *addr)
5d3730c5 113{
7e205b4a 114 struct prefix p;
5d3730c5 115
7e205b4a
QY
116 p.family = IS_IPADDR_V4(addr) ? AF_INET : AF_INET6;
117 p.prefixlen = IS_IPADDR_V4(addr) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN;
118 memcpy(&p.u, &addr->ip, sizeof(addr->ip));
dad18a2f 119
7e205b4a 120 return !!connected_lookup_prefix_exact(ifp, &p);
5d3730c5
QY
121}
122
1d21789e
QY
123/* Configuration controllers ----------------------------------------------- */
124
125void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority)
c23edd74 126{
862f2f37 127 if (vr->priority == priority)
c23edd74
QY
128 return;
129
862f2f37 130 vr->priority = priority;
bac08ded
QY
131 vr->v4->priority = priority;
132 vr->v6->priority = priority;
c23edd74
QY
133}
134
1d21789e
QY
135void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr,
136 uint16_t advertisement_interval)
137{
138 if (vr->advertisement_interval == advertisement_interval)
139 return;
140
862f2f37
QY
141 vr->advertisement_interval = advertisement_interval;
142 vrrp_recalculate_timers(vr->v4);
143 vrrp_recalculate_timers(vr->v6);
144}
145
146void vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4)
147{
148 struct ipaddr *v4_ins = XCALLOC(MTYPE_TMP, sizeof(struct ipaddr));
149
150 v4_ins->ipa_type = IPADDR_V4;
151 v4_ins->ipaddr_v4 = v4;
10133a59 152
7e205b4a 153 if (!vrrp_is_owner(vr->ifp, v4_ins) && vr->v4->is_owner) {
10133a59
QY
154 char ipbuf[INET6_ADDRSTRLEN];
155 ipaddr2str(v4_ins, ipbuf, sizeof(ipbuf));
156 zlog_err(
157 VRRP_LOGPFX VRRP_LOGPFX_VRID
158 "This VRRP router is not the address owner of %s, but is the address owner of other addresses; this config is unsupported.",
159 vr->vrid, ipbuf);
160 /* FIXME: indicate failure with rc */
161 return;
162 }
163
862f2f37 164 listnode_add(vr->v4->addrs, v4_ins);
1d21789e
QY
165}
166
862f2f37 167void vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6)
c23edd74 168{
862f2f37 169 struct ipaddr *v6_ins = XCALLOC(MTYPE_TMP, sizeof(struct ipaddr));
c23edd74 170
862f2f37
QY
171 v6_ins->ipa_type = IPADDR_V6;
172 memcpy(&v6_ins->ipaddr_v6, &v6, sizeof(struct in6_addr));
10133a59 173
7e205b4a 174 if (!vrrp_is_owner(vr->ifp, v6_ins) && vr->v6->is_owner) {
10133a59
QY
175 char ipbuf[INET6_ADDRSTRLEN];
176 ipaddr2str(v6_ins, ipbuf, sizeof(ipbuf));
177 zlog_err(
178 VRRP_LOGPFX VRRP_LOGPFX_VRID
179 "This VRRP router is not the address owner of %s, but is the address owner of other addresses; this config is unsupported.",
180 vr->vrid, ipbuf);
181 /* FIXME: indicate failure with rc */
182 return;
183 }
184
862f2f37 185 listnode_add(vr->v6->addrs, v6_ins);
4f52e9a6
QY
186
187 if (vr->v6->fsm.state == VRRP_STATE_MASTER)
188 vrrp_ndisc_una_send(vr->v6, v6_ins);
862f2f37
QY
189}
190
191void vrrp_add_ip(struct vrrp_vrouter *vr, struct ipaddr ip)
192{
193 if (ip.ipa_type == IPADDR_V4)
194 vrrp_add_ipv4(vr, ip.ipaddr_v4);
195 else if (ip.ipa_type == IPADDR_V6)
196 vrrp_add_ipv6(vr, ip.ipaddr_v6);
c23edd74
QY
197}
198
1d21789e
QY
199
200/* Creation and destruction ------------------------------------------------ */
201
6287cefe
QY
202static void vrrp_router_addr_list_del_cb(void *val)
203{
204 struct ipaddr *ip = val;
205 XFREE(MTYPE_TMP, ip);
206}
207
85467974
QY
208/*
209 * Search for a suitable macvlan subinterface we can attach to, and if found,
210 * attach to it.
211 *
212 * r
213 * Router to attach to interface
214 *
215 * Returns:
216 * Whether an interface was successfully attached
217 */
218static bool vrrp_attach_interface(struct vrrp_router *r)
862f2f37 219{
dad18a2f
QY
220 /* Search for existing interface with computed MAC address */
221 struct interface **ifps;
222 size_t ifps_cnt = if_lookup_by_hwaddr(
223 r->vmac.octet, sizeof(r->vmac.octet), &ifps, VRF_DEFAULT);
224
225 /*
226 * Filter to only those interfaces whose names begin with VRRP
227 * interface name. E.g. if this VRRP instance was configured on eth0,
228 * then we filter the list to only keep interfaces matching ^eth0.*
229 *
230 * If there are still multiple interfaces we just select the first one,
231 * as it should be functionally identical to the others.
232 */
233 unsigned int candidates = 0;
234 struct interface *selection = NULL;
235 for (unsigned int i = 0; i < ifps_cnt; i++) {
dad18a2f
QY
236 if (strncmp(ifps[i]->name, r->vr->ifp->name,
237 strlen(r->vr->ifp->name)))
238 ifps[i] = NULL;
239 else {
240 selection = selection ? selection : ifps[i];
241 candidates++;
242 }
243 }
244
b79640e4
QY
245 if (ifps_cnt)
246 XFREE(MTYPE_TMP, ifps);
dad18a2f
QY
247
248 char ethstr[ETHER_ADDR_STRLEN];
249 prefix_mac2str(&r->vmac, ethstr, sizeof(ethstr));
250
251 assert(!!selection == !!candidates);
252
253 if (candidates == 0)
254 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
85467974 255 "No interface found w/ MAC %s",
dad18a2f
QY
256 r->vr->vrid, ethstr);
257 else if (candidates > 1)
258 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
259 "Multiple VRRP interfaces found; using %s",
260 r->vr->vrid, selection->name);
261 else
262 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Selected %s",
263 r->vr->vrid, selection->name);
264
7e205b4a 265 r->mvl_ifp = selection;
dad18a2f 266
85467974
QY
267 return !!r->mvl_ifp;
268
269}
270
271static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr,
272 int family)
273{
274 struct vrrp_router *r = XCALLOC(MTYPE_TMP, sizeof(struct vrrp_router));
275
276 r->family = family;
277 r->sock_rx = -1;
278 r->sock_tx = -1;
279 r->vr = vr;
280 r->addrs = list_new();
281 r->addrs->del = vrrp_router_addr_list_del_cb;
282 r->priority = vr->priority;
283 r->fsm.state = VRRP_STATE_INITIALIZE;
284 vrrp_mac_set(&r->vmac, family == AF_INET6, vr->vrid);
285
286 vrrp_attach_interface(r);
287
862f2f37
QY
288 return r;
289}
290
291static void vrrp_router_destroy(struct vrrp_router *r)
292{
6287cefe
QY
293 if (r->is_active)
294 vrrp_event(r, VRRP_EVENT_SHUTDOWN);
295
dad18a2f
QY
296 if (r->sock_rx >= 0)
297 close(r->sock_rx);
298 if (r->sock_tx >= 0)
299 close(r->sock_tx);
6287cefe 300
862f2f37
QY
301 /* FIXME: also delete list elements */
302 list_delete(&r->addrs);
303 XFREE(MTYPE_TMP, r);
304}
305
5435a2bf
QY
306struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid)
307{
4f0b6b45 308 struct vrrp_vrouter *vr = vrrp_lookup(ifp, vrid);
6287cefe
QY
309
310 if (vr)
311 return vr;
312
313 vr = XCALLOC(MTYPE_TMP, sizeof(struct vrrp_vrouter));
5435a2bf 314
5435a2bf
QY
315 vr->ifp = ifp;
316 vr->vrid = vrid;
5435a2bf 317 vr->priority = VRRP_DEFAULT_PRIORITY;
5435a2bf
QY
318 vr->preempt_mode = true;
319 vr->accept_mode = false;
862f2f37
QY
320
321 vr->v4 = vrrp_router_create(vr, AF_INET);
322 vr->v6 = vrrp_router_create(vr, AF_INET6);
323
1485d9e5 324 vrrp_set_advertisement_interval(vr, VRRP_DEFAULT_ADVINT);
5435a2bf
QY
325
326 hash_get(vrrp_vrouters_hash, vr, hash_alloc_intern);
327
328 return vr;
329}
330
c23edd74
QY
331void vrrp_vrouter_destroy(struct vrrp_vrouter *vr)
332{
862f2f37
QY
333 vrrp_router_destroy(vr->v4);
334 vrrp_router_destroy(vr->v6);
c23edd74
QY
335 hash_release(vrrp_vrouters_hash, vr);
336 XFREE(MTYPE_TMP, vr);
337}
338
4f0b6b45 339struct vrrp_vrouter *vrrp_lookup(struct interface *ifp, uint8_t vrid)
5435a2bf
QY
340{
341 struct vrrp_vrouter vr;
342 vr.vrid = vrid;
4f0b6b45 343 vr.ifp = ifp;
5435a2bf
QY
344
345 return hash_lookup(vrrp_vrouters_hash, &vr);
346}
347
348/* Network ----------------------------------------------------------------- */
349
10133a59
QY
350/* Forward decls */
351static void vrrp_change_state(struct vrrp_router *r, int to);
352static int vrrp_adver_timer_expire(struct thread *thread);
353static int vrrp_master_down_timer_expire(struct thread *thread);
354
5435a2bf 355/*
91188ca6 356 * Create and multicast a VRRP ADVERTISEMENT message.
5435a2bf 357 *
862f2f37
QY
358 * r
359 * VRRP Router for which to send ADVERTISEMENT
5435a2bf 360 */
862f2f37 361static void vrrp_send_advertisement(struct vrrp_router *r)
5435a2bf 362{
247aa469
QY
363 struct vrrp_pkt *pkt;
364 ssize_t pktlen;
862f2f37
QY
365 struct ipaddr *addrs[r->addrs->count];
366 union sockunion dest;
247aa469 367
862f2f37 368 list_to_array(r->addrs, (void **)addrs, r->addrs->count);
247aa469 369
8071d5c3 370 pktlen = vrrp_pkt_build(&pkt, &r->src, r->vr->vrid, r->priority,
862f2f37
QY
371 r->vr->advertisement_interval, r->addrs->count,
372 (struct ipaddr **)&addrs);
247aa469
QY
373
374 if (pktlen > 0)
375 zlog_hexdump(pkt, (size_t) pktlen);
376 else
377 zlog_warn("Could not build VRRP packet");
378
862f2f37
QY
379 const char *group =
380 r->family == AF_INET ? VRRP_MCASTV4_GROUP_STR : VRRP_MCASTV6_GROUP_STR;
381 str2sockunion(group, &dest);
247aa469 382
dad18a2f 383 ssize_t sent = sendto(r->sock_tx, pkt, (size_t)pktlen, 0, &dest.sa,
862f2f37 384 sockunion_sizeof(&dest));
4ec94408 385
bb54fa3a
QY
386 XFREE(MTYPE_TMP, pkt);
387
4ec94408
QY
388 if (sent < 0) {
389 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
8071d5c3
QY
390 "Failed to send VRRP Advertisement: %s",
391 r->vr->vrid, safe_strerror(errno));
4ec94408 392 }
5435a2bf
QY
393}
394
10133a59
QY
395/*
396 * Receive and parse VRRP advertisement.
397 *
398 * By the time we get here all fields have been validated for basic correctness
399 * and the packet is a valid VRRP packet.
400 *
401 * However, we have not validated whether the VRID is correct for this virtual
402 * router, nor whether the priority is correct (i.e. is not 255 when we are the
403 * address owner).
404 */
405static int vrrp_recv_advertisement(struct vrrp_router *r, struct vrrp_pkt *pkt,
91188ca6 406 size_t pktsize)
5435a2bf 407{
91188ca6
QY
408 char dumpbuf[BUFSIZ];
409 vrrp_pkt_dump(dumpbuf, sizeof(dumpbuf), pkt);
410 zlog_debug("Received VRRP Advertisement:\n%s", dumpbuf);
10133a59
QY
411
412 /* Check that VRID matches our configured VRID */
413 if (pkt->hdr.vrid != r->vr->vrid) {
414 zlog_warn(
415 VRRP_LOGPFX VRRP_LOGPFX_VRID
416 "%s datagram invalid: Advertisement contains VRID %" PRIu8
417 " which does not match our instance",
418 r->vr->vrid, family2str(r->family), pkt->hdr.vrid);
419 return -1;
420 }
421
422 /* Verify that we are not the IPvX address owner */
423 if (r->is_owner) {
424 zlog_warn(
425 VRRP_LOGPFX VRRP_LOGPFX_VRID
426 "%s datagram invalid: Received advertisement but we are the address owner",
427 r->vr->vrid, family2str(r->family));
428 return -1;
429 }
430
431 /* Check that # IPs received matches our # configured IPs */
432 if (pkt->hdr.naddr != r->addrs->count) {
433 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
434 "%s datagram has %" PRIu8
435 " addresses, but this VRRP instance has %u",
436 r->vr->vrid, family2str(r->family), pkt->hdr.naddr,
437 r->addrs->count);
438 }
439
440 switch (r->fsm.state) {
441 case VRRP_STATE_MASTER:
442 if (pkt->hdr.priority == 0) {
443 vrrp_send_advertisement(r);
444 THREAD_OFF(r->t_adver_timer);
445 thread_add_timer_msec(
446 master, vrrp_adver_timer_expire, r,
447 r->vr->advertisement_interval * 10,
448 &r->t_adver_timer);
449 /* FIXME: 6.4.3 mandates checking sender IP address */
450 } else if (pkt->hdr.priority > r->priority) {
451 zlog_err("NOT IMPLEMENTED");
452 THREAD_OFF(r->t_adver_timer);
453 r->master_adver_interval = ntohs(pkt->hdr.v3.adver_int);
454 vrrp_recalculate_timers(r);
455 THREAD_OFF(r->t_master_down_timer);
456 thread_add_timer_msec(master,
457 vrrp_master_down_timer_expire, r,
458 r->master_down_interval * 10,
459 &r->t_master_down_timer);
460 vrrp_change_state(r, VRRP_STATE_BACKUP);
461 } else {
462 /* Discard advertisement */
463 }
464 break;
465 case VRRP_STATE_BACKUP:
466 if (pkt->hdr.priority == 0) {
467 THREAD_OFF(r->t_master_down_timer);
468 thread_add_timer_msec(
469 master, vrrp_master_down_timer_expire, r,
470 r->skew_time * 10, &r->t_master_down_timer);
471 } else if (r->vr->preempt_mode == false
472 || pkt->hdr.priority >= r->priority) {
473 r->master_adver_interval = ntohs(pkt->hdr.v3.adver_int);
474 vrrp_recalculate_timers(r);
475 THREAD_OFF(r->t_master_down_timer);
476 thread_add_timer_msec(master,
477 vrrp_master_down_timer_expire, r,
478 r->master_down_interval * 10,
479 &r->t_master_down_timer);
480 } else if (r->vr->preempt_mode == true
481 && pkt->hdr.priority < r->priority) {
482 /* Discard advertisement */
483 }
484 break;
485 case VRRP_STATE_INITIALIZE:
486 zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID
487 "Received ADVERTISEMENT in state %s; this is a bug",
488 r->vr->vrid, vrrp_state_names[r->fsm.state]);
489 break;
490 }
491
492 return 0;
91188ca6
QY
493}
494
495/*
496 * Read and process next IPvX datagram.
497 */
498static int vrrp_read(struct thread *thread)
499{
500 struct vrrp_router *r = thread->arg;
501
502 struct vrrp_pkt *pkt;
503 ssize_t pktsize;
504 ssize_t nbytes;
505 bool resched;
506 char errbuf[BUFSIZ];
507 uint8_t control[64];
508
509 struct msghdr m;
510 struct iovec iov;
511 iov.iov_base = r->ibuf;
512 iov.iov_len = sizeof(r->ibuf);
513 m.msg_name = NULL;
514 m.msg_namelen = 0;
515 m.msg_iov = &iov;
516 m.msg_iovlen = 1;
517 m.msg_control = control;
518 m.msg_controllen = sizeof(control);
519
dad18a2f 520 nbytes = recvmsg(r->sock_rx, &m, MSG_DONTWAIT);
91188ca6
QY
521
522 if ((nbytes < 0 && ERRNO_IO_RETRY(errno))) {
523 resched = true;
524 goto done;
525 } else if (nbytes <= 0) {
526 vrrp_event(r, VRRP_EVENT_SHUTDOWN);
527 resched = false;
528 goto done;
529 }
530
531 zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID "Received %s datagram: ",
532 r->vr->vrid, family2str(r->family));
533 zlog_hexdump(r->ibuf, nbytes);
534
535 pktsize = vrrp_parse_datagram(r->family, &m, nbytes, &pkt, errbuf,
536 sizeof(errbuf));
537
538 if (pktsize < 0) {
539 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
540 "%s datagram invalid: %s",
541 r->vr->vrid, family2str(r->family), errbuf);
542 } else {
543 zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID "Packet looks good",
544 r->vr->vrid);
545 vrrp_recv_advertisement(r, pkt, pktsize);
546 }
547
548 resched = true;
549
550done:
551 memset(r->ibuf, 0x00, sizeof(r->ibuf));
552
553 if (resched)
dad18a2f
QY
554 thread_add_read(master, vrrp_read, r, r->sock_rx, &r->t_read);
555
556 return 0;
557}
558
559/*
560 * Finds the first connected address of the appropriate family on a VRRP
561 * router's interface and binds the Tx socket of the VRRP router to that
562 * address.
563 *
8071d5c3
QY
564 * Also sets src field of vrrp_router.
565 *
dad18a2f
QY
566 * r
567 * VRRP router to operate on
568 *
569 * Returns:
570 * 0 on success
571 * -1 on failure
572 */
573static int vrrp_bind_to_primary_connected(struct vrrp_router *r)
574{
575 char ipstr[INET6_ADDRSTRLEN];
7e205b4a
QY
576 struct interface *ifp;
577
578 /*
579 * A slight quirk: the RFC specifies that advertisements under IPv6 must
580 * be transmitted using the link local address of the source interface
581 */
582 ifp = r->family == AF_INET ? r->vr->ifp : r->mvl_ifp;
dad18a2f
QY
583
584 struct listnode *ln;
585 struct connected *c = NULL;
7e205b4a 586 for (ALL_LIST_ELEMENTS_RO(ifp->connected, ln, c))
dad18a2f
QY
587 if (c->address->family == r->family)
588 break;
589
590 if (c == NULL) {
591 zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID
592 "Failed to find %s address to bind on %s",
7e205b4a 593 r->vr->vrid, family2str(r->family), ifp->name);
dad18a2f
QY
594 return -1;
595 }
596
7e205b4a
QY
597 union sockunion su;
598 memset(&su, 0x00, sizeof(su));
dad18a2f 599
7e205b4a
QY
600 switch (r->family) {
601 case AF_INET:
8071d5c3
QY
602 r->src.ipa_type = IPADDR_V4;
603 r->src.ipaddr_v4 = c->address->u.prefix4;
7e205b4a
QY
604 su.sin.sin_family = AF_INET;
605 su.sin.sin_addr = c->address->u.prefix4;
606 break;
607 case AF_INET6:
8071d5c3
QY
608 r->src.ipa_type = IPADDR_V6;
609 r->src.ipaddr_v6 = c->address->u.prefix6;
7e205b4a
QY
610 su.sin6.sin6_family = AF_INET6;
611 su.sin6.sin6_scope_id = ifp->ifindex;
612 su.sin6.sin6_addr = c->address->u.prefix6;
613 break;
614 }
dad18a2f
QY
615
616 sockopt_reuseaddr(r->sock_tx);
7e205b4a 617 if (bind(r->sock_tx, (const struct sockaddr *)&su, sizeof(su)) < 0) {
dad18a2f
QY
618 zlog_err(
619 VRRP_LOGPFX VRRP_LOGPFX_VRID
620 "Failed to bind Tx socket to primary IP address %s: %s",
621 r->vr->vrid,
622 inet_ntop(r->family,
623 (const void *)&c->address->u.prefix, ipstr,
624 sizeof(ipstr)),
625 safe_strerror(errno));
626 return -1;
627 } else {
628 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID
629 "Bound Tx socket to primary IP address %s",
630 r->vr->vrid,
631 inet_ntop(r->family,
632 (const void *)&c->address->u.prefix, ipstr,
633 sizeof(ipstr)));
634 }
91188ca6
QY
635
636 return 0;
5435a2bf 637}
5435a2bf
QY
638
639/*
dad18a2f
QY
640 * Creates and configures VRRP router sockets.
641 *
642 * This function:
643 * - Creates two sockets, one for Tx, one for Rx
644 * - Joins the Rx socket to the appropriate VRRP multicast group
645 * - Sets the Tx socket to set the TTL (v4) or Hop Limit (v6) field to 255 for
646 * all transmitted IPvX packets
647 * - Requests the kernel to deliver IPv6 header values needed to validate VRRP
648 * packets
dad18a2f
QY
649 *
650 * If any of the above fail, the sockets are closed. The only exception is if
651 * the TTL / Hop Limit settings fail; these are logged, but configuration
652 * proceeds.
5435a2bf
QY
653 *
654 * The first connected address on the Virtual Router's interface is used as the
655 * interface address.
656 *
862f2f37
QY
657 * r
658 * VRRP Router for which to create listen socket
dad18a2f
QY
659 *
660 * Returns:
661 * 0 on success
662 * -1 on failure
5435a2bf 663 */
862f2f37 664static int vrrp_socket(struct vrrp_router *r)
5435a2bf 665{
5435a2bf 666 int ret;
91188ca6 667 bool failed = false;
5435a2bf 668
dad18a2f
QY
669 frr_elevate_privs(&vrrp_privs)
670 {
671 r->sock_rx = socket(r->family, SOCK_RAW, IPPROTO_VRRP);
672 r->sock_tx = socket(r->family, SOCK_RAW, IPPROTO_VRRP);
5435a2bf
QY
673 }
674
dad18a2f
QY
675 if (r->sock_rx < 0 || r->sock_tx < 0) {
676 const char *rxtx = r->sock_rx < 0 ? "Rx" : "Tx";
4ec94408 677 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
dad18a2f
QY
678 "Can't create %s VRRP %s socket",
679 r->vr->vrid, family2str(r->family), rxtx);
91188ca6
QY
680 failed = true;
681 goto done;
4ec94408 682 }
40744000 683
dad18a2f 684 /* Configure sockets */
862f2f37
QY
685 if (!listcount(r->vr->ifp->connected)) {
686 zlog_warn(
687 VRRP_LOGPFX VRRP_LOGPFX_VRID
688 "No address on interface %s; cannot configure multicast",
689 r->vr->vrid, r->vr->ifp->name);
91188ca6
QY
690 failed = true;
691 goto done;
862f2f37 692 }
a8144d7f 693
91188ca6 694 if (r->family == AF_INET) {
dad18a2f 695 /* Set Tx socket to always Tx with TTL set to 255 */
91188ca6 696 int ttl = 255;
dad18a2f 697 ret = setsockopt(r->sock_tx, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
91188ca6
QY
698 sizeof(ttl));
699 if (ret < 0) {
700 zlog_warn(
701 VRRP_LOGPFX VRRP_LOGPFX_VRID
702 "Failed to set outgoing multicast TTL count to 255; RFC 5798 compliant implementations will drop our packets",
703 r->vr->vrid);
704 }
705
6e9529ed
QY
706 /* Turn off multicast loop on Tx */
707 setsockopt_ipv4_multicast_loop(r->sock_tx, 0);
708
b523b241
QY
709 /* Bind Rx socket to exact interface */
710 vrrp_privs.change(ZPRIVS_RAISE);
711 {
712 ret = setsockopt(r->sock_rx, SOL_SOCKET,
713 SO_BINDTODEVICE, r->vr->ifp->name,
714 strlen(r->vr->ifp->name));
715 }
716 vrrp_privs.change(ZPRIVS_LOWER);
717 if (ret) {
718 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
719 "Failed to bind Rx socket to %s: %s",
720 r->vr->vrid, r->vr->ifp->name,
721 safe_strerror(errno));
722 failed = true;
723 goto done;
724 }
725 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Bound Rx socket to %s",
726 r->vr->vrid, r->vr->ifp->name);
727
728 /* Bind Rx socket to v4 multicast address */
729 struct sockaddr_in sa = {0};
730 sa.sin_family = AF_INET;
731 sa.sin_addr.s_addr = htonl(VRRP_MCASTV4_GROUP);
732 if (bind(r->sock_rx, (struct sockaddr *)&sa, sizeof(sa))) {
733 zlog_err(
734 VRRP_LOGPFX VRRP_LOGPFX_VRID
735 "Failed to bind Rx socket to VRRP %s multicast group: %s",
736 r->vr->vrid, family2str(r->family),
737 safe_strerror(errno));
738 failed = true;
739 goto done;
740 }
741 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID
742 "Bound Rx socket to VRRP %s multicast group",
743 r->vr->vrid, family2str(r->family));
744
dad18a2f
QY
745 /* Join Rx socket to VRRP IPv4 multicast group */
746 struct connected *c = listhead(r->vr->ifp->connected)->data;
91188ca6 747 struct in_addr v4 = c->address->u.prefix4;
dad18a2f
QY
748 ret = setsockopt_ipv4_multicast(r->sock_rx, IP_ADD_MEMBERSHIP,
749 v4, htonl(VRRP_MCASTV4_GROUP),
862f2f37 750 r->vr->ifp->ifindex);
b523b241
QY
751 if (ret < 0) {
752 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
753 "Failed to join VRRP %s multicast group",
754 r->vr->vrid, family2str(r->family));
755 failed = true;
756 goto done;
757 }
758 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID
759 "Joined %s VRRP multicast group",
760 r->vr->vrid, family2str(r->family));
7e205b4a
QY
761
762 /* Set outgoing interface for advertisements */
763 struct ip_mreqn mreqn = {};
764 mreqn.imr_ifindex = r->mvl_ifp->ifindex;
765 ret = setsockopt(r->sock_tx, IPPROTO_IP, IP_MULTICAST_IF,
766 (void *)&mreqn, sizeof(mreqn));
767 if (ret < 0) {
768 zlog_warn(
769 VRRP_LOGPFX VRRP_LOGPFX_VRID
770 "Could not set %s as outgoing multicast interface",
771 r->vr->vrid, r->mvl_ifp->name);
772 failed = true;
773 goto done;
774 }
775 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID
776 "Set %s as outgoing multicast interface",
777 r->vr->vrid, r->mvl_ifp->name);
91188ca6 778 } else if (r->family == AF_INET6) {
dad18a2f
QY
779 /* Always transmit IPv6 packets with hop limit set to 255 */
780 ret = setsockopt_ipv6_multicast_hops(r->sock_tx, 255);
91188ca6
QY
781 if (ret < 0) {
782 zlog_warn(
783 VRRP_LOGPFX VRRP_LOGPFX_VRID
784 "Failed to set outgoing multicast hop count to 255; RFC 5798 compliant implementations will drop our packets",
785 r->vr->vrid);
786 }
dad18a2f 787 ret = setsockopt_ipv6_hoplimit(r->sock_rx, 1);
91188ca6
QY
788 if (ret < 0) {
789 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
790 "Failed to request IPv6 Hop Limit delivery",
791 r->vr->vrid);
792 failed = true;
793 goto done;
794 }
795
6e9529ed
QY
796 /* Turn off multicast loop on Tx */
797 setsockopt_ipv6_multicast_loop(r->sock_tx, 0);
798
b523b241
QY
799 /* Bind Rx socket to exact interface */
800 vrrp_privs.change(ZPRIVS_RAISE);
801 {
802 ret = setsockopt(r->sock_rx, SOL_SOCKET,
803 SO_BINDTODEVICE, r->vr->ifp->name,
804 strlen(r->vr->ifp->name));
805 }
806 vrrp_privs.change(ZPRIVS_LOWER);
807 if (ret) {
808 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
809 "Failed to bind Rx socket to %s: %s",
810 r->vr->vrid, r->vr->ifp->name,
811 safe_strerror(errno));
812 failed = true;
813 goto done;
814 }
815 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Bound Rx socket to %s",
816 r->vr->vrid, r->vr->ifp->name);
817
818 /* Bind Rx socket to v6 multicast address */
819 struct sockaddr_in6 sa = {0};
820 sa.sin6_family = AF_INET6;
821 inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &sa.sin6_addr);
822 if (bind(r->sock_rx, (struct sockaddr *)&sa, sizeof(sa))) {
823 zlog_err(
824 VRRP_LOGPFX VRRP_LOGPFX_VRID
825 "Failed to bind Rx socket to VRRP %s multicast group: %s",
826 r->vr->vrid, family2str(r->family),
827 safe_strerror(errno));
828 failed = true;
829 goto done;
830 }
831 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID
832 "Bound Rx socket to VRRP %s multicast group",
833 r->vr->vrid, family2str(r->family));
834
91188ca6 835 /* Join VRRP IPv6 multicast group */
862f2f37 836 struct ipv6_mreq mreq;
dad18a2f
QY
837 inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR,
838 &mreq.ipv6mr_multiaddr);
862f2f37 839 mreq.ipv6mr_interface = r->vr->ifp->ifindex;
dad18a2f
QY
840 ret = setsockopt(r->sock_rx, IPPROTO_IPV6, IPV6_JOIN_GROUP,
841 &mreq, sizeof(mreq));
b523b241
QY
842 if (ret < 0) {
843 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
844 "Failed to join VRRP %s multicast group",
845 r->vr->vrid, family2str(r->family));
846 failed = true;
847 goto done;
848 }
849 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID
850 "Joined %s VRRP multicast group",
851 r->vr->vrid, family2str(r->family));
7e205b4a
QY
852
853 /* Set outgoing interface for advertisements */
854 ret = setsockopt(r->sock_tx, IPPROTO_IPV6, IPV6_MULTICAST_IF,
855 &r->mvl_ifp->ifindex, sizeof(ifindex_t));
856 if (ret < 0) {
857 zlog_warn(
858 VRRP_LOGPFX VRRP_LOGPFX_VRID
859 "Could not set %s as outgoing multicast interface",
860 r->vr->vrid, r->mvl_ifp->name);
861 failed = true;
862 goto done;
863 }
864 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID
865 "Set %s as outgoing multicast interface",
866 r->vr->vrid, r->mvl_ifp->name);
862f2f37
QY
867 }
868
dad18a2f
QY
869 /* Bind Tx socket to link-local address */
870 if (vrrp_bind_to_primary_connected(r) < 0) {
871 failed = true;
872 goto done;
5435a2bf 873 }
dad18a2f 874
91188ca6
QY
875done:
876 ret = 0;
877 if (failed) {
878 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
879 "Failed to initialize VRRP %s router",
880 r->vr->vrid, family2str(r->family));
dad18a2f
QY
881 if (r->sock_rx >= 0)
882 close(r->sock_rx);
883 if (r->sock_tx >= 0)
884 close(r->sock_tx);
91188ca6
QY
885 ret = -1;
886 }
887
888 return ret;
5435a2bf
QY
889}
890
891
892/* State machine ----------------------------------------------------------- */
893
862f2f37 894DEFINE_HOOK(vrrp_change_state_hook, (struct vrrp_router * r, int to), (r, to));
5435a2bf
QY
895
896/*
897 * Handle any necessary actions during state change to MASTER state.
898 *
862f2f37
QY
899 * r
900 * VRRP Router to operate on
5435a2bf 901 */
862f2f37 902static void vrrp_change_state_master(struct vrrp_router *r)
5435a2bf 903{
f3fe0047
QY
904 /* Enable ND Router Advertisements */
905 if (r->family == AF_INET6)
906 vrrp_zebra_radv_set(r, true);
5435a2bf
QY
907}
908
909/*
910 * Handle any necessary actions during state change to BACKUP state.
911 *
862f2f37 912 * r
5435a2bf
QY
913 * Virtual Router to operate on
914 */
862f2f37 915static void vrrp_change_state_backup(struct vrrp_router *r)
5435a2bf 916{
f3fe0047
QY
917 /* Disable ND Router Advertisements */
918 if (r->family == AF_INET6)
919 vrrp_zebra_radv_set(r, false);
5435a2bf
QY
920}
921
922/*
923 * Handle any necessary actions during state change to INITIALIZE state.
924 *
925 * This is not called for initial startup, only when transitioning from MASTER
926 * or BACKUP.
927 *
862f2f37
QY
928 * r
929 * VRRP Router to operate on
5435a2bf 930 */
862f2f37 931static void vrrp_change_state_initialize(struct vrrp_router *r)
5435a2bf 932{
862f2f37
QY
933 r->vr->advertisement_interval = r->vr->advertisement_interval;
934 r->master_adver_interval = 0;
935 vrrp_recalculate_timers(r);
f3fe0047
QY
936
937 /* Disable ND Router Advertisements */
938 if (r->family == AF_INET6)
939 vrrp_zebra_radv_set(r, false);
5435a2bf
QY
940}
941
862f2f37 942void (*vrrp_change_state_handlers[])(struct vrrp_router *vr) = {
5435a2bf
QY
943 [VRRP_STATE_MASTER] = vrrp_change_state_master,
944 [VRRP_STATE_BACKUP] = vrrp_change_state_backup,
945 [VRRP_STATE_INITIALIZE] = vrrp_change_state_initialize,
946};
947
948/*
949 * Change Virtual Router FSM position. Handles transitional actions and calls
950 * any subscribers to the state change hook.
951 *
862f2f37 952 * r
5435a2bf
QY
953 * Virtual Router for which to change state
954 *
955 * to
956 * State to change to
957 */
862f2f37 958static void vrrp_change_state(struct vrrp_router *r, int to)
5435a2bf 959{
6287cefe
QY
960 if (r->fsm.state == to)
961 return;
962
5435a2bf 963 /* Call our handlers, then any subscribers */
862f2f37
QY
964 vrrp_change_state_handlers[to](r);
965 hook_call(vrrp_change_state_hook, r, to);
966 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "%s -> %s", r->vr->vrid,
967 vrrp_state_names[r->fsm.state], vrrp_state_names[to]);
968 r->fsm.state = to;
5435a2bf
QY
969}
970
971/*
972 * Called when Adver_Timer expires.
973 */
974static int vrrp_adver_timer_expire(struct thread *thread)
975{
862f2f37 976 struct vrrp_router *r = thread->arg;
5435a2bf 977
862f2f37
QY
978 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Adver_Timer expired",
979 r->vr->vrid);
4ec94408 980
862f2f37 981 if (r->fsm.state == VRRP_STATE_MASTER) {
3e7a4043 982 /* Send an ADVERTISEMENT */
862f2f37 983 vrrp_send_advertisement(r);
5435a2bf 984
3e7a4043 985 /* Reset the Adver_Timer to Advertisement_Interval */
862f2f37
QY
986 thread_add_timer_msec(master, vrrp_adver_timer_expire, r,
987 r->vr->advertisement_interval * 10,
988 &r->t_adver_timer);
3e7a4043
QY
989 } else {
990 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
991 "Adver_Timer expired in state '%s'; this is a bug",
862f2f37 992 r->vr->vrid, vrrp_state_names[r->fsm.state]);
5435a2bf 993 }
3e7a4043 994
5435a2bf
QY
995 return 0;
996}
997
998/*
4ec94408 999 * Called when Master_Down_Timer expires.
5435a2bf
QY
1000 */
1001static int vrrp_master_down_timer_expire(struct thread *thread)
1002{
862f2f37 1003 struct vrrp_router *r = thread->arg;
4ec94408
QY
1004
1005 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Master_Down_Timer expired",
862f2f37 1006 r->vr->vrid);
5435a2bf 1007
c7e3b83d
QY
1008 vrrp_send_advertisement(r);
1009 if (r->family == AF_INET)
1010 vrrp_garp_send_all(r);
4f52e9a6
QY
1011 if (r->family == AF_INET6)
1012 vrrp_ndisc_una_send_all(r);
c7e3b83d
QY
1013 thread_add_timer_msec(master, vrrp_adver_timer_expire, r,
1014 r->vr->advertisement_interval * 10,
1015 &r->t_adver_timer);
1016 vrrp_change_state(r, VRRP_STATE_MASTER);
1017
5435a2bf
QY
1018 return 0;
1019}
1020
1021/*
1022 * Event handler for Startup event.
1023 *
1024 * Creates sockets, sends advertisements and ARP requests, starts timers,
1d21789e
QY
1025 * and transitions the Virtual Router to either Master or Backup states.
1026 *
1027 * This function will also initialize the program's global ARP subsystem if it
1028 * has not yet been initialized.
5435a2bf 1029 *
862f2f37
QY
1030 * r
1031 * VRRP Router on which to apply Startup event
1d21789e
QY
1032 *
1033 * Returns:
1034 * < 0 if the session socket could not be created, or the state is not
1035 * Initialize
1036 * 0 on success
5435a2bf 1037 */
862f2f37 1038static int vrrp_startup(struct vrrp_router *r)
5435a2bf 1039{
1d21789e 1040 /* May only be called when the state is Initialize */
862f2f37 1041 if (r->fsm.state != VRRP_STATE_INITIALIZE)
1d21789e
QY
1042 return -1;
1043
7e205b4a 1044 /* Must have a valid macvlan interface available */
85467974 1045 if (r->mvl_ifp == NULL && !vrrp_attach_interface(r)) {
7e205b4a
QY
1046 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
1047 "No appropriate interface for %s VRRP found",
1048 r->vr->vrid, family2str(r->family));
1049 return -1;
1050 }
1051
40744000 1052 /* Initialize global gratuitous ARP socket if necessary */
862f2f37 1053 if (r->family == AF_INET && !vrrp_garp_is_init())
40744000 1054 vrrp_garp_init();
4f52e9a6
QY
1055 if (r->family == AF_INET6 && !vrrp_ndisc_is_init())
1056 vrrp_ndisc_init();
40744000 1057
5435a2bf 1058 /* Create socket */
dad18a2f 1059 if (r->sock_rx < 0 || r->sock_tx < 0) {
862f2f37 1060 int ret = vrrp_socket(r);
dad18a2f 1061 if (ret < 0 || r->sock_tx < 0 || r->sock_rx < 0)
862f2f37
QY
1062 return ret;
1063 }
5435a2bf
QY
1064
1065 /* Schedule listener */
dad18a2f 1066 thread_add_read(master, vrrp_read, r, r->sock_rx, &r->t_read);
5435a2bf 1067
91188ca6 1068 /* Configure effective priority */
862f2f37
QY
1069 struct ipaddr *primary = (struct ipaddr *)listhead(r->addrs)->data;
1070
1071 char ipbuf[INET6_ADDRSTRLEN];
1072 inet_ntop(r->family, &primary->ip.addr, ipbuf, sizeof(ipbuf));
1073
7e205b4a 1074 if (vrrp_is_owner(r->vr->ifp, primary)) {
862f2f37
QY
1075 r->priority = VRRP_PRIO_MASTER;
1076 vrrp_recalculate_timers(r);
1077
5d3730c5
QY
1078 zlog_info(
1079 VRRP_LOGPFX VRRP_LOGPFX_VRID
1080 "%s owns primary Virtual Router IP %s; electing self as Master",
862f2f37 1081 r->vr->vrid, r->vr->ifp->name, ipbuf);
5d3730c5
QY
1082 }
1083
862f2f37
QY
1084 if (r->priority == VRRP_PRIO_MASTER) {
1085 vrrp_send_advertisement(r);
5435a2bf 1086
862f2f37
QY
1087 if (r->family == AF_INET)
1088 vrrp_garp_send_all(r);
4f52e9a6
QY
1089 if (r->family == AF_INET6)
1090 vrrp_ndisc_una_send_all(r);
862f2f37
QY
1091
1092 thread_add_timer_msec(master, vrrp_adver_timer_expire, r,
1093 r->vr->advertisement_interval * 10,
1094 &r->t_adver_timer);
1095 vrrp_change_state(r, VRRP_STATE_MASTER);
5435a2bf 1096 } else {
862f2f37
QY
1097 r->master_adver_interval = r->vr->advertisement_interval;
1098 vrrp_recalculate_timers(r);
1099 thread_add_timer_msec(master, vrrp_master_down_timer_expire, r,
1100 r->master_down_interval * 10,
1101 &r->t_master_down_timer);
1102 vrrp_change_state(r, VRRP_STATE_BACKUP);
5435a2bf 1103 }
a8144d7f 1104
862f2f37
QY
1105 r->is_active = true;
1106
a8144d7f 1107 return 0;
5435a2bf
QY
1108}
1109
1d21789e
QY
1110/*
1111 * Shuts down a Virtual Router and transitions it to Initialize.
1112 *
1113 * This call must be idempotent; it is safe to call multiple times on the same
862f2f37 1114 * VRRP Router.
1d21789e 1115 */
862f2f37 1116static int vrrp_shutdown(struct vrrp_router *r)
5435a2bf 1117{
862f2f37
QY
1118 switch (r->fsm.state) {
1119 case VRRP_STATE_MASTER:
1120 /* Cancel the Adver_Timer */
1121 THREAD_OFF(r->t_adver_timer);
1122 /* Send an ADVERTISEMENT with Priority = 0 */
1123 uint8_t saved_prio = r->priority;
1124 r->priority = 0;
1125 vrrp_send_advertisement(r);
1126 r->priority = saved_prio;
1127 break;
1128 case VRRP_STATE_BACKUP:
1129 /* Cancel the Master_Down_Timer */
1130 THREAD_OFF(r->t_master_down_timer);
1131 break;
1132 case VRRP_STATE_INITIALIZE:
1133 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID
1134 "Received '%s' event in '%s' state; ignoring",
1135 r->vr->vrid, vrrp_event_names[VRRP_EVENT_SHUTDOWN],
1136 vrrp_state_names[VRRP_STATE_INITIALIZE]);
1137 break;
3e7a4043
QY
1138 }
1139
3e7a4043 1140 /* Transition to the Initialize state */
862f2f37 1141 vrrp_change_state(r, VRRP_STATE_INITIALIZE);
1d21789e 1142
73b5cb19
QY
1143 r->is_active = false;
1144
a8144d7f 1145 return 0;
5435a2bf
QY
1146}
1147
862f2f37 1148static int (*vrrp_event_handlers[])(struct vrrp_router *r) = {
5435a2bf
QY
1149 [VRRP_EVENT_STARTUP] = vrrp_startup,
1150 [VRRP_EVENT_SHUTDOWN] = vrrp_shutdown,
1151};
1152
1153/*
862f2f37 1154 * Spawn a VRRP FSM event on a VRRP Router.
5435a2bf
QY
1155 *
1156 * vr
862f2f37 1157 * VRRP Router on which to spawn event
5435a2bf
QY
1158 *
1159 * event
1160 * The event to spawn
1161 */
862f2f37 1162int vrrp_event(struct vrrp_router *r, int event)
5435a2bf 1163{
862f2f37
QY
1164 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "'%s' event", r->vr->vrid,
1165 vrrp_event_names[r->fsm.state]);
1166 return vrrp_event_handlers[event](r);
5435a2bf
QY
1167}
1168
1169
1170/* Other ------------------------------------------------------------------- */
1171
1172static unsigned int vrrp_hash_key(void *arg)
1173{
1174 struct vrrp_vrouter *vr = arg;
1175
4f0b6b45
QY
1176 char key[IFNAMSIZ + 64];
1177 snprintf(key, sizeof(key), "%d%s%u", vr->ifp->ifindex, vr->ifp->name,
1178 vr->vrid);
1179
1180 return string_hash_make(key);
5435a2bf
QY
1181}
1182
1183static bool vrrp_hash_cmp(const void *arg1, const void *arg2)
1184{
1185 const struct vrrp_vrouter *vr1 = arg1;
1186 const struct vrrp_vrouter *vr2 = arg2;
1187
4f0b6b45
QY
1188 if (vr1->ifp != vr2->ifp)
1189 return 0;
1190 if (vr1->vrid != vr2->vrid)
1191 return 0;
1192
1193 return 1;
5435a2bf
QY
1194}
1195
1196void vrrp_init(void)
1197{
1198 vrrp_vrouters_hash = hash_create(&vrrp_hash_key, vrrp_hash_cmp,
1199 "VRRP virtual router hash");
1200 vrf_init(NULL, NULL, NULL, NULL, NULL);
1201}