Skip to content

Commit

Permalink
wg: Add support to set bind-dev on Linux
Browse files Browse the repository at this point in the history
  This introduces support for binding Wireguards UDP socket(s) to a
  given interface allowing to send/receive encapsulated packets via
  a Linux VRF.

Signed-off-by: Maximilian Wilhelm <[email protected]>
  • Loading branch information
BarbarossaTM committed Nov 15, 2021
1 parent b906ecb commit d3ce528
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 6 deletions.
35 changes: 35 additions & 0 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,30 @@ static inline bool parse_fwmark(uint32_t *fwmark, uint32_t *flags, const char *v
return false;
}

#ifdef __linux__
static inline bool parse_bind_dev(uint32_t *bind_dev, char *bind_dev_name, uint32_t *flags, const char *value)
{
if (strlen (value) > IFNAMSIZ) {
fprintf(stderr, "BindDev must be shorter or equal to %d chars, found: '%s'\n", IFNAMSIZ, value);
return false;
}

snprintf(bind_dev_name, IFNAMSIZ, "%s", value);
unsigned int i = if_nametoindex(value);
if (errno) {
fprintf(stderr, "Failed to get ifIndex for BindDev '%s': %s\n", value, strerror(errno));
return false;
}

*flags |= WGDEVICE_HAS_BIND_DEV;
*bind_dev = (int) i;

printf ("bind-dev %s translates to %d\n", value, i);

return true;
}
#endif

