xref: /netbsd-src/crypto/dist/ipsec-tools/src/racoon/grabmyaddr.c (revision d909946ca08dceb44d7d0f22ec9488679695d976)
1 /*	$NetBSD: grabmyaddr.c,v 1.34 2014/06/14 22:39:36 christos Exp $	*/
2 /*
3  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
4  * Copyright (C) 2008 Timo Teras <timo.teras@iki.fi>.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the project nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include "config.h"
33 
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include <sys/queue.h>
40 #include <sys/socket.h>
41 
42 #ifdef __linux__
43 #include <linux/netlink.h>
44 #include <linux/rtnetlink.h>
45 #define USE_NETLINK
46 #else
47 #include <net/route.h>
48 #include <net/if.h>
49 #include <net/if_dl.h>
50 #include <sys/sysctl.h>
51 #define USE_ROUTE
52 #endif
53 
54 #include "var.h"
55 #include "misc.h"
56 #include "vmbuf.h"
57 #include "plog.h"
58 #include "sockmisc.h"
59 #include "session.h"
60 #include "debug.h"
61 
62 #include "localconf.h"
63 #include "handler.h"
64 #include "grabmyaddr.h"
65 #include "sockmisc.h"
66 #include "isakmp_var.h"
67 #include "gcmalloc.h"
68 #include "nattraversal.h"
69 
70 static int kernel_receive __P((void *ctx, int fd));
71 static int kernel_open_socket __P((void));
72 static void kernel_sync __P((void));
73 
74 struct myaddr {
75 	LIST_ENTRY(myaddr) chain;
76 	struct sockaddr_storage addr;
77 	int fd;
78 	int udp_encap;
79 };
80 
81 static LIST_HEAD(_myaddr_list_, myaddr) configured, opened;
82 
83 static void
84 myaddr_delete(my)
85 	struct myaddr *my;
86 {
87 	if (my->fd != -1)
88 		isakmp_close(my->fd);
89 	LIST_REMOVE(my, chain);
90 	racoon_free(my);
91 }
92 
93 static int
94 myaddr_configured(addr)
95 	struct sockaddr *addr;
96 {
97 	struct myaddr *cfg;
98 
99 	if (LIST_EMPTY(&configured))
100 		return TRUE;
101 
102 	LIST_FOREACH(cfg, &configured, chain) {
103 		if (cmpsaddr(addr, (struct sockaddr *) &cfg->addr) <= CMPSADDR_WILDPORT_MATCH)
104 			return TRUE;
105 	}
106 
107 	return FALSE;
108 }
109 
110 static int
111 myaddr_open(addr, udp_encap)
112 	struct sockaddr *addr;
113 	int udp_encap;
114 {
115 	struct myaddr *my;
116 
117 	/* Already open? */
118 	LIST_FOREACH(my, &opened, chain) {
119 		if (cmpsaddr(addr, (struct sockaddr *) &my->addr) <= CMPSADDR_WILDPORT_MATCH)
120 			return TRUE;
121 	}
122 
123 	my = racoon_calloc(1, sizeof(struct myaddr));
124 	if (my == NULL)
125 		return FALSE;
126 
127 	memcpy(&my->addr, addr, sysdep_sa_len(addr));
128 	my->fd = isakmp_open(addr, udp_encap);
129 	if (my->fd < 0) {
130 		racoon_free(my);
131 		return FALSE;
132 	}
133 	my->udp_encap = udp_encap;
134 	LIST_INSERT_HEAD(&opened, my, chain);
135 	return TRUE;
136 }
137 
138 static int
139 myaddr_open_all_configured(addr)
140 	struct sockaddr *addr;
141 {
142 	/* create all configured, not already opened addresses */
143 	struct myaddr *cfg, *my;
144 
145 	if (addr != NULL) {
146 		switch (addr->sa_family) {
147 		case AF_INET:
148 #ifdef INET6
149 		case AF_INET6:
150 #endif
151 			break;
152 		default:
153 			return FALSE;
154 		}
155 	}
156 
157 	LIST_FOREACH(cfg, &configured, chain) {
158 		if (addr != NULL &&
159 		    cmpsaddr(addr, (struct sockaddr *) &cfg->addr) > CMPSADDR_WILDPORT_MATCH)
160 			continue;
161 		if (!myaddr_open((struct sockaddr *) &cfg->addr, cfg->udp_encap))
162 			return FALSE;
163 	}
164 	if (LIST_EMPTY(&configured)) {
165 #ifdef ENABLE_HYBRID
166 		/* Exclude any address we got through ISAKMP mode config */
167 		if (exclude_cfg_addr(addr) == 0)
168 			return FALSE;
169 #endif
170 		set_port(addr, lcconf->port_isakmp);
171 		myaddr_open(addr, FALSE);
172 #ifdef ENABLE_NATT
173 		set_port(addr, lcconf->port_isakmp_natt);
174 		myaddr_open(addr, TRUE);
175 #endif
176 	}
177 	return TRUE;
178 }
179 
180 static void
181 myaddr_close_all_open(addr)
182 	struct sockaddr *addr;
183 {
184 	/* delete all matching open sockets */
185 	struct myaddr *my, *next;
186 
187 	for (my = LIST_FIRST(&opened); my; my = next) {
188 		next = LIST_NEXT(my, chain);
189 
190 		if (cmpsaddr((struct sockaddr *) addr,
191 			     (struct sockaddr *) &my->addr)
192 		    <= CMPSADDR_WOP_MATCH)
193 			myaddr_delete(my);
194 	}
195 }
196 
197 static void
198 myaddr_flush_list(list)
199 	struct _myaddr_list_ *list;
200 {
201 	struct myaddr *my, *next;
202 
203 	for (my = LIST_FIRST(list); my; my = next) {
204 		next = LIST_NEXT(my, chain);
205 		myaddr_delete(my);
206 	}
207 }
208 
209 void
210 myaddr_flush()
211 {
212 	myaddr_flush_list(&configured);
213 }
214 
215 int
216 myaddr_listen(addr, udp_encap)
217 	struct sockaddr *addr;
218 	int udp_encap;
219 {
220 	struct myaddr *my;
221 
222 	if (sysdep_sa_len(addr) > sizeof(my->addr)) {
223 		plog(LLV_ERROR, LOCATION, NULL,
224 		     "sockaddr size larger than sockaddr_storage\n");
225 		return -1;
226 	}
227 
228 	my = racoon_calloc(1, sizeof(struct myaddr));
229 	if (my == NULL)
230 		return -1;
231 
232 	memcpy(&my->addr, addr, sysdep_sa_len(addr));
233 	my->udp_encap = udp_encap;
234 	my->fd = -1;
235 	LIST_INSERT_HEAD(&configured, my, chain);
236 
237 	return 0;
238 }
239 
240 void
241 myaddr_sync()
242 {
243 	struct myaddr *my, *next;
244 
245 	if (!lcconf->strict_address) {
246 		kernel_sync();
247 
248 		/* delete all existing listeners which are not configured */
249 		for (my = LIST_FIRST(&opened); my; my = next) {
250 			next = LIST_NEXT(my, chain);
251 
252 			if (!myaddr_configured((struct sockaddr *) &my->addr))
253 				myaddr_delete(my);
254 		}
255 	}
256 }
257 
258 int
259 myaddr_getfd(addr)
260         struct sockaddr *addr;
261 {
262 	struct myaddr *my;
263 
264 	LIST_FOREACH(my, &opened, chain) {
265 		if (cmpsaddr((struct sockaddr *) &my->addr, addr) <= CMPSADDR_WILDPORT_MATCH)
266 			return my->fd;
267 	}
268 
269 	return -1;
270 }
271 
272 int
273 myaddr_getsport(addr)
274 	struct sockaddr *addr;
275 {
276 	struct myaddr *my;
277 	int port = 0, wport;
278 
279 	LIST_FOREACH(my, &opened, chain) {
280 		switch (cmpsaddr((struct sockaddr *) &my->addr, addr)) {
281 		case CMPSADDR_MATCH:
282 			return extract_port((struct sockaddr *) &my->addr);
283 		case CMPSADDR_WILDPORT_MATCH:
284 			wport = extract_port((struct sockaddr *) &my->addr);
285 			if (port == 0 || wport < port)
286 				port = wport;
287 			break;
288 		}
289 	}
290 
291 	if (port == 0)
292 		port = PORT_ISAKMP;
293 
294 	return port;
295 }
296 
297 void
298 myaddr_init_lists()
299 {
300 	LIST_INIT(&configured);
301 	LIST_INIT(&opened);
302 }
303 
304 int
305 myaddr_init()
306 {
307         if (!lcconf->strict_address) {
308 		lcconf->rtsock = kernel_open_socket();
309 		if (lcconf->rtsock < 0)
310 			return -1;
311 		monitor_fd(lcconf->rtsock, kernel_receive, NULL, 0);
312 	} else {
313 		lcconf->rtsock = -1;
314 		if (!myaddr_open_all_configured(NULL))
315 			return -1;
316 	}
317 	return 0;
318 }
319 
320 void
321 myaddr_close()
322 {
323 	myaddr_flush_list(&configured);
324 	myaddr_flush_list(&opened);
325 	if (lcconf->rtsock != -1) {
326 		unmonitor_fd(lcconf->rtsock);
327 		close(lcconf->rtsock);
328 	}
329 }
330 
331 #if defined(USE_NETLINK)
332 
333 static int netlink_fd = -1;
334 
335 #define NLMSG_TAIL(nmsg) \
336 	((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
337 
338 static void
339 parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
340 {
341 	memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
342 	while (RTA_OK(rta, len)) {
343 		if (rta->rta_type <= max)
344 			tb[rta->rta_type] = rta;
345 		rta = RTA_NEXT(rta,len);
346 	}
347 }
348 
349 static int
350 netlink_add_rtattr_l(struct nlmsghdr *n, int maxlen, int type,
351 		     const void *data, int alen)
352 {
353 	int len = RTA_LENGTH(alen);
354 	struct rtattr *rta;
355 
356 	if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen)
357 		return FALSE;
358 
359 	rta = NLMSG_TAIL(n);
360 	rta->rta_type = type;
361 	rta->rta_len = len;
362 	memcpy(RTA_DATA(rta), data, alen);
363 	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
364 	return TRUE;
365 }
366 
367 static int
368 netlink_enumerate(fd, family, type)
369 	int fd;
370 	int family;
371 	int type;
372 {
373 	struct {
374 		struct nlmsghdr nlh;
375 		struct rtgenmsg g;
376 	} req;
377 	struct sockaddr_nl addr;
378 	static __u32 seq = 0;
379 
380 	memset(&addr, 0, sizeof(addr));
381 	addr.nl_family = AF_NETLINK;
382 
383 	memset(&req, 0, sizeof(req));
384 	req.nlh.nlmsg_len = sizeof(req);
385 	req.nlh.nlmsg_type = type;
386 	req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
387 	req.nlh.nlmsg_pid = 0;
388 	req.nlh.nlmsg_seq = ++seq;
389 	req.g.rtgen_family = family;
390 
391 	return sendto(fd, (void *) &req, sizeof(req), 0,
392 		      (struct sockaddr *) &addr, sizeof(addr)) >= 0;
393 }
394 
395 static void
396 netlink_add_del_address(int add, struct sockaddr *saddr)
397 {
398 	plog(LLV_DEBUG, LOCATION, NULL,
399 	     "Netlink: address %s %s\n",
400 	     saddrwop2str((struct sockaddr *) saddr),
401 	     add ? "added" : "deleted");
402 
403 	if (add)
404 		myaddr_open_all_configured(saddr);
405 	else
406 		myaddr_close_all_open(saddr);
407 }
408 
409 #ifdef INET6
410 static int
411 netlink_process_addr(struct nlmsghdr *h)
412 {
413 	struct sockaddr_storage addr;
414 	struct ifaddrmsg *ifa;
415 	struct rtattr *rta[IFA_MAX+1];
416 	struct sockaddr_in6 *sin6;
417 
418 	ifa = NLMSG_DATA(h);
419 	parse_rtattr(rta, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(h));
420 
421 	if (ifa->ifa_family != AF_INET6)
422 		return 0;
423 	if (ifa->ifa_flags & IFA_F_TENTATIVE)
424 		return 0;
425 	if (rta[IFA_LOCAL] == NULL)
426 		rta[IFA_LOCAL] = rta[IFA_ADDRESS];
427 	if (rta[IFA_LOCAL] == NULL)
428 		return 0;
429 
430 	memset(&addr, 0, sizeof(addr));
431 	addr.ss_family = ifa->ifa_family;
432 	sin6 = (struct sockaddr_in6 *) &addr;
433 	memcpy(&sin6->sin6_addr, RTA_DATA(rta[IFA_LOCAL]),
434 		sizeof(sin6->sin6_addr));
435 	if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
436 		return 0;
437 	sin6->sin6_scope_id = ifa->ifa_index;
438 
439 	netlink_add_del_address(h->nlmsg_type == RTM_NEWADDR,
440 				(struct sockaddr *) &addr);
441 
442 	return 0;
443 }
444 #endif
445 
446 static int
447 netlink_route_is_local(int family, const unsigned char *addr, size_t addr_len)
448 {
449 	struct {
450 		struct nlmsghdr n;
451 		struct rtmsg    r;
452 		char            buf[1024];
453 	} req;
454 	struct rtmsg *r = NLMSG_DATA(&req.n);
455 	struct rtattr *rta[RTA_MAX+1];
456 	struct sockaddr_nl nladdr;
457 	ssize_t rlen;
458 
459 	memset(&req, 0, sizeof(req));
460 	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
461 	req.n.nlmsg_flags = NLM_F_REQUEST;
462 	req.n.nlmsg_type = RTM_GETROUTE;
463 	req.r.rtm_family = family;
464 	netlink_add_rtattr_l(&req.n, sizeof(req), RTA_DST,
465 			     addr, addr_len);
466 	req.r.rtm_dst_len = addr_len * 8;
467 
468 	memset(&nladdr, 0, sizeof(nladdr));
469 	nladdr.nl_family = AF_NETLINK;
470 
471 	if (sendto(netlink_fd, &req, sizeof(req), 0,
472 		   (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0)
473 		return 0;
474 	rlen = recv(netlink_fd, &req, sizeof(req), 0);
475 	if (rlen < 0)
476 		return 0;
477 
478 	return  req.n.nlmsg_type == RTM_NEWROUTE &&
479 		req.r.rtm_type == RTN_LOCAL;
480 }
481 
482 static int
483 netlink_process_route(struct nlmsghdr *h)
484 {
485 	struct sockaddr_storage addr;
486 	struct rtmsg *rtm;
487 	struct rtattr *rta[RTA_MAX+1];
488 	struct sockaddr_in *sin;
489 #ifdef INET6
490 	struct sockaddr_in6 *sin6;
491 #endif
492 
493 	rtm = NLMSG_DATA(h);
494 
495 	/* local IP addresses get local route in the local table */
496 	if (rtm->rtm_type != RTN_LOCAL ||
497 	    rtm->rtm_table != RT_TABLE_LOCAL)
498 		return 0;
499 
500 	parse_rtattr(rta, IFA_MAX, RTM_RTA(rtm), IFA_PAYLOAD(h));
501 	if (rta[RTA_DST] == NULL)
502  		return 0;
503 
504 	/* setup the socket address */
505 	memset(&addr, 0, sizeof(addr));
506 	addr.ss_family = rtm->rtm_family;
507 	switch (rtm->rtm_family) {
508 	case AF_INET:
509 		sin = (struct sockaddr_in *) &addr;
510 		memcpy(&sin->sin_addr, RTA_DATA(rta[RTA_DST]),
511 			sizeof(sin->sin_addr));
512 		break;
513 #ifdef INET6
514 	case AF_INET6:
515 		sin6 = (struct sockaddr_in6 *) &addr;
516 		memcpy(&sin6->sin6_addr, RTA_DATA(rta[RTA_DST]),
517 			sizeof(sin6->sin6_addr));
518 		/* Link-local addresses are handled with RTM_NEWADDR
519 		 * notifications */
520 		if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
521 			return 0;
522 		break;
523 #endif
524 	default:
525 		return 0;
526 	}
527 
528 	/* If local route was deleted, check if there is still local
529 	 * route for the same IP on another interface */
530 	if (h->nlmsg_type == RTM_DELROUTE &&
531 	    netlink_route_is_local(rtm->rtm_family,
532 				   RTA_DATA(rta[RTA_DST]),
533 				   RTA_PAYLOAD(rta[RTA_DST]))) {
534 		plog(LLV_DEBUG, LOCATION, NULL,
535 			"Netlink: not deleting %s yet, it exists still\n",
536 			saddrwop2str((struct sockaddr *) &addr));
537 		return 0;
538 	}
539 
540 	netlink_add_del_address(h->nlmsg_type == RTM_NEWROUTE,
541 				(struct sockaddr *) &addr);
542 	return 0;
543 }
544 
545 static int
546 netlink_process(struct nlmsghdr *h)
547 {
548 	switch (h->nlmsg_type) {
549 #ifdef INET6
550 	case RTM_NEWADDR:
551 	case RTM_DELADDR:
552 		return netlink_process_addr(h);
553 #endif
554 	case RTM_NEWROUTE:
555 	case RTM_DELROUTE:
556 		return netlink_process_route(h);
557 	}
558 	return 0;
559 }
560 
561 static int
562 kernel_receive(ctx, fd)
563 	void *ctx;
564 	int fd;
565 {
566 	struct sockaddr_nl nladdr;
567 	struct iovec iov;
568 	struct msghdr msg = {
569 		.msg_name = &nladdr,
570 		.msg_namelen = sizeof(nladdr),
571 		.msg_iov = &iov,
572 		.msg_iovlen = 1,
573 	};
574 	struct nlmsghdr *h;
575 	int len, status;
576 	char buf[16*1024];
577 
578 	iov.iov_base = buf;
579 	while (1) {
580 		iov.iov_len = sizeof(buf);
581 		status = recvmsg(fd, &msg, MSG_DONTWAIT);
582 		if (status < 0) {
583 			if (errno == EINTR)
584 				continue;
585 			if (errno == EAGAIN)
586 				return FALSE;
587 			continue;
588 		}
589 		if (status == 0)
590 			return FALSE;
591 
592 		h = (struct nlmsghdr *) buf;
593 		while (NLMSG_OK(h, status)) {
594 			netlink_process(h);
595 			h = NLMSG_NEXT(h, status);
596 		}
597 	}
598 
599 	return TRUE;
600 }
601 
602 static int
603 netlink_open_socket()
604 {
605 	int fd;
606 
607 	fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
608 	if (fd < 0) {
609 		plog(LLV_ERROR, LOCATION, NULL,
610 			"socket(PF_NETLINK) failed: %s",
611 			strerror(errno));
612 		return -1;
613 	}
614 	close_on_exec(fd);
615 	if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1)
616 		plog(LLV_WARNING, LOCATION, NULL,
617 		     "failed to put socket in non-blocking mode\n");
618 
619 	return fd;
620 }
621 
622 static int
623 kernel_open_socket()
624 {
625 	struct sockaddr_nl nl;
626 	int fd;
627 
628 	if (netlink_fd < 0) {
629 		netlink_fd = netlink_open_socket();
630 		if (netlink_fd < 0)
631 			return -1;
632 	}
633 
634 	fd = netlink_open_socket();
635 	if (fd < 0)
636 		return fd;
637 
638 	/* We monitor IPv4 addresses using RTMGRP_IPV4_ROUTE group
639 	 * the get the RTN_LOCAL routes which are automatically added
640 	 * by kernel. This is because:
641 	 *  - Linux kernel has a bug that calling bind() immediately
642 	 *    after IPv4 RTM_NEWADDR event can fail
643 	 *  - if IP is configured in multiple interfaces, we get
644 	 *    RTM_DELADDR for each of them. RTN_LOCAL gets deleted only
645 	 *    after the last IP address is deconfigured.
646 	 * The latter reason is also why I chose to use route
647 	 * notifications for IPv6. However, we do need to use RTM_NEWADDR
648 	 * for the link-local IPv6 addresses to get the interface index
649 	 * that is needed in bind().
650 	 */
651 	memset(&nl, 0, sizeof(nl));
652 	nl.nl_family = AF_NETLINK;
653 	nl.nl_groups = RTMGRP_IPV4_ROUTE
654 #ifdef INET6
655 			| RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE
656 #endif
657 			;
658 	if (bind(fd, (struct sockaddr*) &nl, sizeof(nl)) < 0) {
659 		plog(LLV_ERROR, LOCATION, NULL,
660 		     "bind(PF_NETLINK) failed: %s\n",
661 		     strerror(errno));
662 		close(fd);
663 		return -1;
664 	}
665 	return fd;
666 }
667 
668 static void
669 kernel_sync()
670 {
671 	int fd = lcconf->rtsock;
672 
673 	/* refresh addresses */
674 	if (!netlink_enumerate(fd, PF_UNSPEC, RTM_GETROUTE)) {
675 		plog(LLV_ERROR, LOCATION, NULL,
676 		     "unable to enumerate addresses: %s\n",
677 		     strerror(errno));
678 	}
679 	while (kernel_receive(NULL, fd) == TRUE);
680 
681 #ifdef INET6
682 	if (!netlink_enumerate(fd, PF_INET6, RTM_GETADDR)) {
683 		plog(LLV_ERROR, LOCATION, NULL,
684 		     "unable to enumerate addresses: %s\n",
685 		     strerror(errno));
686 	}
687 	while (kernel_receive(NULL, fd) == TRUE);
688 #endif
689 }
690 
691 #elif defined(USE_ROUTE)
692 
693 #define ROUNDUP(a) \
694   ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
695 
696 #define SAROUNDUP(X)   ROUNDUP(((struct sockaddr *)(X))->sa_len)
697 
698 static size_t
699 parse_address(start, end, dest)
700 	caddr_t start;
701 	caddr_t end;
702 	struct sockaddr_storage *dest;
703 {
704 	int len;
705 
706 	if (start >= end)
707 		return 0;
708 
709 	len = SAROUNDUP(start);
710 	if (start + len > end)
711 		return end - start;
712 
713 	if (dest != NULL && len <= sizeof(struct sockaddr_storage))
714 		memcpy(dest, start, len);
715 
716 	return len;
717 }
718 
719 static void
720 parse_addresses(start, end, flags, addr)
721 	caddr_t start;
722 	caddr_t end;
723 	int flags;
724 	struct sockaddr_storage *addr;
725 {
726 	memset(addr, 0, sizeof(*addr));
727 	if (flags & RTA_DST)
728 		start += parse_address(start, end, NULL);
729 	if (flags & RTA_GATEWAY)
730 		start += parse_address(start, end, NULL);
731 	if (flags & RTA_NETMASK)
732 		start += parse_address(start, end, NULL);
733 	if (flags & RTA_GENMASK)
734 		start += parse_address(start, end, NULL);
735 	if (flags & RTA_IFP)
736 		start += parse_address(start, end, NULL);
737 	if (flags & RTA_IFA)
738 		start += parse_address(start, end, addr);
739 	if (flags & RTA_AUTHOR)
740 		start += parse_address(start, end, NULL);
741 	if (flags & RTA_BRD)
742 		start += parse_address(start, end, NULL);
743 }
744 
745 static void
746 kernel_handle_message(msg)
747 	caddr_t msg;
748 {
749 	struct rt_msghdr *rtm = (struct rt_msghdr *) msg;
750 	struct ifa_msghdr *ifa = (struct ifa_msghdr *) msg;
751 	struct sockaddr_storage addr;
752 
753 	switch (rtm->rtm_type) {
754 	case RTM_NEWADDR:
755 		parse_addresses(ifa + 1, msg + ifa->ifam_msglen,
756 				ifa->ifam_addrs, &addr);
757 		myaddr_open_all_configured((struct sockaddr *) &addr);
758 		break;
759 	case RTM_DELADDR:
760 		parse_addresses(ifa + 1, msg + ifa->ifam_msglen,
761 				ifa->ifam_addrs, &addr);
762 		myaddr_close_all_open((struct sockaddr *) &addr);
763 		break;
764 	case RTM_ADD:
765 	case RTM_DELETE:
766 	case RTM_CHANGE:
767 	case RTM_GET:
768 	case RTM_MISS:
769 #ifdef RTM_LOSING
770 	case RTM_LOSING:
771 #endif
772 #ifdef RTM_REDIRECT
773 	case RTM_REDIRECT:
774 #endif
775 	case RTM_IFINFO:
776 #ifdef RTM_OIFINFO
777 	case RTM_OIFINFO:
778 #endif
779 #ifdef RTM_NEWMADDR
780 	case RTM_NEWMADDR:
781 	case RTM_DELMADDR:
782 #endif
783 #ifdef RTM_IFANNOUNCE
784 	case RTM_IFANNOUNCE:
785 #endif
786 #ifdef RTM_IEEE80211
787 	case RTM_IEEE80211:
788 #endif
789 		break;
790 	default:
791 		plog(LLV_WARNING, LOCATION, NULL,
792 		     "unrecognized route message with rtm_type: %d\n",
793 		     rtm->rtm_type);
794 		break;
795 	}
796 }
797 
798 static int
799 kernel_receive(ctx, fd)
800 	void *ctx;
801 	int fd;
802 {
803 	char buf[16*1024];
804 	struct rt_msghdr *rtm = (struct rt_msghdr *) buf;
805 	int len;
806 
807 	len = read(fd, &buf, sizeof(buf));
808 	if (len <= 0) {
809 		if (len < 0 && errno != EWOULDBLOCK && errno != EAGAIN)
810 			plog(LLV_WARNING, LOCATION, NULL,
811 			     "routing socket error: %s", strerror(errno));
812 		return FALSE;
813 	}
814 
815 	if (rtm->rtm_msglen != len) {
816 		plog(LLV_WARNING, LOCATION, NULL,
817 		     "kernel_receive: rtm->rtm_msglen %d, len %d, type %d\n",
818 		     rtm->rtm_msglen, len, rtm->rtm_type);
819 		return FALSE;
820 	}
821 
822 	kernel_handle_message(buf);
823 	return TRUE;
824 }
825 
826 static int
827 kernel_open_socket()
828 {
829 	int fd;
830 
831 	fd = socket(PF_ROUTE, SOCK_RAW, 0);
832 	if (fd < 0) {
833 		plog(LLV_ERROR, LOCATION, NULL,
834 			"socket(PF_ROUTE) failed: %s",
835 			strerror(errno));
836 		return -1;
837 	}
838 	close_on_exec(fd);
839 	if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1)
840 		plog(LLV_WARNING, LOCATION, NULL,
841 		     "failed to put socket in non-blocking mode\n");
842 
843 	return fd;
844 }
845 
846 static void
847 kernel_sync()
848 {
849 	caddr_t ref, buf, end;
850 	size_t bufsiz;
851 	struct if_msghdr *ifm;
852 	struct interface *ifp;
853 
854 #define MIBSIZ 6
855 	int mib[MIBSIZ] = {
856 		CTL_NET,
857 		PF_ROUTE,
858 		0,
859 		0, /*  AF_INET & AF_INET6 */
860 		NET_RT_IFLIST,
861 		0
862 	};
863 
864 	if (sysctl(mib, MIBSIZ, NULL, &bufsiz, NULL, 0) < 0) {
865 		plog(LLV_WARNING, LOCATION, NULL,
866 		     "sysctl() error: %s", strerror(errno));
867 		return;
868 	}
869 
870 	ref = buf = racoon_malloc(bufsiz);
871 
872 	if (sysctl(mib, MIBSIZ, buf, &bufsiz, NULL, 0) >= 0) {
873 		/* Parse both interfaces and addresses. */
874 		for (end = buf + bufsiz; buf < end; buf += ifm->ifm_msglen) {
875 			ifm = (struct if_msghdr *) buf;
876 			kernel_handle_message(buf);
877 		}
878 	} else {
879 		plog(LLV_WARNING, LOCATION, NULL,
880 		     "sysctl() error: %s", strerror(errno));
881 	}
882 
883 	racoon_free(ref);
884 }
885 
886 #else
887 
888 #error No supported interface to monitor local addresses.
889 
890 #endif
891