--- Copyright (c) 2008, Centre for Advanced Internet Architectures
--- Swinburne University of Technology, Melbourne, Australia
--- (CRICOS number 00111D).
---
--- CAIA Deterministic Packet Discard (DPD) Patch v2.0
---
--- This patch was created against svn revision 183456 of the FreeBSD 8-CURRENT
--- source tree.
---
--- This software was developed by James Healy <jhealy@swin.edu.au>
--- and Lawrence Stewart <lastewart@swin.edu.au>
---
--- All rights reserved.
---
--- Redistribution and use in source and binary forms, with or without
--- modification, are permitted provided that the following conditions
--- are met:
--- 1. Redistributions of source code must retain the above copyright
---    notice, this list of conditions and the following disclaimer.
--- 2. Redistributions in binary form must reproduce the above copyright
---    notice, this list of conditions and the following disclaimer in the
---    documentation and/or other materials provided with the distribution.
--- 3. The names of the authors, the "Centre for Advanced Internet Architectures"
---    and "Swinburne University of Technology" may not be used to endorse
---    or promote products derived from this software without specific
---    prior written permission.
---
--- THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS \`\`AS IS'' AND
--- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
--- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
--- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
--- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
--- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
--- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
--- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
--- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
--- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
--- SUCH DAMAGE.
---
Index: sbin/ipfw/ipfw2.c
===================================================================
--- sbin/ipfw/ipfw2.c	(.../vendor/freebsd/8.x)	(revision 560)
+++ sbin/ipfw/ipfw2.c	(.../dpd/trunk/8.x)	(revision 560)
@@ -83,6 +83,9 @@
 		comment_only,		/* only print action and comment */
 		verbose;
 
+#define CHARPTR_IS_INT(X) \
+	(((unsigned int)(((unsigned char)(*(X)))) - (unsigned char)'0') < 11)
+
 #define	IP_MASK_ALL	0xffffffff
 /*
  * the following macro returns an error message if we run out of
@@ -128,6 +131,10 @@
 		printf("%u", (uint32_t)arg);				\
 } while (0)
 
+#define flush_range_array(PLS) \
+	if((PLS)->arr != NULL) \
+		free((PLS)->arr);
+
 /*
  * _s_x is a structure that stores a string <-> token pairs, used in
  * various places in the parser. Entries are stored in arrays,
@@ -303,6 +310,7 @@
 	TOK_COMMENT,
 
 	TOK_PLR,
+	TOK_PLS,
 	TOK_NOERROR,
 	TOK_BUCKETS,
 	TOK_DSTIP,
@@ -348,6 +356,7 @@
 
 struct _s_x dummynet_params[] = {
 	{ "plr",		TOK_PLR },
+	{ "pls",		TOK_PLS },
 	{ "noerror",		TOK_NOERROR },
 	{ "buckets",		TOK_BUCKETS },
 	{ "dst-ip",		TOK_DSTIP },
@@ -626,6 +635,151 @@
 }
 
 /*
+ * Generates a string of the form "1,2,3,6-10,1000,1500-2000" from a specified
+ * range list. If the range list is too large to fit into the provided string
+ * buffer,"..." will be appended to the string to indicate there was more
+ * information that could not be displayed.
+ */
+static void
+gen_pls_str(char *str, u_int len, struct pls_range_array *ranges)
+{
+	struct pls_range *node;
+	u_int i = 0, stroffset = 0, ret = 0, str_truncated = 0;
+
+	for(; i < ranges->count; i++) {
+
+		if(ranges->arr[i].start == ranges->arr[i].end)
+			ret = snprintf(str + stroffset, len - stroffset, "%d,", ranges->arr[i].start);
+		else
+			ret = snprintf(str + stroffset, len - stroffset, "%d-%d,", ranges->arr[i].start, ranges->arr[i].end);
+
+		if(ret >= (len - stroffset)) {
+			str_truncated = 1;
+			break;
+		}
+		
+		stroffset = strlen(str);
+	}
+
+	if(str_truncated) {
+		*(str+len-3) = '.';
+		*(str+len-2) = '.';
+		*(str+len-1) = '.';
+		*(str+len) = '\0';
+	}
+	else
+		/* remove the trailing comma from the list */
+		*(str+stroffset-1) = '\0';
+}
+
+/*
+ * This function parses a string of the form "1,2,6-10,3,1000,2000-1500" and turns it
+ * into an array of pls_range structs. The array is sorted in order of range start
+ * value from lowest to highest, and the range start and end values within a range struct
+ * are ordered from lowest to highest for consistency. In the example above,
+ * the range node list extracted from the parsed string would look like this:
+ *
+ * pls_range1.start = 1, pls_range1.end = 1
+ * pls_range2.start = 2, pls_range2.end = 2
+ * pls_range3.start = 3, pls_range3.end = 3
+ * pls_range4.start = 6, pls_range4.end = 10
+ * pls_range5.start = 1000, pls_range5.end = 1000
+ * pls_range6.start = 1500, pls_range6.end = 2000
+ */
+static int
+parse_pls_str(char *str, struct pls_range_array *ranges)
+{
+	char *ch, *tmpstr;
+	char tmp[16];
+	int i = 0, j = 0, comma_count = 0, arr_index = 0;
+	struct pls_range *current_range = NULL;
+
+	for(i = strlen(str), tmpstr = str; i >= 0; i--) {
+		ch = tmpstr++;
+		if(*ch == ',')
+			comma_count++;
+	}
+
+	ranges->size = comma_count + 1;
+	ranges->arr = (struct pls_range *)malloc(ranges->size * sizeof(struct pls_range));
+	bzero(ranges->arr, ranges->size * sizeof(struct pls_range));
+
+	if(ranges->arr == NULL)
+		return (ENOMEM);
+	
+	for(i = strlen(str), tmpstr = str; i >= 0; i--) {
+		ch = tmpstr++;
+		memset(tmp, '\0', sizeof(tmp));
+		
+		j = 0;
+		
+		/* if the character is a number 0-9 */
+		while(CHARPTR_IS_INT(ch)) {
+			/* read until the number ends */
+			tmp[j++] = *ch;
+			ch = tmpstr++;
+			i--;
+		}
+
+		/*
+		 * if we are at the end of a range, or the end of the string and
+		 * we have a number stored in tmp
+		 */
+		if((*ch == ',' && j > 0) || (i == 0 && j > 0)) {
+			if(ranges->arr[arr_index].start == 0) {
+				ranges->arr[arr_index].start = strtol(tmp, NULL, 10);
+			}
+
+			ranges->arr[arr_index].end = strtol(tmp, NULL, 10);
+			
+			/*
+			 * reorder the start and end of the range if they were
+			 * specified out of order
+			 */
+			if(ranges->arr[arr_index].end < ranges->arr[arr_index].start) {
+				u_int tmp = ranges->arr[arr_index].end;
+				ranges->arr[arr_index].end = ranges->arr[arr_index].start;
+				ranges->arr[arr_index].start = tmp;
+			}
+
+			arr_index++;
+		}
+		else if(*ch == '-' && j > 0) {
+			/*
+			 * we are half way through parsing a range, so let's set
+			 * create a new range and set its start value to the
+			 * first number we parsed in the range
+			 */
+			ranges->arr[arr_index].start = strtol(tmp, NULL, 10);
+		}
+		else {
+			flush_range_array(ranges);
+			return 1; /* failed parsing the string */
+		}
+	}
+
+	ranges->count = arr_index;
+
+	struct pls_range tmp_range;
+	
+	/*
+	 * bubble sort of the list to put them in numerical order of range start
+	 * values
+	 */
+	for(i = 0; i < ranges->count; i++) {
+		for(j = 1; j < ranges->count; j++) {
+			if(ranges->arr[j-1].start > ranges->arr[j].start) {
+				tmp_range = ranges->arr[j-1];
+				ranges->arr[j-1] = ranges->arr[j];
+				ranges->arr[j] = tmp_range;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/*
  * prints one port, symbolic or numeric
  */
 static void
@@ -2254,7 +2408,7 @@
 {
 	int l;
 	char qs[30];
-	char plr[30];
+	char pl[30];
 	char red[90];	/* Display RED parameters */
 
 	l = fs->qsize;
@@ -2266,9 +2420,13 @@
 	} else
 		sprintf(qs, "%3d sl.", l);
 	if (fs->plr)
-		sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
+		sprintf(pl, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
+	else if (fs->pls.count > 0) {
+		sprintf(pl, "pls ");
+		gen_pls_str(pl+strlen(pl), sizeof(pl)-strlen(pl), &(fs->pls));
+	}
 	else
-		plr[0] = '\0';
+		pl[0] = '\0';
 	if (fs->flags_fs & DN_IS_RED)	/* RED parameters */
 		sprintf(red,
 		    "\n\t  %cRED w_q %f min_th %d max_th %d max_p %f",
@@ -2281,7 +2439,7 @@
 		sprintf(red, "droptail");
 
 	printf("%s %s%s %d queues (%d buckets) %s\n",
-	    prefix, qs, plr, fs->rq_elements, fs->rq_size, red);
+	    prefix, qs, pl, fs->rq_elements, fs->rq_size, red);
 }
 
 static void
@@ -2309,7 +2467,7 @@
 		/*
 		 * compute length, as pipe have variable size
 		 */
-		l = sizeof(*p) + p->fs.rq_elements * sizeof(*q);
+		l = sizeof(*p) + p->fs.rq_elements * sizeof(*q) + p->fs.pls.count * sizeof(struct pls_range);
 		next = (char *)p + l;
 		nbytes -= l;
 
@@ -2332,11 +2490,21 @@
 
 		sprintf(prefix, "%05d: %s %4d ms ",
 		    p->pipe_nr, buf, p->delay);
+
+		/*
+		 * the pls array data is tucked in between the dn_pipe struct
+		 * and fs queue data because the pointer isn't recalculated
+		 * during the copy from kernel mem into userspace mem, we have
+		 * to manually set the array pointer we work back from the end
+		 * of the pipe's data boundary (calculated above as "next")
+		 */
+		p->fs.pls.arr = next - (p->fs.pls.count * sizeof(struct pls_range)) - (p->fs.rq_elements * sizeof(*q));
+
 		print_flowset_parms(&(p->fs), prefix);
 		if (verbose)
 			printf("   V %20qd\n", p->V >> MY_M);
 
-		q = (struct dn_flow_queue *)(p+1);
+		q = (struct dn_flow_queue *)(next - p->fs.rq_elements * sizeof(*q));
 		list_queues(&(p->fs), q);
 	}
 	for (fs = next; nbytes >= sizeof *fs; fs = next) {
@@ -4113,12 +4281,22 @@
 			ac--; av++;
 			break;
 
+		case TOK_PLS:
+			NEED1("pls needs argument x,y-z\n");
+			if(parse_pls_str(av[0], &(p.fs.pls)))
+				errx(EX_DATAERR, "invalid packet loss set");
+			ac--; av++;
+			break;
+
 		case TOK_QUEUE:
 			NEED1("queue needs queue size\n");
 			end = NULL;
 			p.fs.qsize = strtoul(av[0], &end, 0);
-			if (*end == 'K' || *end == 'k') {
+			if (*end == 'M' || *end == 'm') {
 				p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
+				p.fs.qsize *= 1048576;
+			} else if (*end == 'K' || *end == 'k') {
+				p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
 				p.fs.qsize *= 1024;
 			} else if (*end == 'B' ||
 			    _substrcmp2(end, "by", "bytes") == 0) {
@@ -4374,6 +4552,9 @@
 		if (p.fs.qsize > limit)
 			errx(EX_DATAERR, "2 <= queue size <= %ld", limit);
 	}
+	if (p.fs.pls.count > 0 && p.fs.plr > 0) {
+		errx(EX_DATAERR, "plr and pls options are mutually exclusive");
+	}
 	if (p.fs.flags_fs & DN_IS_RED) {
 		size_t len;
 		int lookup_depth, avg_pkt_size;
@@ -5764,21 +5945,36 @@
 }
 
 static void
-zero(int ac, char *av[], int optname /* IP_FW_ZERO or IP_FW_RESETLOG */)
+zero(int ac, char *av[], int optname /* IP_FW_ZERO or IP_FW_RESETLOG or IP_DUMMYNET_ZERO */)
 {
 	uint32_t arg, saved_arg;
 	int failed = EX_OK;
-	char const *name = optname == IP_FW_ZERO ?  "ZERO" : "RESETLOG";
+	char const *name;
 	char const *errstr;
+	struct dn_pipe p;
 
+	if(optname == IP_FW_ZERO || optname == IP_DUMMYNET_ZERO)
+		name = "ZERO";
+	else
+		name = "RESETLOG";
+
 	av++; ac--;
 
+	memset(&p, 0, sizeof p);
+
 	if (!ac) {
 		/* clear all entries */
-		if (do_cmd(optname, NULL, 0) < 0)
-			err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name);
+		if (do_pipe)
+			failed = do_cmd(optname, &p, sizeof p);
+		else
+			failed = do_cmd(optname, NULL, 0);
+
+		if (failed < 0)
+			err(EX_UNAVAILABLE, "setsockopt(IP_%s_%s)",
+				do_pipe ? "DUMMYNET" : "FW", name);
+
 		if (!do_quiet)
-			printf("%s.\n", optname == IP_FW_ZERO ?
+			printf("%s.\n", (optname == IP_FW_ZERO || optname == IP_DUMMYNET_ZERO) ?
 			    "Accounting cleared":"Logging counts reset");
 
 		return;
@@ -5796,13 +5992,14 @@
 				arg |= (1 << 24) | ((use_set - 1) << 16);
 			av++;
 			ac--;
-			if (do_cmd(optname, &arg, sizeof(arg))) {
-				warn("rule %u: setsockopt(IP_FW_%s)",
-				    saved_arg, name);
+			p.pipe_nr = arg;
+			if (do_pipe ? do_cmd(optname, &p, sizeof p) : do_cmd(optname, &arg, sizeof(arg))) {
+				warn("%s %u: setsockopt(IP_%s_%s)", do_pipe ? "pipe" : "rule",
+				    saved_arg, do_pipe ? "DUMMYNET" : "FW", name);
 				failed = EX_UNAVAILABLE;
 			} else if (!do_quiet)
 				printf("Entry %d %s.\n", saved_arg,
-				    optname == IP_FW_ZERO ?
+				    (optname == IP_FW_ZERO || optname == IP_DUMMYNET_ZERO) ?
 					"cleared" : "logging count reset");
 		} else {
 			errx(EX_USAGE, "invalid rule number ``%s''", *av);
@@ -6348,6 +6545,8 @@
 			delete(ac, av);
 		else if (_substrcmp(*av, "flush") == 0)
 			flush(do_force);
+		else if (do_pipe && _substrcmp(*av, "zero") == 0)
+			zero(ac, av, IP_DUMMYNET_ZERO);
 		else if (_substrcmp(*av, "zero") == 0)
 			zero(ac, av, IP_FW_ZERO);
 		else if (_substrcmp(*av, "resetlog") == 0)
Index: sbin/ipfw/ipfw.8
===================================================================
--- sbin/ipfw/ipfw.8	(.../vendor/freebsd/8.x)	(revision 560)
+++ sbin/ipfw/ipfw.8	(.../dpd/trunk/8.x)	(revision 560)
@@ -65,7 +65,7 @@
 .Nm
 .Op Fl s Op Ar field
 .Brq Cm pipe | queue
-.Brq Cm delete | list | show
+.Brq Cm delete | zero | list | show
 .Op Ar number ...
 .Pp
 .Nm
@@ -1989,6 +1989,20 @@
 loss, 1 meaning 100% loss.
 The loss rate is internally represented on 31 bits.
 .Pp
+.It Cm pls Ar packet-loss-set
+Packet loss set.
+Argument
+.Ar packet-loss-set
+is a comma-delimited string of the form 10,30-31,1000 identifying the specific
+packets entering a queue/pipe to drop. In the given example, the 10th, 30th,
+31st and 1000th packet to enter the pipe/queue would be dropped. Clearing the
+counters on a pipe will cause the
+.Ar packet-loss-set
+to be evaluated again from scratch. Use of this option mutually excludes use of
+the
+.Nm plr
+option.
+.Pp
 .It Cm queue Brq Ar slots | size Ns Cm Kbytes
 Queue size, in
 .Ar slots
Index: sys/netinet/in.h
===================================================================
--- sys/netinet/in.h	(.../vendor/freebsd/8.x)	(revision 560)
+++ sys/netinet/in.h	(.../dpd/trunk/8.x)	(revision 560)
@@ -464,10 +464,11 @@
 #define	IP_DUMMYNET_DEL		61   /* delete a dummynet pipe from chain */
 #define	IP_DUMMYNET_FLUSH	62   /* flush dummynet */
 #define	IP_DUMMYNET_GET		64   /* get entire dummynet pipes */
+#define	IP_DUMMYNET_ZERO	65   /* clear single/all dummynet counter(s) */
 
-#define	IP_RECVTTL		65   /* bool; receive IP TTL w/dgram */
 #define	IP_MINTTL		66   /* minimum TTL for packet or drop */
 #define	IP_DONTFRAG		67   /* don't fragment packet */
+#define	IP_RECVTTL		68   /* bool; receive IP TTL w/dgram */
 
 /* IPv4 Source Filter Multicast API [RFC3678] */
 #define	IP_ADD_SOURCE_MEMBERSHIP	70   /* join a source-specific group */
Index: sys/netinet/raw_ip.c
===================================================================
--- sys/netinet/raw_ip.c	(.../vendor/freebsd/8.x)	(revision 560)
+++ sys/netinet/raw_ip.c	(.../dpd/trunk/8.x)	(revision 560)
@@ -530,6 +530,7 @@
 		case IP_DUMMYNET_CONFIGURE:
 		case IP_DUMMYNET_DEL:
 		case IP_DUMMYNET_FLUSH:
+		case IP_DUMMYNET_ZERO:
 			if (ip_dn_ctl_ptr != NULL)
 				error = ip_dn_ctl_ptr(sopt);
 			else
Index: sys/netinet/ip_dummynet.c
===================================================================
--- sys/netinet/ip_dummynet.c	(.../vendor/freebsd/8.x)	(revision 560)
+++ sys/netinet/ip_dummynet.c	(.../dpd/trunk/8.x)	(revision 560)
@@ -241,6 +241,10 @@
 static ip_dn_io_t dummynet_io;
 static void	dn_rule_delete(void *);
 
+#define flush_range_array(PLS) \
+	if((PLS)->arr != NULL) \
+		free((PLS)->arr, M_DUMMYNET);
+
 /*
  * Heap management functions.
  *
@@ -1312,6 +1316,23 @@
 	q->tot_pkts++;
 	if (fs->plr && random() < fs->plr)
 		goto dropit;		/* Random pkt drop. */
+	else {
+		while (fs->pls_index < fs->pls.count) {
+			if (q->tot_pkts >= fs->pls.arr[fs->pls_index].start) {
+				if (q->tot_pkts <=
+					fs->pls.arr[fs->pls_index].end)
+					goto dropit; /* Controlled pkt drop. */
+				else
+					fs->pls_index++;
+			}
+			else
+				/*
+				 * q->tot_pkts is lower than the start of the
+				 * current range, so let the packet through
+				 */
+				break;
+		}
+	}
 	if (fs->flags_fs & DN_QSIZE_IS_BYTES) {
 		if (q->len_bytes > fs->qsize)
 			goto dropit;	/* Queue size overflow. */
@@ -1491,12 +1512,31 @@
 			free(fs->w_q_lookup, M_DUMMYNET);
 		if (fs->rq != NULL)
 			free(fs->rq, M_DUMMYNET);
+		flush_range_array(&(fs->pls));
 		/* If this fs is not part of a pipe, free it. */
 		if (fs->pipe == NULL || fs != &(fs->pipe->fs))
 			free(fs, M_DUMMYNET);
 	}
 }
 
+static void
+zero_flow_set(struct dn_flow_set *fs)
+{
+	int i;
+
+	DUMMYNET_LOCK_ASSERT();
+
+	fs->pls_index = 0;
+
+	for (i = 0; i <= fs->rq_size; i++) {
+		if (fs->rq[i] != NULL) {
+			fs->rq[i]->tot_pkts = 0;
+			fs->rq[i]->tot_bytes = 0;
+			fs->rq[i]->drops = 0;
+		}
+	}
+}
+
 /*
  * Dispose all packets queued on a pipe (not a flow_set).
  * Also free all resources associated to a pipe, which is about
@@ -1692,12 +1732,31 @@
     return 0 ;
 }
 
+static int
+set_pls(struct pls_range_array *x, struct pls_range_array *src)
+{
+	flush_range_array(x);
+
+	MALLOC(x->arr, struct pls_range *, src->count * sizeof(struct pls_range), M_DUMMYNET, M_NOWAIT | M_ZERO);
+
+	if(x->arr == NULL)
+		return (ENOMEM);
+
+	x->count = x->size = src->count;
+	copyin(src->arr, x->arr, src->count * sizeof(struct pls_range));
+
+	return 0;
+}
+
+
 static void
 set_fs_parms(struct dn_flow_set *x, struct dn_flow_set *src)
 {
 	x->flags_fs = src->flags_fs;
 	x->qsize = src->qsize;
 	x->plr = src->plr;
+	x->pls_index = 0;
+	set_pls(&(x->pls), &(src->pls));
 	x->flow_mask = src->flow_mask;
 	if (x->flags_fs & DN_QSIZE_IS_BYTES) {
 		if (x->qsize > pipe_byte_limit)
@@ -1984,16 +2043,63 @@
 }
 
 /*
+ * Zeroes counters of a specified pipe/queue or all pipes/queues.
+ */
+static int
+zero_pipe(struct dn_pipe *p)
+{
+	struct dn_pipe *pipe;
+	struct dn_flow_set *fs;
+	int i;
+
+	DUMMYNET_LOCK();
+
+	if (p->pipe_nr > 0) {
+		pipe = locate_pipe(p->pipe_nr); /* locate pipe */
+		if (pipe == NULL) {
+			DUMMYNET_UNLOCK();
+			return (ENOENT); /* not found */
+		}
+		zero_flow_set(&pipe->fs);
+	} else {
+		/* zero all pipes */
+		for (i = 0; i < HASHSIZE; i++) {
+			SLIST_FOREACH(pipe, &pipehash[i], next) {
+				zero_flow_set(&pipe->fs);
+			}
+			SLIST_FOREACH(fs, &flowsethash[i], next) {
+				zero_flow_set(fs);
+			}
+		}
+	}
+
+	DUMMYNET_UNLOCK();
+
+	return 0 ;
+}
+
+static char *
+dn_copy_pls(struct pls_range_array *src_pls, char *bp)
+{
+	bcopy(src_pls->arr, bp, src_pls->count * sizeof(struct pls_range));
+
+	return (bp + src_pls->count * sizeof(struct pls_range));
+}
+
+/*
  * helper function used to copy data from kernel in DUMMYNET_GET
  */
 static char *
 dn_copy_set(struct dn_flow_set *set, char *bp)
 {
     int i, copied = 0 ;
-    struct dn_flow_queue *q, *qp = (struct dn_flow_queue *)bp;
 
     DUMMYNET_LOCK_ASSERT();
 
+    bp = dn_copy_pls(&(set->pls), bp);
+
+    struct dn_flow_queue *q, *qp = (struct dn_flow_queue *)bp;
+
     for (i = 0 ; i <= set->rq_size ; i++)
 	for (q = set->rq[i] ; q ; q = q->next, qp++ ) {
 	    if (q->hash_slot != i)
@@ -2029,10 +2135,12 @@
      */
     for (i = 0; i < HASHSIZE; i++) {
 	SLIST_FOREACH(pipe, &pipehash[i], next)
-		size += sizeof(*pipe) +
+		size += sizeof(*pipe) + pipe->fs.pls.count * sizeof(struct
+		pls_range) +
 		    pipe->fs.rq_elements * sizeof(struct dn_flow_queue);
 	SLIST_FOREACH(fs, &flowsethash[i], next)
-		size += sizeof (*fs) +
+		size += sizeof (*fs) + pipe->fs.pls.count * sizeof(struct
+		pls_range) +
 		    fs->rq_elements * sizeof(struct dn_flow_queue);
     }
     return size;
@@ -2170,6 +2278,15 @@
 
 	error = delete_pipe(p);
 	break ;
+
+    case IP_DUMMYNET_ZERO :
+	p = &tmp_pipe ;
+	error = sooptcopyin(sopt, p, sizeof *p, sizeof *p);
+	if (error)
+	    break ;
+
+	error = zero_pipe(p);
+	break ;
     }
     return error ;
 }
Index: sys/netinet/ip_dummynet.h
===================================================================
--- sys/netinet/ip_dummynet.h	(.../vendor/freebsd/8.x)	(revision 560)
+++ sys/netinet/ip_dummynet.h	(.../dpd/trunk/8.x)	(revision 560)
@@ -230,6 +230,17 @@
      */
 } ;
 
+struct pls_range {
+    unsigned int start;
+    unsigned int end;
+};
+
+struct pls_range_array {
+    unsigned int size;
+    unsigned int count;
+    struct pls_range *arr;
+};
+
 /*
  * flow_set descriptor. Contains the "template" parameters for the
  * queue configuration, and pointers to the hash table of dn_flow_queue's.
@@ -261,6 +272,8 @@
     int weight ;		/* WFQ queue weight */
     int qsize ;			/* queue size in slots or bytes */
     int plr ;			/* pkt loss rate (2^31-1 means 100%) */
+    int pls_index ;		/* index into pls array */
+    struct pls_range_array pls;	/* pkt loss set as an array of pls_range structs */
 
     struct ipfw_flow_id flow_mask ;
 


