Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 69397
b: refs/heads/master
c: 1706d58
h: refs/heads/master
i:
  69395: 9beab51
v: v3
  • Loading branch information
Herbert Xu authored and David S. Miller committed Oct 15, 2007
1 parent 25f27f1 commit 2e5b2bb
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 22 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: e0053ec07e32ec94535c47b10af3377255f00836
refs/heads/master: 1706d58763c36133d7fce6cc78b1444fd40db28c
76 changes: 55 additions & 21 deletions trunk/net/ipv4/ip_fragment.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ static u32 ipfrag_hash_rnd;
static LIST_HEAD(ipq_lru_list);
int ip_frag_nqueues = 0;

static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
struct net_device *dev);

static __inline__ void __ipq_unlink(struct ipq *qp)
{
hlist_del(&qp->list);
Expand Down Expand Up @@ -464,17 +467,20 @@ static int ip_frag_reinit(struct ipq *qp)
}

/* Add new segment to existing queue. */
static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
{
struct sk_buff *prev, *next;
struct net_device *dev;
int flags, offset;
int ihl, end;
int err = -ENOENT;

if (qp->last_in & COMPLETE)
goto err;

if (!(IPCB(skb)->flags & IPSKB_FRAG_COMPLETE) &&
unlikely(ip_frag_too_far(qp)) && unlikely(ip_frag_reinit(qp))) {
unlikely(ip_frag_too_far(qp)) &&
unlikely(err = ip_frag_reinit(qp))) {
ipq_kill(qp);
goto err;
}
Expand All @@ -487,6 +493,7 @@ static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)

/* Determine the position of this fragment. */
end = offset + skb->len - ihl;
err = -EINVAL;

/* Is this the final fragment? */
if ((flags & IP_MF) == 0) {
Expand Down Expand Up @@ -514,9 +521,12 @@ static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
if (end == offset)
goto err;

err = -ENOMEM;
if (pskb_pull(skb, ihl) == NULL)
goto err;
if (pskb_trim_rcsum(skb, end-offset))

err = pskb_trim_rcsum(skb, end - offset);
if (err)
goto err;

/* Find out which fragments are in front and at the back of us
Expand All @@ -539,15 +549,19 @@ static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)

if (i > 0) {
offset += i;
err = -EINVAL;
if (end <= offset)
goto err;
err = -ENOMEM;
if (!pskb_pull(skb, i))
goto err;
if (skb->ip_summed != CHECKSUM_UNNECESSARY)
skb->ip_summed = CHECKSUM_NONE;
}
}

err = -ENOMEM;

while (next && FRAG_CB(next)->offset < end) {
int i = end - FRAG_CB(next)->offset; /* overlap is 'i' bytes */

Expand Down Expand Up @@ -589,48 +603,75 @@ static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
else
qp->fragments = skb;

if (skb->dev)
qp->iif = skb->dev->ifindex;
skb->dev = NULL;
dev = skb->dev;
if (dev) {
qp->iif = dev->ifindex;
skb->dev = NULL;
}
qp->stamp = skb->tstamp;
qp->meat += skb->len;
atomic_add(skb->truesize, &ip_frag_mem);
if (offset == 0)
qp->last_in |= FIRST_IN;

if (qp->last_in == (FIRST_IN | LAST_IN) && qp->meat == qp->len)
return ip_frag_reasm(qp, prev, dev);

write_lock(&ipfrag_lock);
list_move_tail(&qp->lru_list, &ipq_lru_list);
write_unlock(&ipfrag_lock);

return;
return -EINPROGRESS;

err:
kfree_skb(skb);
return err;
}


/* Build a new IP datagram from all its fragments. */

static struct sk_buff *ip_frag_reasm(struct ipq *qp, struct net_device *dev)
static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
struct net_device *dev)
{
struct iphdr *iph;
struct sk_buff *fp, *head = qp->fragments;
int len;
int ihlen;
int err;

ipq_kill(qp);

/* Make the one we just received the head. */
if (prev) {
head = prev->next;
fp = skb_clone(head, GFP_ATOMIC);

if (!fp)
goto out_nomem;

fp->next = head->next;
prev->next = fp;

skb_morph(head, qp->fragments);
head->next = qp->fragments->next;

kfree_skb(qp->fragments);
qp->fragments = head;
}

BUG_TRAP(head != NULL);
BUG_TRAP(FRAG_CB(head)->offset == 0);

/* Allocate a new buffer for the datagram. */
ihlen = ip_hdrlen(head);
len = ihlen + qp->len;

err = -E2BIG;
if (len > 65535)
goto out_oversize;

/* Head of list must not be cloned. */
err = -ENOMEM;
if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC))
goto out_nomem;

Expand Down Expand Up @@ -681,7 +722,7 @@ static struct sk_buff *ip_frag_reasm(struct ipq *qp, struct net_device *dev)
iph->tot_len = htons(len);
IP_INC_STATS_BH(IPSTATS_MIB_REASMOKS);
qp->fragments = NULL;
return head;
return 0;

out_nomem:
LIMIT_NETDEBUG(KERN_ERR "IP: queue_glue: no memory for gluing "
Expand All @@ -694,38 +735,31 @@ static struct sk_buff *ip_frag_reasm(struct ipq *qp, struct net_device *dev)
NIPQUAD(qp->saddr));
out_fail:
IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
return NULL;
return err;
}

/* Process an incoming IP datagram fragment. */
struct sk_buff *ip_defrag(struct sk_buff *skb, u32 user)
{
struct ipq *qp;
struct net_device *dev;

IP_INC_STATS_BH(IPSTATS_MIB_REASMREQDS);

/* Start by cleaning up the memory. */
if (atomic_read(&ip_frag_mem) > sysctl_ipfrag_high_thresh)
ip_evictor();

dev = skb->dev;

/* Lookup (or create) queue header */
if ((qp = ip_find(ip_hdr(skb), user)) != NULL) {
struct sk_buff *ret = NULL;
int ret;

spin_lock(&qp->lock);

ip_frag_queue(qp, skb);

if (qp->last_in == (FIRST_IN|LAST_IN) &&
qp->meat == qp->len)
ret = ip_frag_reasm(qp, dev);
ret = ip_frag_queue(qp, skb);

spin_unlock(&qp->lock);
ipq_put(qp, NULL);
return ret;
return ret ? NULL : skb;
}

IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
Expand Down

0 comments on commit 2e5b2bb

Please sign in to comment.