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