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