diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2020-02-10 20:33:35 +0100 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2020-02-12 20:13:12 +0100 |
commit | fb2b4ccf24b5ce56e58de9d99b67f2a6258e36b3 (patch) | |
tree | b13c19389bc60d0d8f3a210ad14378a7a4fc335b /src/compat | |
parent | a7e4885d83a33ad1f857df1b9d68abafe49378f3 (diff) |
netns: ensure that icmp src address is correct with nat
This is a small test to ensure that icmp_ndo_send is actually doing the
right with with regards to the source address. It tests this by
ensuring that the error comes back along the right path.
Also, backport the new ndo function for this.
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'src/compat')
-rw-r--r-- | src/compat/compat.h | 122 |
1 files changed, 88 insertions, 34 deletions
diff --git a/src/compat/compat.h b/src/compat/compat.h index 948d3fe..5978fe9 100644 --- a/src/compat/compat.h +++ b/src/compat/compat.h @@ -932,6 +932,94 @@ static inline void skb_mark_not_on_list(struct sk_buff *skb) #define chacha20_neon zinc_chacha20_neon #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) +#include <linux/skbuff.h> +static inline int skb_ensure_writable(struct sk_buff *skb, int write_len) +{ + if (!pskb_may_pull(skb, write_len)) + return -ENOMEM; + + if (!skb_cloned(skb) || skb_clone_writable(skb, write_len)) + return 0; + + return pskb_expand_head(skb, 0, 0, GFP_ATOMIC); +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) +#if IS_ENABLED(CONFIG_NF_NAT) +#include <linux/ip.h> +#include <linux/icmpv6.h> +#include <net/ipv6.h> +#include <net/icmp.h> +#include <net/netfilter/nf_conntrack.h> +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0) +#include <net/netfilter/nf_nat_core.h> +#endif +static inline void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info) +{ + struct sk_buff *cloned_skb = NULL; + enum ip_conntrack_info ctinfo; + struct nf_conn *ct; + __be32 orig_ip; + + ct = nf_ct_get(skb_in, &ctinfo); + if (!ct || !(ct->status & IPS_SRC_NAT)) { + icmp_send(skb_in, type, code, info); + return; + } + + if (skb_shared(skb_in)) + skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC); + + if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head || + (skb_network_header(skb_in) + sizeof(struct iphdr)) > + skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in, + skb_network_offset(skb_in) + sizeof(struct iphdr)))) + goto out; + + orig_ip = ip_hdr(skb_in)->saddr; + ip_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.ip; + icmp_send(skb_in, type, code, info); + ip_hdr(skb_in)->saddr = orig_ip; +out: + consume_skb(cloned_skb); +} +static inline void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info) +{ + struct sk_buff *cloned_skb = NULL; + enum ip_conntrack_info ctinfo; + struct in6_addr orig_ip; + struct nf_conn *ct; + + ct = nf_ct_get(skb_in, &ctinfo); + if (!ct || !(ct->status & IPS_SRC_NAT)) { + icmpv6_send(skb_in, type, code, info); + return; + } + + if (skb_shared(skb_in)) + skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC); + + if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head || + (skb_network_header(skb_in) + sizeof(struct ipv6hdr)) > + skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in, + skb_network_offset(skb_in) + sizeof(struct ipv6hdr)))) + goto out; + + orig_ip = ipv6_hdr(skb_in)->saddr; + ipv6_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6; + icmpv6_send(skb_in, type, code, info); + ipv6_hdr(skb_in)->saddr = orig_ip; +out: + consume_skb(cloned_skb); +} +#else +#define icmp_ndo_send icmp_send +#define icmpv6_ndo_send icmpv6_send +#endif +#endif + #if defined(ISUBUNTU1604) #include <linux/siphash.h> #ifndef _WG_LINUX_SIPHASH_H @@ -956,40 +1044,6 @@ static inline void skb_mark_not_on_list(struct sk_buff *skb) #define BUILD_BUG_ON(x) #endif -/* https://lkml.kernel.org/r/20170624021727.17835-1-Jason@zx2c4.com */ -#if IS_ENABLED(CONFIG_NF_CONNTRACK) -#include <linux/ip.h> -#include <linux/icmpv6.h> -#include <net/ipv6.h> -#include <net/icmp.h> -#include <net/netfilter/nf_conntrack.h> -#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0) -#include <net/netfilter/nf_nat_core.h> -#endif -static inline void new_icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) -{ - enum ip_conntrack_info ctinfo; - struct nf_conn *ct = nf_ct_get(skb_in, &ctinfo); - if (skb_network_header(skb_in) < skb_in->head || (skb_network_header(skb_in) + sizeof(struct iphdr)) > skb_tail_pointer(skb_in)) - return; - if (ct) - ip_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.ip; - icmp_send(skb_in, type, code, info); -} -static inline void new_icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) -{ - enum ip_conntrack_info ctinfo; - struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - if (skb_network_header(skb) < skb->head || (skb_network_header(skb) + sizeof(struct ipv6hdr)) > skb_tail_pointer(skb)) - return; - if (ct) - ipv6_hdr(skb)->saddr = ct->tuplehash[0].tuple.src.u3.in6; - icmpv6_send(skb, type, code, info); -} -#define icmp_send(a,b,c,d) new_icmp_send(a,b,c,d) -#define icmpv6_send(a,b,c,d) new_icmpv6_send(a,b,c,d) -#endif - /* PaX compatibility */ #ifdef CONSTIFY_PLUGIN #include <linux/cache.h> |