aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2006-08-05 03:58:52 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2006-09-22 17:53:55 -0400
commit394f545db6e7e4d7a6a2fa3f543b755ca39d58ac (patch)
treebc757b66d1a394948dace8b57a6272858e5a511b
parent4cf411de49c65140b3c259748629b561c0d3340f (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.c2
-rw-r--r--net/netfilter/nf_internals.h2
-rw-r--r--net/netfilter/nf_queue.c80
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 */
26extern int nf_queue(struct sk_buff **skb, 26extern 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 */
77int nf_queue(struct sk_buff **skb, 77static 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
164int 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
164void nf_reinject(struct sk_buff *skb, struct nf_info *info, 204void 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: