xref: /openbsd-src/usr.sbin/ldpd/interface.c (revision ac9b4aacc1da35008afea06a5d23c2f2dea9b93e)
1 /*	$OpenBSD: interface.c,v 1.9 2012/05/14 10:17:21 sthen 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 
42 void		 if_hello_timer(int, short, void *);
43 void		 if_start_hello_timer(struct iface *);
44 void		 if_stop_hello_timer(struct iface *);
45 struct nbr	*if_elect(struct nbr *, struct nbr *);
46 
47 struct {
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 
60 const char * const if_event_names[] = {
61 	"NOTHING",
62 	"UP",
63 	"DOWN"
64 };
65 
66 const char * const if_action_names[] = {
67 	"NOTHING",
68 	"START",
69 	"RESET"
70 };
71 
72 int
73 if_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 
126 struct iface *
127 if_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->lde_nbr_list);
137 
138 	strlcpy(iface->name, kif->ifname, sizeof(iface->name));
139 
140 	/* get type */
141 	if (kif->flags & IFF_POINTOPOINT)
142 		iface->type = IF_TYPE_POINTOPOINT;
143 	if (kif->flags & IFF_BROADCAST &&
144 	    kif->flags & IFF_MULTICAST)
145 		iface->type = IF_TYPE_BROADCAST;
146 	if (kif->flags & IFF_LOOPBACK) {
147 		iface->type = IF_TYPE_POINTOPOINT;
148 		iface->state = IF_STA_LOOPBACK;
149 	}
150 
151 	/* get mtu, index and flags */
152 	iface->mtu = kif->mtu;
153 	iface->ifindex = kif->ifindex;
154 	iface->flags = kif->flags;
155 	iface->linkstate = kif->link_state;
156 	iface->media_type = kif->media_type;
157 	iface->baudrate = kif->baudrate;
158 
159 	/* set address, mask and p2p addr */
160 	iface->addr = ka->addr;
161 	iface->mask = ka->mask;
162 	if (kif->flags & IFF_POINTOPOINT) {
163 		iface->dst = ka->dstbrd;
164 	}
165 
166 	return (iface);
167 }
168 
169 void
170 if_del(struct iface *iface)
171 {
172 	log_debug("if_del: interface %s", iface->name);
173 
174 	if (evtimer_pending(&iface->hello_timer, NULL))
175 		evtimer_del(&iface->hello_timer);
176 
177 	free(iface);
178 }
179 
180 void
181 if_init(struct ldpd_conf *xconf, struct iface *iface)
182 {
183 	/* set event handlers for interface */
184 	evtimer_set(&iface->hello_timer, if_hello_timer, iface);
185 
186 	iface->discovery_fd = xconf->ldp_discovery_socket;
187 }
188 
189 /* timers */
190 /* ARGSUSED */
191 void
192 if_hello_timer(int fd, short event, void *arg)
193 {
194 	struct iface *iface = arg;
195 	struct timeval tv;
196 
197 	send_hello(iface);
198 
199 	/* reschedule hello_timer */
200 	timerclear(&tv);
201 	tv.tv_sec = iface->hello_interval;
202 	if (evtimer_add(&iface->hello_timer, &tv) == -1)
203 		fatal("if_hello_timer");
204 }
205 
206 void
207 if_start_hello_timer(struct iface *iface)
208 {
209 	struct timeval tv;
210 
211 	timerclear(&tv);
212 	tv.tv_sec = iface->hello_interval;
213 	if (evtimer_add(&iface->hello_timer, &tv) == -1)
214 		fatal("if_start_hello_timer");
215 }
216 
217 void
218 if_stop_hello_timer(struct iface *iface)
219 {
220 	if (evtimer_del(&iface->hello_timer) == -1)
221 		fatal("if_stop_hello_timer");
222 }
223 
224 /* actions */
225 int
226 if_act_start(struct iface *iface)
227 {
228 	struct in_addr		 addr;
229 	struct timeval		 now;
230 
231 	if (!((iface->flags & IFF_UP) &&
232 	    LINK_STATE_IS_UP(iface->linkstate))) {
233 		log_debug("if_act_start: interface %s link down",
234 		    iface->name);
235 		return (0);
236 	}
237 
238 	if (iface->media_type == IFT_CARP && iface->passive == 0) {
239 		/* force passive mode on carp interfaces */
240 		log_warnx("if_act_start: forcing interface %s to passive",
241 		    iface->name);
242 		iface->passive = 1;
243 	}
244 
245 	gettimeofday(&now, NULL);
246 	iface->uptime = now.tv_sec;
247 
248 	inet_aton(AllRouters, &addr);
249 	if (if_join_group(iface, &addr))
250 		return (-1);
251 	iface->state = IF_STA_DOWN;
252 
253 	/* hello timer needs to be started in any case */
254 	if_start_hello_timer(iface);
255 	return (0);
256 }
257 
258 int
259 if_act_reset(struct iface *iface)
260 {
261 	struct in_addr		 addr;
262 
263 	inet_aton(AllRouters, &addr);
264 	if (if_leave_group(iface, &addr)) {
265 		log_warnx("if_act_reset: error leaving group %s, "
266 		    "interface %s", inet_ntoa(addr), iface->name);
267 	}
268 	return (0);
269 }
270 
271 struct ctl_iface *
272 if_to_ctl(struct iface *iface)
273 {
274 	static struct ctl_iface	 ictl;
275 	struct timeval		 tv, now, res;
276 
277 	memcpy(ictl.name, iface->name, sizeof(ictl.name));
278 	memcpy(&ictl.addr, &iface->addr, sizeof(ictl.addr));
279 	memcpy(&ictl.mask, &iface->mask, sizeof(ictl.mask));
280 	ictl.rtr_id.s_addr = ldpe_router_id();
281 	ictl.ifindex = iface->ifindex;
282 	ictl.state = iface->state;
283 	ictl.mtu = iface->mtu;
284 	ictl.baudrate = iface->baudrate;
285 	ictl.holdtime = iface->holdtime;
286 	ictl.hello_interval = iface->hello_interval;
287 	ictl.flags = iface->flags;
288 	ictl.type = iface->type;
289 	ictl.linkstate = iface->linkstate;
290 	ictl.mediatype = iface->media_type;
291 	ictl.priority = iface->priority;
292 	ictl.passive = iface->passive;
293 
294 	gettimeofday(&now, NULL);
295 	if (evtimer_pending(&iface->hello_timer, &tv)) {
296 		timersub(&tv, &now, &res);
297 		ictl.hello_timer = res.tv_sec;
298 	} else
299 		ictl.hello_timer = -1;
300 
301 	if (iface->state != IF_STA_DOWN &&
302 	    iface->uptime != 0) {
303 		ictl.uptime = now.tv_sec - iface->uptime;
304 	} else
305 		ictl.uptime = 0;
306 
307 	return (&ictl);
308 }
309 
310 /* misc */
311 int
312 if_set_mcast_ttl(int fd, u_int8_t ttl)
313 {
314 	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
315 	    (char *)&ttl, sizeof(ttl)) < 0) {
316 		log_warn("if_set_mcast_ttl: error setting "
317 		    "IP_MULTICAST_TTL to %d", ttl);
318 		return (-1);
319 	}
320 
321 	return (0);
322 }
323 
324 int
325 if_set_tos(int fd, int tos)
326 {
327 	if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) {
328 		log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos);
329 		return (-1);
330 	}
331 
332 	return (0);
333 }
334 
335 int
336 if_set_recvif(int fd, int enable)
337 {
338 	if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable,
339 	    sizeof(enable)) < 0) {
340 		log_warn("if_set_recvif: error setting IP_RECVIF");
341 		return (-1);
342 	}
343 	return (0);
344 }
345 
346 void
347 if_set_recvbuf(int fd)
348 {
349 	int	bsize;
350 
351 	bsize = 65535;
352 	while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
353 	    sizeof(bsize)) == -1)
354 		bsize /= 2;
355 }
356 
357 int
358 if_set_reuse(int fd, int enable)
359 {
360 	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable,
361 	    sizeof(int)) < 0) {
362 		log_warn("if_set_reuse: error setting SO_REUSEADDR");
363 		return (-1);
364 	}
365 
366 	return (0);
367 }
368 
369 /*
370  * only one JOIN or DROP per interface and address is allowed so we need
371  * to keep track of what is added and removed.
372  */
373 struct if_group_count {
374 	LIST_ENTRY(if_group_count)	entry;
375 	struct in_addr			addr;
376 	unsigned int			ifindex;
377 	int				count;
378 };
379 
380 LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist);
381 
382 int
383 if_join_group(struct iface *iface, struct in_addr *addr)
384 {
385 	struct ip_mreq		 mreq;
386 	struct if_group_count	*ifg;
387 
388 	LIST_FOREACH(ifg, &ifglist, entry)
389 		if (iface->ifindex == ifg->ifindex &&
390 		    addr->s_addr == ifg->addr.s_addr)
391 			break;
392 	if (ifg == NULL) {
393 		if ((ifg = calloc(1, sizeof(*ifg))) == NULL)
394 			fatal("if_join_group");
395 		ifg->addr.s_addr = addr->s_addr;
396 		ifg->ifindex = iface->ifindex;
397 		LIST_INSERT_HEAD(&ifglist, ifg, entry);
398 	}
399 
400 	if (ifg->count++ != 0)
401 		/* already joined */
402 		return (0);
403 
404 	mreq.imr_multiaddr.s_addr = addr->s_addr;
405 	mreq.imr_interface.s_addr = iface->addr.s_addr;
406 
407 	if (setsockopt(iface->discovery_fd, IPPROTO_IP,
408 	    IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) {
409 		log_warn("if_join_group: error IP_ADD_MEMBERSHIP, "
410 		    "interface %s address %s", iface->name,
411 		    inet_ntoa(*addr));
412 		return (-1);
413 	}
414 	return (0);
415 }
416 
417 int
418 if_leave_group(struct iface *iface, struct in_addr *addr)
419 {
420 	struct ip_mreq		 mreq;
421 	struct if_group_count	*ifg;
422 
423 	LIST_FOREACH(ifg, &ifglist, entry)
424 		if (iface->ifindex == ifg->ifindex &&
425 		    addr->s_addr == ifg->addr.s_addr)
426 			break;
427 
428 	/* if interface is not found just try to drop membership */
429 	if (ifg && --ifg->count != 0)
430 		/* others still 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_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) {
438 		log_warn("if_leave_group: error IP_DROP_MEMBERSHIP, "
439 		    "interface %s address %s", iface->name,
440 		    inet_ntoa(*addr));
441 		return (-1);
442 	}
443 
444 	if (ifg) {
445 		LIST_REMOVE(ifg, entry);
446 		free(ifg);
447 	}
448 	return (0);
449 }
450 
451 int
452 if_set_mcast(struct iface *iface)
453 {
454 	if (setsockopt(iface->discovery_fd, IPPROTO_IP, IP_MULTICAST_IF,
455 	    &iface->addr.s_addr, sizeof(iface->addr.s_addr)) < 0) {
456 		log_debug("if_set_mcast: error setting "
457 		    "IP_MULTICAST_IF, interface %s", iface->name);
458 		return (-1);
459 	}
460 
461 	return (0);
462 }
463 
464 int
465 if_set_mcast_loop(int fd)
466 {
467 	u_int8_t	loop = 0;
468 
469 	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
470 	    (char *)&loop, sizeof(loop)) < 0) {
471 		log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP");
472 		return (-1);
473 	}
474 
475 	return (0);
476 }
477