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