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