Skip to content

Commit

Permalink
netfilter: nftables_offload: build mask based from the matching bytes
Browse files Browse the repository at this point in the history
Userspace might match on prefix bytes of header fields if they are on
the byte boundary, this requires that the mask is adjusted accordingly.
Use NFT_OFFLOAD_MATCH_EXACT() for meta since prefix byte matching is not
allowed for this type of selector.

Fixes: c9626a2 ("netfilter: nf_tables: add hardware offload support")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  • Loading branch information
ummakynes authored and intel-lab-lkp committed Nov 4, 2020
1 parent 562bcf5 commit b0df362
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 29 deletions.
3 changes: 3 additions & 0 deletions include/net/netfilter/nf_tables_offload.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ int nft_flow_rule_offload_commit(struct net *net);
offsetof(struct nft_flow_key, __base.__field); \
(__reg)->len = __len; \
(__reg)->key = __key; \

#define NFT_OFFLOAD_MATCH_EXACT(__key, __base, __field, __len, __reg) \
NFT_OFFLOAD_MATCH(__key, __base, __field, __len, __reg) \
memset(&(__reg)->mask, 0xff, (__reg)->len);

int nft_chain_offload_priority(struct nft_base_chain *basechain);
Expand Down
8 changes: 4 additions & 4 deletions net/netfilter/nft_cmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,11 @@ static int __nft_cmp_offload(struct nft_offload_ctx *ctx,
u8 *mask = (u8 *)&flow->match.mask;
u8 *key = (u8 *)&flow->match.key;

if (priv->op != NFT_CMP_EQ || reg->len != priv->len)
if (priv->op != NFT_CMP_EQ || priv->len > reg->len)
return -EOPNOTSUPP;

memcpy(key + reg->offset, &priv->data, priv->len);
memcpy(mask + reg->offset, &reg->mask, priv->len);
memcpy(key + reg->offset, &priv->data, reg->len);
memcpy(mask + reg->offset, &reg->mask, reg->len);

flow->match.dissector.used_keys |= BIT(reg->key);
flow->match.dissector.offset[reg->key] = reg->base_offset;
Expand All @@ -137,7 +137,7 @@ static int __nft_cmp_offload(struct nft_offload_ctx *ctx,
nft_reg_load16(priv->data.data) != ARPHRD_ETHER)
return -EOPNOTSUPP;

nft_offload_update_dependency(ctx, &priv->data, priv->len);
nft_offload_update_dependency(ctx, &priv->data, reg->len);

return 0;
}
Expand Down
16 changes: 8 additions & 8 deletions net/netfilter/nft_meta.c
Original file line number Diff line number Diff line change
Expand Up @@ -724,22 +724,22 @@ static int nft_meta_get_offload(struct nft_offload_ctx *ctx,

switch (priv->key) {
case NFT_META_PROTOCOL:
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_BASIC, basic, n_proto,
sizeof(__u16), reg);
NFT_OFFLOAD_MATCH_EXACT(FLOW_DISSECTOR_KEY_BASIC, basic, n_proto,
sizeof(__u16), reg);
nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_NETWORK);
break;
case NFT_META_L4PROTO:
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_BASIC, basic, ip_proto,
sizeof(__u8), reg);
NFT_OFFLOAD_MATCH_EXACT(FLOW_DISSECTOR_KEY_BASIC, basic, ip_proto,
sizeof(__u8), reg);
nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_TRANSPORT);
break;
case NFT_META_IIF:
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_META, meta,
ingress_ifindex, sizeof(__u32), reg);
NFT_OFFLOAD_MATCH_EXACT(FLOW_DISSECTOR_KEY_META, meta,
ingress_ifindex, sizeof(__u32), reg);
break;
case NFT_META_IIFTYPE:
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_META, meta,
ingress_iftype, sizeof(__u16), reg);
NFT_OFFLOAD_MATCH_EXACT(FLOW_DISSECTOR_KEY_META, meta,
ingress_iftype, sizeof(__u16), reg);
break;
default:
return -EOPNOTSUPP;
Expand Down
64 changes: 47 additions & 17 deletions net/netfilter/nft_payload.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,32 @@ static int nft_payload_dump(struct sk_buff *skb, const struct nft_expr *expr)
return -1;
}

static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
u32 priv_len, u32 field_len)
{
unsigned int remainder, delta, k;
struct nft_data mask = {};

if (priv_len == field_len) {
memset(&reg->mask, 0xff, priv_len);
return true;
} else if (priv_len > field_len) {
return false;
}

memset(&mask, 0xff, field_len);
remainder = priv_len % sizeof(u32);
if (remainder) {
k = priv_len / sizeof(u32);
delta = field_len - priv_len;
mask.data[k] = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1));
}

memcpy(&reg->mask, &mask, field_len);

return true;
}

