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