summaryrefslogtreecommitdiffstats
path: root/iptables/nft.c
diff options
context:
space:
mode:
Diffstat (limited to 'iptables/nft.c')
-rw-r--r--iptables/nft.c327
1 files changed, 293 insertions, 34 deletions
diff --git a/iptables/nft.c b/iptables/nft.c
index 83cf5fb7..2f0a8c4a 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -271,6 +271,7 @@ enum obj_update_type {
NFT_COMPAT_RULE_REPLACE,
NFT_COMPAT_RULE_DELETE,
NFT_COMPAT_RULE_FLUSH,
+ NFT_COMPAT_SET_ADD,
};
enum obj_action {
@@ -288,6 +289,7 @@ struct obj_update {
struct nftnl_table *table;
struct nftnl_chain *chain;
struct nftnl_rule *rule;
+ struct nftnl_set *set;
void *ptr;
};
struct {
@@ -315,6 +317,7 @@ static int mnl_append_error(const struct nft_handle *h,
[NFT_COMPAT_RULE_REPLACE] = "RULE_REPLACE",
[NFT_COMPAT_RULE_DELETE] = "RULE_DELETE",
[NFT_COMPAT_RULE_FLUSH] = "RULE_FLUSH",
+ [NFT_COMPAT_SET_ADD] = "SET_ADD",
};
char errmsg[256];
char tcr[128];
@@ -351,10 +354,14 @@ static int mnl_append_error(const struct nft_handle *h,
nftnl_rule_get_str(o->rule, NFTNL_RULE_CHAIN));
#if 0
{
- nft_rule_print_save(o->rule, NFT_RULE_APPEND, FMT_NOCOUNTS);
+ nft_rule_print_save(h, o->rule, NFT_RULE_APPEND, FMT_NOCOUNTS);
}
#endif
break;
+ case NFT_COMPAT_SET_ADD:
+ snprintf(tcr, sizeof(tcr), "set %s",
+ nftnl_set_get_str(o->set, NFTNL_SET_NAME));
+ break;
}
return snprintf(buf, len, "%s: %s", errmsg, tcr);
@@ -384,6 +391,13 @@ batch_table_add(struct nft_handle *h, enum obj_update_type type,
return batch_add(h, type, t);
}
+static struct obj_update *
+batch_set_add(struct nft_handle *h, enum obj_update_type type,
+ struct nftnl_set *s)
+{
+ return batch_add(h, type, s);
+}
+
static int batch_chain_add(struct nft_handle *h, enum obj_update_type type,
struct nftnl_chain *c)
{
@@ -930,13 +944,181 @@ static int add_nft_limit(struct nftnl_rule *r, struct xt_entry_match *m)
return 0;
}
-int add_match(struct nftnl_rule *r, struct xt_entry_match *m)
+static struct nftnl_set *add_anon_set(struct nft_handle *h, const char *table,
+ uint32_t flags, uint32_t key_type,
+ uint32_t key_len, uint32_t size)
+{
+ static uint32_t set_id = 0;
+ struct nftnl_set *s;
+
+ s = nftnl_set_alloc();
+ if (!s)
+ return NULL;
+
+ nftnl_set_set_u32(s, NFTNL_SET_FAMILY, h->family);
+ nftnl_set_set_str(s, NFTNL_SET_TABLE, table);
+ nftnl_set_set_str(s, NFTNL_SET_NAME, "__set%d");
+ nftnl_set_set_u32(s, NFTNL_SET_ID, ++set_id);
+ nftnl_set_set_u32(s, NFTNL_SET_FLAGS,
+ NFT_SET_ANONYMOUS | NFT_SET_CONSTANT | flags);
+ nftnl_set_set_u32(s, NFTNL_SET_KEY_TYPE, key_type);
+ nftnl_set_set_u32(s, NFTNL_SET_KEY_LEN, key_len);
+ nftnl_set_set_u32(s, NFTNL_SET_DESC_SIZE, size);
+
+ return batch_set_add(h, NFT_COMPAT_SET_ADD, s) ? s : NULL;
+}
+
+static struct nftnl_expr *
+gen_payload(uint32_t base, uint32_t offset, uint32_t len, uint32_t dreg)
+{
+ struct nftnl_expr *e = nftnl_expr_alloc("payload");
+
+ if (!e)
+ return NULL;
+ nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_BASE, base);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_LEN, len);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_DREG, dreg);
+ return e;
+}
+
+static struct nftnl_expr *
+gen_lookup(uint32_t sreg, const char *set_name, uint32_t set_id, uint32_t flags)
+{
+ struct nftnl_expr *e = nftnl_expr_alloc("lookup");
+
+ if (!e)
+ return NULL;
+ nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_SREG, sreg);
+ nftnl_expr_set_str(e, NFTNL_EXPR_LOOKUP_SET, set_name);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_SET_ID, set_id);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_FLAGS, flags);
+ return e;
+}
+
+/* simplified nftables:include/netlink.h, netlink_padded_len() */
+#define NETLINK_ALIGN 4
+
+/* from nftables:include/datatype.h, TYPE_BITS */
+#define CONCAT_TYPE_BITS 6
+
+/* from nftables:include/datatype.h, enum datatypes */
+#define NFT_DATATYPE_IPADDR 7
+#define NFT_DATATYPE_ETHERADDR 9
+
+static int __add_nft_among(struct nft_handle *h, const char *table,
+ struct nftnl_rule *r, struct nft_among_pair *pairs,
+ int cnt, bool dst, bool inv, bool ip)
+{
+ uint32_t set_id, type = NFT_DATATYPE_ETHERADDR, len = ETH_ALEN;
+ /* { !dst, dst } */
+ static const int eth_addr_off[] = {
+ offsetof(struct ether_header, ether_shost),
+ offsetof(struct ether_header, ether_dhost)
+ };
+ static const int ip_addr_off[] = {
+ offsetof(struct iphdr, saddr),
+ offsetof(struct iphdr, daddr)
+ };
+ struct nftnl_expr *e;
+ struct nftnl_set *s;
+ uint32_t flags = 0;
+ int idx = 0;
+
+ if (ip) {
+ type = type << CONCAT_TYPE_BITS | NFT_DATATYPE_IPADDR;
+ len += sizeof(struct in_addr) + NETLINK_ALIGN - 1;
+ len &= ~(NETLINK_ALIGN - 1);
+ flags = NFT_SET_INTERVAL;
+ }
+
+ s = add_anon_set(h, table, flags, type, len, cnt);
+ if (!s)
+ return -ENOMEM;
+ set_id = nftnl_set_get_u32(s, NFTNL_SET_ID);
+
+ if (ip) {
+ uint8_t field_len[2] = { ETH_ALEN, sizeof(struct in_addr) };
+
+ nftnl_set_set_data(s, NFTNL_SET_DESC_CONCAT,
+ field_len, sizeof(field_len));
+ }
+
+ for (idx = 0; idx < cnt; idx++) {
+ struct nftnl_set_elem *elem = nftnl_set_elem_alloc();
+
+ if (!elem)
+ return -ENOMEM;
+ nftnl_set_elem_set(elem, NFTNL_SET_ELEM_KEY,
+ &pairs[idx], len);
+ if (ip) {
+ struct in_addr tmp = pairs[idx].in;
+
+ if (tmp.s_addr == INADDR_ANY)
+ pairs[idx].in.s_addr = INADDR_BROADCAST;
+ nftnl_set_elem_set(elem, NFTNL_SET_ELEM_KEY_END,
+ &pairs[idx], len);
+ pairs[idx].in = tmp;
+ }
+ nftnl_set_elem_add(s, elem);
+ }
+
+ e = gen_payload(NFT_PAYLOAD_LL_HEADER,
+ eth_addr_off[dst], ETH_ALEN, NFT_REG_1);
+ if (!e)
+ return -ENOMEM;
+ nftnl_rule_add_expr(r, e);
+
+ if (ip) {
+ e = gen_payload(NFT_PAYLOAD_NETWORK_HEADER, ip_addr_off[dst],
+ sizeof(struct in_addr), NFT_REG32_02);
+ if (!e)
+ return -ENOMEM;
+ nftnl_rule_add_expr(r, e);
+ }
+
+ e = gen_lookup(NFT_REG_1, "__set%d", set_id, inv);
+ if (!e)
+ return -ENOMEM;
+ nftnl_rule_add_expr(r, e);
+
+ return 0;
+}
+
+static int add_nft_among(struct nft_handle *h,
+ struct nftnl_rule *r, struct xt_entry_match *m)
+{
+ struct nft_among_data *data = (struct nft_among_data *)m->data;
+ const char *table = nftnl_rule_get(r, NFTNL_RULE_TABLE);
+
+ if ((data->src.cnt && data->src.ip) ||
+ (data->dst.cnt && data->dst.ip)) {
+ uint16_t eth_p_ip = htons(ETH_P_IP);
+
+ add_meta(r, NFT_META_PROTOCOL);
+ add_cmp_ptr(r, NFT_CMP_EQ, &eth_p_ip, 2);
+ }
+
+ if (data->src.cnt)
+ __add_nft_among(h, table, r, data->pairs, data->src.cnt,
+ false, data->src.inv, data->src.ip);
+ if (data->dst.cnt)
+ __add_nft_among(h, table, r, data->pairs + data->src.cnt,
+ data->dst.cnt, true, data->dst.inv,
+ data->dst.ip);
+ return 0;
+}
+
+int add_match(struct nft_handle *h,
+ struct nftnl_rule *r, struct xt_entry_match *m)
{
struct nftnl_expr *expr;
int ret;
if (!strcmp(m->u.user.name, "limit"))
return add_nft_limit(r, m);
+ else if (!strcmp(m->u.user.name, "among"))
+ return add_nft_among(h, r, m);
expr = nftnl_expr_alloc("match");
if (expr == NULL)
@@ -1152,7 +1334,7 @@ nft_rule_new(struct nft_handle *h, const char *chain, const char *table,
nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);
nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
- if (h->ops->add(r, data) < 0)
+ if (h->ops->add(h, r, data) < 0)
goto err;
return r;
@@ -1201,7 +1383,7 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
}
if (verbose)
- h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+ h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
if (ref) {
nftnl_chain_rule_insert_at(r, ref);
@@ -1219,16 +1401,14 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
}
void
-nft_rule_print_save(const struct nftnl_rule *r, enum nft_rule_print type,
- unsigned int format)
+nft_rule_print_save(struct nft_handle *h, const struct nftnl_rule *r,
+ enum nft_rule_print type, unsigned int format)
{
const char *chain = nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
- int family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
struct iptables_command_state cs = {};
- struct nft_family_ops *ops;
+ struct nft_family_ops *ops = h->ops;
- ops = nft_family_ops_lookup(family);
- ops->rule_to_cs(r, &cs);
+ ops->rule_to_cs(h, r, &cs);
if (!(format & (FMT_NOCOUNTS | FMT_C_COUNTS)) && ops->save_counters)
ops->save_counters(&cs);
@@ -1337,12 +1517,10 @@ static const char *policy_name[NF_ACCEPT+1] = {
int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list)
{
+ struct nft_family_ops *ops = h->ops;
struct nftnl_chain_list_iter *iter;
- struct nft_family_ops *ops;
struct nftnl_chain *c;
- ops = nft_family_ops_lookup(h->family);
-
iter = nftnl_chain_list_iter_create(list);
if (iter == NULL)
return 0;
@@ -1391,7 +1569,7 @@ static int nft_chain_save_rules(struct nft_handle *h,
r = nftnl_rule_iter_next(iter);
while (r != NULL) {
- nft_rule_print_save(r, NFT_RULE_APPEND, format);
+ nft_rule_print_save(h, r, NFT_RULE_APPEND, format);
r = nftnl_rule_iter_next(iter);
}
@@ -1906,7 +2084,7 @@ nft_rule_find(struct nft_handle *h, struct nftnl_chain *c, void *data, int rulen
r = nftnl_rule_iter_next(iter);
while (r != NULL) {
- found = h->ops->rule_find(h->ops, r, data);
+ found = h->ops->rule_find(h, r, data);
if (found)
break;
r = nftnl_rule_iter_next(iter);
@@ -1934,7 +2112,7 @@ int nft_rule_check(struct nft_handle *h, const char *chain,
goto fail_enoent;
if (verbose)
- h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+ h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
return 1;
fail_enoent:
@@ -1963,7 +2141,7 @@ int nft_rule_delete(struct nft_handle *h, const char *chain,
if (ret < 0)
errno = ENOMEM;
if (verbose)
- h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+ h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
} else
errno = ENOENT;
@@ -2004,7 +2182,7 @@ nft_rule_add(struct nft_handle *h, const char *chain,
}
if (verbose)
- h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+ h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
return r;
}
@@ -2113,8 +2291,8 @@ int nft_rule_replace(struct nft_handle *h, const char *chain,
static int
__nft_rule_list(struct nft_handle *h, struct nftnl_chain *c,
int rulenum, unsigned int format,
- void (*cb)(struct nftnl_rule *r, unsigned int num,
- unsigned int format))
+ void (*cb)(struct nft_handle *h, struct nftnl_rule *r,
+ unsigned int num, unsigned int format))
{
struct nftnl_rule_iter *iter;
struct nftnl_rule *r;
@@ -2127,7 +2305,7 @@ __nft_rule_list(struct nft_handle *h, struct nftnl_chain *c,
* valid chain but invalid rule number
*/
return 1;
- cb(r, rulenum, format);
+ cb(h, r, rulenum, format);
return 1;
}
@@ -2137,7 +2315,7 @@ __nft_rule_list(struct nft_handle *h, struct nftnl_chain *c,
r = nftnl_rule_iter_next(iter);
while (r != NULL) {
- cb(r, ++rule_ctr, format);
+ cb(h, r, ++rule_ctr, format);
r = nftnl_rule_iter_next(iter);
}
@@ -2166,7 +2344,6 @@ static int nft_rule_count(struct nft_handle *h, struct nftnl_chain *c)
}
static void __nft_print_header(struct nft_handle *h,
- const struct nft_family_ops *ops,
struct nftnl_chain *c, unsigned int format)
{
const char *chain_name = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
@@ -2182,14 +2359,14 @@ static void __nft_print_header(struct nft_handle *h,
if (nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY))
pname = policy_name[nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY)];
- ops->print_header(format, chain_name, pname,
+ h->ops->print_header(format, chain_name, pname,
&ctrs, basechain, refs - entries, entries);
}
int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
int rulenum, unsigned int format)
{
- const struct nft_family_ops *ops;
+ const struct nft_family_ops *ops = h->ops;
struct nftnl_chain_list *list;
struct nftnl_chain_list_iter *iter;
struct nftnl_chain *c;
@@ -2198,8 +2375,6 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
nft_xt_builtin_init(h, table);
nft_assert_table_compatible(h, table, chain);
- ops = nft_family_ops_lookup(h->family);
-
list = nft_chain_list_get(h, table, chain);
if (!list)
return 0;
@@ -2212,7 +2387,7 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
if (!rulenum) {
if (ops->print_table_header)
ops->print_table_header(table);
- __nft_print_header(h, ops, c, format);
+ __nft_print_header(h, c, format);
}
__nft_rule_list(h, c, rulenum, format, ops->print_rule);
return 1;
@@ -2230,7 +2405,7 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
if (found)
printf("\n");
- __nft_print_header(h, ops, c, format);
+ __nft_print_header(h, c, format);
__nft_rule_list(h, c, rulenum, format, ops->print_rule);
found = true;
@@ -2241,9 +2416,10 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
}
static void
-list_save(struct nftnl_rule *r, unsigned int num, unsigned int format)
+list_save(struct nft_handle *h, struct nftnl_rule *r,
+ unsigned int num, unsigned int format)
{
- nft_rule_print_save(r, NFT_RULE_APPEND, format);
+ nft_rule_print_save(h, r, NFT_RULE_APPEND, format);
}
static int __nftnl_rule_list_chain_save(struct nftnl_chain *c, void *data)
@@ -2355,7 +2531,7 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
goto error;
}
- nft_rule_to_iptables_command_state(r, &cs);
+ nft_rule_to_iptables_command_state(h, r, &cs);
cs.counters.pcnt = cs.counters.bcnt = 0;
@@ -2376,6 +2552,39 @@ static void nft_compat_table_batch_add(struct nft_handle *h, uint16_t type,
nftnl_table_nlmsg_build_payload(nlh, table);
}
+static void nft_compat_set_batch_add(struct nft_handle *h, uint16_t type,
+ uint16_t flags, uint32_t seq,
+ struct nftnl_set *set)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
+ type, h->family, flags, seq);
+ nftnl_set_nlmsg_build_payload(nlh, set);
+}
+
+static void nft_compat_setelem_batch_add(struct nft_handle *h, uint16_t type,
+ uint16_t flags, uint32_t *seq,
+ struct nftnl_set *set)
+{
+ struct nftnl_set_elems_iter *iter;
+ struct nlmsghdr *nlh;
+
+ iter = nftnl_set_elems_iter_create(set);
+ if (!iter)
+ return;
+
+ while (nftnl_set_elems_iter_cur(iter)) {
+ (*seq)++;
+ mnl_nft_batch_continue(h->batch);
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
+ type, h->family, flags, *seq);
+ if (nftnl_set_elems_nlmsg_build_payload_iter(nlh, iter) <= 0)
+ break;
+ }
+ nftnl_set_elems_iter_destroy(iter);
+}
+
static void nft_compat_chain_batch_add(struct nft_handle *h, uint16_t type,
uint16_t flags, uint32_t seq,
struct nftnl_chain *chain)
@@ -2425,6 +2634,9 @@ static void batch_obj_del(struct nft_handle *h, struct obj_update *o)
case NFT_COMPAT_RULE_FLUSH:
nftnl_rule_free(o->rule);
break;
+ case NFT_COMPAT_SET_ADD:
+ nftnl_set_free(o->set);
+ break;
}
h->obj_list_num--;
list_del(&o->head);
@@ -2491,6 +2703,7 @@ static void nft_refresh_transaction(struct nft_handle *h)
case NFT_COMPAT_RULE_REPLACE:
case NFT_COMPAT_RULE_DELETE:
case NFT_COMPAT_RULE_FLUSH:
+ case NFT_COMPAT_SET_ADD:
break;
}
}
@@ -2581,6 +2794,13 @@ retry:
nft_compat_rule_batch_add(h, NFT_MSG_DELRULE, 0,
n->seq, n->rule);
break;
+ case NFT_COMPAT_SET_ADD:
+ nft_compat_set_batch_add(h, NFT_MSG_NEWSET,
+ NLM_F_CREATE, n->seq, n->set);
+ nft_compat_setelem_batch_add(h, NFT_MSG_NEWSETELEM,
+ NLM_F_CREATE, &n->seq, n->set);
+ seq = n->seq;
+ break;
}
mnl_nft_batch_continue(h->batch);
@@ -2601,7 +2821,6 @@ retry:
nft_refresh_transaction(h);
- i=0;
list_for_each_entry_safe(err, ne, &h->err_list, head)
mnl_err_list_free(err);
@@ -2897,6 +3116,44 @@ const char *nft_strerror(int err)
return strerror(err);
}
+static int recover_rule_compat(struct nftnl_rule *r)
+{
+ struct nftnl_expr_iter *iter;
+ struct nftnl_expr *e;
+ uint32_t reg;
+ int ret = -1;
+
+ iter = nftnl_expr_iter_create(r);
+ if (!iter)
+ return -1;
+
+next_expr:
+ e = nftnl_expr_iter_next(iter);
+ if (!e)
+ goto out;
+
+ if (strcmp("meta", nftnl_expr_get_str(e, NFTNL_EXPR_NAME)) ||
+ nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY) != NFT_META_L4PROTO)
+ goto next_expr;
+
+ reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
+
+ e = nftnl_expr_iter_next(iter);
+ if (!e)
+ goto out;
+
+ if (strcmp("cmp", nftnl_expr_get_str(e, NFTNL_EXPR_NAME)) ||
+ reg != nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG))
+ goto next_expr;
+
+ add_compat(r, nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA),
+ nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ);
+ ret = 0;
+out:
+ nftnl_expr_iter_destroy(iter);
+ return ret;
+}
+
struct chain_zero_data {
struct nft_handle *handle;
bool verbose;
@@ -2961,6 +3218,7 @@ static int __nft_chain_zero_counters(struct nftnl_chain *c, void *data)
* Unset RULE_POSITION for older kernels, we want to replace
* rule based on its handle only.
*/
+ recover_rule_compat(r);
nftnl_rule_unset(r, NFTNL_RULE_POSITION);
if (!batch_rule_add(h, NFT_COMPAT_RULE_REPLACE, r)) {
nftnl_rule_iter_destroy(iter);
@@ -3022,7 +3280,8 @@ static const char *supported_exprs[] = {
"cmp",
"bitwise",
"counter",
- "immediate"
+ "immediate",
+ "lookup",
};