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