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$");
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_priq.h>
40#include <altq/altq_hfsc.h>
41
42#include "pfctl.h"
43#include "pfctl_parser.h"
44
45union class_stats {
46	class_stats_t		cbq_stats;
47	struct priq_classstats	priq_stats;
48	struct hfsc_classstats	hfsc_stats;
49};
50
51#define AVGN_MAX	8
52#define STAT_INTERVAL	5
53
54struct 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
63struct 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
70int			 pfctl_update_qstats(int, struct pf_altq_node **);
71void			 pfctl_insert_altq_node(struct pf_altq_node **,
72			    const struct pf_altq, const struct queue_stats);
73struct pf_altq_node	*pfctl_find_altq_node(struct pf_altq_node *,
74			    const char *, const char *);
75void			 pfctl_print_altq_node(int, const struct pf_altq_node *,
76			    unsigned, int);
77void			 print_cbqstats(struct queue_stats);
78void			 print_priqstats(struct queue_stats);
79void			 print_hfscstats(struct queue_stats);
80void			 pfctl_free_altq_node(struct pf_altq_node *);
81void			 pfctl_print_altq_nodestat(int,
82			    const struct pf_altq_node *);
83
84void			 update_avg(struct pf_altq_node *);
85
86int
87pfctl_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#ifdef __FreeBSD__
93	if (!altqsupport)
94		return (-1);
95#endif
96
97	if ((nodes = pfctl_update_qstats(dev, &root)) < 0)
98		return (-1);
99
100	if (nodes == 0)
101		printf("No queue in use\n");
102	for (node = root; node != NULL; node = node->next) {
103		if (iface != NULL && strcmp(node->altq.ifname, iface))
104			continue;
105		if (dotitle) {
106			pfctl_print_title("ALTQ:");
107			dotitle = 0;
108		}
109		pfctl_print_altq_node(dev, node, 0, opts);
110	}
111
112	while (verbose2 && nodes > 0) {
113		printf("\n");
114		fflush(stdout);
115		sleep(STAT_INTERVAL);
116		if ((nodes = pfctl_update_qstats(dev, &root)) == -1)
117			return (-1);
118		for (node = root; node != NULL; node = node->next) {
119			if (iface != NULL && strcmp(node->altq.ifname, iface))
120				continue;
121#ifdef __FreeBSD__
122			if (node->altq.local_flags & PFALTQ_FLAG_IF_REMOVED)
123				continue;
124#endif
125			pfctl_print_altq_node(dev, node, 0, opts);
126		}
127	}
128	pfctl_free_altq_node(root);
129	return (0);
130}
131
132int
133pfctl_update_qstats(int dev, struct pf_altq_node **root)
134{
135	struct pf_altq_node	*node;
136	struct pfioc_altq	 pa;
137	struct pfioc_qstats	 pq;
138	u_int32_t		 mnr, nr;
139	struct queue_stats	 qstats;
140	static	u_int32_t	 last_ticket;
141
142	memset(&pa, 0, sizeof(pa));
143	memset(&pq, 0, sizeof(pq));
144	memset(&qstats, 0, sizeof(qstats));
145	if (ioctl(dev, DIOCGETALTQS, &pa)) {
146		warn("DIOCGETALTQS");
147		return (-1);
148	}
149
150	/* if a new set is found, start over */
151	if (pa.ticket != last_ticket && *root != NULL) {
152		pfctl_free_altq_node(*root);
153		*root = NULL;
154	}
155	last_ticket = pa.ticket;
156
157	mnr = pa.nr;
158	for (nr = 0; nr < mnr; ++nr) {
159		pa.nr = nr;
160		if (ioctl(dev, DIOCGETALTQ, &pa)) {
161			warn("DIOCGETALTQ");
162			return (-1);
163		}
164#ifdef __FreeBSD__
165		if (pa.altq.qid > 0 &&
166		    !(pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED)) {
167#else
168		if (pa.altq.qid > 0) {
169#endif
170			pq.nr = nr;
171			pq.ticket = pa.ticket;
172			pq.buf = &qstats.data;
173			pq.nbytes = sizeof(qstats.data);
174			if (ioctl(dev, DIOCGETQSTATS, &pq)) {
175				warn("DIOCGETQSTATS");
176				return (-1);
177			}
178			if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
179			    pa.altq.ifname)) != NULL) {
180				memcpy(&node->qstats.data, &qstats.data,
181				    sizeof(qstats.data));
182				update_avg(node);
183			} else {
184				pfctl_insert_altq_node(root, pa.altq, qstats);
185			}
186		}
187#ifdef __FreeBSD__
188		else if (pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED) {
189			memset(&qstats.data, 0, sizeof(qstats.data));
190			if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
191			    pa.altq.ifname)) != NULL) {
192				memcpy(&node->qstats.data, &qstats.data,
193				    sizeof(qstats.data));
194				update_avg(node);
195			} else {
196				pfctl_insert_altq_node(root, pa.altq, qstats);
197			}
198		}
199#endif
200	}
201	return (mnr);
202}
203
204void
205pfctl_insert_altq_node(struct pf_altq_node **root,
206    const struct pf_altq altq, const struct queue_stats qstats)
207{
208	struct pf_altq_node	*node;
209
210	node = calloc(1, sizeof(struct pf_altq_node));
211	if (node == NULL)
212		err(1, "pfctl_insert_altq_node: calloc");
213	memcpy(&node->altq, &altq, sizeof(struct pf_altq));
214	memcpy(&node->qstats, &qstats, sizeof(qstats));
215	node->next = node->children = NULL;
216
217	if (*root == NULL)
218		*root = node;
219	else if (!altq.parent[0]) {
220		struct pf_altq_node	*prev = *root;
221
222		while (prev->next != NULL)
223			prev = prev->next;
224		prev->next = node;
225	} else {
226		struct pf_altq_node	*parent;
227
228		parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
229		if (parent == NULL)
230			errx(1, "parent %s not found", altq.parent);
231		if (parent->children == NULL)
232			parent->children = node;
233		else {
234			struct pf_altq_node *prev = parent->children;
235
236			while (prev->next != NULL)
237				prev = prev->next;
238			prev->next = node;
239		}
240	}
241	update_avg(node);
242}
243
244struct pf_altq_node *
245pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
246    const char *ifname)
247{
248	struct pf_altq_node	*node, *child;
249
250	for (node = root; node != NULL; node = node->next) {
251		if (!strcmp(node->altq.qname, qname)
252		    && !(strcmp(node->altq.ifname, ifname)))
253			return (node);
254		if (node->children != NULL) {
255			child = pfctl_find_altq_node(node->children, qname,
256			    ifname);
257			if (child != NULL)
258				return (child);
259		}
260	}
261	return (NULL);
262}
263
264void
265pfctl_print_altq_node(int dev, const struct pf_altq_node *node,
266    unsigned int level, int opts)
267{
268	const struct pf_altq_node	*child;
269
270	if (node == NULL)
271		return;
272
273	print_altq(&node->altq, level, NULL, NULL);
274
275	if (node->children != NULL) {
276		printf("{");
277		for (child = node->children; child != NULL;
278		    child = child->next) {
279			printf("%s", child->altq.qname);
280			if (child->next != NULL)
281				printf(", ");
282		}
283		printf("}");
284	}
285	printf("\n");
286
287	if (opts & PF_OPT_VERBOSE)
288		pfctl_print_altq_nodestat(dev, node);
289
290	if (opts & PF_OPT_DEBUG)
291		printf("  [ qid=%u ifname=%s ifbandwidth=%s ]\n",
292		    node->altq.qid, node->altq.ifname,
293		    rate2str((double)(node->altq.ifbandwidth)));
294
295	for (child = node->children; child != NULL;
296	    child = child->next)
297		pfctl_print_altq_node(dev, child, level + 1, opts);
298}
299
300void
301pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a)
302{
303	if (a->altq.qid == 0)
304		return;
305
306#ifdef __FreeBSD__
307	if (a->altq.local_flags & PFALTQ_FLAG_IF_REMOVED)
308		return;
309#endif
310	switch (a->altq.scheduler) {
311	case ALTQT_CBQ:
312		print_cbqstats(a->qstats);
313		break;
314	case ALTQT_PRIQ:
315		print_priqstats(a->qstats);
316		break;
317	case ALTQT_HFSC:
318		print_hfscstats(a->qstats);
319		break;
320	}
321}
322
323void
324print_cbqstats(struct queue_stats cur)
325{
326	printf("  [ pkts: %10llu  bytes: %10llu  "
327	    "dropped pkts: %6llu bytes: %6llu ]\n",
328	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
329	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
330	    (unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
331	    (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
332	printf("  [ qlength: %3d/%3d  borrows: %6u  suspends: %6u ]\n",
333	    cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
334	    cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
335
336	if (cur.avgn < 2)
337		return;
338
339	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
340	    cur.avg_packets / STAT_INTERVAL,
341	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
342}
343
344void
345print_priqstats(struct queue_stats cur)
346{
347	printf("  [ pkts: %10llu  bytes: %10llu  "
348	    "dropped pkts: %6llu bytes: %6llu ]\n",
349	    (unsigned long long)cur.data.priq_stats.xmitcnt.packets,
350	    (unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
351	    (unsigned long long)cur.data.priq_stats.dropcnt.packets,
352	    (unsigned long long)cur.data.priq_stats.dropcnt.bytes);
353	printf("  [ qlength: %3d/%3d ]\n",
354	    cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
355
356	if (cur.avgn < 2)
357		return;
358
359	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
360	    cur.avg_packets / STAT_INTERVAL,
361	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
362}
363
364void
365print_hfscstats(struct queue_stats cur)
366{
367	printf("  [ pkts: %10llu  bytes: %10llu  "
368	    "dropped pkts: %6llu bytes: %6llu ]\n",
369	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
370	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
371	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
372	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
373	printf("  [ qlength: %3d/%3d ]\n",
374	    cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
375
376	if (cur.avgn < 2)
377		return;
378
379	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
380	    cur.avg_packets / STAT_INTERVAL,
381	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
382}
383
384void
385pfctl_free_altq_node(struct pf_altq_node *node)
386{
387	while (node != NULL) {
388		struct pf_altq_node	*prev;
389
390		if (node->children != NULL)
391			pfctl_free_altq_node(node->children);
392		prev = node;
393		node = node->next;
394		free(prev);
395	}
396}
397
398void
399update_avg(struct pf_altq_node *a)
400{
401	struct queue_stats	*qs;
402	u_int64_t		 b, p;
403	int			 n;
404
405	if (a->altq.qid == 0)
406		return;
407
408	qs = &a->qstats;
409	n = qs->avgn;
410
411	switch (a->altq.scheduler) {
412	case ALTQT_CBQ:
413		b = qs->data.cbq_stats.xmit_cnt.bytes;
414		p = qs->data.cbq_stats.xmit_cnt.packets;
415		break;
416	case ALTQT_PRIQ:
417		b = qs->data.priq_stats.xmitcnt.bytes;
418		p = qs->data.priq_stats.xmitcnt.packets;
419		break;
420	case ALTQT_HFSC:
421		b = qs->data.hfsc_stats.xmit_cnt.bytes;
422		p = qs->data.hfsc_stats.xmit_cnt.packets;
423		break;
424	default:
425		b = 0;
426		p = 0;
427		break;
428	}
429
430	if (n == 0) {
431		qs->prev_bytes = b;
432		qs->prev_packets = p;
433		qs->avgn++;
434		return;
435	}
436
437	if (b >= qs->prev_bytes)
438		qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
439		    (b - qs->prev_bytes)) / n;
440
441	if (p >= qs->prev_packets)
442		qs->avg_packets = ((qs->avg_packets * (n - 1)) +
443		    (p - qs->prev_packets)) / n;
444
445	qs->prev_bytes = b;
446	qs->prev_packets = p;
447	if (n < AVGN_MAX)
448		qs->avgn++;
449}
450