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