Index: head/sbin/ipfw/altq.c =================================================================== --- head/sbin/ipfw/altq.c +++ head/sbin/ipfw/altq.c @@ -22,6 +22,8 @@ * altq interface */ +#define PFIOC_USE_LATEST + #include #include #include @@ -85,6 +87,7 @@ return; } bzero(&pfioc, sizeof(pfioc)); + pfioc.version = PFIOC_ALTQ_VERSION; if (ioctl(pffd, DIOCGETALTQS, &pfioc) != 0) { warn("altq support getting queue list"); close(pffd); Index: head/sbin/pfctl/parse.y =================================================================== --- head/sbin/pfctl/parse.y +++ head/sbin/pfctl/parse.y @@ -32,6 +32,8 @@ #include __FBSDID("$FreeBSD$"); +#define PFIOC_USE_LATEST + #include #include #include @@ -286,7 +288,7 @@ struct node_queue_bw queue_bwspec; struct node_queue_opt scheduler; int priority; - int tbrsize; + unsigned int tbrsize; int qlimit; } queue_opts; @@ -1623,8 +1625,8 @@ yyerror("tbrsize cannot be respecified"); YYERROR; } - if ($2 < 0 || $2 > 65535) { - yyerror("tbrsize too big: max 65535"); + if ($2 < 0 || $2 > UINT_MAX) { + yyerror("tbrsize too big: max %u", UINT_MAX); YYERROR; } queue_opts.marker |= QOM_TBRSIZE; @@ -1673,10 +1675,10 @@ } } free($1); - $$.bw_absolute = (u_int32_t)bps; + $$.bw_absolute = (u_int64_t)bps; } | NUMBER { - if ($1 < 0 || $1 > UINT_MAX) { + if ($1 < 0 || $1 >= LLONG_MAX) { yyerror("bandwidth number too big"); YYERROR; } Index: head/sbin/pfctl/pfctl.c =================================================================== --- head/sbin/pfctl/pfctl.c +++ head/sbin/pfctl/pfctl.c @@ -36,6 +36,8 @@ #include __FBSDID("$FreeBSD$"); +#define PFIOC_USE_LATEST + #include #include #include @@ -1524,6 +1526,7 @@ } memset(&pa, 0, sizeof(pa)); + pa.version = PFIOC_ALTQ_VERSION; memset(&pf, 0, sizeof(pf)); memset(&trs, 0, sizeof(trs)); if ((path = calloc(1, MAXPATHLEN)) == NULL) @@ -2032,6 +2035,7 @@ { struct pfioc_altq pa; + pa.version = PFIOC_ALTQ_VERSION; if (ioctl(dev, DIOCGETALTQS, &pa)) { if (errno == ENODEV) { if (opts & PF_OPT_VERBOSE) Index: head/sbin/pfctl/pfctl_altq.c =================================================================== --- head/sbin/pfctl/pfctl_altq.c +++ head/sbin/pfctl/pfctl_altq.c @@ -21,6 +21,8 @@ #include __FBSDID("$FreeBSD$"); +#define PFIOC_USE_LATEST + #include #include #include @@ -31,6 +33,7 @@ #include #include +#include #include #include #include @@ -88,14 +91,14 @@ static double sc_x2y(struct service_curve *, double); #ifdef __FreeBSD__ -u_int32_t getifspeed(int, char *); +u_int64_t getifspeed(int, char *); #else u_int32_t getifspeed(char *); #endif u_long getifmtu(char *); int eval_queue_opts(struct pf_altq *, struct node_queue_opt *, - u_int32_t); -u_int32_t eval_bwspec(struct node_queue_bw *, u_int32_t); + u_int64_t); +u_int64_t eval_bwspec(struct node_queue_bw *, u_int64_t); void print_hfsc_sc(const char *, u_int, u_int, u_int, const struct node_hfsc_sc *); void print_fairq_sc(const char *, u_int, u_int, u_int, @@ -258,7 +261,8 @@ eval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, struct node_queue_opt *opts) { - u_int rate, size, errors = 0; + u_int64_t rate; + u_int size, errors = 0; if (bw->bw_absolute > 0) pa->ifbandwidth = bw->bw_absolute; @@ -275,6 +279,15 @@ } else if ((pa->ifbandwidth = eval_bwspec(bw, rate)) == 0) pa->ifbandwidth = rate; + /* + * Limit bandwidth to UINT_MAX for schedulers that aren't 64-bit ready. + */ + if ((pa->scheduler != ALTQT_HFSC) && (pa->ifbandwidth > UINT_MAX)) { + pa->ifbandwidth = UINT_MAX; + warnx("interface %s bandwidth limited to %" PRIu64 " bps " + "because selected scheduler is 32-bit limited\n", pa->ifname, + pa->ifbandwidth); + } errors += eval_queue_opts(pa, opts, pa->ifbandwidth); /* if tbrsize is not specified, use heuristics */ @@ -289,8 +302,6 @@ else size = 24; size = size * getifmtu(pa->ifname); - if (size > 0xffff) - size = 0xffff; pa->tbrsize = size; } return (errors); @@ -338,7 +349,7 @@ { /* should be merged with expand_queue */ struct pf_altq *if_pa, *parent, *altq; - u_int32_t bwsum; + u_int64_t bwsum; int error = 0; /* find the corresponding interface and copy fields used by queues */ @@ -372,7 +383,7 @@ if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC || pa->scheduler == ALTQT_FAIRQ) { pa->bandwidth = eval_bwspec(bw, - parent == NULL ? 0 : parent->bandwidth); + parent == NULL ? pa->ifbandwidth : parent->bandwidth); if (pa->bandwidth > pa->ifbandwidth) { fprintf(stderr, "bandwidth for %s higher than " @@ -403,7 +414,8 @@ } } - if (eval_queue_opts(pa, opts, parent == NULL? 0 : parent->bandwidth)) + if (eval_queue_opts(pa, opts, + parent == NULL ? pa->ifbandwidth : parent->bandwidth)) return (1); switch (pa->scheduler) { @@ -709,7 +721,7 @@ eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa) { struct pf_altq *altq, *parent; - struct hfsc_opts *opts; + struct hfsc_opts_v1 *opts; struct service_curve sc; opts = &pa->pq_u.hfsc_opts; @@ -1001,7 +1013,7 @@ static int print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) { - const struct hfsc_opts *opts; + const struct hfsc_opts_v1 *opts; const struct node_hfsc_sc *rtsc, *lssc, *ulsc; opts = &a->pq_u.hfsc_opts; @@ -1316,7 +1328,7 @@ * FreeBSD does not have SIOCGIFDATA. * To emulate this, DIOCGIFSPEED ioctl added to pf. */ -u_int32_t +u_int64_t getifspeed(int pfdev, char *ifname) { struct pf_ifspeed io; @@ -1327,7 +1339,7 @@ errx(1, "getifspeed: strlcpy"); if (ioctl(pfdev, DIOCGIFSPEED, &io) == -1) err(1, "DIOCGIFSPEED"); - return ((u_int32_t)io.baudrate); + return (io.baudrate); } #else u_int32_t @@ -1382,7 +1394,7 @@ int eval_queue_opts(struct pf_altq *pa, struct node_queue_opt *opts, - u_int32_t ref_bw) + u_int64_t ref_bw) { int errors = 0; @@ -1458,11 +1470,21 @@ return (errors); } -u_int32_t -eval_bwspec(struct node_queue_bw *bw, u_int32_t ref_bw) +/* + * If absolute bandwidth if set, return the lesser of that value and the + * reference bandwidth. Limiting to the reference bandwidth allows simple + * limiting of configured bandwidth parameters for schedulers that are + * 32-bit limited, as the root/interface bandwidth (top-level reference + * bandwidth) will be properly limited in that case. + * + * Otherwise, if the absolute bandwidth is not set, return given percentage + * of reference bandwidth. + */ +u_int64_t +eval_bwspec(struct node_queue_bw *bw, u_int64_t ref_bw) { if (bw->bw_absolute > 0) - return (bw->bw_absolute); + return (MIN(bw->bw_absolute, ref_bw)); if (bw->bw_percent > 0) return (ref_bw / 100 * bw->bw_percent); Index: head/sbin/pfctl/pfctl_parser.h =================================================================== --- head/sbin/pfctl/pfctl_parser.h +++ head/sbin/pfctl/pfctl_parser.h @@ -134,7 +134,7 @@ }; struct node_queue_bw { - u_int32_t bw_absolute; + u_int64_t bw_absolute; u_int16_t bw_percent; }; Index: head/sbin/pfctl/pfctl_qstats.c =================================================================== --- head/sbin/pfctl/pfctl_qstats.c +++ head/sbin/pfctl/pfctl_qstats.c @@ -19,6 +19,8 @@ #include __FBSDID("$FreeBSD$"); +#define PFIOC_USE_LATEST + #include #include #include @@ -148,6 +150,7 @@ memset(&pa, 0, sizeof(pa)); memset(&pq, 0, sizeof(pq)); memset(&qstats, 0, sizeof(qstats)); + pa.version = PFIOC_ALTQ_VERSION; if (ioctl(dev, DIOCGETALTQS, &pa)) { warn("DIOCGETALTQS"); return (-1); @@ -177,6 +180,7 @@ pq.ticket = pa.ticket; pq.buf = &qstats.data; pq.nbytes = sizeof(qstats.data); + pq.version = altq_stats_version(pa.altq.scheduler); if (ioctl(dev, DIOCGETQSTATS, &pq)) { warn("DIOCGETQSTATS"); return (-1); Index: head/sys/net/altq/altq.h =================================================================== --- head/sys/net/altq/altq.h +++ head/sys/net/altq/altq.h @@ -76,8 +76,8 @@ /* simple token backet meter profile */ struct tb_profile { - u_int rate; /* rate in bit-per-sec */ - u_int depth; /* depth in bytes */ + u_int64_t rate; /* rate in bit-per-sec */ + u_int32_t depth; /* depth in bytes */ }; #ifdef ALTQ3_COMPAT @@ -203,4 +203,29 @@ #include #endif +/* + * Can't put these versions in the scheduler-specific headers and include + * them all here as that will cause build failure due to cross-including + * each other scheduler's private bits into each scheduler's + * implementation. + */ +#define CBQ_STATS_VERSION 0 /* Latest version of class_stats_t */ +#define CODEL_STATS_VERSION 0 /* Latest version of codel_ifstats */ +#define FAIRQ_STATS_VERSION 0 /* Latest version of fairq_classstats */ +#define HFSC_STATS_VERSION 1 /* Latest version of hfsc_classstats */ +#define PRIQ_STATS_VERSION 0 /* Latest version of priq_classstats */ + +/* Return the latest stats version for the given scheduler. */ +static inline int altq_stats_version(int scheduler) +{ + switch (scheduler) { + case ALTQT_CBQ: return (CBQ_STATS_VERSION); + case ALTQT_CODEL: return (CODEL_STATS_VERSION); + case ALTQT_FAIRQ: return (FAIRQ_STATS_VERSION); + case ALTQT_HFSC: return (HFSC_STATS_VERSION); + case ALTQT_PRIQ: return (PRIQ_STATS_VERSION); + default: return (0); + } +} + #endif /* _ALTQ_ALTQ_H_ */ Index: head/sys/net/altq/altq_cbq.h =================================================================== --- head/sys/net/altq/altq_cbq.h +++ head/sys/net/altq/altq_cbq.h @@ -99,6 +99,12 @@ struct codel_stats codel; } class_stats_t; +/* + * CBQ_STATS_VERSION is defined in altq.h to work around issues stemming + * from mixing of public-API and internal bits in each scheduler-specific + * header. + */ + #ifdef ALTQ3_COMPAT /* * Define structures associated with IOCTLS for cbq. Index: head/sys/net/altq/altq_cbq.c =================================================================== --- head/sys/net/altq/altq_cbq.c +++ head/sys/net/altq/altq_cbq.c @@ -452,7 +452,7 @@ } int -cbq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes) +cbq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes, int version) { cbq_state_t *cbqp; struct rm_class *cl; Index: head/sys/net/altq/altq_codel.h =================================================================== --- head/sys/net/altq/altq_codel.h +++ head/sys/net/altq/altq_codel.h @@ -57,6 +57,12 @@ struct pktcntr cl_dropcnt; /* dropped packet counter */ }; +/* + * CBQ_STATS_VERSION is defined in altq.h to work around issues stemming + * from mixing of public-API and internal bits in each scheduler-specific + * header. + */ + #ifdef _KERNEL #include Index: head/sys/net/altq/altq_codel.c =================================================================== --- head/sys/net/altq/altq_codel.c +++ head/sys/net/altq/altq_codel.c @@ -156,7 +156,7 @@ } int -codel_getqstats(struct pf_altq *a, void *ubuf, int *nbytes) +codel_getqstats(struct pf_altq *a, void *ubuf, int *nbytes, int version) { struct codel_if *cif; struct codel_ifstats stats; Index: head/sys/net/altq/altq_fairq.h =================================================================== --- head/sys/net/altq/altq_fairq.h +++ head/sys/net/altq/altq_fairq.h @@ -82,6 +82,12 @@ struct codel_stats codel; }; +/* + * FAIRQ_STATS_VERSION is defined in altq.h to work around issues stemming + * from mixing of public-API and internal bits in each scheduler-specific + * header. + */ + #ifdef _KERNEL typedef struct fairq_bucket { Index: head/sys/net/altq/altq_fairq.c =================================================================== --- head/sys/net/altq/altq_fairq.c +++ head/sys/net/altq/altq_fairq.c @@ -229,7 +229,7 @@ } int -fairq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes) +fairq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes, int version) { struct fairq_if *pif; struct fairq_class *cl; Index: head/sys/net/altq/altq_hfsc.h =================================================================== --- head/sys/net/altq/altq_hfsc.h +++ head/sys/net/altq/altq_hfsc.h @@ -43,12 +43,21 @@ extern "C" { #endif -struct service_curve { +struct service_curve_v0 { u_int m1; /* slope of the first segment in bits/sec */ u_int d; /* the x-projection of the first segment in msec */ u_int m2; /* slope of the second segment in bits/sec */ }; +struct service_curve_v1 { + u_int64_t m1; /* slope of the first segment in bits/sec */ + u_int d; /* the x-projection of the first segment in msec */ + u_int64_t m2; /* slope of the second segment in bits/sec */ +}; + +/* Latest version of struct service_curve_vX */ +#define HFSC_SERVICE_CURVE_VERSION 1 + /* special class handles */ #define HFSC_NULLCLASS_HANDLE 0 #define HFSC_MAX_CLASSES 64 @@ -67,12 +76,12 @@ #define HFSC_UPPERLIMITSC 4 #define HFSC_DEFAULTSC (HFSC_REALTIMESC|HFSC_LINKSHARINGSC) -struct hfsc_classstats { +struct hfsc_classstats_v0 { u_int class_id; u_int32_t class_handle; - struct service_curve rsc; - struct service_curve fsc; - struct service_curve usc; /* upper limit service curve */ + struct service_curve_v0 rsc; + struct service_curve_v0 fsc; + struct service_curve_v0 usc; /* upper limit service curve */ u_int64_t total; /* total work in bytes */ u_int64_t cumul; /* cumulative work in bytes @@ -110,6 +119,55 @@ struct codel_stats codel; }; +struct hfsc_classstats_v1 { + u_int class_id; + u_int32_t class_handle; + struct service_curve_v1 rsc; + struct service_curve_v1 fsc; + struct service_curve_v1 usc; /* upper limit service curve */ + + u_int64_t total; /* total work in bytes */ + u_int64_t cumul; /* cumulative work in bytes + done by real-time criteria */ + u_int64_t d; /* deadline */ + u_int64_t e; /* eligible time */ + u_int64_t vt; /* virtual time */ + u_int64_t f; /* fit time for upper-limit */ + + /* info helpful for debugging */ + u_int64_t initvt; /* init virtual time */ + u_int64_t vtoff; /* cl_vt_ipoff */ + u_int64_t cvtmax; /* cl_maxvt */ + u_int64_t myf; /* cl_myf */ + u_int64_t cfmin; /* cl_mincf */ + u_int64_t cvtmin; /* cl_mincvt */ + u_int64_t myfadj; /* cl_myfadj */ + u_int64_t vtadj; /* cl_vtadj */ + u_int64_t cur_time; + u_int32_t machclk_freq; + + u_int qlength; + u_int qlimit; + struct pktcntr xmit_cnt; + struct pktcntr drop_cnt; + u_int period; + + u_int vtperiod; /* vt period sequence no */ + u_int parentperiod; /* parent's vt period seqno */ + int nactive; /* number of active children */ + + /* codel, red and rio related info */ + int qtype; + struct redstats red[3]; + struct codel_stats codel; +}; + +/* + * HFSC_STATS_VERSION is defined in altq.h to work around issues stemming + * from mixing of public-API and internal bits in each scheduler-specific + * header. + */ + #ifdef ALTQ3_COMPAT struct hfsc_interface { char hfsc_ifname[IFNAMSIZ]; /* interface name (e.g., fxp0) */ @@ -309,6 +367,35 @@ struct acc_classifier hif_classifier; #endif }; + +/* + * Kernel code always wants the latest version - avoid a bunch of renames in + * the code to the current latest versioned name. + */ +#define service_curve __CONCAT(service_curve_v, HFSC_SERVICE_CURVE_VERSION) + +#else /* _KERNEL */ + +#ifdef PFIOC_USE_LATEST +/* + * Maintaining in-tree consumers of the ioctl interface is easier when that + * code can be written in terms old names that refer to the latest interface + * version as that reduces the required changes in the consumers to those + * that are functionally necessary to accommodate a new interface version. + */ +#define hfsc_classstats __CONCAT(hfsc_classstats_v, HFSC_STATS_VERSION) +#define service_curve __CONCAT(service_curve_v, HFSC_SERVICE_CURVE_VERSION) + +#else +/* + * When building out-of-tree code that is written for the old interface, + * such as may exist in ports for example, resolve the old struct tags to + * the v0 versions. + */ +#define hfsc_classstats __CONCAT(hfsc_classstats_v, 0) +#define service_curve __CONCAT(service_curve_v, 0) + +#endif /* PFIOC_USE_LATEST */ #endif /* _KERNEL */ Index: head/sys/net/altq/altq_hfsc.c =================================================================== --- head/sys/net/altq/altq_hfsc.c +++ head/sys/net/altq/altq_hfsc.c @@ -116,10 +116,10 @@ static __inline u_int64_t seg_x2y(u_int64_t, u_int64_t); static __inline u_int64_t seg_y2x(u_int64_t, u_int64_t); -static __inline u_int64_t m2sm(u_int); -static __inline u_int64_t m2ism(u_int); +static __inline u_int64_t m2sm(u_int64_t); +static __inline u_int64_t m2ism(u_int64_t); static __inline u_int64_t d2dx(u_int); -static u_int sm2m(u_int64_t); +static u_int64_t sm2m(u_int64_t); static u_int dx2d(u_int64_t); static void sc2isc(struct service_curve *, struct internal_sc *); @@ -130,8 +130,10 @@ static void rtsc_min(struct runtime_sc *, struct internal_sc *, u_int64_t, u_int64_t); -static void get_class_stats(struct hfsc_classstats *, +static void get_class_stats_v0(struct hfsc_classstats_v0 *, struct hfsc_class *); +static void get_class_stats_v1(struct hfsc_classstats_v1 *, + struct hfsc_class *); static struct hfsc_class *clh_to_clp(struct hfsc_if *, u_int32_t); @@ -158,7 +160,7 @@ */ #define is_a_parent_class(cl) ((cl)->cl_children != NULL) -#define HT_INFINITY 0xffffffffffffffffLL /* infinite time value */ +#define HT_INFINITY 0xffffffffffffffffULL /* infinite time value */ #ifdef ALTQ3_COMPAT /* hif_list keeps all hfsc_if's allocated. */ @@ -226,7 +228,7 @@ { struct hfsc_if *hif; struct hfsc_class *cl, *parent; - struct hfsc_opts *opts; + struct hfsc_opts_v1 *opts; struct service_curve rtsc, lssc, ulsc; if ((hif = a->altq_disc) == NULL) @@ -280,11 +282,15 @@ } int -hfsc_getqstats(struct pf_altq *a, void *ubuf, int *nbytes) +hfsc_getqstats(struct pf_altq *a, void *ubuf, int *nbytes, int version) { struct hfsc_if *hif; struct hfsc_class *cl; - struct hfsc_classstats stats; + union { + struct hfsc_classstats_v0 v0; + struct hfsc_classstats_v1 v1; + } stats; + size_t stats_size; int error = 0; if ((hif = altq_lookup(a->ifname, ALTQT_HFSC)) == NULL) @@ -293,14 +299,27 @@ if ((cl = clh_to_clp(hif, a->qid)) == NULL) return (EINVAL); - if (*nbytes < sizeof(stats)) + if (version > HFSC_STATS_VERSION) return (EINVAL); - get_class_stats(&stats, cl); + memset(&stats, 0, sizeof(stats)); + switch (version) { + case 0: + get_class_stats_v0(&stats.v0, cl); + stats_size = sizeof(struct hfsc_classstats_v0); + break; + case 1: + get_class_stats_v1(&stats.v1, cl); + stats_size = sizeof(struct hfsc_classstats_v1); + break; + } - if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0) + if (*nbytes < stats_size) + return (EINVAL); + + if ((error = copyout((caddr_t)&stats, ubuf, stats_size)) != 0) return (error); - *nbytes = sizeof(stats); + *nbytes = stats_size; return (0); } @@ -1357,27 +1376,17 @@ * m: bits/sec * d: msec * internal service curve parameters - * sm: (bytes/tsc_interval) << SM_SHIFT - * ism: (tsc_count/byte) << ISM_SHIFT - * dx: tsc_count + * sm: (bytes/machclk tick) << SM_SHIFT + * ism: (machclk ticks/byte) << ISM_SHIFT + * dx: machclk ticks * - * SM_SHIFT and ISM_SHIFT are scaled in order to keep effective digits. - * we should be able to handle 100K-1Gbps linkspeed with 200Hz-1GHz CPU - * speed. SM_SHIFT and ISM_SHIFT are selected to have at least 3 effective - * digits in decimal using the following table. + * SM_SHIFT and ISM_SHIFT are scaled in order to keep effective digits. we + * should be able to handle 100K-100Gbps linkspeed with 256 MHz machclk + * frequency and at least 3 effective digits in decimal. * - * bits/sec 100Kbps 1Mbps 10Mbps 100Mbps 1Gbps - * ----------+------------------------------------------------------- - * bytes/nsec 12.5e-6 125e-6 1250e-6 12500e-6 125000e-6 - * sm(500MHz) 25.0e-6 250e-6 2500e-6 25000e-6 250000e-6 - * sm(200MHz) 62.5e-6 625e-6 6250e-6 62500e-6 625000e-6 - * - * nsec/byte 80000 8000 800 80 8 - * ism(500MHz) 40000 4000 400 40 4 - * ism(200MHz) 16000 1600 160 16 1.6 */ #define SM_SHIFT 24 -#define ISM_SHIFT 10 +#define ISM_SHIFT 14 #define SM_MASK ((1LL << SM_SHIFT) - 1) #define ISM_MASK ((1LL << ISM_SHIFT) - 1) @@ -1413,16 +1422,16 @@ } static __inline u_int64_t -m2sm(u_int m) +m2sm(u_int64_t m) { u_int64_t sm; - sm = ((u_int64_t)m << SM_SHIFT) / 8 / machclk_freq; + sm = (m << SM_SHIFT) / 8 / machclk_freq; return (sm); } static __inline u_int64_t -m2ism(u_int m) +m2ism(u_int64_t m) { u_int64_t ism; @@ -1442,13 +1451,13 @@ return (dx); } -static u_int +static u_int64_t sm2m(u_int64_t sm) { u_int64_t m; m = (sm * 8 * machclk_freq) >> SM_SHIFT; - return ((u_int)m); + return (m); } static u_int @@ -1597,7 +1606,89 @@ } static void -get_class_stats(struct hfsc_classstats *sp, struct hfsc_class *cl) +get_class_stats_v0(struct hfsc_classstats_v0 *sp, struct hfsc_class *cl) +{ + sp->class_id = cl->cl_id; + sp->class_handle = cl->cl_handle; + +#define SATU32(x) (u_int32_t)uqmin((x), UINT_MAX) + + if (cl->cl_rsc != NULL) { + sp->rsc.m1 = SATU32(sm2m(cl->cl_rsc->sm1)); + sp->rsc.d = dx2d(cl->cl_rsc->dx); + sp->rsc.m2 = SATU32(sm2m(cl->cl_rsc->sm2)); + } else { + sp->rsc.m1 = 0; + sp->rsc.d = 0; + sp->rsc.m2 = 0; + } + if (cl->cl_fsc != NULL) { + sp->fsc.m1 = SATU32(sm2m(cl->cl_fsc->sm1)); + sp->fsc.d = dx2d(cl->cl_fsc->dx); + sp->fsc.m2 = SATU32(sm2m(cl->cl_fsc->sm2)); + } else { + sp->fsc.m1 = 0; + sp->fsc.d = 0; + sp->fsc.m2 = 0; + } + if (cl->cl_usc != NULL) { + sp->usc.m1 = SATU32(sm2m(cl->cl_usc->sm1)); + sp->usc.d = dx2d(cl->cl_usc->dx); + sp->usc.m2 = SATU32(sm2m(cl->cl_usc->sm2)); + } else { + sp->usc.m1 = 0; + sp->usc.d = 0; + sp->usc.m2 = 0; + } + +#undef SATU32 + + sp->total = cl->cl_total; + sp->cumul = cl->cl_cumul; + + sp->d = cl->cl_d; + sp->e = cl->cl_e; + sp->vt = cl->cl_vt; + sp->f = cl->cl_f; + + sp->initvt = cl->cl_initvt; + sp->vtperiod = cl->cl_vtperiod; + sp->parentperiod = cl->cl_parentperiod; + sp->nactive = cl->cl_nactive; + sp->vtoff = cl->cl_vtoff; + sp->cvtmax = cl->cl_cvtmax; + sp->myf = cl->cl_myf; + sp->cfmin = cl->cl_cfmin; + sp->cvtmin = cl->cl_cvtmin; + sp->myfadj = cl->cl_myfadj; + sp->vtadj = cl->cl_vtadj; + + sp->cur_time = read_machclk(); + sp->machclk_freq = machclk_freq; + + sp->qlength = qlen(cl->cl_q); + sp->qlimit = qlimit(cl->cl_q); + sp->xmit_cnt = cl->cl_stats.xmit_cnt; + sp->drop_cnt = cl->cl_stats.drop_cnt; + sp->period = cl->cl_stats.period; + + sp->qtype = qtype(cl->cl_q); +#ifdef ALTQ_RED + if (q_is_red(cl->cl_q)) + red_getstats(cl->cl_red, &sp->red[0]); +#endif +#ifdef ALTQ_RIO + if (q_is_rio(cl->cl_q)) + rio_getstats((rio_t *)cl->cl_red, &sp->red[0]); +#endif +#ifdef ALTQ_CODEL + if (q_is_codel(cl->cl_q)) + codel_getstats(cl->cl_codel, &sp->codel); +#endif +} + +static void +get_class_stats_v1(struct hfsc_classstats_v1 *sp, struct hfsc_class *cl) { sp->class_id = cl->cl_id; sp->class_handle = cl->cl_handle; Index: head/sys/net/altq/altq_priq.h =================================================================== --- head/sys/net/altq/altq_priq.h +++ head/sys/net/altq/altq_priq.h @@ -112,6 +112,12 @@ struct codel_stats codel; }; +/* + * PRIQ_STATS_VERSION is defined in altq.h to work around issues stemming + * from mixing of public-API and internal bits in each scheduler-specific + * header. + */ + #ifdef ALTQ3_COMPAT struct priq_class_stats { struct priq_interface iface; Index: head/sys/net/altq/altq_priq.c =================================================================== --- head/sys/net/altq/altq_priq.c +++ head/sys/net/altq/altq_priq.c @@ -199,7 +199,7 @@ } int -priq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes) +priq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes, int version) { struct priq_if *pif; struct priq_class *cl; Index: head/sys/net/altq/altq_subr.c =================================================================== --- head/sys/net/altq/altq_subr.c +++ head/sys/net/altq/altq_subr.c @@ -292,12 +292,12 @@ /* * internal representation of token bucket parameters - * rate: byte_per_unittime << 32 - * (((bits_per_sec) / 8) << 32) / machclk_freq - * depth: byte << 32 + * rate: (byte_per_unittime << TBR_SHIFT) / machclk_freq + * (((bits_per_sec) / 8) << TBR_SHIFT) / machclk_freq + * depth: byte << TBR_SHIFT * */ -#define TBR_SHIFT 32 +#define TBR_SHIFT 29 #define TBR_SCALE(x) ((int64_t)(x) << TBR_SHIFT) #define TBR_UNSCALE(x) ((x) >> TBR_SHIFT) @@ -394,7 +394,20 @@ if (tbr->tbr_rate > 0) tbr->tbr_filluptime = tbr->tbr_depth / tbr->tbr_rate; else - tbr->tbr_filluptime = 0xffffffffffffffffLL; + tbr->tbr_filluptime = LLONG_MAX; + /* + * The longest time between tbr_dequeue() calls will be about 1 + * system tick, as the callout that drives it is scheduled once per + * tick. The refill-time detection logic in tbr_dequeue() can only + * properly detect the passage of up to LLONG_MAX machclk ticks. + * Therefore, in order for this logic to function properly in the + * extreme case, the maximum value of tbr_filluptime should be + * LLONG_MAX less one system tick's worth of machclk ticks less + * some additional slop factor (here one more system tick's worth + * of machclk ticks). + */ + if (tbr->tbr_filluptime > (LLONG_MAX - 2 * machclk_per_tick)) + tbr->tbr_filluptime = LLONG_MAX - 2 * machclk_per_tick; tbr->tbr_token = tbr->tbr_depth; tbr->tbr_last = read_machclk(); tbr->tbr_lastop = ALTDQ_REMOVE; @@ -456,29 +469,6 @@ } /* - * get token bucket regulator profile - */ -int -tbr_get(ifq, profile) - struct ifaltq *ifq; - struct tb_profile *profile; -{ - struct tb_regulator *tbr; - - IFQ_LOCK(ifq); - if ((tbr = ifq->altq_tbr) == NULL) { - profile->rate = 0; - profile->depth = 0; - } else { - profile->rate = - (u_int)TBR_UNSCALE(tbr->tbr_rate * 8 * machclk_freq); - profile->depth = (u_int)TBR_UNSCALE(tbr->tbr_depth); - } - IFQ_UNLOCK(ifq); - return (0); -} - -/* * attach a discipline to the interface. if one already exists, it is * overridden. * Locking is done in the discipline specific attach functions. Basically @@ -733,34 +723,34 @@ * copyout operations, also it is not yet clear which lock to use. */ int -altq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes) +altq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes, int version) { int error = 0; switch (a->scheduler) { #ifdef ALTQ_CBQ case ALTQT_CBQ: - error = cbq_getqstats(a, ubuf, nbytes); + error = cbq_getqstats(a, ubuf, nbytes, version); break; #endif #ifdef ALTQ_PRIQ case ALTQT_PRIQ: - error = priq_getqstats(a, ubuf, nbytes); + error = priq_getqstats(a, ubuf, nbytes, version); break; #endif #ifdef ALTQ_HFSC case ALTQT_HFSC: - error = hfsc_getqstats(a, ubuf, nbytes); + error = hfsc_getqstats(a, ubuf, nbytes, version); break; #endif #ifdef ALTQ_FAIRQ case ALTQT_FAIRQ: - error = fairq_getqstats(a, ubuf, nbytes); + error = fairq_getqstats(a, ubuf, nbytes, version); break; #endif #ifdef ALTQ_CODEL case ALTQT_CODEL: - error = codel_getqstats(a, ubuf, nbytes); + error = codel_getqstats(a, ubuf, nbytes, version); break; #endif default: Index: head/sys/net/altq/altq_var.h =================================================================== --- head/sys/net/altq/altq_var.h +++ head/sys/net/altq/altq_var.h @@ -196,7 +196,6 @@ void write_dsfield(struct mbuf *, struct altq_pktattr *, u_int8_t); void altq_assert(const char *, int, const char *); int tbr_set(struct ifaltq *, struct tb_profile *); -int tbr_get(struct ifaltq *, struct tb_profile *); int altq_pfattach(struct pf_altq *); int altq_pfdetach(struct pf_altq *); @@ -204,40 +203,40 @@ int altq_remove(struct pf_altq *); int altq_add_queue(struct pf_altq *); int altq_remove_queue(struct pf_altq *); -int altq_getqstats(struct pf_altq *, void *, int *); +int altq_getqstats(struct pf_altq *, void *, int *, int); int cbq_pfattach(struct pf_altq *); int cbq_add_altq(struct pf_altq *); int cbq_remove_altq(struct pf_altq *); int cbq_add_queue(struct pf_altq *); int cbq_remove_queue(struct pf_altq *); -int cbq_getqstats(struct pf_altq *, void *, int *); +int cbq_getqstats(struct pf_altq *, void *, int *, int); int codel_pfattach(struct pf_altq *); int codel_add_altq(struct pf_altq *); int codel_remove_altq(struct pf_altq *); -int codel_getqstats(struct pf_altq *, void *, int *); +int codel_getqstats(struct pf_altq *, void *, int *, int); int priq_pfattach(struct pf_altq *); int priq_add_altq(struct pf_altq *); int priq_remove_altq(struct pf_altq *); int priq_add_queue(struct pf_altq *); int priq_remove_queue(struct pf_altq *); -int priq_getqstats(struct pf_altq *, void *, int *); +int priq_getqstats(struct pf_altq *, void *, int *, int); int hfsc_pfattach(struct pf_altq *); int hfsc_add_altq(struct pf_altq *); int hfsc_remove_altq(struct pf_altq *); int hfsc_add_queue(struct pf_altq *); int hfsc_remove_queue(struct pf_altq *); -int hfsc_getqstats(struct pf_altq *, void *, int *); +int hfsc_getqstats(struct pf_altq *, void *, int *, int); int fairq_pfattach(struct pf_altq *); int fairq_add_altq(struct pf_altq *); int fairq_remove_altq(struct pf_altq *); int fairq_add_queue(struct pf_altq *); int fairq_remove_queue(struct pf_altq *); -int fairq_getqstats(struct pf_altq *, void *, int *); +int fairq_getqstats(struct pf_altq *, void *, int *, int); #endif /* _KERNEL */ #endif /* _ALTQ_ALTQ_VAR_H_ */ Index: head/sys/net/pfvar.h =================================================================== --- head/sys/net/pfvar.h +++ head/sys/net/pfvar.h @@ -1300,21 +1300,56 @@ unsigned limit; }; -struct pfioc_altq { +struct pfioc_altq_v0 { u_int32_t action; u_int32_t ticket; u_int32_t nr; - struct pf_altq altq; + struct pf_altq_v0 altq; }; -struct pfioc_qstats { +struct pfioc_altq_v1 { + u_int32_t action; u_int32_t ticket; u_int32_t nr; + /* + * Placed here so code that only uses the above parameters can be + * written entirely in terms of the v0 or v1 type. + */ + u_int32_t version; + struct pf_altq_v1 altq; +}; + +/* + * Latest version of struct pfioc_altq_vX. This must move in lock-step with + * the latest version of struct pf_altq_vX as it has that struct as a + * member. + */ +#define PFIOC_ALTQ_VERSION PF_ALTQ_VERSION + +struct pfioc_qstats_v0 { + u_int32_t ticket; + u_int32_t nr; void *buf; int nbytes; u_int8_t scheduler; }; +struct pfioc_qstats_v1 { + u_int32_t ticket; + u_int32_t nr; + void *buf; + int nbytes; + u_int8_t scheduler; + /* + * Placed here so code that only uses the above parameters can be + * written entirely in terms of the v0 or v1 type. + */ + u_int32_t version; /* Requested version of stats struct */ +}; + +/* Latest version of struct pfioc_qstats_vX */ +#define PFIOC_QSTATS_VERSION 1 + struct pfioc_ruleset { u_int32_t nr; char path[MAXPATHLEN]; @@ -1403,11 +1438,16 @@ #define DIOCKILLSTATES _IOWR('D', 41, struct pfioc_state_kill) #define DIOCSTARTALTQ _IO ('D', 42) #define DIOCSTOPALTQ _IO ('D', 43) -#define DIOCADDALTQ _IOWR('D', 45, struct pfioc_altq) -#define DIOCGETALTQS _IOWR('D', 47, struct pfioc_altq) -#define DIOCGETALTQ _IOWR('D', 48, struct pfioc_altq) -#define DIOCCHANGEALTQ _IOWR('D', 49, struct pfioc_altq) -#define DIOCGETQSTATS _IOWR('D', 50, struct pfioc_qstats) +#define DIOCADDALTQV0 _IOWR('D', 45, struct pfioc_altq_v0) +#define DIOCADDALTQV1 _IOWR('D', 45, struct pfioc_altq_v1) +#define DIOCGETALTQSV0 _IOWR('D', 47, struct pfioc_altq_v0) +#define DIOCGETALTQSV1 _IOWR('D', 47, struct pfioc_altq_v1) +#define DIOCGETALTQV0 _IOWR('D', 48, struct pfioc_altq_v0) +#define DIOCGETALTQV1 _IOWR('D', 48, struct pfioc_altq_v1) +#define DIOCCHANGEALTQV0 _IOWR('D', 49, struct pfioc_altq_v0) +#define DIOCCHANGEALTQV1 _IOWR('D', 49, struct pfioc_altq_v1) +#define DIOCGETQSTATSV0 _IOWR('D', 50, struct pfioc_qstats_v0) +#define DIOCGETQSTATSV1 _IOWR('D', 50, struct pfioc_qstats_v1) #define DIOCBEGINADDRS _IOWR('D', 51, struct pfioc_pooladdr) #define DIOCADDADDR _IOWR('D', 52, struct pfioc_pooladdr) #define DIOCGETADDRS _IOWR('D', 53, struct pfioc_pooladdr) @@ -1445,11 +1485,63 @@ #define DIOCSETIFFLAG _IOWR('D', 89, struct pfioc_iface) #define DIOCCLRIFFLAG _IOWR('D', 90, struct pfioc_iface) #define DIOCKILLSRCNODES _IOWR('D', 91, struct pfioc_src_node_kill) -struct pf_ifspeed { +struct pf_ifspeed_v0 { char ifname[IFNAMSIZ]; u_int32_t baudrate; }; -#define DIOCGIFSPEED _IOWR('D', 92, struct pf_ifspeed) + +struct pf_ifspeed_v1 { + char ifname[IFNAMSIZ]; + u_int32_t baudrate32; + /* layout identical to struct pf_ifspeed_v0 up to this point */ + u_int64_t baudrate; +}; + +/* Latest version of struct pf_ifspeed_vX */ +#define PF_IFSPEED_VERSION 1 + +#define DIOCGIFSPEEDV0 _IOWR('D', 92, struct pf_ifspeed_v0) +#define DIOCGIFSPEEDV1 _IOWR('D', 92, struct pf_ifspeed_v1) + +/* + * Compatibility and convenience macros + */ +#ifndef _KERNEL +#ifdef PFIOC_USE_LATEST +/* + * Maintaining in-tree consumers of the ioctl interface is easier when that + * code can be written in terms old names that refer to the latest interface + * version as that reduces the required changes in the consumers to those + * that are functionally necessary to accommodate a new interface version. + */ +#define pfioc_altq __CONCAT(pfioc_altq_v, PFIOC_ALTQ_VERSION) +#define pfioc_qstats __CONCAT(pfioc_qstats_v, PFIOC_QSTATS_VERSION) +#define pf_ifspeed __CONCAT(pf_ifspeed_v, PF_IFSPEED_VERSION) + +#define DIOCADDALTQ __CONCAT(DIOCADDALTQV, PFIOC_ALTQ_VERSION) +#define DIOCGETALTQS __CONCAT(DIOCGETALTQSV, PFIOC_ALTQ_VERSION) +#define DIOCGETALTQ __CONCAT(DIOCGETALTQV, PFIOC_ALTQ_VERSION) +#define DIOCCHANGEALTQ __CONCAT(DIOCCHANGEALTQV, PFIOC_ALTQ_VERSION) +#define DIOCGETQSTATS __CONCAT(DIOCGETQSTATSV, PFIOC_QSTATS_VERSION) +#define DIOCGIFSPEED __CONCAT(DIOCGIFSPEEDV, PF_IFSPEED_VERSION) +#else +/* + * When building out-of-tree code that is written for the old interface, + * such as may exist in ports for example, resolve the old struct tags and + * ioctl command names to the v0 versions. + */ +#define pfioc_altq __CONCAT(pfioc_altq_v, 0) +#define pfioc_qstats __CONCAT(pfioc_qstats_v, 0) +#define pf_ifspeed __CONCAT(pf_ifspeed_v, 0) + +#define DIOCADDALTQ __CONCAT(DIOCADDALTQV, 0) +#define DIOCGETALTQS __CONCAT(DIOCGETALTQSV, 0) +#define DIOCGETALTQ __CONCAT(DIOCGETALTQV, 0) +#define DIOCCHANGEALTQ __CONCAT(DIOCCHANGEALTQV, 0) +#define DIOCGETQSTATS __CONCAT(DIOCGETQSTATSV, 0) +#define DIOCGIFSPEED __CONCAT(DIOCGIFSPEEDV, 0) +#endif /* PFIOC_USE_LATEST */ +#endif /* _KERNEL */ #ifdef _KERNEL LIST_HEAD(pf_src_node_list, pf_src_node); Index: head/sys/netpfil/pf/pf_altq.h =================================================================== --- head/sys/netpfil/pf/pf_altq.h +++ head/sys/netpfil/pf/pf_altq.h @@ -57,7 +57,7 @@ int flags; }; -struct hfsc_opts { +struct hfsc_opts_v0 { /* real-time service curve */ u_int rtsc_m1; /* slope of the 1st segment in bps */ u_int rtsc_d; /* the x-projection of m1 in msec */ @@ -73,7 +73,32 @@ int flags; }; +struct hfsc_opts_v1 { + /* real-time service curve */ + u_int64_t rtsc_m1; /* slope of the 1st segment in bps */ + u_int rtsc_d; /* the x-projection of m1 in msec */ + u_int64_t rtsc_m2; /* slope of the 2nd segment in bps */ + /* link-sharing service curve */ + u_int64_t lssc_m1; + u_int lssc_d; + u_int64_t lssc_m2; + /* upper-limit service curve */ + u_int64_t ulsc_m1; + u_int ulsc_d; + u_int64_t ulsc_m2; + int flags; +}; + /* + * struct hfsc_opts doesn't have a version indicator macro or + * backwards-compat and convenience macros because both in the kernel and + * the pfctl parser, there are struct hfsc_opts instances named 'hfsc_opts'. + * It is believed that only in-tree code uses struct hfsc_opts, so + * backwards-compat macros are not necessary. The few in-tree uses can just + * be updated to the latest versioned struct tag. + */ + +/* * XXX this needs some work */ struct fairq_opts { @@ -87,11 +112,22 @@ u_int lssc_m2; }; -struct pf_altq { +/* + * struct pf_altq_v0, struct pf_altq_v1, etc. are the ioctl argument + * structures corresponding to struct pfioc_altq_v0, struct pfioc_altq_v1, + * etc. + * + */ +struct pf_altq_v0 { char ifname[IFNAMSIZ]; - void *altq_disc; /* discipline-specific state */ - TAILQ_ENTRY(pf_altq) entries; + /* + * This member is a holdover from when the kernel state structure + * was reused as the ioctl argument structure, and remains to + * preserve the size and layout of this struct for backwards compat. + */ + void *unused1; + TAILQ_ENTRY(pf_altq_v0) entries; /* scheduler spec */ uint8_t scheduler; /* scheduler type */ @@ -113,11 +149,110 @@ struct cbq_opts cbq_opts; struct codel_opts codel_opts; struct priq_opts priq_opts; - struct hfsc_opts hfsc_opts; + struct hfsc_opts_v0 hfsc_opts; struct fairq_opts fairq_opts; } pq_u; uint32_t qid; /* return value */ }; + +struct pf_altq_v1 { + char ifname[IFNAMSIZ]; + + TAILQ_ENTRY(pf_altq_v1) entries; + + /* scheduler spec */ + uint8_t scheduler; /* scheduler type */ + uint32_t tbrsize; /* tokenbucket regulator size */ + uint64_t ifbandwidth; /* interface bandwidth */ + + /* queue spec */ + char qname[PF_QNAME_SIZE]; /* queue name */ + char parent[PF_QNAME_SIZE]; /* parent name */ + uint32_t parent_qid; /* parent queue id */ + uint64_t bandwidth; /* queue bandwidth */ + uint8_t priority; /* priority */ + uint8_t local_flags; /* dynamic interface, see _v0 */ + + uint16_t qlimit; /* queue size limit */ + uint16_t flags; /* misc flags */ + union { + struct cbq_opts cbq_opts; + struct codel_opts codel_opts; + struct priq_opts priq_opts; + struct hfsc_opts_v1 hfsc_opts; + struct fairq_opts fairq_opts; + } pq_u; + + uint32_t qid; /* return value */ +}; + +/* Latest version of struct pf_altq_vX */ +#define PF_ALTQ_VERSION 1 + +#ifdef _KERNEL +struct pf_kaltq { + char ifname[IFNAMSIZ]; + + void *altq_disc; /* discipline-specific state */ + TAILQ_ENTRY(pf_kaltq) entries; + + /* scheduler spec */ + uint8_t scheduler; /* scheduler type */ + uint32_t tbrsize; /* tokenbucket regulator size */ + uint64_t ifbandwidth; /* interface bandwidth */ + + /* queue spec */ + char qname[PF_QNAME_SIZE]; /* queue name */ + char parent[PF_QNAME_SIZE]; /* parent name */ + uint32_t parent_qid; /* parent queue id */ + uint64_t bandwidth; /* queue bandwidth */ + uint8_t priority; /* priority */ + uint8_t local_flags; /* dynamic interface, see _v0 */ + + uint16_t qlimit; /* queue size limit */ + uint16_t flags; /* misc flags */ + union { + struct cbq_opts cbq_opts; + struct codel_opts codel_opts; + struct priq_opts priq_opts; + struct hfsc_opts_v1 hfsc_opts; + struct fairq_opts fairq_opts; + } pq_u; + + uint32_t qid; /* return value */ +}; +#endif /* _KERNEL */ + +/* + * Compatibility and convenience macros + */ +#ifdef _KERNEL +/* + * Avoid a patch with 100+ lines of name substitution. + */ +#define pf_altq pf_kaltq + +#else /* _KERNEL */ + +#ifdef PFIOC_USE_LATEST +/* + * Maintaining in-tree consumers of the ioctl interface is easier when that + * code can be written in terms old names that refer to the latest interface + * version as that reduces the required changes in the consumers to those + * that are functionally necessary to accommodate a new interface version. + */ +#define pf_altq __CONCAT(pf_altq_v, PF_ALTQ_VERSION) + +#else /* PFIOC_USE_LATEST */ +/* + * When building out-of-tree code that is written for the old interface, + * such as may exist in ports for example, resolve the old pf_altq struct + * tag to the v0 version. + */ +#define pf_altq __CONCAT(pf_altq_v, 0) + +#endif /* PFIOC_USE_LATEST */ +#endif /* _KERNEL */ #endif /* _NET_PF_ALTQ_H_ */ Index: head/sys/netpfil/pf/pf_ioctl.c =================================================================== --- head/sys/netpfil/pf/pf_ioctl.c +++ head/sys/netpfil/pf/pf_ioctl.c @@ -113,6 +113,12 @@ static int pf_addr_setup(struct pf_ruleset *, struct pf_addr_wrap *, sa_family_t); static void pf_addr_copyout(struct pf_addr_wrap *); +#ifdef ALTQ +static int pf_export_kaltq(struct pf_altq *, + struct pfioc_altq_v1 *, size_t); +static int pf_import_kaltq(struct pfioc_altq_v1 *, + struct pf_altq *, size_t); +#endif /* ALTQ */ VNET_DEFINE(struct pf_rule, pf_default_rule); @@ -990,7 +996,223 @@ } } +#ifdef ALTQ +/* + * Handle export of struct pf_kaltq to user binaries that may be using any + * version of struct pf_altq. + */ static int +pf_export_kaltq(struct pf_altq *q, struct pfioc_altq_v1 *pa, size_t ioc_size) +{ + u_int32_t version; + + if (ioc_size == sizeof(struct pfioc_altq_v0)) + version = 0; + else + version = pa->version; + + if (version > PFIOC_ALTQ_VERSION) + return (EINVAL); + +#define ASSIGN(x) exported_q->x = q->x +#define COPY(x) \ + bcopy(&q->x, &exported_q->x, min(sizeof(q->x), sizeof(exported_q->x))) +#define SATU16(x) (u_int32_t)uqmin((x), USHRT_MAX) +#define SATU32(x) (u_int32_t)uqmin((x), UINT_MAX) + + switch (version) { + case 0: { + struct pf_altq_v0 *exported_q = + &((struct pfioc_altq_v0 *)pa)->altq; + + COPY(ifname); + + ASSIGN(scheduler); + ASSIGN(tbrsize); + exported_q->tbrsize = SATU16(q->tbrsize); + exported_q->ifbandwidth = SATU32(q->ifbandwidth); + + COPY(qname); + COPY(parent); + ASSIGN(parent_qid); + exported_q->bandwidth = SATU32(q->bandwidth); + ASSIGN(priority); + ASSIGN(local_flags); + + ASSIGN(qlimit); + ASSIGN(flags); + + if (q->scheduler == ALTQT_HFSC) { +#define ASSIGN_OPT(x) exported_q->pq_u.hfsc_opts.x = q->pq_u.hfsc_opts.x +#define ASSIGN_OPT_SATU32(x) exported_q->pq_u.hfsc_opts.x = \ + SATU32(q->pq_u.hfsc_opts.x) + + ASSIGN_OPT_SATU32(rtsc_m1); + ASSIGN_OPT(rtsc_d); + ASSIGN_OPT_SATU32(rtsc_m2); + + ASSIGN_OPT_SATU32(lssc_m1); + ASSIGN_OPT(lssc_d); + ASSIGN_OPT_SATU32(lssc_m2); + + ASSIGN_OPT_SATU32(ulsc_m1); + ASSIGN_OPT(ulsc_d); + ASSIGN_OPT_SATU32(ulsc_m2); + + ASSIGN_OPT(flags); + +#undef ASSIGN_OPT +#undef ASSIGN_OPT_SATU32 + } else + COPY(pq_u); + + ASSIGN(qid); + break; + } + case 1: { + struct pf_altq_v1 *exported_q = + &((struct pfioc_altq_v1 *)pa)->altq; + + COPY(ifname); + + ASSIGN(scheduler); + ASSIGN(tbrsize); + ASSIGN(ifbandwidth); + + COPY(qname); + COPY(parent); + ASSIGN(parent_qid); + ASSIGN(bandwidth); + ASSIGN(priority); + ASSIGN(local_flags); + + ASSIGN(qlimit); + ASSIGN(flags); + COPY(pq_u); + + ASSIGN(qid); + break; + } + default: + panic("%s: unhandled struct pfioc_altq version", __func__); + break; + } + +#undef ASSIGN +#undef COPY +#undef SATU16 +#undef SATU32 + + return (0); +} + +/* + * Handle import to struct pf_kaltq of struct pf_altq from user binaries + * that may be using any version of it. + */ +static int +pf_import_kaltq(struct pfioc_altq_v1 *pa, struct pf_altq *q, size_t ioc_size) +{ + u_int32_t version; + + if (ioc_size == sizeof(struct pfioc_altq_v0)) + version = 0; + else + version = pa->version; + + if (version > PFIOC_ALTQ_VERSION) + return (EINVAL); + +#define ASSIGN(x) q->x = imported_q->x +#define COPY(x) \ + bcopy(&imported_q->x, &q->x, min(sizeof(imported_q->x), sizeof(q->x))) + + switch (version) { + case 0: { + struct pf_altq_v0 *imported_q = + &((struct pfioc_altq_v0 *)pa)->altq; + + COPY(ifname); + + ASSIGN(scheduler); + ASSIGN(tbrsize); /* 16-bit -> 32-bit */ + ASSIGN(ifbandwidth); /* 32-bit -> 64-bit */ + + COPY(qname); + COPY(parent); + ASSIGN(parent_qid); + ASSIGN(bandwidth); /* 32-bit -> 64-bit */ + ASSIGN(priority); + ASSIGN(local_flags); + + ASSIGN(qlimit); + ASSIGN(flags); + + if (imported_q->scheduler == ALTQT_HFSC) { +#define ASSIGN_OPT(x) q->pq_u.hfsc_opts.x = imported_q->pq_u.hfsc_opts.x + + /* + * The m1 and m2 parameters are being copied from + * 32-bit to 64-bit. + */ + ASSIGN_OPT(rtsc_m1); + ASSIGN_OPT(rtsc_d); + ASSIGN_OPT(rtsc_m2); + + ASSIGN_OPT(lssc_m1); + ASSIGN_OPT(lssc_d); + ASSIGN_OPT(lssc_m2); + + ASSIGN_OPT(ulsc_m1); + ASSIGN_OPT(ulsc_d); + ASSIGN_OPT(ulsc_m2); + + ASSIGN_OPT(flags); + +#undef ASSIGN_OPT + } else + COPY(pq_u); + + ASSIGN(qid); + break; + } + case 1: { + struct pf_altq_v1 *imported_q = + &((struct pfioc_altq_v1 *)pa)->altq; + + COPY(ifname); + + ASSIGN(scheduler); + ASSIGN(tbrsize); + ASSIGN(ifbandwidth); + + COPY(qname); + COPY(parent); + ASSIGN(parent_qid); + ASSIGN(bandwidth); + ASSIGN(priority); + ASSIGN(local_flags); + + ASSIGN(qlimit); + ASSIGN(flags); + COPY(pq_u); + + ASSIGN(qid); + break; + } + default: + panic("%s: unhandled struct pfioc_altq version", __func__); + break; + } + +#undef ASSIGN +#undef COPY + + return (0); +} +#endif /* ALTQ */ + +static int pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { int error = 0; @@ -1013,9 +1235,12 @@ case DIOCGETTIMEOUT: case DIOCCLRRULECTRS: case DIOCGETLIMIT: - case DIOCGETALTQS: - case DIOCGETALTQ: - case DIOCGETQSTATS: + case DIOCGETALTQSV0: + case DIOCGETALTQSV1: + case DIOCGETALTQV0: + case DIOCGETALTQV1: + case DIOCGETQSTATSV0: + case DIOCGETQSTATSV1: case DIOCGETRULESETS: case DIOCGETRULESET: case DIOCRGETTABLES: @@ -1033,7 +1258,8 @@ case DIOCGETSRCNODES: case DIOCCLRSRCNODES: case DIOCIGETIFACES: - case DIOCGIFSPEED: + case DIOCGIFSPEEDV0: + case DIOCGIFSPEEDV1: case DIOCSETIFFLAG: case DIOCCLRIFFLAG: break; @@ -1059,9 +1285,12 @@ case DIOCGETSTATES: case DIOCGETTIMEOUT: case DIOCGETLIMIT: - case DIOCGETALTQS: - case DIOCGETALTQ: - case DIOCGETQSTATS: + case DIOCGETALTQSV0: + case DIOCGETALTQSV1: + case DIOCGETALTQV0: + case DIOCGETALTQV1: + case DIOCGETQSTATSV0: + case DIOCGETQSTATSV1: case DIOCGETRULESETS: case DIOCGETRULESET: case DIOCNATLOOK: @@ -1073,7 +1302,8 @@ case DIOCOSFPGET: case DIOCGETSRCNODES: case DIOCIGETIFACES: - case DIOCGIFSPEED: + case DIOCGIFSPEEDV1: + case DIOCGIFSPEEDV0: break; case DIOCRCLRTABLES: case DIOCRADDTABLES: @@ -2001,18 +2231,22 @@ break; } - case DIOCGIFSPEED: { - struct pf_ifspeed *psp = (struct pf_ifspeed *)addr; - struct pf_ifspeed ps; + case DIOCGIFSPEEDV0: + case DIOCGIFSPEEDV1: { + struct pf_ifspeed_v1 *psp = (struct pf_ifspeed_v1 *)addr; + struct pf_ifspeed_v1 ps; struct ifnet *ifp; if (psp->ifname[0] != 0) { /* Can we completely trust user-land? */ strlcpy(ps.ifname, psp->ifname, IFNAMSIZ); ifp = ifunit(ps.ifname); - if (ifp != NULL) - psp->baudrate = ifp->if_baudrate; - else + if (ifp != NULL) { + psp->baudrate32 = + (u_int32_t)uqmin(ifp->if_baudrate, UINT_MAX); + if (cmd == DIOCGIFSPEEDV1) + psp->baudrate = ifp->if_baudrate; + } else error = EINVAL; } else error = EINVAL; @@ -2060,13 +2294,16 @@ break; } - case DIOCADDALTQ: { - struct pfioc_altq *pa = (struct pfioc_altq *)addr; + case DIOCADDALTQV0: + case DIOCADDALTQV1: { + struct pfioc_altq_v1 *pa = (struct pfioc_altq_v1 *)addr; struct pf_altq *altq, *a; struct ifnet *ifp; - altq = malloc(sizeof(*altq), M_PFALTQ, M_WAITOK); - bcopy(&pa->altq, altq, sizeof(struct pf_altq)); + altq = malloc(sizeof(*altq), M_PFALTQ, M_WAITOK | M_ZERO); + error = pf_import_kaltq(pa, altq, IOCPARM_LEN(cmd)); + if (error) + break; altq->local_flags = 0; PF_RULES_WLOCK(); @@ -2110,13 +2347,15 @@ } TAILQ_INSERT_TAIL(V_pf_altqs_inactive, altq, entries); - bcopy(altq, &pa->altq, sizeof(struct pf_altq)); + /* version error check done on import above */ + pf_export_kaltq(altq, pa, IOCPARM_LEN(cmd)); PF_RULES_WUNLOCK(); break; } - case DIOCGETALTQS: { - struct pfioc_altq *pa = (struct pfioc_altq *)addr; + case DIOCGETALTQSV0: + case DIOCGETALTQSV1: { + struct pfioc_altq_v1 *pa = (struct pfioc_altq_v1 *)addr; struct pf_altq *altq; PF_RULES_RLOCK(); @@ -2128,8 +2367,9 @@ break; } - case DIOCGETALTQ: { - struct pfioc_altq *pa = (struct pfioc_altq *)addr; + case DIOCGETALTQV0: + case DIOCGETALTQV1: { + struct pfioc_altq_v1 *pa = (struct pfioc_altq_v1 *)addr; struct pf_altq *altq; u_int32_t nr; @@ -2150,21 +2390,24 @@ error = EBUSY; break; } - bcopy(altq, &pa->altq, sizeof(struct pf_altq)); + pf_export_kaltq(altq, pa, IOCPARM_LEN(cmd)); PF_RULES_RUNLOCK(); break; } - case DIOCCHANGEALTQ: + case DIOCCHANGEALTQV0: + case DIOCCHANGEALTQV1: /* CHANGEALTQ not supported yet! */ error = ENODEV; break; - case DIOCGETQSTATS: { - struct pfioc_qstats *pq = (struct pfioc_qstats *)addr; + case DIOCGETQSTATSV0: + case DIOCGETQSTATSV1: { + struct pfioc_qstats_v1 *pq = (struct pfioc_qstats_v1 *)addr; struct pf_altq *altq; u_int32_t nr; int nbytes; + u_int32_t version; PF_RULES_RLOCK(); if (pq->ticket != V_ticket_altqs_active) { @@ -2191,7 +2434,11 @@ break; } PF_RULES_RUNLOCK(); - error = altq_getqstats(altq, pq->buf, &nbytes); + if (cmd == DIOCGETQSTATSV0) + version = 0; /* DIOCGETQSTATSV0 means stats struct v0 */ + else + version = pq->version; + error = altq_getqstats(altq, pq->buf, &nbytes, version); if (error == 0) { pq->scheduler = altq->scheduler; pq->nbytes = nbytes; Index: head/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c +++ head/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c @@ -28,6 +28,8 @@ * $FreeBSD$ */ +#define PFIOC_USE_LATEST + #include #include @@ -982,7 +984,8 @@ val->v.integer = e->altq.scheduler; break; case LEAF_pfAltqQueueBandwidth: - val->v.uint32 = e->altq.bandwidth; + val->v.uint32 = (e->altq.bandwidth > UINT_MAX) ? + UINT_MAX : (u_int32_t)e->altq.bandwidth; break; case LEAF_pfAltqQueuePriority: val->v.integer = e->altq.priority; @@ -1228,7 +1231,7 @@ } bzero(&pa, sizeof(pa)); - + pa.version = PFIOC_ALTQ_VERSION; if (ioctl(dev, DIOCGETALTQS, &pa)) { syslog(LOG_ERR, "pfq_refresh: ioctl(DIOCGETALTQS): %s", strerror(errno)); @@ -1646,6 +1649,7 @@ struct pfioc_altq pa; errno = 0; + pa.version = PFIOC_ALTQ_VERSION; if (ioctl(pfdev, DIOCGETALTQS, &pa)) { if (errno == ENODEV) { syslog(LOG_INFO, "No ALTQ support in kernel\n"