netfilter: flowtable: bridge vlan hardware offload and switchdev

The switch might have already added the VLAN tag through PVID hardware
offload. Keep this extra VLAN in the flowtable but skip it on egress.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Felix Fietkau 2021-03-24 02:30:48 +01:00 committed by David S. Miller
parent 73f97025a9
commit 26267bf9bb
7 changed files with 21 additions and 4 deletions

View File

@ -870,6 +870,7 @@ struct net_device_path {
DEV_PATH_BR_VLAN_KEEP, DEV_PATH_BR_VLAN_KEEP,
DEV_PATH_BR_VLAN_TAG, DEV_PATH_BR_VLAN_TAG,
DEV_PATH_BR_VLAN_UNTAG, DEV_PATH_BR_VLAN_UNTAG,
DEV_PATH_BR_VLAN_UNTAG_HW,
} vlan_mode; } vlan_mode;
u16 vlan_id; u16 vlan_id;
__be16 vlan_proto; __be16 vlan_proto;

View File

@ -123,9 +123,10 @@ struct flow_offload_tuple {
/* All members above are keys for lookups, see flow_offload_hash(). */ /* All members above are keys for lookups, see flow_offload_hash(). */
struct { } __hash; struct { } __hash;
u8 dir:4, u8 dir:2,
xmit_type:2, xmit_type:2,
encap_num:2; encap_num:2,
in_vlan_ingress:2;
u16 mtu; u16 mtu;
union { union {
struct dst_entry *dst_cache; struct dst_entry *dst_cache;
@ -185,7 +186,8 @@ struct nf_flow_route {
u16 id; u16 id;
__be16 proto; __be16 proto;
} encap[NF_FLOW_TABLE_ENCAP_MAX]; } encap[NF_FLOW_TABLE_ENCAP_MAX];
u8 num_encaps; u8 num_encaps:2,
ingress_vlans:2;
} in; } in;
struct { struct {
u32 ifindex; u32 ifindex;

View File

@ -422,6 +422,7 @@ static int br_fill_forward_path(struct net_device_path_ctx *ctx,
ctx->vlan[ctx->num_vlans].proto = path->bridge.vlan_proto; ctx->vlan[ctx->num_vlans].proto = path->bridge.vlan_proto;
ctx->num_vlans++; ctx->num_vlans++;
break; break;
case DEV_PATH_BR_VLAN_UNTAG_HW:
case DEV_PATH_BR_VLAN_UNTAG: case DEV_PATH_BR_VLAN_UNTAG:
ctx->num_vlans--; ctx->num_vlans--;
break; break;

View File

@ -1386,6 +1386,8 @@ int br_vlan_fill_forward_path_mode(struct net_bridge *br,
if (path->bridge.vlan_mode == DEV_PATH_BR_VLAN_TAG) if (path->bridge.vlan_mode == DEV_PATH_BR_VLAN_TAG)
path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP; path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP;
else if (v->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV)
path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG_HW;
else else
path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG; path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG;

View File

@ -95,6 +95,8 @@ static int flow_offload_fill_route(struct flow_offload *flow,
for (i = route->tuple[dir].in.num_encaps - 1; i >= 0; i--) { for (i = route->tuple[dir].in.num_encaps - 1; i >= 0; i--) {
flow_tuple->encap[j].id = route->tuple[dir].in.encap[i].id; flow_tuple->encap[j].id = route->tuple[dir].in.encap[i].id;
flow_tuple->encap[j].proto = route->tuple[dir].in.encap[i].proto; flow_tuple->encap[j].proto = route->tuple[dir].in.encap[i].proto;
if (route->tuple[dir].in.ingress_vlans & BIT(i))
flow_tuple->in_vlan_ingress |= BIT(j);
j++; j++;
} }
flow_tuple->encap_num = route->tuple[dir].in.num_encaps; flow_tuple->encap_num = route->tuple[dir].in.num_encaps;

View File

@ -594,8 +594,12 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
other_tuple = &flow->tuplehash[!dir].tuple; other_tuple = &flow->tuplehash[!dir].tuple;
for (i = 0; i < other_tuple->encap_num; i++) { for (i = 0; i < other_tuple->encap_num; i++) {
struct flow_action_entry *entry = flow_action_entry_next(flow_rule); struct flow_action_entry *entry;
if (other_tuple->in_vlan_ingress & BIT(i))
continue;
entry = flow_action_entry_next(flow_rule);
entry->id = FLOW_ACTION_VLAN_PUSH; entry->id = FLOW_ACTION_VLAN_PUSH;
entry->vlan.vid = other_tuple->encap[i].id; entry->vlan.vid = other_tuple->encap[i].id;
entry->vlan.proto = other_tuple->encap[i].proto; entry->vlan.proto = other_tuple->encap[i].proto;

View File

@ -72,6 +72,7 @@ struct nft_forward_info {
__be16 proto; __be16 proto;
} encap[NF_FLOW_TABLE_ENCAP_MAX]; } encap[NF_FLOW_TABLE_ENCAP_MAX];
u8 num_encaps; u8 num_encaps;
u8 ingress_vlans;
u8 h_source[ETH_ALEN]; u8 h_source[ETH_ALEN];
u8 h_dest[ETH_ALEN]; u8 h_dest[ETH_ALEN];
enum flow_offload_xmit_type xmit_type; enum flow_offload_xmit_type xmit_type;
@ -130,6 +131,9 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack,
memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
switch (path->bridge.vlan_mode) { switch (path->bridge.vlan_mode) {
case DEV_PATH_BR_VLAN_UNTAG_HW:
info->ingress_vlans |= BIT(info->num_encaps - 1);
break;
case DEV_PATH_BR_VLAN_TAG: case DEV_PATH_BR_VLAN_TAG:
info->encap[info->num_encaps].id = path->bridge.vlan_id; info->encap[info->num_encaps].id = path->bridge.vlan_id;
info->encap[info->num_encaps].proto = path->bridge.vlan_proto; info->encap[info->num_encaps].proto = path->bridge.vlan_proto;
@ -198,6 +202,7 @@ static void nft_dev_forward_path(struct nf_flow_route *route,
route->tuple[!dir].in.encap[i].proto = info.encap[i].proto; route->tuple[!dir].in.encap[i].proto = info.encap[i].proto;
} }
route->tuple[!dir].in.num_encaps = info.num_encaps; route->tuple[!dir].in.num_encaps = info.num_encaps;
route->tuple[!dir].in.ingress_vlans = info.ingress_vlans;
if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) { if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {
memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN); memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);