From 5a6d43eb15b537e525d724c9fb3400031ae14c70 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Tue, 24 Jan 2017 17:43:35 +0100 Subject: socket: enable setting of fwmark --- src/config.c | 6 ++++++ src/device.h | 1 + src/socket.c | 3 ++- src/tools/completion/wg.bash-completion | 6 ++++-- src/tools/config.c | 28 ++++++++++++++++++++++++++ src/tools/set.c | 2 +- src/tools/show.c | 8 +++++++- src/tools/showconf.c | 4 +++- src/tools/wg-quick.bash | 35 +++++++++++++++++++-------------- src/tools/wg.8 | 6 ++++-- src/uapi.h | 6 ++++-- 11 files changed, 80 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/config.c b/src/config.c index 172287a..30c876e 100644 --- a/src/config.c +++ b/src/config.c @@ -125,6 +125,11 @@ int config_set_device(struct wireguard_device *wg, void __user *user_device) goto out; } + if (in_device.fwmark || (!in_device.fwmark && (in_device.flags & WGDEVICE_REMOVE_FWMARK))) { + wg->fwmark = in_device.fwmark; + peer_for_each_unlocked(wg, clear_peer_endpoint_src, NULL); + } + if (in_device.port) { ret = set_device_port(wg, in_device.port); if (ret) @@ -287,6 +292,7 @@ int config_get_device(struct wireguard_device *wg, void __user *udevice) } out_device.port = wg->incoming_port; + out_device.fwmark = wg->fwmark; strncpy(out_device.interface, dev->name, IFNAMSIZ - 1); out_device.interface[IFNAMSIZ - 1] = 0; diff --git a/src/device.h b/src/device.h index 3cbbbaa..855ec27 100644 --- a/src/device.h +++ b/src/device.h @@ -19,6 +19,7 @@ struct wireguard_device { struct sock __rcu *sock4, *sock6; u16 incoming_port; + u32 fwmark; struct net *creating_net; struct workqueue_struct *workqueue; struct workqueue_struct *parallelqueue; diff --git a/src/socket.c b/src/socket.c index e26504d..f799d91 100644 --- a/src/socket.c +++ b/src/socket.c @@ -20,6 +20,7 @@ static inline int send4(struct wireguard_device *wg, struct sk_buff *skb, struct .daddr = endpoint->addr4.sin_addr.s_addr, .fl4_dport = endpoint->addr4.sin_port, .fl4_sport = htons(wg->incoming_port), + .flowi4_mark = wg->fwmark, .flowi4_proto = IPPROTO_UDP }; struct rtable *rt = NULL; @@ -62,7 +63,6 @@ static inline int send4(struct wireguard_device *wg, struct sk_buff *skb, struct if (cache) dst_cache_set_ip4(cache, &rt->dst, fl.saddr); } - udp_tunnel_xmit_skb(rt, sock, skb, fl.saddr, fl.daddr, ds, ip4_dst_hoplimit(&rt->dst), 0, @@ -85,6 +85,7 @@ static inline int send6(struct wireguard_device *wg, struct sk_buff *skb, struct .daddr = endpoint->addr6.sin6_addr, .fl6_dport = endpoint->addr6.sin6_port, .fl6_sport = htons(wg->incoming_port), + .flowi6_mark = wg->fwmark, .flowi6_oif = endpoint->addr6.sin6_scope_id, .flowi6_proto = IPPROTO_UDP /* TODO: addr->sin6_flowinfo */ diff --git a/src/tools/completion/wg.bash-completion b/src/tools/completion/wg.bash-completion index 76a832a..8822d01 100644 --- a/src/tools/completion/wg.bash-completion +++ b/src/tools/completion/wg.bash-completion @@ -19,7 +19,7 @@ _wg_completion() { fi if [[ $COMP_CWORD -eq 3 && ${COMP_WORDS[1]} == show && ${COMP_WORDS[2]} != interfaces ]]; then - COMPREPLY+=( $(compgen -W "public-key private-key preshared-key listen-port peers endpoints allowed-ips latest-handshakes persistent-keepalive transfer" -- "${COMP_WORDS[3]}") ) + COMPREPLY+=( $(compgen -W "public-key private-key preshared-key listen-port peers endpoints allowed-ips fwmark latest-handshakes persistent-keepalive transfer" -- "${COMP_WORDS[3]}") ) return fi @@ -31,9 +31,10 @@ _wg_completion() { [[ ${COMP_WORDS[1]} == set ]] || return - local has_listen_port=0 has_private_key=0 has_preshared_key=0 has_peer=0 has_remove=0 has_endpoint=0 has_persistent_keepalive=0 has_allowed_ips=0 words=() i j + local has_listen_port=0 has_fwmark=0 has_private_key=0 has_preshared_key=0 has_peer=0 has_remove=0 has_endpoint=0 has_persistent_keepalive=0 has_allowed_ips=0 words=() i j for ((i=3;i UINT32_MAX) + return false; + *fwmark = ret; + if (!ret) + *flags |= WGDEVICE_REMOVE_FWMARK; + return true; +} + static inline bool parse_key(uint8_t key[static WG_KEY_LEN], const char *value) { uint8_t tmp[WG_KEY_LEN + 1]; @@ -281,6 +300,8 @@ static bool process_line(struct config_ctx *ctx, const char *line) if (ctx->is_device_section) { if (key_match("ListenPort")) ret = !!(ctx->buf.dev->port = parse_port(value)); + else if (key_match("FwMark")) + ret = parse_fwmark(&ctx->buf.dev->fwmark, &ctx->buf.dev->flags, value); else if (key_match("PrivateKey")) { ret = parse_key(ctx->buf.dev->private_key, value); if (!ret) @@ -372,6 +393,8 @@ bool config_read_finish(struct config_ctx *ctx) } if (ctx->buf.dev->flags & WGDEVICE_REPLACE_PEERS && !key_is_valid(ctx->buf.dev->preshared_key)) ctx->buf.dev->flags |= WGDEVICE_REMOVE_PRESHARED_KEY; + if (ctx->buf.dev->flags & WGDEVICE_REPLACE_PEERS && !ctx->buf.dev->fwmark) + ctx->buf.dev->flags |= WGDEVICE_REMOVE_FWMARK; for_each_wgpeer(ctx->buf.dev, peer, i) { if (!key_is_valid(peer->public_key)) { @@ -448,6 +471,11 @@ bool config_read_cmd(struct wgdevice **device, char *argv[], int argc) goto error; argv += 2; argc -= 2; + } else if (!strcmp(argv[0], "fwmark") && argc >= 2 && !buf.dev->num_peers) { + if (!parse_fwmark(&buf.dev->fwmark, &buf.dev->flags, argv[1])) + goto error; + argv += 2; + argc -= 2; } else if (!strcmp(argv[0], "private-key") && argc >= 2 && !buf.dev->num_peers) { char *line; int ret = read_line(&line, argv[1]); diff --git a/src/tools/set.c b/src/tools/set.c index 48b0050..5e4291f 100644 --- a/src/tools/set.c +++ b/src/tools/set.c @@ -13,7 +13,7 @@ int set_main(int argc, char *argv[]) int ret = 1; if (argc < 3) { - fprintf(stderr, "Usage: %s %s [listen-port ] [private-key ] [peer [remove] [endpoint :] [persistent-keepalive ] [allowed-ips /[,/]...] ]...\n", PROG_NAME, argv[0]); + fprintf(stderr, "Usage: %s %s [listen-port ] [fwmark ] [private-key ] [peer [remove] [endpoint :] [persistent-keepalive ] [allowed-ips /[,/]...] ]...\n", PROG_NAME, argv[0]); return 1; } diff --git a/src/tools/show.c b/src/tools/show.c index 124e323..c20d858 100644 --- a/src/tools/show.c +++ b/src/tools/show.c @@ -203,7 +203,7 @@ static char *bytes(uint64_t b) static const char *COMMAND_NAME = NULL; static void show_usage(void) { - fprintf(stderr, "Usage: %s %s { | all | interfaces } [public-key | private-key | preshared-key | listen-port | peers | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive]\n", PROG_NAME, COMMAND_NAME); + fprintf(stderr, "Usage: %s %s { | all | interfaces } [public-key | private-key | preshared-key | listen-port | fwmark | peers | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive]\n", PROG_NAME, COMMAND_NAME); } static void pretty_print(struct wgdevice *device) @@ -222,6 +222,8 @@ static void pretty_print(struct wgdevice *device) terminal_printf(" " TERMINAL_BOLD "pre-shared key" TERMINAL_RESET ": %s\n", masked_key(device->preshared_key)); if (device->port) terminal_printf(" " TERMINAL_BOLD "listening port" TERMINAL_RESET ": %u\n", device->port); + if (device->fwmark) + terminal_printf(" " TERMINAL_BOLD "fwmark" TERMINAL_RESET ": 0x%x\n", device->fwmark); if (device->num_peers) { sort_peers(device); terminal_printf("\n"); @@ -271,6 +273,10 @@ static bool ugly_print(struct wgdevice *device, const char *param, bool with_int if (with_interface) printf("%s\t", device->interface); printf("%u\n", device->port); + } else if (!strcmp(param, "fwmark")) { + if (with_interface) + printf("%s\t", device->interface); + printf("0x%x\n", device->fwmark); } else if (!strcmp(param, "endpoints")) { if (with_interface) printf("%s\t", device->interface); diff --git a/src/tools/showconf.c b/src/tools/showconf.c index d2f36e0..585b08d 100644 --- a/src/tools/showconf.c +++ b/src/tools/showconf.c @@ -44,7 +44,9 @@ int showconf_main(int argc, char *argv[]) printf("[Interface]\n"); if (device->port) - printf("ListenPort = %d\n", device->port); + printf("ListenPort = %u\n", device->port); + if (device->fwmark) + printf("FwMark = 0x%x\n", device->fwmark); if (memcmp(device->private_key, zero, WG_KEY_LEN)) { b64_ntop(device->private_key, WG_KEY_LEN, b64, b64_len(WG_KEY_LEN)); printf("PrivateKey = %s\n", b64); diff --git a/src/tools/wg-quick.bash b/src/tools/wg-quick.bash index 1eb71b2..5db3485 100755 --- a/src/tools/wg-quick.bash +++ b/src/tools/wg-quick.bash @@ -78,10 +78,15 @@ add_if() { } del_if() { - if [[ $(ip route show table all) =~ .*\ dev\ $INTERFACE\ table\ ([0-9]+)\ .* ]]; then - while [[ -n $(ip rule show table ${BASH_REMATCH[1]}) ]]; do - cmd ip rule delete table "${BASH_REMATCH[1]}" - [[ $(ip rule show table main) == *"from all lookup main suppress_prefixlength 0"* ]] && cmd ip rule delete table main suppress_prefixlength 0 + DEFAULT_TABLE=$(("$(wg show "$INTERFACE" fwmark)")) + if [[ $DEFAULT_TABLE -ne 0 ]]; then + while [[ -n $(ip -4 rule show table $DEFAULT_TABLE) ]]; do + cmd ip -4 rule delete table $DEFAULT_TABLE + [[ $(ip -4 rule show table main) == *"from all lookup main suppress_prefixlength 0"* ]] && cmd ip -4 rule delete table main suppress_prefixlength 0 + done + while [[ -n $(ip -6 rule show table $DEFAULT_TABLE) ]]; do + cmd ip -6 rule delete table $DEFAULT_TABLE + [[ $(ip -6 rule show table main) == *"from all lookup main suppress_prefixlength 0"* ]] && cmd ip -6 rule delete table main suppress_prefixlength 0 done fi cmd ip link delete dev "$INTERFACE" @@ -104,22 +109,22 @@ add_route() { } DEFAULT_TABLE= -PREVIOUS_ENDPOINT= add_default() { - [[ $(join <(wg show "$INTERFACE" allowed-ips) <(wg show "$INTERFACE" endpoints)) =~ ([A-Za-z0-9/+=]{44})\ ([0-9a-f/.:]+ )*${1//./\\.}\ ([0-9a-f/.:]+ )*\[?([0-9.:a-f]+)\]?:[0-9]+ ]] && local endpoint="${BASH_REMATCH[4]}" - [[ -n $endpoint ]] || return 0 - local first=0 if [[ -z $DEFAULT_TABLE ]]; then - first=1 DEFAULT_TABLE=51820 while [[ -n $(ip route show table $DEFAULT_TABLE) ]]; do ((DEFAULT_TABLE++)); done fi - cmd ip route add "$1" dev "$INTERFACE" table $DEFAULT_TABLE - [[ $PREVIOUS_ENDPOINT == "$endpoint" ]] && return 0 - PREVIOUS_ENDPOINT="$endpoint" - cmd ip rule add not to "$endpoint" table $DEFAULT_TABLE - [[ $first -eq 1 ]] || return 0 - cmd ip rule add table main suppress_prefixlength 0 + local proto=-4 + [[ $1 == *:* ]] && proto=-6 + cmd wg set "$INTERFACE" fwmark $DEFAULT_TABLE + cmd ip $proto route add "$1" dev "$INTERFACE" table $DEFAULT_TABLE + cmd ip $proto rule add not fwmark $DEFAULT_TABLE table $DEFAULT_TABLE + cmd ip $proto rule add table main suppress_prefixlength 0 + local key equals value + while read -r key equals value; do + [[ $value -eq 1 ]] && sysctl -q "$key=2" + done < <(sysctl -a -r 'net\.ipv4.conf\..+\.rp_filter') + return 0 } set_config() { diff --git a/src/tools/wg.8 b/src/tools/wg.8 index be05acf..9aa76cf 100644 --- a/src/tools/wg.8 +++ b/src/tools/wg.8 @@ -36,7 +36,7 @@ Sub-commands that take an INTERFACE must be passed a WireGuard interface. .SH COMMANDS .TP -\fBshow\fP { \fI\fP | \fIall\fP | \fIinterfaces\fP } [\fIpublic-key\fP | \fIprivate-key\fP | \fIpreshared-key\fP | \fIlisten-port\fP | \fIpeers\fP | \fIendpoints\fP | \fIallowed-ips\fP | \fIlatest-handshakes\fP | \fIpersistent-keepalive\fP | \fItransfer\fP] +\fBshow\fP { \fI\fP | \fIall\fP | \fIinterfaces\fP } [\fIpublic-key\fP | \fIprivate-key\fP | \fIpreshared-key\fP | \fIlisten-port\fP | \fIfwmark\fP | \fIpeers\fP | \fIendpoints\fP | \fIallowed-ips\fP | \fIlatest-handshakes\fP | \fIpersistent-keepalive\fP | \fItransfer\fP] Shows current WireGuard configuration of specified \fI\fP. If no \fI\fP is specified, \fI\fP defaults to \fIall\fP. If \fIinterfaces\fP is specified, prints a list of all WireGuard interfaces, @@ -49,7 +49,7 @@ newlines and tabs, meant to be used in scripts. Shows the current configuration of \fI\fP in the format described by \fICONFIGURATION FILE FORMAT\fP below. .TP -\fBset\fP \fI\fP [\fIlisten-port\fP \fI\fP] [\fIprivate-key\fP \fI\fP] [\fIpreshared-key\fP \fI\fP] [\fIpeer\fP \fI\fP [\fIremove\fP] [\fIendpoint\fP \fI:\fP] [\fIpersistent-keepalive\fP \fI\fP] [\fIallowed-ips\fP \fI/\fP[,\fI/\fP]...] ]... +\fBset\fP \fI\fP [\fIlisten-port\fP \fI\fP] [\fIfwmark\fP \fI\fP] [\fIprivate-key\fP \fI\fP] [\fIpreshared-key\fP \fI\fP] [\fIpeer\fP \fI\fP [\fIremove\fP] [\fIendpoint\fP \fI:\fP] [\fIpersistent-keepalive\fP \fI\fP] [\fIallowed-ips\fP \fI/\fP[,\fI/\fP]...] ]... Sets configuration values for the specified \fI\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 @@ -125,6 +125,8 @@ for post-quantum resistance. .IP \(bu ListenPort \(em a 16-bit port for listening. Optional; if not specified, chosen randomly. +.IP \(bu +FwMark \(em a 32-bit fwmark for outgoing packets. Optional. .P The \fIPeer\fP sections may contain the following fields: .IP \(bu diff --git a/src/uapi.h b/src/uapi.h index eb8d9d3..9cf9b2c 100644 --- a/src/uapi.h +++ b/src/uapi.h @@ -121,7 +121,8 @@ struct wgpeer { enum { WGDEVICE_REPLACE_PEERS = (1 << 0), WGDEVICE_REMOVE_PRIVATE_KEY = (1 << 1), - WGDEVICE_REMOVE_PRESHARED_KEY = (1 << 2) + WGDEVICE_REMOVE_PRESHARED_KEY = (1 << 2), + WGDEVICE_REMOVE_FWMARK = (1 << 3) }; struct wgdevice { char interface[IFNAMSIZ]; /* Get */ @@ -130,11 +131,12 @@ struct wgdevice { __u8 public_key[WG_KEY_LEN]; /* Get */ __u8 private_key[WG_KEY_LEN]; /* Get/Set */ __u8 preshared_key[WG_KEY_LEN]; /* Get/Set */ + __u32 fwmark; /* Get/Set */ __u16 port; /* Get/Set */ union { __u16 num_peers; /* Get/Set */ - __u64 peers_size; /* Get */ + __u32 peers_size; /* Get */ }; }; -- cgit v1.2.3-59-g8ed1b