aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--contrib/examples/nat-hole-punching/README4
-rwxr-xr-xcontrib/examples/reresolve-dns/reresolve-dns.sh2
-rw-r--r--src/Kconfig3
-rw-r--r--src/allowedips.c16
-rw-r--r--src/compat/compat-asm.h5
-rw-r--r--src/compat/compat.h26
-rw-r--r--src/crypto/include/zinc/chacha20poly1305.h12
-rw-r--r--src/crypto/zinc/blake2s/blake2s-x86_64.S8
-rw-r--r--src/crypto/zinc/chacha20/chacha20-unrolled-arm.S8
-rw-r--r--src/crypto/zinc/chacha20/chacha20-x86_64.pl4
-rw-r--r--src/crypto/zinc/chacha20poly1305.c218
-rw-r--r--src/crypto/zinc/curve25519/curve25519-arm.S4
-rw-r--r--src/crypto/zinc/poly1305/poly1305-x86_64.pl6
-rw-r--r--src/crypto/zinc/selftest/chacha20poly1305.c80
-rw-r--r--src/device.c6
-rw-r--r--src/device.h8
-rw-r--r--src/dkms.conf2
-rw-r--r--src/main.c1
-rw-r--r--src/messages.h2
-rw-r--r--src/netlink.c10
-rw-r--r--src/receive.c8
-rw-r--r--src/selftest/allowedips.c17
-rw-r--r--src/send.c26
-rw-r--r--src/socket.c13
-rw-r--r--src/tests/qemu/Makefile4
-rw-r--r--src/tests/qemu/arch/powerpc64le.config1
-rw-r--r--src/tools/ipc.c23
-rw-r--r--src/tools/man/wg.810
-rw-r--r--src/tools/setconf.c90
-rw-r--r--src/tools/wg-quick/android.c13
-rwxr-xr-xsrc/tools/wg-quick/linux.bash67
-rw-r--r--src/tools/wg.c1
-rw-r--r--src/uapi/wireguard.h8
-rw-r--r--src/version.h2
34 files changed, 480 insertions, 228 deletions
diff --git a/contrib/examples/nat-hole-punching/README b/contrib/examples/nat-hole-punching/README
index 46e6201..99c20e5 100644
--- a/contrib/examples/nat-hole-punching/README
+++ b/contrib/examples/nat-hole-punching/README
@@ -9,8 +9,8 @@ Compile with:
Server is 1.2.3.4 and is on the public internet accepting UDP:49918.
-Client A is NAT'd and doesnt't know its IP address.
-Client B is NAT'd and doesnt't know its IP address.
+Client A is NAT'd and doesn't know its IP address.
+Client B is NAT'd and doesn't know its IP address.
Server runs:
diff --git a/contrib/examples/reresolve-dns/reresolve-dns.sh b/contrib/examples/reresolve-dns/reresolve-dns.sh
index e579f86..8ab3635 100755
--- a/contrib/examples/reresolve-dns/reresolve-dns.sh
+++ b/contrib/examples/reresolve-dns/reresolve-dns.sh
@@ -15,7 +15,7 @@ INTERFACE="${BASH_REMATCH[1]}"
process_peer() {
[[ $PEER_SECTION -ne 1 || -z $PUBLIC_KEY || -z $ENDPOINT ]] && return 0
- [[ $(wg show "$INTERFACE" latest-handshakes) =~ ^${PUBLIC_KEY//+/\\+}\ ([0-9]+)$ ]] || return 0
+ [[ $(wg show "$INTERFACE" latest-handshakes) =~ ${PUBLIC_KEY//+/\\+}\ ([0-9]+) ]] || return 0
(( ($(date +%s) - ${BASH_REMATCH[1]}) > 135 )) || return 0
wg set "$INTERFACE" peer "$PUBLIC_KEY" endpoint "$ENDPOINT"
reset_peer_section
diff --git a/src/Kconfig b/src/Kconfig
index 65fb31d..156e9db 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -5,8 +5,7 @@ config WIREGUARD
select NET_UDP_TUNNEL
select DST_CACHE
select CRYPTO
- select CRYPTO_BLKCIPHER
- select XOR_BLOCKS
+ select CRYPTO_ALGAPI
select VFP
select VFPv3 if CPU_V7
select NEON if CPU_V7
diff --git a/src/allowedips.c b/src/allowedips.c
index 610aab0..72667d5 100644
--- a/src/allowedips.c
+++ b/src/allowedips.c
@@ -299,14 +299,18 @@ void wg_allowedips_free(struct allowedips *table, struct mutex *lock)
RCU_INIT_POINTER(table->root4, NULL);
RCU_INIT_POINTER(table->root6, NULL);
if (rcu_access_pointer(old4)) {
- root_remove_peer_lists(old4);
- call_rcu(&rcu_dereference_protected(old4,
- lockdep_is_held(lock))->rcu, root_free_rcu);
+ struct allowedips_node *node = rcu_dereference_protected(old4,
+ lockdep_is_held(lock));
+
+ root_remove_peer_lists(node);
+ call_rcu(&node->rcu, root_free_rcu);
}
if (rcu_access_pointer(old6)) {
- root_remove_peer_lists(old6);
- call_rcu(&rcu_dereference_protected(old6,
- lockdep_is_held(lock))->rcu, root_free_rcu);
+ struct allowedips_node *node = rcu_dereference_protected(old6,
+ lockdep_is_held(lock));
+
+ root_remove_peer_lists(node);
+ call_rcu(&node->rcu, root_free_rcu);
}
}
diff --git a/src/compat/compat-asm.h b/src/compat/compat-asm.h
index f5c5bc2..bafd70b 100644
--- a/src/compat/compat-asm.h
+++ b/src/compat/compat-asm.h
@@ -40,4 +40,9 @@
#undef pull
#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0)
+#define SYM_FUNC_START ENTRY
+#define SYM_FUNC_END ENDPROC
+#endif
+
#endif /* _WG_COMPATASM_H */
diff --git a/src/compat/compat.h b/src/compat/compat.h
index 34d6d9e..824f57c 100644
--- a/src/compat/compat.h
+++ b/src/compat/compat.h
@@ -326,7 +326,7 @@ static inline int wait_for_random_bytes(void)
}
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) && LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) && LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) && !defined(ISRHEL8)
#include <linux/random.h>
#include <linux/slab.h>
struct rng_is_initialized_callback {
@@ -831,7 +831,7 @@ static inline void skb_mark_not_on_list(struct sk_buff *skb)
}
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0) && !defined(ISRHEL8)
#define NLA_EXACT_LEN NLA_UNSPEC
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0)
@@ -843,6 +843,28 @@ static inline void skb_mark_not_on_list(struct sk_buff *skb)
#define cpu_have_named_feature(name) (elf_hwcap & (HWCAP_ ## name))
#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
+#include <linux/stddef.h>
+#ifndef offsetofend
+#define offsetofend(TYPE, MEMBER) (offsetof(TYPE, MEMBER) + sizeof(((TYPE *)0)->MEMBER))
+#endif
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0)
+#define genl_dumpit_info(cb) ({ \
+ struct { struct nlattr **attrs; } *a = (void *)((u8 *)cb->args + offsetofend(struct dump_ctx, next_allowedip)); \
+ BUILD_BUG_ON(sizeof(cb->args) < offsetofend(struct dump_ctx, next_allowedip) + sizeof(*a)); \
+ a->attrs = genl_family_attrbuf(&genl_family); \
+ if (nlmsg_parse(cb->nlh, GENL_HDRLEN + genl_family.hdrsize, a->attrs, genl_family.maxattr, device_policy, NULL) < 0) \
+ memset(a->attrs, 0, (genl_family.maxattr + 1) * sizeof(struct nlattr *)); \
+ a; \
+})
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 5) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) || LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 18)
+#define ipv6_dst_lookup_flow(a, b, c, d) ipv6_dst_lookup(a, b, &dst, c) + (void *)0 ?: dst
+#endif
+
#if defined(ISUBUNTU1604)
#include <linux/siphash.h>
#ifndef _WG_LINUX_SIPHASH_H
diff --git a/src/crypto/include/zinc/chacha20poly1305.h b/src/crypto/include/zinc/chacha20poly1305.h
index ce72740..e3339f0 100644
--- a/src/crypto/include/zinc/chacha20poly1305.h
+++ b/src/crypto/include/zinc/chacha20poly1305.h
@@ -22,9 +22,9 @@ void chacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len,
const u64 nonce,
const u8 key[CHACHA20POLY1305_KEY_SIZE]);
-bool __must_check chacha20poly1305_encrypt_sg(
- struct scatterlist *dst, struct scatterlist *src, const size_t src_len,
- const u8 *ad, const size_t ad_len, const u64 nonce,
+bool __must_check chacha20poly1305_encrypt_sg_inplace(
+ struct scatterlist *src, const size_t src_len, const u8 *ad,
+ const size_t ad_len, const u64 nonce,
const u8 key[CHACHA20POLY1305_KEY_SIZE], simd_context_t *simd_context);
bool __must_check
@@ -32,9 +32,9 @@ chacha20poly1305_decrypt(u8 *dst, const u8 *src, const size_t src_len,
const u8 *ad, const size_t ad_len, const u64 nonce,
const u8 key[CHACHA20POLY1305_KEY_SIZE]);
-bool __must_check chacha20poly1305_decrypt_sg(
- struct scatterlist *dst, struct scatterlist *src, const size_t src_len,
- const u8 *ad, const size_t ad_len, const u64 nonce,
+bool __must_check chacha20poly1305_decrypt_sg_inplace(
+ struct scatterlist *src, size_t src_len, const u8 *ad,
+ const size_t ad_len, const u64 nonce,
const u8 key[CHACHA20POLY1305_KEY_SIZE], simd_context_t *simd_context);
void xchacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len,
diff --git a/src/crypto/zinc/blake2s/blake2s-x86_64.S b/src/crypto/zinc/blake2s/blake2s-x86_64.S
index 8591938..24910b7 100644
--- a/src/crypto/zinc/blake2s/blake2s-x86_64.S
+++ b/src/crypto/zinc/blake2s/blake2s-x86_64.S
@@ -47,7 +47,7 @@ SIGMA2:
.text
#ifdef CONFIG_AS_SSSE3
-ENTRY(blake2s_compress_ssse3)
+SYM_FUNC_START(blake2s_compress_ssse3)
testq %rdx,%rdx
je .Lendofloop
movdqu (%rdi),%xmm0
@@ -173,11 +173,11 @@ ENTRY(blake2s_compress_ssse3)
movdqu %xmm14,0x20(%rdi)
.Lendofloop:
ret
-ENDPROC(blake2s_compress_ssse3)
+SYM_FUNC_END(blake2s_compress_ssse3)
#endif /* CONFIG_AS_SSSE3 */
#ifdef CONFIG_AS_AVX512
-ENTRY(blake2s_compress_avx512)
+SYM_FUNC_START(blake2s_compress_avx512)
vmovdqu (%rdi),%xmm0
vmovdqu 0x10(%rdi),%xmm1
vmovdqu 0x20(%rdi),%xmm4
@@ -254,5 +254,5 @@ ENTRY(blake2s_compress_avx512)
vmovdqu %xmm4,0x20(%rdi)
vzeroupper
retq
-ENDPROC(blake2s_compress_avx512)
+SYM_FUNC_END(blake2s_compress_avx512)
#endif /* CONFIG_AS_AVX512 */
diff --git a/src/crypto/zinc/chacha20/chacha20-unrolled-arm.S b/src/crypto/zinc/chacha20/chacha20-unrolled-arm.S
index 2140319..8fb4bc2 100644
--- a/src/crypto/zinc/chacha20/chacha20-unrolled-arm.S
+++ b/src/crypto/zinc/chacha20/chacha20-unrolled-arm.S
@@ -394,7 +394,7 @@
* void chacha20_arm(u8 *out, const u8 *in, size_t len, const u32 key[8],
* const u32 iv[4]);
*/
-ENTRY(chacha20_arm)
+SYM_FUNC_START(chacha20_arm)
cmp r2, #0 // len == 0?
reteq lr
@@ -428,12 +428,12 @@ ENTRY(chacha20_arm)
add sp, #76
pop {r4-r11, pc}
-ENDPROC(chacha20_arm)
+SYM_FUNC_END(chacha20_arm)
/*
* void hchacha20_arm(const u32 state[16], u32 out[8]);
*/
-ENTRY(hchacha20_arm)
+SYM_FUNC_START(hchacha20_arm)
push {r1,r4-r11,lr}
mov r14, r0
@@ -458,4 +458,4 @@ ENTRY(hchacha20_arm)
stm r4, {X0,X1,X2,X3,X12,X13,X14,X15}
pop {r4-r11,pc}
-ENDPROC(hchacha20_arm)
+SYM_FUNC_END(hchacha20_arm)
diff --git a/src/crypto/zinc/chacha20/chacha20-x86_64.pl b/src/crypto/zinc/chacha20/chacha20-x86_64.pl
index cec7572..29906a6 100644
--- a/src/crypto/zinc/chacha20/chacha20-x86_64.pl
+++ b/src/crypto/zinc/chacha20/chacha20-x86_64.pl
@@ -124,7 +124,7 @@ sub declare_function() {
my ($name, $align, $nargs) = @_;
if($kernel) {
$code .= ".align $align\n";
- $code .= "ENTRY($name)\n";
+ $code .= "SYM_FUNC_START($name)\n";
$code .= ".L$name:\n";
} else {
$code .= ".globl $name\n";
@@ -137,7 +137,7 @@ sub declare_function() {
sub end_function() {
my ($name) = @_;
if($kernel) {
- $code .= "ENDPROC($name)\n";
+ $code .= "SYM_FUNC_END($name)\n";
} else {
$code .= ".size $name,.-$name\n";
}
diff --git a/src/crypto/zinc/chacha20poly1305.c b/src/crypto/zinc/chacha20poly1305.c
index 0001c92..571a64e 100644
--- a/src/crypto/zinc/chacha20poly1305.c
+++ b/src/crypto/zinc/chacha20poly1305.c
@@ -18,16 +18,7 @@
#include <linux/init.h>
#include <crypto/scatterwalk.h> // For blkcipher_walk.
-static const u8 pad0[16] = { 0 };
-
-static struct blkcipher_desc desc = { .tfm = &(struct crypto_blkcipher){
- .base = { .__crt_alg = &(struct crypto_alg){
- .cra_blocksize = 1,
-#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
- .cra_alignmask = sizeof(u32) - 1
-#endif
- } }
-} };
+static const u8 pad0[CHACHA20_BLOCK_SIZE] = { 0 };
static inline void
__chacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len,
@@ -82,22 +73,25 @@ void chacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len,
}
EXPORT_SYMBOL(chacha20poly1305_encrypt);
-bool chacha20poly1305_encrypt_sg(struct scatterlist *dst,
- struct scatterlist *src, const size_t src_len,
- const u8 *ad, const size_t ad_len,
- const u64 nonce,
- const u8 key[CHACHA20POLY1305_KEY_SIZE],
- simd_context_t *simd_context)
+bool chacha20poly1305_encrypt_sg_inplace(struct scatterlist *src,
+ const size_t src_len,
+ const u8 *ad, const size_t ad_len,
+ const u64 nonce,
+ const u8 key[CHACHA20POLY1305_KEY_SIZE],
+ simd_context_t *simd_context)
{
struct poly1305_ctx poly1305_state;
struct chacha20_ctx chacha20_state;
- int ret = 0;
- struct blkcipher_walk walk;
+ struct sg_mapping_iter miter;
+ size_t partial = 0;
+ ssize_t sl;
union {
+ u8 chacha20_stream[CHACHA20_BLOCK_SIZE];
u8 block0[POLY1305_KEY_SIZE];
u8 mac[POLY1305_MAC_SIZE];
__le64 lens[2];
- } b = { { 0 } };
+ } b __aligned(16) = { { 0 } };
+
chacha20_init(&chacha20_state, key, nonce);
chacha20(&chacha20_state, b.block0, b.block0, sizeof(b.block0),
@@ -108,32 +102,43 @@ bool chacha20poly1305_encrypt_sg(struct scatterlist *dst,
poly1305_update(&poly1305_state, pad0, (0x10 - ad_len) & 0xf,
simd_context);
- if (likely(src_len)) {
- blkcipher_walk_init(&walk, dst, src, src_len);
- ret = blkcipher_walk_virt_block(&desc, &walk,
- CHACHA20_BLOCK_SIZE);
- while (walk.nbytes >= CHACHA20_BLOCK_SIZE) {
- size_t chunk_len =
- rounddown(walk.nbytes, CHACHA20_BLOCK_SIZE);
-
- chacha20(&chacha20_state, walk.dst.virt.addr,
- walk.src.virt.addr, chunk_len, simd_context);
- poly1305_update(&poly1305_state, walk.dst.virt.addr,
- chunk_len, simd_context);
- simd_relax(simd_context);
- ret = blkcipher_walk_done(&desc, &walk,
- walk.nbytes % CHACHA20_BLOCK_SIZE);
+ sg_miter_start(&miter, src, sg_nents(src), SG_MITER_TO_SG | SG_MITER_ATOMIC);
+ for (sl = src_len; sl > 0 && sg_miter_next(&miter); sl -= miter.length) {
+ u8 *addr = miter.addr;
+ size_t length = min_t(size_t, sl, miter.length);
+
+ if (unlikely(partial)) {
+ size_t l = min(length, CHACHA20_BLOCK_SIZE - partial);
+
+ crypto_xor(addr, b.chacha20_stream + partial, l);
+ partial = (partial + l) & (CHACHA20_BLOCK_SIZE - 1);
+
+ addr += l;
+ length -= l;
}
- if (walk.nbytes) {
- chacha20(&chacha20_state, walk.dst.virt.addr,
- walk.src.virt.addr, walk.nbytes, simd_context);
- poly1305_update(&poly1305_state, walk.dst.virt.addr,
- walk.nbytes, simd_context);
- ret = blkcipher_walk_done(&desc, &walk, 0);
+
+ if (likely(length >= CHACHA20_BLOCK_SIZE || length == sl)) {
+ size_t l = length;
+
+ if (unlikely(length < sl))
+ l &= ~(CHACHA20_BLOCK_SIZE - 1);
+ chacha20(&chacha20_state, addr, addr, l, simd_context);
+ addr += l;
+ length -= l;
+ }
+
+ if (unlikely(length > 0)) {
+ chacha20(&chacha20_state, b.chacha20_stream, pad0,
+ CHACHA20_BLOCK_SIZE, simd_context);
+ crypto_xor(addr, b.chacha20_stream, length);
+ partial = length;
}
+
+ poly1305_update(&poly1305_state, miter.addr,
+ min_t(size_t, sl, miter.length), simd_context);
+
+ simd_relax(simd_context);
}
- if (unlikely(ret))
- goto err;
poly1305_update(&poly1305_state, pad0, (0x10 - src_len) & 0xf,
simd_context);
@@ -143,14 +148,22 @@ bool chacha20poly1305_encrypt_sg(struct scatterlist *dst,
poly1305_update(&poly1305_state, (u8 *)b.lens, sizeof(b.lens),
simd_context);
- poly1305_final(&poly1305_state, b.mac, simd_context);
- scatterwalk_map_and_copy(b.mac, dst, src_len, sizeof(b.mac), 1);
-err:
+ if (likely(sl <= -POLY1305_MAC_SIZE))
+ poly1305_final(&poly1305_state, miter.addr + miter.length + sl,
+ simd_context);
+
+ sg_miter_stop(&miter);
+
+ if (unlikely(sl > -POLY1305_MAC_SIZE)) {
+ poly1305_final(&poly1305_state, b.mac, simd_context);
+ scatterwalk_map_and_copy(b.mac, src, src_len, sizeof(b.mac), 1);
+ }
+
memzero_explicit(&chacha20_state, sizeof(chacha20_state));
memzero_explicit(&b, sizeof(b));
- return !ret;
+ return true;
}
-EXPORT_SYMBOL(chacha20poly1305_encrypt_sg);
+EXPORT_SYMBOL(chacha20poly1305_encrypt_sg_inplace);
static inline bool
__chacha20poly1305_decrypt(u8 *dst, const u8 *src, const size_t src_len,
@@ -217,29 +230,32 @@ bool chacha20poly1305_decrypt(u8 *dst, const u8 *src, const size_t src_len,
}
EXPORT_SYMBOL(chacha20poly1305_decrypt);
-bool chacha20poly1305_decrypt_sg(struct scatterlist *dst,
- struct scatterlist *src, const size_t src_len,
- const u8 *ad, const size_t ad_len,
- const u64 nonce,
- const u8 key[CHACHA20POLY1305_KEY_SIZE],
- simd_context_t *simd_context)
+bool chacha20poly1305_decrypt_sg_inplace(struct scatterlist *src,
+ size_t src_len,
+ const u8 *ad, const size_t ad_len,
+ const u64 nonce,
+ const u8 key[CHACHA20POLY1305_KEY_SIZE],
+ simd_context_t *simd_context)
{
struct poly1305_ctx poly1305_state;
struct chacha20_ctx chacha20_state;
- struct blkcipher_walk walk;
- int ret = 0;
- size_t dst_len;
+ struct sg_mapping_iter miter;
+ size_t partial = 0;
+ ssize_t sl;
union {
+ u8 chacha20_stream[CHACHA20_BLOCK_SIZE];
u8 block0[POLY1305_KEY_SIZE];
struct {
u8 read_mac[POLY1305_MAC_SIZE];
u8 computed_mac[POLY1305_MAC_SIZE];
};
__le64 lens[2];
- } b = { { 0 } };
+ } b __aligned(16) = { { 0 } };
+ bool ret = false;
if (unlikely(src_len < POLY1305_MAC_SIZE))
- return false;
+ return ret;
+ src_len -= POLY1305_MAC_SIZE;
chacha20_init(&chacha20_state, key, nonce);
chacha20(&chacha20_state, b.block0, b.block0, sizeof(b.block0),
@@ -250,52 +266,74 @@ bool chacha20poly1305_decrypt_sg(struct scatterlist *dst,
poly1305_update(&poly1305_state, pad0, (0x10 - ad_len) & 0xf,
simd_context);
- dst_len = src_len - POLY1305_MAC_SIZE;
- if (likely(dst_len)) {
- blkcipher_walk_init(&walk, dst, src, dst_len);
- ret = blkcipher_walk_virt_block(&desc, &walk,
- CHACHA20_BLOCK_SIZE);
- while (walk.nbytes >= CHACHA20_BLOCK_SIZE) {
- size_t chunk_len =
- rounddown(walk.nbytes, CHACHA20_BLOCK_SIZE);
-
- poly1305_update(&poly1305_state, walk.src.virt.addr,
- chunk_len, simd_context);
- chacha20(&chacha20_state, walk.dst.virt.addr,
- walk.src.virt.addr, chunk_len, simd_context);
- simd_relax(simd_context);
- ret = blkcipher_walk_done(&desc, &walk,
- walk.nbytes % CHACHA20_BLOCK_SIZE);
+ sg_miter_start(&miter, src, sg_nents(src), SG_MITER_TO_SG | SG_MITER_ATOMIC);
+ for (sl = src_len; sl > 0 && sg_miter_next(&miter); sl -= miter.length) {
+ u8 *addr = miter.addr;
+ size_t length = min_t(size_t, sl, miter.length);
+
+ poly1305_update(&poly1305_state, addr, length, simd_context);
+
+ if (unlikely(partial)) {
+ size_t l = min(length, CHACHA20_BLOCK_SIZE - partial);
+
+ crypto_xor(addr, b.chacha20_stream + partial, l);
+ partial = (partial + l) & (CHACHA20_BLOCK_SIZE - 1);
+
+ addr += l;
+ length -= l;
}
- if (walk.nbytes) {
- poly1305_update(&poly1305_state, walk.src.virt.addr,
- walk.nbytes, simd_context);
- chacha20(&chacha20_state, walk.dst.virt.addr,
- walk.src.virt.addr, walk.nbytes, simd_context);
- ret = blkcipher_walk_done(&desc, &walk, 0);
+
+ if (likely(length >= CHACHA20_BLOCK_SIZE || length == sl)) {
+ size_t l = length;
+
+ if (unlikely(length < sl))
+ l &= ~(CHACHA20_BLOCK_SIZE - 1);
+ chacha20(&chacha20_state, addr, addr, l, simd_context);
+ addr += l;
+ length -= l;
+ }
+
+ if (unlikely(length > 0)) {
+ chacha20(&chacha20_state, b.chacha20_stream, pad0,
+ CHACHA20_BLOCK_SIZE, simd_context);
+ crypto_xor(addr, b.chacha20_stream, length);
+ partial = length;
}
+
+ simd_relax(simd_context);
}
- if (unlikely(ret))
- goto err;
- poly1305_update(&poly1305_state, pad0, (0x10 - dst_len) & 0xf,
+ poly1305_update(&poly1305_state, pad0, (0x10 - src_len) & 0xf,
simd_context);
b.lens[0] = cpu_to_le64(ad_len);
- b.lens[1] = cpu_to_le64(dst_len);
+ b.lens[1] = cpu_to_le64(src_len);
poly1305_update(&poly1305_state, (u8 *)b.lens, sizeof(b.lens),
simd_context);
- poly1305_final(&poly1305_state, b.computed_mac, simd_context);
+ if (likely(sl <= -POLY1305_MAC_SIZE)) {
+ poly1305_final(&poly1305_state, b.computed_mac, simd_context);
+ ret = !crypto_memneq(b.computed_mac,
+ miter.addr + miter.length + sl,
+ POLY1305_MAC_SIZE);
+ }
+
+ sg_miter_stop(&miter);
+
+ if (unlikely(sl > -POLY1305_MAC_SIZE)) {
+ poly1305_final(&poly1305_state, b.computed_mac, simd_context);
+ scatterwalk_map_and_copy(b.read_mac, src, src_len,
+ sizeof(b.read_mac), 0);
+ ret = !crypto_memneq(b.read_mac, b.computed_mac,
+ POLY1305_MAC_SIZE);
+
+ }
- scatterwalk_map_and_copy(b.read_mac, src, dst_len, POLY1305_MAC_SIZE, 0);
- ret = crypto_memneq(b.read_mac, b.computed_mac, POLY1305_MAC_SIZE);
-err:
memzero_explicit(&chacha20_state, sizeof(chacha20_state));
memzero_explicit(&b, sizeof(b));
- return !ret;
+ return ret;
}
-EXPORT_SYMBOL(chacha20poly1305_decrypt_sg);
+EXPORT_SYMBOL(chacha20poly1305_decrypt_sg_inplace);
void xchacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len,
const u8 *ad, const size_t ad_len,
diff --git a/src/crypto/zinc/curve25519/curve25519-arm.S b/src/crypto/zinc/curve25519/curve25519-arm.S
index 0ef1431..8eca8a1 100644
--- a/src/crypto/zinc/curve25519/curve25519-arm.S
+++ b/src/crypto/zinc/curve25519/curve25519-arm.S
@@ -15,7 +15,7 @@
.arch armv7-a
.align 4
-ENTRY(curve25519_neon)
+SYM_FUNC_START(curve25519_neon)
push {r4-r11, lr}
mov ip, sp
sub r3, sp, #704
@@ -2060,5 +2060,5 @@ ENTRY(curve25519_neon)
movw r0, #0
mov sp, ip
pop {r4-r11, pc}
-ENDPROC(curve25519_neon)
+SYM_FUNC_END(curve25519_neon)
#endif
diff --git a/src/crypto/zinc/poly1305/poly1305-x86_64.pl b/src/crypto/zinc/poly1305/poly1305-x86_64.pl
index 37ed869..f994855 100644
--- a/src/crypto/zinc/poly1305/poly1305-x86_64.pl
+++ b/src/crypto/zinc/poly1305/poly1305-x86_64.pl
@@ -35,7 +35,7 @@
# Skylake-X system performance. Since we are likely to suppress
# AVX512F capability flag [at least on Skylake-X], conversion serves
# as kind of "investment protection". Note that next *lake processor,
-# Cannolake, has AVX512IFMA code path to execute...
+# Cannonlake, has AVX512IFMA code path to execute...
#
# Numbers are cycles per processed byte with poly1305_blocks alone,
# measured with rdtsc at fixed clock frequency.
@@ -109,7 +109,7 @@ sub declare_function() {
my ($name, $align, $nargs) = @_;
if($kernel) {
$code .= ".align $align\n";
- $code .= "ENTRY($name)\n";
+ $code .= "SYM_FUNC_START($name)\n";
$code .= ".L$name:\n";
} else {
$code .= ".globl $name\n";
@@ -122,7 +122,7 @@ sub declare_function() {
sub end_function() {
my ($name) = @_;
if($kernel) {
- $code .= "ENDPROC($name)\n";
+ $code .= "SYM_FUNC_END($name)\n";
} else {
$code .= ".size $name,.-$name\n";
}
diff --git a/src/crypto/zinc/selftest/chacha20poly1305.c b/src/crypto/zinc/selftest/chacha20poly1305.c
index dba9cd7..c58ac6e 100644
--- a/src/crypto/zinc/selftest/chacha20poly1305.c
+++ b/src/crypto/zinc/selftest/chacha20poly1305.c
@@ -8879,15 +8879,15 @@ decryption_success(bool func_ret, bool expect_failure, int memcmp_result)
static bool __init chacha20poly1305_selftest(void)
{
enum { MAXIMUM_TEST_BUFFER_LEN = 1UL << 12 };
- size_t i;
- u8 *computed_output = NULL, *heap_src = NULL;
+ size_t i, j, k, total_len;
+ u8 *computed_output = NULL, *input = NULL;
bool success = true, ret;
simd_context_t simd_context;
- struct scatterlist sg_src, sg_dst;
+ struct scatterlist sg_src[3];
- heap_src = kmalloc(MAXIMUM_TEST_BUFFER_LEN, GFP_KERNEL);
computed_output = kmalloc(MAXIMUM_TEST_BUFFER_LEN, GFP_KERNEL);
- if (!heap_src || !computed_output) {
+ input = kmalloc(MAXIMUM_TEST_BUFFER_LEN, GFP_KERNEL);
+ if (!computed_output || !input) {
pr_err("chacha20poly1305 self-test malloc: FAIL\n");
success = false;
goto out;
@@ -8916,15 +8916,12 @@ static bool __init chacha20poly1305_selftest(void)
for (i = 0; i < ARRAY_SIZE(chacha20poly1305_enc_vectors); ++i) {
if (chacha20poly1305_enc_vectors[i].nlen != 8)
continue;
- memset(computed_output, 0, MAXIMUM_TEST_BUFFER_LEN);
- memcpy(heap_src, chacha20poly1305_enc_vectors[i].input,
+ memcpy(computed_output, chacha20poly1305_enc_vectors[i].input,
chacha20poly1305_enc_vectors[i].ilen);
- sg_init_one(&sg_src, heap_src,
- chacha20poly1305_enc_vectors[i].ilen);
- sg_init_one(&sg_dst, computed_output,
+ sg_init_one(sg_src, computed_output,
chacha20poly1305_enc_vectors[i].ilen +
POLY1305_MAC_SIZE);
- ret = chacha20poly1305_encrypt_sg(&sg_dst, &sg_src,
+ ret = chacha20poly1305_encrypt_sg_inplace(sg_src,
chacha20poly1305_enc_vectors[i].ilen,
chacha20poly1305_enc_vectors[i].assoc,
chacha20poly1305_enc_vectors[i].alen,
@@ -8963,15 +8960,11 @@ static bool __init chacha20poly1305_selftest(void)
}
simd_get(&simd_context);
for (i = 0; i < ARRAY_SIZE(chacha20poly1305_dec_vectors); ++i) {
- memset(computed_output, 0, MAXIMUM_TEST_BUFFER_LEN);
- memcpy(heap_src, chacha20poly1305_dec_vectors[i].input,
+ memcpy(computed_output, chacha20poly1305_dec_vectors[i].input,
chacha20poly1305_dec_vectors[i].ilen);
- sg_init_one(&sg_src, heap_src,
+ sg_init_one(sg_src, computed_output,
chacha20poly1305_dec_vectors[i].ilen);
- sg_init_one(&sg_dst, computed_output,
- chacha20poly1305_dec_vectors[i].ilen -
- POLY1305_MAC_SIZE);
- ret = chacha20poly1305_decrypt_sg(&sg_dst, &sg_src,
+ ret = chacha20poly1305_decrypt_sg_inplace(sg_src,
chacha20poly1305_dec_vectors[i].ilen,
chacha20poly1305_dec_vectors[i].assoc,
chacha20poly1305_dec_vectors[i].alen,
@@ -9027,8 +9020,57 @@ static bool __init chacha20poly1305_selftest(void)
}
}
+ simd_get(&simd_context);
+ for (total_len = POLY1305_MAC_SIZE; IS_ENABLED(DEBUG_CHACHA20POLY1305_SLOW_CHUNK_TEST)
+ && total_len <= 1 << 10; ++total_len) {
+ for (i = 0; i <= total_len; ++i) {
+ for (j = i; j <= total_len; ++j) {
+ sg_init_table(sg_src, 3);
+ sg_set_buf(&sg_src[0], input, i);
+ sg_set_buf(&sg_src[1], input + i, j - i);
+ sg_set_buf(&sg_src[2], input + j, total_len - j);
+ memset(computed_output, 0, total_len);
+ memset(input, 0, total_len);
+
+ if (!chacha20poly1305_encrypt_sg_inplace(sg_src,
+ total_len - POLY1305_MAC_SIZE, NULL, 0,
+ 0, enc_key001, &simd_context))
+ goto chunkfail;
+ chacha20poly1305_encrypt(computed_output,
+ computed_output,
+ total_len - POLY1305_MAC_SIZE, NULL, 0, 0,
+ enc_key001);
+ if (memcmp(computed_output, input, total_len))
+ goto chunkfail;;
+ if (!chacha20poly1305_decrypt(computed_output,
+ input, total_len, NULL, 0, 0, enc_key001))
+ goto chunkfail;
+ for (k = 0; k < total_len - POLY1305_MAC_SIZE; ++k) {
+ if (computed_output[k])
+ goto chunkfail;
+ }
+ if (!chacha20poly1305_decrypt_sg_inplace(sg_src,
+ total_len, NULL, 0, 0, enc_key001,
+ &simd_context))
+ goto chunkfail;
+ for (k = 0; k < total_len - POLY1305_MAC_SIZE; ++k) {
+ if (input[k])
+ goto chunkfail;
+ }
+ continue;
+
+ chunkfail:
+ pr_err("chacha20poly1305 chunked self-test %zu/%zu/%zu: FAIL\n",
+ total_len, i, j);
+ success = false;
+ }
+
+ }
+ }
+ simd_put(&simd_context);
+
out:
- kfree(heap_src);
kfree(computed_output);
+ kfree(input);
return success;
}
diff --git a/src/device.c b/src/device.c
index 532d399..e888ac3 100644
--- a/src/device.c
+++ b/src/device.c
@@ -171,8 +171,8 @@ static netdev_tx_t wg_xmit(struct sk_buff *skb, struct net_device *dev)
dev_kfree_skb(skb);
skb = segs;
}
- do {
- next = skb->next;
+
+ skb_list_walk_safe(skb, skb, next) {
skb_mark_not_on_list(skb);
skb = skb_share_check(skb, GFP_ATOMIC);
@@ -187,7 +187,7 @@ static netdev_tx_t wg_xmit(struct sk_buff *skb, struct net_device *dev)
PACKET_CB(skb)->mtu = mtu;
__skb_queue_tail(&packets, skb);
- } while ((skb = next) != NULL);
+ }
spin_lock_bh(&peer->staged_packet_queue.lock);
/* If the queue is getting too big, we start removing the oldest packets
diff --git a/src/device.h b/src/device.h
index b15a8be..c91f305 100644
--- a/src/device.h
+++ b/src/device.h
@@ -62,4 +62,12 @@ struct wg_device {
int wg_device_init(void);
void wg_device_uninit(void);
+/* Later after the dust settles, this can be moved into include/linux/skbuff.h,
+ * where virtually all code that deals with GSO segs can benefit, around ~30
+ * drivers as of writing.
+ */
+#define skb_list_walk_safe(first, skb, next) \
+ for (skb = first, next = skb->next; skb; \
+ skb = next, next = skb ? skb->next : NULL)
+
#endif /* _WG_DEVICE_H */
diff --git a/src/dkms.conf b/src/dkms.conf
index d1d24b8..cdcd2e7 100644
--- a/src/dkms.conf
+++ b/src/dkms.conf
@@ -1,5 +1,5 @@
PACKAGE_NAME="wireguard"
-PACKAGE_VERSION="0.0.20190913"
+PACKAGE_VERSION="0.0.20191219"
AUTOINSTALL=yes
BUILT_MODULE_NAME="wireguard"
diff --git a/src/main.c b/src/main.c
index 4b5b58e..9ca4195 100644
--- a/src/main.c
+++ b/src/main.c
@@ -12,7 +12,6 @@
#include "uapi/wireguard.h"
#include "crypto/zinc.h"
-#include <linux/version.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/genetlink.h>
diff --git a/src/messages.h b/src/messages.h
index 3cfd1c5..f415cdd 100644
--- a/src/messages.h
+++ b/src/messages.h
@@ -38,7 +38,7 @@ enum counter_values {
};
enum limits {
- REKEY_AFTER_MESSAGES = U64_MAX - 0xffff,
+ REKEY_AFTER_MESSAGES = 1ULL << 60,
REJECT_AFTER_MESSAGES = U64_MAX - COUNTER_WINDOW_SIZE - 1,
REKEY_TIMEOUT = 5,
REKEY_TIMEOUT_JITTER_MAX_JIFFIES = HZ / 3,
diff --git a/src/netlink.c b/src/netlink.c
index 190e405..9bf2f84 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -94,8 +94,8 @@ static int get_allowedips(struct sk_buff *skb, const u8 *ip, u8 cidr,
struct dump_ctx {
struct wg_device *wg;
struct wg_peer *next_peer;
- struct allowedips_node *next_allowedip;
u64 allowedips_seq;
+ struct allowedips_node *next_allowedip;
};
#define DUMP_CTX(cb) ((struct dump_ctx *)(cb)->args)
@@ -196,15 +196,9 @@ err:
static int wg_get_device_start(struct netlink_callback *cb)
{
- struct nlattr **attrs = genl_family_attrbuf(&genl_family);
struct wg_device *wg;
- int ret;
- ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + genl_family.hdrsize, attrs,
- genl_family.maxattr, device_policy, NULL);
- if (ret < 0)
- return ret;
- wg = lookup_interface(attrs, cb->skb);
+ wg = lookup_interface(genl_dumpit_info(cb)->attrs, cb->skb);
if (IS_ERR(wg))
return PTR_ERR(wg);
DUMP_CTX(cb)->wg = wg;
diff --git a/src/receive.c b/src/receive.c
index 247a56b..e00f0f4 100644
--- a/src/receive.c
+++ b/src/receive.c
@@ -281,9 +281,9 @@ static bool decrypt_packet(struct sk_buff *skb, struct noise_symmetric_key *key,
if (skb_to_sgvec(skb, sg, 0, skb->len) <= 0)
return false;
- if (!chacha20poly1305_decrypt_sg(sg, sg, skb->len, NULL, 0,
- PACKET_CB(skb)->nonce, key->key,
- simd_context))
+ if (!chacha20poly1305_decrypt_sg_inplace(sg, skb->len, NULL, 0,
+ PACKET_CB(skb)->nonce, key->key,
+ simd_context))
return false;
/* Another ugly situation of pushing and pulling the header so as to
@@ -382,7 +382,7 @@ static void wg_packet_consume_data_done(struct wg_peer *peer,
/* We've already verified the Poly1305 auth tag, which means this packet
* was not modified in transit. We can therefore tell the networking
* stack that all checksums of every layer of encapsulation have already
- * been checked "by the hardware" and therefore is unneccessary to check
+ * been checked "by the hardware" and therefore is unnecessary to check
* again in software.
*/
skb->ip_summed = CHECKSUM_UNNECESSARY;
diff --git a/src/selftest/allowedips.c b/src/selftest/allowedips.c
index 6e244a9..846db14 100644
--- a/src/selftest/allowedips.c
+++ b/src/selftest/allowedips.c
@@ -307,12 +307,12 @@ static __init bool randomized_test(void)
if (wg_allowedips_insert_v4(&t, (struct in_addr *)ip, cidr,
peer, &mutex) < 0) {
pr_err("allowedips random self-test malloc: FAIL\n");
- goto free;
+ goto free_locked;
}
if (horrible_allowedips_insert_v4(&h, (struct in_addr *)ip,
cidr, peer) < 0) {
pr_err("allowedips random self-test malloc: FAIL\n");
- goto free;
+ goto free_locked;
}
for (j = 0; j < NUM_MUTATED_ROUTES; ++j) {
memcpy(mutated, ip, 4);
@@ -334,12 +334,12 @@ static __init bool randomized_test(void)
(struct in_addr *)mutated,
cidr, peer, &mutex) < 0) {
pr_err("allowedips random malloc: FAIL\n");
- goto free;
+ goto free_locked;
}
if (horrible_allowedips_insert_v4(&h,
(struct in_addr *)mutated, cidr, peer)) {
pr_err("allowedips random self-test malloc: FAIL\n");
- goto free;
+ goto free_locked;
}
}
}
@@ -351,12 +351,12 @@ static __init bool randomized_test(void)
if (wg_allowedips_insert_v6(&t, (struct in6_addr *)ip, cidr,
peer, &mutex) < 0) {
pr_err("allowedips random self-test malloc: FAIL\n");
- goto free;
+ goto free_locked;
}
if (horrible_allowedips_insert_v6(&h, (struct in6_addr *)ip,
cidr, peer) < 0) {
pr_err("allowedips random self-test malloc: FAIL\n");
- goto free;
+ goto free_locked;
}
for (j = 0; j < NUM_MUTATED_ROUTES; ++j) {
memcpy(mutated, ip, 16);
@@ -378,13 +378,13 @@ static __init bool randomized_test(void)
(struct in6_addr *)mutated,
cidr, peer, &mutex) < 0) {
pr_err("allowedips random self-test malloc: FAIL\n");
- goto free;
+ goto free_locked;
}
if (horrible_allowedips_insert_v6(
&h, (struct in6_addr *)mutated, cidr,
peer)) {
pr_err("allowedips random self-test malloc: FAIL\n");
- goto free;
+ goto free_locked;
}
}
}
@@ -417,6 +417,7 @@ static __init bool randomized_test(void)
free:
mutex_lock(&mutex);
+free_locked:
wg_allowedips_free(&t, &mutex);
mutex_unlock(&mutex);
horrible_allowedips_free(&h);
diff --git a/src/send.c b/src/send.c
index 62f1a97..85b83fa 100644
--- a/src/send.c
+++ b/src/send.c
@@ -207,9 +207,10 @@ static bool encrypt_packet(struct sk_buff *skb, struct noise_keypair *keypair,
if (skb_to_sgvec(skb, sg, sizeof(struct message_data),
noise_encrypted_len(plaintext_len)) <= 0)
return false;
- return chacha20poly1305_encrypt_sg(sg, sg, plaintext_len, NULL, 0,
- PACKET_CB(skb)->nonce,
- keypair->sending.key, simd_context);
+ return chacha20poly1305_encrypt_sg_inplace(sg, plaintext_len, NULL, 0,
+ PACKET_CB(skb)->nonce,
+ keypair->sending.key,
+ simd_context);
}
void wg_packet_send_keepalive(struct wg_peer *peer)
@@ -233,17 +234,6 @@ void wg_packet_send_keepalive(struct wg_peer *peer)
wg_packet_send_staged_packets(peer);
}
-#define skb_walk_null_queue_safe(first, skb, next) \
- for (skb = first, next = skb->next; skb; \
- skb = next, next = skb ? skb->next : NULL)
-static void skb_free_null_queue(struct sk_buff *first)
-{
- struct sk_buff *skb, *next;
-
- skb_walk_null_queue_safe(first, skb, next)
- dev_kfree_skb(skb);
-}
-
static void wg_packet_create_data_done(struct sk_buff *first,
struct wg_peer *peer)
{
@@ -252,7 +242,7 @@ static void wg_packet_create_data_done(struct sk_buff *first,
wg_timers_any_authenticated_packet_traversal(peer);
wg_timers_any_authenticated_packet_sent(peer);
- skb_walk_null_queue_safe(first, skb, next) {
+ skb_list_walk_safe(first, skb, next) {
is_keepalive = skb->len == message_data_len(0);
if (likely(!wg_socket_send_skb_to_peer(peer, skb,
PACKET_CB(skb)->ds) && !is_keepalive))
@@ -284,7 +274,7 @@ void wg_packet_tx_worker(struct work_struct *work)
if (likely(state == PACKET_STATE_CRYPTED))
wg_packet_create_data_done(first, peer);
else
- skb_free_null_queue(first);
+ kfree_skb_list(first);
wg_noise_keypair_put(keypair, false);
wg_peer_put(peer);
@@ -302,7 +292,7 @@ void wg_packet_encrypt_worker(struct work_struct *work)
while ((first = ptr_ring_consume_bh(&queue->ring)) != NULL) {
enum packet_state state = PACKET_STATE_CRYPTED;
- skb_walk_null_queue_safe(first, skb, next) {
+ skb_list_walk_safe(first, skb, next) {
if (likely(encrypt_packet(skb,
PACKET_CB(first)->keypair,
&simd_context))) {
@@ -343,7 +333,7 @@ err:
return;
wg_noise_keypair_put(PACKET_CB(first)->keypair, false);
wg_peer_put(peer);
- skb_free_null_queue(first);
+ kfree_skb_list(first);
}
void wg_packet_purge_staged_packets(struct wg_peer *peer)
diff --git a/src/socket.c b/src/socket.c
index d4f65bb..c46256d 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -31,7 +31,7 @@ static int send4(struct wg_device *wg, struct sk_buff *skb,
struct sock *sock;
int ret = 0;
- skb->next = skb->prev = NULL;
+ skb_mark_not_on_list(skb);
skb->dev = wg->dev;
skb->mark = wg->fwmark;
@@ -117,7 +117,7 @@ static int send6(struct wg_device *wg, struct sk_buff *skb,
struct sock *sock;
int ret = 0;
- skb->next = skb->prev = NULL;
+ skb_mark_not_on_list(skb);
skb->dev = wg->dev;
skb->mark = wg->fwmark;
@@ -142,9 +142,10 @@ static int send6(struct wg_device *wg, struct sk_buff *skb,
if (cache)
dst_cache_reset(cache);
}
- ret = ipv6_stub->ipv6_dst_lookup(sock_net(sock), sock, &dst,
- &fl);
- if (unlikely(ret)) {
+ dst = ipv6_stub->ipv6_dst_lookup_flow(sock_net(sock), sock, &fl,
+ NULL);
+ if (unlikely(IS_ERR(dst))) {
+ ret = PTR_ERR(dst);
net_dbg_ratelimited("%s: No route to %pISpfsc, error %d\n",
wg->dev->name, &endpoint->addr, ret);
goto err;
@@ -410,7 +411,7 @@ retry:
}
#endif
- wg_socket_reinit(wg, new4 ? new4->sk : NULL, new6 ? new6->sk : NULL);
+ wg_socket_reinit(wg, new4->sk, new6 ? new6->sk : NULL);
return 0;
}
diff --git a/src/tests/qemu/Makefile b/src/tests/qemu/Makefile
index 02e098a..55aea6a 100644
--- a/src/tests/qemu/Makefile
+++ b/src/tests/qemu/Makefile
@@ -6,7 +6,7 @@ PWD := $(shell pwd)
CHOST := $(shell gcc -dumpmachine)
ifneq (,$(ARCH))
-CBUILD := $(subst -gcc,,$(lastword $(subst /, ,$(firstword $(filter-out android,$(wildcard /usr/bin/$(ARCH)-*-gcc))))))
+CBUILD := $(subst -gcc,,$(lastword $(subst /, ,$(firstword $(filter-out android,$(wildcard $(foreach bindir,$(subst :, ,$(PATH)),$(bindir)/$(ARCH)-*-gcc)))))))
endif
ifeq (,$(CBUILD))
CBUILD := $(CHOST)
@@ -14,7 +14,7 @@ endif
ARCH := $(firstword $(subst -, ,$(CBUILD)))
# Set these from the environment to override
-KERNEL_VERSION ?= 5.3.5
+KERNEL_VERSION ?= 5.4
KERNEL_VERSION := $(KERNEL_VERSION)$(if $(DEBUG_KERNEL),$(if $(findstring -debug,$(KERNEL_VERSION)),,-debug),)
BUILD_PATH ?= $(PWD)/../../../qemu-build/$(ARCH)
DISTFILES_PATH ?= $(PWD)/distfiles
diff --git a/src/tests/qemu/arch/powerpc64le.config b/src/tests/qemu/arch/powerpc64le.config
index b28b547..990c510 100644
--- a/src/tests/qemu/arch/powerpc64le.config
+++ b/src/tests/qemu/arch/powerpc64le.config
@@ -3,6 +3,7 @@ CONFIG_PPC_PSERIES=y
CONFIG_ALTIVEC=y
CONFIG_VSX=y
CONFIG_PPC_OF_BOOT_TRAMPOLINE=y
+CONFIG_PPC_RADIX_MMU=y
CONFIG_HVC_CONSOLE=y
CONFIG_CPU_LITTLE_ENDIAN=y
CONFIG_CMDLINE_BOOL=y
diff --git a/src/tools/ipc.c b/src/tools/ipc.c
index 89484b1..7207efc 100644
--- a/src/tools/ipc.c
+++ b/src/tools/ipc.c
@@ -303,8 +303,12 @@ static int userspace_get_device(struct wgdevice **out, const char *iface)
return -errno;
f = userspace_interface_file(iface);
- if (!f)
- return -errno;
+ if (!f) {
+ ret = -errno;
+ free(dev);
+ *out = NULL;
+ return ret;
+ }
fprintf(f, "get=1\n\n");
fflush(f);
@@ -314,11 +318,8 @@ static int userspace_get_device(struct wgdevice **out, const char *iface)
while (getline(&key, &line_buffer_len, f) > 0) {
line_len = strlen(key);
- if (line_len == 1 && key[0] == '\n') {
- free(key);
- fclose(f);
- return ret;
- }
+ if (line_len == 1 && key[0] == '\n')
+ goto err;
value = strchr(key, '=');
if (!value || line_len == 0 || key[line_len - 1] != '\n')
break;
@@ -382,7 +383,7 @@ static int userspace_get_device(struct wgdevice **out, const char *iface)
*end++ = '\0';
}
if (getaddrinfo(begin, end, &hints, &resolved) != 0) {
- errno = ENETUNREACH;
+ ret = ENETUNREACH;
goto err;
}
if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||
@@ -437,8 +438,10 @@ static int userspace_get_device(struct wgdevice **out, const char *iface)
ret = -EPROTO;
err:
free(key);
- free_wgdevice(dev);
- *out = NULL;
+ if (ret) {
+ free_wgdevice(dev);
+ *out = NULL;
+ }
fclose(f);
errno = -ret;
return ret;
diff --git a/src/tools/man/wg.8 b/src/tools/man/wg.8
index afff749..ac281bc 100644
--- a/src/tools/man/wg.8
+++ b/src/tools/man/wg.8
@@ -94,6 +94,14 @@ Appends the contents of \fI<configuration-filename>\fP, which must
be in the format described by \fICONFIGURATION FILE FORMAT\fP below,
to the current configuration of \fI<interface>\fP.
.TP
+\fBsyncconf\fP \fI<interface>\fP \fI<configuration-filename>\fP
+Like \fBsetconf\fP, but reads back the existing configuration first
+and only makes changes that are explicitly different between the configuration
+file and the interface. This is much less efficient than \fBsetconf\fP,
+but has the benefit of not disrupting current peer sessions. The contents of
+\fI<configuration-filename>\fP must be in the format described by
+\fICONFIGURATION FILE FORMAT\fP below.
+.TP
\fBgenkey\fP
Generates a random \fIprivate\fP key in base64 and prints it to
standard output.
@@ -114,7 +122,7 @@ A private key and a corresponding public key may be generated at once by calling
$ wg genkey | tee private.key | wg pubkey > public.key
.TP
\fBhelp\fP
-Show usage message.
+Shows usage message.
.SH CONFIGURATION FILE FORMAT
The configuration file format is based on \fIINI\fP. There are two top level sections
diff --git a/src/tools/setconf.c b/src/tools/setconf.c
index 8211ebd..f778f40 100644
--- a/src/tools/setconf.c
+++ b/src/tools/setconf.c
@@ -13,6 +13,91 @@
#include "ipc.h"
#include "subcommands.h"
+struct pubkey_origin {
+ uint8_t *pubkey;
+ bool from_file;
+};
+
+static int pubkey_cmp(const void *first, const void *second)
+{
+ const struct pubkey_origin *a = first, *b = second;
+ int ret = memcmp(a->pubkey, b->pubkey, WG_KEY_LEN);
+ if (ret)
+ return ret;
+ return a->from_file - b->from_file;
+}
+
+static bool sync_conf(struct wgdevice *file)
+{
+ struct wgdevice *runtime;
+ struct wgpeer *peer;
+ struct pubkey_origin *pubkeys;
+ size_t peer_count = 0, i = 0;
+
+ if (!file->first_peer)
+ return true;
+
+ for_each_wgpeer(file, peer)
+ ++peer_count;
+
+ if (ipc_get_device(&runtime, file->name) != 0) {
+ perror("Unable to retrieve current interface configuration");
+ return false;
+ }
+
+ if (!runtime->first_peer) {
+ free_wgdevice(runtime);
+ return true;
+ }
+
+ file->flags &= ~WGDEVICE_REPLACE_PEERS;
+
+ for_each_wgpeer(runtime, peer)
+ ++peer_count;
+
+ pubkeys = calloc(peer_count, sizeof(*pubkeys));
+ if (!pubkeys) {
+ free_wgdevice(runtime);
+ perror("Public key allocation");
+ return false;
+ }
+
+ for_each_wgpeer(file, peer) {
+ pubkeys[i].pubkey = peer->public_key;
+ pubkeys[i].from_file = true;
+ ++i;
+ }
+ for_each_wgpeer(runtime, peer) {
+ pubkeys[i].pubkey = peer->public_key;
+ pubkeys[i].from_file = false;
+ ++i;
+ }
+ qsort(pubkeys, peer_count, sizeof(*pubkeys), pubkey_cmp);
+
+ for (i = 0; i < peer_count; ++i) {
+ if (pubkeys[i].from_file)
+ continue;
+ if (i == peer_count - 1 || !pubkeys[i + 1].from_file || memcmp(pubkeys[i].pubkey, pubkeys[i + 1].pubkey, WG_KEY_LEN)) {
+ peer = calloc(1, sizeof(struct wgpeer));
+ if (!peer) {
+ free_wgdevice(runtime);
+ free(pubkeys);
+ perror("Peer allocation");
+ return false;
+ }
+ peer->flags = WGPEER_REMOVE_ME;
+ memcpy(peer->public_key, pubkeys[i].pubkey, WG_KEY_LEN);
+ peer->next_peer = file->first_peer;
+ file->first_peer = peer;
+ if (!file->last_peer)
+ file->last_peer = peer;
+ }
+ }
+ free_wgdevice(runtime);
+ free(pubkeys);
+ return true;
+}
+
int setconf_main(int argc, char *argv[])
{
struct wgdevice *device = NULL;
@@ -50,6 +135,11 @@ int setconf_main(int argc, char *argv[])
strncpy(device->name, argv[1], IFNAMSIZ - 1);
device->name[IFNAMSIZ - 1] = '\0';
+ if (!strcmp(argv[0], "syncconf")) {
+ if (!sync_conf(device))
+ goto cleanup;
+ }
+
if (ipc_set_device(device) != 0) {
perror("Unable to modify interface");
goto cleanup;
diff --git a/src/tools/wg-quick/android.c b/src/tools/wg-quick/android.c
index fefbe38..ad05895 100644
--- a/src/tools/wg-quick/android.c
+++ b/src/tools/wg-quick/android.c
@@ -358,15 +358,18 @@ static __attribute__((__constructor__(65535))) void load_symbols(void)
static void cleanup_binder(AIBinder **binder)
{
- AIBinder_decStrong(*binder);
+ if (*binder)
+ AIBinder_decStrong(*binder);
}
static void cleanup_status(AStatus **status)
{
- AStatus_delete(*status);
+ if (*status)
+ AStatus_delete(*status);
}
static void cleanup_parcel(AParcel **parcel)
{
- AParcel_delete(*parcel);
+ if (*parcel)
+ AParcel_delete(*parcel);
}
#define _cleanup_status_ __attribute__((__cleanup__(cleanup_status)))
@@ -387,10 +390,10 @@ static int32_t string_array_size(char *const *array)
return size;
}
-static const char *string_array_getter(const void *array_data, size_t index, int32_t *outlength)
+static const char *string_array_getter(const void *array_data, size_t index, int32_t *out_length)
{
const char **array = (const char **)array_data;
- *outlength = array[index] ? strlen(array[index]) : -1;
+ *out_length = array[index] ? strlen(array[index]) : -1;
return array[index];
}
diff --git a/src/tools/wg-quick/linux.bash b/src/tools/wg-quick/linux.bash
index e690944..e9c9052 100755
--- a/src/tools/wg-quick/linux.bash
+++ b/src/tools/wg-quick/linux.bash
@@ -95,17 +95,18 @@ add_if() {
del_if() {
local table
[[ $HAVE_SET_DNS -eq 0 ]] || unset_dns
+ [[ $HAVE_SET_FIREWALL -eq 0 ]] || remove_firewall
if [[ -z $TABLE || $TABLE == auto ]] && get_fwmark table && [[ $(wg show "$INTERFACE" allowed-ips) =~ /0(\ |$'\n'|$) ]]; then
- while [[ $(ip -4 rule show) == *"lookup $table"* ]]; do
+ while [[ $(ip -4 rule show 2>/dev/null) == *"lookup $table"* ]]; do
cmd ip -4 rule delete table $table
done
- while [[ $(ip -4 rule show) == *"from all lookup main suppress_prefixlength 0"* ]]; do
+ while [[ $(ip -4 rule show 2>/dev/null) == *"from all lookup main suppress_prefixlength 0"* ]]; do
cmd ip -4 rule delete table main suppress_prefixlength 0
done
- while [[ $(ip -6 rule show) == *"lookup $table"* ]]; do
+ while [[ $(ip -6 rule show 2>/dev/null) == *"lookup $table"* ]]; do
cmd ip -6 rule delete table $table
done
- while [[ $(ip -6 rule show) == *"from all lookup main suppress_prefixlength 0"* ]]; do
+ while [[ $(ip -6 rule show 2>/dev/null) == *"from all lookup main suppress_prefixlength 0"* ]]; do
cmd ip -6 rule delete table main suppress_prefixlength 0
done
fi
@@ -180,23 +181,64 @@ get_fwmark() {
return 0
}
+remove_firewall() {
+ if type -p nft >/dev/null; then
+ local table nftcmd
+ while read -r table; do
+ [[ $table == *" wg-quick-$INTERFACE" ]] && printf -v nftcmd '%sdelete %s\n' "$nftcmd" "$table"
+ done < <(nft list tables 2>/dev/null)
+ [[ -z $nftcmd ]] || cmd nft -f <(echo -n "$nftcmd")
+ fi
+ if type -p iptables >/dev/null; then
+ local line iptables found restore
+ for iptables in iptables ip6tables; do
+ restore="" found=0
+ while read -r line; do
+ [[ $line == "*"* || $line == COMMIT || $line == "-A "*"-m comment --comment \"wg-quick(8) rule for $INTERFACE\""* ]] || continue
+ [[ $line == "-A"* ]] && found=1
+ printf -v restore '%s%s\n' "$restore" "${line/#-A/-D}"
+ done < <($iptables-save 2>/dev/null)
+ [[ $found -ne 1 ]] || echo -n "$restore" | cmd $iptables-restore -n
+ done
+ fi
+}
+
+HAVE_SET_FIREWALL=0
add_default() {
- local table proto key value
+ local table line
if ! get_fwmark table; then
table=51820
- while [[ -n $(ip -4 route show table $table) || -n $(ip -6 route show table $table) ]]; do
+ while [[ -n $(ip -4 route show table $table 2>/dev/null) || -n $(ip -6 route show table $table 2>/dev/null) ]]; do
((table++))
done
cmd wg set "$INTERFACE" fwmark $table
fi
- proto=-4
- [[ $1 == *:* ]] && proto=-6
+ local proto=-4 iptables=iptables pf=ip
+ [[ $1 == *:* ]] && proto=-6 iptables=ip6tables pf=ip6
cmd ip $proto route add "$1" dev "$INTERFACE" table $table
cmd ip $proto rule add not fwmark $table table $table
cmd ip $proto rule add table main suppress_prefixlength 0
- while read -r key _ value; do
- [[ $value -eq 1 ]] && sysctl -q "$key=2"
- done < <(sysctl -a -r '^net\.ipv4.conf\.[^ .=]+\.rp_filter$')
+
+ local marker="-m comment --comment \"wg-quick(8) rule for $INTERFACE\"" restore=$'*raw\n' nftable="wg-quick-$INTERFACE" nftcmd
+ printf -v nftcmd '%sadd table %s %s\n' "$nftcmd" "$pf" "$nftable"
+ printf -v nftcmd '%sadd chain %s %s preraw { type filter hook prerouting priority -300; }\n' "$nftcmd" "$pf" "$nftable"
+ printf -v nftcmd '%sadd chain %s %s premangle { type filter hook prerouting priority -150; }\n' "$nftcmd" "$pf" "$nftable"
+ printf -v nftcmd '%sadd chain %s %s postmangle { type filter hook postrouting priority -150; }\n' "$nftcmd" "$pf" "$nftable"
+ while read -r line; do
+ [[ $line =~ .*inet6?\ ([0-9a-f:.]+)/[0-9]+.* ]] || continue
+ printf -v restore '%s-I PREROUTING ! -i %s -d %s -m addrtype ! --src-type LOCAL -j DROP %s\n' "$restore" "$INTERFACE" "${BASH_REMATCH[1]}" "$marker"
+ printf -v nftcmd '%sadd rule %s %s preraw iifname != %s %s daddr %s fib saddr type != local drop\n' "$nftcmd" "$pf" "$nftable" "$INTERFACE" "$pf" "${BASH_REMATCH[1]}"
+ done < <(ip -o $proto addr show dev "$INTERFACE" 2>/dev/null)
+ printf -v restore '%sCOMMIT\n*mangle\n-I POSTROUTING -m mark --mark %d -p udp -j CONNMARK --save-mark %s\n-I PREROUTING -p udp -j CONNMARK --restore-mark %s\nCOMMIT\n' "$restore" $table "$marker" "$marker"
+ printf -v nftcmd '%sadd rule %s %s postmangle meta l4proto udp mark %d ct mark set mark \n' "$nftcmd" "$pf" "$nftable" $table
+ printf -v nftcmd '%sadd rule %s %s premangle meta l4proto udp meta mark set ct mark \n' "$nftcmd" "$pf" "$nftable"
+ [[ $proto == -4 ]] && cmd sysctl -q net.ipv4.conf.all.src_valid_mark=1
+ if type -p nft >/dev/null; then
+ cmd nft -f <(echo -n "$nftcmd")
+ else
+ echo -n "$restore" | cmd $iptables-restore -n
+ fi
+ HAVE_SET_FIREWALL=1
return 0
}
@@ -300,7 +342,8 @@ cmd_down() {
execute_hooks "${PRE_DOWN[@]}"
[[ $SAVE_CONFIG -eq 0 ]] || save_config
del_if
- unset_dns
+ unset_dns || true
+ remove_firewall || true
execute_hooks "${POST_DOWN[@]}"
}
diff --git a/src/tools/wg.c b/src/tools/wg.c
index 550d9b4..7b5d3af 100644
--- a/src/tools/wg.c
+++ b/src/tools/wg.c
@@ -21,6 +21,7 @@ static const struct {
{ "set", set_main, "Change the current configuration, add peers, remove peers, or change peers" },
{ "setconf", setconf_main, "Applies a configuration file to a WireGuard interface" },
{ "addconf", setconf_main, "Appends a configuration file to a WireGuard interface" },
+ { "syncconf", setconf_main, "Synchronizes a configuration file to a WireGuard interface" },
{ "genkey", genkey_main, "Generates a new private key and writes it to stdout" },
{ "genpsk", genkey_main, "Generates a new preshared key and writes it to stdout" },
{ "pubkey", pubkey_main, "Reads a private key from stdin and writes a public key to stdout" }
diff --git a/src/uapi/wireguard.h b/src/uapi/wireguard.h
index dd8a47c..ae88be1 100644
--- a/src/uapi/wireguard.h
+++ b/src/uapi/wireguard.h
@@ -18,13 +18,13 @@
* one but not both of:
*
* WGDEVICE_A_IFINDEX: NLA_U32
- * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1
+ * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1
*
* The kernel will then return several messages (NLM_F_MULTI) containing the
* following tree of nested items:
*
* WGDEVICE_A_IFINDEX: NLA_U32
- * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1
+ * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1
* WGDEVICE_A_PRIVATE_KEY: NLA_EXACT_LEN, len WG_KEY_LEN
* WGDEVICE_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN
* WGDEVICE_A_LISTEN_PORT: NLA_U16
@@ -77,7 +77,7 @@
* WGDEVICE_A_IFINDEX and WGDEVICE_A_IFNAME:
*
* WGDEVICE_A_IFINDEX: NLA_U32
- * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1
+ * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1
* WGDEVICE_A_FLAGS: NLA_U32, 0 or WGDEVICE_F_REPLACE_PEERS if all current
* peers should be removed prior to adding the list below.
* WGDEVICE_A_PRIVATE_KEY: len WG_KEY_LEN, all zeros to remove
@@ -121,7 +121,7 @@
* filling in information not contained in the prior. Note that if
* WGDEVICE_F_REPLACE_PEERS is specified in the first message, it probably
* should not be specified in fragments that come after, so that the list
- * of peers is only cleared the first time but appened after. Likewise for
+ * of peers is only cleared the first time but appended after. Likewise for
* peers, if WGPEER_F_REPLACE_ALLOWEDIPS is specified in the first message
* of a peer, it likely should not be specified in subsequent fragments.
*
diff --git a/src/version.h b/src/version.h
index cee4e8d..ddba103 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1 +1 @@
-#define WIREGUARD_VERSION "0.0.20190913"
+#define WIREGUARD_VERSION "0.0.20191219"