diff -u'rNF^function' patchdir/linux-2.6.16.19/include/linux/pkt_sched.h original/linux-2.6.16.19/include/linux/pkt_sched.h
--- patchdir/linux-2.6.16.19/include/linux/pkt_sched.h	2006-05-31 02:31:44.000000000 +0200
+++ original/linux-2.6.16.19/include/linux/pkt_sched.h	2006-07-17 16:41:53.000000000 +0200
@@ -443,6 +443,9 @@
 	__u32	gap;		/* re-ordering gap (0 for none) */
 	__u32   duplicate;	/* random packet dup  (0=none ~0=100%) */
 	__u32	jitter;		/* random jitter in latency (us) */
+        __u32   trace;           /* handle of trace*/
+	__u32	index;		/* interface index*/
+	__u32   ticks;           /* number of ticks corresponding to 1us*/
 };
 
 struct tc_netem_corr
diff -u'rNF^function' patchdir/linux-2.6.16.19/include/net/flowseedprocfs.h original/linux-2.6.16.19/include/net/flowseedprocfs.h
--- patchdir/linux-2.6.16.19/include/net/flowseedprocfs.h	1970-01-01 01:00:00.000000000 +0100
+++ original/linux-2.6.16.19/include/net/flowseedprocfs.h	2006-07-17 16:41:02.000000000 +0200
@@ -0,0 +1,79 @@
+/* flowseedprocfs.h     header file for the netem trace enhancement
+ */
+
+#ifndef _FLOWSEEDPROCFS_H
+#define _FLOWSEEDPROCFS_H
+
+/* must be divisible by 4 (=#pkts)*/
+#define DATA_PACKAGE 40000
+#define DATA_PACKAGE_ID DATA_PACKAGE+sizeof(unsigned int)+sizeof(int)
+
+/* maximal amount of parallel flows */
+#define MAX_FLOWS 4
+
+/* struct per flow - kernel */
+typedef struct _flowbuffer {
+        char * buffer1;
+        char * buffer2;
+        char * buffer_in_use;   // buffer that is used by consumer
+        char * offsetpos;       // pointer to actual pos in the buffer in use
+        char * buffer1_empty;   // *buffer1 if buffer is empty, NULL else
+        char * buffer2_empty;   // *buffer2 if buffer is empty, NULL else
+        int flowid;             // NIST Net flow id [array index]
+        int upid;               // pid of the user process corresponding to this flowbuffer
+        int validDataB1;        // 1 if Data in buffer1 is valid, 0 if tracefile reached end and rubish is in B1
+        int validDataB2;        // 1 if Data in buffer2 is valid, 0 if tracefile reached end and rubish is in B2
+} flowbuffer;
+
+typedef struct _strdelay {
+  	u_int8_t head;
+  	int delay;
+} strdelay;
+
+struct _proc_stats {
+  	int packetcount;
+  	int packetok;
+	int normaldelay;
+	int drops;
+	int dupl;
+	int corrupt;
+	int noValidData;
+	int uninitialized;
+	int bufferunderrun;
+	int bufferinuseempty;
+	int noemptybuffer;
+	int readbehindbuffer;
+	int buffer1_reloads;
+	int buffer2_reloads;
+	int tobuffer1_switch;
+	int tobuffer2_switch;
+  	int switch_to_emptybuffer1;
+	int switch_to_emptybuffer2;
+};
+
+
+static strdelay get_next_delay(flowbuffer *myrbuf,unsigned int index);
+
+static int init_flowbuffer(unsigned int pid);
+
+static void free_flowbuffer(flowbuffer *victim);
+
+static int proc_write_input(struct file *file,
+                             const char *buffer,
+                             unsigned long count,
+                             void *data);
+
+static int proc_read_stats(char *page, char **start,
+                            off_t off, int count,
+                            int *eof, void *data);
+
+static int proc_write_stats(struct file *file,
+                             const char *buffer,
+                             unsigned long count,
+                             void *data);
+
+static void reset_stats(int i);
+static int init_procfs(void);
+static void cleanup_procfs(void);
+
+#endif
diff -u'rNF^function' patchdir/linux-2.6.16.19/net/sched/Makefile original/linux-2.6.16.19/net/sched/Makefile
--- patchdir/linux-2.6.16.19/net/sched/Makefile	2006-05-31 02:31:44.000000000 +0200
+++ original/linux-2.6.16.19/net/sched/Makefile	2006-07-17 16:40:08.000000000 +0200
@@ -27,7 +27,7 @@
 obj-$(CONFIG_NET_SCH_TEQL)	+= sch_teql.o
 obj-$(CONFIG_NET_SCH_PRIO)	+= sch_prio.o
 obj-$(CONFIG_NET_SCH_ATM)	+= sch_atm.o
