diff --git a/sbin/ipfw/dummynet.c b/sbin/ipfw/dummynet.c index 20c563b..2d8de87 100644 --- a/sbin/ipfw/dummynet.c +++ b/sbin/ipfw/dummynet.c @@ -53,6 +53,8 @@ static struct _s_x dummynet_params[] = { { "plr", TOK_PLR }, + { "pls", TOK_PLS }, + { "plsr", TOK_PLSR }, { "noerror", TOK_NOERROR }, { "buckets", TOK_BUCKETS }, { "dst-ip", TOK_DSTIP }, @@ -474,6 +476,7 @@ print_flowset_parms(struct dn_fs *fs, char *prefix) char qs[30]; char plr[30]; char red[200]; /* Display RED parameters */ + int i; l = fs->qsize; if (fs->flags & DN_QSIZE_BYTES) { @@ -518,6 +521,21 @@ print_flowset_parms(struct dn_fs *fs, char *prefix) if (fs->flags & DN_HAVE_MASK) print_mask(&fs->flow_mask); } + if (fs->pls[0]) { + printf("pls "); + for (i = 0; i < 10; i++) { + if (fs->pls[i] != 0) + printf("%d", fs->pls[i]); + else + break; + if (fs->pls[i+1] != 0) + printf(","); + } + if (fs->plsr > 0) + printf (" plsr %d", fs->plsr); + + printf("\n"); + } } static void @@ -1416,6 +1434,34 @@ ipfw_config_pipe(int ac, char **av) ac--; av++; break; + case TOK_PLS: + NEED(fs, "pls is only for pipes"); + int i = 0; + char *ppls = av[0]; + char *token; + + while (ppls != NULL) { + token = strsep(&ppls, ","); + if (token != NULL) { + fs->pls[i] = strtod(token, NULL); + } else + ppls = NULL; + i++; + } + ac--; av++; + break; + + case TOK_PLSR: + NEED(fs, "plsr is only for pipes"); + NEED1("plsr needs to be 1 if specified\n"); + d = strtod(av[0], NULL); + if (d < 0) + d = 0; + fs->plsr = d; + ac--; av++; + break; + + case TOK_QUEUE: NEED(fs, "queue is only for pipes or flowsets"); NEED1("queue needs queue size\n"); diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h index 0b63e3b..a73b4a1 100644 --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -152,6 +152,8 @@ enum tokens { TOK_COMMENT, TOK_PLR, + TOK_PLS, + TOK_PLSR, TOK_NOERROR, TOK_BUCKETS, TOK_DSTIP, diff --git a/sys/netinet/ip_dummynet.h b/sys/netinet/ip_dummynet.h index 377b5b0..f442d88 100644 --- a/sys/netinet/ip_dummynet.h +++ b/sys/netinet/ip_dummynet.h @@ -146,6 +146,8 @@ struct dn_fs { uint32_t flags; /* userland flags */ int qsize; /* queue size in slots or bytes */ int32_t plr; /* PLR, pkt loss rate (2^31-1 means 100%) */ + int pls[10]; /* PLS, pkt loss sequence (list of packets to drop) */ + int plsr; /* repeat the pls pkt sequence */ uint32_t buckets; /* buckets used for the queue hash table */ struct ipfw_flow_id flow_mask; @@ -180,6 +182,8 @@ struct dn_flow { struct dn_id oid; struct ipfw_flow_id fid; uint64_t tot_pkts; /* statistics counters */ + uint64_t tot_in_pkts; /* statistics counters */ + uint64_t tot_out_pkts; /* statistics counters */ uint64_t tot_bytes; uint32_t length; /* Queue length, in packets */ uint32_t len_bytes; /* Queue length, in bytes */ diff --git a/sys/netpfil/ipfw/ip_dn_glue.c b/sys/netpfil/ipfw/ip_dn_glue.c index 4c4659a..b396160 100644 --- a/sys/netpfil/ipfw/ip_dn_glue.c +++ b/sys/netpfil/ipfw/ip_dn_glue.c @@ -95,6 +95,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[10]; /* pkt loss sequence */ + int plsr; /* pkt loss sequence on repeat or not */ struct ipfw_flow_id flow_mask ; @@ -422,6 +424,14 @@ dn_compat_config_queue(struct dn_fs *fs, void* v) fs->buckets = f->rq_size; fs->qsize = f->qsize; fs->plr = f->plr; + /* pls assignmment */ + int i = 0; + for (i = 0; i < 10; i++) { + if (f->pls[i] == 0) + break; + fs->pls[i] = f->pls[i]; + } + fs->plsr = f->plsr; fs->par[0] = f->weight; fs->flags = convertflags2new(f->flags_fs); if (fs->flags & DN_IS_GENTLE_RED || fs->flags & DN_IS_RED) { @@ -647,6 +657,14 @@ dn_c_copy_pipe(struct dn_schk *s, struct copy_args *a, int nq) fs->parent_nr = l->link_nr - DN_MAX_ID; fs->qsize = f->fs.qsize; fs->plr = f->fs.plr; + /* pls assignmment */ + int i = 0; + for (i = 0; i < 10; i++) { + if (f->fs.pls[i] == 0) + break; + fs->pls[i] = f->fs.pls[i]; + } + fs->plsr = f->fs.plsr; fs->w_q = f->fs.w_q; fs->max_th = f->max_th; fs->min_th = f->min_th; @@ -701,6 +719,14 @@ dn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq) fs->fs_nr = f->fs.fs_nr; fs->qsize = f->fs.qsize; fs->plr = f->fs.plr; + /* pls assignmment */ + int i = 0; + for (i = 0; i < 10; i++) { + if (f->fs.pls[i] == 0) + break; + fs->pls[i] = f->fs.pls[i]; + } + fs->plsr = f->fs.plsr; fs->w_q = f->fs.w_q; fs->max_th = f->max_th; fs->min_th = f->min_th; diff --git a/sys/netpfil/ipfw/ip_dn_io.c b/sys/netpfil/ipfw/ip_dn_io.c index 831b909..cbcc6b3 100644 --- a/sys/netpfil/ipfw/ip_dn_io.c +++ b/sys/netpfil/ipfw/ip_dn_io.c @@ -508,6 +508,7 @@ dn_enqueue(struct dn_queue *q, struct mbuf* m, int drop) struct dn_fs *f; struct dn_flow *ni; /* stats for scheduler instance */ uint64_t len; + struct dn_pkt_tag *pkt = dn_tag_get(m); if (q->fs == NULL || q->_si == NULL) { printf("%s fs %p si %p, dropping\n", @@ -523,10 +524,47 @@ dn_enqueue(struct dn_queue *q, struct mbuf* m, int drop) q->ni.tot_pkts++; ni->tot_bytes += len; ni->tot_pkts++; + + /* Get pkt count in each direction */ + if (pkt->dn_dir == DIR_OUT) + q->ni.tot_out_pkts++; + if (pkt->dn_dir == DIR_IN) + q->ni.tot_in_pkts++; + if (drop) goto drop; if (f->plr && random() < f->plr) goto drop; + + /* + * pls : pkt loss sequence which can be specified with + * "ipfw pipe config pls plsr + * " + * For example: "ipfw pipe 100 config pls 4,5 plsr 7" - to drop 4th and + * 5th packet and repeat this every 7 packets. i.e. drop 11th, 12th, + * 18th, 19th and so on. + * plsr is optional. + */ + if (f->pls[0]) { + int i = 0; + for (i = 0; i < 10; i++) { + if (f->pls[i] == 0) + break; + if (q->ni.tot_out_pkts > 1 && pkt->dn_dir == DIR_OUT) { + if (f->plsr > 0) { + /* + * repeat is on, use module to find pkts + * to drop + */ + if (q->ni.tot_out_pkts % f->plsr == + f->pls[i] % f->plsr) + goto drop; + } else if (q->ni.tot_out_pkts == f->pls[i]) + goto drop; + } + } + } + #ifdef NEW_AQM /* Call AQM enqueue function */ if (q->fs->aqmfp)