aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge/br_input.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/bridge/br_input.c')
-rw-r--r--net/bridge/br_input.c51
1 files changed, 32 insertions, 19 deletions
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 35b94f9a1ac5..420bbb9955e9 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -112,46 +112,59 @@ static int br_handle_local_finish(struct sk_buff *skb)
112 */ 112 */
113static inline int is_link_local(const unsigned char *dest) 113static inline int is_link_local(const unsigned char *dest)
114{ 114{
115 return memcmp(dest, br_group_address, 5) == 0 && (dest[5] & 0xf0) == 0; 115 const u16 *a = (const u16 *) dest;
116 static const u16 *const b = (const u16 *const ) br_group_address;
117 static const u16 m = __constant_cpu_to_be16(0xfff0);
118
119 return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | ((a[2] ^ b[2]) & m)) == 0;
116} 120}
117 121
118/* 122/*
119 * Called via br_handle_frame_hook. 123 * Called via br_handle_frame_hook.
120 * Return 0 if *pskb should be processed furthur 124 * Return NULL if skb is handled
121 * 1 if *pskb is handled
122 * note: already called with rcu_read_lock (preempt_disabled) 125 * note: already called with rcu_read_lock (preempt_disabled)
123 */ 126 */
124int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb) 127struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb)
125{ 128{
126 struct sk_buff *skb = *pskb;
127 const unsigned char *dest = eth_hdr(skb)->h_dest; 129 const unsigned char *dest = eth_hdr(skb)->h_dest;
128 130
129 if (!is_valid_ether_addr(eth_hdr(skb)->h_source)) 131 if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
130 goto err; 132 goto drop;
131 133
132 if (unlikely(is_link_local(dest))) { 134 if (unlikely(is_link_local(dest))) {
133 skb->pkt_type = PACKET_HOST; 135 /* Pause frames shouldn't be passed up by driver anyway */
134 return NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev, 136 if (skb->protocol == htons(ETH_P_PAUSE))
135 NULL, br_handle_local_finish) != 0; 137 goto drop;
138
139 /* Process STP BPDU's through normal netif_receive_skb() path */
140 if (p->br->stp_enabled != BR_NO_STP) {
141 if (NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
142 NULL, br_handle_local_finish))
143 return NULL;
144 else
145 return skb;
146 }
136 } 147 }
137 148
138 if (p->state == BR_STATE_FORWARDING || p->state == BR_STATE_LEARNING) { 149 switch (p->state) {
150 case BR_STATE_FORWARDING:
151
139 if (br_should_route_hook) { 152 if (br_should_route_hook) {
140 if (br_should_route_hook(pskb)) 153 if (br_should_route_hook(&skb))
141 return 0; 154 return skb;
142 skb = *pskb;
143 dest = eth_hdr(skb)->h_dest; 155 dest = eth_hdr(skb)->h_dest;
144 } 156 }
145 157 /* fall through */
158 case BR_STATE_LEARNING:
146 if (!compare_ether_addr(p->br->dev->dev_addr, dest)) 159 if (!compare_ether_addr(p->br->dev->dev_addr, dest))
147 skb->pkt_type = PACKET_HOST; 160 skb->pkt_type = PACKET_HOST;
148 161
149 NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, 162 NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
150 br_handle_frame_finish); 163 br_handle_frame_finish);
151 return 1; 164 break;
165 default:
166drop:
167 kfree_skb(skb);
152 } 168 }
153 169 return NULL;
154err:
155 kfree_skb(skb);
156 return 1;
157} 170}