From 8ad119715168c730e1800c3b13f6d762530c9beb Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 31 Aug 2022 20:37:05 +0200 Subject: [PATCH] ubus: add support for adding auth_connect hosts at runtime These hosts always need to have a timeout value. After the timeout, they are automatically deleted. Other than that, they work just like regular configured auth_host entries Signed-off-by: Felix Fietkau --- network.c | 1 + pex.c | 43 +++++++++++++++++++++++++++++++++++++---- pex.h | 4 ++++ ubus.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ utils.c | 12 ++++++++++++ utils.h | 2 ++ 6 files changed, 115 insertions(+), 4 deletions(-) diff --git a/network.c b/network.c index b39deb4..beb0230 100644 --- a/network.c +++ b/network.c @@ -445,6 +445,7 @@ static void network_teardown(struct network *net) uloop_timeout_cancel(&net->reload_timer); network_do_update(net, false); network_pex_close(net); + network_pex_free(net); network_hosts_free(net); network_services_free(net); wg_cleanup_network(net); diff --git a/pex.c b/pex.c index 839567d..5234e10 100644 --- a/pex.c +++ b/pex.c @@ -261,10 +261,17 @@ network_pex_request_update_cb(struct uloop_timeout *t) uloop_timeout_set(t, 5000); +retry: if (list_empty(&pex->hosts)) return; host = list_first_entry(&pex->hosts, struct network_pex_host, list); + if (host->timeout && host->timeout < unet_gettime()) { + list_del(&host->list); + free(host); + goto retry; + } + list_move_tail(&host->list, &pex->hosts); network_pex_host_request_update(net, host); } @@ -667,15 +674,29 @@ network_pex_fd_cb(struct uloop_fd *fd, unsigned int events) } } -static void -network_pex_create_host(struct network *net, union network_endpoint *ep) +void network_pex_create_host(struct network *net, union network_endpoint *ep, + unsigned int timeout) { struct network_pex *pex = &net->pex; struct network_pex_host *host; + bool new_host = false; + + list_for_each_entry(host, &pex->hosts, list) { + if (memcmp(&host->endpoint, ep, sizeof(host->endpoint)) != 0) + continue; + + list_move_tail(&host->list, &pex->hosts); + goto out; + } host = calloc(1, sizeof(*host)); + new_host = true; memcpy(&host->endpoint, ep, sizeof(host->endpoint)); list_add_tail(&host->list, &pex->hosts); + +out: + if (timeout && (new_host || host->timeout)) + host->timeout = timeout + unet_gettime(); network_pex_host_request_update(net, host); } @@ -703,7 +724,7 @@ network_pex_open_auth_connect(struct network *net) continue; ep.in.sin_port = htons(UNETD_GLOBAL_PEX_PORT); - network_pex_create_host(net, &ep); + network_pex_create_host(net, &ep, 0); } if (!net->config.auth_connect) @@ -716,7 +737,7 @@ network_pex_open_auth_connect(struct network *net) UNETD_GLOBAL_PEX_PORT, 0) < 0) continue; - network_pex_create_host(net, &ep); + network_pex_create_host(net, &ep, 0); } } @@ -778,6 +799,9 @@ void network_pex_close(struct network *net) uloop_timeout_cancel(&pex->request_update_timer); list_for_each_entry_safe(host, tmp, &pex->hosts, list) { + if (host->timeout) + continue; + list_del(&host->list); free(host); } @@ -790,6 +814,17 @@ void network_pex_close(struct network *net) network_pex_init(net); } +void network_pex_free(struct network *net) +{ + struct network_pex *pex = &net->pex; + struct network_pex_host *host, *tmp; + + list_for_each_entry_safe(host, tmp, &pex->hosts, list) { + list_del(&host->list); + free(host); + } +} + static struct network * global_pex_find_network(const uint8_t *id) { diff --git a/pex.h b/pex.h index 11f783f..123b4a9 100644 --- a/pex.h +++ b/pex.h @@ -11,6 +11,7 @@ struct network; struct network_pex_host { struct list_head list; + uint64_t timeout; union network_endpoint endpoint; }; @@ -30,9 +31,12 @@ enum pex_event { void network_pex_init(struct network *net); int network_pex_open(struct network *net); void network_pex_close(struct network *net); +void network_pex_free(struct network *net); void network_pex_event(struct network *net, struct network_peer *peer, enum pex_event ev); +void network_pex_create_host(struct network *net, union network_endpoint *ep, + unsigned int timeout); static inline bool network_pex_active(struct network_pex *pex) { diff --git a/ubus.c b/ubus.c index 6d3f9c9..323fe44 100644 --- a/ubus.c +++ b/ubus.c @@ -195,12 +195,69 @@ ubus_service_get(struct ubus_context *ctx, struct ubus_object *obj, return 0; } +enum { + CONNECT_ATTR_NAME, + CONNECT_ATTR_ADDRESS, + CONNECT_ATTR_TIMEOUT, + __CONNECT_ATTR_MAX +}; + +static const struct blobmsg_policy connect_policy[__CONNECT_ATTR_MAX] = { + [CONNECT_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING }, + [CONNECT_ATTR_ADDRESS] = { "address", BLOBMSG_TYPE_STRING }, + [CONNECT_ATTR_TIMEOUT] = { "timeout", BLOBMSG_TYPE_INT32 }, +}; + +static int +ubus_network_connect(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__CONNECT_ATTR_MAX]; + union network_endpoint ep = {}; + struct blob_attr *cur; + struct network *net; + unsigned int timeout; + const char *name; + + blobmsg_parse(connect_policy, __CONNECT_ATTR_MAX, tb, + blobmsg_data(msg), blobmsg_len(msg)); + + if ((cur = tb[CONNECT_ATTR_NAME]) != NULL) + name = blobmsg_get_string(cur); + else + return UBUS_STATUS_INVALID_ARGUMENT; + + if ((cur = tb[CONNECT_ATTR_TIMEOUT]) != NULL) + timeout = blobmsg_get_u32(cur); + else + return UBUS_STATUS_INVALID_ARGUMENT; + + if ((cur = tb[CONNECT_ATTR_ADDRESS]) == NULL || + network_get_endpoint(&ep, blobmsg_get_string(cur), UNETD_GLOBAL_PEX_PORT, 0) < 0 || + !ep.in.sin_port) + return UBUS_STATUS_INVALID_ARGUMENT; + + net = avl_find_element(&networks, name, net, node); + if (!net) + return UBUS_STATUS_NOT_FOUND; + + if (net->config.type != NETWORK_TYPE_DYNAMIC) + return UBUS_STATUS_INVALID_ARGUMENT; + + network_pex_create_host(net, &ep, timeout); + + return 0; +} + + static const struct ubus_method unetd_methods[] = { UBUS_METHOD("network_add", ubus_network_add, network_policy), UBUS_METHOD_MASK("network_del", ubus_network_del, network_policy, (1 << NETWORK_ATTR_NAME)), UBUS_METHOD_MASK("network_get", ubus_network_get, network_policy, (1 << NETWORK_ATTR_NAME)), + UBUS_METHOD("network_connect", ubus_network_connect, connect_policy), UBUS_METHOD("service_get", ubus_service_get, service_policy), }; diff --git a/utils.c b/utils.c index 95ab08b..abaa73d 100644 --- a/utils.c +++ b/utils.c @@ -8,6 +8,9 @@ #include #include #include + +#include + #include "unetd.h" int network_get_endpoint(union network_endpoint *dest, const char *str, @@ -179,3 +182,12 @@ error: *len = 0; return NULL; } + +uint64_t unet_gettime(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + return ts.tv_sec; +} diff --git a/utils.h b/utils.h index 94cf057..d79d96c 100644 --- a/utils.h +++ b/utils.h @@ -123,4 +123,6 @@ static inline uint64_t get_unaligned_le64(const uint8_t *p) int rtnl_init(void); int rtnl_call(struct nl_msg *msg); +uint64_t unet_gettime(void); + #endif -- 2.30.2