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