1*adf37648SKyle Evans // SPDX-License-Identifier: GPL-2.0 OR MIT
2*adf37648SKyle Evans /*
3*adf37648SKyle Evans * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4*adf37648SKyle Evans */
5*adf37648SKyle Evans
6*adf37648SKyle Evans #include <stddef.h>
7*adf37648SKyle Evans #include <stdio.h>
8*adf37648SKyle Evans #include <stdlib.h>
9*adf37648SKyle Evans #include <string.h>
10*adf37648SKyle Evans
11*adf37648SKyle Evans #include "containers.h"
12*adf37648SKyle Evans #include "config.h"
13*adf37648SKyle Evans #include "ipc.h"
14*adf37648SKyle Evans #include "subcommands.h"
15*adf37648SKyle Evans
16*adf37648SKyle Evans struct pubkey_origin {
17*adf37648SKyle Evans uint8_t *pubkey;
18*adf37648SKyle Evans bool from_file;
19*adf37648SKyle Evans };
20*adf37648SKyle Evans
pubkey_cmp(const void * first,const void * second)21*adf37648SKyle Evans static int pubkey_cmp(const void *first, const void *second)
22*adf37648SKyle Evans {
23*adf37648SKyle Evans const struct pubkey_origin *a = first, *b = second;
24*adf37648SKyle Evans int ret = memcmp(a->pubkey, b->pubkey, WG_KEY_LEN);
25*adf37648SKyle Evans if (ret)
26*adf37648SKyle Evans return ret;
27*adf37648SKyle Evans return a->from_file - b->from_file;
28*adf37648SKyle Evans }
29*adf37648SKyle Evans
sync_conf(struct wgdevice * file)30*adf37648SKyle Evans static bool sync_conf(struct wgdevice *file)
31*adf37648SKyle Evans {
32*adf37648SKyle Evans struct wgdevice *runtime;
33*adf37648SKyle Evans struct wgpeer *peer;
34*adf37648SKyle Evans struct pubkey_origin *pubkeys;
35*adf37648SKyle Evans size_t peer_count = 0, i = 0;
36*adf37648SKyle Evans
37*adf37648SKyle Evans if (!file->first_peer)
38*adf37648SKyle Evans return true;
39*adf37648SKyle Evans
40*adf37648SKyle Evans for_each_wgpeer(file, peer)
41*adf37648SKyle Evans ++peer_count;
42*adf37648SKyle Evans
43*adf37648SKyle Evans if (ipc_get_device(&runtime, file->name) != 0) {
44*adf37648SKyle Evans perror("Unable to retrieve current interface configuration");
45*adf37648SKyle Evans return false;
46*adf37648SKyle Evans }
47*adf37648SKyle Evans
48*adf37648SKyle Evans if (!runtime->first_peer) {
49*adf37648SKyle Evans free_wgdevice(runtime);
50*adf37648SKyle Evans return true;
51*adf37648SKyle Evans }
52*adf37648SKyle Evans
53*adf37648SKyle Evans file->flags &= ~WGDEVICE_REPLACE_PEERS;
54*adf37648SKyle Evans
55*adf37648SKyle Evans for_each_wgpeer(runtime, peer)
56*adf37648SKyle Evans ++peer_count;
57*adf37648SKyle Evans
58*adf37648SKyle Evans pubkeys = calloc(peer_count, sizeof(*pubkeys));
59*adf37648SKyle Evans if (!pubkeys) {
60*adf37648SKyle Evans free_wgdevice(runtime);
61*adf37648SKyle Evans perror("Public key allocation");
62*adf37648SKyle Evans return false;
63*adf37648SKyle Evans }
64*adf37648SKyle Evans
65*adf37648SKyle Evans for_each_wgpeer(file, peer) {
66*adf37648SKyle Evans pubkeys[i].pubkey = peer->public_key;
67*adf37648SKyle Evans pubkeys[i].from_file = true;
68*adf37648SKyle Evans ++i;
69*adf37648SKyle Evans }
70*adf37648SKyle Evans for_each_wgpeer(runtime, peer) {
71*adf37648SKyle Evans pubkeys[i].pubkey = peer->public_key;
72*adf37648SKyle Evans pubkeys[i].from_file = false;
73*adf37648SKyle Evans ++i;
74*adf37648SKyle Evans }
75*adf37648SKyle Evans qsort(pubkeys, peer_count, sizeof(*pubkeys), pubkey_cmp);
76*adf37648SKyle Evans
77*adf37648SKyle Evans for (i = 0; i < peer_count; ++i) {
78*adf37648SKyle Evans if (pubkeys[i].from_file)
79*adf37648SKyle Evans continue;
80*adf37648SKyle Evans if (i == peer_count - 1 || !pubkeys[i + 1].from_file || memcmp(pubkeys[i].pubkey, pubkeys[i + 1].pubkey, WG_KEY_LEN)) {
81*adf37648SKyle Evans peer = calloc(1, sizeof(struct wgpeer));
82*adf37648SKyle Evans if (!peer) {
83*adf37648SKyle Evans free_wgdevice(runtime);
84*adf37648SKyle Evans free(pubkeys);
85*adf37648SKyle Evans perror("Peer allocation");
86*adf37648SKyle Evans return false;
87*adf37648SKyle Evans }
88*adf37648SKyle Evans peer->flags = WGPEER_REMOVE_ME;
89*adf37648SKyle Evans memcpy(peer->public_key, pubkeys[i].pubkey, WG_KEY_LEN);
90*adf37648SKyle Evans peer->next_peer = file->first_peer;
91*adf37648SKyle Evans file->first_peer = peer;
92*adf37648SKyle Evans if (!file->last_peer)
93*adf37648SKyle Evans file->last_peer = peer;
94*adf37648SKyle Evans }
95*adf37648SKyle Evans }
96*adf37648SKyle Evans free_wgdevice(runtime);
97*adf37648SKyle Evans free(pubkeys);
98*adf37648SKyle Evans return true;
99*adf37648SKyle Evans }
100*adf37648SKyle Evans
setconf_main(int argc,const char * argv[])101*adf37648SKyle Evans int setconf_main(int argc, const char *argv[])
102*adf37648SKyle Evans {
103*adf37648SKyle Evans struct wgdevice *device = NULL;
104*adf37648SKyle Evans struct config_ctx ctx;
105*adf37648SKyle Evans FILE *config_input = NULL;
106*adf37648SKyle Evans char *config_buffer = NULL;
107*adf37648SKyle Evans size_t config_buffer_len = 0;
108*adf37648SKyle Evans int ret = 1;
109*adf37648SKyle Evans
110*adf37648SKyle Evans if (argc != 3) {
111*adf37648SKyle Evans fprintf(stderr, "Usage: %s %s <interface> <configuration filename>\n", PROG_NAME, argv[0]);
112*adf37648SKyle Evans return 1;
113*adf37648SKyle Evans }
114*adf37648SKyle Evans
115*adf37648SKyle Evans config_input = fopen(argv[2], "r");
116*adf37648SKyle Evans if (!config_input) {
117*adf37648SKyle Evans perror("fopen");
118*adf37648SKyle Evans return 1;
119*adf37648SKyle Evans }
120*adf37648SKyle Evans if (!config_read_init(&ctx, !strcmp(argv[0], "addconf"))) {
121*adf37648SKyle Evans fclose(config_input);
122*adf37648SKyle Evans return 1;
123*adf37648SKyle Evans }
124*adf37648SKyle Evans while (getline(&config_buffer, &config_buffer_len, config_input) >= 0) {
125*adf37648SKyle Evans if (!config_read_line(&ctx, config_buffer)) {
126*adf37648SKyle Evans fprintf(stderr, "Configuration parsing error\n");
127*adf37648SKyle Evans goto cleanup;
128*adf37648SKyle Evans }
129*adf37648SKyle Evans }
130*adf37648SKyle Evans device = config_read_finish(&ctx);
131*adf37648SKyle Evans if (!device) {
132*adf37648SKyle Evans fprintf(stderr, "Invalid configuration\n");
133*adf37648SKyle Evans goto cleanup;
134*adf37648SKyle Evans }
135*adf37648SKyle Evans strncpy(device->name, argv[1], IFNAMSIZ - 1);
136*adf37648SKyle Evans device->name[IFNAMSIZ - 1] = '\0';
137*adf37648SKyle Evans
138*adf37648SKyle Evans if (!strcmp(argv[0], "syncconf")) {
139*adf37648SKyle Evans if (!sync_conf(device))
140*adf37648SKyle Evans goto cleanup;
141*adf37648SKyle Evans }
142*adf37648SKyle Evans
143*adf37648SKyle Evans if (ipc_set_device(device) != 0) {
144*adf37648SKyle Evans perror("Unable to modify interface");
145*adf37648SKyle Evans goto cleanup;
146*adf37648SKyle Evans }
147*adf37648SKyle Evans
148*adf37648SKyle Evans ret = 0;
149*adf37648SKyle Evans
150*adf37648SKyle Evans cleanup:
151*adf37648SKyle Evans if (config_input)
152*adf37648SKyle Evans fclose(config_input);
153*adf37648SKyle Evans free(config_buffer);
154*adf37648SKyle Evans free_wgdevice(device);
155*adf37648SKyle Evans return ret;
156*adf37648SKyle Evans }
157