Extended pf(4) ioctl interface and pfctl(8) to allow bandwidths of

2^32 bps or greater to be used.  Prior to this, bandwidth parameters
would simply wrap at the 2^32 boundary.  The computations in the HFSC
scheduler and token bucket regulator have been modified to operate
correctly up to at least 100 Gbps.  No other algorithms have been
examined or modified for correct operation above 2^32 bps (some may
have existing computation resolution or overflow issues at rates below
that threshold).  pfctl(8) will now limit non-HFSC bandwidth
parameters to 2^32 - 1 before passing them to the kernel.

The extensions to the pf(4) ioctl interface have been made in a
backwards-compatible way by versioning affected data structures,
supporting all versions in the kernel, and implementing macros that
will cause existing code that consumes that interface to use version 0
without source modifications.  If version 0 consumers of the interface
are used against a new kernel that has had bandwidth parameters of
2^32 or greater configured by updated tools, such bandwidth parameters
will be reported as 2^32 - 1 bps by those old consumers.

All in-tree consumers of the pf(4) interface have been updated.  To
update out-of-tree consumers to the latest version of the interface,
define PFIOC_USE_LATEST ahead of any includes and use the code of
pfctl(8) as a guide for the ioctls of interest.

PR:	211730
Reviewed by:	jmallett, kp, loos
MFC after:	2 weeks
Relnotes:	yes
Sponsored by:	RG Nets
Differential Revision:	https://reviews.freebsd.org/D16782
This commit is contained in:
Patrick Kelsey 2018-08-22 19:38:48 +00:00
parent fe2bf351fe
commit 249cc75fd1
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=338209
23 changed files with 884 additions and 155 deletions

View file

