]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sbin/pfctl/pfctl_qstats.c
MFC r368207,368607:
[FreeBSD/stable/10.git] / sbin / pfctl / pfctl_qstats.c
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 __FBSDID("$FreeBSD$");
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 <altq/altq.h>
38 #include <altq/altq_cbq.h>
39 #include <altq/altq_codel.h>
40 #include <altq/altq_priq.h>
41 #include <altq/altq_hfsc.h>
42 #include <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
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
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         if (ioctl(dev, DIOCGETALTQS, &pa)) {
152                 warn("DIOCGETALTQS");
153                 return (-1);
154         }
155
156         /* if a new set is found, start over */
157         if (pa.ticket != last_ticket && *root != NULL) {
158                 pfctl_free_altq_node(*root);
159                 *root = NULL;
160         }
161         last_ticket = pa.ticket;
162
163         mnr = pa.nr;
164         for (nr = 0; nr < mnr; ++nr) {
165                 pa.nr = nr;
166                 if (ioctl(dev, DIOCGETALTQ, &pa)) {
167                         warn("DIOCGETALTQ");
168                         return (-1);
169                 }
170 #ifdef __FreeBSD__
171                 if ((pa.altq.qid > 0 || pa.altq.scheduler == ALTQT_CODEL) &&
172                     !(pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED)) {
173 #else
174                 if (pa.altq.qid > 0) {
175 #endif
176                         pq.nr = nr;
177                         pq.ticket = pa.ticket;
178                         pq.buf = &qstats.data;
179                         pq.nbytes = sizeof(qstats.data);
180                         if (ioctl(dev, DIOCGETQSTATS, &pq)) {
181                                 warn("DIOCGETQSTATS");
182                                 return (-1);
183                         }
184                         if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
185                             pa.altq.ifname)) != NULL) {
186                                 memcpy(&node->qstats.data, &qstats.data,
187                                     sizeof(qstats.data));
188                                 update_avg(node);
189                         } else {
190                                 pfctl_insert_altq_node(root, pa.altq, qstats);
191                         }
192                 }
193 #ifdef __FreeBSD__
194                 else if (pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED) {
195                         memset(&qstats.data, 0, sizeof(qstats.data));
196                         if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
197                             pa.altq.ifname)) != NULL) {
198                                 memcpy(&node->qstats.data, &qstats.data,
199                                     sizeof(qstats.data));
200                                 update_avg(node);
201                         } else {
202                                 pfctl_insert_altq_node(root, pa.altq, qstats);
203                         }
204                 }
205 #endif
206         }
207         return (mnr);
208 }
209
210 void
211 pfctl_insert_altq_node(struct pf_altq_node **root,
212     const struct pf_altq altq, const struct queue_stats qstats)
213 {
214         struct pf_altq_node     *node;
215
216         node = calloc(1, sizeof(struct pf_altq_node));
217         if (node == NULL)
218                 err(1, "pfctl_insert_altq_node: calloc");
219         memcpy(&node->altq, &altq, sizeof(struct pf_altq));
220         memcpy(&node->qstats, &qstats, sizeof(qstats));
221         node->next = node->children = NULL;
222
223         if (*root == NULL)
224                 *root = node;
225         else if (!altq.parent[0]) {
226                 struct pf_altq_node     *prev = *root;
227
228                 while (prev->next != NULL)
229                         prev = prev->next;
230                 prev->next = node;
231         } else {
232                 struct pf_altq_node     *parent;
233
234                 parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
235                 if (parent == NULL)
236                         errx(1, "parent %s not found", altq.parent);
237                 if (parent->children == NULL)
238                         parent->children = node;
239                 else {
240                         struct pf_altq_node *prev = parent->children;
241
242                         while (prev->next != NULL)
243                                 prev = prev->next;
244                         prev->next = node;
245                 }
246         }
247         update_avg(node);
248 }
249
250 struct pf_altq_node *
251 pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
252     const char *ifname)
253 {
254         struct pf_altq_node     *node, *child;
255
256         for (node = root; node != NULL; node = node->next) {
257                 if (!strcmp(node->altq.qname, qname)
258                     && !(strcmp(node->altq.ifname, ifname)))
259                         return (node);
260                 if (node->children != NULL) {
261                         child = pfctl_find_altq_node(node->children, qname,
262                             ifname);
263                         if (child != NULL)
264                                 return (child);
265                 }
266         }
267         return (NULL);
268 }
269
270 void
271 pfctl_print_altq_node(int dev, const struct pf_altq_node *node,
272     unsigned int level, int opts)
273 {
274         const struct pf_altq_node       *child;
275
276         if (node == NULL)
277                 return;
278
279         print_altq(&node->altq, level, NULL, NULL);
280
281         if (node->children != NULL) {
282                 printf("{");
283                 for (child = node->children; child != NULL;
284                     child = child->next) {
285                         printf("%s", child->altq.qname);
286                         if (child->next != NULL)
287                                 printf(", ");
288                 }
289                 printf("}");
290         }
291         printf("\n");
292
293         if (opts & PF_OPT_VERBOSE)
294                 pfctl_print_altq_nodestat(dev, node);
295
296         if (opts & PF_OPT_DEBUG)
297                 printf("  [ qid=%u ifname=%s ifbandwidth=%s ]\n",
298                     node->altq.qid, node->altq.ifname,
299                     rate2str((double)(node->altq.ifbandwidth)));
300
301         for (child = node->children; child != NULL;
302             child = child->next)
303                 pfctl_print_altq_node(dev, child, level + 1, opts);
304 }
305
306 void
307 pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a)
308 {
309         if (a->altq.qid == 0 && a->altq.scheduler != ALTQT_CODEL)
310                 return;
311
312 #ifdef __FreeBSD__
313         if (a->altq.local_flags & PFALTQ_FLAG_IF_REMOVED)
314                 return;
315 #endif
316         switch (a->altq.scheduler) {
317         case ALTQT_CBQ:
318                 print_cbqstats(a->qstats);
319                 break;
320         case ALTQT_PRIQ:
321                 print_priqstats(a->qstats);
322                 break;
323         case ALTQT_HFSC:
324                 print_hfscstats(a->qstats);
325                 break;
326         case ALTQT_FAIRQ:
327                 print_fairqstats(a->qstats);
328                 break;
329         case ALTQT_CODEL:
330                 print_codelstats(a->qstats);
331                 break;
332         }
333 }
334
335 void
336 print_cbqstats(struct queue_stats cur)
337 {
338         printf("  [ pkts: %10llu  bytes: %10llu  "
339             "dropped pkts: %6llu bytes: %6llu ]\n",
340             (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
341             (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
342             (unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
343             (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
344         printf("  [ qlength: %3d/%3d  borrows: %6u  suspends: %6u ]\n",
345             cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
346             cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
347
348         if (cur.avgn < 2)
349                 return;
350
351         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
352             cur.avg_packets / STAT_INTERVAL,
353             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
354 }
355
356 void
357 print_codelstats(struct queue_stats cur)
358 {
359         printf("  [ pkts: %10llu  bytes: %10llu  "
360             "dropped pkts: %6llu bytes: %6llu ]\n",
361             (unsigned long long)cur.data.codel_stats.cl_xmitcnt.packets,
362             (unsigned long long)cur.data.codel_stats.cl_xmitcnt.bytes,
363             (unsigned long long)cur.data.codel_stats.cl_dropcnt.packets +
364             cur.data.codel_stats.stats.drop_cnt.packets,
365             (unsigned long long)cur.data.codel_stats.cl_dropcnt.bytes +
366             cur.data.codel_stats.stats.drop_cnt.bytes);
367         printf("  [ qlength: %3d/%3d ]\n",
368             cur.data.codel_stats.qlength, cur.data.codel_stats.qlimit);
369
370         if (cur.avgn < 2)
371                 return;
372
373         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
374             cur.avg_packets / STAT_INTERVAL,
375             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
376 }
377
378 void
379 print_priqstats(struct queue_stats cur)
380 {
381         printf("  [ pkts: %10llu  bytes: %10llu  "
382             "dropped pkts: %6llu bytes: %6llu ]\n",
383             (unsigned long long)cur.data.priq_stats.xmitcnt.packets,
384             (unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
385             (unsigned long long)cur.data.priq_stats.dropcnt.packets,
386             (unsigned long long)cur.data.priq_stats.dropcnt.bytes);
387         printf("  [ qlength: %3d/%3d ]\n",
388             cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
389
390         if (cur.avgn < 2)
391                 return;
392
393         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
394             cur.avg_packets / STAT_INTERVAL,
395             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
396 }
397
398 void
399 print_hfscstats(struct queue_stats cur)
400 {
401         printf("  [ pkts: %10llu  bytes: %10llu  "
402             "dropped pkts: %6llu bytes: %6llu ]\n",
403             (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
404             (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
405             (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
406             (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
407         printf("  [ qlength: %3d/%3d ]\n",
408             cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
409
410         if (cur.avgn < 2)
411                 return;
412
413         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
414             cur.avg_packets / STAT_INTERVAL,
415             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
416 }
417
418 void
419 print_fairqstats(struct queue_stats cur)
420 {
421         printf("  [ pkts: %10llu  bytes: %10llu  "
422             "dropped pkts: %6llu bytes: %6llu ]\n",
423             (unsigned long long)cur.data.fairq_stats.xmit_cnt.packets,
424             (unsigned long long)cur.data.fairq_stats.xmit_cnt.bytes,
425             (unsigned long long)cur.data.fairq_stats.drop_cnt.packets,
426             (unsigned long long)cur.data.fairq_stats.drop_cnt.bytes);
427         printf("  [ qlength: %3d/%3d ]\n",
428             cur.data.fairq_stats.qlength, cur.data.fairq_stats.qlimit);
429
430         if (cur.avgn < 2)
431                 return;
432
433         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
434             cur.avg_packets / STAT_INTERVAL,
435             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
436 }
437
438 void
439 pfctl_free_altq_node(struct pf_altq_node *node)
440 {
441         while (node != NULL) {
442                 struct pf_altq_node     *prev;
443
444                 if (node->children != NULL)
445                         pfctl_free_altq_node(node->children);
446                 prev = node;
447                 node = node->next;
448                 free(prev);
449         }
450 }
451
452 void
453 update_avg(struct pf_altq_node *a)
454 {
455         struct queue_stats      *qs;
456         u_int64_t                b, p;
457         int                      n;
458
459         if (a->altq.qid == 0 && a->altq.scheduler != ALTQT_CODEL)
460                 return;
461
462         qs = &a->qstats;
463         n = qs->avgn;
464
465         switch (a->altq.scheduler) {
466         case ALTQT_CBQ:
467                 b = qs->data.cbq_stats.xmit_cnt.bytes;
468                 p = qs->data.cbq_stats.xmit_cnt.packets;
469                 break;
470         case ALTQT_PRIQ:
471                 b = qs->data.priq_stats.xmitcnt.bytes;
472                 p = qs->data.priq_stats.xmitcnt.packets;
473                 break;
474         case ALTQT_HFSC:
475                 b = qs->data.hfsc_stats.xmit_cnt.bytes;
476                 p = qs->data.hfsc_stats.xmit_cnt.packets;
477                 break;
478         case ALTQT_FAIRQ:
479                 b = qs->data.fairq_stats.xmit_cnt.bytes;
480                 p = qs->data.fairq_stats.xmit_cnt.packets;
481                 break;
482         case ALTQT_CODEL:
483                 b = qs->data.codel_stats.cl_xmitcnt.bytes;
484                 p = qs->data.codel_stats.cl_xmitcnt.packets;
485                 break;
486         default:
487                 b = 0;
488                 p = 0;
489                 break;
490         }
491
492         if (n == 0) {
493                 qs->prev_bytes = b;
494                 qs->prev_packets = p;
495                 qs->avgn++;
496                 return;
497         }
498
499         if (b >= qs->prev_bytes)
500                 qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
501                     (b - qs->prev_bytes)) / n;
502
503         if (p >= qs->prev_packets)
504                 qs->avg_packets = ((qs->avg_packets * (n - 1)) +
505                     (p - qs->prev_packets)) / n;
506
507         qs->prev_bytes = b;
508         qs->prev_packets = p;
509         if (n < AVGN_MAX)
510                 qs->avgn++;
511 }