@@ -31,6 +31,7 @@ struct nft_payload {
enum nft_payload_bases base:8;
u8 offset;
u8 len;
+ enum nft_registers sreg:8;
enum nft_registers dreg:8;
};
@@ -511,6 +511,7 @@ enum nft_payload_bases {
* @NFTA_PAYLOAD_DREG: destination register to load data into (NLA_U32: nft_registers)
* @NFTA_PAYLOAD_BASE: payload base (NLA_U32: nft_payload_bases)
* @NFTA_PAYLOAD_OFFSET: payload offset relative to base (NLA_U32)
+ * @NFTA_PAYLOAD_SREG_EXTRA: source register of an extra offset (NLA_U32: nft_registers)
* @NFTA_PAYLOAD_LEN: payload length (NLA_U32)
*/
enum nft_payload_attributes {
@@ -518,6 +519,7 @@ enum nft_payload_attributes {
NFTA_PAYLOAD_DREG,
NFTA_PAYLOAD_BASE,
NFTA_PAYLOAD_OFFSET,
+ NFTA_PAYLOAD_SREG_EXTRA,
NFTA_PAYLOAD_LEN,
__NFTA_PAYLOAD_MAX
};
@@ -40,6 +40,7 @@ static bool nft_payload_fast_eval(const struct nft_expr *expr,
const struct nft_payload *priv = nft_expr_priv(expr);
const struct sk_buff *skb = pkt->skb;
struct nft_data *dest = &data[priv->dreg];
+ int extra_offset = 0;
unsigned char *ptr;
if (priv->base == NFT_PAYLOAD_NETWORK_HEADER)
@@ -47,7 +48,10 @@ static bool nft_payload_fast_eval(const struct nft_expr *expr,
else
ptr = skb_network_header(skb) + pkt->xt.thoff;
- ptr += priv->offset;
+ if (priv->sreg)
+ extra_offset = (int) *data[priv->sreg].data;
+
+ ptr += extra_offset + priv->offset;
if (unlikely(ptr + priv->len >= skb_tail_pointer(skb)))
return false;
@@ -24,7 +24,7 @@ static void nft_payload_eval(const struct nft_expr *expr,
const struct nft_payload *priv = nft_expr_priv(expr);
const struct sk_buff *skb = pkt->skb;
struct nft_data *dest = &data[priv->dreg];
- int offset;
+ int offset, extra_offset = 0;
switch (priv->base) {
case NFT_PAYLOAD_LL_HEADER:
@@ -41,7 +41,11 @@ static void nft_payload_eval(const struct nft_expr *expr,
default:
BUG();
}
- offset += priv->offset;
+
+ if (priv->sreg)
+ extra_offset = (int) *data[priv->sreg].data;
+
+ offset += extra_offset + priv->offset;
if (skb_copy_bits(skb, offset, dest->data, priv->len) < 0)
goto err;
@@ -51,10 +55,11 @@ err:
}
static const struct nla_policy nft_payload_policy[NFTA_PAYLOAD_MAX + 1] = {
- [NFTA_PAYLOAD_DREG] = { .type = NLA_U32 },
- [NFTA_PAYLOAD_BASE] = { .type = NLA_U32 },
- [NFTA_PAYLOAD_OFFSET] = { .type = NLA_U32 },
- [NFTA_PAYLOAD_LEN] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_DREG] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_BASE] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_OFFSET] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_SREG_EXTRA] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_LEN] = { .type = NLA_U32 },
};
static int nft_payload_init(const struct nft_ctx *ctx,
@@ -68,6 +73,14 @@ static int nft_payload_init(const struct nft_ctx *ctx,
priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET]));
priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN]));
+ if (tb[NFTA_PAYLOAD_SREG_EXTRA]) {
+ priv->sreg = ntohl(nla_get_be32(
+ tb[NFTA_PAYLOAD_SREG_EXTRA]));
+ err = nft_validate_input_register(priv->sreg);
+ if (err < 0)
+ return err;
+ }
+
priv->dreg = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_DREG]));
err = nft_validate_output_register(priv->dreg);
if (err < 0)
Payload expression gets a new attribute: NFTA_PAYLOAD_SREG_EXTRA This attribute will permit to provide a source register where an extra offset will be added to the fixed offset of the expression. This will be useful where some information cannot be found at a pre-determined place. Such as the protocol address of an ARP-ETH packet: its position is determined by the hardware address length. Thus, in this example, a meta expression NFT_META_DEV_ADDRLEN will load the value of the hardware length and put it into a register: such register, as a NFTA_PAYLOAD_SREG_EXTRA in following payload expression, will be used to add the hardware length to the fixed offset. Suggested by Pablo Neira Ayuso. Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com> --- include/net/netfilter/nf_tables_core.h | 1 + include/uapi/linux/netfilter/nf_tables.h | 2 ++ net/netfilter/nf_tables_core.c | 6 +++++- net/netfilter/nft_payload.c | 25 +++++++++++++++++++------ 4 files changed, 27 insertions(+), 7 deletions(-)