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