]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
kernel-pfroute: Improve route lookup depending on information we get back
authorTobias Brunner <tobias@strongswan.org>
Mon, 17 Jun 2013 13:56:44 +0000 (15:56 +0200)
committerTobias Brunner <tobias@strongswan.org>
Fri, 21 Jun 2013 15:03:22 +0000 (17:03 +0200)
Kernels don't provide the same information for all routes.

src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c

index b38a906bdabfee7bd4288f751c8a266fff6f4239..acb21d3711ca66ddb8e435bc5445f69d2649fbb7 100644 (file)
@@ -1086,6 +1086,48 @@ METHOD(kernel_net_t, del_route, status_t,
        return manage_route(this, RTM_DELETE, dst_net, prefixlen, gateway, if_name);
 }
 
+/**
+ * Get an address on the given interface, preferably src.
+ */
+static host_t *get_address_on(private_kernel_pfroute_net_t *this, char *ifname,
+                             host_t *src)
+{
+       enumerator_t *ifaces, *addrs;
+       iface_entry_t *iface;
+       addr_entry_t *addr, *found = NULL;
+       host_t *host = NULL;
+
+       this->lock->read_lock(this->lock);
+       ifaces = this->ifaces->create_enumerator(this->ifaces);
+       while (ifaces->enumerate(ifaces, &iface))
+       {
+               if (streq(ifname, iface->ifname))
+               {
+                       addrs = iface->addrs->create_enumerator(iface->addrs);
+                       while (addrs->enumerate(addrs, &addr))
+                       {
+                               if (src->ip_equals(src, addr->ip))
+                               {
+                                       found = addr;
+                                       break;
+                               }
+                               else if (!found)
+                               {   /* use the first address as fallback if we don't find src */
+                                       found = addr;
+                               }
+                       }
+                       addrs->destroy(addrs);
+                       if (found)
+                       {
+                               host = found->ip->clone(found->ip);
+                       }
+                       break;
+               }
+       }
+       ifaces->destroy(ifaces);
+       this->lock->unlock(this->lock);
+       return host;
+}
 /**
  * Do a route lookup for dest and return either the nexthop or the source
  * address.
@@ -1104,9 +1146,10 @@ static host_t *get_route(private_kernel_pfroute_net_t *this, bool nexthop,
                        .rtm_seq = ++this->seq,
                },
        };
-       host_t *host = NULL;
+       host_t *host = NULL, *gtw = NULL, *dst = NULL, *ifa = NULL;
        enumerator_t *enumerator;
        struct sockaddr *addr;
+       char *ifname = NULL;
        int type;
 
        msg.hdr.rtm_msglen = sizeof(struct rt_msghdr);
@@ -1154,21 +1197,25 @@ static host_t *get_route(private_kernel_pfroute_net_t *this, bool nexthop,
                                                                                                 sizeof(*this->reply));
                        while (enumerator->enumerate(enumerator, &type, &addr))
                        {
-                               if (nexthop && type == RTAX_GATEWAY)
+                               if (type == RTAX_DST && this->reply->rtm_flags & RTF_HOST)
+                               {       /* probably a cloned/cached direct route */
+                                       dst = host_create_from_sockaddr(addr);
+                               }
+                               if (type == RTAX_GATEWAY)
                                {
-                                       host = host_create_from_sockaddr(addr);
-                                       break;
+                                       gtw = host_create_from_sockaddr(addr);
                                }
-                               if (nexthop && type == RTAX_DST &&
-                                       this->reply->rtm_flags & RTF_HOST)
-                               {       /* probably a cloned direct route */
-                                       host = host_create_from_sockaddr(addr);
-                                       break;
+                               if (type == RTAX_IFA)
+                               {
+                                       ifa = host_create_from_sockaddr(addr);
                                }
-                               if (!nexthop && type == RTAX_IFA)
+                               if (type == RTAX_IFP)
                                {
-                                       host = host_create_from_sockaddr(addr);
-                                       break;
+                                       struct sockaddr_dl *sdl = (struct sockaddr_dl*)addr;
+                                       if (addr->sa_family == AF_LINK && sdl->sdl_nlen)
+                                       {
+                                               ifname = strndup(sdl->sdl_data, sdl->sdl_nlen);
+                                       }
                                }
                        }
                        enumerator->destroy(enumerator);
@@ -1184,6 +1231,43 @@ static host_t *get_route(private_kernel_pfroute_net_t *this, bool nexthop,
        this->condvar->signal(this->condvar);
        this->mutex->unlock(this->mutex);
 
+       DBG3(DBG_KNL, "route to %H: dst %H gw %H src %H if %s", dest, dst, gtw,
+                ifa, ifname);
+       if (nexthop)
+       {
+               if (gtw)
+               {
+                       host = gtw->clone(gtw);
+               }
+               else if (dst)
+               {
+                       host = dst->clone(dst);
+               }
+       }
+       else
+       {
+               if (ifa)
+               {
+                       host = ifa->clone(ifa);
+               }
+               else if (ifname)
+               {
+                       host = get_address_on(this, ifname, src);
+               }
+               else if (gtw && !gtw->ip_equals(gtw, dest))
+               {
+                       host = get_route(this, FALSE, gtw, src);
+               }
+       }
+       if (host)
+       {
+               DBG2(DBG_KNL, "using %H as %s to reach %H", host,
+                        nexthop ? "nexthop" : "address", dest);
+       }
+       DESTROY_IF(gtw);
+       DESTROY_IF(dst);
+       DESTROY_IF(ifa);
+       free(ifname);
        return host;
 }