From: Felix Fietkau Date: Wed, 24 Aug 2022 12:02:48 +0000 (+0200) Subject: network: add support for specifying a host gateway X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=1d8ba108ca06783f827b6b0dcdb4673548b62265;p=project%2Funetd.git network: add support for specifying a host gateway A host will only use its gateway as a peer, and connections from other hosts will be routed through the gateway host Signed-off-by: Felix Fietkau --- diff --git a/host.c b/host.c index bef8863..996dbcf 100644 --- a/host.c +++ b/host.c @@ -93,6 +93,7 @@ network_host_create(struct network *net, struct blob_attr *attr) NETWORK_HOST_SUBNET, NETWORK_HOST_PORT, NETWORK_HOST_ENDPOINT, + NETWORK_HOST_GATEWAY, __NETWORK_HOST_MAX }; static const struct blobmsg_policy policy[__NETWORK_HOST_MAX] = { @@ -102,6 +103,7 @@ network_host_create(struct network *net, struct blob_attr *attr) [NETWORK_HOST_SUBNET] = { "subnet", BLOBMSG_TYPE_ARRAY }, [NETWORK_HOST_PORT] = { "port", BLOBMSG_TYPE_INT32 }, [NETWORK_HOST_ENDPOINT] = { "endpoint", BLOBMSG_TYPE_STRING }, + [NETWORK_HOST_GATEWAY] = { "gateway", BLOBMSG_TYPE_STRING }, }; struct blob_attr *tb[__NETWORK_HOST_MAX]; struct blob_attr *cur, *ipaddr, *subnet; @@ -109,8 +111,8 @@ network_host_create(struct network *net, struct blob_attr *attr) struct network_host *host; struct network_peer *peer; int ipaddr_len, subnet_len; - const char *name, *endpoint; - char *name_buf, *endpoint_buf; + const char *name, *endpoint, *gateway; + char *name_buf, *endpoint_buf, *gateway_buf; int rem; blobmsg_parse(policy, __NETWORK_HOST_MAX, tb, blobmsg_data(attr), blobmsg_len(attr)); @@ -133,6 +135,11 @@ network_host_create(struct network *net, struct blob_attr *attr) else endpoint = NULL; + if ((cur = tb[NETWORK_HOST_GATEWAY]) != NULL) + gateway = blobmsg_get_string(cur); + else + gateway = NULL; + if (b64_decode(blobmsg_get_string(tb[NETWORK_HOST_KEY]), key, sizeof(key)) != sizeof(key)) return; @@ -146,7 +153,8 @@ network_host_create(struct network *net, struct blob_attr *attr) &name_buf, strlen(name) + 1, &ipaddr, ipaddr_len, &subnet, subnet_len, - &endpoint_buf, endpoint ? strlen(endpoint) + 1 : 0); + &endpoint_buf, endpoint ? strlen(endpoint) + 1 : 0, + &gateway_buf, gateway ? strlen(endpoint) + 1 : 0); peer = &host->peer; if ((cur = tb[NETWORK_HOST_IPADDR]) != NULL && ipaddr_len) peer->ipaddr = memcpy(ipaddr, cur, ipaddr_len); @@ -158,6 +166,8 @@ network_host_create(struct network *net, struct blob_attr *attr) peer->port = net->net_config.port; if (endpoint) peer->endpoint = strcpy(endpoint_buf, endpoint); + if (gateway) + host->gateway = strcpy(gateway_buf, gateway); memcpy(peer->key, key, sizeof(key)); host->node.key = strcpy(name_buf, name); @@ -202,17 +212,27 @@ void network_hosts_update_start(struct network *net) void network_hosts_update_done(struct network *net) { - struct network_host *host, *tmp; + struct network_host *local, *host, *tmp; + const char *local_name; - if (!net->net_config.local_host) + local = net->net_config.local_host; + if (!local) goto out; + local_name = network_host_name(local); + if (net->net_config.local_host_changed) - wg_init_local(net, &net->net_config.local_host->peer); + wg_init_local(net, &local->peer); - avl_for_each_element(&net->hosts, host, node) - if (host != net->net_config.local_host) - vlist_add(&net->peers, &host->peer.node, host->peer.key); + avl_for_each_element(&net->hosts, host, node) { + if (host == local) + continue; + if (host->gateway && strcmp(host->gateway, local_name) != 0) + continue; + if (local->gateway && strcmp(local->gateway, network_host_name(host)) != 0) + continue; + vlist_add(&net->peers, &host->peer.node, host->peer.key); + } out: vlist_flush(&net->peers); @@ -242,7 +262,7 @@ network_hosts_connect_cb(struct uloop_timeout *t) avl_for_each_element(&net->hosts, host, node) { struct network_peer *peer = &host->peer; - if (host == net->net_config.local_host) + if (!network_host_is_peer(host)) continue; if (peer->state.connected) diff --git a/host.h b/host.h index 985ac45..b802d77 100644 --- a/host.h +++ b/host.h @@ -36,6 +36,7 @@ struct network_peer { struct network_host { struct avl_node node; + const char *gateway; struct network_peer peer; }; @@ -55,6 +56,11 @@ static inline const char *network_host_name(struct network_host *host) return host->node.key; } +static inline bool network_host_is_peer(struct network_host *host) +{ + return !!host->peer.node.avl.key; +} + static inline const char *network_peer_name(struct network_peer *peer) { struct network_host *host; @@ -66,6 +72,29 @@ static inline const char *network_peer_name(struct network_peer *peer) return network_host_name(host); } + +static inline bool +network_host_uses_peer_route(struct network_host *host, struct network *net, + struct network_peer *peer) +{ + if (&host->peer == peer || host == net->net_config.local_host) + return false; + + if (net->net_config.local_host->gateway && + !strcmp(net->net_config.local_host->gateway, network_peer_name(peer))) + return true; + + if (!host->gateway) + return false; + + return !strcmp(host->gateway, network_peer_name(peer)); +} + +#define for_each_routed_host(cur_host, net, peer) \ + avl_for_each_element(&(net)->hosts, cur_host, node) \ + if (network_host_uses_peer_route(host, net, peer)) + + void network_hosts_update_start(struct network *net); void network_hosts_update_done(struct network *net); void network_hosts_add(struct network *net, struct blob_attr *hosts); diff --git a/scripts/unet-cli b/scripts/unet-cli index 06bb709..c5e7025 100755 --- a/scripts/unet-cli +++ b/scripts/unet-cli @@ -51,6 +51,7 @@ function usage() { " ipaddr=[+|-][,...] set/add/remove host ip addresses\n", " subnet=[+|-][,...] set/add/remove host announced subnets\n", " endpoint= set host endpoint address\n", + " gateway= set host gateway (using name of other host)\n", " ssh host options (add-ssh-host, set-ssh-host)\n", " auth_key= use as public auth key on the remote host\n", " priv_key= use as private host key on the remote host (default: generate a new key)\n", @@ -211,6 +212,7 @@ function set_host(name) { set_fields(host, { key: "string", endpoint: "string", + gateway: "string", port: "int", ipaddr: "array", subnet: "array", diff --git a/wg-linux.c b/wg-linux.c index 1dc0c9b..57b8634 100644 --- a/wg-linux.c +++ b/wg-linux.c @@ -147,25 +147,12 @@ wg_linux_peer_req_done(struct wg_linux_peer_req *req) return wg_genl_call(req->msg); } -static int -wg_linux_peer_update(struct network *net, struct network_peer *peer, enum wg_update_cmd cmd) +static void +wg_linux_peer_msg_add_allowed_ip(struct nl_msg *msg, struct network_peer *peer) { - struct wg_linux_peer_req req; struct blob_attr *cur; - struct nl_msg *msg; - struct nlattr *ips; int rem; - msg = wg_linux_peer_req_init(net, peer, &req); - - if (cmd == WG_PEER_DELETE) { - nla_put_u32(msg, WGPEER_A_FLAGS, WGPEER_F_REMOVE_ME); - goto out; - } - - nla_put_u32(msg, WGPEER_A_FLAGS, WGPEER_F_REPLACE_ALLOWEDIPS); - - ips = nla_nest_start(msg, WGPEER_A_ALLOWEDIPS); wg_linux_msg_add_ip(msg, AF_INET6, &peer->local_addr.in6, 128); blobmsg_for_each_attr(cur, peer->ipaddr, rem) { @@ -200,6 +187,31 @@ wg_linux_peer_update(struct network *net, struct network_peer *peer, enum wg_upd wg_linux_msg_add_ip(msg, af, &addr, mask); } +} + +static int +wg_linux_peer_update(struct network *net, struct network_peer *peer, enum wg_update_cmd cmd) +{ + struct wg_linux_peer_req req; + struct network_host *host; + struct nl_msg *msg; + struct nlattr *ips; + + msg = wg_linux_peer_req_init(net, peer, &req); + + if (cmd == WG_PEER_DELETE) { + nla_put_u32(msg, WGPEER_A_FLAGS, WGPEER_F_REMOVE_ME); + goto out; + } + + nla_put_u32(msg, WGPEER_A_FLAGS, WGPEER_F_REPLACE_ALLOWEDIPS); + + ips = nla_nest_start(msg, WGPEER_A_ALLOWEDIPS); + + wg_linux_peer_msg_add_allowed_ip(msg, peer); + for_each_routed_host(host, net, peer) + wg_linux_peer_msg_add_allowed_ip(msg, &host->peer); + nla_nest_end(msg, ips); out: diff --git a/wg-user.c b/wg-user.c index 3a5004a..a057a10 100644 --- a/wg-user.c +++ b/wg-user.c @@ -279,30 +279,15 @@ wg_user_init_local(struct network *net, struct network_peer *peer) return wg_req_done(&req); } -static int -wg_user_peer_update(struct network *net, struct network_peer *peer, enum wg_update_cmd cmd) +static void +wg_user_peer_req_add_allowed_ip(struct wg_req *req, struct network_peer *peer) { - struct blob_attr *cur; - struct wg_req req; char addr[INET6_ADDRSTRLEN]; - char key[WG_KEY_LEN_HEX]; + struct blob_attr *cur; int rem; - if (wg_req_init(&req, net, true)) - return -1; - - key_to_hex(key, peer->key); - wg_req_set(&req, "public_key", key); - - if (cmd == WG_PEER_DELETE) { - wg_req_set(&req, "remove", "true"); - goto out; - } - - wg_req_set(&req, "replace_allowed_ips", "true"); - inet_ntop(AF_INET6, &peer->local_addr.in6, addr, sizeof(addr)); - wg_req_printf(&req, "allowed_ip", "%s/128", addr); + wg_req_printf(req, "allowed_ip", "%s/128", addr); blobmsg_for_each_attr(cur, peer->ipaddr, rem) { const char *str = blobmsg_get_string(cur); @@ -320,7 +305,7 @@ wg_user_peer_update(struct network *net, struct network_peer *peer, enum wg_upda if (inet_pton(af, str, &in6) != 1) continue; - wg_req_printf(&req, "allowed_ip", "%s/%d", str, mask); + wg_req_printf(req, "allowed_ip", "%s/%d", str, mask); } blobmsg_for_each_attr(cur, peer->subnet, rem) { @@ -335,8 +320,32 @@ wg_user_peer_update(struct network *net, struct network_peer *peer, enum wg_upda continue; inet_ntop(af, &addr, buf, sizeof(buf)); - wg_req_printf(&req, "allowed_ip", "%s/%d", buf, mask); + wg_req_printf(req, "allowed_ip", "%s/%d", buf, mask); } +} + +static int +wg_user_peer_update(struct network *net, struct network_peer *peer, enum wg_update_cmd cmd) +{ + struct network_host *host; + struct wg_req req; + char key[WG_KEY_LEN_HEX]; + + if (wg_req_init(&req, net, true)) + return -1; + + key_to_hex(key, peer->key); + wg_req_set(&req, "public_key", key); + + if (cmd == WG_PEER_DELETE) { + wg_req_set(&req, "remove", "true"); + goto out; + } + + wg_req_set(&req, "replace_allowed_ips", "true"); + wg_user_peer_req_add_allowed_ip(&req, peer); + for_each_routed_host(host, net, peer) + wg_user_peer_req_add_allowed_ip(&req, &host->peer); out: return wg_req_done(&req);