Home | History | Annotate | Line # | Download | only in pfctl
pfctl_qstats.c revision 1.4
      1 /*	$NetBSD: pfctl_qstats.c,v 1.4 2004/11/14 11:26:48 yamt Exp $	*/
      2 /*	$OpenBSD: pfctl_qstats.c,v 1.30 2004/04/27 21:47:32 kjc Exp $ */
      3 
      4 /*
      5  * Copyright (c) Henning Brauer <henning (at) openbsd.org>
      6  *
      7  * Permission to use, copy, modify, and distribute this software for any
      8  * purpose with or without fee is hereby granted, provided that the above
      9  * copyright notice and this permission notice appear in all copies.
     10  *
     11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18  */
     19 
     20 #include <sys/types.h>
     21 #include <sys/ioctl.h>
     22 #include <sys/socket.h>
     23 
     24 #include <net/if.h>
     25 #include <netinet/in.h>
     26 #include <net/pfvar.h>
     27 #include <arpa/inet.h>
     28 
     29 #include <err.h>
     30 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include <string.h>
     33 #include <unistd.h>
     34 
     35 #include <altq/altq.h>
     36 #include <altq/altq_cbq.h>
     37 #include <altq/altq_priq.h>
     38 #include <altq/altq_hfsc.h>
     39 
     40 #include "pfctl.h"
     41 #include "pfctl_parser.h"
     42 
     43 union class_stats {
     44 	class_stats_t		cbq_stats;
     45 #ifdef __OpenBSD__
     46 	struct priq_classstats	priq_stats;
     47 	struct hfsc_classstats	hfsc_stats;
     48 #endif
     49 };
     50 
     51 #define AVGN_MAX	8
     52 #define STAT_INTERVAL	5
     53 
     54 struct queue_stats {
     55 	union class_stats	 data;
     56 	int			 avgn;
     57 	double			 avg_bytes;
     58 	double			 avg_packets;
     59 	u_int64_t		 prev_bytes;
     60 	u_int64_t		 prev_packets;
     61 };
     62 
     63 struct pf_altq_node {
     64 	struct pf_altq		 altq;
     65 	struct pf_altq_node	*next;
     66 	struct pf_altq_node	*children;
     67 	struct queue_stats	 qstats;
     68 };
     69 
     70 int			 pfctl_update_qstats(int, struct pf_altq_node **);
     71 void			 pfctl_insert_altq_node(struct pf_altq_node **,
     72 			    const struct pf_altq, const struct queue_stats);
     73 struct pf_altq_node	*pfctl_find_altq_node(struct pf_altq_node *,
     74 			    const char *, const char *);
     75 void			 pfctl_print_altq_node(int, const struct pf_altq_node *,
     76 			     unsigned, int);
     77 void			 print_cbqstats(struct queue_stats);
     78 void			 print_priqstats(struct queue_stats);
     79 void			 print_hfscstats(struct queue_stats);
     80 void			 pfctl_free_altq_node(struct pf_altq_node *);
     81 void			 pfctl_print_altq_nodestat(int,
     82 			    const struct pf_altq_node *);
     83 
     84 void			 update_avg(struct pf_altq_node *);
     85 
     86 int
     87 pfctl_show_altq(int dev, const char *iface, int opts, int verbose2)
     88 {
     89 	struct pf_altq_node	*root = NULL, *node;
     90 	int			 nodes, dotitle = (opts & PF_OPT_SHOWALL);
     91 
     92 
     93 	if ((nodes = pfctl_update_qstats(dev, &root)) < 0)
     94 		return (-1);
     95 
     96 	if (nodes == 0)
     97 		printf("No queue in use\n");
     98 	for (node = root; node != NULL; node = node->next) {
     99 		if (iface != NULL && strcmp(node->altq.ifname, iface))
    100 			continue;
    101 		if (dotitle) {
    102 			pfctl_print_title("ALTQ:");
    103 			dotitle = 0;
    104 		}
    105 		pfctl_print_altq_node(dev, node, 0, opts);
    106 	}
    107 
    108 	while (verbose2 && nodes > 0) {
    109 		printf("\n");
    110 		fflush(stdout);
    111 		sleep(STAT_INTERVAL);
    112 		if ((nodes = pfctl_update_qstats(dev, &root)) == -1)
    113 			return (-1);
    114 		for (node = root; node != NULL; node = node->next) {
    115 			if (iface != NULL && strcmp(node->altq.ifname, iface))
    116 				continue;
    117 			pfctl_print_altq_node(dev, node, 0, opts);
    118 		}
    119 	}
    120 	pfctl_free_altq_node(root);
    121 	return (0);
    122 }
    123 
    124 int
    125 pfctl_update_qstats(int dev, struct pf_altq_node **root)
    126 {
    127 	struct pf_altq_node	*node;
    128 	struct pfioc_altq	 pa;
    129 	struct pfioc_qstats	 pq;
    130 	u_int32_t		 mnr, nr;
    131 	struct queue_stats	 qstats;
    132 	static	u_int32_t	 last_ticket;
    133 
    134 	memset(&pa, 0, sizeof(pa));
    135 	memset(&pq, 0, sizeof(pq));
    136 	memset(&qstats, 0, sizeof(qstats));
    137 	if (ioctl(dev, DIOCGETALTQS, &pa)) {
    138 		warn("DIOCGETALTQS");
    139 		return (-1);
    140 	}
    141 
    142 	/* if a new set is found, start over */
    143 	if (pa.ticket != last_ticket && *root != NULL) {
    144 		pfctl_free_altq_node(*root);
    145 		*root = NULL;
    146 	}
    147 	last_ticket = pa.ticket;
    148 
    149 	mnr = pa.nr;
    150 	for (nr = 0; nr < mnr; ++nr) {
    151 		pa.nr = nr;
    152 		if (ioctl(dev, DIOCGETALTQ, &pa)) {
    153 			warn("DIOCGETALTQ");
    154 			return (-1);
    155 		}
    156 		if (pa.altq.qid > 0) {
    157 			pq.nr = nr;
    158 			pq.ticket = pa.ticket;
    159 			pq.buf = &qstats.data;
    160 			pq.nbytes = sizeof(qstats.data);
    161 			if (ioctl(dev, DIOCGETQSTATS, &pq)) {
    162 				warn("DIOCGETQSTATS");
    163 				return (-1);
    164 			}
    165 			if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
    166 			    pa.altq.ifname)) != NULL) {
    167 				memcpy(&node->qstats.data, &qstats.data,
    168 				    sizeof(qstats.data));
    169 				update_avg(node);
    170 			} else {
    171 				pfctl_insert_altq_node(root, pa.altq, qstats);
    172 			}
    173 		}
    174 	}
    175 	return (mnr);
    176 }
    177 
    178 void
    179 pfctl_insert_altq_node(struct pf_altq_node **root,
    180     const struct pf_altq altq, const struct queue_stats qstats)
    181 {
    182 	struct pf_altq_node	*node;
    183 
    184 	node = calloc(1, sizeof(struct pf_altq_node));
    185 	if (node == NULL)
    186 		err(1, "pfctl_insert_altq_node: calloc");
    187 	memcpy(&node->altq, &altq, sizeof(struct pf_altq));
    188 	memcpy(&node->qstats, &qstats, sizeof(qstats));
    189 	node->next = node->children = NULL;
    190 
    191 	if (*root == NULL)
    192 		*root = node;
    193 	else if (!altq.parent[0]) {
    194 		struct pf_altq_node	*prev = *root;
    195 
    196 		while (prev->next != NULL)
    197 			prev = prev->next;
    198 		prev->next = node;
    199 	} else {
    200 		struct pf_altq_node	*parent;
    201 
    202 		parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
    203 		if (parent == NULL)
    204 			errx(1, "parent %s not found", altq.parent);
    205 		if (parent->children == NULL)
    206 			parent->children = node;
    207 		else {
    208 			struct pf_altq_node *prev = parent->children;
    209 
    210 			while (prev->next != NULL)
    211 				prev = prev->next;
    212 			prev->next = node;
    213 		}
    214 	}
    215 	update_avg(node);
    216 }
    217 
    218 struct pf_altq_node *
    219 pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
    220     const char *ifname)
    221 {
    222 	struct pf_altq_node	*node, *child;
    223 
    224 	for (node = root; node != NULL; node = node->next) {
    225 		if (!strcmp(node->altq.qname, qname)
    226 		    && !(strcmp(node->altq.ifname, ifname)))
    227 			return (node);
    228 		if (node->children != NULL) {
    229 			child = pfctl_find_altq_node(node->children, qname,
    230 			    ifname);
    231 			if (child != NULL)
    232 				return (child);
    233 		}
    234 	}
    235 	return (NULL);
    236 }
    237 
    238 void
    239 pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level,
    240     int opts)
    241 {
    242 	const struct pf_altq_node	*child;
    243 
    244 	if (node == NULL)
    245 		return;
    246 
    247 	print_altq(&node->altq, level, NULL, NULL);
    248 
    249 	if (node->children != NULL) {
    250 		printf("{");
    251 		for (child = node->children; child != NULL;
    252 		    child = child->next) {
    253 			printf("%s", child->altq.qname);
    254 			if (child->next != NULL)
    255 				printf(", ");
    256 		}
    257 		printf("}");
    258 	}
    259 	printf("\n");
    260 
    261 	if (opts & PF_OPT_VERBOSE)
    262 		pfctl_print_altq_nodestat(dev, node);
    263 
    264 	if (opts & PF_OPT_DEBUG)
    265 		printf("  [ qid=%u ifname=%s ifbandwidth=%s ]\n",
    266 		    node->altq.qid, node->altq.ifname,
    267 		    rate2str((double)(node->altq.ifbandwidth)));
    268 
    269 	for (child = node->children; child != NULL;
    270 	    child = child->next)
    271 		pfctl_print_altq_node(dev, child, level + 1, opts);
    272 }
    273 
    274 void
    275 pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a)
    276 {
    277 	if (a->altq.qid == 0)
    278 		return;
    279 
    280 	switch (a->altq.scheduler) {
    281 	case ALTQT_CBQ:
    282 		print_cbqstats(a->qstats);
    283 		break;
    284 	case ALTQT_PRIQ:
    285 		print_priqstats(a->qstats);
    286 		break;
    287 	case ALTQT_HFSC:
    288 		print_hfscstats(a->qstats);
    289 		break;
    290 	}
    291 }
    292 
    293 void
    294 print_cbqstats(struct queue_stats cur)
    295 {
    296 	printf("  [ pkts: %10llu  bytes: %10llu  "
    297 	    "dropped pkts: %6llu bytes: %6llu ]\n",
    298 	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
    299 	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
    300 	    (unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
    301 	    (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
    302 	printf("  [ qlength: %3d/%3d  borrows: %6u  suspends: %6u ]\n",
    303 	    cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
    304 	    cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
    305 
    306 	if (cur.avgn < 2)
    307 		return;
    308 
    309 	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
    310 	    cur.avg_packets / STAT_INTERVAL,
    311 	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
    312 }
    313 
    314 void
    315 print_priqstats(struct queue_stats cur)
    316 {
    317 #ifdef __OpenBSD__
    318 	printf("  [ pkts: %10llu  bytes: %10llu  "
    319 	    "dropped pkts: %6llu bytes: %6llu ]\n",
    320 	    (unsigned long long)cur.data.priq_stats.xmitcnt.packets,
    321 	    (unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
    322 	    (unsigned long long)cur.data.priq_stats.dropcnt.packets,
    323 	    (unsigned long long)cur.data.priq_stats.dropcnt.bytes);
    324 	printf("  [ qlength: %3d/%3d ]\n",
    325 	    cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
    326 
    327 	if (cur.avgn < 2)
    328 		return;
    329 
    330 	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
    331 	    cur.avg_packets / STAT_INTERVAL,
    332 	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
    333 #endif
    334 }
    335 
    336 void
    337 print_hfscstats(struct queue_stats cur)
    338 {
    339 #ifdef __OpenBSD__
    340 	printf("  [ pkts: %10llu  bytes: %10llu  "
    341 	    "dropped pkts: %6llu bytes: %6llu ]\n",
    342 	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
    343 	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
    344 	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
    345 	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
    346 	printf("  [ qlength: %3d/%3d ]\n",
    347 	    cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
    348 
    349 	if (cur.avgn < 2)
    350 		return;
    351 
    352 	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
    353 	    cur.avg_packets / STAT_INTERVAL,
    354 	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
    355 #endif
    356 }
    357 
    358 void
    359 pfctl_free_altq_node(struct pf_altq_node *node)
    360 {
    361 	while (node != NULL) {
    362 		struct pf_altq_node	*prev;
    363 
    364 		if (node->children != NULL)
    365 			pfctl_free_altq_node(node->children);
    366 		prev = node;
    367 		node = node->next;
    368 		free(prev);
    369 	}
    370 }
    371 
    372 void
    373 update_avg(struct pf_altq_node *a)
    374 {
    375 	struct queue_stats	*qs;
    376 	u_int64_t		 b, p;
    377 	int			 n;
    378 
    379 	if (a->altq.qid == 0)
    380 		return;
    381 
    382 	qs = &a->qstats;
    383 	n = qs->avgn;
    384 
    385 	switch (a->altq.scheduler) {
    386 	case ALTQT_CBQ:
    387 		b = qs->data.cbq_stats.xmit_cnt.bytes;
    388 		p = qs->data.cbq_stats.xmit_cnt.packets;
    389 		break;
    390 #ifdef __OpenBSD__
    391 	case ALTQT_PRIQ:
    392 		b = qs->data.priq_stats.xmitcnt.bytes;
    393 		p = qs->data.priq_stats.xmitcnt.packets;
    394 		break;
    395 	case ALTQT_HFSC:
    396 		b = qs->data.hfsc_stats.xmit_cnt.bytes;
    397 		p = qs->data.hfsc_stats.xmit_cnt.packets;
    398 		break;
    399 #endif
    400 	default:
    401 		b = 0;
    402 		p = 0;
    403 		break;
    404 	}
    405 
    406 	if (n == 0) {
    407 		qs->prev_bytes = b;
    408 		qs->prev_packets = p;
    409 		qs->avgn++;
    410 		return;
    411 	}
    412 
    413 	if (b >= qs->prev_bytes)
    414 		qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
    415 		    (b - qs->prev_bytes)) / n;
    416 
    417 	if (p >= qs->prev_packets)
    418 		qs->avg_packets = ((qs->avg_packets * (n - 1)) +
    419 		    (p - qs->prev_packets)) / n;
    420 
    421 	qs->prev_bytes = b;
    422 	qs->prev_packets = p;
    423 	if (n < AVGN_MAX)
    424 		qs->avgn++;
    425 }
    426