nftables: allow to remove nftables table, rule, set, and so on (#43673)

This adds 'add' boolean parameter to sd_nfnl_nft_message_new_table() and
friends, so that we can remove nftables table and so on. In
test-firewall-util, before and after running test cases, remove the
garbage nftables test table.
This commit is contained in:
Yu Watanabe
2026-09-23 13:50:04 +09:00
committed by GitHub
6 changed files with 163 additions and 53 deletions
+47 -17
View File
@@ -196,20 +196,50 @@ int sd_nfnl_message_new(
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 nfproto, const char *table);
int sd_nfnl_nft_message_new_basechain(sd_netlink *nfnl, sd_netlink_message **ret,
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 nfproto, const char *table, const char *chain);
int sd_nfnl_nft_message_new_set(sd_netlink *nfnl, sd_netlink_message **ret,
int nfproto, const char *table, const char *set_name,
uint32_t set_id, uint32_t klen);
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);
int sd_nfnl_nft_message_new_table(
sd_netlink *nfnl,
sd_netlink_message **ret,
int add,
int nfproto,
const char *table);
int sd_nfnl_nft_message_new_basechain(
sd_netlink *nfnl,
sd_netlink_message **ret,
int add,
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 add,
int nfproto,
const char *table,
const char *chain);
int sd_nfnl_nft_message_new_set(
sd_netlink *nfnl,
sd_netlink_message **ret,
int add,
int nfproto,
const char *table,
const char *set_name,
uint32_t set_id,
uint32_t klen);
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);
@@ -232,6 +232,7 @@ int sd_nfnl_call_batch(
int sd_nfnl_nft_message_new_basechain(
sd_netlink *nfnl,
sd_netlink_message **ret,
int add, /* boolean */
int nfproto,
const char *table,
const char *chain,
@@ -242,9 +243,16 @@ int sd_nfnl_nft_message_new_basechain(
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(nfnl);
assert(ret);
assert(table);
assert(chain);
assert(!add || type);
r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWCHAIN, NLM_F_CREATE);
if (add)
r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWCHAIN, NLM_F_CREATE);
else
r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_DELCHAIN, /* flags= */ 0);
if (r < 0)
return r;
@@ -256,25 +264,27 @@ int sd_nfnl_nft_message_new_basechain(
if (r < 0)
return r;
r = sd_netlink_message_append_string(m, NFTA_CHAIN_TYPE, type);
if (r < 0)
return r;
if (add) {
r = sd_netlink_message_append_string(m, NFTA_CHAIN_TYPE, type);
if (r < 0)
return r;
r = sd_netlink_message_open_container(m, NFTA_CHAIN_HOOK);
if (r < 0)
return r;
r = sd_netlink_message_open_container(m, NFTA_CHAIN_HOOK);
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, NFTA_HOOK_HOOKNUM, htobe32(hook));
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, NFTA_HOOK_HOOKNUM, htobe32(hook));
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, NFTA_HOOK_PRIORITY, htobe32(prio));
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, NFTA_HOOK_PRIORITY, htobe32(prio));
if (r < 0)
return r;
r = sd_netlink_message_close_container(m);
if (r < 0)
return r;
r = sd_netlink_message_close_container(m);
if (r < 0)
return r;
}
*ret = TAKE_PTR(m);
return 0;
@@ -283,15 +293,24 @@ int sd_nfnl_nft_message_new_basechain(
int sd_nfnl_nft_message_new_table(
sd_netlink *nfnl,
sd_netlink_message **ret,
int add, /* boolean */
int nfproto,
const char *table) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(nfnl);
assert(ret);
assert(table);
r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWTABLE, NLM_F_CREATE | NLM_F_EXCL);
/* Note, if nfproto == AF_UNSPEC, then the kernel flushes all tables. See nf_tables_deltable() in
* nf_tables_api.c in the kernel. */
if (add)
r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWTABLE, NLM_F_CREATE | NLM_F_EXCL);
else
r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_DELTABLE, /* flags= */ 0);
if (r < 0)
return r;
@@ -306,6 +325,7 @@ int sd_nfnl_nft_message_new_table(
int sd_nfnl_nft_message_new_rule(
sd_netlink *nfnl,
sd_netlink_message **ret,
int add, /* boolean */
int nfproto,
const char *table,
const char *chain) {
@@ -313,9 +333,15 @@ int sd_nfnl_nft_message_new_rule(
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(nfnl);
assert(ret);
assert(table);
assert(chain);
r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWRULE, NLM_F_CREATE);
if (add)
r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWRULE, NLM_F_CREATE);
else
r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_DELRULE, /* flags= */ 0);
if (r < 0)
return r;
@@ -334,6 +360,7 @@ int sd_nfnl_nft_message_new_rule(
int sd_nfnl_nft_message_new_set(
sd_netlink *nfnl,
sd_netlink_message **ret,
int add, /* boolean */
int nfproto,
const char *table,
const char *set_name,
@@ -343,9 +370,15 @@ int sd_nfnl_nft_message_new_set(
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(nfnl);
assert(ret);
assert(table);
assert(set_name);
r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWSET, NLM_F_CREATE);
if (add)
r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWSET, NLM_F_CREATE);
else
r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_DELSET, /* flags= */ 0);
if (r < 0)
return r;
@@ -357,13 +390,15 @@ int sd_nfnl_nft_message_new_set(
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, NFTA_SET_ID, ++set_id);
if (r < 0)
return r;
if (add) {
r = sd_netlink_message_append_u32(m, NFTA_SET_ID, ++set_id);
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, NFTA_SET_KEY_LEN, htobe32(klen));
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, NFTA_SET_KEY_LEN, htobe32(klen));
if (r < 0)
return r;
}
*ret = TAKE_PTR(m);
return r;
@@ -380,12 +415,15 @@ int sd_nfnl_nft_message_new_setelems(
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(nfnl);
assert(ret);
assert(table);
assert(set_name);
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);
r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_DELSETELEM, /* flags= */ 0);
if (r < 0)
return r;
@@ -412,6 +450,11 @@ int sd_nfnl_nft_message_append_setelem(
int r;
assert(m);
assert(key);
assert(key_len > 0);
assert(!data || data_len > 0);
r = sd_netlink_message_open_array(m, index);
if (r < 0)
return r;
@@ -153,11 +153,14 @@ static const NLAPolicy nfnl_nft_setelem_list_policies[] = {
DEFINE_POLICY_SET(nfnl_nft_setelem_list);
static const NLAPolicy nfnl_subsys_nft_policies[] = {
[NFT_MSG_DELTABLE] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_table, sizeof(struct nfgenmsg)),
[NFT_MSG_NEWTABLE] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_table, sizeof(struct nfgenmsg)),
[NFT_MSG_DELTABLE] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_table, sizeof(struct nfgenmsg)),
[NFT_MSG_NEWCHAIN] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_chain, sizeof(struct nfgenmsg)),
[NFT_MSG_DELCHAIN] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_chain, sizeof(struct nfgenmsg)),
[NFT_MSG_NEWRULE] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_rule, sizeof(struct nfgenmsg)),
[NFT_MSG_DELRULE] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_rule, sizeof(struct nfgenmsg)),
[NFT_MSG_NEWSET] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_set, sizeof(struct nfgenmsg)),
[NFT_MSG_DELSET] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_set, sizeof(struct nfgenmsg)),
[NFT_MSG_NEWSETELEM] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_setelem_list, sizeof(struct nfgenmsg)),
[NFT_MSG_DELSETELEM] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_setelem_list, sizeof(struct nfgenmsg)),
};
+26 -8
View File
@@ -331,7 +331,7 @@ static int sd_nfnl_message_new_masq_rule(
assert(IN_SET(family, AF_INET, AF_INET6));
assert(chain);
r = sd_nfnl_nft_message_new_rule(nfnl, &m, family, nft_table_name(), chain);
r = sd_nfnl_nft_message_new_rule(nfnl, &m, /* add= */ true, family, nft_table_name(), chain);
if (r < 0)
return r;
@@ -386,7 +386,7 @@ static int sd_nfnl_message_new_dnat_rule_pre(
assert(IN_SET(family, AF_INET, AF_INET6));
assert(chain);
r = sd_nfnl_nft_message_new_rule(nfnl, &m, family, nft_table_name(), chain);
r = sd_nfnl_nft_message_new_rule(nfnl, &m, /* add= */ true, family, nft_table_name(), chain);
if (r < 0)
return r;
@@ -449,7 +449,7 @@ static int sd_nfnl_message_new_dnat_rule_out(
assert(IN_SET(family, AF_INET, AF_INET6));
assert(chain);
r = sd_nfnl_nft_message_new_rule(nfnl, &m, family, nft_table_name(), chain);
r = sd_nfnl_nft_message_new_rule(nfnl, &m, /* add= */ true, family, nft_table_name(), chain);
if (r < 0)
return r;
@@ -548,7 +548,7 @@ static int nft_new_set(
assert(IN_SET(family, AF_INET, AF_INET6));
assert(set_name);
r = sd_nfnl_nft_message_new_set(nfnl, &m, family, nft_table_name(), set_name, set_id, klen);
r = sd_nfnl_nft_message_new_set(nfnl, &m, /* add= */ true, family, nft_table_name(), set_name, set_id, klen);
if (r < 0)
return r;
@@ -734,23 +734,23 @@ static int fw_nftables_init_family(sd_netlink *nfnl, int family) {
assert(IN_SET(family, AF_INET, AF_INET6));
/* Set F_EXCL so table add fails if the table already exists. */
r = sd_nfnl_nft_message_new_table(nfnl, &messages[msgcnt++], family, nft_table_name());
r = sd_nfnl_nft_message_new_table(nfnl, &messages[msgcnt++], /* add= */ true, family, nft_table_name());
if (r < 0)
return r;
r = sd_nfnl_nft_message_new_basechain(nfnl, &messages[msgcnt++], family, nft_table_name(),
r = sd_nfnl_nft_message_new_basechain(nfnl, &messages[msgcnt++], /* add= */ true, family, nft_table_name(),
"prerouting", "nat",
NF_INET_PRE_ROUTING, NF_IP_PRI_NAT_DST + 1);
if (r < 0)
return r;
r = sd_nfnl_nft_message_new_basechain(nfnl, &messages[msgcnt++], family, nft_table_name(),
r = sd_nfnl_nft_message_new_basechain(nfnl, &messages[msgcnt++], /* add= */ true, family, nft_table_name(),
"output", "nat",
NF_INET_LOCAL_OUT, NF_IP_PRI_NAT_DST + 1);
if (r < 0)
return r;
r = sd_nfnl_nft_message_new_basechain(nfnl, &messages[msgcnt++], family, nft_table_name(),
r = sd_nfnl_nft_message_new_basechain(nfnl, &messages[msgcnt++], /* add= */ true, family, nft_table_name(),
"postrouting", "nat",
NF_INET_POST_ROUTING, NF_IP_PRI_NAT_SRC + 1);
if (r < 0)
@@ -802,6 +802,24 @@ static int fw_nftables_init_family(sd_netlink *nfnl, int family) {
return 0;
}
int fw_nftables_del_table(sd_netlink *nfnl, int family) {
int r;
assert(nfnl);
assert(IN_SET(family, AF_INET, AF_INET6));
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
r = sd_nfnl_nft_message_new_table(nfnl, &m, /* add= */ false, family, nft_table_name());
if (r < 0)
return r;
r = sd_nfnl_call_batch(nfnl, &m, /* n_messages= */ 1, NFNL_DEFAULT_TIMEOUT_USECS);
if (r < 0 && r != -ENOENT)
return r;
return 0;
}
static int nft_message_append_setelem_iprange(
sd_netlink_message *m,
const union in_addr_union *source,
+2
View File
@@ -4,6 +4,8 @@
#include "conf-parser-forward.h"
#include "forward.h"
int fw_nftables_del_table(sd_netlink *nfnl, int family);
int fw_nftables_add_masquerade(
sd_netlink *nfnl,
bool add,
+15 -1
View File
@@ -79,14 +79,28 @@ static int intro(void) {
ASSERT_OK_ERRNO(setenv("SYSTEMD_FIREWALL_UTIL_NFT_TABLE_NAME", "io.systemd-test.nat", /* overwrite= */ true));
ASSERT_OK_ERRNO(setenv("SYSTEMD_FIREWALL_UTIL_DNAT_MAP_NAME", "test_map_port_ipport", /* overwrite= */ true));
r = sd_nfnl_socket_open(&nfnl);
_cleanup_(sd_netlink_unrefp) sd_netlink *nl = NULL;
r = sd_nfnl_socket_open(&nl);
if (r < 0)
return log_tests_skipped_errno(r, "Failed to initialize nftables");
r = fw_nftables_del_table(nl, AF_INET);
if (r < 0)
return log_tests_skipped_errno(r, "Failed to clear nftables test table");
ASSERT_OK(fw_nftables_del_table(nl, AF_INET6));
nfnl = TAKE_PTR(nl);
return 0;
}
static int outro(void) {
if (!nfnl)
return 0;
ASSERT_OK(fw_nftables_del_table(nfnl, AF_INET));
ASSERT_OK(fw_nftables_del_table(nfnl, AF_INET6));
sd_netlink_unref(nfnl);
return 0;
}