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