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