xref: /freebsd/sbin/pfctl/pfctl_qstats.c (revision 02e9120893770924227138ba49df1edb3896112a)
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 #define PFIOC_USE_LATEST
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 <net/altq/altq.h>
38 #include <net/altq/altq_cbq.h>
39 #include <net/altq/altq_codel.h>
40 #include <net/altq/altq_priq.h>
41 #include <net/altq/altq_hfsc.h>
42 #include <net/altq/altq_fairq.h>
43 
44 #include "pfctl.h"
45 #include "pfctl_parser.h"
46 
47 union class_stats {
48 	class_stats_t		cbq_stats;
49 	struct priq_classstats	priq_stats;
50 	struct hfsc_classstats	hfsc_stats;
51 	struct fairq_classstats fairq_stats;
52 	struct codel_ifstats	codel_stats;
53 };
54 
55 #define AVGN_MAX	8
56 #define STAT_INTERVAL	5
57 
58 struct queue_stats {
59 	union class_stats	 data;
60 	int			 avgn;
61 	double			 avg_bytes;
62 	double			 avg_packets;
63 	u_int64_t		 prev_bytes;
64 	u_int64_t		 prev_packets;
65 };
66 
67 struct pf_altq_node {
68 	struct pf_altq		 altq;
69 	struct pf_altq_node	*next;
70 	struct pf_altq_node	*children;
71 	struct queue_stats	 qstats;
72 };
73 
74 int			 pfctl_update_qstats(int, struct pf_altq_node **);
75 void			 pfctl_insert_altq_node(struct pf_altq_node **,
76 			    const struct pf_altq, const struct queue_stats);
77 struct pf_altq_node	*pfctl_find_altq_node(struct pf_altq_node *,
78 			    const char *, const char *);
79 void			 pfctl_print_altq_node(int, const struct pf_altq_node *,
80 			    unsigned, int);
81 void			 print_cbqstats(struct queue_stats);
82 void			 print_codelstats(struct queue_stats);
83 void			 print_priqstats(struct queue_stats);
84 void			 print_hfscstats(struct queue_stats);
85 void			 print_fairqstats(struct queue_stats);
86 void			 pfctl_free_altq_node(struct pf_altq_node *);
87 void			 pfctl_print_altq_nodestat(int,
88 			    const struct pf_altq_node *);
89 
90 void			 update_avg(struct pf_altq_node *);
91 
92 int
93 pfctl_show_altq(int dev, const char *iface, int opts, int verbose2)
94 {
95 	struct pf_altq_node	*root = NULL, *node;
96 	int			 nodes, dotitle = (opts & PF_OPT_SHOWALL);
97 
98 #ifdef __FreeBSD__
99 	if (!altqsupport)
100 		return (-1);
101 #endif
102 
103 	if ((nodes = pfctl_update_qstats(dev, &root)) < 0)
104 		return (-1);
105 
106 	if (nodes == 0)
107 		printf("No queue in use\n");
108 	for (node = root; node != NULL; node = node->next) {
109 		if (iface != NULL && strcmp(node->altq.ifname, iface))
110 			continue;
111 		if (dotitle) {
112 			pfctl_print_title("ALTQ:");
113 			dotitle = 0;
114 		}
115 		pfctl_print_altq_node(dev, node, 0, opts);
116 	}
117 
118 	while (verbose2 && nodes > 0) {
119 		printf("\n");
120 		fflush(stdout);
121 		sleep(STAT_INTERVAL);
122 		if ((nodes = pfctl_update_qstats(dev, &root)) == -1)
123 			return (-1);
124 		for (node = root; node != NULL; node = node->next) {
125 			if (iface != NULL && strcmp(node->altq.ifname, iface))
126 				continue;
127 #ifdef __FreeBSD__
128 			if (node->altq.local_flags & PFALTQ_FLAG_IF_REMOVED)
129 				continue;
130 #endif
131 			pfctl_print_altq_node(dev, node, 0, opts);
132 		}
133 	}
134 	pfctl_free_altq_node(root);
135 	return (0);
136 }
137 
138 int
139 pfctl_update_qstats(int dev, struct pf_altq_node **root)
140 {
141 	struct pf_altq_node	*node;
142 	struct pfioc_altq	 pa;
143 	struct pfioc_qstats	 pq;
144 	u_int32_t		 mnr, nr;
145 	struct queue_stats	 qstats;
146 	static	u_int32_t	 last_ticket;
147 
148 	memset(&pa, 0, sizeof(pa));
149 	memset(&pq, 0, sizeof(pq));
150 	memset(&qstats, 0, sizeof(qstats));
151 	pa.version = PFIOC_ALTQ_VERSION;
152 	if (ioctl(dev, DIOCGETALTQS, &pa)) {
153 		warn("DIOCGETALTQS");
154 		return (-1);
155 	}
156 
157 	/* if a new set is found, start over */
158 	if (pa.ticket != last_ticket && *root != NULL) {
159 		pfctl_free_altq_node(*root);
160 		*root = NULL;
161 	}
162 	last_ticket = pa.ticket;
163 
164 	mnr = pa.nr;
165 	for (nr = 0; nr < mnr; ++nr) {
166 		pa.nr = nr;
167 		if (ioctl(dev, DIOCGETALTQ, &pa)) {
168 			warn("DIOCGETALTQ");
169 			return (-1);
170 		}
171 #ifdef __FreeBSD__
172 		if ((pa.altq.qid > 0 || pa.altq.scheduler == ALTQT_CODEL) &&
173 		    !(pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED)) {
174 #else
175 		if (pa.altq.qid > 0) {
176 #endif
177 			pq.nr = nr;
178 			pq.ticket = pa.ticket;
179 			pq.buf = &qstats.data;
180 			pq.nbytes = sizeof(qstats.data);
181 			pq.version = altq_stats_version(pa.altq.scheduler);
182 			if (ioctl(dev, DIOCGETQSTATS, &pq)) {
183 				warn("DIOCGETQSTATS");
184 				return (-1);
185 			}
186 			if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
187 			    pa.altq.ifname)) != NULL) {
188 				memcpy(&node->qstats.data, &qstats.data,
189 				    sizeof(qstats.data));
190 				update_avg(node);
191 			} else {
192 				pfctl_insert_altq_node(root, pa.altq, qstats);
193 			}
194 		}
195 #ifdef __FreeBSD__
196 		else if (pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED) {
197 			memset(&qstats.data, 0, sizeof(qstats.data));
198 			if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
199 			    pa.altq.ifname)) != NULL) {
200 				memcpy(&node->qstats.data, &qstats.data,
201 				    sizeof(qstats.data));
202 				update_avg(node);
203 			} else {
204 				pfctl_insert_altq_node(root, pa.altq, qstats);
205 			}
206 		}
207 #endif
208 	}
209 	return (mnr);
210 }
211 
212 void
213 pfctl_insert_altq_node(struct pf_altq_node **root,
214     const struct pf_altq altq, const struct queue_stats qstats)
215 {
216 	struct pf_altq_node	*node;
217 
218 	node = calloc(1, sizeof(struct pf_altq_node));
219 	if (node == NULL)
220 		err(1, "pfctl_insert_altq_node: calloc");
221 	memcpy(&node->altq, &altq, sizeof(struct pf_altq));
222 	memcpy(&node->qstats, &qstats, sizeof(qstats));
223 	node->next = node->children = NULL;
224 
225 	if (*root == NULL)
226 		*root = node;
227 	else if (!altq.parent[0]) {
228 		struct pf_altq_node	*prev = *root;
229 
230 		while (prev->next != NULL)
231 			prev = prev->next;
232 		prev->next = node;
233 	} else {
234 		struct pf_altq_node	*parent;
235 
236 		parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
237 		if (parent == NULL)
238 			errx(1, "parent %s not found", altq.parent);
239 		if (parent->children == NULL)
240 			parent->children = node;
241 		else {
242 			struct pf_altq_node *prev = parent->children;
243 
244 			while (prev->next != NULL)
245 				prev = prev->next;
246 			prev->next = node;
247 		}
248 	}
249 	update_avg(node);
250 }
251 
252 struct pf_altq_node *
253 pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
254     const char *ifname)
255 {
256 	struct pf_altq_node	*node, *child;
257 
258 	for (node = root; node != NULL; node = node->next) {
259 		if (!strcmp(node->altq.qname, qname)
260 		    && !(strcmp(node->altq.ifname, ifname)))
261 			return (node);
262 		if (node->children != NULL) {
263 			child = pfctl_find_altq_node(node->children, qname,
264 			    ifname);
265 			if (child != NULL)
266 				return (child);
267 		}
268 	}
269 	return (NULL);
270 }
271 
272 void
273 pfctl_print_altq_node(int dev, const struct pf_altq_node *node,
274     unsigned int level, int opts)
275 {
276 	const struct pf_altq_node	*child;
277 
278 	if (node == NULL)
279 		return;
280 
281 	print_altq(&node->altq, level, NULL, NULL);
282 
283 	if (node->children != NULL) {
284 		printf("{");
285 		for (child = node->children; child != NULL;
286 		    child = child->next) {
287 			printf("%s", child->altq.qname);
288 			if (child->next != NULL)
289 				printf(", ");
290 		}
291 		printf("}");
292 	}
293 	printf("\n");
294 
295 	if (opts & PF_OPT_VERBOSE)
296 		pfctl_print_altq_nodestat(dev, node);
297 
298 	if (opts & PF_OPT_DEBUG)
299 		printf("  [ qid=%u ifname=%s ifbandwidth=%s ]\n",
300 		    node->altq.qid, node->altq.ifname,
301 		    rate2str((double)(node->altq.ifbandwidth)));
302 
303 	for (child = node->children; child != NULL;
304 	    child = child->next)
305 		pfctl_print_altq_node(dev, child, level + 1, opts);
306 }
307 
308 void
309 pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a)
310 {
311 	if (a->altq.qid == 0 && a->altq.scheduler != ALTQT_CODEL)
312 		return;
313 
314 #ifdef __FreeBSD__
315 	if (a->altq.local_flags & PFALTQ_FLAG_IF_REMOVED)
316 		return;
317 #endif
318 	switch (a->altq.scheduler) {
319 	case ALTQT_CBQ:
320 		print_cbqstats(a->qstats);
321 		break;
322 	case ALTQT_PRIQ:
323 		print_priqstats(a->qstats);
324 		break;
325 	case ALTQT_HFSC:
326 		print_hfscstats(a->qstats);
327 		break;
328 	case ALTQT_FAIRQ:
329 		print_fairqstats(a->qstats);
330 		break;
331 	case ALTQT_CODEL:
332 		print_codelstats(a->qstats);
333 		break;
334 	}
335 }
336 
337 void
338 print_cbqstats(struct queue_stats cur)
339 {
340 	printf("  [ pkts: %10llu  bytes: %10llu  "
341 	    "dropped pkts: %6llu bytes: %6llu ]\n",
342 	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
343 	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
344 	    (unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
345 	    (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
346 	printf("  [ qlength: %3d/%3d  borrows: %6u  suspends: %6u ]\n",
347 	    cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
348 	    cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
349 
350 	if (cur.avgn < 2)
351 		return;
352 
353 	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
354 	    cur.avg_packets / STAT_INTERVAL,
355 	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
356 }
357 
358 void
359 print_codelstats(struct queue_stats cur)
360 {
361 	printf("  [ pkts: %10llu  bytes: %10llu  "
362 	    "dropped pkts: %6llu bytes: %6llu ]\n",
363 	    (unsigned long long)cur.data.codel_stats.cl_xmitcnt.packets,
364 	    (unsigned long long)cur.data.codel_stats.cl_xmitcnt.bytes,
365 	    (unsigned long long)cur.data.codel_stats.cl_dropcnt.packets +
366 	    cur.data.codel_stats.stats.drop_cnt.packets,
367 	    (unsigned long long)cur.data.codel_stats.cl_dropcnt.bytes +
368 	    cur.data.codel_stats.stats.drop_cnt.bytes);
369 	printf("  [ qlength: %3d/%3d ]\n",
370 	    cur.data.codel_stats.qlength, cur.data.codel_stats.qlimit);
371 
372 	if (cur.avgn < 2)
373 		return;
374 
375 	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
376 	    cur.avg_packets / STAT_INTERVAL,
377 	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
378 }
379 
380 void
381 print_priqstats(struct queue_stats cur)
382 {
383 	printf("  [ pkts: %10llu  bytes: %10llu  "
384 	    "dropped pkts: %6llu bytes: %6llu ]\n",
385 	    (unsigned long long)cur.data.priq_stats.xmitcnt.packets,
386 	    (unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
387 	    (unsigned long long)cur.data.priq_stats.dropcnt.packets,
388 	    (unsigned long long)cur.data.priq_stats.dropcnt.bytes);
389 	printf("  [ qlength: %3d/%3d ]\n",
390 	    cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
391 
392 	if (cur.avgn < 2)
393 		return;
394 
395 	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
396 	    cur.avg_packets / STAT_INTERVAL,
397 	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
398 }
399 
400 void
401 print_hfscstats(struct queue_stats cur)
402 {
403 	printf("  [ pkts: %10llu  bytes: %10llu  "
404 	    "dropped pkts: %6llu bytes: %6llu ]\n",
405 	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
406 	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
407 	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
408 	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
409 	printf("  [ qlength: %3d/%3d ]\n",
410 	    cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
411 
412 	if (cur.avgn < 2)
413 		return;
414 
415 	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
416 	    cur.avg_packets / STAT_INTERVAL,
417 	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
418 }
419 
420 void
421 print_fairqstats(struct queue_stats cur)
422 {
423 	printf("  [ pkts: %10llu  bytes: %10llu  "
424 	    "dropped pkts: %6llu bytes: %6llu ]\n",
425 	    (unsigned long long)cur.data.fairq_stats.xmit_cnt.packets,
426 	    (unsigned long long)cur.data.fairq_stats.xmit_cnt.bytes,
427 	    (unsigned long long)cur.data.fairq_stats.drop_cnt.packets,
428 	    (unsigned long long)cur.data.fairq_stats.drop_cnt.bytes);
429 	printf("  [ qlength: %3d/%3d ]\n",
430 	    cur.data.fairq_stats.qlength, cur.data.fairq_stats.qlimit);
431 
432 	if (cur.avgn < 2)
433 		return;
434 
435 	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
436 	    cur.avg_packets / STAT_INTERVAL,
437 	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
438 }
439 
440 void
441 pfctl_free_altq_node(struct pf_altq_node *node)
442 {
443 	while (node != NULL) {
444 		struct pf_altq_node	*prev;
445 
446 		if (node->children != NULL)
447 			pfctl_free_altq_node(node->children);
448 		prev = node;
449 		node = node->next;
450 		free(prev);
451 	}
452 }
453 
454 void
455 update_avg(struct pf_altq_node *a)
456 {
457 	struct queue_stats	*qs;
458 	u_int64_t		 b, p;
459 	int			 n;
460 
461 	if (a->altq.qid == 0 && a->altq.scheduler != ALTQT_CODEL)
462 		return;
463 
464 	qs = &a->qstats;
465 	n = qs->avgn;
466 
467 	switch (a->altq.scheduler) {
468 	case ALTQT_CBQ:
469 		b = qs->data.cbq_stats.xmit_cnt.bytes;
470 		p = qs->data.cbq_stats.xmit_cnt.packets;
471 		break;
472 	case ALTQT_PRIQ:
473 		b = qs->data.priq_stats.xmitcnt.bytes;
474 		p = qs->data.priq_stats.xmitcnt.packets;
475 		break;
476 	case ALTQT_HFSC:
477 		b = qs->data.hfsc_stats.xmit_cnt.bytes;
478 		p = qs->data.hfsc_stats.xmit_cnt.packets;
479 		break;
480 	case ALTQT_FAIRQ:
481 		b = qs->data.fairq_stats.xmit_cnt.bytes;
482 		p = qs->data.fairq_stats.xmit_cnt.packets;
483 		break;
484 	case ALTQT_CODEL:
485 		b = qs->data.codel_stats.cl_xmitcnt.bytes;
486 		p = qs->data.codel_stats.cl_xmitcnt.packets;
487 		break;
488 	default:
489 		b = 0;
490 		p = 0;
491 		break;
492 	}
493 
494 	if (n == 0) {
495 		qs->prev_bytes = b;
496 		qs->prev_packets = p;
497 		qs->avgn++;
498 		return;
499 	}
500 
501 	if (b >= qs->prev_bytes)
502 		qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
503 		    (b - qs->prev_bytes)) / n;
504 
505 	if (p >= qs->prev_packets)
506 		qs->avg_packets = ((qs->avg_packets * (n - 1)) +
507 		    (p - qs->prev_packets)) / n;
508 
509 	qs->prev_bytes = b;
510 	qs->prev_packets = p;
511 	if (n < AVGN_MAX)
512 		qs->avgn++;
513 }
514