interface.c revision 1.32
1/*	$OpenBSD: interface.c,v 1.32 2016/05/23 16:20:59 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
42extern struct ldpd_conf        *leconf;
43
44void		 if_hello_timer(int, short, void *);
45void		 if_start_hello_timer(struct iface *);
46void		 if_stop_hello_timer(struct iface *);
47
48struct iface *
49if_new(struct kif *kif)
50{
51	struct iface		*iface;
52
53	if ((iface = calloc(1, sizeof(*iface))) == NULL)
54		fatal("if_new: calloc");
55
56	iface->state = IF_STA_DOWN;
57
58	LIST_INIT(&iface->addr_list);
59	LIST_INIT(&iface->adj_list);
60
61	strlcpy(iface->name, kif->ifname, sizeof(iface->name));
62
63	/* get type */
64	if (kif->flags & IFF_POINTOPOINT)
65		iface->type = IF_TYPE_POINTOPOINT;
66	if (kif->flags & IFF_BROADCAST &&
67	    kif->flags & IFF_MULTICAST)
68		iface->type = IF_TYPE_BROADCAST;
69
70	/* get index and flags */
71	iface->ifindex = kif->ifindex;
72	iface->flags = kif->flags;
73	iface->linkstate = kif->link_state;
74	iface->if_type = kif->if_type;
75
76	return (iface);
77}
78
79void
80if_del(struct iface *iface)
81{
82	struct if_addr		*if_addr;
83
84	if (iface->state == IF_STA_ACTIVE)
85		if_reset(iface);
86
87	log_debug("%s: interface %s", __func__, iface->name);
88
89	while ((if_addr = LIST_FIRST(&iface->addr_list)) != NULL) {
90		LIST_REMOVE(if_addr, entry);
91		free(if_addr);
92	}
93
94	free(iface);
95}
96
97void
98if_init(struct ldpd_conf *xconf, struct iface *iface)
99{
100	/* set event handlers for interface */
101	evtimer_set(&iface->hello_timer, if_hello_timer, iface);
102
103	iface->discovery_fd = xconf->ldp_discovery_socket;
104}
105
106struct iface *
107if_lookup(struct ldpd_conf *xconf, u_short ifindex)
108{
109	struct iface *iface;
110
111	LIST_FOREACH(iface, &xconf->iface_list, entry)
112		if (iface->ifindex == ifindex)
113			return (iface);
114
115	return (NULL);
116}
117
118struct if_addr *
119if_addr_new(struct kaddr *ka)
120{
121	struct if_addr	*if_addr;
122
123	if ((if_addr = calloc(1, sizeof(*if_addr))) == NULL)
124		fatal(__func__);
125
126	if_addr->addr.s_addr = ka->addr.s_addr;
127	if_addr->mask.s_addr = ka->mask.s_addr;
128	if_addr->dstbrd.s_addr = ka->dstbrd.s_addr;
129
130	return (if_addr);
131}
132
133struct if_addr *
134if_addr_lookup(struct if_addr_head *addr_list, struct kaddr *ka)
135{
136	struct if_addr *if_addr;
137
138	LIST_FOREACH(if_addr, addr_list, entry)
139		if (if_addr->addr.s_addr == ka->addr.s_addr &&
140		    if_addr->mask.s_addr == ka->mask.s_addr &&
141		    if_addr->dstbrd.s_addr == ka->dstbrd.s_addr)
142			return (if_addr);
143
144	return (NULL);
145}
146
147void
148if_addr_add(struct kaddr *ka)
149{
150	struct iface		*iface;
151	struct if_addr		*if_addr;
152	struct nbr		*nbr;
153
154	if (if_addr_lookup(&global.addr_list, ka) == NULL) {
155		if_addr = if_addr_new(ka);
156
157		LIST_INSERT_HEAD(&global.addr_list, if_addr, entry);
158		RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
159			if (nbr->state != NBR_STA_OPER)
160				continue;
161
162			send_address(nbr, if_addr, 0);
163		}
164	}
165
166	iface = if_lookup(leconf, ka->ifindex);
167	if (iface &&
168	    if_addr_lookup(&iface->addr_list, ka) == NULL) {
169		if_addr = if_addr_new(ka);
170		LIST_INSERT_HEAD(&iface->addr_list, if_addr, entry);
171		if_update(iface);
172	}
173}
174
175void
176if_addr_del(struct kaddr *ka)
177{
178	struct iface		*iface;
179	struct if_addr		*if_addr;
180	struct nbr		*nbr;
181
182	iface = if_lookup(leconf, ka->ifindex);
183	if (iface) {
184		if_addr = if_addr_lookup(&iface->addr_list, ka);
185		if (if_addr) {
186			LIST_REMOVE(if_addr, entry);
187			free(if_addr);
188			if_update(iface);
189		}
190	}
191
192	if_addr = if_addr_lookup(&global.addr_list, ka);
193	if (if_addr) {
194		RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
195			if (nbr->state != NBR_STA_OPER)
196				continue;
197			send_address(nbr, if_addr, 1);
198		}
199		LIST_REMOVE(if_addr, entry);
200		free(if_addr);
201	}
202}
203
204int
205if_start(struct iface *iface)
206{
207	struct in_addr		 addr;
208	struct timeval		 now;
209
210	log_debug("%s: %s", __func__, iface->name);
211
212	gettimeofday(&now, NULL);
213	iface->uptime = now.tv_sec;
214
215	inet_aton(AllRouters, &addr);
216	if (if_join_group(iface, &addr))
217		return (-1);
218
219	send_hello(HELLO_LINK, iface, NULL);
220	if_start_hello_timer(iface);
221	return (0);
222}
223
224int
225if_reset(struct iface *iface)
226{
227	struct in_addr		 addr;
228	struct adj		*adj;
229
230	log_debug("%s: %s", __func__, iface->name);
231
232	while ((adj = LIST_FIRST(&iface->adj_list)) != NULL) {
233		LIST_REMOVE(adj, iface_entry);
234		adj_del(adj);
235	}
236
237	if_stop_hello_timer(iface);
238
239	/* try to cleanup */
240	inet_aton(AllRouters, &addr);
241	if_leave_group(iface, &addr);
242
243	return (0);
244}
245
246int
247if_update(struct iface *iface)
248{
249	int ret;
250
251	if (iface->state == IF_STA_DOWN) {
252		if (!(iface->flags & IFF_UP) ||
253		    !LINK_STATE_IS_UP(iface->linkstate) ||
254		    LIST_EMPTY(&iface->addr_list))
255			return (0);
256
257		iface->state = IF_STA_ACTIVE;
258		ret = if_start(iface);
259	} else {
260		if ((iface->flags & IFF_UP) &&
261		    LINK_STATE_IS_UP(iface->linkstate) &&
262		    !LIST_EMPTY(&iface->addr_list))
263			return (0);
264
265		iface->state = IF_STA_DOWN;
266		ret = if_reset(iface);
267	}
268
269	return (ret);
270}
271
272/* timers */
273/* ARGSUSED */
274void
275if_hello_timer(int fd, short event, void *arg)
276{
277	struct iface		*iface = arg;
278
279	send_hello(HELLO_LINK, iface, NULL);
280	if_start_hello_timer(iface);
281}
282
283void
284if_start_hello_timer(struct iface *iface)
285{
286	struct timeval		 tv;
287
288	timerclear(&tv);
289	tv.tv_sec = iface->hello_interval;
290	if (evtimer_add(&iface->hello_timer, &tv) == -1)
291		fatal(__func__);
292}
293
294void
295if_stop_hello_timer(struct iface *iface)
296{
297	if (evtimer_pending(&iface->hello_timer, NULL) &&
298	    evtimer_del(&iface->hello_timer) == -1)
299		fatal(__func__);
300}
301
302struct ctl_iface *
303if_to_ctl(struct iface *iface)
304{
305	static struct ctl_iface	 ictl;
306	struct timeval		 now;
307	struct adj		*adj;
308
309	memcpy(ictl.name, iface->name, sizeof(ictl.name));
310	ictl.ifindex = iface->ifindex;
311	ictl.state = iface->state;
312	ictl.hello_holdtime = iface->hello_holdtime;
313	ictl.hello_interval = iface->hello_interval;
314	ictl.flags = iface->flags;
315	ictl.type = iface->type;
316	ictl.linkstate = iface->linkstate;
317	ictl.if_type = iface->if_type;
318
319	gettimeofday(&now, NULL);
320	if (iface->state != IF_STA_DOWN &&
321	    iface->uptime != 0) {
322		ictl.uptime = now.tv_sec - iface->uptime;
323	} else
324		ictl.uptime = 0;
325
326	ictl.adj_cnt = 0;
327	LIST_FOREACH(adj, &iface->adj_list, iface_entry)
328		ictl.adj_cnt++;
329
330	return (&ictl);
331}
332
333/* misc */
334int
335if_set_mcast_ttl(int fd, u_int8_t ttl)
336{
337	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
338	    (char *)&ttl, sizeof(ttl)) < 0) {
339		log_warn("%s: error setting IP_MULTICAST_TTL to %d",
340		    __func__, ttl);
341		return (-1);
342	}
343
344	return (0);
345}
346
347int
348if_set_tos(int fd, int tos)
349{
350	if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) {
351		log_warn("%s: error setting IP_TOS to 0x%x", __func__, tos);
352		return (-1);
353	}
354
355	return (0);
356}
357
358int
359if_set_recvif(int fd, int enable)
360{
361	if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable,
362	    sizeof(enable)) < 0) {
363		log_warn("%s: error setting IP_RECVIF", __func__);
364		return (-1);
365	}
366	return (0);
367}
368
369void
370if_set_recvbuf(int fd)
371{
372	int	bsize;
373
374	bsize = 65535;
375	while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
376	    sizeof(bsize)) == -1)
377		bsize /= 2;
378}
379
380int
381if_set_reuse(int fd, int enable)
382{
383	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable,
384	    sizeof(int)) < 0) {
385		log_warn("%s: error setting SO_REUSEADDR", __func__);
386		return (-1);
387	}
388
389	return (0);
390}
391
392/*
393 * only one JOIN or DROP per interface and address is allowed so we need
394 * to keep track of what is added and removed.
395 */
396struct if_group_count {
397	LIST_ENTRY(if_group_count)	entry;
398	struct in_addr			addr;
399	unsigned int			ifindex;
400	int				count;
401};
402
403LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist);
404
405int
406if_join_group(struct iface *iface, struct in_addr *addr)
407{
408	struct ip_mreq		 mreq;
409	struct if_group_count	*ifg;
410	struct if_addr		*if_addr;
411
412	LIST_FOREACH(ifg, &ifglist, entry)
413		if (iface->ifindex == ifg->ifindex &&
414		    addr->s_addr == ifg->addr.s_addr)
415			break;
416	if (ifg == NULL) {
417		if ((ifg = calloc(1, sizeof(*ifg))) == NULL)
418			fatal(__func__);
419		ifg->addr.s_addr = addr->s_addr;
420		ifg->ifindex = iface->ifindex;
421		LIST_INSERT_HEAD(&ifglist, ifg, entry);
422	}
423
424	if (ifg->count++ != 0)
425		/* already joined */
426		return (0);
427
428	if_addr = LIST_FIRST(&iface->addr_list);
429	mreq.imr_multiaddr.s_addr = addr->s_addr;
430	mreq.imr_interface.s_addr = if_addr->addr.s_addr;
431
432	if (setsockopt(iface->discovery_fd, IPPROTO_IP,
433	    IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) {
434		log_warn("%s: error IP_ADD_MEMBERSHIP, "
435		    "interface %s address %s", __func__, iface->name,
436		    inet_ntoa(*addr));
437		LIST_REMOVE(ifg, entry);
438		free(ifg);
439		return (-1);
440	}
441	return (0);
442}
443
444int
445if_leave_group(struct iface *iface, struct in_addr *addr)
446{
447	struct ip_mreq		 mreq;
448	struct if_group_count	*ifg;
449	struct if_addr		*if_addr;
450
451	LIST_FOREACH(ifg, &ifglist, entry)
452		if (iface->ifindex == ifg->ifindex &&
453		    addr->s_addr == ifg->addr.s_addr)
454			break;
455
456	/* if interface is not found just try to drop membership */
457	if (ifg) {
458		if (--ifg->count != 0)
459			/* others still joined */
460			return (0);
461
462		LIST_REMOVE(ifg, entry);
463		free(ifg);
464	}
465
466	if_addr = LIST_FIRST(&iface->addr_list);
467	if (!if_addr)
468		return (0);
469
470	mreq.imr_multiaddr.s_addr = addr->s_addr;
471	mreq.imr_interface.s_addr = if_addr->addr.s_addr;
472
473	if (setsockopt(iface->discovery_fd, IPPROTO_IP,
474	    IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) {
475		log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s "
476		    "address %s", __func__, iface->name, inet_ntoa(*addr));
477		return (-1);
478	}
479
480	return (0);
481}
482
483int
484if_set_mcast(struct iface *iface)
485{
486	struct if_addr		*if_addr;
487
488	if_addr = LIST_FIRST(&iface->addr_list);
489
490	if (setsockopt(iface->discovery_fd, IPPROTO_IP, IP_MULTICAST_IF,
491	    &if_addr->addr.s_addr, sizeof(if_addr->addr.s_addr)) < 0) {
492		log_debug("%s: error setting IP_MULTICAST_IF, interface %s",
493		    __func__, iface->name);
494		return (-1);
495	}
496
497	return (0);
498}
499
500int
501if_set_mcast_loop(int fd)
502{
503	u_int8_t	loop = 0;
504
505	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
506	    (char *)&loop, sizeof(loop)) < 0) {
507		log_warn("%s: error setting IP_MULTICAST_LOOP", __func__);
508		return (-1);
509	}
510
511	return (0);
512}
513