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