keep table/chain/rule object around after commiting a batch. If we get errors, we can correlate them with the line numbers of the restore file. Currently uses fprintf, xtables_error() would be more appropriate, but then it exits after first error. We probably need ot add xtables_warn() and use that. Signed-off-by: Florian Westphal <fw@xxxxxxxxx> --- iptables/nft.c | 175 +++++++++++++++++++++++++++++++++++++++------ iptables/nft.h | 5 ++ iptables/xtables-restore.c | 1 + 3 files changed, 159 insertions(+), 22 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index b6426a96de18..d7c57f1042b4 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -81,6 +81,7 @@ int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh, return 0; } +static LIST_HEAD(err_list); static LIST_HEAD(batch_page_list); static int batch_num_pages; @@ -190,6 +191,23 @@ static ssize_t mnl_nft_socket_sendmsg(const struct mnl_socket *nl) return ret; } +struct mnl_err { + struct list_head head; + int err; + uint32_t seqnum; +}; + +static void mnl_err_list_node_add(int error, int seqnum) +{ + struct mnl_err *err = malloc(sizeof(struct mnl_err)); + + if (err) { + err->seqnum = seqnum; + err->err = error; + list_add_tail(&err->head, &err_list); + } +} + static int mnl_nftnl_batch_talk(struct nft_handle *h) { int ret, fd = mnl_socket_get_fd(h->nl); @@ -214,6 +232,8 @@ static int mnl_nftnl_batch_talk(struct nft_handle *h) return -1; while (ret > 0 && FD_ISSET(fd, &readfds)) { + struct nlmsghdr *nlh = (struct nlmsghdr *)rcv_buf; + ret = mnl_socket_recvfrom(h->nl, rcv_buf, sizeof(rcv_buf)); if (ret == -1) return -1; @@ -222,8 +242,11 @@ static int mnl_nftnl_batch_talk(struct nft_handle *h) /* Annotate first error and continue, make sure we get all * acknoledgments. */ - if (!err && ret == -1) - err = errno; + if (ret == -1) { + mnl_err_list_node_add(errno, nlh->nlmsg_seq); + if (!err) + err = errno; + } ret = select(fd+1, &readfds, NULL, NULL, &tv); if (ret == -1) @@ -274,14 +297,80 @@ enum obj_action { struct obj_update { struct list_head head; enum obj_update_type type; + int seq; union { struct nftnl_table *table; struct nftnl_chain *chain; - struct nftnl_rule *rule; + struct nftnl_rule *rule; void *ptr; }; + struct { + unsigned int lineno; + } error; }; +static void mnl_show_error(const struct nft_handle *h, + const struct obj_update *o, + const struct mnl_err *err) +{ + const char *prog = xt_params->program_name; + static const char *type_name[] = { + [NFT_COMPAT_TABLE_ADD] = "TABLE_ADD", + [NFT_COMPAT_TABLE_FLUSH] = "TABLE_FLUSH", + [NFT_COMPAT_CHAIN_ADD] = "CHAIN_ADD", + [NFT_COMPAT_CHAIN_USER_ADD] = "CHAIN_USER_ADD", + [NFT_COMPAT_CHAIN_USER_DEL] = "CHAIN_USER_DEL", + [NFT_COMPAT_CHAIN_USER_FLUSH] = "CHAIN_USER_FLUSH", + [NFT_COMPAT_CHAIN_UPDATE] = "CHAIN_UPDATE", + [NFT_COMPAT_CHAIN_RENAME] = "CHAIN_RENAME", + [NFT_COMPAT_RULE_APPEND] = "RULE_APPEND", + [NFT_COMPAT_RULE_INSERT] = "RULE_INSERT", + [NFT_COMPAT_RULE_REPLACE] = "RULE_REPLACE", + [NFT_COMPAT_RULE_DELETE] = "RULE_DELETE", + [NFT_COMPAT_RULE_FLUSH] = "RULE_FLUSH", + }; + char errmsg[256]; + char tcr[128]; + + snprintf(errmsg, sizeof(errmsg), "%s: line %u: %s failed (%s): ", + prog, o->error.lineno, type_name[o->type], strerror(err->err)); + + switch (o->type) { + case NFT_COMPAT_TABLE_ADD: + case NFT_COMPAT_TABLE_FLUSH: + snprintf(tcr, sizeof(tcr), "table %s", + nftnl_table_get_str(o->table, NFTNL_TABLE_NAME)); + break; + case NFT_COMPAT_CHAIN_ADD: + case NFT_COMPAT_CHAIN_USER_ADD: + case NFT_COMPAT_CHAIN_USER_DEL: + case NFT_COMPAT_CHAIN_USER_FLUSH: + case NFT_COMPAT_CHAIN_UPDATE: + case NFT_COMPAT_CHAIN_RENAME: + snprintf(tcr, sizeof(tcr), "chain %s", + nftnl_chain_get_str(o->chain, NFTNL_CHAIN_NAME)); + break; + case NFT_COMPAT_RULE_APPEND: + case NFT_COMPAT_RULE_INSERT: + case NFT_COMPAT_RULE_REPLACE: + case NFT_COMPAT_RULE_DELETE: + case NFT_COMPAT_RULE_FLUSH: + snprintf(tcr, sizeof(tcr), "rule in chain %s", + nftnl_rule_get_str(o->rule, NFTNL_RULE_CHAIN)); +#if 0 + { + struct iptables_command_state cs = {}; + nft_rule_to_iptables_command_state(o->rule, &cs); + nft_rule_print_save(&cs, o->rule, NFT_RULE_APPEND, FMT_NOCOUNTS); + } +#endif + break; + } + + xtables_error + fprintf(stderr, "%s: %s", errmsg, tcr); +} + static int batch_add(struct nft_handle *h, enum obj_update_type type, void *ptr) { struct obj_update *obj; @@ -291,6 +380,7 @@ static int batch_add(struct nft_handle *h, enum obj_update_type type, void *ptr) return -1; obj->ptr = ptr; + obj->error.lineno = h->error.lineno; obj->type = type; list_add_tail(&obj->head, &h->obj_list); h->obj_list_num++; @@ -2288,7 +2378,6 @@ static void nft_compat_table_batch_add(struct nft_handle *h, uint16_t type, nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch), type, h->family, flags, seq); nftnl_table_nlmsg_build_payload(nlh, table); - nftnl_table_free(table); } static void nft_compat_chain_batch_add(struct nft_handle *h, uint16_t type, @@ -2301,7 +2390,6 @@ static void nft_compat_chain_batch_add(struct nft_handle *h, uint16_t type, type, h->family, flags, seq); nftnl_chain_nlmsg_build_payload(nlh, chain); nft_chain_print_debug(chain, nlh); - nftnl_chain_free(chain); } static void nft_compat_rule_batch_add(struct nft_handle *h, uint16_t type, @@ -2314,84 +2402,110 @@ static void nft_compat_rule_batch_add(struct nft_handle *h, uint16_t type, type, h->family, flags, seq); nftnl_rule_nlmsg_build_payload(nlh, rule); nft_rule_print_debug(rule, nlh); - nftnl_rule_free(rule); +} + +static void batch_obj_del(struct nft_handle *h, struct obj_update *o) +{ + switch (o->type) { + case NFT_COMPAT_TABLE_ADD: + case NFT_COMPAT_TABLE_FLUSH: + nftnl_table_free(o->table); + break; + case NFT_COMPAT_CHAIN_ADD: + case NFT_COMPAT_CHAIN_USER_ADD: + case NFT_COMPAT_CHAIN_USER_DEL: + case NFT_COMPAT_CHAIN_USER_FLUSH: + case NFT_COMPAT_CHAIN_UPDATE: + case NFT_COMPAT_CHAIN_RENAME: + nftnl_chain_free(o->chain); + break; + case NFT_COMPAT_RULE_APPEND: + case NFT_COMPAT_RULE_INSERT: + case NFT_COMPAT_RULE_REPLACE: + case NFT_COMPAT_RULE_DELETE: + case NFT_COMPAT_RULE_FLUSH: + nftnl_rule_free(o->rule); + break; + } + h->obj_list_num--; + list_del(&o->head); + free(o); } static int nft_action(struct nft_handle *h, int action) { struct obj_update *n, *tmp; + struct mnl_err *err; uint32_t seq = 1; int ret = 0; mnl_nftnl_batch_begin(h->batch, seq++); - list_for_each_entry_safe(n, tmp, &h->obj_list, head) { + list_for_each_entry(n, &h->obj_list, head) { + n->seq = seq++; switch (n->type) { case NFT_COMPAT_TABLE_ADD: nft_compat_table_batch_add(h, NFT_MSG_NEWTABLE, - NLM_F_CREATE, seq++, + NLM_F_CREATE, n->seq, n->table); break; case NFT_COMPAT_TABLE_FLUSH: nft_compat_table_batch_add(h, NFT_MSG_DELTABLE, 0, - seq++, n->table); + n->seq, n->table); break; case NFT_COMPAT_CHAIN_ADD: nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN, - NLM_F_CREATE, seq++, + NLM_F_CREATE, n->seq, n->chain); break; case NFT_COMPAT_CHAIN_USER_ADD: nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN, - NLM_F_EXCL, seq++, + NLM_F_EXCL, n->seq, n->chain); break; case NFT_COMPAT_CHAIN_USER_DEL: nft_compat_chain_batch_add(h, NFT_MSG_DELCHAIN, - NLM_F_NONREC, seq++, + NLM_F_NONREC, n->seq, n->chain); break; case NFT_COMPAT_CHAIN_USER_FLUSH: nft_compat_chain_batch_add(h, NFT_MSG_DELCHAIN, - 0, seq++, + 0, n->seq, n->chain); break; case NFT_COMPAT_CHAIN_UPDATE: nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN, h->restore ? NLM_F_CREATE : 0, - seq++, n->chain); + n->seq, n->chain); break; case NFT_COMPAT_CHAIN_RENAME: nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN, 0, - seq++, n->chain); + n->seq, n->chain); break; case NFT_COMPAT_RULE_APPEND: nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE, NLM_F_CREATE | NLM_F_APPEND, - seq++, n->rule); + n->seq, n->rule); break; case NFT_COMPAT_RULE_INSERT: nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE, - NLM_F_CREATE, seq++, + NLM_F_CREATE, n->seq, n->rule); break; case NFT_COMPAT_RULE_REPLACE: nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE, NLM_F_CREATE | NLM_F_REPLACE, - seq++, n->rule); + n->seq, n->rule); break; case NFT_COMPAT_RULE_DELETE: case NFT_COMPAT_RULE_FLUSH: nft_compat_rule_batch_add(h, NFT_MSG_DELRULE, 0, - seq++, n->rule); + n->seq, n->rule); break; } - h->obj_list_num--; - list_del(&n->head); - free(n); if (!mnl_nlmsg_batch_next(h->batch)) h->batch = mnl_nftnl_batch_page_add(h->batch); @@ -2410,6 +2524,23 @@ static int nft_action(struct nft_handle *h, int action) ret = mnl_nftnl_batch_talk(h); + list_for_each_entry(err, &err_list, head) { + list_for_each_entry_safe(n, tmp, &h->obj_list, head) { + bool next_err = false; + + if (err->seqnum == n->seq) { + mnl_show_error(h, n, err); + next_err = true; + } + batch_obj_del(h, n); + if (next_err) + break; + } + } + + list_for_each_entry_safe(n, tmp, &h->obj_list, head) + batch_obj_del(h, n); + mnl_nlmsg_batch_reset(h->batch); return ret == 0 ? 1 : 0; diff --git a/iptables/nft.h b/iptables/nft.h index 5d0576c8ee45..3606ef034796 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -37,6 +37,11 @@ struct nft_handle { struct builtin_table *tables; struct nftnl_rule_list *rule_cache; bool restore; + + /* meta data, for error reporting */ + struct { + unsigned int lineno; + } error; }; extern struct builtin_table xtables_ipv4[TABLES_MAX]; diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index d977dabfae50..57b8c8b68d4d 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -228,6 +228,7 @@ void xtables_restore_parse(struct nft_handle *h, int ret = 0; line++; + h->error.lineno = line; if (buffer[0] == '\n') continue; else if (buffer[0] == '#') { -- 2.16.1 -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html