]>
Commit | Line | Data |
---|---|---|
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 | ||
38 | const char *vrrp_state_names[3] = { | |
39 | [VRRP_STATE_INITIALIZE] = "Initialize", | |
40 | [VRRP_STATE_MASTER] = "Master", | |
41 | [VRRP_STATE_BACKUP] = "Backup", | |
42 | }; | |
43 | ||
44 | const 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 | */ | |
64 | static 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 | 85 | static 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 | */ | |
102 | static 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 | ||
125 | void 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 |
133 | void 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 | ||
144 | void 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 | 153 | void 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 | ||
162 | void 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 |
173 | static 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 | ||
189 | static 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 |
198 | struct 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 |
219 | void 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 |
228 | struct 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 | 244 | static 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 |
278 | static 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 | */ | |
289 | static 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 | ||
341 | done: | |
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 | 359 | static 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 |
435 | done: |
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 | 452 | DEFINE_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 | 460 | static 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 | 471 | static 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 | 486 | static 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 | 493 | void (*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 | 509 | static 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 | */ | |
522 | static 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 | */ |
549 | static 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 | 576 | static 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 | 642 | static 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 | 672 | static 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 | 686 | int 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 | ||
696 | static unsigned int vrrp_hash_key(void *arg) | |
697 | { | |
698 | struct vrrp_vrouter *vr = arg; | |
699 | ||
700 | return vr->vrid; | |
701 | } | |
702 | ||
703 | static 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 | ||
711 | void 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 | } |