patch-1.3.37 linux/net/ipv4/ip_fragment.c
Next file: linux/net/ipv4/ip_options.c
Previous file: linux/net/ipv4/ip_forward.c
Back to the patch index
Back to the overall index
- Lines: 222
- Date:
Sat Oct 28 15:18:24 1995
- Orig file:
v1.3.36/linux/net/ipv4/ip_fragment.c
- Orig date:
Mon Oct 23 18:02:23 1995
diff -u --recursive --new-file v1.3.36/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
@@ -19,6 +19,7 @@
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/icmp.h>
+#include <linux/netdevice.h>
#include <net/sock.h>
#include <net/ip.h>
#include <net/icmp.h>
@@ -29,12 +30,61 @@
#include <net/checksum.h>
/*
+ * Fragment cache limits. We will commit 256K at one time. Should we
+ * cross that limit we will prune down to 192K. This should cope with
+ * even the most extreme cases without allowing an attacker to measurably
+ * harm machine performance.
+ */
+
+#define IPFRAG_HIGH_THRESH (256*1024)
+#define IPFRAG_LOW_THRESH (192*1024)
+
+/*
* This fragment handler is a bit of a heap. On the other hand it works quite
* happily and handles things quite well.
*/
static struct ipq *ipqueue = NULL; /* IP fragment queue */
+unsigned long ip_frag_mem = 0; /* Memory used for fragments */
+
+/*
+ * Memory Tracking Functions
+ */
+
+extern __inline__ void frag_kfree_skb(struct sk_buff *skb, int type)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ ip_frag_mem-=skb->truesize;
+ restore_flags(flags);
+ kfree_skb(skb,type);
+}
+
+extern __inline__ void frag_kfree_s(void *ptr, int len)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ ip_frag_mem-=len;
+ restore_flags(flags);
+ kfree_s(ptr,len);
+}
+
+extern __inline__ void *frag_kmalloc(int size, int pri)
+{
+ unsigned long flags;
+ void *vp=kmalloc(size,pri);
+ if(!vp)
+ return NULL;
+ save_flags(flags);
+ cli();
+ ip_frag_mem+=size;
+ restore_flags(flags);
+ return vp;
+}
+
/*
* Create a new fragment entry.
*/
@@ -42,8 +92,9 @@
static struct ipfrag *ip_frag_create(int offset, int end, struct sk_buff *skb, unsigned char *ptr)
{
struct ipfrag *fp;
+ unsigned long flags;
- fp = (struct ipfrag *) kmalloc(sizeof(struct ipfrag), GFP_ATOMIC);
+ fp = (struct ipfrag *) frag_kmalloc(sizeof(struct ipfrag), GFP_ATOMIC);
if (fp == NULL)
{
NETDEBUG(printk("IP: frag_create: no memory left !\n"));
@@ -57,6 +108,15 @@
fp->len = end - offset;
fp->skb = skb;
fp->ptr = ptr;
+
+ /*
+ * Charge for the SKB as well.
+ */
+
+ save_flags(flags);
+ cli();
+ ip_frag_mem+=skb->truesize;
+ restore_flags(flags);
return(fp);
}
@@ -128,16 +188,16 @@
{
xp = fp->next;
IS_SKB(fp->skb);
- kfree_skb(fp->skb,FREE_READ);
- kfree_s(fp, sizeof(struct ipfrag));
+ frag_kfree_skb(fp->skb,FREE_READ);
+ frag_kfree_s(fp, sizeof(struct ipfrag));
fp = xp;
}
/* Release the IP header. */
- kfree_s(qp->iph, 64 + 8);
+ frag_kfree_s(qp->iph, 64 + 8);
/* Finally, release the queue descriptor itself. */
- kfree_s(qp, sizeof(struct ipq));
+ frag_kfree_s(qp, sizeof(struct ipq));
sti();
}
@@ -169,6 +229,20 @@
ip_free(qp);
}
+/*
+ * Memory limiting on fragments. Evictor trashes the oldest
+ * fragment queue until we are back under the low threshold
+ */
+
+static void ip_evictor(void)
+{
+ while(ip_frag_mem>IPFRAG_LOW_THRESH)
+ {
+ if(!ipqueue)
+ panic("ip_evictor: memcount");
+ ip_free(ipqueue);
+ }
+}
/*
* Add an entry to the 'ipq' queue for a newly received IP datagram.
@@ -182,7 +256,7 @@
struct ipq *qp;
int ihlen;
- qp = (struct ipq *) kmalloc(sizeof(struct ipq), GFP_ATOMIC);
+ qp = (struct ipq *) frag_kmalloc(sizeof(struct ipq), GFP_ATOMIC);
if (qp == NULL)
{
NETDEBUG(printk("IP: create: no memory left !\n"));
@@ -196,11 +270,11 @@
*/
ihlen = iph->ihl * 4;
- qp->iph = (struct iphdr *) kmalloc(64 + 8, GFP_ATOMIC);
+ qp->iph = (struct iphdr *) frag_kmalloc(64 + 8, GFP_ATOMIC);
if (qp->iph == NULL)
{
NETDEBUG(printk("IP: create: no memory left !\n"));
- kfree_s(qp, sizeof(struct ipq));
+ frag_kfree_s(qp, sizeof(struct ipq));
return(NULL);
}
@@ -306,7 +380,7 @@
{
NETDEBUG(printk("Invalid fragment list: Fragment over size.\n"));
ip_free(qp);
- kfree_skb(skb,FREE_WRITE);
+ frag_kfree_skb(skb,FREE_WRITE);
ip_statistics.IpReasmFails++;
return NULL;
}
@@ -342,10 +416,19 @@
unsigned char *ptr;
int flags, offset;
int i, ihl, end;
-
+
ip_statistics.IpReasmReqds++;
- /* Find the entry of this IP datagram in the "incomplete datagrams" queue. */
+ /*
+ * Start by cleaning up the memory
+ */
+
+ if(ip_frag_mem>IPFRAG_HIGH_THRESH)
+ ip_evictor();
+ /*
+ * Find the entry of this IP datagram in the "incomplete datagrams" queue.
+ */
+
qp = ip_find(iph);
/* Is this a non-fragmented datagram? */
@@ -392,7 +475,7 @@
if ((qp = ip_create(skb, iph, dev)) == NULL)
{
skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
+ frag_kfree_skb(skb, FREE_READ);
ip_statistics.IpReasmFails++;
return NULL;
}
@@ -474,8 +557,8 @@
next=tfp; /* We have killed the original next frame */
- kfree_skb(tmp->skb,FREE_READ);
- kfree_s(tmp, sizeof(struct ipfrag));
+ frag_kfree_skb(tmp->skb,FREE_READ);
+ frag_kfree_s(tmp, sizeof(struct ipfrag));
}
}
@@ -493,7 +576,7 @@
if (!tfp)
{
skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
+ frag_kfree_skb(skb, FREE_READ);
return NULL;
}
tfp->prev = prev;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this