]> git.ipfire.org Git - thirdparty/wireguard-tools.git/commitdiff
wg: add syncconf command
authorJason A. Donenfeld <Jason@zx2c4.com>
Tue, 11 Jun 2019 17:22:52 +0000 (19:22 +0200)
committerJason A. Donenfeld <Jason@zx2c4.com>
Wed, 27 Nov 2019 13:42:34 +0000 (14:42 +0100)
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
src/man/wg.8
src/setconf.c
src/wg.c

index afff74939bb40b0a2b48b555e29829bb125e4f16..043ffb0d6144134fbbc4267b71db7f6519986dd3 100644 (file)
@@ -94,6 +94,14 @@ Appends the contents of \fI<configuration-filename>\fP, which must
 be in the format described by \fICONFIGURATION FILE FORMAT\fP below,
 to the current configuration of \fI<interface>\fP.
 .TP
+\fBsyncconf\fP \fI<interface>\fP \fI<configuration-filename>\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<configuration-filename>\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.
index 8211ebd765bf24c72065b63d6f807c210767453e..a244c07d3ff90ad2ba43cd3626280ecdad90ddb1 100644 (file)
 #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;
index 550d9b4e0c67e0c4ccd3d0ca6959afc69875853d..7b5d3af6d02da179345f68c3e69b611d6b4cb7ef 100644 (file)
--- 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" }