Skip to content

Commit bc5a9b8

Browse files
author
Florian Westphal
committed
firewall-util-nft: attempt table recreation when add operation fails
When someone runs 'nft flush ruleset' in the same net namespace this will also tear down the systemd nat table. Unlike iptables -t nat -F, which will remove all rules added by the systemd iptables backend, iptables has builtin chains that cannot be deleted. IOW, the next add operation will 'just work'. In the nftables case however, the entire table gets removed. When the systemd nat table is removed by an external entity next attempt to add a set element will yield -ENOENT. If this happens, recreate the table, and, if successful, re-do the add operation. Note that this doesn't protect against external sabotage such as a running 'while true; nft flush ruleset;done'. However, there is nothing that could be done short of extending the kernel to allow tables to be "frozen" or otherwise tied to a process such as systemd-networkd.
1 parent 715a70e commit bc5a9b8

1 file changed

Lines changed: 55 additions & 0 deletions

File tree

src/shared/firewall-util-nft.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,40 @@ static int nft_message_add_setelem_iprange(sd_netlink_message *m,
778778
return 0;
779779
}
780780

781+
/* When someone runs 'nft flush ruleset' in the same net namespace
782+
* this will also tear down the systemd nat table.
783+
*
784+
* Unlike iptables -t nat -F (which will remove all rules added by the
785+
* systemd iptables backend, iptables has builtin chains that cannot be
786+
* deleted -- the next add operation will 'just work'.
787+
*
788+
* In the nftables case, everything gets removed. The next add operation
789+
* will yield -ENOENT.
790+
*
791+
* If we see -ENOENT on add, replay the inital table setup.
792+
* If that works, re-do the add operation.
793+
*
794+
* Note that this doesn't protect against external sabotage such as a
795+
* 'while true; nft flush ruleset;done'. There is nothing that could be
796+
* done about that short of extending the kernel to allow tables to be
797+
* owned by stystemd-networkd and making them non-deleteable except by
798+
* the 'owning process'.
799+
*/
800+
static int fw_nftables_recreate_table(sd_netlink *nfnl, int af, sd_netlink_message **old, size_t size) {
801+
int r = fw_nftables_init_family(nfnl, af);
802+
803+
if (r != 0)
804+
return r;
805+
806+
while (size > 0) {
807+
size_t i = --size;
808+
809+
old[i] = sd_netlink_message_unref(old[i]);
810+
}
811+
812+
return 0;
813+
}
814+
781815
#define NFT_MASQ_MSGS 3
782816

783817
int fw_nftables_add_masquerade(
@@ -787,12 +821,14 @@ int fw_nftables_add_masquerade(
787821
const union in_addr_union *source,
788822
unsigned int source_prefixlen) {
789823
sd_netlink_message *transaction[NFT_MASQ_MSGS] = {};
824+
bool retry = true;
790825
size_t tsize;
791826
int r;
792827

793828
if (!source || source_prefixlen == 0)
794829
return -EINVAL;
795830

831+
again:
796832
r = sd_nfnl_message_batch_begin(ctx->nfnl, &transaction[0]);
797833
if (r < 0)
798834
return r;
@@ -817,6 +853,14 @@ int fw_nftables_add_masquerade(
817853
++tsize;
818854
r = nfnl_netlink_sendv(ctx->nfnl, transaction, tsize);
819855

856+
if (retry && r == -ENOENT) {
857+
int tmp = fw_nftables_recreate_table(ctx->nfnl, af, transaction, tsize);
858+
if (tmp == 0) {
859+
retry = false;
860+
goto again;
861+
}
862+
}
863+
820864
out_unref:
821865
while (tsize > 0)
822866
sd_netlink_message_unref(transaction[--tsize]);
@@ -836,6 +880,7 @@ int fw_nftables_add_local_dnat(
836880
const union in_addr_union *previous_remote) {
837881
uint32_t data[2], key[2];
838882
sd_netlink_message *transaction[NFT_DNAT_MSGS] = {};
883+
bool retry = true;
839884
size_t tsize;
840885
int r;
841886

@@ -850,6 +895,7 @@ int fw_nftables_add_local_dnat(
850895
if (local_port <= 0)
851896
return -EINVAL;
852897

898+
again:
853899
key[0] = protocol;
854900
key[1] = htobe16(local_port);
855901

@@ -896,6 +942,15 @@ int fw_nftables_add_local_dnat(
896942
assert(tsize <= NFT_DNAT_MSGS);
897943
r = nfnl_netlink_sendv(ctx->nfnl, transaction, tsize);
898944

945+
if (retry && r == -ENOENT) {
946+
int tmp = fw_nftables_recreate_table(ctx->nfnl, af, transaction, tsize);
947+
948+
if (tmp == 0) {
949+
retry = false;
950+
goto again;
951+
}
952+
}
953+
899954
out_unref:
900955
while (tsize > 0)
901956
sd_netlink_message_unref(transaction[--tsize]);

0 commit comments

Comments
 (0)