diff options
Diffstat (limited to 'wg-dynamic-server.c')
-rw-r--r-- | wg-dynamic-server.c | 113 |
1 files changed, 72 insertions, 41 deletions
diff --git a/wg-dynamic-server.c b/wg-dynamic-server.c index d1229cc..2b073f1 100644 --- a/wg-dynamic-server.c +++ b/wg-dynamic-server.c @@ -87,8 +87,7 @@ static int data_cb(const struct nlmsghdr *nlh, void *data) static bool validate_link_local_ip(uint32_t ifindex) { struct mnl_cb_data cb_data = { - .ifindex = ifindex, - .valid_ip_found = false, + .ifindex = ifindex, .valid_ip_found = false, }; iface_get_all_addrs(AF_INET6, data_cb, &cb_data); @@ -228,18 +227,22 @@ static bool send_error(struct wg_dynamic_request *req, int error) char buf[MAX_RESPONSE_SIZE]; size_t msglen = 0; - print_to_buf(buf, sizeof buf, &msglen, "errno=%d\nerrmsg=%s\n\n", error, - WG_DYNAMIC_ERR[error]); + debug("Parse error, errno=%d\n", error); + + print_to_buf(buf, sizeof buf, &msglen, "errno=%d\nerrmsg=%s\n\n", + E_INVALID_REQ, WG_DYNAMIC_ERR[E_INVALID_REQ]); return send_message(req, buf, msglen); } -static size_t serialize_lease(char *buf, size_t len, - const struct wg_dynamic_lease *lease) +static size_t serialize_request_ip(char *buf, size_t len, + const struct wg_dynamic_lease *lease) { char addrbuf[INET6_ADDRSTRLEN]; size_t off = 0; + print_to_buf(buf, len, &off, "request_ip=1\n"); + if (lease->ipv4.s_addr) { if (!inet_ntop(AF_INET, &lease->ipv4, addrbuf, sizeof addrbuf)) fatal("inet_ntop()"); @@ -254,47 +257,69 @@ static size_t serialize_lease(char *buf, size_t len, print_to_buf(buf, len, &off, "ipv6=%s/%d\n", addrbuf, 128); } - print_to_buf(buf, len, &off, "leasestart=%u\nleasetime=%u\nerrno=0\n\n", + print_to_buf(buf, len, &off, "leasestart=%u\nleasetime=%u\n", lease->start_real, lease->leasetime); return off; } -static void add_allowed_ips(wg_key pubkey, struct in_addr *ipv4, - struct in6_addr *ipv6) +static void adjust_allowed_ips(wg_key pubkey, struct in_addr *ipv4, + struct in6_addr *ipv6) { - wg_allowedip allowed_v4, allowed_v6; + wg_allowedip lladdr, allowed_v4, allowed_v6; wg_peer peer = { 0 }; - wg_device dev = { .first_peer = &peer }; + 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) { + wg_peer *ourpeer; + wg_for_each_peer (device, ourpeer) { + if (!memcmp(ourpeer->public_key, pubkey, sizeof(wg_key))) { + peer.flags |= WGPEER_REPLACE_ALLOWEDIPS; + memcpy(peer.public_key, pubkey, sizeof(wg_key)); + + wg_allowedip *allowedip; + wg_for_each_allowedip (ourpeer, allowedip) { + if (allowedip->family == AF_INET6 && + allowedip->cidr == 128 && + IN6_IS_ADDR_LINKLOCAL(&allowedip->ip6)) { + lladdr.family = AF_INET6; + memcpy(&lladdr.ip6, &allowedip->ip6, + sizeof(struct in6_addr)); + lladdr.cidr = 128; + *cur = &lladdr; + cur = &lladdr.next_allowedip; + break; + } + } + break; + } + } + + if (ipv4 && ipv4->s_addr) { allowed_v4 = (wg_allowedip){ - .family = AF_INET, - .cidr = 32, - .ip4 = *ipv4, + .family = AF_INET, .cidr = 32, .ip4 = *ipv4, }; *cur = &allowed_v4; cur = &allowed_v4.next_allowedip; } - if (ipv6) { + if (ipv6 && !IN6_IS_ADDR_UNSPECIFIED(ipv6)) { allowed_v6 = (wg_allowedip){ - .family = AF_INET6, - .cidr = 128, - .ip6 = *ipv6, + .family = AF_INET6, .cidr = 128, .ip6 = *ipv6, }; *cur = &allowed_v6; } - if (wg_set_device(&dev)) - fatal("wg_set_device()"); + if ((ipv4 && ipv4->s_addr) || (ipv6 && !IN6_IS_ADDR_UNSPECIFIED(ipv6))) + if (wg_set_device(&dev)) + fatal("wg_set_device()"); } -/* TODO: have UPDATES contain {wg_key, ip4 and ip6} and remove only matching addrs */ +/* TODO: have UPDATES contain {wg_key, ip4, ip6} and remove only matching addrs */ +/* FIXME: rename to remove_allowed_ips() */ static void update_allowed_ips(wg_key *updates, int nupdates) { wg_device newdev = { 0 }; @@ -348,13 +373,12 @@ static void update_allowed_ips(wg_key *updates, int nupdates) } static int response_request_ip(struct wg_dynamic_attr *cur, wg_key pubkey, - struct wg_dynamic_lease **lease) + struct wg_dynamic_lease **lease_out) { struct in_addr *ipv4 = NULL; struct in6_addr *ipv6 = NULL; uint32_t leasetime = WG_DYNAMIC_LEASETIME; - - *lease = get_leases(pubkey); + struct wg_dynamic_lease *current = NULL; while (cur) { switch (cur->key) { @@ -364,9 +388,6 @@ static int response_request_ip(struct wg_dynamic_attr *cur, wg_key pubkey, case WGKEY_IPV6: ipv6 = &((struct wg_combined_ip *)cur->value)->ip6; break; - case WGKEY_LEASETIME: - leasetime = *(uint32_t *)cur->value; - break; default: debug("Ignoring invalid attribute for request_ip: %d\n", cur->key); @@ -374,12 +395,12 @@ 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; + current = get_leases(pubkey); + debug("current lease: %s\n", lease_to_str(current)); + + *lease_out = new_lease(pubkey, leasetime, ipv4, ipv6, current); - *lease = new_lease(pubkey, leasetime, ipv4, ipv6); - if (!*lease) - return E_IP_UNAVAIL; + release_lease(current, pubkey); return E_NO_ERROR; } @@ -388,27 +409,37 @@ static bool send_response(struct wg_dynamic_request *req) { char buf[MAX_RESPONSE_SIZE]; struct wg_dynamic_attr *cur = req->first; - struct wg_dynamic_lease *lease; - size_t msglen; - int ret; + size_t msglen = 0; + int ret = 0; switch (req->cmd) { - case WGKEY_REQUEST_IP: + case WGKEY_REQUEST_IP: { + struct wg_dynamic_lease *lease = NULL; ret = response_request_ip(cur, req->pubkey, &lease); if (ret) break; - add_allowed_ips(req->pubkey, &lease->ipv4, &lease->ipv6); - msglen = serialize_lease(buf, sizeof buf, lease); + if (lease) { + adjust_allowed_ips(req->pubkey, &lease->ipv4, + &lease->ipv6); + msglen = serialize_request_ip(buf, sizeof buf, lease); + } break; + } default: debug("Unknown command: %d\n", req->cmd); BUG(); } - if (ret) - return send_error(req, ret); + if (ret) { + print_to_buf(buf, sizeof buf, &msglen, + "request_ip=1\nerrno=%d\nerrmsg=%s\n\n", ret, + WG_DYNAMIC_ERR[ret]); + + return send_message(req, buf, msglen); + } + print_to_buf(buf, sizeof buf, &msglen, "errno=0\n\n"); return send_message(req, buf, msglen); } |