summaryrefslogtreecommitdiffstats
path: root/net/ipv4/ip_fragment.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/ip_fragment.c')
-rw-r--r--net/ipv4/ip_fragment.c34
1 files changed, 23 insertions, 11 deletions
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 07041a3e5..7091bf82c 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -5,7 +5,7 @@
*
* The IP fragmentation functionality.
*
- * Version: $Id: ip_fragment.c,v 1.50 2000/07/07 22:29:42 davem Exp $
+ * Version: $Id: ip_fragment.c,v 1.53 2000/12/08 17:15:53 davem Exp $
*
* Authors: Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
* Alan Cox <Alan.Cox@linux.org>
@@ -51,6 +51,9 @@
int sysctl_ipfrag_high_thresh = 256*1024;
int sysctl_ipfrag_low_thresh = 192*1024;
+/* Important NOTE! Fragment queue must be destroyed before MSL expires.
+ * RFC791 is wrong proposing to prolongate timer each fragment arrival by TTL.
+ */
int sysctl_ipfrag_time = IP_FRAG_TIME;
struct ipfrag_skb_cb
@@ -80,7 +83,7 @@ struct ipq {
atomic_t refcnt;
struct timer_list timer; /* when will this queue expire? */
struct ipq **pprev;
- struct net_device *dev; /* Device - for icmp replies */
+ int iif; /* Device index - for icmp replies */
};
/* Hash table. */
@@ -252,8 +255,13 @@ static void ip_expire(unsigned long arg)
IP_INC_STATS_BH(IpReasmFails);
if ((qp->last_in&FIRST_IN) && qp->fragments != NULL) {
+ struct sk_buff *head = qp->fragments;
+
/* Send an ICMP "Fragment Reassembly Timeout" message. */
- icmp_send(qp->fragments, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0);
+ if ((head->dev = dev_get_by_index(qp->iif)) != NULL) {
+ icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0);
+ dev_put(head->dev);
+ }
}
out:
spin_unlock(&qp->lock);
@@ -287,6 +295,9 @@ static struct ipq *ip_frag_intern(unsigned int hash, struct ipq *qp_in)
#endif
qp = qp_in;
+ if (!mod_timer(&qp->timer, jiffies + sysctl_ipfrag_time))
+ atomic_inc(&qp->refcnt);
+
atomic_inc(&qp->refcnt);
if((qp->next = ipq_hash[hash]) != NULL)
qp->next->pprev = &qp->next;
@@ -367,9 +378,6 @@ static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
if (qp->last_in & COMPLETE)
goto err;
- if (!mod_timer(&qp->timer, jiffies + sysctl_ipfrag_time))
- atomic_inc(&qp->refcnt);
-
offset = ntohs(iph->frag_off);
flags = offset & ~IP_OFFSET;
offset &= IP_OFFSET;
@@ -477,7 +485,8 @@ static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
else
qp->fragments = skb;
- qp->dev = skb->dev;
+ qp->iif = skb->dev->ifindex;
+ skb->dev = NULL;
qp->meat += skb->len;
atomic_add(skb->truesize, &ip_frag_mem);
if (offset == 0)
@@ -496,7 +505,7 @@ err:
* of bits on input. Until the new skb data handling is in I'm not going
* to touch this with a bargepole.
*/
-static struct sk_buff *ip_frag_reasm(struct ipq *qp)
+static struct sk_buff *ip_frag_reasm(struct ipq *qp, struct net_device *dev)
{
struct sk_buff *skb;
struct iphdr *iph;
@@ -537,13 +546,13 @@ static struct sk_buff *ip_frag_reasm(struct ipq *qp)
if (skb->ip_summed != fp->ip_summed)
skb->ip_summed = CHECKSUM_NONE;
else if (skb->ip_summed == CHECKSUM_HW)
- skb->csum = csum_chain(skb->csum, fp->csum);
+ skb->csum = csum_add(skb->csum, fp->csum);
}
skb->dst = dst_clone(head->dst);
skb->pkt_type = head->pkt_type;
skb->protocol = head->protocol;
- skb->dev = qp->dev;
+ skb->dev = dev;
/*
* Clearly bogus, because security markings of the individual
@@ -592,6 +601,7 @@ struct sk_buff *ip_defrag(struct sk_buff *skb)
{
struct iphdr *iph = skb->nh.iph;
struct ipq *qp;
+ struct net_device *dev;
IP_INC_STATS_BH(IpReasmReqds);
@@ -599,6 +609,8 @@ struct sk_buff *ip_defrag(struct sk_buff *skb)
if (atomic_read(&ip_frag_mem) > sysctl_ipfrag_high_thresh)
ip_evictor();
+ dev = skb->dev;
+
/* Lookup (or create) queue header */
if ((qp = ip_find(iph)) != NULL) {
struct sk_buff *ret = NULL;
@@ -609,7 +621,7 @@ struct sk_buff *ip_defrag(struct sk_buff *skb)
if (qp->last_in == (FIRST_IN|LAST_IN) &&
qp->meat == qp->len)
- ret = ip_frag_reasm(qp);
+ ret = ip_frag_reasm(qp, dev);
spin_unlock(&qp->lock);
ipq_put(qp);