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