-obj-$(CONFIG_NET_SCH_NETEM)	+= sch_netem.o
+obj-$(CONFIG_NET_SCH_NETEM)     += sch_netem.o
 obj-$(CONFIG_NET_CLS_U32)	+= cls_u32.o
 obj-$(CONFIG_NET_CLS_ROUTE4)	+= cls_route.o
 obj-$(CONFIG_NET_CLS_FW)	+= cls_fw.o
diff -u'rNF^function' patchdir/linux-2.6.16.19/net/sched/sch_netem.c original/linux-2.6.16.19/net/sched/sch_netem.c
--- patchdir/linux-2.6.16.19/net/sched/sch_netem.c	2006-05-31 02:31:44.000000000 +0200
+++ original/linux-2.6.16.19/net/sched/sch_netem.c	2006-07-18 10:05:48.000000000 +0200
@@ -11,6 +11,9 @@
  *
  * Authors:	Stephen Hemminger <shemminger@osdl.org>
  *		Catalin(ux aka Dino) BOIE <catab at umbrella dot ro>
+ *              netem trace enhancement: Ariane Keller <arkeller@ee.ethz.ch> ETH Zurich
+ *                                       Rainer Baumann <baumann@hypert.net> ETH Zurich
+ *                                       Ulrich Fiedler <fiedler@tik.ee.ethz.ch> ETH Zurich
  */
 
 #include <linux/config.h>
@@ -22,10 +25,16 @@
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
 #include <linux/rtnetlink.h>
-
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
 #include <net/pkt_sched.h>
 
-#define VERSION "1.2"
+#include "net/flowseedprocfs.h"
+
+#define VERSION "1.3"
+#define bzero(addr, len)        memset(addr, 0, len)
+
+//-----------------------------------------
 
 /*	Network Emulation Queuing algorithm.
 	====================================
@@ -51,6 +60,11 @@
 
 	 The simulator is limited by the Linux timer resolution
 	 and will create packet bursts on the HZ boundary (1ms).
+
+	 The trace option allows us to read the values for packet delay,
+         duplication, loss and corruption from a tracefile. This permits
+	 the modulation of statistical properties such as long-range 
+	 dependences. See tcn.hypert.net.
 */
 
 struct netem_sched_data {
@@ -66,6 +80,9 @@
 	u32 duplicate;
 	u32 reorder;
 	u32 corrupt;
+        u32 trace;
+        u32 index;
+        u32 ticks;
 
 	struct crndstate {
 		unsigned long last;
@@ -83,6 +100,20 @@
 	psched_time_t	time_to_send;
 };
 
+
+/*trace extension*/
+int mask_head = -536870912; // 11100000000000000000000000000000
+int mask_delay = 536870911; // 00011111111111111111111111111111
+
+/* the buffer procfs is writing into after copy_from_user */
+char * procbuf = NULL;
+static struct proc_dir_entry *netem_dir, *netem_input_file, *netem_stats_file;
+flowbuffer *flowbufferptr[MAX_FLOWS];
+unsigned int map[MAX_FLOWS];
+struct _proc_stats procstats[MAX_FLOWS];
+/*end trace extension*/
+
+
 /* init_crandom - initialize correlated random number generator
  * Use entropy source for initial seed.
  */
@@ -153,18 +184,26 @@
 	struct sk_buff *skb2;
 	int ret;
 	int count = 1;
-
+	flowbuffer *mybuf;
+	strdelay mydelay;
+	mydelay.delay=0; //inizialize to 0 delay and no duplication
+	mydelay.head=0;
 	pr_debug("netem_enqueue skb=%p\n", skb);
 
+	if(q->trace){
+	  mybuf=flowbufferptr[(q->trace)-1];
+	  mydelay=get_next_delay(mybuf,q->index);
+	}
+
 	/* Random duplication */
-	if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor))
+	if (mydelay.head==2||(q->duplicate && q->duplicate >= get_crandom(&q->dup_cor)))
 		++count;
 
 	/* Random packet drop 0 => none, ~0 => all */
