diff --git a/src/libsystemd-network/test-acd.c b/src/libsystemd-network/test-acd.c index e369f5a592..73105f5a04 100644 --- a/src/libsystemd-network/test-acd.c +++ b/src/libsystemd-network/test-acd.c @@ -12,7 +12,6 @@ #include "sd-netlink.h" #include "in-addr-util.h" -#include "netlink-util.h" #include "tests.h" #include "util.h" diff --git a/src/libsystemd-network/test-ipv4ll-manual.c b/src/libsystemd-network/test-ipv4ll-manual.c index d5ef3535ea..5453ef93a4 100644 --- a/src/libsystemd-network/test-ipv4ll-manual.c +++ b/src/libsystemd-network/test-ipv4ll-manual.c @@ -12,7 +12,6 @@ #include "alloc-util.h" #include "in-addr-util.h" -#include "netlink-util.h" #include "parse-util.h" #include "string-util.h" #include "tests.h" diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 24dfee7095..fb6e6f6431 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -818,6 +818,7 @@ global: sd_netlink_message_append_s32; sd_netlink_message_append_s64; sd_netlink_message_append_data; + sd_netlink_message_append_container_data; sd_netlink_message_append_in_addr; sd_netlink_message_append_in6_addr; sd_netlink_message_append_sockaddr_in; diff --git a/src/libsystemd/sd-netlink/netlink-internal.h b/src/libsystemd/sd-netlink/netlink-internal.h index 497ffe9112..e3bb80e496 100644 --- a/src/libsystemd/sd-netlink/netlink-internal.h +++ b/src/libsystemd/sd-netlink/netlink-internal.h @@ -176,26 +176,38 @@ int netlink_add_match_internal( /* nfnl */ /* TODO: to be exported later */ int sd_nfnl_socket_open(sd_netlink **ret); -int sd_nfnl_message_batch_begin(sd_netlink *nfnl, sd_netlink_message **ret); -int sd_nfnl_message_batch_end(sd_netlink *nfnl, sd_netlink_message **ret); -int sd_nfnl_nft_message_del_table(sd_netlink *nfnl, sd_netlink_message **ret, - int family, const char *table); +int sd_nfnl_send_batch( + sd_netlink *nfnl, + sd_netlink_message **messages, + size_t msgcount, + uint32_t **ret_serials); +int sd_nfnl_call_batch( + sd_netlink *nfnl, + sd_netlink_message **messages, + size_t n_messages, + uint64_t usec, + sd_netlink_message ***ret_messages); +int sd_nfnl_message_new( + sd_netlink *nfnl, + sd_netlink_message **ret, + int nfproto, + uint16_t subsys, + uint16_t msg_type, + uint16_t flags); int sd_nfnl_nft_message_new_table(sd_netlink *nfnl, sd_netlink_message **ret, - int family, const char *table); + int nfproto, const char *table); int sd_nfnl_nft_message_new_basechain(sd_netlink *nfnl, sd_netlink_message **ret, - int family, const char *table, const char *chain, + int nfproto, const char *table, const char *chain, const char *type, uint8_t hook, int prio); int sd_nfnl_nft_message_new_rule(sd_netlink *nfnl, sd_netlink_message **ret, - int family, const char *table, const char *chain); + int nfproto, const char *table, const char *chain); int sd_nfnl_nft_message_new_set(sd_netlink *nfnl, sd_netlink_message **ret, - int family, const char *table, const char *set_name, + int nfproto, const char *table, const char *set_name, uint32_t setid, uint32_t klen); -int sd_nfnl_nft_message_new_setelems_begin(sd_netlink *nfnl, sd_netlink_message **ret, - int family, const char *table, const char *set_name); -int sd_nfnl_nft_message_del_setelems_begin(sd_netlink *nfnl, sd_netlink_message **ret, - int family, const char *table, const char *set_name); -int sd_nfnl_nft_message_add_setelem(sd_netlink_message *m, - uint32_t num, - const void *key, uint32_t klen, - const void *data, uint32_t dlen); -int sd_nfnl_nft_message_add_setelem_end(sd_netlink_message *m); +int sd_nfnl_nft_message_new_setelems(sd_netlink *nfnl, sd_netlink_message **ret, + int add, int nfproto, const char *table, const char *set_name); +int sd_nfnl_nft_message_append_setelem(sd_netlink_message *m, + uint32_t index, + const void *key, size_t key_len, + const void *data, size_t data_len, + uint32_t flags); diff --git a/src/libsystemd/sd-netlink/netlink-message-nfnl.c b/src/libsystemd/sd-netlink/netlink-message-nfnl.c index 6f32167772..582f623efe 100644 --- a/src/libsystemd/sd-netlink/netlink-message-nfnl.c +++ b/src/libsystemd/sd-netlink/netlink-message-nfnl.c @@ -1,73 +1,224 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include #include #include -#include -#include -#include +#include #include "sd-netlink.h" -#include "format-util.h" +#include "io-util.h" #include "netlink-internal.h" #include "netlink-types.h" -#include "socket-util.h" +#include "netlink-util.h" -static int nft_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int family, uint16_t msg_type, uint16_t flags) { +static bool nfproto_is_valid(int nfproto) { + return IN_SET(nfproto, + NFPROTO_UNSPEC, + NFPROTO_INET, + NFPROTO_IPV4, + NFPROTO_ARP, + NFPROTO_NETDEV, + NFPROTO_BRIDGE, + NFPROTO_IPV6, + NFPROTO_DECNET); +} + +int sd_nfnl_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int nfproto, uint16_t subsys, uint16_t msg_type, uint16_t flags) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; assert_return(nfnl, -EINVAL); assert_return(ret, -EINVAL); + assert_return(nfproto_is_valid(nfproto), -EINVAL); + assert_return(NFNL_MSG_TYPE(msg_type) == msg_type, -EINVAL); - r = message_new(nfnl, &m, NFNL_SUBSYS_NFTABLES << 8 | msg_type); + r = message_new(nfnl, &m, subsys << 8 | msg_type); if (r < 0) return r; m->hdr->nlmsg_flags |= flags; *(struct nfgenmsg*) NLMSG_DATA(m->hdr) = (struct nfgenmsg) { - .nfgen_family = family, + .nfgen_family = nfproto, .version = NFNETLINK_V0, - .res_id = nfnl->serial, }; *ret = TAKE_PTR(m); return 0; } -static int nfnl_message_batch(sd_netlink *nfnl, sd_netlink_message **ret, uint16_t msg_type) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; +static int nfnl_message_set_res_id(sd_netlink_message *m, uint16_t res_id) { + struct nfgenmsg *nfgen; + + assert(m); + assert(m->hdr); + + nfgen = NLMSG_DATA(m->hdr); + nfgen->res_id = htobe16(res_id); + + return 0; +} + +static int nfnl_message_get_subsys(sd_netlink_message *m, uint16_t *ret) { + uint16_t t; int r; - r = message_new(nfnl, &m, NFNL_SUBSYS_NONE << 8 | msg_type); + assert(m); + assert(ret); + + r = sd_netlink_message_get_type(m, &t); if (r < 0) return r; - *(struct nfgenmsg*) NLMSG_DATA(m->hdr) = (struct nfgenmsg) { - .nfgen_family = AF_UNSPEC, - .version = NFNETLINK_V0, - .res_id = NFNL_SUBSYS_NFTABLES, - }; + *ret = NFNL_SUBSYS_ID(t); + return 0; +} + +static int nfnl_message_new_batch(sd_netlink *nfnl, sd_netlink_message **ret, uint16_t subsys, uint16_t msg_type) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + assert_return(nfnl, -EINVAL); + assert_return(ret, -EINVAL); + assert_return(NFNL_MSG_TYPE(msg_type) == msg_type, -EINVAL); + + r = sd_nfnl_message_new(nfnl, &m, NFPROTO_UNSPEC, NFNL_SUBSYS_NONE, msg_type, 0); + if (r < 0) + return r; + + r = nfnl_message_set_res_id(m, subsys); + if (r < 0) + return r; *ret = TAKE_PTR(m); return 0; } -int sd_nfnl_message_batch_begin(sd_netlink *nfnl, sd_netlink_message **ret) { - return nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_BEGIN); +int sd_nfnl_send_batch( + sd_netlink *nfnl, + sd_netlink_message **messages, + size_t n_messages, + uint32_t **ret_serials) { + + /* iovs refs batch_begin and batch_end, hence, free iovs first, then free batch_begin and batch_end. */ + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *batch_begin = NULL, *batch_end = NULL; + _cleanup_free_ struct iovec *iovs = NULL; + _cleanup_free_ uint32_t *serials = NULL; + uint16_t subsys; + ssize_t k; + size_t c = 0; + int r; + + assert_return(nfnl, -EINVAL); + assert_return(!netlink_pid_changed(nfnl), -ECHILD); + assert_return(messages, -EINVAL); + assert_return(n_messages > 0, -EINVAL); + + iovs = new(struct iovec, n_messages + 2); + if (!iovs) + return -ENOMEM; + + if (ret_serials) { + serials = new(uint32_t, n_messages); + if (!serials) + return -ENOMEM; + } + + r = nfnl_message_get_subsys(messages[0], &subsys); + if (r < 0) + return r; + + r = nfnl_message_new_batch(nfnl, &batch_begin, subsys, NFNL_MSG_BATCH_BEGIN); + if (r < 0) + return r; + + netlink_seal_message(nfnl, batch_begin); + iovs[c++] = IOVEC_MAKE(batch_begin->hdr, batch_begin->hdr->nlmsg_len); + + for (size_t i = 0; i < n_messages; i++) { + uint16_t s; + + r = nfnl_message_get_subsys(messages[i], &s); + if (r < 0) + return r; + + if (s != subsys) + return -EINVAL; + + netlink_seal_message(nfnl, messages[i]); + if (serials) + serials[i] = message_get_serial(messages[i]); + + /* It seems that the kernel accepts an arbitrary number. Let's set the serial of the + * first message. */ + nfnl_message_set_res_id(messages[i], message_get_serial(batch_begin)); + + iovs[c++] = IOVEC_MAKE(messages[i]->hdr, messages[i]->hdr->nlmsg_len); + } + + r = nfnl_message_new_batch(nfnl, &batch_end, subsys, NFNL_MSG_BATCH_END); + if (r < 0) + return r; + + netlink_seal_message(nfnl, batch_end); + iovs[c++] = IOVEC_MAKE(batch_end->hdr, batch_end->hdr->nlmsg_len); + + assert(c == n_messages + 2); + k = writev(nfnl->fd, iovs, n_messages + 2); + if (k < 0) + return -errno; + + if (ret_serials) + *ret_serials = TAKE_PTR(serials); + + return 0; } -int sd_nfnl_message_batch_end(sd_netlink *nfnl, sd_netlink_message **ret) { - return nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_END); +int sd_nfnl_call_batch( + sd_netlink *nfnl, + sd_netlink_message **messages, + size_t n_messages, + uint64_t usec, + sd_netlink_message ***ret_messages) { + + _cleanup_free_ sd_netlink_message **replies = NULL; + _cleanup_free_ uint32_t *serials = NULL; + int k, r; + + assert_return(nfnl, -EINVAL); + assert_return(!netlink_pid_changed(nfnl), -ECHILD); + assert_return(messages, -EINVAL); + assert_return(n_messages > 0, -EINVAL); + + if (ret_messages) { + replies = new0(sd_netlink_message*, n_messages); + if (!replies) + return -ENOMEM; + } + + r = sd_nfnl_send_batch(nfnl, messages, n_messages, &serials); + if (r < 0) + return r; + + for (size_t i = 0; i < n_messages; i++) { + k = sd_netlink_read(nfnl, serials[i], usec, ret_messages ? replies + i : NULL); + if (k < 0 && r >= 0) + r = k; + } + if (r < 0) + return r; + + if (ret_messages) + *ret_messages = TAKE_PTR(replies); + + return 0; } int sd_nfnl_nft_message_new_basechain( sd_netlink *nfnl, sd_netlink_message **ret, - int family, + int nfproto, const char *table, const char *chain, const char *type, @@ -77,7 +228,7 @@ int sd_nfnl_nft_message_new_basechain( _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; - r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWCHAIN, NLM_F_CREATE); + r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWCHAIN, NLM_F_CREATE); if (r < 0) return r; @@ -113,37 +264,16 @@ int sd_nfnl_nft_message_new_basechain( return 0; } -int sd_nfnl_nft_message_del_table( - sd_netlink *nfnl, - sd_netlink_message **ret, - int family, - const char *table) { - - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - int r; - - r = nft_message_new(nfnl, &m, family, NFT_MSG_DELTABLE, NLM_F_CREATE); - if (r < 0) - return r; - - r = sd_netlink_message_append_string(m, NFTA_TABLE_NAME, table); - if (r < 0) - return r; - - *ret = TAKE_PTR(m); - return r; -} - int sd_nfnl_nft_message_new_table( sd_netlink *nfnl, sd_netlink_message **ret, - int family, + int nfproto, const char *table) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; - r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWTABLE, NLM_F_CREATE | NLM_F_EXCL); + r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWTABLE, NLM_F_CREATE | NLM_F_EXCL); if (r < 0) return r; @@ -158,14 +288,14 @@ int sd_nfnl_nft_message_new_table( int sd_nfnl_nft_message_new_rule( sd_netlink *nfnl, sd_netlink_message **ret, - int family, + int nfproto, const char *table, const char *chain) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; - r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWRULE, NLM_F_CREATE); + r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWRULE, NLM_F_CREATE); if (r < 0) return r; @@ -184,7 +314,7 @@ int sd_nfnl_nft_message_new_rule( int sd_nfnl_nft_message_new_set( sd_netlink *nfnl, sd_netlink_message **ret, - int family, + int nfproto, const char *table, const char *set_name, uint32_t set_id, @@ -193,7 +323,7 @@ int sd_nfnl_nft_message_new_set( _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; - r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWSET, NLM_F_CREATE); + r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWSET, NLM_F_CREATE); if (r < 0) return r; @@ -217,17 +347,21 @@ int sd_nfnl_nft_message_new_set( return r; } -int sd_nfnl_nft_message_new_setelems_begin( +int sd_nfnl_nft_message_new_setelems( sd_netlink *nfnl, sd_netlink_message **ret, - int family, + int add, /* boolean */ + int nfproto, const char *table, const char *set_name) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; - r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWSETELEM, NLM_F_CREATE); + if (add) + r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWSETELEM, NLM_F_CREATE); + else + r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_DELSETELEM, 0); if (r < 0) return r; @@ -239,93 +373,48 @@ int sd_nfnl_nft_message_new_setelems_begin( if (r < 0) return r; - r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS); - if (r < 0) - return r; - *ret = TAKE_PTR(m); return r; } -int sd_nfnl_nft_message_del_setelems_begin( - sd_netlink *nfnl, - sd_netlink_message **ret, - int family, - const char *table, - const char *set_name) { - - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - int r; - - r = nft_message_new(nfnl, &m, family, NFT_MSG_DELSETELEM, 0); - if (r < 0) - return r; - - r = sd_netlink_message_append_string(m, NFTA_SET_ELEM_LIST_TABLE, table); - if (r < 0) - return r; - - r = sd_netlink_message_append_string(m, NFTA_SET_ELEM_LIST_SET, set_name); - if (r < 0) - return r; - - r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS); - if (r < 0) - return r; - - *ret = TAKE_PTR(m); - return r; -} - -static int add_data(sd_netlink_message *m, uint16_t attr, const void *data, uint32_t dlen) { - int r; - - r = sd_netlink_message_open_container(m, attr); - if (r < 0) - return r; - - r = sd_netlink_message_append_data(m, NFTA_DATA_VALUE, data, dlen); - if (r < 0) - return r; - - return sd_netlink_message_close_container(m); /* attr */ -} - -int sd_nfnl_nft_message_add_setelem( +int sd_nfnl_nft_message_append_setelem( sd_netlink_message *m, - uint32_t num, + uint32_t index, const void *key, - uint32_t klen, + size_t key_len, const void *data, - uint32_t dlen) { + size_t data_len, + uint32_t flags) { int r; - r = sd_netlink_message_open_array(m, num); + r = sd_netlink_message_open_array(m, index); if (r < 0) return r; - r = add_data(m, NFTA_SET_ELEM_KEY, key, klen); + r = sd_netlink_message_append_container_data(m, NFTA_SET_ELEM_KEY, NFTA_DATA_VALUE, key, key_len); if (r < 0) goto cancel; if (data) { - r = add_data(m, NFTA_SET_ELEM_DATA, data, dlen); + r = sd_netlink_message_append_container_data(m, NFTA_SET_ELEM_DATA, NFTA_DATA_VALUE, data, data_len); if (r < 0) goto cancel; } - return 0; + if (flags != 0) { + r = sd_netlink_message_append_u32(m, NFTA_SET_ELEM_FLAGS, htobe32(flags)); + if (r < 0) + goto cancel; + } + + return sd_netlink_message_close_container(m); /* array */ cancel: - sd_netlink_message_cancel_array(m); + (void) sd_netlink_message_cancel_array(m); return r; } -int sd_nfnl_nft_message_add_setelem_end(sd_netlink_message *m) { - return sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */ -} - int sd_nfnl_socket_open(sd_netlink **ret) { return netlink_open_family(ret, NETLINK_NETFILTER); } diff --git a/src/libsystemd/sd-netlink/netlink-message-rtnl.c b/src/libsystemd/sd-netlink/netlink-message-rtnl.c index 289eb41ca9..0ff25f8e6c 100644 --- a/src/libsystemd/sd-netlink/netlink-message-rtnl.c +++ b/src/libsystemd/sd-netlink/netlink-message-rtnl.c @@ -17,6 +17,46 @@ #include "socket-util.h" #include "util.h" +static bool rtnl_message_type_is_neigh(uint16_t type) { + return IN_SET(type, RTM_NEWNEIGH, RTM_GETNEIGH, RTM_DELNEIGH); +} + +static bool rtnl_message_type_is_route(uint16_t type) { + return IN_SET(type, RTM_NEWROUTE, RTM_GETROUTE, RTM_DELROUTE); +} + +static bool rtnl_message_type_is_nexthop(uint16_t type) { + return IN_SET(type, RTM_NEWNEXTHOP, RTM_GETNEXTHOP, RTM_DELNEXTHOP); +} + +static bool rtnl_message_type_is_link(uint16_t type) { + return IN_SET(type, + RTM_NEWLINK, RTM_SETLINK, RTM_GETLINK, RTM_DELLINK, + RTM_NEWLINKPROP, RTM_DELLINKPROP, RTM_GETLINKPROP); +} + +static bool rtnl_message_type_is_addr(uint16_t type) { + return IN_SET(type, RTM_NEWADDR, RTM_GETADDR, RTM_DELADDR); +} + +static bool rtnl_message_type_is_addrlabel(uint16_t type) { + return IN_SET(type, RTM_NEWADDRLABEL, RTM_DELADDRLABEL, RTM_GETADDRLABEL); +} + +static bool rtnl_message_type_is_routing_policy_rule(uint16_t type) { + return IN_SET(type, RTM_NEWRULE, RTM_DELRULE, RTM_GETRULE); +} + +static bool rtnl_message_type_is_traffic_control(uint16_t type) { + return IN_SET(type, + RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC, + RTM_NEWTCLASS, RTM_DELTCLASS, RTM_GETTCLASS); +} + +static bool rtnl_message_type_is_mdb(uint16_t type) { + return IN_SET(type, RTM_NEWMDB, RTM_DELMDB, RTM_GETMDB); +} + _public_ int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen) { struct rtmsg *rtm; diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index 34b4c23bd5..f76827d42c 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -148,7 +148,7 @@ _public_ sd_netlink_message* sd_netlink_message_unref(sd_netlink_message *m) { _public_ int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *type) { assert_return(m, -EINVAL); - assert_return(type != 0, -EINVAL); + assert_return(type, -EINVAL); *type = m->hdr->nlmsg_type; @@ -452,6 +452,29 @@ _public_ int sd_netlink_message_append_data(sd_netlink_message *m, unsigned shor return 0; } +_public_ int sd_netlink_message_append_container_data( + sd_netlink_message *m, + unsigned short container_type, + unsigned short type, + const void *data, + size_t len) { + + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = sd_netlink_message_open_container(m, container_type); + if (r < 0) + return r; + + r = sd_netlink_message_append_data(m, type, data, len); + if (r < 0) + return r; + + return sd_netlink_message_close_container(m); +} + int netlink_message_append_in_addr_union(sd_netlink_message *m, unsigned short type, int family, const union in_addr_union *data) { int r; diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c index 70f5b85a54..e834f31122 100644 --- a/src/libsystemd/sd-netlink/netlink-socket.c +++ b/src/libsystemd/sd-netlink/netlink-socket.c @@ -13,7 +13,6 @@ #include "io-util.h" #include "netlink-internal.h" #include "netlink-types.h" -#include "netlink-util.h" #include "socket-util.h" #include "util.h" diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h index 606d14eeaf..9473e15700 100644 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ b/src/libsystemd/sd-netlink/netlink-util.h @@ -29,46 +29,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(MultipathRoute*, multipath_route_free); int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret); -static inline bool rtnl_message_type_is_neigh(uint16_t type) { - return IN_SET(type, RTM_NEWNEIGH, RTM_GETNEIGH, RTM_DELNEIGH); -} - -static inline bool rtnl_message_type_is_route(uint16_t type) { - return IN_SET(type, RTM_NEWROUTE, RTM_GETROUTE, RTM_DELROUTE); -} - -static inline bool rtnl_message_type_is_nexthop(uint16_t type) { - return IN_SET(type, RTM_NEWNEXTHOP, RTM_GETNEXTHOP, RTM_DELNEXTHOP); -} - -static inline bool rtnl_message_type_is_link(uint16_t type) { - return IN_SET(type, - RTM_NEWLINK, RTM_SETLINK, RTM_GETLINK, RTM_DELLINK, - RTM_NEWLINKPROP, RTM_DELLINKPROP, RTM_GETLINKPROP); -} - -static inline bool rtnl_message_type_is_addr(uint16_t type) { - return IN_SET(type, RTM_NEWADDR, RTM_GETADDR, RTM_DELADDR); -} - -static inline bool rtnl_message_type_is_addrlabel(uint16_t type) { - return IN_SET(type, RTM_NEWADDRLABEL, RTM_DELADDRLABEL, RTM_GETADDRLABEL); -} - -static inline bool rtnl_message_type_is_routing_policy_rule(uint16_t type) { - return IN_SET(type, RTM_NEWRULE, RTM_DELRULE, RTM_GETRULE); -} - -static inline bool rtnl_message_type_is_traffic_control(uint16_t type) { - return IN_SET(type, - RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC, - RTM_NEWTCLASS, RTM_DELTCLASS, RTM_GETTCLASS); -} - -static inline bool rtnl_message_type_is_mdb(uint16_t type) { - return IN_SET(type, RTM_NEWMDB, RTM_DELMDB, RTM_GETMDB); -} - int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name); int rtnl_set_link_properties( sd_netlink **rtnl, diff --git a/src/libsystemd/sd-netlink/test-netlink.c b/src/libsystemd/sd-netlink/test-netlink.c index fbc3ef0609..1117814332 100644 --- a/src/libsystemd/sd-netlink/test-netlink.c +++ b/src/libsystemd/sd-netlink/test-netlink.c @@ -16,7 +16,6 @@ #include "macro.h" #include "netlink-genl.h" #include "netlink-internal.h" -#include "netlink-util.h" #include "socket-util.h" #include "stdio-util.h" #include "string-util.h" diff --git a/src/shared/firewall-util-nft.c b/src/shared/firewall-util-nft.c index 87a326cafd..b5f0d1bab7 100644 --- a/src/shared/firewall-util-nft.c +++ b/src/shared/firewall-util-nft.c @@ -24,74 +24,44 @@ #include "time-util.h" #define NFT_SYSTEMD_DNAT_MAP_NAME "map_port_ipport" -#define NFT_SYSTEMD_TABLE_NAME "io.systemd.nat" +#define NFT_SYSTEMD_TABLE_NAME "io.systemd.nat" #define NFT_SYSTEMD_MASQ_SET_NAME "masq_saddr" #define NFNL_DEFAULT_TIMEOUT_USECS (1ULL * USEC_PER_SEC) #define UDP_DPORT_OFFSET 2 -static int nfnl_netlink_sendv( - sd_netlink *nfnl, - sd_netlink_message *messages[static 1], - size_t msgcount) { +static sd_netlink_message **netlink_message_unref_many(sd_netlink_message **m) { + if (!m) + return NULL; - _cleanup_free_ uint32_t *serial = NULL; - int r; + /* This does not free array. The end of the array must be NULL. */ - assert(nfnl); - assert(messages); - assert(msgcount > 0); + for (sd_netlink_message **p = m; *p; p++) + *p = sd_netlink_message_unref(*p); - r = sd_netlink_sendv(nfnl, messages, msgcount, &serial); - if (r < 0) - return r; - - r = 0; - for (size_t i = 1; i < msgcount - 1; i++) { - int tmp; - - /* If message is an error, this returns embedded errno */ - tmp = sd_netlink_read(nfnl, serial[i], NFNL_DEFAULT_TIMEOUT_USECS, NULL); - if (tmp < 0 && r == 0) - r = tmp; - } - - return r; + return m; } -static int nfnl_add_open_expr_container(sd_netlink_message *m, const char *name) { +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_netlink_message**, netlink_message_unref_many); + +static int nfnl_open_expr_container(sd_netlink_message *m, const char *name) { int r; - r = sd_netlink_message_open_array(m, NFTA_LIST_ELEM); - if (r < 0) - return r; + assert(m); + assert(name); - r = sd_netlink_message_append_string(m, NFTA_EXPR_NAME, name); + r = sd_netlink_message_open_array(m, NFTA_LIST_ELEM); if (r < 0) return r; return sd_netlink_message_open_container_union(m, NFTA_EXPR_DATA, name); } -static int nfnl_add_expr_fib(sd_netlink_message *m, uint32_t nft_fib_flags, - enum nft_fib_result result, - enum nft_registers dreg) { +static int nfnl_close_expr_container(sd_netlink_message *m) { int r; - r = nfnl_add_open_expr_container(m, "fib"); - if (r < 0) - return r; - - r = sd_netlink_message_append_u32(m, NFTA_FIB_FLAGS, htobe32(nft_fib_flags)); - if (r < 0) - return r; - r = sd_netlink_message_append_u32(m, NFTA_FIB_RESULT, htobe32(result)); - if (r < 0) - return r; - r = sd_netlink_message_append_u32(m, NFTA_FIB_DREG, htobe32(dreg)); - if (r < 0) - return r; + assert(m); r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */ if (r < 0) @@ -100,11 +70,45 @@ static int nfnl_add_expr_fib(sd_netlink_message *m, uint32_t nft_fib_flags, return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */ } -static int nfnl_add_expr_meta(sd_netlink_message *m, enum nft_meta_keys key, - enum nft_registers dreg) { +static int nfnl_add_expr_fib( + sd_netlink_message *m, + uint32_t nft_fib_flags, + enum nft_fib_result result, + enum nft_registers dreg) { + int r; - r = nfnl_add_open_expr_container(m, "meta"); + assert(m); + + r = nfnl_open_expr_container(m, "fib"); + if (r < 0) + return r; + + r = sd_netlink_message_append_u32(m, NFTA_FIB_FLAGS, htobe32(nft_fib_flags)); + if (r < 0) + return r; + + r = sd_netlink_message_append_u32(m, NFTA_FIB_RESULT, htobe32(result)); + if (r < 0) + return r; + + r = sd_netlink_message_append_u32(m, NFTA_FIB_DREG, htobe32(dreg)); + if (r < 0) + return r; + + return nfnl_close_expr_container(m); +} + +static int nfnl_add_expr_meta( + sd_netlink_message *m, + enum nft_meta_keys key, + enum nft_registers dreg) { + + int r; + + assert(m); + + r = nfnl_open_expr_container(m, "meta"); if (r < 0) return r; @@ -116,45 +120,55 @@ static int nfnl_add_expr_meta(sd_netlink_message *m, enum nft_meta_keys key, if (r < 0) return r; - r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */ - if (r < 0) - return r; - - return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */ + return nfnl_close_expr_container(m); } -static int nfnl_add_expr_payload(sd_netlink_message *m, enum nft_payload_bases pb, - uint32_t offset, uint32_t len, enum nft_registers dreg) { +static int nfnl_add_expr_payload( + sd_netlink_message *m, + enum nft_payload_bases pb, + uint32_t offset, + uint32_t len, + enum nft_registers dreg) { + int r; - r = nfnl_add_open_expr_container(m, "payload"); + assert(m); + + r = nfnl_open_expr_container(m, "payload"); if (r < 0) return r; r = sd_netlink_message_append_u32(m, NFTA_PAYLOAD_DREG, htobe32(dreg)); if (r < 0) return r; + r = sd_netlink_message_append_u32(m, NFTA_PAYLOAD_BASE, htobe32(pb)); if (r < 0) return r; + r = sd_netlink_message_append_u32(m, NFTA_PAYLOAD_OFFSET, htobe32(offset)); if (r < 0) return r; + r = sd_netlink_message_append_u32(m, NFTA_PAYLOAD_LEN, htobe32(len)); if (r < 0) return r; - r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */ - if (r < 0) - return r; - return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */ + return nfnl_close_expr_container(m); } -static int nfnl_add_expr_lookup_set_data(sd_netlink_message *m, const char *set_name, - enum nft_registers sreg) { +static int nfnl_add_expr_lookup( + sd_netlink_message *m, + const char *set_name, + enum nft_registers sreg, + enum nft_registers dreg) { + int r; - r = nfnl_add_open_expr_container(m, "lookup"); + assert(m); + assert(set_name); + + r = nfnl_open_expr_container(m, "lookup"); if (r < 0) return r; @@ -162,126 +176,102 @@ static int nfnl_add_expr_lookup_set_data(sd_netlink_message *m, const char *set_ if (r < 0) return r; - return sd_netlink_message_append_u32(m, NFTA_LOOKUP_SREG, htobe32(sreg)); + r = sd_netlink_message_append_u32(m, NFTA_LOOKUP_SREG, htobe32(sreg)); + if (r < 0) + return r; + + if (dreg != 0) { + r = sd_netlink_message_append_u32(m, NFTA_LOOKUP_DREG, htobe32(dreg)); + if (r < 0) + return r; + } + + return nfnl_close_expr_container(m); } -static int nfnl_add_expr_lookup_set(sd_netlink_message *m, const char *set_name, - enum nft_registers sreg) { +static int nfnl_add_expr_cmp( + sd_netlink_message *m, + enum nft_cmp_ops cmp_op, + enum nft_registers sreg, + const void *data, + size_t dlen) { + int r; - r = nfnl_add_expr_lookup_set_data(m, set_name, sreg); - if (r < 0) - return r; + assert(m); + assert(data); - r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */ - if (r < 0) - return r; - return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */ -} - -static int nfnl_add_expr_lookup_map(sd_netlink_message *m, const char *set_name, - enum nft_registers sreg, enum nft_registers dreg) { - int r; - - r = nfnl_add_expr_lookup_set_data(m, set_name, sreg); - if (r < 0) - return r; - - r = sd_netlink_message_append_u32(m, NFTA_LOOKUP_DREG, htobe32(dreg)); - if (r < 0) - return r; - - r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */ - if (r < 0) - return r; - - return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */ -} - -static int nfnl_add_expr_data(sd_netlink_message *m, int attr, const void *data, uint32_t dlen) { - int r; - - r = sd_netlink_message_open_container(m, attr); - if (r < 0) - return r; - r = sd_netlink_message_append_data(m, NFTA_DATA_VALUE, data, dlen); - if (r < 0) - return r; - - return sd_netlink_message_close_container(m); /* attr */ -} - -static int nfnl_add_expr_cmp_data(sd_netlink_message *m, const void *data, uint32_t dlen) { - return nfnl_add_expr_data(m, NFTA_CMP_DATA, data, dlen); -} - -static int nfnl_add_expr_cmp(sd_netlink_message *m, enum nft_cmp_ops cmp_op, - enum nft_registers sreg, const void *data, uint32_t dlen) { - int r; - - r = nfnl_add_open_expr_container(m, "cmp"); + r = nfnl_open_expr_container(m, "cmp"); if (r < 0) return r; r = sd_netlink_message_append_u32(m, NFTA_CMP_OP, htobe32(cmp_op)); if (r < 0) return r; + r = sd_netlink_message_append_u32(m, NFTA_CMP_SREG, htobe32(sreg)); if (r < 0) return r; - r = nfnl_add_expr_cmp_data(m, data, dlen); + r = sd_netlink_message_append_container_data(m, NFTA_CMP_DATA, NFTA_DATA_VALUE, data, dlen); if (r < 0) return r; - r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */ - if (r < 0) - return r; - return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */ + return nfnl_close_expr_container(m); } -static int nfnl_add_expr_bitwise(sd_netlink_message *m, - enum nft_registers sreg, - enum nft_registers dreg, - const void *and, - const void *xor, uint32_t len) { +static int nfnl_add_expr_bitwise( + sd_netlink_message *m, + enum nft_registers sreg, + enum nft_registers dreg, + const void *and, + const void *xor, + uint32_t len) { + int r; - r = nfnl_add_open_expr_container(m, "bitwise"); + assert(m); + assert(and); + assert(xor); + + r = nfnl_open_expr_container(m, "bitwise"); if (r < 0) return r; r = sd_netlink_message_append_u32(m, NFTA_BITWISE_SREG, htobe32(sreg)); if (r < 0) return r; + r = sd_netlink_message_append_u32(m, NFTA_BITWISE_DREG, htobe32(dreg)); if (r < 0) return r; + r = sd_netlink_message_append_u32(m, NFTA_BITWISE_LEN, htobe32(len)); if (r < 0) return r; - r = nfnl_add_expr_data(m, NFTA_BITWISE_MASK, and, len); + r = sd_netlink_message_append_container_data(m, NFTA_BITWISE_MASK, NFTA_DATA_VALUE, and, len); if (r < 0) return r; - r = nfnl_add_expr_data(m, NFTA_BITWISE_XOR, xor, len); + r = sd_netlink_message_append_container_data(m, NFTA_BITWISE_XOR, NFTA_DATA_VALUE, xor, len); if (r < 0) return r; - r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */ - if (r < 0) - return r; - return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */ + return nfnl_close_expr_container(m); } -static int nfnl_add_expr_dnat(sd_netlink_message *m, - int family, - enum nft_registers areg, - enum nft_registers preg) { +static int nfnl_add_expr_dnat( + sd_netlink_message *m, + int family, + enum nft_registers areg, + enum nft_registers preg) { + int r; - r = nfnl_add_open_expr_container(m, "nat"); + assert(m); + + r = nfnl_open_expr_container(m, "nat"); if (r < 0) return r; @@ -296,14 +286,12 @@ static int nfnl_add_expr_dnat(sd_netlink_message *m, r = sd_netlink_message_append_u32(m, NFTA_NAT_REG_ADDR_MIN, htobe32(areg)); if (r < 0) return r; + r = sd_netlink_message_append_u32(m, NFTA_NAT_REG_PROTO_MIN, htobe32(preg)); - if (r < 0) - return r; - r = sd_netlink_message_close_container(m); if (r < 0) return r; - return sd_netlink_message_close_container(m); + return nfnl_close_expr_container(m); } static int nfnl_add_expr_masq(sd_netlink_message *m) { @@ -320,13 +308,22 @@ static int nfnl_add_expr_masq(sd_netlink_message *m) { return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */ } -static int sd_nfnl_message_new_masq_rule(sd_netlink *nfnl, sd_netlink_message **ret, int family, - const char *chain) { +static int sd_nfnl_message_new_masq_rule( + sd_netlink *nfnl, + sd_netlink_message **ret, + int family, + const char *chain) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; /* -t nat -A POSTROUTING -p protocol -s source/pflen -o out_interface -d destination/pflen -j MASQUERADE */ + assert(nfnl); + assert(ret); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(chain); + r = sd_nfnl_nft_message_new_rule(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, chain); if (r < 0) return r; @@ -346,7 +343,7 @@ static int sd_nfnl_message_new_masq_rule(sd_netlink *nfnl, sd_netlink_message ** return r; /* 1st statement: use reg1 content to make lookup in @masq_saddr set. */ - r = nfnl_add_expr_lookup_set(m, NFT_SYSTEMD_MASQ_SET_NAME, NFT_REG32_01); + r = nfnl_add_expr_lookup(m, NFT_SYSTEMD_MASQ_SET_NAME, NFT_REG32_01, 0); if (r < 0) return r; @@ -358,12 +355,17 @@ static int sd_nfnl_message_new_masq_rule(sd_netlink *nfnl, sd_netlink_message ** r = sd_netlink_message_close_container(m); /* NFTA_RULE_EXPRESSIONS */ if (r < 0) return r; + *ret = TAKE_PTR(m); return 0; } -static int sd_nfnl_message_new_dnat_rule_pre(sd_netlink *nfnl, sd_netlink_message **ret, int family, - const char *chain) { +static int sd_nfnl_message_new_dnat_rule_pre( + sd_netlink *nfnl, + sd_netlink_message **ret, + int family, + const char *chain) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; enum nft_registers proto_reg; uint32_t local = RTN_LOCAL; @@ -372,6 +374,11 @@ static int sd_nfnl_message_new_dnat_rule_pre(sd_netlink *nfnl, sd_netlink_messag /* -t nat -A PREROUTING -p protocol --dport local_port -i in_interface -s source/pflen * -d destination/pflen -j DNAT --to-destination remote_addr:remote_port */ + assert(nfnl); + assert(ret); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(chain); + r = sd_nfnl_nft_message_new_rule(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, chain); if (r < 0) return r; @@ -401,9 +408,8 @@ static int sd_nfnl_message_new_dnat_rule_pre(sd_netlink *nfnl, sd_netlink_messag return r; /* 3rd statement: lookup 'l4proto . dport', e.g. 'tcp . 22' as key and - * store address and port for the dnat mapping in REG1/REG2. - */ - r = nfnl_add_expr_lookup_map(m, NFT_SYSTEMD_DNAT_MAP_NAME, NFT_REG32_01, NFT_REG32_01); + * store address and port for the dnat mapping in REG1/REG2. */ + r = nfnl_add_expr_lookup(m, NFT_SYSTEMD_DNAT_MAP_NAME, NFT_REG32_01, NFT_REG32_01); if (r < 0) return r; @@ -415,18 +421,27 @@ static int sd_nfnl_message_new_dnat_rule_pre(sd_netlink *nfnl, sd_netlink_messag r = sd_netlink_message_close_container(m); /* NFTA_RULE_EXPRESSIONS */ if (r < 0) return r; + *ret = TAKE_PTR(m); return 0; } -static int sd_nfnl_message_new_dnat_rule_out(sd_netlink *nfnl, sd_netlink_message **ret, - int family, const char *chain) { - static const uint32_t zero = 0, one = 1; +static int sd_nfnl_message_new_dnat_rule_out( + sd_netlink *nfnl, + sd_netlink_message **ret, + int family, + const char *chain) { + static const uint32_t zero = 0, one = 1; _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; enum nft_registers proto_reg; int r; + assert(nfnl); + assert(ret); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(chain); + r = sd_nfnl_nft_message_new_rule(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, chain); if (r < 0) return r; @@ -488,9 +503,8 @@ static int sd_nfnl_message_new_dnat_rule_out(sd_netlink *nfnl, sd_netlink_messag * the new destination ip and port number. * * reg1 and reg2 are clobbered and will then contain the new - * address/port number. - */ - r = nfnl_add_expr_lookup_map(m, NFT_SYSTEMD_DNAT_MAP_NAME, NFT_REG32_01, NFT_REG32_01); + * address/port number. */ + r = nfnl_add_expr_lookup(m, NFT_SYSTEMD_DNAT_MAP_NAME, NFT_REG32_01, NFT_REG32_01); if (r < 0) return r; @@ -504,18 +518,29 @@ static int sd_nfnl_message_new_dnat_rule_out(sd_netlink *nfnl, sd_netlink_messag r = sd_netlink_message_close_container(m); /* NFTA_RULE_EXPRESSIONS */ if (r < 0) return r; + *ret = TAKE_PTR(m); return 0; } -static int nft_new_set(struct sd_netlink *nfnl, - sd_netlink_message **ret, - int family, const char *set_name, - uint32_t set_id, - uint32_t flags, uint32_t type, uint32_t klen) { +static int nft_new_set( + struct sd_netlink *nfnl, + sd_netlink_message **ret, + int family, + const char *set_name, + uint32_t set_id, + uint32_t flags, + uint32_t type, + uint32_t klen) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; + assert(nfnl); + assert(ret); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(set_name); + r = sd_nfnl_nft_message_new_set(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, set_name, set_id, klen); if (r < 0) return r; @@ -534,13 +559,26 @@ static int nft_new_set(struct sd_netlink *nfnl, return r; } -static int nft_new_map(struct sd_netlink *nfnl, - sd_netlink_message **ret, - int family, const char *set_name, uint32_t set_id, - uint32_t flags, uint32_t type, uint32_t klen, uint32_t dtype, uint32_t dlen) { +static int nft_new_map( + struct sd_netlink *nfnl, + sd_netlink_message **ret, + int family, + const char *set_name, + uint32_t set_id, + uint32_t flags, + uint32_t type, + uint32_t klen, + uint32_t dtype, + uint32_t dlen) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; + assert(nfnl); + assert(ret); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(set_name); + r = nft_new_set(nfnl, &m, family, set_name, set_id, flags | NFT_SET_MAP, type, klen); if (r < 0) return r; @@ -552,17 +590,31 @@ static int nft_new_map(struct sd_netlink *nfnl, r = sd_netlink_message_append_u32(m, NFTA_SET_DATA_LEN, htobe32(dlen)); if (r < 0) return r; + *ret = TAKE_PTR(m); return 0; } -static int nft_add_element(sd_netlink *nfnl, sd_netlink_message **ret, - int family, const char *set_name, - const void *key, uint32_t klen, - const void *data, uint32_t dlen) { +static int nft_add_element( + sd_netlink *nfnl, + sd_netlink_message **ret, + int family, + const char *set_name, + const void *key, + uint32_t klen, + const void *data, + uint32_t dlen) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; + assert(nfnl); + assert(ret); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(set_name); + assert(key); + assert(data); + /* * Ideally there would be an API that provides: * @@ -576,40 +628,64 @@ static int nft_add_element(sd_netlink *nfnl, sd_netlink_message **ret, * This replicated here and each element gets added to the set * one-by-one. */ - r = sd_nfnl_nft_message_new_setelems_begin(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, set_name); + r = sd_nfnl_nft_message_new_setelems(nfnl, &m, /* add = */ true, family, NFT_SYSTEMD_TABLE_NAME, set_name); if (r < 0) return r; - r = sd_nfnl_nft_message_add_setelem(m, 0, key, klen, data, dlen); + r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS); + if (r < 0) + return r; + + r = sd_nfnl_nft_message_append_setelem(m, 0, key, klen, data, dlen, 0); if (r < 0) return r; /* could theoretically append more set elements to add here */ - r = sd_nfnl_nft_message_add_setelem_end(m); + + r = sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */ if (r < 0) return r; + *ret = TAKE_PTR(m); return 0; } -static int nft_del_element(sd_netlink *nfnl, - sd_netlink_message **ret, int family, const char *set_name, - const void *key, uint32_t klen, - const void *data, uint32_t dlen) { +static int nft_del_element( + sd_netlink *nfnl, + sd_netlink_message **ret, + int family, + const char *set_name, + const void *key, + uint32_t klen, + const void *data, + uint32_t dlen) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; - r = sd_nfnl_nft_message_del_setelems_begin(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, set_name); + assert(nfnl); + assert(ret); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(set_name); + assert(key); + assert(data); + + r = sd_nfnl_nft_message_new_setelems(nfnl, &m, /* add = */ false, family, NFT_SYSTEMD_TABLE_NAME, set_name); if (r < 0) return r; - r = sd_nfnl_nft_message_add_setelem(m, 0, key, klen, data, dlen); - if (r < 0) - return r; - - r = sd_nfnl_nft_message_add_setelem_end(m); + r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS); if (r < 0) return r; + + r = sd_nfnl_nft_message_append_setelem(m, 0, key, klen, data, dlen, 0); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */ + if (r < 0) + return r; + *ret = TAKE_PTR(m); return 0; } @@ -617,15 +693,14 @@ static int nft_del_element(sd_netlink *nfnl, /* This is needed so 'nft' userspace tool can properly format the contents * of the set/map when someone uses 'nft' to inspect their content. * - * The values cannot be changed, they are part of the nft tool type identifier ABI. - */ + * The values cannot be changed, they are part of the nft tool type identifier ABI. */ #define TYPE_BITS 6 enum nft_key_types { - TYPE_IPADDR = 7, - TYPE_IP6ADDR = 8, + TYPE_IPADDR = 7, + TYPE_IP6ADDR = 8, TYPE_INET_PROTOCOL = 12, - TYPE_INET_SERVICE = 13, + TYPE_INET_SERVICE = 13, }; static uint32_t concat_types2(enum nft_key_types a, enum nft_key_types b) { @@ -637,51 +712,38 @@ static uint32_t concat_types2(enum nft_key_types a, enum nft_key_types b) { return type; } -/* enough space to hold netlink messages for table skeleton */ -#define NFT_INIT_MSGS 16 static int fw_nftables_init_family(sd_netlink *nfnl, int family) { - sd_netlink_message *batch[NFT_INIT_MSGS] = {}; - size_t msgcnt = 0, i, ip_type_size; + sd_netlink_message *messages[10] = {}; + _unused_ _cleanup_(netlink_message_unref_manyp) sd_netlink_message **unref = messages; + size_t msgcnt = 0, ip_type_size; uint32_t set_id = 0; int ip_type, r; + assert(nfnl); assert(IN_SET(family, AF_INET, AF_INET6)); - r = sd_nfnl_message_batch_begin(nfnl, &batch[msgcnt]); - if (r < 0) - goto out_unref; - - msgcnt++; - assert(msgcnt < NFT_INIT_MSGS); /* Set F_EXCL so table add fails if the table already exists. */ - r = sd_nfnl_nft_message_new_table(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME); + r = sd_nfnl_nft_message_new_table(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_TABLE_NAME); if (r < 0) - goto out_unref; + return r; - msgcnt++; - assert(msgcnt < NFT_INIT_MSGS); - - r = sd_nfnl_nft_message_new_basechain(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME, + r = sd_nfnl_nft_message_new_basechain(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_TABLE_NAME, "prerouting", "nat", NF_INET_PRE_ROUTING, NF_IP_PRI_NAT_DST + 1); if (r < 0) - goto out_unref; + return r; - msgcnt++; - assert(msgcnt < NFT_INIT_MSGS); - r = sd_nfnl_nft_message_new_basechain(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME, + r = sd_nfnl_nft_message_new_basechain(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_TABLE_NAME, "output", "nat", NF_INET_LOCAL_OUT, NF_IP_PRI_NAT_DST + 1); if (r < 0) - goto out_unref; + return r; - msgcnt++; - assert(msgcnt < NFT_INIT_MSGS); - r = sd_nfnl_nft_message_new_basechain(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME, + r = sd_nfnl_nft_message_new_basechain(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_TABLE_NAME, "postrouting", "nat", NF_INET_POST_ROUTING, NF_IP_PRI_NAT_SRC + 1); if (r < 0) - goto out_unref; + return r; if (family == AF_INET) { ip_type_size = sizeof(uint32_t); @@ -691,12 +753,10 @@ static int fw_nftables_init_family(sd_netlink *nfnl, int family) { ip_type_size = sizeof(struct in6_addr); ip_type = TYPE_IP6ADDR; } - msgcnt++; - assert(msgcnt < NFT_INIT_MSGS); /* set to store ip address ranges we should masquerade for */ - r = nft_new_set(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_MASQ_SET_NAME, ++set_id, NFT_SET_INTERVAL, ip_type, ip_type_size); + r = nft_new_set(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_MASQ_SET_NAME, ++set_id, NFT_SET_INTERVAL, ip_type, ip_type_size); if (r < 0) - goto out_unref; + return r; /* * map to store ip address:port pair to dnat to. elements in concatenation @@ -705,54 +765,39 @@ static int fw_nftables_init_family(sd_netlink *nfnl, int family) { * Example: ip protocol . tcp daddr is sizeof(uint32_t) + sizeof(uint32_t), not * sizeof(uint8_t) + sizeof(uint16_t). */ - msgcnt++; - assert(msgcnt < NFT_INIT_MSGS); - r = nft_new_map(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_DNAT_MAP_NAME, ++set_id, 0, + r = nft_new_map(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_DNAT_MAP_NAME, ++set_id, 0, concat_types2(TYPE_INET_PROTOCOL, TYPE_INET_SERVICE), sizeof(uint32_t) * 2, concat_types2(ip_type, TYPE_INET_SERVICE), ip_type_size + sizeof(uint32_t)); if (r < 0) - goto out_unref; + return r; - msgcnt++; - assert(msgcnt < NFT_INIT_MSGS); - r = sd_nfnl_message_new_dnat_rule_pre(nfnl, &batch[msgcnt], family, "prerouting"); + r = sd_nfnl_message_new_dnat_rule_pre(nfnl, &messages[msgcnt++], family, "prerouting"); if (r < 0) - goto out_unref; + return r; - msgcnt++; - assert(msgcnt < NFT_INIT_MSGS); - r = sd_nfnl_message_new_dnat_rule_out(nfnl, &batch[msgcnt], family, "output"); + r = sd_nfnl_message_new_dnat_rule_out(nfnl, &messages[msgcnt++], family, "output"); if (r < 0) - goto out_unref; + return r; - msgcnt++; - r = sd_nfnl_message_new_masq_rule(nfnl, &batch[msgcnt], family, "postrouting"); + r = sd_nfnl_message_new_masq_rule(nfnl, &messages[msgcnt++], family, "postrouting"); if (r < 0) - goto out_unref; + return r; - msgcnt++; - assert(msgcnt < NFT_INIT_MSGS); - r = sd_nfnl_message_batch_end(nfnl, &batch[msgcnt]); - if (r < 0) - goto out_unref; + assert(msgcnt < ELEMENTSOF(messages)); + r = sd_nfnl_call_batch(nfnl, messages, msgcnt, NFNL_DEFAULT_TIMEOUT_USECS, NULL); + if (r < 0 && r != -EEXIST) + return r; - msgcnt++; - assert(msgcnt <= NFT_INIT_MSGS); - r = nfnl_netlink_sendv(nfnl, batch, msgcnt); - if (r == -EEXIST) - r = 0; - -out_unref: - for (i = 0; i < msgcnt; i++) - sd_netlink_message_unref(batch[i]); - - return r; + return 0; } int fw_nftables_init(FirewallContext *ctx) { _cleanup_(sd_netlink_unrefp) sd_netlink *nfnl = NULL; int r; + assert(ctx); + assert(!ctx->nfnl); + r = sd_nfnl_socket_open(&nfnl); if (r < 0) return r; @@ -772,28 +817,35 @@ int fw_nftables_init(FirewallContext *ctx) { } void fw_nftables_exit(FirewallContext *ctx) { + assert(ctx); + ctx->nfnl = sd_netlink_unref(ctx->nfnl); } -static int nft_message_add_setelem_iprange(sd_netlink_message *m, - const union in_addr_union *source, - unsigned int prefixlen) { +static int nft_message_append_setelem_iprange( + sd_netlink_message *m, + const union in_addr_union *source, + unsigned int prefixlen) { + uint32_t mask, start, end; unsigned int nplen; int r; + assert(m); + assert(source); assert(prefixlen <= 32); + nplen = 32 - prefixlen; mask = (1U << nplen) - 1U; mask = htobe32(~mask); start = source->in.s_addr & mask; - r = sd_nfnl_nft_message_add_setelem(m, 0, &start, sizeof(start), NULL, 0); + r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS); if (r < 0) return r; - r = sd_nfnl_nft_message_add_setelem_end(m); + r = sd_nfnl_nft_message_append_setelem(m, 0, &start, sizeof(start), NULL, 0, 0); if (r < 0) return r; @@ -802,22 +854,14 @@ static int nft_message_add_setelem_iprange(sd_netlink_message *m, end = 0U; end = htobe32(end); - r = sd_nfnl_nft_message_add_setelem(m, 1, &end, sizeof(end), NULL, 0); + r = sd_nfnl_nft_message_append_setelem(m, 1, &end, sizeof(end), NULL, 0, NFT_SET_ELEM_INTERVAL_END); if (r < 0) return r; - r = sd_netlink_message_append_u32(m, NFTA_SET_ELEM_FLAGS, htobe32(NFT_SET_ELEM_INTERVAL_END)); - if (r < 0) - return r; - - r = sd_nfnl_nft_message_add_setelem_end(m); - if (r < 0) - return r; - - return 0; + return sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */ } -static int nft_message_add_setelem_ip6range( +static int nft_message_append_setelem_ip6range( sd_netlink_message *m, const union in_addr_union *source, unsigned int prefixlen) { @@ -825,79 +869,59 @@ static int nft_message_add_setelem_ip6range( union in_addr_union start, end; int r; + assert(m); + assert(source); + r = in_addr_prefix_range(AF_INET6, source, prefixlen, &start, &end); if (r < 0) return r; - r = sd_nfnl_nft_message_add_setelem(m, 0, &start.in6, sizeof(start.in6), NULL, 0); + r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS); if (r < 0) return r; - r = sd_nfnl_nft_message_add_setelem_end(m); + r = sd_nfnl_nft_message_append_setelem(m, 0, &start.in6, sizeof(start.in6), NULL, 0, 0); if (r < 0) return r; - r = sd_nfnl_nft_message_add_setelem(m, 1, &end.in6, sizeof(end.in6), NULL, 0); + r = sd_nfnl_nft_message_append_setelem(m, 1, &end.in6, sizeof(end.in6), NULL, 0, NFT_SET_ELEM_INTERVAL_END); if (r < 0) return r; - r = sd_netlink_message_append_u32(m, NFTA_SET_ELEM_FLAGS, htobe32(NFT_SET_ELEM_INTERVAL_END)); - if (r < 0) - return r; - - return sd_nfnl_nft_message_add_setelem_end(m); + return sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */ } -#define NFT_MASQ_MSGS 3 - static int fw_nftables_add_masquerade_internal( - FirewallContext *ctx, + sd_netlink *nfnl, bool add, int af, const union in_addr_union *source, unsigned int source_prefixlen) { - sd_netlink_message *transaction[NFT_MASQ_MSGS] = {}; - size_t tsize; + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; + assert(nfnl); + assert(IN_SET(af, AF_INET, AF_INET6)); + if (!source || source_prefixlen == 0) return -EINVAL; if (af == AF_INET6 && source_prefixlen < 8) return -EINVAL; - r = sd_nfnl_message_batch_begin(ctx->nfnl, &transaction[0]); + r = sd_nfnl_nft_message_new_setelems(nfnl, &m, add, af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_MASQ_SET_NAME); if (r < 0) return r; - tsize = 1; - if (add) - r = sd_nfnl_nft_message_new_setelems_begin(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_MASQ_SET_NAME); - else - r = sd_nfnl_nft_message_del_setelems_begin(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_MASQ_SET_NAME); - if (r < 0) - goto out_unref; if (af == AF_INET) - r = nft_message_add_setelem_iprange(transaction[tsize], source, source_prefixlen); + r = nft_message_append_setelem_iprange(m, source, source_prefixlen); else - r = nft_message_add_setelem_ip6range(transaction[tsize], source, source_prefixlen); - if (r < 0) - goto out_unref; - - ++tsize; - assert(tsize < NFT_MASQ_MSGS); - r = sd_nfnl_message_batch_end(ctx->nfnl, &transaction[tsize]); + r = nft_message_append_setelem_ip6range(m, source, source_prefixlen); if (r < 0) return r; - ++tsize; - r = nfnl_netlink_sendv(ctx->nfnl, transaction, tsize); - -out_unref: - while (tsize > 0) - sd_netlink_message_unref(transaction[--tsize]); - return r < 0 ? r : 0; + return sd_nfnl_call_batch(nfnl, &m, 1, NFNL_DEFAULT_TIMEOUT_USECS, NULL); } int fw_nftables_add_masquerade( @@ -909,10 +933,14 @@ int fw_nftables_add_masquerade( int r; + assert(ctx); + assert(ctx->nfnl); + assert(IN_SET(af, AF_INET, AF_INET6)); + if (!socket_ipv6_is_supported() && af == AF_INET6) return -EOPNOTSUPP; - r = fw_nftables_add_masquerade_internal(ctx, add, af, source, source_prefixlen); + r = fw_nftables_add_masquerade_internal(ctx->nfnl, add, af, source, source_prefixlen); if (r != -ENOENT) return r; @@ -937,13 +965,11 @@ int fw_nftables_add_masquerade( if (r < 0) return r; - return fw_nftables_add_masquerade_internal(ctx, add, af, source, source_prefixlen); + return fw_nftables_add_masquerade_internal(ctx->nfnl, add, af, source, source_prefixlen); } -#define NFT_DNAT_MSGS 4 - static int fw_nftables_add_local_dnat_internal( - FirewallContext *ctx, + sd_netlink *nfnl, bool add, int af, int protocol, @@ -952,13 +978,16 @@ static int fw_nftables_add_local_dnat_internal( uint16_t remote_port, const union in_addr_union *previous_remote) { - sd_netlink_message *transaction[NFT_DNAT_MSGS] = {}; + sd_netlink_message *messages[3] = {}; + _unused_ _cleanup_(netlink_message_unref_manyp) sd_netlink_message **unref = messages; static bool ipv6_supported = true; uint32_t data[5], key[2], dlen; - size_t tsize; + size_t msgcnt = 0; int r; + assert(nfnl); assert(add || !previous_remote); + assert(IN_SET(af, AF_INET, AF_INET6)); if (!ipv6_supported && af == AF_INET6) return -EOPNOTSUPP; @@ -987,11 +1016,6 @@ static int fw_nftables_add_local_dnat_internal( data[4] = htobe16(remote_port); } - r = sd_nfnl_message_batch_begin(ctx->nfnl, &transaction[0]); - if (r < 0) - return r; - - tsize = 1; /* If a previous remote is set, remove its entry */ if (add && previous_remote && !in_addr_equal(af, previous_remote, remote)) { if (af == AF_INET) @@ -999,11 +1023,9 @@ static int fw_nftables_add_local_dnat_internal( else memcpy(data, &previous_remote->in6, sizeof(previous_remote->in6)); - r = nft_del_element(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen); + r = nft_del_element(nfnl, &messages[msgcnt++], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen); if (r < 0) - goto out_unref; - - tsize++; + return r; } if (af == AF_INET) @@ -1011,39 +1033,27 @@ static int fw_nftables_add_local_dnat_internal( else memcpy(data, &remote->in6, sizeof(remote->in6)); - assert(tsize < NFT_DNAT_MSGS); if (add) - r = nft_add_element(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen); + r = nft_add_element(nfnl, &messages[msgcnt++], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen); else - r = nft_del_element(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen); + r = nft_del_element(nfnl, &messages[msgcnt++], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen); if (r < 0) - goto out_unref; + return r; - tsize++; - assert(tsize < NFT_DNAT_MSGS); - - r = sd_nfnl_message_batch_end(ctx->nfnl, &transaction[tsize]); - if (r < 0) - goto out_unref; - - tsize++; - assert(tsize <= NFT_DNAT_MSGS); - - r = nfnl_netlink_sendv(ctx->nfnl, transaction, tsize); + assert(msgcnt < ELEMENTSOF(messages)); + r = sd_nfnl_call_batch(nfnl, messages, msgcnt, NFNL_DEFAULT_TIMEOUT_USECS, NULL); if (r == -EOVERFLOW && af == AF_INET6) { /* The current implementation of DNAT in systemd requires kernel's * fdb9c405e35bdc6e305b9b4e20ebc141ed14fc81 (v5.8), and the older kernel returns * -EOVERFLOW. Let's treat the error as -EOPNOTSUPP. */ log_debug_errno(r, "The current implementation of IPv6 DNAT in systemd requires kernel 5.8 or newer, ignoring: %m"); ipv6_supported = false; - r = -EOPNOTSUPP; + return -EOPNOTSUPP; } + if (r < 0) + return r; -out_unref: - while (tsize > 0) - sd_netlink_message_unref(transaction[--tsize]); - - return r < 0 ? r : 0; + return 0; } int fw_nftables_add_local_dnat( @@ -1058,10 +1068,14 @@ int fw_nftables_add_local_dnat( int r; + assert(ctx); + assert(ctx->nfnl); + assert(IN_SET(af, AF_INET, AF_INET6)); + if (!socket_ipv6_is_supported() && af == AF_INET6) return -EOPNOTSUPP; - r = fw_nftables_add_local_dnat_internal(ctx, add, af, protocol, local_port, remote, remote_port, previous_remote); + r = fw_nftables_add_local_dnat_internal(ctx->nfnl, add, af, protocol, local_port, remote, remote_port, previous_remote); if (r != -ENOENT) return r; @@ -1071,5 +1085,5 @@ int fw_nftables_add_local_dnat( return r; /* table created anew; previous address already gone */ - return fw_nftables_add_local_dnat_internal(ctx, add, af, protocol, local_port, remote, remote_port, NULL); + return fw_nftables_add_local_dnat_internal(ctx->nfnl, add, af, protocol, local_port, remote, remote_port, NULL); } diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index dcdf77c4fe..541eb1110a 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -83,6 +83,12 @@ int sd_netlink_message_append_s16(sd_netlink_message *m, unsigned short type, in int sd_netlink_message_append_s32(sd_netlink_message *m, unsigned short type, int32_t data); int sd_netlink_message_append_s64(sd_netlink_message *m, unsigned short type, int64_t data); int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len); +int sd_netlink_message_append_container_data( + sd_netlink_message *m, + unsigned short container_type, + unsigned short type, + const void *data, + size_t len); int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data); int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data); int sd_netlink_message_append_sockaddr_in(sd_netlink_message *m, unsigned short type, const struct sockaddr_in *data);