pfctl_qstats.c revision 298133
1/*	$OpenBSD: pfctl_qstats.c,v 1.30 2004/04/27 21:47:32 kjc Exp $ */
2
3/*
4 * Copyright (c) Henning Brauer <henning@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/cdefs.h>
20__FBSDID("$FreeBSD: stable/10/sbin/pfctl/pfctl_qstats.c 298133 2016-04-16 22:02:32Z loos $");
21
22#include <sys/types.h>
23#include <sys/ioctl.h>
24#include <sys/socket.h>
25
26#include <net/if.h>
27#include <netinet/in.h>
28#include <net/pfvar.h>
29#include <arpa/inet.h>
30
31#include <err.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <unistd.h>
36
37#include <altq/altq.h>
38#include <altq/altq_cbq.h>
39#include <altq/altq_codel.h>
40#include <altq/altq_priq.h>
41#include <altq/altq_hfsc.h>
42#include <altq/altq_fairq.h>
43
44#include "pfctl.h"
45#include "pfctl_parser.h"
46
47union class_stats {
48	class_stats_t		cbq_stats;
49	struct priq_classstats	priq_stats;
50	struct hfsc_classstats	hfsc_stats;
51	struct fairq_classstats fairq_stats;
52	struct codel_ifstats	codel_stats;
53};
54
55#define AVGN_MAX	8
56#define STAT_INTERVAL	5
57
58struct queue_stats {
59	union class_stats	 data;
60	int			 avgn;
61	double			 avg_bytes;
62	double			 avg_packets;
63	u_int64_t		 prev_bytes;
64	u_int64_t		 prev_packets;
65};
66
67struct pf_altq_node {
68	struct pf_altq		 altq;
69	struct pf_altq_node	*next;
70	struct pf_altq_node	*children;
71	struct queue_stats	 qstats;
72};
73
74int			 pfctl_update_qstats(int, struct pf_altq_node **);
75void			 pfctl_insert_altq_node(struct pf_altq_node **,
76			    const struct pf_altq, const struct queue_stats);
77struct pf_altq_node	*pfctl_find_altq_node(struct pf_altq_node *,
78			    const char *, const char *);
79void			 pfctl_print_altq_node(int, const struct pf_altq_node *,
80			    unsigned, int);
81void			 print_cbqstats(struct queue_stats);
82void			 print_codelstats(struct queue_stats);
83void			 print_priqstats(struct queue_stats);
84void			 print_hfscstats(struct queue_stats);
85void			 print_fairqstats(struct queue_stats);
86void			 pfctl_free_altq_node(struct pf_altq_node *);
87void			 pfctl_print_altq_nodestat(int,
88			    const struct pf_altq_node *);
89
90void			 update_avg(struct pf_altq_node *);
91
92int
93pfctl_show_altq(int dev, const char *iface, int opts, int verbose2)
94{
95	struct pf_altq_node	*root = NULL, *node;
96	int			 nodes, dotitle = (opts & PF_OPT_SHOWALL);
97
98#ifdef __FreeBSD__
99	if (!altqsupport)
100		return (-1);
101#endif
102
103	if ((nodes = pfctl_update_qstats(dev, &root)) < 0)
104		return (-1);
105
106	if (nodes == 0)
107		printf("No queue in use\n");
108	for (node = root; node != NULL; node = node->next) {
109		if (iface != NULL && strcmp(node->altq.ifname, iface))
110			continue;
111		if (dotitle) {
112			pfctl_print_title("ALTQ:");
113			dotitle = 0;
114		}
115		pfctl_print_altq_node(dev, node, 0, opts);
116	}
117
118	while (verbose2 && nodes > 0) {
119		printf("\n");
120		fflush(stdout);
121		sleep(STAT_INTERVAL);
122		if ((nodes = pfctl_update_qstats(dev, &root)) == -1)
123			return (-1);
124		for (node = root; node != NULL; node = node->next) {
125			if (iface != NULL && strcmp(node->altq.ifname, iface))
126				continue;
127#ifdef __FreeBSD__
128			if (node->altq.local_flags & PFALTQ_FLAG_IF_REMOVED)
129				continue;
130#endif
131			pfctl_print_altq_node(dev, node, 0, opts);
132		}
133	}
134	pfctl_free_altq_node(root);
135	return (0);
136}
137
138int
139pfctl_update_qstats(int dev, struct pf_altq_node **root)
140{
141	struct pf_altq_node	*node;
142	struct pfioc_altq	 pa;
143	struct pfioc_qstats	 pq;
144	u_int32_t		 mnr, nr;
145	struct queue_stats	 qstats;
146	static	u_int32_t	 last_ticket;
147
148	memset(&pa, 0, sizeof(pa));
149	memset(&pq, 0, sizeof(pq));
150	memset(&qstats, 0, sizeof(qstats));
151	if (ioctl(dev, DIOCGETALTQS, &pa)) {
152		warn("DIOCGETALTQS");
153		return (-1);
154	}
155
156	/* if a new set is found, start over */
157	if (pa.ticket != last_ticket && *root != NULL) {
158		pfctl_free_altq_node(*root);
159		*root = NULL;
160	}
161	last_ticket = pa.ticket;
162
163	mnr = pa.nr;
164	for (nr = 0; nr < mnr; ++nr) {
165		pa.nr = nr;
166		if (ioctl(dev, DIOCGETALTQ, &pa)) {
167			warn("DIOCGETALTQ");
168			return (-1);
169		}
170#ifdef __FreeBSD__
171		if ((pa.altq.qid > 0 || pa.altq.scheduler == ALTQT_CODEL) &&
172		    !(pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED)) {
173#else
174		if (pa.altq.qid > 0) {
175#endif
176			pq.nr = nr;
177			pq.ticket = pa.ticket;
178			pq.buf = &qstats.data;
179			pq.nbytes = sizeof(qstats.data);
180			if (ioctl(dev, DIOCGETQSTATS, &pq)) {
181				warn("DIOCGETQSTATS");
182				return (-1);
183			}
184			if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
185			    pa.altq.ifname)) != NULL) {
186				memcpy(&node->qstats.data, &qstats.data,
187				    sizeof(qstats.data));
188				update_avg(node);
189			} else {
190				pfctl_insert_altq_node(root, pa.altq, qstats);
191			}
192		}
193#ifdef __FreeBSD__
194		else if (pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED) {
195			memset(&qstats.data, 0, sizeof(qstats.data));
196			if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
197			    pa.altq.ifname)) != NULL) {
198				memcpy(&node->qstats.data, &qstats.data,
199				    sizeof(qstats.data));
200				update_avg(node);
201			} else {
202				pfctl_insert_altq_node(root, pa.altq, qstats);
203			}
204		}
205#endif
206	}
207	return (mnr);
208}
209
210void
211pfctl_insert_altq_node(struct pf_altq_node **root,
212    const struct pf_altq altq, const struct queue_stats qstats)
213{
214	struct pf_altq_node	*node;
215
216	node = calloc(1, sizeof(struct pf_altq_node));
217	if (node == NULL)
218		err(1, "pfctl_insert_altq_node: calloc");
219	memcpy(&node->altq, &altq, sizeof(struct pf_altq));
220	memcpy(&node->qstats, &qstats, sizeof(qstats));
221	node->next = node->children = NULL;
222
223	if (*root == NULL)
224		*root = node;
225	else if (!altq.parent[0]) {
226		struct pf_altq_node	*prev = *root;
227
228		while (prev->next != NULL)
229			prev = prev->next;
230		prev->next = node;
231	} else {
232		struct pf_altq_node	*parent;
233
234		parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
235		if (parent == NULL)
236			errx(1, "parent %s not found", altq.parent);
237		if (parent->children == NULL)
238			parent->children = node;
239		else {
240			struct pf_altq_node *prev = parent->children;
241
242			while (prev->next != NULL)
243				prev = prev->next;
244			prev->next = node;
245		}
246	}
247	update_avg(node);
248}
249
250struct pf_altq_node *
251pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
252    const char *ifname)
253{
254	struct pf_altq_node	*node, *child;
255
256	for (node = root; node != NULL; node = node->next) {
257		if (!strcmp(node->altq.qname, qname)
258		    && !(strcmp(node->altq.ifname, ifname)))
259			return (node);
260		if (node->children != NULL) {
261			child = pfctl_find_altq_node(node->children, qname,
262			    ifname);
263			if (child != NULL)
264				return (child);
265		}
266	}
267	return (NULL);
268}
269
270void
271pfctl_print_altq_node(int dev, const struct pf_altq_node *node,
272    unsigned int level, int opts)
273{
274	const struct pf_altq_node	*child;
275
276	if (node == NULL)
277		return;
278
279	print_altq(&node->altq, level, NULL, NULL);
280
281	if (node->children != NULL) {
282		printf("{");
283		for (child = node->children; child != NULL;
284		    child = child->next) {
285			printf("%s", child->altq.qname);
286			if (child->next != NULL)
287				printf(", ");
288		}
289		printf("}");
290	}
291	printf("\n");
292
293	if (opts & PF_OPT_VERBOSE)
294		pfctl_print_altq_nodestat(dev, node);
295
296	if (opts & PF_OPT_DEBUG)
297		printf("  [ qid=%u ifname=%s ifbandwidth=%s ]\n",
298		    node->altq.qid, node->altq.ifname,
299		    rate2str((double)(node->altq.ifbandwidth)));
300
301	for (child = node->children; child != NULL;
302	    child = child->next)
303		pfctl_print_altq_node(dev, child, level + 1, opts);
304}
305
306void
307pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a)
308{
309	if (a->altq.qid == 0 && a->altq.scheduler != ALTQT_CODEL)
310		return;
311
312#ifdef __FreeBSD__
313	if (a->altq.local_flags & PFALTQ_FLAG_IF_REMOVED)
314		return;
315#endif
316	switch (a->altq.scheduler) {
317	case ALTQT_CBQ:
318		print_cbqstats(a->qstats);
319		break;
320	case ALTQT_PRIQ:
321		print_priqstats(a->qstats);
322		break;
323	case ALTQT_HFSC:
324		print_hfscstats(a->qstats);
325		break;
326	case ALTQT_FAIRQ:
327		print_fairqstats(a->qstats);
328		break;
329	case ALTQT_CODEL:
330		print_codelstats(a->qstats);
331		break;
332	}
333}
334
335void
336print_cbqstats(struct queue_stats cur)
337{
338	printf("  [ pkts: %10llu  bytes: %10llu  "
339	    "dropped pkts: %6llu bytes: %6llu ]\n",
340	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
341	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
342	    (unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
343	    (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
344	printf("  [ qlength: %3d/%3d  borrows: %6u  suspends: %6u ]\n",
345	    cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
346	    cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
347
348	if (cur.avgn < 2)
349		return;
350
351	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
352	    cur.avg_packets / STAT_INTERVAL,
353	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
354}
355
356void
357print_codelstats(struct queue_stats cur)
358{
359	printf("  [ pkts: %10llu  bytes: %10llu  "
360	    "dropped pkts: %6llu bytes: %6llu ]\n",
361	    (unsigned long long)cur.data.codel_stats.cl_xmitcnt.packets,
362	    (unsigned long long)cur.data.codel_stats.cl_xmitcnt.bytes,
363	    (unsigned long long)cur.data.codel_stats.cl_dropcnt.packets +
364	    cur.data.codel_stats.stats.drop_cnt.packets,
365	    (unsigned long long)cur.data.codel_stats.cl_dropcnt.bytes +
366	    cur.data.codel_stats.stats.drop_cnt.bytes);
367	printf("  [ qlength: %3d/%3d ]\n",
368	    cur.data.codel_stats.qlength, cur.data.codel_stats.qlimit);
369
370	if (cur.avgn < 2)
371		return;
372
373	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
374	    cur.avg_packets / STAT_INTERVAL,
375	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
376}
377
378void
379print_priqstats(struct queue_stats cur)
380{
381	printf("  [ pkts: %10llu  bytes: %10llu  "
382	    "dropped pkts: %6llu bytes: %6llu ]\n",
383	    (unsigned long long)cur.data.priq_stats.xmitcnt.packets,
384	    (unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
385	    (unsigned long long)cur.data.priq_stats.dropcnt.packets,
386	    (unsigned long long)cur.data.priq_stats.dropcnt.bytes);
387	printf("  [ qlength: %3d/%3d ]\n",
388	    cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
389
390	if (cur.avgn < 2)
391		return;
392
393	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
394	    cur.avg_packets / STAT_INTERVAL,
395	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
396}
397
398void
399print_hfscstats(struct queue_stats cur)
400{
401	printf("  [ pkts: %10llu  bytes: %10llu  "
402	    "dropped pkts: %6llu bytes: %6llu ]\n",
403	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
404	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
405	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
406	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
407	printf("  [ qlength: %3d/%3d ]\n",
408	    cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
409
410	if (cur.avgn < 2)
411		return;
412
413	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
414	    cur.avg_packets / STAT_INTERVAL,
415	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
416}
417
418void
419print_fairqstats(struct queue_stats cur)
420{
421	printf("  [ pkts: %10llu  bytes: %10llu  "
422	    "dropped pkts: %6llu bytes: %6llu ]\n",
423	    (unsigned long long)cur.data.fairq_stats.xmit_cnt.packets,
424	    (unsigned long long)cur.data.fairq_stats.xmit_cnt.bytes,
425	    (unsigned long long)cur.data.fairq_stats.drop_cnt.packets,
426	    (unsigned long long)cur.data.fairq_stats.drop_cnt.bytes);
427	printf("  [ qlength: %3d/%3d ]\n",
428	    cur.data.fairq_stats.qlength, cur.data.fairq_stats.qlimit);
429
430	if (cur.avgn < 2)
431		return;
432
433	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
434	    cur.avg_packets / STAT_INTERVAL,
435	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
436}
437
438void
439pfctl_free_altq_node(struct pf_altq_node *node)
440{
441	while (node != NULL) {
442		struct pf_altq_node	*prev;
443
444		if (node->children != NULL)
445			pfctl_free_altq_node(node->children);
446		prev = node;
447		node = node->next;
448		free(prev);
449	}
450}
451
452void
453update_avg(struct pf_altq_node *a)
454{
455	struct queue_stats	*qs;
456	u_int64_t		 b, p;
457	int			 n;
458
459	if (a->altq.qid == 0 && a->altq.scheduler != ALTQT_CODEL)
460		return;
461
462	qs = &a->qstats;
463	n = qs->avgn;
464
465	switch (a->altq.scheduler) {
466	case ALTQT_CBQ:
467		b = qs->data.cbq_stats.xmit_cnt.bytes;
468		p = qs->data.cbq_stats.xmit_cnt.packets;
469		break;
470	case ALTQT_PRIQ:
471		b = qs->data.priq_stats.xmitcnt.bytes;
472		p = qs->data.priq_stats.xmitcnt.packets;
473		break;
474	case ALTQT_HFSC:
475		b = qs->data.hfsc_stats.xmit_cnt.bytes;
476		p = qs->data.hfsc_stats.xmit_cnt.packets;
477		break;
478	case ALTQT_FAIRQ:
479		b = qs->data.fairq_stats.xmit_cnt.bytes;
480		p = qs->data.fairq_stats.xmit_cnt.packets;
481		break;
482	case ALTQT_CODEL:
483		b = qs->data.codel_stats.cl_xmitcnt.bytes;
484		p = qs->data.codel_stats.cl_xmitcnt.packets;
485		break;
486	default:
487		b = 0;
488		p = 0;
489		break;
490	}
491
492	if (n == 0) {
493		qs->prev_bytes = b;
494		qs->prev_packets = p;
495		qs->avgn++;
496		return;
497	}
498
499	if (b >= qs->prev_bytes)
500		qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
501		    (b - qs->prev_bytes)) / n;
502
503	if (p >= qs->prev_packets)
504		qs->avg_packets = ((qs->avg_packets * (n - 1)) +
505		    (p - qs->prev_packets)) / n;
506
507	qs->prev_bytes = b;
508	qs->prev_packets = p;
509	if (n < AVGN_MAX)
510		qs->avgn++;
511}
512