wg: add syncconf command

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2019-06-11 19:22:52 +02:00
parent 34ea0caf1f
commit ae374129ab
3 changed files with 97 additions and 0 deletions

View 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.

View file

@ -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;

View file

@ -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" }