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