net: bridge: resolve forwarding path for VLAN tag actions in bridge devices
Depending on the VLAN settings of the bridge and the port, the bridge can either add or remove a tag. When vlan filtering is enabled, the fdb lookup also needs to know the VLAN tag/proto for the destination address To provide this, keep track of the stack of VLAN tags for the path in the lookup context 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:
Родитель
ec9d16bab6
Коммит
bcf2766b13
|
@ -862,10 +862,20 @@ struct net_device_path {
|
||||||
u16 id;
|
u16 id;
|
||||||
__be16 proto;
|
__be16 proto;
|
||||||
} encap;
|
} encap;
|
||||||
|
struct {
|
||||||
|
enum {
|
||||||
|
DEV_PATH_BR_VLAN_KEEP,
|
||||||
|
DEV_PATH_BR_VLAN_TAG,
|
||||||
|
DEV_PATH_BR_VLAN_UNTAG,
|
||||||
|
} vlan_mode;
|
||||||
|
u16 vlan_id;
|
||||||
|
__be16 vlan_proto;
|
||||||
|
} bridge;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NET_DEVICE_PATH_STACK_MAX 5
|
#define NET_DEVICE_PATH_STACK_MAX 5
|
||||||
|
#define NET_DEVICE_PATH_VLAN_MAX 2
|
||||||
|
|
||||||
struct net_device_path_stack {
|
struct net_device_path_stack {
|
||||||
int num_paths;
|
int num_paths;
|
||||||
|
@ -875,6 +885,12 @@ struct net_device_path_stack {
|
||||||
struct net_device_path_ctx {
|
struct net_device_path_ctx {
|
||||||
const struct net_device *dev;
|
const struct net_device *dev;
|
||||||
const u8 *daddr;
|
const u8 *daddr;
|
||||||
|
|
||||||
|
int num_vlans;
|
||||||
|
struct {
|
||||||
|
u16 id;
|
||||||
|
__be16 proto;
|
||||||
|
} vlan[NET_DEVICE_PATH_VLAN_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
enum tc_setup_type {
|
enum tc_setup_type {
|
||||||
|
|
|
@ -786,6 +786,12 @@ static int vlan_dev_fill_forward_path(struct net_device_path_ctx *ctx,
|
||||||
path->encap.proto = vlan->vlan_proto;
|
path->encap.proto = vlan->vlan_proto;
|
||||||
path->dev = ctx->dev;
|
path->dev = ctx->dev;
|
||||||
ctx->dev = vlan->real_dev;
|
ctx->dev = vlan->real_dev;
|
||||||
|
if (ctx->num_vlans >= ARRAY_SIZE(ctx->vlan))
|
||||||
|
return -ENOSPC;
|
||||||
|
|
||||||
|
ctx->vlan[ctx->num_vlans].id = vlan->vlan_id;
|
||||||
|
ctx->vlan[ctx->num_vlans].proto = vlan->vlan_proto;
|
||||||
|
ctx->num_vlans++;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -396,7 +396,10 @@ static int br_fill_forward_path(struct net_device_path_ctx *ctx,
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
br = netdev_priv(ctx->dev);
|
br = netdev_priv(ctx->dev);
|
||||||
f = br_fdb_find_rcu(br, ctx->daddr, 0);
|
|
||||||
|
br_vlan_fill_forward_path_pvid(br, ctx, path);
|
||||||
|
|
||||||
|
f = br_fdb_find_rcu(br, ctx->daddr, path->bridge.vlan_id);
|
||||||
if (!f || !f->dst)
|
if (!f || !f->dst)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -404,10 +407,28 @@ static int br_fill_forward_path(struct net_device_path_ctx *ctx,
|
||||||
if (!dst)
|
if (!dst)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
if (br_vlan_fill_forward_path_mode(br, dst, path))
|
||||||
|
return -1;
|
||||||
|
|
||||||
path->type = DEV_PATH_BRIDGE;
|
path->type = DEV_PATH_BRIDGE;
|
||||||
path->dev = dst->br->dev;
|
path->dev = dst->br->dev;
|
||||||
ctx->dev = dst->dev;
|
ctx->dev = dst->dev;
|
||||||
|
|
||||||
|
switch (path->bridge.vlan_mode) {
|
||||||
|
case DEV_PATH_BR_VLAN_TAG:
|
||||||
|
if (ctx->num_vlans >= ARRAY_SIZE(ctx->vlan))
|
||||||
|
return -ENOSPC;
|
||||||
|
ctx->vlan[ctx->num_vlans].id = path->bridge.vlan_id;
|
||||||
|
ctx->vlan[ctx->num_vlans].proto = path->bridge.vlan_proto;
|
||||||
|
ctx->num_vlans++;
|
||||||
|
break;
|
||||||
|
case DEV_PATH_BR_VLAN_UNTAG:
|
||||||
|
ctx->num_vlans--;
|
||||||
|
break;
|
||||||
|
case DEV_PATH_BR_VLAN_KEEP:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1118,6 +1118,13 @@ void br_vlan_notify(const struct net_bridge *br,
|
||||||
bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
|
bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
|
||||||
const struct net_bridge_vlan *range_end);
|
const struct net_bridge_vlan *range_end);
|
||||||
|
|
||||||
|
void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
|
||||||
|
struct net_device_path_ctx *ctx,
|
||||||
|
struct net_device_path *path);
|
||||||
|
int br_vlan_fill_forward_path_mode(struct net_bridge *br,
|
||||||
|
struct net_bridge_port *dst,
|
||||||
|
struct net_device_path *path);
|
||||||
|
|
||||||
static inline struct net_bridge_vlan_group *br_vlan_group(
|
static inline struct net_bridge_vlan_group *br_vlan_group(
|
||||||
const struct net_bridge *br)
|
const struct net_bridge *br)
|
||||||
{
|
{
|
||||||
|
@ -1277,6 +1284,19 @@ static inline int nbp_get_num_vlan_infos(struct net_bridge_port *p,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
|
||||||
|
struct net_device_path_ctx *ctx,
|
||||||
|
struct net_device_path *path)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int br_vlan_fill_forward_path_mode(struct net_bridge *br,
|
||||||
|
struct net_bridge_port *dst,
|
||||||
|
struct net_device_path *path)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static inline struct net_bridge_vlan_group *br_vlan_group(
|
static inline struct net_bridge_vlan_group *br_vlan_group(
|
||||||
const struct net_bridge *br)
|
const struct net_bridge *br)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1339,6 +1339,59 @@ int br_vlan_get_pvid_rcu(const struct net_device *dev, u16 *p_pvid)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(br_vlan_get_pvid_rcu);
|
EXPORT_SYMBOL_GPL(br_vlan_get_pvid_rcu);
|
||||||
|
|
||||||
|
void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
|
||||||
|
struct net_device_path_ctx *ctx,
|
||||||
|
struct net_device_path *path)
|
||||||
|
{
|
||||||
|
struct net_bridge_vlan_group *vg;
|
||||||
|
int idx = ctx->num_vlans - 1;
|
||||||
|
u16 vid;
|
||||||
|
|
||||||
|
path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP;
|
||||||
|
|
||||||
|
if (!br_opt_get(br, BROPT_VLAN_ENABLED))
|
||||||
|
return;
|
||||||
|
|
||||||
|
vg = br_vlan_group(br);
|
||||||
|
|
||||||
|
if (idx >= 0 &&
|
||||||
|
ctx->vlan[idx].proto == br->vlan_proto) {
|
||||||
|
vid = ctx->vlan[idx].id;
|
||||||
|
} else {
|
||||||
|
path->bridge.vlan_mode = DEV_PATH_BR_VLAN_TAG;
|
||||||
|
vid = br_get_pvid(vg);
|
||||||
|
}
|
||||||
|
|
||||||
|
path->bridge.vlan_id = vid;
|
||||||
|
path->bridge.vlan_proto = br->vlan_proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
int br_vlan_fill_forward_path_mode(struct net_bridge *br,
|
||||||
|
struct net_bridge_port *dst,
|
||||||
|
struct net_device_path *path)
|
||||||
|
{
|
||||||
|
struct net_bridge_vlan_group *vg;
|
||||||
|
struct net_bridge_vlan *v;
|
||||||
|
|
||||||
|
if (!br_opt_get(br, BROPT_VLAN_ENABLED))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
vg = nbp_vlan_group_rcu(dst);
|
||||||
|
v = br_vlan_find(vg, path->bridge.vlan_id);
|
||||||
|
if (!v || !br_vlan_should_use(v))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!(v->flags & BRIDGE_VLAN_INFO_UNTAGGED))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (path->bridge.vlan_mode == DEV_PATH_BR_VLAN_TAG)
|
||||||
|
path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP;
|
||||||
|
else
|
||||||
|
path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int br_vlan_get_info(const struct net_device *dev, u16 vid,
|
int br_vlan_get_info(const struct net_device *dev, u16 vid,
|
||||||
struct bridge_vlan_info *p_vinfo)
|
struct bridge_vlan_info *p_vinfo)
|
||||||
{
|
{
|
||||||
|
|
Загрузка…
Ссылка в новой задаче