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