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