Index: sbin/ipfw/dummynet.c =================================================================== --- sbin/ipfw/dummynet.c +++ sbin/ipfw/dummynet.c @@ -740,6 +740,18 @@ * to the chosen format. * The unit for delay is milliseconds. * + * channel_bytes N + * If non-zero, when we insert a CDT delay allow + * up to N bytes to be transmitted before another + * CDT delay is inserted. + * + * clear_on_empty + * If we are using channel_bytes N, if the queue goes empty + * clear out the count tracking the bytes so the next + * new packet arrival will cause a CDT delay (i.e. no + * hold over of the previous bytes allowed to send on + * the channel). + * * Data points does not need to be ordered or equal to the number * specified in the "samples" line. ipfw will sort and interpolate * the curve as needed. @@ -749,6 +761,8 @@ name bla_bla_bla samples 100 loss-level 0.86 + channel_bytes 1500 + clear_on_empty 1 prob delay 0 200 # minimum overhead is 200ms 0.5 200 @@ -759,12 +773,19 @@ * Internally, we will convert the curve to a fixed number of * samples, and when it is time to transmit a packet we will - * model the extra delay as extra bits in the packet. - * + * model the extra delay as extra bits in the packet. If the + * channel_bytes feature is used once a delay is inserted, any other + * queued packets will go out without a further delay. clear_on_empty + * is also a channel_bytes feature, which clears any memory of + * past bytes allowed to go out if set to 1 (so another CDT will occur). + * If set to zero then channel_bytes causes us to skip CDT until at + * least that many bytes have been sent. */ #define ED_MAX_LINE_LEN 256+ED_MAX_NAME_LEN #define ED_TOK_SAMPLES "samples" +#define ED_TOK_CHANNEL "channel_bytes" +#define ED_TOK_CLR_EMP "clear_on_empty" #define ED_TOK_LOSS "loss-level" #define ED_TOK_NAME "name" #define ED_TOK_DELAY "delay" @@ -884,6 +905,8 @@ p->link_nr = link->link_nr; profile_name[0] = '\0'; + p->clear_cb_on_empty = 0; + p->channel_bytes = p->bytes_used = 0; f = fopen(filename, "r"); if (f == NULL) err(EX_UNAVAILABLE, "fopen: %s", filename); @@ -922,6 +945,12 @@ errx(ED_EFMT("too many samples, maximum is %d"), ED_MAX_SAMPLES_NO); do_points = 0; + } else if (!strcasecmp(name, ED_TOK_CLR_EMP)) { + p->clear_cb_on_empty = 1; + } else if (!strcasecmp(name, ED_TOK_CHANNEL)) { + if (atoi(arg) < 0) + errx(ED_EFMT("invalid channel bytes")); + p->channel_bytes = atoi(arg); } else if (!strcasecmp(name, ED_TOK_BW)) { char buf[IFNAMSIZ]; read_bandwidth(arg, &link->bandwidth, buf, sizeof(buf)); Index: sbin/ipfw/ipfw.8 =================================================================== --- sbin/ipfw/ipfw.8 +++ sbin/ipfw/ipfw.8 @@ -2607,6 +2607,13 @@ .It Cm samples Ar N the number of samples used in the internal representation of the curve (2..1024; default 100); +.It Cm channel_bytes Ar N +the number of bytes that can be sent without incurring +an added delay. +.It Cm clear_on_empty Ar bool +if using +.Nm channel_bytes +do we clear the channel byte count when the queue goes empty? .It Cm "delay prob" | "prob delay" One of these two lines is mandatory and defines the format of the following lines with data points. Index: sys/netinet/ip_dummynet.h =================================================================== --- sys/netinet/ip_dummynet.h +++ sys/netinet/ip_dummynet.h @@ -216,7 +216,10 @@ char name[ED_MAX_NAME_LEN]; int link_nr; int loss_level; - int bandwidth; // XXX use link bandwidth? + int bandwidth; // XXX use link bandwidth? + int channel_bytes; /* If non-zero how much at CDT can be sent */ + int bytes_used; /* Bytes currently sent since on channel */ + int clear_cb_on_empty; /* Clear the bytes_used on empty queue */ int samples_no; /* actual len of samples[] */ int samples[ED_MAX_SAMPLES_NO]; /* may be shorter */ }; Index: sys/netpfil/ipfw/ip_dn_io.c =================================================================== --- sys/netpfil/ipfw/ip_dn_io.c +++ sys/netpfil/ipfw/ip_dn_io.c @@ -574,7 +574,7 @@ * in milliseconds so we need to divide by 1000. */ static uint64_t -extra_bits(struct mbuf *m, struct dn_schk *s) +extra_bits(struct mbuf *m, struct dn_schk *s, uint32_t pkt_len) { int index; uint64_t bits; @@ -582,12 +582,20 @@ if (!pf || pf->samples_no == 0) return 0; - index = random() % pf->samples_no; - bits = div64((uint64_t)pf->samples[index] * s->link.bandwidth, 1000); - if (index >= pf->loss_level) { - struct dn_pkt_tag *dt = dn_tag_get(m); - if (dt) - dt->dn_dir = DIR_DROP; + if (pf->bytes_used == 0) { + index = random() % pf->samples_no; + bits = div64((uint64_t)pf->samples[index] * s->link.bandwidth, 1000); + if (index >= pf->loss_level) { + struct dn_pkt_tag *dt = dn_tag_get(m); + if (dt) + dt->dn_dir = DIR_DROP; + } + } else + bits = 0; + if (pf->channel_bytes) { + pf->bytes_used += pkt_len; + if (pf->bytes_used >= pf->channel_bytes) + pf->bytes_used = 0; } return bits; } @@ -624,13 +632,12 @@ done++; len_scaled = (bw == 0) ? 0 : hz * - (m->m_pkthdr.len * 8 + extra_bits(m, s)); + (m->m_pkthdr.len * 8 + extra_bits(m, s, m->m_pkthdr.len)); si->credit -= len_scaled; /* Move packet in the delay line */ dn_tag_get(m)->output_time = dn_cfg.curr_time + s->link.delay ; mq_append(&si->dline.mq, m); } - /* * If credit >= 0 the instance is idle, mark time. * Otherwise put back in the heap, and adjust the output @@ -649,6 +656,15 @@ } if (delay_line_idle && done) transmit_event(q, &si->dline, now); + if (s->profile && + s->profile->clear_cb_on_empty && + (si->dline.mq.head == NULL) && + (si->ni.len_bytes == 0)) { + /* + * The queue & delay line's are empty. + */ + s->profile->bytes_used = 0; + } return q->head; } @@ -902,6 +918,15 @@ if (q == NULL) goto dropit; } + if (si->sched->profile && + si->sched->profile->clear_cb_on_empty && + (si->dline.mq.head == NULL) && + (si->ni.len_bytes == 0)) { + /* + * The queue & delay line's are empty. + */ + si->sched->profile->bytes_used = 0; + } if (fs->sched->fp->enqueue(si, q, m)) { /* packet was dropped by enqueue() */ m = *m0 = NULL;