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