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