xref: /openbsd/usr.sbin/ldpd/interface.c (revision 404b540a)
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 
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_ANY,	IF_EVT_DOWN,		IF_ACT_RST,	IF_STA_DOWN},
56     {-1,		IF_EVT_NOTHING,		IF_ACT_NOTHING,	0},
57 };
58 
59 static int vlink_cnt = 0;
60 
61 const char * const if_event_names[] = {
62 	"NOTHING",
63 	"UP",
64 	"DOWN"
65 };
66 
67 const char * const if_action_names[] = {
68 	"NOTHING",
69 	"START",
70 	"RESET"
71 };
72 
73 int
74 if_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 
127 struct iface *
128 if_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 
180 void
181 if_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 
197 void
198 if_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 */
211 void
212 if_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 
226 void
227 if_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 
237 void
238 if_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 */
245 int
246 if_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 
300 int
301 if_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 
337 struct ctl_iface *
338 if_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 */
385 int
386 if_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 
398 int
399 if_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 
409 int
410 if_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 
420 void
421 if_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 
431 int
432 if_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 
443 int
444 if_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  */
459 struct if_group_count {
460 	LIST_ENTRY(if_group_count)	entry;
461 	struct in_addr			addr;
462 	unsigned int			ifindex;
463 	int				count;
464 };
465 
466 LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist);
467 
468 int
469 if_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 
517 int
518 if_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 
565 int
566 if_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 
591 int
592 if_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