diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2017-08-11 23:28:44 +0200 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2017-08-23 09:05:19 -0600 |
commit | bbf116c0cde4b2ea27c38dbcc9ab296588c8815f (patch) | |
tree | b9300b345de4b09fd317603f341f476d50de53d9 /src/compat/compat.h | |
parent | c81d33b0bbdfb7cdcf5ef37a1e1264e6e8c11b3b (diff) |
socket: improve reply-to-src algorithm
We store the destination IP of incoming packets as the source IP of
outgoing packets. When we send outgoing packets, we then ask the routing
table for which interface to use and which source address, given our
inputs of the destination address and a suggested source address. This
all is good and fine, since it means we'll successfully reply using the
correct source address, correlating with the destination address for
incoming packets. However, what happens when default routes change? Or
when interface IP addresses change?
Prior to this commit, after getting the response from the routing table
of the source address, destination address, and interface, we would then
make sure that the source address actually belonged to the outbound
interface. If it didn't, we'd reset our source address to zero and
re-ask the routing table, in which case the routing table would then
give us the default IP address for sending that packet. This worked
mostly fine for most purposes, but there was a problem: what if
WireGuard legitimately accepted an inbound packet on a default interface
using an IP of another interface? In this case, falling back to asking
for the default source IP was not a good strategy, since it'd nearly
always mean we'd fail to reply using the right source.
So, this commit changes the algorithm slightly. Rather than falling back
to using the default IP if the preferred source IP doesn't belong to the
outbound interface, we have two checks: we make sure that the source IP
address belongs to _some_ interface on the system, no matter which one
(so long as it's within the network namespace), and we check whether or
not the interface of an incoming packet matches the returned interface
for the outbound traffic. If both these conditions are true, then we
proceed with using this source IP address. If not, we fall back to the
default IP address.
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'src/compat/compat.h')
-rw-r--r-- | src/compat/compat.h | 47 |
1 files changed, 46 insertions, 1 deletions
diff --git a/src/compat/compat.h b/src/compat/compat.h index a373c83..451078f 100644 --- a/src/compat/compat.h +++ b/src/compat/compat.h @@ -305,7 +305,52 @@ static inline u64 ktime_get_ns(void) #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) #include <linux/inetdevice.h> -#define inet_confirm_addr(a,b,c,d,e) inet_confirm_addr(b,c,d,e) +static inline __be32 our_confirm_addr_indev(struct in_device *in_dev, __be32 dst, __be32 local, int scope) +{ + int same = 0; + __be32 addr = 0; + for_ifa(in_dev) { + if (!addr && (local == ifa->ifa_local || !local) && ifa->ifa_scope <= scope) { + addr = ifa->ifa_local; + if (same) + break; + } + if (!same) { + same = (!local || inet_ifa_match(local, ifa)) && (!dst || inet_ifa_match(dst, ifa)); + if (same && addr) { + if (local || !dst) + break; + if (inet_ifa_match(addr, ifa)) + break; + if (ifa->ifa_scope <= scope) { + addr = ifa->ifa_local; + break; + } + same = 0; + } + } + } endfor_ifa(in_dev); + return same ? addr : 0; +} +static inline __be32 our_inet_confirm_addr(struct net *net, struct in_device *in_dev, __be32 dst, __be32 local, int scope) +{ + __be32 addr = 0; + struct net_device *dev; + if (in_dev) + return our_confirm_addr_indev(in_dev, dst, local, scope); + rcu_read_lock(); + for_each_netdev_rcu(net, dev) { + in_dev = __in_dev_get_rcu(dev); + if (in_dev) { + addr = our_confirm_addr_indev(in_dev, dst, local, scope); + if (addr) + break; + } + } + rcu_read_unlock(); + return addr; +} +#define inet_confirm_addr our_inet_confirm_addr #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) |