From 63493afccf6d3714c1408335eecfef888b00c19a Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Tue, 1 Oct 2019 00:57:42 +0200 Subject: Server lease handling. --- wg-dynamic-server.c | 60 ++++++++++++----------------------------------------- 1 file changed, 13 insertions(+), 47 deletions(-) (limited to 'wg-dynamic-server.c') diff --git a/wg-dynamic-server.c b/wg-dynamic-server.c index fca1dfc..3c1fb53 100644 --- a/wg-dynamic-server.c +++ b/wg-dynamic-server.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include @@ -167,7 +166,8 @@ static wg_key *addr_to_pubkey(struct sockaddr_storage *addr) return NULL; } -static int accept_connection(int sockfd, wg_key *dest) +static int accept_connection(int sockfd, wg_key *dest_pubkey, + struct in6_addr *dest_lladdr) { int fd; wg_key *pubkey; @@ -211,7 +211,10 @@ static int accept_connection(int sockfd, wg_key *dest) return -ENOENT; } } - memcpy(dest, pubkey, sizeof *dest); + memcpy(dest_pubkey, pubkey, sizeof *dest_pubkey); + + memcpy(dest_lladdr, &((struct sockaddr_in6 *)&addr)->sin6_addr, + sizeof *dest_lladdr); wg_key_b64_string key; char out[INET6_ADDRSTRLEN]; @@ -260,49 +263,14 @@ static size_t serialize_lease(char *buf, size_t len, return off; } -static void add_allowed_ips(wg_key pubkey, struct in_addr *ipv4, - struct in6_addr *ipv6) -{ - wg_allowedip allowed_v4, allowed_v6; - wg_peer peer = { 0 }; - wg_device dev = { .first_peer = &peer }; - - strcpy(dev.name, wg_interface); - memcpy(peer.public_key, pubkey, sizeof peer.public_key); - wg_allowedip **cur = &peer.first_allowedip; - - if (ipv4) { - allowed_v4 = (wg_allowedip){ - .family = AF_INET, - .cidr = 32, - .ip4 = *ipv4, - }; - *cur = &allowed_v4; - cur = &allowed_v4.next_allowedip; - } - - if (ipv6) { - allowed_v6 = (wg_allowedip){ - .family = AF_INET6, - .cidr = 128, - .ip6 = *ipv6, - }; - *cur = &allowed_v6; - } - - if (wg_set_device(&dev)) - fatal("wg_set_device()"); -} - static int response_request_ip(struct wg_dynamic_attr *cur, wg_key pubkey, + const struct in6_addr *lladdr, struct wg_dynamic_lease **lease) { struct in_addr *ipv4 = NULL; struct in6_addr *ipv6 = NULL; uint32_t leasetime = WG_DYNAMIC_LEASETIME; - *lease = get_leases(pubkey); - while (cur) { switch (cur->key) { case WGKEY_IPV4: @@ -321,10 +289,7 @@ static int response_request_ip(struct wg_dynamic_attr *cur, wg_key pubkey, cur = cur->next; } - if (ipv4 && ipv6 && !ipv4->s_addr && IN6_IS_ADDR_UNSPECIFIED(ipv6)) - return E_INVALID_REQ; - - *lease = new_lease(pubkey, leasetime, ipv4, ipv6); + *lease = set_lease(wg_interface, pubkey, leasetime, lladdr, ipv4, ipv6); if (!*lease) return E_IP_UNAVAIL; @@ -341,11 +306,11 @@ static bool send_response(struct wg_dynamic_request *req) switch (req->cmd) { case WGKEY_REQUEST_IP: - ret = response_request_ip(cur, req->pubkey, &lease); + ret = response_request_ip(cur, req->pubkey, &req->lladdr, + &lease); if (ret) break; - add_allowed_ips(req->pubkey, &lease->ipv4, &lease->ipv6); msglen = serialize_lease(buf, sizeof buf, lease); break; default: @@ -479,7 +444,8 @@ static void accept_incoming(int sockfd, int epollfd, struct epoll_event ev; while ((n = get_avail_request()) >= 0) { - fd = accept_connection(sockfd, &requests[n].pubkey); + fd = accept_connection(sockfd, &requests[n].pubkey, + &requests[n].lladdr); if (fd < 0) { if (fd == -ENOENT) { debug("Failed to match IP to pubkey\n"); @@ -546,7 +512,7 @@ static void poll_loop() fatal("epoll_ctl()"); while (1) { - time_t next = leases_refresh() * 1000; + time_t next = leases_refresh(wg_interface) * 1000; int nfds = epoll_wait(epollfd, events, MAX_CONNECTIONS, next); if (nfds == -1) { if (errno == EINTR) -- cgit v1.2.3-59-g8ed1b From 042bcaf5868b5d3b814ae1877e6d56fde647994f Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Mon, 30 Sep 2019 08:44:34 +0200 Subject: [server] Make leasetime a commandline option --- common.h | 2 +- tests/netsh.sh | 2 +- wg-dynamic-server.c | 28 +++++++++++++++++++++++----- 3 files changed, 25 insertions(+), 7 deletions(-) (limited to 'wg-dynamic-server.c') diff --git a/common.h b/common.h index 426ad20..ea550d9 100644 --- a/common.h +++ b/common.h @@ -25,7 +25,7 @@ static const char WG_DYNAMIC_ADDR[] = "fe80::"; static const uint16_t WG_DYNAMIC_PORT = 970; /* ASCII sum of "wireguard" */ -#define WG_DYNAMIC_LEASETIME 10 /* NOTE: 10s is good for testing purposes */ +#define WG_DYNAMIC_DEFAULT_LEASETIME 3600 #define ITEMS \ E(WGKEY_UNKNOWN, "") /* must be the first entry */ \ diff --git a/tests/netsh.sh b/tests/netsh.sh index 9f022c8..f9e9c1e 100755 --- a/tests/netsh.sh +++ b/tests/netsh.sh @@ -86,4 +86,4 @@ echo echo wg-test-$$ $server_public echo -nn 1 ./wg-dynamic-server wg0 +nn 1 ./wg-dynamic-server --leasetime 10 wg0 diff --git a/wg-dynamic-server.c b/wg-dynamic-server.c index 3c1fb53..cf5e860 100644 --- a/wg-dynamic-server.c +++ b/wg-dynamic-server.c @@ -28,11 +28,12 @@ #include "netlink.h" static const char *progname; -static const char *wg_interface; +static const char *wg_interface = NULL; static struct in6_addr well_known; static wg_device *device = NULL; static struct wg_dynamic_request requests[MAX_CONNECTIONS] = { 0 }; +static uint32_t leasetime = WG_DYNAMIC_DEFAULT_LEASETIME; static int sockfd = -1; static int epollfd = -1; @@ -48,7 +49,7 @@ struct mnl_cb_data { static void usage() { - die("usage: %s \n", progname); + die("usage: %s [--leasetime ] \n", progname); } static int data_cb(const struct nlmsghdr *nlh, void *data) @@ -269,7 +270,6 @@ static int response_request_ip(struct wg_dynamic_attr *cur, wg_key pubkey, { struct in_addr *ipv4 = NULL; struct in6_addr *ipv6 = NULL; - uint32_t leasetime = WG_DYNAMIC_LEASETIME; while (cur) { switch (cur->key) { @@ -528,11 +528,29 @@ static void poll_loop() int main(int argc, char *argv[]) { + char *endptr = NULL; + progname = argv[0]; - if (argc != 2) + ++argv; + --argc; + + while (argc > 0) { + if (!strcmp(argv[0], "--leasetime") && argc >= 2) { + leasetime = (uint32_t) strtoul(argv[1], &endptr, 10); + if (*endptr) + usage(); + argv += 2; + argc -= 2; + } else { + wg_interface = argv[0]; + argv += 1; + argc -= 1; + break; + } + } + if (!wg_interface || argc > 0) usage(); - wg_interface = argv[1]; setup(); poll_loop(); -- cgit v1.2.3-59-g8ed1b From d8ec36b7ef3a54652fa59492159b615896fef2f3 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Mon, 30 Sep 2019 09:17:16 +0200 Subject: [server] Restore leases from allowedips at startup --- tests/restore-leases.bash | 162 ++++++++++++++++++++++++++++++++++++++++++++++ wg-dynamic-server.c | 30 ++++++++- 2 files changed, 191 insertions(+), 1 deletion(-) create mode 100755 tests/restore-leases.bash (limited to 'wg-dynamic-server.c') diff --git a/tests/restore-leases.bash b/tests/restore-leases.bash new file mode 100755 index 0000000..43e4737 --- /dev/null +++ b/tests/restore-leases.bash @@ -0,0 +1,162 @@ +set -e +exec 3>&1 + +export WG_HIDE_KEYS=never +netnsn() { echo wg-test-$$-$1; } +pretty() { echo -e "\x1b[32m\x1b[1m[+] ${1:+NS$1: }${2}\x1b[0m" >&3; } +pp() { pretty "" "$*"; "$@"; } +maybe_exec() { if [[ $BASHPID -eq $$ ]]; then "$@"; else exec "$@"; fi; } +nn() { local netns=$(netnsn $1) n=$1; shift; pretty $n "$*"; maybe_exec ip netns exec $netns "$@"; } +ipn() { local netns=$(netnsn $1) n=$1; shift; pretty $n "ip $*"; ip -n $netns "$@"; } + +ns="0 1 2" + +cleanup() { + set +e + exec 2>/dev/null + + for n in $ns; do ipn $n link del dev wg0; done + + local to_kill="$(for n in $ns; do $(ip netns pids $(netnsn $n)); done)" + [[ -n $to_kill ]] && kill $to_kill + + for n in $ns; do pp ip netns del $(netnsn $n); done + + exit +} + +trap cleanup EXIT + +pp ip netns add $(netnsn 0) +pp ip netns add $(netnsn 1) +pp ip netns add $(netnsn 2) +ipn 0 link set up dev lo + +ipn 0 link add dev wg0 type wireguard +ipn 0 link set wg0 netns $(netnsn 1) +ipn 0 link add dev wg0 type wireguard +ipn 0 link set wg0 netns $(netnsn 2) +server_private=$(wg genkey) +server_public=$(wg pubkey <<< $server_private) +client_private=$(wg genkey) +client_public=$(wg pubkey <<< $client_private) + +configure_peers() { + ipn 1 addr add fe80::/64 dev wg0 + ipn 2 addr add fe80::badc:0ffe:e0dd:f00d/128 dev wg0 + + nn 1 wg set wg0 \ + private-key <(echo $server_private) \ + listen-port 1 \ + peer $client_public \ + allowed-ips fe80::badc:0ffe:e0dd:f00d/128 + + nn 2 wg set wg0 \ + private-key <(echo $client_private) \ + listen-port 2 \ + peer $server_public \ + allowed-ips 0.0.0.0/0,::/0 + + ipn 1 link set up dev wg0 + ipn 2 link set up dev wg0 + + ipn 2 route add fe80::/128 dev wg0 + ipn 1 route add 192.168.4.0/28 dev wg0 + ipn 1 route add 192.168.73.0/27 dev wg0 + ipn 1 route add 2001:db8:1234::/124 dev wg0 + ipn 1 route add 2001:db8:7777::/124 dev wg0 +} +configure_peers + +nn 1 wg set wg0 peer "$client_public" endpoint [::1]:2 +nn 2 wg set wg0 peer "$server_public" endpoint [::1]:1 +nn 2 ping6 -c 10 -f -W 1 fe80::%wg0 +nn 1 ping6 -c 10 -f -W 1 fe80::badc:0ffe:e0dd:f00d%wg0 + +pretty "" "clientsh.bash can be run with the following arguments:" +echo +echo wg-test-$$ $server_public +echo + +## Start server in the background with 30s lease time +exec 4< <(nn 1 ./wg-dynamic-server --leasetime 30 wg0) +server_pid=$! +pretty "" "server_pid: $server_pid" + +## Get a lease +send_cmd() { + local n=$1; shift + local REQ="$1"; shift + + eval $( + printf $REQ | nn $n ncat -v -p 970 fe80::%wg0 970 | + while read -r line; do + key="${line%%=*}" + value="${line#*=}" + case "$key" in + ipv4) echo IPV4[$n]="$value"; continue ;; + ipv6) echo IPV6[$n]="$value"; continue ;; + leasestart) echo LEASESTART[$n]="$value"; continue ;; + leasetime) echo LEASETIME[$n]="$value"; continue ;; + errno) echo ERRNO[$n]="$value"; continue ;; + esac + done + ) +} +check_alowedips() { + local pubkey="$1"; shift + local ip="$1"; shift + + [[ -z "$ip" ]] && return 0 + + nn 1 wg show wg0 allowed-ips | + while read -r _pubkey _ips; do + [[ "$_pubkey" = "$pubkey" ]] || continue + for _ip in $_ips; do + [[ "$_ip" = "$ip" ]] && return 0 + done + done && return 0 + + pretty "" "Missing $ip in allowedips" + return 1 +} +declare -a IPV4 +declare -a IPV6 +declare -a LEASESTART +declare -a LEASETIME +declare -a ERRNO + +send_cmd 2 "request_ip=1\n\n" +check_alowedips "$client_public" "${IPV4[2]}" +check_alowedips "$client_public" "${IPV6[2]}" + +## Restart server with 10s leasetime +nn 1 kill $server_pid +sleep 1 +exec 4< <(nn 1 ./wg-dynamic-server --leasetime 3 wg0) || { pretty "" $?; false; } +server_pid=$! +pretty "" "server_pid 2: $server_pid" + +## Verify that the lease has been restored +check_alowedips "$client_public" "${IPV4[2]}" +check_alowedips "$client_public" "${IPV6[2]}" + +## Wait for the lease to expire +pp sleep 4 + +## Restart server +nn 1 kill $server_pid +sleep 1 +exec 4< <(nn 1 ./wg-dynamic-server wg0) + +## Verify that the lease has not reappeared +notlladdr='192.168.*/32|2001:.*/128' +nn 1 wg show wg0 allowed-ips | + while read -r _pubkey _ips; do + [[ "$_pubkey" = "$client_public" ]] || continue + for _ip in $_ips; do + [[ "$_ip" =~ $notlladdr ]] && { pretty "" "FAIL: $_ip"; false; } + done + done && true + +pretty "" "SUCCESS\n" diff --git a/wg-dynamic-server.c b/wg-dynamic-server.c index cf5e860..27f4054 100644 --- a/wg-dynamic-server.c +++ b/wg-dynamic-server.c @@ -398,6 +398,33 @@ static void cleanup() } } +static void init_leaess_from_peers() +{ + wg_peer *peer; + + wg_for_each_peer (device, peer) { + wg_allowedip *allowedip; + struct in6_addr *lladdr = NULL; + struct in_addr *ipv4 = NULL; + struct in6_addr *ipv6 = NULL; + wg_for_each_allowedip (peer, allowedip) { + if (allowedip->family == AF_INET6 && + IN6_IS_ADDR_LINKLOCAL(&allowedip->ip6)) + lladdr = &allowedip->ip6; + else if (allowedip->family == AF_INET && !ipv4) + ipv4 = &allowedip->ip4; + else if (allowedip->family == AF_INET6 && !ipv6) + ipv6 = &allowedip->ip6; + } + + if (!ipv4 && !ipv6) + continue; + + set_lease(wg_interface, peer->public_key, leasetime, lladdr, + ipv4, ipv6); + } +} + static void setup() { if (inet_pton(AF_INET6, WG_DYNAMIC_ADDR, &well_known) != 1) @@ -423,7 +450,8 @@ static void setup() wg_interface); setup_sockets(); - leases_init("leases_file", nlsock); + leases_init(NULL, nlsock); + init_leaess_from_peers(); } static int get_avail_request() -- cgit v1.2.3-59-g8ed1b