@ -22,6 +22,8 @@
* altq interface
*/
#define PFIOC_USE_LATEST
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sockio.h>
@ -85,6 +87,7 @@ altq_fetch(void)
return;
}
bzero(&pfioc, sizeof(pfioc));
pfioc.version = PFIOC_ALTQ_VERSION;
if (ioctl(pffd, DIOCGETALTQS, &pfioc) != 0) {
warn("altq support getting queue list");
close(pffd);

View file

@ -32,6 +32,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#define PFIOC_USE_LATEST
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
@ -286,7 +288,7 @@ static struct queue_opts {
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 @@ queue_opt : BANDWIDTH bandwidth {
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 @@ bandwidth : STRING {
}
}
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;
}

View file

@ -36,6 +36,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#define PFIOC_USE_LATEST
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@ -1524,6 +1526,7 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
}
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 @@ pfctl_test_altqsupport(int dev, int opts)
{
struct pfioc_altq pa;
pa.version = PFIOC_ALTQ_VERSION;
if (ioctl(dev, DIOCGETALTQS, &pa)) {
if (errno == ENODEV) {
if (opts & PF_OPT_VERBOSE)

View file

@ -21,6 +21,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#define PFIOC_USE_LATEST
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@ -31,6 +33,7 @@ __FBSDID("$FreeBSD$");
#include <err.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
@ -88,14 +91,14 @@ static int gsc_add_seg(struct gen_sc *, double, double, double,
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 @@ int
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 @@ eval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,
} 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 @@ eval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,
else
size = 24;
size = size * getifmtu(pa->ifname);
if (size > 0xffff)
size = 0xffff;
pa->tbrsize = size;
}
return (errors);
@ -338,7 +349,7 @@ eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,
{
/* 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 @@ eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,
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 @@ eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,
}
}
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 @@ static int
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 @@ check_commit_fairq(int dev __unused, int opts __unused, struct pf_altq *pa)
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 @@ rate2str(double rate)
* 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 @@ getifspeed(int pfdev, char *ifname)
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 @@ getifmtu(char *ifname)
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 @@ eval_queue_opts(struct pf_altq *pa, struct node_queue_opt *opts,
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);

View file

@ -134,7 +134,7 @@ struct node_os {
};
struct node_queue_bw {
u_int32_t bw_absolute;
u_int64_t bw_absolute;
u_int16_t bw_percent;
};

View file

@ -19,6 +19,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#define PFIOC_USE_LATEST
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@ -148,6 +150,7 @@ pfctl_update_qstats(int dev, struct pf_altq_node **root)
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 @@ pfctl_update_qstats(int dev, struct pf_altq_node **root)
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);

View file

@ -76,8 +76,8 @@ struct altqreq {
/* 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 @@ struct pktcntr {
#include <net/altq/altq_var.h>
#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_ */

View file

@ -452,7 +452,7 @@ cbq_remove_queue(struct pf_altq *a)
}
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;

View file

@ -99,6 +99,12 @@ typedef struct _cbq_class_stats_ {
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.

View file

@ -156,7 +156,7 @@ codel_remove_altq(struct pf_altq *a)
}
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;

View file

@ -57,6 +57,12 @@ struct codel_ifstats {
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 <net/altq/altq_classq.h>

View file

@ -229,7 +229,7 @@ fairq_remove_queue(struct pf_altq *a)
}
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;

View file

@ -82,6 +82,12 @@ struct fairq_classstats {
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 {

View file

@ -116,10 +116,10 @@ static struct hfsc_class *actlist_firstfit(struct hfsc_class *,
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,7 +130,9 @@ static u_int64_t rtsc_x2y(struct runtime_sc *, u_int64_t);
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 @@ altqdev_decl(hfsc);
*/
#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 @@ hfsc_add_queue(struct pf_altq *a)
{
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 @@ hfsc_remove_queue(struct pf_altq *a)
}
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 @@ hfsc_getqstats(struct pf_altq *a, void *ubuf, int *nbytes)
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 @@ actlist_firstfit(struct hfsc_class *cl, u_int64_t cur_time)
* 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 @@ seg_y2x(u_int64_t y, u_int64_t ism)
}
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 @@ d2dx(u_int d)
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 @@ rtsc_min(struct runtime_sc *rtsc, struct internal_sc *isc, u_int64_t x,
}
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;

View file

@ -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 @@ struct service_curve {
#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 hfsc_classstats {
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) */
@ -310,6 +368,35 @@ struct hfsc_if {
#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 */
#ifdef __cplusplus

View file

@ -199,7 +199,7 @@ priq_remove_queue(struct pf_altq *a)
}
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;

View file

@ -112,6 +112,12 @@ struct priq_classstats {
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;

View file

@ -292,12 +292,12 @@ altq_assert(file, line, failedexpr)
/*
* 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 @@ tbr_set(ifq, profile)
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;
@ -455,29 +468,6 @@ tbr_timeout(arg)
tbr_timer = 0; /* don't need tbr_timer anymore */
}
/*
* 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.
@ -733,34 +723,34 @@ altq_remove_queue(struct pf_altq *a)
* 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:

View file

@ -196,7 +196,6 @@ u_int8_t read_dsfield(struct mbuf *, struct altq_pktattr *);
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_add(struct pf_altq *);
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_ */

View file

@ -1300,14 +1300,33 @@ struct pfioc_limit {
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;
@ -1315,6 +1334,22 @@ struct pfioc_qstats {
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 @@ struct pfioc_iface {
#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 @@ struct pfioc_iface {
#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);

View file

@ -57,7 +57,7 @@ struct priq_opts {
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,6 +73,31 @@ struct hfsc_opts {
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
*/
@ -87,11 +112,22 @@ struct fairq_opts {
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 pf_altq {
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_ */

View file

@ -113,6 +113,12 @@ static int pf_commit_rules(u_int32_t, int, char *);
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,6 +996,222 @@ pf_addr_copyout(struct pf_addr_wrap *addr)
}
}
#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)
{
@ -1013,9 +1235,12 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
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 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
case DIOCGETSRCNODES:
case DIOCCLRSRCNODES:
case DIOCIGETIFACES:
case DIOCGIFSPEED:
case DIOCGIFSPEEDV0:
case DIOCGIFSPEEDV1:
case DIOCSETIFFLAG:
case DIOCCLRIFFLAG:
break;
@ -1059,9 +1285,12 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
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 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
case DIOCOSFPGET:
case DIOCGETSRCNODES:
case DIOCIGETIFACES:
case DIOCGIFSPEED:
case DIOCGIFSPEEDV1:
case DIOCGIFSPEEDV0:
break;
case DIOCRCLRTABLES:
case DIOCRADDTABLES:
@ -2001,18 +2231,22 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
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 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
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 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
}
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 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
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 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
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 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
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;

View file

@ -28,6 +28,8 @@
* $FreeBSD$
*/
#define PFIOC_USE_LATEST
#include <sys/queue.h>
#include <bsnmp/snmpmod.h>
@ -982,7 +984,8 @@ pf_altqq(struct snmp_context __unused *ctx, struct snmp_value *val,
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 @@ pfq_refresh(void)
}
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 @@ altq_is_enabled(int pfdev)
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"