HAMMER utility - Add ^C support, improve verbosity.
[dragonfly.git] / usr.sbin / pfctl / pfctl_qstats.c
blobd6e95cee8306f1a96cf42e42b2564aa9e7fdfc9d
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 $ */
4 /*
5 * Copyright (c) Henning Brauer <henning@openbsd.org>
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.
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.
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #include <sys/socket.h>
24 #include <net/if.h>
25 #include <netinet/in.h>
26 #include <net/pf/pfvar.h>
27 #include <arpa/inet.h>
29 #include <err.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
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>
41 #include "pfctl.h"
42 #include "pfctl_parser.h"
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;
51 #define AVGN_MAX 8
52 #define STAT_INTERVAL 5
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;
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;
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 *);
85 void update_avg(struct pf_altq_node *);
87 int
88 pfctl_show_altq(int dev, const char *iface, int opts, int verbose2)
90 struct pf_altq_node *root = NULL, *node;
91 int nodes, dotitle = (opts & PF_OPT_SHOWALL);
94 if ((nodes = pfctl_update_qstats(dev, &root)) < 0)
95 return (-1);
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;
104 pfctl_print_altq_node(dev, node, 0, opts);
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);
119 pfctl_free_altq_node(root);
120 return (0);
124 pfctl_update_qstats(int dev, struct pf_altq_node **root)
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;
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);
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;
146 last_ticket = pa.ticket;
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);
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);
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);
174 return (mnr);
177 void
178 pfctl_insert_altq_node(struct pf_altq_node **root,
179 const struct pf_altq altq, const struct queue_stats qstats)
181 struct pf_altq_node *node;
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;
190 if (*root == NULL)
191 *root = node;
192 else if (!altq.parent[0]) {
193 struct pf_altq_node *prev = *root;
195 while (prev->next != NULL)
196 prev = prev->next;
197 prev->next = node;
198 } else {
199 struct pf_altq_node *parent;
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;
209 while (prev->next != NULL)
210 prev = prev->next;
211 prev->next = node;
214 update_avg(node);
217 struct pf_altq_node *
218 pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
219 const char *ifname)
221 struct pf_altq_node *node, *child;
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);
234 return (NULL);
237 void
238 pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level,
239 int opts)
241 const struct pf_altq_node *child;
243 if (node == NULL)
244 return;
246 print_altq(&node->altq, level, NULL, NULL);
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(", ");
256 printf("}");
258 printf("\n");
260 if (opts & PF_OPT_VERBOSE)
261 pfctl_print_altq_nodestat(dev, node);
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)));
268 for (child = node->children; child != NULL;
269 child = child->next)
270 pfctl_print_altq_node(dev, child, level + 1, opts);
273 void
274 pfctl_print_altq_nodestat(int dev __unused, const struct pf_altq_node *a)
276 if (a->altq.qid == 0)
277 return;
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;
295 void
296 print_cbqstats(struct queue_stats cur)
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);
308 if (cur.avgn < 2)
309 return;
311 printf(" [ measured: %7.1f packets/s, %s/s ]\n",
312 cur.avg_packets / STAT_INTERVAL,
313 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
316 void
317 print_priqstats(struct queue_stats cur)
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);
328 if (cur.avgn < 2)
329 return;
331 printf(" [ measured: %7.1f packets/s, %s/s ]\n",
332 cur.avg_packets / STAT_INTERVAL,
333 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
336 void
337 print_hfscstats(struct queue_stats cur)
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);
348 if (cur.avgn < 2)
349 return;
351 printf(" [ measured: %7.1f packets/s, %s/s ]\n",
352 cur.avg_packets / STAT_INTERVAL,
353 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
356 void
357 print_fairqstats(struct queue_stats cur)
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);
368 if (cur.avgn < 2)
369 return;
371 printf(" [ measured: %7.1f packets/s, %s/s ]\n",
372 cur.avg_packets / STAT_INTERVAL,
373 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
377 void
378 pfctl_free_altq_node(struct pf_altq_node *node)
380 while (node != NULL) {
381 struct pf_altq_node *prev;
383 if (node->children != NULL)
384 pfctl_free_altq_node(node->children);
385 prev = node;
386 node = node->next;
387 free(prev);
391 void
392 update_avg(struct pf_altq_node *a)
394 struct queue_stats *qs;
395 u_int64_t b, p;
396 int n;
398 if (a->altq.qid == 0)
399 return;
401 qs = &a->qstats;
402 n = qs->avgn;
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;
427 if (n == 0) {
428 qs->prev_bytes = b;
429 qs->prev_packets = p;
430 qs->avgn++;
431 return;
434 if (b >= qs->prev_bytes)
435 qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
436 (b - qs->prev_bytes)) / n;
438 if (p >= qs->prev_packets)
439 qs->avg_packets = ((qs->avg_packets * (n - 1)) +
440 (p - qs->prev_packets)) / n;
442 qs->prev_bytes = b;
443 qs->prev_packets = p;
444 if (n < AVGN_MAX)
445 qs->avgn++;