1145840Smlaier/*	$OpenBSD: pfctl_qstats.c,v 1.30 2004/04/27 21:47:32 kjc Exp $ */
2126353Smlaier
3126353Smlaier/*
4126353Smlaier * Copyright (c) Henning Brauer <henning@openbsd.org>
5126353Smlaier *
6126353Smlaier * Permission to use, copy, modify, and distribute this software for any
7126353Smlaier * purpose with or without fee is hereby granted, provided that the above
8126353Smlaier * copyright notice and this permission notice appear in all copies.
9126353Smlaier *
10126353Smlaier * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11126353Smlaier * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12126353Smlaier * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13126353Smlaier * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14126353Smlaier * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15126353Smlaier * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16126353Smlaier * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17126353Smlaier */
18126353Smlaier
19127082Sobrien#include <sys/cdefs.h>
20127082Sobrien__FBSDID("$FreeBSD$");
21127082Sobrien
22126353Smlaier#include <sys/types.h>
23126353Smlaier#include <sys/ioctl.h>
24126353Smlaier#include <sys/socket.h>
25126353Smlaier
26126353Smlaier#include <net/if.h>
27126353Smlaier#include <netinet/in.h>
28126353Smlaier#include <net/pfvar.h>
29126353Smlaier#include <arpa/inet.h>
30126353Smlaier
31126353Smlaier#include <err.h>
32126353Smlaier#include <stdio.h>
33126353Smlaier#include <stdlib.h>
34126353Smlaier#include <string.h>
35126353Smlaier#include <unistd.h>
36126353Smlaier
37126353Smlaier#include <altq/altq.h>
38126353Smlaier#include <altq/altq_cbq.h>
39126353Smlaier#include <altq/altq_priq.h>
40126353Smlaier#include <altq/altq_hfsc.h>
41126353Smlaier
42126353Smlaier#include "pfctl.h"
43126353Smlaier#include "pfctl_parser.h"
44126353Smlaier
45126353Smlaierunion class_stats {
46126353Smlaier	class_stats_t		cbq_stats;
47126353Smlaier	struct priq_classstats	priq_stats;
48126353Smlaier	struct hfsc_classstats	hfsc_stats;
49126353Smlaier};
50126353Smlaier
51126353Smlaier#define AVGN_MAX	8
52126353Smlaier#define STAT_INTERVAL	5
53126353Smlaier
54126353Smlaierstruct queue_stats {
55126353Smlaier	union class_stats	 data;
56126353Smlaier	int			 avgn;
57126353Smlaier	double			 avg_bytes;
58126353Smlaier	double			 avg_packets;
59126353Smlaier	u_int64_t		 prev_bytes;
60126353Smlaier	u_int64_t		 prev_packets;
61126353Smlaier};
62126353Smlaier
63126353Smlaierstruct pf_altq_node {
64126353Smlaier	struct pf_altq		 altq;
65126353Smlaier	struct pf_altq_node	*next;
66126353Smlaier	struct pf_altq_node	*children;
67126353Smlaier	struct queue_stats	 qstats;
68126353Smlaier};
69126353Smlaier
70126353Smlaierint			 pfctl_update_qstats(int, struct pf_altq_node **);
71126353Smlaiervoid			 pfctl_insert_altq_node(struct pf_altq_node **,
72126353Smlaier			    const struct pf_altq, const struct queue_stats);
73126353Smlaierstruct pf_altq_node	*pfctl_find_altq_node(struct pf_altq_node *,
74126353Smlaier			    const char *, const char *);
75126353Smlaiervoid			 pfctl_print_altq_node(int, const struct pf_altq_node *,
76223637Sbz			    unsigned, int);
77126353Smlaiervoid			 print_cbqstats(struct queue_stats);
78126353Smlaiervoid			 print_priqstats(struct queue_stats);
79126353Smlaiervoid			 print_hfscstats(struct queue_stats);
80126353Smlaiervoid			 pfctl_free_altq_node(struct pf_altq_node *);
81126353Smlaiervoid			 pfctl_print_altq_nodestat(int,
82126353Smlaier			    const struct pf_altq_node *);
83126353Smlaier
84126353Smlaiervoid			 update_avg(struct pf_altq_node *);
85126353Smlaier
86126353Smlaierint
87130617Smlaierpfctl_show_altq(int dev, const char *iface, int opts, int verbose2)
88126353Smlaier{
89126353Smlaier	struct pf_altq_node	*root = NULL, *node;
90130617Smlaier	int			 nodes, dotitle = (opts & PF_OPT_SHOWALL);
91126353Smlaier
92127024Smlaier#ifdef __FreeBSD__
93126355Smlaier	if (!altqsupport)
94126355Smlaier		return (-1);
95126355Smlaier#endif
96130617Smlaier
97130617Smlaier	if ((nodes = pfctl_update_qstats(dev, &root)) < 0)
98126353Smlaier		return (-1);
99126353Smlaier
100145840Smlaier	if (nodes == 0)
101145840Smlaier		printf("No queue in use\n");
102130617Smlaier	for (node = root; node != NULL; node = node->next) {
103130617Smlaier		if (iface != NULL && strcmp(node->altq.ifname, iface))
104130617Smlaier			continue;
105130617Smlaier		if (dotitle) {
106130617Smlaier			pfctl_print_title("ALTQ:");
107130617Smlaier			dotitle = 0;
108130617Smlaier		}
109126353Smlaier		pfctl_print_altq_node(dev, node, 0, opts);
110130617Smlaier	}
111126353Smlaier
112145840Smlaier	while (verbose2 && nodes > 0) {
113126353Smlaier		printf("\n");
114126353Smlaier		fflush(stdout);
115126353Smlaier		sleep(STAT_INTERVAL);
116145840Smlaier		if ((nodes = pfctl_update_qstats(dev, &root)) == -1)
117126353Smlaier			return (-1);
118130617Smlaier		for (node = root; node != NULL; node = node->next) {
119130617Smlaier			if (iface != NULL && strcmp(node->altq.ifname, iface))
120130617Smlaier				continue;
121177700Smlaier#ifdef __FreeBSD__
122177700Smlaier			if (node->altq.local_flags & PFALTQ_FLAG_IF_REMOVED)
123177700Smlaier				continue;
124177700Smlaier#endif
125126353Smlaier			pfctl_print_altq_node(dev, node, 0, opts);
126130617Smlaier		}
127126353Smlaier	}
128126353Smlaier	pfctl_free_altq_node(root);
129126353Smlaier	return (0);
130126353Smlaier}
131126353Smlaier
132126353Smlaierint
133126353Smlaierpfctl_update_qstats(int dev, struct pf_altq_node **root)
134126353Smlaier{
135126353Smlaier	struct pf_altq_node	*node;
136126353Smlaier	struct pfioc_altq	 pa;
137126353Smlaier	struct pfioc_qstats	 pq;
138126353Smlaier	u_int32_t		 mnr, nr;
139126353Smlaier	struct queue_stats	 qstats;
140126353Smlaier	static	u_int32_t	 last_ticket;
141126353Smlaier
142126353Smlaier	memset(&pa, 0, sizeof(pa));
143126353Smlaier	memset(&pq, 0, sizeof(pq));
144126353Smlaier	memset(&qstats, 0, sizeof(qstats));
145126353Smlaier	if (ioctl(dev, DIOCGETALTQS, &pa)) {
146126353Smlaier		warn("DIOCGETALTQS");
147126353Smlaier		return (-1);
148126353Smlaier	}
149126353Smlaier
150126353Smlaier	/* if a new set is found, start over */
151126353Smlaier	if (pa.ticket != last_ticket && *root != NULL) {
152126353Smlaier		pfctl_free_altq_node(*root);
153126353Smlaier		*root = NULL;
154126353Smlaier	}
155126353Smlaier	last_ticket = pa.ticket;
156126353Smlaier
157126353Smlaier	mnr = pa.nr;
158126353Smlaier	for (nr = 0; nr < mnr; ++nr) {
159126353Smlaier		pa.nr = nr;
160126353Smlaier		if (ioctl(dev, DIOCGETALTQ, &pa)) {
161126353Smlaier			warn("DIOCGETALTQ");
162126353Smlaier			return (-1);
163126353Smlaier		}
164177700Smlaier#ifdef __FreeBSD__
165177700Smlaier		if (pa.altq.qid > 0 &&
166177700Smlaier		    !(pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED)) {
167177700Smlaier#else
168126353Smlaier		if (pa.altq.qid > 0) {
169177700Smlaier#endif
170126353Smlaier			pq.nr = nr;
171126353Smlaier			pq.ticket = pa.ticket;
172126353Smlaier			pq.buf = &qstats.data;
173126353Smlaier			pq.nbytes = sizeof(qstats.data);
174126353Smlaier			if (ioctl(dev, DIOCGETQSTATS, &pq)) {
175126353Smlaier				warn("DIOCGETQSTATS");
176126353Smlaier				return (-1);
177126353Smlaier			}
178126353Smlaier			if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
179126353Smlaier			    pa.altq.ifname)) != NULL) {
180126353Smlaier				memcpy(&node->qstats.data, &qstats.data,
181126353Smlaier				    sizeof(qstats.data));
182126353Smlaier				update_avg(node);
183126353Smlaier			} else {
184126353Smlaier				pfctl_insert_altq_node(root, pa.altq, qstats);
185126353Smlaier			}
186126353Smlaier		}
187177700Smlaier#ifdef __FreeBSD__
188223637Sbz		else if (pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED) {
189223637Sbz			memset(&qstats.data, 0, sizeof(qstats.data));
190177700Smlaier			if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
191177700Smlaier			    pa.altq.ifname)) != NULL) {
192177700Smlaier				memcpy(&node->qstats.data, &qstats.data,
193177700Smlaier				    sizeof(qstats.data));
194177700Smlaier				update_avg(node);
195177700Smlaier			} else {
196177700Smlaier				pfctl_insert_altq_node(root, pa.altq, qstats);
197223637Sbz			}
198177700Smlaier		}
199177700Smlaier#endif
200126353Smlaier	}
201130617Smlaier	return (mnr);
202126353Smlaier}
203126353Smlaier
204126353Smlaiervoid
205126353Smlaierpfctl_insert_altq_node(struct pf_altq_node **root,
206126353Smlaier    const struct pf_altq altq, const struct queue_stats qstats)
207126353Smlaier{
208126353Smlaier	struct pf_altq_node	*node;
209126353Smlaier
210126353Smlaier	node = calloc(1, sizeof(struct pf_altq_node));
211126353Smlaier	if (node == NULL)
212126353Smlaier		err(1, "pfctl_insert_altq_node: calloc");
213126353Smlaier	memcpy(&node->altq, &altq, sizeof(struct pf_altq));
214126353Smlaier	memcpy(&node->qstats, &qstats, sizeof(qstats));
215126353Smlaier	node->next = node->children = NULL;
216126353Smlaier
217126353Smlaier	if (*root == NULL)
218126353Smlaier		*root = node;
219126353Smlaier	else if (!altq.parent[0]) {
220126353Smlaier		struct pf_altq_node	*prev = *root;
221126353Smlaier
222126353Smlaier		while (prev->next != NULL)
223126353Smlaier			prev = prev->next;
224126353Smlaier		prev->next = node;
225126353Smlaier	} else {
226126353Smlaier		struct pf_altq_node	*parent;
227126353Smlaier
228126353Smlaier		parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
229126353Smlaier		if (parent == NULL)
230126353Smlaier			errx(1, "parent %s not found", altq.parent);
231126353Smlaier		if (parent->children == NULL)
232126353Smlaier			parent->children = node;
233126353Smlaier		else {
234126353Smlaier			struct pf_altq_node *prev = parent->children;
235126353Smlaier
236126353Smlaier			while (prev->next != NULL)
237126353Smlaier				prev = prev->next;
238126353Smlaier			prev->next = node;
239126353Smlaier		}
240126353Smlaier	}
241126353Smlaier	update_avg(node);
242126353Smlaier}
243126353Smlaier
244126353Smlaierstruct pf_altq_node *
245126353Smlaierpfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
246126353Smlaier    const char *ifname)
247126353Smlaier{
248126353Smlaier	struct pf_altq_node	*node, *child;
249126353Smlaier
250126353Smlaier	for (node = root; node != NULL; node = node->next) {
251126353Smlaier		if (!strcmp(node->altq.qname, qname)
252126353Smlaier		    && !(strcmp(node->altq.ifname, ifname)))
253126353Smlaier			return (node);
254126353Smlaier		if (node->children != NULL) {
255126353Smlaier			child = pfctl_find_altq_node(node->children, qname,
256126353Smlaier			    ifname);
257126353Smlaier			if (child != NULL)
258126353Smlaier				return (child);
259126353Smlaier		}
260126353Smlaier	}
261126353Smlaier	return (NULL);
262126353Smlaier}
263126353Smlaier
264126353Smlaiervoid
265223637Sbzpfctl_print_altq_node(int dev, const struct pf_altq_node *node,
266223637Sbz    unsigned int level, int opts)
267126353Smlaier{
268126353Smlaier	const struct pf_altq_node	*child;
269126353Smlaier
270126353Smlaier	if (node == NULL)
271126353Smlaier		return;
272126353Smlaier
273126353Smlaier	print_altq(&node->altq, level, NULL, NULL);
274126353Smlaier
275126353Smlaier	if (node->children != NULL) {
276126353Smlaier		printf("{");
277126353Smlaier		for (child = node->children; child != NULL;
278126353Smlaier		    child = child->next) {
279126353Smlaier			printf("%s", child->altq.qname);
280126353Smlaier			if (child->next != NULL)
281126353Smlaier				printf(", ");
282126353Smlaier		}
283126353Smlaier		printf("}");
284126353Smlaier	}
285126353Smlaier	printf("\n");
286126353Smlaier
287126353Smlaier	if (opts & PF_OPT_VERBOSE)
288126353Smlaier		pfctl_print_altq_nodestat(dev, node);
289126353Smlaier
290126353Smlaier	if (opts & PF_OPT_DEBUG)
291130617Smlaier		printf("  [ qid=%u ifname=%s ifbandwidth=%s ]\n",
292130617Smlaier		    node->altq.qid, node->altq.ifname,
293130617Smlaier		    rate2str((double)(node->altq.ifbandwidth)));
294126353Smlaier
295126353Smlaier	for (child = node->children; child != NULL;
296126353Smlaier	    child = child->next)
297130617Smlaier		pfctl_print_altq_node(dev, child, level + 1, opts);
298126353Smlaier}
299126353Smlaier
300126353Smlaiervoid
301126353Smlaierpfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a)
302126353Smlaier{
303126353Smlaier	if (a->altq.qid == 0)
304126353Smlaier		return;
305223637Sbz
306177700Smlaier#ifdef __FreeBSD__
307177700Smlaier	if (a->altq.local_flags & PFALTQ_FLAG_IF_REMOVED)
308177700Smlaier		return;
309177700Smlaier#endif
310126353Smlaier	switch (a->altq.scheduler) {
311126353Smlaier	case ALTQT_CBQ:
312126353Smlaier		print_cbqstats(a->qstats);
313126353Smlaier		break;
314126353Smlaier	case ALTQT_PRIQ:
315126353Smlaier		print_priqstats(a->qstats);
316126353Smlaier		break;
317126353Smlaier	case ALTQT_HFSC:
318126353Smlaier		print_hfscstats(a->qstats);
319126353Smlaier		break;
320126353Smlaier	}
321126353Smlaier}
322126353Smlaier
323126353Smlaiervoid
324126353Smlaierprint_cbqstats(struct queue_stats cur)
325126353Smlaier{
326127024Smlaier	printf("  [ pkts: %10llu  bytes: %10llu  "
327127024Smlaier	    "dropped pkts: %6llu bytes: %6llu ]\n",
328127024Smlaier	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
329127024Smlaier	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
330127024Smlaier	    (unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
331127024Smlaier	    (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
332126353Smlaier	printf("  [ qlength: %3d/%3d  borrows: %6u  suspends: %6u ]\n",
333126353Smlaier	    cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
334126353Smlaier	    cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
335126353Smlaier
336126353Smlaier	if (cur.avgn < 2)
337126353Smlaier		return;
338126353Smlaier
339126353Smlaier	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
340126353Smlaier	    cur.avg_packets / STAT_INTERVAL,
341126353Smlaier	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
342126353Smlaier}
343126353Smlaier
344126353Smlaiervoid
345126353Smlaierprint_priqstats(struct queue_stats cur)
346126353Smlaier{
347127024Smlaier	printf("  [ pkts: %10llu  bytes: %10llu  "
348127024Smlaier	    "dropped pkts: %6llu bytes: %6llu ]\n",
349127024Smlaier	    (unsigned long long)cur.data.priq_stats.xmitcnt.packets,
350127024Smlaier	    (unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
351127024Smlaier	    (unsigned long long)cur.data.priq_stats.dropcnt.packets,
352127024Smlaier	    (unsigned long long)cur.data.priq_stats.dropcnt.bytes);
353126353Smlaier	printf("  [ qlength: %3d/%3d ]\n",
354126353Smlaier	    cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
355126353Smlaier
356126353Smlaier	if (cur.avgn < 2)
357126353Smlaier		return;
358126353Smlaier
359126353Smlaier	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
360126353Smlaier	    cur.avg_packets / STAT_INTERVAL,
361126353Smlaier	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
362126353Smlaier}
363126353Smlaier
364126353Smlaiervoid
365126353Smlaierprint_hfscstats(struct queue_stats cur)
366126353Smlaier{
367127024Smlaier	printf("  [ pkts: %10llu  bytes: %10llu  "
368127024Smlaier	    "dropped pkts: %6llu bytes: %6llu ]\n",
369127024Smlaier	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
370127024Smlaier	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
371127024Smlaier	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
372127024Smlaier	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
373126353Smlaier	printf("  [ qlength: %3d/%3d ]\n",
374126353Smlaier	    cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
375126353Smlaier
376126353Smlaier	if (cur.avgn < 2)
377126353Smlaier		return;
378126353Smlaier
379126353Smlaier	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
380126353Smlaier	    cur.avg_packets / STAT_INTERVAL,
381126353Smlaier	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
382126353Smlaier}
383126353Smlaier
384126353Smlaiervoid
385126353Smlaierpfctl_free_altq_node(struct pf_altq_node *node)
386126353Smlaier{
387126353Smlaier	while (node != NULL) {
388126353Smlaier		struct pf_altq_node	*prev;
389126353Smlaier
390126353Smlaier		if (node->children != NULL)
391126353Smlaier			pfctl_free_altq_node(node->children);
392126353Smlaier		prev = node;
393126353Smlaier		node = node->next;
394126353Smlaier		free(prev);
395126353Smlaier	}
396126353Smlaier}
397126353Smlaier
398126353Smlaiervoid
399126353Smlaierupdate_avg(struct pf_altq_node *a)
400126353Smlaier{
401126353Smlaier	struct queue_stats	*qs;
402126353Smlaier	u_int64_t		 b, p;
403126353Smlaier	int			 n;
404126353Smlaier
405126353Smlaier	if (a->altq.qid == 0)
406126353Smlaier		return;
407126353Smlaier
408126353Smlaier	qs = &a->qstats;
409126353Smlaier	n = qs->avgn;
410126353Smlaier
411126353Smlaier	switch (a->altq.scheduler) {
412126353Smlaier	case ALTQT_CBQ:
413126353Smlaier		b = qs->data.cbq_stats.xmit_cnt.bytes;
414126353Smlaier		p = qs->data.cbq_stats.xmit_cnt.packets;
415126353Smlaier		break;
416126353Smlaier	case ALTQT_PRIQ:
417126353Smlaier		b = qs->data.priq_stats.xmitcnt.bytes;
418126353Smlaier		p = qs->data.priq_stats.xmitcnt.packets;
419126353Smlaier		break;
420126353Smlaier	case ALTQT_HFSC:
421126353Smlaier		b = qs->data.hfsc_stats.xmit_cnt.bytes;
422126353Smlaier		p = qs->data.hfsc_stats.xmit_cnt.packets;
423126353Smlaier		break;
424126353Smlaier	default:
425126353Smlaier		b = 0;
426126353Smlaier		p = 0;
427126353Smlaier		break;
428126353Smlaier	}
429126353Smlaier
430126353Smlaier	if (n == 0) {
431126353Smlaier		qs->prev_bytes = b;
432126353Smlaier		qs->prev_packets = p;
433126353Smlaier		qs->avgn++;
434126353Smlaier		return;
435126353Smlaier	}
436126353Smlaier
437126353Smlaier	if (b >= qs->prev_bytes)
438126353Smlaier		qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
439126353Smlaier		    (b - qs->prev_bytes)) / n;
440126353Smlaier
441126353Smlaier	if (p >= qs->prev_packets)
442126353Smlaier		qs->avg_packets = ((qs->avg_packets * (n - 1)) +
443126353Smlaier		    (p - qs->prev_packets)) / n;
444126353Smlaier
445126353Smlaier	qs->prev_bytes = b;
446126353Smlaier	qs->prev_packets = p;
447126353Smlaier	if (n < AVGN_MAX)
448126353Smlaier		qs->avgn++;
449126353Smlaier}
450