Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: nf_queue: fix queueing of bridged gro skbs

When trying to nf_queue GRO/GSO skbs, nf_queue uses skb_gso_segment
to split the skb.

However, if nf_queue is called via bridge netfilter, the mac header
won't be preserved -- packets will thus contain a bogus mac header.

Fix this by setting skb->data to the mac header when skb->nf_bridge
is set and restoring skb->data afterwards for all segments.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Florian Westphal and committed by
Pablo Neira Ayuso
a8db7b2d e0aac52e

+32 -8
+32 -8
net/netfilter/nf_queue.c
··· 203 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 227 int nf_queue(struct sk_buff *skb, 207 228 struct list_head *elem, 208 229 u_int8_t pf, unsigned int hook, ··· 233 212 unsigned int queuenum) 234 213 { 235 214 struct sk_buff *segs; 236 - int err; 215 + int err = -EINVAL; 237 216 unsigned int queued; 238 217 239 218 if (!skb_is_gso(skb)) ··· 249 228 break; 250 229 } 251 230 231 + nf_bridge_adjust_skb_data(skb); 252 232 segs = skb_gso_segment(skb, 0); 253 233 /* Does not use PTR_ERR to limit the number of error codes that can be 254 234 * returned by nf_queue. For instance, callers rely on -ECANCELED to mean 255 235 * 'ignore this hook'. 256 236 */ 257 237 if (IS_ERR(segs)) 258 - return -EINVAL; 259 - 238 + goto out_err; 260 239 queued = 0; 261 240 err = 0; 262 241 do { 263 242 struct sk_buff *nskb = segs->next; 264 243 265 244 segs->next = NULL; 266 - if (err == 0) 245 + if (err == 0) { 246 + nf_bridge_adjust_segmented_data(segs); 267 247 err = __nf_queue(segs, elem, pf, hook, indev, 268 248 outdev, okfn, queuenum); 249 + } 269 250 if (err == 0) 270 251 queued++; 271 252 else ··· 275 252 segs = nskb; 276 253 } while (segs); 277 254 278 - /* also free orig skb if only some segments were queued */ 279 - if (unlikely(err && queued)) 280 - err = 0; 281 - if (err == 0) 255 + if (queued) { 282 256 kfree_skb(skb); 257 + return 0; 258 + } 259 + out_err: 260 + nf_bridge_adjust_segmented_data(skb); 283 261 return err; 284 262 } 285 263