diff options
author | 2025-05-17 12:29:51 -0700 | |
---|---|---|
committer | 2025-05-20 22:03:58 +0200 | |
commit | 0788f90810efde88cfa07ed96e7eca77c7f2eedd (patch) | |
tree | 4ad2ca7cf268a645ba0f7a9a76ab0ba712e6097a | |
parent | ipc: freebsd: use AF_LOCAL for the control socket (diff) | |
download | wireguard-tools-0788f90810efde88cfa07ed96e7eca77c7f2eedd.tar.xz wireguard-tools-0788f90810efde88cfa07ed96e7eca77c7f2eedd.zip |
ipc: linux: support incremental allowed ips updates
Extend the interface of `wg set` to leverage the WGALLOWEDIP_F_REMOVE_ME
flag, a direct way of removing a single allowed ip from a peer,
allowing for incremental updates to a peer's configuration. By default,
allowed-ips fully replaces a peer's allowed ips using
WGPEER_REPLACE_ALLOWEDIPS under the hood. When '+' or '-' is prepended
to any ip in the list, wg clears WGPEER_F_REPLACE_ALLOWEDIPS and sets
the WGALLOWEDIP_F_REMOVE_ME flag on any ip prefixed with '-'.
$ wg set wg0 peer <PUBKEY> allowed-ips +192.168.88.0/24,-192.168.0.1/32
This command means "add 192.168.88.0/24 to this peer's allowed ips if
not present, and remove 192.168.0.1/32 if present".
Use -isystem so that headers in uapi/ take precedence over system
headers; otherwise, the build will fail on systems running kernels
without the WGALLOWEDIP_F_REMOVE_ME flag.
Note that this patch is meant to be merged alongside the kernel patch
that introduces the flag.
Signed-off-by: Jordan Rife <jordan@jrife.io>
[Jason: removed linux ifdefs; this will eventually come to other platforms too. minor style nits.]
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r-- | src/Makefile | 2 | ||||
-rw-r--r-- | src/config.c | 18 | ||||
-rw-r--r-- | src/containers.h | 5 | ||||
-rw-r--r-- | src/ipc-linux.h | 2 | ||||
-rw-r--r-- | src/man/wg.8 | 8 | ||||
-rw-r--r-- | src/set.c | 2 | ||||
-rw-r--r-- | src/uapi/linux/linux/wireguard.h | 9 |
7 files changed, 42 insertions, 4 deletions
diff --git a/src/Makefile b/src/Makefile index 0533910..1c4b3f6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -39,7 +39,7 @@ PLATFORM ?= $(shell uname -s | tr '[:upper:]' '[:lower:]') CFLAGS ?= -O3 ifneq ($(wildcard uapi/$(PLATFORM)/.),) -CFLAGS += -idirafter uapi/$(PLATFORM) +CFLAGS += -isystem uapi/$(PLATFORM) endif CFLAGS += -std=gnu99 -D_GNU_SOURCE CFLAGS += -Wall -Wextra diff --git a/src/config.c b/src/config.c index 81ccb47..4d2d386 100644 --- a/src/config.c +++ b/src/config.c @@ -337,6 +337,20 @@ static bool validate_netmask(struct wgallowedip *allowedip) return true; } +static inline void parse_ip_prefix(struct wgpeer *peer, uint32_t *flags, char **mask) +{ + /* If the IP is prefixed with either '+' or '-' consider this an + * incremental change. Disable WGPEER_REPLACE_ALLOWEDIPS. */ + switch ((*mask)[0]) { + case '-': + *flags |= WGALLOWEDIP_REMOVE_ME; + /* fall through */ + case '+': + peer->flags &= ~WGPEER_REPLACE_ALLOWEDIPS; + ++(*mask); + } +} + static inline bool parse_allowedips(struct wgpeer *peer, struct wgallowedip **last_allowedip, const char *value) { struct wgallowedip *allowedip = *last_allowedip, *new_allowedip; @@ -353,9 +367,12 @@ static inline bool parse_allowedips(struct wgpeer *peer, struct wgallowedip **la } sep = mutable; while ((mask = strsep(&sep, ","))) { + uint32_t flags = 0; unsigned long cidr; char *end, *ip; + parse_ip_prefix(peer, &flags, &mask); + saved_entry = strdup(mask); ip = strsep(&mask, "/"); @@ -387,6 +404,7 @@ static inline bool parse_allowedips(struct wgpeer *peer, struct wgallowedip **la else goto err; new_allowedip->cidr = cidr; + new_allowedip->flags = flags; if (!validate_netmask(new_allowedip)) fprintf(stderr, "Warning: AllowedIP has nonzero host part: %s/%s\n", ip, mask); diff --git a/src/containers.h b/src/containers.h index a82e8dd..8fd813a 100644 --- a/src/containers.h +++ b/src/containers.h @@ -28,6 +28,10 @@ struct timespec64 { int64_t tv_nsec; }; +enum { + WGALLOWEDIP_REMOVE_ME = 1U << 0, +}; + struct wgallowedip { uint16_t family; union { @@ -35,6 +39,7 @@ struct wgallowedip { struct in6_addr ip6; }; uint8_t cidr; + uint32_t flags; struct wgallowedip *next_allowedip; }; diff --git a/src/ipc-linux.h b/src/ipc-linux.h index d29c0c5..01247f1 100644 --- a/src/ipc-linux.h +++ b/src/ipc-linux.h @@ -228,6 +228,8 @@ again: } if (!mnl_attr_put_u8_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_CIDR_MASK, allowedip->cidr)) goto toobig_allowedips; + if (allowedip->flags && !mnl_attr_put_u32_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_FLAGS, allowedip->flags)) + goto toobig_allowedips; mnl_attr_nest_end(nlh, allowedip_nest); allowedip_nest = NULL; } diff --git a/src/man/wg.8 b/src/man/wg.8 index 7984539..a0fc04c 100644 --- a/src/man/wg.8 +++ b/src/man/wg.8 @@ -55,7 +55,7 @@ transfer-rx, transfer-tx, persistent-keepalive. Shows the current configuration of \fI<interface>\fP in the format described by \fICONFIGURATION FILE FORMAT\fP below. .TP -\fBset\fP \fI<interface>\fP [\fIlisten-port\fP \fI<port>\fP] [\fIfwmark\fP \fI<fwmark>\fP] [\fIprivate-key\fP \fI<file-path>\fP] [\fIpeer\fP \fI<base64-public-key>\fP [\fIremove\fP] [\fIpreshared-key\fP \fI<file-path>\fP] [\fIendpoint\fP \fI<ip>:<port>\fP] [\fIpersistent-keepalive\fP \fI<interval seconds>\fP] [\fIallowed-ips\fP \fI<ip1>/<cidr1>\fP[,\fI<ip2>/<cidr2>\fP]...] ]... +\fBset\fP \fI<interface>\fP [\fIlisten-port\fP \fI<port>\fP] [\fIfwmark\fP \fI<fwmark>\fP] [\fIprivate-key\fP \fI<file-path>\fP] [\fIpeer\fP \fI<base64-public-key>\fP [\fIremove\fP] [\fIpreshared-key\fP \fI<file-path>\fP] [\fIendpoint\fP \fI<ip>:<port>\fP] [\fIpersistent-keepalive\fP \fI<interval seconds>\fP] [\fIallowed-ips\fP \fI[+|-]<ip1>/<cidr1>\fP[,\fI[+|-]<ip2>/<cidr2>\fP]...] ]... Sets configuration values for the specified \fI<interface>\fP. Multiple \fIpeer\fPs may be specified, and if the \fIremove\fP argument is given for a peer, that peer is removed, not configured. If \fIlisten-port\fP @@ -72,7 +72,11 @@ the device. The use of \fIpreshared-key\fP is optional, and may be omitted; it adds an additional layer of symmetric-key cryptography to be mixed into the already existing public-key cryptography, for post-quantum resistance. If \fIallowed-ips\fP is specified, but the value is the empty string, all -allowed ips are removed from the peer. The use of \fIpersistent-keepalive\fP +allowed ips are removed from the peer. By default, \fIallowed-ips\fP replaces +a peer's allowed ips. If + or - is prepended to any of the ips then +the update is incremental; ips prefixed with '+' or '' are added to the peer's +allowed ips if not present while ips prefixed with '-' are removed if present. +The use of \fIpersistent-keepalive\fP is optional and is by default off; setting it to 0 or "off" disables it. Otherwise it represents, in seconds, between 1 and 65535 inclusive, how often to send an authenticated empty packet to the peer, for the purpose of keeping @@ -18,7 +18,7 @@ int set_main(int argc, const char *argv[]) int ret = 1; if (argc < 3) { - fprintf(stderr, "Usage: %s %s <interface> [listen-port <port>] [fwmark <mark>] [private-key <file path>] [peer <base64 public key> [remove] [preshared-key <file path>] [endpoint <ip>:<port>] [persistent-keepalive <interval seconds>] [allowed-ips <ip1>/<cidr1>[,<ip2>/<cidr2>]...] ]...\n", PROG_NAME, argv[0]); + fprintf(stderr, "Usage: %s %s <interface> [listen-port <port>] [fwmark <mark>] [private-key <file path>] [peer <base64 public key> [remove] [preshared-key <file path>] [endpoint <ip>:<port>] [persistent-keepalive <interval seconds>] [allowed-ips [+|-]<ip1>/<cidr1>[,[+|-]<ip2>/<cidr2>]...] ]...\n", PROG_NAME, argv[0]); return 1; } diff --git a/src/uapi/linux/linux/wireguard.h b/src/uapi/linux/linux/wireguard.h index 0efd52c..6ca266a 100644 --- a/src/uapi/linux/linux/wireguard.h +++ b/src/uapi/linux/linux/wireguard.h @@ -101,6 +101,10 @@ * WGALLOWEDIP_A_FAMILY: NLA_U16 * WGALLOWEDIP_A_IPADDR: struct in_addr or struct in6_addr * WGALLOWEDIP_A_CIDR_MASK: NLA_U8 + * WGALLOWEDIP_A_FLAGS: NLA_U32, WGALLOWEDIP_F_REMOVE_ME if + * the specified IP should be removed; + * otherwise, this IP will be added if + * it is not already present. * 0: NLA_NESTED * ... * 0: NLA_NESTED @@ -184,11 +188,16 @@ enum wgpeer_attribute { }; #define WGPEER_A_MAX (__WGPEER_A_LAST - 1) +enum wgallowedip_flag { + WGALLOWEDIP_F_REMOVE_ME = 1U << 0, + __WGALLOWEDIP_F_ALL = WGALLOWEDIP_F_REMOVE_ME +}; enum wgallowedip_attribute { WGALLOWEDIP_A_UNSPEC, WGALLOWEDIP_A_FAMILY, WGALLOWEDIP_A_IPADDR, WGALLOWEDIP_A_CIDR_MASK, + WGALLOWEDIP_A_FLAGS, __WGALLOWEDIP_A_LAST }; #define WGALLOWEDIP_A_MAX (__WGALLOWEDIP_A_LAST - 1) |