pfctl_qstats.c revision 130617
1/*	$OpenBSD: pfctl_qstats.c,v 1.29 2004/03/15 15:25:44 dhartmei 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: head/contrib/pf/pfctl/pfctl_qstats.c 130617 2004-06-16 23:39:33Z mlaier $");
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	for (node = root; node != NULL; node = node->next) {
101		if (iface != NULL && strcmp(node->altq.ifname, iface))
102			continue;
103		if (dotitle) {
104			pfctl_print_title("ALTQ:");
105			dotitle = 0;
106		}
107		pfctl_print_altq_node(dev, node, 0, opts);
108	}
109
110	while (verbose2) {
111		printf("\n");
112		fflush(stdout);
113		sleep(STAT_INTERVAL);
114		if (pfctl_update_qstats(dev, &root) == -1)
115			return (-1);
116		for (node = root; node != NULL; node = node->next) {
117			if (iface != NULL && strcmp(node->altq.ifname, iface))
118				continue;
119			pfctl_print_altq_node(dev, node, 0, opts);
120		}
121	}
122	pfctl_free_altq_node(root);
123	return (0);
124}
125
126int
127pfctl_update_qstats(int dev, struct pf_altq_node **root)
128{
129	struct pf_altq_node	*node;
130	struct pfioc_altq	 pa;
131	struct pfioc_qstats	 pq;
132	u_int32_t		 mnr, nr;
133	struct queue_stats	 qstats;
134	static	u_int32_t	 last_ticket;
135
136	memset(&pa, 0, sizeof(pa));
137	memset(&pq, 0, sizeof(pq));
138	memset(&qstats, 0, sizeof(qstats));
139	if (ioctl(dev, DIOCGETALTQS, &pa)) {
140		warn("DIOCGETALTQS");
141		return (-1);
142	}
143
144	/* if a new set is found, start over */
145	if (pa.ticket != last_ticket && *root != NULL) {
146		pfctl_free_altq_node(*root);
147		*root = NULL;
148	}
149	last_ticket = pa.ticket;
150
151	mnr = pa.nr;
152	for (nr = 0; nr < mnr; ++nr) {
153		pa.nr = nr;
154		if (ioctl(dev, DIOCGETALTQ, &pa)) {
155			warn("DIOCGETALTQ");
156			return (-1);
157		}
158		if (pa.altq.qid > 0) {
159			pq.nr = nr;
160			pq.ticket = pa.ticket;
161			pq.buf = &qstats.data;
162			pq.nbytes = sizeof(qstats.data);
163			if (ioctl(dev, DIOCGETQSTATS, &pq)) {
164				warn("DIOCGETQSTATS");
165				return (-1);
166			}
167			if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
168			    pa.altq.ifname)) != NULL) {
169				memcpy(&node->qstats.data, &qstats.data,
170				    sizeof(qstats.data));
171				update_avg(node);
172			} else {
173				pfctl_insert_altq_node(root, pa.altq, qstats);
174			}
175		}
176	}
177	return (mnr);
178}
179
180void
181pfctl_insert_altq_node(struct pf_altq_node **root,
182    const struct pf_altq altq, const struct queue_stats qstats)
183{
184	struct pf_altq_node	*node;
185
186	node = calloc(1, sizeof(struct pf_altq_node));
187	if (node == NULL)
188		err(1, "pfctl_insert_altq_node: calloc");
189	memcpy(&node->altq, &altq, sizeof(struct pf_altq));
190	memcpy(&node->qstats, &qstats, sizeof(qstats));
191	node->next = node->children = NULL;
192
193	if (*root == NULL)
194		*root = node;
195	else if (!altq.parent[0]) {
196		struct pf_altq_node	*prev = *root;
197
198		while (prev->next != NULL)
199			prev = prev->next;
200		prev->next = node;
201	} else {
202		struct pf_altq_node	*parent;
203
204		parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
205		if (parent == NULL)
206			errx(1, "parent %s not found", altq.parent);
207		if (parent->children == NULL)
208			parent->children = node;
209		else {
210			struct pf_altq_node *prev = parent->children;
211
212			while (prev->next != NULL)
213				prev = prev->next;
214			prev->next = node;
215		}
216	}
217	update_avg(node);
218}
219
220struct pf_altq_node *
221pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
222    const char *ifname)
223{
224	struct pf_altq_node	*node, *child;
225
226	for (node = root; node != NULL; node = node->next) {
227		if (!strcmp(node->altq.qname, qname)
228		    && !(strcmp(node->altq.ifname, ifname)))
229			return (node);
230		if (node->children != NULL) {
231			child = pfctl_find_altq_node(node->children, qname,
232			    ifname);
233			if (child != NULL)
234				return (child);
235		}
236	}
237	return (NULL);
238}
239
240void
241pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level,
242    int opts)
243{
244	const struct pf_altq_node	*child;
245
246	if (node == NULL)
247		return;
248
249	print_altq(&node->altq, level, NULL, NULL);
250
251	if (node->children != NULL) {
252		printf("{");
253		for (child = node->children; child != NULL;
254		    child = child->next) {
255			printf("%s", child->altq.qname);
256			if (child->next != NULL)
257				printf(", ");
258		}
259		printf("}");
260	}
261	printf("\n");
262
263	if (opts & PF_OPT_VERBOSE)
264		pfctl_print_altq_nodestat(dev, node);
265
266	if (opts & PF_OPT_DEBUG)
267		printf("  [ qid=%u ifname=%s ifbandwidth=%s ]\n",
268		    node->altq.qid, node->altq.ifname,
269		    rate2str((double)(node->altq.ifbandwidth)));
270
271	for (child = node->children; child != NULL;
272	    child = child->next)
273		pfctl_print_altq_node(dev, child, level + 1, opts);
274}
275
276void
277pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a)
278{
279	if (a->altq.qid == 0)
280		return;
281
282	switch (a->altq.scheduler) {
283	case ALTQT_CBQ:
284		print_cbqstats(a->qstats);
285		break;
286	case ALTQT_PRIQ:
287		print_priqstats(a->qstats);
288		break;
289	case ALTQT_HFSC:
290		print_hfscstats(a->qstats);
291		break;
292	}
293}
294
295void
296print_cbqstats(struct queue_stats cur)
297{
298	printf("  [ pkts: %10llu  bytes: %10llu  "
299	    "dropped pkts: %6llu bytes: %6llu ]\n",
300	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
301	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
302	    (unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
303	    (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
304	printf("  [ qlength: %3d/%3d  borrows: %6u  suspends: %6u ]\n",
305	    cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
306	    cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
307
308	if (cur.avgn < 2)
309		return;
310
311	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
312	    cur.avg_packets / STAT_INTERVAL,
313	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
314}
315
316void
317print_priqstats(struct queue_stats cur)
318{
319	printf("  [ pkts: %10llu  bytes: %10llu  "
320	    "dropped pkts: %6llu bytes: %6llu ]\n",
321	    (unsigned long long)cur.data.priq_stats.xmitcnt.packets,
322	    (unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
323	    (unsigned long long)cur.data.priq_stats.dropcnt.packets,
324	    (unsigned long long)cur.data.priq_stats.dropcnt.bytes);
325	printf("  [ qlength: %3d/%3d ]\n",
326	    cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
327
328	if (cur.avgn < 2)
329		return;
330
331	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
332	    cur.avg_packets / STAT_INTERVAL,
333	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
334}
335
336void
337print_hfscstats(struct queue_stats cur)
338{
339	printf("  [ pkts: %10llu  bytes: %10llu  "
340	    "dropped pkts: %6llu bytes: %6llu ]\n",
341	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
342	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
343	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
344	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
345	printf("  [ qlength: %3d/%3d ]\n",
346	    cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
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
357pfctl_free_altq_node(struct pf_altq_node *node)
358{
359	while (node != NULL) {
360		struct pf_altq_node	*prev;
361
362		if (node->children != NULL)
363			pfctl_free_altq_node(node->children);
364		prev = node;
365		node = node->next;
366		free(prev);
367	}
368}
369
370void
371update_avg(struct pf_altq_node *a)
372{
373	struct queue_stats	*qs;
374	u_int64_t		 b, p;
375	int			 n;
376
377	if (a->altq.qid == 0)
378		return;
379
380	qs = &a->qstats;
381	n = qs->avgn;
382
383	switch (a->altq.scheduler) {
384	case ALTQT_CBQ:
385		b = qs->data.cbq_stats.xmit_cnt.bytes;
386		p = qs->data.cbq_stats.xmit_cnt.packets;
387		break;
388	case ALTQT_PRIQ:
389		b = qs->data.priq_stats.xmitcnt.bytes;
390		p = qs->data.priq_stats.xmitcnt.packets;
391		break;
392	case ALTQT_HFSC:
393		b = qs->data.hfsc_stats.xmit_cnt.bytes;
394		p = qs->data.hfsc_stats.xmit_cnt.packets;
395		break;
396	default:
397		b = 0;
398		p = 0;
399		break;
400	}
401
402	if (n == 0) {
403		qs->prev_bytes = b;
404		qs->prev_packets = p;
405		qs->avgn++;
406		return;
407	}
408
409	if (b >= qs->prev_bytes)
410		qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
411		    (b - qs->prev_bytes)) / n;
412
413	if (p >= qs->prev_packets)
414		qs->avg_packets = ((qs->avg_packets * (n - 1)) +
415		    (p - qs->prev_packets)) / n;
416
417	qs->prev_bytes = b;
418	qs->prev_packets = p;
419	if (n < AVGN_MAX)
420		qs->avgn++;
421}
422