|
@@ -60,7 +60,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
|
|
|
* that ever changes this code will allow tagged
|
|
|
* traffic to enter the bridge.
|
|
|
*/
|
|
|
- err = vlan_vid_add(dev, htons(ETH_P_8021Q), vid);
|
|
|
+ err = vlan_vid_add(dev, br->vlan_proto, vid);
|
|
|
if (err)
|
|
|
return err;
|
|
|
}
|
|
@@ -80,7 +80,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
|
|
|
|
|
|
out_filt:
|
|
|
if (p)
|
|
|
- vlan_vid_del(dev, htons(ETH_P_8021Q), vid);
|
|
|
+ vlan_vid_del(dev, br->vlan_proto, vid);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
@@ -92,8 +92,10 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid)
|
|
|
__vlan_delete_pvid(v, vid);
|
|
|
clear_bit(vid, v->untagged_bitmap);
|
|
|
|
|
|
- if (v->port_idx)
|
|
|
- vlan_vid_del(v->parent.port->dev, htons(ETH_P_8021Q), vid);
|
|
|
+ if (v->port_idx) {
|
|
|
+ struct net_bridge_port *p = v->parent.port;
|
|
|
+ vlan_vid_del(p->dev, p->br->vlan_proto, vid);
|
|
|
+ }
|
|
|
|
|
|
clear_bit(vid, v->vlan_bitmap);
|
|
|
v->num_vlans--;
|
|
@@ -158,7 +160,8 @@ out:
|
|
|
bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
|
|
|
struct sk_buff *skb, u16 *vid)
|
|
|
{
|
|
|
- int err;
|
|
|
+ bool tagged;
|
|
|
+ __be16 proto;
|
|
|
|
|
|
/* If VLAN filtering is disabled on the bridge, all packets are
|
|
|
* permitted.
|
|
@@ -172,19 +175,41 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
|
|
|
if (!v)
|
|
|
goto drop;
|
|
|
|
|
|
+ proto = br->vlan_proto;
|
|
|
+
|
|
|
/* If vlan tx offload is disabled on bridge device and frame was
|
|
|
* sent from vlan device on the bridge device, it does not have
|
|
|
* HW accelerated vlan tag.
|
|
|
*/
|
|
|
if (unlikely(!vlan_tx_tag_present(skb) &&
|
|
|
- (skb->protocol == htons(ETH_P_8021Q) ||
|
|
|
- skb->protocol == htons(ETH_P_8021AD)))) {
|
|
|
+ skb->protocol == proto)) {
|
|
|
skb = vlan_untag(skb);
|
|
|
if (unlikely(!skb))
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- err = br_vlan_get_tag(skb, vid);
|
|
|
+ if (!br_vlan_get_tag(skb, vid)) {
|
|
|
+ /* Tagged frame */
|
|
|
+ if (skb->vlan_proto != proto) {
|
|
|
+ /* Protocol-mismatch, empty out vlan_tci for new tag */
|
|
|
+ skb_push(skb, ETH_HLEN);
|
|
|
+ skb = __vlan_put_tag(skb, skb->vlan_proto,
|
|
|
+ vlan_tx_tag_get(skb));
|
|
|
+ if (unlikely(!skb))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ skb_pull(skb, ETH_HLEN);
|
|
|
+ skb_reset_mac_len(skb);
|
|
|
+ *vid = 0;
|
|
|
+ tagged = false;
|
|
|
+ } else {
|
|
|
+ tagged = true;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* Untagged frame */
|
|
|
+ tagged = false;
|
|
|
+ }
|
|
|
+
|
|
|
if (!*vid) {
|
|
|
u16 pvid = br_get_pvid(v);
|
|
|
|
|
@@ -199,9 +224,9 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
|
|
|
* ingress frame is considered to belong to this vlan.
|
|
|
*/
|
|
|
*vid = pvid;
|
|
|
- if (likely(err))
|
|
|
+ if (likely(!tagged))
|
|
|
/* Untagged Frame. */
|
|
|
- __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), pvid);
|
|
|
+ __vlan_hwaccel_put_tag(skb, proto, pvid);
|
|
|
else
|
|
|
/* Priority-tagged Frame.
|
|
|
* At this point, We know that skb->vlan_tci had
|
|
@@ -254,7 +279,9 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
|
|
|
if (!v)
|
|
|
return false;
|
|
|
|
|
|
- br_vlan_get_tag(skb, vid);
|
|
|
+ if (!br_vlan_get_tag(skb, vid) && skb->vlan_proto != br->vlan_proto)
|
|
|
+ *vid = 0;
|
|
|
+
|
|
|
if (!*vid) {
|
|
|
*vid = br_get_pvid(v);
|
|
|
if (*vid == VLAN_N_VID)
|
|
@@ -351,6 +378,33 @@ out:
|
|
|
return found;
|
|
|
}
|
|
|
|
|
|
+/* Must be protected by RTNL. */
|
|
|
+static void recalculate_group_addr(struct net_bridge *br)
|
|
|
+{
|
|
|
+ if (br->group_addr_set)
|
|
|
+ return;
|
|
|
+
|
|
|
+ spin_lock_bh(&br->lock);
|
|
|
+ if (!br->vlan_enabled || br->vlan_proto == htons(ETH_P_8021Q)) {
|
|
|
+ /* Bridge Group Address */
|
|
|
+ br->group_addr[5] = 0x00;
|
|
|
+ } else { /* vlan_enabled && ETH_P_8021AD */
|
|
|
+ /* Provider Bridge Group Address */
|
|
|
+ br->group_addr[5] = 0x08;
|
|
|
+ }
|
|
|
+ spin_unlock_bh(&br->lock);
|
|
|
+}
|
|
|
+
|
|
|
+/* Must be protected by RTNL. */
|
|
|
+void br_recalculate_fwd_mask(struct net_bridge *br)
|
|
|
+{
|
|
|
+ if (!br->vlan_enabled || br->vlan_proto == htons(ETH_P_8021Q))
|
|
|
+ br->group_fwd_mask_required = BR_GROUPFWD_DEFAULT;
|
|
|
+ else /* vlan_enabled && ETH_P_8021AD */
|
|
|
+ br->group_fwd_mask_required = BR_GROUPFWD_8021AD &
|
|
|
+ ~(1u << br->group_addr[5]);
|
|
|
+}
|
|
|
+
|
|
|
int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
|
|
|
{
|
|
|
if (!rtnl_trylock())
|
|
@@ -361,12 +415,87 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
|
|
|
|
|
|
br->vlan_enabled = val;
|
|
|
br_manage_promisc(br);
|
|
|
+ recalculate_group_addr(br);
|
|
|
+ br_recalculate_fwd_mask(br);
|
|
|
|
|
|
unlock:
|
|
|
rtnl_unlock();
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+int br_vlan_set_proto(struct net_bridge *br, unsigned long val)
|
|
|
+{
|
|
|
+ int err = 0;
|
|
|
+ struct net_bridge_port *p;
|
|
|
+ struct net_port_vlans *pv;
|
|
|
+ __be16 proto, oldproto;
|
|
|
+ u16 vid, errvid;
|
|
|
+
|
|
|
+ if (val != ETH_P_8021Q && val != ETH_P_8021AD)
|
|
|
+ return -EPROTONOSUPPORT;
|
|
|
+
|
|
|
+ if (!rtnl_trylock())
|
|
|
+ return restart_syscall();
|
|
|
+
|
|
|
+ proto = htons(val);
|
|
|
+ if (br->vlan_proto == proto)
|
|
|
+ goto unlock;
|
|
|
+
|
|
|
+ /* Add VLANs for the new proto to the device filter. */
|
|
|
+ list_for_each_entry(p, &br->port_list, list) {
|
|
|
+ pv = rtnl_dereference(p->vlan_info);
|
|
|
+ if (!pv)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
|
|
|
+ err = vlan_vid_add(p->dev, proto, vid);
|
|
|
+ if (err)
|
|
|
+ goto err_filt;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ oldproto = br->vlan_proto;
|
|
|
+ br->vlan_proto = proto;
|
|
|
+
|
|
|
+ recalculate_group_addr(br);
|
|
|
+ br_recalculate_fwd_mask(br);
|
|
|
+
|
|
|
+ /* Delete VLANs for the old proto from the device filter. */
|
|
|
+ list_for_each_entry(p, &br->port_list, list) {
|
|
|
+ pv = rtnl_dereference(p->vlan_info);
|
|
|
+ if (!pv)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
|
|
|
+ vlan_vid_del(p->dev, oldproto, vid);
|
|
|
+ }
|
|
|
+
|
|
|
+unlock:
|
|
|
+ rtnl_unlock();
|
|
|
+ return err;
|
|
|
+
|
|
|
+err_filt:
|
|
|
+ errvid = vid;
|
|
|
+ for_each_set_bit(vid, pv->vlan_bitmap, errvid)
|
|
|
+ vlan_vid_del(p->dev, proto, vid);
|
|
|
+
|
|
|
+ list_for_each_entry_continue_reverse(p, &br->port_list, list) {
|
|
|
+ pv = rtnl_dereference(p->vlan_info);
|
|
|
+ if (!pv)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
|
|
|
+ vlan_vid_del(p->dev, proto, vid);
|
|
|
+ }
|
|
|
+
|
|
|
+ goto unlock;
|
|
|
+}
|
|
|
+
|
|
|
+void br_vlan_init(struct net_bridge *br)
|
|
|
+{
|
|
|
+ br->vlan_proto = htons(ETH_P_8021Q);
|
|
|
+}
|
|
|
+
|
|
|
/* Must be protected by RTNL.
|
|
|
* Must be called with vid in range from 1 to 4094 inclusive.
|
|
|
*/
|
|
@@ -433,7 +562,7 @@ void nbp_vlan_flush(struct net_bridge_port *port)
|
|
|
return;
|
|
|
|
|
|
for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
|
|
|
- vlan_vid_del(port->dev, htons(ETH_P_8021Q), vid);
|
|
|
+ vlan_vid_del(port->dev, port->br->vlan_proto, vid);
|
|
|
|
|
|
__vlan_flush(pv);
|
|
|
}
|