]> git.proxmox.com Git - mirror_frr.git/blame - ldpd/interface.c
Merge pull request #8231 from idryzhov/bfd-admin-down
[mirror_frr.git] / ldpd / interface.c
CommitLineData
8429abe0
RW
1/* $OpenBSD$ */
2
3/*
4 * Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
5 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
6 * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org>
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20
eac6e3f0 21#include <zebra.h>
8429abe0
RW
22
23#include "ldpd.h"
24#include "ldpe.h"
25#include "log.h"
e1894ff7 26#include "ldp_debug.h"
8429abe0 27
eac6e3f0
RW
28#include "sockopt.h"
29
45926e58 30static __inline int iface_compare(const struct iface *, const struct iface *);
8429abe0
RW
31static struct if_addr *if_addr_new(struct kaddr *);
32static struct if_addr *if_addr_lookup(struct if_addr_head *, struct kaddr *);
33static int if_start(struct iface *, int);
34static int if_reset(struct iface *, int);
988ded8d 35static void if_update_af(struct iface_af *);
eac6e3f0 36static int if_hello_timer(struct thread *);
8429abe0
RW
37static void if_start_hello_timer(struct iface_af *);
38static void if_stop_hello_timer(struct iface_af *);
39static int if_join_ipv4_group(struct iface *, struct in_addr *);
40static int if_leave_ipv4_group(struct iface *, struct in_addr *);
41static int if_join_ipv6_group(struct iface *, struct in6_addr *);
42static int if_leave_ipv6_group(struct iface *, struct in6_addr *);
43
e1894ff7
KS
44static int ldp_sync_fsm_init(struct iface *iface, int state);
45static int ldp_sync_act_iface_start_sync(struct iface *iface);
46static int iface_wait_for_ldp_sync_timer(struct thread *thread);
47static void start_wait_for_ldp_sync_timer(struct iface *iface);
48static void stop_wait_for_ldp_sync_timer(struct iface *iface);
49static int ldp_sync_act_ldp_start_sync(struct iface *iface);
50static int ldp_sync_act_ldp_complete_sync(struct iface *iface);
51static int iface_to_oper_nbr_count(struct iface *iface, unsigned int type);
52static void ldp_sync_get_peer_ldp_id(struct iface *iface,
53 struct in_addr *peer_ldp_id);
54
7d3d7491
RW
55RB_GENERATE(iface_head, iface, entry, iface_compare)
56
57static __inline int
45926e58 58iface_compare(const struct iface *a, const struct iface *b)
7d3d7491 59{
36de6e0e 60 return if_cmp_name_func(a->name, b->name);
7d3d7491
RW
61}
62
8429abe0 63struct iface *
52b530fc 64if_new(const char *name)
8429abe0
RW
65{
66 struct iface *iface;
67
68 if ((iface = calloc(1, sizeof(*iface))) == NULL)
69 fatal("if_new: calloc");
70
52b530fc 71 strlcpy(iface->name, name, sizeof(iface->name));
8429abe0
RW
72
73 /* ipv4 */
74 iface->ipv4.af = AF_INET;
75 iface->ipv4.iface = iface;
76 iface->ipv4.enabled = 0;
8429abe0
RW
77
78 /* ipv6 */
79 iface->ipv6.af = AF_INET6;
80 iface->ipv6.iface = iface;
81 iface->ipv6.enabled = 0;
8429abe0
RW
82
83 return (iface);
84}
85
8429abe0 86void
1d75a89d
RW
87ldpe_if_init(struct iface *iface)
88{
89 log_debug("%s: interface %s", __func__, iface->name);
90
91 LIST_INIT(&iface->addr_list);
92
93 /* ipv4 */
94 iface->ipv4.iface = iface;
95 iface->ipv4.state = IF_STA_DOWN;
522faa1f 96 RB_INIT(ia_adj_head, &iface->ipv4.adj_tree);
1d75a89d
RW
97
98 /* ipv6 */
99 iface->ipv6.iface = iface;
100 iface->ipv6.state = IF_STA_DOWN;
522faa1f 101 RB_INIT(ia_adj_head, &iface->ipv6.adj_tree);
e1894ff7
KS
102
103 /* LGP IGP Sync */
104 ldp_sync_fsm_init(iface, LDP_SYNC_STA_NOT_ACH);
1d75a89d
RW
105}
106
107void
108ldpe_if_exit(struct iface *iface)
8429abe0
RW
109{
110 struct if_addr *if_addr;
111
112 log_debug("%s: interface %s", __func__, iface->name);
113
e1894ff7
KS
114 ldp_sync_fsm(iface, LDP_SYNC_EVT_CONFIG_LDP_OFF);
115
8429abe0
RW
116 if (iface->ipv4.state == IF_STA_ACTIVE)
117 if_reset(iface, AF_INET);
118 if (iface->ipv6.state == IF_STA_ACTIVE)
119 if_reset(iface, AF_INET6);
120
121 while ((if_addr = LIST_FIRST(&iface->addr_list)) != NULL) {
122 LIST_REMOVE(if_addr, entry);
11bf8e13 123 assert(if_addr != LIST_FIRST(&iface->addr_list));
8429abe0
RW
124 free(if_addr);
125 }
126}
127
eac6e3f0 128struct iface *
6cd8093d 129if_lookup(struct ldpd_conf *xconf, ifindex_t ifindex)
eac6e3f0
RW
130{
131 struct iface *iface;
132
7d3d7491
RW
133 RB_FOREACH(iface, iface_head, &xconf->iface_tree)
134 if (iface->ifindex == ifindex)
eac6e3f0
RW
135 return (iface);
136
137 return (NULL);
138}
139
7d3d7491
RW
140struct iface *
141if_lookup_name(struct ldpd_conf *xconf, const char *ifname)
142{
143 struct iface iface;
144 strlcpy(iface.name, ifname, sizeof(iface.name));
145 return (RB_FIND(iface_head, &xconf->iface_tree, &iface));
146}
147
eac6e3f0
RW
148void
149if_update_info(struct iface *iface, struct kif *kif)
150{
151 /* get type */
152 if (kif->flags & IFF_POINTOPOINT)
153 iface->type = IF_TYPE_POINTOPOINT;
154 if (kif->flags & IFF_BROADCAST &&
155 kif->flags & IFF_MULTICAST)
156 iface->type = IF_TYPE_BROADCAST;
157
e1894ff7
KS
158 if (ldpd_process == PROC_LDP_ENGINE && iface->operative &&
159 !kif->operative)
160 ldp_sync_fsm(iface, LDP_SYNC_EVT_IFACE_SHUTDOWN);
161
eac6e3f0
RW
162 /* get index and flags */
163 iface->ifindex = kif->ifindex;
988ded8d 164 iface->operative = kif->operative;
eac6e3f0
RW
165}
166
8429abe0
RW
167struct iface_af *
168iface_af_get(struct iface *iface, int af)
169{
170 switch (af) {
171 case AF_INET:
172 return (&iface->ipv4);
173 case AF_INET6:
174 return (&iface->ipv6);
175 default:
176 fatalx("iface_af_get: unknown af");
177 }
178}
179
180static struct if_addr *
181if_addr_new(struct kaddr *ka)
182{
183 struct if_addr *if_addr;
184
185 if ((if_addr = calloc(1, sizeof(*if_addr))) == NULL)
186 fatal(__func__);
187
188 if_addr->af = ka->af;
189 if_addr->addr = ka->addr;
190 if_addr->prefixlen = ka->prefixlen;
191 if_addr->dstbrd = ka->dstbrd;
192
193 return (if_addr);
194}
195
196static struct if_addr *
197if_addr_lookup(struct if_addr_head *addr_list, struct kaddr *ka)
198{
199 struct if_addr *if_addr;
200 int af = ka->af;
201
202 LIST_FOREACH(if_addr, addr_list, entry)
203 if (!ldp_addrcmp(af, &if_addr->addr, &ka->addr) &&
204 if_addr->prefixlen == ka->prefixlen &&
205 !ldp_addrcmp(af, &if_addr->dstbrd, &ka->dstbrd))
206 return (if_addr);
207
208 return (NULL);
209}
210
211void
212if_addr_add(struct kaddr *ka)
213{
214 struct iface *iface;
215 struct if_addr *if_addr;
216 struct nbr *nbr;
217
218 if (if_addr_lookup(&global.addr_list, ka) == NULL) {
219 if_addr = if_addr_new(ka);
220
221 LIST_INSERT_HEAD(&global.addr_list, if_addr, entry);
222 RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
223 if (nbr->state != NBR_STA_OPER)
224 continue;
225 if (if_addr->af == AF_INET && !nbr->v4_enabled)
226 continue;
227 if (if_addr->af == AF_INET6 && !nbr->v6_enabled)
228 continue;
229
230 send_address_single(nbr, if_addr, 0);
231 }
232 }
233
9cf67225 234 iface = if_lookup_name(leconf, ka->ifname);
8429abe0
RW
235 if (iface) {
236 if (ka->af == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&ka->addr.v6))
237 iface->linklocal = ka->addr.v6;
238
239 if (if_addr_lookup(&iface->addr_list, ka) == NULL) {
240 if_addr = if_addr_new(ka);
241 LIST_INSERT_HEAD(&iface->addr_list, if_addr, entry);
61cd1100 242 ldp_if_update(iface, if_addr->af);
8429abe0
RW
243 }
244 }
245}
246
247void
248if_addr_del(struct kaddr *ka)
249{
250 struct iface *iface;
251 struct if_addr *if_addr;
252 struct nbr *nbr;
253
9cf67225 254 iface = if_lookup_name(leconf, ka->ifname);
8429abe0
RW
255 if (iface) {
256 if (ka->af == AF_INET6 &&
257 IN6_ARE_ADDR_EQUAL(&iface->linklocal, &ka->addr.v6))
258 memset(&iface->linklocal, 0, sizeof(iface->linklocal));
259
260 if_addr = if_addr_lookup(&iface->addr_list, ka);
261 if (if_addr) {
262 LIST_REMOVE(if_addr, entry);
61cd1100 263 ldp_if_update(iface, if_addr->af);
8429abe0
RW
264 free(if_addr);
265 }
266 }
267
268 if_addr = if_addr_lookup(&global.addr_list, ka);
269 if (if_addr) {
270 RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
271 if (nbr->state != NBR_STA_OPER)
272 continue;
273 if (if_addr->af == AF_INET && !nbr->v4_enabled)
274 continue;
275 if (if_addr->af == AF_INET6 && !nbr->v6_enabled)
276 continue;
277 send_address_single(nbr, if_addr, 1);
278 }
279 LIST_REMOVE(if_addr, entry);
280 free(if_addr);
281 }
282}
283
284static int
285if_start(struct iface *iface, int af)
286{
287 struct iface_af *ia;
288 struct timeval now;
289
290 log_debug("%s: %s address-family %s", __func__, iface->name,
291 af_name(af));
292
293 ia = iface_af_get(iface, af);
294
295 gettimeofday(&now, NULL);
296 ia->uptime = now.tv_sec;
297
298 switch (af) {
299 case AF_INET:
300 if (if_join_ipv4_group(iface, &global.mcast_addr_v4))
301 return (-1);
302 break;
303 case AF_INET6:
304 if (if_join_ipv6_group(iface, &global.mcast_addr_v6))
305 return (-1);
306 break;
307 default:
308 fatalx("if_start: unknown af");
309 }
310
311 send_hello(HELLO_LINK, ia, NULL);
8429abe0 312 if_start_hello_timer(ia);
8a26d0cf
RW
313 ia->state = IF_STA_ACTIVE;
314
8429abe0
RW
315 return (0);
316}
317
318static int
319if_reset(struct iface *iface, int af)
320{
321 struct iface_af *ia;
322 struct adj *adj;
323
324 log_debug("%s: %s address-family %s", __func__, iface->name,
325 af_name(af));
326
327 ia = iface_af_get(iface, af);
328 if_stop_hello_timer(ia);
329
55cd0f61
DS
330 while (!RB_EMPTY(ia_adj_head, &ia->adj_tree)) {
331 adj = RB_ROOT(ia_adj_head, &ia->adj_tree);
332
8429abe0 333 adj_del(adj, S_SHUTDOWN);
55cd0f61 334 }
8429abe0
RW
335
336 /* try to cleanup */
337 switch (af) {
338 case AF_INET:
339 if (global.ipv4.ldp_disc_socket != -1)
340 if_leave_ipv4_group(iface, &global.mcast_addr_v4);
341 break;
342 case AF_INET6:
343 if (global.ipv6.ldp_disc_socket != -1)
344 if_leave_ipv6_group(iface, &global.mcast_addr_v6);
345 break;
346 default:
8a26d0cf 347 fatalx("if_reset: unknown af");
8429abe0
RW
348 }
349
8a26d0cf
RW
350 ia->state = IF_STA_DOWN;
351
8429abe0
RW
352 return (0);
353}
354
355static void
988ded8d 356if_update_af(struct iface_af *ia)
8429abe0
RW
357{
358 int addr_ok = 0, socket_ok, rtr_id_ok;
359 struct if_addr *if_addr;
360
361 switch (ia->af) {
362 case AF_INET:
363 /*
364 * NOTE: for LDPv4, each interface should have at least one
365 * valid IP address otherwise they can not be enabled.
366 */
367 LIST_FOREACH(if_addr, &ia->iface->addr_list, entry) {
368 if (if_addr->af == AF_INET) {
369 addr_ok = 1;
370 break;
371 }
372 }
373 break;
374 case AF_INET6:
375 /* for IPv6 the link-local address is enough. */
376 if (IN6_IS_ADDR_LINKLOCAL(&ia->iface->linklocal))
377 addr_ok = 1;
378 break;
379 default:
380 fatalx("if_update_af: unknown af");
381 }
382
383 if ((ldp_af_global_get(&global, ia->af))->ldp_disc_socket != -1)
384 socket_ok = 1;
385 else
386 socket_ok = 0;
387
eac6e3f0 388 if (ldp_rtr_id_get(leconf) != INADDR_ANY)
8429abe0
RW
389 rtr_id_ok = 1;
390 else
391 rtr_id_ok = 0;
392
393 if (ia->state == IF_STA_DOWN) {
988ded8d
RW
394 if (!ia->enabled || !ia->iface->operative || !addr_ok ||
395 !socket_ok || !rtr_id_ok)
8429abe0
RW
396 return;
397
8429abe0
RW
398 if_start(ia->iface, ia->af);
399 } else if (ia->state == IF_STA_ACTIVE) {
988ded8d
RW
400 if (ia->enabled && ia->iface->operative && addr_ok &&
401 socket_ok && rtr_id_ok)
8429abe0
RW
402 return;
403
8429abe0
RW
404 if_reset(ia->iface, ia->af);
405 }
406}
407
408void
61cd1100 409ldp_if_update(struct iface *iface, int af)
8429abe0 410{
8429abe0 411 if (af == AF_INET || af == AF_UNSPEC)
988ded8d 412 if_update_af(&iface->ipv4);
8429abe0 413 if (af == AF_INET6 || af == AF_UNSPEC)
988ded8d 414 if_update_af(&iface->ipv6);
8429abe0
RW
415}
416
417void
418if_update_all(int af)
419{
420 struct iface *iface;
421
7d3d7491 422 RB_FOREACH(iface, iface_head, &leconf->iface_tree)
61cd1100 423 ldp_if_update(iface, af);
8429abe0
RW
424}
425
eac6e3f0
RW
426uint16_t
427if_get_hello_holdtime(struct iface_af *ia)
428{
429 if (ia->hello_holdtime != 0)
430 return (ia->hello_holdtime);
431
432 if ((ldp_af_conf_get(leconf, ia->af))->lhello_holdtime != 0)
433 return ((ldp_af_conf_get(leconf, ia->af))->lhello_holdtime);
434
435 return (leconf->lhello_holdtime);
436}
437
438uint16_t
439if_get_hello_interval(struct iface_af *ia)
440{
441 if (ia->hello_interval != 0)
442 return (ia->hello_interval);
443
444 if ((ldp_af_conf_get(leconf, ia->af))->lhello_interval != 0)
445 return ((ldp_af_conf_get(leconf, ia->af))->lhello_interval);
446
447 return (leconf->lhello_interval);
448}
449
e1894ff7
KS
450uint16_t
451if_get_wait_for_sync_interval(void)
452{
453 return (leconf->wait_for_sync_interval);
454}
455
8429abe0
RW
456/* timers */
457/* ARGSUSED */
eac6e3f0
RW
458static int
459if_hello_timer(struct thread *thread)
8429abe0 460{
eac6e3f0 461 struct iface_af *ia = THREAD_ARG(thread);
8429abe0 462
eac6e3f0 463 ia->hello_timer = NULL;
8429abe0
RW
464 send_hello(HELLO_LINK, ia, NULL);
465 if_start_hello_timer(ia);
eac6e3f0
RW
466
467 return (0);
8429abe0
RW
468}
469
470static void
471if_start_hello_timer(struct iface_af *ia)
472{
50478845 473 thread_cancel(&ia->hello_timer);
66e78ae6
QY
474 ia->hello_timer = NULL;
475 thread_add_timer(master, if_hello_timer, ia, if_get_hello_interval(ia),
476 &ia->hello_timer);
8429abe0
RW
477}
478
479static void
480if_stop_hello_timer(struct iface_af *ia)
481{
50478845 482 thread_cancel(&ia->hello_timer);
8429abe0
RW
483}
484
485struct ctl_iface *
486if_to_ctl(struct iface_af *ia)
487{
488 static struct ctl_iface ictl;
489 struct timeval now;
490 struct adj *adj;
491
492 ictl.af = ia->af;
493 memcpy(ictl.name, ia->iface->name, sizeof(ictl.name));
494 ictl.ifindex = ia->iface->ifindex;
495 ictl.state = ia->state;
8429abe0 496 ictl.type = ia->iface->type;
eac6e3f0
RW
497 ictl.hello_holdtime = if_get_hello_holdtime(ia);
498 ictl.hello_interval = if_get_hello_interval(ia);
8429abe0
RW
499
500 gettimeofday(&now, NULL);
501 if (ia->state != IF_STA_DOWN &&
502 ia->uptime != 0) {
503 ictl.uptime = now.tv_sec - ia->uptime;
504 } else
505 ictl.uptime = 0;
506
507 ictl.adj_cnt = 0;
057d48bd 508 RB_FOREACH(adj, ia_adj_head, &ia->adj_tree)
8429abe0
RW
509 ictl.adj_cnt++;
510
511 return (&ictl);
512}
513
e1894ff7
KS
514static void
515ldp_sync_get_peer_ldp_id(struct iface *iface, struct in_addr *peer_ldp_id)
516{
517 struct iface_af *ia;
518 struct adj *adj;
519
520 if (iface->ipv4.state == IF_STA_ACTIVE) {
521 ia = iface_af_get(iface, AF_INET);
522 RB_FOREACH(adj, ia_adj_head, &ia->adj_tree)
523 if (adj->nbr && adj->nbr->state == NBR_STA_OPER) {
524 *peer_ldp_id = adj->nbr->id;
525 return;
526 }
527 }
528
529 if (iface->ipv6.state == IF_STA_ACTIVE) {
530 ia = iface_af_get(iface, AF_INET6);
531 RB_FOREACH(adj, ia_adj_head, &ia->adj_tree)
532 if (adj->nbr && adj->nbr->state == NBR_STA_OPER) {
533 *peer_ldp_id = adj->nbr->id;
534 return;
535 }
536 }
537}
538
539struct ctl_ldp_sync *
540ldp_sync_to_ctl(struct iface *iface)
541{
542 static struct ctl_ldp_sync ictl;
543
544 memcpy(ictl.name, iface->name, sizeof(ictl.name));
545 ictl.ifindex = iface->ifindex;
546 ictl.in_sync = (iface->ldp_sync.state == LDP_SYNC_STA_ACH);
547 ictl.wait_time = if_get_wait_for_sync_interval();
548 ictl.timer_running = iface->ldp_sync.wait_for_sync_timer ? true : false;
549
550 if (iface->ldp_sync.wait_for_sync_timer)
551 ictl.wait_time_remaining =
552 thread_timer_remain_second(iface->ldp_sync.wait_for_sync_timer);
553 else
554 ictl.wait_time_remaining = 0;
555
556 memset(&ictl.peer_ldp_id, 0, sizeof(ictl.peer_ldp_id));
557
558 ldp_sync_get_peer_ldp_id(iface, &ictl.peer_ldp_id);
559
560 return (&ictl);
561}
562
8429abe0
RW
563/* multicast membership sockopts */
564in_addr_t
565if_get_ipv4_addr(struct iface *iface)
566{
567 struct if_addr *if_addr;
568
569 LIST_FOREACH(if_addr, &iface->addr_list, entry)
570 if (if_addr->af == AF_INET)
571 return (if_addr->addr.v4.s_addr);
572
573 return (INADDR_ANY);
574}
575
576static int
577if_join_ipv4_group(struct iface *iface, struct in_addr *addr)
578{
eac6e3f0 579 struct in_addr if_addr;
8429abe0 580
903a7226
MS
581 log_debug("%s: interface %s addr %pI4", __func__, iface->name,
582 addr);
8429abe0 583
eac6e3f0 584 if_addr.s_addr = if_get_ipv4_addr(iface);
8429abe0 585
eac6e3f0
RW
586 if (setsockopt_ipv4_multicast(global.ipv4.ldp_disc_socket,
587 IP_ADD_MEMBERSHIP, if_addr, addr->s_addr, iface->ifindex) < 0) {
903a7226
MS
588 log_warn("%s: error IP_ADD_MEMBERSHIP, interface %s address %pI4",
589 __func__, iface->name, addr);
8429abe0
RW
590 return (-1);
591 }
592 return (0);
593}
594
595static int
596if_leave_ipv4_group(struct iface *iface, struct in_addr *addr)
597{
eac6e3f0 598 struct in_addr if_addr;
8429abe0 599
903a7226
MS
600 log_debug("%s: interface %s addr %pI4", __func__, iface->name,
601 addr);
8429abe0 602
eac6e3f0 603 if_addr.s_addr = if_get_ipv4_addr(iface);
8429abe0 604
eac6e3f0
RW
605 if (setsockopt_ipv4_multicast(global.ipv4.ldp_disc_socket,
606 IP_DROP_MEMBERSHIP, if_addr, addr->s_addr, iface->ifindex) < 0) {
903a7226 607 log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s address %pI4", __func__, iface->name, addr);
8429abe0
RW
608 return (-1);
609 }
610
611 return (0);
612}
613
614static int
615if_join_ipv6_group(struct iface *iface, struct in6_addr *addr)
616{
617 struct ipv6_mreq mreq;
618
619 log_debug("%s: interface %s addr %s", __func__, iface->name,
620 log_in6addr(addr));
621
622 mreq.ipv6mr_multiaddr = *addr;
623 mreq.ipv6mr_interface = iface->ifindex;
624
625 if (setsockopt(global.ipv6.ldp_disc_socket, IPPROTO_IPV6,
626 IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) {
627 log_warn("%s: error IPV6_JOIN_GROUP, interface %s address %s",
628 __func__, iface->name, log_in6addr(addr));
629 return (-1);
630 }
631
632 return (0);
633}
634
635static int
636if_leave_ipv6_group(struct iface *iface, struct in6_addr *addr)
637{
638 struct ipv6_mreq mreq;
639
640 log_debug("%s: interface %s addr %s", __func__, iface->name,
641 log_in6addr(addr));
642
643 mreq.ipv6mr_multiaddr = *addr;
644 mreq.ipv6mr_interface = iface->ifindex;
645
646 if (setsockopt(global.ipv6.ldp_disc_socket, IPPROTO_IPV6,
647 IPV6_LEAVE_GROUP, (void *)&mreq, sizeof(mreq)) < 0) {
648 log_warn("%s: error IPV6_LEAVE_GROUP, interface %s address %s",
649 __func__, iface->name, log_in6addr(addr));
650 return (-1);
651 }
652
653 return (0);
654}
e1894ff7
KS
655
656const struct {
657 int state;
658 enum ldp_sync_event event;
659 enum ldp_sync_action action;
660 int new_state;
661} ldp_sync_fsm_tbl[] = {
662 /* current state event that happened action to take resulting state */
663/* LDP IGP Sync not achieved */
664 {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_LDP_SYNC_START, LDP_SYNC_ACT_LDP_START_SYNC, 0},
665 {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_LDP_SYNC_COMPLETE, LDP_SYNC_ACT_LDP_COMPLETE_SYNC, LDP_SYNC_STA_ACH},
666 {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_CONFIG_LDP_OFF, LDP_SYNC_ACT_CONFIG_LDP_OFF, 0},
667 {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_IFACE_SHUTDOWN, LDP_SYNC_ACT_IFACE_SHUTDOWN, 0},
668 {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_SESSION_CLOSE, LDP_SYNC_ACT_NOTHING, 0},
669 {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_ADJ_DEL, LDP_SYNC_ACT_NOTHING, 0},
670 {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_ADJ_NEW, LDP_SYNC_ACT_NOTHING, 0},
671/* LDP IGP Sync achieved */
672 {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_CONFIG_LDP_OFF, LDP_SYNC_ACT_CONFIG_LDP_OFF, LDP_SYNC_STA_NOT_ACH},
673 {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_LDP_SYNC_COMPLETE, LDP_SYNC_ACT_NOTHING, 0},
674 {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_LDP_SYNC_START, LDP_SYNC_ACT_NOTHING, 0},
675 {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_IFACE_SHUTDOWN, LDP_SYNC_ACT_IFACE_SHUTDOWN, LDP_SYNC_STA_NOT_ACH},
676 {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_SESSION_CLOSE, LDP_SYNC_ACT_IFACE_START_SYNC, LDP_SYNC_STA_NOT_ACH},
677 {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_ADJ_DEL, LDP_SYNC_ACT_IFACE_START_SYNC, LDP_SYNC_STA_NOT_ACH},
678 {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_ADJ_NEW, LDP_SYNC_ACT_NOTHING, 0},
679 {-1, LDP_SYNC_EVT_NOTHING, LDP_SYNC_ACT_NOTHING, 0},
680};
681
682const char * const ldp_sync_event_names[] = {
683 "NOTHING",
684 "LDP SYNC START",
685 "LDP SYNC COMPLETE",
686 "CONFIG LDP OFF",
687 "IFACE SYNC START (ADJ DEL)",
688 "IFACE SYNC START (ADJ NEW)",
689 "IFACE SYNC START (SESSION CLOSE)",
690 "IFACE SYNC START (CONFIG LDP ON)",
691 "IFACE SHUTDOWN",
692 "N/A"
693};
694
695const char * const ldp_sync_action_names[] = {
696 "NOTHING",
697 "IFACE SYNC START",
698 "LDP START SYNC",
699 "LDP COMPLETE SYNC",
700 "CONFIG LDP OFF",
701 "IFACE SHUTDOWN",
702 "N/A"
703};
704
705const char *
706ldp_sync_state_name(int state)
707{
708 switch (state) {
709 case LDP_SYNC_STA_NOT_ACH:
710 return ("NOT ACHIEVED");
711 case LDP_SYNC_STA_ACH:
712 return ("ACHIEVED");
713 default:
714 return ("UNKNOWN");
715 }
716}
717
718static int
719send_ldp_sync_state_update(char *name, int ifindex, int sync_start)
720{
721 debug_evt_ldp_sync("%s: interface %s (%d), sync_start=%d",
722 __func__, name, ifindex, sync_start);
723
724 struct ldp_igp_sync_if_state state;
725
726 state.ifindex = ifindex;
727 state.sync_start = sync_start;
728
729 return ldpe_imsg_compose_parent(IMSG_LDP_SYNC_IF_STATE_UPDATE,
730 getpid(), &state, sizeof(state));
731}
732
733static int
734ldp_sync_act_iface_start_sync(struct iface *iface)
735{
736 send_ldp_sync_state_update(iface->name, iface->ifindex, true);
737
738 return (0);
739}
740
741static int
742iface_wait_for_ldp_sync_timer(struct thread *thread)
743{
744 struct iface *iface = THREAD_ARG(thread);
745
746 ldp_sync_fsm(iface, LDP_SYNC_EVT_LDP_SYNC_COMPLETE);
747
748 return (0);
749}
750
751static void start_wait_for_ldp_sync_timer(struct iface *iface)
752{
753 if (iface->ldp_sync.wait_for_sync_timer)
754 return;
755
50478845 756 THREAD_OFF(iface->ldp_sync.wait_for_sync_timer);
e1894ff7
KS
757 thread_add_timer(master, iface_wait_for_ldp_sync_timer, iface,
758 if_get_wait_for_sync_interval(),
759 &iface->ldp_sync.wait_for_sync_timer);
760}
761
762static void stop_wait_for_ldp_sync_timer(struct iface *iface)
763{
50478845 764 THREAD_OFF(iface->ldp_sync.wait_for_sync_timer);
e1894ff7
KS
765}
766
767static int
768ldp_sync_act_ldp_start_sync(struct iface *iface)
769{
770 start_wait_for_ldp_sync_timer(iface);
771
772 return 0;
773}
774
775static int
776ldp_sync_act_ldp_complete_sync(struct iface *iface)
777{
778 send_ldp_sync_state_update(iface->name, iface->ifindex, false);
779
780 return 0;
781}
782
783static int
784iface_to_oper_nbr_count(struct iface *iface, unsigned int type)
785{
786 int oper_nbr_count = 0;
787 struct adj *adj;
788
789 RB_FOREACH(adj, ia_adj_head, &iface->ipv4.adj_tree) {
790 if (type == adj->source.type && adj->nbr &&
791 adj->nbr->state == NBR_STA_OPER)
792 oper_nbr_count++;
793 }
794
795 RB_FOREACH(adj, ia_adj_head, &iface->ipv6.adj_tree) {
796 if (type == adj->source.type && adj->nbr &&
797 adj->nbr->state == NBR_STA_OPER)
798 oper_nbr_count++;
799 }
800
801 return oper_nbr_count;
802}
803
804int
805ldp_sync_fsm_adj_event(struct adj *adj, enum ldp_sync_event event)
806{
807 if (adj->source.type != HELLO_LINK)
808 return -1;
809
810 struct iface *iface = adj->source.link.ia->iface;
811
812 if (!iface->operative)
813 return 0;
814
815 if (event == LDP_SYNC_EVT_ADJ_NEW) {
816 struct nbr *nbr = adj->nbr;
817 if (nbr && nbr->state == NBR_STA_OPER) {
818 event = LDP_SYNC_EVT_LDP_SYNC_START;
819 }
820 } else if (event == LDP_SYNC_EVT_ADJ_DEL) {
821 /* Ignore if an operational neighbor exists.
822 */
823 int oper_nbr_count = iface_to_oper_nbr_count(iface, HELLO_LINK);
824 if (oper_nbr_count > 0)
825 return 0;
826 }
827
828 debug_evt_ldp_sync("%s: event %s, "
903a7226
MS
829 "adj iface %s (%d) lsr-id %pI4 "
830 "source address %s transport address %s",
831 __func__, ldp_sync_event_names[event],
832 adj->source.link.ia->iface->name,
833 adj->source.link.ia->iface->ifindex,
834 &adj->lsr_id,
835 log_addr(adj_get_af(adj), &adj->source.link.src_addr),
836 log_addr(adj_get_af(adj), &adj->trans_addr));
e1894ff7
KS
837
838 return ldp_sync_fsm(iface, event);
839}
840
841int
842ldp_sync_fsm_nbr_event(struct nbr *nbr, enum ldp_sync_event event)
843{
844 struct adj *adj;
845 struct iface *iface = NULL;
846 RB_FOREACH(adj, nbr_adj_head, &nbr->adj_tree) {
847 if (HELLO_LINK != adj->source.type)
848 continue;
849
850 iface = adj->source.link.ia->iface;
851
852 if (!iface || !iface->operative)
853 continue;
854
855 int oper_nbr_count = iface_to_oper_nbr_count(iface, HELLO_LINK);
856
857 if (event == LDP_SYNC_EVT_SESSION_CLOSE && oper_nbr_count > 0)
858 /* Ignore if an operational neighbor exists.
859 */
860 continue;
861
903a7226 862 debug_evt_ldp_sync("%s: event %s, iface %s, lsr-id %pI4",
e1894ff7 863 __func__, ldp_sync_event_names[event],
903a7226 864 iface->name, &nbr->id);
e1894ff7
KS
865
866 ldp_sync_fsm(iface, event);
867 }
868
869 return 0;
870}
871
872int
873ldp_sync_fsm_state_req(struct ldp_igp_sync_if_state_req *state_req)
874{
875 debug_evt_ldp_sync("%s: interface %s (%d) proto %s",
876 __func__, state_req->name, state_req->ifindex,
877 zebra_route_string(state_req->proto));
878
879 struct iface *iface = if_lookup_name(leconf, state_req->name);
880
881 if (!iface) {
882 debug_evt_ldp_sync("%s: Warning: Ignoring LDP IGP SYNC "
883 "interface state request for interface %s (%d). "
884 "Interface does not exist in LDP.",
885 __func__, state_req->name, state_req->ifindex);
886
887 return 0;
888 }
889
890 return send_ldp_sync_state_update(state_req->name,
891 state_req->ifindex,
892 (iface->ldp_sync.state != LDP_SYNC_STA_ACH));
893}
894
895static int
896ldp_sync_fsm_init(struct iface *iface, int state)
897{
898 int old_state = iface->ldp_sync.state;
899
900 iface->ldp_sync.state = state;
901 stop_wait_for_ldp_sync_timer(iface);
902
903 send_ldp_sync_state_update(iface->name, iface->ifindex,
904 (iface->ldp_sync.state != LDP_SYNC_STA_ACH));
905
906 if (old_state != iface->ldp_sync.state) {
907 debug_evt_ldp_sync("%s: resulted in "
908 "changing state for interface %s (%d) from %s to %s",
909 __func__,
910 iface->name, iface->ifindex,
911 ldp_sync_state_name(old_state),
912 ldp_sync_state_name(iface->ldp_sync.state));
913 }
914
915 return 0;
916}
917
918int
919ldp_sync_fsm(struct iface *iface, enum ldp_sync_event event)
920{
921 int old_state = iface->ldp_sync.state;
922 int new_state = 0;
923 int i;
924
925 for (i = 0; ldp_sync_fsm_tbl[i].state != -1; i++)
926 if ((ldp_sync_fsm_tbl[i].state & old_state) &&
927 (ldp_sync_fsm_tbl[i].event == event)) {
928 new_state = ldp_sync_fsm_tbl[i].new_state;
929 break;
930 }
931
932 if (ldp_sync_fsm_tbl[i].state == -1) {
933 /* event outside of the defined fsm, ignore it. */
934 log_warnx("%s: interface %s, event %s not expected in "
935 "state %s ", __func__, iface->name,
936 ldp_sync_event_names[event],
937 ldp_sync_state_name(old_state));
938 return (0);
939 }
940
941 if (new_state != 0)
942 iface->ldp_sync.state = new_state;
943
944 switch (ldp_sync_fsm_tbl[i].action) {
945 case LDP_SYNC_ACT_IFACE_START_SYNC:
946 ldp_sync_act_iface_start_sync(iface);
947 break;
948 case LDP_SYNC_ACT_LDP_START_SYNC:
949 ldp_sync_act_ldp_start_sync(iface);
950 break;
951 case LDP_SYNC_ACT_LDP_COMPLETE_SYNC:
952 ldp_sync_act_ldp_complete_sync(iface);
953 break;
954 case LDP_SYNC_ACT_CONFIG_LDP_OFF:
955 ldp_sync_fsm_init(iface, LDP_SYNC_STA_NOT_ACH);
956 break;
957 case LDP_SYNC_ACT_IFACE_SHUTDOWN:
958 ldp_sync_fsm_init(iface, iface->ldp_sync.state);
959 break;
960 case LDP_SYNC_ACT_NOTHING:
961 /* do nothing */
962 break;
963 }
964
965 if (old_state != iface->ldp_sync.state) {
966
967 debug_evt_ldp_sync("%s: event %s resulted in action %s "
968 "for interface %s, changing state from %s to %s",
969 __func__, ldp_sync_event_names[event],
970 ldp_sync_action_names[ldp_sync_fsm_tbl[i].action],
971 iface->name, ldp_sync_state_name(old_state),
972 ldp_sync_state_name(iface->ldp_sync.state));
973
974 } else {
975 debug_evt_ldp_sync("%s: event %s resulted in action %s "
976 "for interface %s, remaining in state %s",
977 __func__, ldp_sync_event_names[event],
978 ldp_sync_action_names[ldp_sync_fsm_tbl[i].action],
979 iface->name,
980 ldp_sync_state_name(iface->ldp_sync.state));
981 }
982
983 return (0);
984}
985
986void
987ldp_sync_fsm_reset_all(void)
988{
989 struct iface *iface;
990
991 RB_FOREACH(iface, iface_head, &leconf->iface_tree)
992 ldp_sync_fsm(iface, LDP_SYNC_EVT_CONFIG_LDP_OFF);
993}