Commit b8bde1c4 authored by David S. Miller's avatar David S. Miller

Merge branch 'bridge_pvid'

Toshiaki Makita says:

====================
bridge: Fix problems around the PVID

There seem to be some undesirable behaviors related with PVID.
1. It has no effect assigning PVID to a port. PVID cannot be applied
to any frame regardless of whether we set it or not.
2. FDB entries learned via frames applied PVID are registered with
VID 0 rather than VID value of PVID.
3. We can set 0 or 4095 as a PVID that are not allowed in IEEE 802.1Q.
This leads interoperational problems such as sending frames with VID
4095, which is not allowed in IEEE 802.1Q, and treating frames with VID
0 as they belong to VLAN 0, which is expected to be handled as they have
no VID according to IEEE 802.1Q.

Note: 2nd and 3rd problems are potential and not exposed unless 1st problem
is fixed, because we cannot activate PVID due to it.

This is my analysis for each behavior.
1. We are using VLAN_TAG_PRESENT bit when getting PVID, and not when
adding/deleting PVID.
It can be fixed in either way using or not using VLAN_TAG_PRESENT,
but I think the latter is slightly more efficient.

2. We are setting skb->vlan_tci with the value of PVID but the variable
vid, which is used in FDB later, is set to 0 at br_allowed_ingress()
when untagged frames arrive at a port with PVID valid. I'm afraid that
vid should be updated to the value of PVID if PVID is valid.

3. According to IEEE 802.1Q-2011 (6.9.1 and Table 9-2), we cannot use
VID 0 or 4095 as a PVID.
It looks like that there are more stuff to consider.

- VID 0:
VID 0 shall not be configured in any FDB entry and used in a tag header
to indicate it is a 802.1p priority-tagged frame.
Priority-tagged frames should be applied PVID (from IEEE 802.1Q 6.9.1).
In my opinion, since we can filter incomming priority-tagged frames by
deleting PVID, we don't need to filter them by vlan_bitmap.
In other words, priority-tagged frames don't have VID 0 but have no VID,
which is the same as untagged frames, and should be filtered by unsetting
PVID.
So, not only we cannot set PVID as 0, but also we don't need to add 0 to
vlan_bitmap, which enables us to simply forbid to add vlan 0.

- VID 4095:
VID 4095 shall not be transmitted in a tag header. This VID value may be
used to indicate a wildcard match for the VID in management operations or
FDB entries (from IEEE 802.1Q Table 9-2).
In current implementation, we can create a static FDB entry with all
existing VIDs by not specifying any VID when creating it.
I don't think this way to add wildcard-like entries needs to change,
and VID 4095 looks no use and can be unacceptable to add.

Consequently, I believe what we should do for 3rd problem is below:
- Not allowing VID 0 and 4095 to be added.
- Applying PVID to priority-tagged (VID 0) frames.

Note: It has been descovered that another problem related to priority-tags
remains. If we use vlan 0 interface such as eth0.0, we cannot communicate
with another end station via a linux bridge.
This problem exists regardless of whether this patch set is applied or not
because we might receive untagged frames from another end station even if we
are sending priority-tagged frames.
This issue will be addressed by another patch set introducing an additional
egress policy, on which Vlad Yasevich is working.
See http://marc.info/?t=137880893800001&r=1&w=2 for detailed discussion.

Patch set follows this mail.
The order of patches is not the same as described above, because the way
to fix 1st problem is based on the assumption that we don't use VID 0 as
a PVID, which is realized by fixing 3rd problem.
(1/4)(2/4): Fix 3rd problem.
(3/4): Fix 1st problem.
(4/4): Fix 2nd probelm.

