Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/crypto/dist/ipsec-tools/src/racoon Change Linux Netlink addr...



details:   https://anonhg.NetBSD.org/src/rev/628e30c4e465
branches:  trunk
changeset: 758149:628e30c4e465
user:      tteras <tteras%NetBSD.org@localhost>
date:      Fri Oct 22 06:26:26 2010 +0000

description:
Change Linux Netlink address monitoring to monitor local route changes.
This works around a kernel bug, and slightly improves behaviour on some
special cases.

diffstat:

 crypto/dist/ipsec-tools/src/racoon/grabmyaddr.c |  145 ++++++++++++++++++-----
 1 files changed, 112 insertions(+), 33 deletions(-)

diffs (212 lines):

diff -r a6edccaa1f9f -r 628e30c4e465 crypto/dist/ipsec-tools/src/racoon/grabmyaddr.c
--- a/crypto/dist/ipsec-tools/src/racoon/grabmyaddr.c   Fri Oct 22 00:49:15 2010 +0000
+++ b/crypto/dist/ipsec-tools/src/racoon/grabmyaddr.c   Fri Oct 22 06:26:26 2010 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: grabmyaddr.c,v 1.25 2010/10/21 06:15:28 tteras Exp $   */
+/*     $NetBSD: grabmyaddr.c,v 1.26 2010/10/22 06:26:26 tteras Exp $   */
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
  * Copyright (C) 2008 Timo Teras <timo.teras%iki.fi@localhost>.
@@ -358,65 +358,121 @@
                      (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(struct nlmsghdr *h)
+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_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
 
-       /* is this message interesting? */
-       if (h->nlmsg_type != RTM_NEWADDR &&
-           h->nlmsg_type != RTM_DELADDR)
+       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;
 
-       ifa = NLMSG_DATA(h);
-       parse_rtattr(rta, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(h));
-
-       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;
+       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 = ifa->ifa_family;
-       switch (ifa->ifa_family) {
+       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[IFA_LOCAL]),
+               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[IFA_LOCAL]),
+               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))
-                       sin6->sin6_scope_id = ifa->ifa_index;
+                       return 0;
                break;
 #endif
        default:
                return 0;
        }
 
-       plog(LLV_DEBUG, LOCATION, NULL,
-            "Netlink: address %s %s\n",
-            saddrwop2str((struct sockaddr *) &addr),
-            h->nlmsg_type == RTM_NEWADDR ? "added" : "deleted");
+       netlink_add_del_address(h->nlmsg_type == RTM_NEWROUTE,
+                               (struct sockaddr *) &addr);
+       return 0;
+}
 
-       if (h->nlmsg_type == RTM_NEWADDR)
-               myaddr_open_all_configured((struct sockaddr *) &addr);
-       else
-               myaddr_close_all_open((struct sockaddr *) &addr);
-
+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;
 }
 
@@ -479,9 +535,26 @@
                plog(LLV_WARNING, LOCATION, NULL,
                     "failed to put socket in non-blocking mode\n");
 
+       /* 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_IFADDR | RTMGRP_IPV6_IFADDR;
+       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",
@@ -498,15 +571,21 @@
        int fd = lcconf->rtsock;
 
        /* refresh addresses */
-       if (!netlink_enumerate(fd, PF_UNSPEC, RTM_GETADDR)) {
+       if (!netlink_enumerate(fd, PF_UNSPEC, RTM_GETROUTE)) {
                plog(LLV_ERROR, LOCATION, NULL,
                     "unable to enumerate addresses: %s\n",
                     strerror(errno));
-               return;
        }
+       while (kernel_receive(NULL, fd) == TRUE);
 
-       /* receive replies */
+#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)



Home | Main Index | Thread Index | Old Index