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