| From ba85ed49433d533597ce1b13cf723894869c758f Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Mon, 12 Apr 2021 14:20:55 +0200 |
| Subject: netfilter: nftables_offload: special ethertype handling for VLAN |
| |
| From: Pablo Neira Ayuso <pablo@netfilter.org> |
| |
| [ Upstream commit 783003f3bb8a565326e89d18bbd948ad8ffc816a ] |
| |
| The nftables offload parser sets FLOW_DISSECTOR_KEY_BASIC .n_proto to the |
| ethertype field in the ethertype frame. However: |
| |
| - FLOW_DISSECTOR_KEY_BASIC .n_proto field always stores either IPv4 or IPv6 |
| ethertypes. |
| - FLOW_DISSECTOR_KEY_VLAN .vlan_tpid stores either the 802.1q and 802.1ad |
| ethertypes. Same as for FLOW_DISSECTOR_KEY_CVLAN. |
| |
| This function adjusts the flow dissector to handle two scenarios: |
| |
| 1) FLOW_DISSECTOR_KEY_VLAN .vlan_tpid is set to 802.1q or 802.1ad. |
| Then, transfer: |
| - the .n_proto field to FLOW_DISSECTOR_KEY_VLAN .tpid. |
| - the original FLOW_DISSECTOR_KEY_VLAN .tpid to the |
| FLOW_DISSECTOR_KEY_CVLAN .tpid |
| - the original FLOW_DISSECTOR_KEY_CVLAN .tpid to the .n_proto field. |
| |
| 2) .n_proto is set to 802.1q or 802.1ad. Then, transfer: |
| - the .n_proto field to FLOW_DISSECTOR_KEY_VLAN .tpid. |
| - the original FLOW_DISSECTOR_KEY_VLAN .tpid to the .n_proto field. |
| |
| Fixes: a82055af5959 ("netfilter: nft_payload: add VLAN offload support") |
| Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/netfilter/nf_tables_offload.c | 44 +++++++++++++++++++++++++++++++ |
| 1 file changed, 44 insertions(+) |
| |
| diff --git a/net/netfilter/nf_tables_offload.c b/net/netfilter/nf_tables_offload.c |
| index 9ae14270c543..2b00f7f47693 100644 |
| --- a/net/netfilter/nf_tables_offload.c |
| +++ b/net/netfilter/nf_tables_offload.c |
| @@ -45,6 +45,48 @@ void nft_flow_rule_set_addr_type(struct nft_flow_rule *flow, |
| offsetof(struct nft_flow_key, control); |
| } |
| |
| +struct nft_offload_ethertype { |
| + __be16 value; |
| + __be16 mask; |
| +}; |
| + |
| +static void nft_flow_rule_transfer_vlan(struct nft_offload_ctx *ctx, |
| + struct nft_flow_rule *flow) |
| +{ |
| + struct nft_flow_match *match = &flow->match; |
| + struct nft_offload_ethertype ethertype; |
| + |
| + if (match->dissector.used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL) && |
| + match->key.basic.n_proto != htons(ETH_P_8021Q) && |
| + match->key.basic.n_proto != htons(ETH_P_8021AD)) |
| + return; |
| + |
| + ethertype.value = match->key.basic.n_proto; |
| + ethertype.mask = match->mask.basic.n_proto; |
| + |
| + if (match->dissector.used_keys & BIT(FLOW_DISSECTOR_KEY_VLAN) && |
| + (match->key.vlan.vlan_tpid == htons(ETH_P_8021Q) || |
| + match->key.vlan.vlan_tpid == htons(ETH_P_8021AD))) { |
| + match->key.basic.n_proto = match->key.cvlan.vlan_tpid; |
| + match->mask.basic.n_proto = match->mask.cvlan.vlan_tpid; |
| + match->key.cvlan.vlan_tpid = match->key.vlan.vlan_tpid; |
| + match->mask.cvlan.vlan_tpid = match->mask.vlan.vlan_tpid; |
| + match->key.vlan.vlan_tpid = ethertype.value; |
| + match->mask.vlan.vlan_tpid = ethertype.mask; |
| + match->dissector.offset[FLOW_DISSECTOR_KEY_CVLAN] = |
| + offsetof(struct nft_flow_key, cvlan); |
| + match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_CVLAN); |
| + } else { |
| + match->key.basic.n_proto = match->key.vlan.vlan_tpid; |
| + match->mask.basic.n_proto = match->mask.vlan.vlan_tpid; |
| + match->key.vlan.vlan_tpid = ethertype.value; |
| + match->mask.vlan.vlan_tpid = ethertype.mask; |
| + match->dissector.offset[FLOW_DISSECTOR_KEY_VLAN] = |
| + offsetof(struct nft_flow_key, vlan); |
| + match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN); |
| + } |
| +} |
| + |
| struct nft_flow_rule *nft_flow_rule_create(struct net *net, |
| const struct nft_rule *rule) |
| { |
| @@ -89,6 +131,8 @@ struct nft_flow_rule *nft_flow_rule_create(struct net *net, |
| |
| expr = nft_expr_next(expr); |
| } |
| + nft_flow_rule_transfer_vlan(ctx, flow); |
| + |
| flow->proto = ctx->dep.l3num; |
| kfree(ctx); |
| |
| -- |
| 2.30.2 |
| |