]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
daemon: support Linux eBPF socket filters with new net.bpf_set(fd) and net.bpf_clear...
authorAlex Forster <aforster@cloudflare.com>
Wed, 5 Dec 2018 23:01:33 +0000 (17:01 -0600)
committerPetr Špaček <petr.spacek@nic.cz>
Thu, 13 Dec 2018 11:26:38 +0000 (11:26 +0000)
daemon/bindings.c
daemon/network.c
daemon/network.h

index 1dbac917e9913b878b2916f99a95c0df6baa75d9..271c52882751ea2de4dbff09a068851fd5587647 100644 (file)
@@ -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);
index bae3fa8407bdb5e83c2d91433cd9fa6eaa345cc6..17cc92250388ad72f81fab895a629c6dfe3b46b4 100644 (file)
@@ -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));
+}
index 07d84adcae53a4eb7c7d04f975fa6368f614a326..1554f44b9d3798f5faf637815974906c022b7d74 100644 (file)
@@ -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);