diff options
author | Patrick McHardy <kaber@trash.net> | 2006-08-05 03:58:52 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-09-22 17:53:55 -0400 |
commit | 394f545db6e7e4d7a6a2fa3f543b755ca39d58ac (patch) | |
tree | bc757b66d1a394948dace8b57a6272858e5a511b | |
parent | 4cf411de49c65140b3c259748629b561c0d3340f (diff) |
[NETFILTER]: nf_queue: handle GSO packets
Handle GSO packets in nf_queue by segmenting them before queueing to
avoid breaking GSO in case they get mangled.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/netfilter/core.c | 2 | ||||
-rw-r--r-- | net/netfilter/nf_internals.h | 2 | ||||
-rw-r--r-- | net/netfilter/nf_queue.c | 80 |
3 files changed, 62 insertions, 22 deletions
diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 27f639f3ac2a..d80b935b3a92 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c | |||
@@ -182,7 +182,7 @@ next_hook: | |||
182 | ret = -EPERM; | 182 | ret = -EPERM; |
183 | } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) { | 183 | } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) { |
184 | NFDEBUG("nf_hook: Verdict = QUEUE.\n"); | 184 | NFDEBUG("nf_hook: Verdict = QUEUE.\n"); |
185 | if (!nf_queue(pskb, elem, pf, hook, indev, outdev, okfn, | 185 | if (!nf_queue(*pskb, elem, pf, hook, indev, outdev, okfn, |
186 | verdict >> NF_VERDICT_BITS)) | 186 | verdict >> NF_VERDICT_BITS)) |
187 | goto next_hook; | 187 | goto next_hook; |
188 | } | 188 | } |
diff --git a/net/netfilter/nf_internals.h b/net/netfilter/nf_internals.h index 86e392bfe833..a981971ce1d5 100644 --- a/net/netfilter/nf_internals.h +++ b/net/netfilter/nf_internals.h | |||
@@ -23,7 +23,7 @@ extern unsigned int nf_iterate(struct list_head *head, | |||
23 | int hook_thresh); | 23 | int hook_thresh); |
24 | 24 | ||
25 | /* nf_queue.c */ | 25 | /* nf_queue.c */ |
26 | extern int nf_queue(struct sk_buff **skb, | 26 | extern int nf_queue(struct sk_buff *skb, |
27 | struct list_head *elem, | 27 | struct list_head *elem, |
28 | int pf, unsigned int hook, | 28 | int pf, unsigned int hook, |
29 | struct net_device *indev, | 29 | struct net_device *indev, |
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 662a869593bf..4d8936ed581d 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c | |||
@@ -74,13 +74,13 @@ EXPORT_SYMBOL_GPL(nf_unregister_queue_handlers); | |||
74 | * Any packet that leaves via this function must come back | 74 | * Any packet that leaves via this function must come back |
75 | * through nf_reinject(). | 75 | * through nf_reinject(). |
76 | */ | 76 | */ |
77 | int nf_queue(struct sk_buff **skb, | 77 | static int __nf_queue(struct sk_buff *skb, |
78 | struct list_head *elem, | 78 | struct list_head *elem, |
79 | int pf, unsigned int hook, | 79 | int pf, unsigned int hook, |
80 | struct net_device *indev, | 80 | struct net_device *indev, |
81 | struct net_device *outdev, | 81 | struct net_device *outdev, |
82 | int (*okfn)(struct sk_buff *), | 82 | int (*okfn)(struct sk_buff *), |
83 | unsigned int queuenum) | 83 | unsigned int queuenum) |
84 | { | 84 | { |
85 | int status; | 85 | int status; |
86 | struct nf_info *info; | 86 | struct nf_info *info; |
@@ -94,14 +94,14 @@ int nf_queue(struct sk_buff **skb, | |||
94 | read_lock(&queue_handler_lock); | 94 | read_lock(&queue_handler_lock); |
95 | if (!queue_handler[pf]) { | 95 | if (!queue_handler[pf]) { |
96 | read_unlock(&queue_handler_lock); | 96 | read_unlock(&queue_handler_lock); |
97 | kfree_skb(*skb); | 97 | kfree_skb(skb); |
98 | return 1; | 98 | return 1; |
99 | } | 99 | } |
100 | 100 | ||
101 | afinfo = nf_get_afinfo(pf); | 101 | afinfo = nf_get_afinfo(pf); |
102 | if (!afinfo) { | 102 | if (!afinfo) { |
103 | read_unlock(&queue_handler_lock); | 103 | read_unlock(&queue_handler_lock); |
104 | kfree_skb(*skb); | 104 | kfree_skb(skb); |
105 | return 1; | 105 | return 1; |
106 | } | 106 | } |
107 | 107 | ||
@@ -109,9 +109,9 @@ int nf_queue(struct sk_buff **skb, | |||
109 | if (!info) { | 109 | if (!info) { |
110 | if (net_ratelimit()) | 110 | if (net_ratelimit()) |
111 | printk(KERN_ERR "OOM queueing packet %p\n", | 111 | printk(KERN_ERR "OOM queueing packet %p\n", |
112 | *skb); | 112 | skb); |
113 | read_unlock(&queue_handler_lock); | 113 | read_unlock(&queue_handler_lock); |
114 | kfree_skb(*skb); | 114 | kfree_skb(skb); |
115 | return 1; | 115 | return 1; |
116 | } | 116 | } |
117 | 117 | ||
@@ -130,15 +130,15 @@ int nf_queue(struct sk_buff **skb, | |||
130 | if (outdev) dev_hold(outdev); | 130 | if (outdev) dev_hold(outdev); |
131 | 131 | ||
132 | #ifdef CONFIG_BRIDGE_NETFILTER | 132 | #ifdef CONFIG_BRIDGE_NETFILTER |
133 | if ((*skb)->nf_bridge) { | 133 | if (skb->nf_bridge) { |
134 | physindev = (*skb)->nf_bridge->physindev; | 134 | physindev = skb->nf_bridge->physindev; |
135 | if (physindev) dev_hold(physindev); | 135 | if (physindev) dev_hold(physindev); |
136 | physoutdev = (*skb)->nf_bridge->physoutdev; | 136 | physoutdev = skb->nf_bridge->physoutdev; |
137 | if (physoutdev) dev_hold(physoutdev); | 137 | if (physoutdev) dev_hold(physoutdev); |
138 | } | 138 | } |
139 | #endif | 139 | #endif |
140 | afinfo->saveroute(*skb, info); | 140 | afinfo->saveroute(skb, info); |
141 | status = queue_handler[pf]->outfn(*skb, info, queuenum, | 141 | status = queue_handler[pf]->outfn(skb, info, queuenum, |
142 | queue_handler[pf]->data); | 142 | queue_handler[pf]->data); |
143 | 143 | ||
144 | read_unlock(&queue_handler_lock); | 144 | read_unlock(&queue_handler_lock); |
@@ -153,7 +153,7 @@ int nf_queue(struct sk_buff **skb, | |||
153 | #endif | 153 | #endif |
154 | module_put(info->elem->owner); | 154 | module_put(info->elem->owner); |
155 | kfree(info); | 155 | kfree(info); |
156 | kfree_skb(*skb); | 156 | kfree_skb(skb); |
157 | 157 | ||
158 | return 1; | 158 | return 1; |
159 | } | 159 | } |
@@ -161,6 +161,46 @@ int nf_queue(struct sk_buff **skb, | |||
161 | return 1; | 161 | return 1; |
162 | } | 162 | } |
163 | 163 | ||
164 | int nf_queue(struct sk_buff *skb, | ||
165 | struct list_head *elem, | ||
166 | int pf, unsigned int hook, | ||
167 | struct net_device *indev, | ||
168 | struct net_device *outdev, | ||
169 | int (*okfn)(struct sk_buff *), | ||
170 | unsigned int queuenum) | ||
171 | { | ||
172 | struct sk_buff *segs; | ||
173 | |||
174 | if (!skb_is_gso(skb)) | ||
175 | return __nf_queue(skb, elem, pf, hook, indev, outdev, okfn, | ||
176 | queuenum); | ||
177 | |||
178 | switch (pf) { | ||
179 | case AF_INET: | ||
180 | skb->protocol = htons(ETH_P_IP); | ||
181 | break; | ||
182 | case AF_INET6: | ||
183 | skb->protocol = htons(ETH_P_IPV6); | ||
184 | break; | ||
185 | } | ||
186 | |||
187 | segs = skb_gso_segment(skb, 0); | ||
188 | kfree_skb(skb); | ||
189 | if (unlikely(IS_ERR(segs))) | ||
190 | return 1; | ||
191 | |||
192 | do { | ||
193 | struct sk_buff *nskb = segs->next; | ||
194 | |||
195 | segs->next = NULL; | ||
196 | if (!__nf_queue(segs, elem, pf, hook, indev, outdev, okfn, | ||
197 | queuenum)) | ||
198 | kfree_skb(segs); | ||
199 | segs = nskb; | ||
200 | } while (segs); | ||
201 | return 1; | ||
202 | } | ||
203 | |||
164 | void nf_reinject(struct sk_buff *skb, struct nf_info *info, | 204 | void nf_reinject(struct sk_buff *skb, struct nf_info *info, |
165 | unsigned int verdict) | 205 | unsigned int verdict) |
166 | { | 206 | { |
@@ -224,9 +264,9 @@ void nf_reinject(struct sk_buff *skb, struct nf_info *info, | |||
224 | case NF_STOLEN: | 264 | case NF_STOLEN: |
225 | break; | 265 | break; |
226 | case NF_QUEUE: | 266 | case NF_QUEUE: |
227 | if (!nf_queue(&skb, elem, info->pf, info->hook, | 267 | if (!__nf_queue(skb, elem, info->pf, info->hook, |
228 | info->indev, info->outdev, info->okfn, | 268 | info->indev, info->outdev, info->okfn, |
229 | verdict >> NF_VERDICT_BITS)) | 269 | verdict >> NF_VERDICT_BITS)) |
230 | goto next_hook; | 270 | goto next_hook; |
231 | break; | 271 | break; |
232 | default: | 272 | default: |