diff options
Diffstat (limited to 'net/bridge/br_input.c')
-rw-r--r-- | net/bridge/br_input.c | 51 |
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 | */ |
113 | static inline int is_link_local(const unsigned char *dest) | 113 | static 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 | */ |
124 | int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb) | 127 | struct 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: | ||
166 | drop: | ||
167 | kfree_skb(skb); | ||
152 | } | 168 | } |
153 | 169 | return NULL; | |
154 | err: | ||
155 | kfree_skb(skb); | ||
156 | return 1; | ||
157 | } | 170 | } |