source: rtems-libbsd/freebsd/contrib/pf/pfctl/pfctl_altq.c @ 709fbfa

4.11
Last change on this file since 709fbfa was 4831944, checked in by Christian Mauderer <Christian.Mauderer@…>, on 07/05/16 at 14:31:43

pfctl: Adapt for RTEMS.

  • Property mode set to 100644
File size: 30.8 KB
Line 
1#include <machine/rtems-bsd-user-space.h>
2
3/*      $OpenBSD: pfctl_altq.c,v 1.93 2007/10/15 02:16:35 deraadt Exp $ */
4
5/*
6 * Copyright (c) 2002
7 *      Sony Computer Science Laboratories Inc.
8 * Copyright (c) 2002, 2003 Henning Brauer <henning@openbsd.org>
9 *
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#ifdef __rtems__
24#include <machine/rtems-bsd-program.h>
25#endif /* __rtems__ */
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD$");
28
29#include <rtems/bsd/sys/types.h>
30#include <sys/ioctl.h>
31#include <sys/socket.h>
32
33#include <net/if.h>
34#include <netinet/in.h>
35#include <net/pfvar.h>
36
37#include <err.h>
38#include <errno.h>
39#include <limits.h>
40#include <math.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
45
46#include <altq/altq.h>
47#include <altq/altq_cbq.h>
48#include <altq/altq_priq.h>
49#include <altq/altq_hfsc.h>
50
51#include "pfctl_parser.h"
52#include "pfctl.h"
53
54#define is_sc_null(sc)  (((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0))
55
56#ifndef __rtems__
57TAILQ_HEAD(altqs, pf_altq) altqs = TAILQ_HEAD_INITIALIZER(altqs);
58LIST_HEAD(gen_sc, segment) rtsc, lssc;
59#else /* __rtems__ */
60static TAILQ_HEAD(altqs, pf_altq) altqs = TAILQ_HEAD_INITIALIZER(altqs);
61static LIST_HEAD(gen_sc, segment) rtsc, lssc;
62#endif /* __rtems__ */
63
64struct pf_altq  *qname_to_pfaltq(const char *, const char *);
65u_int32_t        qname_to_qid(const char *);
66
67static int      eval_pfqueue_cbq(struct pfctl *, struct pf_altq *);
68static int      cbq_compute_idletime(struct pfctl *, struct pf_altq *);
69static int      check_commit_cbq(int, int, struct pf_altq *);
70static int      print_cbq_opts(const struct pf_altq *);
71
72static int      eval_pfqueue_priq(struct pfctl *, struct pf_altq *);
73static int      check_commit_priq(int, int, struct pf_altq *);
74static int      print_priq_opts(const struct pf_altq *);
75
76static int      eval_pfqueue_hfsc(struct pfctl *, struct pf_altq *);
77static int      check_commit_hfsc(int, int, struct pf_altq *);
78static int      print_hfsc_opts(const struct pf_altq *,
79                    const struct node_queue_opt *);
80
81static void              gsc_add_sc(struct gen_sc *, struct service_curve *);
82static int               is_gsc_under_sc(struct gen_sc *,
83                             struct service_curve *);
84static void              gsc_destroy(struct gen_sc *);
85static struct segment   *gsc_getentry(struct gen_sc *, double);
86static int               gsc_add_seg(struct gen_sc *, double, double, double,
87                             double);
88static double            sc_x2y(struct service_curve *, double);
89
90#ifdef __FreeBSD__
91u_int32_t       getifspeed(int, char *);
92#else
93u_int32_t        getifspeed(char *);
94#endif
95u_long           getifmtu(char *);
96int              eval_queue_opts(struct pf_altq *, struct node_queue_opt *,
97                     u_int32_t);
98u_int32_t        eval_bwspec(struct node_queue_bw *, u_int32_t);
99void             print_hfsc_sc(const char *, u_int, u_int, u_int,
100                     const struct node_hfsc_sc *);
101
102void
103pfaltq_store(struct pf_altq *a)
104{
105        struct pf_altq  *altq;
106
107        if ((altq = malloc(sizeof(*altq))) == NULL)
108                err(1, "malloc");
109        memcpy(altq, a, sizeof(struct pf_altq));
110        TAILQ_INSERT_TAIL(&altqs, altq, entries);
111}
112
113struct pf_altq *
114pfaltq_lookup(const char *ifname)
115{
116        struct pf_altq  *altq;
117
118        TAILQ_FOREACH(altq, &altqs, entries) {
119                if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 &&
120                    altq->qname[0] == 0)
121                        return (altq);
122        }
123        return (NULL);
124}
125
126struct pf_altq *
127qname_to_pfaltq(const char *qname, const char *ifname)
128{
129        struct pf_altq  *altq;
130
131        TAILQ_FOREACH(altq, &altqs, entries) {
132                if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 &&
133                    strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0)
134                        return (altq);
135        }
136        return (NULL);
137}
138
139u_int32_t
140qname_to_qid(const char *qname)
141{
142        struct pf_altq  *altq;
143
144        /*
145         * We guarantee that same named queues on different interfaces
146         * have the same qid, so we do NOT need to limit matching on
147         * one interface!
148         */
149
150        TAILQ_FOREACH(altq, &altqs, entries) {
151                if (strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0)
152                        return (altq->qid);
153        }
154        return (0);
155}
156
157void
158print_altq(const struct pf_altq *a, unsigned int level,
159    struct node_queue_bw *bw, struct node_queue_opt *qopts)
160{
161        if (a->qname[0] != 0) {
162                print_queue(a, level, bw, 1, qopts);
163                return;
164        }
165
166#ifdef __FreeBSD__
167        if (a->local_flags & PFALTQ_FLAG_IF_REMOVED)
168                printf("INACTIVE ");
169#endif
170
171        printf("altq on %s ", a->ifname);
172
173        switch (a->scheduler) {
174        case ALTQT_CBQ:
175                if (!print_cbq_opts(a))
176                        printf("cbq ");
177                break;
178        case ALTQT_PRIQ:
179                if (!print_priq_opts(a))
180                        printf("priq ");
181                break;
182        case ALTQT_HFSC:
183                if (!print_hfsc_opts(a, qopts))
184                        printf("hfsc ");
185                break;
186        }
187
188        if (bw != NULL && bw->bw_percent > 0) {
189                if (bw->bw_percent < 100)
190                        printf("bandwidth %u%% ", bw->bw_percent);
191        } else
192                printf("bandwidth %s ", rate2str((double)a->ifbandwidth));
193
194        if (a->qlimit != DEFAULT_QLIMIT)
195                printf("qlimit %u ", a->qlimit);
196        printf("tbrsize %u ", a->tbrsize);
197}
198
199void
200print_queue(const struct pf_altq *a, unsigned int level,
201    struct node_queue_bw *bw, int print_interface,
202    struct node_queue_opt *qopts)
203{
204        unsigned int    i;
205
206#ifdef __FreeBSD__
207        if (a->local_flags & PFALTQ_FLAG_IF_REMOVED)
208                printf("INACTIVE ");
209#endif
210        printf("queue ");
211        for (i = 0; i < level; ++i)
212                printf(" ");
213        printf("%s ", a->qname);
214        if (print_interface)
215                printf("on %s ", a->ifname);
216        if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC) {
217                if (bw != NULL && bw->bw_percent > 0) {
218                        if (bw->bw_percent < 100)
219                                printf("bandwidth %u%% ", bw->bw_percent);
220                } else
221                        printf("bandwidth %s ", rate2str((double)a->bandwidth));
222        }
223        if (a->priority != DEFAULT_PRIORITY)
224                printf("priority %u ", a->priority);
225        if (a->qlimit != DEFAULT_QLIMIT)
226                printf("qlimit %u ", a->qlimit);
227        switch (a->scheduler) {
228        case ALTQT_CBQ:
229                print_cbq_opts(a);
230                break;
231        case ALTQT_PRIQ:
232                print_priq_opts(a);
233                break;
234        case ALTQT_HFSC:
235                print_hfsc_opts(a, qopts);
236                break;
237        }
238}
239
240/*
241 * eval_pfaltq computes the discipline parameters.
242 */
243int
244eval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,
245    struct node_queue_opt *opts)
246{
247        u_int   rate, size, errors = 0;
248
249        if (bw->bw_absolute > 0)
250                pa->ifbandwidth = bw->bw_absolute;
251        else
252#ifdef __FreeBSD__
253                if ((rate = getifspeed(pf->dev, pa->ifname)) == 0) {
254#else
255                if ((rate = getifspeed(pa->ifname)) == 0) {
256#endif
257                        fprintf(stderr, "interface %s does not know its bandwidth, "
258                            "please specify an absolute bandwidth\n",
259                            pa->ifname);
260                        errors++;
261                } else if ((pa->ifbandwidth = eval_bwspec(bw, rate)) == 0)
262                        pa->ifbandwidth = rate;
263
264        errors += eval_queue_opts(pa, opts, pa->ifbandwidth);
265
266        /* if tbrsize is not specified, use heuristics */
267        if (pa->tbrsize == 0) {
268                rate = pa->ifbandwidth;
269                if (rate <= 1 * 1000 * 1000)
270                        size = 1;
271                else if (rate <= 10 * 1000 * 1000)
272                        size = 4;
273                else if (rate <= 200 * 1000 * 1000)
274                        size = 8;
275                else
276                        size = 24;
277                size = size * getifmtu(pa->ifname);
278                if (size > 0xffff)
279                        size = 0xffff;
280                pa->tbrsize = size;
281        }
282        return (errors);
283}
284
285/*
286 * check_commit_altq does consistency check for each interface
287 */
288int
289check_commit_altq(int dev, int opts)
290{
291        struct pf_altq  *altq;
292        int              error = 0;
293
294        /* call the discipline check for each interface. */
295        TAILQ_FOREACH(altq, &altqs, entries) {
296                if (altq->qname[0] == 0) {
297                        switch (altq->scheduler) {
298                        case ALTQT_CBQ:
299                                error = check_commit_cbq(dev, opts, altq);
300                                break;
301                        case ALTQT_PRIQ:
302                                error = check_commit_priq(dev, opts, altq);
303                                break;
304                        case ALTQT_HFSC:
305                                error = check_commit_hfsc(dev, opts, altq);
306                                break;
307                        default:
308                                break;
309                        }
310                }
311        }
312        return (error);
313}
314
315/*
316 * eval_pfqueue computes the queue parameters.
317 */
318int
319eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,
320    struct node_queue_opt *opts)
321{
322        /* should be merged with expand_queue */
323        struct pf_altq  *if_pa, *parent, *altq;
324        u_int32_t        bwsum;
325        int              error = 0;
326
327        /* find the corresponding interface and copy fields used by queues */
328        if ((if_pa = pfaltq_lookup(pa->ifname)) == NULL) {
329                fprintf(stderr, "altq not defined on %s\n", pa->ifname);
330                return (1);
331        }
332        pa->scheduler = if_pa->scheduler;
333        pa->ifbandwidth = if_pa->ifbandwidth;
334
335        if (qname_to_pfaltq(pa->qname, pa->ifname) != NULL) {
336                fprintf(stderr, "queue %s already exists on interface %s\n",
337                    pa->qname, pa->ifname);
338                return (1);
339        }
340        pa->qid = qname_to_qid(pa->qname);
341
342        parent = NULL;
343        if (pa->parent[0] != 0) {
344                parent = qname_to_pfaltq(pa->parent, pa->ifname);
345                if (parent == NULL) {
346                        fprintf(stderr, "parent %s not found for %s\n",
347                            pa->parent, pa->qname);
348                        return (1);
349                }
350                pa->parent_qid = parent->qid;
351        }
352        if (pa->qlimit == 0)
353                pa->qlimit = DEFAULT_QLIMIT;
354
355        if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC) {
356                pa->bandwidth = eval_bwspec(bw,
357                    parent == NULL ? 0 : parent->bandwidth);
358
359                if (pa->bandwidth > pa->ifbandwidth) {
360                        fprintf(stderr, "bandwidth for %s higher than "
361                            "interface\n", pa->qname);
362                        return (1);
363                }
364                /* check the sum of the child bandwidth is under parent's */
365                if (parent != NULL) {
366                        if (pa->bandwidth > parent->bandwidth) {
367                                warnx("bandwidth for %s higher than parent",
368                                    pa->qname);
369                                return (1);
370                        }
371                        bwsum = 0;
372                        TAILQ_FOREACH(altq, &altqs, entries) {
373                                if (strncmp(altq->ifname, pa->ifname,
374                                    IFNAMSIZ) == 0 &&
375                                    altq->qname[0] != 0 &&
376                                    strncmp(altq->parent, pa->parent,
377                                    PF_QNAME_SIZE) == 0)
378                                        bwsum += altq->bandwidth;
379                        }
380                        bwsum += pa->bandwidth;
381                        if (bwsum > parent->bandwidth) {
382                                warnx("the sum of the child bandwidth higher"
383                                    " than parent \"%s\"", parent->qname);
384                        }
385                }
386        }
387
388        if (eval_queue_opts(pa, opts, parent == NULL? 0 : parent->bandwidth))
389                return (1);
390
391        switch (pa->scheduler) {
392        case ALTQT_CBQ:
393                error = eval_pfqueue_cbq(pf, pa);
394                break;
395        case ALTQT_PRIQ:
396                error = eval_pfqueue_priq(pf, pa);
397                break;
398        case ALTQT_HFSC:
399                error = eval_pfqueue_hfsc(pf, pa);
400                break;
401        default:
402                break;
403        }
404        return (error);
405}
406
407/*
408 * CBQ support functions
409 */
410#define RM_FILTER_GAIN  5       /* log2 of gain, e.g., 5 => 31/32 */
411#define RM_NS_PER_SEC   (1000000000)
412
413static int
414eval_pfqueue_cbq(struct pfctl *pf, struct pf_altq *pa)
415{
416        struct cbq_opts *opts;
417        u_int            ifmtu;
418
419        if (pa->priority >= CBQ_MAXPRI) {
420                warnx("priority out of range: max %d", CBQ_MAXPRI - 1);
421                return (-1);
422        }
423
424        ifmtu = getifmtu(pa->ifname);
425        opts = &pa->pq_u.cbq_opts;
426
427        if (opts->pktsize == 0) {       /* use default */
428                opts->pktsize = ifmtu;
429                if (opts->pktsize > MCLBYTES)   /* do what TCP does */
430                        opts->pktsize &= ~MCLBYTES;
431        } else if (opts->pktsize > ifmtu)
432                opts->pktsize = ifmtu;
433        if (opts->maxpktsize == 0)      /* use default */
434                opts->maxpktsize = ifmtu;
435        else if (opts->maxpktsize > ifmtu)
436                opts->pktsize = ifmtu;
437
438        if (opts->pktsize > opts->maxpktsize)
439                opts->pktsize = opts->maxpktsize;
440
441        if (pa->parent[0] == 0)
442                opts->flags |= (CBQCLF_ROOTCLASS | CBQCLF_WRR);
443
444        cbq_compute_idletime(pf, pa);
445        return (0);
446}
447
448/*
449 * compute ns_per_byte, maxidle, minidle, and offtime
450 */
451static int
452cbq_compute_idletime(struct pfctl *pf, struct pf_altq *pa)
453{
454        struct cbq_opts *opts;
455        double           maxidle_s, maxidle, minidle;
456        double           offtime, nsPerByte, ifnsPerByte, ptime, cptime;
457        double           z, g, f, gton, gtom;
458        u_int            minburst, maxburst;
459
460        opts = &pa->pq_u.cbq_opts;
461        ifnsPerByte = (1.0 / (double)pa->ifbandwidth) * RM_NS_PER_SEC * 8;
462        minburst = opts->minburst;
463        maxburst = opts->maxburst;
464
465        if (pa->bandwidth == 0)
466                f = 0.0001;     /* small enough? */
467        else
468                f = ((double) pa->bandwidth / (double) pa->ifbandwidth);
469
470        nsPerByte = ifnsPerByte / f;
471        ptime = (double)opts->pktsize * ifnsPerByte;
472        cptime = ptime * (1.0 - f) / f;
473
474        if (nsPerByte * (double)opts->maxpktsize > (double)INT_MAX) {
475                /*
476                 * this causes integer overflow in kernel!
477                 * (bandwidth < 6Kbps when max_pkt_size=1500)
478                 */
479                if (pa->bandwidth != 0 && (pf->opts & PF_OPT_QUIET) == 0)
480                        warnx("queue bandwidth must be larger than %s",
481                            rate2str(ifnsPerByte * (double)opts->maxpktsize /
482                            (double)INT_MAX * (double)pa->ifbandwidth));
483                        fprintf(stderr, "cbq: queue %s is too slow!\n",
484                            pa->qname);
485                nsPerByte = (double)(INT_MAX / opts->maxpktsize);
486        }
487
488        if (maxburst == 0) {  /* use default */
489                if (cptime > 10.0 * 1000000)
490                        maxburst = 4;
491                else
492                        maxburst = 16;
493        }
494        if (minburst == 0)  /* use default */
495                minburst = 2;
496        if (minburst > maxburst)
497                minburst = maxburst;
498
499        z = (double)(1 << RM_FILTER_GAIN);
500        g = (1.0 - 1.0 / z);
501        gton = pow(g, (double)maxburst);
502        gtom = pow(g, (double)(minburst-1));
503        maxidle = ((1.0 / f - 1.0) * ((1.0 - gton) / gton));
504        maxidle_s = (1.0 - g);
505        if (maxidle > maxidle_s)
506                maxidle = ptime * maxidle;
507        else
508                maxidle = ptime * maxidle_s;
509        offtime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom);
510        minidle = -((double)opts->maxpktsize * (double)nsPerByte);
511
512        /* scale parameters */
513        maxidle = ((maxidle * 8.0) / nsPerByte) *
514            pow(2.0, (double)RM_FILTER_GAIN);
515        offtime = (offtime * 8.0) / nsPerByte *
516            pow(2.0, (double)RM_FILTER_GAIN);
517        minidle = ((minidle * 8.0) / nsPerByte) *
518            pow(2.0, (double)RM_FILTER_GAIN);
519
520        maxidle = maxidle / 1000.0;
521        offtime = offtime / 1000.0;
522        minidle = minidle / 1000.0;
523
524        opts->minburst = minburst;
525        opts->maxburst = maxburst;
526        opts->ns_per_byte = (u_int)nsPerByte;
527        opts->maxidle = (u_int)fabs(maxidle);
528        opts->minidle = (int)minidle;
529        opts->offtime = (u_int)fabs(offtime);
530
531        return (0);
532}
533
534static int
535check_commit_cbq(int dev, int opts, struct pf_altq *pa)
536{
537        struct pf_altq  *altq;
538        int              root_class, default_class;
539        int              error = 0;
540
541        /*
542         * check if cbq has one root queue and one default queue
543         * for this interface
544         */
545        root_class = default_class = 0;
546        TAILQ_FOREACH(altq, &altqs, entries) {
547                if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
548                        continue;
549                if (altq->qname[0] == 0)  /* this is for interface */
550                        continue;
551                if (altq->pq_u.cbq_opts.flags & CBQCLF_ROOTCLASS)
552                        root_class++;
553                if (altq->pq_u.cbq_opts.flags & CBQCLF_DEFCLASS)
554                        default_class++;
555        }
556        if (root_class != 1) {
557                warnx("should have one root queue on %s", pa->ifname);
558                error++;
559        }
560        if (default_class != 1) {
561                warnx("should have one default queue on %s", pa->ifname);
562                error++;
563        }
564        return (error);
565}
566
567static int
568print_cbq_opts(const struct pf_altq *a)
569{
570        const struct cbq_opts   *opts;
571
572        opts = &a->pq_u.cbq_opts;
573        if (opts->flags) {
574                printf("cbq(");
575                if (opts->flags & CBQCLF_RED)
576                        printf(" red");
577                if (opts->flags & CBQCLF_ECN)
578                        printf(" ecn");
579                if (opts->flags & CBQCLF_RIO)
580                        printf(" rio");
581                if (opts->flags & CBQCLF_CLEARDSCP)
582                        printf(" cleardscp");
583                if (opts->flags & CBQCLF_FLOWVALVE)
584                        printf(" flowvalve");
585                if (opts->flags & CBQCLF_BORROW)
586                        printf(" borrow");
587                if (opts->flags & CBQCLF_WRR)
588                        printf(" wrr");
589                if (opts->flags & CBQCLF_EFFICIENT)
590                        printf(" efficient");
591                if (opts->flags & CBQCLF_ROOTCLASS)
592                        printf(" root");
593                if (opts->flags & CBQCLF_DEFCLASS)
594                        printf(" default");
595                printf(" ) ");
596
597                return (1);
598        } else
599                return (0);
600}
601
602/*
603 * PRIQ support functions
604 */
605static int
606eval_pfqueue_priq(struct pfctl *pf, struct pf_altq *pa)
607{
608        struct pf_altq  *altq;
609
610        if (pa->priority >= PRIQ_MAXPRI) {
611                warnx("priority out of range: max %d", PRIQ_MAXPRI - 1);
612                return (-1);
613        }
614        /* the priority should be unique for the interface */
615        TAILQ_FOREACH(altq, &altqs, entries) {
616                if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) == 0 &&
617                    altq->qname[0] != 0 && altq->priority == pa->priority) {
618                        warnx("%s and %s have the same priority",
619                            altq->qname, pa->qname);
620                        return (-1);
621                }
622        }
623
624        return (0);
625}
626
627static int
628check_commit_priq(int dev, int opts, struct pf_altq *pa)
629{
630        struct pf_altq  *altq;
631        int              default_class;
632        int              error = 0;
633
634        /*
635         * check if priq has one default class for this interface
636         */
637        default_class = 0;
638        TAILQ_FOREACH(altq, &altqs, entries) {
639                if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
640                        continue;
641                if (altq->qname[0] == 0)  /* this is for interface */
642                        continue;
643                if (altq->pq_u.priq_opts.flags & PRCF_DEFAULTCLASS)
644                        default_class++;
645        }
646        if (default_class != 1) {
647                warnx("should have one default queue on %s", pa->ifname);
648                error++;
649        }
650        return (error);
651}
652
653static int
654print_priq_opts(const struct pf_altq *a)
655{
656        const struct priq_opts  *opts;
657
658        opts = &a->pq_u.priq_opts;
659
660        if (opts->flags) {
661                printf("priq(");
662                if (opts->flags & PRCF_RED)
663                        printf(" red");
664                if (opts->flags & PRCF_ECN)
665                        printf(" ecn");
666                if (opts->flags & PRCF_RIO)
667                        printf(" rio");
668                if (opts->flags & PRCF_CLEARDSCP)
669                        printf(" cleardscp");
670                if (opts->flags & PRCF_DEFAULTCLASS)
671                        printf(" default");
672                printf(" ) ");
673
674                return (1);
675        } else
676                return (0);
677}
678
679/*
680 * HFSC support functions
681 */
682static int
683eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa)
684{
685        struct pf_altq          *altq, *parent;
686        struct hfsc_opts        *opts;
687        struct service_curve     sc;
688
689        opts = &pa->pq_u.hfsc_opts;
690
691        if (pa->parent[0] == 0) {
692                /* root queue */
693                opts->lssc_m1 = pa->ifbandwidth;
694                opts->lssc_m2 = pa->ifbandwidth;
695                opts->lssc_d = 0;
696                return (0);
697        }
698
699        LIST_INIT(&rtsc);
700        LIST_INIT(&lssc);
701
702        /* if link_share is not specified, use bandwidth */
703        if (opts->lssc_m2 == 0)
704                opts->lssc_m2 = pa->bandwidth;
705
706        if ((opts->rtsc_m1 > 0 && opts->rtsc_m2 == 0) ||
707            (opts->lssc_m1 > 0 && opts->lssc_m2 == 0) ||
708            (opts->ulsc_m1 > 0 && opts->ulsc_m2 == 0)) {
709                warnx("m2 is zero for %s", pa->qname);
710                return (-1);
711        }
712
713        if ((opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0) ||
714            (opts->lssc_m1 < opts->lssc_m2 && opts->lssc_m1 != 0) ||
715            (opts->ulsc_m1 < opts->ulsc_m2 && opts->ulsc_m1 != 0)) {
716                warnx("m1 must be zero for convex curve: %s", pa->qname);
717                return (-1);
718        }
719
720        /*
721         * admission control:
722         * for the real-time service curve, the sum of the service curves
723         * should not exceed 80% of the interface bandwidth.  20% is reserved
724         * not to over-commit the actual interface bandwidth.
725         * for the linkshare service curve, the sum of the child service
726         * curve should not exceed the parent service curve.
727         * for the upper-limit service curve, the assigned bandwidth should
728         * be smaller than the interface bandwidth, and the upper-limit should
729         * be larger than the real-time service curve when both are defined.
730         */
731        parent = qname_to_pfaltq(pa->parent, pa->ifname);
732        if (parent == NULL)
733                errx(1, "parent %s not found for %s", pa->parent, pa->qname);
734
735        TAILQ_FOREACH(altq, &altqs, entries) {
736                if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
737                        continue;
738                if (altq->qname[0] == 0)  /* this is for interface */
739                        continue;
740
741                /* if the class has a real-time service curve, add it. */
742                if (opts->rtsc_m2 != 0 && altq->pq_u.hfsc_opts.rtsc_m2 != 0) {
743                        sc.m1 = altq->pq_u.hfsc_opts.rtsc_m1;
744                        sc.d = altq->pq_u.hfsc_opts.rtsc_d;
745                        sc.m2 = altq->pq_u.hfsc_opts.rtsc_m2;
746                        gsc_add_sc(&rtsc, &sc);
747                }
748
749                if (strncmp(altq->parent, pa->parent, PF_QNAME_SIZE) != 0)
750                        continue;
751
752                /* if the class has a linkshare service curve, add it. */
753                if (opts->lssc_m2 != 0 && altq->pq_u.hfsc_opts.lssc_m2 != 0) {
754                        sc.m1 = altq->pq_u.hfsc_opts.lssc_m1;
755                        sc.d = altq->pq_u.hfsc_opts.lssc_d;
756                        sc.m2 = altq->pq_u.hfsc_opts.lssc_m2;
757                        gsc_add_sc(&lssc, &sc);
758                }
759        }
760
761        /* check the real-time service curve.  reserve 20% of interface bw */
762        if (opts->rtsc_m2 != 0) {
763                /* add this queue to the sum */
764                sc.m1 = opts->rtsc_m1;
765                sc.d = opts->rtsc_d;
766                sc.m2 = opts->rtsc_m2;
767                gsc_add_sc(&rtsc, &sc);
768                /* compare the sum with 80% of the interface */
769                sc.m1 = 0;
770                sc.d = 0;
771                sc.m2 = pa->ifbandwidth / 100 * 80;
772                if (!is_gsc_under_sc(&rtsc, &sc)) {
773                        warnx("real-time sc exceeds 80%% of the interface "
774                            "bandwidth (%s)", rate2str((double)sc.m2));
775                        goto err_ret;
776                }
777        }
778
779        /* check the linkshare service curve. */
780        if (opts->lssc_m2 != 0) {
781                /* add this queue to the child sum */
782                sc.m1 = opts->lssc_m1;
783                sc.d = opts->lssc_d;
784                sc.m2 = opts->lssc_m2;
785                gsc_add_sc(&lssc, &sc);
786                /* compare the sum of the children with parent's sc */
787                sc.m1 = parent->pq_u.hfsc_opts.lssc_m1;
788                sc.d = parent->pq_u.hfsc_opts.lssc_d;
789                sc.m2 = parent->pq_u.hfsc_opts.lssc_m2;
790                if (!is_gsc_under_sc(&lssc, &sc)) {
791                        warnx("linkshare sc exceeds parent's sc");
792                        goto err_ret;
793                }
794        }
795
796        /* check the upper-limit service curve. */
797        if (opts->ulsc_m2 != 0) {
798                if (opts->ulsc_m1 > pa->ifbandwidth ||
799                    opts->ulsc_m2 > pa->ifbandwidth) {
800                        warnx("upper-limit larger than interface bandwidth");
801                        goto err_ret;
802                }
803                if (opts->rtsc_m2 != 0 && opts->rtsc_m2 > opts->ulsc_m2) {
804                        warnx("upper-limit sc smaller than real-time sc");
805                        goto err_ret;
806                }
807        }
808
809        gsc_destroy(&rtsc);
810        gsc_destroy(&lssc);
811
812        return (0);
813
814err_ret:
815        gsc_destroy(&rtsc);
816        gsc_destroy(&lssc);
817        return (-1);
818}
819
820static int
821check_commit_hfsc(int dev, int opts, struct pf_altq *pa)
822{
823        struct pf_altq  *altq, *def = NULL;
824        int              default_class;
825        int              error = 0;
826
827        /* check if hfsc has one default queue for this interface */
828        default_class = 0;
829        TAILQ_FOREACH(altq, &altqs, entries) {
830                if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
831                        continue;
832                if (altq->qname[0] == 0)  /* this is for interface */
833                        continue;
834                if (altq->parent[0] == 0)  /* dummy root */
835                        continue;
836                if (altq->pq_u.hfsc_opts.flags & HFCF_DEFAULTCLASS) {
837                        default_class++;
838                        def = altq;
839                }
840        }
841        if (default_class != 1) {
842                warnx("should have one default queue on %s", pa->ifname);
843                return (1);
844        }
845        /* make sure the default queue is a leaf */
846        TAILQ_FOREACH(altq, &altqs, entries) {
847                if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
848                        continue;
849                if (altq->qname[0] == 0)  /* this is for interface */
850                        continue;
851                if (strncmp(altq->parent, def->qname, PF_QNAME_SIZE) == 0) {
852                        warnx("default queue is not a leaf");
853                        error++;
854                }
855        }
856        return (error);
857}
858
859static int
860print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts)
861{
862        const struct hfsc_opts          *opts;
863        const struct node_hfsc_sc       *rtsc, *lssc, *ulsc;
864
865        opts = &a->pq_u.hfsc_opts;
866        if (qopts == NULL)
867                rtsc = lssc = ulsc = NULL;
868        else {
869                rtsc = &qopts->data.hfsc_opts.realtime;
870                lssc = &qopts->data.hfsc_opts.linkshare;
871                ulsc = &qopts->data.hfsc_opts.upperlimit;
872        }
873
874        if (opts->flags || opts->rtsc_m2 != 0 || opts->ulsc_m2 != 0 ||
875            (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth ||
876            opts->lssc_d != 0))) {
877                printf("hfsc(");
878                if (opts->flags & HFCF_RED)
879                        printf(" red");
880                if (opts->flags & HFCF_ECN)
881                        printf(" ecn");
882                if (opts->flags & HFCF_RIO)
883                        printf(" rio");
884                if (opts->flags & HFCF_CLEARDSCP)
885                        printf(" cleardscp");
886                if (opts->flags & HFCF_DEFAULTCLASS)
887                        printf(" default");
888                if (opts->rtsc_m2 != 0)
889                        print_hfsc_sc("realtime", opts->rtsc_m1, opts->rtsc_d,
890                            opts->rtsc_m2, rtsc);
891                if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth ||
892                    opts->lssc_d != 0))
893                        print_hfsc_sc("linkshare", opts->lssc_m1, opts->lssc_d,
894                            opts->lssc_m2, lssc);
895                if (opts->ulsc_m2 != 0)
896                        print_hfsc_sc("upperlimit", opts->ulsc_m1, opts->ulsc_d,
897                            opts->ulsc_m2, ulsc);
898                printf(" ) ");
899
900                return (1);
901        } else
902                return (0);
903}
904
905/*
906 * admission control using generalized service curve
907 */
908
909/* add a new service curve to a generalized service curve */
910static void
911gsc_add_sc(struct gen_sc *gsc, struct service_curve *sc)
912{
913        if (is_sc_null(sc))
914                return;
915        if (sc->d != 0)
916                gsc_add_seg(gsc, 0.0, 0.0, (double)sc->d, (double)sc->m1);
917        gsc_add_seg(gsc, (double)sc->d, 0.0, INFINITY, (double)sc->m2);
918}
919
920/*
921 * check whether all points of a generalized service curve have
922 * their y-coordinates no larger than a given two-piece linear
923 * service curve.
924 */
925static int
926is_gsc_under_sc(struct gen_sc *gsc, struct service_curve *sc)
927{
928        struct segment  *s, *last, *end;
929        double           y;
930
931        if (is_sc_null(sc)) {
932                if (LIST_EMPTY(gsc))
933                        return (1);
934                LIST_FOREACH(s, gsc, _next) {
935                        if (s->m != 0)
936                                return (0);
937                }
938                return (1);
939        }
940        /*
941         * gsc has a dummy entry at the end with x = INFINITY.
942         * loop through up to this dummy entry.
943         */
944        end = gsc_getentry(gsc, INFINITY);
945        if (end == NULL)
946                return (1);
947        last = NULL;
948        for (s = LIST_FIRST(gsc); s != end; s = LIST_NEXT(s, _next)) {
949                if (s->y > sc_x2y(sc, s->x))
950                        return (0);
951                last = s;
952        }
953        /* last now holds the real last segment */
954        if (last == NULL)
955                return (1);
956        if (last->m > sc->m2)
957                return (0);
958        if (last->x < sc->d && last->m > sc->m1) {
959                y = last->y + (sc->d - last->x) * last->m;
960                if (y > sc_x2y(sc, sc->d))
961                        return (0);
962        }
963        return (1);
964}
965
966static void
967gsc_destroy(struct gen_sc *gsc)
968{
969        struct segment  *s;
970
971        while ((s = LIST_FIRST(gsc)) != NULL) {
972                LIST_REMOVE(s, _next);
973                free(s);
974        }
975}
976
977/*
978 * return a segment entry starting at x.
979 * if gsc has no entry starting at x, a new entry is created at x.
980 */
981static struct segment *
982gsc_getentry(struct gen_sc *gsc, double x)
983{
984        struct segment  *new, *prev, *s;
985
986        prev = NULL;
987        LIST_FOREACH(s, gsc, _next) {
988                if (s->x == x)
989                        return (s);     /* matching entry found */
990                else if (s->x < x)
991                        prev = s;
992                else
993                        break;
994        }
995
996        /* we have to create a new entry */
997        if ((new = calloc(1, sizeof(struct segment))) == NULL)
998                return (NULL);
999
1000        new->x = x;
1001        if (x == INFINITY || s == NULL)
1002                new->d = 0;
1003        else if (s->x == INFINITY)
1004                new->d = INFINITY;
1005        else
1006                new->d = s->x - x;
1007        if (prev == NULL) {
1008                /* insert the new entry at the head of the list */
1009                new->y = 0;
1010                new->m = 0;
1011                LIST_INSERT_HEAD(gsc, new, _next);
1012        } else {
1013                /*
1014                 * the start point intersects with the segment pointed by
1015                 * prev.  divide prev into 2 segments
1016                 */
1017                if (x == INFINITY) {
1018                        prev->d = INFINITY;
1019                        if (prev->m == 0)
1020                                new->y = prev->y;
1021                        else
1022                                new->y = INFINITY;
1023                } else {
1024                        prev->d = x - prev->x;
1025                        new->y = prev->d * prev->m + prev->y;
1026                }
1027                new->m = prev->m;
1028                LIST_INSERT_AFTER(prev, new, _next);
1029        }
1030        return (new);
1031}
1032
1033/* add a segment to a generalized service curve */
1034static int
1035gsc_add_seg(struct gen_sc *gsc, double x, double y, double d, double m)
1036{
1037        struct segment  *start, *end, *s;
1038        double           x2;
1039
1040        if (d == INFINITY)
1041                x2 = INFINITY;
1042        else
1043                x2 = x + d;
1044        start = gsc_getentry(gsc, x);
1045        end = gsc_getentry(gsc, x2);
1046        if (start == NULL || end == NULL)
1047                return (-1);
1048
1049        for (s = start; s != end; s = LIST_NEXT(s, _next)) {
1050                s->m += m;
1051                s->y += y + (s->x - x) * m;
1052        }
1053
1054        end = gsc_getentry(gsc, INFINITY);
1055        for (; s != end; s = LIST_NEXT(s, _next)) {
1056                s->y += m * d;
1057        }
1058
1059        return (0);
1060}
1061
1062/* get y-projection of a service curve */
1063static double
1064sc_x2y(struct service_curve *sc, double x)
1065{
1066        double  y;
1067
1068        if (x <= (double)sc->d)
1069                /* y belongs to the 1st segment */
1070                y = x * (double)sc->m1;
1071        else
1072                /* y belongs to the 2nd segment */
1073                y = (double)sc->d * (double)sc->m1
1074                        + (x - (double)sc->d) * (double)sc->m2;
1075        return (y);
1076}
1077
1078/*
1079 * misc utilities
1080 */
1081#define R2S_BUFS        8
1082#define RATESTR_MAX     16
1083
1084#ifdef __rtems__
1085static char      r2sbuf[R2S_BUFS][RATESTR_MAX];  /* ring bufer */
1086static int       r2sidx = 0;
1087
1088#endif /* __rtems__ */
1089char *
1090rate2str(double rate)
1091{
1092        char            *buf;
1093#ifndef __rtems__
1094        static char      r2sbuf[R2S_BUFS][RATESTR_MAX];  /* ring bufer */
1095        static int       idx = 0;
1096#endif /* __rtems__ */
1097        int              i;
1098        static const char unit[] = " KMG";
1099
1100#ifndef __rtems__
1101        buf = r2sbuf[idx++];
1102        if (idx == R2S_BUFS)
1103                idx = 0;
1104#else /* __rtems__ */
1105        buf = r2sbuf[r2sidx++];
1106        if (r2sidx == R2S_BUFS)
1107                r2sidx = 0;
1108#endif /* __rtems__ */
1109
1110        for (i = 0; rate >= 1000 && i <= 3; i++)
1111                rate /= 1000;
1112
1113        if ((int)(rate * 100) % 100)
1114                snprintf(buf, RATESTR_MAX, "%.2f%cb", rate, unit[i]);
1115        else
1116                snprintf(buf, RATESTR_MAX, "%d%cb", (int)rate, unit[i]);
1117
1118        return (buf);
1119}
1120
1121#ifdef __FreeBSD__
1122/*
1123 * XXX
1124 * FreeBSD does not have SIOCGIFDATA.
1125 * To emulate this, DIOCGIFSPEED ioctl added to pf.
1126 */
1127u_int32_t
1128getifspeed(int pfdev, char *ifname)
1129{
1130        struct pf_ifspeed io;
1131
1132        bzero(&io, sizeof io);
1133        if (strlcpy(io.ifname, ifname, IFNAMSIZ) >=
1134            sizeof(io.ifname))
1135                errx(1, "getifspeed: strlcpy");
1136        if (ioctl(pfdev, DIOCGIFSPEED, &io) == -1)
1137                err(1, "DIOCGIFSPEED");
1138        return ((u_int32_t)io.baudrate);
1139}
1140#else
1141u_int32_t
1142getifspeed(char *ifname)
1143{
1144        int             s;
1145        struct ifreq    ifr;
1146        struct if_data  ifrdat;
1147
1148        if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
1149                err(1, "socket");
1150        bzero(&ifr, sizeof(ifr));
1151        if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >=
1152            sizeof(ifr.ifr_name))
1153                errx(1, "getifspeed: strlcpy");
1154        ifr.ifr_data = (caddr_t)&ifrdat;
1155        if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == -1)
1156                err(1, "SIOCGIFDATA");
1157        if (close(s))
1158                err(1, "close");
1159        return ((u_int32_t)ifrdat.ifi_baudrate);
1160}
1161#endif
1162
1163u_long
1164getifmtu(char *ifname)
1165{
1166        int             s;
1167        struct ifreq    ifr;
1168
1169        if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
1170                err(1, "socket");
1171        bzero(&ifr, sizeof(ifr));
1172        if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >=
1173            sizeof(ifr.ifr_name))
1174                errx(1, "getifmtu: strlcpy");
1175        if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == -1)
1176#ifdef __FreeBSD__
1177                ifr.ifr_mtu = 1500;
1178#else
1179                err(1, "SIOCGIFMTU");
1180#endif
1181        if (close(s))
1182                err(1, "close");
1183        if (ifr.ifr_mtu > 0)
1184                return (ifr.ifr_mtu);
1185        else {
1186                warnx("could not get mtu for %s, assuming 1500", ifname);
1187                return (1500);
1188        }
1189}
1190
1191int
1192eval_queue_opts(struct pf_altq *pa, struct node_queue_opt *opts,
1193    u_int32_t ref_bw)
1194{
1195        int     errors = 0;
1196
1197        switch (pa->scheduler) {
1198        case ALTQT_CBQ:
1199                pa->pq_u.cbq_opts = opts->data.cbq_opts;
1200                break;
1201        case ALTQT_PRIQ:
1202                pa->pq_u.priq_opts = opts->data.priq_opts;
1203                break;
1204        case ALTQT_HFSC:
1205                pa->pq_u.hfsc_opts.flags = opts->data.hfsc_opts.flags;
1206                if (opts->data.hfsc_opts.linkshare.used) {
1207                        pa->pq_u.hfsc_opts.lssc_m1 =
1208                            eval_bwspec(&opts->data.hfsc_opts.linkshare.m1,
1209                            ref_bw);
1210                        pa->pq_u.hfsc_opts.lssc_m2 =
1211                            eval_bwspec(&opts->data.hfsc_opts.linkshare.m2,
1212                            ref_bw);
1213                        pa->pq_u.hfsc_opts.lssc_d =
1214                            opts->data.hfsc_opts.linkshare.d;
1215                }
1216                if (opts->data.hfsc_opts.realtime.used) {
1217                        pa->pq_u.hfsc_opts.rtsc_m1 =
1218                            eval_bwspec(&opts->data.hfsc_opts.realtime.m1,
1219                            ref_bw);
1220                        pa->pq_u.hfsc_opts.rtsc_m2 =
1221                            eval_bwspec(&opts->data.hfsc_opts.realtime.m2,
1222                            ref_bw);
1223                        pa->pq_u.hfsc_opts.rtsc_d =
1224                            opts->data.hfsc_opts.realtime.d;
1225                }
1226                if (opts->data.hfsc_opts.upperlimit.used) {
1227                        pa->pq_u.hfsc_opts.ulsc_m1 =
1228                            eval_bwspec(&opts->data.hfsc_opts.upperlimit.m1,
1229                            ref_bw);
1230                        pa->pq_u.hfsc_opts.ulsc_m2 =
1231                            eval_bwspec(&opts->data.hfsc_opts.upperlimit.m2,
1232                            ref_bw);
1233                        pa->pq_u.hfsc_opts.ulsc_d =
1234                            opts->data.hfsc_opts.upperlimit.d;
1235                }
1236                break;
1237        default:
1238                warnx("eval_queue_opts: unknown scheduler type %u",
1239                    opts->qtype);
1240                errors++;
1241                break;
1242        }
1243
1244        return (errors);
1245}
1246
1247u_int32_t
1248eval_bwspec(struct node_queue_bw *bw, u_int32_t ref_bw)
1249{
1250        if (bw->bw_absolute > 0)
1251                return (bw->bw_absolute);
1252
1253        if (bw->bw_percent > 0)
1254                return (ref_bw / 100 * bw->bw_percent);
1255
1256        return (0);
1257}
1258
1259void
1260print_hfsc_sc(const char *scname, u_int m1, u_int d, u_int m2,
1261    const struct node_hfsc_sc *sc)
1262{
1263        printf(" %s", scname);
1264
1265        if (d != 0) {
1266                printf("(");
1267                if (sc != NULL && sc->m1.bw_percent > 0)
1268                        printf("%u%%", sc->m1.bw_percent);
1269                else
1270                        printf("%s", rate2str((double)m1));
1271                printf(" %u", d);
1272        }
1273
1274        if (sc != NULL && sc->m2.bw_percent > 0)
1275                printf(" %u%%", sc->m2.bw_percent);
1276        else
1277                printf(" %s", rate2str((double)m2));
1278
1279        if (d != 0)
1280                printf(")");
1281}
1282#ifdef __rtems__
1283#include "pfctl_altq-data.h"
1284#endif /* __rtems__ */
Note: See TracBrowser for help on using the repository browser.