v2:
- Add descriptions about the problem related to priority-tags in cover letter.
- Revise patch comments to reference the newest spec.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 4b6c7879 dfb5fa32
...@@ -700,7 +700,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], ...@@ -700,7 +700,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
vid = nla_get_u16(tb[NDA_VLAN]); vid = nla_get_u16(tb[NDA_VLAN]);
if (vid >= VLAN_N_VID) { if (!vid || vid >= VLAN_VID_MASK) {
pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n", pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n",
vid); vid);
return -EINVAL; return -EINVAL;
...@@ -794,7 +794,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], ...@@ -794,7 +794,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
vid = nla_get_u16(tb[NDA_VLAN]); vid = nla_get_u16(tb[NDA_VLAN]);
if (vid >= VLAN_N_VID) { if (!vid || vid >= VLAN_VID_MASK) {
pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n", pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n",
vid); vid);
return -EINVAL; return -EINVAL;
......
...@@ -243,7 +243,7 @@ static int br_afspec(struct net_bridge *br, ...@@ -243,7 +243,7 @@ static int br_afspec(struct net_bridge *br,
vinfo = nla_data(tb[IFLA_BRIDGE_VLAN_INFO]); vinfo = nla_data(tb[IFLA_BRIDGE_VLAN_INFO]);
if (vinfo->vid >= VLAN_N_VID) if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK)
return -EINVAL; return -EINVAL;
switch (cmd) { switch (cmd) {
......
...@@ -643,9 +643,7 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v) ...@@ -643,9 +643,7 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
* vid wasn't set * vid wasn't set
*/ */
smp_rmb(); smp_rmb();
return (v->pvid & VLAN_TAG_PRESENT) ? return v->pvid ?: VLAN_N_VID;
(v->pvid & ~VLAN_TAG_PRESENT) :
VLAN_N_VID;
} }
#else #else
......
...@@ -45,7 +45,6 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags) ...@@ -45,7 +45,6 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
return 0; return 0;
} }
if (vid) {
if (v->port_idx) { if (v->port_idx) {
p = v->parent.port; p = v->parent.port;
br = p->br; br = p->br;
...@@ -76,8 +75,6 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags) ...@@ -76,8 +75,6 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
goto out_filt; goto out_filt;
} }
}
set_bit(vid, v->vlan_bitmap); set_bit(vid, v->vlan_bitmap);
v->num_vlans++; v->num_vlans++;
__vlan_add_flags(v, vid, flags); __vlan_add_flags(v, vid, flags);
...@@ -98,7 +95,7 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid) ...@@ -98,7 +95,7 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid)
__vlan_delete_pvid(v, vid); __vlan_delete_pvid(v, vid);
clear_bit(vid, v->untagged_bitmap); clear_bit(vid, v->untagged_bitmap);
if (v->port_idx && vid) { if (v->port_idx) {
struct net_device *dev = v->parent.port->dev; struct net_device *dev = v->parent.port->dev;
const struct net_device_ops *ops = dev->netdev_ops; const struct net_device_ops *ops = dev->netdev_ops;
...@@ -192,6 +189,8 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br, ...@@ -192,6 +189,8 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
struct sk_buff *skb, u16 *vid) struct sk_buff *skb, u16 *vid)
{ {
int err;
/* If VLAN filtering is disabled on the bridge, all packets are /* If VLAN filtering is disabled on the bridge, all packets are
* permitted. * permitted.
*/ */
...@@ -204,20 +203,32 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, ...@@ -204,20 +203,32 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
if (!v) if (!v)
return false; return false;
if (br_vlan_get_tag(skb, vid)) { err = br_vlan_get_tag(skb, vid);
if (!*vid) {
u16 pvid = br_get_pvid(v); u16 pvid = br_get_pvid(v);
/* Frame did not have a tag. See if pvid is set /* Frame had a tag with VID 0 or did not have a tag.
* on this port. That tells us which vlan untagged * See if pvid is set on this port. That tells us which
* traffic belongs to. * vlan untagged or priority-tagged traffic belongs to.
*/ */
if (pvid == VLAN_N_VID) if (pvid == VLAN_N_VID)
return false; return false;
/* PVID is set on this port. Any untagged ingress /* PVID is set on this port. Any untagged or priority-tagged
* frame is considered to belong to this vlan. * ingress frame is considered to belong to this vlan.
*/ */
*vid = pvid;
if (likely(err))
/* Untagged Frame. */
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), pvid); __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), pvid);
else
/* Priority-tagged Frame.
* At this point, We know that skb->vlan_tci had
* VLAN_TAG_PRESENT bit and its VID field was 0x000.
* We update only VID field and preserve PCP field.
*/
skb->vlan_tci |= pvid;
return true; return true;
} }
...@@ -248,7 +259,9 @@ bool br_allowed_egress(struct net_bridge *br, ...@@ -248,7 +259,9 @@ bool br_allowed_egress(struct net_bridge *br,
return false; return false;
} }
/* Must be protected by RTNL */ /* Must be protected by RTNL.
* Must be called with vid in range from 1 to 4094 inclusive.
*/
int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags) int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags)
{ {
struct net_port_vlans *pv = NULL; struct net_port_vlans *pv = NULL;
...@@ -278,7 +291,9 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags) ...@@ -278,7 +291,9 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags)
return err; return err;
} }
/* Must be protected by RTNL */ /* Must be protected by RTNL.
* Must be called with vid in range from 1 to 4094 inclusive.
*/
int br_vlan_delete(struct net_bridge *br, u16 vid) int br_vlan_delete(struct net_bridge *br, u16 vid)
{ {
struct net_port_vlans *pv; struct net_port_vlans *pv;
...@@ -289,14 +304,9 @@ int br_vlan_delete(struct net_bridge *br, u16 vid) ...@@ -289,14 +304,9 @@ int br_vlan_delete(struct net_bridge *br, u16 vid)
if (!pv) if (!pv)
return -EINVAL; return -EINVAL;
if (vid) {
/* If the VID !=0 remove fdb for this vid. VID 0 is special
* in that it's the default and is always there in the fdb.
*/
spin_lock_bh(&br->hash_lock); spin_lock_bh(&br->hash_lock);
fdb_delete_by_addr(br, br->dev->dev_addr, vid); fdb_delete_by_addr(br, br->dev->dev_addr, vid);
spin_unlock_bh(&br->hash_lock); spin_unlock_bh(&br->hash_lock);
}
__vlan_del(pv, vid); __vlan_del(pv, vid);
return 0; return 0;
...@@ -329,7 +339,9 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val) ...@@ -329,7 +339,9 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
return 0; return 0;
} }
/* Must be protected by RTNL */ /* Must be protected by RTNL.
* Must be called with vid in range from 1 to 4094 inclusive.
*/
int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
{ {
struct net_port_vlans *pv = NULL; struct net_port_vlans *pv = NULL;
...@@ -363,7 +375,9 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) ...@@ -363,7 +375,9 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
return err; return err;
} }
/* Must be protected by RTNL */ /* Must be protected by RTNL.
* Must be called with vid in range from 1 to 4094 inclusive.
*/
int nbp_vlan_delete(struct net_bridge_port *port, u16 vid) int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
{ {
struct net_port_vlans *pv; struct net_port_vlans *pv;
...@@ -374,14 +388,9 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid) ...@@ -374,14 +388,9 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
if (!pv) if (!pv)
return -EINVAL; return -EINVAL;
if (vid) {
/* If the VID !=0 remove fdb for this vid. VID 0 is special
* in that it's the default and is always there in the fdb.
*/
spin_lock_bh(&port->br->hash_lock); spin_lock_bh(&port->br->hash_lock);
fdb_delete_by_addr(port->br, port->dev->dev_addr, vid); fdb_delete_by_addr(port->br, port->dev->dev_addr, vid);
spin_unlock_bh(&port->br->hash_lock); spin_unlock_bh(&port->br->hash_lock);
}
return __vlan_del(pv, vid); return __vlan_del(pv, vid);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment