aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTobias Brunner <tobias@strongswan.org>2013-06-17 15:56:44 +0200
committerTobias Brunner <tobias@strongswan.org>2013-06-21 17:03:22 +0200
commitb0629f7d9b37d98308aefaec3da89b6c427b3f5c (patch)
treed95bed3ab819c3819155234c0a62a355939630a3 /src
parent1c697ff1c5520353bec35003bfb8ac426516800a (diff)
downloadstrongswan-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.c108
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;
}