diff options
Diffstat (limited to 'net/ipv4/ip_fragment.c')
-rw-r--r-- | net/ipv4/ip_fragment.c | 159 |
1 files changed, 51 insertions, 108 deletions
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 443b3f89192f..2143bf30597a 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c | |||
@@ -108,6 +108,11 @@ int ip_frag_mem(void) | |||
108 | static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, | 108 | static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, |
109 | struct net_device *dev); | 109 | struct net_device *dev); |
110 | 110 | ||
111 | struct ip4_create_arg { | ||
112 | struct iphdr *iph; | ||
113 | u32 user; | ||
114 | }; | ||
115 | |||
111 | static unsigned int ipqhashfn(__be16 id, __be32 saddr, __be32 daddr, u8 prot) | 116 | static unsigned int ipqhashfn(__be16 id, __be32 saddr, __be32 daddr, u8 prot) |
112 | { | 117 | { |
113 | return jhash_3words((__force u32)id << 16 | prot, | 118 | return jhash_3words((__force u32)id << 16 | prot, |
@@ -123,6 +128,19 @@ static unsigned int ip4_hashfn(struct inet_frag_queue *q) | |||
123 | return ipqhashfn(ipq->id, ipq->saddr, ipq->daddr, ipq->protocol); | 128 | return ipqhashfn(ipq->id, ipq->saddr, ipq->daddr, ipq->protocol); |
124 | } | 129 | } |
125 | 130 | ||
131 | static int ip4_frag_match(struct inet_frag_queue *q, void *a) | ||
132 | { | ||
133 | struct ipq *qp; | ||
134 | struct ip4_create_arg *arg = a; | ||
135 | |||
136 | qp = container_of(q, struct ipq, q); | ||
137 | return (qp->id == arg->iph->id && | ||
138 | qp->saddr == arg->iph->saddr && | ||
139 | qp->daddr == arg->iph->daddr && | ||
140 | qp->protocol == arg->iph->protocol && | ||
141 | qp->user == arg->user); | ||
142 | } | ||
143 | |||
126 | /* Memory Tracking Functions. */ | 144 | /* Memory Tracking Functions. */ |
127 | static __inline__ void frag_kfree_skb(struct sk_buff *skb, int *work) | 145 | static __inline__ void frag_kfree_skb(struct sk_buff *skb, int *work) |
128 | { | 146 | { |
@@ -132,6 +150,20 @@ static __inline__ void frag_kfree_skb(struct sk_buff *skb, int *work) | |||
132 | kfree_skb(skb); | 150 | kfree_skb(skb); |
133 | } | 151 | } |
134 | 152 | ||
153 | static void ip4_frag_init(struct inet_frag_queue *q, void *a) | ||
154 | { | ||
155 | struct ipq *qp = container_of(q, struct ipq, q); | ||
156 | struct ip4_create_arg *arg = a; | ||
157 | |||
158 | qp->protocol = arg->iph->protocol; | ||
159 | qp->id = arg->iph->id; | ||
160 | qp->saddr = arg->iph->saddr; | ||
161 | qp->daddr = arg->iph->daddr; | ||
162 | qp->user = arg->user; | ||
163 | qp->peer = sysctl_ipfrag_max_dist ? | ||
164 | inet_getpeer(arg->iph->saddr, 1) : NULL; | ||
165 | } | ||
166 | |||
135 | static __inline__ void ip4_frag_free(struct inet_frag_queue *q) | 167 | static __inline__ void ip4_frag_free(struct inet_frag_queue *q) |
136 | { | 168 | { |
137 | struct ipq *qp; | 169 | struct ipq *qp; |
@@ -139,17 +171,6 @@ static __inline__ void ip4_frag_free(struct inet_frag_queue *q) | |||
139 | qp = container_of(q, struct ipq, q); | 171 | qp = container_of(q, struct ipq, q); |
140 | if (qp->peer) | 172 | if (qp->peer) |
141 | inet_putpeer(qp->peer); | 173 | inet_putpeer(qp->peer); |
142 | kfree(qp); | ||
143 | } | ||
144 | |||
145 | static __inline__ struct ipq *frag_alloc_queue(void) | ||
146 | { | ||
147 | struct ipq *qp = kzalloc(sizeof(struct ipq), GFP_ATOMIC); | ||
148 | |||
149 | if (!qp) | ||
150 | return NULL; | ||
151 | atomic_add(sizeof(struct ipq), &ip4_frags.mem); | ||
152 | return qp; | ||
153 | } | 174 | } |
154 | 175 | ||
155 | 176 | ||
@@ -185,7 +206,9 @@ static void ip_evictor(void) | |||
185 | */ | 206 | */ |
186 | static void ip_expire(unsigned long arg) | 207 | static void ip_expire(unsigned long arg) |
187 | { | 208 | { |
188 | struct ipq *qp = (struct ipq *) arg; | 209 | struct ipq *qp; |
210 | |||
211 | qp = container_of((struct inet_frag_queue *) arg, struct ipq, q); | ||
189 | 212 | ||
190 | spin_lock(&qp->q.lock); | 213 | spin_lock(&qp->q.lock); |
191 | 214 | ||
@@ -210,112 +233,30 @@ out: | |||
210 | ipq_put(qp); | 233 | ipq_put(qp); |
211 | } | 234 | } |
212 | 235 | ||
213 | /* Creation primitives. */ | 236 | /* Find the correct entry in the "incomplete datagrams" queue for |
214 | 237 | * this IP datagram, and create new one, if nothing is found. | |
215 | static struct ipq *ip_frag_intern(struct ipq *qp_in) | 238 | */ |
239 | static inline struct ipq *ip_find(struct iphdr *iph, u32 user) | ||
216 | { | 240 | { |
217 | struct ipq *qp; | 241 | struct inet_frag_queue *q; |
218 | #ifdef CONFIG_SMP | 242 | struct ip4_create_arg arg; |
219 | struct hlist_node *n; | ||
220 | #endif | ||
221 | unsigned int hash; | 243 | unsigned int hash; |
222 | 244 | ||
223 | write_lock(&ip4_frags.lock); | 245 | arg.iph = iph; |
224 | hash = ipqhashfn(qp_in->id, qp_in->saddr, qp_in->daddr, | 246 | arg.user = user; |
225 | qp_in->protocol); | 247 | hash = ipqhashfn(iph->id, iph->saddr, iph->daddr, iph->protocol); |
226 | #ifdef CONFIG_SMP | ||
227 | /* With SMP race we have to recheck hash table, because | ||
228 | * such entry could be created on other cpu, while we | ||
229 | * promoted read lock to write lock. | ||
230 | */ | ||
231 | hlist_for_each_entry(qp, n, &ip4_frags.hash[hash], q.list) { | ||
232 | if (qp->id == qp_in->id && | ||
233 | qp->saddr == qp_in->saddr && | ||
234 | qp->daddr == qp_in->daddr && | ||
235 | qp->protocol == qp_in->protocol && | ||
236 | qp->user == qp_in->user) { | ||
237 | atomic_inc(&qp->q.refcnt); | ||
238 | write_unlock(&ip4_frags.lock); | ||
239 | qp_in->q.last_in |= COMPLETE; | ||
240 | ipq_put(qp_in); | ||
241 | return qp; | ||
242 | } | ||
243 | } | ||
244 | #endif | ||
245 | qp = qp_in; | ||
246 | |||
247 | if (!mod_timer(&qp->q.timer, jiffies + ip4_frags_ctl.timeout)) | ||
248 | atomic_inc(&qp->q.refcnt); | ||
249 | 248 | ||
250 | atomic_inc(&qp->q.refcnt); | 249 | q = inet_frag_find(&ip4_frags, &arg, hash); |
251 | hlist_add_head(&qp->q.list, &ip4_frags.hash[hash]); | 250 | if (q == NULL) |
252 | INIT_LIST_HEAD(&qp->q.lru_list); | ||
253 | list_add_tail(&qp->q.lru_list, &ip4_frags.lru_list); | ||
254 | ip4_frags.nqueues++; | ||
255 | write_unlock(&ip4_frags.lock); | ||
256 | return qp; | ||
257 | } | ||
258 | |||
259 | /* Add an entry to the 'ipq' queue for a newly received IP datagram. */ | ||
260 | static struct ipq *ip_frag_create(struct iphdr *iph, u32 user) | ||
261 | { | ||
262 | struct ipq *qp; | ||
263 | |||
264 | if ((qp = frag_alloc_queue()) == NULL) | ||
265 | goto out_nomem; | 251 | goto out_nomem; |
266 | 252 | ||
267 | qp->protocol = iph->protocol; | 253 | return container_of(q, struct ipq, q); |
268 | qp->id = iph->id; | ||
269 | qp->saddr = iph->saddr; | ||
270 | qp->daddr = iph->daddr; | ||
271 | qp->user = user; | ||
272 | qp->peer = sysctl_ipfrag_max_dist ? inet_getpeer(iph->saddr, 1) : NULL; | ||
273 | |||
274 | /* Initialize a timer for this entry. */ | ||
275 | init_timer(&qp->q.timer); | ||
276 | qp->q.timer.data = (unsigned long) qp; /* pointer to queue */ | ||
277 | qp->q.timer.function = ip_expire; /* expire function */ | ||
278 | spin_lock_init(&qp->q.lock); | ||
279 | atomic_set(&qp->q.refcnt, 1); | ||
280 | |||
281 | return ip_frag_intern(qp); | ||
282 | 254 | ||
283 | out_nomem: | 255 | out_nomem: |
284 | LIMIT_NETDEBUG(KERN_ERR "ip_frag_create: no memory left !\n"); | 256 | LIMIT_NETDEBUG(KERN_ERR "ip_frag_create: no memory left !\n"); |
285 | return NULL; | 257 | return NULL; |
286 | } | 258 | } |
287 | 259 | ||
288 | /* Find the correct entry in the "incomplete datagrams" queue for | ||
289 | * this IP datagram, and create new one, if nothing is found. | ||
290 | */ | ||
291 | static inline struct ipq *ip_find(struct iphdr *iph, u32 user) | ||
292 | { | ||
293 | __be16 id = iph->id; | ||
294 | __be32 saddr = iph->saddr; | ||
295 | __be32 daddr = iph->daddr; | ||
296 | __u8 protocol = iph->protocol; | ||
297 | unsigned int hash; | ||
298 | struct ipq *qp; | ||
299 | struct hlist_node *n; | ||
300 | |||
301 | read_lock(&ip4_frags.lock); | ||
302 | hash = ipqhashfn(id, saddr, daddr, protocol); | ||
303 | hlist_for_each_entry(qp, n, &ip4_frags.hash[hash], q.list) { | ||
304 | if (qp->id == id && | ||
305 | qp->saddr == saddr && | ||
306 | qp->daddr == daddr && | ||
307 | qp->protocol == protocol && | ||
308 | qp->user == user) { | ||
309 | atomic_inc(&qp->q.refcnt); | ||
310 | read_unlock(&ip4_frags.lock); | ||
311 | return qp; | ||
312 | } | ||
313 | } | ||
314 | read_unlock(&ip4_frags.lock); | ||
315 | |||
316 | return ip_frag_create(iph, user); | ||
317 | } | ||
318 | |||
319 | /* Is the fragment too far ahead to be part of ipq? */ | 260 | /* Is the fragment too far ahead to be part of ipq? */ |
320 | static inline int ip_frag_too_far(struct ipq *qp) | 261 | static inline int ip_frag_too_far(struct ipq *qp) |
321 | { | 262 | { |
@@ -545,7 +486,6 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, | |||
545 | if (prev) { | 486 | if (prev) { |
546 | head = prev->next; | 487 | head = prev->next; |
547 | fp = skb_clone(head, GFP_ATOMIC); | 488 | fp = skb_clone(head, GFP_ATOMIC); |
548 | |||
549 | if (!fp) | 489 | if (!fp) |
550 | goto out_nomem; | 490 | goto out_nomem; |
551 | 491 | ||
@@ -571,7 +511,6 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, | |||
571 | goto out_oversize; | 511 | goto out_oversize; |
572 | 512 | ||
573 | /* Head of list must not be cloned. */ | 513 | /* Head of list must not be cloned. */ |
574 | err = -ENOMEM; | ||
575 | if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC)) | 514 | if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC)) |
576 | goto out_nomem; | 515 | goto out_nomem; |
577 | 516 | ||
@@ -627,6 +566,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, | |||
627 | out_nomem: | 566 | out_nomem: |
628 | LIMIT_NETDEBUG(KERN_ERR "IP: queue_glue: no memory for gluing " | 567 | LIMIT_NETDEBUG(KERN_ERR "IP: queue_glue: no memory for gluing " |
629 | "queue %p\n", qp); | 568 | "queue %p\n", qp); |
569 | err = -ENOMEM; | ||
630 | goto out_fail; | 570 | goto out_fail; |
631 | out_oversize: | 571 | out_oversize: |
632 | if (net_ratelimit()) | 572 | if (net_ratelimit()) |
@@ -671,9 +611,12 @@ void __init ipfrag_init(void) | |||
671 | { | 611 | { |
672 | ip4_frags.ctl = &ip4_frags_ctl; | 612 | ip4_frags.ctl = &ip4_frags_ctl; |
673 | ip4_frags.hashfn = ip4_hashfn; | 613 | ip4_frags.hashfn = ip4_hashfn; |
614 | ip4_frags.constructor = ip4_frag_init; | ||
674 | ip4_frags.destructor = ip4_frag_free; | 615 | ip4_frags.destructor = ip4_frag_free; |
675 | ip4_frags.skb_free = NULL; | 616 | ip4_frags.skb_free = NULL; |
676 | ip4_frags.qsize = sizeof(struct ipq); | 617 | ip4_frags.qsize = sizeof(struct ipq); |
618 | ip4_frags.match = ip4_frag_match; | ||
619 | ip4_frags.frag_expire = ip_expire; | ||
677 | inet_frags_init(&ip4_frags); | 620 | inet_frags_init(&ip4_frags); |
678 | } | 621 | } |
679 | 622 | ||