xref: /dragonfly/usr.sbin/pfctl/pfctl_qstats.c (revision 62f7f702)
1 /*	$OpenBSD: pfctl_qstats.c,v 1.29 2004/03/15 15:25:44 dhartmei Exp $ */
2 /*	$DragonFly: src/usr.sbin/pfctl/pfctl_qstats.c,v 1.3 2008/04/06 18:58:14 dillon Exp $ */
3 
4 /*
5  * Copyright (c) Henning Brauer <henning@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #include <sys/socket.h>
23 
24 #include <net/if.h>
25 #include <netinet/in.h>
26 #include <net/pf/pfvar.h>
27 #include <arpa/inet.h>
28 
29 #include <err.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 
35 #include <net/altq/altq.h>
36 #include <net/altq/altq_cbq.h>
37 #include <net/altq/altq_priq.h>
38 #include <net/altq/altq_hfsc.h>
39 #include <net/altq/altq_fairq.h>
40 
41 #include "pfctl.h"
42 #include "pfctl_parser.h"
43 
44 union class_stats {
45 	class_stats_t		cbq_stats;
46 	struct priq_classstats	priq_stats;
47 	struct hfsc_classstats	hfsc_stats;
48 	struct fairq_classstats	fairq_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			 print_fairqstats(struct queue_stats);
81 void			 pfctl_free_altq_node(struct pf_altq_node *);
82 void			 pfctl_print_altq_nodestat(int,
83 			    const struct pf_altq_node *);
84 
85 void			 update_avg(struct pf_altq_node *);
86 
87 int
88 pfctl_show_altq(int dev, const char *iface, int opts, int verbose2)
89 {
90 	struct pf_altq_node	*root = NULL, *node;
91 	int			 nodes, dotitle = (opts & PF_OPT_SHOWALL);
92 
93 
94 	if ((nodes = pfctl_update_qstats(dev, &root)) < 0)
95 		return (-1);
96 
97 	for (node = root; node != NULL; node = node->next) {
98 		if (iface != NULL && strcmp(node->altq.ifname, iface))
99 			continue;
100 		if (dotitle) {
101 			pfctl_print_title("ALTQ:");
102 			dotitle = 0;
103 		}
104 		pfctl_print_altq_node(dev, node, 0, opts);
105 	}
106 
107 	while (verbose2) {
108 		printf("\n");
109 		fflush(stdout);
110 		sleep(STAT_INTERVAL);
111 		if (pfctl_update_qstats(dev, &root) == -1)
112 			return (-1);
113 		for (node = root; node != NULL; node = node->next) {
114 			if (iface != NULL && strcmp(node->altq.ifname, iface))
115 				continue;
116 			pfctl_print_altq_node(dev, node, 0, opts);
117 		}
118 	}
119 	pfctl_free_altq_node(root);
120 	return (0);
121 }
122 
123 int
124 pfctl_update_qstats(int dev, struct pf_altq_node **root)
125 {
126 	struct pf_altq_node	*node;
127 	struct pfioc_altq	 pa;
128 	struct pfioc_qstats	 pq;
129 	u_int32_t		 mnr, nr;
130 	struct queue_stats	 qstats;
131 	static	u_int32_t	 last_ticket;
132 
133 	memset(&pa, 0, sizeof(pa));
134 	memset(&pq, 0, sizeof(pq));
135 	memset(&qstats, 0, sizeof(qstats));
136 	if (ioctl(dev, DIOCGETALTQS, &pa)) {
137 		warn("DIOCGETALTQS");
138 		return (-1);
139 	}
140 
141 	/* if a new set is found, start over */
142 	if (pa.ticket != last_ticket && *root != NULL) {
143 		pfctl_free_altq_node(*root);
144 		*root = NULL;
145 	}
146 	last_ticket = pa.ticket;
147 
148 	mnr = pa.nr;
149 	for (nr = 0; nr < mnr; ++nr) {
150 		pa.nr = nr;
151 		if (ioctl(dev, DIOCGETALTQ, &pa)) {
152 			warn("DIOCGETALTQ");
153 			return (-1);
154 		}
155 		if (pa.altq.qid > 0) {
156 			pq.nr = nr;
157 			pq.ticket = pa.ticket;
158 			pq.buf = &qstats.data;
159 			pq.nbytes = sizeof(qstats.data);
160 			if (ioctl(dev, DIOCGETQSTATS, &pq)) {
161 				warn("DIOCGETQSTATS");
162 				return (-1);
163 			}
164 			if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
165 			    pa.altq.ifname)) != NULL) {
166 				memcpy(&node->qstats.data, &qstats.data,
167 				    sizeof(qstats.data));
168 				update_avg(node);
169 			} else {
170 				pfctl_insert_altq_node(root, pa.altq, qstats);
171 			}
172 		}
173 	}
174 	return (mnr);
175 }
176 
177 void
178 pfctl_insert_altq_node(struct pf_altq_node **root,
179     const struct pf_altq altq, const struct queue_stats qstats)
180 {
181 	struct pf_altq_node	*node;
182 
183 	node = calloc(1, sizeof(struct pf_altq_node));
184 	if (node == NULL)
185 		err(1, "pfctl_insert_altq_node: calloc");
186 	memcpy(&node->altq, &altq, sizeof(struct pf_altq));
187 	memcpy(&node->qstats, &qstats, sizeof(qstats));
188 	node->next = node->children = NULL;
189 
190 	if (*root == NULL)
191 		*root = node;
192 	else if (!altq.parent[0]) {
193 		struct pf_altq_node	*prev = *root;
194 
195 		while (prev->next != NULL)
196 			prev = prev->next;
197 		prev->next = node;
198 	} else {
199 		struct pf_altq_node	*parent;
200 
201 		parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
202 		if (parent == NULL)
203 			errx(1, "parent %s not found", altq.parent);
204 		if (parent->children == NULL)
205 			parent->children = node;
206 		else {
207 			struct pf_altq_node *prev = parent->children;
208 
209 			while (prev->next != NULL)
210 				prev = prev->next;
211 			prev->next = node;
212 		}
213 	}
214 	update_avg(node);
215 }
216 
217 struct pf_altq_node *
218 pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
219     const char *ifname)
220 {
221 	struct pf_altq_node	*node, *child;
222 
223 	for (node = root; node != NULL; node = node->next) {
224 		if (!strcmp(node->altq.qname, qname)
225 		    && !(strcmp(node->altq.ifname, ifname)))
226 			return (node);
227 		if (node->children != NULL) {
228 			child = pfctl_find_altq_node(node->children, qname,
229 			    ifname);
230 			if (child != NULL)
231 				return (child);
232 		}
233 	}
234 	return (NULL);
235 }
236 
237 void
238 pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level,
239     int opts)
240 {
241 	const struct pf_altq_node	*child;
242 
243 	if (node == NULL)
244 		return;
245 
246 	print_altq(&node->altq, level, NULL, NULL);
247 
248 	if (node->children != NULL) {
249 		printf("{");
250 		for (child = node->children; child != NULL;
251 		    child = child->next) {
252 			printf("%s", child->altq.qname);
253 			if (child->next != NULL)
254 				printf(", ");
255 		}
256 		printf("}");
257 	}
258 	printf("\n");
259 
260 	if (opts & PF_OPT_VERBOSE)
261 		pfctl_print_altq_nodestat(dev, node);
262 
263 	if (opts & PF_OPT_DEBUG)
264 		printf("  [ qid=%u ifname=%s ifbandwidth=%s ]\n",
265 		    node->altq.qid, node->altq.ifname,
266 		    rate2str((double)(node->altq.ifbandwidth)));
267 
268 	for (child = node->children; child != NULL;
269 	    child = child->next)
270 		pfctl_print_altq_node(dev, child, level + 1, opts);
271 }
272 
273 void
274 pfctl_print_altq_nodestat(int dev __unused, const struct pf_altq_node *a)
275 {
276 	if (a->altq.qid == 0)
277 		return;
278 
279 	switch (a->altq.scheduler) {
280 	case ALTQT_CBQ:
281 		print_cbqstats(a->qstats);
282 		break;
283 	case ALTQT_PRIQ:
284 		print_priqstats(a->qstats);
285 		break;
286 	case ALTQT_HFSC:
287 		print_hfscstats(a->qstats);
288 		break;
289 	case ALTQT_FAIRQ:
290 		print_fairqstats(a->qstats);
291 		break;
292 	}
293 }
294 
295 void
296 print_cbqstats(struct queue_stats cur)
297 {
298 	printf("  [ pkts: %10llu  bytes: %10llu  "
299 	    "dropped pkts: %6llu bytes: %6llu ]\n",
300 	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
301 	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
302 	    (unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
303 	    (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
304 	printf("  [ qlength: %3d/%3d  borrows: %6u  suspends: %6u ]\n",
305 	    cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
306 	    cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
307 
308 	if (cur.avgn < 2)
309 		return;
310 
311 	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
312 	    cur.avg_packets / STAT_INTERVAL,
313 	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
314 }
315 
316 void
317 print_priqstats(struct queue_stats cur)
318 {
319 	printf("  [ pkts: %10llu  bytes: %10llu  "
320 	    "dropped pkts: %6llu bytes: %6llu ]\n",
321 	    (unsigned long long)cur.data.priq_stats.xmitcnt.packets,
322 	    (unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
323 	    (unsigned long long)cur.data.priq_stats.dropcnt.packets,
324 	    (unsigned long long)cur.data.priq_stats.dropcnt.bytes);
325 	printf("  [ qlength: %3d/%3d ]\n",
326 	    cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
327 
328 	if (cur.avgn < 2)
329 		return;
330 
331 	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
332 	    cur.avg_packets / STAT_INTERVAL,
333 	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
334 }
335 
336 void
337 print_hfscstats(struct queue_stats cur)
338 {
339 	printf("  [ pkts: %10llu  bytes: %10llu  "
340 	    "dropped pkts: %6llu bytes: %6llu ]\n",
341 	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
342 	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
343 	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
344 	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
345 	printf("  [ qlength: %3d/%3d ]\n",
346 	    cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
347 
348 	if (cur.avgn < 2)
349 		return;
350 
351 	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
352 	    cur.avg_packets / STAT_INTERVAL,
353 	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
354 }
355 
356 void
357 print_fairqstats(struct queue_stats cur)
358 {
359 	printf("  [ pkts: %10llu  bytes: %10llu  "
360 	    "dropped pkts: %6llu bytes: %6llu ]\n",
361 	    (unsigned long long)cur.data.fairq_stats.xmit_cnt.packets,
362 	    (unsigned long long)cur.data.fairq_stats.xmit_cnt.bytes,
363 	    (unsigned long long)cur.data.fairq_stats.drop_cnt.packets,
364 	    (unsigned long long)cur.data.fairq_stats.drop_cnt.bytes);
365 	printf("  [ qlength: %3d/%3d ]\n",
366 	    cur.data.fairq_stats.qlength, cur.data.fairq_stats.qlimit);
367 
368 	if (cur.avgn < 2)
369 		return;
370 
371 	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
372 	    cur.avg_packets / STAT_INTERVAL,
373 	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
374 }
375 
376 
377 void
378 pfctl_free_altq_node(struct pf_altq_node *node)
379 {
380 	while (node != NULL) {
381 		struct pf_altq_node	*prev;
382 
383 		if (node->children != NULL)
384 			pfctl_free_altq_node(node->children);
385 		prev = node;
386 		node = node->next;
387 		free(prev);
388 	}
389 }
390 
391 void
392 update_avg(struct pf_altq_node *a)
393 {
394 	struct queue_stats	*qs;
395 	u_int64_t		 b, p;
396 	int			 n;
397 
398 	if (a->altq.qid == 0)
399 		return;
400 
401 	qs = &a->qstats;
402 	n = qs->avgn;
403 
404 	switch (a->altq.scheduler) {
405 	case ALTQT_CBQ:
406 		b = qs->data.cbq_stats.xmit_cnt.bytes;
407 		p = qs->data.cbq_stats.xmit_cnt.packets;
408 		break;
409 	case ALTQT_PRIQ:
410 		b = qs->data.priq_stats.xmitcnt.bytes;
411 		p = qs->data.priq_stats.xmitcnt.packets;
412 		break;
413 	case ALTQT_HFSC:
414 		b = qs->data.hfsc_stats.xmit_cnt.bytes;
415 		p = qs->data.hfsc_stats.xmit_cnt.packets;
416 		break;
417 	case ALTQT_FAIRQ:
418 		b = qs->data.fairq_stats.xmit_cnt.bytes;
419 		p = qs->data.fairq_stats.xmit_cnt.packets;
420 		break;
421 	default:
422 		b = 0;
423 		p = 0;
424 		break;
425 	}
426 
427 	if (n == 0) {
428 		qs->prev_bytes = b;
429 		qs->prev_packets = p;
430 		qs->avgn++;
431 		return;
432 	}
433 
434 	if (b >= qs->prev_bytes)
435 		qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
436 		    (b - qs->prev_bytes)) / n;
437 
438 	if (p >= qs->prev_packets)
439 		qs->avg_packets = ((qs->avg_packets * (n - 1)) +
440 		    (p - qs->prev_packets)) / n;
441 
442 	qs->prev_bytes = b;
443 	qs->prev_packets = p;
444 	if (n < AVGN_MAX)
445 		qs->avgn++;
446 }
447