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