aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gschwantner <tharre3@gmail.com>2019-09-25 19:10:20 +0200
committerThomas Gschwantner <tharre3@gmail.com>2019-12-11 06:22:17 +0100
commitc42c5b2d4384a1111695016aa293f13fbeede03a (patch)
tree913dbd7e7b27cb5086d3200083840eb9d145c852
parentSerialize/deserialize messages into a struct (diff)
downloadwg-dynamic-c42c5b2d4384a1111695016aa293f13fbeede03a.tar.xz
wg-dynamic-c42c5b2d4384a1111695016aa293f13fbeede03a.zip
Extract all RTNETLINK code into ipm.{c,h}
-rw-r--r--Makefile4
-rw-r--r--common.c67
-rw-r--r--common.h2
-rw-r--r--ipm.c216
-rw-r--r--ipm.h21
-rw-r--r--wg-dynamic-server.c81
6 files changed, 263 insertions, 128 deletions
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 a0f1ab0..da9226e 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 0e97f97..0bb0741 100644
--- a/common.h
+++ b/common.h
@@ -95,6 +95,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 <arpa/inet.h>
+#include <libmnl/libmnl.h>
+#include <linux/rtnetlink.h>
+#include <stdint.h>
+#include <time.h>
+
+#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 <stdint.h>
+
+#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 99c7559..3b4b1f1 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"
@@ -53,11 +54,6 @@ 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()
{
fprintf(stderr, "usage: %s [--leasetime <leasetime>] <wg-interface>\n",
@@ -65,51 +61,7 @@ static void usage()
exit(EXIT_FAILURE);
}
-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()
+static bool valid_peer_found(wg_device *device)
{
wg_peer *peer;
wg_key_b64_string key;
@@ -461,6 +413,9 @@ static void init_leases_from_peers()
static void setup()
{
+ struct wg_combined_ip ip;
+ int ret;
+
if (inet_pton(AF_INET6, WG_DYNAMIC_ADDR, &well_known) != 1)
fatal("inet_pton()");
@@ -474,11 +429,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);
@@ -511,13 +478,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;