| @@ -1,816 +1,820 @@ | | | @@ -1,816 +1,820 @@ |
1 | /* $NetBSD: wgconfig.c,v 1.4 2020/08/21 17:51:31 martin Exp $ */ | | 1 | /* $NetBSD: wgconfig.c,v 1.5 2020/08/28 17:17:53 tih Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (C) Ryota Ozaki <ozaki.ryota@gmail.com> | | 4 | * Copyright (C) Ryota Ozaki <ozaki.ryota@gmail.com> |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. | | 11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 14 | * documentation and/or other materials provided with the distribution. |
15 | * 3. Neither the name of the project nor the names of its contributors | | 15 | * 3. Neither the name of the project nor the names of its contributors |
16 | * may be used to endorse or promote products derived from this software | | 16 | * may be used to endorse or promote products derived from this software |
17 | * without specific prior written permission. | | 17 | * without specific prior written permission. |
18 | * | | 18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND | | 19 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE | | 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 | * SUCH DAMAGE. | | 29 | * SUCH DAMAGE. |
30 | */ | | 30 | */ |
31 | | | 31 | |
32 | #include <sys/cdefs.h> | | 32 | #include <sys/cdefs.h> |
33 | __RCSID("$NetBSD: wgconfig.c,v 1.4 2020/08/21 17:51:31 martin Exp $"); | | 33 | __RCSID("$NetBSD: wgconfig.c,v 1.5 2020/08/28 17:17:53 tih Exp $"); |
34 | | | 34 | |
35 | #include <sys/ioctl.h> | | 35 | #include <sys/ioctl.h> |
36 | | | 36 | |
37 | #include <net/if.h> | | 37 | #include <net/if.h> |
38 | #include <net/if_wg.h> | | 38 | #include <net/if_wg.h> |
39 | | | 39 | |
40 | #include <arpa/inet.h> | | 40 | #include <arpa/inet.h> |
41 | | | 41 | |
42 | #include <stdio.h> | | 42 | #include <stdio.h> |
43 | #include <stdlib.h> | | 43 | #include <stdlib.h> |
44 | #include <string.h> | | 44 | #include <string.h> |
45 | #include <err.h> | | 45 | #include <err.h> |
46 | #include <unistd.h> | | 46 | #include <unistd.h> |
47 | #include <errno.h> | | 47 | #include <errno.h> |
48 | #include <resolv.h> | | 48 | #include <resolv.h> |
49 | #include <util.h> | | 49 | #include <util.h> |
50 | #include <netdb.h> | | 50 | #include <netdb.h> |
51 | | | 51 | |
52 | #include <prop/proplib.h> | | 52 | #include <prop/proplib.h> |
53 | | | 53 | |
54 | #define PROP_BUFFER_LEN 4096 | | 54 | #define PROP_BUFFER_LEN 4096 |
55 | #define KEY_LEN 32 | | 55 | #define KEY_LEN 32 |
56 | #define KEY_BASE64_LEN 44 | | 56 | #define KEY_BASE64_LEN 44 |
57 | | | 57 | |
58 | __dead static void | | 58 | __dead static void |
59 | usage(void) | | 59 | usage(void) |
60 | { | | 60 | { |
61 | const char *progname = getprogname(); | | 61 | const char *progname = getprogname(); |
62 | #define P(str) fprintf(stderr, "\t%s <interface> %s\n", progname, str) | | 62 | #define P(str) fprintf(stderr, "\t%s <interface> %s\n", progname, str) |
63 | | | 63 | |
64 | fprintf(stderr, "Usage:\n"); | | 64 | fprintf(stderr, "Usage:\n"); |
65 | P("[show all]"); | | 65 | P("[show all]"); |
66 | P("show peer <peer name> [--show-preshared-key]"); | | 66 | P("show peer <peer name> [--show-preshared-key]"); |
67 | P("show private-key"); | | 67 | P("show private-key"); |
68 | P("set private-key <file path>"); | | 68 | P("set private-key <file path>"); |
69 | P("set listen-port <port>"); | | 69 | P("set listen-port <port>"); |
70 | P("add peer <peer name> <base64 public key>\n" | | 70 | P("add peer <peer name> <base64 public key>\n" |
71 | "\t [--preshared-key=<file path>] [--endpoint=<ip>:<port>]\n" | | 71 | "\t [--preshared-key=<file path>] [--endpoint=<ip>:<port>]\n" |
72 | "\t [--allowed-ips=<ip1>/<cidr1>[,<ip2>/<cidr2>]...]"); | | 72 | "\t [--allowed-ips=<ip1>/<cidr1>[,<ip2>/<cidr2>]...]"); |
73 | P("delete peer <peer name>"); | | 73 | P("delete peer <peer name>"); |
74 | | | 74 | |
75 | exit(EXIT_FAILURE); | | 75 | exit(EXIT_FAILURE); |
76 | #undef P | | 76 | #undef P |
77 | } | | 77 | } |
78 | | | 78 | |
79 | static const char * | | 79 | static const char * |
80 | format_key(prop_object_t key_prop) | | 80 | format_key(prop_object_t key_prop) |
81 | { | | 81 | { |
82 | int error; | | 82 | int error; |
83 | const void *key; | | 83 | const void *key; |
84 | size_t key_len; | | 84 | size_t key_len; |
85 | static char key_b64[KEY_BASE64_LEN + 1]; | | 85 | static char key_b64[KEY_BASE64_LEN + 1]; |
86 | | | 86 | |
87 | if (key_prop == NULL) | | 87 | if (key_prop == NULL) |
88 | return "(none)"; | | 88 | return "(none)"; |
89 | if (prop_object_type(key_prop) != PROP_TYPE_DATA) | | 89 | if (prop_object_type(key_prop) != PROP_TYPE_DATA) |
90 | errx(EXIT_FAILURE, "invalid key"); | | 90 | errx(EXIT_FAILURE, "invalid key"); |
91 | | | 91 | |
92 | key = prop_data_value(key_prop); | | 92 | key = prop_data_value(key_prop); |
93 | key_len = prop_data_size(key_prop); | | 93 | key_len = prop_data_size(key_prop); |
94 | if (key_len != KEY_LEN) | | 94 | if (key_len != KEY_LEN) |
95 | errx(EXIT_FAILURE, "invalid key len: %zu", key_len); | | 95 | errx(EXIT_FAILURE, "invalid key len: %zu", key_len); |
96 | error = b64_ntop(key, key_len, key_b64, KEY_BASE64_LEN + 1); | | 96 | error = b64_ntop(key, key_len, key_b64, KEY_BASE64_LEN + 1); |
97 | if (error == -1) | | 97 | if (error == -1) |
98 | errx(EXIT_FAILURE, "b64_ntop failed"); | | 98 | errx(EXIT_FAILURE, "b64_ntop failed"); |
99 | key_b64[KEY_BASE64_LEN] = '\0'; /* just in case */ | | 99 | key_b64[KEY_BASE64_LEN] = '\0'; /* just in case */ |
100 | | | 100 | |
101 | return key_b64; | | 101 | return key_b64; |
102 | } | | 102 | } |
103 | | | 103 | |
104 | static const char * | | 104 | static const char * |
105 | format_endpoint(prop_object_t endpoint_prop) | | 105 | format_endpoint(prop_object_t endpoint_prop) |
106 | { | | 106 | { |
107 | int error; | | 107 | int error; |
108 | static char buf[INET6_ADDRSTRLEN]; | | 108 | static char buf[INET6_ADDRSTRLEN]; |
109 | struct sockaddr_storage sockaddr; | | 109 | struct sockaddr_storage sockaddr; |
110 | const void *addr; | | 110 | const void *addr; |
111 | size_t addr_len; | | 111 | size_t addr_len; |
112 | | | 112 | |
113 | if (prop_object_type(endpoint_prop) != PROP_TYPE_DATA) | | 113 | if (prop_object_type(endpoint_prop) != PROP_TYPE_DATA) |
114 | errx(EXIT_FAILURE, "invalid endpoint"); | | 114 | errx(EXIT_FAILURE, "invalid endpoint"); |
115 | | | 115 | |
116 | addr = prop_data_value(endpoint_prop); | | 116 | addr = prop_data_value(endpoint_prop); |
117 | addr_len = prop_data_size(endpoint_prop); | | 117 | addr_len = prop_data_size(endpoint_prop); |
118 | memcpy(&sockaddr, addr, addr_len); | | 118 | memcpy(&sockaddr, addr, addr_len); |
119 | | | 119 | |
120 | error = sockaddr_snprintf(buf, sizeof(buf), "%a:%p", | | 120 | error = sockaddr_snprintf(buf, sizeof(buf), "%a:%p", |
121 | (struct sockaddr *)&sockaddr); | | 121 | (struct sockaddr *)&sockaddr); |
122 | if (error == -1) | | 122 | if (error == -1) |
123 | err(EXIT_FAILURE, "sockaddr_snprintf failed"); | | 123 | err(EXIT_FAILURE, "sockaddr_snprintf failed"); |
124 | | | 124 | |
125 | return buf; | | 125 | return buf; |
126 | } | | 126 | } |
127 | | | 127 | |
128 | static void | | 128 | static void |
129 | handle_allowed_ips(prop_dictionary_t peer, const char *prefix) | | 129 | handle_allowed_ips(prop_dictionary_t peer, const char *prefix) |
130 | { | | 130 | { |
131 | prop_object_t prop_obj; | | 131 | prop_object_t prop_obj; |
132 | prop_array_t allowedips; | | 132 | prop_array_t allowedips; |
133 | prop_object_iterator_t it; | | 133 | prop_object_iterator_t it; |
134 | prop_dictionary_t allowedip; | | 134 | prop_dictionary_t allowedip; |
135 | bool first = true; | | 135 | bool first = true; |
136 | | | 136 | |
137 | prop_obj = prop_dictionary_get(peer, "allowedips"); | | 137 | prop_obj = prop_dictionary_get(peer, "allowedips"); |
138 | if (prop_obj == NULL) | | 138 | if (prop_obj == NULL) |
139 | return; | | 139 | return; |
140 | if (prop_object_type(prop_obj) != PROP_TYPE_ARRAY) | | 140 | if (prop_object_type(prop_obj) != PROP_TYPE_ARRAY) |
141 | errx(EXIT_FAILURE, "invalid allowedips"); | | 141 | errx(EXIT_FAILURE, "invalid allowedips"); |
142 | allowedips = prop_obj; | | 142 | allowedips = prop_obj; |
143 | | | 143 | |
144 | printf("%sallowed-ips: ", prefix); | | 144 | printf("%sallowed-ips: ", prefix); |
145 | | | 145 | |
146 | it = prop_array_iterator(allowedips); | | 146 | it = prop_array_iterator(allowedips); |
147 | while ((prop_obj = prop_object_iterator_next(it)) != NULL) { | | 147 | while ((prop_obj = prop_object_iterator_next(it)) != NULL) { |
148 | uint8_t family; | | 148 | uint8_t family; |
149 | uint8_t cidr; | | 149 | uint8_t cidr; |
150 | const void *addr; | | 150 | const void *addr; |
151 | size_t addrlen, famaddrlen; | | 151 | size_t addrlen, famaddrlen; |
152 | char ntopbuf[INET6_ADDRSTRLEN]; | | 152 | char ntopbuf[INET6_ADDRSTRLEN]; |
153 | const char *ntopret; | | 153 | const char *ntopret; |
154 | | | 154 | |
155 | if (prop_object_type(prop_obj) != PROP_TYPE_DICTIONARY) { | | 155 | if (prop_object_type(prop_obj) != PROP_TYPE_DICTIONARY) { |
156 | warnx("invalid allowedip"); | | 156 | warnx("invalid allowedip"); |
157 | continue; | | 157 | continue; |
158 | } | | 158 | } |
159 | allowedip = prop_obj; | | 159 | allowedip = prop_obj; |
160 | | | 160 | |
161 | if (!prop_dictionary_get_uint8(allowedip, "family", &family)) { | | 161 | if (!prop_dictionary_get_uint8(allowedip, "family", &family)) { |
162 | warnx("allowed-ip without family"); | | 162 | warnx("allowed-ip without family"); |
163 | continue; | | 163 | continue; |
164 | } | | 164 | } |
165 | | | 165 | |
166 | if (!prop_dictionary_get_uint8(allowedip, "cidr", &cidr)) { | | 166 | if (!prop_dictionary_get_uint8(allowedip, "cidr", &cidr)) { |
167 | warnx("allowed-ip without cidr"); | | 167 | warnx("allowed-ip without cidr"); |
168 | continue; | | 168 | continue; |
169 | } | | 169 | } |
170 | | | 170 | |
171 | if (!prop_dictionary_get_data(allowedip, "ip", | | 171 | if (!prop_dictionary_get_data(allowedip, "ip", |
172 | &addr, &addrlen)) { | | 172 | &addr, &addrlen)) { |
173 | warnx("allowed-ip without ip"); | | 173 | warnx("allowed-ip without ip"); |
174 | continue; | | 174 | continue; |
175 | } | | 175 | } |
176 | | | 176 | |
177 | switch (family) { | | 177 | switch (family) { |
178 | case AF_INET: | | 178 | case AF_INET: |
179 | famaddrlen = sizeof(struct in_addr); | | 179 | famaddrlen = sizeof(struct in_addr); |
180 | break; | | 180 | break; |
181 | case AF_INET6: | | 181 | case AF_INET6: |
182 | famaddrlen = sizeof(struct in6_addr); | | 182 | famaddrlen = sizeof(struct in6_addr); |
183 | break; | | 183 | break; |
184 | default: | | 184 | default: |
185 | warnx("unknown family %d", family); | | 185 | warnx("unknown family %d", family); |
186 | continue; | | 186 | continue; |
187 | } | | 187 | } |
188 | if (addrlen != famaddrlen) { | | 188 | if (addrlen != famaddrlen) { |
189 | warnx("allowed-ip bad ip length"); | | 189 | warnx("allowed-ip bad ip length"); |
190 | continue; | | 190 | continue; |
191 | } | | 191 | } |
192 | | | 192 | |
193 | ntopret = inet_ntop(family, addr, ntopbuf, sizeof(ntopbuf)); | | 193 | ntopret = inet_ntop(family, addr, ntopbuf, sizeof(ntopbuf)); |
194 | if (ntopret == NULL) | | 194 | if (ntopret == NULL) |
195 | errx(EXIT_FAILURE, "inet_ntop failed"); | | 195 | errx(EXIT_FAILURE, "inet_ntop failed"); |
196 | printf("%s%s/%u", first ? "" : ",", ntopbuf, cidr); | | 196 | printf("%s%s/%u", first ? "" : ",", ntopbuf, cidr); |
197 | first = false; | | 197 | first = false; |
198 | } | | 198 | } |
199 | if (first) | | 199 | if (first) |
200 | printf("(none)\n"); | | 200 | printf("(none)\n"); |
201 | else | | 201 | else |
202 | printf("\n"); | | 202 | printf("\n"); |
203 | } | | 203 | } |
204 | | | 204 | |
205 | static prop_dictionary_t | | 205 | static prop_dictionary_t |
206 | ioctl_get(const char *interface) | | 206 | ioctl_get(const char *interface) |
207 | { | | 207 | { |
208 | int error = 0; | | 208 | int error = 0; |
209 | struct ifdrv ifd; | | 209 | struct ifdrv ifd; |
210 | int sock; | | 210 | int sock; |
211 | char *buf; | | 211 | char *buf; |
212 | prop_dictionary_t prop_dict; | | 212 | prop_dictionary_t prop_dict; |
213 | | | 213 | |
214 | sock = socket(AF_INET, SOCK_DGRAM, 0); | | 214 | sock = socket(AF_INET, SOCK_DGRAM, 0); |
215 | if (error == -1) | | 215 | if (error == -1) |
216 | err(EXIT_FAILURE, "socket"); | | 216 | err(EXIT_FAILURE, "socket"); |
217 | buf = malloc(PROP_BUFFER_LEN); | | 217 | buf = malloc(PROP_BUFFER_LEN); |
218 | if (buf == NULL) | | 218 | if (buf == NULL) |
219 | errx(EXIT_FAILURE, "malloc failed"); | | 219 | errx(EXIT_FAILURE, "malloc failed"); |
220 | | | 220 | |
221 | strlcpy(ifd.ifd_name, interface, sizeof(ifd.ifd_name)); | | 221 | strlcpy(ifd.ifd_name, interface, sizeof(ifd.ifd_name)); |
222 | ifd.ifd_cmd = 0; | | 222 | ifd.ifd_cmd = 0; |
223 | ifd.ifd_data = buf; | | 223 | ifd.ifd_data = buf; |
224 | ifd.ifd_len = PROP_BUFFER_LEN; | | 224 | ifd.ifd_len = PROP_BUFFER_LEN; |
225 | | | 225 | |
226 | error = ioctl(sock, SIOCGDRVSPEC, &ifd); | | 226 | error = ioctl(sock, SIOCGDRVSPEC, &ifd); |
227 | if (error == -1) | | 227 | if (error == -1) |
228 | err(EXIT_FAILURE, "ioctl(SIOCGDRVSPEC)"); | | 228 | err(EXIT_FAILURE, "ioctl(SIOCGDRVSPEC)"); |
229 | | | 229 | |
230 | prop_dict = prop_dictionary_internalize(buf); | | 230 | prop_dict = prop_dictionary_internalize(buf); |
231 | if (prop_dict == NULL) | | 231 | if (prop_dict == NULL) |
232 | errx(EXIT_FAILURE, "prop_dictionary_internalize failed"); | | 232 | errx(EXIT_FAILURE, "prop_dictionary_internalize failed"); |
233 | | | 233 | |
234 | free(buf); | | 234 | free(buf); |
235 | close(sock); | | 235 | close(sock); |
236 | | | 236 | |
237 | return prop_dict; | | 237 | return prop_dict; |
238 | } | | 238 | } |
239 | | | 239 | |
240 | static void | | 240 | static void |
241 | show_peer(prop_dictionary_t peer, const char *prefix, bool show_psk) | | 241 | show_peer(prop_dictionary_t peer, const char *prefix, bool show_psk) |
242 | { | | 242 | { |
243 | prop_object_t prop_obj; | | 243 | prop_object_t prop_obj; |
244 | uint64_t sec; | | 244 | time_t sec; |
245 | | | 245 | |
246 | prop_obj = prop_dictionary_get(peer, "public_key"); | | 246 | prop_obj = prop_dictionary_get(peer, "public_key"); |
247 | if (prop_obj == NULL) { | | 247 | if (prop_obj == NULL) { |
248 | warnx("peer without public-key"); | | 248 | warnx("peer without public-key"); |
249 | return; | | 249 | return; |
250 | } | | 250 | } |
251 | printf("%spublic-key: %s\n", prefix, format_key(prop_obj)); | | 251 | printf("%spublic-key: %s\n", prefix, format_key(prop_obj)); |
252 | | | 252 | |
253 | prop_obj = prop_dictionary_get(peer, "endpoint"); | | 253 | prop_obj = prop_dictionary_get(peer, "endpoint"); |
254 | if (prop_obj == NULL) | | 254 | if (prop_obj == NULL) |
255 | printf("%sendpoint: (none)\n", prefix); | | 255 | printf("%sendpoint: (none)\n", prefix); |
256 | else | | 256 | else |
257 | printf("%sendpoint: %s\n", prefix, format_endpoint(prop_obj)); | | 257 | printf("%sendpoint: %s\n", prefix, format_endpoint(prop_obj)); |
258 | | | 258 | |
259 | if (show_psk) { | | 259 | if (show_psk) { |
260 | prop_obj = prop_dictionary_get(peer, "preshared_key"); | | 260 | prop_obj = prop_dictionary_get(peer, "preshared_key"); |
261 | printf("%spreshared-key: %s\n", prefix, format_key(prop_obj)); | | 261 | printf("%spreshared-key: %s\n", prefix, format_key(prop_obj)); |
262 | } else { | | 262 | } else { |
263 | printf("%spreshared-key: (hidden)\n", prefix); | | 263 | printf("%spreshared-key: (hidden)\n", prefix); |
264 | } | | 264 | } |
265 | | | 265 | |
266 | handle_allowed_ips(peer, prefix); | | 266 | handle_allowed_ips(peer, prefix); |
267 | | | 267 | |
268 | if (prop_dictionary_get_uint64(peer, "last_handshake_time_sec", &sec)) | | 268 | if (prop_dictionary_get_int64(peer, "last_handshake_time_sec", &sec)) { |
269 | printf("%slatest-handshake: %"PRIu64"\n", prefix, sec); | | 269 | if (sec > 0) |
270 | else | | 270 | printf("%slatest-handshake: %s", prefix, ctime(&sec)); |
| | | 271 | else |
| | | 272 | printf("%slatest-handshake: (never)\n", prefix); |
| | | 273 | } else { |
271 | printf("%slatest-handshake: (none)\n", prefix); | | 274 | printf("%slatest-handshake: (none)\n", prefix); |
| | | 275 | } |
272 | } | | 276 | } |
273 | | | 277 | |
274 | static int | | 278 | static int |
275 | cmd_show_all(const char *interface, int argc, char *argv[]) | | 279 | cmd_show_all(const char *interface, int argc, char *argv[]) |
276 | { | | 280 | { |
277 | prop_dictionary_t prop_dict; | | 281 | prop_dictionary_t prop_dict; |
278 | prop_object_t prop_obj; | | 282 | prop_object_t prop_obj; |
279 | uint16_t port; | | 283 | uint16_t port; |
280 | prop_array_t peers; | | 284 | prop_array_t peers; |
281 | | | 285 | |
282 | prop_dict = ioctl_get(interface); | | 286 | prop_dict = ioctl_get(interface); |
283 | | | 287 | |
284 | printf("interface: %s\n", interface); | | 288 | printf("interface: %s\n", interface); |
285 | | | 289 | |
286 | #if 0 | | 290 | #if 0 |
287 | prop_obj = prop_dictionary_get(prop_dict, "private_key"); | | 291 | prop_obj = prop_dictionary_get(prop_dict, "private_key"); |
288 | printf("\tprivate-key: %s\n", format_key(prop_obj)); | | 292 | printf("\tprivate-key: %s\n", format_key(prop_obj)); |
289 | #else | | 293 | #else |
290 | printf("\tprivate-key: (hidden)\n"); | | 294 | printf("\tprivate-key: (hidden)\n"); |
291 | #endif | | 295 | #endif |
292 | | | 296 | |
293 | if (prop_dictionary_get_uint16(prop_dict, "listen_port", &port)) { | | 297 | if (prop_dictionary_get_uint16(prop_dict, "listen_port", &port)) { |
294 | printf("\tlisten-port: %u\n", port); | | 298 | printf("\tlisten-port: %u\n", port); |
295 | } else { | | 299 | } else { |
296 | printf("\tlisten-port: (none)\n"); | | 300 | printf("\tlisten-port: (none)\n"); |
297 | } | | 301 | } |
298 | | | 302 | |
299 | prop_obj = prop_dictionary_get(prop_dict, "peers"); | | 303 | prop_obj = prop_dictionary_get(prop_dict, "peers"); |
300 | if (prop_obj == NULL) | | 304 | if (prop_obj == NULL) |
301 | return EXIT_SUCCESS; | | 305 | return EXIT_SUCCESS; |
302 | if (prop_object_type(prop_obj) != PROP_TYPE_ARRAY) | | 306 | if (prop_object_type(prop_obj) != PROP_TYPE_ARRAY) |
303 | errx(EXIT_FAILURE, "invalid peers"); | | 307 | errx(EXIT_FAILURE, "invalid peers"); |
304 | peers = prop_obj; | | 308 | peers = prop_obj; |
305 | | | 309 | |
306 | prop_object_iterator_t it = prop_array_iterator(peers); | | 310 | prop_object_iterator_t it = prop_array_iterator(peers); |
307 | while ((prop_obj = prop_object_iterator_next(it)) != NULL) { | | 311 | while ((prop_obj = prop_object_iterator_next(it)) != NULL) { |
308 | const char *name; | | 312 | const char *name; |
309 | | | 313 | |
310 | if (prop_object_type(prop_obj) != PROP_TYPE_DICTIONARY) | | 314 | if (prop_object_type(prop_obj) != PROP_TYPE_DICTIONARY) |
311 | errx(EXIT_FAILURE, "invalid peer"); | | 315 | errx(EXIT_FAILURE, "invalid peer"); |
312 | prop_dictionary_t peer = prop_obj; | | 316 | prop_dictionary_t peer = prop_obj; |
313 | | | 317 | |
314 | if (prop_dictionary_get_string(peer, "name", &name)) { | | 318 | if (prop_dictionary_get_string(peer, "name", &name)) { |
315 | printf("\tpeer: %s\n", name); | | 319 | printf("\tpeer: %s\n", name); |
316 | } else | | 320 | } else |
317 | printf("\tpeer: (none)\n"); | | 321 | printf("\tpeer: (none)\n"); |
318 | | | 322 | |
319 | show_peer(peer, "\t\t", false); | | 323 | show_peer(peer, "\t\t", false); |
320 | } | | 324 | } |
321 | | | 325 | |
322 | return EXIT_SUCCESS; | | 326 | return EXIT_SUCCESS; |
323 | } | | 327 | } |
324 | | | 328 | |
325 | static int | | 329 | static int |
326 | cmd_show_peer(const char *interface, int argc, char *argv[]) | | 330 | cmd_show_peer(const char *interface, int argc, char *argv[]) |
327 | { | | 331 | { |
328 | prop_dictionary_t prop_dict; | | 332 | prop_dictionary_t prop_dict; |
329 | prop_object_t prop_obj; | | 333 | prop_object_t prop_obj; |
330 | const char *target; | | 334 | const char *target; |
331 | const char *opt = "--show-preshared-key"; | | 335 | const char *opt = "--show-preshared-key"; |
332 | bool show_psk = false; | | 336 | bool show_psk = false; |
333 | | | 337 | |
334 | if (argc != 1 && argc != 2) | | 338 | if (argc != 1 && argc != 2) |
335 | usage(); | | 339 | usage(); |
336 | target = argv[0]; | | 340 | target = argv[0]; |
337 | if (argc == 2) { | | 341 | if (argc == 2) { |
338 | if (strncmp(argv[1], opt, strlen(opt)) != 0) | | 342 | if (strncmp(argv[1], opt, strlen(opt)) != 0) |
339 | usage(); | | 343 | usage(); |
340 | show_psk = true; | | 344 | show_psk = true; |
341 | } | | 345 | } |
342 | | | 346 | |
343 | prop_dict = ioctl_get(interface); | | 347 | prop_dict = ioctl_get(interface); |
344 | | | 348 | |
345 | prop_obj = prop_dictionary_get(prop_dict, "peers"); | | 349 | prop_obj = prop_dictionary_get(prop_dict, "peers"); |
346 | if (prop_obj == NULL) | | 350 | if (prop_obj == NULL) |
347 | return EXIT_SUCCESS; | | 351 | return EXIT_SUCCESS; |
348 | if (prop_object_type(prop_obj) != PROP_TYPE_ARRAY) | | 352 | if (prop_object_type(prop_obj) != PROP_TYPE_ARRAY) |
349 | errx(EXIT_FAILURE, "invalid peers"); | | 353 | errx(EXIT_FAILURE, "invalid peers"); |
350 | | | 354 | |
351 | prop_array_t peers = prop_obj; | | 355 | prop_array_t peers = prop_obj; |
352 | prop_object_iterator_t it = prop_array_iterator(peers); | | 356 | prop_object_iterator_t it = prop_array_iterator(peers); |
353 | while ((prop_obj = prop_object_iterator_next(it)) != NULL) { | | 357 | while ((prop_obj = prop_object_iterator_next(it)) != NULL) { |
354 | const char *name; | | 358 | const char *name; |
355 | | | 359 | |
356 | if (prop_object_type(prop_obj) != PROP_TYPE_DICTIONARY) | | 360 | if (prop_object_type(prop_obj) != PROP_TYPE_DICTIONARY) |
357 | errx(EXIT_FAILURE, "invalid peer"); | | 361 | errx(EXIT_FAILURE, "invalid peer"); |
358 | prop_dictionary_t peer = prop_obj; | | 362 | prop_dictionary_t peer = prop_obj; |
359 | | | 363 | |
360 | if (!prop_dictionary_get_string(peer, "name", &name)) | | 364 | if (!prop_dictionary_get_string(peer, "name", &name)) |
361 | continue; | | 365 | continue; |
362 | if (strcmp(name, target) == 0) { | | 366 | if (strcmp(name, target) == 0) { |
363 | printf("peer: %s\n", name); | | 367 | printf("peer: %s\n", name); |
364 | show_peer(peer, "\t", show_psk); | | 368 | show_peer(peer, "\t", show_psk); |
365 | return EXIT_SUCCESS; | | 369 | return EXIT_SUCCESS; |
366 | } | | 370 | } |
367 | } | | 371 | } |
368 | | | 372 | |
369 | return EXIT_FAILURE; | | 373 | return EXIT_FAILURE; |
370 | } | | 374 | } |
371 | | | 375 | |
372 | static int | | 376 | static int |
373 | cmd_show_private_key(const char *interface, int argc, char *argv[]) | | 377 | cmd_show_private_key(const char *interface, int argc, char *argv[]) |
374 | { | | 378 | { |
375 | prop_dictionary_t prop_dict; | | 379 | prop_dictionary_t prop_dict; |
376 | prop_object_t prop_obj; | | 380 | prop_object_t prop_obj; |
377 | | | 381 | |
378 | prop_dict = ioctl_get(interface); | | 382 | prop_dict = ioctl_get(interface); |
379 | | | 383 | |
380 | prop_obj = prop_dictionary_get(prop_dict, "private_key"); | | 384 | prop_obj = prop_dictionary_get(prop_dict, "private_key"); |
381 | printf("private-key: %s\n", format_key(prop_obj)); | | 385 | printf("private-key: %s\n", format_key(prop_obj)); |
382 | | | 386 | |
383 | return EXIT_SUCCESS; | | 387 | return EXIT_SUCCESS; |
384 | } | | 388 | } |
385 | | | 389 | |
386 | static void | | 390 | static void |
387 | ioctl_set(const char *interface, int cmd, char *propstr) | | 391 | ioctl_set(const char *interface, int cmd, char *propstr) |
388 | { | | 392 | { |
389 | int error; | | 393 | int error; |
390 | struct ifdrv ifd; | | 394 | struct ifdrv ifd; |
391 | int sock; | | 395 | int sock; |
392 | | | 396 | |
393 | strlcpy(ifd.ifd_name, interface, sizeof(ifd.ifd_name)); | | 397 | strlcpy(ifd.ifd_name, interface, sizeof(ifd.ifd_name)); |
394 | ifd.ifd_cmd = cmd; | | 398 | ifd.ifd_cmd = cmd; |
395 | ifd.ifd_data = propstr; | | 399 | ifd.ifd_data = propstr; |
396 | ifd.ifd_len = strlen(propstr); | | 400 | ifd.ifd_len = strlen(propstr); |
397 | sock = socket(AF_INET, SOCK_DGRAM, 0); | | 401 | sock = socket(AF_INET, SOCK_DGRAM, 0); |
398 | error = ioctl(sock, SIOCSDRVSPEC, &ifd); | | 402 | error = ioctl(sock, SIOCSDRVSPEC, &ifd); |
399 | if (error == -1) | | 403 | if (error == -1) |
400 | err(EXIT_FAILURE, "ioctl(SIOCSDRVSPEC): cmd=%d", cmd); | | 404 | err(EXIT_FAILURE, "ioctl(SIOCSDRVSPEC): cmd=%d", cmd); |
401 | close(sock); | | 405 | close(sock); |
402 | } | | 406 | } |
403 | | | 407 | |
404 | static void | | 408 | static void |
405 | base64_decode(const char keyb64buf[KEY_BASE64_LEN + 1], | | 409 | base64_decode(const char keyb64buf[KEY_BASE64_LEN + 1], |
406 | unsigned char keybuf[KEY_LEN]) | | 410 | unsigned char keybuf[KEY_LEN]) |
407 | { | | 411 | { |
408 | int ret; | | 412 | int ret; |
409 | | | 413 | |
410 | ret = b64_pton(keyb64buf, keybuf, KEY_LEN); | | 414 | ret = b64_pton(keyb64buf, keybuf, KEY_LEN); |
411 | if (ret == -1) | | 415 | if (ret == -1) |
412 | errx(EXIT_FAILURE, "b64_pton failed"); | | 416 | errx(EXIT_FAILURE, "b64_pton failed"); |
413 | } | | 417 | } |
414 | | | 418 | |
415 | static void | | 419 | static void |
416 | read_key(const char *path, unsigned char keybuf[KEY_LEN]) | | 420 | read_key(const char *path, unsigned char keybuf[KEY_LEN]) |
417 | { | | 421 | { |
418 | FILE *fp; | | 422 | FILE *fp; |
419 | char keyb64buf[KEY_BASE64_LEN + 1]; | | 423 | char keyb64buf[KEY_BASE64_LEN + 1]; |
420 | size_t n; | | 424 | size_t n; |
421 | | | 425 | |
422 | fp = fopen(path, "r"); | | 426 | fp = fopen(path, "r"); |
423 | if (fp == NULL) | | 427 | if (fp == NULL) |
424 | err(EXIT_FAILURE, "fopen"); | | 428 | err(EXIT_FAILURE, "fopen"); |
425 | | | 429 | |
426 | n = fread(keyb64buf, 1, KEY_BASE64_LEN, fp); | | 430 | n = fread(keyb64buf, 1, KEY_BASE64_LEN, fp); |
427 | if (n != KEY_BASE64_LEN) | | 431 | if (n != KEY_BASE64_LEN) |
428 | errx(EXIT_FAILURE, "base64 key len is short: %zu", n); | | 432 | errx(EXIT_FAILURE, "base64 key len is short: %zu", n); |
429 | keyb64buf[KEY_BASE64_LEN] = '\0'; | | 433 | keyb64buf[KEY_BASE64_LEN] = '\0'; |
430 | | | 434 | |
431 | base64_decode(keyb64buf, keybuf); | | 435 | base64_decode(keyb64buf, keybuf); |
432 | } | | 436 | } |
433 | | | 437 | |
434 | static int | | 438 | static int |
435 | cmd_set_private_key(const char *interface, int argc, char *argv[]) | | 439 | cmd_set_private_key(const char *interface, int argc, char *argv[]) |
436 | { | | 440 | { |
437 | unsigned char keybuf[KEY_LEN]; | | 441 | unsigned char keybuf[KEY_LEN]; |
438 | | | 442 | |
439 | if (argc != 1) | | 443 | if (argc != 1) |
440 | usage(); | | 444 | usage(); |
441 | | | 445 | |
442 | read_key(argv[0], keybuf); | | 446 | read_key(argv[0], keybuf); |
443 | | | 447 | |
444 | prop_dictionary_t prop_dict; | | 448 | prop_dictionary_t prop_dict; |
445 | prop_dict = prop_dictionary_create(); | | 449 | prop_dict = prop_dictionary_create(); |
446 | if (prop_dict == NULL) | | 450 | if (prop_dict == NULL) |
447 | errx(EXIT_FAILURE, "prop_dictionary_create"); | | 451 | errx(EXIT_FAILURE, "prop_dictionary_create"); |
448 | | | 452 | |
449 | if (!prop_dictionary_set_data(prop_dict, "private_key", | | 453 | if (!prop_dictionary_set_data(prop_dict, "private_key", |
450 | keybuf, sizeof(keybuf))) | | 454 | keybuf, sizeof(keybuf))) |
451 | errx(EXIT_FAILURE, "prop_dictionary_set_data"); | | 455 | errx(EXIT_FAILURE, "prop_dictionary_set_data"); |
452 | | | 456 | |
453 | char *buf = prop_dictionary_externalize(prop_dict); | | 457 | char *buf = prop_dictionary_externalize(prop_dict); |
454 | if (buf == NULL) | | 458 | if (buf == NULL) |
455 | err(EXIT_FAILURE, "prop_dictionary_externalize failed"); | | 459 | err(EXIT_FAILURE, "prop_dictionary_externalize failed"); |
456 | ioctl_set(interface, WG_IOCTL_SET_PRIVATE_KEY, buf); | | 460 | ioctl_set(interface, WG_IOCTL_SET_PRIVATE_KEY, buf); |
457 | | | 461 | |
458 | return EXIT_SUCCESS; | | 462 | return EXIT_SUCCESS; |
459 | } | | 463 | } |
460 | | | 464 | |
461 | static uint16_t | | 465 | static uint16_t |
462 | strtouint16(const char *str) | | 466 | strtouint16(const char *str) |
463 | { | | 467 | { |
464 | char *ep; | | 468 | char *ep; |
465 | long val; | | 469 | long val; |
466 | | | 470 | |
467 | errno = 0; | | 471 | errno = 0; |
468 | val = strtol(str, &ep, 10); | | 472 | val = strtol(str, &ep, 10); |
469 | if (ep == str) | | 473 | if (ep == str) |
470 | errx(EXIT_FAILURE, "strtol: not a number"); | | 474 | errx(EXIT_FAILURE, "strtol: not a number"); |
471 | if (*ep != '\0') | | 475 | if (*ep != '\0') |
472 | errx(EXIT_FAILURE, "strtol: trailing garbage"); | | 476 | errx(EXIT_FAILURE, "strtol: trailing garbage"); |
473 | if (errno != 0) | | 477 | if (errno != 0) |
474 | err(EXIT_FAILURE, "strtol"); | | 478 | err(EXIT_FAILURE, "strtol"); |
475 | if (val < 0 || val > USHRT_MAX) | | 479 | if (val < 0 || val > USHRT_MAX) |
476 | errx(EXIT_FAILURE, "out of range"); | | 480 | errx(EXIT_FAILURE, "out of range"); |
477 | | | 481 | |
478 | return (uint16_t)val; | | 482 | return (uint16_t)val; |
479 | } | | 483 | } |
480 | | | 484 | |
481 | static int | | 485 | static int |
482 | cmd_set_listen_port(const char *interface, int argc, char *argv[]) | | 486 | cmd_set_listen_port(const char *interface, int argc, char *argv[]) |
483 | { | | 487 | { |
484 | uint16_t port; | | 488 | uint16_t port; |
485 | | | 489 | |
486 | if (argc != 1) | | 490 | if (argc != 1) |
487 | usage(); | | 491 | usage(); |
488 | | | 492 | |
489 | port = strtouint16(argv[0]); | | 493 | port = strtouint16(argv[0]); |
490 | if (port == 0) | | 494 | if (port == 0) |
491 | errx(EXIT_FAILURE, "port 0 is not allowed"); | | 495 | errx(EXIT_FAILURE, "port 0 is not allowed"); |
492 | | | 496 | |
493 | prop_dictionary_t prop_dict; | | 497 | prop_dictionary_t prop_dict; |
494 | prop_dict = prop_dictionary_create(); | | 498 | prop_dict = prop_dictionary_create(); |
495 | if (prop_dict == NULL) | | 499 | if (prop_dict == NULL) |
496 | errx(EXIT_FAILURE, "prop_dictionary_create"); | | 500 | errx(EXIT_FAILURE, "prop_dictionary_create"); |
497 | | | 501 | |
498 | if (!prop_dictionary_set_uint16(prop_dict, "listen_port", port)) | | 502 | if (!prop_dictionary_set_uint16(prop_dict, "listen_port", port)) |
499 | errx(EXIT_FAILURE, "prop_dictionary_set_uint16"); | | 503 | errx(EXIT_FAILURE, "prop_dictionary_set_uint16"); |
500 | | | 504 | |
501 | char *buf = prop_dictionary_externalize(prop_dict); | | 505 | char *buf = prop_dictionary_externalize(prop_dict); |
502 | if (buf == NULL) | | 506 | if (buf == NULL) |
503 | err(EXIT_FAILURE, "prop_dictionary_externalize failed"); | | 507 | err(EXIT_FAILURE, "prop_dictionary_externalize failed"); |
504 | ioctl_set(interface, WG_IOCTL_SET_LISTEN_PORT, buf); | | 508 | ioctl_set(interface, WG_IOCTL_SET_LISTEN_PORT, buf); |
505 | | | 509 | |
506 | return EXIT_SUCCESS; | | 510 | return EXIT_SUCCESS; |
507 | } | | 511 | } |
508 | | | 512 | |
509 | static void | | 513 | static void |
510 | handle_option_endpoint(const char *_addr_port, prop_dictionary_t prop_dict) | | 514 | handle_option_endpoint(const char *_addr_port, prop_dictionary_t prop_dict) |
511 | { | | 515 | { |
512 | int error; | | 516 | int error; |
513 | char *port; | | 517 | char *port; |
514 | struct addrinfo hints, *res; | | 518 | struct addrinfo hints, *res; |
515 | char *addr_port, *addr; | | 519 | char *addr_port, *addr; |
516 | | | 520 | |
517 | addr = addr_port = strdup(_addr_port); | | 521 | addr = addr_port = strdup(_addr_port); |
518 | | | 522 | |
519 | if (addr_port[0] == '[') { | | 523 | if (addr_port[0] == '[') { |
520 | /* [<ipv6>]:<port> */ | | 524 | /* [<ipv6>]:<port> */ |
521 | /* Accept [<ipv4>]:<port> too, but it's not a big deal. */ | | 525 | /* Accept [<ipv4>]:<port> too, but it's not a big deal. */ |
522 | char *bracket, *colon; | | 526 | char *bracket, *colon; |
523 | if (strlen(addr_port) < strlen("[::]:0")) | | 527 | if (strlen(addr_port) < strlen("[::]:0")) |
524 | errx(EXIT_FAILURE, "invalid endpoint format"); | | 528 | errx(EXIT_FAILURE, "invalid endpoint format"); |
525 | addr = addr_port + 1; | | 529 | addr = addr_port + 1; |
526 | bracket = strchr(addr, ']'); | | 530 | bracket = strchr(addr, ']'); |
527 | if (bracket == NULL) | | 531 | if (bracket == NULL) |
528 | errx(EXIT_FAILURE, "invalid endpoint format"); | | 532 | errx(EXIT_FAILURE, "invalid endpoint format"); |
529 | *bracket = '\0'; | | 533 | *bracket = '\0'; |
530 | colon = bracket + 1; | | 534 | colon = bracket + 1; |
531 | if (*colon != ':') | | 535 | if (*colon != ':') |
532 | errx(EXIT_FAILURE, "invalid endpoint format"); | | 536 | errx(EXIT_FAILURE, "invalid endpoint format"); |
533 | *colon = '\0'; | | 537 | *colon = '\0'; |
534 | port = colon + 1; | | 538 | port = colon + 1; |
535 | } else { | | 539 | } else { |
536 | char *colon, *tmp; | | 540 | char *colon, *tmp; |
537 | colon = strchr(addr_port, ':'); | | 541 | colon = strchr(addr_port, ':'); |
538 | if (colon == NULL) | | 542 | if (colon == NULL) |
539 | errx(EXIT_FAILURE, "no ':' found in endpoint"); | | 543 | errx(EXIT_FAILURE, "no ':' found in endpoint"); |
540 | tmp = strchr(colon + 1, ':'); | | 544 | tmp = strchr(colon + 1, ':'); |
541 | if (tmp != NULL) { | | 545 | if (tmp != NULL) { |
542 | /* <ipv6>:<port> */ | | 546 | /* <ipv6>:<port> */ |
543 | /* Assume the last colon is a separator */ | | 547 | /* Assume the last colon is a separator */ |
544 | char *last_colon = tmp; | | 548 | char *last_colon = tmp; |
545 | while ((tmp = strchr(tmp + 1, ':')) != NULL) | | 549 | while ((tmp = strchr(tmp + 1, ':')) != NULL) |
546 | last_colon = tmp; | | 550 | last_colon = tmp; |
547 | colon = last_colon; | | 551 | colon = last_colon; |
548 | *colon = '\0'; | | 552 | *colon = '\0'; |
549 | port = colon + 1; | | 553 | port = colon + 1; |
550 | } else { | | 554 | } else { |
551 | /* <ipv4>:<port> */ | | 555 | /* <ipv4>:<port> */ |
552 | *colon = '\0'; | | 556 | *colon = '\0'; |
553 | port = colon + 1; | | 557 | port = colon + 1; |
554 | } | | 558 | } |
555 | } | | 559 | } |
556 | | | 560 | |
557 | memset(&hints, 0, sizeof(hints)); | | 561 | memset(&hints, 0, sizeof(hints)); |
558 | hints.ai_family = AF_UNSPEC; | | 562 | hints.ai_family = AF_UNSPEC; |
559 | hints.ai_flags = AI_NUMERICHOST; | | 563 | hints.ai_flags = AI_NUMERICHOST; |
560 | error = getaddrinfo(addr, port, &hints, &res); | | 564 | error = getaddrinfo(addr, port, &hints, &res); |
561 | if (error) | | 565 | if (error) |
562 | errx(EXIT_FAILURE, "getaddrinfo: %s", gai_strerror(error)); | | 566 | errx(EXIT_FAILURE, "getaddrinfo: %s", gai_strerror(error)); |
563 | | | 567 | |
564 | if (!prop_dictionary_set_data(prop_dict, "endpoint", | | 568 | if (!prop_dictionary_set_data(prop_dict, "endpoint", |
565 | res->ai_addr, res->ai_addrlen)) | | 569 | res->ai_addr, res->ai_addrlen)) |
566 | errx(EXIT_FAILURE, "prop_dictionary_set_data"); | | 570 | errx(EXIT_FAILURE, "prop_dictionary_set_data"); |
567 | | | 571 | |
568 | freeaddrinfo(res); | | 572 | freeaddrinfo(res); |
569 | free(addr_port); | | 573 | free(addr_port); |
570 | } | | 574 | } |
571 | | | 575 | |
572 | static void | | 576 | static void |
573 | handle_option_allowed_ips(const char *_allowed_ips, prop_dictionary_t prop_dict) | | 577 | handle_option_allowed_ips(const char *_allowed_ips, prop_dictionary_t prop_dict) |
574 | { | | 578 | { |
575 | prop_array_t allowedips; | | 579 | prop_array_t allowedips; |
576 | int i; | | 580 | int i; |
577 | char *allowed_ips, *ip; | | 581 | char *allowed_ips, *ip; |
578 | | | 582 | |
579 | allowed_ips = strdup(_allowed_ips); | | 583 | allowed_ips = strdup(_allowed_ips); |
580 | if (allowed_ips == NULL) | | 584 | if (allowed_ips == NULL) |
581 | errx(EXIT_FAILURE, "strdup"); | | 585 | errx(EXIT_FAILURE, "strdup"); |
582 | | | 586 | |
583 | allowedips = prop_array_create(); | | 587 | allowedips = prop_array_create(); |
584 | if (allowedips == NULL) | | 588 | if (allowedips == NULL) |
585 | errx(EXIT_FAILURE, "prop_array_create"); | | 589 | errx(EXIT_FAILURE, "prop_array_create"); |
586 | | | 590 | |
587 | for (i = 0; (ip = strsep(&allowed_ips, ",")) != NULL; i++) { | | 591 | for (i = 0; (ip = strsep(&allowed_ips, ",")) != NULL; i++) { |
588 | prop_dictionary_t prop_allowedip; | | 592 | prop_dictionary_t prop_allowedip; |
589 | uint16_t cidr; | | 593 | uint16_t cidr; |
590 | char *cidrp; | | 594 | char *cidrp; |
591 | struct addrinfo hints, *res; | | 595 | struct addrinfo hints, *res; |
592 | int error; | | 596 | int error; |
593 | | | 597 | |
594 | prop_allowedip = prop_dictionary_create(); | | 598 | prop_allowedip = prop_dictionary_create(); |
595 | if (prop_allowedip == NULL) | | 599 | if (prop_allowedip == NULL) |
596 | errx(EXIT_FAILURE, "prop_dictionary_create"); | | 600 | errx(EXIT_FAILURE, "prop_dictionary_create"); |
597 | | | 601 | |
598 | cidrp = strchr(ip, '/'); | | 602 | cidrp = strchr(ip, '/'); |
599 | if (cidrp == NULL) | | 603 | if (cidrp == NULL) |
600 | errx(EXIT_FAILURE, "no '/' found in allowed-ip"); | | 604 | errx(EXIT_FAILURE, "no '/' found in allowed-ip"); |
601 | *cidrp = '\0'; | | 605 | *cidrp = '\0'; |
602 | cidrp++; | | 606 | cidrp++; |
603 | | | 607 | |
604 | cidr = strtouint16(cidrp); | | 608 | cidr = strtouint16(cidrp); |
605 | | | 609 | |
606 | memset(&hints, 0, sizeof(hints)); | | 610 | memset(&hints, 0, sizeof(hints)); |
607 | hints.ai_family = AF_UNSPEC; | | 611 | hints.ai_family = AF_UNSPEC; |
608 | hints.ai_flags = AI_NUMERICHOST; | | 612 | hints.ai_flags = AI_NUMERICHOST; |
609 | error = getaddrinfo(ip, 0, &hints, &res); | | 613 | error = getaddrinfo(ip, 0, &hints, &res); |
610 | if (error) | | 614 | if (error) |
611 | errx(EXIT_FAILURE, "getaddrinfo: %s", | | 615 | errx(EXIT_FAILURE, "getaddrinfo: %s", |
612 | gai_strerror(errno)); | | 616 | gai_strerror(errno)); |
613 | | | 617 | |
614 | sa_family_t family = res->ai_addr->sa_family; | | 618 | sa_family_t family = res->ai_addr->sa_family; |
615 | if (!prop_dictionary_set_uint8(prop_allowedip, "family", | | 619 | if (!prop_dictionary_set_uint8(prop_allowedip, "family", |
616 | family)) | | 620 | family)) |
617 | errx(EXIT_FAILURE, "prop_dictionary_set_uint8"); | | 621 | errx(EXIT_FAILURE, "prop_dictionary_set_uint8"); |
618 | | | 622 | |
619 | const void *addr; | | 623 | const void *addr; |
620 | size_t addrlen; | | 624 | size_t addrlen; |
621 | switch (family) { | | 625 | switch (family) { |
622 | case AF_INET: { | | 626 | case AF_INET: { |
623 | const struct sockaddr_in *sin = | | 627 | const struct sockaddr_in *sin = |
624 | (const struct sockaddr_in *)res->ai_addr; | | 628 | (const struct sockaddr_in *)res->ai_addr; |
625 | addr = &sin->sin_addr; | | 629 | addr = &sin->sin_addr; |
626 | addrlen = sizeof(sin->sin_addr); | | 630 | addrlen = sizeof(sin->sin_addr); |
627 | break; | | 631 | break; |
628 | } | | 632 | } |
629 | case AF_INET6: { | | 633 | case AF_INET6: { |
630 | const struct sockaddr_in6 *sin6 = | | 634 | const struct sockaddr_in6 *sin6 = |
631 | (const struct sockaddr_in6 *)res->ai_addr; | | 635 | (const struct sockaddr_in6 *)res->ai_addr; |
632 | addr = &sin6->sin6_addr; | | 636 | addr = &sin6->sin6_addr; |
633 | addrlen = sizeof(sin6->sin6_addr); | | 637 | addrlen = sizeof(sin6->sin6_addr); |
634 | break; | | 638 | break; |
635 | } | | 639 | } |
636 | default: | | 640 | default: |
637 | errx(EXIT_FAILURE, "invalid family: %d", family); | | 641 | errx(EXIT_FAILURE, "invalid family: %d", family); |
638 | } | | 642 | } |
639 | if (!prop_dictionary_set_data(prop_allowedip, "ip", | | 643 | if (!prop_dictionary_set_data(prop_allowedip, "ip", |
640 | addr, addrlen)) | | 644 | addr, addrlen)) |
641 | errx(EXIT_FAILURE, "prop_dictionary_set_data"); | | 645 | errx(EXIT_FAILURE, "prop_dictionary_set_data"); |
642 | if (!prop_dictionary_set_uint16(prop_allowedip, "cidr", cidr)) | | 646 | if (!prop_dictionary_set_uint16(prop_allowedip, "cidr", cidr)) |
643 | errx(EXIT_FAILURE, "prop_dictionary_set_uint16"); | | 647 | errx(EXIT_FAILURE, "prop_dictionary_set_uint16"); |
644 | | | 648 | |
645 | freeaddrinfo(res); | | 649 | freeaddrinfo(res); |
646 | prop_array_set(allowedips, i, prop_allowedip); | | 650 | prop_array_set(allowedips, i, prop_allowedip); |
647 | } | | 651 | } |
648 | prop_dictionary_set(prop_dict, "allowedips", allowedips); | | 652 | prop_dictionary_set(prop_dict, "allowedips", allowedips); |
649 | prop_object_release(allowedips); | | 653 | prop_object_release(allowedips); |
650 | | | 654 | |
651 | free(allowed_ips); | | 655 | free(allowed_ips); |
652 | } | | 656 | } |
653 | | | 657 | |
654 | static void | | 658 | static void |
655 | handle_option_preshared_key(const char *path, prop_dictionary_t prop_dict) | | 659 | handle_option_preshared_key(const char *path, prop_dictionary_t prop_dict) |
656 | { | | 660 | { |
657 | unsigned char keybuf[KEY_LEN]; | | 661 | unsigned char keybuf[KEY_LEN]; |
658 | | | 662 | |
659 | read_key(path, keybuf); | | 663 | read_key(path, keybuf); |
660 | if (!prop_dictionary_set_data(prop_dict, "preshared_key", | | 664 | if (!prop_dictionary_set_data(prop_dict, "preshared_key", |
661 | keybuf, sizeof(keybuf))) | | 665 | keybuf, sizeof(keybuf))) |
662 | errx(EXIT_FAILURE, "prop_dictionary_set_data"); | | 666 | errx(EXIT_FAILURE, "prop_dictionary_set_data"); |
663 | } | | 667 | } |
664 | | | 668 | |
665 | static const struct option { | | 669 | static const struct option { |
666 | const char *option; | | 670 | const char *option; |
667 | void (*func)(const char *, prop_dictionary_t); | | 671 | void (*func)(const char *, prop_dictionary_t); |
668 | } options[] = { | | 672 | } options[] = { |
669 | {"--endpoint=", handle_option_endpoint}, | | 673 | {"--endpoint=", handle_option_endpoint}, |
670 | {"--allowed-ips=", handle_option_allowed_ips}, | | 674 | {"--allowed-ips=", handle_option_allowed_ips}, |
671 | {"--preshared-key=", handle_option_preshared_key}, | | 675 | {"--preshared-key=", handle_option_preshared_key}, |
672 | }; | | 676 | }; |
673 | | | 677 | |
674 | static void | | 678 | static void |
675 | handle_options(int argc, char *argv[], prop_dictionary_t prop_dict) | | 679 | handle_options(int argc, char *argv[], prop_dictionary_t prop_dict) |
676 | { | | 680 | { |
677 | | | 681 | |
678 | while (argc > 0) { | | 682 | while (argc > 0) { |
679 | for (size_t i = 0; i < __arraycount(options); i++) { | | 683 | for (size_t i = 0; i < __arraycount(options); i++) { |
680 | const struct option *opt = &options[i]; | | 684 | const struct option *opt = &options[i]; |
681 | size_t optlen = strlen(opt->option); | | 685 | size_t optlen = strlen(opt->option); |
682 | if (strncmp(argv[0], opt->option, optlen) == 0) { | | 686 | if (strncmp(argv[0], opt->option, optlen) == 0) { |
683 | opt->func(argv[0] + optlen, prop_dict); | | 687 | opt->func(argv[0] + optlen, prop_dict); |
684 | break; | | 688 | break; |
685 | } | | 689 | } |
686 | } | | 690 | } |
687 | argc -= 1; | | 691 | argc -= 1; |
688 | argv += 1; | | 692 | argv += 1; |
689 | } | | 693 | } |
690 | | | 694 | |
691 | if (argc != 0) | | 695 | if (argc != 0) |
692 | usage(); | | 696 | usage(); |
693 | } | | 697 | } |
694 | | | 698 | |
695 | static int | | 699 | static int |
696 | cmd_add_peer(const char *interface, int argc, char *argv[]) | | 700 | cmd_add_peer(const char *interface, int argc, char *argv[]) |
697 | { | | 701 | { |
698 | const char *name; | | 702 | const char *name; |
699 | unsigned char keybuf[KEY_LEN]; | | 703 | unsigned char keybuf[KEY_LEN]; |
700 | | | 704 | |
701 | if (argc < 2) | | 705 | if (argc < 2) |
702 | usage(); | | 706 | usage(); |
703 | | | 707 | |
704 | prop_dictionary_t prop_dict; | | 708 | prop_dictionary_t prop_dict; |
705 | prop_dict = prop_dictionary_create(); | | 709 | prop_dict = prop_dictionary_create(); |
706 | if (prop_dict == NULL) | | 710 | if (prop_dict == NULL) |
707 | errx(EXIT_FAILURE, "prop_dictionary_create"); | | 711 | errx(EXIT_FAILURE, "prop_dictionary_create"); |
708 | | | 712 | |
709 | name = argv[0]; | | 713 | name = argv[0]; |
710 | if (strlen(name) > WG_PEER_NAME_MAXLEN) | | 714 | if (strlen(name) > WG_PEER_NAME_MAXLEN) |
711 | errx(EXIT_FAILURE, "peer name too long"); | | 715 | errx(EXIT_FAILURE, "peer name too long"); |
712 | if (strnlen(argv[1], KEY_BASE64_LEN + 1) != KEY_BASE64_LEN) | | 716 | if (strnlen(argv[1], KEY_BASE64_LEN + 1) != KEY_BASE64_LEN) |
713 | errx(EXIT_FAILURE, "invalid public-key length: %zu", | | 717 | errx(EXIT_FAILURE, "invalid public-key length: %zu", |
714 | strlen(argv[1])); | | 718 | strlen(argv[1])); |
715 | base64_decode(argv[1], keybuf); | | 719 | base64_decode(argv[1], keybuf); |
716 | | | 720 | |
717 | if (!prop_dictionary_set_string(prop_dict, "name", name)) | | 721 | if (!prop_dictionary_set_string(prop_dict, "name", name)) |
718 | errx(EXIT_FAILURE, "prop_dictionary_set_string"); | | 722 | errx(EXIT_FAILURE, "prop_dictionary_set_string"); |
719 | if (!prop_dictionary_set_data(prop_dict, "public_key", | | 723 | if (!prop_dictionary_set_data(prop_dict, "public_key", |
720 | keybuf, sizeof(keybuf))) | | 724 | keybuf, sizeof(keybuf))) |
721 | errx(EXIT_FAILURE, "prop_dictionary_set_data"); | | 725 | errx(EXIT_FAILURE, "prop_dictionary_set_data"); |
722 | | | 726 | |
723 | argc -= 2; | | 727 | argc -= 2; |
724 | argv += 2; | | 728 | argv += 2; |
725 | | | 729 | |
726 | handle_options(argc, argv, prop_dict); | | 730 | handle_options(argc, argv, prop_dict); |
727 | | | 731 | |
728 | char *buf = prop_dictionary_externalize(prop_dict); | | 732 | char *buf = prop_dictionary_externalize(prop_dict); |
729 | if (buf == NULL) | | 733 | if (buf == NULL) |
730 | err(EXIT_FAILURE, "prop_dictionary_externalize failed"); | | 734 | err(EXIT_FAILURE, "prop_dictionary_externalize failed"); |
731 | ioctl_set(interface, WG_IOCTL_ADD_PEER, buf); | | 735 | ioctl_set(interface, WG_IOCTL_ADD_PEER, buf); |
732 | | | 736 | |
733 | return EXIT_SUCCESS; | | 737 | return EXIT_SUCCESS; |
734 | } | | 738 | } |
735 | | | 739 | |
736 | static int | | 740 | static int |
737 | cmd_delete_peer(const char *interface, int argc, char *argv[]) | | 741 | cmd_delete_peer(const char *interface, int argc, char *argv[]) |
738 | { | | 742 | { |
739 | const char *name; | | 743 | const char *name; |
740 | | | 744 | |
741 | if (argc != 1) | | 745 | if (argc != 1) |
742 | usage(); | | 746 | usage(); |
743 | | | 747 | |
744 | prop_dictionary_t prop_dict; | | 748 | prop_dictionary_t prop_dict; |
745 | prop_dict = prop_dictionary_create(); | | 749 | prop_dict = prop_dictionary_create(); |
746 | if (prop_dict == NULL) | | 750 | if (prop_dict == NULL) |
747 | errx(EXIT_FAILURE, "prop_dictionary_create"); | | 751 | errx(EXIT_FAILURE, "prop_dictionary_create"); |
748 | | | 752 | |
749 | name = argv[0]; | | 753 | name = argv[0]; |
750 | if (strlen(name) > WG_PEER_NAME_MAXLEN) | | 754 | if (strlen(name) > WG_PEER_NAME_MAXLEN) |
751 | errx(EXIT_FAILURE, "peer name too long"); | | 755 | errx(EXIT_FAILURE, "peer name too long"); |
752 | | | 756 | |
753 | if (!prop_dictionary_set_string(prop_dict, "name", name)) | | 757 | if (!prop_dictionary_set_string(prop_dict, "name", name)) |
754 | errx(EXIT_FAILURE, "prop_dictionary_set_string"); | | 758 | errx(EXIT_FAILURE, "prop_dictionary_set_string"); |
755 | | | 759 | |
756 | char *buf = prop_dictionary_externalize(prop_dict); | | 760 | char *buf = prop_dictionary_externalize(prop_dict); |
757 | if (buf == NULL) | | 761 | if (buf == NULL) |
758 | err(EXIT_FAILURE, "prop_dictionary_externalize failed"); | | 762 | err(EXIT_FAILURE, "prop_dictionary_externalize failed"); |
759 | ioctl_set(interface, WG_IOCTL_DELETE_PEER, buf); | | 763 | ioctl_set(interface, WG_IOCTL_DELETE_PEER, buf); |
760 | | | 764 | |
761 | return EXIT_SUCCESS; | | 765 | return EXIT_SUCCESS; |
762 | } | | 766 | } |
763 | | | 767 | |
764 | static const struct command { | | 768 | static const struct command { |
765 | const char *command; | | 769 | const char *command; |
766 | const char *target; | | 770 | const char *target; |
767 | int (*func)(const char *, int, char **); | | 771 | int (*func)(const char *, int, char **); |
768 | } commands[] = { | | 772 | } commands[] = { |
769 | {"show", "all", cmd_show_all}, | | 773 | {"show", "all", cmd_show_all}, |
770 | {"show", "peer", cmd_show_peer}, | | 774 | {"show", "peer", cmd_show_peer}, |
771 | {"show", "private-key", cmd_show_private_key}, | | 775 | {"show", "private-key", cmd_show_private_key}, |
772 | {"set", "private-key", cmd_set_private_key}, | | 776 | {"set", "private-key", cmd_set_private_key}, |
773 | {"set", "listen-port", cmd_set_listen_port}, | | 777 | {"set", "listen-port", cmd_set_listen_port}, |
774 | {"add", "peer", cmd_add_peer}, | | 778 | {"add", "peer", cmd_add_peer}, |
775 | {"delete", "peer", cmd_delete_peer}, | | 779 | {"delete", "peer", cmd_delete_peer}, |
776 | }; | | 780 | }; |
777 | | | 781 | |
778 | int | | 782 | int |
779 | main(int argc, char *argv[]) | | 783 | main(int argc, char *argv[]) |
780 | { | | 784 | { |
781 | const char *interface; | | 785 | const char *interface; |
782 | const char *command; | | 786 | const char *command; |
783 | const char *target; | | 787 | const char *target; |
784 | | | 788 | |
785 | if (argc < 2 || | | 789 | if (argc < 2 || |
786 | strcmp(argv[1], "-h") == 0 || | | 790 | strcmp(argv[1], "-h") == 0 || |
787 | strcmp(argv[1], "-?") == 0 || | | 791 | strcmp(argv[1], "-?") == 0 || |
788 | strcmp(argv[1], "--help") == 0) { | | 792 | strcmp(argv[1], "--help") == 0) { |
789 | usage(); | | 793 | usage(); |
790 | } | | 794 | } |
791 | | | 795 | |
792 | interface = argv[1]; | | 796 | interface = argv[1]; |
793 | if (strlen(interface) > IFNAMSIZ) | | 797 | if (strlen(interface) > IFNAMSIZ) |
794 | errx(EXIT_FAILURE, "interface name too long"); | | 798 | errx(EXIT_FAILURE, "interface name too long"); |
795 | if (argc == 2) { | | 799 | if (argc == 2) { |
796 | return cmd_show_all(interface, 0, NULL); | | 800 | return cmd_show_all(interface, 0, NULL); |
797 | } | | 801 | } |
798 | if (argc < 4) { | | 802 | if (argc < 4) { |
799 | usage(); | | 803 | usage(); |
800 | } | | 804 | } |
801 | command = argv[2]; | | 805 | command = argv[2]; |
802 | target = argv[3]; | | 806 | target = argv[3]; |
803 | | | 807 | |
804 | argc -= 4; | | 808 | argc -= 4; |
805 | argv += 4; | | 809 | argv += 4; |
806 | | | 810 | |
807 | for (size_t i = 0; i < __arraycount(commands); i++) { | | 811 | for (size_t i = 0; i < __arraycount(commands); i++) { |
808 | const struct command *cmd = &commands[i]; | | 812 | const struct command *cmd = &commands[i]; |
809 | if (strncmp(command, cmd->command, strlen(cmd->command)) == 0 && | | 813 | if (strncmp(command, cmd->command, strlen(cmd->command)) == 0 && |
810 | strncmp(target, cmd->target, strlen(cmd->target)) == 0) { | | 814 | strncmp(target, cmd->target, strlen(cmd->target)) == 0) { |
811 | return cmd->func(interface, argc, argv); | | 815 | return cmd->func(interface, argc, argv); |
812 | } | | 816 | } |
813 | } | | 817 | } |
814 | | | 818 | |
815 | usage(); | | 819 | usage(); |
816 | } | | 820 | } |