bridge: Add a default_pvid sysfs attribute
[firefly-linux-kernel-4.4.55.git] / net / bridge / br_vlan.c
index e1bcd653899b4ed0a7ca8714817f9a9350eb414d..dfa7c9a7e1935114fcd90e446c5710b16f5c4b83 100644 (file)
@@ -27,9 +27,13 @@ static void __vlan_add_flags(struct net_port_vlans *v, u16 vid, u16 flags)
 {
        if (flags & BRIDGE_VLAN_INFO_PVID)
                __vlan_add_pvid(v, vid);
+       else
+               __vlan_delete_pvid(v, vid);
 
        if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
                set_bit(vid, v->untagged_bitmap);
+       else
+               clear_bit(vid, v->untagged_bitmap);
 }
 
 static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
@@ -125,7 +129,8 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
 {
        u16 vid;
 
-       if (!br->vlan_enabled)
+       /* If this packet was not filtered at input, let it pass */
+       if (!BR_INPUT_SKB_CB(skb)->vlan_filtered)
                goto out;
 
        /* Vlan filter table must be configured at this point.  The
@@ -164,8 +169,10 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
        /* If VLAN filtering is disabled on the bridge, all packets are
         * permitted.
         */
-       if (!br->vlan_enabled)
+       if (!br->vlan_enabled) {
+               BR_INPUT_SKB_CB(skb)->vlan_filtered = false;
                return true;
+       }
 
        /* If there are no vlan in the permitted list, all packets are
         * rejected.
@@ -173,6 +180,7 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
        if (!v)
                goto drop;
 
+       BR_INPUT_SKB_CB(skb)->vlan_filtered = true;
        proto = br->vlan_proto;
 
        /* If vlan tx offload is disabled on bridge device and frame was
@@ -251,7 +259,8 @@ bool br_allowed_egress(struct net_bridge *br,
 {
        u16 vid;
 
-       if (!br->vlan_enabled)
+       /* If this packet was not filtered at input, let it pass */
+       if (!BR_INPUT_SKB_CB(skb)->vlan_filtered)
                return true;
 
        if (!v)
@@ -270,6 +279,7 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
        struct net_bridge *br = p->br;
        struct net_port_vlans *v;
 
+       /* If filtering was disabled at input, let it pass. */
        if (!br->vlan_enabled)
                return true;
 
@@ -489,9 +499,38 @@ err_filt:
        goto unlock;
 }
 
+int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val)
+{
+       u16 pvid = val;
+       int err = 0;
+
+       if (!val || val >= VLAN_VID_MASK)
+               return -EINVAL;
+
+       if (!rtnl_trylock())
+               return restart_syscall();
+
+       if (pvid == br->default_pvid)
+               goto unlock;
+
+       /* Only allow default pvid change when filtering is disabled */
+       if (br->vlan_enabled) {
+               pr_info_once("Please disable vlan filtering to change default_pvid\n");
+               err = -EPERM;
+               goto unlock;
+       }
+
+       br->default_pvid = pvid;
+
+unlock:
+       rtnl_unlock();
+       return err;
+}
+
 void br_vlan_init(struct net_bridge *br)
 {
        br->vlan_proto = htons(ETH_P_8021Q);
+       br->default_pvid = 1;
 }
 
 /* Must be protected by RTNL.