static int nft_payload_offload_ll(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
const struct nft_payload *priv)
Expand All @@ -173,52 +199,52 @@ static int nft_payload_offload_ll(struct nft_offload_ctx *ctx,

switch (priv->offset) {
case offsetof(struct ethhdr, h_source):
if (priv->len != ETH_ALEN)
if (!nft_payload_offload_mask(reg, priv->len, ETH_ALEN))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_ETH_ADDRS, eth_addrs,
src, ETH_ALEN, reg);
break;
case offsetof(struct ethhdr, h_dest):
if (priv->len != ETH_ALEN)
if (!nft_payload_offload_mask(reg, priv->len, ETH_ALEN))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_ETH_ADDRS, eth_addrs,
dst, ETH_ALEN, reg);
break;
case offsetof(struct ethhdr, h_proto):
if (priv->len != sizeof(__be16))
if (!nft_payload_offload_mask(reg, priv->len, sizeof(__be16)))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_BASIC, basic,
n_proto, sizeof(__be16), reg);
nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_NETWORK);
break;
case offsetof(struct vlan_ethhdr, h_vlan_TCI):
if (priv->len != sizeof(__be16))
if (!nft_payload_offload_mask(reg, priv->len, sizeof(__be16)))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_VLAN, vlan,
vlan_tci, sizeof(__be16), reg);
break;
case offsetof(struct vlan_ethhdr, h_vlan_encapsulated_proto):
if (priv->len != sizeof(__be16))
if (!nft_payload_offload_mask(reg, priv->len, sizeof(__be16)))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_VLAN, vlan,
vlan_tpid, sizeof(__be16), reg);
nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_NETWORK);
break;
case offsetof(struct vlan_ethhdr, h_vlan_TCI) + sizeof(struct vlan_hdr):
if (priv->len != sizeof(__be16))
if (!nft_payload_offload_mask(reg, priv->len, sizeof(__be16)))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_CVLAN, vlan,
vlan_tci, sizeof(__be16), reg);
break;
case offsetof(struct vlan_ethhdr, h_vlan_encapsulated_proto) +
sizeof(struct vlan_hdr):
if (priv->len != sizeof(__be16))
if (!nft_payload_offload_mask(reg, priv->len, sizeof(__be16)))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_CVLAN, vlan,
Expand All @@ -239,23 +265,25 @@ static int nft_payload_offload_ip(struct nft_offload_ctx *ctx,

switch (priv->offset) {
case offsetof(struct iphdr, saddr):
if (priv->len != sizeof(struct in_addr))
if (!nft_payload_offload_mask(reg, priv->len,
sizeof(struct in_addr)))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4, src,
sizeof(struct in_addr), reg);
nft_flow_rule_set_addr_type(flow, FLOW_DISSECTOR_KEY_IPV4_ADDRS);
break;
case offsetof(struct iphdr, daddr):
if (priv->len != sizeof(struct in_addr))
if (!nft_payload_offload_mask(reg, priv->len,
sizeof(struct in_addr)))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4, dst,
sizeof(struct in_addr), reg);
nft_flow_rule_set_addr_type(flow, FLOW_DISSECTOR_KEY_IPV4_ADDRS);
break;
case offsetof(struct iphdr, protocol):
if (priv->len != sizeof(__u8))
if (!nft_payload_offload_mask(reg, priv->len, sizeof(__u8)))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_BASIC, basic, ip_proto,
Expand All @@ -277,23 +305,25 @@ static int nft_payload_offload_ip6(struct nft_offload_ctx *ctx,

switch (priv->offset) {
case offsetof(struct ipv6hdr, saddr):
if (priv->len != sizeof(struct in6_addr))
if (!nft_payload_offload_mask(reg, priv->len,
sizeof(struct in6_addr)))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6, src,
sizeof(struct in6_addr), reg);
nft_flow_rule_set_addr_type(flow, FLOW_DISSECTOR_KEY_IPV6_ADDRS);
break;
case offsetof(struct ipv6hdr, daddr):
if (priv->len != sizeof(struct in6_addr))
if (!nft_payload_offload_mask(reg, priv->len,
sizeof(struct in6_addr)))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6, dst,
sizeof(struct in6_addr), reg);
nft_flow_rule_set_addr_type(flow, FLOW_DISSECTOR_KEY_IPV6_ADDRS);
break;
case offsetof(struct ipv6hdr, nexthdr):
if (priv->len != sizeof(__u8))
if (!nft_payload_offload_mask(reg, priv->len, sizeof(__u8)))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_BASIC, basic, ip_proto,
Expand Down Expand Up @@ -335,14 +365,14 @@ static int nft_payload_offload_tcp(struct nft_offload_ctx *ctx,

switch (priv->offset) {
case offsetof(struct tcphdr, source):
if (priv->len != sizeof(__be16))
if (!nft_payload_offload_mask(reg, priv->len, sizeof(__be16)))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_PORTS, tp, src,
sizeof(__be16), reg);
break;
case offsetof(struct tcphdr, dest):
if (priv->len != sizeof(__be16))
if (!nft_payload_offload_mask(reg, priv->len, sizeof(__be16)))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_PORTS, tp, dst,
Expand All @@ -363,14 +393,14 @@ static int nft_payload_offload_udp(struct nft_offload_ctx *ctx,

switch (priv->offset) {
case offsetof(struct udphdr, source):
if (priv->len != sizeof(__be16))
if (!nft_payload_offload_mask(reg, priv->len, sizeof(__be16)))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_PORTS, tp, src,
sizeof(__be16), reg);
break;
case offsetof(struct udphdr, dest):
if (priv->len != sizeof(__be16))
if (!nft_payload_offload_mask(reg, priv->len, sizeof(__be16)))
return -EOPNOTSUPP;

NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_PORTS, tp, dst,
Expand Down

0 comments on commit b0df362

Please sign in to comment.