From owner-svn-src-user@FreeBSD.ORG Fri Nov 14 03:37:16 2008 Return-Path: Delivered-To: svn-src-user@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 9D5651065673; Fri, 14 Nov 2008 03:37:16 +0000 (UTC) (envelope-from lstewart@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 825988FC08; Fri, 14 Nov 2008 03:37:16 +0000 (UTC) (envelope-from lstewart@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id mAE3bGwB077486; Fri, 14 Nov 2008 03:37:16 GMT (envelope-from lstewart@svn.freebsd.org) Received: (from lstewart@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id mAE3bGQx077480; Fri, 14 Nov 2008 03:37:16 GMT (envelope-from lstewart@svn.freebsd.org) Message-Id: <200811140337.mAE3bGQx077480@svn.freebsd.org> From: Lawrence Stewart Date: Fri, 14 Nov 2008 03:37:16 +0000 (UTC) To: src-committers@freebsd.org, svn-src-user@freebsd.org X-SVN-Group: user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r184953 - in user/lstewart/dummynet_8.x: sbin/ipfw sys/netinet X-BeenThere: svn-src-user@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the experimental " user" src tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 14 Nov 2008 03:37:16 -0000 Author: lstewart Date: Fri Nov 14 03:37:15 2008 New Revision: 184953 URL: http://svn.freebsd.org/changeset/base/184953 Log: Import the DPDv2 patch (dpdv2_r560_8.x.r183456.patch) unchanged Modified: user/lstewart/dummynet_8.x/sbin/ipfw/ipfw.8 user/lstewart/dummynet_8.x/sbin/ipfw/ipfw2.c user/lstewart/dummynet_8.x/sys/netinet/in.h user/lstewart/dummynet_8.x/sys/netinet/ip_dummynet.c user/lstewart/dummynet_8.x/sys/netinet/ip_dummynet.h user/lstewart/dummynet_8.x/sys/netinet/raw_ip.c Modified: user/lstewart/dummynet_8.x/sbin/ipfw/ipfw.8 ============================================================================== --- user/lstewart/dummynet_8.x/sbin/ipfw/ipfw.8 Fri Nov 14 02:11:57 2008 (r184952) +++ user/lstewart/dummynet_8.x/sbin/ipfw/ipfw.8 Fri Nov 14 03:37:15 2008 (r184953) @@ -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 @@ is a floating-point number between 0 and 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 Modified: user/lstewart/dummynet_8.x/sbin/ipfw/ipfw2.c ============================================================================== --- user/lstewart/dummynet_8.x/sbin/ipfw/ipfw2.c Fri Nov 14 02:11:57 2008 (r184952) +++ user/lstewart/dummynet_8.x/sbin/ipfw/ipfw2.c Fri Nov 14 03:37:15 2008 (r184953) @@ -83,6 +83,9 @@ int 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 @@ int 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 @@ enum tokens { TOK_COMMENT, TOK_PLR, + TOK_PLS, TOK_NOERROR, TOK_BUCKETS, TOK_DSTIP, @@ -348,6 +356,7 @@ enum tokens { 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 @@ _substrcmp2(const char *str1, const char } /* + * 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 @@ print_flowset_parms(struct dn_flow_set * { int l; char qs[30]; - char plr[30]; + char pl[30]; char red[90]; /* Display RED parameters */ l = fs->qsize; @@ -2266,9 +2420,13 @@ print_flowset_parms(struct dn_flow_set * } 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 @@ print_flowset_parms(struct dn_flow_set * 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 @@ list_pipes(void *data, uint nbytes, int /* * 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 @@ list_pipes(void *data, uint nbytes, int 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,11 +4281,21 @@ config_pipe(int ac, char **av) 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' || @@ -4374,6 +4552,9 @@ end_mask: 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 @@ done: } 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 @@ zero(int ac, char *av[], int optname /* 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); @@ -6351,6 +6548,8 @@ ipfw_main(int oldac, char **oldav) 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) Modified: user/lstewart/dummynet_8.x/sys/netinet/in.h ============================================================================== --- user/lstewart/dummynet_8.x/sys/netinet/in.h Fri Nov 14 02:11:57 2008 (r184952) +++ user/lstewart/dummynet_8.x/sys/netinet/in.h Fri Nov 14 03:37:15 2008 (r184953) @@ -464,10 +464,11 @@ __END_DECLS #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 */ Modified: user/lstewart/dummynet_8.x/sys/netinet/ip_dummynet.c ============================================================================== --- user/lstewart/dummynet_8.x/sys/netinet/ip_dummynet.c Fri Nov 14 02:11:57 2008 (r184952) +++ user/lstewart/dummynet_8.x/sys/netinet/ip_dummynet.c Fri Nov 14 03:37:15 2008 (r184953) @@ -241,6 +241,10 @@ void dummynet_drain(void); 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 @@ dummynet_io(struct mbuf **m0, int dir, s 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 @@ purge_flow_set(struct dn_flow_set *fs, i 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 @@ alloc_hash(struct dn_flow_set *x, struct 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 @@ delete_pipe(struct dn_pipe *p) } /* + * 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 @@ dn_calc_size(void) */ 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 @@ ip_dn_ctl(struct sockopt *sopt) 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 ; } Modified: user/lstewart/dummynet_8.x/sys/netinet/ip_dummynet.h ============================================================================== --- user/lstewart/dummynet_8.x/sys/netinet/ip_dummynet.h Fri Nov 14 02:11:57 2008 (r184952) +++ user/lstewart/dummynet_8.x/sys/netinet/ip_dummynet.h Fri Nov 14 03:37:15 2008 (r184953) @@ -230,6 +230,17 @@ struct dn_flow_queue { */ } ; +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 @@ struct dn_flow_set { 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 ; Modified: user/lstewart/dummynet_8.x/sys/netinet/raw_ip.c ============================================================================== --- user/lstewart/dummynet_8.x/sys/netinet/raw_ip.c Fri Nov 14 02:11:57 2008 (r184952) +++ user/lstewart/dummynet_8.x/sys/netinet/raw_ip.c Fri Nov 14 03:37:15 2008 (r184953) @@ -534,6 +534,7 @@ rip_ctloutput(struct socket *so, struct 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