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