-	if (q->loss && q->loss >= get_crandom(&q->loss_cor))
+	if (!q->trace&&q->loss && q->loss >= get_crandom(&q->loss_cor))
 		--count;
 
-	if (count == 0) {
+	if (count == 0||(mydelay.head==1)) {
 		sch->qstats.drops++;
 		kfree_skb(skb);
 		return NET_XMIT_DROP;
@@ -175,13 +214,14 @@
 	 * qdisc tree, since parent queuer expects that only one
 	 * skb will be queued.
 	 */
-	if (count > 1 && (skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
-		struct Qdisc *rootq = sch->dev->qdisc;
-		u32 dupsave = q->duplicate; /* prevent duplicating a dup... */
-		q->duplicate = 0;
 
-		rootq->enqueue(skb2, rootq);
-		q->duplicate = dupsave;
+	if ((count > 1 && (skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL)) {
+	  struct Qdisc *rootq = sch->dev->qdisc;
+	  u32 dupsave = q->duplicate; /* prevent duplicating a dup...*/
+	  q->duplicate = 0;
+	  rootq->enqueue(skb2, rootq);
+	  q->duplicate = dupsave;
+	
 	}
 
 	/*
@@ -190,7 +230,8 @@
 	 * If packet is going to be hardware checksummed, then
 	 * do it now in software before we mangle it.
 	 */
-	if (q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor)) {
+
+	if ((!q->trace&&q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor))||mydelay.head==3) {
 		if (!(skb = skb_unshare(skb, GFP_ATOMIC))
 		    || (skb->ip_summed == CHECKSUM_HW
 			&& skb_checksum_help(skb, 0))) {
@@ -201,17 +242,23 @@
 		skb->data[net_random() % skb_headlen(skb)] ^= 1<<(net_random() % 8);
 	}
 
-	if (q->gap == 0 		/* not doing reordering */
+	if ((q->gap == 0 		/* not doing reordering */
 	    || q->counter < q->gap 	/* inside last reordering gap */
-	    || q->reorder < get_crandom(&q->reorder_cor)) {
+	    || q->reorder < get_crandom(&q->reorder_cor))) {
 		psched_time_t now;
 		psched_tdiff_t delay;
-
-		delay = tabledist(q->latency, q->jitter,
+	
+		if(q->trace){
+		  delay=mydelay.delay;
+		  delay=delay*q->ticks;
+		  delay=delay/1000;
+		}
+		else{
+		  delay = tabledist(q->latency, q->jitter,
 				  &q->delay_cor, q->delay_dist);
-
+		}
 		PSCHED_GET_TIME(now);
-		PSCHED_TADD2(now, delay, cb->time_to_send);
+		PSCHED_TADD2(now, delay, cb->time_to_send);     //add delay to packet
 		++q->counter;
 		ret = q->qdisc->enqueue(skb, q->qdisc);
 	} else {
@@ -233,6 +280,7 @@
 
 	pr_debug("netem: enqueue ret %d\n", ret);
 	return ret;
+
 }
 
 /* Requeue packets but don't change time stamp */
@@ -268,6 +316,8 @@
 
 	skb = q->qdisc->dequeue(q->qdisc);
 	if (skb) {
+	 
+	       
 		const struct netem_skb_cb *cb
 			= (const struct netem_skb_cb *)skb->cb;
 		psched_time_t now;
@@ -282,7 +332,6 @@
 			return skb;
 		} else {
 			psched_tdiff_t delay = PSCHED_TDIFF(cb->time_to_send, now);
-
 			if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) {
 				sch->qstats.drops++;
 
@@ -312,8 +361,7 @@
 
 static void netem_reset(struct Qdisc *sch)
 {
-	struct netem_sched_data *q = qdisc_priv(sch);
-
+        struct netem_sched_data *q = qdisc_priv(sch);
 	qdisc_reset(q->qdisc);
 	sch->q.qlen = 0;
 	sch->flags &= ~TCQ_F_THROTTLED;
@@ -416,7 +464,7 @@
 /* Parse netlink message to set options */
 static int netem_change(struct Qdisc *sch, struct rtattr *opt)
 {
-	struct netem_sched_data *q = qdisc_priv(sch);
+        struct netem_sched_data *q = qdisc_priv(sch);
 	struct tc_netem_qopt *qopt;
 	int ret;
 	
@@ -438,6 +486,30 @@
 	q->loss = qopt->loss;
 	q->duplicate = qopt->duplicate;
 
+	if(q->trace){   //there is already a flowseed process running -> kill actual one
+	  int temp=q->trace-1;
+	  q->trace=0;
+	  reset_stats(temp);
+	  free_flowbuffer(flowbufferptr[temp]);
+	}
+	if(qopt->trace){// the new one is a trace
+	  //correction us -> ticks
+	  q->ticks=qopt->ticks;
+	  int ind;
+	  ind=init_flowbuffer(qopt->trace);
+	  if(ind<0){ //there is no more space
+	    printk("netem: maximum number of traces:%d change in net/flowseedprocfs.h\n",MAX_FLOWS);
+	    kill_proc(qopt->trace,SIGKILL,1); //kill flowseed process
+	    kill_proc(qopt->trace,SIGCONT,1); //wake him up to be killed
+	    return -EINVAL;
+	  }
+	  q->trace=ind+1;
+	  
+	}
+	else
+	  q->trace = 0;
+	q->index=qopt->index;
+	
 	/* for compatiablity with earlier versions.
 	 * if gap is set, need to assume 100% probablity
 	 */
@@ -452,7 +524,7 @@
 				 RTA_DATA(opt) + sizeof(*qopt),
 				 RTA_PAYLOAD(opt) - sizeof(*qopt)))
 			return -EINVAL;
-
+	
 		if (tb[TCA_NETEM_CORR-1]) {
 			ret = get_correlation(sch, tb[TCA_NETEM_CORR-1]);
 			if (ret)
@@ -589,6 +661,12 @@
 {
 	struct netem_sched_data *q = qdisc_priv(sch);
 
+	if(q->trace){
+	  int temp=q->trace-1;
+	  q->trace=0;  //first: stop reading values form buffer
+	  free_flowbuffer(flowbufferptr[temp]); //second: delete buffer
+	}
+
 	del_timer_sync(&q->timer);
 	qdisc_destroy(q->qdisc);
 	kfree(q->delay_dist);
@@ -610,6 +688,11 @@
 	qopt.loss = q->loss;
 	qopt.gap = q->gap;
 	qopt.duplicate = q->duplicate;
+
+	qopt.trace = q->trace;
+	qopt.index = q->index;
+	qopt.ticks = q->ticks;
+
 	RTA_PUT(skb, TCA_OPTIONS, sizeof(qopt), &qopt);
 
 	cor.delay_corr = q->delay_cor.rho;
@@ -708,6 +791,458 @@
 	return NULL;
 }
 
+/*functions of the trace enhancement*/
+
+/* don't call this function directly. it is called after a packet has been taken out
+ * of a buffer and it was the last. */
+static int reload_flowbuffer (flowbuffer *myrbuf) {
+	if (myrbuf->buffer_in_use ==  myrbuf->buffer1) {
+		myrbuf->buffer1_empty = myrbuf->buffer1;
+
+		if (myrbuf->buffer2_empty!=NULL) {
+		  procstats[myrbuf->flowid].switch_to_emptybuffer2++;
+		  return -EFAULT;
+		}else{
+		  procstats[myrbuf->flowid].tobuffer2_switch++;
+		}
+
+		myrbuf->buffer_in_use = myrbuf->buffer2;
+		myrbuf->offsetpos =myrbuf->buffer2;
+		
+	}else {
+		myrbuf->buffer2_empty = myrbuf->buffer2;
+
+		if (myrbuf->buffer1_empty!=NULL) {
+		 	procstats[myrbuf->flowid].switch_to_emptybuffer1++;
+			return -EFAULT;
+		}else{
+		  procstats[myrbuf->flowid].tobuffer1_switch++;
+		}
+
+		myrbuf->buffer_in_use = myrbuf->buffer1;
+		myrbuf->offsetpos = myrbuf->buffer1;
+	
+	}
+	/*the flowseed process can send more data*/
+	kill_proc(myrbuf->upid,SIGCONT,1);
+
+	return 0;
+}
+
+/* return delay struct with delay and drop/dupl/corrupt option */
+static strdelay get_next_delay(flowbuffer *myrbuf, unsigned int index) {
+  
+	strdelay retval;
+	bzero(&retval, sizeof(retval));	
+
+	int variout;
+
+	/*choose whether to drop or 0 delay packets on default (on error or on end off tracefile) */
+	retval.head = index;
+	retval.delay=0;
+	/* sanity check... does our flow exist? */
+	if (myrbuf == NULL) {
+	  	printk("netem: read from an uninitialized flow.\n");
+		procstats[myrbuf->flowid].uninitialized++;
+		return retval;
+	}
+
+	procstats[myrbuf->flowid].packetcount++;
+
+	/* check if we have to reload a buffer */
+
+	if (myrbuf->offsetpos - myrbuf->buffer_in_use == DATA_PACKAGE) {
+	 
+	   reload_flowbuffer(myrbuf);
+	}
+	
+	/*do checks only if the Data is valid (e.g. not on end of Tracefile)*/
+	if((myrbuf->buffer_in_use ==  myrbuf->buffer1&&myrbuf->validDataB1)||
+	     ( myrbuf->buffer_in_use ==  myrbuf->buffer2&&myrbuf->validDataB2)){
+
+	  /* sanity check... empty buffers? */
+	  if ((myrbuf->buffer1_empty != NULL) && (myrbuf->buffer2_empty != NULL)) {
+	    procstats[myrbuf->flowid].bufferunderrun++;
+	    return retval;
+	  }
+	  
+	  /* sanity check ... active buffer empty? */
+	  if (myrbuf->buffer1_empty == myrbuf->buffer_in_use ||
+	      myrbuf->buffer2_empty == myrbuf->buffer_in_use) {
+	    procstats[myrbuf->flowid].bufferinuseempty++;
+	    return retval;
+	  }
+	  
+	  /* offset check */
+	  if (myrbuf->offsetpos - myrbuf->buffer_in_use >= DATA_PACKAGE) {
+	    procstats[myrbuf->flowid].readbehindbuffer++;
+	    return retval;
+	  }
+	}else{                                       //end of tracefile reached
+	  procstats[myrbuf->flowid].noValidData++;
+	  return retval;
+	}
+	/* now it's safe to read */
+	memcpy(&variout, myrbuf->offsetpos, 4);
+	myrbuf->offsetpos+=4;
+	
+	/* printout what we've read */
+	retval.delay = variout & mask_delay;
+	retval.head =  (variout & mask_head) >> 29 ;
+
+	/* head 00 (0) -> normal delay
+	 *	01 (1) -> drop packet
+	 *	10 (2) -> duplicate
+	 *	11 (3) -> currupt
+	 */
+
+	switch (retval.head) {
+	  case 0:
+		procstats[myrbuf->flowid].normaldelay++;
+		break;
+	  case 1:
+		procstats[myrbuf->flowid].drops++;
+		break;
+	  case 2:
+		procstats[myrbuf->flowid].dupl++;
+		break;
+	  case 3:
+	        procstats[myrbuf->flowid].corrupt++;
+	        break;
+	}
+
+	procstats[myrbuf->flowid].packetok++;
+
+	return retval;
+}
+
+
+static void free_flowbuffer(flowbuffer *victim) {
+
+        /* flowseed delete section */
+        int flowid=0, upid=0;
+
+        
+	/* if the bufferptr ist NULL, there isn't a corresponding process and tracefile
+         * to this flow and nothing has to be deleted here.
+         */
+	
+        if (victim != NULL) {
+	       
+                upid = victim->upid;
+		if (upid > 0) {
+		     kill_proc(upid,SIGKILL,1);
+                     kill_proc(upid,SIGCONT,1);
+                }
+		
+		flowid=victim->flowid;
+		map[flowid]=0;
+                flowbufferptr[flowid]=NULL;
+		
+         	if(victim->buffer1!=NULL){
+		  kfree(victim->buffer1);
+		}
+		if(victim->buffer2!=NULL)
+		  kfree(victim->buffer2);
+                
+		kfree(victim);
+
+		/* set the bufferptr to zero or bad things will happen... */
+	        victim=NULL;
+		
+        } else{
+	  printk("netem: can't free given flowbuffer, nullpointer\n");
+	}
+}
+
+static int init_flowbuffer(unsigned int pid) {
+  int i,flowid=-1;
+  flowbuffer *mybufB;
+  
+  for(i=0;i<MAX_FLOWS;i++){
+    if(map[i]==0){ /*found an empty place*/
+      flowid=i;
+      map[i]=pid;
+      break;
+    }
+  }
+
+  if(flowid!=-1){
+    flowbufferptr[flowid] = kmalloc(sizeof(flowbuffer),GFP_ATOMIC);
+    mybufB = flowbufferptr[flowid];
+    mybufB->buffer1 = kmalloc(DATA_PACKAGE,GFP_ATOMIC);
+    mybufB->buffer2 = kmalloc(DATA_PACKAGE,GFP_ATOMIC);
+    mybufB->buffer_in_use = mybufB->buffer1;
+    mybufB->offsetpos = mybufB->buffer1;
+    mybufB->buffer1_empty = mybufB->buffer1;
+    mybufB->buffer2_empty = mybufB->buffer2;
+    mybufB->flowid=flowid; 
+    mybufB->upid=pid;
+    mybufB->validDataB1=0;
+    mybufB->validDataB2=0;
+  }
+
+  return flowid; /*-1 if no more space exists, index in flowbufferptr otherwise*/
+
+}
+
+static int proc_write_input(struct file *file,
+                             const char *buffer,
+                             unsigned long count, 
+                             void *data)
+{
+        int len;
+	file=NULL;
+	data=NULL;	
+	flowbuffer *mybufA;
+	if(count != DATA_PACKAGE_ID) {
+		printk("netem: Unexpected data received. Size too big.... exit.\n");
+        
+		return -EFAULT;
+	}
+	else 
+	  	len = count;
+
+        if(copy_from_user(procbuf, buffer, len)) {
+	  return -EFAULT;
+        }
+
+	int upid,i,validData=0;
+	int flowid=-1;
+
+	memcpy(&upid, procbuf+DATA_PACKAGE, sizeof(int));
+	memcpy(&validData,procbuf+DATA_PACKAGE+sizeof(int),sizeof(int));
+
+
+	/*check whether this process is allowed to send data*/
+	for(i=0;i<MAX_FLOWS;i++){
+	  if(map[i]==upid){ /*ok*/ 
+	    flowid=i;
+	    break;
+	    }
+	  } 
+	/*exit if process isnt allowed to send*/ 
+	if (flowid < 0) {
+		printk("netem: Invalid PID received. Killing process.\n");
+		kill_proc(upid,SIGKILL,1);
+		return -EFAULT;   /*not allowed process*/
+		
+	}
+	
+	/* ahhh, i don't like long names */
+	mybufA = flowbufferptr[flowid];
+
+	/* check if flowbuffer has empty buffer and copy data into it */
+	if (mybufA->buffer1_empty != NULL) {
+	  memcpy(mybufA->buffer1, procbuf, DATA_PACKAGE);
+	  mybufA->buffer1_empty = NULL;
+	  mybufA->validDataB1=validData;
+	  procstats[flowid].buffer1_reloads++;
+	 
+	} else if (mybufA->buffer2_empty != NULL) {
+	  memcpy(mybufA->buffer2, procbuf, DATA_PACKAGE);
+	  mybufA->buffer2_empty = NULL;
+	  mybufA->validDataB2=validData;
+	  procstats[flowid].buffer2_reloads++;
+	
+	} else {
+	  printk("netem: flow %d: no empty buffer. data loss. exit.\n",flowid);
+	  procstats[flowid].noemptybuffer++;
+	}
+
+	if(validData){
+	  /* send stop signal to process if no more empty buffers exist */
+	  kill_proc(upid,SIGSTOP,1);
+		 
+	  /* if buffers are loaded the first time, only buffer1 gets data. the
+	   * following call sends a start for the process to send again data for buffer2 */
+	  if (mybufA->buffer2_empty != NULL) {
+	    kill_proc(upid,SIGCONT,1);
+	  }
+	 
+	}
+
+        return DATA_PACKAGE_ID;
+}
+
+static int proc_read_stats(char *page, char **start,
+                            off_t off, int count, 
+                            int *eof, void *data)
+{
+        start=NULL;
+        off=0;
+        count=0;
+        data=NULL;
+        eof=NULL;
+
+	char * message;
+	message = kmalloc(1024,GFP_ATOMIC);
+
+	int i, offset=0;
+	bzero(message, 1024);
+	
+       	for (i=0; i < MAX_FLOWS; i++) {
+	  		
+		if (procstats[i].packetcount <= 0) continue;
+
+		bzero(message, 1024);
+	
+ 		sprintf(message, "Statistics for Flow %2d\n"
+		    	         "----------------------\n"
+		    	 "Packet count:                 %d\n"
+	    		 "Packets ok:                   %d\n"
+			 "Packets with normal Delay:    %d\n"
+			 "Duplicated Packets:           %d\n"
+			 "Drops on Request:             %d\n"
+	  		 "Corrupted Packets:            %d\n"
+		         "No valid data available:      %d\n"
+			 "Uninitialized access:         %d\n"
+			 "Bufferunderruns:              %d\n"
+			 "Use of empty Buffer:          %d\n"
+			 "No empty Buffer:              %d\n"
+			 "Read behind Buffer:           %d\n"
+			 "Buffer1 reloads:              %d\n"
+			 "Buffer2 reloads:              %d\n"
+			 "Switches to Buffer1:          %d\n" 
+			 "Switches to Buffer2:          %d\n" 
+			 "Switches to empty Buffer1:    %d\n" 
+			 "Switches to empty Buffer2:    %d\n\n", 
+			 i,
+			 procstats[i].packetcount, procstats[i].packetok,
+			 procstats[i].normaldelay, procstats[i].dupl,
+			procstats[i].drops, procstats[i].corrupt,procstats[i].noValidData,
+			 procstats[i].uninitialized, procstats[i].bufferunderrun,
+			 procstats[i].bufferinuseempty, procstats[i].noemptybuffer,
+			 procstats[i].readbehindbuffer, procstats[i].buffer1_reloads,
+			 procstats[i].buffer2_reloads, procstats[i].tobuffer1_switch,
+			 procstats[i].tobuffer2_switch, 
+			 procstats[i].switch_to_emptybuffer1, 
+			 procstats[i].switch_to_emptybuffer2);
+
+		memcpy(page+offset, message, strlen(message));
+
+		offset+=strlen(message);
+	}
+
+
+	kfree(message);
+	return offset;
+}
+
+/* a write to /proc/netem/stats resets statistic*/
+static int proc_write_stats(struct file *file,
+                             const char *buffer,
+                             unsigned long count, 
+                             void *data)
+{
+        file=NULL;
+        data=NULL;      
+
+	/* clean up */
+	bzero(&procstats, sizeof(procstats));
+
+        return count;
+}
+
+static void reset_stats(int i)
+{
+  procstats[i].packetcount=0;
+  procstats[i].packetok=0;
+  procstats[i].normaldelay=0;
+  procstats[i].dupl=0;
+  procstats[i].drops=0;
+  procstats[i].corrupt=0;
+  procstats[i].noValidData=0;
+  procstats[i].uninitialized=0;
+  procstats[i].bufferunderrun=0;
+  procstats[i].bufferinuseempty=0;
+  procstats[i].noemptybuffer=0;
+  procstats[i].readbehindbuffer=0;
+  procstats[i].buffer1_reloads=0;
+  procstats[i].buffer2_reloads=0;
+  procstats[i].tobuffer1_switch=0;
+  procstats[i].tobuffer2_switch=0; 
+  procstats[i].switch_to_emptybuffer1=0; 
+  procstats[i].switch_to_emptybuffer2=0;
+  return;
+}
+
+static int init_procfs(void)
+{
+        int rv = 0;
+	bzero(&procstats, sizeof(procstats));
+
+        /* allocate buffer for data from procfs */
+        procbuf = vmalloc(DATA_PACKAGE_ID);
+
+        /* initialize flowbufferpointers all to NULL */
+        int i;
+        for (i = 0 ; i < MAX_FLOWS ; i++){
+	  flowbufferptr[i] = NULL;
+	  map[i]=0;
+	}
+       
+        /* create directory */
+	netem_dir = proc_mkdir("netem", NULL);
+        if(netem_dir == NULL) {
+                rv = -ENOMEM;
+                goto out;
+        }
+        
+        netem_dir->owner = THIS_MODULE;
+        
+        /* create files */
+        netem_input_file = create_proc_entry("input", 0644, netem_dir);
+        if(netem_input_file == NULL) {
+                rv = -ENOMEM;
+                goto no_input;
+        }
+
+	netem_stats_file = create_proc_entry("stats", 0644, netem_dir);
+	if(netem_stats_file == NULL) {
+	  	rv = -ENOMEM;
+		goto no_stats;
+	}
+
+	netem_input_file->write_proc = proc_write_input;
+        netem_input_file->owner = THIS_MODULE;
+
+        netem_stats_file->read_proc = proc_read_stats;
+        netem_stats_file->write_proc = proc_write_stats;
+        netem_stats_file->owner = THIS_MODULE;
+	
+
+        /* everything OK */
+        return 0;
+
+no_stats:
+	remove_proc_entry("stats", netem_dir);
+no_input:
+        remove_proc_entry("input", netem_dir);
+	remove_proc_entry("netem", NULL);
+out:
+        return rv;
+}
+
+
+static void cleanup_procfs(void)
+{
+        remove_proc_entry("input", netem_dir);
+        remove_proc_entry("stats", netem_dir);
+        remove_proc_entry("netem", NULL);
+        vfree(procbuf);
+
+        /* free allocated mem for flowbufferpointers */
+        int i;
+        for (i = 0 ; i < MAX_FLOWS ; i++) {
+                if (flowbufferptr[i] != NULL) {
+		  kfree(flowbufferptr[i]->buffer1);
+		  kfree(flowbufferptr[i]->buffer2);
+                }
+        }
+}
+/*end functions of trace enhancement*/
+
 static struct Qdisc_class_ops netem_class_ops = {
 	.graft		=	netem_graft,
 	.leaf		=	netem_leaf,
@@ -740,12 +1275,17 @@
 static int __init netem_module_init(void)
 {
 	pr_info("netem: version " VERSION "\n");
+	init_procfs();
 	return register_qdisc(&netem_qdisc_ops);
 }
 static void __exit netem_module_exit(void)
 {
 	unregister_qdisc(&netem_qdisc_ops);
+	cleanup_procfs();
 }
 module_init(netem_module_init)
 module_exit(netem_module_exit)
 MODULE_LICENSE("GPL");
+
+
+
