freebsd-dev/contrib/pf/pfctl/pfctl_qstats.c
Max Laier 4239d24b98 Make ALTQ cope with disappearing interfaces (particularly common with mpd
and netgraph in gernal).  This also allows to add queues for an interface
that is not yet existing (you have to provide the bandwidth for the
interface, however).

PR:		kern/106400, kern/117827
MFC after:	2 weeks
2008-03-29 00:24:36 +00:00

450 lines
11 KiB
C

/* $OpenBSD: pfctl_qstats.c,v 1.30 2004/04/27 21:47:32 kjc Exp $ */
/*
* Copyright (c) Henning Brauer <henning@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <net/pfvar.h>
#include <arpa/inet.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <altq/altq.h>
#include <altq/altq_cbq.h>
#include <altq/altq_priq.h>
#include <altq/altq_hfsc.h>
#include "pfctl.h"
#include "pfctl_parser.h"
union class_stats {
class_stats_t cbq_stats;
struct priq_classstats priq_stats;
struct hfsc_classstats hfsc_stats;
};
#define AVGN_MAX 8
#define STAT_INTERVAL 5
struct queue_stats {
union class_stats data;
int avgn;
double avg_bytes;
double avg_packets;
u_int64_t prev_bytes;
u_int64_t prev_packets;
};
struct pf_altq_node {
struct pf_altq altq;
struct pf_altq_node *next;
struct pf_altq_node *children;
struct queue_stats qstats;
};
int pfctl_update_qstats(int, struct pf_altq_node **);
void pfctl_insert_altq_node(struct pf_altq_node **,
const struct pf_altq, const struct queue_stats);
struct pf_altq_node *pfctl_find_altq_node(struct pf_altq_node *,
const char *, const char *);
void pfctl_print_altq_node(int, const struct pf_altq_node *,
unsigned, int);
void print_cbqstats(struct queue_stats);
void print_priqstats(struct queue_stats);
void print_hfscstats(struct queue_stats);
void pfctl_free_altq_node(struct pf_altq_node *);
void pfctl_print_altq_nodestat(int,
const struct pf_altq_node *);
void update_avg(struct pf_altq_node *);
int
pfctl_show_altq(int dev, const char *iface, int opts, int verbose2)
{
struct pf_altq_node *root = NULL, *node;
int nodes, dotitle = (opts & PF_OPT_SHOWALL);
#ifdef __FreeBSD__
if (!altqsupport)
return (-1);
#endif
if ((nodes = pfctl_update_qstats(dev, &root)) < 0)
return (-1);
if (nodes == 0)
printf("No queue in use\n");
for (node = root; node != NULL; node = node->next) {
if (iface != NULL && strcmp(node->altq.ifname, iface))
continue;
if (dotitle) {
pfctl_print_title("ALTQ:");
dotitle = 0;
}
pfctl_print_altq_node(dev, node, 0, opts);
}
while (verbose2 && nodes > 0) {
printf("\n");
fflush(stdout);
sleep(STAT_INTERVAL);
if ((nodes = pfctl_update_qstats(dev, &root)) == -1)
return (-1);
for (node = root; node != NULL; node = node->next) {
if (iface != NULL && strcmp(node->altq.ifname, iface))
continue;
#ifdef __FreeBSD__
if (node->altq.local_flags & PFALTQ_FLAG_IF_REMOVED)
continue;
#endif
pfctl_print_altq_node(dev, node, 0, opts);
}
}
pfctl_free_altq_node(root);
return (0);
}
int
pfctl_update_qstats(int dev, struct pf_altq_node **root)
{
struct pf_altq_node *node;
struct pfioc_altq pa;
struct pfioc_qstats pq;
u_int32_t mnr, nr;
struct queue_stats qstats;
static u_int32_t last_ticket;
memset(&pa, 0, sizeof(pa));
memset(&pq, 0, sizeof(pq));
memset(&qstats, 0, sizeof(qstats));
if (ioctl(dev, DIOCGETALTQS, &pa)) {
warn("DIOCGETALTQS");
return (-1);
}
/* if a new set is found, start over */
if (pa.ticket != last_ticket && *root != NULL) {
pfctl_free_altq_node(*root);
*root = NULL;
}
last_ticket = pa.ticket;
mnr = pa.nr;
for (nr = 0; nr < mnr; ++nr) {
pa.nr = nr;
if (ioctl(dev, DIOCGETALTQ, &pa)) {
warn("DIOCGETALTQ");
return (-1);
}
#ifdef __FreeBSD__
if (pa.altq.qid > 0 &&
!(pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED)) {
#else
if (pa.altq.qid > 0) {
#endif
pq.nr = nr;
pq.ticket = pa.ticket;
pq.buf = &qstats.data;
pq.nbytes = sizeof(qstats.data);
if (ioctl(dev, DIOCGETQSTATS, &pq)) {
warn("DIOCGETQSTATS");
return (-1);
}
if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
pa.altq.ifname)) != NULL) {
memcpy(&node->qstats.data, &qstats.data,
sizeof(qstats.data));
update_avg(node);
} else {
pfctl_insert_altq_node(root, pa.altq, qstats);
}
}
#ifdef __FreeBSD__
else if (pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED) {
memset(&qstats.data, 0, sizeof(qstats.data));
if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
pa.altq.ifname)) != NULL) {
memcpy(&node->qstats.data, &qstats.data,
sizeof(qstats.data));
update_avg(node);
} else {
pfctl_insert_altq_node(root, pa.altq, qstats);
}
}
#endif
}
return (mnr);
}
void
pfctl_insert_altq_node(struct pf_altq_node **root,
const struct pf_altq altq, const struct queue_stats qstats)
{
struct pf_altq_node *node;
node = calloc(1, sizeof(struct pf_altq_node));
if (node == NULL)
err(1, "pfctl_insert_altq_node: calloc");
memcpy(&node->altq, &altq, sizeof(struct pf_altq));
memcpy(&node->qstats, &qstats, sizeof(qstats));
node->next = node->children = NULL;
if (*root == NULL)
*root = node;
else if (!altq.parent[0]) {
struct pf_altq_node *prev = *root;
while (prev->next != NULL)
prev = prev->next;
prev->next = node;
} else {
struct pf_altq_node *parent;
parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
if (parent == NULL)
errx(1, "parent %s not found", altq.parent);
if (parent->children == NULL)
parent->children = node;
else {
struct pf_altq_node *prev = parent->children;
while (prev->next != NULL)
prev = prev->next;
prev->next = node;
}
}
update_avg(node);
}
struct pf_altq_node *
pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
const char *ifname)
{
struct pf_altq_node *node, *child;
for (node = root; node != NULL; node = node->next) {
if (!strcmp(node->altq.qname, qname)
&& !(strcmp(node->altq.ifname, ifname)))
return (node);
if (node->children != NULL) {
child = pfctl_find_altq_node(node->children, qname,
ifname);
if (child != NULL)
return (child);
}
}
return (NULL);
}
void
pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level,
int opts)
{
const struct pf_altq_node *child;
if (node == NULL)
return;
print_altq(&node->altq, level, NULL, NULL);
if (node->children != NULL) {
printf("{");
for (child = node->children; child != NULL;
child = child->next) {
printf("%s", child->altq.qname);
if (child->next != NULL)
printf(", ");
}
printf("}");
}
printf("\n");
if (opts & PF_OPT_VERBOSE)
pfctl_print_altq_nodestat(dev, node);
if (opts & PF_OPT_DEBUG)
printf(" [ qid=%u ifname=%s ifbandwidth=%s ]\n",
node->altq.qid, node->altq.ifname,
rate2str((double)(node->altq.ifbandwidth)));
for (child = node->children; child != NULL;
child = child->next)
pfctl_print_altq_node(dev, child, level + 1, opts);
}
void
pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a)
{
if (a->altq.qid == 0)
return;
#ifdef __FreeBSD__
if (a->altq.local_flags & PFALTQ_FLAG_IF_REMOVED)
return;
#endif
switch (a->altq.scheduler) {
case ALTQT_CBQ:
print_cbqstats(a->qstats);
break;
case ALTQT_PRIQ:
print_priqstats(a->qstats);
break;
case ALTQT_HFSC:
print_hfscstats(a->qstats);
break;
}
}
void
print_cbqstats(struct queue_stats cur)
{
printf(" [ pkts: %10llu bytes: %10llu "
"dropped pkts: %6llu bytes: %6llu ]\n",
(unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
(unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
(unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
(unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
printf(" [ qlength: %3d/%3d borrows: %6u suspends: %6u ]\n",
cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
if (cur.avgn < 2)
return;
printf(" [ measured: %7.1f packets/s, %s/s ]\n",
cur.avg_packets / STAT_INTERVAL,
rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
}
void
print_priqstats(struct queue_stats cur)
{
printf(" [ pkts: %10llu bytes: %10llu "
"dropped pkts: %6llu bytes: %6llu ]\n",
(unsigned long long)cur.data.priq_stats.xmitcnt.packets,
(unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
(unsigned long long)cur.data.priq_stats.dropcnt.packets,
(unsigned long long)cur.data.priq_stats.dropcnt.bytes);
printf(" [ qlength: %3d/%3d ]\n",
cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
if (cur.avgn < 2)
return;
printf(" [ measured: %7.1f packets/s, %s/s ]\n",
cur.avg_packets / STAT_INTERVAL,
rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
}
void
print_hfscstats(struct queue_stats cur)
{
printf(" [ pkts: %10llu bytes: %10llu "
"dropped pkts: %6llu bytes: %6llu ]\n",
(unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
(unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
(unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
(unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
printf(" [ qlength: %3d/%3d ]\n",
cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
if (cur.avgn < 2)
return;
printf(" [ measured: %7.1f packets/s, %s/s ]\n",
cur.avg_packets / STAT_INTERVAL,
rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
}
void
pfctl_free_altq_node(struct pf_altq_node *node)
{
while (node != NULL) {
struct pf_altq_node *prev;
if (node->children != NULL)
pfctl_free_altq_node(node->children);
prev = node;
node = node->next;
free(prev);
}
}
void
update_avg(struct pf_altq_node *a)
{
struct queue_stats *qs;
u_int64_t b, p;
int n;
if (a->altq.qid == 0)
return;
qs = &a->qstats;
n = qs->avgn;
switch (a->altq.scheduler) {
case ALTQT_CBQ:
b = qs->data.cbq_stats.xmit_cnt.bytes;
p = qs->data.cbq_stats.xmit_cnt.packets;
break;
case ALTQT_PRIQ:
b = qs->data.priq_stats.xmitcnt.bytes;
p = qs->data.priq_stats.xmitcnt.packets;
break;
case ALTQT_HFSC:
b = qs->data.hfsc_stats.xmit_cnt.bytes;
p = qs->data.hfsc_stats.xmit_cnt.packets;
break;
default:
b = 0;
p = 0;
break;
}
if (n == 0) {
qs->prev_bytes = b;
qs->prev_packets = p;
qs->avgn++;
return;
}
if (b >= qs->prev_bytes)
qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
(b - qs->prev_bytes)) / n;
if (p >= qs->prev_packets)
qs->avg_packets = ((qs->avg_packets * (n - 1)) +
(p - qs->prev_packets)) / n;
qs->prev_bytes = b;
qs->prev_packets = p;
if (n < AVGN_MAX)
qs->avgn++;
}