From ae374129ab46d7cfc6f089e6a1ad71968764397d Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Tue, 11 Jun 2019 19:22:52 +0200 Subject: wg: add syncconf command Signed-off-by: Jason A. Donenfeld --- src/man/wg.8 | 8 ++++++ src/setconf.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/wg.c | 1 + 3 files changed, 97 insertions(+) diff --git a/src/man/wg.8 b/src/man/wg.8 index afff749..043ffb0 100644 --- a/src/man/wg.8 +++ b/src/man/wg.8 @@ -94,6 +94,14 @@ Appends the contents of \fI\fP, which must be in the format described by \fICONFIGURATION FILE FORMAT\fP below, to the current configuration of \fI\fP. .TP +\fBsyncconf\fP \fI\fP \fI\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\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. diff --git a/src/setconf.c b/src/setconf.c index 8211ebd..a244c07 100644 --- a/src/setconf.c +++ b/src/setconf.c @@ -13,6 +13,89 @@ #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) + 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 +133,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/wg.c b/src/wg.c index 550d9b4..7b5d3af 100644 --- a/src/wg.c +++ b/src/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" } -- cgit v1.2.3-59-g8ed1b