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