Commit 78851988 authored by Vlad Yasevich's avatar Vlad Yasevich Committed by David S. Miller

bridge: Implement vlan ingress/egress policy with PVID.

At ingress, any untagged traffic is assigned to the PVID.
Any tagged traffic is filtered according to membership bitmap.

At egress, if the vlan matches the PVID, the frame is sent
untagged.  Otherwise the frame is sent tagged.
Signed-off-by: default avatarVlad Yasevich <vyasevic@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6cbdceeb
...@@ -144,6 +144,7 @@ struct sk_buff *vlan_untag(struct sk_buff *skb) ...@@ -144,6 +144,7 @@ struct sk_buff *vlan_untag(struct sk_buff *skb)
kfree_skb(skb); kfree_skb(skb);
return NULL; return NULL;
} }
EXPORT_SYMBOL(vlan_untag);
/* /*
......
...@@ -30,6 +30,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -30,6 +30,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
struct net_bridge_fdb_entry *dst; struct net_bridge_fdb_entry *dst;
struct net_bridge_mdb_entry *mdst; struct net_bridge_mdb_entry *mdst;
struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats); struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats);
u16 vid = 0;
rcu_read_lock(); rcu_read_lock();
#ifdef CONFIG_BRIDGE_NETFILTER #ifdef CONFIG_BRIDGE_NETFILTER
...@@ -45,7 +46,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -45,7 +46,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
brstats->tx_bytes += skb->len; brstats->tx_bytes += skb->len;
u64_stats_update_end(&brstats->syncp); u64_stats_update_end(&brstats->syncp);
if (!br_allowed_ingress(br, br_get_vlan_info(br), skb)) if (!br_allowed_ingress(br, br_get_vlan_info(br), skb, &vid))
goto out; goto out;
BR_INPUT_SKB_CB(skb)->brdev = dev; BR_INPUT_SKB_CB(skb)->brdev = dev;
......
...@@ -64,6 +64,10 @@ int br_forward_finish(struct sk_buff *skb) ...@@ -64,6 +64,10 @@ int br_forward_finish(struct sk_buff *skb)
static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
{ {
skb = br_handle_vlan(to->br, nbp_get_vlan_info(to), skb);
if (!skb)
return;
skb->dev = to->dev; skb->dev = to->dev;
if (unlikely(netpoll_tx_running(to->br->dev))) { if (unlikely(netpoll_tx_running(to->br->dev))) {
...@@ -89,6 +93,10 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb) ...@@ -89,6 +93,10 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
return; return;
} }
skb = br_handle_vlan(to->br, nbp_get_vlan_info(to), skb);
if (!skb)
return;
indev = skb->dev; indev = skb->dev;
skb->dev = to->dev; skb->dev = to->dev;
skb_forward_csum(skb); skb_forward_csum(skb);
......
...@@ -45,6 +45,10 @@ static int br_pass_frame_up(struct sk_buff *skb) ...@@ -45,6 +45,10 @@ static int br_pass_frame_up(struct sk_buff *skb)
return NET_RX_DROP; return NET_RX_DROP;
} }
skb = br_handle_vlan(br, br_get_vlan_info(br), skb);
if (!skb)
return NET_RX_DROP;
indev = skb->dev; indev = skb->dev;
skb->dev = brdev; skb->dev = brdev;
...@@ -61,11 +65,12 @@ int br_handle_frame_finish(struct sk_buff *skb) ...@@ -61,11 +65,12 @@ int br_handle_frame_finish(struct sk_buff *skb)
struct net_bridge_fdb_entry *dst; struct net_bridge_fdb_entry *dst;
struct net_bridge_mdb_entry *mdst; struct net_bridge_mdb_entry *mdst;
struct sk_buff *skb2; struct sk_buff *skb2;
u16 vid = 0;
if (!p || p->state == BR_STATE_DISABLED) if (!p || p->state == BR_STATE_DISABLED)
goto drop; goto drop;
if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb)) if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb, &vid))
goto drop; goto drop;
/* insert into forwarding database after filtering to avoid spoofing */ /* insert into forwarding database after filtering to avoid spoofing */
......
...@@ -67,6 +67,7 @@ struct br_ip ...@@ -67,6 +67,7 @@ struct br_ip
struct net_port_vlans { struct net_port_vlans {
u16 port_idx; u16 port_idx;
u16 pvid;
union { union {
struct net_bridge_port *port; struct net_bridge_port *port;
struct net_bridge *br; struct net_bridge *br;
...@@ -554,10 +555,13 @@ static inline void br_mdb_uninit(void) ...@@ -554,10 +555,13 @@ static inline void br_mdb_uninit(void)
/* br_vlan.c */ /* br_vlan.c */
#ifdef CONFIG_BRIDGE_VLAN_FILTERING #ifdef CONFIG_BRIDGE_VLAN_FILTERING
extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
struct sk_buff *skb); struct sk_buff *skb, u16 *vid);
extern bool br_allowed_egress(struct net_bridge *br, extern bool br_allowed_egress(struct net_bridge *br,
const struct net_port_vlans *v, const struct net_port_vlans *v,
const struct sk_buff *skb); const struct sk_buff *skb);
extern struct sk_buff *br_handle_vlan(struct net_bridge *br,
const struct net_port_vlans *v,
struct sk_buff *skb);
extern int br_vlan_add(struct net_bridge *br, u16 vid); extern int br_vlan_add(struct net_bridge *br, u16 vid);
extern int br_vlan_delete(struct net_bridge *br, u16 vid); extern int br_vlan_delete(struct net_bridge *br, u16 vid);
extern void br_vlan_flush(struct net_bridge *br); extern void br_vlan_flush(struct net_bridge *br);
...@@ -594,10 +598,23 @@ static inline int br_vlan_get_tag(const struct sk_buff *skb, u16 *vid) ...@@ -594,10 +598,23 @@ static inline int br_vlan_get_tag(const struct sk_buff *skb, u16 *vid)
return err; return err;
} }
static inline u16 br_get_pvid(const struct net_port_vlans *v)
{
/* Return just the VID if it is set, or VLAN_N_VID (invalid vid) if
* vid wasn't set
*/
smp_rmb();
return (v->pvid & VLAN_TAG_PRESENT) ?
(v->pvid & ~VLAN_TAG_PRESENT) :
VLAN_N_VID;
}
#else #else
static inline bool br_allowed_ingress(struct net_bridge *br, static inline bool br_allowed_ingress(struct net_bridge *br,
struct net_port_vlans *v, struct net_port_vlans *v,
struct sk_buff *skb) struct sk_buff *skb,
u16 *vid)
{ {
return true; return true;
} }
...@@ -609,6 +626,13 @@ static inline bool br_allowed_egress(struct net_bridge *br, ...@@ -609,6 +626,13 @@ static inline bool br_allowed_egress(struct net_bridge *br,
return true; return true;
} }
static inline struct sk_buff *br_handle_vlan(struct net_bridge *br,
const struct net_port_vlans *v,
struct sk_buff *skb)
{
return skb;
}
static inline int br_vlan_add(struct net_bridge *br, u16 vid) static inline int br_vlan_add(struct net_bridge *br, u16 vid)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -648,10 +672,14 @@ static inline struct net_port_vlans *nbp_get_vlan_info( ...@@ -648,10 +672,14 @@ static inline struct net_port_vlans *nbp_get_vlan_info(
return NULL; return NULL;
} }
static inline u16 br_vlan_get_tag(const struct sk_buff *skb) static inline u16 br_vlan_get_tag(const struct sk_buff *skb, u16 *tag)
{ {
return 0; return 0;
} }
static inline u16 br_get_pvid(const struct net_port_vlans *v)
{
return VLAN_N_VID; /* Returns invalid vid */
}
#endif #endif
/* br_netfilter.c */ /* br_netfilter.c */
......
...@@ -66,12 +66,68 @@ static void __vlan_flush(struct net_port_vlans *v) ...@@ -66,12 +66,68 @@ static void __vlan_flush(struct net_port_vlans *v)
kfree_rcu(v, rcu); kfree_rcu(v, rcu);
} }
/* Called under RCU */ /* Strip the tag from the packet. Will return skb with tci set 0. */
bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, static struct sk_buff *br_vlan_untag(struct sk_buff *skb)
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; 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 /* If VLAN filtering is disabled on the bridge, all packets are
* permitted. * permitted.
*/ */
...@@ -84,8 +140,25 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, ...@@ -84,8 +140,25 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
if (!v) if (!v)
return false; return false;
br_vlan_get_tag(skb, &vid); if (br_vlan_get_tag(skb, vid)) {
if (test_bit(vid, v->vlan_bitmap)) 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 true;
return false; return false;
......
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