summaryrefslogtreecommitdiffstats
path: root/ipsec-tools/src/racoon/grabmyaddr.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipsec-tools/src/racoon/grabmyaddr.c')
-rw-r--r--ipsec-tools/src/racoon/grabmyaddr.c881
1 files changed, 881 insertions, 0 deletions
diff --git a/ipsec-tools/src/racoon/grabmyaddr.c b/ipsec-tools/src/racoon/grabmyaddr.c
new file mode 100644
index 00000000..467a4876
--- /dev/null
+++ b/ipsec-tools/src/racoon/grabmyaddr.c
@@ -0,0 +1,881 @@
+/* $NetBSD: grabmyaddr.c,v 1.28.2.2 2013/04/12 09:53:52 tteras Exp $ */
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * Copyright (C) 2008 Timo Teras <timo.teras@iki.fi>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+
+#ifdef __linux__
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#define USE_NETLINK
+#else
+#include <net/route.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <sys/sysctl.h>
+#define USE_ROUTE
+#endif
+
+#include "var.h"
+#include "misc.h"
+#include "vmbuf.h"
+#include "plog.h"
+#include "sockmisc.h"
+#include "session.h"
+#include "debug.h"
+
+#include "localconf.h"
+#include "handler.h"
+#include "grabmyaddr.h"
+#include "sockmisc.h"
+#include "isakmp_var.h"
+#include "gcmalloc.h"
+#include "nattraversal.h"
+
+static int kernel_receive __P((void *ctx, int fd));
+static int kernel_open_socket __P((void));
+static void kernel_sync __P((void));
+
+struct myaddr {
+ LIST_ENTRY(myaddr) chain;
+ struct sockaddr_storage addr;
+ int fd;
+ int udp_encap;
+};
+
+static LIST_HEAD(_myaddr_list_, myaddr) configured, opened;
+
+static void
+myaddr_delete(my)
+ struct myaddr *my;
+{
+ if (my->fd != -1)
+ isakmp_close(my->fd);
+ LIST_REMOVE(my, chain);
+ racoon_free(my);
+}
+
+static int
+myaddr_configured(addr)
+ struct sockaddr *addr;
+{
+ struct myaddr *cfg;
+
+ if (LIST_EMPTY(&configured))
+ return TRUE;
+
+ LIST_FOREACH(cfg, &configured, chain) {
+ if (cmpsaddr(addr, (struct sockaddr *) &cfg->addr) <= CMPSADDR_WILDPORT_MATCH)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int
+myaddr_open(addr, udp_encap)
+ struct sockaddr *addr;
+ int udp_encap;
+{
+ struct myaddr *my;
+
+ /* Already open? */
+ LIST_FOREACH(my, &opened, chain) {
+ if (cmpsaddr(addr, (struct sockaddr *) &my->addr) <= CMPSADDR_WILDPORT_MATCH)
+ return TRUE;
+ }
+
+ my = racoon_calloc(1, sizeof(struct myaddr));
+ if (my == NULL)
+ return FALSE;
+
+ memcpy(&my->addr, addr, sysdep_sa_len(addr));
+ my->fd = isakmp_open(addr, udp_encap);
+ if (my->fd < 0) {
+ racoon_free(my);
+ return FALSE;
+ }
+ my->udp_encap = udp_encap;
+ LIST_INSERT_HEAD(&opened, my, chain);
+ return TRUE;
+}
+
+static int
+myaddr_open_all_configured(addr)
+ struct sockaddr *addr;
+{
+ /* create all configured, not already opened addresses */
+ struct myaddr *cfg, *my;
+
+ if (addr != NULL) {
+ switch (addr->sa_family) {
+ case AF_INET:
+#ifdef INET6
+ case AF_INET6:
+#endif
+ break;
+ default:
+ return FALSE;
+ }
+ }
+
+ LIST_FOREACH(cfg, &configured, chain) {
+ if (addr != NULL &&
+ cmpsaddr(addr, (struct sockaddr *) &cfg->addr) > CMPSADDR_WILDPORT_MATCH)
+ continue;
+ if (!myaddr_open((struct sockaddr *) &cfg->addr, cfg->udp_encap))
+ return FALSE;
+ }
+ if (LIST_EMPTY(&configured)) {
+#ifdef ENABLE_HYBRID
+ /* Exclude any address we got through ISAKMP mode config */
+ if (exclude_cfg_addr(addr) == 0)
+ return FALSE;
+#endif
+ set_port(addr, lcconf->port_isakmp);
+ myaddr_open(addr, FALSE);
+#ifdef ENABLE_NATT
+ set_port(addr, lcconf->port_isakmp_natt);
+ myaddr_open(addr, TRUE);
+#endif
+ }
+ return TRUE;
+}
+
+static void
+myaddr_close_all_open(addr)
+ struct sockaddr *addr;
+{
+ /* delete all matching open sockets */
+ struct myaddr *my, *next;
+
+ for (my = LIST_FIRST(&opened); my; my = next) {
+ next = LIST_NEXT(my, chain);
+
+ if (cmpsaddr((struct sockaddr *) addr,
+ (struct sockaddr *) &my->addr)
+ <= CMPSADDR_WOP_MATCH)
+ myaddr_delete(my);
+ }
+}
+
+static void
+myaddr_flush_list(list)
+ struct _myaddr_list_ *list;
+{
+ struct myaddr *my, *next;
+
+ for (my = LIST_FIRST(list); my; my = next) {
+ next = LIST_NEXT(my, chain);
+ myaddr_delete(my);
+ }
+}
+
+void
+myaddr_flush()
+{
+ myaddr_flush_list(&configured);
+}
+
+int
+myaddr_listen(addr, udp_encap)
+ struct sockaddr *addr;
+ int udp_encap;
+{
+ struct myaddr *my;
+
+ if (sysdep_sa_len(addr) > sizeof(my->addr)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "sockaddr size larger than sockaddr_storage\n");
+ return -1;
+ }
+
+ my = racoon_calloc(1, sizeof(struct myaddr));
+ if (my == NULL)
+ return -1;
+
+ memcpy(&my->addr, addr, sysdep_sa_len(addr));
+ my->udp_encap = udp_encap;
+ my->fd = -1;
+ LIST_INSERT_HEAD(&configured, my, chain);
+
+ return 0;
+}
+
+void
+myaddr_sync()
+{
+ struct myaddr *my, *next;
+
+ if (!lcconf->strict_address) {
+ kernel_sync();
+
+ /* delete all existing listeners which are not configured */
+ for (my = LIST_FIRST(&opened); my; my = next) {
+ next = LIST_NEXT(my, chain);
+
+ if (!myaddr_configured((struct sockaddr *) &my->addr))
+ myaddr_delete(my);
+ }
+ }
+}
+
+int
+myaddr_getfd(addr)
+ struct sockaddr *addr;
+{
+ struct myaddr *my;
+
+ LIST_FOREACH(my, &opened, chain) {
+ if (cmpsaddr((struct sockaddr *) &my->addr, addr) <= CMPSADDR_WILDPORT_MATCH)
+ return my->fd;
+ }
+
+ return -1;
+}
+
+int
+myaddr_getsport(addr)
+ struct sockaddr *addr;
+{
+ struct myaddr *my;
+ int port = 0, wport;
+
+ LIST_FOREACH(my, &opened, chain) {
+ switch (cmpsaddr((struct sockaddr *) &my->addr, addr)) {
+ case CMPSADDR_MATCH:
+ return extract_port((struct sockaddr *) &my->addr);
+ case CMPSADDR_WILDPORT_MATCH:
+ wport = extract_port((struct sockaddr *) &my->addr);
+ if (port == 0 || wport < port)
+ port = wport;
+ break;
+ }
+ }
+
+ if (port == 0)
+ port = PORT_ISAKMP;
+
+ return port;
+}
+
+void
+myaddr_init_lists()
+{
+ LIST_INIT(&configured);
+ LIST_INIT(&opened);
+}
+
+int
+myaddr_init()
+{
+ if (!lcconf->strict_address) {
+ lcconf->rtsock = kernel_open_socket();
+ if (lcconf->rtsock < 0)
+ return -1;
+ monitor_fd(lcconf->rtsock, kernel_receive, NULL, 0);
+ } else {
+ lcconf->rtsock = -1;
+ if (!myaddr_open_all_configured(NULL))
+ return -1;
+ }
+ return 0;
+}
+
+void
+myaddr_close()
+{
+ myaddr_flush_list(&configured);
+ myaddr_flush_list(&opened);
+ if (lcconf->rtsock != -1) {
+ unmonitor_fd(lcconf->rtsock);
+ close(lcconf->rtsock);
+ }
+}
+
+#if defined(USE_NETLINK)
+
+static int netlink_fd = -1;
+
+#define NLMSG_TAIL(nmsg) \
+ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+static void
+parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= max)
+ tb[rta->rta_type] = rta;
+ rta = RTA_NEXT(rta,len);
+ }
+}
+
+static int
+netlink_add_rtattr_l(struct nlmsghdr *n, int maxlen, int type,
+ const void *data, int alen)
+{
+ int len = RTA_LENGTH(alen);
+ struct rtattr *rta;
+
+ if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen)
+ return FALSE;
+
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), data, alen);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+ return TRUE;
+}
+
+static int
+netlink_enumerate(fd, family, type)
+ int fd;
+ int family;
+ int type;
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct rtgenmsg g;
+ } req;
+ struct sockaddr_nl addr;
+ static __u32 seq = 0;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+
+ memset(&req, 0, sizeof(req));
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = ++seq;
+ req.g.rtgen_family = family;
+
+ return sendto(fd, (void *) &req, sizeof(req), 0,
+ (struct sockaddr *) &addr, sizeof(addr)) >= 0;
+}
+
+static void
+netlink_add_del_address(int add, struct sockaddr *saddr)
+{
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "Netlink: address %s %s\n",
+ saddrwop2str((struct sockaddr *) saddr),
+ add ? "added" : "deleted");
+
+ if (add)
+ myaddr_open_all_configured(saddr);
+ else
+ myaddr_close_all_open(saddr);
+}
+
+#ifdef INET6
+static int
+netlink_process_addr(struct nlmsghdr *h)
+{
+ struct sockaddr_storage addr;
+ struct ifaddrmsg *ifa;
+ struct rtattr *rta[IFA_MAX+1];
+ struct sockaddr_in6 *sin6;
+
+ ifa = NLMSG_DATA(h);
+ parse_rtattr(rta, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(h));
+
+ if (ifa->ifa_family != AF_INET6)
+ return 0;
+ if (ifa->ifa_flags & IFA_F_TENTATIVE)
+ return 0;
+ if (rta[IFA_LOCAL] == NULL)
+ rta[IFA_LOCAL] = rta[IFA_ADDRESS];
+ if (rta[IFA_LOCAL] == NULL)
+ return 0;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.ss_family = ifa->ifa_family;
+ sin6 = (struct sockaddr_in6 *) &addr;
+ memcpy(&sin6->sin6_addr, RTA_DATA(rta[IFA_LOCAL]),
+ sizeof(sin6->sin6_addr));
+ if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+ return 0;
+ sin6->sin6_scope_id = ifa->ifa_index;
+
+ netlink_add_del_address(h->nlmsg_type == RTM_NEWADDR,
+ (struct sockaddr *) &addr);
+
+ return 0;
+}
+#endif
+
+static int
+netlink_route_is_local(int family, const unsigned char *addr, size_t addr_len)
+{
+ struct {
+ struct nlmsghdr n;
+ struct rtmsg r;
+ char buf[1024];
+ } req;
+ struct rtmsg *r = NLMSG_DATA(&req.n);
+ struct rtattr *rta[RTA_MAX+1];
+ struct sockaddr_nl nladdr;
+ ssize_t rlen;
+
+ memset(&req, 0, sizeof(req));
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_GETROUTE;
+ req.r.rtm_family = family;
+ netlink_add_rtattr_l(&req.n, sizeof(req), RTA_DST,
+ addr, addr_len);
+ req.r.rtm_dst_len = addr_len * 8;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ if (sendto(netlink_fd, &req, sizeof(req), 0,
+ (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0)
+ return 0;
+ rlen = recv(netlink_fd, &req, sizeof(req), 0);
+ if (rlen < 0)
+ return 0;
+
+ return req.n.nlmsg_type == RTM_NEWROUTE &&
+ req.r.rtm_type == RTN_LOCAL;
+}
+
+static int
+netlink_process_route(struct nlmsghdr *h)
+{
+ struct sockaddr_storage addr;
+ struct rtmsg *rtm;
+ struct rtattr *rta[RTA_MAX+1];
+ struct sockaddr_in *sin;
+#ifdef INET6
+ struct sockaddr_in6 *sin6;
+#endif
+
+ rtm = NLMSG_DATA(h);
+
+ /* local IP addresses get local route in the local table */
+ if (rtm->rtm_type != RTN_LOCAL ||
+ rtm->rtm_table != RT_TABLE_LOCAL)
+ return 0;
+
+ parse_rtattr(rta, IFA_MAX, RTM_RTA(rtm), IFA_PAYLOAD(h));
+ if (rta[RTA_DST] == NULL)
+ return 0;
+
+ /* setup the socket address */
+ memset(&addr, 0, sizeof(addr));
+ addr.ss_family = rtm->rtm_family;
+ switch (rtm->rtm_family) {
+ case AF_INET:
+ sin = (struct sockaddr_in *) &addr;
+ memcpy(&sin->sin_addr, RTA_DATA(rta[RTA_DST]),
+ sizeof(sin->sin_addr));
+ break;
+#ifdef INET6
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) &addr;
+ memcpy(&sin6->sin6_addr, RTA_DATA(rta[RTA_DST]),
+ sizeof(sin6->sin6_addr));
+ /* Link-local addresses are handled with RTM_NEWADDR
+ * notifications */
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+ return 0;
+ break;
+#endif
+ default:
+ return 0;
+ }
+
+ /* If local route was deleted, check if there is still local
+ * route for the same IP on another interface */
+ if (h->nlmsg_type == RTM_DELROUTE &&
+ netlink_route_is_local(rtm->rtm_family,
+ RTA_DATA(rta[RTA_DST]),
+ RTA_PAYLOAD(rta[RTA_DST]))) {
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "Netlink: not deleting %s yet, it exists still\n",
+ saddrwop2str((struct sockaddr *) &addr));
+ return 0;
+ }
+
+ netlink_add_del_address(h->nlmsg_type == RTM_NEWROUTE,
+ (struct sockaddr *) &addr);
+ return 0;
+}
+
+static int
+netlink_process(struct nlmsghdr *h)
+{
+ switch (h->nlmsg_type) {
+#ifdef INET6
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ return netlink_process_addr(h);
+#endif
+ case RTM_NEWROUTE:
+ case RTM_DELROUTE:
+ return netlink_process_route(h);
+ }
+ return 0;
+}
+
+static int
+kernel_receive(ctx, fd)
+ void *ctx;
+ int fd;
+{
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ struct nlmsghdr *h;
+ int len, status;
+ char buf[16*1024];
+
+ iov.iov_base = buf;
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(fd, &msg, MSG_DONTWAIT);
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EAGAIN)
+ return FALSE;
+ continue;
+ }
+ if (status == 0)
+ return FALSE;
+
+ h = (struct nlmsghdr *) buf;
+ while (NLMSG_OK(h, status)) {
+ netlink_process(h);
+ h = NLMSG_NEXT(h, status);
+ }
+ }
+
+ return TRUE;
+}
+
+static int
+netlink_open_socket()
+{
+ int fd;
+
+ fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (fd < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "socket(PF_NETLINK) failed: %s",
+ strerror(errno));
+ return -1;
+ }
+ close_on_exec(fd);
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1)
+ plog(LLV_WARNING, LOCATION, NULL,
+ "failed to put socket in non-blocking mode\n");
+
+ return fd;
+}
+
+static int
+kernel_open_socket()
+{
+ struct sockaddr_nl nl;
+ int fd;
+
+ if (netlink_fd < 0) {
+ netlink_fd = netlink_open_socket();
+ if (netlink_fd < 0)
+ return -1;
+ }
+
+ fd = netlink_open_socket();
+ if (fd < 0)
+ return fd;
+
+ /* We monitor IPv4 addresses using RTMGRP_IPV4_ROUTE group
+ * the get the RTN_LOCAL routes which are automatically added
+ * by kernel. This is because:
+ * - Linux kernel has a bug that calling bind() immediately
+ * after IPv4 RTM_NEWADDR event can fail
+ * - if IP is configured in multiple interfaces, we get
+ * RTM_DELADDR for each of them. RTN_LOCAL gets deleted only
+ * after the last IP address is deconfigured.
+ * The latter reason is also why I chose to use route
+ * notifications for IPv6. However, we do need to use RTM_NEWADDR
+ * for the link-local IPv6 addresses to get the interface index
+ * that is needed in bind().
+ */
+ memset(&nl, 0, sizeof(nl));
+ nl.nl_family = AF_NETLINK;
+ nl.nl_groups = RTMGRP_IPV4_ROUTE
+#ifdef INET6
+ | RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE
+#endif
+ ;
+ if (bind(fd, (struct sockaddr*) &nl, sizeof(nl)) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "bind(PF_NETLINK) failed: %s\n",
+ strerror(errno));
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+static void
+kernel_sync()
+{
+ int fd = lcconf->rtsock;
+
+ /* refresh addresses */
+ if (!netlink_enumerate(fd, PF_UNSPEC, RTM_GETROUTE)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "unable to enumerate addresses: %s\n",
+ strerror(errno));
+ }
+ while (kernel_receive(NULL, fd) == TRUE);
+
+#ifdef INET6
+ if (!netlink_enumerate(fd, PF_INET6, RTM_GETADDR)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "unable to enumerate addresses: %s\n",
+ strerror(errno));
+ }
+ while (kernel_receive(NULL, fd) == TRUE);
+#endif
+}
+
+#elif defined(USE_ROUTE)
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+#define SAROUNDUP(X) ROUNDUP(((struct sockaddr *)(X))->sa_len)
+
+static size_t
+parse_address(start, end, dest)
+ caddr_t start;
+ caddr_t end;
+ struct sockaddr_storage *dest;
+{
+ int len;
+
+ if (start >= end)
+ return 0;
+
+ len = SAROUNDUP(start);
+ if (start + len > end)
+ return end - start;
+
+ if (dest != NULL && len <= sizeof(struct sockaddr_storage))
+ memcpy(dest, start, len);
+
+ return len;
+}
+
+static void
+parse_addresses(start, end, flags, addr)
+ caddr_t start;
+ caddr_t end;
+ int flags;
+ struct sockaddr_storage *addr;
+{
+ memset(addr, 0, sizeof(*addr));
+ if (flags & RTA_DST)
+ start += parse_address(start, end, NULL);
+ if (flags & RTA_GATEWAY)
+ start += parse_address(start, end, NULL);
+ if (flags & RTA_NETMASK)
+ start += parse_address(start, end, NULL);
+ if (flags & RTA_GENMASK)
+ start += parse_address(start, end, NULL);
+ if (flags & RTA_IFP)
+ start += parse_address(start, end, NULL);
+ if (flags & RTA_IFA)
+ start += parse_address(start, end, addr);
+ if (flags & RTA_AUTHOR)
+ start += parse_address(start, end, NULL);
+ if (flags & RTA_BRD)
+ start += parse_address(start, end, NULL);
+}
+
+static void
+kernel_handle_message(msg)
+ caddr_t msg;
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *) msg;
+ struct ifa_msghdr *ifa = (struct ifa_msghdr *) msg;
+ struct sockaddr_storage addr;
+
+ switch (rtm->rtm_type) {
+ case RTM_NEWADDR:
+ parse_addresses(ifa + 1, msg + ifa->ifam_msglen,
+ ifa->ifam_addrs, &addr);
+ myaddr_open_all_configured((struct sockaddr *) &addr);
+ break;
+ case RTM_DELADDR:
+ parse_addresses(ifa + 1, msg + ifa->ifam_msglen,
+ ifa->ifam_addrs, &addr);
+ myaddr_close_all_open((struct sockaddr *) &addr);
+ break;
+ case RTM_ADD:
+ case RTM_DELETE:
+ case RTM_CHANGE:
+ case RTM_GET:
+ case RTM_MISS:
+ case RTM_IFINFO:
+#ifdef RTM_OIFINFO
+ case RTM_OIFINFO:
+#endif
+#ifdef RTM_NEWMADDR
+ case RTM_NEWMADDR:
+ case RTM_DELMADDR:
+#endif
+#ifdef RTM_IFANNOUNCE
+ case RTM_IFANNOUNCE:
+#endif
+ break;
+ default:
+ plog(LLV_WARNING, LOCATION, NULL,
+ "unrecognized route message with rtm_type: %d\n",
+ rtm->rtm_type);
+ break;
+ }
+}
+
+static int
+kernel_receive(ctx, fd)
+ void *ctx;
+ int fd;
+{
+ char buf[16*1024];
+ struct rt_msghdr *rtm = (struct rt_msghdr *) buf;
+ int len;
+
+ len = read(fd, &buf, sizeof(buf));
+ if (len <= 0) {
+ if (len < 0 && errno != EWOULDBLOCK && errno != EAGAIN)
+ plog(LLV_WARNING, LOCATION, NULL,
+ "routing socket error: %s", strerror(errno));
+ return FALSE;
+ }
+
+ if (rtm->rtm_msglen != len) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "kernel_receive: rtm->rtm_msglen %d, len %d, type %d\n",
+ rtm->rtm_msglen, len, rtm->rtm_type);
+ return FALSE;
+ }
+
+ kernel_handle_message(buf);
+ return TRUE;
+}
+
+static int
+kernel_open_socket()
+{
+ int fd;
+
+ fd = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (fd < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "socket(PF_ROUTE) failed: %s",
+ strerror(errno));
+ return -1;
+ }
+ close_on_exec(fd);
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1)
+ plog(LLV_WARNING, LOCATION, NULL,
+ "failed to put socket in non-blocking mode\n");
+
+ return fd;
+}
+
+static void
+kernel_sync()
+{
+ caddr_t ref, buf, end;
+ size_t bufsiz;
+ struct if_msghdr *ifm;
+ struct interface *ifp;
+
+#define MIBSIZ 6
+ int mib[MIBSIZ] = {
+ CTL_NET,
+ PF_ROUTE,
+ 0,
+ 0, /* AF_INET & AF_INET6 */
+ NET_RT_IFLIST,
+ 0
+ };
+
+ if (sysctl(mib, MIBSIZ, NULL, &bufsiz, NULL, 0) < 0) {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "sysctl() error: %s", strerror(errno));
+ return;
+ }
+
+ ref = buf = racoon_malloc(bufsiz);
+
+ if (sysctl(mib, MIBSIZ, buf, &bufsiz, NULL, 0) >= 0) {
+ /* Parse both interfaces and addresses. */
+ for (end = buf + bufsiz; buf < end; buf += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *) buf;
+ kernel_handle_message(buf);
+ }
+ } else {
+ plog(LLV_WARNING, LOCATION, NULL,
+ "sysctl() error: %s", strerror(errno));
+ }
+
+ racoon_free(ref);
+}
+
+#else
+
+#error No supported interface to monitor local addresses.
+
+#endif