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