diff options
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" @@ -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); @@ -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" |