interface.c revision 1.23
1/*	$OpenBSD: interface.c,v 1.23 2015/07/19 21:04:38 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		err(1, "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->media_type = kif->media_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("if_del: interface %s", iface->name);
88
89	while ((if_addr = LIST_FIRST(&iface->addr_list)) != NULL)
90		LIST_REMOVE(if_addr, iface_entry);
91
92	free(iface);
93}
94
95void
96if_init(struct ldpd_conf *xconf, struct iface *iface)
97{
98	/* set event handlers for interface */
99	evtimer_set(&iface->hello_timer, if_hello_timer, iface);
100
101	iface->discovery_fd = xconf->ldp_discovery_socket;
102}
103
104struct iface *
105if_lookup(u_short ifindex)
106{
107	struct iface *iface;
108
109	LIST_FOREACH(iface, &leconf->iface_list, entry)
110		if (iface->ifindex == ifindex)
111			return (iface);
112
113	return (NULL);
114}
115
116/* timers */
117/* ARGSUSED */
118void
119if_hello_timer(int fd, short event, void *arg)
120{
121	struct iface *iface = arg;
122	struct timeval tv;
123
124	send_hello(HELLO_LINK, iface, NULL);
125
126	/* reschedule hello_timer */
127	timerclear(&tv);
128	tv.tv_sec = iface->hello_interval;
129	if (evtimer_add(&iface->hello_timer, &tv) == -1)
130		fatal("if_hello_timer");
131}
132
133void
134if_start_hello_timer(struct iface *iface)
135{
136	struct timeval tv;
137
138	send_hello(HELLO_LINK, iface, NULL);
139
140	timerclear(&tv);
141	tv.tv_sec = iface->hello_interval;
142	if (evtimer_add(&iface->hello_timer, &tv) == -1)
143		fatal("if_start_hello_timer");
144}
145
146void
147if_stop_hello_timer(struct iface *iface)
148{
149	if (evtimer_pending(&iface->hello_timer, NULL) &&
150	    evtimer_del(&iface->hello_timer) == -1)
151		fatal("if_stop_hello_timer");
152}
153
154int
155if_start(struct iface *iface)
156{
157	struct in_addr		 addr;
158	struct timeval		 now;
159
160	log_debug("if_start: %s", iface->name);
161
162	gettimeofday(&now, NULL);
163	iface->uptime = now.tv_sec;
164
165	inet_aton(AllRouters, &addr);
166	if (if_join_group(iface, &addr))
167		return (-1);
168
169	/* hello timer needs to be started in any case */
170	if_start_hello_timer(iface);
171	return (0);
172}
173
174int
175if_reset(struct iface *iface)
176{
177	struct in_addr		 addr;
178	struct adj		*adj;
179
180	log_debug("if_reset: %s", iface->name);
181
182	while ((adj = LIST_FIRST(&iface->adj_list)) != NULL) {
183		LIST_REMOVE(adj, iface_entry);
184		adj_del(adj);
185	}
186
187	if_stop_hello_timer(iface);
188
189	/* try to cleanup */
190	inet_aton(AllRouters, &addr);
191	if_leave_group(iface, &addr);
192
193	return (0);
194}
195
196int
197if_update(struct iface *iface)
198{
199	int ret;
200
201	if (iface->state == IF_STA_DOWN) {
202		if (!(iface->flags & IFF_UP) ||
203		    !LINK_STATE_IS_UP(iface->linkstate) ||
204		    LIST_EMPTY(&iface->addr_list))
205			return (0);
206
207		iface->state = IF_STA_ACTIVE;
208		ret = if_start(iface);
209	} else {
210		if ((iface->flags & IFF_UP) &&
211		    LINK_STATE_IS_UP(iface->linkstate) &&
212		    !LIST_EMPTY(&iface->addr_list))
213			return (0);
214
215		iface->state = IF_STA_DOWN;
216		ret = if_reset(iface);
217	}
218
219	return (ret);
220}
221
222struct ctl_iface *
223if_to_ctl(struct iface *iface)
224{
225	static struct ctl_iface	 ictl;
226	struct timeval		 now;
227	struct adj		*adj;
228
229	memcpy(ictl.name, iface->name, sizeof(ictl.name));
230	ictl.ifindex = iface->ifindex;
231	ictl.state = iface->state;
232	ictl.hello_holdtime = iface->hello_holdtime;
233	ictl.hello_interval = iface->hello_interval;
234	ictl.flags = iface->flags;
235	ictl.type = iface->type;
236	ictl.linkstate = iface->linkstate;
237	ictl.mediatype = iface->media_type;
238
239	gettimeofday(&now, NULL);
240	if (iface->state != IF_STA_DOWN &&
241	    iface->uptime != 0) {
242		ictl.uptime = now.tv_sec - iface->uptime;
243	} else
244		ictl.uptime = 0;
245
246	ictl.adj_cnt = 0;
247	LIST_FOREACH(adj, &iface->adj_list, iface_entry)
248		ictl.adj_cnt++;
249
250	return (&ictl);
251}
252
253/* misc */
254int
255if_set_mcast_ttl(int fd, u_int8_t ttl)
256{
257	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
258	    (char *)&ttl, sizeof(ttl)) < 0) {
259		log_warn("if_set_mcast_ttl: error setting "
260		    "IP_MULTICAST_TTL to %d", ttl);
261		return (-1);
262	}
263
264	return (0);
265}
266
267int
268if_set_tos(int fd, int tos)
269{
270	if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) {
271		log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos);
272		return (-1);
273	}
274
275	return (0);
276}
277
278int
279if_set_recvif(int fd, int enable)
280{
281	if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable,
282	    sizeof(enable)) < 0) {
283		log_warn("if_set_recvif: error setting IP_RECVIF");
284		return (-1);
285	}
286	return (0);
287}
288
289void
290if_set_recvbuf(int fd)
291{
292	int	bsize;
293
294	bsize = 65535;
295	while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
296	    sizeof(bsize)) == -1)
297		bsize /= 2;
298}
299
300int
301if_set_reuse(int fd, int enable)
302{
303	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable,
304	    sizeof(int)) < 0) {
305		log_warn("if_set_reuse: error setting SO_REUSEADDR");
306		return (-1);
307	}
308
309	return (0);
310}
311
312/*
313 * only one JOIN or DROP per interface and address is allowed so we need
314 * to keep track of what is added and removed.
315 */
316struct if_group_count {
317	LIST_ENTRY(if_group_count)	entry;
318	struct in_addr			addr;
319	unsigned int			ifindex;
320	int				count;
321};
322
323LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist);
324
325int
326if_join_group(struct iface *iface, struct in_addr *addr)
327{
328	struct ip_mreq		 mreq;
329	struct if_group_count	*ifg;
330	struct if_addr		*if_addr;
331
332	LIST_FOREACH(ifg, &ifglist, entry)
333		if (iface->ifindex == ifg->ifindex &&
334		    addr->s_addr == ifg->addr.s_addr)
335			break;
336	if (ifg == NULL) {
337		if ((ifg = calloc(1, sizeof(*ifg))) == NULL)
338			fatal("if_join_group");
339		ifg->addr.s_addr = addr->s_addr;
340		ifg->ifindex = iface->ifindex;
341		LIST_INSERT_HEAD(&ifglist, ifg, entry);
342	}
343
344	if (ifg->count++ != 0)
345		/* already joined */
346		return (0);
347
348	if_addr = LIST_FIRST(&iface->addr_list);
349	mreq.imr_multiaddr.s_addr = addr->s_addr;
350	mreq.imr_interface.s_addr = if_addr->addr.s_addr;
351
352	if (setsockopt(iface->discovery_fd, IPPROTO_IP,
353	    IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) {
354		log_warn("if_join_group: error IP_ADD_MEMBERSHIP, "
355		    "interface %s address %s", iface->name,
356		    inet_ntoa(*addr));
357		LIST_REMOVE(ifg, entry);
358		free(ifg);
359		return (-1);
360	}
361	return (0);
362}
363
364int
365if_leave_group(struct iface *iface, struct in_addr *addr)
366{
367	struct ip_mreq		 mreq;
368	struct if_group_count	*ifg;
369	struct if_addr		*if_addr;
370
371	LIST_FOREACH(ifg, &ifglist, entry)
372		if (iface->ifindex == ifg->ifindex &&
373		    addr->s_addr == ifg->addr.s_addr)
374			break;
375
376	/* if interface is not found just try to drop membership */
377	if (ifg) {
378		if (--ifg->count != 0)
379			/* others still joined */
380			return (0);
381
382		LIST_REMOVE(ifg, entry);
383		free(ifg);
384	}
385
386	if_addr = LIST_FIRST(&iface->addr_list);
387	if (!if_addr)
388		return (0);
389
390	mreq.imr_multiaddr.s_addr = addr->s_addr;
391	mreq.imr_interface.s_addr = if_addr->addr.s_addr;
392
393	if (setsockopt(iface->discovery_fd, IPPROTO_IP,
394	    IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) {
395		log_warn("if_leave_group: error IP_DROP_MEMBERSHIP, "
396		    "interface %s address %s", iface->name,
397		    inet_ntoa(*addr));
398		return (-1);
399	}
400
401	return (0);
402}
403
404int
405if_set_mcast(struct iface *iface)
406{
407	struct if_addr		*if_addr;
408
409	if_addr = LIST_FIRST(&iface->addr_list);
410
411	if (setsockopt(iface->discovery_fd, IPPROTO_IP, IP_MULTICAST_IF,
412	    &if_addr->addr.s_addr, sizeof(if_addr->addr.s_addr)) < 0) {
413		log_debug("if_set_mcast: error setting "
414		    "IP_MULTICAST_IF, interface %s", iface->name);
415		return (-1);
416	}
417
418	return (0);
419}
420
421int
422if_set_mcast_loop(int fd)
423{
424	u_int8_t	loop = 0;
425
426	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
427	    (char *)&loop, sizeof(loop)) < 0) {
428		log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP");
429		return (-1);
430	}
431
432	return (0);
433}
434