]>
git.proxmox.com Git - mirror_frr.git/blob - ldpd/interface.c
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>
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.
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.
29 static __inline
int iface_compare(const struct iface
*, const struct iface
*);
30 static struct if_addr
*if_addr_new(struct kaddr
*);
31 static struct if_addr
*if_addr_lookup(struct if_addr_head
*, struct kaddr
*);
32 static int if_start(struct iface
*, int);
33 static int if_reset(struct iface
*, int);
34 static void if_update_af(struct iface_af
*);
35 static int if_hello_timer(struct thread
*);
36 static void if_start_hello_timer(struct iface_af
*);
37 static void if_stop_hello_timer(struct iface_af
*);
38 static int if_join_ipv4_group(struct iface
*, struct in_addr
*);
39 static int if_leave_ipv4_group(struct iface
*, struct in_addr
*);
40 static int if_join_ipv6_group(struct iface
*, struct in6_addr
*);
41 static int if_leave_ipv6_group(struct iface
*, struct in6_addr
*);
43 RB_GENERATE(iface_head
, iface
, entry
, iface_compare
)
46 iface_compare(const struct iface
*a
, const struct iface
*b
)
48 return if_cmp_name_func(a
->name
, b
->name
);
52 if_new(const char *name
)
56 if ((iface
= calloc(1, sizeof(*iface
))) == NULL
)
57 fatal("if_new: calloc");
59 strlcpy(iface
->name
, name
, sizeof(iface
->name
));
62 iface
->ipv4
.af
= AF_INET
;
63 iface
->ipv4
.iface
= iface
;
64 iface
->ipv4
.enabled
= 0;
67 iface
->ipv6
.af
= AF_INET6
;
68 iface
->ipv6
.iface
= iface
;
69 iface
->ipv6
.enabled
= 0;
75 ldpe_if_init(struct iface
*iface
)
77 log_debug("%s: interface %s", __func__
, iface
->name
);
79 LIST_INIT(&iface
->addr_list
);
82 iface
->ipv4
.iface
= iface
;
83 iface
->ipv4
.state
= IF_STA_DOWN
;
84 RB_INIT(ia_adj_head
, &iface
->ipv4
.adj_tree
);
87 iface
->ipv6
.iface
= iface
;
88 iface
->ipv6
.state
= IF_STA_DOWN
;
89 RB_INIT(ia_adj_head
, &iface
->ipv6
.adj_tree
);
93 ldpe_if_exit(struct iface
*iface
)
95 struct if_addr
*if_addr
;
97 log_debug("%s: interface %s", __func__
, iface
->name
);
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
);
104 while ((if_addr
= LIST_FIRST(&iface
->addr_list
)) != NULL
) {
105 LIST_REMOVE(if_addr
, entry
);
106 assert(if_addr
!= LIST_FIRST(&iface
->addr_list
));
112 if_lookup(struct ldpd_conf
*xconf
, ifindex_t ifindex
)
116 RB_FOREACH(iface
, iface_head
, &xconf
->iface_tree
)
117 if (iface
->ifindex
== ifindex
)
124 if_lookup_name(struct ldpd_conf
*xconf
, const char *ifname
)
127 strlcpy(iface
.name
, ifname
, sizeof(iface
.name
));
128 return (RB_FIND(iface_head
, &xconf
->iface_tree
, &iface
));
132 if_update_info(struct iface
*iface
, struct kif
*kif
)
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
;
141 /* get index and flags */
142 iface
->ifindex
= kif
->ifindex
;
143 iface
->operative
= kif
->operative
;
147 iface_af_get(struct iface
*iface
, int af
)
151 return (&iface
->ipv4
);
153 return (&iface
->ipv6
);
155 fatalx("iface_af_get: unknown af");
159 static struct if_addr
*
160 if_addr_new(struct kaddr
*ka
)
162 struct if_addr
*if_addr
;
164 if ((if_addr
= calloc(1, sizeof(*if_addr
))) == NULL
)
167 if_addr
->af
= ka
->af
;
168 if_addr
->addr
= ka
->addr
;
169 if_addr
->prefixlen
= ka
->prefixlen
;
170 if_addr
->dstbrd
= ka
->dstbrd
;
175 static struct if_addr
*
176 if_addr_lookup(struct if_addr_head
*addr_list
, struct kaddr
*ka
)
178 struct if_addr
*if_addr
;
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
))
191 if_addr_add(struct kaddr
*ka
)
194 struct if_addr
*if_addr
;
197 if (if_addr_lookup(&global
.addr_list
, ka
) == NULL
) {
198 if_addr
= if_addr_new(ka
);
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
)
204 if (if_addr
->af
== AF_INET
&& !nbr
->v4_enabled
)
206 if (if_addr
->af
== AF_INET6
&& !nbr
->v6_enabled
)
209 send_address_single(nbr
, if_addr
, 0);
213 iface
= if_lookup_name(leconf
, ka
->ifname
);
215 if (ka
->af
== AF_INET6
&& IN6_IS_ADDR_LINKLOCAL(&ka
->addr
.v6
))
216 iface
->linklocal
= ka
->addr
.v6
;
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
);
221 ldp_if_update(iface
, if_addr
->af
);
227 if_addr_del(struct kaddr
*ka
)
230 struct if_addr
*if_addr
;
233 iface
= if_lookup_name(leconf
, ka
->ifname
);
235 if (ka
->af
== AF_INET6
&&
236 IN6_ARE_ADDR_EQUAL(&iface
->linklocal
, &ka
->addr
.v6
))
237 memset(&iface
->linklocal
, 0, sizeof(iface
->linklocal
));
239 if_addr
= if_addr_lookup(&iface
->addr_list
, ka
);
241 LIST_REMOVE(if_addr
, entry
);
242 ldp_if_update(iface
, if_addr
->af
);
247 if_addr
= if_addr_lookup(&global
.addr_list
, ka
);
249 RB_FOREACH(nbr
, nbr_id_head
, &nbrs_by_id
) {
250 if (nbr
->state
!= NBR_STA_OPER
)
252 if (if_addr
->af
== AF_INET
&& !nbr
->v4_enabled
)
254 if (if_addr
->af
== AF_INET6
&& !nbr
->v6_enabled
)
256 send_address_single(nbr
, if_addr
, 1);
258 LIST_REMOVE(if_addr
, entry
);
264 if_start(struct iface
*iface
, int af
)
269 log_debug("%s: %s address-family %s", __func__
, iface
->name
,
272 ia
= iface_af_get(iface
, af
);
274 gettimeofday(&now
, NULL
);
275 ia
->uptime
= now
.tv_sec
;
279 if (if_join_ipv4_group(iface
, &global
.mcast_addr_v4
))
283 if (if_join_ipv6_group(iface
, &global
.mcast_addr_v6
))
287 fatalx("if_start: unknown af");
290 send_hello(HELLO_LINK
, ia
, NULL
);
291 if_start_hello_timer(ia
);
292 ia
->state
= IF_STA_ACTIVE
;
298 if_reset(struct iface
*iface
, int af
)
303 log_debug("%s: %s address-family %s", __func__
, iface
->name
,
306 ia
= iface_af_get(iface
, af
);
307 if_stop_hello_timer(ia
);
309 while (!RB_EMPTY(ia_adj_head
, &ia
->adj_tree
)) {
310 adj
= RB_ROOT(ia_adj_head
, &ia
->adj_tree
);
312 adj_del(adj
, S_SHUTDOWN
);
318 if (global
.ipv4
.ldp_disc_socket
!= -1)
319 if_leave_ipv4_group(iface
, &global
.mcast_addr_v4
);
322 if (global
.ipv6
.ldp_disc_socket
!= -1)
323 if_leave_ipv6_group(iface
, &global
.mcast_addr_v6
);
326 fatalx("if_reset: unknown af");
329 ia
->state
= IF_STA_DOWN
;
335 if_update_af(struct iface_af
*ia
)
337 int addr_ok
= 0, socket_ok
, rtr_id_ok
;
338 struct if_addr
*if_addr
;
343 * NOTE: for LDPv4, each interface should have at least one
344 * valid IP address otherwise they can not be enabled.
346 LIST_FOREACH(if_addr
, &ia
->iface
->addr_list
, entry
) {
347 if (if_addr
->af
== AF_INET
) {
354 /* for IPv6 the link-local address is enough. */
355 if (IN6_IS_ADDR_LINKLOCAL(&ia
->iface
->linklocal
))
359 fatalx("if_update_af: unknown af");
362 if ((ldp_af_global_get(&global
, ia
->af
))->ldp_disc_socket
!= -1)
367 if (ldp_rtr_id_get(leconf
) != INADDR_ANY
)
372 if (ia
->state
== IF_STA_DOWN
) {
373 if (!ia
->enabled
|| !ia
->iface
->operative
|| !addr_ok
||
374 !socket_ok
|| !rtr_id_ok
)
377 if_start(ia
->iface
, ia
->af
);
378 } else if (ia
->state
== IF_STA_ACTIVE
) {
379 if (ia
->enabled
&& ia
->iface
->operative
&& addr_ok
&&
380 socket_ok
&& rtr_id_ok
)
383 if_reset(ia
->iface
, ia
->af
);
388 ldp_if_update(struct iface
*iface
, int af
)
390 if (af
== AF_INET
|| af
== AF_UNSPEC
)
391 if_update_af(&iface
->ipv4
);
392 if (af
== AF_INET6
|| af
== AF_UNSPEC
)
393 if_update_af(&iface
->ipv6
);
397 if_update_all(int af
)
401 RB_FOREACH(iface
, iface_head
, &leconf
->iface_tree
)
402 ldp_if_update(iface
, af
);
406 if_get_hello_holdtime(struct iface_af
*ia
)
408 if (ia
->hello_holdtime
!= 0)
409 return (ia
->hello_holdtime
);
411 if ((ldp_af_conf_get(leconf
, ia
->af
))->lhello_holdtime
!= 0)
412 return ((ldp_af_conf_get(leconf
, ia
->af
))->lhello_holdtime
);
414 return (leconf
->lhello_holdtime
);
418 if_get_hello_interval(struct iface_af
*ia
)
420 if (ia
->hello_interval
!= 0)
421 return (ia
->hello_interval
);
423 if ((ldp_af_conf_get(leconf
, ia
->af
))->lhello_interval
!= 0)
424 return ((ldp_af_conf_get(leconf
, ia
->af
))->lhello_interval
);
426 return (leconf
->lhello_interval
);
432 if_hello_timer(struct thread
*thread
)
434 struct iface_af
*ia
= THREAD_ARG(thread
);
436 ia
->hello_timer
= NULL
;
437 send_hello(HELLO_LINK
, ia
, NULL
);
438 if_start_hello_timer(ia
);
444 if_start_hello_timer(struct iface_af
*ia
)
446 THREAD_TIMER_OFF(ia
->hello_timer
);
447 ia
->hello_timer
= NULL
;
448 thread_add_timer(master
, if_hello_timer
, ia
, if_get_hello_interval(ia
),
453 if_stop_hello_timer(struct iface_af
*ia
)
455 THREAD_TIMER_OFF(ia
->hello_timer
);
459 if_to_ctl(struct iface_af
*ia
)
461 static struct ctl_iface ictl
;
466 memcpy(ictl
.name
, ia
->iface
->name
, sizeof(ictl
.name
));
467 ictl
.ifindex
= ia
->iface
->ifindex
;
468 ictl
.state
= ia
->state
;
469 ictl
.type
= ia
->iface
->type
;
470 ictl
.hello_holdtime
= if_get_hello_holdtime(ia
);
471 ictl
.hello_interval
= if_get_hello_interval(ia
);
473 gettimeofday(&now
, NULL
);
474 if (ia
->state
!= IF_STA_DOWN
&&
476 ictl
.uptime
= now
.tv_sec
- ia
->uptime
;
481 RB_FOREACH(adj
, ia_adj_head
, &ia
->adj_tree
)
487 /* multicast membership sockopts */
489 if_get_ipv4_addr(struct iface
*iface
)
491 struct if_addr
*if_addr
;
493 LIST_FOREACH(if_addr
, &iface
->addr_list
, entry
)
494 if (if_addr
->af
== AF_INET
)
495 return (if_addr
->addr
.v4
.s_addr
);
501 if_join_ipv4_group(struct iface
*iface
, struct in_addr
*addr
)
503 struct in_addr if_addr
;
505 log_debug("%s: interface %s addr %s", __func__
, iface
->name
,
508 if_addr
.s_addr
= if_get_ipv4_addr(iface
);
510 if (setsockopt_ipv4_multicast(global
.ipv4
.ldp_disc_socket
,
511 IP_ADD_MEMBERSHIP
, if_addr
, addr
->s_addr
, iface
->ifindex
) < 0) {
512 log_warn("%s: error IP_ADD_MEMBERSHIP, interface %s address %s",
513 __func__
, iface
->name
, inet_ntoa(*addr
));
520 if_leave_ipv4_group(struct iface
*iface
, struct in_addr
*addr
)
522 struct in_addr if_addr
;
524 log_debug("%s: interface %s addr %s", __func__
, iface
->name
,
527 if_addr
.s_addr
= if_get_ipv4_addr(iface
);
529 if (setsockopt_ipv4_multicast(global
.ipv4
.ldp_disc_socket
,
530 IP_DROP_MEMBERSHIP
, if_addr
, addr
->s_addr
, iface
->ifindex
) < 0) {
531 log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s "
532 "address %s", __func__
, iface
->name
, inet_ntoa(*addr
));
540 if_join_ipv6_group(struct iface
*iface
, struct in6_addr
*addr
)
542 struct ipv6_mreq mreq
;
544 log_debug("%s: interface %s addr %s", __func__
, iface
->name
,
547 mreq
.ipv6mr_multiaddr
= *addr
;
548 mreq
.ipv6mr_interface
= iface
->ifindex
;
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
));
561 if_leave_ipv6_group(struct iface
*iface
, struct in6_addr
*addr
)
563 struct ipv6_mreq mreq
;
565 log_debug("%s: interface %s addr %s", __func__
, iface
->name
,
568 mreq
.ipv6mr_multiaddr
= *addr
;
569 mreq
.ipv6mr_interface
= iface
->ifindex
;
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
));