]> git.proxmox.com Git - mirror_frr.git/blame - ldpd/interface.c
Merge pull request #3405 from LabNConsulting/working/master/fix-vrf
[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"
26
eac6e3f0
RW
27#include "sockopt.h"
28
45926e58 29static __inline int iface_compare(const struct iface *, const struct iface *);
8429abe0
RW
30static struct if_addr *if_addr_new(struct kaddr *);
31static struct if_addr *if_addr_lookup(struct if_addr_head *, struct kaddr *);
32static int if_start(struct iface *, int);
33static int if_reset(struct iface *, int);
988ded8d 34static void if_update_af(struct iface_af *);
eac6e3f0 35static int if_hello_timer(struct thread *);
8429abe0
RW
36static void if_start_hello_timer(struct iface_af *);
37static void if_stop_hello_timer(struct iface_af *);
38static int if_join_ipv4_group(struct iface *, struct in_addr *);
39static int if_leave_ipv4_group(struct iface *, struct in_addr *);
40static int if_join_ipv6_group(struct iface *, struct in6_addr *);
41static int if_leave_ipv6_group(struct iface *, struct in6_addr *);
42
7d3d7491
RW
43RB_GENERATE(iface_head, iface, entry, iface_compare)
44
45static __inline int
45926e58 46iface_compare(const struct iface *a, const struct iface *b)
7d3d7491 47{
36de6e0e 48 return if_cmp_name_func(a->name, b->name);
7d3d7491
RW
49}
50
8429abe0 51struct iface *
52b530fc 52if_new(const char *name)
8429abe0
RW
53{
54 struct iface *iface;
55
56 if ((iface = calloc(1, sizeof(*iface))) == NULL)
57 fatal("if_new: calloc");
58
52b530fc 59 strlcpy(iface->name, name, sizeof(iface->name));
8429abe0
RW
60
61 /* ipv4 */
62 iface->ipv4.af = AF_INET;
63 iface->ipv4.iface = iface;
64 iface->ipv4.enabled = 0;
8429abe0
RW
65
66 /* ipv6 */
67 iface->ipv6.af = AF_INET6;
68 iface->ipv6.iface = iface;
69 iface->ipv6.enabled = 0;
8429abe0
RW
70
71 return (iface);
72}
73
8429abe0 74void
1d75a89d
RW
75ldpe_if_init(struct iface *iface)
76{
77 log_debug("%s: interface %s", __func__, iface->name);
78
79 LIST_INIT(&iface->addr_list);
80
81 /* ipv4 */
82 iface->ipv4.iface = iface;
83 iface->ipv4.state = IF_STA_DOWN;
522faa1f 84 RB_INIT(ia_adj_head, &iface->ipv4.adj_tree);
1d75a89d
RW
85
86 /* ipv6 */
87 iface->ipv6.iface = iface;
88 iface->ipv6.state = IF_STA_DOWN;
522faa1f 89 RB_INIT(ia_adj_head, &iface->ipv6.adj_tree);
1d75a89d
RW
90}
91
92void
93ldpe_if_exit(struct iface *iface)
8429abe0
RW
94{
95 struct if_addr *if_addr;
96
97 log_debug("%s: interface %s", __func__, iface->name);
98
99 if (iface->ipv4.state == IF_STA_ACTIVE)
100 if_reset(iface, AF_INET);
101 if (iface->ipv6.state == IF_STA_ACTIVE)
102 if_reset(iface, AF_INET6);
103
104 while ((if_addr = LIST_FIRST(&iface->addr_list)) != NULL) {
105 LIST_REMOVE(if_addr, entry);
11bf8e13 106 assert(if_addr != LIST_FIRST(&iface->addr_list));
8429abe0
RW
107 free(if_addr);
108 }
109}
110
eac6e3f0 111struct iface *
7d3d7491 112if_lookup(struct ldpd_conf *xconf, unsigned short ifindex)
eac6e3f0
RW
113{
114 struct iface *iface;
115
7d3d7491
RW
116 RB_FOREACH(iface, iface_head, &xconf->iface_tree)
117 if (iface->ifindex == ifindex)
eac6e3f0
RW
118 return (iface);
119
120 return (NULL);
121}
122
7d3d7491
RW
123struct iface *
124if_lookup_name(struct ldpd_conf *xconf, const char *ifname)
125{
126 struct iface iface;
127 strlcpy(iface.name, ifname, sizeof(iface.name));
128 return (RB_FIND(iface_head, &xconf->iface_tree, &iface));
129}
130
eac6e3f0
RW
131void
132if_update_info(struct iface *iface, struct kif *kif)
133{
134 /* get type */
135 if (kif->flags & IFF_POINTOPOINT)
136 iface->type = IF_TYPE_POINTOPOINT;
137 if (kif->flags & IFF_BROADCAST &&
138 kif->flags & IFF_MULTICAST)
139 iface->type = IF_TYPE_BROADCAST;
140
141 /* get index and flags */
142 iface->ifindex = kif->ifindex;
988ded8d 143 iface->operative = kif->operative;
eac6e3f0
RW
144}
145
8429abe0
RW
146struct iface_af *
147iface_af_get(struct iface *iface, int af)
148{
149 switch (af) {
150 case AF_INET:
151 return (&iface->ipv4);
152 case AF_INET6:
153 return (&iface->ipv6);
154 default:
155 fatalx("iface_af_get: unknown af");
156 }
157}
158
159static struct if_addr *
160if_addr_new(struct kaddr *ka)
161{
162 struct if_addr *if_addr;
163
164 if ((if_addr = calloc(1, sizeof(*if_addr))) == NULL)
165 fatal(__func__);
166
167 if_addr->af = ka->af;
168 if_addr->addr = ka->addr;
169 if_addr->prefixlen = ka->prefixlen;
170 if_addr->dstbrd = ka->dstbrd;
171
172 return (if_addr);
173}
174
175static struct if_addr *
176if_addr_lookup(struct if_addr_head *addr_list, struct kaddr *ka)
177{
178 struct if_addr *if_addr;
179 int af = ka->af;
180
181 LIST_FOREACH(if_addr, addr_list, entry)
182 if (!ldp_addrcmp(af, &if_addr->addr, &ka->addr) &&
183 if_addr->prefixlen == ka->prefixlen &&
184 !ldp_addrcmp(af, &if_addr->dstbrd, &ka->dstbrd))
185 return (if_addr);
186
187 return (NULL);
188}
189
190void
191if_addr_add(struct kaddr *ka)
192{
193 struct iface *iface;
194 struct if_addr *if_addr;
195 struct nbr *nbr;
196
197 if (if_addr_lookup(&global.addr_list, ka) == NULL) {
198 if_addr = if_addr_new(ka);
199
200 LIST_INSERT_HEAD(&global.addr_list, if_addr, entry);
201 RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
202 if (nbr->state != NBR_STA_OPER)
203 continue;
204 if (if_addr->af == AF_INET && !nbr->v4_enabled)
205 continue;
206 if (if_addr->af == AF_INET6 && !nbr->v6_enabled)
207 continue;
208
209 send_address_single(nbr, if_addr, 0);
210 }
211 }
212
9cf67225 213 iface = if_lookup_name(leconf, ka->ifname);
8429abe0
RW
214 if (iface) {
215 if (ka->af == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&ka->addr.v6))
216 iface->linklocal = ka->addr.v6;
217
218 if (if_addr_lookup(&iface->addr_list, ka) == NULL) {
219 if_addr = if_addr_new(ka);
220 LIST_INSERT_HEAD(&iface->addr_list, if_addr, entry);
61cd1100 221 ldp_if_update(iface, if_addr->af);
8429abe0
RW
222 }
223 }
224}
225
226void
227if_addr_del(struct kaddr *ka)
228{
229 struct iface *iface;
230 struct if_addr *if_addr;
231 struct nbr *nbr;
232
9cf67225 233 iface = if_lookup_name(leconf, ka->ifname);
8429abe0
RW
234 if (iface) {
235 if (ka->af == AF_INET6 &&
236 IN6_ARE_ADDR_EQUAL(&iface->linklocal, &ka->addr.v6))
237 memset(&iface->linklocal, 0, sizeof(iface->linklocal));
238
239 if_addr = if_addr_lookup(&iface->addr_list, ka);
240 if (if_addr) {
241 LIST_REMOVE(if_addr, entry);
61cd1100 242 ldp_if_update(iface, if_addr->af);
8429abe0
RW
243 free(if_addr);
244 }
245 }
246
247 if_addr = if_addr_lookup(&global.addr_list, ka);
248 if (if_addr) {
249 RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
250 if (nbr->state != NBR_STA_OPER)
251 continue;
252 if (if_addr->af == AF_INET && !nbr->v4_enabled)
253 continue;
254 if (if_addr->af == AF_INET6 && !nbr->v6_enabled)
255 continue;
256 send_address_single(nbr, if_addr, 1);
257 }
258 LIST_REMOVE(if_addr, entry);
259 free(if_addr);
260 }
261}
262
263static int
264if_start(struct iface *iface, int af)
265{
266 struct iface_af *ia;
267 struct timeval now;
268
269 log_debug("%s: %s address-family %s", __func__, iface->name,
270 af_name(af));
271
272 ia = iface_af_get(iface, af);
273
274 gettimeofday(&now, NULL);
275 ia->uptime = now.tv_sec;
276
277 switch (af) {
278 case AF_INET:
279 if (if_join_ipv4_group(iface, &global.mcast_addr_v4))
280 return (-1);
281 break;
282 case AF_INET6:
283 if (if_join_ipv6_group(iface, &global.mcast_addr_v6))
284 return (-1);
285 break;
286 default:
287 fatalx("if_start: unknown af");
288 }
289
290 send_hello(HELLO_LINK, ia, NULL);
8429abe0 291 if_start_hello_timer(ia);
8a26d0cf
RW
292 ia->state = IF_STA_ACTIVE;
293
8429abe0
RW
294 return (0);
295}
296
297static int
298if_reset(struct iface *iface, int af)
299{
300 struct iface_af *ia;
301 struct adj *adj;
302
303 log_debug("%s: %s address-family %s", __func__, iface->name,
304 af_name(af));
305
306 ia = iface_af_get(iface, af);
307 if_stop_hello_timer(ia);
308
55cd0f61
DS
309 while (!RB_EMPTY(ia_adj_head, &ia->adj_tree)) {
310 adj = RB_ROOT(ia_adj_head, &ia->adj_tree);
311
8429abe0 312 adj_del(adj, S_SHUTDOWN);
55cd0f61 313 }
8429abe0
RW
314
315 /* try to cleanup */
316 switch (af) {
317 case AF_INET:
318 if (global.ipv4.ldp_disc_socket != -1)
319 if_leave_ipv4_group(iface, &global.mcast_addr_v4);
320 break;
321 case AF_INET6:
322 if (global.ipv6.ldp_disc_socket != -1)
323 if_leave_ipv6_group(iface, &global.mcast_addr_v6);
324 break;
325 default:
8a26d0cf 326 fatalx("if_reset: unknown af");
8429abe0
RW
327 }
328
8a26d0cf
RW
329 ia->state = IF_STA_DOWN;
330
8429abe0
RW
331 return (0);
332}
333
334static void
988ded8d 335if_update_af(struct iface_af *ia)
8429abe0
RW
336{
337 int addr_ok = 0, socket_ok, rtr_id_ok;
338 struct if_addr *if_addr;
339
340 switch (ia->af) {
341 case AF_INET:
342 /*
343 * NOTE: for LDPv4, each interface should have at least one
344 * valid IP address otherwise they can not be enabled.
345 */
346 LIST_FOREACH(if_addr, &ia->iface->addr_list, entry) {
347 if (if_addr->af == AF_INET) {
348 addr_ok = 1;
349 break;
350 }
351 }
352 break;
353 case AF_INET6:
354 /* for IPv6 the link-local address is enough. */
355 if (IN6_IS_ADDR_LINKLOCAL(&ia->iface->linklocal))
356 addr_ok = 1;
357 break;
358 default:
359 fatalx("if_update_af: unknown af");
360 }
361
362 if ((ldp_af_global_get(&global, ia->af))->ldp_disc_socket != -1)
363 socket_ok = 1;
364 else
365 socket_ok = 0;
366
eac6e3f0 367 if (ldp_rtr_id_get(leconf) != INADDR_ANY)
8429abe0
RW
368 rtr_id_ok = 1;
369 else
370 rtr_id_ok = 0;
371
372 if (ia->state == IF_STA_DOWN) {
988ded8d
RW
373 if (!ia->enabled || !ia->iface->operative || !addr_ok ||
374 !socket_ok || !rtr_id_ok)
8429abe0
RW
375 return;
376
8429abe0
RW
377 if_start(ia->iface, ia->af);
378 } else if (ia->state == IF_STA_ACTIVE) {
988ded8d
RW
379 if (ia->enabled && ia->iface->operative && addr_ok &&
380 socket_ok && rtr_id_ok)
8429abe0
RW
381 return;
382
8429abe0
RW
383 if_reset(ia->iface, ia->af);
384 }
385}
386
387void
61cd1100 388ldp_if_update(struct iface *iface, int af)
8429abe0 389{
8429abe0 390 if (af == AF_INET || af == AF_UNSPEC)
988ded8d 391 if_update_af(&iface->ipv4);
8429abe0 392 if (af == AF_INET6 || af == AF_UNSPEC)
988ded8d 393 if_update_af(&iface->ipv6);
8429abe0
RW
394}
395
396void
397if_update_all(int af)
398{
399 struct iface *iface;
400
7d3d7491 401 RB_FOREACH(iface, iface_head, &leconf->iface_tree)
61cd1100 402 ldp_if_update(iface, af);
8429abe0
RW
403}
404
eac6e3f0
RW
405uint16_t
406if_get_hello_holdtime(struct iface_af *ia)
407{
408 if (ia->hello_holdtime != 0)
409 return (ia->hello_holdtime);
410
411 if ((ldp_af_conf_get(leconf, ia->af))->lhello_holdtime != 0)
412 return ((ldp_af_conf_get(leconf, ia->af))->lhello_holdtime);
413
414 return (leconf->lhello_holdtime);
415}
416
417uint16_t
418if_get_hello_interval(struct iface_af *ia)
419{
420 if (ia->hello_interval != 0)
421 return (ia->hello_interval);
422
423 if ((ldp_af_conf_get(leconf, ia->af))->lhello_interval != 0)
424 return ((ldp_af_conf_get(leconf, ia->af))->lhello_interval);
425
426 return (leconf->lhello_interval);
427}
428
8429abe0
RW
429/* timers */
430/* ARGSUSED */
eac6e3f0
RW
431static int
432if_hello_timer(struct thread *thread)
8429abe0 433{
eac6e3f0 434 struct iface_af *ia = THREAD_ARG(thread);
8429abe0 435
eac6e3f0 436 ia->hello_timer = NULL;
8429abe0
RW
437 send_hello(HELLO_LINK, ia, NULL);
438 if_start_hello_timer(ia);
eac6e3f0
RW
439
440 return (0);
8429abe0
RW
441}
442
443static void
444if_start_hello_timer(struct iface_af *ia)
445{
eac6e3f0 446 THREAD_TIMER_OFF(ia->hello_timer);
66e78ae6
QY
447 ia->hello_timer = NULL;
448 thread_add_timer(master, if_hello_timer, ia, if_get_hello_interval(ia),
449 &ia->hello_timer);
8429abe0
RW
450}
451
452static void
453if_stop_hello_timer(struct iface_af *ia)
454{
eac6e3f0 455 THREAD_TIMER_OFF(ia->hello_timer);
8429abe0
RW
456}
457
458struct ctl_iface *
459if_to_ctl(struct iface_af *ia)
460{
461 static struct ctl_iface ictl;
462 struct timeval now;
463 struct adj *adj;
464
465 ictl.af = ia->af;
466 memcpy(ictl.name, ia->iface->name, sizeof(ictl.name));
467 ictl.ifindex = ia->iface->ifindex;
468 ictl.state = ia->state;
8429abe0 469 ictl.type = ia->iface->type;
eac6e3f0
RW
470 ictl.hello_holdtime = if_get_hello_holdtime(ia);
471 ictl.hello_interval = if_get_hello_interval(ia);
8429abe0
RW
472
473 gettimeofday(&now, NULL);
474 if (ia->state != IF_STA_DOWN &&
475 ia->uptime != 0) {
476 ictl.uptime = now.tv_sec - ia->uptime;
477 } else
478 ictl.uptime = 0;
479
480 ictl.adj_cnt = 0;
057d48bd 481 RB_FOREACH(adj, ia_adj_head, &ia->adj_tree)
8429abe0
RW
482 ictl.adj_cnt++;
483
484 return (&ictl);
485}
486
487/* multicast membership sockopts */
488in_addr_t
489if_get_ipv4_addr(struct iface *iface)
490{
491 struct if_addr *if_addr;
492
493 LIST_FOREACH(if_addr, &iface->addr_list, entry)
494 if (if_addr->af == AF_INET)
495 return (if_addr->addr.v4.s_addr);
496
497 return (INADDR_ANY);
498}
499
500static int
501if_join_ipv4_group(struct iface *iface, struct in_addr *addr)
502{
eac6e3f0 503 struct in_addr if_addr;
8429abe0
RW
504
505 log_debug("%s: interface %s addr %s", __func__, iface->name,
506 inet_ntoa(*addr));
507
eac6e3f0 508 if_addr.s_addr = if_get_ipv4_addr(iface);
8429abe0 509
eac6e3f0
RW
510 if (setsockopt_ipv4_multicast(global.ipv4.ldp_disc_socket,
511 IP_ADD_MEMBERSHIP, if_addr, addr->s_addr, iface->ifindex) < 0) {
8429abe0
RW
512 log_warn("%s: error IP_ADD_MEMBERSHIP, interface %s address %s",
513 __func__, iface->name, inet_ntoa(*addr));
514 return (-1);
515 }
516 return (0);
517}
518
519static int
520if_leave_ipv4_group(struct iface *iface, struct in_addr *addr)
521{
eac6e3f0 522 struct in_addr if_addr;
8429abe0
RW
523
524 log_debug("%s: interface %s addr %s", __func__, iface->name,
525 inet_ntoa(*addr));
526
eac6e3f0 527 if_addr.s_addr = if_get_ipv4_addr(iface);
8429abe0 528
eac6e3f0
RW
529 if (setsockopt_ipv4_multicast(global.ipv4.ldp_disc_socket,
530 IP_DROP_MEMBERSHIP, if_addr, addr->s_addr, iface->ifindex) < 0) {
8429abe0
RW
531 log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s "
532 "address %s", __func__, iface->name, inet_ntoa(*addr));
533 return (-1);
534 }
535
536 return (0);
537}
538
539static int
540if_join_ipv6_group(struct iface *iface, struct in6_addr *addr)
541{
542 struct ipv6_mreq mreq;
543
544 log_debug("%s: interface %s addr %s", __func__, iface->name,
545 log_in6addr(addr));
546
547 mreq.ipv6mr_multiaddr = *addr;
548 mreq.ipv6mr_interface = iface->ifindex;
549
550 if (setsockopt(global.ipv6.ldp_disc_socket, IPPROTO_IPV6,
551 IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) {
552 log_warn("%s: error IPV6_JOIN_GROUP, interface %s address %s",
553 __func__, iface->name, log_in6addr(addr));
554 return (-1);
555 }
556
557 return (0);
558}
559
560static int
561if_leave_ipv6_group(struct iface *iface, struct in6_addr *addr)
562{
563 struct ipv6_mreq mreq;
564
565 log_debug("%s: interface %s addr %s", __func__, iface->name,
566 log_in6addr(addr));
567
568 mreq.ipv6mr_multiaddr = *addr;
569 mreq.ipv6mr_interface = iface->ifindex;
570
571 if (setsockopt(global.ipv6.ldp_disc_socket, IPPROTO_IPV6,
572 IPV6_LEAVE_GROUP, (void *)&mreq, sizeof(mreq)) < 0) {
573 log_warn("%s: error IPV6_LEAVE_GROUP, interface %s address %s",
574 __func__, iface->name, log_in6addr(addr));
575 return (-1);
576 }
577
578 return (0);
579}