Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wg: Add support to set bind-dev on Linux #12

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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