]> git.proxmox.com Git - mirror_frr.git/blame - vrrpd/vrrp.c
vrrpd: fix memleak when sending advertisements
[mirror_frr.git] / vrrpd / vrrp.c
CommitLineData
5435a2bf 1/*
1d21789e 2 * VRRPD global definitions and state machine
5435a2bf
QY
3 * Copyright (C) 2018 Cumulus Networks, Inc.
4 * Quentin Young
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"
40744000 30#include "lib/vrf.h"
5435a2bf
QY
31
32#include "vrrp.h"
40744000 33#include "vrrp_arp.h"
247aa469 34#include "vrrp_packet.h"
5435a2bf 35
4ec94408
QY
36#define VRRP_LOGPFX "[CORE] "
37
38const char *vrrp_state_names[3] = {
39 [VRRP_STATE_INITIALIZE] = "Initialize",
40 [VRRP_STATE_MASTER] = "Master",
41 [VRRP_STATE_BACKUP] = "Backup",
42};
43
44const char *vrrp_event_names[2] = {
45 [VRRP_EVENT_STARTUP] = "Startup",
46 [VRRP_EVENT_SHUTDOWN] = "Shutdown",
47};
48
49
5435a2bf
QY
50/* Utility functions ------------------------------------------------------- */
51
52/*
53 * Sets an ethaddr to RFC-defined Virtual Router MAC address.
54 *
55 * mac
56 * ethaddr to set
57 *
58 * v6
59 * Whether this is a V6 or V4 Virtual Router MAC
60 *
61 * vrid
62 * Virtual Router Identifier
63 */
64static void vrrp_mac_set(struct ethaddr *mac, bool v6, uint8_t vrid)
65{
66 /*
67 * V4: 00-00-5E-00-01-{VRID}
68 * V6: 00-00-5E-00-02-{VRID}
69 */
70 mac->octet[0] = 0x00;
71 mac->octet[1] = 0x00;
72 mac->octet[2] = 0x5E;
73 mac->octet[3] = 0x00;
74 mac->octet[4] = v6 ? 0x02 : 0x01;
75 mac->octet[5] = vrid;
76}
77
1d21789e 78/*
862f2f37 79 * Recalculates and sets skew_time and master_down_interval based
1d21789e
QY
80 * values.
81 *
862f2f37
QY
82 * r
83 * VRRP Router to operate on
1d21789e 84 */
862f2f37 85static void vrrp_recalculate_timers(struct vrrp_router *r)
1d21789e 86{
862f2f37
QY
87 r->skew_time =
88 ((256 - r->vr->priority) * r->master_adver_interval) / 256;
89 r->master_down_interval = (3 * r->master_adver_interval);
90 r->master_down_interval += r->skew_time;
1d21789e
QY
91}
92
5d3730c5
QY
93/*
94 * Determines if a VRRP router is the owner of the specified address.
95 *
96 * vr
862f2f37 97 * Virtual Router
5d3730c5
QY
98 *
99 * Returns:
100 * whether or not vr owns the specified address
101 */
102static bool vrrp_is_owner(struct vrrp_vrouter *vr, struct ipaddr *addr)
103{
104 struct prefix *p;
105 struct prefix_ipv4 p4;
106 struct prefix_ipv6 p6;
107
108 if (IS_IPADDR_V4(addr)) {
109 p4.family = AF_INET;
110 p4.prefixlen = IPV4_MAX_BITLEN;
111 p4.prefix = addr->ipaddr_v4;
112 p = (struct prefix *)&p4;
113 } else {
114 p6.family = AF_INET6;
115 p6.prefixlen = IPV6_MAX_BITLEN;
116 memcpy(&p6.prefix, &addr->ipaddr_v6, sizeof(struct in6_addr));
117 p = (struct prefix *)&p6;
118 }
119
120 return !!connected_lookup_prefix_exact(vr->ifp, p);
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;
c23edd74
QY
131}
132
1d21789e
QY
133void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr,
134 uint16_t advertisement_interval)
135{
136 if (vr->advertisement_interval == advertisement_interval)
137 return;
138
862f2f37
QY
139 vr->advertisement_interval = advertisement_interval;
140 vrrp_recalculate_timers(vr->v4);
141 vrrp_recalculate_timers(vr->v6);
142}
143
144void vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4)
145{
146 struct ipaddr *v4_ins = XCALLOC(MTYPE_TMP, sizeof(struct ipaddr));
147
148 v4_ins->ipa_type = IPADDR_V4;
149 v4_ins->ipaddr_v4 = v4;
150 listnode_add(vr->v4->addrs, v4_ins);
1d21789e
QY
151}
152
862f2f37 153void vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6)
c23edd74 154{
862f2f37 155 struct ipaddr *v6_ins = XCALLOC(MTYPE_TMP, sizeof(struct ipaddr));
c23edd74 156
862f2f37
QY
157 v6_ins->ipa_type = IPADDR_V6;
158 memcpy(&v6_ins->ipaddr_v6, &v6, sizeof(struct in6_addr));
159 listnode_add(vr->v6->addrs, v6_ins);
160}
161
162void vrrp_add_ip(struct vrrp_vrouter *vr, struct ipaddr ip)
163{
164 if (ip.ipa_type == IPADDR_V4)
165 vrrp_add_ipv4(vr, ip.ipaddr_v4);
166 else if (ip.ipa_type == IPADDR_V6)
167 vrrp_add_ipv6(vr, ip.ipaddr_v6);
c23edd74
QY
168}
169
1d21789e
QY
170
171/* Creation and destruction ------------------------------------------------ */
172
862f2f37
QY
173static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr,
174 int family)
175{
176 struct vrrp_router *r = XCALLOC(MTYPE_TMP, sizeof(struct vrrp_router));
177
862f2f37 178 r->family = family;
91188ca6 179 r->sock = -1;
862f2f37
QY
180 r->vr = vr;
181 r->addrs = list_new();
182 r->priority = vr->priority;
183 r->fsm.state = VRRP_STATE_INITIALIZE;
184 vrrp_mac_set(&r->vmac, family == AF_INET6, vr->vrid);
185
186 return r;
187}
188
189static void vrrp_router_destroy(struct vrrp_router *r)
190{
191 if (r->sock >= 0)
192 close(r->sock);
193 /* FIXME: also delete list elements */
194 list_delete(&r->addrs);
195 XFREE(MTYPE_TMP, r);
196}
197
5435a2bf
QY
198struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid)
199{
200 struct vrrp_vrouter *vr =
201 XCALLOC(MTYPE_TMP, sizeof(struct vrrp_vrouter));
202
5435a2bf
QY
203 vr->ifp = ifp;
204 vr->vrid = vrid;
5435a2bf 205 vr->priority = VRRP_DEFAULT_PRIORITY;
5435a2bf
QY
206 vr->preempt_mode = true;
207 vr->accept_mode = false;
862f2f37
QY
208
209 vr->v4 = vrrp_router_create(vr, AF_INET);
210 vr->v6 = vrrp_router_create(vr, AF_INET6);
211
1485d9e5 212 vrrp_set_advertisement_interval(vr, VRRP_DEFAULT_ADVINT);
5435a2bf
QY
213
214 hash_get(vrrp_vrouters_hash, vr, hash_alloc_intern);
215
216 return vr;
217}
218
c23edd74
QY
219void vrrp_vrouter_destroy(struct vrrp_vrouter *vr)
220{
c23edd74 221 vr->ifp = NULL;
862f2f37
QY
222 vrrp_router_destroy(vr->v4);
223 vrrp_router_destroy(vr->v6);
c23edd74
QY
224 hash_release(vrrp_vrouters_hash, vr);
225 XFREE(MTYPE_TMP, vr);
226}
227
5435a2bf
QY
228struct vrrp_vrouter *vrrp_lookup(uint8_t vrid)
229{
230 struct vrrp_vrouter vr;
231 vr.vrid = vrid;
232
233 return hash_lookup(vrrp_vrouters_hash, &vr);
234}
235
236/* Network ----------------------------------------------------------------- */
237
238/*
91188ca6 239 * Create and multicast a VRRP ADVERTISEMENT message.
5435a2bf 240 *
862f2f37
QY
241 * r
242 * VRRP Router for which to send ADVERTISEMENT
5435a2bf 243 */
862f2f37 244static void vrrp_send_advertisement(struct vrrp_router *r)
5435a2bf 245{
247aa469
QY
246 struct vrrp_pkt *pkt;
247 ssize_t pktlen;
862f2f37
QY
248 struct ipaddr *addrs[r->addrs->count];
249 union sockunion dest;
247aa469 250
862f2f37 251 list_to_array(r->addrs, (void **)addrs, r->addrs->count);
247aa469 252
862f2f37
QY
253 pktlen = vrrp_pkt_build(&pkt, r->vr->vrid, r->priority,
254 r->vr->advertisement_interval, r->addrs->count,
255 (struct ipaddr **)&addrs);
247aa469
QY
256
257 if (pktlen > 0)
258 zlog_hexdump(pkt, (size_t) pktlen);
259 else
260 zlog_warn("Could not build VRRP packet");
261
862f2f37
QY
262 const char *group =
263 r->family == AF_INET ? VRRP_MCASTV4_GROUP_STR : VRRP_MCASTV6_GROUP_STR;
264 str2sockunion(group, &dest);
247aa469 265
862f2f37
QY
266 ssize_t sent = sendto(r->sock, pkt, (size_t)pktlen, 0, &dest.sa,
267 sockunion_sizeof(&dest));
4ec94408 268
bb54fa3a
QY
269 XFREE(MTYPE_TMP, pkt);
270
4ec94408
QY
271 if (sent < 0) {
272 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
273 "Failed to send VRRP Advertisement",
862f2f37 274 r->vr->vrid);
4ec94408 275 }
5435a2bf
QY
276}
277
91188ca6
QY
278static void vrrp_recv_advertisement(struct vrrp_router *r, struct vrrp_pkt *pkt,
279 size_t pktsize)
5435a2bf 280{
91188ca6
QY
281 char dumpbuf[BUFSIZ];
282 vrrp_pkt_dump(dumpbuf, sizeof(dumpbuf), pkt);
283 zlog_debug("Received VRRP Advertisement:\n%s", dumpbuf);
284}
285
286/*
287 * Read and process next IPvX datagram.
288 */
289static int vrrp_read(struct thread *thread)
290{
291 struct vrrp_router *r = thread->arg;
292
293 struct vrrp_pkt *pkt;
294 ssize_t pktsize;
295 ssize_t nbytes;
296 bool resched;
297 char errbuf[BUFSIZ];
298 uint8_t control[64];
299
300 struct msghdr m;
301 struct iovec iov;
302 iov.iov_base = r->ibuf;
303 iov.iov_len = sizeof(r->ibuf);
304 m.msg_name = NULL;
305 m.msg_namelen = 0;
306 m.msg_iov = &iov;
307 m.msg_iovlen = 1;
308 m.msg_control = control;
309 m.msg_controllen = sizeof(control);
310
311 nbytes = recvmsg(r->sock, &m, MSG_DONTWAIT);
312
313 if ((nbytes < 0 && ERRNO_IO_RETRY(errno))) {
314 resched = true;
315 goto done;
316 } else if (nbytes <= 0) {
317 vrrp_event(r, VRRP_EVENT_SHUTDOWN);
318 resched = false;
319 goto done;
320 }
321
322 zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID "Received %s datagram: ",
323 r->vr->vrid, family2str(r->family));
324 zlog_hexdump(r->ibuf, nbytes);
325
326 pktsize = vrrp_parse_datagram(r->family, &m, nbytes, &pkt, errbuf,
327 sizeof(errbuf));
328
329 if (pktsize < 0) {
330 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
331 "%s datagram invalid: %s",
332 r->vr->vrid, family2str(r->family), errbuf);
333 } else {
334 zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID "Packet looks good",
335 r->vr->vrid);
336 vrrp_recv_advertisement(r, pkt, pktsize);
337 }
338
339 resched = true;
340
341done:
342 memset(r->ibuf, 0x00, sizeof(r->ibuf));
343
344 if (resched)
345 thread_add_read(master, vrrp_read, r, r->sock, &r->t_read);
346
347 return 0;
5435a2bf 348}
5435a2bf
QY
349
350/*
351 * Create Virtual Router listen socket and join it to the VRRP multicast group.
352 *
353 * The first connected address on the Virtual Router's interface is used as the
354 * interface address.
355 *
862f2f37
QY
356 * r
357 * VRRP Router for which to create listen socket
5435a2bf 358 */
862f2f37 359static int vrrp_socket(struct vrrp_router *r)
5435a2bf 360{
5435a2bf 361 int ret;
91188ca6 362 bool failed = false;
a8144d7f 363 struct connected *c;
5435a2bf 364
40744000 365 frr_elevate_privs(&vrrp_privs) {
862f2f37 366 r->sock = socket(r->family, SOCK_RAW, IPPROTO_VRRP);
5435a2bf
QY
367 }
368
862f2f37 369 if (r->sock < 0) {
4ec94408 370 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
862f2f37
QY
371 "Can't create %s VRRP socket",
372 r->vr->vrid, r->family == AF_INET ? "v4" : "v6");
91188ca6
QY
373 failed = true;
374 goto done;
4ec94408 375 }
40744000 376
862f2f37
QY
377 if (!listcount(r->vr->ifp->connected)) {
378 zlog_warn(
379 VRRP_LOGPFX VRRP_LOGPFX_VRID
380 "No address on interface %s; cannot configure multicast",
381 r->vr->vrid, r->vr->ifp->name);
91188ca6
QY
382 failed = true;
383 goto done;
862f2f37 384 }
a8144d7f 385
91188ca6
QY
386 if (r->family == AF_INET) {
387 int ttl = 255;
388 ret = setsockopt(r->sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
389 sizeof(ttl));
390 if (ret < 0) {
391 zlog_warn(
392 VRRP_LOGPFX VRRP_LOGPFX_VRID
393 "Failed to set outgoing multicast TTL count to 255; RFC 5798 compliant implementations will drop our packets",
394 r->vr->vrid);
395 }
396
397 c = listhead(r->vr->ifp->connected)->data;
398 struct in_addr v4 = c->address->u.prefix4;
399
400 /* Join VRRP IPv4 multicast group */
862f2f37
QY
401 ret = setsockopt_ipv4_multicast(r->sock, IP_ADD_MEMBERSHIP, v4,
402 htonl(VRRP_MCASTV4_GROUP),
403 r->vr->ifp->ifindex);
91188ca6
QY
404 } else if (r->family == AF_INET6) {
405 ret = setsockopt_ipv6_multicast_hops(r->sock, 255);
406 if (ret < 0) {
407 zlog_warn(
408 VRRP_LOGPFX VRRP_LOGPFX_VRID
409 "Failed to set outgoing multicast hop count to 255; RFC 5798 compliant implementations will drop our packets",
410 r->vr->vrid);
411 }
412 ret = setsockopt_ipv6_hoplimit(r->sock, 1);
413 if (ret < 0) {
414 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
415 "Failed to request IPv6 Hop Limit delivery",
416 r->vr->vrid);
417 failed = true;
418 goto done;
419 }
420
421 /* Join VRRP IPv6 multicast group */
862f2f37
QY
422 struct ipv6_mreq mreq;
423 inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &mreq.ipv6mr_multiaddr);
424 mreq.ipv6mr_interface = r->vr->ifp->ifindex;
425 ret = setsockopt(r->sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
426 sizeof(mreq));
427 }
428
5435a2bf 429 if (ret < 0) {
4ec94408 430 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
91188ca6
QY
431 "Failed to join VRRP %s multicast group",
432 r->vr->vrid, family2str(r->family));
433 failed = true;
5435a2bf 434 }
91188ca6
QY
435done:
436 ret = 0;
437 if (failed) {
438 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
439 "Failed to initialize VRRP %s router",
440 r->vr->vrid, family2str(r->family));
441 if (r->sock >= 0)
442 close(r->sock);
443 ret = -1;
444 }
445
446 return ret;
5435a2bf
QY
447}
448
449
450/* State machine ----------------------------------------------------------- */
451
862f2f37 452DEFINE_HOOK(vrrp_change_state_hook, (struct vrrp_router * r, int to), (r, to));
5435a2bf
QY
453
454/*
455 * Handle any necessary actions during state change to MASTER state.
456 *
862f2f37
QY
457 * r
458 * VRRP Router to operate on
5435a2bf 459 */
862f2f37 460static void vrrp_change_state_master(struct vrrp_router *r)
5435a2bf 461{
862f2f37 462 /* NOTHING */
5435a2bf
QY
463}
464
465/*
466 * Handle any necessary actions during state change to BACKUP state.
467 *
862f2f37 468 * r
5435a2bf
QY
469 * Virtual Router to operate on
470 */
862f2f37 471static void vrrp_change_state_backup(struct vrrp_router *r)
5435a2bf 472{
862f2f37 473 /* Uninstall ARP entry for router MAC */
5435a2bf
QY
474 /* ... */
475}
476
477/*
478 * Handle any necessary actions during state change to INITIALIZE state.
479 *
480 * This is not called for initial startup, only when transitioning from MASTER
481 * or BACKUP.
482 *
862f2f37
QY
483 * r
484 * VRRP Router to operate on
5435a2bf 485 */
862f2f37 486static void vrrp_change_state_initialize(struct vrrp_router *r)
5435a2bf 487{
862f2f37
QY
488 r->vr->advertisement_interval = r->vr->advertisement_interval;
489 r->master_adver_interval = 0;
490 vrrp_recalculate_timers(r);
5435a2bf
QY
491}
492
862f2f37 493void (*vrrp_change_state_handlers[])(struct vrrp_router *vr) = {
5435a2bf
QY
494 [VRRP_STATE_MASTER] = vrrp_change_state_master,
495 [VRRP_STATE_BACKUP] = vrrp_change_state_backup,
496 [VRRP_STATE_INITIALIZE] = vrrp_change_state_initialize,
497};
498
499/*
500 * Change Virtual Router FSM position. Handles transitional actions and calls
501 * any subscribers to the state change hook.
502 *
862f2f37 503 * r
5435a2bf
QY
504 * Virtual Router for which to change state
505 *
506 * to
507 * State to change to
508 */
862f2f37 509static void vrrp_change_state(struct vrrp_router *r, int to)
5435a2bf
QY
510{
511 /* Call our handlers, then any subscribers */
862f2f37
QY
512 vrrp_change_state_handlers[to](r);
513 hook_call(vrrp_change_state_hook, r, to);
514 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "%s -> %s", r->vr->vrid,
515 vrrp_state_names[r->fsm.state], vrrp_state_names[to]);
516 r->fsm.state = to;
5435a2bf
QY
517}
518
519/*
520 * Called when Adver_Timer expires.
521 */
522static int vrrp_adver_timer_expire(struct thread *thread)
523{
862f2f37 524 struct vrrp_router *r = thread->arg;
5435a2bf 525
862f2f37
QY
526 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Adver_Timer expired",
527 r->vr->vrid);
4ec94408 528
862f2f37 529 if (r->fsm.state == VRRP_STATE_MASTER) {
3e7a4043 530 /* Send an ADVERTISEMENT */
862f2f37 531 vrrp_send_advertisement(r);
5435a2bf 532
3e7a4043 533 /* Reset the Adver_Timer to Advertisement_Interval */
862f2f37
QY
534 thread_add_timer_msec(master, vrrp_adver_timer_expire, r,
535 r->vr->advertisement_interval * 10,
536 &r->t_adver_timer);
3e7a4043
QY
537 } else {
538 zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
539 "Adver_Timer expired in state '%s'; this is a bug",
862f2f37 540 r->vr->vrid, vrrp_state_names[r->fsm.state]);
5435a2bf 541 }
3e7a4043 542
5435a2bf
QY
543 return 0;
544}
545
546/*
4ec94408 547 * Called when Master_Down_Timer expires.
5435a2bf
QY
548 */
549static int vrrp_master_down_timer_expire(struct thread *thread)
550{
862f2f37 551 struct vrrp_router *r = thread->arg;
4ec94408
QY
552
553 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Master_Down_Timer expired",
862f2f37 554 r->vr->vrid);
5435a2bf
QY
555
556 return 0;
557}
558
559/*
560 * Event handler for Startup event.
561 *
562 * Creates sockets, sends advertisements and ARP requests, starts timers,
1d21789e
QY
563 * and transitions the Virtual Router to either Master or Backup states.
564 *
565 * This function will also initialize the program's global ARP subsystem if it
566 * has not yet been initialized.
5435a2bf 567 *
862f2f37
QY
568 * r
569 * VRRP Router on which to apply Startup event
1d21789e
QY
570 *
571 * Returns:
572 * < 0 if the session socket could not be created, or the state is not
573 * Initialize
574 * 0 on success
5435a2bf 575 */
862f2f37 576static int vrrp_startup(struct vrrp_router *r)
5435a2bf 577{
1d21789e 578 /* May only be called when the state is Initialize */
862f2f37 579 if (r->fsm.state != VRRP_STATE_INITIALIZE)
1d21789e
QY
580 return -1;
581
40744000 582 /* Initialize global gratuitous ARP socket if necessary */
862f2f37 583 if (r->family == AF_INET && !vrrp_garp_is_init())
40744000
QY
584 vrrp_garp_init();
585
5435a2bf 586 /* Create socket */
862f2f37
QY
587 if (r->sock < 0) {
588 int ret = vrrp_socket(r);
91188ca6 589 if (ret < 0 || r->sock < 0)
862f2f37
QY
590 return ret;
591 }
5435a2bf
QY
592
593 /* Schedule listener */
91188ca6 594 thread_add_read(master, vrrp_read, r, r->sock, &r->t_read);
5435a2bf 595
91188ca6 596 /* Configure effective priority */
862f2f37
QY
597 struct ipaddr *primary = (struct ipaddr *)listhead(r->addrs)->data;
598
599 char ipbuf[INET6_ADDRSTRLEN];
600 inet_ntop(r->family, &primary->ip.addr, ipbuf, sizeof(ipbuf));
601
602 if (vrrp_is_owner(r->vr, primary)) {
603 r->priority = VRRP_PRIO_MASTER;
604 vrrp_recalculate_timers(r);
605
5d3730c5
QY
606 zlog_info(
607 VRRP_LOGPFX VRRP_LOGPFX_VRID
608 "%s owns primary Virtual Router IP %s; electing self as Master",
862f2f37 609 r->vr->vrid, r->vr->ifp->name, ipbuf);
5d3730c5
QY
610 }
611
862f2f37
QY
612 if (r->priority == VRRP_PRIO_MASTER) {
613 vrrp_send_advertisement(r);
5435a2bf 614
862f2f37
QY
615 if (r->family == AF_INET)
616 vrrp_garp_send_all(r);
617
618 thread_add_timer_msec(master, vrrp_adver_timer_expire, r,
619 r->vr->advertisement_interval * 10,
620 &r->t_adver_timer);
621 vrrp_change_state(r, VRRP_STATE_MASTER);
5435a2bf 622 } else {
862f2f37
QY
623 r->master_adver_interval = r->vr->advertisement_interval;
624 vrrp_recalculate_timers(r);
625 thread_add_timer_msec(master, vrrp_master_down_timer_expire, r,
626 r->master_down_interval * 10,
627 &r->t_master_down_timer);
628 vrrp_change_state(r, VRRP_STATE_BACKUP);
5435a2bf 629 }
a8144d7f 630
862f2f37
QY
631 r->is_active = true;
632
a8144d7f 633 return 0;
5435a2bf
QY
634}
635
1d21789e
QY
636/*
637 * Shuts down a Virtual Router and transitions it to Initialize.
638 *
639 * This call must be idempotent; it is safe to call multiple times on the same
862f2f37 640 * VRRP Router.
1d21789e 641 */
862f2f37 642static int vrrp_shutdown(struct vrrp_router *r)
5435a2bf 643{
862f2f37
QY
644 switch (r->fsm.state) {
645 case VRRP_STATE_MASTER:
646 /* Cancel the Adver_Timer */
647 THREAD_OFF(r->t_adver_timer);
648 /* Send an ADVERTISEMENT with Priority = 0 */
649 uint8_t saved_prio = r->priority;
650 r->priority = 0;
651 vrrp_send_advertisement(r);
652 r->priority = saved_prio;
653 break;
654 case VRRP_STATE_BACKUP:
655 /* Cancel the Master_Down_Timer */
656 THREAD_OFF(r->t_master_down_timer);
657 break;
658 case VRRP_STATE_INITIALIZE:
659 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID
660 "Received '%s' event in '%s' state; ignoring",
661 r->vr->vrid, vrrp_event_names[VRRP_EVENT_SHUTDOWN],
662 vrrp_state_names[VRRP_STATE_INITIALIZE]);
663 break;
3e7a4043
QY
664 }
665
3e7a4043 666 /* Transition to the Initialize state */
862f2f37 667 vrrp_change_state(r, VRRP_STATE_INITIALIZE);
1d21789e 668
a8144d7f 669 return 0;
5435a2bf
QY
670}
671
862f2f37 672static int (*vrrp_event_handlers[])(struct vrrp_router *r) = {
5435a2bf
QY
673 [VRRP_EVENT_STARTUP] = vrrp_startup,
674 [VRRP_EVENT_SHUTDOWN] = vrrp_shutdown,
675};
676
677/*
862f2f37 678 * Spawn a VRRP FSM event on a VRRP Router.
5435a2bf
QY
679 *
680 * vr
862f2f37 681 * VRRP Router on which to spawn event
5435a2bf
QY
682 *
683 * event
684 * The event to spawn
685 */
862f2f37 686int vrrp_event(struct vrrp_router *r, int event)
5435a2bf 687{
862f2f37
QY
688 zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "'%s' event", r->vr->vrid,
689 vrrp_event_names[r->fsm.state]);
690 return vrrp_event_handlers[event](r);
5435a2bf
QY
691}
692
693
694/* Other ------------------------------------------------------------------- */
695
696static unsigned int vrrp_hash_key(void *arg)
697{
698 struct vrrp_vrouter *vr = arg;
699
700 return vr->vrid;
701}
702
703static bool vrrp_hash_cmp(const void *arg1, const void *arg2)
704{
705 const struct vrrp_vrouter *vr1 = arg1;
706 const struct vrrp_vrouter *vr2 = arg2;
707
c23edd74 708 return vr1->vrid == vr2->vrid;
5435a2bf
QY
709}
710
711void vrrp_init(void)
712{
713 vrrp_vrouters_hash = hash_create(&vrrp_hash_key, vrrp_hash_cmp,
714 "VRRP virtual router hash");
715 vrf_init(NULL, NULL, NULL, NULL, NULL);
716}