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