#include "br_private.h"
-static int __vlan_add(struct net_port_vlans *v, u16 vid)
+static void __vlan_add_pvid(struct net_port_vlans *v, u16 vid)
+{
+ if (v->pvid == vid)
+ return;
+
+ smp_wmb();
+ v->pvid = vid;
+}
+
+static void __vlan_delete_pvid(struct net_port_vlans *v, u16 vid)
+{
+ if (v->pvid != vid)
+ return;
+
+ smp_wmb();
+ v->pvid = 0;
+}
+
+static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
{
int err;
- if (test_bit(vid, v->vlan_bitmap))
- return -EEXIST;
+ if (test_bit(vid, v->vlan_bitmap)) {
+ if (flags & BRIDGE_VLAN_INFO_PVID)
+ __vlan_add_pvid(v, vid);
+ return 0;
+ }
if (v->port_idx && vid) {
struct net_device *dev = v->parent.port->dev;
}
set_bit(vid, v->vlan_bitmap);
+ v->num_vlans++;
+ if (flags & BRIDGE_VLAN_INFO_PVID)
+ __vlan_add_pvid(v, vid);
+
return 0;
}
if (!test_bit(vid, v->vlan_bitmap))
return -EINVAL;
+ __vlan_delete_pvid(v, vid);
+
if (v->port_idx && vid) {
struct net_device *dev = v->parent.port->dev;
}
clear_bit(vid, v->vlan_bitmap);
+ v->num_vlans--;
if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN)) {
if (v->port_idx)
rcu_assign_pointer(v->parent.port->vlan_info, NULL);
static void __vlan_flush(struct net_port_vlans *v)
{
+ smp_wmb();
+ v->pvid = 0;
bitmap_zero(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
if (v->port_idx)
rcu_assign_pointer(v->parent.port->vlan_info, NULL);
kfree_rcu(v, rcu);
}
-/* Called under RCU */
-bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
- struct sk_buff *skb)
+/* Strip the tag from the packet. Will return skb with tci set 0. */
+static struct sk_buff *br_vlan_untag(struct sk_buff *skb)
+{
+ if (skb->protocol != htons(ETH_P_8021Q)) {
+ skb->vlan_tci = 0;
+ return skb;
+ }
+
+ skb->vlan_tci = 0;
+ skb = vlan_untag(skb);
+ if (skb)
+ skb->vlan_tci = 0;
+
+ return skb;
+}
+
+struct sk_buff *br_handle_vlan(struct net_bridge *br,
+ const struct net_port_vlans *pv,
+ struct sk_buff *skb)
{
u16 vid;
+ if (!br->vlan_enabled)
+ goto out;
+
+ /* At this point, we know that the frame was filtered and contains
+ * a valid vlan id. If the vlan id matches the pvid of current port
+ * send untagged; otherwise, send taged.
+ */
+ br_vlan_get_tag(skb, &vid);
+ if (vid == br_get_pvid(pv))
+ skb = br_vlan_untag(skb);
+ else {
+ /* Egress policy says "send tagged". If output device
+ * is the bridge, we need to add the VLAN header
+ * ourselves since we'll be going through the RX path.
+ * Sending to ports puts the frame on the TX path and
+ * we let dev_hard_start_xmit() add the header.
+ */
+ if (skb->protocol != htons(ETH_P_8021Q) &&
+ pv->port_idx == 0) {
+ /* vlan_put_tag expects skb->data to point to
+ * mac header.
+ */
+ skb_push(skb, ETH_HLEN);
+ skb = __vlan_put_tag(skb, skb->vlan_tci);
+ if (!skb)
+ goto out;
+ /* put skb->data back to where it was */
+ skb_pull(skb, ETH_HLEN);
+ skb->vlan_tci = 0;
+ }
+ }
+
+out:
+ return skb;
+}
+
+/* Called under RCU */
+bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
+ struct sk_buff *skb, u16 *vid)
+{
/* If VLAN filtering is disabled on the bridge, all packets are
* permitted.
*/
if (!v)
return false;
- br_vlan_get_tag(skb, &vid);
- if (test_bit(vid, v->vlan_bitmap))
+ if (br_vlan_get_tag(skb, vid)) {
+ u16 pvid = br_get_pvid(v);
+
+ /* Frame did not have a tag. See if pvid is set
+ * on this port. That tells us which vlan untagged
+ * traffic belongs to.
+ */
+ if (pvid == VLAN_N_VID)
+ return false;
+
+ /* PVID is set on this port. Any untagged ingress
+ * frame is considered to belong to this vlan.
+ */
+ __vlan_hwaccel_put_tag(skb, pvid);
+ return true;
+ }
+
+ /* Frame had a valid vlan tag. See if vlan is allowed */
+ if (test_bit(*vid, v->vlan_bitmap))
return true;
return false;
}
/* Must be protected by RTNL */
-int br_vlan_add(struct net_bridge *br, u16 vid)
+int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags)
{
struct net_port_vlans *pv = NULL;
int err;
pv = rtnl_dereference(br->vlan_info);
if (pv)
- return __vlan_add(pv, vid);
+ return __vlan_add(pv, vid, flags);
/* Create port vlan infomration
*/
return -ENOMEM;
pv->parent.br = br;
- err = __vlan_add(pv, vid);
+ err = __vlan_add(pv, vid, flags);
if (err)
goto out;
struct net_port_vlans *pv;
ASSERT_RTNL();
-
pv = rtnl_dereference(br->vlan_info);
if (!pv)
return;
}
/* Must be protected by RTNL */
-int nbp_vlan_add(struct net_bridge_port *port, u16 vid)
+int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
{
struct net_port_vlans *pv = NULL;
int err;
pv = rtnl_dereference(port->vlan_info);
if (pv)
- return __vlan_add(pv, vid);
+ return __vlan_add(pv, vid, flags);
/* Create port vlan infomration
*/
pv->port_idx = port->port_no;
pv->parent.port = port;
- err = __vlan_add(pv, vid);
+ err = __vlan_add(pv, vid, flags);
if (err)
goto clean_up;