interface.c revision 1.34
1/*	$OpenBSD: interface.c,v 1.34 2016/05/23 16:31:27 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		adj_del(adj);
234
235	if_stop_hello_timer(iface);
236
237	/* try to cleanup */
238	inet_aton(AllRouters, &addr);
239	if_leave_group(iface, &addr);
240
241	return (0);
242}
243
244int
245if_update(struct iface *iface)
246{
247	int ret;
248
249	if (iface->state == IF_STA_DOWN) {
250		if (!(iface->flags & IFF_UP) ||
251		    !LINK_STATE_IS_UP(iface->linkstate) ||
252		    LIST_EMPTY(&iface->addr_list))
253			return (0);
254
255		iface->state = IF_STA_ACTIVE;
256		ret = if_start(iface);
257	} else {
258		if ((iface->flags & IFF_UP) &&
259		    LINK_STATE_IS_UP(iface->linkstate) &&
260		    !LIST_EMPTY(&iface->addr_list))
261			return (0);
262
263		iface->state = IF_STA_DOWN;
264		ret = if_reset(iface);
265	}
266
267	return (ret);
268}
269
270/* timers */
271/* ARGSUSED */
272void
273if_hello_timer(int fd, short event, void *arg)
274{
275	struct iface		*iface = arg;
276
277	send_hello(HELLO_LINK, iface, NULL);
278	if_start_hello_timer(iface);
279}
280
281void
282if_start_hello_timer(struct iface *iface)
283{
284	struct timeval		 tv;
285
286	timerclear(&tv);
287	tv.tv_sec = iface->hello_interval;
288	if (evtimer_add(&iface->hello_timer, &tv) == -1)
289		fatal(__func__);
290}
291
292void
293if_stop_hello_timer(struct iface *iface)
294{
295	if (evtimer_pending(&iface->hello_timer, NULL) &&
296	    evtimer_del(&iface->hello_timer) == -1)
297		fatal(__func__);
298}
299
300struct ctl_iface *
301if_to_ctl(struct iface *iface)
302{
303	static struct ctl_iface	 ictl;
304	struct timeval		 now;
305	struct adj		*adj;
306
307	memcpy(ictl.name, iface->name, sizeof(ictl.name));
308	ictl.ifindex = iface->ifindex;
309	ictl.state = iface->state;
310	ictl.hello_holdtime = iface->hello_holdtime;
311	ictl.hello_interval = iface->hello_interval;
312	ictl.flags = iface->flags;
313	ictl.type = iface->type;
314	ictl.linkstate = iface->linkstate;
315	ictl.if_type = iface->if_type;
316
317	gettimeofday(&now, NULL);
318	if (iface->state != IF_STA_DOWN &&
319	    iface->uptime != 0) {
320		ictl.uptime = now.tv_sec - iface->uptime;
321	} else
322		ictl.uptime = 0;
323
324	ictl.adj_cnt = 0;
325	LIST_FOREACH(adj, &iface->adj_list, iface_entry)
326		ictl.adj_cnt++;
327
328	return (&ictl);
329}
330
331/* misc */
332int
333if_set_mcast_ttl(int fd, u_int8_t ttl)
334{
335	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
336	    (char *)&ttl, sizeof(ttl)) < 0) {
337		log_warn("%s: error setting IP_MULTICAST_TTL to %d",
338		    __func__, ttl);
339		return (-1);
340	}
341
342	return (0);
343}
344
345int
346if_set_tos(int fd, int tos)
347{
348	if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) {
349		log_warn("%s: error setting IP_TOS to 0x%x", __func__, tos);
350		return (-1);
351	}
352
353	return (0);
354}
355
356int
357if_set_recvif(int fd, int enable)
358{
359	if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable,
360	    sizeof(enable)) < 0) {
361		log_warn("%s: error setting IP_RECVIF", __func__);
362		return (-1);
363	}
364	return (0);
365}
366
367void
368if_set_recvbuf(int fd)
369{
370	int	bsize;
371
372	bsize = 65535;
373	while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
374	    sizeof(bsize)) == -1)
375		bsize /= 2;
376}
377
378int
379if_set_reuse(int fd, int enable)
380{
381	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable,
382	    sizeof(int)) < 0) {
383		log_warn("%s: error setting SO_REUSEADDR", __func__);
384		return (-1);
385	}
386
387	return (0);
388}
389
390/*
391 * only one JOIN or DROP per interface and address is allowed so we need
392 * to keep track of what is added and removed.
393 */
394struct if_group_count {
395	LIST_ENTRY(if_group_count)	entry;
396	struct in_addr			addr;
397	unsigned int			ifindex;
398	int				count;
399};
400
401LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist);
402
403int
404if_join_group(struct iface *iface, struct in_addr *addr)
405{
406	struct ip_mreq		 mreq;
407	struct if_group_count	*ifg;
408	struct if_addr		*if_addr;
409
410	LIST_FOREACH(ifg, &ifglist, entry)
411		if (iface->ifindex == ifg->ifindex &&
412		    addr->s_addr == ifg->addr.s_addr)
413			break;
414	if (ifg == NULL) {
415		if ((ifg = calloc(1, sizeof(*ifg))) == NULL)
416			fatal(__func__);
417		ifg->addr.s_addr = addr->s_addr;
418		ifg->ifindex = iface->ifindex;
419		LIST_INSERT_HEAD(&ifglist, ifg, entry);
420	}
421
422	if (ifg->count++ != 0)
423		/* already joined */
424		return (0);
425
426	if_addr = LIST_FIRST(&iface->addr_list);
427	mreq.imr_multiaddr.s_addr = addr->s_addr;
428	mreq.imr_interface.s_addr = if_addr->addr.s_addr;
429
430	if (setsockopt(iface->discovery_fd, IPPROTO_IP,
431	    IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) {
432		log_warn("%s: error IP_ADD_MEMBERSHIP, "
433		    "interface %s address %s", __func__, iface->name,
434		    inet_ntoa(*addr));
435		LIST_REMOVE(ifg, entry);
436		free(ifg);
437		return (-1);
438	}
439	return (0);
440}
441
442int
443if_leave_group(struct iface *iface, struct in_addr *addr)
444{
445	struct ip_mreq		 mreq;
446	struct if_group_count	*ifg;
447	struct if_addr		*if_addr;
448
449	LIST_FOREACH(ifg, &ifglist, entry)
450		if (iface->ifindex == ifg->ifindex &&
451		    addr->s_addr == ifg->addr.s_addr)
452			break;
453
454	/* if interface is not found just try to drop membership */
455	if (ifg) {
456		if (--ifg->count != 0)
457			/* others still joined */
458			return (0);
459
460		LIST_REMOVE(ifg, entry);
461		free(ifg);
462	}
463
464	if_addr = LIST_FIRST(&iface->addr_list);
465	if (!if_addr)
466		return (0);
467
468	mreq.imr_multiaddr.s_addr = addr->s_addr;
469	mreq.imr_interface.s_addr = if_addr->addr.s_addr;
470
471	if (setsockopt(iface->discovery_fd, IPPROTO_IP,
472	    IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) {
473		log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s "
474		    "address %s", __func__, iface->name, inet_ntoa(*addr));
475		return (-1);
476	}
477
478	return (0);
479}
480
481int
482if_set_mcast(struct iface *iface)
483{
484	struct if_addr		*if_addr;
485
486	if_addr = LIST_FIRST(&iface->addr_list);
487
488	if (setsockopt(global.ldp_disc_socket, IPPROTO_IP, IP_MULTICAST_IF,
489	    &if_addr->addr.s_addr, sizeof(if_addr->addr.s_addr)) < 0) {
490		log_debug("%s: error setting IP_MULTICAST_IF, interface %s",
491		    __func__, iface->name);
492		return (-1);
493	}
494
495	return (0);
496}
497
498int
499if_set_mcast_loop(int fd)
500{
501	u_int8_t	loop = 0;
502
503	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
504	    (char *)&loop, sizeof(loop)) < 0) {
505		log_warn("%s: error setting IP_MULTICAST_LOOP", __func__);
506		return (-1);
507	}
508
509	return (0);
510}
511