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

Attachment: signature.asc
Description: PGP signature

Reply via email to