NETWORK_HOST_SUBNET,
NETWORK_HOST_PORT,
NETWORK_HOST_ENDPOINT,
+ NETWORK_HOST_GATEWAY,
__NETWORK_HOST_MAX
};
static const struct blobmsg_policy policy[__NETWORK_HOST_MAX] = {
[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;
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));
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;
&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);
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);
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);
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)
struct network_host {
struct avl_node node;
+ const char *gateway;
struct network_peer peer;
};
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;
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);
" ipaddr=[+|-]<val>[,<val>...] set/add/remove host ip addresses\n",
" subnet=[+|-]<val>[,<val>...] set/add/remove host announced subnets\n",
" endpoint=<val> set host endpoint address\n",
+ " gateway=<name> set host gateway (using name of other host)\n",
" ssh host options (add-ssh-host, set-ssh-host)\n",
" auth_key=<key> use <key> as public auth key on the remote host\n",
" priv_key=<key> use <key> as private host key on the remote host (default: generate a new key)\n",
set_fields(host, {
key: "string",
endpoint: "string",
+ gateway: "string",
port: "int",
ipaddr: "array",
subnet: "array",
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) {
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:
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);
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) {
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);