xref: /dragonfly/usr.sbin/pfctl/pfctl_qstats.c (revision e1acdbad)
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.2 2005/02/11 22:31:45 joerg 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 
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
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 	for (node = root; node != NULL; node = node->next) {
95 		if (iface != NULL && strcmp(node->altq.ifname, iface))
96 			continue;
97 		if (dotitle) {
98 			pfctl_print_title("ALTQ:");
99 			dotitle = 0;
100 		}
101 		pfctl_print_altq_node(dev, node, 0, opts);
102 	}
103 
104 	while (verbose2) {
105 		printf("\n");
106 		fflush(stdout);
107 		sleep(STAT_INTERVAL);
108 		if (pfctl_update_qstats(dev, &root) == -1)
109 			return (-1);
110 		for (node = root; node != NULL; node = node->next) {
111 			if (iface != NULL && strcmp(node->altq.ifname, iface))
112 				continue;
113 			pfctl_print_altq_node(dev, node, 0, opts);
114 		}
115 	}
116 	pfctl_free_altq_node(root);
117 	return (0);
118 }
119 
120 int
121 pfctl_update_qstats(int dev, struct pf_altq_node **root)
122 {
123 	struct pf_altq_node	*node;
124 	struct pfioc_altq	 pa;
125 	struct pfioc_qstats	 pq;
126 	u_int32_t		 mnr, nr;
127 	struct queue_stats	 qstats;
128 	static	u_int32_t	 last_ticket;
129 
130 	memset(&pa, 0, sizeof(pa));
131 	memset(&pq, 0, sizeof(pq));
132 	memset(&qstats, 0, sizeof(qstats));
133 	if (ioctl(dev, DIOCGETALTQS, &pa)) {
134 		warn("DIOCGETALTQS");
135 		return (-1);
136 	}
137 
138 	/* if a new set is found, start over */
139 	if (pa.ticket != last_ticket && *root != NULL) {
140 		pfctl_free_altq_node(*root);
141 		*root = NULL;
142 	}
143 	last_ticket = pa.ticket;
144 
145 	mnr = pa.nr;
146 	for (nr = 0; nr < mnr; ++nr) {
147 		pa.nr = nr;
148 		if (ioctl(dev, DIOCGETALTQ, &pa)) {
149 			warn("DIOCGETALTQ");
150 			return (-1);
151 		}
152 		if (pa.altq.qid > 0) {
153 			pq.nr = nr;
154 			pq.ticket = pa.ticket;
155 			pq.buf = &qstats.data;
156 			pq.nbytes = sizeof(qstats.data);
157 			if (ioctl(dev, DIOCGETQSTATS, &pq)) {
158 				warn("DIOCGETQSTATS");
159 				return (-1);
160 			}
161 			if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
162 			    pa.altq.ifname)) != NULL) {
163 				memcpy(&node->qstats.data, &qstats.data,
164 				    sizeof(qstats.data));
165 				update_avg(node);
166 			} else {
167 				pfctl_insert_altq_node(root, pa.altq, qstats);
168 			}
169 		}
170 	}
171 	return (mnr);
172 }
173 
174 void
175 pfctl_insert_altq_node(struct pf_altq_node **root,
176     const struct pf_altq altq, const struct queue_stats qstats)
177 {
178 	struct pf_altq_node	*node;
179 
180 	node = calloc(1, sizeof(struct pf_altq_node));
181 	if (node == NULL)
182 		err(1, "pfctl_insert_altq_node: calloc");
183 	memcpy(&node->altq, &altq, sizeof(struct pf_altq));
184 	memcpy(&node->qstats, &qstats, sizeof(qstats));
185 	node->next = node->children = NULL;
186 
187 	if (*root == NULL)
188 		*root = node;
189 	else if (!altq.parent[0]) {
190 		struct pf_altq_node	*prev = *root;
191 
192 		while (prev->next != NULL)
193 			prev = prev->next;
194 		prev->next = node;
195 	} else {
196 		struct pf_altq_node	*parent;
197 
198 		parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
199 		if (parent == NULL)
200 			errx(1, "parent %s not found", altq.parent);
201 		if (parent->children == NULL)
202 			parent->children = node;
203 		else {
204 			struct pf_altq_node *prev = parent->children;
205 
206 			while (prev->next != NULL)
207 				prev = prev->next;
208 			prev->next = node;
209 		}
210 	}
211 	update_avg(node);
212 }
213 
214 struct pf_altq_node *
215 pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
216     const char *ifname)
217 {
218 	struct pf_altq_node	*node, *child;
219 
220 	for (node = root; node != NULL; node = node->next) {
221 		if (!strcmp(node->altq.qname, qname)
222 		    && !(strcmp(node->altq.ifname, ifname)))
223 			return (node);
224 		if (node->children != NULL) {
225 			child = pfctl_find_altq_node(node->children, qname,
226 			    ifname);
227 			if (child != NULL)
228 				return (child);
229 		}
230 	}
231 	return (NULL);
232 }
233 
234 void
235 pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level,
236     int opts)
237 {
238 	const struct pf_altq_node	*child;
239 
240 	if (node == NULL)
241 		return;
242 
243 	print_altq(&node->altq, level, NULL, NULL);
244 
245 	if (node->children != NULL) {
246 		printf("{");
247 		for (child = node->children; child != NULL;
248 		    child = child->next) {
249 			printf("%s", child->altq.qname);
250 			if (child->next != NULL)
251 				printf(", ");
252 		}
253 		printf("}");
254 	}
255 	printf("\n");
256 
257 	if (opts & PF_OPT_VERBOSE)
258 		pfctl_print_altq_nodestat(dev, node);
259 
260 	if (opts & PF_OPT_DEBUG)
261 		printf("  [ qid=%u ifname=%s ifbandwidth=%s ]\n",
262 		    node->altq.qid, node->altq.ifname,
263 		    rate2str((double)(node->altq.ifbandwidth)));
264 
265 	for (child = node->children; child != NULL;
266 	    child = child->next)
267 		pfctl_print_altq_node(dev, child, level + 1, opts);
268 }
269 
270 void
271 pfctl_print_altq_nodestat(int dev __unused, const struct pf_altq_node *a)
272 {
273 	if (a->altq.qid == 0)
274 		return;
275 
276 	switch (a->altq.scheduler) {
277 	case ALTQT_CBQ:
278 		print_cbqstats(a->qstats);
279 		break;
280 	case ALTQT_PRIQ:
281 		print_priqstats(a->qstats);
282 		break;
283 	case ALTQT_HFSC:
284 		print_hfscstats(a->qstats);
285 		break;
286 	}
287 }
288 
289 void
290 print_cbqstats(struct queue_stats cur)
291 {
292 	printf("  [ pkts: %10llu  bytes: %10llu  "
293 	    "dropped pkts: %6llu bytes: %6llu ]\n",
294 	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
295 	    (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
296 	    (unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
297 	    (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
298 	printf("  [ qlength: %3d/%3d  borrows: %6u  suspends: %6u ]\n",
299 	    cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
300 	    cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
301 
302 	if (cur.avgn < 2)
303 		return;
304 
305 	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
306 	    cur.avg_packets / STAT_INTERVAL,
307 	    rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
308 }
309 
310 void
311 print_priqstats(struct queue_stats cur)
312 {
313 	printf("  [ pkts: %10llu  bytes: %10llu  "
314 	    "dropped pkts: %6llu bytes: %6llu ]\n",
315 	    (unsigned long long)cur.data.priq_stats.xmitcnt.packets,
316 	    (unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
317 	    (unsigned long long)cur.data.priq_stats.dropcnt.packets,
318 	    (unsigned long long)cur.data.priq_stats.dropcnt.bytes);
319 	printf("  [ qlength: %3d/%3d ]\n",
320 	    cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
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_hfscstats(struct queue_stats cur)
332 {
333 	printf("  [ pkts: %10llu  bytes: %10llu  "
334 	    "dropped pkts: %6llu bytes: %6llu ]\n",
335 	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
336 	    (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
337 	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
338 	    (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
339 	printf("  [ qlength: %3d/%3d ]\n",
340 	    cur.data.hfsc_stats.qlength, cur.data.hfsc_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 pfctl_free_altq_node(struct pf_altq_node *node)
352 {
353 	while (node != NULL) {
354 		struct pf_altq_node	*prev;
355 
356 		if (node->children != NULL)
357 			pfctl_free_altq_node(node->children);
358 		prev = node;
359 		node = node->next;
360 		free(prev);
361 	}
362 }
363 
364 void
365 update_avg(struct pf_altq_node *a)
366 {
367 	struct queue_stats	*qs;
368 	u_int64_t		 b, p;
369 	int			 n;
370 
371 	if (a->altq.qid == 0)
372 		return;
373 
374 	qs = &a->qstats;
375 	n = qs->avgn;
376 
377 	switch (a->altq.scheduler) {
378 	case ALTQT_CBQ:
379 		b = qs->data.cbq_stats.xmit_cnt.bytes;
380 		p = qs->data.cbq_stats.xmit_cnt.packets;
381 		break;
382 	case ALTQT_PRIQ:
383 		b = qs->data.priq_stats.xmitcnt.bytes;
384 		p = qs->data.priq_stats.xmitcnt.packets;
385 		break;
386 	case ALTQT_HFSC:
387 		b = qs->data.hfsc_stats.xmit_cnt.bytes;
388 		p = qs->data.hfsc_stats.xmit_cnt.packets;
389 		break;
390 	default:
391 		b = 0;
392 		p = 0;
393 		break;
394 	}
395 
396 	if (n == 0) {
397 		qs->prev_bytes = b;
398 		qs->prev_packets = p;
399 		qs->avgn++;
400 		return;
401 	}
402 
403 	if (b >= qs->prev_bytes)
404 		qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
405 		    (b - qs->prev_bytes)) / n;
406 
407 	if (p >= qs->prev_packets)
408 		qs->avg_packets = ((qs->avg_packets * (n - 1)) +
409 		    (p - qs->prev_packets)) / n;
410 
411 	qs->prev_bytes = b;
412 	qs->prev_packets = p;
413 	if (n < AVGN_MAX)
414 		qs->avgn++;
415 }
416