summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/compat/Kbuild.include6
-rw-r--r--src/compat/checksum/checksum_partial_compat.h201
-rw-r--r--src/compat/compat.h24
-rw-r--r--src/compat/fpu/include/asm/fpu/api.h (renamed from src/compat/i387/include/asm/fpu/api.h)0
-rw-r--r--src/compat/simd/include/asm/simd.h1
-rw-r--r--src/compat/siphash/siphash.c10
-rw-r--r--src/dkms.conf4
-rw-r--r--src/tests/qemu/kernel.config1
8 files changed, 240 insertions, 7 deletions
diff --git a/src/compat/Kbuild.include b/src/compat/Kbuild.include
index ebd5aaf..aaaffcb 100644
--- a/src/compat/Kbuild.include
+++ b/src/compat/Kbuild.include
@@ -15,7 +15,11 @@ wireguard-y += compat/dst_cache/dst_cache.o
endif
ifeq ($(wildcard $(srctree)/arch/x86/include/asm/fpu/api.h),)
-ccflags-y += -I$(src)/compat/i387/include
+ccflags-y += -I$(src)/compat/fpu/include
+endif
+
+ifeq ($(wildcard $(srctree)/arch/x86/include/asm/simd.h),)
+ccflags-y += -I$(src)/compat/simd/include
endif
ifeq ($(wildcard $(srctree)/include/net/udp_tunnel.h),)
diff --git a/src/compat/checksum/checksum_partial_compat.h b/src/compat/checksum/checksum_partial_compat.h
new file mode 100644
index 0000000..5175d84
--- /dev/null
+++ b/src/compat/checksum/checksum_partial_compat.h
@@ -0,0 +1,201 @@
+#include <net/route.h>
+#include <net/esp.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#define IP6_MF 0x0001
+#define IP6_OFFSET 0xFFF8
+static inline int skb_maybe_pull_tail(struct sk_buff *skb, unsigned int len, unsigned int max)
+{
+ if (skb_headlen(skb) >= len)
+ return 0;
+ if (max > skb->len)
+ max = skb->len;
+ if (__pskb_pull_tail(skb, max - skb_headlen(skb)) == NULL)
+ return -ENOMEM;
+ if (skb_headlen(skb) < len)
+ return -EPROTO;
+ return 0;
+}
+#define MAX_IP_HDR_LEN 128
+static inline int skb_checksum_setup_ip(struct sk_buff *skb, bool recalculate)
+{
+ unsigned int off;
+ bool fragment;
+ int err;
+ fragment = false;
+ err = skb_maybe_pull_tail(skb, sizeof(struct iphdr), MAX_IP_HDR_LEN);
+ if (err < 0)
+ goto out;
+ if (ip_hdr(skb)->frag_off & htons(IP_OFFSET | IP_MF))
+ fragment = true;
+ off = ip_hdrlen(skb);
+ err = -EPROTO;
+ if (fragment)
+ goto out;
+ switch (ip_hdr(skb)->protocol) {
+ case IPPROTO_TCP:
+ err = skb_maybe_pull_tail(skb,
+ off + sizeof(struct tcphdr),
+ MAX_IP_HDR_LEN);
+ if (err < 0)
+ goto out;
+
+ if (!skb_partial_csum_set(skb, off,
+ offsetof(struct tcphdr, check))) {
+ err = -EPROTO;
+ goto out;
+ }
+
+ if (recalculate)
+ tcp_hdr(skb)->check =
+ ~csum_tcpudp_magic(ip_hdr(skb)->saddr,
+ ip_hdr(skb)->daddr,
+ skb->len - off,
+ IPPROTO_TCP, 0);
+ break;
+ case IPPROTO_UDP:
+ err = skb_maybe_pull_tail(skb,
+ off + sizeof(struct udphdr),
+ MAX_IP_HDR_LEN);
+ if (err < 0)
+ goto out;
+
+ if (!skb_partial_csum_set(skb, off,
+ offsetof(struct udphdr, check))) {
+ err = -EPROTO;
+ goto out;
+ }
+
+ if (recalculate)
+ udp_hdr(skb)->check =
+ ~csum_tcpudp_magic(ip_hdr(skb)->saddr,
+ ip_hdr(skb)->daddr,
+ skb->len - off,
+ IPPROTO_UDP, 0);
+ break;
+ default:
+ goto out;
+ }
+ err = 0;
+out:
+ return err;
+}
+#define MAX_IPV6_HDR_LEN 256
+#define OPT_HDR(type, skb, off) \
+ (type *)(skb_network_header(skb) + (off))
+static inline int skb_checksum_setup_ipv6(struct sk_buff *skb, bool recalculate)
+{
+ int err;
+ u8 nexthdr;
+ unsigned int off;
+ unsigned int len;
+ bool fragment;
+ bool done;
+ fragment = false;
+ done = false;
+ off = sizeof(struct ipv6hdr);
+ err = skb_maybe_pull_tail(skb, off, MAX_IPV6_HDR_LEN);
+ if (err < 0)
+ goto out;
+ nexthdr = ipv6_hdr(skb)->nexthdr;
+ len = sizeof(struct ipv6hdr) + ntohs(ipv6_hdr(skb)->payload_len);
+ while (off <= len && !done) {
+ switch (nexthdr) {
+ case IPPROTO_DSTOPTS:
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_ROUTING: {
+ struct ipv6_opt_hdr *hp;
+
+ err = skb_maybe_pull_tail(skb, off + sizeof(struct ipv6_opt_hdr), MAX_IPV6_HDR_LEN);
+ if (err < 0)
+ goto out;
+ hp = OPT_HDR(struct ipv6_opt_hdr, skb, off);
+ nexthdr = hp->nexthdr;
+ off += ipv6_optlen(hp);
+ break;
+ }
+ case IPPROTO_FRAGMENT: {
+ struct frag_hdr *hp;
+ err = skb_maybe_pull_tail(skb, off + sizeof(struct frag_hdr), MAX_IPV6_HDR_LEN);
+ if (err < 0)
+ goto out;
+ hp = OPT_HDR(struct frag_hdr, skb, off);
+ if (hp->frag_off & htons(IP6_OFFSET | IP6_MF))
+ fragment = true;
+ nexthdr = hp->nexthdr;
+ off += sizeof(struct frag_hdr);
+ break;
+ }
+ default:
+ done = true;
+ break;
+ }
+ }
+ err = -EPROTO;
+ if (!done || fragment)
+ goto out;
+ switch (nexthdr) {
+ case IPPROTO_TCP:
+ err = skb_maybe_pull_tail(skb,
+ off + sizeof(struct tcphdr),
+ MAX_IPV6_HDR_LEN);
+ if (err < 0)
+ goto out;
+
+ if (!skb_partial_csum_set(skb, off,
+ offsetof(struct tcphdr, check))) {
+ err = -EPROTO;
+ goto out;
+ }
+
+ if (recalculate)
+ tcp_hdr(skb)->check =
+ ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+ &ipv6_hdr(skb)->daddr,
+ skb->len - off,
+ IPPROTO_TCP, 0);
+ break;
+ case IPPROTO_UDP:
+ err = skb_maybe_pull_tail(skb,
+ off + sizeof(struct udphdr),
+ MAX_IPV6_HDR_LEN);
+ if (err < 0)
+ goto out;
+
+ if (!skb_partial_csum_set(skb, off,
+ offsetof(struct udphdr, check))) {
+ err = -EPROTO;
+ goto out;
+ }
+
+ if (recalculate)
+ udp_hdr(skb)->check =
+ ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+ &ipv6_hdr(skb)->daddr,
+ skb->len - off,
+ IPPROTO_UDP, 0);
+ break;
+ default:
+ goto out;
+ }
+ err = 0;
+out:
+ return err;
+}
+static inline int skb_checksum_setup(struct sk_buff *skb, bool recalculate)
+{
+ int err;
+ switch (skb->protocol) {
+ case htons(ETH_P_IP):
+ err = skb_checksum_setup_ip(skb, recalculate);
+ break;
+
+ case htons(ETH_P_IPV6):
+ err = skb_checksum_setup_ipv6(skb, recalculate);
+ break;
+ default:
+ err = -EPROTO;
+ break;
+ }
+ return err;
+}
diff --git a/src/compat/compat.h b/src/compat/compat.h
index 4f2acb9..a586b93 100644
--- a/src/compat/compat.h
+++ b/src/compat/compat.h
@@ -7,8 +7,8 @@
#include <linux/version.h>
#include <linux/types.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
-#error "WireGuard requires Linux >= 3.14"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0)
+#error "WireGuard requires Linux >= 3.12"
#endif
/* These conditionals can't be enforced by an out of tree module very easily,
@@ -41,7 +41,7 @@
#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 19, 0) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 6)) || \
(LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 12) && LINUX_VERSION_CODE > KERNEL_VERSION(3, 17, 0)) || \
(LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 8) && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) || \
- (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 40) && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0))
+ LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 40)
#define dev_recursion_level() 0
#endif
@@ -110,7 +110,10 @@ static inline void netif_keep_dst(struct net_device *dev)
}
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
+#define pcpu_sw_netstats pcpu_tstats
+#define netdev_alloc_pcpu_stats alloc_percpu
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)
#define netdev_alloc_pcpu_stats(type) \
({ \
typeof(type) __percpu *pcpu_stats = alloc_percpu(type); \
@@ -126,6 +129,19 @@ static inline void netif_keep_dst(struct net_device *dev)
})
#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
+#include "checksum/checksum_partial_compat.h"
+static inline void *our_pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len)
+{
+ if (tail != skb) {
+ skb->data_len += len;
+ skb->len += len;
+ }
+ return skb_put(tail, len);
+}
+#define pskb_put our_pskb_put
+#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/compat/i387/include/asm/fpu/api.h b/src/compat/fpu/include/asm/fpu/api.h
index f3f9117..f3f9117 100644
--- a/src/compat/i387/include/asm/fpu/api.h
+++ b/src/compat/fpu/include/asm/fpu/api.h
diff --git a/src/compat/simd/include/asm/simd.h b/src/compat/simd/include/asm/simd.h
new file mode 100644
index 0000000..f3f9117
--- /dev/null
+++ b/src/compat/simd/include/asm/simd.h
@@ -0,0 +1 @@
+#include <asm/i387.h>
diff --git a/src/compat/siphash/siphash.c b/src/compat/siphash/siphash.c
index 3ae58b4..1ebfd5e 100644
--- a/src/compat/siphash/siphash.c
+++ b/src/compat/siphash/siphash.c
@@ -13,6 +13,16 @@
#include <linux/siphash.h>
#include <asm/unaligned.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
+#ifdef __LITTLE_ENDIAN
+ #define HASH_LEN_DECLARE u32 hash; u32 len;
+ #define bytemask_from_count(cnt) (~(~0ul << (cnt)*8))
+#else
+ #define HASH_LEN_DECLARE u32 len; u32 hash;
+ #define bytemask_from_count(cnt) (~(~0ul >> (cnt)*8))
+#endif
+#endif
+
#if defined(CONFIG_DCACHE_WORD_ACCESS) && BITS_PER_LONG == 64
#include <linux/dcache.h>
#include <asm/word-at-a-time.h>
diff --git a/src/dkms.conf b/src/dkms.conf
index d772902..2e66bc9 100644
--- a/src/dkms.conf
+++ b/src/dkms.conf
@@ -5,5 +5,5 @@ AUTOINSTALL=yes
BUILT_MODULE_NAME="wireguard"
DEST_MODULE_LOCATION="/kernel/net"
-# requires kernel 3.14 or greater:
-BUILD_EXCLUSIVE_KERNEL="^(([^1230]\.)|(3\.1[456789]))"
+# requires kernel 3.12 or greater:
+BUILD_EXCLUSIVE_KERNEL="^(([^1230]\.)|(3\.1[23456789]))"
diff --git a/src/tests/qemu/kernel.config b/src/tests/qemu/kernel.config
index 9df706a..c6cc5c9 100644
--- a/src/tests/qemu/kernel.config
+++ b/src/tests/qemu/kernel.config
@@ -1,6 +1,7 @@
CONFIG_NET=y
CONFIG_NETDEVICES=y
CONFIG_NET_CORE=y
+CONFIG_NET_IPIP=y
CONFIG_VETH=y
CONFIG_MULTIUSER=y
CONFIG_NAMESPACES=y