Hello, For several releases I used a similar patch for altq to crank up the bandwidth of my uplink traffic shaper without compromising its reliability with varying packet sizes over an ATM DSL link. Now that newqueue is committed I took the time and prepared a proper diff with pf.conf syntax and manpage. If you want to read about why this is useful, you can read for example: https://en.wikipedia.org/wiki/Point-to-point_protocol_over_Ethernet#Protocol_overhead
For me it is useful because I have an asymmetric bandwidth of 223kbit up / 2000kbit down. Subsequently the proportion of very small TCP ack and VoIP packets on my uplink is large. Those packages are underestimated without correction for the PPPoEoA and ATM fragmentation overhead. Christopher Index: sbin/pfctl/parse.y =================================================================== RCS file: /cvs/src/sbin/pfctl/parse.y,v retrieving revision 1.631 diff -u -p -r1.631 parse.y --- sbin/pfctl/parse.y 22 Jan 2014 00:21:16 -0000 1.631 +++ sbin/pfctl/parse.y 25 Feb 2014 16:23:31 -0000 @@ -317,6 +317,9 @@ struct queue_opts { struct node_sc upperlimit; char *parent; int flags; + int16_t overhead; + u_int16_t payload; + u_int16_t size; u_int qlimit; } queue_opts; @@ -481,8 +484,8 @@ int parseport(char *, struct range *r, i %token BITMASK RANDOM SOURCEHASH ROUNDROBIN LEASTSTATES STATICPORT PROBABILITY %token WEIGHT %token ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT -%token QUEUE OLDQUEUE PRIORITY QLIMIT RTABLE RDOMAIN MINIMUM BURST PARENT -%token LOAD RULESET_OPTIMIZATION RTABLE RDOMAIN PRIO ONCE DEFAULT +%token QUANTIZE QUEUE OLDQUEUE PRIORITY QLIMIT RTABLE RDOMAIN MINIMUM BURST +%token PARENT LOAD RULESET_OPTIMIZATION RTABLE RDOMAIN PRIO ONCE DEFAULT %token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE %token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW %token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE @@ -1366,6 +1369,16 @@ queue_opt : BANDWIDTH scspec optscs { queue_opts.marker |= QOM_QLIMIT; queue_opts.qlimit = $2; } + | QUANTIZE '(' NUMBER comma NUMBER comma NUMBER ')' { + if (queue_opts.flags & HFSC_QUANTIZE) { + yyerror("quantize cannot be respecified"); + YYERROR; + } + queue_opts.flags |= HFSC_QUANTIZE; + queue_opts.overhead = $3; + queue_opts.payload = $5; + queue_opts.size = $7; + } ; optscs : /* nada */ { @@ -4745,6 +4758,12 @@ expand_queue(char *qname, struct node_if qspec.flags = opts->flags; qspec.qlimit = opts->qlimit; + if (qspec.flags & HFSC_QUANTIZE) { + qspec.overhead = opts->overhead; + qspec.payload = opts->payload; + qspec.size = opts->size; + } + if (pfctl_add_queue(pf, &qspec)) { yyerror("cannot add queue"); return (1); @@ -5484,6 +5503,7 @@ lookup(char *s) { "probability", PROBABILITY}, { "proto", PROTO}, { "qlimit", QLIMIT}, + { "quantize", QUANTIZE}, { "queue", QUEUE}, { "quick", QUICK}, { "random", RANDOM}, Index: share/man/man5/pf.conf.5 =================================================================== RCS file: /cvs/src/share/man/man5/pf.conf.5,v retrieving revision 1.536 diff -u -p -r1.536 pf.conf.5 --- share/man/man5/pf.conf.5 21 Jan 2014 03:15:46 -0000 1.536 +++ share/man/man5/pf.conf.5 25 Feb 2014 16:23:35 -0000 @@ -1501,6 +1501,29 @@ The parent queue must exist. .It Ar qlimit Aq Ar limit The maximum number of packets held in the queue. The default is 50. +.It Xo Ar quantize +.No ( Aq Ar overhead , +.Aq Ar payload , +.Aq Ar size ) +.Xc +This option is useful for traffic shaping on ATM links +like DSL or cable modems. +With this option it is counted how many units (ATM cells) of size +.Ar payload +(48 bytes for ATM) are needed to fit the packet size and +.Ar overhead . +The number of units is then multiplied with +.Ar size +(53 bytes for ATM). The new size is calculated using this formula: +( original size + +.Ar overhead ++ +.Ar payload +- 1 ) / +.Ar payload +* +.Ar size +where '/' is integer division. .El .Pp Packets can be assigned to queues based on filter rules by using the Index: sys/net/hfsc.c =================================================================== RCS file: /cvs/src/sys/net/hfsc.c,v retrieving revision 1.8 diff -u -p -r1.8 hfsc.c --- sys/net/hfsc.c 27 Jan 2014 15:41:06 -0000 1.8 +++ sys/net/hfsc.c 25 Feb 2014 16:23:38 -0000 @@ -66,8 +66,9 @@ */ struct hfsc_class *hfsc_class_create(struct hfsc_if *, struct hfsc_sc *, struct hfsc_sc *, - struct hfsc_sc *, struct hfsc_class *, int, - int, int); + struct hfsc_sc *, + int16_t, u_int16_t, u_int16_t, + struct hfsc_class *, int, int, int); int hfsc_class_destroy(struct hfsc_class *); struct hfsc_class *hfsc_nextclass(struct hfsc_class *); @@ -227,6 +228,7 @@ hfsc_addqueue(struct pf_queuespec *q) ulsc.m2 = q->upperlimit.m2.absolute; cl = hfsc_class_create(hif, &rtsc, &lssc, &ulsc, + q->overhead, q->payload, q->size, parent, q->qlimit, q->flags, q->qid); if (cl == NULL) return (ENOMEM); @@ -288,8 +290,9 @@ hfsc_purge(struct ifqueue *ifq) struct hfsc_class * hfsc_class_create(struct hfsc_if *hif, struct hfsc_sc *rsc, - struct hfsc_sc *fsc, struct hfsc_sc *usc, struct hfsc_class *parent, - int qlimit, int flags, int qid) + struct hfsc_sc *fsc, struct hfsc_sc *usc, + int16_t overhead, u_int16_t payload, u_int16_t size, + struct hfsc_class *parent, int qlimit, int flags, int qid) { struct hfsc_class *cl, *p; int i, s; @@ -364,6 +367,17 @@ hfsc_class_create(struct hfsc_if *hif, s cl->cl_hif = hif; cl->cl_parent = parent; + if (flags & HFSC_QUANTIZE) { + cl->cl_overhead = overhead; + cl->cl_payload = payload; + cl->cl_size = size; + } + else if (parent != NULL) { + cl->cl_overhead = parent->cl_overhead; + cl->cl_payload = parent->cl_payload; + cl->cl_size = parent->cl_size; + } + s = splnet(); hif->hif_classes++; @@ -533,7 +547,7 @@ hfsc_enqueue(struct ifqueue *ifq, struct if (hfsc_addq(cl, m) != 0) { /* drop occurred. mbuf needs to be freed */ - PKTCNTR_INC(&cl->cl_stats.drop_cnt, m->m_pkthdr.len); + PKTCNTR_INC(&cl->cl_stats.drop_cnt, HFSC_PKG_COST(cl, m)); m_freem(m); return (ENOBUFS); } @@ -543,7 +557,7 @@ hfsc_enqueue(struct ifqueue *ifq, struct /* successfully queued. */ if (cl->cl_q->qlen == 1) - hfsc_set_active(cl, m->m_pkthdr.len); + hfsc_set_active(cl, HFSC_PKG_COST(cl, m)); return (0); } @@ -622,16 +636,16 @@ hfsc_dequeue(struct ifqueue *ifq, int re cl->cl_hif->hif_packets--; IFQ_DEC_LEN(ifq); - PKTCNTR_INC(&cl->cl_stats.xmit_cnt, m->m_pkthdr.len); + PKTCNTR_INC(&cl->cl_stats.xmit_cnt, HFSC_PKG_COST(cl, m)); - hfsc_update_vf(cl, m->m_pkthdr.len, cur_time); + hfsc_update_vf(cl, HFSC_PKG_COST(cl, m), cur_time); if (realtime) - cl->cl_cumul += m->m_pkthdr.len; + cl->cl_cumul += HFSC_PKG_COST(cl, m); if (cl->cl_q->qlen > 0) { if (cl->cl_rsc != NULL) { /* update ed */ - next_len = cl->cl_q->tail->m_nextpkt->m_pkthdr.len; + next_len = HFSC_PKG_COST(cl, cl->cl_q->tail->m_nextpkt); if (realtime) hfsc_update_ed(cl, next_len); @@ -717,7 +731,7 @@ hfsc_purgeq(struct hfsc_class *cl) return; while ((m = hfsc_getq(cl)) != NULL) { - PKTCNTR_INC(&cl->cl_stats.drop_cnt, m->m_pkthdr.len); + PKTCNTR_INC(&cl->cl_stats.drop_cnt, HFSC_PKG_COST(cl, m)); m_freem(m); cl->cl_hif->hif_packets--; IFQ_DEC_LEN(cl->cl_hif->hif_ifq); Index: sys/net/hfsc.h =================================================================== RCS file: /cvs/src/sys/net/hfsc.h,v retrieving revision 1.5 diff -u -p -r1.5 hfsc.h --- sys/net/hfsc.h 27 Jan 2014 15:41:06 -0000 1.5 +++ sys/net/hfsc.h 25 Feb 2014 16:23:38 -0000 @@ -39,6 +39,7 @@ #define HFSC_RED 0x0001 /* use RED */ #define HFSC_ECN 0x0002 /* use RED/ECN */ #define HFSC_RIO 0x0004 /* use RIO */ +#define HFSC_QUANTIZE 0x0008 /* use quantize */ #define HFSC_DEFAULTCLASS 0x1000 /* default class */ struct hfsc_pktcntr { @@ -222,12 +223,25 @@ struct hfsc_class { hfsc_actentry_t cl_actlist; /* active children list entry */ hfsc_elentry_t cl_ellist; /* eligible list entry */ + /* quantize */ + int16_t cl_overhead; /* overhead per packet */ + u_int16_t cl_payload; /* payload per quant */ + u_int16_t cl_size; /* total quant size */ + struct { struct hfsc_pktcntr xmit_cnt; struct hfsc_pktcntr drop_cnt; u_int period; } cl_stats; }; + +#define HFSC_PKG_COST(cl,m) ( \ + (cl)->cl_payload ? \ + ( (m)->m_pkthdr.len + (cl)->cl_overhead + (cl)->cl_payload - 1) \ + / (cl)->cl_payload * (cl)->cl_size \ + : \ + (m)->m_pkthdr.len \ +) /* * hfsc interface state Index: sys/net/pfvar.h =================================================================== RCS file: /cvs/src/sys/net/pfvar.h,v retrieving revision 1.397 diff -u -p -r1.397 pfvar.h --- sys/net/pfvar.h 21 Jan 2014 01:50:07 -0000 1.397 +++ sys/net/pfvar.h 25 Feb 2014 16:23:40 -0000 @@ -1449,6 +1449,9 @@ struct pf_queuespec { u_int qlimit; u_int32_t qid; u_int32_t parent_qid; + int16_t overhead; + u_int16_t payload; + u_int16_t size; }; struct cbq_opts { ################################################################# ################################################################# ################################################################# Here's a small UDP flood sender/receiver program I used for finding the right overhead for my PPPoEoA connection: #include<stdlib.h> #include<stdio.h> #include<string.h> #include<err.h> #include<assert.h> #include<unistd.h> #include<sys/time.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include<netdb.h> int main(int argc, char *argv[]) { int sock; struct hostent *host; struct sockaddr_in addr; uint16_t port; struct in_addr in_addr; char *ipaddress; uint8_t buf[1500]; uint32_t length=200; int32_t intervall=1000000; uint32_t num=1000000; uint8_t bitmap[(num+7)/8]; int listen = 0; char* progname = argv[0]; argv++; argc--; while(argc > 0 && argv[0][0] == '-') { assert(argv[0][1] != 0 && argv[0][2] == 0); switch(argv[0][1]) { case 'l': listen = 1; break; case 'f': intervall = 1000000 / atol(argv[1]); argv++; argc--; break; case 'n': num = atol(argv[1]); argv++; argc--; break; case 's': length = atol(argv[1]); argv++; argc--; break; default: errx(1, "Usage: %s [-l] [-s size] [-n num] [-f frequency] host port\n", progname); } argv++; argc--; } if(argc != 2) errx(1, "Usage: %s [-l] [-s size] [-n num] [-f frequency] host port\n", progname); memset(buf, 0, sizeof(buf)); memset(bitmap, 0, sizeof(bitmap)); port = atoi(argv[1]); host = gethostbyname(argv[0]); if(host == NULL) switch(h_errno) { case HOST_NOT_FOUND: errx(1, "gethostbyname: Host not found"); break; case TRY_AGAIN: errx(1, "gethostbyname: Try again"); break; default: errx(1, "gethostbyname failed with %d", h_errno); } if(host->h_addr_list == NULL || host->h_addr_list[0] == NULL) errx(1, "No or empty address returnded by gethostbyname"); if(host->h_addrtype != AF_INET) errx(1, "Address has wrong address type of %d", host->h_addrtype); in_addr = *(struct in_addr *)host->h_addr_list[0]; ipaddress = (char *)inet_ntoa(in_addr); //addr.sin_len = sizeof(addr); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr = in_addr; sock = socket(AF_INET, SOCK_DGRAM, 0); if(listen) { uint32_t seccount = 0, totalcount = 0, expect = 0; struct timeval timeval; long cursec, lastsec = 0, totalsec = 0; bind(sock, (struct sockaddr *)&addr, sizeof(addr)); fprintf(stderr, "Listening %s on port %hu \n", ipaddress, port); while(1) { uint32_t n; recv(sock, buf, sizeof(buf), 0); n = ntohl(((uint32_t *)buf)[0]); num = ntohl(((uint32_t *)buf)[1]); if(bitmap[n/8] & 1 << n%8){ fprintf(stderr, "Received dup #%u.\n", n); break; } else { totalcount++; seccount++; if(n < expect) fprintf(stderr, "Received late datagram #%u. Count: %u\n", n, totalcount); #if 0 else if(n > expect) fprintf(stderr, "Received early datagram #%u. Count: %u\n", n, totalcount); else fprintf(stderr, "Received datagram #%u. Count: %u\n", n, totalcount); #endif expect = n + 1; } bitmap[n/8] |= 1 << n%8; if(gettimeofday(&timeval, NULL)) err(1, "gettimeofday failed"); cursec = timeval.tv_sec; if(lastsec == 0) lastsec = cursec; if(cursec > lastsec) { totalsec += cursec - lastsec; fprintf(stderr, "%lds #%u: %u pkts this sec, %lu pkts/s average\n", totalsec, n, seccount, totalcount / totalsec); seccount = 0; lastsec = cursec; } if(n == num-1) break; } fprintf(stderr, "Received %u of %u datagrams in %lds. %lf pkts/s average\n", totalcount, num, totalsec, (double)totalcount/(double)totalsec); } else { struct timeval cur_time, next_time; int32_t timediff; fprintf(stderr, "Flooding %s on port %hu \n", ipaddress, port); connect(sock, (struct sockaddr *)&addr, sizeof(addr)); if(gettimeofday(&next_time, NULL)) err(1, "gettimeofday failed"); ((uint32_t *)buf)[1] = htonl(num); for(uint32_t n=0; n < num;) { if(gettimeofday(&cur_time, NULL)) err(1, "gettimeofday failed"); timediff = (cur_time.tv_sec - next_time.tv_sec) * 1000000; timediff += cur_time.tv_usec - next_time.tv_usec; if(timediff >= 0) { ((uint32_t *)buf)[0] = htonl(n++); send(sock, buf, length, 0); next_time.tv_usec += intervall; next_time.tv_sec += next_time.tv_usec / 1000000; next_time.tv_usec %= 1000000; if(intervall - timediff > 0) usleep(intervall - timediff); } } } return 0; } -- http://gmerlin.de OpenPGP: http://gmerlin.de/christopher.pub F190 D013 8F01 AA53 E080 3F3C F17F B0A1 D44E 4FEE
signature.asc
Description: PGP signature