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/Kbuild1
-rw-r--r--src/Kconfig3
-rw-r--r--src/Makefile5
-rw-r--r--src/allowedips.c16
-rw-r--r--src/compat/Kbuild.include6
-rw-r--r--src/compat/compat-asm.h5
-rw-r--r--src/compat/compat.h52
-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.c27
-rw-r--r--src/noise.c8
-rw-r--r--src/peer.c11
-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
-rwxr-xr-xsrc/tests/netns.sh22
-rw-r--r--src/tests/qemu/Makefile4
-rw-r--r--src/tests/qemu/arch/powerpc64le.config1
-rw-r--r--src/tests/qemu/kernel.config3
-rw-r--r--src/tools/ipc.c55
-rw-r--r--src/tools/man/wg.810
-rw-r--r--src/tools/setconf.c90
-rw-r--r--src/tools/wg-quick/android.c439
-rwxr-xr-xsrc/tools/wg-quick/linux.bash69
-rwxr-xr-xsrc/tools/wg-quick/openbsd.bash2
-rw-r--r--src/tools/wg.c1
-rw-r--r--src/tools/wincompat/ipc.c30
-rw-r--r--src/uapi/wireguard.h22
-rw-r--r--src/version.h2
43 files changed, 1035 insertions, 278 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/Kbuild b/src/Kbuild
index f84b4de..920797e 100644
--- a/src/Kbuild
+++ b/src/Kbuild
@@ -5,6 +5,7 @@
ccflags-y := -O3 -fvisibility=hidden
ccflags-$(CONFIG_WIREGUARD_DEBUG) += -DDEBUG -g
ccflags-y += -D'pr_fmt(fmt)=KBUILD_MODNAME ": " fmt'
+ccflags-y += -Wframe-larger-than=2048
wireguard-y := main.o noise.o device.o peer.o timers.o queueing.o send.o receive.o socket.o peerlookup.o allowedips.o ratelimiter.o cookie.o netlink.o
diff --git a/src/Kconfig b/src/Kconfig
index 3a68884..156e9db 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -4,7 +4,8 @@ config WIREGUARD
depends on IPV6 || !IPV6
select NET_UDP_TUNNEL
select DST_CACHE
- select CRYPTO_BLKCIPHER
+ select CRYPTO
+ select CRYPTO_ALGAPI
select VFP
select VFPv3 if CPU_V7
select NEON if CPU_V7
diff --git a/src/Makefile b/src/Makefile
index 24b2ba0..8226038 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -2,7 +2,8 @@
#
# Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
-KERNELDIR ?= /lib/modules/$(shell uname -r)/build
+KERNELRELEASE ?= $(shell uname -r)
+KERNELDIR ?= /lib/modules/$(KERNELRELEASE)/build
PREFIX ?= /usr
DESTDIR ?=
SRCDIR ?= $(PREFIX)/src
@@ -45,7 +46,7 @@ clean:
module-install:
@$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
- $(DEPMOD) -a
+ $(DEPMOD) -a $(KERNELRELEASE)
install: module-install tools-install
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/Kbuild.include b/src/compat/Kbuild.include
index 1b555f8..db4b0a6 100644
--- a/src/compat/Kbuild.include
+++ b/src/compat/Kbuild.include
@@ -47,9 +47,15 @@ ccflags-y += -include $(kbuild-dir)/compat/memneq/include.h
wireguard-y += compat/memneq/memneq.o
endif
+ifeq ($(shell grep -s -F "addr_gen_mode" "$(srctree)/include/linux/ipv6.h"),)
+ccflags-y += -DCOMPAT_CANNOT_USE_DEV_CNF
+endif
+
+ifdef CONFIG_HZ
ifeq ($(wildcard $(srctree)/include/generated/timeconst.h),)
ccflags-y += $(shell echo 'define gcd(a,b){auto t;while(b){t=b;b=a%b;a=t;};return a;};hz=$(CONFIG_HZ);cd=gcd(hz,1000000);print "-DHZ_TO_USEC_NUM=",1000000/cd," -DHZ_TO_USEC_DEN=",hz/cd;halt;' | bc -q)
endif
+endif
ifeq ($(wildcard $(srctree)/arch/arm/include/asm/neon.h)$(CONFIG_ARM),y)
ccflags-y += -I$(kbuild-dir)/compat/neon-arm/include
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 6288550..824f57c 100644
--- a/src/compat/compat.h
+++ b/src/compat/compat.h
@@ -21,6 +21,8 @@
#ifdef UTS_UBUNTU_RELEASE_ABI
#if LINUX_VERSION_CODE == KERNEL_VERSION(3, 13, 11)
#define ISUBUNTU1404
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)
+#define ISUBUNTU1604
#endif
#endif
#ifdef CONFIG_SUSE_KERNEL
@@ -45,6 +47,7 @@
#endif
#include <linux/cache.h>
+#include <linux/init.h>
#ifndef __ro_after_init
#define __ro_after_init __read_mostly
#endif
@@ -87,7 +90,7 @@
(LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 27) && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) || \
(LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 8) && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) || \
(LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 40) && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)) || \
- (LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 54))) && !defined(ISUBUNTU1404)
+ (LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 54))) && !defined(ISUBUNTU1404) && (!defined(ISRHEL7) || RHEL_MINOR < 7) /* TODO: remove < 7 workaround once CentOS 7.7 comes out. */
#include <linux/if.h>
#include <net/ip_tunnels.h>
#define IP6_ECN_set_ce(a, b) IP6_ECN_set_ce(b)
@@ -323,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 {
@@ -600,10 +603,6 @@ static int wg_get_device_dump_real(a, b)
#define COMPAT_CANNOT_USE_IN6_DEV_GET
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
-#define COMPAT_CANNOT_USE_DEV_CNF
-#endif
-
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
#define COMPAT_CANNOT_USE_IFF_NO_QUEUE
#endif
@@ -832,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)
@@ -844,6 +843,45 @@ 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
+#define hsiphash_2u32 siphash_2u32
+#define hsiphash_3u32 siphash_3u32
+#define hsiphash_key_t siphash_key_t
+#endif
+#endif
+
+#ifdef CONFIG_VE
+#include <linux/netdev_features.h>
+#ifdef NETIF_F_VIRTUAL
+#undef NETIF_F_LLTX
+#define NETIF_F_LLTX (__NETIF_F(LLTX) | __NETIF_F(VIRTUAL))
+#endif
+#endif
+
/* https://github.com/ClangBuiltLinux/linux/issues/7 */
#if defined( __clang__) && (!defined(CONFIG_CLANG_VERSION) || CONFIG_CLANG_VERSION < 80000)
#include <linux/bug.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 f72841c..cdcd2e7 100644
--- a/src/dkms.conf
+++ b/src/dkms.conf
@@ -1,5 +1,5 @@
PACKAGE_NAME="wireguard"
-PACKAGE_VERSION="0.0.20190702"
+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 eb94f4d..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;
@@ -389,10 +383,10 @@ static int set_peer(struct wg_device *wg, struct nlattr **attrs)
peer = wg_pubkey_hashtable_lookup(wg->peer_hashtable,
nla_data(attrs[WGPEER_A_PUBLIC_KEY]));
+ ret = 0;
if (!peer) { /* Peer doesn't exist yet. Add a new one. */
- ret = -ENODEV;
- if (flags & WGPEER_F_REMOVE_ME)
- goto out; /* Tried to remove a non-existing peer. */
+ if (flags & (WGPEER_F_REMOVE_ME | WGPEER_F_UPDATE_ONLY))
+ goto out;
/* The peer is new, so there aren't allowed IPs to remove. */
flags &= ~WGPEER_F_REPLACE_ALLOWEDIPS;
@@ -413,17 +407,22 @@ static int set_peer(struct wg_device *wg, struct nlattr **attrs)
}
up_read(&wg->static_identity.lock);
- ret = -ENOMEM;
peer = wg_peer_create(wg, public_key, preshared_key);
- if (!peer)
+ if (IS_ERR(peer)) {
+ /* Similar to the above, if the key is invalid, we skip
+ * it without fanfare, so that services don't need to
+ * worry about doing key validation themselves.
+ */
+ ret = PTR_ERR(peer) == -EKEYREJECTED ? 0 : PTR_ERR(peer);
+ peer = NULL;
goto out;
+ }
/* Take additional reference, as though we've just been
* looked up.
*/
wg_peer_get(peer);
}
- ret = 0;
if (flags & WGPEER_F_REMOVE_ME) {
wg_peer_remove(peer);
goto out;
diff --git a/src/noise.c b/src/noise.c
index 57f749c..269b69f 100644
--- a/src/noise.c
+++ b/src/noise.c
@@ -566,6 +566,7 @@ wg_noise_handshake_consume_initiation(struct message_handshake_initiation *src,
u8 s[NOISE_PUBLIC_KEY_LEN];
u8 e[NOISE_PUBLIC_KEY_LEN];
u8 t[NOISE_TIMESTAMP_LEN];
+ u64 initiation_consumption;
down_read(&wg->static_identity.lock);
if (unlikely(!wg->static_identity.has_identity))
@@ -614,11 +615,14 @@ wg_noise_handshake_consume_initiation(struct message_handshake_initiation *src,
/* Success! Copy everything to peer */
down_write(&handshake->lock);
memcpy(handshake->remote_ephemeral, e, NOISE_PUBLIC_KEY_LEN);
- memcpy(handshake->latest_timestamp, t, NOISE_TIMESTAMP_LEN);
+ if (memcmp(t, handshake->latest_timestamp, NOISE_TIMESTAMP_LEN) > 0)
+ memcpy(handshake->latest_timestamp, t, NOISE_TIMESTAMP_LEN);
memcpy(handshake->hash, hash, NOISE_HASH_LEN);
memcpy(handshake->chaining_key, chaining_key, NOISE_HASH_LEN);
handshake->remote_index = src->sender_index;
- handshake->last_initiation_consumption = ktime_get_coarse_boottime_ns();
+ if ((s64)(handshake->last_initiation_consumption -
+ (initiation_consumption = ktime_get_coarse_boottime_ns())) < 0)
+ handshake->last_initiation_consumption = initiation_consumption;
handshake->state = HANDSHAKE_CONSUMED_INITIATION;
up_write(&handshake->lock);
ret_peer = peer;
diff --git a/src/peer.c b/src/peer.c
index ffb911f..071eedf 100644
--- a/src/peer.c
+++ b/src/peer.c
@@ -22,20 +22,23 @@ struct wg_peer *wg_peer_create(struct wg_device *wg,
const u8 preshared_key[NOISE_SYMMETRIC_KEY_LEN])
{
struct wg_peer *peer;
+ int ret = -ENOMEM;
lockdep_assert_held(&wg->device_update_lock);
if (wg->num_peers >= MAX_PEERS_PER_DEVICE)
- return NULL;
+ return ERR_PTR(ret);
peer = kzalloc(sizeof(*peer), GFP_KERNEL);
if (unlikely(!peer))
- return NULL;
+ return ERR_PTR(ret);
peer->device = wg;
if (!wg_noise_handshake_init(&peer->handshake, &wg->static_identity,
- public_key, preshared_key, peer))
+ public_key, preshared_key, peer)) {
+ ret = -EKEYREJECTED;
goto err_1;
+ }
if (dst_cache_init(&peer->endpoint_cache, GFP_KERNEL))
goto err_1;
if (wg_packet_queue_init(&peer->tx_queue, wg_packet_tx_worker, false,
@@ -74,7 +77,7 @@ err_2:
dst_cache_destroy(&peer->endpoint_cache);
err_1:
kfree(peer);
- return NULL;
+ return ERR_PTR(ret);
}
struct wg_peer *wg_peer_get_maybe_zero(struct wg_peer *peer)
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/netns.sh b/src/tests/netns.sh
index 9edf12b..e7310d9 100755
--- a/src/tests/netns.sh
+++ b/src/tests/netns.sh
@@ -241,7 +241,7 @@ ip2 link del wg0
# │ ┌─────┐ ┌─────┐ │ │ ┌──────┐ ┌──────┐ │ │ ┌─────┐ ┌─────┐ │
# │ │ wg0 │─────────────│vethc│───────────┼────┼────│vethrc│ │vethrs│──────────────┼─────┼──│veths│────────────│ wg0 │ │
# │ ├─────┴──────────┐ ├─────┴──────────┐│ │ ├──────┴─────────┐ ├──────┴────────────┐ │ │ ├─────┴──────────┐ ├─────┴──────────┐ │
-# │ │192.168.241.1/24│ │192.168.1.100/24││ │ │192.168.1.100/24│ │10.0.0.1/24 │ │ │ │10.0.0.100/24 │ │192.168.241.2/24│ │
+# │ │192.168.241.1/24│ │192.168.1.100/24││ │ │192.168.1.1/24 │ │10.0.0.1/24 │ │ │ │10.0.0.100/24 │ │192.168.241.2/24│ │
# │ │fd00::1/24 │ │ ││ │ │ │ │SNAT:192.168.1.0/24│ │ │ │ │ │fd00::2/24 │ │
# │ └────────────────┘ └────────────────┘│ │ └────────────────┘ └───────────────────┘ │ │ └────────────────┘ └────────────────┘ │
# └────────────────────────────────────────┘ └────────────────────────────────────────────────┘ └────────────────────────────────────────┘
@@ -280,6 +280,26 @@ n2 ping -W 1 -c 1 192.168.241.1
# Demonstrate n2 can still send packets to n1, since persistent-keepalive will prevent connection tracking entry from expiring (to see entries: `n0 conntrack -L`).
pp sleep 3
n2 ping -W 1 -c 1 192.168.241.1
+n1 wg set wg0 peer "$pub2" persistent-keepalive 0
+
+# Do a wg-quick(8)-style policy routing for the default route, making sure vethc has a v6 address to tease out bugs.
+ip1 -6 addr add fc00::9/96 dev vethc
+ip1 -6 route add default via fc00::1
+ip2 -4 addr add 192.168.99.7/32 dev wg0
+ip2 -6 addr add abab::1111/128 dev wg0
+n1 wg set wg0 fwmark 51820 peer "$pub2" allowed-ips 192.168.99.7,abab::1111
+ip1 -6 route add default dev wg0 table 51820
+ip1 -6 rule add not fwmark 51820 table 51820
+ip1 -6 rule add table main suppress_prefixlength 0
+ip1 -4 route add default dev wg0 table 51820
+ip1 -4 rule add not fwmark 51820 table 51820
+ip1 -4 rule add table main suppress_prefixlength 0
+# suppress_prefixlength only got added in 3.12, and we want to support 3.10+.
+if [[ $(ip1 -4 rule show all) == *suppress_prefixlength* ]]; then
+ # Flood the pings instead of sending just one, to trigger routing table reference counting bugs.
+ n1 ping -W 1 -c 100 -f 192.168.99.7
+ n1 ping -W 1 -c 100 -f abab::1111
+fi
n0 iptables -t nat -F
ip0 link del vethrc
diff --git a/src/tests/qemu/Makefile b/src/tests/qemu/Makefile
index acae128..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.1.15
+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/tests/qemu/kernel.config b/src/tests/qemu/kernel.config
index 85e7d20..0458314 100644
--- a/src/tests/qemu/kernel.config
+++ b/src/tests/qemu/kernel.config
@@ -22,6 +22,9 @@ CONFIG_NF_NAT_IPV4=y
CONFIG_IP_NF_IPTABLES=y
CONFIG_IP_NF_FILTER=y
CONFIG_IP_NF_NAT=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_TTY=y
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_SCRIPT=y
diff --git a/src/tools/ipc.c b/src/tools/ipc.c
index e0be413..7207efc 100644
--- a/src/tools/ipc.c
+++ b/src/tools/ipc.c
@@ -96,7 +96,7 @@ static int add_next_to_inflatable_buffer(struct inflatable_buffer *buffer)
}
#ifndef WINCOMPAT
-static FILE *userspace_interface_file(const char *interface)
+static FILE *userspace_interface_file(const char *iface)
{
struct stat sbuf;
struct sockaddr_un addr = { .sun_family = AF_UNIX };
@@ -104,9 +104,9 @@ static FILE *userspace_interface_file(const char *interface)
FILE *f = NULL;
errno = EINVAL;
- if (strchr(interface, '/'))
+ if (strchr(iface, '/'))
goto out;
- ret = snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, interface);
+ ret = snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, iface);
if (ret < 0)
goto out;
ret = stat(addr.sun_path, &sbuf);
@@ -140,15 +140,15 @@ out:
return f;
}
-static bool userspace_has_wireguard_interface(const char *interface)
+static bool userspace_has_wireguard_interface(const char *iface)
{
struct stat sbuf;
struct sockaddr_un addr = { .sun_family = AF_UNIX };
int fd, ret;
- if (strchr(interface, '/'))
+ if (strchr(iface, '/'))
return false;
- if (snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, interface) < 0)
+ if (snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, iface) < 0)
return false;
if (stat(addr.sun_path, &sbuf) < 0)
return false;
@@ -288,7 +288,7 @@ static int userspace_set_device(struct wgdevice *dev)
num; \
})
-static int userspace_get_device(struct wgdevice **out, const char *interface)
+static int userspace_get_device(struct wgdevice **out, const char *iface)
{
struct wgdevice *dev;
struct wgpeer *peer = NULL;
@@ -302,23 +302,24 @@ static int userspace_get_device(struct wgdevice **out, const char *interface)
if (!dev)
return -errno;
- f = userspace_interface_file(interface);
- if (!f)
- return -errno;
+ f = userspace_interface_file(iface);
+ if (!f) {
+ ret = -errno;
+ free(dev);
+ *out = NULL;
+ return ret;
+ }
fprintf(f, "get=1\n\n");
fflush(f);
- strncpy(dev->name, interface, IFNAMSIZ - 1);
+ strncpy(dev->name, iface, IFNAMSIZ - 1);
dev->name[IFNAMSIZ - 1] = '\0';
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 *interface)
*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 *interface)
ret = -EPROTO;
err:
free(key);
- free_wgdevice(dev);
- *out = NULL;
+ if (ret) {
+ free_wgdevice(dev);
+ *out = NULL;
+ }
fclose(f);
errno = -ret;
return ret;
@@ -889,7 +892,7 @@ static void coalesce_peers(struct wgdevice *device)
}
}
-static int kernel_get_device(struct wgdevice **device, const char *interface)
+static int kernel_get_device(struct wgdevice **device, const char *iface)
{
int ret = 0;
struct nlmsghdr *nlh;
@@ -908,7 +911,7 @@ try_again:
}
nlh = mnlg_msg_prepare(nlg, WG_CMD_GET_DEVICE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
- mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, interface);
+ mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, iface);
if (mnlg_socket_send(nlg, nlh) < 0) {
ret = -errno;
goto out;
@@ -963,14 +966,14 @@ cleanup:
return buffer.buffer;
}
-int ipc_get_device(struct wgdevice **dev, const char *interface)
+int ipc_get_device(struct wgdevice **dev, const char *iface)
{
#ifdef __linux__
- if (userspace_has_wireguard_interface(interface))
- return userspace_get_device(dev, interface);
- return kernel_get_device(dev, interface);
+ if (userspace_has_wireguard_interface(iface))
+ return userspace_get_device(dev, iface);
+ return kernel_get_device(dev, iface);
#else
- return userspace_get_device(dev, interface);
+ return userspace_get_device(dev, iface);
#endif
}
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 e8ea4b4..ad05895 100644
--- a/src/tools/wg-quick/android.c
+++ b/src/tools/wg-quick/android.c
@@ -20,6 +20,7 @@
#include <unistd.h>
#include <errno.h>
#include <regex.h>
+#include <dlfcn.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -37,6 +38,7 @@
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
static bool is_exiting = false;
+static bool binder_available = false;
static void *xmalloc(size_t size)
{
@@ -235,6 +237,376 @@ _printf_(1, 2) static void cndc(const char *cmd_fmt, ...)
}
}
+/* Values are from AOSP repository platform/frameworks/native in libs/binder/ndk/include_ndk/android/binder_status.h. */
+enum {
+ STATUS_OK = 0,
+ STATUS_UNKNOWN_ERROR = -2147483647 - 1,
+ STATUS_NO_MEMORY = -ENOMEM,
+ STATUS_INVALID_OPERATION = -ENOSYS,
+ STATUS_BAD_VALUE = -EINVAL,
+ STATUS_BAD_TYPE = STATUS_UNKNOWN_ERROR + 1,
+ STATUS_NAME_NOT_FOUND = -ENOENT,
+ STATUS_PERMISSION_DENIED = -EPERM,
+ STATUS_NO_INIT = -ENODEV,
+ STATUS_ALREADY_EXISTS = -EEXIST,
+ STATUS_DEAD_OBJECT = -EPIPE,
+ STATUS_FAILED_TRANSACTION = STATUS_UNKNOWN_ERROR + 2,
+ STATUS_BAD_INDEX = -EOVERFLOW,
+ STATUS_NOT_ENOUGH_DATA = -ENODATA,
+ STATUS_WOULD_BLOCK = -EWOULDBLOCK,
+ STATUS_TIMED_OUT = -ETIMEDOUT,
+ STATUS_UNKNOWN_TRANSACTION = -EBADMSG,
+ STATUS_FDS_NOT_ALLOWED = STATUS_UNKNOWN_ERROR + 7,
+ STATUS_UNEXPECTED_NULL = STATUS_UNKNOWN_ERROR + 8
+};
+enum {
+ EX_NONE = 0,
+ EX_SECURITY = -1,
+ EX_BAD_PARCELABLE = -2,
+ EX_ILLEGAL_ARGUMENT = -3,
+ EX_NULL_POINTER = -4,
+ EX_ILLEGAL_STATE = -5,
+ EX_NETWORK_MAIN_THREAD = -6,
+ EX_UNSUPPORTED_OPERATION = -7,
+ EX_SERVICE_SPECIFIC = -8,
+ EX_PARCELABLE = -9,
+ EX_TRANSACTION_FAILED = -129
+};
+enum {
+ FLAG_ONEWAY = 0x01,
+};
+enum {
+ FIRST_CALL_TRANSACTION = 0x00000001,
+ LAST_CALL_TRANSACTION = 0x00ffffff
+};
+struct AIBinder;
+struct AParcel;
+struct AStatus;
+struct AIBinder_Class;
+typedef struct AIBinder AIBinder;
+typedef struct AParcel AParcel;
+typedef struct AStatus AStatus;
+typedef struct AIBinder_Class AIBinder_Class;
+typedef int32_t binder_status_t;
+typedef int32_t binder_exception_t;
+typedef uint32_t transaction_code_t;
+typedef uint32_t binder_flags_t;
+typedef void *(*AIBinder_Class_onCreate)(void *args);
+typedef void (*AIBinder_Class_onDestroy)(void *userData);
+typedef binder_status_t (*AIBinder_Class_onTransact)(AIBinder *binder, transaction_code_t code, const AParcel *in, AParcel *out);
+typedef const char *(*AParcel_stringArrayElementGetter)(const void *arrayData, size_t index, int32_t *outLength);
+static AIBinder_Class *(*AIBinder_Class_define)(const char *interfaceDescriptor, AIBinder_Class_onCreate onCreate, AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact) __attribute__((warn_unused_result));
+static bool (*AIBinder_associateClass)(AIBinder *binder, const AIBinder_Class *clazz);
+static void (*AIBinder_decStrong)(AIBinder *binder);
+static binder_status_t (*AIBinder_prepareTransaction)(AIBinder *binder, AParcel **in);
+static binder_status_t (*AIBinder_transact)(AIBinder *binder, transaction_code_t code, AParcel **in, AParcel **out, binder_flags_t flags);
+static binder_status_t (*AIBinder_ping)(AIBinder *binder);
+static binder_status_t (*AIBinder_dump)(AIBinder *binder, int fd, const char **args, uint32_t numArgs);
+static binder_status_t (*AParcel_readStatusHeader)(const AParcel *parcel, AStatus **status);
+static binder_status_t (*AParcel_readBool)(const AParcel *parcel, bool *value);
+static void (*AParcel_delete)(AParcel *parcel);
+static binder_status_t (*AParcel_setDataPosition)(const AParcel *parcel, int32_t position);
+static int32_t (*AParcel_getDataPosition)(const AParcel *parcel);
+static binder_status_t (*AParcel_writeInt32)(AParcel *parcel, int32_t value);
+static binder_status_t (*AParcel_writeStringArray)(AParcel *parcel, const void *arrayData, int32_t length, AParcel_stringArrayElementGetter getter);
+static binder_status_t (*AParcel_writeString)(AParcel *parcel, const char *string, int32_t length);
+static bool (*AStatus_isOk)(const AStatus *status);
+static void (*AStatus_delete)(AStatus *status);
+static binder_exception_t (*AStatus_getExceptionCode)(const AStatus *status);
+static int32_t (*AStatus_getServiceSpecificError)(const AStatus *status);
+static const char* (*AStatus_getMessage)(const AStatus *status);
+static binder_status_t (*AStatus_getStatus)(const AStatus *status);
+static AIBinder *(*AServiceManager_getService)(const char *instance) __attribute__((__warn_unused_result__));
+
+static __attribute__((__constructor__(65535))) void load_symbols(void)
+{
+ void *handle = dlopen("libbinder_ndk.so", RTLD_LAZY);
+ binder_available = !!handle;
+ if (!binder_available)
+ return;
+
+#define X(symb) do { \
+ if (!((symb) = (typeof(symb))dlsym(handle, #symb))) { \
+ fprintf(stderr, "Error: unable to import " #symb " from libbinder_ndk.so\n"); \
+ exit(ELIBACC); \
+ } \
+ } while (0)
+ X(AIBinder_Class_define);
+ X(AIBinder_associateClass);
+ X(AIBinder_decStrong);
+ X(AIBinder_prepareTransaction);
+ X(AIBinder_transact);
+ X(AIBinder_ping);
+ X(AIBinder_dump);
+ X(AParcel_readStatusHeader);
+ X(AParcel_readBool);
+ X(AParcel_delete);
+ X(AParcel_setDataPosition);
+ X(AParcel_getDataPosition);
+ X(AParcel_writeInt32);
+ X(AParcel_writeStringArray);
+ X(AParcel_writeString);
+ X(AStatus_isOk);
+ X(AStatus_delete);
+ X(AStatus_getExceptionCode);
+ X(AStatus_getServiceSpecificError);
+ X(AStatus_getMessage);
+ X(AStatus_getStatus);
+ X(AServiceManager_getService);
+#undef X
+}
+
+static void cleanup_binder(AIBinder **binder)
+{
+ if (*binder)
+ AIBinder_decStrong(*binder);
+}
+static void cleanup_status(AStatus **status)
+{
+ if (*status)
+ AStatus_delete(*status);
+}
+static void cleanup_parcel(AParcel **parcel)
+{
+ if (*parcel)
+ AParcel_delete(*parcel);
+}
+
+#define _cleanup_status_ __attribute__((__cleanup__(cleanup_status)))
+#define _cleanup_parcel_ __attribute__((__cleanup__(cleanup_parcel)))
+#define _cleanup_binder_ __attribute__((__cleanup__(cleanup_binder)))
+
+static int32_t string_size(const char *str)
+{
+ return str ? strlen(str) : -1;
+}
+
+static int32_t string_array_size(char *const *array)
+{
+ int32_t size = -1;
+ if (!array)
+ return size;
+ for (size = 0; array[size]; ++size);
+ return size;
+}
+
+static const char *string_array_getter(const void *array_data, size_t index, int32_t *out_length)
+{
+ const char **array = (const char **)array_data;
+ *out_length = array[index] ? strlen(array[index]) : -1;
+ return array[index];
+}
+
+static binder_status_t meaningful_binder_status(const AStatus *status_out)
+{
+ binder_status_t status = STATUS_OK;
+ binder_exception_t exc_code;
+ int32_t exc_code_service;
+ const char *message;
+
+ if (!AStatus_isOk(status_out)) {
+ exc_code = AStatus_getExceptionCode(status_out);
+ if (exc_code == EX_TRANSACTION_FAILED) {
+ status = AStatus_getStatus(status_out);
+ fprintf(stderr, "Error: transaction failed: %d\n", status);
+ }
+ else {
+ message = AStatus_getMessage(status_out);
+
+ if (exc_code == EX_SERVICE_SPECIFIC) {
+ exc_code_service = AStatus_getServiceSpecificError(status_out);
+ fprintf(stderr, "Error: service specific exception code: %d%s%s\n", exc_code_service, message ? ": " : "", message ?: "");
+ }
+ else
+ fprintf(stderr, "Error: exception code: %d%s%s\n", exc_code, message ? ": " : "", message ?: "");
+
+ status = STATUS_FAILED_TRANSACTION;
+ }
+ }
+
+ return status;
+}
+
+/* These values are default values observed in AOSP. */
+enum {
+ DNSRESOLVER_SAMPLE_VALIDITY = 1800 /* sec */,
+ DNSRESOLVER_SUCCESS_THRESHOLD = 25,
+ DNSRESOLVER_MIN_SAMPLES = 8,
+ DNSRESOLVER_MAX_SAMPLES = 8,
+ DNSRESOLVER_BASE_TIMEOUT = 5000 /* msec */,
+ DNSRESOLVER_RETRY_COUNT = 2
+};
+
+struct dnsresolver_params {
+ int32_t netid;
+ int32_t sample_validity_seconds;
+ int32_t success_threshold;
+ int32_t min_samples;
+ int32_t max_samples;
+ int32_t base_timeout_msec;
+ int32_t retry_count;
+ char **servers; /* NULL terminated array of zero-terminated UTF-8 strings */
+ char **domains; /* NULL terminated array of zero-terminated UTF-8 strings */
+ char *tls_name; /* zero-terminated UTF-8 string */
+ char **tls_servers; /* NULL terminated array of zero-terminated UTF-8 strings */
+ char **tls_fingerprints; /* NULL terminated array of zero-terminated UTF-8 strings */
+};
+
+static void *on_create()
+{
+ fprintf(stderr, "Error: on_create called on proxy object\n");
+ exit(ENOTSUP);
+ return NULL;
+}
+
+static void on_destroy()
+{
+ fprintf(stderr, "Error: on_destroy called on proxy object\n");
+ exit(ENOTSUP);
+}
+
+static binder_status_t on_transact()
+{
+ fprintf(stderr, "Error: on_transact called on a proxy object\n");
+ exit(ENOTSUP);
+ return 0;
+}
+
+static AIBinder *dnsresolver_get_handle(void)
+{
+ AIBinder *binder;
+ AIBinder_Class *clazz;
+
+ if (!binder_available)
+ return NULL;
+
+ binder = AServiceManager_getService("dnsresolver");
+ if (!binder)
+ return NULL;
+ clazz = AIBinder_Class_define("android.net.IDnsResolver", &on_create, &on_destroy, &on_transact);
+ if (!clazz)
+ goto error;
+
+ if (!AIBinder_associateClass(binder, clazz))
+ goto error;
+
+ return binder;
+error:
+ AIBinder_decStrong(binder);
+ return NULL;
+}
+
+static int32_t dnsresolver_create_network_cache(void *handle, int32_t netid)
+{
+ AIBinder *const binder = handle;
+ binder_status_t status;
+ _cleanup_parcel_ AParcel *parcel_in = NULL;
+ _cleanup_parcel_ AParcel *parcel_out = NULL;
+ _cleanup_status_ AStatus *status_out = NULL;
+
+ status = AIBinder_prepareTransaction(binder, &parcel_in);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AParcel_writeInt32(parcel_in, netid);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AIBinder_transact(binder, FIRST_CALL_TRANSACTION + 7 /* createNetworkCache */, &parcel_in, &parcel_out, 0);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AParcel_readStatusHeader(parcel_out, &status_out);
+ if (status != STATUS_OK)
+ return status;
+
+ if (!AStatus_isOk(status_out))
+ return meaningful_binder_status(status_out);
+
+ return STATUS_OK;
+}
+
+static int32_t dnsresolver_set_resolver_configuration(void *handle, const struct dnsresolver_params *params)
+{
+ AIBinder *const binder = handle;
+ binder_status_t status;
+ _cleanup_parcel_ AParcel *parcel_in = NULL;
+ _cleanup_parcel_ AParcel *parcel_out = NULL;
+ _cleanup_status_ AStatus *status_out = NULL;
+ int32_t start_position, end_position;
+
+ status = AIBinder_prepareTransaction(binder, &parcel_in);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AParcel_writeInt32(parcel_in, 1);
+ if (status != STATUS_OK)
+ return status;
+
+ start_position = AParcel_getDataPosition(parcel_in);
+ status = AParcel_writeInt32(parcel_in, 0);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AParcel_writeInt32(parcel_in, params->netid);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->sample_validity_seconds);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->success_threshold);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->min_samples);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->max_samples);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->base_timeout_msec);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->retry_count);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeStringArray(parcel_in, params->servers, string_array_size(params->servers), &string_array_getter);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeStringArray(parcel_in, params->domains, string_array_size(params->domains), &string_array_getter);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeString(parcel_in, params->tls_name, string_size(params->tls_name));
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeStringArray(parcel_in, params->tls_servers, string_array_size(params->tls_servers), &string_array_getter);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeStringArray(parcel_in, params->tls_fingerprints, string_array_size(params->tls_fingerprints), &string_array_getter);
+ if (status != STATUS_OK)
+ return status;
+
+ end_position = AParcel_getDataPosition(parcel_in);
+ status = AParcel_setDataPosition(parcel_in, start_position);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, end_position - start_position);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_setDataPosition(parcel_in, end_position);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AIBinder_transact(binder, FIRST_CALL_TRANSACTION + 2 /* setResolverConfiguration */, &parcel_in, &parcel_out, 0);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AParcel_readStatusHeader(parcel_out, &status_out);
+ if (status != STATUS_OK)
+ return status;
+
+ return meaningful_binder_status(status_out);
+}
+
static void auto_su(int argc, char *argv[])
{
char *args[argc + 4];
@@ -440,22 +812,75 @@ static void set_dnses(unsigned int netid, const char *dnses)
if (len > (1<<16))
return;
_cleanup_free_ char *mutable = xstrdup(dnses);
- _cleanup_free_ char *arglist = xmalloc(len * 4 + 1);
+ _cleanup_free_ char *shell_arglist = xmalloc(len * 4 + 1);
+ _cleanup_free_ char *function_arglist = xmalloc(len * 4 + 1);
_cleanup_free_ char *arg = xmalloc(len + 4);
+ _cleanup_free_ char **dns_list = NULL;
+ _cleanup_binder_ AIBinder *handle = NULL;
+ size_t dns_list_size = 0;
if (!len)
return;
- arglist[0] = '\0';
+ for (char *dns = strtok(mutable, ", \t\n"); dns; dns = strtok(NULL, ", \t\n")) {
+ if (strchr(dns, '\'') || strchr(dns, '\\'))
+ continue;
+ ++dns_list_size;
+ }
+ if (!dns_list_size)
+ return;
+ dns_list = xcalloc(dns_list_size + 1, sizeof(*dns_list));
+ free(mutable);
+ mutable = xstrdup(dnses);
+ shell_arglist[0] = '\0';
+ function_arglist[0] = '\0';
+ dns_list_size = 0;
for (char *dns = strtok(mutable, ", \t\n"); dns; dns = strtok(NULL, ", \t\n")) {
if (strchr(dns, '\'') || strchr(dns, '\\'))
continue;
snprintf(arg, len + 3, "'%s' ", dns);
- strncat(arglist, arg, len * 4 - 1);
+ strncat(shell_arglist, arg, len * 4 - 1);
+ snprintf(arg, len + 2, function_arglist[0] == '\0' ? "%s" : ", %s", dns);
+ strncat(function_arglist, arg, len * 4 - 1);
+ dns_list[dns_list_size++] = dns;
}
- if (!strlen(arglist))
- return;
- cndc("resolver setnetdns %u '' %s", netid, arglist);
+
+ if ((handle = dnsresolver_get_handle())) {
+ binder_status_t status;
+
+ printf("[#] <binder>::dnsResolver->createNetworkCache(%u)\n", netid);
+ status = dnsresolver_create_network_cache(handle, netid);
+ if (status != 0) {
+ fprintf(stderr, "Error: unable to create network cache\n");
+ exit(ENONET);
+ }
+
+ struct dnsresolver_params params = {
+ .netid = netid,
+ .sample_validity_seconds = DNSRESOLVER_SAMPLE_VALIDITY,
+ .success_threshold = DNSRESOLVER_SUCCESS_THRESHOLD,
+ .min_samples = DNSRESOLVER_MIN_SAMPLES,
+ .max_samples = DNSRESOLVER_MAX_SAMPLES,
+ .base_timeout_msec = DNSRESOLVER_BASE_TIMEOUT,
+ .retry_count = DNSRESOLVER_RETRY_COUNT,
+ .servers = dns_list,
+ .domains = (char *[]){NULL},
+ .tls_name = "",
+ .tls_servers = (char *[]){NULL},
+ .tls_fingerprints = (char *[]){NULL}
+ };
+
+ printf("[#] <binder>::dnsResolver->setResolverConfiguration(%u, [%s], [], %d, %d, %d, %d, %d, %d, [], [])\n",
+ netid, function_arglist, DNSRESOLVER_SAMPLE_VALIDITY, DNSRESOLVER_SUCCESS_THRESHOLD,
+ DNSRESOLVER_MIN_SAMPLES, DNSRESOLVER_MAX_SAMPLES, DNSRESOLVER_BASE_TIMEOUT, DNSRESOLVER_RETRY_COUNT);
+ status = dnsresolver_set_resolver_configuration(handle, &params);
+
+ if (status != 0) {
+ fprintf(stderr, "Error: unable to set DNS servers through Binder: %d\n", status);
+ exit(ENONET);
+ }
+ } else
+ cndc("resolver setnetdns %u '' %s", netid, shell_arglist);
}
static void add_addr(const char *iface, const char *addr)
@@ -662,8 +1087,8 @@ static void cmd_up(const char *iface, const char *config, unsigned int mtu, cons
add_if(iface);
set_config(iface, config);
listen_port = determine_listen_port(iface);
- set_addr(iface, addrs);
up_if(&netid, iface, listen_port);
+ set_addr(iface, addrs);
set_dnses(netid, dnses);
set_routes(iface, netid);
set_mtu(iface, mtu);
diff --git a/src/tools/wg-quick/linux.bash b/src/tools/wg-quick/linux.bash
index 2f36dee..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
@@ -155,7 +156,7 @@ set_dns() {
unset_dns() {
[[ ${#DNS[@]} -gt 0 ]] || return 0
- cmd resolvconf -d "$(resolvconf_iface_prefix)$INTERFACE"
+ cmd resolvconf -d "$(resolvconf_iface_prefix)$INTERFACE" -f
}
add_route() {
@@ -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-quick/openbsd.bash b/src/tools/wg-quick/openbsd.bash
index b234609..2cadeec 100755
--- a/src/tools/wg-quick/openbsd.bash
+++ b/src/tools/wg-quick/openbsd.bash
@@ -293,7 +293,7 @@ add_route() {
fi
if [[ -n $TABLE && $TABLE != auto ]]; then
- cmd route -q -n add "-$family" -rdomain "$TABLE" "$1" -iface "$ifaceroute"
+ cmd route -q -n -T "$TABLE" add "-$family" "$1" -iface "$ifaceroute"
elif [[ $1 == */0 ]]; then
if [[ $1 == *:* ]]; then
AUTO_ROUTE6=1
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/tools/wincompat/ipc.c b/src/tools/wincompat/ipc.c
index a7a59f8..25471b0 100644
--- a/src/tools/wincompat/ipc.c
+++ b/src/tools/wincompat/ipc.c
@@ -5,18 +5,23 @@
#include <windows.h>
#include <tlhelp32.h>
+#include <accctrl.h>
+#include <aclapi.h>
#include <stdio.h>
#include <stdbool.h>
#include <fcntl.h>
-static FILE *userspace_interface_file(const char *interface)
+static FILE *userspace_interface_file(const char *iface)
{
char fname[MAX_PATH], error_message[1024 * 128] = { 0 };
HANDLE thread_token, process_snapshot, winlogon_process, winlogon_token, duplicated_token, pipe_handle = INVALID_HANDLE_VALUE;
PROCESSENTRY32 entry = { .dwSize = sizeof(PROCESSENTRY32) };
+ PSECURITY_DESCRIPTOR pipe_sd;
+ PSID pipe_sid;
+ SID expected_sid;
BOOL ret;
int fd;
- DWORD last_error = ERROR_SUCCESS;
+ DWORD last_error = ERROR_SUCCESS, bytes = sizeof(expected_sid);
TOKEN_PRIVILEGES privileges = {
.PrivilegeCount = 1,
.Privileges = {{ .Attributes = SE_PRIVILEGE_ENABLED }}
@@ -24,6 +29,8 @@ static FILE *userspace_interface_file(const char *interface)
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &privileges.Privileges[0].Luid))
goto err;
+ if (!CreateWellKnownSid(WinLocalSystemSid, NULL, &expected_sid, &bytes))
+ goto err;
process_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (process_snapshot == INVALID_HANDLE_VALUE)
@@ -63,13 +70,24 @@ static FILE *userspace_interface_file(const char *interface)
}
CloseHandle(duplicated_token);
- snprintf(fname, sizeof(fname), "\\\\.\\pipe\\WireGuard\\%s", interface);
+ snprintf(fname, sizeof(fname), "\\\\.\\pipe\\ProtectedPrefix\\Administrators\\WireGuard\\%s", iface);
pipe_handle = CreateFile(fname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
last_error = GetLastError();
- if (pipe_handle != INVALID_HANDLE_VALUE) {
- last_error = ERROR_SUCCESS;
- break;
+ if (pipe_handle == INVALID_HANDLE_VALUE)
+ continue;
+ last_error = GetSecurityInfo(pipe_handle, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &pipe_sid, NULL, NULL, NULL, &pipe_sd);
+ if (last_error != ERROR_SUCCESS) {
+ CloseHandle(pipe_handle);
+ continue;
+ }
+ last_error = EqualSid(&expected_sid, pipe_sid) ? ERROR_SUCCESS : ERROR_ACCESS_DENIED;
+ LocalFree(pipe_sd);
+ if (last_error != ERROR_SUCCESS) {
+ CloseHandle(pipe_handle);
+ continue;
}
+ last_error = ERROR_SUCCESS;
+ break;
}
RevertToSelf();
CloseHandle(process_snapshot);
diff --git a/src/uapi/wireguard.h b/src/uapi/wireguard.h
index 5a3e5a7..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
@@ -87,10 +87,12 @@
* 0: NLA_NESTED
* WGPEER_A_PUBLIC_KEY: len WG_KEY_LEN
* WGPEER_A_FLAGS: NLA_U32, 0 and/or WGPEER_F_REMOVE_ME if the
- * specified peer should be removed rather than
- * added/updated and/or WGPEER_F_REPLACE_ALLOWEDIPS
- * if all current allowed IPs of this peer should be
- * removed prior to adding the list below.
+ * specified peer should not exist at the end of the
+ * operation, rather than added/updated and/or
+ * WGPEER_F_REPLACE_ALLOWEDIPS if all current allowed
+ * IPs of this peer should be removed prior to adding
+ * the list below and/or WGPEER_F_UPDATE_ONLY if the
+ * peer should only be set if it already exists.
* WGPEER_A_PRESHARED_KEY: len WG_KEY_LEN, all zeros to remove
* WGPEER_A_ENDPOINT: struct sockaddr_in or struct sockaddr_in6
* WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: NLA_U16, 0 to disable
@@ -119,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.
*
@@ -162,7 +164,9 @@ enum wgdevice_attribute {
enum wgpeer_flag {
WGPEER_F_REMOVE_ME = 1U << 0,
WGPEER_F_REPLACE_ALLOWEDIPS = 1U << 1,
- __WGPEER_F_ALL = WGPEER_F_REMOVE_ME | WGPEER_F_REPLACE_ALLOWEDIPS
+ WGPEER_F_UPDATE_ONLY = 1U << 2,
+ __WGPEER_F_ALL = WGPEER_F_REMOVE_ME | WGPEER_F_REPLACE_ALLOWEDIPS |
+ WGPEER_F_UPDATE_ONLY
};
enum wgpeer_attribute {
WGPEER_A_UNSPEC,
diff --git a/src/version.h b/src/version.h
index ae36872..ddba103 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1 +1 @@
-#define WIREGUARD_VERSION "0.0.20190702"
+#define WIREGUARD_VERSION "0.0.20191219"