From: Alex Forster Date: Wed, 5 Dec 2018 23:01:33 +0000 (-0600) Subject: daemon: support Linux eBPF socket filters with new net.bpf_set(fd) and net.bpf_clear... X-Git-Tag: v3.2.0~9^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=622a61e9176889cee7c6a3ffb7fb505d114ce921;p=thirdparty%2Fknot-resolver.git daemon: support Linux eBPF socket filters with new net.bpf_set(fd) and net.bpf_clear() bindings --- diff --git a/daemon/bindings.c b/daemon/bindings.c index 1dbac917e..271c52882 100644 --- a/daemon/bindings.c +++ b/daemon/bindings.c @@ -880,6 +880,67 @@ static int net_tls_handshake_timeout(lua_State *L) return net_update_timeout(L, &net->tcp.tls_handshake_timeout, "net.tls_handshake_timeout"); } +static int net_bpf_set(lua_State *L) +{ + int ret; + struct engine *engine = engine_luaget(L); + struct network *net = &engine->net; + + if (lua_gettop(L) != 1 || lua_type(L, 1) != LUA_TINTEGER) { + format_error(L, "net.bpf_set(fd) takes one parameter: the open file descriptor of a loaded BPF program"); + lua_error(L); + return 0; + } + +#if __linux__ + + int progfd = lua_tointeger(L, 1); + lua_pop(L, 1); + + if (!network_set_bpf(net, progfd)) { + char errmsg[256] = {}; + snprintf(errmsg, sizeof(errmsg), "failed to attach BPF program to some networks: %s", strerror(errno)); + format_error(L, errmsg); + lua_error(L); + return 0; + } + + lua_pushboolean(L, 1); + return 1; + +#endif + + format_error(L, "BPF is not supported on this operating system"); + lua_error(L); + return 0; +} + +static int net_bpf_clear(lua_State *L) +{ + int ret; + struct engine *engine = engine_luaget(L); + struct network *net = &engine->net; + + if (lua_gettop(L) != 0) { + format_error(L, "net.bpf_clear() does not take any parameters"); + lua_error(L); + return 0; + } + +#if __linux__ + + network_clear_bpf(net); + + lua_pushboolean(L, 1); + return 1; + +#endif + + format_error(L, "BPF is not supported on this operating system"); + lua_error(L); + return 0; +} + int lib_net(lua_State *L) { static const luaL_Reg lib[] = { @@ -900,6 +961,8 @@ int lib_net(lua_State *L) { "outgoing_v6", net_outgoing_v6 }, { "tcp_in_idle", net_tcp_in_idle }, { "tls_handshake_timeout", net_tls_handshake_timeout }, + { "bpf_set", net_bpf_set }, + { "bpf_clear", net_bpf_clear }, { NULL, NULL } }; register_lib(L, "net", lib); diff --git a/daemon/network.c b/daemon/network.c index bae3fa840..17cc92250 100644 --- a/daemon/network.c +++ b/daemon/network.c @@ -363,3 +363,58 @@ void network_new_hostname(struct network *net, struct engine *engine) } } } + +static int set_bpf_cb(const char *key, void *val, void *ext) +{ + endpoint_array_t *endpoints = (endpoint_array_t *)val; + assert(endpoints != NULL); + int *bpffd = (int *)ext; + assert(bpffd != NULL); + + for (size_t i = 0; i < endpoints->len; i++) { + struct endpoint *endpoint = (struct endpoint *)endpoints->at[i]; + uv_os_fd_t sockfd = -1; + if (endpoint->tcp != NULL) uv_fileno((const uv_handle_t *)endpoint->tcp, &sockfd); + if (endpoint->udp != NULL) uv_fileno((const uv_handle_t *)endpoint->udp, &sockfd); + assert(sockfd != -1); + + if (setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_BPF, bpffd, sizeof(int)) != 0) { + return 0; + } + } + + return 1; +} + +bool network_set_bpf(struct network *net, int bpf_fd) +{ + if (!map_walk(&net->endpoints, set_bpf_cb, &bpf_fd)) { + network_clear_bpf(net); + return 0; + } + + return 1; +} + +static int clear_bpf_cb(const char *key, void *val, void *ext) +{ + endpoint_array_t *endpoints = (endpoint_array_t *)val; + assert(endpoints != NULL); + + for (size_t i = 0; i < endpoints->len; i++) { + struct endpoint *endpoint = (struct endpoint *)endpoints->at[i]; + uv_os_fd_t sockfd = -1; + if (endpoint->tcp != NULL) uv_fileno((const uv_handle_t *)endpoint->tcp, &sockfd); + if (endpoint->udp != NULL) uv_fileno((const uv_handle_t *)endpoint->udp, &sockfd); + assert(sockfd != -1); + + setsockopt(sockfd, SOL_SOCKET, SO_DETACH_BPF, NULL, 0); + } + + return 1; +} + +void network_clear_bpf(struct network *net) +{ + assert(map_walk(&net->endpoints, clear_bpf_cb, NULL)); +} diff --git a/daemon/network.h b/daemon/network.h index 07d84adca..1554f44b9 100644 --- a/daemon/network.h +++ b/daemon/network.h @@ -66,3 +66,5 @@ int network_close(struct network *net, const char *addr, uint16_t port); int network_set_tls_cert(struct network *net, const char *cert); int network_set_tls_key(struct network *net, const char *key); void network_new_hostname(struct network *net, struct engine *engine); +bool network_set_bpf(struct network *net, int bpf_fd); +void network_clear_bpf(struct network *net);