diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/8021q/vlan_core.c | 1 | ||||
-rw-r--r-- | net/bridge/br_device.c | 3 | ||||
-rw-r--r-- | net/bridge/br_forward.c | 8 | ||||
-rw-r--r-- | net/bridge/br_input.c | 7 | ||||
-rw-r--r-- | net/bridge/br_private.h | 34 | ||||
-rw-r--r-- | net/bridge/br_vlan.c | 83 |
6 files changed, 126 insertions, 10 deletions
diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 71b64fde8dc9..f3b6f515eba6 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c | |||
@@ -144,6 +144,7 @@ err_free: | |||
144 | kfree_skb(skb); | 144 | kfree_skb(skb); |
145 | return NULL; | 145 | return NULL; |
146 | } | 146 | } |
147 | EXPORT_SYMBOL(vlan_untag); | ||
147 | 148 | ||
148 | 149 | ||
149 | /* | 150 | /* |
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 091bedf266a0..9509139da49c 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c | |||
@@ -30,6 +30,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) | |||
30 | struct net_bridge_fdb_entry *dst; | 30 | struct net_bridge_fdb_entry *dst; |
31 | struct net_bridge_mdb_entry *mdst; | 31 | struct net_bridge_mdb_entry *mdst; |
32 | struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats); | 32 | struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats); |
33 | u16 vid = 0; | ||
33 | 34 | ||
34 | rcu_read_lock(); | 35 | rcu_read_lock(); |
35 | #ifdef CONFIG_BRIDGE_NETFILTER | 36 | #ifdef CONFIG_BRIDGE_NETFILTER |
@@ -45,7 +46,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) | |||
45 | brstats->tx_bytes += skb->len; | 46 | brstats->tx_bytes += skb->len; |
46 | u64_stats_update_end(&brstats->syncp); | 47 | u64_stats_update_end(&brstats->syncp); |
47 | 48 | ||
48 | if (!br_allowed_ingress(br, br_get_vlan_info(br), skb)) | 49 | if (!br_allowed_ingress(br, br_get_vlan_info(br), skb, &vid)) |
49 | goto out; | 50 | goto out; |
50 | 51 | ||
51 | BR_INPUT_SKB_CB(skb)->brdev = dev; | 52 | BR_INPUT_SKB_CB(skb)->brdev = dev; |
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 35b0671f135d..092b20e4ee4c 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c | |||
@@ -64,6 +64,10 @@ int br_forward_finish(struct sk_buff *skb) | |||
64 | 64 | ||
65 | static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) | 65 | static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) |
66 | { | 66 | { |
67 | skb = br_handle_vlan(to->br, nbp_get_vlan_info(to), skb); | ||
68 | if (!skb) | ||
69 | return; | ||
70 | |||
67 | skb->dev = to->dev; | 71 | skb->dev = to->dev; |
68 | 72 | ||
69 | if (unlikely(netpoll_tx_running(to->br->dev))) { | 73 | 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 | return; | 93 | return; |
90 | } | 94 | } |
91 | 95 | ||
96 | skb = br_handle_vlan(to->br, nbp_get_vlan_info(to), skb); | ||
97 | if (!skb) | ||
98 | return; | ||
99 | |||
92 | indev = skb->dev; | 100 | indev = skb->dev; |
93 | skb->dev = to->dev; | 101 | skb->dev = to->dev; |
94 | skb_forward_csum(skb); | 102 | skb_forward_csum(skb); |
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 787d7dad6b7e..a63f227ad963 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c | |||
@@ -45,6 +45,10 @@ static int br_pass_frame_up(struct sk_buff *skb) | |||
45 | return NET_RX_DROP; | 45 | return NET_RX_DROP; |
46 | } | 46 | } |
47 | 47 | ||
48 | skb = br_handle_vlan(br, br_get_vlan_info(br), skb); | ||
49 | if (!skb) | ||
50 | return NET_RX_DROP; | ||
51 | |||
48 | indev = skb->dev; | 52 | indev = skb->dev; |
49 | skb->dev = brdev; | 53 | skb->dev = brdev; |
50 | 54 | ||
@@ -61,11 +65,12 @@ int br_handle_frame_finish(struct sk_buff *skb) | |||
61 | struct net_bridge_fdb_entry *dst; | 65 | struct net_bridge_fdb_entry *dst; |
62 | struct net_bridge_mdb_entry *mdst; | 66 | struct net_bridge_mdb_entry *mdst; |
63 | struct sk_buff *skb2; | 67 | struct sk_buff *skb2; |
68 | u16 vid = 0; | ||
64 | 69 | ||
65 | if (!p || p->state == BR_STATE_DISABLED) | 70 | if (!p || p->state == BR_STATE_DISABLED) |
66 | goto drop; | 71 | goto drop; |
67 | 72 | ||
68 | if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb)) | 73 | if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb, &vid)) |
69 | goto drop; | 74 | goto drop; |
70 | 75 | ||
71 | /* insert into forwarding database after filtering to avoid spoofing */ | 76 | /* insert into forwarding database after filtering to avoid spoofing */ |
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index ce2235255c2f..ea8e7efd9137 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h | |||
@@ -67,6 +67,7 @@ struct br_ip | |||
67 | 67 | ||
68 | struct net_port_vlans { | 68 | struct net_port_vlans { |
69 | u16 port_idx; | 69 | u16 port_idx; |
70 | u16 pvid; | ||
70 | union { | 71 | union { |
71 | struct net_bridge_port *port; | 72 | struct net_bridge_port *port; |
72 | struct net_bridge *br; | 73 | struct net_bridge *br; |
@@ -554,10 +555,13 @@ static inline void br_mdb_uninit(void) | |||
554 | /* br_vlan.c */ | 555 | /* br_vlan.c */ |
555 | #ifdef CONFIG_BRIDGE_VLAN_FILTERING | 556 | #ifdef CONFIG_BRIDGE_VLAN_FILTERING |
556 | extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, | 557 | extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, |
557 | struct sk_buff *skb); | 558 | struct sk_buff *skb, u16 *vid); |
558 | extern bool br_allowed_egress(struct net_bridge *br, | 559 | extern bool br_allowed_egress(struct net_bridge *br, |
559 | const struct net_port_vlans *v, | 560 | const struct net_port_vlans *v, |
560 | const struct sk_buff *skb); | 561 | const struct sk_buff *skb); |
562 | extern struct sk_buff *br_handle_vlan(struct net_bridge *br, | ||
563 | const struct net_port_vlans *v, | ||
564 | struct sk_buff *skb); | ||
561 | extern int br_vlan_add(struct net_bridge *br, u16 vid); | 565 | extern int br_vlan_add(struct net_bridge *br, u16 vid); |
562 | extern int br_vlan_delete(struct net_bridge *br, u16 vid); | 566 | extern int br_vlan_delete(struct net_bridge *br, u16 vid); |
563 | extern void br_vlan_flush(struct net_bridge *br); | 567 | 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 | 598 | ||
595 | return err; | 599 | return err; |
596 | } | 600 | } |
601 | |||
602 | static inline u16 br_get_pvid(const struct net_port_vlans *v) | ||
603 | { | ||
604 | /* Return just the VID if it is set, or VLAN_N_VID (invalid vid) if | ||
605 | * vid wasn't set | ||
606 | */ | ||
607 | smp_rmb(); | ||
608 | return (v->pvid & VLAN_TAG_PRESENT) ? | ||
609 | (v->pvid & ~VLAN_TAG_PRESENT) : | ||
610 | VLAN_N_VID; | ||
611 | } | ||
612 | |||
597 | #else | 613 | #else |
598 | static inline bool br_allowed_ingress(struct net_bridge *br, | 614 | static inline bool br_allowed_ingress(struct net_bridge *br, |
599 | struct net_port_vlans *v, | 615 | struct net_port_vlans *v, |
600 | struct sk_buff *skb) | 616 | struct sk_buff *skb, |
617 | u16 *vid) | ||
601 | { | 618 | { |
602 | return true; | 619 | return true; |
603 | } | 620 | } |
@@ -609,6 +626,13 @@ static inline bool br_allowed_egress(struct net_bridge *br, | |||
609 | return true; | 626 | return true; |
610 | } | 627 | } |
611 | 628 | ||
629 | static inline struct sk_buff *br_handle_vlan(struct net_bridge *br, | ||
630 | const struct net_port_vlans *v, | ||
631 | struct sk_buff *skb) | ||
632 | { | ||
633 | return skb; | ||
634 | } | ||
635 | |||
612 | static inline int br_vlan_add(struct net_bridge *br, u16 vid) | 636 | static inline int br_vlan_add(struct net_bridge *br, u16 vid) |
613 | { | 637 | { |
614 | return -EOPNOTSUPP; | 638 | return -EOPNOTSUPP; |
@@ -648,10 +672,14 @@ static inline struct net_port_vlans *nbp_get_vlan_info( | |||
648 | return NULL; | 672 | return NULL; |
649 | } | 673 | } |
650 | 674 | ||
651 | static inline u16 br_vlan_get_tag(const struct sk_buff *skb) | 675 | static inline u16 br_vlan_get_tag(const struct sk_buff *skb, u16 *tag) |
652 | { | 676 | { |
653 | return 0; | 677 | return 0; |
654 | } | 678 | } |
679 | static inline u16 br_get_pvid(const struct net_port_vlans *v) | ||
680 | { | ||
681 | return VLAN_N_VID; /* Returns invalid vid */ | ||
682 | } | ||
655 | #endif | 683 | #endif |
656 | 684 | ||
657 | /* br_netfilter.c */ | 685 | /* br_netfilter.c */ |
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index f2bf5a197ea3..20057de56db0 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c | |||
@@ -66,12 +66,68 @@ static void __vlan_flush(struct net_port_vlans *v) | |||
66 | kfree_rcu(v, rcu); | 66 | kfree_rcu(v, rcu); |
67 | } | 67 | } |
68 | 68 | ||
69 | /* Called under RCU */ | 69 | /* Strip the tag from the packet. Will return skb with tci set 0. */ |
70 | bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, | 70 | static struct sk_buff *br_vlan_untag(struct sk_buff *skb) |
71 | struct sk_buff *skb) | 71 | { |
72 | if (skb->protocol != htons(ETH_P_8021Q)) { | ||
73 | skb->vlan_tci = 0; | ||
74 | return skb; | ||
75 | } | ||
76 | |||
77 | skb->vlan_tci = 0; | ||
78 | skb = vlan_untag(skb); | ||
79 | if (skb) | ||
80 | skb->vlan_tci = 0; | ||
81 | |||
82 | return skb; | ||
83 | } | ||
84 | |||
85 | struct sk_buff *br_handle_vlan(struct net_bridge *br, | ||
86 | const struct net_port_vlans *pv, | ||
87 | struct sk_buff *skb) | ||
72 | { | 88 | { |
73 | u16 vid; | 89 | u16 vid; |
74 | 90 | ||
91 | if (!br->vlan_enabled) | ||
92 | goto out; | ||
93 | |||
94 | /* At this point, we know that the frame was filtered and contains | ||
95 | * a valid vlan id. If the vlan id matches the pvid of current port | ||
96 | * send untagged; otherwise, send taged. | ||
97 | */ | ||
98 | br_vlan_get_tag(skb, &vid); | ||
99 | if (vid == br_get_pvid(pv)) | ||
100 | skb = br_vlan_untag(skb); | ||
101 | else { | ||
102 | /* Egress policy says "send tagged". If output device | ||
103 | * is the bridge, we need to add the VLAN header | ||
104 | * ourselves since we'll be going through the RX path. | ||
105 | * Sending to ports puts the frame on the TX path and | ||
106 | * we let dev_hard_start_xmit() add the header. | ||
107 | */ | ||
108 | if (skb->protocol != htons(ETH_P_8021Q) && | ||
109 | pv->port_idx == 0) { | ||
110 | /* vlan_put_tag expects skb->data to point to | ||
111 | * mac header. | ||
112 | */ | ||
113 | skb_push(skb, ETH_HLEN); | ||
114 | skb = __vlan_put_tag(skb, skb->vlan_tci); | ||
115 | if (!skb) | ||
116 | goto out; | ||
117 | /* put skb->data back to where it was */ | ||
118 | skb_pull(skb, ETH_HLEN); | ||
119 | skb->vlan_tci = 0; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | out: | ||
124 | return skb; | ||
125 | } | ||
126 | |||
127 | /* Called under RCU */ | ||
128 | bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, | ||
129 | struct sk_buff *skb, u16 *vid) | ||
130 | { | ||
75 | /* If VLAN filtering is disabled on the bridge, all packets are | 131 | /* If VLAN filtering is disabled on the bridge, all packets are |
76 | * permitted. | 132 | * permitted. |
77 | */ | 133 | */ |
@@ -84,8 +140,25 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, | |||
84 | if (!v) | 140 | if (!v) |
85 | return false; | 141 | return false; |
86 | 142 | ||
87 | br_vlan_get_tag(skb, &vid); | 143 | if (br_vlan_get_tag(skb, vid)) { |
88 | if (test_bit(vid, v->vlan_bitmap)) | 144 | u16 pvid = br_get_pvid(v); |
145 | |||
146 | /* Frame did not have a tag. See if pvid is set | ||
147 | * on this port. That tells us which vlan untagged | ||
148 | * traffic belongs to. | ||
149 | */ | ||
150 | if (pvid == VLAN_N_VID) | ||
151 | return false; | ||
152 | |||
153 | /* PVID is set on this port. Any untagged ingress | ||
154 | * frame is considered to belong to this vlan. | ||
155 | */ | ||
156 | __vlan_hwaccel_put_tag(skb, pvid); | ||
157 | return true; | ||
158 | } | ||
159 | |||
160 | /* Frame had a valid vlan tag. See if vlan is allowed */ | ||
161 | if (test_bit(*vid, v->vlan_bitmap)) | ||
89 | return true; | 162 | return true; |
90 | 163 | ||
91 | return false; | 164 | return false; |