interface.c revision 1.51
1/*	$OpenBSD: interface.c,v 1.51 2019/06/28 13:32:48 deraadt Exp $ */
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
21#include <sys/types.h>
22#include <sys/time.h>
23#include <arpa/inet.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include "ldpd.h"
28#include "ldpe.h"
29#include "log.h"
30
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);
35static void		 if_update_af(struct iface_af *, int);
36static void		 if_hello_timer(int, short, void *);
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
44struct iface *
45if_new(struct kif *kif)
46{
47	struct iface		*iface;
48
49	if ((iface = calloc(1, sizeof(*iface))) == NULL)
50		fatal("if_new: calloc");
51
52	strlcpy(iface->name, kif->ifname, sizeof(iface->name));
53
54	/* get type */
55	if (kif->flags & IFF_POINTOPOINT)
56		iface->type = IF_TYPE_POINTOPOINT;
57	if (kif->flags & IFF_BROADCAST &&
58	    kif->flags & IFF_MULTICAST)
59		iface->type = IF_TYPE_BROADCAST;
60
61	/* get index and flags */
62	LIST_INIT(&iface->addr_list);
63	iface->ifindex = kif->ifindex;
64	iface->rdomain = kif->rdomain;
65	iface->flags = kif->flags;
66	iface->linkstate = kif->link_state;
67	iface->if_type = kif->if_type;
68
69	/* ipv4 */
70	iface->ipv4.af = AF_INET;
71	iface->ipv4.iface = iface;
72	iface->ipv4.enabled = 0;
73	iface->ipv4.state = IF_STA_DOWN;
74	LIST_INIT(&iface->ipv4.adj_list);
75
76	/* ipv6 */
77	iface->ipv6.af = AF_INET6;
78	iface->ipv6.iface = iface;
79	iface->ipv6.enabled = 0;
80	iface->ipv6.state = IF_STA_DOWN;
81	LIST_INIT(&iface->ipv6.adj_list);
82
83	return (iface);
84}
85
86void
87if_exit(struct iface *iface)
88{
89	struct if_addr		*if_addr;
90
91	log_debug("%s: interface %s", __func__, iface->name);
92
93	if (iface->ipv4.state == IF_STA_ACTIVE)
94		if_reset(iface, AF_INET);
95	if (iface->ipv6.state == IF_STA_ACTIVE)
96		if_reset(iface, AF_INET6);
97
98	while ((if_addr = LIST_FIRST(&iface->addr_list)) != NULL) {
99		LIST_REMOVE(if_addr, entry);
100		free(if_addr);
101	}
102}
103
104struct iface *
105if_lookup(struct ldpd_conf *xconf, unsigned short ifindex)
106{
107	struct iface *iface;
108
109	LIST_FOREACH(iface, &xconf->iface_list, entry)
110		if (iface->ifindex == ifindex)
111			return (iface);
112
113	return (NULL);
114}
115
116struct iface_af *
117iface_af_get(struct iface *iface, int af)
118{
119	switch (af) {
120	case AF_INET:
121		return (&iface->ipv4);
122	case AF_INET6:
123		return (&iface->ipv6);
124	default:
125		fatalx("iface_af_get: unknown af");
126	}
127}
128
129static struct if_addr *
130if_addr_new(struct kaddr *ka)
131{
132	struct if_addr	*if_addr;
133
134	if ((if_addr = calloc(1, sizeof(*if_addr))) == NULL)
135		fatal(__func__);
136
137	if_addr->af = ka->af;
138	if_addr->addr = ka->addr;
139	if_addr->prefixlen = ka->prefixlen;
140	if_addr->dstbrd = ka->dstbrd;
141
142	return (if_addr);
143}
144
145static struct if_addr *
146if_addr_lookup(struct if_addr_head *addr_list, struct kaddr *ka)
147{
148	struct if_addr	*if_addr;
149	int		 af = ka->af;
150
151	LIST_FOREACH(if_addr, addr_list, entry)
152		if (!ldp_addrcmp(af, &if_addr->addr, &ka->addr) &&
153		    if_addr->prefixlen == ka->prefixlen &&
154		    !ldp_addrcmp(af, &if_addr->dstbrd, &ka->dstbrd))
155			return (if_addr);
156
157	return (NULL);
158}
159
160void
161if_addr_add(struct kaddr *ka)
162{
163	struct iface		*iface;
164	struct if_addr		*if_addr;
165	struct nbr		*nbr;
166
167	if (if_addr_lookup(&global.addr_list, ka) == NULL) {
168		if_addr = if_addr_new(ka);
169
170		LIST_INSERT_HEAD(&global.addr_list, if_addr, entry);
171		RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
172			if (nbr->state != NBR_STA_OPER)
173				continue;
174			if (if_addr->af == AF_INET && !nbr->v4_enabled)
175				continue;
176			if (if_addr->af == AF_INET6 && !nbr->v6_enabled)
177				continue;
178
179			send_address_single(nbr, if_addr, 0);
180		}
181	}
182
183	iface = if_lookup(leconf, ka->ifindex);
184	if (iface) {
185		if (ka->af == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&ka->addr.v6))
186			iface->linklocal = ka->addr.v6;
187
188		if (if_addr_lookup(&iface->addr_list, ka) == NULL) {
189			if_addr = if_addr_new(ka);
190			LIST_INSERT_HEAD(&iface->addr_list, if_addr, entry);
191			if_update(iface, if_addr->af);
192		}
193	}
194}
195
196void
197if_addr_del(struct kaddr *ka)
198{
199	struct iface		*iface;
200	struct if_addr		*if_addr;
201	struct nbr		*nbr;
202
203	iface = if_lookup(leconf, ka->ifindex);
204	if (iface) {
205		if (ka->af == AF_INET6 &&
206		    IN6_ARE_ADDR_EQUAL(&iface->linklocal, &ka->addr.v6))
207			memset(&iface->linklocal, 0, sizeof(iface->linklocal));
208
209		if_addr = if_addr_lookup(&iface->addr_list, ka);
210		if (if_addr) {
211			LIST_REMOVE(if_addr, entry);
212			if_update(iface, if_addr->af);
213			free(if_addr);
214		}
215	}
216
217	if_addr = if_addr_lookup(&global.addr_list, ka);
218	if (if_addr) {
219		RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
220			if (nbr->state != NBR_STA_OPER)
221				continue;
222			if (if_addr->af == AF_INET && !nbr->v4_enabled)
223				continue;
224			if (if_addr->af == AF_INET6 && !nbr->v6_enabled)
225				continue;
226			send_address_single(nbr, if_addr, 1);
227		}
228		LIST_REMOVE(if_addr, entry);
229		free(if_addr);
230	}
231}
232
233static int
234if_start(struct iface *iface, int af)
235{
236	struct iface_af		*ia;
237	struct timeval		 now;
238
239	log_debug("%s: %s address-family %s", __func__, iface->name,
240	    af_name(af));
241
242	ia = iface_af_get(iface, af);
243
244	gettimeofday(&now, NULL);
245	ia->uptime = now.tv_sec;
246
247	switch (af) {
248	case AF_INET:
249		if (if_join_ipv4_group(iface, &global.mcast_addr_v4))
250			return (-1);
251		break;
252	case AF_INET6:
253		if (if_join_ipv6_group(iface, &global.mcast_addr_v6))
254			return (-1);
255		break;
256	default:
257		fatalx("if_start: unknown af");
258	}
259
260	send_hello(HELLO_LINK, ia, NULL);
261
262	evtimer_set(&ia->hello_timer, if_hello_timer, ia);
263	if_start_hello_timer(ia);
264	return (0);
265}
266
267static int
268if_reset(struct iface *iface, int af)
269{
270	struct iface_af		*ia;
271	struct adj		*adj;
272
273	log_debug("%s: %s address-family %s", __func__, iface->name,
274	    af_name(af));
275
276	ia = iface_af_get(iface, af);
277	if_stop_hello_timer(ia);
278
279	while ((adj = LIST_FIRST(&ia->adj_list)) != NULL)
280		adj_del(adj, S_SHUTDOWN);
281
282	/* try to cleanup */
283	switch (af) {
284	case AF_INET:
285		if (global.ipv4.ldp_disc_socket != -1)
286			if_leave_ipv4_group(iface, &global.mcast_addr_v4);
287		break;
288	case AF_INET6:
289		if (global.ipv6.ldp_disc_socket != -1)
290			if_leave_ipv6_group(iface, &global.mcast_addr_v6);
291		break;
292	default:
293		fatalx("if_start: unknown af");
294	}
295
296	return (0);
297}
298
299static void
300if_update_af(struct iface_af *ia, int link_ok)
301{
302	int			 addr_ok = 0, socket_ok, rtr_id_ok;
303	struct if_addr		*if_addr;
304
305	switch (ia->af) {
306	case AF_INET:
307		/*
308		 * NOTE: for LDPv4, each interface should have at least one
309		 * valid IP address otherwise they can not be enabled.
310		 */
311		LIST_FOREACH(if_addr, &ia->iface->addr_list, entry) {
312			if (if_addr->af == AF_INET) {
313				addr_ok = 1;
314				break;
315			}
316		}
317		break;
318	case AF_INET6:
319		/* for IPv6 the link-local address is enough. */
320		if (IN6_IS_ADDR_LINKLOCAL(&ia->iface->linklocal))
321			addr_ok = 1;
322		break;
323	default:
324		fatalx("if_update_af: unknown af");
325	}
326
327	if ((ldp_af_global_get(&global, ia->af))->ldp_disc_socket != -1)
328		socket_ok = 1;
329	else
330		socket_ok = 0;
331
332	if (leconf->rtr_id.s_addr != INADDR_ANY)
333		rtr_id_ok = 1;
334	else
335		rtr_id_ok = 0;
336
337	if (ia->state == IF_STA_DOWN) {
338		if (!ia->enabled || !link_ok || !addr_ok || !socket_ok ||
339		    !rtr_id_ok)
340			return;
341
342		ia->state = IF_STA_ACTIVE;
343		if_start(ia->iface, ia->af);
344	} else if (ia->state == IF_STA_ACTIVE) {
345		if (ia->enabled && link_ok && addr_ok && socket_ok && rtr_id_ok)
346			return;
347
348		ia->state = IF_STA_DOWN;
349		if_reset(ia->iface, ia->af);
350	}
351}
352
353void
354if_update(struct iface *iface, int af)
355{
356	int			 link_ok;
357
358	link_ok = (iface->flags & IFF_UP) &&
359	    LINK_STATE_IS_UP(iface->linkstate);
360
361	if (af == AF_INET || af == AF_UNSPEC)
362		if_update_af(&iface->ipv4, link_ok);
363	if (af == AF_INET6 || af == AF_UNSPEC)
364		if_update_af(&iface->ipv6, link_ok);
365}
366
367void
368if_update_all(int af)
369{
370	struct iface		*iface;
371
372	LIST_FOREACH(iface, &leconf->iface_list, entry)
373		if_update(iface, af);
374}
375
376/* timers */
377/* ARGSUSED */
378static void
379if_hello_timer(int fd, short event, void *arg)
380{
381	struct iface_af		*ia = arg;
382
383	send_hello(HELLO_LINK, ia, NULL);
384	if_start_hello_timer(ia);
385}
386
387static void
388if_start_hello_timer(struct iface_af *ia)
389{
390	struct timeval		 tv;
391
392	timerclear(&tv);
393	tv.tv_sec = ia->hello_interval;
394	if (evtimer_add(&ia->hello_timer, &tv) == -1)
395		fatal(__func__);
396}
397
398static void
399if_stop_hello_timer(struct iface_af *ia)
400{
401	if (evtimer_pending(&ia->hello_timer, NULL) &&
402	    evtimer_del(&ia->hello_timer) == -1)
403		fatal(__func__);
404}
405
406struct ctl_iface *
407if_to_ctl(struct iface_af *ia)
408{
409	static struct ctl_iface	 ictl;
410	struct timeval		 now;
411	struct adj		*adj;
412
413	ictl.af = ia->af;
414	memcpy(ictl.name, ia->iface->name, sizeof(ictl.name));
415	ictl.ifindex = ia->iface->ifindex;
416	ictl.state = ia->state;
417	ictl.flags = ia->iface->flags;
418	ictl.linkstate = ia->iface->linkstate;
419	ictl.type = ia->iface->type;
420	ictl.if_type = ia->iface->if_type;
421	ictl.hello_holdtime = ia->hello_holdtime;
422	ictl.hello_interval = ia->hello_interval;
423
424	gettimeofday(&now, NULL);
425	if (ia->state != IF_STA_DOWN &&
426	    ia->uptime != 0) {
427		ictl.uptime = now.tv_sec - ia->uptime;
428	} else
429		ictl.uptime = 0;
430
431	ictl.adj_cnt = 0;
432	LIST_FOREACH(adj, &ia->adj_list, ia_entry)
433		ictl.adj_cnt++;
434
435	return (&ictl);
436}
437
438/* multicast membership sockopts */
439in_addr_t
440if_get_ipv4_addr(struct iface *iface)
441{
442	struct if_addr		*if_addr;
443
444	LIST_FOREACH(if_addr, &iface->addr_list, entry)
445		if (if_addr->af == AF_INET)
446			return (if_addr->addr.v4.s_addr);
447
448	return (INADDR_ANY);
449}
450
451static int
452if_join_ipv4_group(struct iface *iface, struct in_addr *addr)
453{
454	struct ip_mreq		 mreq;
455
456	log_debug("%s: interface %s addr %s", __func__, iface->name,
457	    inet_ntoa(*addr));
458
459	mreq.imr_multiaddr = *addr;
460	mreq.imr_interface.s_addr = if_get_ipv4_addr(iface);
461
462	if (setsockopt(global.ipv4.ldp_disc_socket, IPPROTO_IP,
463	    IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) == -1) {
464		log_warn("%s: error IP_ADD_MEMBERSHIP, interface %s address %s",
465		     __func__, iface->name, inet_ntoa(*addr));
466		return (-1);
467	}
468	return (0);
469}
470
471static int
472if_leave_ipv4_group(struct iface *iface, struct in_addr *addr)
473{
474	struct ip_mreq		 mreq;
475
476	log_debug("%s: interface %s addr %s", __func__, iface->name,
477	    inet_ntoa(*addr));
478
479	mreq.imr_multiaddr = *addr;
480	mreq.imr_interface.s_addr = if_get_ipv4_addr(iface);
481
482	if (setsockopt(global.ipv4.ldp_disc_socket, IPPROTO_IP,
483	    IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) == -1) {
484		log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s "
485		    "address %s", __func__, iface->name, inet_ntoa(*addr));
486		return (-1);
487	}
488
489	return (0);
490}
491
492static int
493if_join_ipv6_group(struct iface *iface, struct in6_addr *addr)
494{
495	struct ipv6_mreq	 mreq;
496
497	log_debug("%s: interface %s addr %s", __func__, iface->name,
498	    log_in6addr(addr));
499
500	mreq.ipv6mr_multiaddr = *addr;
501	mreq.ipv6mr_interface = iface->ifindex;
502
503	if (setsockopt(global.ipv6.ldp_disc_socket, IPPROTO_IPV6,
504	    IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) {
505		log_warn("%s: error IPV6_JOIN_GROUP, interface %s address %s",
506		    __func__, iface->name, log_in6addr(addr));
507		return (-1);
508	}
509
510	return (0);
511}
512
513static int
514if_leave_ipv6_group(struct iface *iface, struct in6_addr *addr)
515{
516	struct ipv6_mreq	 mreq;
517
518	log_debug("%s: interface %s addr %s", __func__, iface->name,
519	    log_in6addr(addr));
520
521	mreq.ipv6mr_multiaddr = *addr;
522	mreq.ipv6mr_interface = iface->ifindex;
523
524	if (setsockopt(global.ipv6.ldp_disc_socket, IPPROTO_IPV6,
525	    IPV6_LEAVE_GROUP, (void *)&mreq, sizeof(mreq)) == -1) {
526		log_warn("%s: error IPV6_LEAVE_GROUP, interface %s address %s",
527		    __func__, iface->name, log_in6addr(addr));
528		return (-1);
529	}
530
531	return (0);
532}
533