From 4c8b602b1d2a305d7fbb1c3a8b065bae072c5186 Mon Sep 17 00:00:00 2001 From: Thomas Gschwantner Date: Wed, 25 Sep 2019 19:10:20 +0200 Subject: Extract all RTNETLINK code into ipm.{c,h} --- Makefile | 4 +- common.c | 67 ---------------- common.h | 2 - ipm.c | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ipm.h | 21 +++++ wg-dynamic-server.c | 79 ++++++------------- 6 files changed, 262 insertions(+), 127 deletions(-) create mode 100644 ipm.c create mode 100644 ipm.h diff --git a/Makefile b/Makefile index 7d7a327..720acb8 100644 --- a/Makefile +++ b/Makefile @@ -45,8 +45,8 @@ endif all: wg-dynamic-server -wg-dynamic-client: wg-dynamic-client.o netlink.o common.o -wg-dynamic-server: wg-dynamic-server.o netlink.o radix-trie.o common.o random.o lease.o +wg-dynamic-client: wg-dynamic-client.o netlink.o common.o ipm.o +wg-dynamic-server: wg-dynamic-server.o netlink.o radix-trie.o common.o random.o lease.o ipm.o ifneq ($(V),1) clean: diff --git a/common.c b/common.c index 906d1f7..fe0e522 100644 --- a/common.c +++ b/common.c @@ -384,70 +384,3 @@ bool is_link_local(unsigned char *addr) /* TODO: check if the remaining 54 bits are 0 */ return IN6_IS_ADDR_LINKLOCAL(addr); } - -void iface_get_all_addrs(uint8_t family, mnl_cb_t data_cb, void *cb_data) -{ - struct mnl_socket *nl; - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh; - /* TODO: rtln-addr-dump from libmnl uses rtgenmsg here? */ - struct ifaddrmsg *ifaddr; - int ret; - unsigned int seq, portid; - - nl = mnl_socket_open(NETLINK_ROUTE); - if (nl == NULL) - fatal("mnl_socket_open"); - - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) - fatal("mnl_socket_bind"); - - /* You'd think that we could just request addresses from a specific - * interface, via NLM_F_MATCH or something, but we can't. See also: - * https://marc.info/?l=linux-netdev&m=132508164508217 - */ - seq = time(NULL); - portid = mnl_socket_get_portid(nl); - nlh = mnl_nlmsg_put_header(buf); - nlh->nlmsg_type = RTM_GETADDR; - nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - nlh->nlmsg_seq = seq; - ifaddr = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifaddrmsg)); - ifaddr->ifa_family = family; - - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) - fatal("mnl_socket_sendto"); - - do { - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - if (ret <= MNL_CB_STOP) - break; - ret = mnl_cb_run(buf, ret, seq, portid, data_cb, cb_data); - } while (ret > 0); - - if (ret == -1) - fatal("mnl_cb_run/mnl_socket_recvfrom"); - - mnl_socket_close(nl); -} - -int data_attr_cb(const struct nlattr *attr, void *data) -{ - const struct nlattr **tb = data; - int type = mnl_attr_get_type(attr); - - /* skip unsupported attribute in user-space */ - if (mnl_attr_type_valid(attr, IFA_MAX) < 0) - return MNL_CB_OK; - - switch (type) { - case IFA_ADDRESS: - if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { - perror("mnl_attr_validate"); - return MNL_CB_ERROR; - } - break; - } - tb[type] = attr; - return MNL_CB_OK; -} diff --git a/common.h b/common.h index 4e5da51..1304e37 100644 --- a/common.h +++ b/common.h @@ -96,6 +96,4 @@ size_t serialize_request_ip(bool include_header, char *buf, size_t len, void print_to_buf(char *buf, size_t bufsize, size_t *offset, char *fmt, ...); uint32_t current_time(); bool is_link_local(unsigned char *addr); -void iface_get_all_addrs(uint8_t family, mnl_cb_t data_cb, void *cb_data); -int data_attr_cb(const struct nlattr *attr, void *data); #endif diff --git a/ipm.c b/ipm.c new file mode 100644 index 0000000..81135bb --- /dev/null +++ b/ipm.c @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +#include +#include +#include +#include +#include + +#include "dbg.h" +#include "common.h" + +struct mnl_cb_data { + uint32_t ifindex; + struct wg_combined_ip *ip; + bool ip_found; + bool duplicate; +}; + +static struct mnl_socket *nl = NULL; + +static int iface_update(uint16_t cmd, uint16_t flags, uint32_t ifindex, + const uint8_t *addr, uint8_t cidr, sa_family_t family) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + unsigned int seq, portid; + struct ifaddrmsg *ifaddr; + int ret; + + portid = mnl_socket_get_portid(nl); + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = cmd; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; + nlh->nlmsg_seq = seq = time(NULL); + ifaddr = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifaddrmsg)); + ifaddr->ifa_family = family; + ifaddr->ifa_prefixlen = cidr; + ifaddr->ifa_scope = RT_SCOPE_UNIVERSE; + ifaddr->ifa_index = ifindex; + mnl_attr_put(nlh, IFA_LOCAL, family == AF_INET ? 4 : 16, addr); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) + return -1; + + do { + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret <= MNL_CB_STOP) + break; + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + } while (ret > 0); + + if (ret == -1) + return -1; + + return 0; +} + +static int data_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, IFA_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case IFA_ADDRESS: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[IFA_MAX + 1] = {}; + struct ifaddrmsg *ifa = mnl_nlmsg_get_payload(nlh); + struct mnl_cb_data *cb_data = (struct mnl_cb_data *)data; + + if (ifa->ifa_index != cb_data->ifindex) + return MNL_CB_OK; + + if (ifa->ifa_scope != RT_SCOPE_LINK) + return MNL_CB_OK; + + mnl_attr_parse(nlh, sizeof(*ifa), data_attr_cb, tb); + + if (!tb[IFA_ADDRESS]) + return MNL_CB_OK; + + if (cb_data->ip_found) { + cb_data->duplicate = true; + return MNL_CB_OK; + } + + memcpy(cb_data->ip, mnl_attr_get_payload(tb[IFA_ADDRESS]), + ifa->ifa_family == AF_INET ? 4 : 16); + cb_data->ip->cidr = ifa->ifa_prefixlen; + cb_data->ip->family = ifa->ifa_family; + + char out[INET6_ADDRSTRLEN]; + inet_ntop(ifa->ifa_family, cb_data->ip, out, sizeof(out)); + debug("index=%d, family=%d, addr=%s\n", ifa->ifa_index, ifa->ifa_family, + out); + + cb_data->ip_found = true; + + return MNL_CB_OK; +} + +static int iface_get_all_addrs(uint8_t family, void *cb_data) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + /* TODO: rtln-addr-dump from libmnl uses rtgenmsg here? */ + struct ifaddrmsg *ifaddr; + int ret; + unsigned int seq, portid; + + /* You'd think that we could just request addresses from a specific + * interface, via NLM_F_MATCH or something, but we can't. See also: + * https://marc.info/?l=linux-netdev&m=132508164508217 + */ + seq = time(NULL); + portid = mnl_socket_get_portid(nl); + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_GETADDR; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlh->nlmsg_seq = seq; + ifaddr = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifaddrmsg)); + ifaddr->ifa_family = family; + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) + return -1; + + do { + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret <= MNL_CB_STOP) + break; + ret = mnl_cb_run(buf, ret, seq, portid, data_cb, cb_data); + } while (ret > 0); + + if (ret == -1) + return -1; + + return 0; +} + +void ipm_init() +{ + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) + fatal("mnl_socket_open()"); + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) + fatal("mnl_socket_bind()"); +} + +void ipm_free() +{ + if (nl) + mnl_socket_close(nl); +} + +int ipm_newaddr_v4(uint32_t ifindex, const struct in_addr *ip) +{ + return iface_update(RTM_NEWADDR, NLM_F_REPLACE | NLM_F_CREATE, ifindex, + (uint8_t *)ip, 32, AF_INET); +} + +int ipm_newaddr_v6(uint32_t ifindex, const struct in6_addr *ip) +{ + return iface_update(RTM_NEWADDR, NLM_F_REPLACE | NLM_F_CREATE, ifindex, + (uint8_t *)ip, 128, AF_INET6); +} + +int ipm_deladdr_v4(uint32_t ifindex, const struct in_addr *ip) +{ + return iface_update(RTM_DELADDR, 0, ifindex, (uint8_t *)ip, 32, + AF_INET); +} + +int ipm_deladdr_v6(uint32_t ifindex, const struct in6_addr *ip) +{ + return iface_update(RTM_DELADDR, 0, ifindex, (uint8_t *)ip, 128, + AF_INET6); +} + +int ipm_getlladdr(uint32_t ifindex, struct wg_combined_ip *addr) +{ + struct mnl_cb_data cb_data = { + .ifindex = ifindex, + .ip = addr, + .ip_found = false, + .duplicate = false, + }; + + if (iface_get_all_addrs(AF_INET6, &cb_data)) + return -1; + + if (!cb_data.ip_found) + return -2; + + if (cb_data.duplicate) + return -3; + + return 0; +} diff --git a/ipm.h b/ipm.h new file mode 100644 index 0000000..b10feac --- /dev/null +++ b/ipm.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +#ifndef __IPM_H__ +#define __IPM_H__ + +#include + +#include "common.h" + +void ipm_init(); +void ipm_free(); +int ipm_newaddr_v4(uint32_t ifindex, const struct in_addr *ip); +int ipm_newaddr_v6(uint32_t ifindex, const struct in6_addr *ip); +int ipm_deladdr_v4(uint32_t ifindex, const struct in_addr *ip); +int ipm_deladdr_v6(uint32_t ifindex, const struct in6_addr *ip); +int ipm_getlladdr(uint32_t ifindex, struct wg_combined_ip *addr); + +#endif diff --git a/wg-dynamic-server.c b/wg-dynamic-server.c index 36f3335..0f16710 100644 --- a/wg-dynamic-server.c +++ b/wg-dynamic-server.c @@ -24,6 +24,7 @@ #include "common.h" #include "dbg.h" +#include "ipm.h" #include "khash.h" #include "lease.h" #include "netlink.h" @@ -51,60 +52,11 @@ struct wg_dynamic_connection { static struct wg_dynamic_connection connections[MAX_CONNECTIONS] = { 0 }; -struct mnl_cb_data { - uint32_t ifindex; - bool valid_ip_found; -}; - static void usage() { die("usage: %s \n", progname); } -static int data_cb(const struct nlmsghdr *nlh, void *data) -{ - struct nlattr *tb[IFA_MAX + 1] = {}; - struct ifaddrmsg *ifa = mnl_nlmsg_get_payload(nlh); - struct mnl_cb_data *cb_data = (struct mnl_cb_data *)data; - unsigned char *addr; - - if (ifa->ifa_index != cb_data->ifindex) - return MNL_CB_OK; - - if (ifa->ifa_scope != RT_SCOPE_LINK) - return MNL_CB_OK; - - mnl_attr_parse(nlh, sizeof(*ifa), data_attr_cb, tb); - - if (!tb[IFA_ADDRESS]) - return MNL_CB_OK; - - addr = mnl_attr_get_payload(tb[IFA_ADDRESS]); - char out[INET6_ADDRSTRLEN]; - inet_ntop(ifa->ifa_family, addr, out, sizeof(out)); - debug("index=%d, family=%d, addr=%s\n", ifa->ifa_index, ifa->ifa_family, - out); - - if (ifa->ifa_prefixlen != 64 || memcmp(addr, well_known.s6_addr, 16)) - return MNL_CB_OK; - - cb_data->valid_ip_found = true; - - return MNL_CB_OK; -} - -static bool validate_link_local_ip(uint32_t ifindex) -{ - struct mnl_cb_data cb_data = { - .ifindex = ifindex, - .valid_ip_found = false, - }; - - iface_get_all_addrs(AF_INET6, data_cb, &cb_data); - - return cb_data.valid_ip_found; -} - static bool valid_peer_found(wg_device *device) { wg_peer *peer; @@ -452,6 +404,9 @@ static void cleanup() static void setup() { + struct wg_combined_ip ip; + int ret; + if (inet_pton(AF_INET6, WG_DYNAMIC_ADDR, &well_known) != 1) fatal("inet_pton()"); @@ -465,11 +420,23 @@ static void setup() rebuild_allowedips_ht(); - if (!validate_link_local_ip(device->ifindex)) - // TODO: assign IP instead? + ipm_init(); + ret = ipm_getlladdr(device->ifindex, &ip); + if (ret == -1) + fatal("ipm_getlladdr()"); + if (ret == -2) + die("Interface must not have multiple link-local addresses assigned\n"); + ipm_free(); + + if (ret == -1 || ip.family != AF_INET6 || + memcmp(&ip.ip6, well_known.s6_addr, 16)) + /* TODO: assign IP instead? */ die("%s needs to have %s assigned\n", wg_interface, WG_DYNAMIC_ADDR); + if (ip.cidr != 64) + die("Link-local address must have a CIDR of 64\n"); + if (!valid_peer_found(device)) die("%s has no peers with link-local allowedips\n", wg_interface); @@ -500,13 +467,13 @@ static void accept_incoming() if (fd == -ENOENT) { debug("Failed to match IP to pubkey\n"); continue; - } else if (fd != -EAGAIN && fd != -EWOULDBLOCK) { - debug("Failed to accept connection: %s\n", - strerror(-fd)); - continue; + } else if (fd == -EAGAIN || fd == -EWOULDBLOCK) { + return; } - break; + debug("Failed to accept connection: %s\n", + strerror(-fd)); + continue; } ev.events = EPOLLIN | EPOLLET; -- cgit v1.2.3-59-g8ed1b