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