diff options
-rw-r--r-- | net/bridge/br_device.c | 3 | ||||
-rw-r--r-- | net/bridge/br_input.c | 4 | ||||
-rw-r--r-- | net/bridge/br_private.h | 53 | ||||
-rw-r--r-- | net/bridge/br_vlan.c | 25 |
4 files changed, 85 insertions, 0 deletions
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index ca98fa5b2c78..35a2c2c84f33 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c | |||
@@ -45,6 +45,9 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) | |||
45 | brstats->tx_bytes += skb->len; | 45 | brstats->tx_bytes += skb->len; |
46 | u64_stats_update_end(&brstats->syncp); | 46 | u64_stats_update_end(&brstats->syncp); |
47 | 47 | ||
48 | if (!br_allowed_ingress(br, br_get_vlan_info(br), skb)) | ||
49 | goto out; | ||
50 | |||
48 | BR_INPUT_SKB_CB(skb)->brdev = dev; | 51 | BR_INPUT_SKB_CB(skb)->brdev = dev; |
49 | 52 | ||
50 | skb_reset_mac_header(skb); | 53 | skb_reset_mac_header(skb); |
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 4b34207419b1..4ef3f6b17bd0 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/etherdevice.h> | 17 | #include <linux/etherdevice.h> |
18 | #include <linux/netfilter_bridge.h> | 18 | #include <linux/netfilter_bridge.h> |
19 | #include <linux/export.h> | 19 | #include <linux/export.h> |
20 | #include <linux/rculist.h> | ||
20 | #include "br_private.h" | 21 | #include "br_private.h" |
21 | 22 | ||
22 | /* Hook for brouter */ | 23 | /* Hook for brouter */ |
@@ -54,6 +55,9 @@ int br_handle_frame_finish(struct sk_buff *skb) | |||
54 | if (!p || p->state == BR_STATE_DISABLED) | 55 | if (!p || p->state == BR_STATE_DISABLED) |
55 | goto drop; | 56 | goto drop; |
56 | 57 | ||
58 | if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb)) | ||
59 | goto drop; | ||
60 | |||
57 | /* insert into forwarding database after filtering to avoid spoofing */ | 61 | /* insert into forwarding database after filtering to avoid spoofing */ |
58 | br = p->br; | 62 | br = p->br; |
59 | br_fdb_update(br, p, eth_hdr(skb)->h_source); | 63 | br_fdb_update(br, p, eth_hdr(skb)->h_source); |
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 1f3b309beea8..ed7c764ee9da 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h | |||
@@ -552,6 +552,8 @@ static inline void br_mdb_uninit(void) | |||
552 | 552 | ||
553 | /* br_vlan.c */ | 553 | /* br_vlan.c */ |
554 | #ifdef CONFIG_BRIDGE_VLAN_FILTERING | 554 | #ifdef CONFIG_BRIDGE_VLAN_FILTERING |
555 | extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, | ||
556 | struct sk_buff *skb); | ||
555 | extern int br_vlan_add(struct net_bridge *br, u16 vid); | 557 | extern int br_vlan_add(struct net_bridge *br, u16 vid); |
556 | extern int br_vlan_delete(struct net_bridge *br, u16 vid); | 558 | extern int br_vlan_delete(struct net_bridge *br, u16 vid); |
557 | extern void br_vlan_flush(struct net_bridge *br); | 559 | 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); | |||
559 | extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid); | 561 | extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid); |
560 | extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); | 562 | extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); |
561 | extern void nbp_vlan_flush(struct net_bridge_port *port); | 563 | extern void nbp_vlan_flush(struct net_bridge_port *port); |
564 | |||
565 | static inline struct net_port_vlans *br_get_vlan_info( | ||
566 | const struct net_bridge *br) | ||
567 | { | ||
568 | return rcu_dereference(br->vlan_info); | ||
569 | } | ||
570 | |||
571 | static inline struct net_port_vlans *nbp_get_vlan_info( | ||
572 | const struct net_bridge_port *p) | ||
573 | { | ||
574 | return rcu_dereference(p->vlan_info); | ||
575 | } | ||
576 | |||
577 | /* Since bridge now depends on 8021Q module, but the time bridge sees the | ||
578 | * skb, the vlan tag will always be present if the frame was tagged. | ||
579 | */ | ||
580 | static inline int br_vlan_get_tag(const struct sk_buff *skb, u16 *vid) | ||
581 | { | ||
582 | int err = 0; | ||
583 | |||
584 | if (vlan_tx_tag_present(skb)) | ||
585 | *vid = vlan_tx_tag_get(skb) & VLAN_VID_MASK; | ||
586 | else { | ||
587 | *vid = 0; | ||
588 | err = -EINVAL; | ||
589 | } | ||
590 | |||
591 | return err; | ||
592 | } | ||
562 | #else | 593 | #else |
594 | static inline bool br_allowed_ingress(struct net_bridge *br, | ||
595 | struct net_port_vlans *v, | ||
596 | struct sk_buff *skb) | ||
597 | { | ||
598 | return true; | ||
599 | } | ||
600 | |||
563 | static inline int br_vlan_add(struct net_bridge *br, u16 vid) | 601 | static inline int br_vlan_add(struct net_bridge *br, u16 vid) |
564 | { | 602 | { |
565 | return -EOPNOTSUPP; | 603 | return -EOPNOTSUPP; |
@@ -588,6 +626,21 @@ static inline void nbp_vlan_flush(struct net_bridge_port *port) | |||
588 | { | 626 | { |
589 | } | 627 | } |
590 | 628 | ||
629 | static inline struct net_port_vlans *br_get_vlan_info( | ||
630 | const struct net_bridge *br) | ||
631 | { | ||
632 | return NULL; | ||
633 | } | ||
634 | static inline struct net_port_vlans *nbp_get_vlan_info( | ||
635 | const struct net_bridge_port *p) | ||
636 | { | ||
637 | return NULL; | ||
638 | } | ||
639 | |||
640 | static inline u16 br_vlan_get_tag(const struct sk_buff *skb) | ||
641 | { | ||
642 | return 0; | ||
643 | } | ||
591 | #endif | 644 | #endif |
592 | 645 | ||
593 | /* br_netfilter.c */ | 646 | /* br_netfilter.c */ |
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 209464ef5242..8b4bcd8ff46e 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c | |||
@@ -64,6 +64,31 @@ static void __vlan_flush(struct net_port_vlans *v) | |||
64 | kfree_rcu(v, rcu); | 64 | kfree_rcu(v, rcu); |
65 | } | 65 | } |
66 | 66 | ||
67 | /* Called under RCU */ | ||
68 | bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, | ||
69 | struct sk_buff *skb) | ||
70 | { | ||
71 | u16 vid; | ||
72 | |||
73 | /* If VLAN filtering is disabled on the bridge, all packets are | ||
74 | * permitted. | ||
75 | */ | ||
76 | if (!br->vlan_enabled) | ||
77 | return true; | ||
78 | |||
79 | /* If there are no vlan in the permitted list, all packets are | ||
80 | * rejected. | ||
81 | */ | ||
82 | if (!v) | ||
83 | return false; | ||
84 | |||
85 | br_vlan_get_tag(skb, &vid); | ||
86 | if (test_bit(vid, v->vlan_bitmap)) | ||
87 | return true; | ||
88 | |||
89 | return false; | ||
90 | } | ||
91 | |||
67 | /* Must be protected by RTNL */ | 92 | /* Must be protected by RTNL */ |
68 | int br_vlan_add(struct net_bridge *br, u16 vid) | 93 | int br_vlan_add(struct net_bridge *br, u16 vid) |
69 | { | 94 | { |