* YOSHIFUJI,H. @USAGI Always remove fragment header to
* calculate ICV correctly.
*/
+#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/string.h>
write_unlock(&ip6_frag_lock);
}
-/*
- * callers should be careful not to use the hash value outside the ipfrag_lock
- * as doing so could race with ipfrag_hash_rnd being recalculated.
- */
static unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr,
struct in6_addr *daddr)
{
static inline struct frag_queue *frag_alloc_queue(void)
{
- struct frag_queue *fq = kzalloc(sizeof(struct frag_queue), GFP_ATOMIC);
+ struct frag_queue *fq = kmalloc(sizeof(struct frag_queue), GFP_ATOMIC);
if(!fq)
return NULL;
static void ip6_frag_expire(unsigned long data)
{
struct frag_queue *fq = (struct frag_queue *) data;
- struct net_device *dev;
spin_lock(&fq->lock);
IP6_INC_STATS_BH(IPSTATS_MIB_REASMTIMEOUT);
IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
- /* Don't send error if the first segment did not arrive. */
- if (!(fq->last_in&FIRST_IN) || !fq->fragments)
- goto out;
-
- dev = dev_get_by_index(fq->iif);
- if (!dev)
- goto out;
+ /* Send error only if the first segment arrived. */
+ if (fq->last_in&FIRST_IN && fq->fragments) {
+ struct net_device *dev = dev_get_by_index(fq->iif);
- /*
- But use as source device on which LAST ARRIVED
- segment was received. And do not use fq->dev
- pointer directly, device might already disappeared.
- */
- fq->fragments->dev = dev;
- icmpv6_send(fq->fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0, dev);
- dev_put(dev);
+ /*
+ But use as source device on which LAST ARRIVED
+ segment was received. And do not use fq->dev
+ pointer directly, device might already disappeared.
+ */
+ if (dev) {
+ fq->fragments->dev = dev;
+ icmpv6_send(fq->fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0,
+ dev);
+ dev_put(dev);
+ }
+ }
out:
spin_unlock(&fq->lock);
fq_put(fq, NULL);
/* Creation primitives. */
-static struct frag_queue *ip6_frag_intern(struct frag_queue *fq_in)
+static struct frag_queue *ip6_frag_intern(unsigned int hash,
+ struct frag_queue *fq_in)
{
struct frag_queue *fq;
- unsigned int hash;
#ifdef CONFIG_SMP
struct hlist_node *n;
#endif
write_lock(&ip6_frag_lock);
- hash = ip6qhashfn(fq_in->id, &fq_in->saddr, &fq_in->daddr);
#ifdef CONFIG_SMP
hlist_for_each_entry(fq, n, &ip6_frag_hash[hash], list) {
if (fq->id == fq_in->id &&
static struct frag_queue *
-ip6_frag_create(u32 id, struct in6_addr *src, struct in6_addr *dst)
+ip6_frag_create(unsigned int hash, u32 id, struct in6_addr *src, struct in6_addr *dst)
{
struct frag_queue *fq;
if ((fq = frag_alloc_queue()) == NULL)
goto oom;
+ memset(fq, 0, sizeof(struct frag_queue));
+
fq->id = id;
ipv6_addr_copy(&fq->saddr, src);
ipv6_addr_copy(&fq->daddr, dst);
spin_lock_init(&fq->lock);
atomic_set(&fq->refcnt, 1);
- return ip6_frag_intern(fq);
+ return ip6_frag_intern(hash, fq);
oom:
IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
{
struct frag_queue *fq;
struct hlist_node *n;
- unsigned int hash;
+ unsigned int hash = ip6qhashfn(id, src, dst);
read_lock(&ip6_frag_lock);
- hash = ip6qhashfn(id, src, dst);
hlist_for_each_entry(fq, n, &ip6_frag_hash[hash], list) {
if (fq->id == id &&
ipv6_addr_equal(src, &fq->saddr) &&
}
read_unlock(&ip6_frag_lock);
- return ip6_frag_create(id, src, dst);
+ return ip6_frag_create(hash, id, src, dst);
}