diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2017-06-23 20:45:24 +0200 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2017-06-26 12:35:06 +0200 |
commit | 36fe0f3a410858990c992132b4ae8ba4bc511739 (patch) | |
tree | 4be2cfee73875cbd647ea030c382eca917716e65 | |
parent | 9eed02a30cf9c5ad36c94724ca3ac3b8f09cf7d2 (diff) |
socket: verify saddr belongs to interface
This helps "unstick" stuck source addresses, when changing routes
dynamically.
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r-- | src/compat/compat.h | 5 | ||||
-rw-r--r-- | src/socket.c | 5 |
2 files changed, 9 insertions, 1 deletions
diff --git a/src/compat/compat.h b/src/compat/compat.h index feb4347..eb17c8e 100644 --- a/src/compat/compat.h +++ b/src/compat/compat.h @@ -279,6 +279,11 @@ static inline u64 ktime_get_ns(void) } #endif +#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) +#endif + /* https://lkml.org/lkml/2015/6/12/415 */ #include <linux/netdevice.h> static inline struct net_device *netdev_pub(void *dev) diff --git a/src/socket.c b/src/socket.c index acf3b4a..76e4e24 100644 --- a/src/socket.c +++ b/src/socket.c @@ -10,6 +10,7 @@ #include <linux/net.h> #include <linux/if_vlan.h> #include <linux/if_ether.h> +#include <linux/inetdevice.h> #include <net/udp_tunnel.h> #include <net/ipv6.h> @@ -44,10 +45,12 @@ static inline int send4(struct wireguard_device *wg, struct sk_buff *skb, struct if (!rt) { security_sk_classify_flow(sock, flowi4_to_flowi(&fl)); rt = ip_route_output_flow(sock_net(sock), &fl, sock); - if (unlikely(IS_ERR(rt) && PTR_ERR(rt) == -EINVAL && fl.saddr)) { + if (unlikely(endpoint->src4.s_addr && ((IS_ERR(rt) && PTR_ERR(rt) == -EINVAL) || (!IS_ERR(rt) && !inet_confirm_addr(sock_net(sock), __in_dev_get_rcu(rt->dst.dev), 0, fl.saddr, RT_SCOPE_HOST))))) { endpoint->src4.s_addr = fl.saddr = 0; if (cache) dst_cache_reset(cache); + if (!IS_ERR(rt)) + dst_release(&rt->dst); rt = ip_route_output_flow(sock_net(sock), &fl, sock); } if (unlikely(IS_ERR(rt))) { |