#include "daemon/network.h"
+#include "contrib/cleanup.h"
#include "daemon/bindings/impl.h"
#include "daemon/io.h"
#include "daemon/tls.h"
#include "daemon/worker.h"
+#include "lib/utils.h"
#if ENABLE_XDP
#include <libknot/xdp/eth.h>
#include <sys/un.h>
#include <unistd.h>
+/** Determines the type of `struct endpoint_key`. */
+enum endpoint_key_type
+{
+ ENDPOINT_KEY_SOCKADDR = 1,
+ ENDPOINT_KEY_IFNAME = 2,
+};
+
+/** Used as a key in the `struct network::endpoints` trie. */
+struct endpoint_key {
+ enum endpoint_key_type type;
+ char data[];
+};
+
+struct __attribute__((packed)) endpoint_key_sockaddr {
+ enum endpoint_key_type type;
+ struct kr_sockaddr_key_storage sa_key;
+};
+
+struct __attribute__((packed)) endpoint_key_ifname {
+ enum endpoint_key_type type;
+ char ifname[128];
+};
+
+/** Used for reserving enough storage for `endpoint_key`. */
+struct endpoint_key_storage {
+ union {
+ struct endpoint_key_sockaddr sa;
+ struct endpoint_key_ifname ifname;
+ const char bytes[1]; /* for easier casting */
+ };
+};
+
+static_assert(_Alignof(struct endpoint_key) <= 4, "endpoint_key must be aligned to <=4");
+static_assert(_Alignof(struct endpoint_key_sockaddr) <= 4, "endpoint_key must be aligned to <=4");
+static_assert(_Alignof(struct endpoint_key_ifname) <= 4, "endpoint_key must be aligned to <=4");
+
void network_init(struct network *net, uv_loop_t *loop, int tcp_backlog)
{
if (net != NULL) {
net->loop = loop;
- net->endpoints = map_make(NULL);
+ net->endpoints = trie_create(NULL);
net->endpoint_kinds = trie_create(NULL);
net->proxy_all4 = false;
net->proxy_all6 = false;
return kr_ok();
}
-static int engage_endpoint_array(const char *key, void *endpoints, void *net)
+static int engage_endpoint_array(const char *b_key, uint32_t key_len, trie_val_t *val, void *net)
{
- endpoint_array_t *eps = (endpoint_array_t *)endpoints;
+ const char *log_addr = network_endpoint_key_str((struct endpoint_key *) b_key);
+ if (!log_addr)
+ log_addr = "[unknown]";
+
+ endpoint_array_t *eps = *val;
for (int i = 0; i < eps->len; ++i) {
struct endpoint *ep = &eps->at[i];
const bool match = !ep->engaged && ep->flags.kind;
if (!match) continue;
- int ret = endpoint_open_lua_cb(net, ep, key);
+ int ret = endpoint_open_lua_cb(net, ep, log_addr);
if (ret) return ret;
}
return 0;
}
+
int network_engage_endpoints(struct network *net)
{
if (net->missing_kind_is_error)
return kr_ok(); /* maybe weird, but let's make it idempotent */
net->missing_kind_is_error = true;
- int ret = map_walk(&net->endpoints, engage_endpoint_array, net);
+ int ret = trie_apply_with_key(net->endpoints, engage_endpoint_array, net);
if (ret) {
net->missing_kind_is_error = false; /* avoid the same errors when closing */
return ret;
return kr_ok();
}
+const char *network_endpoint_key_str(const struct endpoint_key *key)
+{
+ switch (key->type)
+ {
+ case ENDPOINT_KEY_SOCKADDR:;
+ const struct endpoint_key_sockaddr *sa_key =
+ (struct endpoint_key_sockaddr *) key;
+ struct sockaddr_storage sa_storage;
+ struct sockaddr *sa = kr_sockaddr_from_key(&sa_storage, (const char *) &sa_key->sa_key);
+ return kr_straddr(sa);
+ case ENDPOINT_KEY_IFNAME:;
+ const struct endpoint_key_ifname *if_key =
+ (struct endpoint_key_ifname *) key;
+ return if_key->ifname;
+ default:
+ kr_assert(false);
+ return NULL;
+ }
+}
/** Notify the registered function about endpoint about to be closed. */
static void endpoint_close_lua_cb(struct network *net, struct endpoint *ep)
}
/** Endpoint visitor (see @file map.h) */
-static int close_key(const char *key, void *val, void *net)
+static int close_key(trie_val_t *val, void* net)
{
- endpoint_array_t *ep_array = val;
+ endpoint_array_t *ep_array = *val;
for (int i = 0; i < ep_array->len; ++i) {
endpoint_close(net, &ep_array->at[i], true);
}
return 0;
}
-static int free_key(const char *key, void *val, void *ext)
+static int free_key(trie_val_t *val, void* ext)
{
- endpoint_array_t *ep_array = val;
+ endpoint_array_t *ep_array = *val;
array_clear(*ep_array);
free(ep_array);
return kr_ok();
void network_close_force(struct network *net)
{
if (net != NULL) {
- map_walk(&net->endpoints, close_key, net);
- map_walk(&net->endpoints, free_key, 0);
- map_clear(&net->endpoints);
+ trie_apply(net->endpoints, close_key, net);
+ trie_apply(net->endpoints, free_key, NULL);
+ trie_clear(net->endpoints);
}
}
network_close_force(net);
trie_apply(net->endpoint_kinds, kind_unregister, the_worker->engine->L);
trie_free(net->endpoint_kinds);
+ trie_free(net->endpoints);
network_proxy_free_addr_data(net->proxy_addrs4);
trie_free(net->proxy_addrs4);
network_proxy_free_addr_data(net->proxy_addrs6);
}
}
+static ssize_t endpoint_key_create(struct endpoint_key_storage *dst,
+ const char *addr_str,
+ const struct sockaddr *sa)
+{
+ memset(dst, 0, sizeof(*dst));
+ if (sa) {
+ struct endpoint_key_sockaddr *key = &dst->sa;
+ key->type = ENDPOINT_KEY_SOCKADDR;
+ ssize_t keylen = kr_sockaddr_key(&key->sa_key, sa);
+ if (keylen < 0)
+ return keylen;
+ return sizeof(struct endpoint_key) + keylen;
+ } else {
+ struct endpoint_key_ifname *key = &dst->ifname;
+ key->type = ENDPOINT_KEY_IFNAME;
+ strncpy(key->ifname, addr_str, sizeof(key->ifname) - 1);
+ return sizeof(struct endpoint_key) + strnlen(key->ifname, sizeof(key->ifname));
+ }
+}
+
/** Fetch or create endpoint array and insert endpoint (shallow memcpy). */
-static int insert_endpoint(struct network *net, const char *addr, struct endpoint *ep)
+static int insert_endpoint(struct network *net, const char *addr_str,
+ const struct sockaddr *addr, struct endpoint *ep)
{
/* Fetch or insert address into map */
- endpoint_array_t *ep_array = map_get(&net->endpoints, addr);
- if (ep_array == NULL) {
+ struct endpoint_key_storage key;
+ ssize_t keylen = endpoint_key_create(&key, addr_str, addr);
+ if (keylen < 0)
+ return keylen;
+ trie_val_t *val = trie_get_ins(net->endpoints, key.bytes, keylen);
+ endpoint_array_t *ep_array;
+ if (*val) {
+ ep_array = *val;
+ } else {
ep_array = malloc(sizeof(*ep_array));
- if (ep_array == NULL) {
- return kr_error(ENOMEM);
- }
- if (map_set(&net->endpoints, addr, ep_array) != 0) {
- free(ep_array);
- return kr_error(ENOMEM);
- }
+ kr_require(ep_array);
array_init(*ep_array);
+ *val = ep_array;
}
if (array_reserve(*ep_array, ep_array->len + 1)) {
{
const bool is_control = ep->flags.kind && strcmp(ep->flags.kind, "control") == 0;
const bool is_xdp = ep->family == AF_XDP;
- bool ok = is_xdp
- ? sa == NULL && ep->fd == -1 && ep->nic_queue >= 0
- && ep->flags.sock_type == SOCK_DGRAM && !ep->flags.tls
- : (sa != NULL) != (ep->fd != -1);
+ bool ok = (!is_xdp)
+ || (sa == NULL && ep->fd == -1 && ep->nic_queue >= 0
+ && ep->flags.sock_type == SOCK_DGRAM && !ep->flags.tls);
if (kr_fails_assert(ok))
return kr_error(EINVAL);
if (ep->handle) {
return kr_error(EEXIST);
}
- if (sa) {
+ if (sa && ep->fd == -1) {
if (sa->sa_family == AF_UNIX) {
struct sockaddr_un *sun = (struct sockaddr_un*)sa;
char *dirc = strdup(sun->sun_path);
* Beware that there might be multiple matches, though that's not common.
* The matching isn't really precise in the sense that it might not find
* and endpoint that would *collide* the passed one. */
-static struct endpoint * endpoint_get(struct network *net, const char *addr,
- uint16_t port, endpoint_flags_t flags)
+static struct endpoint * endpoint_get(struct network *net,
+ const char *addr_str,
+ const struct sockaddr *sa,
+ endpoint_flags_t flags)
{
- endpoint_array_t *ep_array = map_get(&net->endpoints, addr);
- if (!ep_array) {
+ struct endpoint_key_storage key;
+ ssize_t keylen = endpoint_key_create(&key, addr_str, sa);
+ if (keylen < 0)
return NULL;
- }
+ trie_val_t *val = trie_get_try(net->endpoints, key.bytes, keylen);
+ if (!val)
+ return NULL;
+ endpoint_array_t *ep_array = *val;
+
+ uint16_t port = kr_inaddr_port(sa);
for (int i = 0; i < ep_array->len; ++i) {
struct endpoint *ep = &ep_array->at[i];
- if (ep->port == port && endpoint_flags_eq(ep->flags, flags)) {
+ if ((flags.xdp || ep->port == port) && endpoint_flags_eq(ep->flags, flags)) {
return ep;
}
}
* \note in XDP case addr_str is interface name
* \note ownership of ep.flags.* is taken on success. */
static int create_endpoint(struct network *net, const char *addr_str,
- struct endpoint *ep, const struct sockaddr *sa)
+ struct endpoint *ep, const struct sockaddr *sa)
{
int ret = open_endpoint(net, addr_str, ep, sa);
if (ret == 0) {
- ret = insert_endpoint(net, addr_str, ep);
+ ret = insert_endpoint(net, addr_str, sa, ep);
}
if (ret != 0 && ep->handle) {
endpoint_close(net, ep, false);
/* always create endpoint for supervisor supplied fd
* even if addr+port is not unique */
- return create_endpoint(net, addr_str, &ep, NULL);
+ return create_endpoint(net, addr_str, &ep, (struct sockaddr *) &ss);
}
/** Try selecting XDP queue automatically. */
}
// XDP: if addr failed to parse as address, we assume it's an interface name.
- if (endpoint_get(net, addr, port, flags)) {
+ if (endpoint_get(net, addr, sa, flags)) {
return kr_error(EADDRINUSE); // Already listening
}
trie_clear(net->proxy_addrs6);
}
-int network_close(struct network *net, const char *addr, int port)
+int network_close(struct network *net, const char *addr_str, int port)
{
- endpoint_array_t *ep_array = map_get(&net->endpoints, addr);
- if (!ep_array) {
+ auto_free struct sockaddr *addr = kr_straddr_socket(addr_str, port, NULL);
+ struct endpoint_key_storage key;
+ ssize_t keylen = endpoint_key_create(&key, addr_str, addr);
+ if (keylen < 0)
+ return keylen;
+ trie_val_t *val = trie_get_try(net->endpoints, key.bytes, keylen);
+ if (!val)
return kr_error(ENOENT);
- }
+ endpoint_array_t *ep_array = *val;
size_t i = 0;
bool matched = false; /*< at least one match */
if (ep_array->len == 0) {
array_clear(*ep_array);
free(ep_array);
- map_del(&net->endpoints, addr);
+ trie_del(net->endpoints, key.bytes, keylen, NULL);
}
return kr_ok();
}
#ifdef SO_ATTACH_BPF
-static int set_bpf_cb(const char *key, void *val, void *ext)
+static int set_bpf_cb(trie_val_t *val, void *ctx)
{
- endpoint_array_t *endpoints = (endpoint_array_t *)val;
- int *bpffd = (int *)ext;
+ endpoint_array_t *endpoints = *val;
+ int *bpffd = (int *)ctx;
if (kr_fails_assert(endpoints && bpffd))
return kr_error(EINVAL);
int network_set_bpf(struct network *net, int bpf_fd)
{
#ifdef SO_ATTACH_BPF
- if (map_walk(&net->endpoints, set_bpf_cb, &bpf_fd) != 0) {
+ if (trie_apply(net->endpoints, set_bpf_cb, &bpf_fd) != 0) {
/* set_bpf_cb() has returned error. */
network_clear_bpf(net);
return 0;
}
#ifdef SO_DETACH_BPF
-static int clear_bpf_cb(const char *key, void *val, void *ext)
+static int clear_bpf_cb(trie_val_t *val, void *ctx)
{
- endpoint_array_t *endpoints = (endpoint_array_t *)val;
+ endpoint_array_t *endpoints = *val;
if (kr_fails_assert(endpoints))
return kr_error(EINVAL);
void network_clear_bpf(struct network *net)
{
#ifdef SO_DETACH_BPF
- map_walk(&net->endpoints, clear_bpf_cb, NULL);
+ trie_apply(net->endpoints, clear_bpf_cb, NULL);
#else
kr_log_error(NETWORK, "SO_DETACH_BPF socket option doesn't supported\n");
(void)net;