diff options
author | Tobias Brunner <tobias@strongswan.org> | 2013-06-17 15:56:44 +0200 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2013-06-21 17:03:22 +0200 |
commit | b0629f7d9b37d98308aefaec3da89b6c427b3f5c (patch) | |
tree | d95bed3ab819c3819155234c0a62a355939630a3 /src | |
parent | 1c697ff1c5520353bec35003bfb8ac426516800a (diff) | |
download | strongswan-b0629f7d9b37d98308aefaec3da89b6c427b3f5c.tar.bz2 strongswan-b0629f7d9b37d98308aefaec3da89b6c427b3f5c.tar.xz |
kernel-pfroute: Improve route lookup depending on information we get back
Kernels don't provide the same information for all routes.
Diffstat (limited to 'src')
-rw-r--r-- | src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c | 108 |
1 files changed, 96 insertions, 12 deletions
diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index b38a906bd..acb21d371 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -1087,6 +1087,48 @@ METHOD(kernel_net_t, del_route, status_t, } /** + * 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; } |