From: Felix Fietkau Date: Wed, 10 Feb 2021 19:44:33 +0100 Subject: [PATCH] netfilter: flowtable: handle bridge vlan filter offload tags from DSA/switchdev When a switchdev/DSA port is an untagged member of a bridge vlan, ingress packets could potentially be tagged with the id of the VLAN. When the VLAN port group has been uploaded to switchdev, report the bridge tag mode as DEV_PATH_BR_VLAN_UNTAG_HW instead of DEV_PATH_BR_VLAN_UNTAG and handle it in netfilter flow offloading by storing the tag in the tuple in_pvid field. This allows the ingress hook to detect the optional tag and remove it for software offload. This tag processing is for ingress only, egress needs to be fully untagged Signed-off-by: Felix Fietkau --- --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -853,6 +853,7 @@ struct net_device_path { DEV_PATH_BR_VLAN_KEEP, DEV_PATH_BR_VLAN_TAG, DEV_PATH_BR_VLAN_UNTAG, + DEV_PATH_BR_VLAN_UNTAG_HW, } vlan_mode; u16 vlan_id; __be16 vlan_proto; --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h @@ -183,6 +183,10 @@ struct nf_flow_route { u32 ifindex; u16 vid[NF_FLOW_TABLE_VLAN_MAX]; __be16 vproto[NF_FLOW_TABLE_VLAN_MAX]; + struct { + u16 id; + __be16 proto; + } pvid; u8 num_vlans; } in; struct { --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -435,6 +435,7 @@ static int br_fill_forward_path(struct n ctx->vlan[ctx->num_vlans].proto = path->bridge.vlan_proto; ctx->num_vlans++; break; + case DEV_PATH_BR_VLAN_UNTAG_HW: case DEV_PATH_BR_VLAN_UNTAG: ctx->num_vlans--; break; --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -1374,6 +1374,8 @@ int br_vlan_fill_forward_path_mode(struc if (path->bridge.vlan_mode == DEV_PATH_BR_VLAN_TAG) 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 path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG; --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -98,6 +98,8 @@ static int flow_offload_fill_route(struc j++; } flow_tuple->in_vlan_num = route->tuple[dir].in.num_vlans; + flow_tuple->in_pvid.id = route->tuple[dir].in.pvid.id; + flow_tuple->in_pvid.proto = route->tuple[dir].in.pvid.proto; switch (route->tuple[dir].xmit_type) { case FLOW_OFFLOAD_XMIT_DIRECT: --- a/net/netfilter/nft_flow_offload.c +++ b/net/netfilter/nft_flow_offload.c @@ -67,6 +67,10 @@ struct nft_forward_info { const struct net_device *dev; __u16 vid[NF_FLOW_TABLE_VLAN_MAX]; __be16 vproto[NF_FLOW_TABLE_VLAN_MAX]; + struct { + __u16 id; + __be16 proto; + } pvid; u8 num_vlans; u8 h_source[ETH_ALEN]; u8 h_dest[ETH_ALEN]; @@ -127,6 +131,10 @@ static void nft_dev_path_info(const stru info->vproto[info->num_vlans] = path->bridge.vlan_proto; info->num_vlans++; break; + case DEV_PATH_BR_VLAN_UNTAG_HW: + info->pvid.id = info->vid[info->num_vlans - 1]; + info->pvid.proto = info->vproto[info->num_vlans - 1]; + fallthrough; case DEV_PATH_BR_VLAN_UNTAG: info->num_vlans--; break; @@ -185,6 +193,8 @@ static void nft_dev_forward_path(struct route->tuple[!dir].in.vid[i] = info.vid[i]; route->tuple[!dir].in.vproto[i] = info.vproto[i]; } + route->tuple[!dir].in.pvid.id = info.pvid.id; + route->tuple[!dir].in.pvid.proto = info.pvid.proto; route->tuple[!dir].in.num_vlans = info.num_vlans; if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {