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