From 9e62dc8637f210cdeaed784396fecab9b6e5f043 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 20 Jan 2013 20:19:20 +0100 Subject: xtables-restore: support atomic commit Use new services in nf_tables to support atomic commit. Commit per table, although we support global commit at once, call commit for each table to emulate iptables-restore behaviour by now. Keep table dormant/wake up code in iptables/nft.c as it can be used in the future. Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 56 +++++++++++++++++++++++++++++++++++++++++++ iptables/nft.h | 7 ++++++ iptables/xtables-restore.c | 22 +++++++++++++---- iptables/xtables-standalone.c | 2 ++ 4 files changed, 82 insertions(+), 5 deletions(-) (limited to 'iptables') diff --git a/iptables/nft.c b/iptables/nft.c index fd19ff55..f42e4377 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -946,6 +946,10 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, flags |= NLM_F_REPLACE; } + if (h->commit) { + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, + NFT_RULE_F_COMMIT); + } nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, h->family, flags, h->seq); @@ -1626,6 +1630,11 @@ __nft_rule_flush(struct nft_handle *h, const char *table, const char *chain) nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table); nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain); + if (h->commit) { + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, + NFT_RULE_F_COMMIT); + } + /* Delete all rules in this table + chain */ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, h->family, NLM_F_ACK, h->seq); @@ -2773,6 +2782,10 @@ int nft_rule_delete(struct nft_handle *h, const char *chain, if (r != NULL) { ret = 1; + if (h->commit) { + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, + NFT_RULE_F_COMMIT); + } DEBUGP("deleting rule\n"); __nft_rule_del(h, r); } else @@ -2802,6 +2815,10 @@ int nft_rule_delete_num(struct nft_handle *h, const char *chain, if (r != NULL) { ret = 1; + if (h->commit) { + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, + NFT_RULE_F_COMMIT); + } DEBUGP("deleting rule by number %d\n", rulenum); __nft_rule_del(h, r); } else @@ -2834,6 +2851,10 @@ int nft_rule_replace(struct nft_handle *h, const char *chain, (unsigned long long) nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE)); + if (h->commit) { + nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS, + NFT_RULE_F_COMMIT); + } ret = nft_rule_add(h, chain, table, cs, true, nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE), verbose); @@ -3435,6 +3456,41 @@ next: return 1; } +static int nft_action(struct nft_handle *h, int type) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t seq; + int ret; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_NFTABLES<< 8) | type; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_INET; + nfg->version = NFNETLINK_V0; + nfg->res_id = 0; + + ret = mnl_talk(h, nlh, NULL, NULL); + if (ret < 0) { + if (errno != EEXIST) + perror("mnl-talk:nft_commit"); + } + return ret; +} + +int nft_commit(struct nft_handle *h) +{ + return nft_action(h, NFT_MSG_COMMIT); +} + +int nft_abort(struct nft_handle *h) +{ + return nft_action(h, NFT_MSG_ABORT); +} + int nft_compatible_revision(const char *name, uint8_t rev, int opt) { struct mnl_socket *nl; diff --git a/iptables/nft.h b/iptables/nft.h index f7ed0a38..834fff0d 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -8,6 +8,7 @@ struct nft_handle { struct mnl_socket *nl; uint32_t portid; uint32_t seq; + bool commit; }; int nft_init(struct nft_handle *h); @@ -55,6 +56,12 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain, const char *tabl int nft_rule_save(struct nft_handle *h, const char *table, bool counters); int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table); +/* + * global commit and abort + */ +int nft_commit(struct nft_handle *h); +int nft_abort(struct nft_handle *h); + /* * revision compatibility. */ diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index 9778a9f7..ca9e0c05 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -164,6 +164,7 @@ xtables_restore_main(int argc, char *argv[]) { struct nft_handle h = { .family = AF_INET, /* default to IPv4 */ + .commit = true, }; char buffer[10240]; int c; @@ -253,10 +254,14 @@ xtables_restore_main(int argc, char *argv[]) continue; } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) { if (!testing) { - if (nft_table_wake_dormant(&h, curtable) < 0) { - fprintf(stderr, "Failed to wake up " - "dormant table `%s'\n", - curtable); + /* Commit per table, although we support + * global commit at once, stick by now to + * the existing behaviour. + */ + if (nft_commit(&h)) { + fprintf(stderr, "Failed to commit " + "table %s\n", + curtable); } DEBUGP("Calling commit\n"); ret = 1; @@ -288,7 +293,6 @@ xtables_restore_main(int argc, char *argv[]) if (tablename && (strcmp(tablename, table) != 0)) continue; - nft_table_set_dormant(&h, table); if (noflush == 0) { DEBUGP("Cleaning all chains of table '%s'\n", table); @@ -426,6 +430,14 @@ xtables_restore_main(int argc, char *argv[]) DEBUGP("argv[%u]: %s\n", a, newargv[a]); ret = do_commandx(&h, newargc, newargv, &newargv[2]); + if (ret < 0) { + ret = nft_abort(&h); + if (ret < 0) { + fprintf(stderr, "failed to abort " + "commit operation\n"); + } + exit(1); + } free_argv(); fflush(stdout); diff --git a/iptables/xtables-standalone.c b/iptables/xtables-standalone.c index f746c902..28416117 100644 --- a/iptables/xtables-standalone.c +++ b/iptables/xtables-standalone.c @@ -46,6 +46,8 @@ xtables_main(int argc, char *argv[]) char *table = "filter"; struct nft_handle h; + memset(&h, 0, sizeof(h)); + iptables_globals.program_name = "xtables"; ret = xtables_init_all(&xtables_globals, NFPROTO_IPV4); if (ret < 0) { -- cgit v1.2.3