interface.c revision 1.14
1/*	$OpenBSD: interface.c,v 1.14 2013/06/03 16:53:49 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	LIST_INIT(&iface->lde_nbr_list);
148
149	strlcpy(iface->name, kif->ifname, sizeof(iface->name));
150
151	/* get type */
152	if (kif->flags & IFF_POINTOPOINT)
153		iface->type = IF_TYPE_POINTOPOINT;
154	if (kif->flags & IFF_BROADCAST &&
155	    kif->flags & IFF_MULTICAST)
156		iface->type = IF_TYPE_BROADCAST;
157
158	/* get mtu, index and flags */
159	iface->mtu = kif->mtu;
160	iface->ifindex = kif->ifindex;
161	iface->flags = kif->flags;
162	iface->linkstate = kif->link_state;
163	iface->media_type = kif->media_type;
164	iface->baudrate = kif->baudrate;
165
166	return (iface);
167}
168
169void
170if_del(struct iface *iface)
171{
172	struct if_addr		*if_addr;
173
174	log_debug("if_del: interface %s", iface->name);
175
176	if_stop_hello_timer(iface);
177
178	while ((if_addr = LIST_FIRST(&iface->addr_list)) != NULL)
179		LIST_REMOVE(if_addr, iface_entry);
180
181	free(iface);
182}
183
184void
185if_init(struct ldpd_conf *xconf, struct iface *iface)
186{
187	/* set event handlers for interface */
188	evtimer_set(&iface->hello_timer, if_hello_timer, iface);
189
190	iface->discovery_fd = xconf->ldp_discovery_socket;
191}
192
193struct iface *
194if_lookup(u_short ifindex)
195{
196	struct iface *iface;
197
198	LIST_FOREACH(iface, &leconf->iface_list, entry)
199		if (iface->ifindex == ifindex)
200			return (iface);
201
202	return (NULL);
203}
204
205/* timers */
206/* ARGSUSED */
207void
208if_hello_timer(int fd, short event, void *arg)
209{
210	struct iface *iface = arg;
211	struct timeval tv;
212
213	send_hello(iface);
214
215	/* reschedule hello_timer */
216	timerclear(&tv);
217	tv.tv_sec = iface->hello_interval;
218	if (evtimer_add(&iface->hello_timer, &tv) == -1)
219		fatal("if_hello_timer");
220}
221
222void
223if_start_hello_timer(struct iface *iface)
224{
225	struct timeval tv;
226
227	timerclear(&tv);
228	tv.tv_sec = iface->hello_interval;
229	if (evtimer_add(&iface->hello_timer, &tv) == -1)
230		fatal("if_start_hello_timer");
231}
232
233void
234if_stop_hello_timer(struct iface *iface)
235{
236	if (evtimer_pending(&iface->hello_timer, NULL) &&
237	    evtimer_del(&iface->hello_timer) == -1)
238		fatal("if_stop_hello_timer");
239}
240
241/* actions */
242int
243if_act_start(struct iface *iface)
244{
245	struct in_addr		 addr;
246	struct timeval		 now;
247
248	if (!((iface->flags & IFF_UP) &&
249	    LINK_STATE_IS_UP(iface->linkstate))) {
250		log_debug("if_act_start: interface %s link down",
251		    iface->name);
252		return (0);
253	}
254
255	gettimeofday(&now, NULL);
256	iface->uptime = now.tv_sec;
257
258	inet_aton(AllRouters, &addr);
259	if (if_join_group(iface, &addr))
260		return (-1);
261	iface->state = IF_STA_ACTIVE;
262
263	/* hello timer needs to be started in any case */
264	if_start_hello_timer(iface);
265	return (0);
266}
267
268int
269if_act_reset(struct iface *iface)
270{
271	struct in_addr		 addr;
272
273	if_stop_hello_timer(iface);
274
275	/* try to cleanup */
276	inet_aton(AllRouters, &addr);
277	if_leave_group(iface, &addr);
278
279	return (0);
280}
281
282int
283if_act_update(struct iface *iface)
284{
285	int ret;
286
287	if (iface->state == IF_STA_DOWN) {
288		if (!((iface->flags & IFF_UP) &&
289		    LINK_STATE_IS_UP(iface->linkstate)))
290			return (0);
291
292		if (LIST_EMPTY(&iface->addr_list))
293			return (0);
294
295		iface->state = IF_STA_ACTIVE;
296		ret = if_act_start(iface);
297	} else {
298		if (!LIST_EMPTY(&iface->addr_list))
299			return (0);
300
301		iface->state = IF_STA_DOWN;
302		ret = if_act_reset(iface);
303	}
304
305	return (ret);
306}
307
308struct ctl_iface *
309if_to_ctl(struct iface *iface)
310{
311	static struct ctl_iface	 ictl;
312	struct timeval		 tv, now, res;
313
314	memcpy(ictl.name, iface->name, sizeof(ictl.name));
315	ictl.rtr_id.s_addr = ldpe_router_id();
316	ictl.ifindex = iface->ifindex;
317	ictl.state = iface->state;
318	ictl.mtu = iface->mtu;
319	ictl.baudrate = iface->baudrate;
320	ictl.holdtime = iface->holdtime;
321	ictl.hello_interval = iface->hello_interval;
322	ictl.flags = iface->flags;
323	ictl.type = iface->type;
324	ictl.linkstate = iface->linkstate;
325	ictl.mediatype = iface->media_type;
326	ictl.priority = iface->priority;
327
328	gettimeofday(&now, NULL);
329	if (evtimer_pending(&iface->hello_timer, &tv)) {
330		timersub(&tv, &now, &res);
331		ictl.hello_timer = res.tv_sec;
332	} else
333		ictl.hello_timer = -1;
334
335	if (iface->state != IF_STA_DOWN &&
336	    iface->uptime != 0) {
337		ictl.uptime = now.tv_sec - iface->uptime;
338	} else
339		ictl.uptime = 0;
340
341	return (&ictl);
342}
343
344/* misc */
345int
346if_set_mcast_ttl(int fd, u_int8_t ttl)
347{
348	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
349	    (char *)&ttl, sizeof(ttl)) < 0) {
350		log_warn("if_set_mcast_ttl: error setting "
351		    "IP_MULTICAST_TTL to %d", ttl);
352		return (-1);
353	}
354
355	return (0);
356}
357
358int
359if_set_tos(int fd, int tos)
360{
361	if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) {
362		log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos);
363		return (-1);
364	}
365
366	return (0);
367}
368
369int
370if_set_recvif(int fd, int enable)
371{
372	if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable,
373	    sizeof(enable)) < 0) {
374		log_warn("if_set_recvif: error setting IP_RECVIF");
375		return (-1);
376	}
377	return (0);
378}
379
380void
381if_set_recvbuf(int fd)
382{
383	int	bsize;
384
385	bsize = 65535;
386	while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
387	    sizeof(bsize)) == -1)
388		bsize /= 2;
389}
390
391int
392if_set_reuse(int fd, int enable)
393{
394	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable,
395	    sizeof(int)) < 0) {
396		log_warn("if_set_reuse: error setting SO_REUSEADDR");
397		return (-1);
398	}
399
400	return (0);
401}
402
403/*
404 * only one JOIN or DROP per interface and address is allowed so we need
405 * to keep track of what is added and removed.
406 */
407struct if_group_count {
408	LIST_ENTRY(if_group_count)	entry;
409	struct in_addr			addr;
410	unsigned int			ifindex;
411	int				count;
412};
413
414LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist);
415
416int
417if_join_group(struct iface *iface, struct in_addr *addr)
418{
419	struct ip_mreq		 mreq;
420	struct if_group_count	*ifg;
421	struct if_addr		*if_addr;
422
423	LIST_FOREACH(ifg, &ifglist, entry)
424		if (iface->ifindex == ifg->ifindex &&
425		    addr->s_addr == ifg->addr.s_addr)
426			break;
427	if (ifg == NULL) {
428		if ((ifg = calloc(1, sizeof(*ifg))) == NULL)
429			fatal("if_join_group");
430		ifg->addr.s_addr = addr->s_addr;
431		ifg->ifindex = iface->ifindex;
432		LIST_INSERT_HEAD(&ifglist, ifg, entry);
433	}
434
435	if (ifg->count++ != 0)
436		/* already joined */
437		return (0);
438
439	if_addr = LIST_FIRST(&iface->addr_list);
440	mreq.imr_multiaddr.s_addr = addr->s_addr;
441	mreq.imr_interface.s_addr = if_addr->addr.s_addr;
442
443	if (setsockopt(iface->discovery_fd, IPPROTO_IP,
444	    IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) {
445		log_warn("if_join_group: error IP_ADD_MEMBERSHIP, "
446		    "interface %s address %s", iface->name,
447		    inet_ntoa(*addr));
448		LIST_REMOVE(ifg, entry);
449		free(ifg);
450		return (-1);
451	}
452	return (0);
453}
454
455int
456if_leave_group(struct iface *iface, struct in_addr *addr)
457{
458	struct ip_mreq		 mreq;
459	struct if_group_count	*ifg;
460	struct if_addr		*if_addr;
461
462	LIST_FOREACH(ifg, &ifglist, entry)
463		if (iface->ifindex == ifg->ifindex &&
464		    addr->s_addr == ifg->addr.s_addr)
465			break;
466
467	/* if interface is not found just try to drop membership */
468	if (ifg) {
469		if (--ifg->count != 0)
470			/* others still joined */
471			return (0);
472
473		LIST_REMOVE(ifg, entry);
474		free(ifg);
475	}
476
477	if_addr = LIST_FIRST(&iface->addr_list);
478	if (!if_addr)
479		return (0);
480
481	mreq.imr_multiaddr.s_addr = addr->s_addr;
482	mreq.imr_interface.s_addr = if_addr->addr.s_addr;
483
484	if (setsockopt(iface->discovery_fd, IPPROTO_IP,
485	    IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) {
486		log_warn("if_leave_group: error IP_DROP_MEMBERSHIP, "
487		    "interface %s address %s", iface->name,
488		    inet_ntoa(*addr));
489		return (-1);
490	}
491
492	return (0);
493}
494
495int
496if_set_mcast(struct iface *iface)
497{
498	struct if_addr		*if_addr;
499
500	if_addr = LIST_FIRST(&iface->addr_list);
501
502	if (setsockopt(iface->discovery_fd, IPPROTO_IP, IP_MULTICAST_IF,
503	    &if_addr->addr.s_addr, sizeof(if_addr->addr.s_addr)) < 0) {
504		log_debug("if_set_mcast: error setting "
505		    "IP_MULTICAST_IF, interface %s", iface->name);
506		return (-1);
507	}
508
509	return (0);
510}
511
512int
513if_set_mcast_loop(int fd)
514{
515	u_int8_t	loop = 0;
516
517	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
518	    (char *)&loop, sizeof(loop)) < 0) {
519		log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP");
520		return (-1);
521	}
522
523	return (0);
524}
525