netfilter: nft_payload: fix missing check for matching length in offloads

Payload offload rule should also check the length of the match.
Moreover, check for unsupported link-layer fields:

 nft --debug=netlink add rule firewall zones vlan id 100
 ...
 [ payload load 2b @ link header + 0 => reg 1 ]

this loads 2byte base on ll header and offset 0.

This also fixes unsupported raw payload match.

Fixes: 92ad6325cb ("netfilter: nf_tables: add hardware offload support")
Signed-off-by: wenxu <wenxu@ucloud.cn>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
wenxu 2019-10-24 15:52:45 +08:00 committed by Pablo Neira Ayuso
parent 085461c897
commit a69a85da45
1 changed files with 38 additions and 0 deletions

View File

@ -161,13 +161,21 @@ static int nft_payload_offload_ll(struct nft_offload_ctx *ctx,
switch (priv->offset) { switch (priv->offset) {
case offsetof(struct ethhdr, h_source): case offsetof(struct ethhdr, h_source):
if (priv->len != ETH_ALEN)
return -EOPNOTSUPP;
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_ETH_ADDRS, eth_addrs, NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_ETH_ADDRS, eth_addrs,
src, ETH_ALEN, reg); src, ETH_ALEN, reg);
break; break;
case offsetof(struct ethhdr, h_dest): case offsetof(struct ethhdr, h_dest):
if (priv->len != ETH_ALEN)
return -EOPNOTSUPP;
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_ETH_ADDRS, eth_addrs, NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_ETH_ADDRS, eth_addrs,
dst, ETH_ALEN, reg); dst, ETH_ALEN, reg);
break; break;
default:
return -EOPNOTSUPP;
} }
return 0; return 0;
@ -181,14 +189,23 @@ static int nft_payload_offload_ip(struct nft_offload_ctx *ctx,
switch (priv->offset) { switch (priv->offset) {
case offsetof(struct iphdr, saddr): case offsetof(struct iphdr, saddr):
if (priv->len != sizeof(struct in_addr))
return -EOPNOTSUPP;
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4, src, NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4, src,
sizeof(struct in_addr), reg); sizeof(struct in_addr), reg);
break; break;
case offsetof(struct iphdr, daddr): case offsetof(struct iphdr, daddr):
if (priv->len != sizeof(struct in_addr))
return -EOPNOTSUPP;
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4, dst, NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4, dst,
sizeof(struct in_addr), reg); sizeof(struct in_addr), reg);
break; break;
case offsetof(struct iphdr, protocol): case offsetof(struct iphdr, protocol):
if (priv->len != sizeof(__u8))
return -EOPNOTSUPP;
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_BASIC, basic, ip_proto, NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_BASIC, basic, ip_proto,
sizeof(__u8), reg); sizeof(__u8), reg);
nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_TRANSPORT); nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_TRANSPORT);
@ -208,14 +225,23 @@ static int nft_payload_offload_ip6(struct nft_offload_ctx *ctx,
switch (priv->offset) { switch (priv->offset) {
case offsetof(struct ipv6hdr, saddr): case offsetof(struct ipv6hdr, saddr):
if (priv->len != sizeof(struct in6_addr))
return -EOPNOTSUPP;
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6, src, NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6, src,
sizeof(struct in6_addr), reg); sizeof(struct in6_addr), reg);
break; break;
case offsetof(struct ipv6hdr, daddr): case offsetof(struct ipv6hdr, daddr):
if (priv->len != sizeof(struct in6_addr))
return -EOPNOTSUPP;
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6, dst, NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6, dst,
sizeof(struct in6_addr), reg); sizeof(struct in6_addr), reg);
break; break;
case offsetof(struct ipv6hdr, nexthdr): case offsetof(struct ipv6hdr, nexthdr):
if (priv->len != sizeof(__u8))
return -EOPNOTSUPP;
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_BASIC, basic, ip_proto, NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_BASIC, basic, ip_proto,
sizeof(__u8), reg); sizeof(__u8), reg);
nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_TRANSPORT); nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_TRANSPORT);
@ -255,10 +281,16 @@ static int nft_payload_offload_tcp(struct nft_offload_ctx *ctx,
switch (priv->offset) { switch (priv->offset) {
case offsetof(struct tcphdr, source): case offsetof(struct tcphdr, source):
if (priv->len != sizeof(__be16))
return -EOPNOTSUPP;
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_PORTS, tp, src, NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_PORTS, tp, src,
sizeof(__be16), reg); sizeof(__be16), reg);
break; break;
case offsetof(struct tcphdr, dest): case offsetof(struct tcphdr, dest):
if (priv->len != sizeof(__be16))
return -EOPNOTSUPP;
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_PORTS, tp, dst, NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_PORTS, tp, dst,
sizeof(__be16), reg); sizeof(__be16), reg);
break; break;
@ -277,10 +309,16 @@ static int nft_payload_offload_udp(struct nft_offload_ctx *ctx,
switch (priv->offset) { switch (priv->offset) {
case offsetof(struct udphdr, source): case offsetof(struct udphdr, source):
if (priv->len != sizeof(__be16))
return -EOPNOTSUPP;
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_PORTS, tp, src, NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_PORTS, tp, src,
sizeof(__be16), reg); sizeof(__be16), reg);
break; break;
case offsetof(struct udphdr, dest): case offsetof(struct udphdr, dest):
if (priv->len != sizeof(__be16))
return -EOPNOTSUPP;
NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_PORTS, tp, dst, NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_PORTS, tp, dst,
sizeof(__be16), reg); sizeof(__be16), reg);
break; break;