static inline bool parse_key(uint8_t key[static WG_KEY_LEN], const char *value)
{
if (!key_from_base64(key, value)) {
Expand Down Expand Up @@ -446,6 +470,10 @@ static bool process_line(struct config_ctx *ctx, const char *line)
ret = parse_port(&ctx->device->listen_port, &ctx->device->flags, value);
else if (key_match("FwMark"))
ret = parse_fwmark(&ctx->device->fwmark, &ctx->device->flags, value);
#ifdef __linux__
else if (key_match("BindDev"))
ret = parse_bind_dev(&ctx->device->bind_dev, (char *) &ctx->device->bind_dev_name, &ctx->device->flags, value);
#endif
else if (key_match("PrivateKey")) {
ret = parse_key(ctx->device->private_key, value);
if (ret)
Expand Down Expand Up @@ -582,6 +610,13 @@ struct wgdevice *config_read_cmd(const char *argv[], int argc)
goto error;
argv += 2;
argc -= 2;
#ifdef __linux__
} else if (!strcmp(argv[0], "bind-dev") && argc >= 2 && !peer) {
if (!parse_bind_dev(&device->bind_dev, (char *) device->bind_dev_name, &device->flags, argv[1]))
goto error;
argv += 2;
argc -= 2;
#endif
} else if (!strcmp(argv[0], "private-key") && argc >= 2 && !peer) {
if (!parse_keyfile(device->private_key, argv[1]))
goto error;
Expand Down
6 changes: 5 additions & 1 deletion src/containers.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ enum {
WGDEVICE_HAS_PRIVATE_KEY = 1U << 1,
WGDEVICE_HAS_PUBLIC_KEY = 1U << 2,
WGDEVICE_HAS_LISTEN_PORT = 1U << 3,
WGDEVICE_HAS_FWMARK = 1U << 4
WGDEVICE_HAS_FWMARK = 1U << 4,
WGDEVICE_HAS_BIND_DEV = 1U << 5
};

struct wgdevice {
Expand All @@ -87,6 +88,9 @@ struct wgdevice {
uint16_t listen_port;

struct wgpeer *first_peer, *last_peer;

uint32_t bind_dev;
char bind_dev_name[IFNAMSIZ];
};

#define for_each_wgpeer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer)
Expand Down
20 changes: 20 additions & 0 deletions src/ipc-linux.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ struct interface {
bool is_wireguard;
};

static void get_bind_dev_name(char *ifname, const int ifindex)
{
if_indextoname (ifindex, ifname);
if (errno) {
fprintf(stderr, "Failed to get interface name for BindDev with ID '%d': %s\n", ifindex, strerror(errno));
ifname = "ERROR";
}
}

static int parse_linkinfo(const struct nlattr *attr, void *data)
{
struct interface *interface = data;
Expand Down Expand Up @@ -165,6 +174,8 @@ static int kernel_set_device(struct wgdevice *dev)
mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port);
if (dev->flags & WGDEVICE_HAS_FWMARK)
mnl_attr_put_u32(nlh, WGDEVICE_A_FWMARK, dev->fwmark);
if (dev->flags & WGDEVICE_HAS_BIND_DEV)
mnl_attr_put_u32(nlh, WGDEVICE_A_BIND_IFINDEX, dev->bind_dev);
if (dev->flags & WGDEVICE_REPLACE_PEERS)
flags |= WGDEVICE_F_REPLACE_PEERS;
if (flags)
Expand Down Expand Up @@ -439,6 +450,15 @@ static int parse_device(const struct nlattr *attr, void *data)
if (!mnl_attr_validate(attr, MNL_TYPE_U32))
device->fwmark = mnl_attr_get_u32(attr);
break;
case WGDEVICE_A_BIND_IFINDEX:
if (!mnl_attr_validate(attr, MNL_TYPE_U32)) {
device->bind_dev = mnl_attr_get_u32(attr);
if (device->bind_dev) {
device->flags |= WGDEVICE_HAS_BIND_DEV;
get_bind_dev_name(device->bind_dev_name, device->bind_dev);
}
}
break;
case WGDEVICE_A_PEERS:
return mnl_attr_parse_nested(attr, parse_peers, device);
}
Expand Down
7 changes: 5 additions & 2 deletions src/man/wg.8
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Sub-commands that take an INTERFACE must be passed a WireGuard interface.
.SH COMMANDS

.TP
\fBshow\fP { \fI<interface>\fP | \fIall\fP | \fIinterfaces\fP } [\fIpublic-key\fP | \fIprivate-key\fP | \fIlisten-port\fP | \fIfwmark\fP | \fIpeers\fP | \fIpreshared-keys\fP | \fIendpoints\fP | \fIallowed-ips\fP | \fIlatest-handshakes\fP | \fIpersistent-keepalive\fP | \fItransfer\fP | \fIdump\fP]
\fBshow\fP { \fI<interface>\fP | \fIall\fP | \fIinterfaces\fP } [\fIpublic-key\fP | \fIprivate-key\fP | \fIlisten-port\fP | \fIbind-dev\fP | \fIfwmark\fP | \fIpeers\fP | \fIpreshared-keys\fP | \fIendpoints\fP | \fIallowed-ips\fP | \fIlatest-handshakes\fP | \fIpersistent-keepalive\fP | \fItransfer\fP | \fIdump\fP]
Shows current WireGuard configuration and runtime information of specified \fI<interface>\fP.
If no \fI<interface>\fP is specified, \fI<interface>\fP defaults to \fIall\fP.
If \fIinterfaces\fP is specified, prints a list of all WireGuard interfaces,
Expand All @@ -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] [\fIbind-dev\fP \fI<bind-dev>\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
Expand Down Expand Up @@ -137,6 +137,9 @@ PrivateKey \(em a base64 private key generated by \fIwg genkey\fP. Required.
ListenPort \(em a 16-bit port for listening. Optional; if not specified, chosen
randomly.
.IP \(bu
BindDev \(em an interface to bind the listening/sending UDP socket to. This can be
use to make Wireguard send/receive encrypted packets via a VRF on Linux. Optional.
.IP \(bu
FwMark \(em a 32-bit fwmark for outgoing packets. If set to 0 or "off", this
option is disabled. May be specified in hexadecimal by prepending "0x". Optional.
.P
Expand Down
2 changes: 1 addition & 1 deletion src/set.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>] [bind-dev <interface>] [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;
}

Expand Down
21 changes: 19 additions & 2 deletions src/show.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ static char *bytes(uint64_t b)
static const char *COMMAND_NAME;
static void show_usage(void)
{
fprintf(stderr, "Usage: %s %s { <interface> | all | interfaces } [public-key | private-key | listen-port | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]\n", PROG_NAME, COMMAND_NAME);
fprintf(stderr, "Usage: %s %s { <interface> | all | interfaces } [public-key | private-key | listen-port | bind-dev | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]\n", PROG_NAME, COMMAND_NAME);
}

static void pretty_print(struct wgdevice *device)
Expand All @@ -220,6 +220,8 @@ static void pretty_print(struct wgdevice *device)
terminal_printf(" " TERMINAL_BOLD "listening port" TERMINAL_RESET ": %u\n", device->listen_port);
if (device->fwmark)
terminal_printf(" " TERMINAL_BOLD "fwmark" TERMINAL_RESET ": 0x%x\n", device->fwmark);
if (device->bind_dev)
terminal_printf(" " TERMINAL_BOLD "bind dev" TERMINAL_RESET ": %s\n", device->bind_dev_name);
if (device->first_peer) {
sort_peers(device);
terminal_printf("\n");
Expand Down Expand Up @@ -261,7 +263,11 @@ static void dump_print(struct wgdevice *device, bool with_interface)
printf("%s\t", maybe_key(device->public_key, device->flags & WGDEVICE_HAS_PUBLIC_KEY));
printf("%u\t", device->listen_port);
if (device->fwmark)
printf("0x%x\n", device->fwmark);
printf("0x%x\t", device->fwmark);
else
printf("off\t");
if (device->bind_dev)
printf("%s\n", device->bind_dev_name);
else
printf("off\n");
for_each_wgpeer(device, peer) {
Expand Down Expand Up @@ -311,6 +317,17 @@ static bool ugly_print(struct wgdevice *device, const char *param, bool with_int
printf("0x%x\n", device->fwmark);
else
printf("off\n");
} else if (!strcmp(param, "bind-dev")) {
if (with_interface)
printf("%s\t", device->name);
#ifdef __linux__
if (device->bind_dev)
printf("%s\n", device->bind_dev_name);
else
printf("off\n");
#else
printf("(unsupported)\n");
#endif
} else if (!strcmp(param, "endpoints")) {
if (with_interface)
printf("%s\t", device->name);
Expand Down
2 changes: 2 additions & 0 deletions src/showconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ int showconf_main(int argc, const char *argv[])
printf("ListenPort = %u\n", device->listen_port);
if (device->fwmark)
printf("FwMark = 0x%x\n", device->fwmark);
if (device->bind_dev)
printf("BindDev = %s\n", device->bind_dev_name);
if (device->flags & WGDEVICE_HAS_PRIVATE_KEY) {
key_to_base64(base64, device->private_key);
printf("PrivateKey = %s\n", base64);
Expand Down
1 change: 1 addition & 0 deletions src/uapi/linux/linux/wireguard.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ enum wgdevice_attribute {
WGDEVICE_A_LISTEN_PORT,
WGDEVICE_A_FWMARK,
WGDEVICE_A_PEERS,
WGDEVICE_A_BIND_IFINDEX,
__WGDEVICE_A_LAST
};
#define WGDEVICE_A_MAX (__WGDEVICE_A_LAST - 1)
Expand Down

0 comments on commit d3ce528

Please sign in to comment.