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