diff options
Diffstat (limited to 'iptables/nft.c')
-rw-r--r-- | iptables/nft.c | 357 |
1 files changed, 152 insertions, 205 deletions
diff --git a/iptables/nft.c b/iptables/nft.c index e3b07e03..6cb03a0d 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -61,10 +61,8 @@ int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh, int ret; char buf[MNL_SOCKET_BUFFER_SIZE]; - if (mnl_socket_sendto(h->nl, nlh, nlh->nlmsg_len) < 0) { - perror("mnl_socket_send"); + if (mnl_socket_sendto(h->nl, nlh, nlh->nlmsg_len) < 0) return -1; - } ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf)); while (ret > 0) { @@ -142,6 +140,18 @@ static void mnl_nft_set_sndbuffer(const struct mnl_socket *nl) nlbuffsiz = newbuffsiz; } +static void mnl_nft_batch_reset(void) +{ + struct batch_page *batch_page, *next; + + list_for_each_entry_safe(batch_page, next, &batch_page_list, head) { + list_del(&batch_page->head); + free(batch_page->batch); + free(batch_page); + batch_num_pages--; + } +} + static ssize_t mnl_nft_socket_sendmsg(const struct mnl_socket *nl) { static const struct sockaddr_nl snl = { @@ -154,12 +164,12 @@ static ssize_t mnl_nft_socket_sendmsg(const struct mnl_socket *nl) .msg_iov = iov, .msg_iovlen = batch_num_pages, }; - struct batch_page *batch_page, *next; - int i = 0; + struct batch_page *batch_page; + int i = 0, ret; mnl_nft_set_sndbuffer(nl); - list_for_each_entry_safe(batch_page, next, &batch_page_list, head) { + list_for_each_entry(batch_page, &batch_page_list, head) { iov[i].iov_base = mnl_nlmsg_batch_head(batch_page->batch); iov[i].iov_len = mnl_nlmsg_batch_size(batch_page->batch); i++; @@ -169,26 +179,14 @@ static ssize_t mnl_nft_socket_sendmsg(const struct mnl_socket *nl) mnl_nlmsg_batch_size(batch_page->batch), sizeof(struct nfgenmsg)); #endif - list_del(&batch_page->head); - free(batch_page->batch); - free(batch_page); - batch_num_pages--; } - return sendmsg(mnl_socket_get_fd(nl), &msg, 0); -} + ret = sendmsg(mnl_socket_get_fd(nl), &msg, 0); + mnl_nft_batch_reset(); -static int cb_err(const struct nlmsghdr *nlh, void *data) -{ - /* We can provide better error reporting than iptables-restore */ - errno = EINVAL; - return MNL_CB_ERROR; + return ret; } -static mnl_cb_t cb_ctl_array[NLMSG_MIN_TYPE] = { - [NLMSG_ERROR] = cb_err, -}; - static int mnl_nft_batch_talk(struct nft_handle *h) { int ret, fd = mnl_socket_get_fd(h->nl); @@ -201,78 +199,59 @@ static int mnl_nft_batch_talk(struct nft_handle *h) int err = 0; ret = mnl_nft_socket_sendmsg(h->nl); - if (ret == -1) { - perror("mnl_socket_sendmsg"); + if (ret == -1) return -1; - } FD_ZERO(&readfds); FD_SET(fd, &readfds); /* receive and digest all the acknowledgments from the kernel. */ ret = select(fd+1, &readfds, NULL, NULL, &tv); - if (ret == -1) { - perror("select"); + if (ret == -1) return -1; - } + while (ret > 0 && FD_ISSET(fd, &readfds)) { ret = mnl_socket_recvfrom(h->nl, rcv_buf, sizeof(rcv_buf)); - if (ret == -1) { - perror("mnl_socket_recvfrom"); + if (ret == -1) return -1; - } - ret = mnl_cb_run2(rcv_buf, ret, 0, h->portid, - NULL, NULL, cb_ctl_array, - MNL_ARRAY_SIZE(cb_ctl_array)); - /* Continue on error, make sure we get all acknoledgments */ - if (ret == -1) + ret = mnl_cb_run(rcv_buf, ret, 0, h->portid, NULL, NULL); + /* Annotate first error and continue, make sure we get all + * acknoledgments. + */ + if (!err && ret == -1) err = errno; ret = select(fd+1, &readfds, NULL, NULL, &tv); - if (ret == -1) { - perror("select"); + if (ret == -1) return -1; - } + FD_ZERO(&readfds); FD_SET(fd, &readfds); } + errno = err; return err ? -1 : 0; } -static void mnl_nft_batch_put(struct mnl_nlmsg_batch *batch, int type, - uint32_t seq) +static void mnl_nft_batch_begin(struct mnl_nlmsg_batch *batch, uint32_t seq) { - struct nlmsghdr *nlh; - struct nfgenmsg *nfg; - - nlh = mnl_nlmsg_put_header(mnl_nlmsg_batch_current(batch)); - nlh->nlmsg_type = type; - nlh->nlmsg_flags = NLM_F_REQUEST; - nlh->nlmsg_seq = seq; - - nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); - nfg->nfgen_family = AF_INET; - nfg->version = NFNETLINK_V0; - nfg->res_id = NFNL_SUBSYS_NFTABLES; - + nft_batch_begin(mnl_nlmsg_batch_current(batch), seq); if (!mnl_nlmsg_batch_next(batch)) mnl_nft_batch_page_add(batch); } -static void mnl_nft_batch_begin(struct mnl_nlmsg_batch *batch, uint32_t seq) -{ - mnl_nft_batch_put(batch, NFNL_MSG_BATCH_BEGIN, seq); -} - static void mnl_nft_batch_end(struct mnl_nlmsg_batch *batch, uint32_t seq) { - mnl_nft_batch_put(batch, NFNL_MSG_BATCH_END, seq); + nft_batch_end(mnl_nlmsg_batch_current(batch), seq); + if (!mnl_nlmsg_batch_next(batch)) + mnl_nft_batch_page_add(batch); } enum obj_update_type { NFT_COMPAT_TABLE_ADD, NFT_COMPAT_CHAIN_ADD, + NFT_COMPAT_CHAIN_USER_ADD, + NFT_COMPAT_CHAIN_USER_DEL, NFT_COMPAT_CHAIN_UPDATE, NFT_COMPAT_RULE_APPEND, NFT_COMPAT_RULE_INSERT, @@ -539,7 +518,7 @@ static int nft_table_builtin_add(struct nft_handle *h, return ret; } -struct nft_chain * +static struct nft_chain * nft_chain_builtin_alloc(struct builtin_table *table, struct builtin_chain *chain, int policy) { @@ -582,12 +561,13 @@ int nft_chain_add(struct nft_handle *h, struct nft_chain *c, uint16_t flags) return mnl_talk(h, nlh, NULL, NULL); } -void nft_chain_builtin_add(struct nft_handle *h, struct builtin_table *table, - struct builtin_chain *chain, int policy) +static void nft_chain_builtin_add(struct nft_handle *h, + struct builtin_table *table, + struct builtin_chain *chain) { struct nft_chain *c; - c = nft_chain_builtin_alloc(table, chain, policy); + c = nft_chain_builtin_alloc(table, chain, NF_ACCEPT); if (c == NULL) return; @@ -598,7 +578,7 @@ void nft_chain_builtin_add(struct nft_handle *h, struct builtin_table *table, } /* find if built-in table already exists */ -struct builtin_table * +static struct builtin_table * nft_table_builtin_find(struct nft_handle *h, const char *table) { int i; @@ -619,7 +599,7 @@ nft_table_builtin_find(struct nft_handle *h, const char *table) } /* find if built-in chain already exists */ -struct builtin_chain * +static struct builtin_chain * nft_chain_builtin_find(struct builtin_table *t, const char *chain) { int i; @@ -635,30 +615,28 @@ nft_chain_builtin_find(struct builtin_table *t, const char *chain) return found ? &t->chains[i] : NULL; } -static void -__nft_chain_builtin_init(struct nft_handle *h, - struct builtin_table *table, const char *chain, - int policy) +static void nft_chain_builtin_init(struct nft_handle *h, + struct builtin_table *table) { - int i, default_policy; + int i; + struct nft_chain_list *list = nft_chain_dump(h); + struct nft_chain *c; - /* Initialize all built-in chains. Exception, for e one received as - * parameter, set the default policy as requested. - */ + /* Initialize built-in chains if they don't exist yet */ for (i=0; i<NF_IP_NUMHOOKS && table->chains[i].name != NULL; i++) { - if (chain && strcmp(table->chains[i].name, chain) == 0) - default_policy = policy; - else - default_policy = NF_ACCEPT; - nft_chain_builtin_add(h, table, &table->chains[i], - default_policy); + c = nft_chain_list_find(list, table->name, + table->chains[i].name); + if (c != NULL) + continue; + + nft_chain_builtin_add(h, table, &table->chains[i]); } + + nft_chain_list_free(list); } -int -nft_chain_builtin_init(struct nft_handle *h, const char *table, - const char *chain, int policy) +static int nft_xt_builtin_init(struct nft_handle *h, const char *table) { int ret = 0; struct builtin_table *t; @@ -673,7 +651,7 @@ nft_chain_builtin_init(struct nft_handle *h, const char *table, if (errno == EEXIST) goto out; } - __nft_chain_builtin_init(h, t, chain, policy); + nft_chain_builtin_init(h, t); out: return ret; } @@ -732,15 +710,12 @@ err: int nft_init(struct nft_handle *h, struct builtin_table *t) { h->nl = mnl_socket_open(NETLINK_NETFILTER); - if (h->nl == NULL) { - perror("mnl_socket_open"); + if (h->nl == NULL) return -1; - } - if (mnl_socket_bind(h->nl, 0, MNL_SOCKET_AUTOPID) < 0) { - perror("mnl_socket_bind"); + if (mnl_socket_bind(h->nl, 0, MNL_SOCKET_AUTOPID) < 0) return -1; - } + h->portid = mnl_socket_get_portid(h->nl); h->tables = t; @@ -1019,7 +994,7 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table, /* If built-in chains don't exist for this table, create them */ if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) - nft_chain_builtin_init(h, table, chain, NF_ACCEPT); + nft_xt_builtin_init(h, table); nft_fn = nft_rule_append; @@ -1074,15 +1049,11 @@ static int nft_chain_list_cb(const struct nlmsghdr *nlh, void *data) struct nft_chain_list *list = data; c = nft_chain_alloc(); - if (c == NULL) { - perror("OOM"); + if (c == NULL) goto err; - } - if (nft_chain_nlmsg_parse(nlh, c) < 0) { - perror("nft_rule_nlmsg_parse"); + if (nft_chain_nlmsg_parse(nlh, c) < 0) goto out; - } nft_chain_list_add_tail(c, list); @@ -1180,15 +1151,11 @@ static int nft_rule_list_cb(const struct nlmsghdr *nlh, void *data) struct nft_rule_list *list = data; r = nft_rule_alloc(); - if (r == NULL) { - perror("OOM"); + if (r == NULL) goto err; - } - if (nft_rule_nlmsg_parse(nlh, r) < 0) { - perror("nft_rule_nlmsg_parse"); + if (nft_rule_nlmsg_parse(nlh, r) < 0) goto out; - } nft_rule_list_add_tail(r, list); @@ -1328,14 +1295,14 @@ err: int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table) { - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh; struct nft_chain *c; int ret; + nft_fn = nft_chain_user_add; + /* If built-in chains don't exist for this table, create them */ if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) - nft_chain_builtin_init(h, table, NULL, NF_ACCEPT); + nft_xt_builtin_init(h, table); c = nft_chain_alloc(); if (c == NULL) @@ -1344,12 +1311,19 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table); nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain); - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family, - NLM_F_ACK|NLM_F_EXCL, h->seq); - nft_chain_nlmsg_build_payload(nlh, c); - nft_chain_free(c); + if (h->batch_support) { + ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c); + } else { + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; - ret = mnl_talk(h, nlh, NULL, NULL); + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, + h->family, + NLM_F_ACK|NLM_F_EXCL, h->seq); + nft_chain_nlmsg_build_payload(nlh, c); + nft_chain_free(c); + ret = mnl_talk(h, nlh, NULL, NULL); + } /* the core expects 1 for success and 0 for error */ return ret == 0 ? 1 : 0; @@ -1400,7 +1374,11 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *tabl if (chain != NULL && strcmp(chain, chain_name) != 0) goto next; - ret = __nft_chain_del(h, c); + if (h->batch_support) + ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_DEL, c); + else + ret = __nft_chain_del(h, c); + if (ret < 0) break; @@ -1414,11 +1392,14 @@ next: nft_chain_list_iter_destroy(iter); err: - nft_chain_list_free(list); + if (!h->batch_support) + nft_chain_list_free(list); /* chain not found */ - if (ret < 0 && deleted_ctr == 0) + if (deleted_ctr == 0) { + ret = -1; errno = ENOENT; + } /* the core expects 1 for success and 0 for error */ return ret == 0 ? 1 : 0; @@ -1472,15 +1453,13 @@ nft_chain_find(struct nft_handle *h, const char *table, const char *chain) int nft_chain_user_rename(struct nft_handle *h,const char *chain, const char *table, const char *newname) { - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh; struct nft_chain *c; uint64_t handle; int ret; /* If built-in chains don't exist for this table, create them */ if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) - nft_chain_builtin_init(h, table, NULL, NF_ACCEPT); + nft_xt_builtin_init(h, table); /* Find the old chain to be renamed */ c = nft_chain_find(h, table, chain); @@ -1499,12 +1478,19 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain, nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)newname); nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_HANDLE, handle); - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family, - NLM_F_ACK, h->seq); - nft_chain_nlmsg_build_payload(nlh, c); - nft_chain_free(c); + if (h->batch_support) { + ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c); + } else { + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; - ret = mnl_talk(h, nlh, NULL, NULL); + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, + h->family, NLM_F_ACK, h->seq); + nft_chain_nlmsg_build_payload(nlh, c); + nft_chain_free(c); + + ret = mnl_talk(h, nlh, NULL, NULL); + } /* the core expects 1 for success and 0 for error */ return ret == 0 ? 1 : 0; @@ -1516,15 +1502,11 @@ static int nft_table_list_cb(const struct nlmsghdr *nlh, void *data) struct nft_table_list *list = data; t = nft_table_alloc(); - if (t == NULL) { - perror("OOM"); + if (t == NULL) goto err; - } - if (nft_table_nlmsg_parse(nlh, t) < 0) { - perror("nft_rule_nlmsg_parse"); + if (nft_table_nlmsg_parse(nlh, t) < 0) goto out; - } nft_table_list_add_tail(t, list); @@ -1804,7 +1786,7 @@ int nft_rule_insert(struct nft_handle *h, const char *chain, /* If built-in chains don't exist for this table, create them */ if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) - nft_chain_builtin_init(h, table, chain, NF_ACCEPT); + nft_xt_builtin_init(h, table); nft_fn = nft_rule_insert; @@ -1890,50 +1872,6 @@ int nft_rule_replace(struct nft_handle *h, const char *chain, return ret; } -static void -print_header(unsigned int format, const char *chain, const char *pol, - const struct xt_counters *counters, bool basechain, uint32_t refs) -{ - printf("Chain %s", chain); - if (basechain) { - printf(" (policy %s", pol); - if (!(format & FMT_NOCOUNTS)) { - fputc(' ', stdout); - xtables_print_num(counters->pcnt, (format|FMT_NOTABLE)); - fputs("packets, ", stdout); - xtables_print_num(counters->bcnt, (format|FMT_NOTABLE)); - fputs("bytes", stdout); - } - printf(")\n"); - } else { - printf(" (%u references)\n", refs); - } - - if (format & FMT_LINENUMBERS) - printf(FMT("%-4s ", "%s "), "num"); - if (!(format & FMT_NOCOUNTS)) { - if (format & FMT_KILOMEGAGIGA) { - printf(FMT("%5s ","%s "), "pkts"); - printf(FMT("%5s ","%s "), "bytes"); - } else { - printf(FMT("%8s ","%s "), "pkts"); - printf(FMT("%10s ","%s "), "bytes"); - } - } - if (!(format & FMT_NOTARGET)) - printf(FMT("%-9s ","%s "), "target"); - fputs(" prot ", stdout); - if (format & FMT_OPTIONS) - fputs("opt", stdout); - if (format & FMT_VIA) { - printf(FMT(" %-6s ","%s "), "in"); - printf(FMT("%-6s ","%s "), "out"); - } - printf(FMT(" %-19s ","%s "), "source"); - printf(FMT(" %-19s "," %s "), "destination"); - printf("\n"); -} - static int __nft_rule_list(struct nft_handle *h, const char *chain, const char *table, int rulenum, unsigned int format, @@ -2001,8 +1939,14 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, bool found = false; /* If built-in chains don't exist for this table, create them */ - if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) - nft_chain_builtin_init(h, table, NULL, NF_ACCEPT); + if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0) { + nft_xt_builtin_init(h, table); + /* Force table and chain creation, otherwise first iptables -L + * lists no table/chains. + */ + if (!list_empty(&h->obj_list)) + nft_commit(h); + } ops = nft_family_ops_lookup(h->family); @@ -2045,8 +1989,8 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, if (found) printf("\n"); - print_header(format, chain_name, policy_name[policy], - &ctrs, basechain, refs); + ops->print_header(format, chain_name, policy_name[policy], + &ctrs, basechain, refs); __nft_rule_list(h, chain_name, table, rulenum, format, ops->print_firewall); @@ -2266,6 +2210,15 @@ static int nft_action(struct nft_handle *h, int action) NLM_F_CREATE, seq++, n->chain); break; + case NFT_COMPAT_CHAIN_USER_ADD: + nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN, + NLM_F_EXCL, seq++, + n->chain); + break; + case NFT_COMPAT_CHAIN_USER_DEL: + nft_compat_chain_batch_add(h, NFT_MSG_DELCHAIN, + 0, seq++, n->chain); + break; case NFT_COMPAT_CHAIN_UPDATE: nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN, h->restore ? @@ -2314,8 +2267,6 @@ static int nft_action(struct nft_handle *h, int action) h->batch = mnl_nft_batch_page_add(h->batch); ret = mnl_nft_batch_talk(h); - if (ret < 0) - perror("mnl_nft_batch_talk:"); mnl_nlmsg_batch_reset(h->batch); @@ -2364,33 +2315,24 @@ int nft_compatible_revision(const char *name, uint8_t rev, int opt) name, rev, type); nl = mnl_socket_open(NETLINK_NETFILTER); - if (nl == NULL) { - perror("mnl_socket_open"); + if (nl == NULL) return 0; - } - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - perror("mnl_socket_bind"); + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) goto err; - } + portid = mnl_socket_get_portid(nl); - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - perror("mnl_socket_send"); + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) goto err; - } ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - if (ret == -1) { - perror("mnl_socket_recvfrom"); + if (ret == -1) goto err; - } ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); - if (ret == -1) { - perror("mnl_cb_run"); + if (ret == -1) goto err; - } err: mnl_socket_close(nl); @@ -2410,10 +2352,12 @@ const char *nft_strerror(int err) { { nft_chain_user_del, ENOTEMPTY, "Chain is not empty" }, { nft_chain_user_del, EINVAL, "Can't delete built-in chain" }, + { nft_chain_user_del, EBUSY, "Directory not empty" }, { nft_chain_user_del, EMLINK, "Can't delete chain with references left" }, { nft_chain_user_add, EEXIST, "Chain already exists" }, { nft_rule_add, E2BIG, "Index of insertion too big" }, + { nft_rule_check, ENOENT, "Bad rule (does a matching rule exist in that chain?)" }, { nft_rule_replace, E2BIG, "Index of replacement too big" }, { nft_rule_delete_num, E2BIG, "Index of deletion too big" }, /* { TC_READ_COUNTER, E2BIG, "Index of counter too big" }, @@ -2567,8 +2511,6 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain, struct nft_chain_list *list; struct nft_chain_list_iter *iter; struct nft_chain *c; - struct nlmsghdr *nlh; - char buf[MNL_SOCKET_BUFFER_SIZE]; int ret = 0; list = nft_chain_list_get(h); @@ -2597,14 +2539,18 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain, nft_chain_attr_unset(c, NFT_CHAIN_ATTR_HANDLE); - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, - h->family, NLM_F_ACK, h->seq); - - nft_chain_nlmsg_build_payload(nlh, c); - - ret = mnl_talk(h, nlh, NULL, NULL); - if (ret < 0) - perror("mnl_talk:nft_chain_zero_counters"); + if (h->batch_support) { + ret = batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, c); + } else { + struct nlmsghdr *nlh; + char buf[MNL_SOCKET_BUFFER_SIZE]; + + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, + h->family, NLM_F_ACK, + h->seq); + nft_chain_nlmsg_build_payload(nlh, c); + ret = mnl_talk(h, nlh, NULL, NULL); + } if (chain != NULL) break; @@ -2612,11 +2558,12 @@ next: c = nft_chain_list_iter_next(iter); } + if (!h->batch_support) + nft_chain_list_free(list); + nft_chain_list_iter_destroy(iter); err: - nft_chain_list_free(list); - /* the core expects 1 for success and 0 for error */ return ret == 0 ? 1 : 0; } |