Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a37b85c9 authored by Vlad Yasevich's avatar Vlad Yasevich Committed by David S. Miller
Browse files

bridge: Validate that vlan is permitted on ingress



When a frame arrives on a port or transmitted by the bridge,
if we have VLANs configured, validate that a given VLAN is allowed
to enter the bridge.

Signed-off-by: default avatarVlad Yasevich <vyasevic@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 243a2e63
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -45,6 +45,9 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
	brstats->tx_bytes += skb->len;
	u64_stats_update_end(&brstats->syncp);

	if (!br_allowed_ingress(br, br_get_vlan_info(br), skb))
		goto out;

	BR_INPUT_SKB_CB(skb)->brdev = dev;

	skb_reset_mac_header(skb);
+4 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/etherdevice.h>
#include <linux/netfilter_bridge.h>
#include <linux/export.h>
#include <linux/rculist.h>
#include "br_private.h"

/* Hook for brouter */
@@ -54,6 +55,9 @@ int br_handle_frame_finish(struct sk_buff *skb)
	if (!p || p->state == BR_STATE_DISABLED)
		goto drop;

	if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb))
		goto drop;

	/* insert into forwarding database after filtering to avoid spoofing */
	br = p->br;
	br_fdb_update(br, p, eth_hdr(skb)->h_source);
+53 −0
Original line number Diff line number Diff line
@@ -552,6 +552,8 @@ static inline void br_mdb_uninit(void)

/* br_vlan.c */
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
			       struct sk_buff *skb);
extern int br_vlan_add(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);
@@ -559,7 +561,43 @@ extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid);
extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
extern void nbp_vlan_flush(struct net_bridge_port *port);

static inline struct net_port_vlans *br_get_vlan_info(
						const struct net_bridge *br)
{
	return rcu_dereference(br->vlan_info);
}

static inline struct net_port_vlans *nbp_get_vlan_info(
						const struct net_bridge_port *p)
{
	return rcu_dereference(p->vlan_info);
}

/* Since bridge now depends on 8021Q module, but the time bridge sees the
 * skb, the vlan tag will always be present if the frame was tagged.
 */
static inline int br_vlan_get_tag(const struct sk_buff *skb, u16 *vid)
{
	int err = 0;

	if (vlan_tx_tag_present(skb))
		*vid = vlan_tx_tag_get(skb) & VLAN_VID_MASK;
	else {
		*vid = 0;
		err = -EINVAL;
	}

	return err;
}
#else
static inline bool br_allowed_ingress(struct net_bridge *br,
				      struct net_port_vlans *v,
				      struct sk_buff *skb)
{
	return true;
}

static inline int br_vlan_add(struct net_bridge *br, u16 vid)
{
	return -EOPNOTSUPP;
@@ -588,6 +626,21 @@ static inline void nbp_vlan_flush(struct net_bridge_port *port)
{
}

static inline struct net_port_vlans *br_get_vlan_info(
						const struct net_bridge *br)
{
	return NULL;
}
static inline struct net_port_vlans *nbp_get_vlan_info(
						const struct net_bridge_port *p)
{
	return NULL;
}

static inline u16 br_vlan_get_tag(const struct sk_buff *skb)
{
	return 0;
}
#endif

/* br_netfilter.c */
+25 −0
Original line number Diff line number Diff line
@@ -64,6 +64,31 @@ static void __vlan_flush(struct net_port_vlans *v)
	kfree_rcu(v, rcu);
}

/* 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 (!br->vlan_enabled)
		return true;

	/* If there are no vlan in the permitted list, all packets are
	 * rejected.
	 */
	if (!v)
		return false;

	br_vlan_get_tag(skb, &vid);
	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)
{