Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: nf_tables: add range expression

Inverse ranges != [a,b] are not currently possible because rules are
composites of && operations, and we need to express this:

data < a || data > b

This patch adds a new range expression. Positive ranges can be already
through two cmp expressions:

cmp(sreg, data, >=)
cmp(sreg, data, <=)

This new range expression provides an alternative way to express this.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

+178 -2
+3
include/net/netfilter/nf_tables_core.h
··· 28 28 int nft_cmp_module_init(void); 29 29 void nft_cmp_module_exit(void); 30 30 31 + int nft_range_module_init(void); 32 + void nft_range_module_exit(void); 33 + 31 34 int nft_lookup_module_init(void); 32 35 void nft_lookup_module_exit(void); 33 36
+29
include/uapi/linux/netfilter/nf_tables.h
··· 546 546 }; 547 547 #define NFTA_CMP_MAX (__NFTA_CMP_MAX - 1) 548 548 549 + /** 550 + * enum nft_range_ops - nf_tables range operator 551 + * 552 + * @NFT_RANGE_EQ: equal 553 + * @NFT_RANGE_NEQ: not equal 554 + */ 555 + enum nft_range_ops { 556 + NFT_RANGE_EQ, 557 + NFT_RANGE_NEQ, 558 + }; 559 + 560 + /** 561 + * enum nft_range_attributes - nf_tables range expression netlink attributes 562 + * 563 + * @NFTA_RANGE_SREG: source register of data to compare (NLA_U32: nft_registers) 564 + * @NFTA_RANGE_OP: cmp operation (NLA_U32: nft_cmp_ops) 565 + * @NFTA_RANGE_FROM_DATA: data range from (NLA_NESTED: nft_data_attributes) 566 + * @NFTA_RANGE_TO_DATA: data range to (NLA_NESTED: nft_data_attributes) 567 + */ 568 + enum nft_range_attributes { 569 + NFTA_RANGE_UNSPEC, 570 + NFTA_RANGE_SREG, 571 + NFTA_RANGE_OP, 572 + NFTA_RANGE_FROM_DATA, 573 + NFTA_RANGE_TO_DATA, 574 + __NFTA_RANGE_MAX 575 + }; 576 + #define NFTA_RANGE_MAX (__NFTA_RANGE_MAX - 1) 577 + 549 578 enum nft_lookup_flags { 550 579 NFT_LOOKUP_F_INV = (1 << 0), 551 580 };
+2 -1
net/netfilter/Makefile
··· 71 71 72 72 # nf_tables 73 73 nf_tables-objs += nf_tables_core.o nf_tables_api.o nf_tables_trace.o 74 - nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o nft_dynset.o 74 + nf_tables-objs += nft_immediate.o nft_cmp.o nft_range.o 75 75 nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o 76 + nf_tables-objs += nft_lookup.o nft_dynset.o 76 77 77 78 obj-$(CONFIG_NF_TABLES) += nf_tables.o 78 79 obj-$(CONFIG_NF_TABLES_INET) += nf_tables_inet.o
+6 -1
net/netfilter/nf_tables_core.c
··· 263 263 if (err < 0) 264 264 goto err7; 265 265 266 - return 0; 266 + err = nft_range_module_init(); 267 + if (err < 0) 268 + goto err8; 267 269 270 + return 0; 271 + err8: 272 + nft_dynset_module_exit(); 268 273 err7: 269 274 nft_payload_module_exit(); 270 275 err6:
+138
net/netfilter/nft_range.c
··· 1 + /* 2 + * Copyright (c) 2016 Pablo Neira Ayuso <pablo@netfilter.org> 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License version 2 as 6 + * published by the Free Software Foundation. 7 + */ 8 + 9 + #include <linux/kernel.h> 10 + #include <linux/init.h> 11 + #include <linux/module.h> 12 + #include <linux/netlink.h> 13 + #include <linux/netfilter.h> 14 + #include <linux/netfilter/nf_tables.h> 15 + #include <net/netfilter/nf_tables_core.h> 16 + #include <net/netfilter/nf_tables.h> 17 + 18 + struct nft_range_expr { 19 + struct nft_data data_from; 20 + struct nft_data data_to; 21 + enum nft_registers sreg:8; 22 + u8 len; 23 + enum nft_range_ops op:8; 24 + }; 25 + 26 + static void nft_range_eval(const struct nft_expr *expr, 27 + struct nft_regs *regs, 28 + const struct nft_pktinfo *pkt) 29 + { 30 + const struct nft_range_expr *priv = nft_expr_priv(expr); 31 + bool mismatch; 32 + int d1, d2; 33 + 34 + d1 = memcmp(&regs->data[priv->sreg], &priv->data_from, priv->len); 35 + d2 = memcmp(&regs->data[priv->sreg], &priv->data_to, priv->len); 36 + switch (priv->op) { 37 + case NFT_RANGE_EQ: 38 + mismatch = (d1 < 0 || d2 > 0); 39 + break; 40 + case NFT_RANGE_NEQ: 41 + mismatch = (d1 >= 0 && d2 <= 0); 42 + break; 43 + } 44 + 45 + if (mismatch) 46 + regs->verdict.code = NFT_BREAK; 47 + } 48 + 49 + static const struct nla_policy nft_range_policy[NFTA_RANGE_MAX + 1] = { 50 + [NFTA_RANGE_SREG] = { .type = NLA_U32 }, 51 + [NFTA_RANGE_OP] = { .type = NLA_U32 }, 52 + [NFTA_RANGE_FROM_DATA] = { .type = NLA_NESTED }, 53 + [NFTA_RANGE_TO_DATA] = { .type = NLA_NESTED }, 54 + }; 55 + 56 + static int nft_range_init(const struct nft_ctx *ctx, const struct nft_expr *expr, 57 + const struct nlattr * const tb[]) 58 + { 59 + struct nft_range_expr *priv = nft_expr_priv(expr); 60 + struct nft_data_desc desc_from, desc_to; 61 + int err; 62 + 63 + err = nft_data_init(NULL, &priv->data_from, sizeof(priv->data_from), 64 + &desc_from, tb[NFTA_RANGE_FROM_DATA]); 65 + if (err < 0) 66 + return err; 67 + 68 + err = nft_data_init(NULL, &priv->data_to, sizeof(priv->data_to), 69 + &desc_to, tb[NFTA_RANGE_TO_DATA]); 70 + if (err < 0) 71 + goto err1; 72 + 73 + if (desc_from.len != desc_to.len) { 74 + err = -EINVAL; 75 + goto err2; 76 + } 77 + 78 + priv->sreg = nft_parse_register(tb[NFTA_RANGE_SREG]); 79 + err = nft_validate_register_load(priv->sreg, desc_from.len); 80 + if (err < 0) 81 + goto err2; 82 + 83 + priv->op = ntohl(nla_get_be32(tb[NFTA_RANGE_OP])); 84 + priv->len = desc_from.len; 85 + return 0; 86 + err2: 87 + nft_data_uninit(&priv->data_to, desc_to.type); 88 + err1: 89 + nft_data_uninit(&priv->data_from, desc_from.type); 90 + return err; 91 + } 92 + 93 + static int nft_range_dump(struct sk_buff *skb, const struct nft_expr *expr) 94 + { 95 + const struct nft_range_expr *priv = nft_expr_priv(expr); 96 + 97 + if (nft_dump_register(skb, NFTA_RANGE_SREG, priv->sreg)) 98 + goto nla_put_failure; 99 + if (nla_put_be32(skb, NFTA_RANGE_OP, htonl(priv->op))) 100 + goto nla_put_failure; 101 + 102 + if (nft_data_dump(skb, NFTA_RANGE_FROM_DATA, &priv->data_from, 103 + NFT_DATA_VALUE, priv->len) < 0 || 104 + nft_data_dump(skb, NFTA_RANGE_TO_DATA, &priv->data_to, 105 + NFT_DATA_VALUE, priv->len) < 0) 106 + goto nla_put_failure; 107 + return 0; 108 + 109 + nla_put_failure: 110 + return -1; 111 + } 112 + 113 + static struct nft_expr_type nft_range_type; 114 + static const struct nft_expr_ops nft_range_ops = { 115 + .type = &nft_range_type, 116 + .size = NFT_EXPR_SIZE(sizeof(struct nft_range_expr)), 117 + .eval = nft_range_eval, 118 + .init = nft_range_init, 119 + .dump = nft_range_dump, 120 + }; 121 + 122 + static struct nft_expr_type nft_range_type __read_mostly = { 123 + .name = "range", 124 + .ops = &nft_range_ops, 125 + .policy = nft_range_policy, 126 + .maxattr = NFTA_RANGE_MAX, 127 + .owner = THIS_MODULE, 128 + }; 129 + 130 + int __init nft_range_module_init(void) 131 + { 132 + return nft_register_expr(&nft_range_type); 133 + } 134 + 135 + void nft_range_module_exit(void) 136 + { 137 + nft_unregister_expr(&nft_range_type); 138 + }