├── wireguard-tools ├── mnlg.c ├── wg-quick │ ├── wg │ ├── linux.bash │ ├── openbsd.bash │ ├── freebsd.bash │ ├── darwin.bash │ └── android.c ├── wincompat │ ├── include │ │ ├── net │ │ │ └── if.h │ │ ├── netdb.h │ │ ├── sys │ │ │ ├── un.h │ │ │ ├── ioctl.h │ │ │ └── socket.h │ │ ├── arpa │ │ │ └── inet.h │ │ └── netinet │ │ │ └── in.h │ ├── getrandom.c │ ├── compat.h │ ├── init.c │ ├── libc.c │ └── ipc.c ├── mnlg.h ├── test.conf ├── ipc.h ├── subcommands.h ├── systemd │ └── wg-quick@.service ├── config.h ├── curve25519.h ├── encoding.h ├── set.c ├── pubkey.c ├── completion │ ├── wg-quick.bash-completion │ └── wg.bash-completion ├── setconf.c ├── terminal.c ├── terminal.h ├── wg.c ├── INSTALL ├── genkey.c ├── curve25519.c ├── containers.h ├── showconf.c ├── Makefile ├── encoding.c ├── man │ ├── wg-quick.8 │ └── wg.8 ├── show.c └── config.c ├── hello-world └── main.go ├── .gitignore └── ssh-shell-server └── main.go /wireguard-tools/mnlg.c: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /wireguard-tools/wg-quick/wg: -------------------------------------------------------------------------------- 1 | ../wg -------------------------------------------------------------------------------- /wireguard-tools/wincompat/include/net/if.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /wireguard-tools/wincompat/include/netdb.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /wireguard-tools/wincompat/include/sys/un.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /wireguard-tools/wincompat/include/arpa/inet.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /wireguard-tools/wincompat/include/netinet/in.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /wireguard-tools/wincompat/include/sys/ioctl.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /wireguard-tools/wincompat/include/sys/socket.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /hello-world/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("Hello from the switch") 7 | } 8 | -------------------------------------------------------------------------------- /wireguard-tools/mnlg.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | * 5 | * Original author: Jiri Pirko 6 | */ 7 | -------------------------------------------------------------------------------- /wireguard-tools/test.conf: -------------------------------------------------------------------------------- 1 | [Interface] 2 | PrivateKey = ME9F0hAoTkIIDkaSOVd23yS//Fjr2ABIaipa3PKdBmw= 3 | ListenPort = 9999 4 | 5 | [Peer] 6 | PublicKey = Sijbz2dMX6fs53HgITe2SH2unR1IdRPjwOcmI0RSWFI= 7 | AllowedIPs = 25.25.25.0/24 8 | Endpoint = 192.168.2.1:9999 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cscope.out 2 | *.o 3 | *.d 4 | *.dwo 5 | *.ko 6 | *.mod.c 7 | src/tools/wg 8 | Module.symvers 9 | *.cmd 10 | .tmp_versions 11 | *.swp 12 | modules.order 13 | modules.builtin 14 | maint/ 15 | qemu-build/ 16 | src/tests/qemu/distfiles/ 17 | *.id0 18 | *.id1 19 | *.id2 20 | *.nam 21 | *.til 22 | *.pro.user 23 | .cache.mk 24 | wireguard-tools/wg 25 | -------------------------------------------------------------------------------- /wireguard-tools/wincompat/getrandom.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len) 10 | { 11 | return RtlGenRandom(out, len); 12 | } 13 | -------------------------------------------------------------------------------- /wireguard-tools/ipc.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #ifndef IPC_H 7 | #define IPC_H 8 | 9 | #include 10 | 11 | struct wgdevice; 12 | 13 | int ipc_set_device(struct wgdevice *dev); 14 | int ipc_get_device(struct wgdevice **dev, const char *interface); 15 | char *ipc_list_devices(void); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /wireguard-tools/subcommands.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #ifndef SUBCOMMANDS_H 7 | #define SUBCOMMANDS_H 8 | 9 | extern const char *PROG_NAME; 10 | int show_main(int argc, char *argv[]); 11 | int showconf_main(int argc, char *argv[]); 12 | int set_main(int argc, char *argv[]); 13 | int setconf_main(int argc, char *argv[]); 14 | int genkey_main(int argc, char *argv[]); 15 | int pubkey_main(int argc, char *argv[]); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /wireguard-tools/systemd/wg-quick@.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=WireGuard via wg-quick(8) for %I 3 | After=network-online.target nss-lookup.target 4 | Wants=network-online.target nss-lookup.target 5 | Documentation=man:wg-quick(8) 6 | Documentation=man:wg(8) 7 | Documentation=https://www.wireguard.com/ 8 | Documentation=https://www.wireguard.com/quickstart/ 9 | Documentation=https://git.zx2c4.com/WireGuard/about/src/tools/man/wg-quick.8 10 | Documentation=https://git.zx2c4.com/WireGuard/about/src/tools/man/wg.8 11 | 12 | [Service] 13 | Type=oneshot 14 | RemainAfterExit=yes 15 | ExecStart=/usr/bin/wg-quick up %i 16 | ExecStop=/usr/bin/wg-quick down %i 17 | Environment=WG_ENDPOINT_RESOLUTION_RETRIES=infinity 18 | 19 | [Install] 20 | WantedBy=multi-user.target 21 | -------------------------------------------------------------------------------- /wireguard-tools/config.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #ifndef CONFIG_H 7 | #define CONFIG_H 8 | 9 | #include 10 | 11 | struct wgdevice; 12 | struct wgpeer; 13 | struct wgallowedip; 14 | 15 | struct config_ctx { 16 | struct wgdevice *device; 17 | struct wgpeer *last_peer; 18 | struct wgallowedip *last_allowedip; 19 | bool is_peer_section, is_device_section; 20 | }; 21 | 22 | struct wgdevice *config_read_cmd(char *argv[], int argc); 23 | bool config_read_init(struct config_ctx *ctx, bool append); 24 | bool config_read_line(struct config_ctx *ctx, const char *line); 25 | struct wgdevice *config_read_finish(struct config_ctx *ctx); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /wireguard-tools/curve25519.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #ifndef CURVE25519_H 7 | #define CURVE25519_H 8 | 9 | #include 10 | #include 11 | 12 | enum curve25519_lengths { 13 | CURVE25519_KEY_SIZE = 32 14 | }; 15 | 16 | void curve25519(uint8_t mypublic[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE], const uint8_t basepoint[static CURVE25519_KEY_SIZE]); 17 | void curve25519_generate_public(uint8_t pub[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE]); 18 | static inline void curve25519_clamp_secret(uint8_t secret[static CURVE25519_KEY_SIZE]) 19 | { 20 | secret[0] &= 248; 21 | secret[31] = (secret[31] & 127) | 64; 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /wireguard-tools/encoding.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #ifndef ENCODING_H 7 | #define ENCODING_H 8 | 9 | #include 10 | #include 11 | #include "containers.h" 12 | 13 | #define WG_KEY_LEN_BASE64 ((((WG_KEY_LEN) + 2) / 3) * 4 + 1) 14 | #define WG_KEY_LEN_HEX (WG_KEY_LEN * 2 + 1) 15 | 16 | void key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN]); 17 | bool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64); 18 | 19 | void key_to_hex(char hex[static WG_KEY_LEN_HEX], const uint8_t key[static WG_KEY_LEN]); 20 | bool key_from_hex(uint8_t key[static WG_KEY_LEN], const char *hex); 21 | 22 | bool key_is_zero(const uint8_t key[static WG_KEY_LEN]); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /wireguard-tools/wincompat/compat.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #define __USE_MINGW_ANSI_STDIO 1 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #undef interface 18 | #undef min 19 | #undef max 20 | 21 | #define WINCOMPAT 22 | 23 | #define IFNAMSIZ 64 24 | #define EAI_SYSTEM -99 25 | 26 | /* libc.c */ 27 | char *strsep(char **str, const char *sep); 28 | ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp); 29 | ssize_t getline(char **buf, size_t *bufsiz, FILE *fp); 30 | int inet_pton(int af, const char *src, void *dst); 31 | const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); 32 | -------------------------------------------------------------------------------- /wireguard-tools/wincompat/init.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING 10 | #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4 11 | #endif 12 | 13 | __attribute__((constructor)) static void init(void) 14 | { 15 | char *colormode; 16 | DWORD console_mode; 17 | HANDLE stdout_handle; 18 | WSADATA wsaData; 19 | WSAStartup(MAKEWORD(2, 2), &wsaData); 20 | 21 | stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); // We don't close this. 22 | if (stdout_handle == INVALID_HANDLE_VALUE) 23 | goto no_color; 24 | if (!GetConsoleMode(stdout_handle, &console_mode)) 25 | goto no_color; 26 | if (!SetConsoleMode(stdout_handle, ENABLE_VIRTUAL_TERMINAL_PROCESSING | console_mode)) 27 | goto no_color; 28 | return; 29 | 30 | no_color: 31 | colormode = getenv("WG_COLOR_MODE"); 32 | if (!colormode) 33 | putenv("WG_COLOR_MODE=never"); 34 | } 35 | 36 | __attribute__((destructor)) static void deinit(void) 37 | { 38 | WSACleanup(); 39 | } 40 | -------------------------------------------------------------------------------- /wireguard-tools/set.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "containers.h" 11 | #include "config.h" 12 | #include "ipc.h" 13 | #include "subcommands.h" 14 | 15 | int set_main(int argc, char *argv[]) 16 | { 17 | struct wgdevice *device = NULL; 18 | int ret = 1; 19 | 20 | if (argc < 3) { 21 | fprintf(stderr, "Usage: %s %s [listen-port ] [fwmark ] [private-key ] [peer [remove] [preshared-key ] [endpoint :] [persistent-keepalive ] [allowed-ips /[,/]...] ]...\n", PROG_NAME, argv[0]); 22 | return 1; 23 | } 24 | 25 | device = config_read_cmd(argv + 2, argc - 2); 26 | if (!device) 27 | goto cleanup; 28 | strncpy(device->name, argv[1], IFNAMSIZ - 1); 29 | device->name[IFNAMSIZ - 1] = '\0'; 30 | 31 | if (ipc_set_device(device) != 0) { 32 | perror("Unable to modify interface"); 33 | goto cleanup; 34 | } 35 | 36 | ret = 0; 37 | 38 | cleanup: 39 | free_wgdevice(device); 40 | return ret; 41 | } 42 | -------------------------------------------------------------------------------- /wireguard-tools/pubkey.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "curve25519.h" 11 | #include "encoding.h" 12 | #include "subcommands.h" 13 | 14 | int pubkey_main(int argc, char *argv[]) 15 | { 16 | uint8_t key[WG_KEY_LEN] __attribute__((aligned(sizeof(uintptr_t)))); 17 | char base64[WG_KEY_LEN_BASE64]; 18 | int trailing_char; 19 | 20 | if (argc != 1) { 21 | fprintf(stderr, "Usage: %s %s\n", PROG_NAME, argv[0]); 22 | return 1; 23 | } 24 | 25 | if (fread(base64, 1, sizeof(base64) - 1, stdin) != sizeof(base64) - 1) { 26 | errno = EINVAL; 27 | fprintf(stderr, "%s: Key is not the correct length or format\n", PROG_NAME); 28 | return 1; 29 | } 30 | base64[WG_KEY_LEN_BASE64 - 1] = '\0'; 31 | 32 | for (;;) { 33 | trailing_char = getc(stdin); 34 | if (!trailing_char || isspace(trailing_char) || isblank(trailing_char)) 35 | continue; 36 | if (trailing_char == EOF) 37 | break; 38 | fprintf(stderr, "%s: Trailing characters found after key\n", PROG_NAME); 39 | return 1; 40 | } 41 | 42 | if (!key_from_base64(key, base64)) { 43 | fprintf(stderr, "%s: Key is not the correct length or format\n", PROG_NAME); 44 | return 1; 45 | } 46 | curve25519_generate_public(key, key); 47 | key_to_base64(base64, key); 48 | puts(base64); 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /wireguard-tools/completion/wg-quick.bash-completion: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 3 | 4 | _wg_quick_completion() { 5 | local p i a search_paths old_glob 6 | search_paths=( /etc/wireguard ) 7 | 8 | old_glob="$(shopt -p nullglob)" 9 | shopt -s nullglob 10 | 11 | [[ $OSTYPE == *freebsd* || $OSTYPE == *darwin* ]] && search_paths+=( /usr/local/etc/wireguard ) 12 | 13 | if [[ $COMP_CWORD -eq 1 ]]; then 14 | COMPREPLY+=( $(compgen -W "up down" -- "${COMP_WORDS[1]}") ) 15 | elif [[ $COMP_CWORD -eq 2 ]]; then 16 | if [[ ${COMP_WORDS[1]} == up ]]; then 17 | for p in "${search_paths[@]}"; do 18 | for i in "$p"/*.conf; do 19 | i="${i##*/}"; i="${i%.conf}" 20 | mapfile -t a < <(compgen -W "$i" -- "${COMP_WORDS[2]}") 21 | COMPREPLY+=( "${a[@]}" ) 22 | done 23 | done 24 | mapfile -t a < <(compgen -f -X '!*.conf' -- "${COMP_WORDS[2]}") 25 | COMPREPLY+=( "${a[@]}" ) 26 | mapfile -t a < <(compgen -d -- "${COMP_WORDS[2]}") 27 | COMPREPLY+=( "${a[@]}" ) 28 | elif [[ ${COMP_WORDS[1]} == down ]]; then 29 | if [[ $OSTYPE == *openbsd* || $OSTYPE == *darwin* ]]; then 30 | for i in /var/run/wireguard/*.name; do 31 | i="${i##*/}"; i="${i%.name}" 32 | mapfile -t a < <(compgen -W "$i" -- "${COMP_WORDS[2]}") 33 | COMPREPLY+=( "${a[@]}" ) 34 | done 35 | else 36 | COMPREPLY+=( $(compgen -W "$(wg show interfaces)" -- "${COMP_WORDS[2]}") ) 37 | fi 38 | fi 39 | fi 40 | eval "$old_glob" 41 | } 42 | 43 | complete -o filenames -o nosort -F _wg_quick_completion wg-quick 44 | -------------------------------------------------------------------------------- /wireguard-tools/setconf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "containers.h" 12 | #include "config.h" 13 | #include "ipc.h" 14 | #include "subcommands.h" 15 | 16 | int setconf_main(int argc, char *argv[]) 17 | { 18 | struct wgdevice *device = NULL; 19 | struct config_ctx ctx; 20 | FILE *config_input = NULL; 21 | char *config_buffer = NULL; 22 | size_t config_buffer_len = 0; 23 | int ret = 1; 24 | 25 | if (argc != 3) { 26 | fprintf(stderr, "Usage: %s %s \n", PROG_NAME, argv[0]); 27 | return 1; 28 | } 29 | 30 | config_input = fopen(argv[2], "r"); 31 | if (!config_input) { 32 | perror("fopen"); 33 | return 1; 34 | } 35 | if (!config_read_init(&ctx, !strcmp(argv[0], "addconf"))) { 36 | fclose(config_input); 37 | return 1; 38 | } 39 | while (getline(&config_buffer, &config_buffer_len, config_input) >= 0) { 40 | if (!config_read_line(&ctx, config_buffer)) { 41 | fprintf(stderr, "Configuration parsing error\n"); 42 | goto cleanup; 43 | } 44 | } 45 | device = config_read_finish(&ctx); 46 | if (!device) { 47 | fprintf(stderr, "Invalid configuration\n"); 48 | goto cleanup; 49 | } 50 | strncpy(device->name, argv[1], IFNAMSIZ - 1); 51 | device->name[IFNAMSIZ - 1] = '\0'; 52 | 53 | if (ipc_set_device(device) != 0) { 54 | perror("Unable to modify interface"); 55 | goto cleanup; 56 | } 57 | 58 | ret = 0; 59 | 60 | cleanup: 61 | if (config_input) 62 | fclose(config_input); 63 | free(config_buffer); 64 | free_wgdevice(device); 65 | return ret; 66 | } 67 | -------------------------------------------------------------------------------- /wireguard-tools/terminal.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | static bool color_mode(FILE *file) 16 | { 17 | static int mode = -1; 18 | const char *var; 19 | 20 | if (mode != -1) 21 | return mode; 22 | var = getenv("WG_COLOR_MODE"); 23 | if (var && !strcmp(var, "always")) 24 | mode = true; 25 | else if (var && !strcmp(var, "never")) 26 | mode = false; 27 | else 28 | return isatty(fileno(file)); 29 | return mode; 30 | } 31 | 32 | static void filter_ansi(FILE *file, const char *fmt, va_list args) 33 | { 34 | char *str = NULL; 35 | size_t len, i, j; 36 | 37 | if (color_mode(file)) { 38 | vfprintf(file, fmt, args); 39 | return; 40 | } 41 | 42 | len = vasprintf(&str, fmt, args); 43 | 44 | if (len >= 2) { 45 | for (i = 0; i < len - 2; ++i) { 46 | if (str[i] == '\x1b' && str[i + 1] == '[') { 47 | str[i] = str[i + 1] = '\0'; 48 | for (j = i + 2; j < len; ++j) { 49 | if (isalpha(str[j])) 50 | break; 51 | str[j] = '\0'; 52 | } 53 | str[j] = '\0'; 54 | } 55 | } 56 | } 57 | for (i = 0; i < len; i = j) { 58 | fputs(&str[i], file); 59 | for (j = i + strlen(&str[i]); j < len; ++j) { 60 | if (str[j] != '\0') 61 | break; 62 | } 63 | } 64 | 65 | free(str); 66 | } 67 | 68 | void terminal_printf(const char *fmt, ...) 69 | { 70 | va_list args; 71 | 72 | va_start(args, fmt); 73 | filter_ansi(stdout, fmt, args); 74 | va_end(args); 75 | } 76 | 77 | void terminal_fprintf(FILE *file, const char *fmt, ...) 78 | { 79 | va_list args; 80 | 81 | va_start(args, fmt); 82 | filter_ansi(file, fmt, args); 83 | va_end(args); 84 | } 85 | -------------------------------------------------------------------------------- /wireguard-tools/terminal.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #ifndef TERMINAL_H 7 | #define TERMINAL_H 8 | 9 | #define TERMINAL_FG_BLACK "\x1b[30m" 10 | #define TERMINAL_FG_RED "\x1b[31m" 11 | #define TERMINAL_FG_GREEN "\x1b[32m" 12 | #define TERMINAL_FG_YELLOW "\x1b[33m" 13 | #define TERMINAL_FG_BLUE "\x1b[34m" 14 | #define TERMINAL_FG_MAGENTA "\x1b[35m" 15 | #define TERMINAL_FG_CYAN "\x1b[36m" 16 | #define TERMINAL_FG_WHITE "\x1b[37m" 17 | #define TERMINAL_FG_DEFAULT "\x1b[39m" 18 | 19 | #define TERMINAL_BG_BLACK "\x1b[40m" 20 | #define TERMINAL_BG_RED "\x1b[41m" 21 | #define TERMINAL_BG_GREEN "\x1b[42m" 22 | #define TERMINAL_BG_YELLOW "\x1b[43m" 23 | #define TERMINAL_BG_BLUE "\x1b[44m" 24 | #define TERMINAL_BG_MAGENTA "\x1b[45m" 25 | #define TERMINAL_BG_CYAN "\x1b[46m" 26 | #define TERMINAL_BG_WHITE "\x1b[47m" 27 | #define TERMINAL_BG_DEFAULT "\x1b[49m" 28 | 29 | #define TERMINAL_BOLD "\x1b[1m" 30 | #define TERMINAL_NO_BOLD "\x1b[22m" 31 | #define TERMINAL_UNDERLINE "\x1b[4m" 32 | #define TERMINAL_NO_UNDERLINE "\x1b[24m" 33 | 34 | #define TERMINAL_RESET "\x1b[0m" 35 | 36 | #define TERMINAL_SAVE_CURSOR "\x1b[s" 37 | #define TERMINAL_RESTORE_CURSOR "\x1b[u" 38 | #define TERMINAL_UP_CURSOR(l) "\x1b[" #l "A" 39 | #define TERMINAL_DOWN_CURSOR(l) "\x1b[" #l "B" 40 | #define TERMINAL_RIGHT_CURSOR(c) "\x1b[" #c "C" 41 | #define TERMINAL_LEFT_CURSOR(c) "\x1b[" #c "D" 42 | #define TERMINAL_CLEAR_DOWN "\x1b[0J" 43 | #define TERMINAL_CLEAR_UP "\x1b[1J" 44 | #define TERMINAL_CLEAR_RIGHT "\x1b[0K" 45 | #define TERMINAL_CLEAR_LEFT "\x1b[1K" 46 | #define TERMINAL_CLEAR_LINE "\x1b[2K" 47 | #define TERMINAL_CLEAR_ALL "\x1b[2J" 48 | 49 | void terminal_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); 50 | void terminal_fprintf(FILE *file, const char *fmt, ...) __attribute__((format(printf, 2, 3))); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /wireguard-tools/wg.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "subcommands.h" 11 | 12 | const char *PROG_NAME; 13 | 14 | static const struct { 15 | const char *subcommand; 16 | int (*function)(int, char**); 17 | const char *description; 18 | } subcommands[] = { 19 | { "show", show_main, "Shows the current configuration and device information" }, 20 | { "showconf", showconf_main, "Shows the current configuration of a given WireGuard interface, for use with `setconf'" }, 21 | { "set", set_main, "Change the current configuration, add peers, remove peers, or change peers" }, 22 | { "setconf", setconf_main, "Applies a configuration file to a WireGuard interface" }, 23 | { "addconf", setconf_main, "Appends a configuration file to a WireGuard interface" }, 24 | { "genkey", genkey_main, "Generates a new private key and writes it to stdout" }, 25 | { "genpsk", genkey_main, "Generates a new preshared key and writes it to stdout" }, 26 | { "pubkey", pubkey_main, "Reads a private key from stdin and writes a public key to stdout" } 27 | }; 28 | 29 | static void show_usage(FILE *file) 30 | { 31 | fprintf(file, "Usage: %s []\n\n", PROG_NAME); 32 | fprintf(file, "Available subcommands:\n"); 33 | for (size_t i = 0; i < sizeof(subcommands) / sizeof(subcommands[0]); ++i) 34 | fprintf(file, " %s: %s\n", subcommands[i].subcommand, subcommands[i].description); 35 | fprintf(file, "You may pass `--help' to any of these subcommands to view usage.\n"); 36 | } 37 | 38 | int main(int argc, char *argv[]) 39 | { 40 | PROG_NAME = argv[0]; 41 | 42 | if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "help"))) { 43 | show_usage(stdout); 44 | return 0; 45 | } 46 | 47 | if (argc == 1) { 48 | static char *new_argv[] = { "show", NULL }; 49 | return show_main(1, new_argv); 50 | } 51 | 52 | for (size_t i = 0; i < sizeof(subcommands) / sizeof(subcommands[0]); ++i) { 53 | if (!strcmp(argv[1], subcommands[i].subcommand)) 54 | return subcommands[i].function(argc - 1, argv + 1); 55 | } 56 | 57 | fprintf(stderr, "Invalid subcommand: `%s'\n", argv[1]); 58 | show_usage(stderr); 59 | return 1; 60 | } 61 | -------------------------------------------------------------------------------- /wireguard-tools/INSTALL: -------------------------------------------------------------------------------- 1 | Installation Makefile Target 2 | ============================ 3 | 4 | # make install 5 | 6 | This command takes into account several environment variables: 7 | 8 | * PREFIX default: /usr 9 | * DESTDIR default: 10 | * BINDIR default: $(PREFIX)/bin 11 | * LIBDIR default: $(PREFIX)/lib 12 | * MANDIR default: $(PREFIX)/share/man 13 | * BASHCOMPDIR default: $(PREFIX)/share/bash-completion/completions 14 | * RUNSTATEDIR default: /var/run 15 | * PKG_CONFIG default: pkg-config 16 | 17 | * WITH_BASHCOMPLETION default: [auto-detect] 18 | * WITH_WGQUICK default: [auto-detect] 19 | * WITH_SYSTEMDUNITS default: [auto-detect] 20 | 21 | The first section is rather standard. The second section is not: 22 | 23 | * WITH_BASHCOMPLETION decides whether or not bash completion files for the 24 | tools are installed. This is just a nice thing for people who have bash. 25 | If you don't have bash, or don't want this, set the environment variable 26 | to `no'. If you'd like to force its use, even if bash-completion isn't 27 | detected in DESTDIR, then set it to `yes'. 28 | 29 | * WITH_WGQUICK decides whether or not the wg-quick(8) script is installed. 30 | This is a very quick and dirty bash script for reading a few extra 31 | variables from wg(8)-style configuration files, and automatically 32 | configures the interface. If you don't have bash, you probably don't want 33 | this at all. Likewise, if you already have a working network management 34 | tool or configuration, you probably want to integrate wg(8) or the direct 35 | WireGuard API into your network manager, rather than using wg-quick(8). 36 | But for folks who like simple quick&dirty scripts, this is nice. If you'd 37 | like to force its use, even if bash isn't detected in DESTDIR, then set it 38 | to `yes'. 39 | 40 | * WITH_SYSTEMDUNITS decides whether or not systemd units are installed for 41 | wg-quick(8). If you don't use systemd, you certainly don't want this, and 42 | should set it to `no'. If systemd isn't auto-detected, but you still would 43 | like to install it, set this to `yes'. 44 | 45 | If you're a simple `make && make install` kind of user, you can get away with 46 | not setting these variables and relying on the auto-detection. However, if 47 | you're writing a package for a distro, you'll want to explicitly set these, 48 | depending on what you want. 49 | -------------------------------------------------------------------------------- /wireguard-tools/genkey.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #ifdef __linux__ 15 | #include 16 | #endif 17 | #ifdef __APPLE__ 18 | #include 19 | #ifndef MAC_OS_X_VERSION_10_12 20 | #define MAC_OS_X_VERSION_10_12 101200 21 | #endif 22 | #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12 23 | #include 24 | #endif 25 | #endif 26 | 27 | #include "curve25519.h" 28 | #include "encoding.h" 29 | #include "subcommands.h" 30 | 31 | #ifndef WINCOMPAT 32 | static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len) 33 | { 34 | ssize_t ret = 0; 35 | size_t i; 36 | int fd; 37 | 38 | if (len > 256) { 39 | errno = EOVERFLOW; 40 | return false; 41 | } 42 | 43 | #if defined(__OpenBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) || (defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) 44 | if (!getentropy(out, len)) 45 | return true; 46 | #endif 47 | 48 | #if defined(__NR_getrandom) && defined(__linux__) 49 | if (syscall(__NR_getrandom, out, len, 0) == (ssize_t)len) 50 | return true; 51 | #endif 52 | 53 | fd = open("/dev/urandom", O_RDONLY); 54 | if (fd < 0) 55 | return false; 56 | for (errno = 0, i = 0; i < len; i += ret, ret = 0) { 57 | ret = read(fd, out + i, len - i); 58 | if (ret <= 0) { 59 | ret = errno ? -errno : -EIO; 60 | break; 61 | } 62 | } 63 | close(fd); 64 | errno = -ret; 65 | return i == len; 66 | } 67 | #else 68 | #include "wincompat/getrandom.c" 69 | #endif 70 | 71 | int genkey_main(int argc, char *argv[]) 72 | { 73 | uint8_t key[WG_KEY_LEN]; 74 | char base64[WG_KEY_LEN_BASE64]; 75 | struct stat stat; 76 | 77 | if (argc != 1) { 78 | fprintf(stderr, "Usage: %s %s\n", PROG_NAME, argv[0]); 79 | return 1; 80 | } 81 | 82 | if (!fstat(STDOUT_FILENO, &stat) && S_ISREG(stat.st_mode) && stat.st_mode & S_IRWXO) 83 | fputs("Warning: writing to world accessible file.\nConsider setting the umask to 077 and trying again.\n", stderr); 84 | 85 | if (!get_random_bytes(key, WG_KEY_LEN)) { 86 | perror("getrandom"); 87 | return 1; 88 | } 89 | if (!strcmp(argv[0], "genkey")) 90 | curve25519_clamp_secret(key); 91 | 92 | key_to_base64(base64, key); 93 | puts(base64); 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /wireguard-tools/wincompat/libc.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | char *strsep(char **str, const char *sep) 14 | { 15 | char *s = *str, *end; 16 | if (!s) 17 | return NULL; 18 | end = s + strcspn(s, sep); 19 | if (*end) 20 | *end++ = 0; 21 | else 22 | end = 0; 23 | *str = end; 24 | return s; 25 | } 26 | 27 | ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp) 28 | { 29 | char *ptr, *eptr; 30 | 31 | if (!*buf || !*bufsiz) { 32 | *bufsiz = BUFSIZ; 33 | if (!(*buf = malloc(*bufsiz))) 34 | return -1; 35 | } 36 | 37 | for (ptr = *buf, eptr = *buf + *bufsiz;;) { 38 | int c = fgetc(fp); 39 | if (c == -1) { 40 | if (feof(fp)) { 41 | ssize_t diff = (ssize_t)(ptr - *buf); 42 | if (diff != 0) { 43 | *ptr = '\0'; 44 | return diff; 45 | } 46 | } 47 | return -1; 48 | } 49 | *ptr++ = c; 50 | if (c == delimiter) { 51 | *ptr = '\0'; 52 | return ptr - *buf; 53 | } 54 | if (ptr + 2 >= eptr) { 55 | char *nbuf; 56 | size_t nbufsiz = *bufsiz * 2; 57 | ssize_t d = ptr - *buf; 58 | if ((nbuf = realloc(*buf, nbufsiz)) == NULL) 59 | return -1; 60 | *buf = nbuf; 61 | *bufsiz = nbufsiz; 62 | eptr = nbuf + nbufsiz; 63 | ptr = nbuf + d; 64 | } 65 | } 66 | } 67 | 68 | ssize_t getline(char **buf, size_t *bufsiz, FILE *fp) 69 | { 70 | return getdelim(buf, bufsiz, '\n', fp); 71 | } 72 | 73 | int inet_pton(int af, const char *src, void *dst) 74 | { 75 | struct sockaddr_storage ss = { 0 }; 76 | int size = sizeof(ss); 77 | char s[INET6_ADDRSTRLEN + 1]; 78 | 79 | strncpy(s, src, INET6_ADDRSTRLEN + 1); 80 | s[INET6_ADDRSTRLEN] = '\0'; 81 | 82 | if (WSAStringToAddress(s, af, NULL, (struct sockaddr *)&ss, &size)) 83 | return 0; 84 | if (af == AF_INET) 85 | *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr; 86 | else if (af == AF_INET6) 87 | *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr; 88 | else 89 | return 0; 90 | return 1; 91 | } 92 | 93 | const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) 94 | { 95 | struct sockaddr_storage ss = { .ss_family = af }; 96 | unsigned long s = size; 97 | 98 | if (af == AF_INET) 99 | ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src; 100 | else if (af == AF_INET6) 101 | ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src; 102 | else 103 | return NULL; 104 | return WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) ? NULL : dst; 105 | } 106 | -------------------------------------------------------------------------------- /wireguard-tools/curve25519.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2018-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include "curve25519.h" 7 | 8 | #include 9 | #include 10 | 11 | #ifndef __BYTE_ORDER__ 12 | #include 13 | #if !defined(BYTE_ORDER) || !defined(BIG_ENDIAN) || !defined(LITTLE_ENDIAN) 14 | #error "Unable to determine endianness." 15 | #endif 16 | #define __BYTE_ORDER__ BYTE_ORDER 17 | #define __ORDER_BIG_ENDIAN__ BIG_ENDIAN 18 | #define __ORDER_LITTLE_ENDIAN__ LITTLE_ENDIAN 19 | #endif 20 | 21 | #ifdef __linux__ 22 | #include 23 | typedef __u64 u64; 24 | typedef __u32 u32; 25 | typedef __u8 u8; 26 | typedef __s64 s64; 27 | #else 28 | typedef uint64_t u64, __le64; 29 | typedef uint32_t u32, __le32; 30 | typedef uint8_t u8; 31 | typedef int64_t s64; 32 | #endif 33 | #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 34 | #define le64_to_cpup(a) __builtin_bswap64(*(a)) 35 | #define le32_to_cpup(a) __builtin_bswap32(*(a)) 36 | #define cpu_to_le64(a) __builtin_bswap64(a) 37 | #else 38 | #define le64_to_cpup(a) (*(a)) 39 | #define le32_to_cpup(a) (*(a)) 40 | #define cpu_to_le64(a) (a) 41 | #endif 42 | static inline __le32 get_unaligned_le32(const u8 *a) 43 | { 44 | __le32 l; 45 | __builtin_memcpy(&l, a, sizeof(l)); 46 | return le32_to_cpup(&l); 47 | } 48 | static inline __le64 get_unaligned_le64(const u8 *a) 49 | { 50 | __le64 l; 51 | __builtin_memcpy(&l, a, sizeof(l)); 52 | return le64_to_cpup(&l); 53 | } 54 | static inline void put_unaligned_le64(u64 s, u8 *d) 55 | { 56 | __le64 l = cpu_to_le64(s); 57 | __builtin_memcpy(d, &l, sizeof(l)); 58 | } 59 | #ifndef __always_inline 60 | #define __always_inline __inline __attribute__((__always_inline__)) 61 | #endif 62 | #ifndef noinline 63 | #define noinline __attribute__((noinline)) 64 | #endif 65 | #ifndef __aligned 66 | #define __aligned(x) __attribute__((aligned(x))) 67 | #endif 68 | #ifndef __force 69 | #define __force 70 | #endif 71 | 72 | static noinline void memzero_explicit(void *s, size_t count) 73 | { 74 | memset(s, 0, count); 75 | asm volatile("": :"r"(s) : "memory"); 76 | } 77 | 78 | #ifdef __SIZEOF_INT128__ 79 | #include "../crypto/zinc/curve25519/curve25519-hacl64.c" 80 | #else 81 | #include "../crypto/zinc/curve25519/curve25519-fiat32.c" 82 | #endif 83 | 84 | void curve25519_generate_public(uint8_t pub[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE]) 85 | { 86 | static const uint8_t basepoint[CURVE25519_KEY_SIZE] __aligned(sizeof(uintptr_t)) = { 9 }; 87 | 88 | curve25519(pub, secret, basepoint); 89 | } 90 | 91 | void curve25519(uint8_t mypublic[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE], const uint8_t basepoint[static CURVE25519_KEY_SIZE]) 92 | { 93 | curve25519_generic(mypublic, secret, basepoint); 94 | } 95 | -------------------------------------------------------------------------------- /wireguard-tools/containers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #ifndef CONTAINERS_H 7 | #define CONTAINERS_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "../uapi/wireguard.h" 17 | 18 | /* Cross platform __kernel_timespec */ 19 | struct timespec64 { 20 | int64_t tv_sec; 21 | int64_t tv_nsec; 22 | }; 23 | 24 | struct wgallowedip { 25 | uint16_t family; 26 | union { 27 | struct in_addr ip4; 28 | struct in6_addr ip6; 29 | }; 30 | uint8_t cidr; 31 | struct wgallowedip *next_allowedip; 32 | }; 33 | 34 | enum { 35 | WGPEER_REMOVE_ME = 1U << 0, 36 | WGPEER_REPLACE_ALLOWEDIPS = 1U << 1, 37 | WGPEER_HAS_PUBLIC_KEY = 1U << 2, 38 | WGPEER_HAS_PRESHARED_KEY = 1U << 3, 39 | WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4 40 | }; 41 | 42 | struct wgpeer { 43 | uint32_t flags; 44 | 45 | uint8_t public_key[WG_KEY_LEN]; 46 | uint8_t preshared_key[WG_KEY_LEN]; 47 | 48 | union { 49 | struct sockaddr addr; 50 | struct sockaddr_in addr4; 51 | struct sockaddr_in6 addr6; 52 | } endpoint; 53 | 54 | struct timespec64 last_handshake_time; 55 | uint64_t rx_bytes, tx_bytes; 56 | uint16_t persistent_keepalive_interval; 57 | 58 | struct wgallowedip *first_allowedip, *last_allowedip; 59 | struct wgpeer *next_peer; 60 | }; 61 | 62 | enum { 63 | WGDEVICE_REPLACE_PEERS = 1U << 0, 64 | WGDEVICE_HAS_PRIVATE_KEY = 1U << 1, 65 | WGDEVICE_HAS_PUBLIC_KEY = 1U << 2, 66 | WGDEVICE_HAS_LISTEN_PORT = 1U << 3, 67 | WGDEVICE_HAS_FWMARK = 1U << 4 68 | }; 69 | 70 | struct wgdevice { 71 | char name[IFNAMSIZ]; 72 | uint32_t ifindex; 73 | 74 | uint32_t flags; 75 | 76 | uint8_t public_key[WG_KEY_LEN]; 77 | uint8_t private_key[WG_KEY_LEN]; 78 | 79 | uint32_t fwmark; 80 | uint16_t listen_port; 81 | 82 | struct wgpeer *first_peer, *last_peer; 83 | }; 84 | 85 | #define for_each_wgpeer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer) 86 | #define for_each_wgallowedip(__peer, __allowedip) for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); (__allowedip) = (__allowedip)->next_allowedip) 87 | 88 | static inline void free_wgdevice(struct wgdevice *dev) 89 | { 90 | if (!dev) 91 | return; 92 | for (struct wgpeer *peer = dev->first_peer, *np = peer ? peer->next_peer : NULL; peer; peer = np, np = peer ? peer->next_peer : NULL) { 93 | for (struct wgallowedip *allowedip = peer->first_allowedip, *na = allowedip ? allowedip->next_allowedip : NULL; allowedip; allowedip = na, na = allowedip ? allowedip->next_allowedip : NULL) 94 | free(allowedip); 95 | free(peer); 96 | } 97 | free(dev); 98 | } 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /wireguard-tools/showconf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "containers.h" 17 | #include "encoding.h" 18 | #include "ipc.h" 19 | #include "subcommands.h" 20 | 21 | int showconf_main(int argc, char *argv[]) 22 | { 23 | char base64[WG_KEY_LEN_BASE64]; 24 | char ip[INET6_ADDRSTRLEN]; 25 | struct wgdevice *device = NULL; 26 | struct wgpeer *peer; 27 | struct wgallowedip *allowedip; 28 | int ret = 1; 29 | 30 | if (argc != 2) { 31 | fprintf(stderr, "Usage: %s %s \n", PROG_NAME, argv[0]); 32 | return 1; 33 | } 34 | 35 | if (ipc_get_device(&device, argv[1])) { 36 | perror("Unable to access interface"); 37 | goto cleanup; 38 | } 39 | 40 | printf("[Interface]\n"); 41 | if (device->listen_port) 42 | printf("ListenPort = %u\n", device->listen_port); 43 | if (device->fwmark) 44 | printf("FwMark = 0x%x\n", device->fwmark); 45 | if (device->flags & WGDEVICE_HAS_PRIVATE_KEY) { 46 | key_to_base64(base64, device->private_key); 47 | printf("PrivateKey = %s\n", base64); 48 | } 49 | printf("\n"); 50 | for_each_wgpeer(device, peer) { 51 | key_to_base64(base64, peer->public_key); 52 | printf("[Peer]\nPublicKey = %s\n", base64); 53 | if (peer->flags & WGPEER_HAS_PRESHARED_KEY) { 54 | key_to_base64(base64, peer->preshared_key); 55 | printf("PresharedKey = %s\n", base64); 56 | } 57 | if (peer->first_allowedip) 58 | printf("AllowedIPs = "); 59 | for_each_wgallowedip(peer, allowedip) { 60 | if (allowedip->family == AF_INET) { 61 | if (!inet_ntop(AF_INET, &allowedip->ip4, ip, INET6_ADDRSTRLEN)) 62 | continue; 63 | } else if (allowedip->family == AF_INET6) { 64 | if (!inet_ntop(AF_INET6, &allowedip->ip6, ip, INET6_ADDRSTRLEN)) 65 | continue; 66 | } else 67 | continue; 68 | printf("%s/%d", ip, allowedip->cidr); 69 | if (allowedip->next_allowedip) 70 | printf(", "); 71 | } 72 | if (peer->first_allowedip) 73 | printf("\n"); 74 | 75 | if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) { 76 | char host[4096 + 1]; 77 | char service[512 + 1]; 78 | socklen_t addr_len = 0; 79 | 80 | if (peer->endpoint.addr.sa_family == AF_INET) 81 | addr_len = sizeof(struct sockaddr_in); 82 | else if (peer->endpoint.addr.sa_family == AF_INET6) 83 | addr_len = sizeof(struct sockaddr_in6); 84 | if (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) { 85 | if (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':')) 86 | printf("Endpoint = [%s]:%s\n", host, service); 87 | else 88 | printf("Endpoint = %s:%s\n", host, service); 89 | } 90 | } 91 | 92 | if (peer->persistent_keepalive_interval) 93 | printf("PersistentKeepalive = %u\n", peer->persistent_keepalive_interval); 94 | 95 | if (peer->next_peer) 96 | printf("\n"); 97 | } 98 | ret = 0; 99 | 100 | cleanup: 101 | free_wgdevice(device); 102 | return ret; 103 | } 104 | -------------------------------------------------------------------------------- /wireguard-tools/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # 3 | # Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | 5 | PKG_CONFIG ?= pkg-config 6 | PREFIX ?= /usr 7 | DESTDIR ?= 8 | SYSCONFDIR ?= /etc 9 | BINDIR ?= $(PREFIX)/bin 10 | LIBDIR ?= $(PREFIX)/lib 11 | MANDIR ?= $(PREFIX)/share/man 12 | BASHCOMPDIR ?= $(PREFIX)/share/bash-completion/completions 13 | SYSTEMDUNITDIR ?= $(shell $(PKG_CONFIG) --variable=systemdsystemunitdir systemd 2>/dev/null || echo "$(PREFIX)/lib/systemd/system") 14 | RUNSTATEDIR ?= /var/run 15 | WITH_BASHCOMPLETION ?= 16 | WITH_WGQUICK ?= 17 | WITH_SYSTEMDUNITS ?= 18 | 19 | ifeq ($(WITH_BASHCOMPLETION),) 20 | ifneq ($(strip $(wildcard $(BASHCOMPDIR))),) 21 | WITH_BASHCOMPLETION := yes 22 | endif 23 | endif 24 | ifeq ($(WITH_WGQUICK),) 25 | ifneq ($(strip $(wildcard $(BINDIR)/bash)),) 26 | WITH_WGQUICK := yes 27 | endif 28 | ifneq ($(strip $(wildcard $(DESTDIR)/bin/bash)),) 29 | WITH_WGQUICK := yes 30 | endif 31 | endif 32 | ifeq ($(WITH_SYSTEMDUNITS),) 33 | ifneq ($(strip $(wildcard $(SYSTEMDUNITDIR))),) 34 | WITH_SYSTEMDUNITS := yes 35 | endif 36 | endif 37 | 38 | PLATFORM ?= $(shell uname -s | tr '[:upper:]' '[:lower:]') 39 | 40 | CFLAGS ?= -O3 41 | CFLAGS += -std=gnu99 -D_GNU_SOURCE 42 | CFLAGS += -Wall -Wextra 43 | CFLAGS += -MMD -MP 44 | CFLAGS += -DRUNSTATEDIR="\"$(RUNSTATEDIR)\"" 45 | ifeq ($(DEBUG_TOOLS),y) 46 | CFLAGS += -g 47 | endif 48 | ifeq ($(PLATFORM),linuxxx) 49 | LIBMNL_CFLAGS := $(shell $(PKG_CONFIG) --cflags libmnl 2>/dev/null) 50 | LIBMNL_LDLIBS := $(shell $(PKG_CONFIG) --libs libmnl 2>/dev/null || echo -lmnl) 51 | CFLAGS += $(LIBMNL_CFLAGS) 52 | LDLIBS += $(LIBMNL_LDLIBS) 53 | endif 54 | ifeq ($(PLATFORM),haiku) 55 | LDLIBS += -lnetwork -lbsd 56 | endif 57 | ifeq ($(PLATFORM),windows) 58 | CC := x86_64-w64-mingw32-gcc 59 | CFLAGS += -Iwincompat/include -include wincompat/compat.h 60 | LDLIBS += -lws2_32 61 | wg: wincompat/libc.o wincompat/init.o 62 | endif 63 | 64 | ifneq ($(V),1) 65 | BUILT_IN_LINK.o := $(LINK.o) 66 | LINK.o = @echo " LD $$(pwd)/$@"; 67 | LINK.o += $(BUILT_IN_LINK.o) 68 | BUILT_IN_COMPILE.c := $(COMPILE.c) 69 | COMPILE.c = @echo " CC $$(pwd)/$@"; 70 | COMPILE.c += $(BUILT_IN_COMPILE.c) 71 | endif 72 | 73 | wg: $(patsubst %.c,%.o,$(wildcard *.c)) 74 | 75 | ifneq ($(V),1) 76 | clean: 77 | @echo " CLEAN $$(pwd)/{wg,*.o,*.d}" 78 | @$(RM) wg *.o *.d 79 | else 80 | clean: 81 | $(RM) wg *.o *.d 82 | endif 83 | 84 | install: wg 85 | @install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 wg "$(DESTDIR)$(BINDIR)/wg" 86 | @install -v -d "$(DESTDIR)$(MANDIR)/man8" && install -v -m 0644 man/wg.8 "$(DESTDIR)$(MANDIR)/man8/wg.8" 87 | @[ "$(WITH_BASHCOMPLETION)" = "yes" ] || exit 0; \ 88 | install -v -d "$(DESTDIR)$(BASHCOMPDIR)" && install -v -m 0644 completion/wg.bash-completion "$(DESTDIR)$(BASHCOMPDIR)/wg" 89 | @[ "$(WITH_WGQUICK)" = "yes" ] || exit 0; \ 90 | install -v -m 0755 wg-quick/$(PLATFORM).bash "$(DESTDIR)$(BINDIR)/wg-quick" && install -v -m 0700 -d "$(DESTDIR)$(SYSCONFDIR)/wireguard" 91 | @[ "$(WITH_WGQUICK)" = "yes" ] || exit 0; \ 92 | install -v -m 0644 man/wg-quick.8 "$(DESTDIR)$(MANDIR)/man8/wg-quick.8" 93 | @[ "$(WITH_WGQUICK)" = "yes" -a "$(WITH_BASHCOMPLETION)" = "yes" ] || exit 0; \ 94 | install -v -m 0644 completion/wg-quick.bash-completion "$(DESTDIR)$(BASHCOMPDIR)/wg-quick" 95 | @[ "$(WITH_WGQUICK)" = "yes" -a "$(WITH_SYSTEMDUNITS)" = "yes" ] || exit 0; \ 96 | install -v -d "$(DESTDIR)$(SYSTEMDUNITDIR)" && install -v -m 0644 systemd/wg-quick@.service "$(DESTDIR)$(SYSTEMDUNITDIR)/wg-quick@.service" 97 | 98 | help: 99 | @cat INSTALL 100 | 101 | .PHONY: clean install help 102 | 103 | -include *.d 104 | -------------------------------------------------------------------------------- /wireguard-tools/completion/wg.bash-completion: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 3 | 4 | _wg_completion() { 5 | local a 6 | 7 | if [[ $COMP_CWORD -eq 1 ]]; then 8 | COMPREPLY+=( $(compgen -W "show showconf set setconf addconf genkey genpsk pubkey" -- "${COMP_WORDS[1]}") ) 9 | return 10 | fi 11 | case "${COMP_WORDS[1]}" in 12 | genkey|genpsk|pubkey|help) return; ;; 13 | show|showconf|set|setconf|addconf) ;; 14 | *) return; 15 | esac 16 | 17 | if [[ $COMP_CWORD -eq 2 ]]; then 18 | local extra 19 | [[ ${COMP_WORDS[1]} == show ]] && extra=" all interfaces" 20 | COMPREPLY+=( $(compgen -W "$(wg show interfaces 2>/dev/null)$extra" -- "${COMP_WORDS[2]}") ) 21 | return 22 | fi 23 | 24 | if [[ $COMP_CWORD -eq 3 && ${COMP_WORDS[1]} == show && ${COMP_WORDS[2]} != interfaces ]]; then 25 | COMPREPLY+=( $(compgen -W "public-key private-key listen-port peers preshared-keys endpoints allowed-ips fwmark latest-handshakes persistent-keepalive transfer dump" -- "${COMP_WORDS[3]}") ) 26 | return 27 | fi 28 | 29 | if [[ $COMP_CWORD -eq 3 && ( ${COMP_WORDS[1]} == setconf || ${COMP_WORDS[1]} == addconf ) ]]; then 30 | compopt -o filenames 31 | mapfile -t a < <(compgen -f -- "${COMP_WORDS[3]}") 32 | COMPREPLY+=( "${a[@]}" ) 33 | return 34 | fi 35 | 36 | [[ ${COMP_WORDS[1]} == set ]] || return 37 | 38 | local has_listen_port=0 has_fwmark=0 has_private_key=0 has_preshared_key=0 has_peer=0 has_remove=0 has_endpoint=0 has_persistent_keepalive=0 has_allowed_ips=0 words=() i j 39 | for ((i=3;i/dev/null)" -- "${COMP_WORDS[COMP_CWORD]}") ) 62 | return 63 | fi 64 | 65 | for ((i=has_peer;i. All Rights Reserved. 4 | * 5 | * This is a specialized constant-time base64/hex implementation that resists side-channel attacks. 6 | */ 7 | 8 | #include 9 | #include "encoding.h" 10 | 11 | static inline void encode_base64(char dest[static 4], const uint8_t src[static 3]) 12 | { 13 | const uint8_t input[] = { (src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63 }; 14 | 15 | for (unsigned int i = 0; i < 4; ++i) 16 | dest[i] = input[i] + 'A' 17 | + (((25 - input[i]) >> 8) & 6) 18 | - (((51 - input[i]) >> 8) & 75) 19 | - (((61 - input[i]) >> 8) & 15) 20 | + (((62 - input[i]) >> 8) & 3); 21 | 22 | } 23 | 24 | void key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN]) 25 | { 26 | unsigned int i; 27 | 28 | for (i = 0; i < WG_KEY_LEN / 3; ++i) 29 | encode_base64(&base64[i * 4], &key[i * 3]); 30 | encode_base64(&base64[i * 4], (const uint8_t[]){ key[i * 3 + 0], key[i * 3 + 1], 0 }); 31 | base64[WG_KEY_LEN_BASE64 - 2] = '='; 32 | base64[WG_KEY_LEN_BASE64 - 1] = '\0'; 33 | } 34 | 35 | static inline int decode_base64(const char src[static 4]) 36 | { 37 | int val = 0; 38 | 39 | for (unsigned int i = 0; i < 4; ++i) 40 | val |= (-1 41 | + ((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) & (src[i] - 64)) 42 | + ((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) & (src[i] - 70)) 43 | + ((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5)) 44 | + ((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63) 45 | + ((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64) 46 | ) << (18 - 6 * i); 47 | return val; 48 | } 49 | 50 | bool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64) 51 | { 52 | unsigned int i; 53 | volatile uint8_t ret = 0; 54 | int val; 55 | 56 | if (strlen(base64) != WG_KEY_LEN_BASE64 - 1 || base64[WG_KEY_LEN_BASE64 - 2] != '=') 57 | return false; 58 | 59 | for (i = 0; i < WG_KEY_LEN / 3; ++i) { 60 | val = decode_base64(&base64[i * 4]); 61 | ret |= (uint32_t)val >> 31; 62 | key[i * 3 + 0] = (val >> 16) & 0xff; 63 | key[i * 3 + 1] = (val >> 8) & 0xff; 64 | key[i * 3 + 2] = val & 0xff; 65 | } 66 | val = decode_base64((const char[]){ base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' }); 67 | ret |= ((uint32_t)val >> 31) | (val & 0xff); 68 | key[i * 3 + 0] = (val >> 16) & 0xff; 69 | key[i * 3 + 1] = (val >> 8) & 0xff; 70 | 71 | return 1 & ((ret - 1) >> 8); 72 | } 73 | 74 | void key_to_hex(char hex[static WG_KEY_LEN_HEX], const uint8_t key[static WG_KEY_LEN]) 75 | { 76 | unsigned int i; 77 | 78 | for (i = 0; i < WG_KEY_LEN; ++i) { 79 | hex[i * 2] = 87U + (key[i] >> 4) + ((((key[i] >> 4) - 10U) >> 8) & ~38U); 80 | hex[i * 2 + 1] = 87U + (key[i] & 0xf) + ((((key[i] & 0xf) - 10U) >> 8) & ~38U); 81 | } 82 | hex[i * 2] = '\0'; 83 | } 84 | 85 | bool key_from_hex(uint8_t key[static WG_KEY_LEN], const char *hex) 86 | { 87 | uint8_t c, c_acc, c_alpha0, c_alpha, c_num0, c_num, c_val; 88 | volatile uint8_t ret = 0; 89 | 90 | if (strlen(hex) != WG_KEY_LEN_HEX - 1) 91 | return false; 92 | 93 | for (unsigned int i = 0; i < WG_KEY_LEN_HEX - 1; i += 2) { 94 | c = (uint8_t)hex[i]; 95 | c_num = c ^ 48U; 96 | c_num0 = (c_num - 10U) >> 8; 97 | c_alpha = (c & ~32U) - 55U; 98 | c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8; 99 | ret |= ((c_num0 | c_alpha0) - 1) >> 8; 100 | c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha); 101 | c_acc = c_val * 16U; 102 | 103 | c = (uint8_t)hex[i + 1]; 104 | c_num = c ^ 48U; 105 | c_num0 = (c_num - 10U) >> 8; 106 | c_alpha = (c & ~32U) - 55U; 107 | c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8; 108 | ret |= ((c_num0 | c_alpha0) - 1) >> 8; 109 | c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha); 110 | key[i / 2] = c_acc | c_val; 111 | } 112 | 113 | return 1 & ((ret - 1) >> 8); 114 | } 115 | 116 | bool key_is_zero(const uint8_t key[static WG_KEY_LEN]) 117 | { 118 | volatile uint8_t acc = 0; 119 | 120 | for (unsigned int i = 0; i < WG_KEY_LEN; ++i) { 121 | acc |= key[i]; 122 | asm volatile("" : "=r"(acc) : "0"(acc)); 123 | } 124 | return 1 & ((acc - 1) >> 8); 125 | } 126 | -------------------------------------------------------------------------------- /wireguard-tools/wincompat/ipc.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static FILE *userspace_interface_file(const char *interface) 13 | { 14 | char fname[MAX_PATH], error_message[1024 * 128] = { 0 }; 15 | HANDLE thread_token, process_snapshot, winlogon_process, winlogon_token, duplicated_token, pipe_handle = INVALID_HANDLE_VALUE; 16 | PROCESSENTRY32 entry = { .dwSize = sizeof(PROCESSENTRY32) }; 17 | BOOL ret; 18 | int fd; 19 | DWORD last_error = ERROR_SUCCESS; 20 | TOKEN_PRIVILEGES privileges = { 21 | .PrivilegeCount = 1, 22 | .Privileges = {{ .Attributes = SE_PRIVILEGE_ENABLED }} 23 | }; 24 | 25 | if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &privileges.Privileges[0].Luid)) 26 | goto err; 27 | 28 | process_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 29 | if (process_snapshot == INVALID_HANDLE_VALUE) 30 | goto err; 31 | for (ret = Process32First(process_snapshot, &entry); ret; last_error = GetLastError(), ret = Process32Next(process_snapshot, &entry)) { 32 | if (strcasecmp(entry.szExeFile, "winlogon.exe")) 33 | continue; 34 | 35 | RevertToSelf(); 36 | if (!ImpersonateSelf(SecurityImpersonation)) 37 | continue; 38 | if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, FALSE, &thread_token)) 39 | continue; 40 | if (!AdjustTokenPrivileges(thread_token, FALSE, &privileges, sizeof(privileges), NULL, NULL)) { 41 | last_error = GetLastError(); 42 | CloseHandle(thread_token); 43 | continue; 44 | } 45 | CloseHandle(thread_token); 46 | 47 | winlogon_process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, entry.th32ProcessID); 48 | if (!winlogon_process) 49 | continue; 50 | if (!OpenProcessToken(winlogon_process, TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &winlogon_token)) 51 | continue; 52 | CloseHandle(winlogon_process); 53 | if (!DuplicateToken(winlogon_token, SecurityImpersonation, &duplicated_token)) { 54 | last_error = GetLastError(); 55 | RevertToSelf(); 56 | continue; 57 | } 58 | CloseHandle(winlogon_token); 59 | if (!SetThreadToken(NULL, duplicated_token)) { 60 | last_error = GetLastError(); 61 | CloseHandle(duplicated_token); 62 | continue; 63 | } 64 | CloseHandle(duplicated_token); 65 | 66 | snprintf(fname, sizeof(fname), "\\\\.\\pipe\\WireGuard\\%s", interface); 67 | pipe_handle = CreateFile(fname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); 68 | last_error = GetLastError(); 69 | if (pipe_handle != INVALID_HANDLE_VALUE) { 70 | last_error = ERROR_SUCCESS; 71 | break; 72 | } 73 | } 74 | RevertToSelf(); 75 | CloseHandle(process_snapshot); 76 | 77 | if (last_error != ERROR_SUCCESS || pipe_handle == INVALID_HANDLE_VALUE) 78 | goto err; 79 | fd = _open_osfhandle((intptr_t)pipe_handle, _O_RDWR); 80 | if (fd == -1) { 81 | last_error = GetLastError(); 82 | CloseHandle(pipe_handle); 83 | goto err; 84 | } 85 | return _fdopen(fd, "r+"); 86 | 87 | err: 88 | if (last_error == ERROR_SUCCESS) 89 | last_error = GetLastError(); 90 | if (last_error == ERROR_SUCCESS) 91 | last_error = ERROR_ACCESS_DENIED; 92 | FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), error_message, sizeof(error_message) - 1, NULL); 93 | fprintf(stderr, "Error: Unable to open IPC handle via SYSTEM impersonation: %ld: %s\n", last_error, error_message); 94 | errno = EACCES; 95 | return NULL; 96 | } 97 | 98 | static int userspace_get_wireguard_interfaces(struct inflatable_buffer *buffer) 99 | { 100 | WIN32_FIND_DATA find_data; 101 | HANDLE find_handle; 102 | int ret = 0; 103 | 104 | find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data); 105 | if (find_handle == INVALID_HANDLE_VALUE) 106 | return -GetLastError(); 107 | do { 108 | if (strncmp("WireGuard\\", find_data.cFileName, 10)) 109 | continue; 110 | buffer->next = strdup(find_data.cFileName + 10); 111 | buffer->good = true; 112 | ret = add_next_to_inflatable_buffer(buffer); 113 | if (ret < 0) 114 | goto out; 115 | } while (FindNextFile(find_handle, &find_data)); 116 | 117 | out: 118 | FindClose(find_handle); 119 | return ret; 120 | } 121 | -------------------------------------------------------------------------------- /ssh-shell-server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | "log" 8 | "net" 9 | "os/exec" 10 | "sync" 11 | 12 | "golang.org/x/crypto/ssh" 13 | ) 14 | 15 | func main() { 16 | 17 | // In the latest version of crypto/ssh (after Go 1.3), the SSH server type has been removed 18 | // in favour of an SSH connection type. A ssh.ServerConn is created by passing an existing 19 | // net.Conn and a ssh.ServerConfig to ssh.NewServerConn, in effect, upgrading the net.Conn 20 | // into an ssh.ServerConn 21 | 22 | config := &ssh.ServerConfig{ 23 | //Define a function to run when a client attempts a password login 24 | PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { 25 | // Should use constant-time compare (or better, salt+hash) in a production setting. 26 | if c.User() == "foo" && string(pass) == "bar" { 27 | return nil, nil 28 | } 29 | return nil, fmt.Errorf("password rejected for %q", c.User()) 30 | }, 31 | // You may also explicitly allow anonymous client authentication, though anon bash 32 | // sessions may not be a wise idea 33 | // NoClientAuth: true, 34 | } 35 | 36 | // You can generate a keypair with 'ssh-keygen -t rsa' 37 | privateBytes, err := ioutil.ReadFile("/mnt/fastpath/user-apps/id_rsa") 38 | if err != nil { 39 | log.Fatal("Failed to load private key (./id_rsa)") 40 | } 41 | 42 | private, err := ssh.ParsePrivateKey(privateBytes) 43 | if err != nil { 44 | log.Fatal("Failed to parse private key") 45 | } 46 | 47 | config.AddHostKey(private) 48 | 49 | // Once a ServerConfig has been configured, connections can be accepted. 50 | listener, err := net.Listen("tcp", "0.0.0.0:2200") 51 | if err != nil { 52 | log.Fatalf("Failed to listen on 2200 (%s)", err) 53 | } 54 | 55 | // Accept all connections 56 | log.Print("Listening on 2200...") 57 | for { 58 | tcpConn, err := listener.Accept() 59 | if err != nil { 60 | log.Printf("Failed to accept incoming connection (%s)", err) 61 | continue 62 | } 63 | // Before use, a handshake must be performed on the incoming net.Conn. 64 | sshConn, chans, reqs, err := ssh.NewServerConn(tcpConn, config) 65 | if err != nil { 66 | log.Printf("Failed to handshake (%s)", err) 67 | continue 68 | } 69 | 70 | log.Printf("New SSH connection from %s (%s)", sshConn.RemoteAddr(), sshConn.ClientVersion()) 71 | // Discard all global out-of-band Requests 72 | go ssh.DiscardRequests(reqs) 73 | // Accept all channels 74 | go handleChannels(chans) 75 | } 76 | } 77 | 78 | func handleChannels(chans <-chan ssh.NewChannel) { 79 | // Service the incoming Channel channel in go routine 80 | for newChannel := range chans { 81 | go handleChannel(newChannel) 82 | } 83 | } 84 | 85 | func handleChannel(newChannel ssh.NewChannel) { 86 | // Since we're handling a shell, we expect a 87 | // channel type of "session". The also describes 88 | // "x11", "direct-tcpip" and "forwarded-tcpip" 89 | // channel types. 90 | if t := newChannel.ChannelType(); t != "session" { 91 | newChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t)) 92 | return 93 | } 94 | 95 | // At this point, we have the opportunity to reject the client's 96 | // request for another logical connection 97 | connection, requests, err := newChannel.Accept() 98 | if err != nil { 99 | log.Printf("Could not accept channel (%s)", err) 100 | return 101 | } 102 | 103 | // Fire up bash for this session 104 | bash := exec.Command("/bin/sh", "-i") 105 | stdin, _ := bash.StdinPipe() 106 | stdout, _ := bash.StdoutPipe() 107 | stderr, _ := bash.StderrPipe() 108 | err = bash.Start() 109 | if err != nil { 110 | log.Printf("ah shit, %s", err.Error()) 111 | } 112 | 113 | // Prepare teardown function 114 | close := func() { 115 | connection.Close() 116 | _, err := bash.Process.Wait() 117 | if err != nil { 118 | log.Printf("Failed to exit bash (%s)", err) 119 | } 120 | log.Printf("Session closed") 121 | } 122 | 123 | // Allocate a terminal for this channel 124 | log.Print("Creating pty...") 125 | // pty.StartWithSize(bash, nil) 126 | 127 | // bashf, err := pty.Start(bash) 128 | // if err != nil { 129 | // log.Printf("Could not start pty (%s)", err) 130 | // close() 131 | // return 132 | // } 133 | 134 | go func() { 135 | bash.Wait() 136 | }() 137 | 138 | //pipe session to bash and visa-versa 139 | var once sync.Once 140 | go func() { 141 | io.Copy(connection, stdout) 142 | once.Do(close) 143 | }() 144 | go func() { 145 | io.Copy(connection, stderr) 146 | once.Do(close) 147 | }() 148 | go func() { 149 | io.Copy(stdin, connection) 150 | once.Do(close) 151 | }() 152 | 153 | // Sessions have out-of-band requests such as "shell", "pty-req" and "env" 154 | go func() { 155 | for req := range requests { 156 | switch req.Type { 157 | case "shell": 158 | // We only accept the default shell 159 | // (i.e. no command in the Payload) 160 | if len(req.Payload) == 0 { 161 | req.Reply(true, nil) 162 | } 163 | case "pty-req": 164 | // termLen := req.Payload[3] 165 | // w, h := parseDims(req.Payload[termLen+4:]) 166 | // SetWinsize(bashf.Fd(), w, h) 167 | // Responding true (OK) here will let the client 168 | // know we have a pty ready for input 169 | req.Reply(true, nil) 170 | case "window-change": 171 | } 172 | } 173 | }() 174 | } 175 | -------------------------------------------------------------------------------- /wireguard-tools/man/wg-quick.8: -------------------------------------------------------------------------------- 1 | .TH WG-QUICK 8 "2016 January 1" ZX2C4 "WireGuard" 2 | 3 | .SH NAME 4 | wg-quick - set up a WireGuard interface simply 5 | 6 | .SH SYNOPSIS 7 | .B wg-quick 8 | [ 9 | .I up 10 | | 11 | .I down 12 | | 13 | .I save 14 | | 15 | .I strip 16 | ] [ 17 | .I CONFIG_FILE 18 | | 19 | .I INTERFACE 20 | ] 21 | 22 | .SH DESCRIPTION 23 | 24 | This is an extremely simple script for easily bringing up a WireGuard interface, 25 | suitable for a few common use cases. 26 | 27 | Use \fIup\fP to add and set up an interface, and use \fIdown\fP to tear down and remove 28 | an interface. Running \fIup\fP adds a WireGuard interface, brings up the interface with the 29 | supplied IP addresses, sets up mtu and routes, and optionally runs pre/post up scripts. Running \fIdown\fP 30 | optionally saves the current configuration, removes the WireGuard interface, and optionally 31 | runs pre/post down scripts. Running \fIsave\fP saves the configuration of an existing 32 | interface without bringing the interface down. Use \fIstrip\fP to output a configuration file 33 | with all 34 | .BR wg-quick (8)-specific 35 | options removed, suitable for use with 36 | .BR wg (8). 37 | 38 | \fICONFIG_FILE\fP is a configuration file, whose filename is the interface name 39 | followed by `.conf'. Otherwise, \fIINTERFACE\fP is an interface name, with configuration 40 | found at `/etc/wireguard/\fIINTERFACE\fP.conf', searched first, followed by distro-specific 41 | search paths. 42 | 43 | Generally speaking, this utility is just a simple script that wraps invocations to 44 | .BR wg (8) 45 | and 46 | .BR ip (8) 47 | in order to set up a WireGuard interface. It is designed for users with simple 48 | needs, and users with more advanced needs are highly encouraged to use a more 49 | specific tool, a more complete network manager, or otherwise just use 50 | .BR wg (8) 51 | and 52 | .BR ip (8), 53 | as usual. 54 | 55 | .SH CONFIGURATION 56 | 57 | The configuration file adds a few extra configuration values to the format understood by 58 | .BR wg (8) 59 | in order to configure additional attribute of an interface. It handles the 60 | values that it understands, and then it passes the remaining ones directly to 61 | .BR wg (8) 62 | for further processing. 63 | 64 | It infers all routes from the list of peers' allowed IPs, and automatically adds 65 | them to the system routing table. If one of those routes is the default route 66 | (0.0.0.0/0 or ::/0), then it uses 67 | .BR ip-rule (8) 68 | to handle overriding of the default gateway. 69 | 70 | The configuration file will be passed directly to \fBwg\fP(8)'s `setconf' 71 | sub-command, with the exception of the following additions to the \fIInterface\fP section, 72 | which are handled by this tool: 73 | 74 | .IP \(bu 75 | Address \(em a comma-separated list of IP (v4 or v6) addresses (optionally with CIDR masks) 76 | to be assigned to the interface. May be specified multiple times. 77 | .IP \(bu 78 | DNS \(em a comma-separated list of IP (v4 or v6) addresses to be set as the interface's 79 | DNS servers. May be specified multiple times. Upon bringing the interface up, this runs 80 | `resolvconf -a tun.\fIINTERFACE\fP -m 0 -x` and upon bringing it down, this runs 81 | `resolvconf -d tun.\fIINTERFACE\fP`. If these particular invocations of 82 | .BR resolvconf (8) 83 | are undesirable, the PostUp and PostDown keys below may be used instead. 84 | .IP \(bu 85 | MTU \(em if not specified, the MTU is automatically determined from the endpoint addresses 86 | or the system default route, which is usually a sane choice. However, to manually specify 87 | an MTU to override this automatic discovery, this value may be specified explicitly. 88 | .IP \(bu 89 | Table \(em Controls the routing table to which routes are added. There are two 90 | special values: `off' disables the creation of routes altogether, and `auto' 91 | (the default) adds routes to the default table and enables special handling of 92 | default routes. 93 | .IP \(bu 94 | PreUp, PostUp, PreDown, PostDown \(em script snippets which will be executed by 95 | .BR bash (1) 96 | before/after setting up/tearing down the interface, most commonly used 97 | to configure custom DNS options or firewall rules. The special string `%i' 98 | is expanded to \fIINTERFACE\fP. Each one may be specified multiple times, in which case 99 | the commands are executed in order. 100 | .IP \(bu 101 | SaveConfig \(em if set to `true', the configuration is saved from the current state of the 102 | interface upon shutdown. 103 | 104 | .P 105 | Recommended \fIINTERFACE\fP names include `wg0' or `wgvpn0' or even `wgmgmtlan0'. 106 | However, the number at the end is in fact optional, and really 107 | any free-form string [a-zA-Z0-9_=+.-]{1,15} will work. So even interface names corresponding 108 | to geographic locations would suffice, such as `cincinnati', `nyc', or `paris', if that's 109 | somehow desirable. 110 | 111 | .SH EXAMPLES 112 | 113 | These examples draw on the same syntax found for 114 | .BR wg (8), 115 | and a more complete description may be found there. Bold lines below are for options that extend 116 | .BR wg (8). 117 | 118 | The following might be used for connecting as a client to a VPN gateway for tunneling all 119 | traffic: 120 | 121 | [Interface] 122 | .br 123 | \fBAddress = 10.200.100.8/24\fP 124 | .br 125 | \fBDNS = 10.200.100.1\fP 126 | .br 127 | PrivateKey = oK56DE9Ue9zK76rAc8pBl6opph+1v36lm7cXXsQKrQM= 128 | .br 129 | 130 | .br 131 | [Peer] 132 | .br 133 | PublicKey = GtL7fZc/bLnqZldpVofMCD6hDjrK28SsdLxevJ+qtKU= 134 | .br 135 | PresharedKey = /UwcSPg38hW/D9Y3tcS1FOV0K1wuURMbS0sesJEP5ak= 136 | .br 137 | AllowedIPs = 0.0.0.0/0 138 | .br 139 | Endpoint = demo.wireguard.com:51820 140 | .br 141 | 142 | The `Address` field is added here in order to set up the address for the interface. The `DNS` field 143 | indicates that a DNS server for the interface should be configured via 144 | .BR resolvconf (8). 145 | The peer's allowed IPs entry implies that this interface should be configured as the default gateway, 146 | which this script does. 147 | 148 | Building on the last example, one might attempt the so-called ``kill-switch'', in order 149 | to prevent the flow of unencrypted packets through the non-WireGuard interfaces, by adding the following 150 | two lines `PostUp` and `PreDown` lines to the `[Interface]` section: 151 | 152 | \fBPostUp = iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT\fP 153 | .br 154 | \fBPreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT\fP 155 | .br 156 | 157 | The `PostUp' and `PreDown' fields have been added to specify an 158 | .BR iptables (8) 159 | command which, when used with interfaces that have a peer that specifies 0.0.0.0/0 as part of the 160 | `AllowedIPs', works together with wg-quick's fwmark usage in order to drop all packets that 161 | are either not coming out of the tunnel encrypted or not going through the tunnel itself. (Note 162 | that this continues to allow most DHCP traffic through, since most DHCP clients make use of PF_PACKET 163 | sockets, which bypass Netfilter.) When IPv6 is in use, additional similar lines could be added using 164 | .BR ip6tables (8). 165 | 166 | Or, perhaps it is desirable to store private keys in encrypted form, such as through use of 167 | .BR pass (1): 168 | 169 | \fBPostUp = wg set %i private-key <(pass WireGuard/private-keys/%i)\fP 170 | .br 171 | 172 | For use on a server, the following is a more complicated example involving multiple peers: 173 | 174 | [Interface] 175 | .br 176 | \fBAddress = 10.192.122.1/24\fP 177 | .br 178 | \fBAddress = 10.10.0.1/16\fP 179 | .br 180 | \fBSaveConfig = true\fP 181 | .br 182 | PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk= 183 | .br 184 | ListenPort = 51820 185 | .br 186 | 187 | .br 188 | [Peer] 189 | .br 190 | PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg= 191 | .br 192 | AllowedIPs = 10.192.122.3/32, 10.192.124.1/24 193 | .br 194 | 195 | .br 196 | [Peer] 197 | .br 198 | PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0= 199 | .br 200 | AllowedIPs = 10.192.122.4/32, 192.168.0.0/16 201 | .br 202 | 203 | .br 204 | [Peer] 205 | .br 206 | PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA= 207 | .br 208 | AllowedIPs = 10.10.10.230/32 209 | 210 | Notice the two `Address' lines at the top, and that `SaveConfig' is set to `true', indicating 211 | that the configuration file should be saved on shutdown using the current status of the 212 | interface. 213 | 214 | A combination of the `Table', `PostUp', and `PreDown' fields may be used for policy routing 215 | as well. For example, the following may be used to send SSH traffic (TCP port 22) traffic 216 | through the tunnel: 217 | 218 | [Interface] 219 | .br 220 | Address = 10.192.122.1/24 221 | .br 222 | PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk= 223 | .br 224 | ListenPort = 51820 225 | .br 226 | \fBTable = 1234\fP 227 | .br 228 | \fBPostUp = ip rule add ipproto tcp dport 22 table 1234\fP 229 | .br 230 | \fBPreDown = ip rule delete ipproto tcp dport 22 table 1234\fP 231 | .br 232 | 233 | .br 234 | [Peer] 235 | .br 236 | PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg= 237 | .br 238 | AllowedIPs = 0.0.0.0/0 239 | 240 | These configuration files may be placed in any directory, putting the desired interface name 241 | in the filename: 242 | 243 | \fB # wg-quick up /path/to/wgnet0.conf\fP 244 | 245 | For convenience, if only an interface name is supplied, it automatically chooses a path in 246 | `/etc/wireguard/': 247 | 248 | \fB # wg-quick up wgnet0\fP 249 | 250 | This will load the configuration file `/etc/wireguard/wgnet0.conf'. 251 | 252 | The \fIstrip\fP command is useful for reloading configuration files without disrupting active 253 | sessions: 254 | 255 | \fB # wg addconf wgnet0 <(wg-quick strip wgnet0)\fP 256 | 257 | (Note that the above command will add and update peers but will not remove peers.) 258 | 259 | .SH SEE ALSO 260 | .BR wg (8), 261 | .BR ip (8), 262 | .BR ip-link (8), 263 | .BR ip-address (8), 264 | .BR ip-route (8), 265 | .BR ip-rule (8), 266 | .BR resolvconf (8). 267 | 268 | .SH AUTHOR 269 | .B wg-quick 270 | was written by 271 | .MT Jason@zx2c4.com 272 | Jason A. Donenfeld 273 | .ME . 274 | For updates and more information, a project page is available on the 275 | .UR https://\:www.wireguard.com/ 276 | World Wide Web 277 | .UE . 278 | -------------------------------------------------------------------------------- /wireguard-tools/man/wg.8: -------------------------------------------------------------------------------- 1 | .TH WG 8 "2015 August 13" ZX2C4 "WireGuard" 2 | 3 | .SH NAME 4 | wg - set and retrieve configuration of WireGuard interfaces 5 | 6 | .SH SYNOPSIS 7 | .B wg 8 | [ 9 | .I COMMAND 10 | ] [ 11 | .I OPTIONS 12 | ]... [ 13 | .I ARGS 14 | ]... 15 | 16 | .SH DESCRIPTION 17 | 18 | .B wg 19 | is the configuration utility for getting and setting the configuration of 20 | WireGuard tunnel interfaces. The interfaces themselves can be added and removed 21 | using 22 | .BR ip-link (8) 23 | and their IP addresses and routing tables can be set using 24 | .BR ip-address (8) 25 | and 26 | .BR ip-route (8). 27 | The 28 | .B wg 29 | utility provides a series of sub-commands for changing WireGuard-specific 30 | aspects of WireGuard interfaces. 31 | 32 | If no COMMAND is specified, COMMAND defaults to 33 | .BR show . 34 | Sub-commands that take an INTERFACE must be passed a WireGuard interface. 35 | 36 | .SH COMMANDS 37 | 38 | .TP 39 | \fBshow\fP { \fI\fP | \fIall\fP | \fIinterfaces\fP } [\fIpublic-key\fP | \fIprivate-key\fP | \fIlisten-port\fP | \fIfwmark\fP | \fIpeers\fP | \fIpreshared-keys\fP | \fIendpoints\fP | \fIallowed-ips\fP | \fIlatest-handshakes\fP | \fIpersistent-keepalive\fP | \fItransfer\fP | \fIdump\fP] 40 | Shows current WireGuard configuration and runtime information of specified \fI\fP. 41 | If no \fI\fP is specified, \fI\fP defaults to \fIall\fP. 42 | If \fIinterfaces\fP is specified, prints a list of all WireGuard interfaces, 43 | one per line, and quit. If no options are given after the interface 44 | specification, then prints a list of all attributes in a visually pleasing way 45 | meant for the terminal. Otherwise, prints specified information grouped by 46 | newlines and tabs, meant to be used in scripts. For this script-friendly display, 47 | if \fIall\fP is specified, then the first field for all categories of information 48 | is the interface name. If \fPdump\fP is specified, then several lines are printed; 49 | the first contains in order separated by tab: private-key, public-key, listen-port, 50 | fwmark. Subsequent lines are printed for each peer and contain in order separated 51 | by tab: public-key, preshared-key, endpoint, allowed-ips, latest-handshake, 52 | transfer-rx, transfer-tx, persistent-keepalive. 53 | .TP 54 | \fBshowconf\fP \fI\fP 55 | Shows the current configuration of \fI\fP in the format described 56 | by \fICONFIGURATION FILE FORMAT\fP below. 57 | .TP 58 | \fBset\fP \fI\fP [\fIlisten-port\fP \fI\fP] [\fIfwmark\fP \fI\fP] [\fIprivate-key\fP \fI\fP] [\fIpeer\fP \fI\fP [\fIremove\fP] [\fIpreshared-key\fP \fI\fP] [\fIendpoint\fP \fI:\fP] [\fIpersistent-keepalive\fP \fI\fP] [\fIallowed-ips\fP \fI/\fP[,\fI/\fP]...] ]... 59 | Sets configuration values for the specified \fI\fP. Multiple 60 | \fIpeer\fPs may be specified, and if the \fIremove\fP argument is given 61 | for a peer, that peer is removed, not configured. If \fIlisten-port\fP 62 | is not specified, the port will be chosen randomly when the 63 | interface comes up. Both \fIprivate-key\fP and \fIpreshared-key\fP must 64 | be a files, because command line arguments are not considered private on 65 | most systems but if you are using 66 | .BR bash (1), 67 | you may safely pass in a string by specifying as \fIprivate-key\fP or 68 | \fIpreshared-key\fP the expression: <(echo PRIVATEKEYSTRING). If 69 | \fI/dev/null\fP or another empty file is specified as the filename for 70 | either \fIprivate-key\fP or \fIpreshared-key\fP, the key is removed from 71 | the device. The use of \fIpreshared-key\fP is optional, and may be omitted; 72 | it adds an additional layer of symmetric-key cryptography to be mixed into 73 | the already existing public-key cryptography, for post-quantum resistance. 74 | If \fIallowed-ips\fP is specified, but the value is the empty string, all 75 | allowed ips are removed from the peer. The use of \fIpersistent-keepalive\fP 76 | is optional and is by default off; setting it to 0 or "off" disables it. 77 | Otherwise it represents, in seconds, between 1 and 65535 inclusive, how often 78 | to send an authenticated empty packet to the peer, for the purpose of keeping 79 | a stateful firewall or NAT mapping valid persistently. For example, if the 80 | interface very rarely sends traffic, but it might at anytime receive traffic 81 | from a peer, and it is behind NAT, the interface might benefit from having a 82 | persistent keepalive interval of 25 seconds; however, most users will not need 83 | this. The use of \fIfwmark\fP is optional and is by default off; setting it to 84 | 0 or "off" disables it. Otherwise it is a 32-bit fwmark for outgoing packets 85 | and may be specified in hexadecimal by prepending "0x". 86 | .TP 87 | \fBsetconf\fP \fI\fP \fI\fP 88 | Sets the current configuration of \fI\fP to the contents of 89 | \fI\fP, which must be in the format described 90 | by \fICONFIGURATION FILE FORMAT\fP below. 91 | .TP 92 | \fBaddconf\fP \fI\fP \fI\fP 93 | Appends the contents of \fI\fP, which must 94 | be in the format described by \fICONFIGURATION FILE FORMAT\fP below, 95 | to the current configuration of \fI\fP. 96 | .TP 97 | \fBgenkey\fP 98 | Generates a random \fIprivate\fP key in base64 and prints it to 99 | standard output. 100 | .TP 101 | \fBgenpsk\fP 102 | Generates a random \fIpreshared\fP key in base64 and prints it to 103 | standard output. 104 | .TP 105 | \fBpubkey\fP 106 | Calculates a \fIpublic\fP key and prints it in base64 to standard 107 | output from a corresponding \fIprivate\fP key (generated with 108 | \fIgenkey\fP) given in base64 on standard input. 109 | 110 | A private key and a corresponding public key may be generated at once by calling: 111 | .br 112 | $ umask 077 113 | .br 114 | $ wg genkey | tee private.key | wg pubkey > public.key 115 | .TP 116 | \fBhelp\fP 117 | Show usage message. 118 | 119 | .SH CONFIGURATION FILE FORMAT 120 | The configuration file format is based on \fIINI\fP. There are two top level sections 121 | -- \fIInterface\fP and \fIPeer\fP. Multiple \fIPeer\fP sections may be specified, but 122 | only one \fIInterface\fP section may be specified. 123 | 124 | .P 125 | The \fIInterface\fP section may contain the following fields: 126 | .IP \(bu 127 | PrivateKey \(em a base64 private key generated by \fIwg genkey\fP. Required. 128 | .IP \(bu 129 | ListenPort \(em a 16-bit port for listening. Optional; if not specified, chosen 130 | randomly. 131 | .IP \(bu 132 | FwMark \(em a 32-bit fwmark for outgoing packets. If set to 0 or "off", this 133 | option is disabled. May be specified in hexadecimal by prepending "0x". Optional. 134 | .P 135 | The \fIPeer\fP sections may contain the following fields: 136 | .IP \(bu 137 | PublicKey \(em a base64 public key calculated by \fIwg pubkey\fP from a 138 | private key, and usually transmitted out of band to the author of the 139 | configuration file. Required. 140 | .IP \(bu 141 | PresharedKey \(em a base64 preshared key generated by \fIwg genpsk\fP. Optional, 142 | and may be omitted. This option adds an additional layer of symmetric-key 143 | cryptography to be mixed into the already existing public-key cryptography, 144 | for post-quantum resistance. 145 | .IP \(bu 146 | AllowedIPs \(em a comma-separated list of IP (v4 or v6) addresses with 147 | CIDR masks from which incoming traffic for this peer is allowed and to 148 | which outgoing traffic for this peer is directed. The catch-all 149 | \fI0.0.0.0/0\fP may be specified for matching all IPv4 addresses, and 150 | \fI::/0\fP may be specified for matching all IPv6 addresses. May be specified 151 | multiple times. 152 | .IP \(bu 153 | Endpoint \(em an endpoint IP or hostname, followed by a colon, and then a 154 | port number. This endpoint will be updated automatically to the most recent 155 | source IP address and port of correctly authenticated packets from the peer. 156 | Optional. 157 | .IP \(bu 158 | PersistentKeepalive \(em a seconds interval, between 1 and 65535 inclusive, of 159 | how often to send an authenticated empty packet to the peer for the purpose of keeping a 160 | stateful firewall or NAT mapping valid persistently. For example, if the interface 161 | very rarely sends traffic, but it might at anytime receive traffic from a peer, 162 | and it is behind NAT, the interface might benefit from having a persistent keepalive 163 | interval of 25 seconds. If set to 0 or "off", this option is disabled. By default or 164 | when unspecified, this option is off. Most users will not need this. Optional. 165 | 166 | .SH CONFIGURATION FILE FORMAT EXAMPLE 167 | This example may be used as a model for writing configuration files, following an 168 | INI-like syntax. Characters after and including a '#' are considered comments and 169 | are thus ignored. 170 | 171 | [Interface] 172 | .br 173 | PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk= 174 | .br 175 | ListenPort = 51820 176 | .br 177 | 178 | .br 179 | [Peer] 180 | .br 181 | PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg= 182 | .br 183 | Endpoint = 192.95.5.67:1234 184 | .br 185 | AllowedIPs = 10.192.122.3/32, 10.192.124.1/24 186 | .br 187 | 188 | .br 189 | [Peer] 190 | .br 191 | PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0= 192 | .br 193 | Endpoint = [2607:5300:60:6b0::c05f:543]:2468 194 | .br 195 | AllowedIPs = 10.192.122.4/32, 192.168.0.0/16 196 | .br 197 | 198 | .br 199 | [Peer] 200 | .br 201 | PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA= 202 | .br 203 | Endpoint = test.wireguard.com:18981 204 | .br 205 | AllowedIPs = 10.10.10.230/32 206 | 207 | .SH ENVIRONMENT VARIABLES 208 | .TP 209 | .I WG_COLOR_MODE 210 | If set to \fIalways\fP, always print ANSI colorized output. If set to \fInever\fP, never print ANSI colorized output. If set to \fIauto\fP, something invalid, or unset, then print ANSI colorized output only when writing to a TTY. 211 | .TP 212 | .I WG_HIDE_KEYS 213 | If set to \fInever\fP, then the pretty-printing \fBshow\fP sub-command will show private and preshared keys in the output. If set to \fIalways\fP, something invalid, or unset, then private and preshared keys will be printed as "(hidden)". 214 | .TP 215 | .I WG_ENDPOINT_RESOLUTION_RETRIES 216 | If set to an integer or to \fIinfinity\fP, DNS resolution for each peer's endpoint will be retried that many times for non-permanent errors, with an increasing delay between retries. If unset, the default is 15 retries. 217 | 218 | .SH SEE ALSO 219 | .BR ip (8), 220 | .BR ip-link (8), 221 | .BR ip-address (8), 222 | .BR ip-route (8). 223 | 224 | .SH AUTHOR 225 | .B wg 226 | was written by 227 | .MT Jason@zx2c4.com 228 | Jason A. Donenfeld 229 | .ME . 230 | For updates and more information, a project page is available on the 231 | .UR https://\:www.wireguard.com/ 232 | World Wide Web 233 | .UE . 234 | -------------------------------------------------------------------------------- /wireguard-tools/wg-quick/linux.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 5 | # 6 | 7 | set -e -o pipefail 8 | shopt -s extglob 9 | export LC_ALL=C 10 | 11 | SELF="$(readlink -f "${BASH_SOURCE[0]}")" 12 | export PATH="${SELF%/*}:$PATH" 13 | 14 | WG_CONFIG="" 15 | INTERFACE="" 16 | ADDRESSES=( ) 17 | MTU="" 18 | DNS=( ) 19 | TABLE="" 20 | PRE_UP=( ) 21 | POST_UP=( ) 22 | PRE_DOWN=( ) 23 | POST_DOWN=( ) 24 | SAVE_CONFIG=0 25 | CONFIG_FILE="" 26 | PROGRAM="${0##*/}" 27 | ARGS=( "$@" ) 28 | 29 | cmd() { 30 | echo "[#] $*" >&2 31 | "$@" 32 | } 33 | 34 | die() { 35 | echo "$PROGRAM: $*" >&2 36 | exit 1 37 | } 38 | 39 | parse_options() { 40 | local interface_section=0 line key value stripped 41 | CONFIG_FILE="$1" 42 | [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE="/etc/wireguard/$CONFIG_FILE.conf" 43 | [[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist" 44 | [[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]] || die "The config file must be a valid interface name, followed by .conf" 45 | CONFIG_FILE="$(readlink -f "$CONFIG_FILE")" 46 | ((($(stat -c '0%#a' "$CONFIG_FILE") & $(stat -c '0%#a' "${CONFIG_FILE%/*}") & 0007) == 0)) || echo "Warning: \`$CONFIG_FILE' is world accessible" >&2 47 | INTERFACE="${BASH_REMATCH[2]}" 48 | shopt -s nocasematch 49 | while read -r line || [[ -n $line ]]; do 50 | stripped="${line%%\#*}" 51 | key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}" 52 | value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}" 53 | [[ $key == "["* ]] && interface_section=0 54 | [[ $key == "[Interface]" ]] && interface_section=1 55 | if [[ $interface_section -eq 1 ]]; then 56 | case "$key" in 57 | Address) ADDRESSES+=( ${value//,/ } ); continue ;; 58 | MTU) MTU="$value"; continue ;; 59 | DNS) DNS+=( ${value//,/ } ); continue ;; 60 | Table) TABLE="$value"; continue ;; 61 | PreUp) PRE_UP+=( "$value" ); continue ;; 62 | PreDown) PRE_DOWN+=( "$value" ); continue ;; 63 | PostUp) POST_UP+=( "$value" ); continue ;; 64 | PostDown) POST_DOWN+=( "$value" ); continue ;; 65 | SaveConfig) read_bool SAVE_CONFIG "$value"; continue ;; 66 | esac 67 | fi 68 | WG_CONFIG+="$line"$'\n' 69 | done < "$CONFIG_FILE" 70 | shopt -u nocasematch 71 | } 72 | 73 | read_bool() { 74 | case "$2" in 75 | true) printf -v "$1" 1 ;; 76 | false) printf -v "$1" 0 ;; 77 | *) die "\`$2' is neither true nor false" 78 | esac 79 | } 80 | 81 | auto_su() { 82 | [[ $UID == 0 ]] || exec sudo -p "$PROGRAM must be run as root. Please enter the password for %u to continue: " -- "$BASH" -- "$SELF" "${ARGS[@]}" 83 | } 84 | 85 | add_if() { 86 | local ret 87 | if ! cmd ip link add "$INTERFACE" type wireguard; then 88 | ret=$? 89 | [[ -e /sys/module/wireguard ]] || ! command -v "${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}" >/dev/null && exit $ret 90 | echo "[!] Missing WireGuard kernel module. Falling back to slow userspace implementation." 91 | cmd "${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}" "$INTERFACE" 92 | fi 93 | } 94 | 95 | del_if() { 96 | local table 97 | [[ $HAVE_SET_DNS -eq 0 ]] || unset_dns 98 | if [[ -z $TABLE || $TABLE == auto ]] && get_fwmark table && [[ $(wg show "$INTERFACE" allowed-ips) =~ /0(\ |$'\n'|$) ]]; then 99 | while [[ $(ip -4 rule show) == *"lookup $table"* ]]; do 100 | cmd ip -4 rule delete table $table 101 | done 102 | while [[ $(ip -4 rule show) == *"from all lookup main suppress_prefixlength 0"* ]]; do 103 | cmd ip -4 rule delete table main suppress_prefixlength 0 104 | done 105 | while [[ $(ip -6 rule show) == *"lookup $table"* ]]; do 106 | cmd ip -6 rule delete table $table 107 | done 108 | while [[ $(ip -6 rule show) == *"from all lookup main suppress_prefixlength 0"* ]]; do 109 | cmd ip -6 rule delete table main suppress_prefixlength 0 110 | done 111 | fi 112 | cmd ip link delete dev "$INTERFACE" 113 | } 114 | 115 | add_addr() { 116 | local proto=-4 117 | [[ $1 == *:* ]] && proto=-6 118 | cmd ip $proto address add "$1" dev "$INTERFACE" 119 | } 120 | 121 | set_mtu_up() { 122 | local mtu=0 endpoint output 123 | if [[ -n $MTU ]]; then 124 | cmd ip link set mtu "$MTU" up dev "$INTERFACE" 125 | return 126 | fi 127 | while read -r _ endpoint; do 128 | [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue 129 | output="$(ip route get "${BASH_REMATCH[1]}" || true)" 130 | [[ ( $output =~ mtu\ ([0-9]+) || ( $output =~ dev\ ([^ ]+) && $(ip link show dev "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) ) ) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}" 131 | done < <(wg show "$INTERFACE" endpoints) 132 | if [[ $mtu -eq 0 ]]; then 133 | read -r output < <(ip route show default || true) || true 134 | [[ ( $output =~ mtu\ ([0-9]+) || ( $output =~ dev\ ([^ ]+) && $(ip link show dev "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) ) ) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}" 135 | fi 136 | [[ $mtu -gt 0 ]] || mtu=1500 137 | cmd ip link set mtu $(( mtu - 80 )) up dev "$INTERFACE" 138 | } 139 | 140 | resolvconf_iface_prefix() { 141 | [[ -f /etc/resolvconf/interface-order ]] || return 0 142 | local iface 143 | while read -r iface; do 144 | [[ $iface =~ ^([A-Za-z0-9-]+)\*$ ]] || continue 145 | echo "${BASH_REMATCH[1]}." && return 0 146 | done < /etc/resolvconf/interface-order 147 | } 148 | 149 | HAVE_SET_DNS=0 150 | set_dns() { 151 | [[ ${#DNS[@]} -gt 0 ]] || return 0 152 | printf 'nameserver %s\n' "${DNS[@]}" | cmd resolvconf -a "$(resolvconf_iface_prefix)$INTERFACE" -m 0 -x 153 | HAVE_SET_DNS=1 154 | } 155 | 156 | unset_dns() { 157 | [[ ${#DNS[@]} -gt 0 ]] || return 0 158 | cmd resolvconf -d "$(resolvconf_iface_prefix)$INTERFACE" 159 | } 160 | 161 | add_route() { 162 | local proto=-4 163 | [[ $1 == *:* ]] && proto=-6 164 | [[ $TABLE != off ]] || return 0 165 | 166 | if [[ -n $TABLE && $TABLE != auto ]]; then 167 | cmd ip $proto route add "$1" dev "$INTERFACE" table "$TABLE" 168 | elif [[ $1 == */0 ]]; then 169 | add_default "$1" 170 | else 171 | [[ -n $(ip $proto route show dev "$INTERFACE" match "$1" 2>/dev/null) ]] || cmd ip $proto route add "$1" dev "$INTERFACE" 172 | fi 173 | } 174 | 175 | get_fwmark() { 176 | local fwmark 177 | fwmark="$(wg show "$INTERFACE" fwmark)" || return 1 178 | [[ -n $fwmark && $fwmark != off ]] || return 1 179 | printf -v "$1" "%d" "$fwmark" 180 | return 0 181 | } 182 | 183 | add_default() { 184 | local table proto key value 185 | if ! get_fwmark table; then 186 | table=51820 187 | while [[ -n $(ip -4 route show table $table) || -n $(ip -6 route show table $table) ]]; do 188 | ((table++)) 189 | done 190 | cmd wg set "$INTERFACE" fwmark $table 191 | fi 192 | proto=-4 193 | [[ $1 == *:* ]] && proto=-6 194 | cmd ip $proto route add "$1" dev "$INTERFACE" table $table 195 | cmd ip $proto rule add not fwmark $table table $table 196 | cmd ip $proto rule add table main suppress_prefixlength 0 197 | while read -r key _ value; do 198 | [[ $value -eq 1 ]] && sysctl -q "$key=2" 199 | done < <(sysctl -a -r '^net\.ipv4.conf\.[^ .=]+\.rp_filter$') 200 | return 0 201 | } 202 | 203 | set_config() { 204 | cmd wg setconf "$INTERFACE" <(echo "$WG_CONFIG") 205 | } 206 | 207 | save_config() { 208 | local old_umask new_config current_config address cmd 209 | [[ $(ip -all -brief address show dev "$INTERFACE") =~ ^$INTERFACE\ +\ [A-Z]+\ +(.+)$ ]] || true 210 | new_config=$'[Interface]\n' 211 | for address in ${BASH_REMATCH[1]}; do 212 | new_config+="Address = $address"$'\n' 213 | done 214 | while read -r address; do 215 | [[ $address =~ ^nameserver\ ([a-zA-Z0-9_=+:%.-]+)$ ]] && new_config+="DNS = ${BASH_REMATCH[1]}"$'\n' 216 | done < <(resolvconf -l "$(resolvconf_iface_prefix)$INTERFACE" 2>/dev/null || cat "/etc/resolvconf/run/interface/$(resolvconf_iface_prefix)$INTERFACE" 2>/dev/null) 217 | [[ -n $MTU && $(ip link show dev "$INTERFACE") =~ mtu\ ([0-9]+) ]] && new_config+="MTU = ${BASH_REMATCH[1]}"$'\n' 218 | [[ -n $TABLE ]] && new_config+="Table = $TABLE"$'\n' 219 | [[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\n' 220 | for cmd in "${PRE_UP[@]}"; do 221 | new_config+="PreUp = $cmd"$'\n' 222 | done 223 | for cmd in "${POST_UP[@]}"; do 224 | new_config+="PostUp = $cmd"$'\n' 225 | done 226 | for cmd in "${PRE_DOWN[@]}"; do 227 | new_config+="PreDown = $cmd"$'\n' 228 | done 229 | for cmd in "${POST_DOWN[@]}"; do 230 | new_config+="PostDown = $cmd"$'\n' 231 | done 232 | old_umask="$(umask)" 233 | umask 077 234 | current_config="$(cmd wg showconf "$INTERFACE")" 235 | trap 'rm -f "$CONFIG_FILE.tmp"; exit' INT TERM EXIT 236 | echo "${current_config/\[Interface\]$'\n'/$new_config}" > "$CONFIG_FILE.tmp" || die "Could not write configuration file" 237 | sync "$CONFIG_FILE.tmp" 238 | mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" || die "Could not move configuration file" 239 | trap - INT TERM EXIT 240 | umask "$old_umask" 241 | } 242 | 243 | execute_hooks() { 244 | local hook 245 | for hook in "$@"; do 246 | hook="${hook//%i/$INTERFACE}" 247 | echo "[#] $hook" >&2 248 | (eval "$hook") 249 | done 250 | } 251 | 252 | cmd_usage() { 253 | cat >&2 <<-_EOF 254 | Usage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ] 255 | 256 | CONFIG_FILE is a configuration file, whose filename is the interface name 257 | followed by \`.conf'. Otherwise, INTERFACE is an interface name, with 258 | configuration found at /etc/wireguard/INTERFACE.conf. It is to be readable 259 | by wg(8)'s \`setconf' sub-command, with the exception of the following additions 260 | to the [Interface] section, which are handled by $PROGRAM: 261 | 262 | - Address: may be specified one or more times and contains one or more 263 | IP addresses (with an optional CIDR mask) to be set for the interface. 264 | - DNS: an optional DNS server to use while the device is up. 265 | - MTU: an optional MTU for the interface; if unspecified, auto-calculated. 266 | - Table: an optional routing table to which routes will be added; if 267 | unspecified or \`auto', the default table is used. If \`off', no routes 268 | are added. 269 | - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed 270 | by bash(1) at the corresponding phases of the link, most commonly used 271 | to configure DNS. The string \`%i' is expanded to INTERFACE. 272 | - SaveConfig: if set to \`true', the configuration is saved from the current 273 | state of the interface upon shutdown. 274 | 275 | See wg-quick(8) for more info and examples. 276 | _EOF 277 | } 278 | 279 | cmd_up() { 280 | local i 281 | [[ -z $(ip link show dev "$INTERFACE" 2>/dev/null) ]] || die "\`$INTERFACE' already exists" 282 | trap 'del_if; exit' INT TERM EXIT 283 | execute_hooks "${PRE_UP[@]}" 284 | add_if 285 | set_config 286 | for i in "${ADDRESSES[@]}"; do 287 | add_addr "$i" 288 | done 289 | set_mtu_up 290 | set_dns 291 | for i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$i"; done; done < <(wg show "$INTERFACE" allowed-ips) | sort -nr -k 2 -t /); do 292 | add_route "$i" 293 | done 294 | execute_hooks "${POST_UP[@]}" 295 | trap - INT TERM EXIT 296 | } 297 | 298 | cmd_down() { 299 | [[ " $(wg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface" 300 | execute_hooks "${PRE_DOWN[@]}" 301 | [[ $SAVE_CONFIG -eq 0 ]] || save_config 302 | del_if 303 | unset_dns 304 | execute_hooks "${POST_DOWN[@]}" 305 | } 306 | 307 | cmd_save() { 308 | [[ " $(wg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface" 309 | save_config 310 | } 311 | 312 | cmd_strip() { 313 | echo "$WG_CONFIG" 314 | } 315 | 316 | # ~~ function override insertion point ~~ 317 | 318 | if [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then 319 | cmd_usage 320 | elif [[ $# -eq 2 && $1 == up ]]; then 321 | auto_su 322 | parse_options "$2" 323 | cmd_up 324 | elif [[ $# -eq 2 && $1 == down ]]; then 325 | auto_su 326 | parse_options "$2" 327 | cmd_down 328 | elif [[ $# -eq 2 && $1 == save ]]; then 329 | auto_su 330 | parse_options "$2" 331 | cmd_save 332 | elif [[ $# -eq 2 && $1 == strip ]]; then 333 | auto_su 334 | parse_options "$2" 335 | cmd_strip 336 | else 337 | cmd_usage 338 | exit 1 339 | fi 340 | 341 | exit 0 342 | -------------------------------------------------------------------------------- /wireguard-tools/wg-quick/openbsd.bash: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 5 | # 6 | 7 | set -e -o pipefail 8 | shopt -s extglob 9 | export LC_ALL=C 10 | 11 | SELF="$(readlink -f "${BASH_SOURCE[0]}")" 12 | export PATH="${SELF%/*}:$PATH" 13 | 14 | WG_CONFIG="" 15 | INTERFACE="" 16 | ADDRESSES=( ) 17 | MTU="" 18 | DNS=( ) 19 | TABLE="" 20 | PRE_UP=( ) 21 | POST_UP=( ) 22 | PRE_DOWN=( ) 23 | POST_DOWN=( ) 24 | SAVE_CONFIG=0 25 | CONFIG_FILE="" 26 | PROGRAM="${0##*/}" 27 | ARGS=( "$@" ) 28 | 29 | cmd() { 30 | echo "[#] $*" >&2 31 | "$@" 32 | } 33 | 34 | die() { 35 | echo "$PROGRAM: $*" >&2 36 | exit 1 37 | } 38 | 39 | parse_options() { 40 | local interface_section=0 line key value stripped 41 | CONFIG_FILE="$1" 42 | [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE="/etc/wireguard/$CONFIG_FILE.conf" 43 | [[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist" 44 | [[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]] || die "The config file must be a valid interface name, followed by .conf" 45 | CONFIG_FILE="$(readlink -f "$CONFIG_FILE")" 46 | ((($(stat -f '0%#p' "$CONFIG_FILE") & $(stat -f '0%#p' "${CONFIG_FILE%/*}") & 0007) == 0)) || echo "Warning: \`$CONFIG_FILE' is world accessible" >&2 47 | INTERFACE="${BASH_REMATCH[2]}" 48 | shopt -s nocasematch 49 | while read -r line || [[ -n $line ]]; do 50 | stripped="${line%%\#*}" 51 | key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}" 52 | value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}" 53 | [[ $key == "["* ]] && interface_section=0 54 | [[ $key == "[Interface]" ]] && interface_section=1 55 | if [[ $interface_section -eq 1 ]]; then 56 | case "$key" in 57 | Address) ADDRESSES+=( ${value//,/ } ); continue ;; 58 | MTU) MTU="$value"; continue ;; 59 | DNS) DNS+=( ${value//,/ } ); continue ;; 60 | Table) TABLE="$value"; continue ;; 61 | PreUp) PRE_UP+=( "$value" ); continue ;; 62 | PreDown) PRE_DOWN+=( "$value" ); continue ;; 63 | PostUp) POST_UP+=( "$value" ); continue ;; 64 | PostDown) POST_DOWN+=( "$value" ); continue ;; 65 | SaveConfig) read_bool SAVE_CONFIG "$value"; continue ;; 66 | esac 67 | fi 68 | WG_CONFIG+="$line"$'\n' 69 | done < "$CONFIG_FILE" 70 | shopt -u nocasematch 71 | } 72 | 73 | read_bool() { 74 | case "$2" in 75 | true) printf -v "$1" 1 ;; 76 | false) printf -v "$1" 0 ;; 77 | *) die "\`$2' is neither true nor false" 78 | esac 79 | } 80 | 81 | auto_su() { 82 | [[ $UID == 0 ]] || exec doas -- "$BASH" -- "$SELF" "${ARGS[@]}" 83 | } 84 | 85 | 86 | get_real_interface() { 87 | local interface diff 88 | wg show interfaces >/dev/null 89 | [[ -f "/var/run/wireguard/$INTERFACE.name" ]] || return 1 90 | interface="$(< "/var/run/wireguard/$INTERFACE.name")" 91 | [[ -n $interface && -S "/var/run/wireguard/$interface.sock" ]] || return 1 92 | diff=$(( $(stat -f %m "/var/run/wireguard/$interface.sock" 2>/dev/null || echo 200) - $(stat -f %m "/var/run/wireguard/$INTERFACE.name" 2>/dev/null || echo 100) )) 93 | [[ $diff -ge 2 || $diff -le -2 ]] && return 1 94 | REAL_INTERFACE="$interface" 95 | echo "[+] Interface for $INTERFACE is $REAL_INTERFACE" >&2 96 | return 0 97 | } 98 | 99 | add_if() { 100 | export WG_TUN_NAME_FILE="/var/run/wireguard/$INTERFACE.name" 101 | mkdir -p "/var/run/wireguard/" 102 | cmd "${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}" tun 103 | get_real_interface 104 | } 105 | 106 | del_routes() { 107 | local todelete=( ) destination gateway netif 108 | [[ -n $REAL_INTERFACE ]] || return 0 109 | while read -r destination _ _ _ _ netif _; do 110 | [[ $netif == "$REAL_INTERFACE" ]] && todelete+=( "$destination" ) 111 | done < <(netstat -nr -f inet) 112 | for destination in "${todelete[@]}"; do 113 | cmd route -q -n delete -inet "$destination" || true 114 | done 115 | todelete=( ) 116 | while read -r destination gateway _ netif; do 117 | [[ $netif == "$REAL_INTERFACE" || ( $netif == lo* && $gateway == "$REAL_INTERFACE" ) ]] && todelete+=( "$destination" ) 118 | done < <(netstat -nr -f inet6) 119 | for destination in "${todelete[@]}"; do 120 | cmd route -q -n delete -inet6 "$destination" || true 121 | done 122 | for destination in "${ENDPOINTS[@]}"; do 123 | if [[ $destination == *:* ]]; then 124 | cmd route -q -n delete -inet6 "$destination" || true 125 | else 126 | cmd route -q -n delete -inet "$destination" || true 127 | fi 128 | done 129 | } 130 | 131 | del_if() { 132 | unset_dns 133 | [[ -z $REAL_INTERFACE ]] || cmd rm -f "/var/run/wireguard/$REAL_INTERFACE.sock" 134 | cmd rm -f "/var/run/wireguard/$INTERFACE.name" 135 | } 136 | 137 | up_if() { 138 | cmd ifconfig "$REAL_INTERFACE" up 139 | } 140 | 141 | add_addr() { 142 | local family 143 | if [[ $1 == *:* ]]; then 144 | family=inet6 145 | [[ -n $FIRSTADDR6 ]] || FIRSTADDR6="${1%/*}" 146 | else 147 | family=inet 148 | [[ -n $FIRSTADDR4 ]] || FIRSTADDR4="${1%/*}" 149 | fi 150 | cmd ifconfig "$REAL_INTERFACE" $family "$1" alias 151 | } 152 | 153 | set_mtu() { 154 | local mtu=0 endpoint output family 155 | if [[ -n $MTU ]]; then 156 | cmd ifconfig "$REAL_INTERFACE" mtu "$MTU" 157 | return 158 | fi 159 | while read -r _ endpoint; do 160 | [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue 161 | family=inet 162 | [[ ${BASH_REMATCH[1]} == *:* ]] && family=inet6 163 | output="$(route -n get "-$family" "${BASH_REMATCH[1]}" || true)" 164 | [[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}" 165 | done < <(wg show "$REAL_INTERFACE" endpoints) 166 | if [[ $mtu -eq 0 ]]; then 167 | read -r output < <(route -n get default || true) || true 168 | [[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}" 169 | fi 170 | [[ $mtu -gt 0 ]] || mtu=1500 171 | cmd ifconfig "$REAL_INTERFACE" mtu $(( mtu - 80 )) 172 | } 173 | 174 | 175 | collect_gateways() { 176 | local destination gateway 177 | 178 | GATEWAY4="" 179 | while read -r destination gateway _; do 180 | [[ $destination == default ]] || continue 181 | GATEWAY4="$gateway" 182 | break 183 | done < <(netstat -nr -f inet) 184 | 185 | GATEWAY6="" 186 | while read -r destination gateway _; do 187 | [[ $destination == default ]] || continue 188 | GATEWAY6="$gateway" 189 | break 190 | done < <(netstat -nr -f inet6) 191 | } 192 | 193 | collect_endpoints() { 194 | ENDPOINTS=( ) 195 | while read -r _ endpoint; do 196 | [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue 197 | ENDPOINTS+=( "${BASH_REMATCH[1]}" ) 198 | done < <(wg show "$REAL_INTERFACE" endpoints) 199 | } 200 | 201 | set_endpoint_direct_route() { 202 | local old_endpoints endpoint old_gateway4 old_gateway6 remove_all_old=0 added=( ) 203 | old_endpoints=( "${ENDPOINTS[@]}" ) 204 | old_gateway4="$GATEWAY4" 205 | old_gateway6="$GATEWAY6" 206 | collect_gateways 207 | collect_endpoints 208 | 209 | [[ $old_gateway4 != "$GATEWAY4" || $old_gateway6 != "$GATEWAY6" ]] && remove_all_old=1 210 | 211 | if [[ $remove_all_old -eq 1 ]]; then 212 | for endpoint in "${ENDPOINTS[@]}"; do 213 | [[ " ${old_endpoints[*]} " == *" $endpoint "* ]] || old_endpoints+=( "$endpoint" ) 214 | done 215 | fi 216 | 217 | for endpoint in "${old_endpoints[@]}"; do 218 | [[ $remove_all_old -eq 0 && " ${ENDPOINTS[*]} " == *" $endpoint "* ]] && continue 219 | if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then 220 | cmd route -q -n delete -inet6 "$endpoint" 2>/dev/null || true 221 | elif [[ $AUTO_ROUTE4 -eq 1 ]]; then 222 | cmd route -q -n delete -inet "$endpoint" 2>/dev/null || true 223 | fi 224 | done 225 | 226 | for endpoint in "${ENDPOINTS[@]}"; do 227 | if [[ $remove_all_old -eq 0 && " ${old_endpoints[*]} " == *" $endpoint "* ]]; then 228 | added+=( "$endpoint" ) 229 | continue 230 | fi 231 | if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then 232 | if [[ -n $GATEWAY6 ]]; then 233 | cmd route -q -n add -inet6 "$endpoint" -gateway "$GATEWAY6" || true 234 | else 235 | # Prevent routing loop 236 | cmd route -q -n add -inet6 "$endpoint" ::1 -blackhole || true 237 | fi 238 | added+=( "$endpoint" ) 239 | elif [[ $AUTO_ROUTE4 -eq 1 ]]; then 240 | if [[ -n $GATEWAY4 ]]; then 241 | cmd route -q -n add -inet "$endpoint" -gateway "$GATEWAY4" || true 242 | else 243 | # Prevent routing loop 244 | cmd route -q -n add -inet "$endpoint" 127.0.0.1 -blackhole || true 245 | fi 246 | added+=( "$endpoint" ) 247 | fi 248 | done 249 | ENDPOINTS=( "${added[@]}" ) 250 | } 251 | 252 | monitor_daemon() { 253 | echo "[+] Backgrounding route monitor" >&2 254 | (trap 'del_routes; exit 0' INT TERM EXIT 255 | exec >/dev/null 2>&1 256 | local event 257 | # TODO: this should also check to see if the endpoint actually changes 258 | # in response to incoming packets, and then call set_endpoint_direct_route 259 | # then too. That function should be able to gracefully cleanup if the 260 | # endpoints change. 261 | while read -r event; do 262 | [[ $event == RTM_* ]] || continue 263 | ifconfig "$REAL_INTERFACE" >/dev/null 2>&1 || break 264 | [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route 265 | # TODO: set the mtu as well, but only if up 266 | done < <(route -n monitor)) & disown 267 | } 268 | 269 | set_dns() { 270 | [[ ${#DNS[@]} -gt 0 ]] || return 0 271 | # TODO: this is a horrible way of doing it. Has OpenBSD no resolvconf? 272 | cmd cp /etc/resolv.conf "/etc/resolv.conf.wg-quick-backup.$INTERFACE" 273 | cmd printf 'nameserver %s\n' "${DNS[@]}" > /etc/resolv.conf 274 | } 275 | 276 | unset_dns() { 277 | [[ -f "/etc/resolv.conf.wg-quick-backup.$INTERFACE" ]] || return 0 278 | cmd mv "/etc/resolv.conf.wg-quick-backup.$INTERFACE" /etc/resolv.conf 279 | } 280 | 281 | add_route() { 282 | [[ $TABLE != off ]] || return 0 283 | local family ifaceroute 284 | 285 | if [[ $1 == *:* ]]; then 286 | family=inet6 287 | [[ -n $FIRSTADDR6 ]] || die "Local IPv6 address must be set to have routes" 288 | ifaceroute="$FIRSTADDR6" 289 | else 290 | family=inet 291 | [[ -n $FIRSTADDR4 ]] || die "Local IPv4 address must be set to have routes" 292 | ifaceroute="$FIRSTADDR4" 293 | fi 294 | 295 | if [[ -n $TABLE && $TABLE != auto ]]; then 296 | cmd route -q -n add "-$family" -rdomain "$TABLE" "$1" -iface "$ifaceroute" 297 | elif [[ $1 == */0 ]]; then 298 | if [[ $1 == *:* ]]; then 299 | AUTO_ROUTE6=1 300 | cmd route -q -n add -inet6 ::/1 -iface "$ifaceroute" 301 | cmd route -q -n add -inet6 8000::/1 -iface "$ifaceroute" 302 | else 303 | AUTO_ROUTE4=1 304 | cmd route -q -n add -inet 0.0.0.0/1 -iface "$ifaceroute" 305 | cmd route -q -n add -inet 128.0.0.0/1 -iface "$ifaceroute" 306 | fi 307 | else 308 | [[ $(route -n get "-$family" "$1" 2>/dev/null) =~ interface:\ $REAL_INTERFACE$'\n' ]] || cmd route -q -n add "-$family" "$1" -iface "$ifaceroute" 309 | fi 310 | } 311 | 312 | set_config() { 313 | cmd wg setconf "$REAL_INTERFACE" <(echo "$WG_CONFIG") 314 | } 315 | 316 | save_config() { 317 | local old_umask new_config current_config address network cmd 318 | new_config=$'[Interface]\n' 319 | { read -r _; while read -r _ _ network address _; do 320 | [[ $network == *Link* ]] || new_config+="Address = $address"$'\n' 321 | done } < <(netstat -I "$REAL_INTERFACE" -n -v) 322 | # TODO: actually determine current DNS for interface 323 | for address in "${DNS[@]}"; do 324 | new_config+="DNS = $address"$'\n' 325 | done 326 | [[ -n $MTU ]] && new_config+="MTU = $MTU"$'\n' 327 | [[ -n $TABLE ]] && new_config+="Table = $TABLE"$'\n' 328 | [[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\n' 329 | for cmd in "${PRE_UP[@]}"; do 330 | new_config+="PreUp = $cmd"$'\n' 331 | done 332 | for cmd in "${POST_UP[@]}"; do 333 | new_config+="PostUp = $cmd"$'\n' 334 | done 335 | for cmd in "${PRE_DOWN[@]}"; do 336 | new_config+="PreDown = $cmd"$'\n' 337 | done 338 | for cmd in "${POST_DOWN[@]}"; do 339 | new_config+="PostDown = $cmd"$'\n' 340 | done 341 | old_umask="$(umask)" 342 | umask 077 343 | current_config="$(cmd wg showconf "$REAL_INTERFACE")" 344 | trap 'rm -f "$CONFIG_FILE.tmp"; exit' INT TERM EXIT 345 | echo "${current_config/\[Interface\]$'\n'/$new_config}" > "$CONFIG_FILE.tmp" || die "Could not write configuration file" 346 | sync "$CONFIG_FILE.tmp" 347 | mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" || die "Could not move configuration file" 348 | trap - INT TERM EXIT 349 | umask "$old_umask" 350 | } 351 | 352 | execute_hooks() { 353 | local hook 354 | for hook in "$@"; do 355 | hook="${hook//%i/$REAL_INTERFACE}" 356 | hook="${hook//%I/$INTERFACE}" 357 | echo "[#] $hook" >&2 358 | (eval "$hook") 359 | done 360 | } 361 | 362 | cmd_usage() { 363 | cat >&2 <<-_EOF 364 | Usage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ] 365 | 366 | CONFIG_FILE is a configuration file, whose filename is the interface name 367 | followed by \`.conf'. Otherwise, INTERFACE is an interface name, with 368 | configuration found at /etc/wireguard/INTERFACE.conf. It is to be readable 369 | by wg(8)'s \`setconf' sub-command, with the exception of the following additions 370 | to the [Interface] section, which are handled by $PROGRAM: 371 | 372 | - Address: may be specified one or more times and contains one or more 373 | IP addresses (with an optional CIDR mask) to be set for the interface. 374 | - DNS: an optional DNS server to use while the device is up. 375 | - MTU: an optional MTU for the interface; if unspecified, auto-calculated. 376 | - Table: an optional routing table to which routes will be added; if 377 | unspecified or \`auto', the default table is used. If \`off', no routes 378 | are added. 379 | - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed 380 | by bash(1) at the corresponding phases of the link, most commonly used 381 | to configure DNS. The string \`%i' is expanded to INTERFACE. 382 | - SaveConfig: if set to \`true', the configuration is saved from the current 383 | state of the interface upon shutdown. 384 | 385 | See wg-quick(8) for more info and examples. 386 | _EOF 387 | } 388 | 389 | cmd_up() { 390 | local i 391 | get_real_interface && die "\`$INTERFACE' already exists as \`$REAL_INTERFACE'" 392 | trap 'del_if; del_routes; exit' INT TERM EXIT 393 | execute_hooks "${PRE_UP[@]}" 394 | add_if 395 | set_config 396 | for i in "${ADDRESSES[@]}"; do 397 | add_addr "$i" 398 | done 399 | set_mtu 400 | up_if 401 | set_dns 402 | for i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$i"; done; done < <(wg show "$REAL_INTERFACE" allowed-ips) | sort -nr -k 2 -t /); do 403 | add_route "$i" 404 | done 405 | [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route 406 | monitor_daemon 407 | execute_hooks "${POST_UP[@]}" 408 | trap - INT TERM EXIT 409 | } 410 | 411 | cmd_down() { 412 | if ! get_real_interface || [[ " $(wg show interfaces) " != *" $REAL_INTERFACE "* ]]; then 413 | die "\`$INTERFACE' is not a WireGuard interface" 414 | fi 415 | execute_hooks "${PRE_DOWN[@]}" 416 | [[ $SAVE_CONFIG -eq 0 ]] || save_config 417 | del_if 418 | unset_dns 419 | execute_hooks "${POST_DOWN[@]}" 420 | } 421 | 422 | cmd_save() { 423 | if ! get_real_interface || [[ " $(wg show interfaces) " != *" $REAL_INTERFACE "* ]]; then 424 | die "\`$INTERFACE' is not a WireGuard interface" 425 | fi 426 | save_config 427 | } 428 | 429 | cmd_strip() { 430 | echo "$WG_CONFIG" 431 | } 432 | 433 | # ~~ function override insertion point ~~ 434 | 435 | if [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then 436 | cmd_usage 437 | elif [[ $# -eq 2 && $1 == up ]]; then 438 | auto_su 439 | parse_options "$2" 440 | cmd_up 441 | elif [[ $# -eq 2 && $1 == down ]]; then 442 | auto_su 443 | parse_options "$2" 444 | cmd_down 445 | elif [[ $# -eq 2 && $1 == save ]]; then 446 | auto_su 447 | parse_options "$2" 448 | cmd_save 449 | elif [[ $# -eq 2 && $1 == strip ]]; then 450 | auto_su 451 | parse_options "$2" 452 | cmd_strip 453 | else 454 | cmd_usage 455 | exit 1 456 | fi 457 | 458 | exit 0 459 | -------------------------------------------------------------------------------- /wireguard-tools/wg-quick/freebsd.bash: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 5 | # 6 | 7 | set -e -o pipefail 8 | shopt -s extglob 9 | export LC_ALL=C 10 | 11 | SELF="$(readlink -f "${BASH_SOURCE[0]}")" 12 | export PATH="${SELF%/*}:$PATH" 13 | 14 | WG_CONFIG="" 15 | INTERFACE="" 16 | ADDRESSES=( ) 17 | MTU="" 18 | DNS=( ) 19 | TABLE="" 20 | PRE_UP=( ) 21 | POST_UP=( ) 22 | PRE_DOWN=( ) 23 | POST_DOWN=( ) 24 | SAVE_CONFIG=0 25 | CONFIG_FILE="" 26 | PROGRAM="${0##*/}" 27 | ARGS=( "$@" ) 28 | 29 | cmd() { 30 | echo "[#] $*" >&2 31 | "$@" 32 | } 33 | 34 | die() { 35 | echo "$PROGRAM: $*" >&2 36 | exit 1 37 | } 38 | 39 | CONFIG_SEARCH_PATHS=( /etc/wireguard /usr/local/etc/wireguard ) 40 | 41 | unset ORIGINAL_TMPDIR 42 | make_temp() { 43 | local old_umask 44 | 45 | [[ -v ORIGINAL_TMPDIR ]] && export TMPDIR="$ORIGINAL_TMPDIR" 46 | ORIGINAL_TMPDIR="$TMPDIR" 47 | [[ -z $TMPDIR ]] && unset TMPDIR 48 | 49 | old_umask="$(umask)" 50 | umask 077 51 | export TMPDIR="$(mktemp -d)" 52 | umask "$old_umask" 53 | 54 | [[ -d $TMPDIR ]] || die "Unable to create safe temporary directory" 55 | CLEANUP_TMPDIR="$TMPDIR" 56 | } 57 | 58 | clean_temp() { 59 | [[ -n $CLEANUP_TMPDIR ]] && rm -rf "$CLEANUP_TMPDIR" 60 | } 61 | 62 | parse_options() { 63 | local interface_section=0 line key value stripped path 64 | CONFIG_FILE="$1" 65 | if [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]]; then 66 | for path in "${CONFIG_SEARCH_PATHS[@]}"; do 67 | CONFIG_FILE="$path/$1.conf" 68 | [[ -e $CONFIG_FILE ]] && break 69 | done 70 | fi 71 | [[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist" 72 | [[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]] || die "The config file must be a valid interface name, followed by .conf" 73 | CONFIG_FILE="$(readlink -f "$CONFIG_FILE")" 74 | ((($(stat -f '0%#p' "$CONFIG_FILE") & $(stat -f '0%#p' "${CONFIG_FILE%/*}") & 0007) == 0)) || echo "Warning: \`$CONFIG_FILE' is world accessible" >&2 75 | INTERFACE="${BASH_REMATCH[2]}" 76 | shopt -s nocasematch 77 | while read -r line || [[ -n $line ]]; do 78 | stripped="${line%%\#*}" 79 | key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}" 80 | value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}" 81 | [[ $key == "["* ]] && interface_section=0 82 | [[ $key == "[Interface]" ]] && interface_section=1 83 | if [[ $interface_section -eq 1 ]]; then 84 | case "$key" in 85 | Address) ADDRESSES+=( ${value//,/ } ); continue ;; 86 | MTU) MTU="$value"; continue ;; 87 | DNS) DNS+=( ${value//,/ } ); continue ;; 88 | Table) TABLE="$value"; continue ;; 89 | PreUp) PRE_UP+=( "$value" ); continue ;; 90 | PreDown) PRE_DOWN+=( "$value" ); continue ;; 91 | PostUp) POST_UP+=( "$value" ); continue ;; 92 | PostDown) POST_DOWN+=( "$value" ); continue ;; 93 | SaveConfig) read_bool SAVE_CONFIG "$value"; continue ;; 94 | esac 95 | fi 96 | WG_CONFIG+="$line"$'\n' 97 | done < "$CONFIG_FILE" 98 | shopt -u nocasematch 99 | } 100 | 101 | read_bool() { 102 | case "$2" in 103 | true) printf -v "$1" 1 ;; 104 | false) printf -v "$1" 0 ;; 105 | *) die "\`$2' is neither true nor false" 106 | esac 107 | } 108 | 109 | auto_su() { 110 | [[ $UID == 0 ]] || exec sudo -p "$PROGRAM must be run as root. Please enter the password for %u to continue: " -- "$BASH" -- "$SELF" "${ARGS[@]}" 111 | } 112 | 113 | add_if() { 114 | cmd "${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}" "$INTERFACE" 115 | } 116 | 117 | del_routes() { 118 | local todelete=( ) destination gateway netif 119 | while read -r destination _ _ _ _ netif _; do 120 | [[ $netif == "$INTERFACE" ]] && todelete+=( "$destination" ) 121 | done < <(netstat -nr -f inet) 122 | for destination in "${todelete[@]}"; do 123 | cmd route -q -n delete -inet "$destination" || true 124 | done 125 | todelete=( ) 126 | while read -r destination gateway _ netif; do 127 | [[ $netif == "$INTERFACE" || ( $netif == lo* && $gateway == "$INTERFACE" ) ]] && todelete+=( "$destination" ) 128 | done < <(netstat -nr -f inet6) 129 | for destination in "${todelete[@]}"; do 130 | cmd route -q -n delete -inet6 "$destination" || true 131 | done 132 | for destination in "${ENDPOINTS[@]}"; do 133 | if [[ $destination == *:* ]]; then 134 | cmd route -q -n delete -inet6 "$destination" || true 135 | else 136 | cmd route -q -n delete -inet "$destination" || true 137 | fi 138 | done 139 | } 140 | 141 | if_exists() { 142 | # HACK: The goal is simply to determine whether or not the interface exists. The 143 | # straight-forward way of doing this would be `ifconfig $INTERFACE`, but this 144 | # invokes the SIOCGIFSTATUS ioctl, which races with interface shutdown inside 145 | # the tun driver, resulting in a kernel panic. So we work around it the stupid 146 | # way by using the one utility that appears to call if_nametoindex fairly early 147 | # and fails if it doesn't exist: `arp`. 148 | if arp -i "$INTERFACE" -a -n >/dev/null 2>&1; then 149 | return 0 150 | else 151 | return 1 152 | fi 153 | } 154 | 155 | del_if() { 156 | [[ $HAVE_SET_DNS -eq 0 ]] || unset_dns 157 | cmd rm -f "/var/run/wireguard/$INTERFACE.sock" 158 | while if_exists; do 159 | # HACK: it would be nice to `route monitor` here and wait for RTM_IFANNOUNCE 160 | # but it turns out that the announcement is made before the interface 161 | # disappears so we sometimes get a hang. So, we're instead left with polling 162 | # in a sleep loop like this. 163 | sleep 0.1 164 | done 165 | } 166 | 167 | up_if() { 168 | cmd ifconfig "$INTERFACE" up 169 | } 170 | 171 | add_addr() { 172 | if [[ $1 == *:* ]]; then 173 | cmd ifconfig "$INTERFACE" inet6 "$1" alias 174 | else 175 | cmd ifconfig "$INTERFACE" inet "$1" "${1%%/*}" alias 176 | fi 177 | } 178 | 179 | set_mtu() { 180 | local mtu=0 endpoint output family 181 | if [[ -n $MTU ]]; then 182 | cmd ifconfig "$INTERFACE" mtu "$MTU" 183 | return 184 | fi 185 | while read -r _ endpoint; do 186 | [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue 187 | family=inet 188 | [[ ${BASH_REMATCH[1]} == *:* ]] && family=inet6 189 | output="$(route -n get "-$family" "${BASH_REMATCH[1]}" || true)" 190 | [[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}" 191 | done < <(wg show "$INTERFACE" endpoints) 192 | if [[ $mtu -eq 0 ]]; then 193 | read -r output < <(route -n get default || true) || true 194 | [[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}" 195 | fi 196 | [[ $mtu -gt 0 ]] || mtu=1500 197 | cmd ifconfig "$INTERFACE" mtu $(( mtu - 80 )) 198 | } 199 | 200 | 201 | collect_gateways() { 202 | local destination gateway 203 | 204 | GATEWAY4="" 205 | while read -r destination gateway _; do 206 | [[ $destination == default ]] || continue 207 | GATEWAY4="$gateway" 208 | break 209 | done < <(netstat -nr -f inet) 210 | 211 | GATEWAY6="" 212 | while read -r destination gateway _; do 213 | [[ $destination == default ]] || continue 214 | GATEWAY6="$gateway" 215 | break 216 | done < <(netstat -nr -f inet6) 217 | } 218 | 219 | collect_endpoints() { 220 | ENDPOINTS=( ) 221 | while read -r _ endpoint; do 222 | [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue 223 | ENDPOINTS+=( "${BASH_REMATCH[1]}" ) 224 | done < <(wg show "$INTERFACE" endpoints) 225 | } 226 | 227 | set_endpoint_direct_route() { 228 | local old_endpoints endpoint old_gateway4 old_gateway6 remove_all_old=0 added=( ) 229 | old_endpoints=( "${ENDPOINTS[@]}" ) 230 | old_gateway4="$GATEWAY4" 231 | old_gateway6="$GATEWAY6" 232 | collect_gateways 233 | collect_endpoints 234 | 235 | [[ $old_gateway4 != "$GATEWAY4" || $old_gateway6 != "$GATEWAY6" ]] && remove_all_old=1 236 | 237 | if [[ $remove_all_old -eq 1 ]]; then 238 | for endpoint in "${ENDPOINTS[@]}"; do 239 | [[ " ${old_endpoints[*]} " == *" $endpoint "* ]] || old_endpoints+=( "$endpoint" ) 240 | done 241 | fi 242 | 243 | for endpoint in "${old_endpoints[@]}"; do 244 | [[ $remove_all_old -eq 0 && " ${ENDPOINTS[*]} " == *" $endpoint "* ]] && continue 245 | if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then 246 | cmd route -q -n delete -inet6 "$endpoint" 2>/dev/null || true 247 | elif [[ $AUTO_ROUTE4 -eq 1 ]]; then 248 | cmd route -q -n delete -inet "$endpoint" 2>/dev/null || true 249 | fi 250 | done 251 | 252 | for endpoint in "${ENDPOINTS[@]}"; do 253 | if [[ $remove_all_old -eq 0 && " ${old_endpoints[*]} " == *" $endpoint "* ]]; then 254 | added+=( "$endpoint" ) 255 | continue 256 | fi 257 | if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then 258 | if [[ -n $GATEWAY6 ]]; then 259 | cmd route -q -n add -inet6 "$endpoint" -gateway "$GATEWAY6" || true 260 | else 261 | # Prevent routing loop 262 | cmd route -q -n add -inet6 "$endpoint" ::1 -blackhole || true 263 | fi 264 | added+=( "$endpoint" ) 265 | elif [[ $AUTO_ROUTE4 -eq 1 ]]; then 266 | if [[ -n $GATEWAY4 ]]; then 267 | cmd route -q -n add -inet "$endpoint" -gateway "$GATEWAY4" || true 268 | else 269 | # Prevent routing loop 270 | cmd route -q -n add -inet "$endpoint" 127.0.0.1 -blackhole || true 271 | fi 272 | added+=( "$endpoint" ) 273 | fi 274 | done 275 | ENDPOINTS=( "${added[@]}" ) 276 | } 277 | 278 | monitor_daemon() { 279 | echo "[+] Backgrounding route monitor" >&2 280 | (make_temp 281 | trap 'del_routes; clean_temp; exit 0' INT TERM EXIT 282 | exec >/dev/null 2>&1 283 | local event 284 | # TODO: this should also check to see if the endpoint actually changes 285 | # in response to incoming packets, and then call set_endpoint_direct_route 286 | # then too. That function should be able to gracefully cleanup if the 287 | # endpoints change. 288 | while read -r event; do 289 | [[ $event == RTM_* ]] || continue 290 | [[ -e /var/run/wireguard/$INTERFACE.sock ]] || break 291 | if_exists || break 292 | [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route 293 | # TODO: set the mtu as well, but only if up 294 | done < <(route -n monitor)) & disown 295 | } 296 | 297 | HAVE_SET_DNS=0 298 | set_dns() { 299 | [[ ${#DNS[@]} -gt 0 ]] || return 0 300 | printf 'nameserver %s\n' "${DNS[@]}" | cmd resolvconf -a "$INTERFACE" -x 301 | HAVE_SET_DNS=1 302 | } 303 | 304 | unset_dns() { 305 | [[ ${#DNS[@]} -gt 0 ]] || return 0 306 | cmd resolvconf -d "$INTERFACE" 307 | } 308 | 309 | add_route() { 310 | [[ $TABLE != off ]] || return 0 311 | 312 | local family=inet 313 | [[ $1 == *:* ]] && family=inet6 314 | 315 | if [[ -n $TABLE && $TABLE != auto ]]; then 316 | cmd route -q -n add "-$family" -fib "$TABLE" "$1" -interface "$INTERFACE" 317 | elif [[ $1 == */0 ]]; then 318 | if [[ $1 == *:* ]]; then 319 | AUTO_ROUTE6=1 320 | cmd route -q -n add -inet6 ::/1 -interface "$INTERFACE" 321 | cmd route -q -n add -inet6 8000::/1 -interface "$INTERFACE" 322 | else 323 | AUTO_ROUTE4=1 324 | cmd route -q -n add -inet 0.0.0.0/1 -interface "$INTERFACE" 325 | cmd route -q -n add -inet 128.0.0.0/1 -interface "$INTERFACE" 326 | fi 327 | else 328 | [[ $(route -n get "-$family" "$1" 2>/dev/null) =~ interface:\ $INTERFACE$'\n' ]] || cmd route -q -n add "-$family" "$1" -interface "$INTERFACE" 329 | fi 330 | } 331 | 332 | set_config() { 333 | cmd wg setconf "$INTERFACE" <(echo "$WG_CONFIG") 334 | } 335 | 336 | save_config() { 337 | local old_umask new_config current_config address cmd 338 | new_config=$'[Interface]\n' 339 | { read -r _; while read -r _ _ _ address _; do 340 | new_config+="Address = $address"$'\n' 341 | done } < <(netstat -I "$INTERFACE" -n -W -f inet) 342 | { read -r _; while read -r _ _ _ address _; do 343 | new_config+="Address = $address"$'\n' 344 | done } < <(netstat -I "$INTERFACE" -n -W -f inet6) 345 | while read -r address; do 346 | [[ $address =~ ^nameserver\ ([a-zA-Z0-9_=+:%.-]+)$ ]] && new_config+="DNS = ${BASH_REMATCH[1]}"$'\n' 347 | done < <(resolvconf -l "$INTERFACE" 2>/dev/null) 348 | [[ -n $MTU ]] && new_config+="MTU = $MTU"$'\n' 349 | [[ -n $TABLE ]] && new_config+="Table = $TABLE"$'\n' 350 | [[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\n' 351 | for cmd in "${PRE_UP[@]}"; do 352 | new_config+="PreUp = $cmd"$'\n' 353 | done 354 | for cmd in "${POST_UP[@]}"; do 355 | new_config+="PostUp = $cmd"$'\n' 356 | done 357 | for cmd in "${PRE_DOWN[@]}"; do 358 | new_config+="PreDown = $cmd"$'\n' 359 | done 360 | for cmd in "${POST_DOWN[@]}"; do 361 | new_config+="PostDown = $cmd"$'\n' 362 | done 363 | old_umask="$(umask)" 364 | umask 077 365 | current_config="$(cmd wg showconf "$INTERFACE")" 366 | trap 'rm -f "$CONFIG_FILE.tmp"; clean_temp; exit' INT TERM EXIT 367 | echo "${current_config/\[Interface\]$'\n'/$new_config}" > "$CONFIG_FILE.tmp" || die "Could not write configuration file" 368 | sync "$CONFIG_FILE.tmp" 369 | mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" || die "Could not move configuration file" 370 | trap 'clean_temp; exit' INT TERM EXIT 371 | umask "$old_umask" 372 | } 373 | 374 | execute_hooks() { 375 | local hook 376 | for hook in "$@"; do 377 | hook="${hook//%i/$INTERFACE}" 378 | echo "[#] $hook" >&2 379 | (eval "$hook") 380 | done 381 | } 382 | 383 | cmd_usage() { 384 | cat >&2 <<-_EOF 385 | Usage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ] 386 | 387 | CONFIG_FILE is a configuration file, whose filename is the interface name 388 | followed by \`.conf'. Otherwise, INTERFACE is an interface name, with 389 | configuration found at: 390 | ${CONFIG_SEARCH_PATHS[@]/%//INTERFACE.conf}. 391 | It is to be readable by wg(8)'s \`setconf' sub-command, with the exception 392 | of the following additions to the [Interface] section, which are handled 393 | by $PROGRAM: 394 | 395 | - Address: may be specified one or more times and contains one or more 396 | IP addresses (with an optional CIDR mask) to be set for the interface. 397 | - DNS: an optional DNS server to use while the device is up. 398 | - MTU: an optional MTU for the interface; if unspecified, auto-calculated. 399 | - Table: an optional routing table to which routes will be added; if 400 | unspecified or \`auto', the default table is used. If \`off', no routes 401 | are added. 402 | - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed 403 | by bash(1) at the corresponding phases of the link, most commonly used 404 | to configure DNS. The string \`%i' is expanded to INTERFACE. 405 | - SaveConfig: if set to \`true', the configuration is saved from the current 406 | state of the interface upon shutdown. 407 | 408 | See wg-quick(8) for more info and examples. 409 | _EOF 410 | } 411 | 412 | cmd_up() { 413 | local i 414 | [[ -z $(ifconfig "$INTERFACE" 2>/dev/null) ]] || die "\`$INTERFACE' already exists" 415 | trap 'del_if; del_routes; clean_temp; exit' INT TERM EXIT 416 | execute_hooks "${PRE_UP[@]}" 417 | add_if 418 | set_config 419 | for i in "${ADDRESSES[@]}"; do 420 | add_addr "$i" 421 | done 422 | set_mtu 423 | up_if 424 | set_dns 425 | for i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$i"; done; done < <(wg show "$INTERFACE" allowed-ips) | sort -nr -k 2 -t /); do 426 | add_route "$i" 427 | done 428 | [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route 429 | monitor_daemon 430 | execute_hooks "${POST_UP[@]}" 431 | trap 'clean_temp; exit' INT TERM EXIT 432 | } 433 | 434 | cmd_down() { 435 | [[ " $(wg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface" 436 | execute_hooks "${PRE_DOWN[@]}" 437 | [[ $SAVE_CONFIG -eq 0 ]] || save_config 438 | del_if 439 | unset_dns 440 | execute_hooks "${POST_DOWN[@]}" 441 | } 442 | 443 | cmd_save() { 444 | [[ " $(wg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface" 445 | save_config 446 | } 447 | 448 | cmd_strip() { 449 | echo "$WG_CONFIG" 450 | } 451 | 452 | # ~~ function override insertion point ~~ 453 | 454 | make_temp 455 | trap 'clean_temp; exit' INT TERM EXIT 456 | 457 | if [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then 458 | cmd_usage 459 | elif [[ $# -eq 2 && $1 == up ]]; then 460 | auto_su 461 | parse_options "$2" 462 | cmd_up 463 | elif [[ $# -eq 2 && $1 == down ]]; then 464 | auto_su 465 | parse_options "$2" 466 | cmd_down 467 | elif [[ $# -eq 2 && $1 == save ]]; then 468 | auto_su 469 | parse_options "$2" 470 | cmd_save 471 | elif [[ $# -eq 2 && $1 == strip ]]; then 472 | auto_su 473 | parse_options "$2" 474 | cmd_strip 475 | else 476 | cmd_usage 477 | exit 1 478 | fi 479 | 480 | exit 0 481 | -------------------------------------------------------------------------------- /wireguard-tools/show.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "containers.h" 22 | #include "ipc.h" 23 | #include "terminal.h" 24 | #include "encoding.h" 25 | #include "subcommands.h" 26 | 27 | static int peer_cmp(const void *first, const void *second) 28 | { 29 | time_t diff; 30 | const struct wgpeer *a = *(const void **)first, *b = *(const void **)second; 31 | 32 | if (!a->last_handshake_time.tv_sec && !a->last_handshake_time.tv_nsec && (b->last_handshake_time.tv_sec || b->last_handshake_time.tv_nsec)) 33 | return 1; 34 | if (!b->last_handshake_time.tv_sec && !b->last_handshake_time.tv_nsec && (a->last_handshake_time.tv_sec || a->last_handshake_time.tv_nsec)) 35 | return -1; 36 | diff = a->last_handshake_time.tv_sec - b->last_handshake_time.tv_sec; 37 | if (!diff) 38 | diff = a->last_handshake_time.tv_nsec - b->last_handshake_time.tv_nsec; 39 | if (diff < 0) 40 | return 1; 41 | if (diff > 0) 42 | return -1; 43 | return 0; 44 | } 45 | 46 | /* This, hilariously, is not the right way to sort a linked list... */ 47 | static void sort_peers(struct wgdevice *device) 48 | { 49 | size_t peer_count = 0, i = 0; 50 | struct wgpeer *peer, **peers; 51 | 52 | for_each_wgpeer(device, peer) 53 | ++peer_count; 54 | if (!peer_count) 55 | return; 56 | peers = calloc(peer_count, sizeof(*peers)); 57 | if (!peers) 58 | return; 59 | for_each_wgpeer(device, peer) 60 | peers[i++] = peer; 61 | qsort(peers, peer_count, sizeof(*peers), peer_cmp); 62 | device->first_peer = peers[0]; 63 | for (i = 1; i < peer_count; ++i) { 64 | peers[i - 1]->next_peer = peers[i]; 65 | } 66 | peers[peer_count - 1]->next_peer = NULL; 67 | free(peers); 68 | } 69 | 70 | static char *key(const uint8_t key[static WG_KEY_LEN]) 71 | { 72 | static char base64[WG_KEY_LEN_BASE64]; 73 | 74 | key_to_base64(base64, key); 75 | return base64; 76 | } 77 | 78 | static char *maybe_key(const uint8_t maybe_key[static WG_KEY_LEN], bool have_it) 79 | { 80 | if (!have_it) 81 | return "(none)"; 82 | return key(maybe_key); 83 | } 84 | 85 | static char *masked_key(const uint8_t masked_key[static WG_KEY_LEN]) 86 | { 87 | const char *var = getenv("WG_HIDE_KEYS"); 88 | 89 | if (var && !strcmp(var, "never")) 90 | return key(masked_key); 91 | return "(hidden)"; 92 | } 93 | 94 | static char *ip(const struct wgallowedip *ip) 95 | { 96 | static char buf[INET6_ADDRSTRLEN + 1]; 97 | 98 | memset(buf, 0, INET6_ADDRSTRLEN + 1); 99 | if (ip->family == AF_INET) 100 | inet_ntop(AF_INET, &ip->ip4, buf, INET6_ADDRSTRLEN); 101 | else if (ip->family == AF_INET6) 102 | inet_ntop(AF_INET6, &ip->ip6, buf, INET6_ADDRSTRLEN); 103 | return buf; 104 | } 105 | 106 | static char *endpoint(const struct sockaddr *addr) 107 | { 108 | char host[4096 + 1]; 109 | char service[512 + 1]; 110 | static char buf[sizeof(host) + sizeof(service) + 4]; 111 | int ret; 112 | socklen_t addr_len = 0; 113 | 114 | memset(buf, 0, sizeof(buf)); 115 | if (addr->sa_family == AF_INET) 116 | addr_len = sizeof(struct sockaddr_in); 117 | else if (addr->sa_family == AF_INET6) 118 | addr_len = sizeof(struct sockaddr_in6); 119 | 120 | ret = getnameinfo(addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST); 121 | if (ret) { 122 | strncpy(buf, gai_strerror(ret), sizeof(buf) - 1); 123 | buf[sizeof(buf) - 1] = '\0'; 124 | } else 125 | snprintf(buf, sizeof(buf), (addr->sa_family == AF_INET6 && strchr(host, ':')) ? "[%s]:%s" : "%s:%s", host, service); 126 | return buf; 127 | } 128 | 129 | static size_t pretty_time(char *buf, const size_t len, unsigned long long left) 130 | { 131 | size_t offset = 0; 132 | unsigned long long years, days, hours, minutes, seconds; 133 | 134 | years = left / (365 * 24 * 60 * 60); 135 | left = left % (365 * 24 * 60 * 60); 136 | days = left / (24 * 60 * 60); 137 | left = left % (24 * 60 * 60); 138 | hours = left / (60 * 60); 139 | left = left % (60 * 60); 140 | minutes = left / 60; 141 | seconds = left % 60; 142 | 143 | if (years) 144 | offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "year%s" TERMINAL_RESET, offset ? ", " : "", years, years == 1 ? "" : "s"); 145 | if (days) 146 | offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "day%s" TERMINAL_RESET, offset ? ", " : "", days, days == 1 ? "" : "s"); 147 | if (hours) 148 | offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "hour%s" TERMINAL_RESET, offset ? ", " : "", hours, hours == 1 ? "" : "s"); 149 | if (minutes) 150 | offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "minute%s" TERMINAL_RESET, offset ? ", " : "", minutes, minutes == 1 ? "" : "s"); 151 | if (seconds) 152 | offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "second%s" TERMINAL_RESET, offset ? ", " : "", seconds, seconds == 1 ? "" : "s"); 153 | 154 | return offset; 155 | } 156 | 157 | static char *ago(const struct timespec64 *t) 158 | { 159 | static char buf[1024]; 160 | size_t offset; 161 | time_t now = time(NULL); 162 | 163 | if (now == t->tv_sec) 164 | strncpy(buf, "Now", sizeof(buf) - 1); 165 | else if (now < t->tv_sec) 166 | strncpy(buf, "(" TERMINAL_FG_RED "System clock wound backward; connection problems may ensue." TERMINAL_RESET ")", sizeof(buf) - 1); 167 | else { 168 | offset = pretty_time(buf, sizeof(buf), now - t->tv_sec); 169 | strncpy(buf + offset, " ago", sizeof(buf) - offset - 1); 170 | } 171 | buf[sizeof(buf) - 1] = '\0'; 172 | 173 | return buf; 174 | } 175 | 176 | static char *every(uint16_t seconds) 177 | { 178 | static char buf[1024] = "every "; 179 | 180 | pretty_time(buf + strlen("every "), sizeof(buf) - strlen("every ") - 1, seconds); 181 | return buf; 182 | } 183 | 184 | static char *bytes(uint64_t b) 185 | { 186 | static char buf[1024]; 187 | 188 | if (b < 1024ULL) 189 | snprintf(buf, sizeof(buf), "%u " TERMINAL_FG_CYAN "B" TERMINAL_RESET, (unsigned int)b); 190 | else if (b < 1024ULL * 1024ULL) 191 | snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "KiB" TERMINAL_RESET, (double)b / 1024); 192 | else if (b < 1024ULL * 1024ULL * 1024ULL) 193 | snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "MiB" TERMINAL_RESET, (double)b / (1024 * 1024)); 194 | else if (b < 1024ULL * 1024ULL * 1024ULL * 1024ULL) 195 | snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "GiB" TERMINAL_RESET, (double)b / (1024 * 1024 * 1024)); 196 | else 197 | snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "TiB" TERMINAL_RESET, (double)b / (1024 * 1024 * 1024) / 1024); 198 | 199 | return buf; 200 | } 201 | 202 | static const char *COMMAND_NAME; 203 | static void show_usage(void) 204 | { 205 | fprintf(stderr, "Usage: %s %s { | all | interfaces } [public-key | private-key | listen-port | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]\n", PROG_NAME, COMMAND_NAME); 206 | } 207 | 208 | static void pretty_print(struct wgdevice *device) 209 | { 210 | struct wgpeer *peer; 211 | struct wgallowedip *allowedip; 212 | 213 | terminal_printf(TERMINAL_RESET); 214 | terminal_printf(TERMINAL_FG_GREEN TERMINAL_BOLD "interface" TERMINAL_RESET ": " TERMINAL_FG_GREEN "%s" TERMINAL_RESET "\n", device->name); 215 | if (device->flags & WGDEVICE_HAS_PUBLIC_KEY) 216 | terminal_printf(" " TERMINAL_BOLD "public key" TERMINAL_RESET ": %s\n", key(device->public_key)); 217 | if (device->flags & WGDEVICE_HAS_PRIVATE_KEY) 218 | terminal_printf(" " TERMINAL_BOLD "private key" TERMINAL_RESET ": %s\n", masked_key(device->private_key)); 219 | if (device->listen_port) 220 | terminal_printf(" " TERMINAL_BOLD "listening port" TERMINAL_RESET ": %u\n", device->listen_port); 221 | if (device->fwmark) 222 | terminal_printf(" " TERMINAL_BOLD "fwmark" TERMINAL_RESET ": 0x%x\n", device->fwmark); 223 | if (device->first_peer) { 224 | sort_peers(device); 225 | terminal_printf("\n"); 226 | } 227 | for_each_wgpeer(device, peer) { 228 | terminal_printf(TERMINAL_FG_YELLOW TERMINAL_BOLD "peer" TERMINAL_RESET ": " TERMINAL_FG_YELLOW "%s" TERMINAL_RESET "\n", key(peer->public_key)); 229 | if (peer->flags & WGPEER_HAS_PRESHARED_KEY) 230 | terminal_printf(" " TERMINAL_BOLD "preshared key" TERMINAL_RESET ": %s\n", masked_key(peer->preshared_key)); 231 | if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) 232 | terminal_printf(" " TERMINAL_BOLD "endpoint" TERMINAL_RESET ": %s\n", endpoint(&peer->endpoint.addr)); 233 | terminal_printf(" " TERMINAL_BOLD "allowed ips" TERMINAL_RESET ": "); 234 | if (peer->first_allowedip) { 235 | for_each_wgallowedip(peer, allowedip) 236 | terminal_printf("%s" TERMINAL_FG_CYAN "/" TERMINAL_RESET "%u%s", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ", " : "\n"); 237 | } else 238 | terminal_printf("(none)\n"); 239 | if (peer->last_handshake_time.tv_sec) 240 | terminal_printf(" " TERMINAL_BOLD "latest handshake" TERMINAL_RESET ": %s\n", ago(&peer->last_handshake_time)); 241 | if (peer->rx_bytes || peer->tx_bytes) { 242 | terminal_printf(" " TERMINAL_BOLD "transfer" TERMINAL_RESET ": "); 243 | terminal_printf("%s received, ", bytes(peer->rx_bytes)); 244 | terminal_printf("%s sent\n", bytes(peer->tx_bytes)); 245 | } 246 | if (peer->persistent_keepalive_interval) 247 | terminal_printf(" " TERMINAL_BOLD "persistent keepalive" TERMINAL_RESET ": %s\n", every(peer->persistent_keepalive_interval)); 248 | if (peer->next_peer) 249 | terminal_printf("\n"); 250 | } 251 | } 252 | 253 | static void dump_print(struct wgdevice *device, bool with_interface) 254 | { 255 | struct wgpeer *peer; 256 | struct wgallowedip *allowedip; 257 | 258 | if (with_interface) 259 | printf("%s\t", device->name); 260 | printf("%s\t", maybe_key(device->private_key, device->flags & WGDEVICE_HAS_PRIVATE_KEY)); 261 | printf("%s\t", maybe_key(device->public_key, device->flags & WGDEVICE_HAS_PUBLIC_KEY)); 262 | printf("%u\t", device->listen_port); 263 | if (device->fwmark) 264 | printf("0x%x\n", device->fwmark); 265 | else 266 | printf("off\n"); 267 | for_each_wgpeer(device, peer) { 268 | if (with_interface) 269 | printf("%s\t", device->name); 270 | printf("%s\t", key(peer->public_key)); 271 | printf("%s\t", maybe_key(peer->preshared_key, peer->flags & WGPEER_HAS_PRESHARED_KEY)); 272 | if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) 273 | printf("%s\t", endpoint(&peer->endpoint.addr)); 274 | else 275 | printf("(none)\t"); 276 | if (peer->first_allowedip) { 277 | for_each_wgallowedip(peer, allowedip) 278 | printf("%s/%u%c", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ',' : '\t'); 279 | } else 280 | printf("(none)\t"); 281 | printf("%llu\t", (unsigned long long)peer->last_handshake_time.tv_sec); 282 | printf("%" PRIu64 "\t%" PRIu64 "\t", (uint64_t)peer->rx_bytes, (uint64_t)peer->tx_bytes); 283 | if (peer->persistent_keepalive_interval) 284 | printf("%u\n", peer->persistent_keepalive_interval); 285 | else 286 | printf("off\n"); 287 | } 288 | } 289 | 290 | static bool ugly_print(struct wgdevice *device, const char *param, bool with_interface) 291 | { 292 | struct wgpeer *peer; 293 | struct wgallowedip *allowedip; 294 | 295 | if (!strcmp(param, "public-key")) { 296 | if (with_interface) 297 | printf("%s\t", device->name); 298 | printf("%s\n", maybe_key(device->public_key, device->flags & WGDEVICE_HAS_PUBLIC_KEY)); 299 | } else if (!strcmp(param, "private-key")) { 300 | if (with_interface) 301 | printf("%s\t", device->name); 302 | printf("%s\n", maybe_key(device->private_key, device->flags & WGDEVICE_HAS_PRIVATE_KEY)); 303 | } else if (!strcmp(param, "listen-port")) { 304 | if (with_interface) 305 | printf("%s\t", device->name); 306 | printf("%u\n", device->listen_port); 307 | } else if (!strcmp(param, "fwmark")) { 308 | if (with_interface) 309 | printf("%s\t", device->name); 310 | if (device->fwmark) 311 | printf("0x%x\n", device->fwmark); 312 | else 313 | printf("off\n"); 314 | } else if (!strcmp(param, "endpoints")) { 315 | if (with_interface) 316 | printf("%s\t", device->name); 317 | for_each_wgpeer(device, peer) { 318 | printf("%s\t", key(peer->public_key)); 319 | if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) 320 | printf("%s\n", endpoint(&peer->endpoint.addr)); 321 | else 322 | printf("(none)\n"); 323 | } 324 | } else if (!strcmp(param, "allowed-ips")) { 325 | for_each_wgpeer(device, peer) { 326 | if (with_interface) 327 | printf("%s\t", device->name); 328 | printf("%s\t", key(peer->public_key)); 329 | if (peer->first_allowedip) { 330 | for_each_wgallowedip(peer, allowedip) 331 | printf("%s/%u%c", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ' ' : '\n'); 332 | } else 333 | printf("(none)\n"); 334 | } 335 | } else if (!strcmp(param, "latest-handshakes")) { 336 | for_each_wgpeer(device, peer) { 337 | if (with_interface) 338 | printf("%s\t", device->name); 339 | printf("%s\t%llu\n", key(peer->public_key), (unsigned long long)peer->last_handshake_time.tv_sec); 340 | } 341 | } else if (!strcmp(param, "transfer")) { 342 | for_each_wgpeer(device, peer) { 343 | if (with_interface) 344 | printf("%s\t", device->name); 345 | printf("%s\t%" PRIu64 "\t%" PRIu64 "\n", key(peer->public_key), (uint64_t)peer->rx_bytes, (uint64_t)peer->tx_bytes); 346 | } 347 | } else if (!strcmp(param, "persistent-keepalive")) { 348 | for_each_wgpeer(device, peer) { 349 | if (with_interface) 350 | printf("%s\t", device->name); 351 | if (peer->persistent_keepalive_interval) 352 | printf("%s\t%u\n", key(peer->public_key), peer->persistent_keepalive_interval); 353 | else 354 | printf("%s\toff\n", key(peer->public_key)); 355 | } 356 | } else if (!strcmp(param, "preshared-keys")) { 357 | for_each_wgpeer(device, peer) { 358 | if (with_interface) 359 | printf("%s\t", device->name); 360 | printf("%s\t", key(peer->public_key)); 361 | printf("%s\n", maybe_key(peer->preshared_key, peer->flags & WGPEER_HAS_PRESHARED_KEY)); 362 | } 363 | } else if (!strcmp(param, "peers")) { 364 | for_each_wgpeer(device, peer) { 365 | if (with_interface) 366 | printf("%s\t", device->name); 367 | printf("%s\n", key(peer->public_key)); 368 | } 369 | } else if (!strcmp(param, "dump")) 370 | dump_print(device, with_interface); 371 | else { 372 | fprintf(stderr, "Invalid parameter: `%s'\n", param); 373 | show_usage(); 374 | return false; 375 | } 376 | return true; 377 | } 378 | 379 | int show_main(int argc, char *argv[]) 380 | { 381 | int ret = 0; 382 | 383 | COMMAND_NAME = argv[0]; 384 | 385 | if (argc > 3) { 386 | show_usage(); 387 | return 1; 388 | } 389 | 390 | if (argc == 1 || !strcmp(argv[1], "all")) { 391 | char *interfaces = ipc_list_devices(), *interface; 392 | 393 | if (!interfaces) { 394 | perror("Unable to list interfaces"); 395 | return 1; 396 | } 397 | ret = !!*interfaces; 398 | interface = interfaces; 399 | for (size_t len = 0; (len = strlen(interface)); interface += len + 1) { 400 | struct wgdevice *device = NULL; 401 | 402 | if (ipc_get_device(&device, interface) < 0) { 403 | fprintf(stderr, "Unable to access interface %s: %s\n", interface, strerror(errno)); 404 | continue; 405 | } 406 | if (argc == 3) { 407 | if (!ugly_print(device, argv[2], true)) { 408 | ret = 1; 409 | free_wgdevice(device); 410 | break; 411 | } 412 | } else { 413 | pretty_print(device); 414 | if (strlen(interface + len + 1)) 415 | printf("\n"); 416 | } 417 | free_wgdevice(device); 418 | ret = 0; 419 | } 420 | free(interfaces); 421 | } else if (!strcmp(argv[1], "interfaces")) { 422 | char *interfaces, *interface; 423 | 424 | if (argc > 2) { 425 | show_usage(); 426 | return 1; 427 | } 428 | interfaces = ipc_list_devices(); 429 | if (!interfaces) { 430 | perror("Unable to list interfaces"); 431 | return 1; 432 | } 433 | interface = interfaces; 434 | for (size_t len = 0; (len = strlen(interface)); interface += len + 1) 435 | printf("%s%c", interface, strlen(interface + len + 1) ? ' ' : '\n'); 436 | free(interfaces); 437 | } else if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "help"))) 438 | show_usage(); 439 | else { 440 | struct wgdevice *device = NULL; 441 | 442 | if (ipc_get_device(&device, argv[1]) < 0) { 443 | perror("Unable to access interface"); 444 | return 1; 445 | } 446 | if (argc == 3) { 447 | if (!ugly_print(device, argv[2], false)) 448 | ret = 1; 449 | } else 450 | pretty_print(device); 451 | free_wgdevice(device); 452 | } 453 | return ret; 454 | } 455 | -------------------------------------------------------------------------------- /wireguard-tools/wg-quick/darwin.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 5 | # 6 | 7 | set -e -o pipefail 8 | shopt -s extglob 9 | export LC_ALL=C 10 | 11 | SELF="${BASH_SOURCE[0]}" 12 | [[ $SELF == */* ]] || SELF="./$SELF" 13 | SELF="$(cd "${SELF%/*}" && pwd -P)/${SELF##*/}" 14 | export PATH="/usr/bin:/bin:/usr/sbin:/sbin:${SELF%/*}:$PATH" 15 | 16 | WG_CONFIG="" 17 | INTERFACE="" 18 | ADDRESSES=( ) 19 | MTU="" 20 | DNS=( ) 21 | TABLE="" 22 | PRE_UP=( ) 23 | POST_UP=( ) 24 | PRE_DOWN=( ) 25 | POST_DOWN=( ) 26 | SAVE_CONFIG=0 27 | CONFIG_FILE="" 28 | PROGRAM="${0##*/}" 29 | ARGS=( "$@" ) 30 | 31 | cmd() { 32 | echo "[#] $*" >&2 33 | "$@" 34 | } 35 | 36 | die() { 37 | echo "$PROGRAM: $*" >&2 38 | exit 1 39 | } 40 | 41 | [[ ${BASH_VERSINFO[0]} -ge 4 ]] || die "Version mismatch: bash ${BASH_VERSINFO[0]} detected, when bash 4+ required" 42 | 43 | CONFIG_SEARCH_PATHS=( /etc/wireguard /usr/local/etc/wireguard ) 44 | 45 | parse_options() { 46 | local interface_section=0 line key value stripped path 47 | CONFIG_FILE="$1" 48 | if [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]]; then 49 | for path in "${CONFIG_SEARCH_PATHS[@]}"; do 50 | CONFIG_FILE="$path/$1.conf" 51 | [[ -e $CONFIG_FILE ]] && break 52 | done 53 | fi 54 | [[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist" 55 | [[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]] || die "The config file must be a valid interface name, followed by .conf" 56 | CONFIG_FILE="$(cd "${CONFIG_FILE%/*}" && pwd -P)/${CONFIG_FILE##*/}" 57 | ((($(stat -f '0%#p' "$CONFIG_FILE") & $(stat -f '0%#p' "${CONFIG_FILE%/*}") & 0007) == 0)) || echo "Warning: \`$CONFIG_FILE' is world accessible" >&2 58 | INTERFACE="${BASH_REMATCH[2]}" 59 | shopt -s nocasematch 60 | while read -r line || [[ -n $line ]]; do 61 | stripped="${line%%\#*}" 62 | key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}" 63 | value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}" 64 | [[ $key == "["* ]] && interface_section=0 65 | [[ $key == "[Interface]" ]] && interface_section=1 66 | if [[ $interface_section -eq 1 ]]; then 67 | case "$key" in 68 | Address) ADDRESSES+=( ${value//,/ } ); continue ;; 69 | MTU) MTU="$value"; continue ;; 70 | DNS) DNS+=( ${value//,/ } ); continue ;; 71 | Table) TABLE="$value"; continue ;; 72 | PreUp) PRE_UP+=( "$value" ); continue ;; 73 | PreDown) PRE_DOWN+=( "$value" ); continue ;; 74 | PostUp) POST_UP+=( "$value" ); continue ;; 75 | PostDown) POST_DOWN+=( "$value" ); continue ;; 76 | SaveConfig) read_bool SAVE_CONFIG "$value"; continue ;; 77 | esac 78 | fi 79 | WG_CONFIG+="$line"$'\n' 80 | done < "$CONFIG_FILE" 81 | shopt -u nocasematch 82 | } 83 | 84 | read_bool() { 85 | case "$2" in 86 | true) printf -v "$1" 1 ;; 87 | false) printf -v "$1" 0 ;; 88 | *) die "\`$2' is neither true nor false" 89 | esac 90 | } 91 | 92 | auto_su() { 93 | [[ $UID == 0 ]] || exec sudo -p "$PROGRAM must be run as root. Please enter the password for %u to continue: " -- "$BASH" -- "$SELF" "${ARGS[@]}" 94 | } 95 | 96 | get_real_interface() { 97 | local interface diff 98 | wg show interfaces >/dev/null 99 | [[ -f "/var/run/wireguard/$INTERFACE.name" ]] || return 1 100 | interface="$(< "/var/run/wireguard/$INTERFACE.name")" 101 | [[ -n $interface && -S "/var/run/wireguard/$interface.sock" ]] || return 1 102 | diff=$(( $(stat -f %m "/var/run/wireguard/$interface.sock" 2>/dev/null || echo 200) - $(stat -f %m "/var/run/wireguard/$INTERFACE.name" 2>/dev/null || echo 100) )) 103 | [[ $diff -ge 2 || $diff -le -2 ]] && return 1 104 | REAL_INTERFACE="$interface" 105 | echo "[+] Interface for $INTERFACE is $REAL_INTERFACE" >&2 106 | return 0 107 | } 108 | 109 | add_if() { 110 | export WG_TUN_NAME_FILE="/var/run/wireguard/$INTERFACE.name" 111 | mkdir -p "/var/run/wireguard/" 112 | cmd "${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}" utun 113 | get_real_interface 114 | } 115 | 116 | del_routes() { 117 | [[ -n $REAL_INTERFACE ]] || return 0 118 | local todelete=( ) destination gateway netif 119 | while read -r destination _ _ _ _ netif _; do 120 | [[ $netif == "$REAL_INTERFACE" ]] && todelete+=( "$destination" ) 121 | done < <(netstat -nr -f inet) 122 | for destination in "${todelete[@]}"; do 123 | cmd route -q -n delete -inet "$destination" >/dev/null || true 124 | done 125 | todelete=( ) 126 | while read -r destination gateway _ netif; do 127 | [[ $netif == "$REAL_INTERFACE" || ( $netif == lo* && $gateway == "$REAL_INTERFACE" ) ]] && todelete+=( "$destination" ) 128 | done < <(netstat -nr -f inet6) 129 | for destination in "${todelete[@]}"; do 130 | cmd route -q -n delete -inet6 "$destination" >/dev/null || true 131 | done 132 | for destination in "${ENDPOINTS[@]}"; do 133 | if [[ $destination == *:* ]]; then 134 | cmd route -q -n delete -inet6 "$destination" >/dev/null || true 135 | else 136 | cmd route -q -n delete -inet "$destination" >/dev/null || true 137 | fi 138 | done 139 | } 140 | 141 | del_if() { 142 | [[ -z $REAL_INTERFACE ]] || cmd rm -f "/var/run/wireguard/$REAL_INTERFACE.sock" 143 | cmd rm -f "/var/run/wireguard/$INTERFACE.name" 144 | } 145 | 146 | up_if() { 147 | cmd ifconfig "$REAL_INTERFACE" up 148 | } 149 | 150 | add_addr() { 151 | if [[ $1 == *:* ]]; then 152 | cmd ifconfig "$REAL_INTERFACE" inet6 "$1" alias 153 | else 154 | cmd ifconfig "$REAL_INTERFACE" inet "$1" "${1%%/*}" alias 155 | fi 156 | } 157 | 158 | set_mtu() { 159 | # TODO: use better set_mtu algorithm from freebsd.bash 160 | local mtu=0 current_mtu=-1 destination netif defaultif 161 | if [[ -n $MTU ]]; then 162 | cmd ifconfig "$REAL_INTERFACE" mtu "$MTU" 163 | return 164 | fi 165 | while read -r destination _ _ _ _ netif _; do 166 | if [[ $destination == default ]]; then 167 | defaultif="$netif" 168 | break 169 | fi 170 | done < <(netstat -nr -f inet) 171 | [[ -n $defaultif && $(ifconfig "$defaultif") =~ mtu\ ([0-9]+) ]] && mtu="${BASH_REMATCH[1]}" 172 | [[ $mtu -gt 0 ]] || mtu=1500 173 | mtu=$(( mtu - 80 )) 174 | [[ $(ifconfig "$REAL_INTERFACE") =~ mtu\ ([0-9]+) ]] && current_mtu="${BASH_REMATCH[1]}" 175 | [[ $mtu -eq $current_mtu ]] || cmd ifconfig "$REAL_INTERFACE" mtu "$mtu" 176 | } 177 | 178 | collect_gateways() { 179 | local destination gateway 180 | 181 | GATEWAY4="" 182 | while read -r destination gateway _; do 183 | [[ $destination == default ]] || continue 184 | GATEWAY4="$gateway" 185 | break 186 | done < <(netstat -nr -f inet) 187 | 188 | GATEWAY6="" 189 | while read -r destination gateway _; do 190 | [[ $destination == default ]] || continue 191 | GATEWAY6="$gateway" 192 | break 193 | done < <(netstat -nr -f inet6) 194 | } 195 | 196 | collect_endpoints() { 197 | ENDPOINTS=( ) 198 | while read -r _ endpoint; do 199 | [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue 200 | ENDPOINTS+=( "${BASH_REMATCH[1]}" ) 201 | done < <(wg show "$REAL_INTERFACE" endpoints) 202 | } 203 | 204 | declare -A SERVICE_DNS 205 | collect_new_service_dns() { 206 | local service get_response 207 | local -A found_services 208 | { read -r _ && while read -r service; do 209 | [[ $service == "*"* ]] && service="${service:1}" 210 | found_services["$service"]=1 211 | [[ -n ${SERVICE_DNS["$service"]} ]] && continue 212 | get_response="$(cmd networksetup -getdnsservers "$service")" 213 | [[ $get_response == *" "* ]] && get_response="Empty" 214 | [[ -n $get_response ]] && SERVICE_DNS["$service"]="$get_response" 215 | done; } < <(networksetup -listallnetworkservices) 216 | 217 | for service in "${!SERVICE_DNS[@]}"; do 218 | [[ -n ${found_services["$service"]} ]] || unset SERVICE_DNS["$service"] 219 | done 220 | } 221 | 222 | set_endpoint_direct_route() { 223 | local old_endpoints endpoint old_gateway4 old_gateway6 remove_all_old=0 added=( ) 224 | old_endpoints=( "${ENDPOINTS[@]}" ) 225 | old_gateway4="$GATEWAY4" 226 | old_gateway6="$GATEWAY6" 227 | collect_gateways 228 | collect_endpoints 229 | 230 | [[ $old_gateway4 != "$GATEWAY4" || $old_gateway6 != "$GATEWAY6" ]] && remove_all_old=1 231 | 232 | if [[ $remove_all_old -eq 1 ]]; then 233 | for endpoint in "${ENDPOINTS[@]}"; do 234 | [[ " ${old_endpoints[*]} " == *" $endpoint "* ]] || old_endpoints+=( "$endpoint" ) 235 | done 236 | fi 237 | 238 | for endpoint in "${old_endpoints[@]}"; do 239 | [[ $remove_all_old -eq 0 && " ${ENDPOINTS[*]} " == *" $endpoint "* ]] && continue 240 | if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then 241 | cmd route -q -n delete -inet6 "$endpoint" >/dev/null 2>&1 || true 242 | elif [[ $AUTO_ROUTE4 -eq 1 ]]; then 243 | cmd route -q -n delete -inet "$endpoint" >/dev/null 2>&1 || true 244 | fi 245 | done 246 | 247 | for endpoint in "${ENDPOINTS[@]}"; do 248 | if [[ $remove_all_old -eq 0 && " ${old_endpoints[*]} " == *" $endpoint "* ]]; then 249 | added+=( "$endpoint" ) 250 | continue 251 | fi 252 | if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then 253 | if [[ -n $GATEWAY6 ]]; then 254 | cmd route -q -n add -inet6 "$endpoint" -gateway "$GATEWAY6" >/dev/null || true 255 | else 256 | # Prevent routing loop 257 | cmd route -q -n add -inet6 "$endpoint" ::1 -blackhole >/dev/null || true 258 | fi 259 | added+=( "$endpoint" ) 260 | elif [[ $AUTO_ROUTE4 -eq 1 ]]; then 261 | if [[ -n $GATEWAY4 ]]; then 262 | cmd route -q -n add -inet "$endpoint" -gateway "$GATEWAY4" >/dev/null || true 263 | else 264 | # Prevent routing loop 265 | cmd route -q -n add -inet "$endpoint" 127.0.0.1 -blackhole >/dev/null || true 266 | fi 267 | added+=( "$endpoint" ) 268 | fi 269 | done 270 | ENDPOINTS=( "${added[@]}" ) 271 | } 272 | 273 | set_dns() { 274 | collect_new_service_dns 275 | local service response 276 | for service in "${!SERVICE_DNS[@]}"; do 277 | while read -r response; do 278 | [[ $response == *Error* ]] && echo "$response" >&2 279 | done < <(cmd networksetup -setdnsservers "$service" "${DNS[@]}") 280 | done 281 | } 282 | 283 | del_dns() { 284 | local service response 285 | for service in "${!SERVICE_DNS[@]}"; do 286 | while read -r response; do 287 | [[ $response == *Error* ]] && echo "$response" >&2 288 | done < <(cmd networksetup -setdnsservers "$service" ${SERVICE_DNS["$service"]} || true) 289 | done 290 | } 291 | 292 | monitor_daemon() { 293 | echo "[+] Backgrounding route monitor" >&2 294 | (trap 'del_routes; del_dns; exit 0' INT TERM EXIT 295 | exec >/dev/null 2>&1 296 | local event pid=$BASHPID 297 | [[ ${#DNS[@]} -gt 0 ]] && trap set_dns ALRM 298 | # TODO: this should also check to see if the endpoint actually changes 299 | # in response to incoming packets, and then call set_endpoint_direct_route 300 | # then too. That function should be able to gracefully cleanup if the 301 | # endpoints change. 302 | while read -r event; do 303 | [[ $event == RTM_* ]] || continue 304 | ifconfig "$REAL_INTERFACE" >/dev/null 2>&1 || break 305 | [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route 306 | [[ -z $MTU ]] && set_mtu 307 | if [[ ${#DNS[@]} -gt 0 ]]; then 308 | set_dns 309 | sleep 2 && kill -ALRM $pid 2>/dev/null & 310 | fi 311 | done < <(route -n monitor)) & disown 312 | } 313 | 314 | add_route() { 315 | [[ $TABLE != off ]] || return 0 316 | 317 | local family=inet 318 | [[ $1 == *:* ]] && family=inet6 319 | 320 | if [[ $1 == */0 && ( -z $TABLE || $TABLE == auto ) ]]; then 321 | if [[ $1 == *:* ]]; then 322 | AUTO_ROUTE6=1 323 | cmd route -q -n add -inet6 ::/1 -interface "$REAL_INTERFACE" >/dev/null 324 | cmd route -q -n add -inet6 8000::/1 -interface "$REAL_INTERFACE" >/dev/null 325 | else 326 | AUTO_ROUTE4=1 327 | cmd route -q -n add -inet 0.0.0.0/1 -interface "$REAL_INTERFACE" >/dev/null 328 | cmd route -q -n add -inet 128.0.0.0/1 -interface "$REAL_INTERFACE" >/dev/null 329 | fi 330 | else 331 | [[ $TABLE == main || $TABLE == auto || -z $TABLE ]] || die "Darwin only supports TABLE=auto|main|off" 332 | [[ $(route -n get "-$family" "$1" 2>/dev/null) =~ interface:\ $REAL_INTERFACE$'\n' ]] || cmd route -q -n add -$family "$1" -interface "$REAL_INTERFACE" >/dev/null 333 | 334 | fi 335 | } 336 | 337 | set_config() { 338 | cmd wg setconf "$REAL_INTERFACE" <(echo "$WG_CONFIG") 339 | } 340 | 341 | save_config() { 342 | local old_umask new_config current_config address cmd 343 | new_config=$'[Interface]\n' 344 | while read -r address; do 345 | [[ $address =~ inet6?\ ([^ ]+) ]] && new_config+="Address = ${BASH_REMATCH[1]}"$'\n' 346 | done < <(ifconfig "$REAL_INTERFACE") 347 | # TODO: actually determine current DNS for interface 348 | for address in "${DNS[@]}"; do 349 | new_config+="DNS = $address"$'\n' 350 | done 351 | [[ -n $MTU ]] && new_config+="MTU = $MTU"$'\n' 352 | [[ -n $TABLE ]] && new_config+="Table = $TABLE"$'\n' 353 | [[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\n' 354 | for cmd in "${PRE_UP[@]}"; do 355 | new_config+="PreUp = $cmd"$'\n' 356 | done 357 | for cmd in "${POST_UP[@]}"; do 358 | new_config+="PostUp = $cmd"$'\n' 359 | done 360 | for cmd in "${PRE_DOWN[@]}"; do 361 | new_config+="PreDown = $cmd"$'\n' 362 | done 363 | for cmd in "${POST_DOWN[@]}"; do 364 | new_config+="PostDown = $cmd"$'\n' 365 | done 366 | old_umask="$(umask)" 367 | umask 077 368 | current_config="$(cmd wg showconf "$REAL_INTERFACE")" 369 | trap 'rm -f "$CONFIG_FILE.tmp"; exit' INT TERM EXIT 370 | echo "${current_config/\[Interface\]$'\n'/$new_config}" > "$CONFIG_FILE.tmp" || die "Could not write configuration file" 371 | sync "$CONFIG_FILE.tmp" 372 | mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" || die "Could not move configuration file" 373 | trap - INT TERM EXIT 374 | umask "$old_umask" 375 | } 376 | 377 | execute_hooks() { 378 | local hook 379 | for hook in "$@"; do 380 | hook="${hook//%i/$REAL_INTERFACE}" 381 | hook="${hook//%I/$INTERFACE}" 382 | echo "[#] $hook" >&2 383 | (eval "$hook") 384 | done 385 | } 386 | 387 | cmd_usage() { 388 | cat >&2 <<-_EOF 389 | Usage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ] 390 | 391 | CONFIG_FILE is a configuration file, whose filename is the interface name 392 | followed by \`.conf'. Otherwise, INTERFACE is an interface name, with 393 | configuration found at: 394 | ${CONFIG_SEARCH_PATHS[@]/%//INTERFACE.conf}. 395 | It is to be readable by wg(8)'s \`setconf' sub-command, with the exception 396 | of the following additions to the [Interface] section, which are handled 397 | by $PROGRAM: 398 | 399 | - Address: may be specified one or more times and contains one or more 400 | IP addresses (with an optional CIDR mask) to be set for the interface. 401 | - DNS: an optional DNS server to use while the device is up. 402 | - MTU: an optional MTU for the interface; if unspecified, auto-calculated. 403 | - Table: an optional routing table to which routes will be added; if 404 | unspecified or \`auto', the default table is used. If \`off', no routes 405 | are added. Besides \`auto' and \`off', only \`main' is supported on 406 | this platform. 407 | - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed 408 | by bash(1) at the corresponding phases of the link, most commonly used 409 | to configure DNS. The string \`%i' is expanded to INTERFACE. 410 | - SaveConfig: if set to \`true', the configuration is saved from the current 411 | state of the interface upon shutdown. 412 | 413 | See wg-quick(8) for more info and examples. 414 | _EOF 415 | } 416 | 417 | cmd_up() { 418 | local i 419 | get_real_interface && die "\`$INTERFACE' already exists as \`$REAL_INTERFACE'" 420 | trap 'del_if; del_routes; exit' INT TERM EXIT 421 | execute_hooks "${PRE_UP[@]}" 422 | add_if 423 | set_config 424 | for i in "${ADDRESSES[@]}"; do 425 | add_addr "$i" 426 | done 427 | set_mtu 428 | up_if 429 | for i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$i"; done; done < <(wg show "$REAL_INTERFACE" allowed-ips) | sort -nr -k 2 -t /); do 430 | add_route "$i" 431 | done 432 | [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route 433 | [[ ${#DNS[@]} -gt 0 ]] && set_dns 434 | monitor_daemon 435 | execute_hooks "${POST_UP[@]}" 436 | trap - INT TERM EXIT 437 | } 438 | 439 | cmd_down() { 440 | if ! get_real_interface || [[ " $(wg show interfaces) " != *" $REAL_INTERFACE "* ]]; then 441 | die "\`$INTERFACE' is not a WireGuard interface" 442 | fi 443 | execute_hooks "${PRE_DOWN[@]}" 444 | [[ $SAVE_CONFIG -eq 0 ]] || save_config 445 | del_if 446 | execute_hooks "${POST_DOWN[@]}" 447 | } 448 | 449 | cmd_save() { 450 | if ! get_real_interface || [[ " $(wg show interfaces) " != *" $REAL_INTERFACE "* ]]; then 451 | die "\`$INTERFACE' is not a WireGuard interface" 452 | fi 453 | save_config 454 | } 455 | 456 | cmd_strip() { 457 | echo "$WG_CONFIG" 458 | } 459 | 460 | # ~~ function override insertion point ~~ 461 | 462 | if [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then 463 | cmd_usage 464 | elif [[ $# -eq 2 && $1 == up ]]; then 465 | auto_su 466 | parse_options "$2" 467 | cmd_up 468 | elif [[ $# -eq 2 && $1 == down ]]; then 469 | auto_su 470 | parse_options "$2" 471 | cmd_down 472 | elif [[ $# -eq 2 && $1 == save ]]; then 473 | auto_su 474 | parse_options "$2" 475 | cmd_save 476 | elif [[ $# -eq 2 && $1 == strip ]]; then 477 | auto_su 478 | parse_options "$2" 479 | cmd_strip 480 | else 481 | cmd_usage 482 | exit 1 483 | fi 484 | 485 | exit 0 486 | -------------------------------------------------------------------------------- /wireguard-tools/config.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "config.h" 19 | #include "containers.h" 20 | #include "ipc.h" 21 | #include "encoding.h" 22 | 23 | #define COMMENT_CHAR '#' 24 | 25 | static const char *get_value(const char *line, const char *key) 26 | { 27 | size_t linelen = strlen(line); 28 | size_t keylen = strlen(key); 29 | 30 | if (keylen >= linelen) 31 | return NULL; 32 | 33 | if (strncasecmp(line, key, keylen)) 34 | return NULL; 35 | 36 | return line + keylen; 37 | } 38 | 39 | static inline bool parse_port(uint16_t *port, uint32_t *flags, const char *value) 40 | { 41 | int ret; 42 | struct addrinfo *resolved; 43 | struct addrinfo hints = { 44 | .ai_family = AF_UNSPEC, 45 | .ai_socktype = SOCK_DGRAM, 46 | .ai_protocol = IPPROTO_UDP, 47 | .ai_flags = AI_PASSIVE 48 | }; 49 | 50 | if (!strlen(value)) { 51 | fprintf(stderr, "Unable to parse empty port\n"); 52 | return false; 53 | } 54 | 55 | ret = getaddrinfo(NULL, value, &hints, &resolved); 56 | if (ret) { 57 | fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value); 58 | return false; 59 | } 60 | 61 | ret = -1; 62 | if (resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) { 63 | *port = ntohs(((struct sockaddr_in *)resolved->ai_addr)->sin_port); 64 | ret = 0; 65 | } else if (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)) { 66 | *port = ntohs(((struct sockaddr_in6 *)resolved->ai_addr)->sin6_port); 67 | ret = 0; 68 | } else 69 | fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value); 70 | 71 | freeaddrinfo(resolved); 72 | if (!ret) 73 | *flags |= WGDEVICE_HAS_LISTEN_PORT; 74 | return ret == 0; 75 | } 76 | 77 | static inline bool parse_fwmark(uint32_t *fwmark, uint32_t *flags, const char *value) 78 | { 79 | unsigned long ret; 80 | char *end; 81 | int base = 10; 82 | 83 | if (!strcasecmp(value, "off")) { 84 | *fwmark = 0; 85 | *flags |= WGDEVICE_HAS_FWMARK; 86 | return true; 87 | } 88 | 89 | if (!isdigit(value[0])) 90 | goto err; 91 | 92 | if (strlen(value) > 2 && value[0] == '0' && value[1] == 'x') 93 | base = 16; 94 | 95 | ret = strtoul(value, &end, base); 96 | if (*end || ret > UINT32_MAX) 97 | goto err; 98 | 99 | *fwmark = ret; 100 | *flags |= WGDEVICE_HAS_FWMARK; 101 | return true; 102 | err: 103 | fprintf(stderr, "Fwmark is neither 0/off nor 0-0xffffffff: `%s'\n", value); 104 | return false; 105 | } 106 | 107 | static inline bool parse_key(uint8_t key[static WG_KEY_LEN], const char *value) 108 | { 109 | if (!key_from_base64(key, value)) { 110 | fprintf(stderr, "Key is not the correct length or format: `%s'\n", value); 111 | memset(key, 0, WG_KEY_LEN); 112 | return false; 113 | } 114 | return true; 115 | } 116 | 117 | static bool parse_keyfile(uint8_t key[static WG_KEY_LEN], const char *path) 118 | { 119 | FILE *f; 120 | int c; 121 | char dst[WG_KEY_LEN_BASE64]; 122 | bool ret = false; 123 | 124 | f = fopen(path, "r"); 125 | if (!f) { 126 | perror("fopen"); 127 | return false; 128 | } 129 | 130 | if (fread(dst, WG_KEY_LEN_BASE64 - 1, 1, f) != 1) { 131 | /* If we're at the end and we didn't read anything, we're /dev/null or an empty file. */ 132 | if (!ferror(f) && feof(f) && !ftell(f)) { 133 | memset(key, 0, WG_KEY_LEN); 134 | ret = true; 135 | goto out; 136 | } 137 | 138 | fprintf(stderr, "Invalid length key in key file\n"); 139 | goto out; 140 | } 141 | dst[WG_KEY_LEN_BASE64 - 1] = '\0'; 142 | 143 | while ((c = getc(f)) != EOF) { 144 | if (!isspace(c)) { 145 | fprintf(stderr, "Found trailing character in key file: `%c'\n", c); 146 | goto out; 147 | } 148 | } 149 | if (ferror(f) && errno) { 150 | perror("getc"); 151 | goto out; 152 | } 153 | ret = parse_key(key, dst); 154 | 155 | out: 156 | fclose(f); 157 | return ret; 158 | } 159 | 160 | static inline bool parse_ip(struct wgallowedip *allowedip, const char *value) 161 | { 162 | allowedip->family = AF_UNSPEC; 163 | if (strchr(value, ':')) { 164 | if (inet_pton(AF_INET6, value, &allowedip->ip6) == 1) 165 | allowedip->family = AF_INET6; 166 | } else { 167 | if (inet_pton(AF_INET, value, &allowedip->ip4) == 1) 168 | allowedip->family = AF_INET; 169 | } 170 | if (allowedip->family == AF_UNSPEC) { 171 | fprintf(stderr, "Unable to parse IP address: `%s'\n", value); 172 | return false; 173 | } 174 | return true; 175 | } 176 | 177 | static inline int parse_dns_retries(void) 178 | { 179 | unsigned long ret; 180 | char *retries = getenv("WG_ENDPOINT_RESOLUTION_RETRIES"), *end; 181 | 182 | if (!retries) 183 | return 15; 184 | if (!strcmp(retries, "infinity")) 185 | return -1; 186 | 187 | ret = strtoul(retries, &end, 10); 188 | if (*end || ret > INT_MAX) { 189 | fprintf(stderr, "Unable to parse WG_ENDPOINT_RESOLUTION_RETRIES: `%s'\n", retries); 190 | exit(1); 191 | } 192 | return (int)ret; 193 | } 194 | 195 | static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value) 196 | { 197 | char *mutable = strdup(value); 198 | char *begin, *end; 199 | int ret, retries = parse_dns_retries(); 200 | struct addrinfo *resolved; 201 | struct addrinfo hints = { 202 | .ai_family = AF_UNSPEC, 203 | .ai_socktype = SOCK_DGRAM, 204 | .ai_protocol = IPPROTO_UDP 205 | }; 206 | if (!mutable) { 207 | perror("strdup"); 208 | return false; 209 | } 210 | if (!strlen(value)) { 211 | free(mutable); 212 | fprintf(stderr, "Unable to parse empty endpoint\n"); 213 | return false; 214 | } 215 | if (mutable[0] == '[') { 216 | begin = &mutable[1]; 217 | end = strchr(mutable, ']'); 218 | if (!end) { 219 | free(mutable); 220 | fprintf(stderr, "Unable to find matching brace of endpoint: `%s'\n", value); 221 | return false; 222 | } 223 | *end++ = '\0'; 224 | if (*end++ != ':' || !*end) { 225 | free(mutable); 226 | fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value); 227 | return false; 228 | } 229 | } else { 230 | begin = mutable; 231 | end = strrchr(mutable, ':'); 232 | if (!end || !*(end + 1)) { 233 | free(mutable); 234 | fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value); 235 | return false; 236 | } 237 | *end++ = '\0'; 238 | } 239 | 240 | #define min(a, b) ((a) < (b) ? (a) : (b)) 241 | for (unsigned int timeout = 1000000;; timeout = min(20000000, timeout * 6 / 5)) { 242 | ret = getaddrinfo(begin, end, &hints, &resolved); 243 | if (!ret) 244 | break; 245 | /* The set of return codes that are "permanent failures". All other possibilities are potentially transient. 246 | * 247 | * This is according to https://sourceware.org/glibc/wiki/NameResolver which states: 248 | * "From the perspective of the application that calls getaddrinfo() it perhaps 249 | * doesn't matter that much since EAI_FAIL, EAI_NONAME and EAI_NODATA are all 250 | * permanent failure codes and the causes are all permanent failures in the 251 | * sense that there is no point in retrying later." 252 | * 253 | * So this is what we do, except FreeBSD removed EAI_NODATA some time ago, so that's conditional. 254 | */ 255 | if (ret == EAI_NONAME || ret == EAI_FAIL || 256 | #ifdef EAI_NODATA 257 | ret == EAI_NODATA || 258 | #endif 259 | (retries >= 0 && !retries--)) { 260 | free(mutable); 261 | fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value); 262 | return false; 263 | } 264 | fprintf(stderr, "%s: `%s'. Trying again in %.2f seconds...\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value, timeout / 1000000.0); 265 | usleep(timeout); 266 | } 267 | 268 | if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) || 269 | (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6))) 270 | memcpy(endpoint, resolved->ai_addr, resolved->ai_addrlen); 271 | else { 272 | freeaddrinfo(resolved); 273 | free(mutable); 274 | fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value); 275 | return false; 276 | } 277 | freeaddrinfo(resolved); 278 | free(mutable); 279 | return true; 280 | } 281 | 282 | static inline bool parse_persistent_keepalive(uint16_t *interval, uint32_t *flags, const char *value) 283 | { 284 | unsigned long ret; 285 | char *end; 286 | 287 | if (!strcasecmp(value, "off")) { 288 | *interval = 0; 289 | *flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL; 290 | return true; 291 | } 292 | 293 | if (!isdigit(value[0])) 294 | goto err; 295 | 296 | ret = strtoul(value, &end, 10); 297 | if (*end || ret > 65535) 298 | goto err; 299 | 300 | *interval = (uint16_t)ret; 301 | *flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL; 302 | return true; 303 | err: 304 | fprintf(stderr, "Persistent keepalive interval is neither 0/off nor 1-65535: `%s'\n", value); 305 | return false; 306 | } 307 | 308 | static bool validate_netmask(struct wgallowedip *allowedip) 309 | { 310 | uint32_t *ip; 311 | int last; 312 | 313 | switch (allowedip->family) { 314 | case AF_INET: 315 | last = 0; 316 | ip = (uint32_t *)&allowedip->ip4; 317 | break; 318 | case AF_INET6: 319 | last = 3; 320 | ip = (uint32_t *)&allowedip->ip6; 321 | break; 322 | default: 323 | return true; /* We don't know how to validate it, so say 'okay'. */ 324 | } 325 | 326 | for (int i = last; i >= 0; --i) { 327 | uint32_t mask = ~0; 328 | 329 | if (allowedip->cidr >= 32 * (i + 1)) 330 | break; 331 | if (allowedip->cidr > 32 * i) 332 | mask >>= (allowedip->cidr - 32 * i); 333 | if (ntohl(ip[i]) & mask) 334 | return false; 335 | } 336 | 337 | return true; 338 | } 339 | 340 | static inline bool parse_allowedips(struct wgpeer *peer, struct wgallowedip **last_allowedip, const char *value) 341 | { 342 | struct wgallowedip *allowedip = *last_allowedip, *new_allowedip; 343 | char *mask, *mutable = strdup(value), *sep, *saved_entry; 344 | 345 | if (!mutable) { 346 | perror("strdup"); 347 | return false; 348 | } 349 | peer->flags |= WGPEER_REPLACE_ALLOWEDIPS; 350 | if (!strlen(value)) { 351 | free(mutable); 352 | return true; 353 | } 354 | sep = mutable; 355 | while ((mask = strsep(&sep, ","))) { 356 | unsigned long cidr; 357 | char *end, *ip; 358 | 359 | saved_entry = strdup(mask); 360 | ip = strsep(&mask, "/"); 361 | 362 | new_allowedip = calloc(1, sizeof(*new_allowedip)); 363 | if (!new_allowedip) { 364 | perror("calloc"); 365 | free(saved_entry); 366 | free(mutable); 367 | return false; 368 | } 369 | 370 | if (!parse_ip(new_allowedip, ip)) { 371 | free(new_allowedip); 372 | free(saved_entry); 373 | free(mutable); 374 | return false; 375 | } 376 | 377 | if (mask) { 378 | if (!isdigit(mask[0])) 379 | goto err; 380 | cidr = strtoul(mask, &end, 10); 381 | if (*end || (cidr > 32 && new_allowedip->family == AF_INET) || (cidr > 128 && new_allowedip->family == AF_INET6)) 382 | goto err; 383 | } else if (new_allowedip->family == AF_INET) 384 | cidr = 32; 385 | else if (new_allowedip->family == AF_INET6) 386 | cidr = 128; 387 | else 388 | goto err; 389 | new_allowedip->cidr = cidr; 390 | 391 | if (!validate_netmask(new_allowedip)) 392 | fprintf(stderr, "Warning: AllowedIP has nonzero host part: %s/%s\n", ip, mask); 393 | 394 | if (allowedip) 395 | allowedip->next_allowedip = new_allowedip; 396 | else 397 | peer->first_allowedip = new_allowedip; 398 | allowedip = new_allowedip; 399 | free(saved_entry); 400 | } 401 | free(mutable); 402 | *last_allowedip = allowedip; 403 | return true; 404 | 405 | err: 406 | free(new_allowedip); 407 | free(mutable); 408 | fprintf(stderr, "AllowedIP is not in the correct format: `%s'\n", saved_entry); 409 | free(saved_entry); 410 | return false; 411 | } 412 | 413 | static bool process_line(struct config_ctx *ctx, const char *line) 414 | { 415 | const char *value; 416 | bool ret = true; 417 | 418 | if (!strcasecmp(line, "[Interface]")) { 419 | ctx->is_peer_section = false; 420 | ctx->is_device_section = true; 421 | return true; 422 | } 423 | if (!strcasecmp(line, "[Peer]")) { 424 | struct wgpeer *new_peer = calloc(1, sizeof(struct wgpeer)); 425 | 426 | if (!new_peer) { 427 | perror("calloc"); 428 | return false; 429 | } 430 | ctx->last_allowedip = NULL; 431 | if (ctx->last_peer) 432 | ctx->last_peer->next_peer = new_peer; 433 | else 434 | ctx->device->first_peer = new_peer; 435 | ctx->last_peer = new_peer; 436 | ctx->is_peer_section = true; 437 | ctx->is_device_section = false; 438 | ctx->last_peer->flags |= WGPEER_REPLACE_ALLOWEDIPS; 439 | return true; 440 | } 441 | 442 | #define key_match(key) (value = get_value(line, key "=")) 443 | 444 | if (ctx->is_device_section) { 445 | if (key_match("ListenPort")) 446 | ret = parse_port(&ctx->device->listen_port, &ctx->device->flags, value); 447 | else if (key_match("FwMark")) 448 | ret = parse_fwmark(&ctx->device->fwmark, &ctx->device->flags, value); 449 | else if (key_match("PrivateKey")) { 450 | ret = parse_key(ctx->device->private_key, value); 451 | if (ret) 452 | ctx->device->flags |= WGDEVICE_HAS_PRIVATE_KEY; 453 | } else 454 | goto error; 455 | } else if (ctx->is_peer_section) { 456 | if (key_match("Endpoint")) 457 | ret = parse_endpoint(&ctx->last_peer->endpoint.addr, value); 458 | else if (key_match("PublicKey")) { 459 | ret = parse_key(ctx->last_peer->public_key, value); 460 | if (ret) 461 | ctx->last_peer->flags |= WGPEER_HAS_PUBLIC_KEY; 462 | } else if (key_match("AllowedIPs")) 463 | ret = parse_allowedips(ctx->last_peer, &ctx->last_allowedip, value); 464 | else if (key_match("PersistentKeepalive")) 465 | ret = parse_persistent_keepalive(&ctx->last_peer->persistent_keepalive_interval, &ctx->last_peer->flags, value); 466 | else if (key_match("PresharedKey")) { 467 | ret = parse_key(ctx->last_peer->preshared_key, value); 468 | if (ret) 469 | ctx->last_peer->flags |= WGPEER_HAS_PRESHARED_KEY; 470 | } else 471 | goto error; 472 | } else 473 | goto error; 474 | return ret; 475 | 476 | #undef key_match 477 | 478 | error: 479 | fprintf(stderr, "Line unrecognized: `%s'\n", line); 480 | return false; 481 | } 482 | 483 | bool config_read_line(struct config_ctx *ctx, const char *input) 484 | { 485 | size_t len, cleaned_len = 0; 486 | char *line, *comment; 487 | bool ret = true; 488 | 489 | /* This is what strchrnul is for, but that isn't portable. */ 490 | comment = strchr(input, COMMENT_CHAR); 491 | if (comment) 492 | len = comment - input; 493 | else 494 | len = strlen(input); 495 | 496 | line = calloc(len + 1, sizeof(char)); 497 | if (!line) { 498 | perror("calloc"); 499 | ret = false; 500 | goto out; 501 | } 502 | 503 | for (size_t i = 0; i < len; ++i) { 504 | if (!isspace(input[i])) 505 | line[cleaned_len++] = input[i]; 506 | } 507 | if (!cleaned_len) 508 | goto out; 509 | ret = process_line(ctx, line); 510 | out: 511 | free(line); 512 | if (!ret) 513 | free_wgdevice(ctx->device); 514 | return ret; 515 | } 516 | 517 | bool config_read_init(struct config_ctx *ctx, bool append) 518 | { 519 | memset(ctx, 0, sizeof(*ctx)); 520 | ctx->device = calloc(1, sizeof(*ctx->device)); 521 | if (!ctx->device) { 522 | perror("calloc"); 523 | return false; 524 | } 525 | if (!append) 526 | ctx->device->flags |= WGDEVICE_REPLACE_PEERS | WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_FWMARK | WGDEVICE_HAS_LISTEN_PORT; 527 | return true; 528 | } 529 | 530 | struct wgdevice *config_read_finish(struct config_ctx *ctx) 531 | { 532 | struct wgpeer *peer; 533 | 534 | for_each_wgpeer(ctx->device, peer) { 535 | if (!(peer->flags & WGPEER_HAS_PUBLIC_KEY)) { 536 | fprintf(stderr, "A peer is missing a public key\n"); 537 | goto err; 538 | } 539 | } 540 | return ctx->device; 541 | err: 542 | free_wgdevice(ctx->device); 543 | return NULL; 544 | } 545 | 546 | static char *strip_spaces(const char *in) 547 | { 548 | char *out; 549 | size_t t, l, i; 550 | 551 | t = strlen(in); 552 | out = calloc(t + 1, sizeof(char)); 553 | if (!out) { 554 | perror("calloc"); 555 | return NULL; 556 | } 557 | for (i = 0, l = 0; i < t; ++i) { 558 | if (!isspace(in[i])) 559 | out[l++] = in[i]; 560 | } 561 | return out; 562 | } 563 | 564 | struct wgdevice *config_read_cmd(char *argv[], int argc) 565 | { 566 | struct wgdevice *device = calloc(1, sizeof(*device)); 567 | struct wgpeer *peer = NULL; 568 | struct wgallowedip *allowedip = NULL; 569 | 570 | if (!device) { 571 | perror("calloc"); 572 | return false; 573 | } 574 | while (argc > 0) { 575 | if (!strcmp(argv[0], "listen-port") && argc >= 2 && !peer) { 576 | if (!parse_port(&device->listen_port, &device->flags, argv[1])) 577 | goto error; 578 | argv += 2; 579 | argc -= 2; 580 | } else if (!strcmp(argv[0], "fwmark") && argc >= 2 && !peer) { 581 | if (!parse_fwmark(&device->fwmark, &device->flags, argv[1])) 582 | goto error; 583 | argv += 2; 584 | argc -= 2; 585 | } else if (!strcmp(argv[0], "private-key") && argc >= 2 && !peer) { 586 | if (!parse_keyfile(device->private_key, argv[1])) 587 | goto error; 588 | device->flags |= WGDEVICE_HAS_PRIVATE_KEY; 589 | argv += 2; 590 | argc -= 2; 591 | } else if (!strcmp(argv[0], "peer") && argc >= 2) { 592 | struct wgpeer *new_peer = calloc(1, sizeof(*new_peer)); 593 | 594 | allowedip = NULL; 595 | if (!new_peer) { 596 | perror("calloc"); 597 | goto error; 598 | } 599 | if (peer) 600 | peer->next_peer = new_peer; 601 | else 602 | device->first_peer = new_peer; 603 | peer = new_peer; 604 | if (!parse_key(peer->public_key, argv[1])) 605 | goto error; 606 | peer->flags |= WGPEER_HAS_PUBLIC_KEY; 607 | argv += 2; 608 | argc -= 2; 609 | } else if (!strcmp(argv[0], "remove") && argc >= 1 && peer) { 610 | peer->flags |= WGPEER_REMOVE_ME; 611 | argv += 1; 612 | argc -= 1; 613 | } else if (!strcmp(argv[0], "endpoint") && argc >= 2 && peer) { 614 | if (!parse_endpoint(&peer->endpoint.addr, argv[1])) 615 | goto error; 616 | argv += 2; 617 | argc -= 2; 618 | } else if (!strcmp(argv[0], "allowed-ips") && argc >= 2 && peer) { 619 | char *line = strip_spaces(argv[1]); 620 | 621 | if (!line) 622 | goto error; 623 | if (!parse_allowedips(peer, &allowedip, line)) { 624 | free(line); 625 | goto error; 626 | } 627 | free(line); 628 | argv += 2; 629 | argc -= 2; 630 | } else if (!strcmp(argv[0], "persistent-keepalive") && argc >= 2 && peer) { 631 | if (!parse_persistent_keepalive(&peer->persistent_keepalive_interval, &peer->flags, argv[1])) 632 | goto error; 633 | argv += 2; 634 | argc -= 2; 635 | } else if (!strcmp(argv[0], "preshared-key") && argc >= 2 && peer) { 636 | if (!parse_keyfile(peer->preshared_key, argv[1])) 637 | goto error; 638 | peer->flags |= WGPEER_HAS_PRESHARED_KEY; 639 | argv += 2; 640 | argc -= 2; 641 | } else { 642 | fprintf(stderr, "Invalid argument: %s\n", argv[0]); 643 | goto error; 644 | } 645 | } 646 | return device; 647 | error: 648 | free_wgdevice(device); 649 | return false; 650 | } 651 | -------------------------------------------------------------------------------- /wireguard-tools/wg-quick/android.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | * 5 | * This is a shell script written in C. It very intentionally still functions like 6 | * a shell script, calling out to external executables such as ip(8). 7 | */ 8 | 9 | #define _GNU_SOURCE 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #ifndef WG_PACKAGE_NAME 29 | #define WG_PACKAGE_NAME "com.wireguard.android" 30 | #endif 31 | #ifndef WG_CONFIG_SEARCH_PATHS 32 | #define WG_CONFIG_SEARCH_PATHS "/data/misc/wireguard /data/data/" WG_PACKAGE_NAME "/files" 33 | #endif 34 | 35 | #define _printf_(x, y) __attribute__((format(printf, x, y))) 36 | #define _cleanup_(x) __attribute__((cleanup(x))) 37 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) 38 | 39 | static bool is_exiting = false; 40 | 41 | static void *xmalloc(size_t size) 42 | { 43 | void *ret = malloc(size); 44 | if (ret) 45 | return ret; 46 | perror("Error: malloc"); 47 | exit(errno); 48 | } 49 | 50 | static void *xcalloc(size_t nmemb, size_t size) 51 | { 52 | void *ret = calloc(nmemb, size); 53 | if (ret) 54 | return ret; 55 | perror("Error: calloc"); 56 | exit(errno); 57 | } 58 | 59 | static void *xstrdup(const char *str) 60 | { 61 | char *ret = strdup(str); 62 | if (ret) 63 | return ret; 64 | perror("Error: strdup"); 65 | exit(errno); 66 | } 67 | 68 | static void xregcomp(regex_t *preg, const char *regex, int cflags) 69 | { 70 | if (regcomp(preg, regex, cflags)) { 71 | fprintf(stderr, "Error: Regex compilation error\n"); 72 | exit(EBADR); 73 | } 74 | } 75 | 76 | static char *concat(char *first, ...) 77 | { 78 | va_list args; 79 | size_t len = 0; 80 | char *ret; 81 | 82 | va_start(args, first); 83 | for (char *i = first; i; i = va_arg(args, char *)) 84 | len += strlen(i); 85 | va_end(args); 86 | 87 | ret = xmalloc(len + 1); 88 | ret[0] = '\0'; 89 | 90 | va_start(args, first); 91 | for (char *i = first; i; i = va_arg(args, char *)) 92 | strcat(ret, i); 93 | va_end(args); 94 | 95 | return ret; 96 | } 97 | 98 | static char *concat_and_free(char *orig, const char *delim, const char *new_line) 99 | { 100 | char *ret; 101 | 102 | if (!orig) 103 | ret = xstrdup(new_line); 104 | else 105 | ret = concat(orig, delim, new_line, NULL); 106 | free(orig); 107 | return ret; 108 | } 109 | 110 | struct command_buffer { 111 | char *line; 112 | size_t len; 113 | FILE *stream; 114 | }; 115 | 116 | static void free_command_buffer(struct command_buffer *c) 117 | { 118 | if (!c) 119 | return; 120 | if (c->stream) 121 | pclose(c->stream); 122 | free(c->line); 123 | } 124 | 125 | static void freep(void *p) 126 | { 127 | free(*(void **)p); 128 | } 129 | static void fclosep(FILE **f) 130 | { 131 | if (*f) 132 | fclose(*f); 133 | } 134 | #define _cleanup_free_ _cleanup_(freep) 135 | #define _cleanup_fclose_ _cleanup_(fclosep) 136 | #define _cleanup_regfree_ _cleanup_(regfree) 137 | 138 | #define DEFINE_CMD(name) _cleanup_(free_command_buffer) struct command_buffer name = { 0 }; 139 | 140 | static char *vcmd_ret(struct command_buffer *c, const char *cmd_fmt, va_list args) 141 | { 142 | _cleanup_free_ char *cmd = NULL; 143 | 144 | if (!c->stream && !cmd_fmt) 145 | return NULL; 146 | if (c->stream && cmd_fmt) 147 | pclose(c->stream); 148 | 149 | if (cmd_fmt) { 150 | if (vasprintf(&cmd, cmd_fmt, args) < 0) { 151 | perror("Error: vasprintf"); 152 | exit(errno); 153 | } 154 | 155 | c->stream = popen(cmd, "r"); 156 | if (!c->stream) { 157 | perror("Error: popen"); 158 | exit(errno); 159 | } 160 | } 161 | errno = 0; 162 | if (getline(&c->line, &c->len, c->stream) < 0) { 163 | if (errno) { 164 | perror("Error: getline"); 165 | exit(errno); 166 | } 167 | return NULL; 168 | } 169 | return c->line; 170 | } 171 | 172 | _printf_(1, 2) static void cmd(const char *cmd_fmt, ...) 173 | { 174 | _cleanup_free_ char *cmd = NULL; 175 | va_list args; 176 | int ret; 177 | 178 | va_start(args, cmd_fmt); 179 | if (vasprintf(&cmd, cmd_fmt, args) < 0) { 180 | perror("Error: vasprintf"); 181 | exit(errno); 182 | } 183 | va_end(args); 184 | 185 | printf("[#] %s\n", cmd); 186 | ret = system(cmd); 187 | 188 | if (ret < 0) 189 | ret = ESRCH; 190 | else if (ret > 0) 191 | ret = WIFEXITED(ret) ? WEXITSTATUS(ret) : EIO; 192 | 193 | if (ret && !is_exiting) 194 | exit(ret); 195 | } 196 | 197 | _printf_(2, 3) static char *cmd_ret(struct command_buffer *c, const char *cmd_fmt, ...) 198 | { 199 | va_list args; 200 | char *ret; 201 | 202 | va_start(args, cmd_fmt); 203 | ret = vcmd_ret(c, cmd_fmt, args); 204 | va_end(args); 205 | return ret; 206 | } 207 | 208 | _printf_(1, 2) static void cndc(const char *cmd_fmt, ...) 209 | { 210 | DEFINE_CMD(c); 211 | int error_code; 212 | char *ret; 213 | va_list args; 214 | _cleanup_free_ char *ndc_fmt = concat("ndc ", cmd_fmt, NULL); 215 | 216 | va_start(args, cmd_fmt); 217 | printf("[#] "); 218 | vprintf(ndc_fmt, args); 219 | printf("\n"); 220 | va_end(args); 221 | 222 | va_start(args, cmd_fmt); 223 | ret = vcmd_ret(&c, ndc_fmt, args); 224 | va_end(args); 225 | 226 | if (!ret) { 227 | fprintf(stderr, "Error: could not call ndc\n"); 228 | exit(ENOSYS); 229 | } 230 | 231 | error_code = atoi(ret); 232 | if (error_code >= 400 && error_code < 600) { 233 | fprintf(stderr, "Error: %s\n", ret); 234 | exit(ENONET); 235 | } 236 | } 237 | 238 | static void auto_su(int argc, char *argv[]) 239 | { 240 | char *args[argc + 4]; 241 | 242 | if (!getuid()) 243 | return; 244 | 245 | args[0] = "su"; 246 | args[1] = "-p"; 247 | args[2] = "-c"; 248 | memcpy(&args[3], argv, argc * sizeof(*args)); 249 | args[argc + 3] = NULL; 250 | 251 | printf("[$] su -p -c "); 252 | for (int i = 0; i < argc; ++i) 253 | printf("%s%c", argv[i], i == argc - 1 ? '\n' : ' '); 254 | 255 | execvp("su", args); 256 | exit(errno); 257 | } 258 | 259 | static void add_if(const char *iface) 260 | { 261 | cmd("ip link add %s type wireguard", iface); 262 | } 263 | 264 | static void del_if(const char *iface) 265 | { 266 | DEFINE_CMD(c); 267 | _cleanup_regfree_ regex_t reg = { 0 }; 268 | regmatch_t matches[2]; 269 | char *netid = NULL; 270 | _cleanup_free_ char *regex = concat("0xc([0-9a-f]+)/0xcffff lookup ", iface, NULL); 271 | 272 | xregcomp(®, regex, REG_EXTENDED); 273 | 274 | cmd("iptables -D OUTPUT -m mark --mark 0x20000 -j ACCEPT -m comment --comment \"wireguard rule %s\"", iface); 275 | cmd("ip6tables -D OUTPUT -m mark --mark 0x20000 -j ACCEPT -m comment --comment \"wireguard rule %s\"", iface); 276 | cmd("ip link del %s", iface); 277 | for (char *ret = cmd_ret(&c, "ip rule show"); ret; ret = cmd_ret(&c, NULL)) { 278 | if (!regexec(®, ret, ARRAY_SIZE(matches), matches, 0)) { 279 | ret[matches[1].rm_eo] = '\0'; 280 | netid = &ret[matches[1].rm_so]; 281 | break; 282 | } 283 | } 284 | 285 | if (netid) 286 | cndc("network destroy %lu", strtoul(netid, NULL, 16)); 287 | } 288 | 289 | static void up_if(unsigned int *netid, const char *iface) 290 | { 291 | srandom(time(NULL) ^ getpid()); /* Not real randomness. */ 292 | 293 | while (*netid < 4096) 294 | *netid = random() & 0xfffe; 295 | 296 | cmd("wg set %s fwmark 0x20000", iface); 297 | cmd("iptables -I OUTPUT 1 -m mark --mark 0x20000 -j ACCEPT -m comment --comment \"wireguard rule %s\"", iface); 298 | cmd("ip6tables -I OUTPUT 1 -m mark --mark 0x20000 -j ACCEPT -m comment --comment \"wireguard rule %s\"", iface); 299 | cndc("interface setcfg %s up", iface); 300 | cndc("network create %u vpn 1 1", *netid); 301 | cndc("network interface add %u %s", *netid, iface); 302 | } 303 | 304 | static int compare_uid(const void *a, const void *b) 305 | { 306 | return *(const uid_t *)a - *(const uid_t *)b; 307 | } 308 | 309 | static uid_t *get_uid_list(const char *selected_applications) 310 | { 311 | _cleanup_fclose_ FILE *package_list = NULL; 312 | _cleanup_free_ char *line = NULL; 313 | uid_t package_uid; 314 | size_t line_len = 0, i; 315 | const char *comma, *start; 316 | uid_t *uid_list; 317 | 318 | if (!selected_applications) 319 | return xcalloc(1, sizeof(*uid_list)); 320 | 321 | for (i = 1, comma = selected_applications; comma; comma = strchr(comma + 1, ','), ++i); 322 | uid_list = xcalloc(i, sizeof(*uid_list)); 323 | i = 0; 324 | 325 | package_list = fopen("/data/system/packages.list", "r"); 326 | if (!package_list) { 327 | perror("Error: Unable to open package list"); 328 | exit(errno); 329 | } 330 | 331 | while (getline(&line, &line_len, package_list) >= 0) { 332 | char *package_name, *package_uid_str, *endptr; 333 | 334 | package_name = line; 335 | package_uid_str = strchr(package_name, ' '); 336 | if (!package_uid_str) 337 | continue; 338 | *package_uid_str++ = '\0'; 339 | *strchrnul(package_uid_str, ' ') = '\0'; 340 | package_uid = strtoul(package_uid_str, &endptr, 10); 341 | if (!package_uid || !*package_uid_str || *endptr) 342 | continue; 343 | 344 | for (start = selected_applications; comma = strchrnul(start, ','), *start; start = comma + 1) { 345 | ptrdiff_t token_len = comma - start; 346 | 347 | if (token_len && strlen(package_name) == token_len && !strncmp(start, package_name, token_len)) 348 | uid_list[i++] = package_uid; 349 | } 350 | } 351 | qsort(uid_list, i, sizeof(*uid_list), compare_uid); 352 | return uid_list; 353 | } 354 | 355 | static void set_users(unsigned int netid, const char *excluded_applications) 356 | { 357 | _cleanup_free_ uid_t *excluded_uids = get_uid_list(excluded_applications); 358 | _cleanup_free_ char *ranges = NULL; 359 | char range[22]; 360 | uid_t start; 361 | 362 | for (start = 0; *excluded_uids; start = *excluded_uids + 1, ++excluded_uids) { 363 | if (start > *excluded_uids - 1) 364 | continue; 365 | else if (start == *excluded_uids - 1) 366 | snprintf(range, sizeof(range), "%u", start); 367 | else 368 | snprintf(range, sizeof(range), "%u-%u", start, *excluded_uids - 1); 369 | ranges = concat_and_free(ranges, " ", range); 370 | } 371 | if (start < 99999) { 372 | snprintf(range, sizeof(range), "%u-99999", start); 373 | ranges = concat_and_free(ranges, " ", range); 374 | } 375 | 376 | cndc("network users add %u %s", netid, ranges); 377 | } 378 | 379 | static void set_dnses(unsigned int netid, const char *dnses) 380 | { 381 | size_t len = strlen(dnses); 382 | if (len > (1<<16)) 383 | return; 384 | _cleanup_free_ char *mutable = xstrdup(dnses); 385 | _cleanup_free_ char *arglist = xmalloc(len * 4 + 1); 386 | _cleanup_free_ char *arg = xmalloc(len + 4); 387 | 388 | if (!len) 389 | return; 390 | arglist[0] = '\0'; 391 | 392 | for (char *dns = strtok(mutable, ", \t\n"); dns; dns = strtok(NULL, ", \t\n")) { 393 | if (strchr(dns, '\'') || strchr(dns, '\\')) 394 | continue; 395 | snprintf(arg, len + 3, "'%s' ", dns); 396 | strncat(arglist, arg, len * 4 - 1); 397 | } 398 | if (!strlen(arglist)) 399 | return; 400 | cndc("resolver setnetdns %u '' %s", netid, arglist); 401 | } 402 | 403 | static void add_addr(const char *iface, const char *addr) 404 | { 405 | if (strchr(addr, ':')) { 406 | cndc("interface ipv6 %s enable", iface); 407 | cmd("ip -6 addr add '%s' dev %s", addr, iface); 408 | } else { 409 | _cleanup_free_ char *mut_addr = strdup(addr); 410 | char *slash = strchr(mut_addr, '/'); 411 | unsigned char mask = 32; 412 | 413 | if (slash) { 414 | *slash = '\0'; 415 | mask = atoi(slash + 1); 416 | } 417 | cndc("interface setcfg %s '%s' %u", iface, mut_addr, mask); 418 | } 419 | } 420 | 421 | static void set_addr(const char *iface, const char *addrs) 422 | { 423 | _cleanup_free_ char *mutable = xstrdup(addrs); 424 | 425 | for (char *addr = strtok(mutable, ", \t\n"); addr; addr = strtok(NULL, ", \t\n")) { 426 | if (strchr(addr, '\'') || strchr(addr, '\\')) 427 | continue; 428 | add_addr(iface, addr); 429 | } 430 | } 431 | 432 | static int get_route_mtu(const char *endpoint) 433 | { 434 | DEFINE_CMD(c_route); 435 | DEFINE_CMD(c_dev); 436 | regmatch_t matches[2]; 437 | _cleanup_regfree_ regex_t regex_mtu = { 0 }, regex_dev = { 0 }; 438 | char *route, *mtu, *dev; 439 | 440 | xregcomp(®ex_mtu, "mtu ([0-9]+)", REG_EXTENDED); 441 | xregcomp(®ex_dev, "dev ([^ ]+)", REG_EXTENDED); 442 | 443 | if (strcmp(endpoint, "default")) 444 | route = cmd_ret(&c_route, "ip -o route get %s", endpoint); 445 | else 446 | route = cmd_ret(&c_route, "ip -o route show %s", endpoint); 447 | if (!route) 448 | return -1; 449 | 450 | if (!regexec(®ex_mtu, route, ARRAY_SIZE(matches), matches, 0)) { 451 | route[matches[1].rm_eo] = '\0'; 452 | mtu = &route[matches[1].rm_so]; 453 | } else if (!regexec(®ex_dev, route, ARRAY_SIZE(matches), matches, 0)) { 454 | route[matches[1].rm_eo] = '\0'; 455 | dev = &route[matches[1].rm_so]; 456 | route = cmd_ret(&c_dev, "ip -o link show dev %s", dev); 457 | if (!route) 458 | return -1; 459 | if (regexec(®ex_mtu, route, ARRAY_SIZE(matches), matches, 0)) 460 | return -1; 461 | route[matches[1].rm_eo] = '\0'; 462 | mtu = &route[matches[1].rm_so]; 463 | } else 464 | return -1; 465 | return atoi(mtu); 466 | } 467 | 468 | static void set_mtu(const char *iface, unsigned int mtu) 469 | { 470 | DEFINE_CMD(c_endpoints); 471 | _cleanup_regfree_ regex_t regex_endpoint = { 0 }; 472 | regmatch_t matches[2]; 473 | int endpoint_mtu, next_mtu; 474 | 475 | if (mtu) { 476 | cndc("interface setmtu %s %u", iface, mtu); 477 | return; 478 | } 479 | 480 | xregcomp(®ex_endpoint, "^\\[?([a-z0-9:.]+)\\]?:[0-9]+$", REG_EXTENDED); 481 | 482 | endpoint_mtu = get_route_mtu("default"); 483 | if (endpoint_mtu == -1) 484 | endpoint_mtu = 1500; 485 | 486 | for (char *endpoint = cmd_ret(&c_endpoints, "wg show %s endpoints", iface); endpoint; endpoint = cmd_ret(&c_endpoints, NULL)) { 487 | if (regexec(®ex_endpoint, endpoint, ARRAY_SIZE(matches), matches, 0)) 488 | continue; 489 | endpoint[matches[1].rm_eo] = '\0'; 490 | endpoint = &endpoint[matches[1].rm_so]; 491 | 492 | next_mtu = get_route_mtu(endpoint); 493 | if (next_mtu > 0 && next_mtu < endpoint_mtu) 494 | endpoint_mtu = next_mtu; 495 | } 496 | 497 | cndc("interface setmtu %s %d", iface, endpoint_mtu - 80); 498 | } 499 | 500 | static void add_route(const char *iface, unsigned int netid, const char *route) 501 | { 502 | cndc("network route add %u %s %s", netid, iface, route); 503 | } 504 | 505 | static void set_routes(const char *iface, unsigned int netid) 506 | { 507 | DEFINE_CMD(c); 508 | 509 | for (char *allowedips = cmd_ret(&c, "wg show %s allowed-ips", iface); allowedips; allowedips = cmd_ret(&c, NULL)) { 510 | char *start = strchr(allowedips, '\t'); 511 | 512 | if (!start) 513 | continue; 514 | ++start; 515 | for (char *allowedip = strtok(start, " \n"); allowedip; allowedip = strtok(NULL, " \n")) { 516 | if (!strcmp(allowedip, "(none)")) 517 | continue; 518 | add_route(iface, netid, allowedip); 519 | } 520 | } 521 | } 522 | 523 | static void maybe_block_ipv6(const char *iface) 524 | { 525 | DEFINE_CMD(c_endpoints); 526 | DEFINE_CMD(c_listenport); 527 | bool has_ipv6 = false, has_all_none = true; 528 | char *value; 529 | unsigned long listenport; 530 | 531 | for (char *endpoint = cmd_ret(&c_endpoints, "wg show %s endpoints", iface); endpoint; endpoint = cmd_ret(&c_endpoints, NULL)) { 532 | char *start = strchr(endpoint, '\t'); 533 | 534 | if (!start) 535 | continue; 536 | ++start; 537 | if (start[0] != '(') 538 | has_all_none = false; 539 | if (start[0] == '[') 540 | has_ipv6 = true; 541 | } 542 | if (has_ipv6 || has_all_none) 543 | return; 544 | 545 | cmd("ip link set up dev %s", iface); 546 | value = cmd_ret(&c_listenport, "wg show %s listen-port", iface); 547 | if (!value) 548 | goto set_back_down; 549 | listenport = strtoul(value, NULL, 10); 550 | if (listenport > UINT16_MAX || !listenport) 551 | goto set_back_down; 552 | cmd("ip6tables -I INPUT 1 -p udp --dport %lu -j DROP -m comment --comment \"wireguard rule %s\"", listenport, iface); 553 | set_back_down: 554 | cmd("ip link set down dev %s", iface); 555 | } 556 | 557 | static void maybe_unblock_ipv6(const char *iface) 558 | { 559 | regmatch_t matches[2]; 560 | _cleanup_regfree_ regex_t reg = { 0 }; 561 | _cleanup_free_ char *regex = concat("^-A (.* --comment \"wireguard rule ", iface, "\"[^\n]*)\n*$", NULL); 562 | DEFINE_CMD(c); 563 | 564 | xregcomp(®, regex, REG_EXTENDED); 565 | for (char *rule = cmd_ret(&c, "ip6tables-save"); rule; rule = cmd_ret(&c, NULL)) { 566 | if (!regexec(®, rule, ARRAY_SIZE(matches), matches, 0)) { 567 | rule[matches[1].rm_eo] = '\0'; 568 | cmd("ip6tables -D %s", &rule[matches[1].rm_so]); 569 | } 570 | } 571 | } 572 | 573 | static void set_config(const char *iface, const char *config) 574 | { 575 | FILE *config_writer; 576 | _cleanup_free_ char *cmd = concat("wg setconf ", iface, " /proc/self/fd/0", NULL); 577 | int ret; 578 | 579 | printf("[#] %s\n", cmd); 580 | 581 | config_writer = popen(cmd, "w"); 582 | if (!config_writer) { 583 | perror("Error: popen"); 584 | exit(errno); 585 | } 586 | if (fputs(config, config_writer) < 0) { 587 | perror("Error: fputs"); 588 | exit(errno); 589 | } 590 | ret = pclose(config_writer); 591 | if (ret) 592 | exit(WIFEXITED(ret) ? WEXITSTATUS(ret) : EIO); 593 | } 594 | 595 | static void broadcast_change(void) 596 | { 597 | const char *pkg = getenv("CALLING_PACKAGE"); 598 | 599 | if (!pkg || strcmp(pkg, WG_PACKAGE_NAME)) 600 | cmd("am broadcast -a com.wireguard.android.action.REFRESH_TUNNEL_STATES " WG_PACKAGE_NAME); 601 | } 602 | 603 | static void print_search_paths(FILE *file, const char *prefix) 604 | { 605 | _cleanup_free_ char *paths = strdup(WG_CONFIG_SEARCH_PATHS); 606 | 607 | for (char *path = strtok(paths, " "); path; path = strtok(NULL, " ")) 608 | fprintf(file, "%s%s\n", prefix, path); 609 | } 610 | 611 | static void cmd_usage(const char *program) 612 | { 613 | printf( "Usage: %s [ up | down ] [ CONFIG_FILE | INTERFACE ]\n" 614 | "\n" 615 | " CONFIG_FILE is a configuration file, whose filename is the interface name\n" 616 | " followed by `.conf'. Otherwise, INTERFACE is an interface name, with\n" 617 | " configuration found at:\n\n", program); 618 | print_search_paths(stdout, " - "); 619 | printf( "\n It is to be readable by wg(8)'s `setconf' sub-command, with the exception\n" 620 | " of the following additions to the [Interface] section, which are handled by\n" 621 | " this program:\n\n" 622 | " - Address: may be specified one or more times and contains one or more\n" 623 | " IP addresses (with an optional CIDR mask) to be set for the interface.\n" 624 | " - MTU: an optional MTU for the interface; if unspecified, auto-calculated.\n" 625 | " - DNS: an optional DNS server to use while the device is up.\n" 626 | " - ExcludedApplications: optional applications to exclude from the tunnel.\n\n" 627 | " See wg-quick(8) for more info and examples.\n"); 628 | } 629 | 630 | static char *cleanup_iface = NULL; 631 | 632 | static void cmd_up_cleanup(void) 633 | { 634 | is_exiting = true; 635 | if (cleanup_iface) 636 | del_if(cleanup_iface); 637 | free(cleanup_iface); 638 | } 639 | 640 | static void cmd_up(const char *iface, const char *config, unsigned int mtu, const char *addrs, const char *dnses, const char *excluded_applications) 641 | { 642 | DEFINE_CMD(c); 643 | unsigned int netid = 0; 644 | 645 | if (cmd_ret(&c, "ip link show dev %s 2>/dev/null", iface)) { 646 | fprintf(stderr, "Error: %s already exists\n", iface); 647 | exit(EEXIST); 648 | } 649 | 650 | cleanup_iface = xstrdup(iface); 651 | atexit(cmd_up_cleanup); 652 | 653 | add_if(iface); 654 | set_config(iface, config); 655 | maybe_block_ipv6(iface); 656 | set_addr(iface, addrs); 657 | up_if(&netid, iface); 658 | set_dnses(netid, dnses); 659 | set_routes(iface, netid); 660 | set_mtu(iface, mtu); 661 | set_users(netid, excluded_applications); 662 | broadcast_change(); 663 | 664 | free(cleanup_iface); 665 | cleanup_iface = NULL; 666 | exit(EXIT_SUCCESS); 667 | } 668 | 669 | static void cmd_down(const char *iface) 670 | { 671 | DEFINE_CMD(c); 672 | bool found = false; 673 | 674 | char *ifaces = cmd_ret(&c, "wg show interfaces"); 675 | if (ifaces) { 676 | for (char *eiface = strtok(ifaces, " \n"); eiface; eiface = strtok(NULL, " \n")) { 677 | if (!strcmp(iface, eiface)) { 678 | found = true; 679 | break; 680 | } 681 | } 682 | } 683 | if (!found) { 684 | fprintf(stderr, "Error: %s is not a WireGuard interface\n", iface); 685 | exit(EMEDIUMTYPE); 686 | } 687 | 688 | del_if(iface); 689 | maybe_unblock_ipv6(iface); 690 | broadcast_change(); 691 | exit(EXIT_SUCCESS); 692 | } 693 | 694 | static void parse_options(char **iface, char **config, unsigned int *mtu, char **addrs, char **dnses, char **excluded_applications, const char *arg) 695 | { 696 | _cleanup_fclose_ FILE *file = NULL; 697 | _cleanup_free_ char *line = NULL; 698 | _cleanup_free_ char *filename = NULL; 699 | _cleanup_free_ char *paths = strdup(WG_CONFIG_SEARCH_PATHS); 700 | _cleanup_regfree_ regex_t regex_iface = { 0 }, regex_conf = { 0 }; 701 | regmatch_t matches[2]; 702 | struct stat sbuf; 703 | size_t n = 0; 704 | bool in_interface_section = false; 705 | 706 | *iface = *config = *addrs = *dnses = NULL; 707 | *mtu = 0; 708 | 709 | xregcomp(®ex_iface, "^[a-zA-Z0-9_=+.-]{1,15}$", REG_EXTENDED | REG_NOSUB); 710 | xregcomp(®ex_conf, "/?([a-zA-Z0-9_=+.-]{1,15})\\.conf$", REG_EXTENDED); 711 | 712 | if (!regexec(®ex_iface, arg, 0, NULL, 0)) { 713 | for (char *path = strtok(paths, " "); path; path = strtok(NULL, " ")) { 714 | free(filename); 715 | if (asprintf(&filename, "%s/%s.conf", path, arg) < 0) { 716 | perror("Error: asprintf"); 717 | exit(errno); 718 | } 719 | file = fopen(filename, "r"); 720 | if (file) 721 | break; 722 | } 723 | if (!file) { 724 | fprintf(stderr, "Error: Unable to find configuration file for `%s' in:\n", arg); 725 | print_search_paths(stderr, "- "); 726 | exit(errno); 727 | } 728 | } else { 729 | filename = xstrdup(arg); 730 | file = fopen(filename, "r"); 731 | if (!file) { 732 | fprintf(stderr, "Error: Unable to find configuration file at `%s'\n", filename); 733 | exit(errno); 734 | } 735 | } 736 | 737 | if (regexec(®ex_conf, filename, ARRAY_SIZE(matches), matches, 0)) { 738 | fprintf(stderr, "Error: The config file must be a valid interface name, followed by .conf\n"); 739 | exit(EINVAL); 740 | } 741 | 742 | if (fstat(fileno(file), &sbuf) < 0) { 743 | perror("Error: fstat"); 744 | exit(errno); 745 | } 746 | if (sbuf.st_mode & 0007) 747 | fprintf(stderr, "Warning: `%s' is world accessible\n", filename); 748 | 749 | filename[matches[1].rm_eo] = '\0'; 750 | *iface = xstrdup(&filename[matches[1].rm_so]); 751 | 752 | while (getline(&line, &n, file) >= 0) { 753 | size_t len = strlen(line), j = 0; 754 | if (len > (1<<16)) 755 | return; 756 | _cleanup_free_ char *clean = xmalloc(len + 1); 757 | 758 | for (size_t i = 0; i < len; ++i) { 759 | if (!isspace(line[i])) 760 | clean[j++] = line[i]; 761 | } 762 | clean[j] = '\0'; 763 | 764 | if (clean[0] == '[') 765 | in_interface_section = false; 766 | if (!strcasecmp(clean, "[Interface]")) 767 | in_interface_section = true; 768 | if (in_interface_section) { 769 | if (!strncasecmp(clean, "Address=", 8) && j > 8) { 770 | *addrs = concat_and_free(*addrs, ",", clean + 8); 771 | continue; 772 | } else if (!strncasecmp(clean, "DNS=", 4) && j > 4) { 773 | *dnses = concat_and_free(*dnses, ",", clean + 4); 774 | continue; 775 | } else if (!strncasecmp(clean, "ExcludedApplications=", 21) && j > 4) { 776 | *excluded_applications = concat_and_free(*excluded_applications, ",", clean + 21); 777 | continue; 778 | } else if (!strncasecmp(clean, "MTU=", 4) && j > 4) { 779 | *mtu = atoi(clean + 4); 780 | continue; 781 | } 782 | } 783 | *config = concat_and_free(*config, "", line); 784 | } 785 | 786 | if (!*iface) 787 | *iface = xstrdup(""); 788 | if (!*config) 789 | *config = xstrdup(""); 790 | if (!*addrs) 791 | *addrs = xstrdup(""); 792 | if (!*dnses) 793 | *dnses = xstrdup(""); 794 | } 795 | 796 | int main(int argc, char *argv[]) 797 | { 798 | _cleanup_free_ char *iface = NULL; 799 | _cleanup_free_ char *config = NULL; 800 | _cleanup_free_ char *addrs = NULL; 801 | _cleanup_free_ char *dnses = NULL; 802 | _cleanup_free_ char *excluded_applications = NULL; 803 | unsigned int mtu; 804 | 805 | if (argc == 2 && (!strcmp(argv[1], "help") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) 806 | cmd_usage(argv[0]); 807 | else if (argc == 3 && !strcmp(argv[1], "up")) { 808 | auto_su(argc, argv); 809 | parse_options(&iface, &config, &mtu, &addrs, &dnses, &excluded_applications, argv[2]); 810 | cmd_up(iface, config, mtu, addrs, dnses, excluded_applications); 811 | } else if (argc == 3 && !strcmp(argv[1], "down")) { 812 | auto_su(argc, argv); 813 | parse_options(&iface, &config, &mtu, &addrs, &dnses, &excluded_applications, argv[2]); 814 | cmd_down(iface); 815 | } else { 816 | cmd_usage(argv[0]); 817 | return 1; 818 | } 819 | return 0; 820 | } 821 | --------------------------------------------------------------------------------