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