[nf-next,2/2] netfilter: bitwise: add support for passing mask and xor values in registers.
diff mbox series

Message ID 20200224124931.512416-3-jeremy@azazel.net
State Accepted
Delegated to: Pablo Neira
Headers show
Series
  • netfilter: bitwise: support variable RHS operands
Related show

Commit Message

Jeremy Sowden Feb. 24, 2020, 12:49 p.m. UTC
Currently bitwise boolean operations can only have one variable operand
because the mask and xor values have to be passed as immediate data.
Support operations with two variable operands by allowing the mask and
xor to be passed in registers.

Signed-off-by: Jeremy Sowden <jeremy@azazel.net>
---
 include/uapi/linux/netfilter/nf_tables.h |   4 +
 net/netfilter/nft_bitwise.c              | 118 +++++++++++++++++------
 2 files changed, 93 insertions(+), 29 deletions(-)

Patch
diff mbox series

diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index 9c3d2d04d6a1..bd48b5358890 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -526,6 +526,8 @@  enum nft_bitwise_ops {
  * @NFTA_BITWISE_OP: type of operation (NLA_U32: nft_bitwise_ops)
  * @NFTA_BITWISE_DATA: argument for non-boolean operations
  *                     (NLA_NESTED: nft_data_attributes)
+ * @NFTA_BITWISE_MREG: mask register (NLA_U32: nft_registers)
+ * @NFTA_BITWISE_XREG: xor register (NLA_U32: nft_registers)
  *
  * The bitwise expression supports boolean and shift operations.  It implements
  * the boolean operations by performing the following operation:
@@ -549,6 +551,8 @@  enum nft_bitwise_attributes {
 	NFTA_BITWISE_XOR,
 	NFTA_BITWISE_OP,
 	NFTA_BITWISE_DATA,
+	NFTA_BITWISE_MREG,
+	NFTA_BITWISE_XREG,
 	__NFTA_BITWISE_MAX
 };
 #define NFTA_BITWISE_MAX	(__NFTA_BITWISE_MAX - 1)
diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c
index bc37d6c59db4..8877b50fed78 100644
--- a/net/netfilter/nft_bitwise.c
+++ b/net/netfilter/nft_bitwise.c
@@ -15,23 +15,47 @@ 
 #include <net/netfilter/nf_tables.h>
 #include <net/netfilter/nf_tables_offload.h>
 
+enum nft_bitwise_flags {
+	NFT_BITWISE_MASK_REG = 1,
+	NFT_BITWISE_XOR_REG,
+};
+
 struct nft_bitwise {
 	enum nft_registers	sreg:8;
 	enum nft_registers	dreg:8;
 	enum nft_bitwise_ops	op:8;
 	u8			len;
-	struct nft_data		mask;
-	struct nft_data		xor;
+	enum nft_bitwise_flags	flags;
 	struct nft_data		data;
+	union {
+		struct nft_data         data;
+		enum nft_registers      reg:8;
+	} mask;
+	union {
+		struct nft_data         data;
+		enum nft_registers      reg:8;
+	} xor;
 };
 
 static void nft_bitwise_eval_bool(u32 *dst, const u32 *src,
-				  const struct nft_bitwise *priv)
+				  const struct nft_bitwise *priv,
+				  struct nft_regs *regs)
 {
+	const u32 *mask, *xor;
 	unsigned int i;
 
+	if (priv->flags & NFT_BITWISE_MASK_REG)
+		mask = &regs->data[priv->mask.reg];
+	else
+		mask = priv->mask.data.data;
+
+	if (priv->flags & NFT_BITWISE_XOR_REG)
+		xor = &regs->data[priv->xor.reg];
+	else
+		xor = priv->xor.data.data;
+
 	for (i = 0; i < DIV_ROUND_UP(priv->len, 4); i++)
-		dst[i] = (src[i] & priv->mask.data[i]) ^ priv->xor.data[i];
+		dst[i] = (src[i] & mask[i]) ^ xor[i];
 }
 
 static void nft_bitwise_eval_lshift(u32 *dst, const u32 *src,
@@ -69,7 +93,7 @@  void nft_bitwise_eval(const struct nft_expr *expr,
 
 	switch (priv->op) {
 	case NFT_BITWISE_BOOL:
-		nft_bitwise_eval_bool(dst, src, priv);
+		nft_bitwise_eval_bool(dst, src, priv, regs);
 		break;
 	case NFT_BITWISE_LSHIFT:
 		nft_bitwise_eval_lshift(dst, src, priv);
@@ -88,6 +112,8 @@  static const struct nla_policy nft_bitwise_policy[NFTA_BITWISE_MAX + 1] = {
 	[NFTA_BITWISE_XOR]	= { .type = NLA_NESTED },
 	[NFTA_BITWISE_OP]	= { .type = NLA_U32 },
 	[NFTA_BITWISE_DATA]	= { .type = NLA_NESTED },
+	[NFTA_BITWISE_MREG]	= { .type = NLA_U32 },
+	[NFTA_BITWISE_XREG]	= { .type = NLA_U32 },
 };
 
 static int nft_bitwise_init_bool(struct nft_bitwise *priv,
@@ -99,33 +125,57 @@  static int nft_bitwise_init_bool(struct nft_bitwise *priv,
 	if (tb[NFTA_BITWISE_DATA])
 		return -EINVAL;
 
-	if (!tb[NFTA_BITWISE_MASK] ||
-	    !tb[NFTA_BITWISE_XOR])
+	if ((!tb[NFTA_BITWISE_MASK] && !tb[NFTA_BITWISE_MREG]) ||
+	    (tb[NFTA_BITWISE_MASK] && tb[NFTA_BITWISE_MREG]))
 		return -EINVAL;
 
-	err = nft_data_init(NULL, &priv->mask, sizeof(priv->mask), &mask,
-			    tb[NFTA_BITWISE_MASK]);
-	if (err < 0)
-		return err;
-	if (mask.type != NFT_DATA_VALUE || mask.len != priv->len) {
-		err = -EINVAL;
-		goto err1;
+	if ((!tb[NFTA_BITWISE_XOR] && !tb[NFTA_BITWISE_XREG]) ||
+	    (tb[NFTA_BITWISE_XOR] && tb[NFTA_BITWISE_XREG]))
+		return -EINVAL;
+
+	if (tb[NFTA_BITWISE_MASK]) {
+		err = nft_data_init(NULL, &priv->mask.data,
+				    sizeof(priv->mask.data), &mask,
+				    tb[NFTA_BITWISE_MASK]);
+		if (err < 0)
+			return err;
+		if (mask.type != NFT_DATA_VALUE || mask.len != priv->len) {
+			err = -EINVAL;
+			goto err1;
+		}
+	} else {
+		priv->mask.reg = nft_parse_register(tb[NFTA_BITWISE_MREG]);
+		err = nft_validate_register_load(priv->mask.reg, priv->len);
+		if (err < 0)
+			return err;
+		priv->flags |= NFT_BITWISE_MASK_REG;
 	}
 
-	err = nft_data_init(NULL, &priv->xor, sizeof(priv->xor), &xor,
-			    tb[NFTA_BITWISE_XOR]);
-	if (err < 0)
-		goto err1;
-	if (xor.type != NFT_DATA_VALUE || xor.len != priv->len) {
-		err = -EINVAL;
-		goto err2;
+	if (tb[NFTA_BITWISE_XOR]) {
+		err = nft_data_init(NULL, &priv->xor.data,
+				    sizeof(priv->xor.data), &xor,
+				    tb[NFTA_BITWISE_XOR]);
+		if (err < 0)
+			goto err1;
+		if (xor.type != NFT_DATA_VALUE || xor.len != priv->len) {
+			err = -EINVAL;
+			goto err2;
+		}
+	} else {
+		priv->xor.reg = nft_parse_register(tb[NFTA_BITWISE_XREG]);
+		err = nft_validate_register_load(priv->xor.reg, priv->len);
+		if (err < 0)
+			return err;
+		priv->flags |= NFT_BITWISE_XOR_REG;
 	}
 
 	return 0;
 err2:
-	nft_data_release(&priv->xor, xor.type);
+	if (tb[NFTA_BITWISE_XOR])
+		nft_data_release(&priv->xor.data, xor.type);
 err1:
-	nft_data_release(&priv->mask, mask.type);
+	if (tb[NFTA_BITWISE_MASK])
+		nft_data_release(&priv->mask.data, mask.type);
 	return err;
 }
 
@@ -215,13 +265,23 @@  static int nft_bitwise_init(const struct nft_ctx *ctx,
 static int nft_bitwise_dump_bool(struct sk_buff *skb,
 				 const struct nft_bitwise *priv)
 {
-	if (nft_data_dump(skb, NFTA_BITWISE_MASK, &priv->mask,
-			  NFT_DATA_VALUE, priv->len) < 0)
-		return -1;
+	if (priv->flags & NFT_BITWISE_MASK_REG) {
+		if (nft_dump_register(skb, NFTA_BITWISE_MREG, priv->mask.reg))
+			return -1;
+	} else {
+		if (nft_data_dump(skb, NFTA_BITWISE_MASK, &priv->mask.data,
+				  NFT_DATA_VALUE, priv->len) < 0)
+			return -1;
+	}
 
-	if (nft_data_dump(skb, NFTA_BITWISE_XOR, &priv->xor,
-			  NFT_DATA_VALUE, priv->len) < 0)
-		return -1;
+	if (priv->flags & NFT_BITWISE_XOR_REG) {
+		if (nft_dump_register(skb, NFTA_BITWISE_XREG, priv->xor.reg))
+			return -1;
+	} else {
+		if (nft_data_dump(skb, NFTA_BITWISE_XOR, &priv->xor.data,
+				  NFT_DATA_VALUE, priv->len) < 0)
+			return -1;
+	}
 
 	return 0;
 }