diff --git a/daemon/Makefile b/daemon/Makefile index 9fd29a24e..53bbbf3ab 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -27,8 +27,6 @@ CFLAGS+= $(CFLAGS_LIBPCRE) CFLAGS+= $(CFLAGS_LIBCURL) CFLAGS+= $(CFLAGS_JSON_GLIB) CFLAGS+= $(CFLAGS_LIBWEBSOCKETS) -CFLAGS+= $(CFLAGS_LIBNFTNL) -CFLAGS+= $(CFLAGS_LIBMNL) ifeq ($(with_iptables_option),yes) CFLAGS+= $(CFLAGS_LIBIPTC) CFLAGS+= -DWITH_IPTABLES_OPTION @@ -67,8 +65,6 @@ LDLIBS+= $(LDLIBS_LIBPCAP) LDLIBS+= $(LDLIBS_LIBHIREDIS) LDLIBS+= $(LDLIBS_JSON_GLIB) LDLIBS+= $(LDLIBS_LIBWEBSOCKETS) -LDLIBS+= $(LDLIBS_LIBNFTNL) -LDLIBS+= $(LDLIBS_LIBMNL) ifeq ($(with_iptables_option),yes) LDLIBS+= $(LDLIBS_LIBIPTC) endif @@ -104,7 +100,7 @@ ifneq ($(without_nftables),yes) SRCS+= nftables.c endif LIBSRCS= loglib.c auxlib.c rtplib.c str.c socket.c streambuf.c ssllib.c dtmflib.c mix_buffer.c poller.c \ - bufferpool.c bencode.c + bufferpool.c bencode.c netfilter_api.c ifeq ($(with_transcoding),yes) LIBSRCS+= codeclib.strhash.c resample.c LIBASM= mvr2s_x64_avx2.S mvr2s_x64_avx512.S mix_in_x64_avx2.S mix_in_x64_avx512bw.S mix_in_x64_sse2.S diff --git a/daemon/nftables.c b/daemon/nftables.c index 3dca2c2c1..53135d44b 100644 --- a/daemon/nftables.c +++ b/daemon/nftables.c @@ -8,48 +8,48 @@ #include #include +#include #include +#include +#include #include -#include -#include -#include -#include -#include - +#include "netfilter_api.h" #include "helpers.h" #include "xt_RTPENGINE.h" struct iterate_callbacks { // called for each expression - int (*parse_expr)(struct nftnl_expr *e, void *data); + const char *(*parse_expr)(const char *name, const int8_t *data, size_t len, void *userdata); // called after all expressions have been parsed - void (*rule_final)(struct nftnl_rule *r, struct iterate_callbacks *); + void (*rule_final)(struct iterate_callbacks *); // called after all rules have been iterated - const char *(*iterate_final)(struct mnl_socket *nl, int family, const char *chain, - uint32_t *seq, struct iterate_callbacks *); + const char *(*iterate_final)(nfapi_socket *nl, int family, const char *chain, + struct iterate_callbacks *); // common arguments const char *chain; const char *base_chain; // scratch area for rule callbacks, set to zero for every rule - union { + struct { bool rule_matched; + bool have_handle; + int64_t handle; } rule_scratch; // scratch area for rule iterating - union { + struct { GQueue handles; bool rule_matched; } iterate_scratch; }; struct add_rule_callbacks { - const char *(*callback)(struct nftnl_rule *, int family, struct add_rule_callbacks *); + const char *(*rule_callback)(nfapi_buf *, int family, struct add_rule_callbacks *); const char *chain; const char *base_chain; int table; @@ -58,148 +58,98 @@ struct add_rule_callbacks { -typedef struct nftnl_expr _nftnl_expr; -typedef struct nftnl_rule _nftnl_rule; -typedef struct nftnl_chain _nftnl_chain; -typedef struct nftnl_table _nftnl_table; -typedef struct mnl_socket _mnl_socket; - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(_nftnl_expr, nftnl_expr_free); -G_DEFINE_AUTOPTR_CLEANUP_FUNC(_nftnl_rule, nftnl_rule_free); -G_DEFINE_AUTOPTR_CLEANUP_FUNC(_nftnl_chain, nftnl_chain_free); -G_DEFINE_AUTOPTR_CLEANUP_FUNC(_nftnl_table, nftnl_table_free); -G_DEFINE_AUTOPTR_CLEANUP_FUNC(_mnl_socket, mnl_socket_close); - +static const char *match_immediate(const char *name, const int8_t *data, size_t len, void *userdata) { + struct iterate_callbacks *callbacks = userdata; - -static int match_immediate(struct nftnl_expr *e, void *data) { - struct iterate_callbacks *callbacks = data; - - uint32_t len; - const char *n = nftnl_expr_get(e, NFTNL_EXPR_NAME, &len); // match jumps to our configured chain - if (!strcmp(n, "immediate")) { - n = nftnl_expr_get(e, NFTNL_EXPR_IMM_CHAIN, &len); - if (n && !strcmp(n, callbacks->chain)) + if (!strcmp(name, "immediate")) { + const char *chain = nfapi_get_immediate_chain(data, len); + if (chain && !strcmp(chain, callbacks->chain)) callbacks->rule_scratch.rule_matched = true; } - return 0; + return NULL; } -static int match_rtpe(struct nftnl_expr *e, void *data) { - struct iterate_callbacks *callbacks = data; +static const char *match_rtpe(const char *name, const int8_t *data, size_t len, void *userdata) { + struct iterate_callbacks *callbacks = userdata; - uint32_t len; - const char *n = nftnl_expr_get(e, NFTNL_EXPR_NAME, &len); // match top-level targets - if (!strcmp(n, "target")) { - n = nftnl_expr_get(e, NFTNL_EXPR_TG_NAME, &len); + if (!strcmp(name, "target")) { + const char *n = nfapi_get_target(data, len, NULL, NULL); if (n && !strcmp(n, "RTPENGINE")) callbacks->rule_scratch.rule_matched = true; } - return 0; + return NULL; } -static int match_immediate_rtpe(struct nftnl_expr *e, void *data) { - match_immediate(e, data); - match_rtpe(e, data); - return 0; +static const char *match_immediate_rtpe(const char *name, const int8_t *data, size_t len, void *userdata) { + const char *err = match_immediate(name, data, len, userdata); + if (err) + return err; + return match_rtpe(name, data, len, userdata); } -static void check_matched_queue(struct nftnl_rule *r, struct iterate_callbacks *callbacks) { +static void check_matched_queue(struct iterate_callbacks *callbacks) { if (!callbacks->rule_scratch.rule_matched) return; - uint64_t handle = nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE); + uint64_t handle = callbacks->rule_scratch.handle; g_queue_push_tail(&callbacks->iterate_scratch.handles, __g_memdup(&handle, sizeof(handle))); } -static void check_matched_flag(struct nftnl_rule *r, struct iterate_callbacks *callbacks) { +static void check_matched_flag(struct iterate_callbacks *callbacks) { if (callbacks->rule_scratch.rule_matched) callbacks->iterate_scratch.rule_matched = true; } -static int nftables_do_rule(const struct nlmsghdr *nlh, void *data) { - struct iterate_callbacks *callbacks = data; - g_autoptr(_nftnl_rule) r = nftnl_rule_alloc(); - if (!r) - return MNL_CB_ERROR; +static void set_handle(int64_t handle, void *data) { + struct iterate_callbacks *callbacks = data; + callbacks->rule_scratch.handle = handle; + callbacks->rule_scratch.have_handle = true; +} - if (nftnl_rule_nlmsg_parse(nlh, r) < 0) - return MNL_CB_OK; +static const char *nftables_do_rule(const int8_t *b, size_t l, void *data) { + struct iterate_callbacks *callbacks = data; memset(&callbacks->rule_scratch, 0, sizeof(callbacks->rule_scratch)); - if (nftnl_expr_foreach(r, callbacks->parse_expr, callbacks) < 0) - return MNL_CB_OK; + const char *err = nfapi_rule_iter(b, l, &(nfapi_callbacks) { + .expression = callbacks->parse_expr, + .handle = set_handle, + }, callbacks); + if (err) + return err; if (callbacks->rule_final) - callbacks->rule_final(r, callbacks); - - return MNL_CB_OK; -} - - -static const char *__read_response(struct mnl_socket *nl, uint32_t seq, mnl_cb_t cb_data, void *data, - const char *err1, const char *err2) -{ - uint32_t portid = mnl_socket_get_portid(nl); - char buf[MNL_SOCKET_BUFFER_SIZE]; - - while (true) { - int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - if (ret < 0) - return err1; - if (ret == 0) - break; - - ret = mnl_cb_run(buf, ret, 0, portid, cb_data, data); - if (ret < 0) - return err2; - if (ret == 0) - break; - } + callbacks->rule_final(callbacks); return NULL; } -// macro for customised error strings -#define read_response(instance, ...) __read_response(__VA_ARGS__, \ - "failed to receive from netlink socket for " instance, \ - "error returned from netlink for " instance) - -static const char *iterate_rules(struct mnl_socket *nl, int family, const char *chain, - uint32_t *seq, +static const char *iterate_rules(nfapi_socket *nl, int family, const char *chain, struct iterate_callbacks *callbacks) { - g_autoptr(_nftnl_rule) r = nftnl_rule_alloc(); - if (!r) - return "failed to allocate rule for iteration"; - - nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family); - nftnl_rule_set_str(r, NFTNL_RULE_TABLE, "filter"); - nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain); + g_autoptr(nfapi_buf) b = nfapi_buf_new(); - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family, - NLM_F_DUMP, *seq); + nfapi_add_msg(b, NFT_MSG_GETRULE, family, NLM_F_REQUEST | NLM_F_DUMP); - nftnl_rule_nlmsg_build_payload(nlh, r); + nfapi_add_str_attr(b, NFTA_RULE_TABLE, "filter"); + nfapi_add_str_attr(b, NFTA_RULE_CHAIN, chain); - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) + if (!nfapi_send_buf(nl, b)) return "failed to write to netlink socket for iteration"; - const char *err = read_response("iterate rules", nl, *seq, nftables_do_rule, callbacks); + const char *err = nfapi_recv_iter(nl, &(nfapi_callbacks) { .rule = nftables_do_rule }, callbacks); if (err) return err; if (callbacks->iterate_final) - err = callbacks->iterate_final(nl, family, chain, seq, callbacks); + err = callbacks->iterate_final(nl, family, chain, callbacks); if (err) return err; @@ -207,87 +157,54 @@ static const char *iterate_rules(struct mnl_socket *nl, int family, const char * } -static bool set_rule_handle(struct nftnl_rule *r, void *data) { +static bool set_rule_handle(nfapi_buf *b, void *data) { uint64_t *handle = data; - nftnl_rule_set_u64(r, NFTNL_RULE_HANDLE, *handle); + nfapi_add_u64_attr(b, NFTA_RULE_HANDLE, *handle); return true; } -static const char *__batch_request(struct mnl_socket *nl, int family, uint32_t *seq, - uint16_t type, uint16_t flags, - union { - void (*table_fn)(struct nlmsghdr *, const struct nftnl_table *); - void (*rule_fn)(struct nlmsghdr *, struct nftnl_rule *); - void (*chain_fn)(struct nlmsghdr *, const struct nftnl_chain *); - void (*generic_fn)(struct nlmsghdr *, void *); - } __attribute__ ((__transparent_union__)) build_payload, - void *ptr, - const char *err1, const char *err2, const char *err3) -{ - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); - nftnl_batch_begin(mnl_nlmsg_batch_current(batch), (*seq)++); - mnl_nlmsg_batch_next(batch); - - uint32_t req_seq = *seq; - struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), - type, family, - flags | NLM_F_ACK, (*seq)++); - build_payload.generic_fn(nlh, ptr); - mnl_nlmsg_batch_next(batch); - - nftnl_batch_end(mnl_nlmsg_batch_current(batch), (*seq)++); - mnl_nlmsg_batch_next(batch); - - if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) - return err1; - mnl_nlmsg_batch_stop(batch); +static const char *delete_rules(nfapi_socket *nl, int family, const char *chain, + bool (*callback)(nfapi_buf *b, void *data), void *data) +{ + g_autoptr(nfapi_buf) b = nfapi_buf_new(); - return __read_response(nl, req_seq, NULL, NULL, err2, err3); -} + nfapi_batch_begin(b); -// macro for customised error strings -#define batch_request(instance, ...) __batch_request(__VA_ARGS__, \ - "failed to write to netlink socket for " instance, \ - "failed to receive from netlink socket for " instance, \ - "error returned from netlink for " instance) + nfapi_add_msg(b, NFT_MSG_DELRULE, family, NLM_F_REQUEST | NLM_F_ACK); + nfapi_add_str_attr(b, NFTA_RULE_TABLE, "filter"); + nfapi_add_str_attr(b, NFTA_RULE_CHAIN, chain); + if (callback) { + if (!callback(b, data)) + return "delete rule callback returned error"; + } -static const char *delete_rules(struct mnl_socket *nl, int family, const char *chain, uint32_t *seq, - bool (*callback)(struct nftnl_rule *r, void *data), void *data) -{ - g_autoptr(_nftnl_rule) r = nftnl_rule_alloc(); - if (!r) - return "failed to allocate rule for deletion"; + nfapi_batch_end(b); - nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family); - nftnl_rule_set_str(r, NFTNL_RULE_TABLE, "filter"); - nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain); + if (!nfapi_send_buf(nl, b)) + return "failed to write to netlink socket for delete rule"; - if (callback) { - if (!callback(r, data)) - return NULL; - } + const char *err = nfapi_recv_iter(nl, NULL, NULL); + if (err) + return err; - return batch_request("delete rule", nl, family, seq, NFT_MSG_DELRULE, 0, - nftnl_rule_nlmsg_build_payload, r); + return NULL; } -static const char *iterate_delete_rules(struct mnl_socket *nl, int family, const char *chain, uint32_t *seq, +static const char *iterate_delete_rules(nfapi_socket *nl, int family, const char *chain, struct iterate_callbacks *callbacks) { - while (callbacks->iterate_scratch.handles.length) { uint64_t *handle = g_queue_pop_head(&callbacks->iterate_scratch.handles); // transfer to stack and free uint64_t h = *handle; g_free(handle); - const char *err = delete_rules(nl, family, chain, seq, set_rule_handle, &h); + const char *err = delete_rules(nl, family, chain, set_rule_handle, &h); if (err) return err; } @@ -295,47 +212,37 @@ static const char *iterate_delete_rules(struct mnl_socket *nl, int family, const } -static const char *local_input_chain(struct nftnl_chain *c) { - nftnl_chain_set_u32(c, NFTNL_CHAIN_HOOKNUM, NF_INET_LOCAL_IN); - nftnl_chain_set_u32(c, NFTNL_CHAIN_PRIO, 0); - nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, NF_ACCEPT); - return NULL; -} +static const char *local_input_chain(nfapi_buf *b) { + nfapi_nested_begin(b, NFTA_CHAIN_HOOK); + nfapi_add_u32_attr(b, NFTA_HOOK_HOOKNUM, htonl(NF_INET_LOCAL_IN)); + nfapi_add_u32_attr(b, NFTA_HOOK_PRIORITY, htonl(0)); + nfapi_nested_end(b); + nfapi_add_u32_attr(b, NFTA_CHAIN_POLICY, htonl(NF_ACCEPT)); -static int nftables_do_chain(const struct nlmsghdr *nlh, void *data) { - bool *exists = data; - - g_autoptr(_nftnl_chain) c = nftnl_chain_alloc(); - if (!c) - return MNL_CB_ERROR; + return NULL; +} - if (nftnl_chain_nlmsg_parse(nlh, c) < 0) - return MNL_CB_OK; +static const char *nftables_do_chain(const int8_t *b, size_t l, void *userdata) { + bool *exists = userdata; *exists = true; - - return MNL_CB_OK; + return NULL; } -static const char *chain_exists(struct mnl_socket *nl, int family, const char *chain, uint32_t *seq) { - g_autoptr(_nftnl_chain) c = nftnl_chain_alloc(); - - nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, "filter"); - nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain); +static const char *chain_exists(nfapi_socket *nl, int family, const char *chain) { + g_autoptr(nfapi_buf) b = nfapi_buf_new(); - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, family, - NLM_F_ACK, *seq); + nfapi_add_msg(b, NFT_MSG_GETCHAIN, family, NLM_F_REQUEST | NLM_F_ACK); + nfapi_add_str_attr(b, NFTA_CHAIN_TABLE, "filter"); + nfapi_add_str_attr(b, NFTA_CHAIN_NAME, chain); - nftnl_chain_nlmsg_build_payload(nlh, c); - - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) + if (!nfapi_send_buf(nl, b)) return "failed to write to netlink socket for chain exists"; bool exists = false; - const char *err = read_response("get chain", nl, *seq, nftables_do_chain, &exists); + const char *err = nfapi_recv_iter(nl, &(nfapi_callbacks) { .chain = nftables_do_chain }, &exists); if (err) return err; @@ -343,192 +250,264 @@ static const char *chain_exists(struct mnl_socket *nl, int family, const char *c } -static const char *add_chain(struct mnl_socket *nl, int family, const char *chain, uint32_t *seq, - const char *(*callback)(struct nftnl_chain *)) +static const char *add_chain(nfapi_socket *nl, int family, const char *chain, + const char *(*callback)(nfapi_buf *)) { - if (chain_exists(nl, family, chain, seq) == NULL) + if (chain_exists(nl, family, chain) == NULL) return NULL; - g_autoptr(_nftnl_chain) c = nftnl_chain_alloc(); - if (!c) - return "failed to allocate chain for adding"; + g_autoptr(nfapi_buf) b = nfapi_buf_new(); + + nfapi_batch_begin(b); - nftnl_chain_set_u32(c, NFTNL_CHAIN_FAMILY, family); - nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, "filter"); - nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain); + nfapi_add_msg(b, NFT_MSG_NEWCHAIN, family, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK); + nfapi_add_str_attr(b, NFTA_CHAIN_TABLE, "filter"); + nfapi_add_str_attr(b, NFTA_CHAIN_NAME, chain); if (callback) { - const char *err = callback(c); + const char *err = callback(b); if (err) return err; } - return batch_request("add chain", nl, family, seq, NFT_MSG_NEWCHAIN, NLM_F_CREATE, - nftnl_chain_nlmsg_build_payload, c); + nfapi_batch_end(b); + + if (!nfapi_send_buf(nl, b)) + return "failed to write to netlink socket for add chain"; + + const char *err = nfapi_recv_iter(nl, NULL, NULL); + if (err) + return err; + + return NULL; } -static const char *add_rule(struct mnl_socket *nl, int family, uint32_t *seq, +static const char *add_rule(nfapi_socket *nl, int family, struct add_rule_callbacks callbacks) { - g_autoptr(_nftnl_rule) r = nftnl_rule_alloc(); - if (!r) - return "failed to allocate rule for adding"; + g_autoptr(nfapi_buf) b = nfapi_buf_new(); + + nfapi_batch_begin(b); - nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family); - nftnl_rule_set_str(r, NFTNL_RULE_TABLE, "filter"); + nfapi_add_msg(b, NFT_MSG_NEWRULE, family, + NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | (callbacks.append ? NLM_F_APPEND : 0)); + nfapi_add_str_attr(b, NFTA_RULE_TABLE, "filter"); - const char *err = callbacks.callback(r, family, &callbacks); + const char *err = callbacks.rule_callback(b, family, &callbacks); if (err) return err; - return batch_request("add rule", nl, family, seq, NFT_MSG_NEWRULE, - (callbacks.append ? NLM_F_APPEND : 0) | NLM_F_CREATE, - nftnl_rule_nlmsg_build_payload, r); + nfapi_batch_end(b); + + if (!nfapi_send_buf(nl, b)) + return "failed to write to netlink socket for add rule"; + + err = nfapi_recv_iter(nl, NULL, NULL); + if (err) + return err; + + return NULL; } -static const char *udp_filter(struct nftnl_rule *r, int family) { - g_autoptr(_nftnl_expr) e = NULL; +static void counter(nfapi_buf *b) { + // buffer is in the nested expressions + + nfapi_nested_begin(b, NFTA_LIST_ELEM); + + nfapi_add_str_attr(b, NFTA_EXPR_NAME, "counter"); + + nfapi_nested_begin(b, NFTA_EXPR_DATA); + + nfapi_nested_end(b); + + nfapi_nested_end(b); +} + + +static const char *udp_filter(nfapi_buf *b, int family) { + // buffer is in the nested expressions static const uint8_t proto = IPPROTO_UDP; - e = nftnl_expr_alloc("payload"); - if (!e) - return "failed to allocate payload expr for UDP filter"; + nfapi_nested_begin(b, NFTA_LIST_ELEM); + + nfapi_add_str_attr(b, NFTA_EXPR_NAME, "payload"); + + nfapi_nested_begin(b, NFTA_EXPR_DATA); + + nfapi_add_u32_attr(b, NFTA_PAYLOAD_DREG, htonl(NFT_REG_1)); + nfapi_add_u32_attr(b, NFTA_PAYLOAD_BASE, htonl(NFT_PAYLOAD_NETWORK_HEADER)); + + if (family == NFPROTO_IPV4) + nfapi_add_u32_attr(b, NFTA_PAYLOAD_OFFSET, + htonl(offsetof(struct iphdr, protocol))); + else if (family == NFPROTO_IPV6) + nfapi_add_u32_attr(b, NFTA_PAYLOAD_OFFSET, + htonl(offsetof(struct ip6_hdr, ip6_nxt))); + else + return "unsupported address family for UDP filter"; + + nfapi_add_u32_attr(b, NFTA_PAYLOAD_LEN, htonl(sizeof(proto))); + + nfapi_nested_end(b); + + nfapi_nested_end(b); + + nfapi_nested_begin(b, NFTA_LIST_ELEM); + + nfapi_add_str_attr(b, NFTA_EXPR_NAME, "cmp"); + + nfapi_nested_begin(b, NFTA_EXPR_DATA); + + nfapi_add_u32_attr(b, NFTA_CMP_SREG, htonl(NFT_REG_1)); + nfapi_add_u32_attr(b, NFTA_CMP_OP, htonl(NFT_CMP_EQ)); - nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_BASE, NFT_PAYLOAD_NETWORK_HEADER); - nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_DREG, NFT_REG_1); - if (family == NFPROTO_IPV4) - nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET, offsetof(struct iphdr, protocol)); - else if (family == NFPROTO_IPV6) - nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET, offsetof(struct ip6_hdr, ip6_nxt)); - else - return "unsupported address family for UDP filter"; - nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_LEN, sizeof(proto)); + nfapi_nested_begin(b, NFTA_CMP_DATA); - nftnl_rule_add_expr(r, e); - e = NULL; + nfapi_add_attr(b, NFTA_DATA_VALUE, &proto, sizeof(proto)); - e = nftnl_expr_alloc("cmp"); - if (!e) - return "failed to allocate cmp expr for UDP filter"; + nfapi_nested_end(b); - nftnl_expr_set_u32(e, NFTNL_EXPR_CMP_SREG, NFT_REG_1); - nftnl_expr_set_u32(e, NFTNL_EXPR_CMP_OP, NFT_CMP_EQ); - nftnl_expr_set(e, NFTNL_EXPR_CMP_DATA, &proto, sizeof(proto)); + nfapi_nested_end(b); - nftnl_rule_add_expr(r, e); - e = NULL; + nfapi_nested_end(b); - e = nftnl_expr_alloc("counter"); - if (!e) - return "failed to allocate counter expr for UDP filter"; - nftnl_rule_add_expr(r, e); - e = NULL; + counter(b); return NULL; } -static const char *input_immediate(struct nftnl_rule *r, int family, struct add_rule_callbacks *callbacks) { - nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, callbacks->base_chain); +static const char *input_immediate(nfapi_buf *b, int family, struct add_rule_callbacks *callbacks) { + nfapi_add_str_attr(b, NFTA_RULE_CHAIN, callbacks->base_chain); - const char *err = udp_filter(r, family); - if (err) - return err; + nfapi_nested_begin(b, NFTA_RULE_EXPRESSIONS); + + const char *err = udp_filter(b, family); + if (err) + return err; + + nfapi_nested_begin(b, NFTA_LIST_ELEM); + + nfapi_add_str_attr(b, NFTA_EXPR_NAME, "immediate"); + + nfapi_nested_begin(b, NFTA_EXPR_DATA); + + nfapi_add_u32_attr(b, NFTA_IMMEDIATE_DREG, 0); - g_autoptr(_nftnl_expr) e = nftnl_expr_alloc("immediate"); - if (!e) - return "failed to allocate immediate expr"; + nfapi_nested_begin(b, NFTA_IMMEDIATE_DATA); - nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_DREG, 0); - nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_VERDICT, NFT_JUMP); - nftnl_expr_set_str(e, NFTNL_EXPR_IMM_CHAIN, callbacks->chain); + nfapi_nested_begin(b, NFTA_DATA_VERDICT); - nftnl_rule_add_expr(r, e); - e = NULL; + nfapi_add_u32_attr(b, NFTA_VERDICT_CODE, htonl(NFT_JUMP)); + nfapi_add_str_attr(b, NFTA_VERDICT_CHAIN, callbacks->chain); + + nfapi_nested_end(b); + + nfapi_nested_end(b); + + nfapi_nested_end(b); + + nfapi_nested_end(b); + + nfapi_nested_end(b); return NULL; } -static const char *rtpe_target_base(struct nftnl_rule *r, struct add_rule_callbacks *callbacks) { - g_autoptr(_nftnl_expr) e = nftnl_expr_alloc("target"); - if (!e) - return "failed to allocate target expr for RTPENGINE"; +static const char *rtpe_target_base(nfapi_buf *b, struct add_rule_callbacks *callbacks) { + // buffer is in the nested expressions + + struct xt_rtpengine_info info = { .id = callbacks->table }; + + nfapi_nested_begin(b, NFTA_LIST_ELEM); + + nfapi_add_str_attr(b, NFTA_EXPR_NAME, "target"); - nftnl_expr_set_str(e, NFTNL_EXPR_TG_NAME, "RTPENGINE"); - nftnl_expr_set_u32(e, NFTNL_EXPR_TG_REV, 0); + nfapi_nested_begin(b, NFTA_EXPR_DATA); - struct xt_rtpengine_info *info = malloc(sizeof(*info)); - if (!info) - return "failed to allocate target info for RTPENGINE"; - *info = (__typeof__(*info)) { .id = callbacks->table }; + nfapi_add_str_attr(b, NFTA_TARGET_NAME, "RTPENGINE"); + nfapi_add_u32_attr(b, NFTA_TARGET_REV, htonl(0)); + nfapi_add_attr(b, NFTA_TARGET_INFO, &info, sizeof(info)); - nftnl_expr_set(e, NFTNL_EXPR_TG_INFO, info, sizeof(*info)); + nfapi_nested_end(b); - nftnl_rule_add_expr(r, e); - e = NULL; + nfapi_nested_end(b); return NULL; } -static const char *rtpe_target(struct nftnl_rule *r, int family, struct add_rule_callbacks *callbacks) { - nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, callbacks->chain); +static const char *rtpe_target(nfapi_buf *b, int family, struct add_rule_callbacks *callbacks) { + nfapi_add_str_attr(b, NFTA_RULE_CHAIN, callbacks->chain); - const char *err = rtpe_target_base(r, callbacks); - if (err) - return err; + nfapi_nested_begin(b, NFTA_RULE_EXPRESSIONS); + + const char *err = rtpe_target_base(b, callbacks); + if (err) + return err; - g_autoptr(_nftnl_expr) e = nftnl_expr_alloc("counter"); - if (!e) - return "failed to allocate counter expr for RTPENGINE"; - nftnl_rule_add_expr(r, e); - e = NULL; + counter(b); + + nfapi_nested_end(b); return NULL; } -static const char *rtpe_target_filter(struct nftnl_rule *r, int family, struct add_rule_callbacks *callbacks) { - nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, callbacks->chain); +static const char *rtpe_target_filter(nfapi_buf *b, int family, struct add_rule_callbacks *callbacks) { + nfapi_add_str_attr(b, NFTA_RULE_CHAIN, callbacks->chain); - const char *err = rtpe_target_base(r, callbacks); - if (err) - return err; + nfapi_nested_begin(b, NFTA_RULE_EXPRESSIONS); - err = udp_filter(r, family); - if (err) - return err; + const char *err = rtpe_target_base(b, callbacks); + if (err) + return err; + + err = udp_filter(b, family); + if (err) + return err; + + nfapi_nested_end(b); return NULL; } -static const char *delete_chain(struct mnl_socket *nl, int family, uint32_t *seq, const char *chain) { - g_autoptr(_nftnl_chain) c = nftnl_chain_alloc(); - if (!c) - return "failed to allocate chain for deletion"; +static const char *delete_chain(nfapi_socket *nl, int family, const char *chain) { + g_autoptr(nfapi_buf) b = nfapi_buf_new(); + + nfapi_batch_begin(b); - nftnl_chain_set_u32(c, NFTNL_RULE_FAMILY, family); - nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, "filter"); - nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain); + nfapi_add_msg(b, NFT_MSG_DELCHAIN, family, NLM_F_REQUEST | NLM_F_ACK); - return batch_request("delete chain", nl, family, seq, NFT_MSG_DELCHAIN, 0, - nftnl_chain_nlmsg_build_payload, c); + nfapi_add_str_attr(b, NFTA_CHAIN_TABLE, "filter"); + nfapi_add_str_attr(b, NFTA_CHAIN_NAME, chain); + + nfapi_batch_end(b); + + if (!nfapi_send_buf(nl, b)) + return "failed to write to netlink socket for delete chain"; + + const char *err = nfapi_recv_iter(nl, NULL, NULL); + if (err) + return err; + + return NULL; } -static const char *nftables_shutdown_family(struct mnl_socket *nl, int family, uint32_t *seq, +static const char *nftables_shutdown_family(nfapi_socket *nl, int family, const char *chain, const char *base_chain, nftables_args *dummy) { const char *err; if (!base_chain || strcmp(base_chain, "none")) { // clean up rules in legacy `INPUT` chain - err = iterate_rules(nl, family, "INPUT", seq, + err = iterate_rules(nl, family, "INPUT", &(struct iterate_callbacks) { .parse_expr = match_immediate_rtpe, .chain = chain, @@ -539,7 +518,7 @@ static const char *nftables_shutdown_family(struct mnl_socket *nl, int family, u return err; // clean up rules in `input` chain - err = iterate_rules(nl, family, "input", seq, + err = iterate_rules(nl, family, "input", &(struct iterate_callbacks) { .parse_expr = match_immediate_rtpe, .chain = chain, @@ -552,7 +531,7 @@ static const char *nftables_shutdown_family(struct mnl_socket *nl, int family, u if (base_chain && strcmp(base_chain, "none")) { // clean up rules in other base chain chain if any - err = iterate_rules(nl, family, base_chain, seq, + err = iterate_rules(nl, family, base_chain, &(struct iterate_callbacks) { .parse_expr = match_immediate_rtpe, .chain = chain, @@ -564,13 +543,13 @@ static const char *nftables_shutdown_family(struct mnl_socket *nl, int family, u } // clear out custom chain if it already exists - err = delete_rules(nl, family, chain, seq, NULL, NULL); + err = delete_rules(nl, family, chain, NULL, NULL); if (err) { if (errno != ENOENT) // ignore trying to delete stuff that doesn't exist return err; } - err = delete_chain(nl, family, seq, chain); + err = delete_chain(nl, family, chain); if (err) { if (errno != ENOENT && errno != EBUSY) // ignore trying to delete stuff that doesn't exist return err; @@ -580,46 +559,54 @@ static const char *nftables_shutdown_family(struct mnl_socket *nl, int family, u } -static const char *add_table(struct mnl_socket *nl, int family, uint32_t *seq) { - g_autoptr(_nftnl_table) t = nftnl_table_alloc(); - if (!t) - return "failed to allocate table"; +static const char *add_table(nfapi_socket *nl, int family) { + g_autoptr(nfapi_buf) b = nfapi_buf_new(); + + nfapi_batch_begin(b); - nftnl_table_set_u32(t, NFTNL_TABLE_FAMILY, family); - nftnl_table_set_str(t, NFTNL_TABLE_NAME, "filter"); + nfapi_add_msg(b, NFT_MSG_NEWTABLE, family, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK); + nfapi_add_str_attr(b, NFTA_TABLE_NAME, "filter"); - return batch_request("add table", nl, family, seq, NFT_MSG_NEWTABLE, NLM_F_CREATE, - nftnl_table_nlmsg_build_payload, t); + nfapi_batch_end(b); + + if (!nfapi_send_buf(nl, b)) + return "failed to write to netlink socket for add table"; + + const char *err = nfapi_recv_iter(nl, NULL, NULL); + if (err) + return err; + + return NULL; } -static const char *nftables_setup_family(struct mnl_socket *nl, int family, uint32_t *seq, +static const char *nftables_setup_family(nfapi_socket *nl, int family, const char *chain, const char *base_chain, nftables_args *args) { - const char *err = nftables_shutdown_family(nl, family, seq, chain, base_chain, NULL); + const char *err = nftables_shutdown_family(nl, family, chain, base_chain, NULL); if (err) return err; // create the table in case it doesn't exist - err = add_table(nl, family, seq); + err = add_table(nl, family); if (err) return err; if (base_chain) { // add custom chain - err = add_chain(nl, family, chain, seq, NULL); + err = add_chain(nl, family, chain, NULL); if (err) return err; if (strcmp(base_chain, "none")) { // make sure we have a local input base chain - err = add_chain(nl, family, base_chain, seq, local_input_chain); + err = add_chain(nl, family, base_chain, local_input_chain); if (err) return err; // add jump rule from input base chain to custom chain - err = add_rule(nl, family, seq, (struct add_rule_callbacks) { - .callback = input_immediate, + err = add_rule(nl, family, (struct add_rule_callbacks) { + .rule_callback = input_immediate, .chain = chain, .base_chain = base_chain, .append = args->append, @@ -629,21 +616,21 @@ static const char *nftables_setup_family(struct mnl_socket *nl, int family, uint } // add rule for kernel forwarding - return add_rule(nl, family, seq, (struct add_rule_callbacks) { - .callback = rtpe_target, + return add_rule(nl, family, (struct add_rule_callbacks) { + .rule_callback = rtpe_target, .chain = chain, .table = args->table, }); } else { // create custom base chain - err = add_chain(nl, family, chain, seq, local_input_chain); + err = add_chain(nl, family, chain, local_input_chain); if (err) return err; // add rule for kernel forwarding - return add_rule(nl, family, seq, (struct add_rule_callbacks) { - .callback = rtpe_target_filter, + return add_rule(nl, family, (struct add_rule_callbacks) { + .rule_callback = rtpe_target_filter, .chain = chain, .table = args->table, }); @@ -652,7 +639,7 @@ static const char *nftables_setup_family(struct mnl_socket *nl, int family, uint static const char *nftables_do(const char *chain, const char *base_chain, - const char *(*do_func)(struct mnl_socket *nl, int family, uint32_t *seq, + const char *(*do_func)(nfapi_socket *nl, int family, const char *chain, const char *base_chain, nftables_args *args), nftables_args *args) { @@ -661,24 +648,19 @@ static const char *nftables_do(const char *chain, const char *base_chain, if (!base_chain[0]) base_chain = NULL; - g_autoptr(_mnl_socket) nl = mnl_socket_open(NETLINK_NETFILTER); + g_autoptr(nfapi_socket) nl = nfapi_socket_open(); if (!nl) return "failed to open netlink socket"; - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) - return "failed to bind netlink socket"; - - uint32_t seq = time(NULL); - const char *err = NULL; if (args->family == 0 || args->family == NFPROTO_IPV4) - err = do_func(nl, NFPROTO_IPV4, &seq, chain, base_chain, args); + err = do_func(nl, NFPROTO_IPV4, chain, base_chain, args); if (err) return err; if (args->family == 0 || args->family == NFPROTO_IPV6) - err = do_func(nl, NFPROTO_IPV6, &seq, chain, base_chain, args); + err = do_func(nl, NFPROTO_IPV6, chain, base_chain, args); if (err) return err; @@ -686,7 +668,7 @@ static const char *nftables_do(const char *chain, const char *base_chain, } -static const char *nftables_check_family(struct mnl_socket *nl, int family, uint32_t *seq, +static const char *nftables_check_family(nfapi_socket *nl, int family, const char *chain, const char *base_chain, nftables_args *dummy) { // look for our custom module rule in the specified chain @@ -696,7 +678,7 @@ static const char *nftables_check_family(struct mnl_socket *nl, int family, uint .rule_final = check_matched_flag, }; - iterate_rules(nl, family, chain, seq, &callbacks); + iterate_rules(nl, family, chain, &callbacks); if (!callbacks.iterate_scratch.rule_matched) return "RTPENGINE rule not found"; @@ -709,11 +691,11 @@ static const char *nftables_check_family(struct mnl_socket *nl, int family, uint .rule_final = check_matched_flag, }; - iterate_rules(nl, family, "INPUT", seq, &callbacks); - iterate_rules(nl, family, "input", seq, &callbacks); + iterate_rules(nl, family, "INPUT", &callbacks); + iterate_rules(nl, family, "input", &callbacks); if (base_chain && strcmp(base_chain, "none")) - iterate_rules(nl, family, base_chain, seq, &callbacks); + iterate_rules(nl, family, base_chain, &callbacks); if (!callbacks.iterate_scratch.rule_matched) return "immediate-goto rule not found"; diff --git a/debian/control b/debian/control index 341b4b835..ccf94a82c 100644 --- a/debian/control +++ b/debian/control @@ -7,8 +7,8 @@ Standards-Version: 4.6.2 Rules-Requires-Root: no Build-Depends: debhelper-compat (= 13), - default-libmysqlclient-dev, dh-sequence-dkms, + default-libmysqlclient-dev, discount, gperf, libavcodec-dev (>= 6:10), @@ -32,11 +32,9 @@ Build-Depends: libjson-glib-dev, libjson-perl, libjwt-dev, - libmnl-dev, libmosquitto-dev, libncurses-dev, libnet-interface-perl, - libnftnl-dev, libopus-dev, libpcap0.8-dev, libpcre2-dev, diff --git a/el/rtpengine.spec b/el/rtpengine.spec index 4c0e3594f..4033a6fa3 100644 --- a/el/rtpengine.spec +++ b/el/rtpengine.spec @@ -47,7 +47,7 @@ BuildRequires: gcc-toolset-13 %endif Requires(pre): shadow-utils %if 0%{?rhel} >= 8 -BuildRequires: pkgconfig(libmnl) pkgconfig(libnftnl) pandoc ncurses-devel +BuildRequires: pandoc ncurses-devel %endif %if 0%{?rhel} >= 9 BuildRequires: pkgconfig(libiptc) diff --git a/lib/netfilter_api.c b/lib/netfilter_api.c index 38059bb53..783cf8ba8 100644 --- a/lib/netfilter_api.c +++ b/lib/netfilter_api.c @@ -140,15 +140,6 @@ void nfapi_add_attr(nfapi_buf *b, uint16_t type, const void *data, size_t len) { memcpy(d, data, len); } -void nfapi_add_str_attr(nfapi_buf *b, uint16_t type, const char *s) { - nfapi_add_attr(b, type, s, strlen(s) + 1); -} -void nfapi_add_u32_attr(nfapi_buf *b, uint16_t type, uint32_t u) { - nfapi_add_attr(b, type, &u, sizeof(u)); -} -void nfapi_add_u64_attr(nfapi_buf *b, uint16_t type, uint64_t u) { - nfapi_add_attr(b, type, &u, sizeof(u)); -} void nfapi_nested_begin(nfapi_buf *b, uint16_t type) { g_queue_push_tail(&b->nested, (void *) b->s->len); @@ -369,10 +360,12 @@ const char *nfapi_rule_iter(const int8_t *buf, size_t l, const nfapi_callbacks * //table = data; //printf("table %s\n", data); break; + case NFTA_RULE_CHAIN: //chain = data; //printf("chain %s\n", data); break; + case NFTA_RULE_HANDLE: if (data_len != sizeof(handle)) return "handle size incorrect"; @@ -380,6 +373,16 @@ const char *nfapi_rule_iter(const int8_t *buf, size_t l, const nfapi_callbacks * if (c && c->handle) c->handle(handle, userdata); break; + + case NFTA_RULE_USERDATA:; + const struct { + uint16_t len; + char comment[]; + } *comment = (void *) data; + if (c && c->comment && data_len <= ntohs(comment->len) + 2) + c->comment(comment->comment, userdata); + break; + case NFTA_RULE_EXPRESSIONS:; const char *err = expr_iter(data, data_len, c, userdata); if (err) diff --git a/lib/netfilter_api.h b/lib/netfilter_api.h index 22fefe23b..f9f173699 100644 --- a/lib/netfilter_api.h +++ b/lib/netfilter_api.h @@ -14,6 +14,7 @@ typedef struct { const char *(*chain)(const int8_t *, size_t, void *userdata); const char *(*expression)(const char *, const int8_t *, size_t, void *userdata); void (*handle)(int64_t, void *userdata); + void (*comment)(const char *, void *userdata); } nfapi_callbacks; @@ -27,9 +28,27 @@ void nfapi_buf_free(nfapi_buf *); void nfapi_add_msg(nfapi_buf *, uint16_t type, uint16_t family, uint16_t flags); void nfapi_add_attr(nfapi_buf *b, uint16_t type, const void *data, size_t len); -void nfapi_add_str_attr(nfapi_buf *b, uint16_t type, const char *s); -void nfapi_add_u32_attr(nfapi_buf *b, uint16_t type, uint32_t u); -void nfapi_add_u64_attr(nfapi_buf *b, uint16_t type, uint64_t u); + + +static inline void nfapi_add_str_attr(nfapi_buf *b, uint16_t type, const char *s) { + nfapi_add_attr(b, type, s, strlen(s) + 1); +} +static inline void nfapi_add_u32_attr(nfapi_buf *b, uint16_t type, uint32_t u) { + nfapi_add_attr(b, type, &u, sizeof(u)); +} +static inline void nfapi_add_u64_attr(nfapi_buf *b, uint16_t type, uint64_t u) { + nfapi_add_attr(b, type, &u, sizeof(u)); +} + +#define nfapi_add_binary_str_attr(b, type, s) \ + nfapi_add_attr(b, type, &(struct { \ + uint16_t len; \ + char buf[sizeof(s)]; \ + }) { \ + .len = htons(sizeof(s)), \ + .buf = s, \ + }, 2 + sizeof(s)) + void nfapi_nested_begin(nfapi_buf *, uint16_t type); void nfapi_nested_end(nfapi_buf *); diff --git a/utils/gen-common-flags b/utils/gen-common-flags index f9f0531f6..7c4cbef6f 100755 --- a/utils/gen-common-flags +++ b/utils/gen-common-flags @@ -80,8 +80,6 @@ gen-pkgconf-flags LIBCURL libcurl gen-pkgconf-flags LIBCRYPTO libcrypto gen-pkgconf-flags LIBEVENT libevent_pthreads gen-pkgconf-flags LIBIPTC libiptc -gen-pkgconf-flags LIBMNL libmnl -gen-pkgconf-flags LIBNFTNL libnftnl gen-pkgconf-flags LIBPCRE libpcre2-8 echo "CFLAGS_LIBPCRE += -DPCRE2_CODE_UNIT_WIDTH=8" gen-pkgconf-flags LIBSWRESAMPLE libswresample