diff options
-rw-r--r-- | net/netfilter/nf_queue.c | 40 |
1 files changed, 32 insertions, 8 deletions
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index b3a7db678b8d..ce60cf0f6c11 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c | |||
@@ -203,6 +203,27 @@ err: | |||
203 | return status; | 203 | return status; |
204 | } | 204 | } |
205 | 205 | ||
206 | #ifdef CONFIG_BRIDGE_NETFILTER | ||
207 | /* When called from bridge netfilter, skb->data must point to MAC header | ||
208 | * before calling skb_gso_segment(). Else, original MAC header is lost | ||
209 | * and segmented skbs will be sent to wrong destination. | ||
210 | */ | ||
211 | static void nf_bridge_adjust_skb_data(struct sk_buff *skb) | ||
212 | { | ||
213 | if (skb->nf_bridge) | ||
214 | __skb_push(skb, skb->network_header - skb->mac_header); | ||
215 | } | ||
216 | |||
217 | static void nf_bridge_adjust_segmented_data(struct sk_buff *skb) | ||
218 | { | ||
219 | if (skb->nf_bridge) | ||
220 | __skb_pull(skb, skb->network_header - skb->mac_header); | ||
221 | } | ||
222 | #else | ||
223 | #define nf_bridge_adjust_skb_data(s) do {} while (0) | ||
224 | #define nf_bridge_adjust_segmented_data(s) do {} while (0) | ||
225 | #endif | ||
226 | |||
206 | int nf_queue(struct sk_buff *skb, | 227 | int nf_queue(struct sk_buff *skb, |
207 | struct list_head *elem, | 228 | struct list_head *elem, |
208 | u_int8_t pf, unsigned int hook, | 229 | u_int8_t pf, unsigned int hook, |
@@ -212,7 +233,7 @@ int nf_queue(struct sk_buff *skb, | |||
212 | unsigned int queuenum) | 233 | unsigned int queuenum) |
213 | { | 234 | { |
214 | struct sk_buff *segs; | 235 | struct sk_buff *segs; |
215 | int err; | 236 | int err = -EINVAL; |
216 | unsigned int queued; | 237 | unsigned int queued; |
217 | 238 | ||
218 | if (!skb_is_gso(skb)) | 239 | if (!skb_is_gso(skb)) |
@@ -228,23 +249,25 @@ int nf_queue(struct sk_buff *skb, | |||
228 | break; | 249 | break; |
229 | } | 250 | } |
230 | 251 | ||
252 | nf_bridge_adjust_skb_data(skb); | ||
231 | segs = skb_gso_segment(skb, 0); | 253 | segs = skb_gso_segment(skb, 0); |
232 | /* Does not use PTR_ERR to limit the number of error codes that can be | 254 | /* Does not use PTR_ERR to limit the number of error codes that can be |
233 | * returned by nf_queue. For instance, callers rely on -ECANCELED to mean | 255 | * returned by nf_queue. For instance, callers rely on -ECANCELED to mean |
234 | * 'ignore this hook'. | 256 | * 'ignore this hook'. |
235 | */ | 257 | */ |
236 | if (IS_ERR(segs)) | 258 | if (IS_ERR(segs)) |
237 | return -EINVAL; | 259 | goto out_err; |
238 | |||
239 | queued = 0; | 260 | queued = 0; |
240 | err = 0; | 261 | err = 0; |
241 | do { | 262 | do { |
242 | struct sk_buff *nskb = segs->next; | 263 | struct sk_buff *nskb = segs->next; |
243 | 264 | ||
244 | segs->next = NULL; | 265 | segs->next = NULL; |
245 | if (err == 0) | 266 | if (err == 0) { |
267 | nf_bridge_adjust_segmented_data(segs); | ||
246 | err = __nf_queue(segs, elem, pf, hook, indev, | 268 | err = __nf_queue(segs, elem, pf, hook, indev, |
247 | outdev, okfn, queuenum); | 269 | outdev, okfn, queuenum); |
270 | } | ||
248 | if (err == 0) | 271 | if (err == 0) |
249 | queued++; | 272 | queued++; |
250 | else | 273 | else |
@@ -252,11 +275,12 @@ int nf_queue(struct sk_buff *skb, | |||
252 | segs = nskb; | 275 | segs = nskb; |
253 | } while (segs); | 276 | } while (segs); |
254 | 277 | ||
255 | /* also free orig skb if only some segments were queued */ | 278 | if (queued) { |
256 | if (unlikely(err && queued)) | ||
257 | err = 0; | ||
258 | if (err == 0) | ||
259 | kfree_skb(skb); | 279 | kfree_skb(skb); |
280 | return 0; | ||
281 | } | ||
282 | out_err: | ||
283 | nf_bridge_adjust_segmented_data(skb); | ||
260 | return err; | 284 | return err; |
261 | } | 285 | } |
262 | 286 | ||