Fix buildworld.
[dragonfly.git] / usr.sbin / pfctl / pfctl_qstats.c
1 /*      $OpenBSD: pfctl_qstats.c,v 1.31 2007/10/15 02:16:35 deraadt 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 #define _KERNEL_STRUCTURES
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 #include <net/altq/altq_fairq.h>
40
41 #include "pfctl.h"
42 #include "pfctl_parser.h"
43
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;
49 };
50
51 #define AVGN_MAX        8
52 #define STAT_INTERVAL   5
53
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;
61 };
62
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;
68 };
69
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 *);
84
85 void                     update_avg(struct pf_altq_node *);
86
87 int
88 pfctl_show_altq(int dev, const char *iface, int opts, int verbose2)
89 {
90         struct pf_altq_node     *root = NULL, *node;
91         int                      nodes, dotitle = (opts & PF_OPT_SHOWALL);
92
93
94         if ((nodes = pfctl_update_qstats(dev, &root)) < 0)
95                 return (-1);
96
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;
103                 }
104                 pfctl_print_altq_node(dev, node, 0, opts);
105         }
106
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);
117                 }
118         }
119         pfctl_free_altq_node(root);
120         return (0);
121 }
122
123 int
124 pfctl_update_qstats(int dev, struct pf_altq_node **root)
125 {
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;
132
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);
139         }
140
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;
145         }
146         last_ticket = pa.ticket;
147
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);
154                 }
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);
163                         }
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);
171                         }
172                 }
173         }
174         return (mnr);
175 }
176
177 void
178 pfctl_insert_altq_node(struct pf_altq_node **root,
179     const struct pf_altq altq, const struct queue_stats qstats)
180 {
181         struct pf_altq_node     *node;
182
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;
189
190         if (*root == NULL)
191                 *root = node;
192         else if (!altq.parent[0]) {
193                 struct pf_altq_node     *prev = *root;
194
195                 while (prev->next != NULL)
196                         prev = prev->next;
197                 prev->next = node;
198         } else {
199                 struct pf_altq_node     *parent;
200
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;
208
209                         while (prev->next != NULL)
210                                 prev = prev->next;
211                         prev->next = node;
212                 }
213         }
214         update_avg(node);
215 }
216
217 struct pf_altq_node *
218 pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
219     const char *ifname)
220 {
221         struct pf_altq_node     *node, *child;
222
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);
232                 }
233         }
234         return (NULL);
235 }
236
237 void
238 pfctl_print_altq_node(int dev, const struct pf_altq_node *node,
239     unsigned int level, int opts)
240 {
241         const struct pf_altq_node       *child;
242
243         if (node == NULL)
244                 return;
245
246         print_altq(&node->altq, level, NULL, NULL);
247
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(", ");
255                 }
256                 printf("}");
257         }
258         printf("\n");
259
260         if (opts & PF_OPT_VERBOSE)
261                 pfctl_print_altq_nodestat(dev, node);
262
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)));
267
268         for (child = node->children; child != NULL;
269             child = child->next)
270                 pfctl_print_altq_node(dev, child, level + 1, opts);
271 }
272
273 void
274 pfctl_print_altq_nodestat(int dev __unused, const struct pf_altq_node *a)
275 {
276         if (a->altq.qid == 0)
277                 return;
278
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;
292         }
293 }
294
295 void
296 print_cbqstats(struct queue_stats cur)
297 {
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);
307
308         if (cur.avgn < 2)
309                 return;
310
311         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
312             cur.avg_packets / STAT_INTERVAL,
313             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
314 }
315
316 void
317 print_priqstats(struct queue_stats cur)
318 {
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);
327
328         if (cur.avgn < 2)
329                 return;
330
331         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
332             cur.avg_packets / STAT_INTERVAL,
333             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
334 }
335
336 void
337 print_hfscstats(struct queue_stats cur)
338 {
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);
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_fairqstats(struct queue_stats cur)
358 {
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);
367
368         if (cur.avgn < 2)
369                 return;
370
371         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
372             cur.avg_packets / STAT_INTERVAL,
373             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
374 }
375
376
377 void
378 pfctl_free_altq_node(struct pf_altq_node *node)
379 {
380         while (node != NULL) {
381                 struct pf_altq_node     *prev;
382
383                 if (node->children != NULL)
384                         pfctl_free_altq_node(node->children);
385                 prev = node;
386                 node = node->next;
387                 free(prev);
388         }
389 }
390
391 void
392 update_avg(struct pf_altq_node *a)
393 {
394         struct queue_stats      *qs;
395         u_int64_t                b, p;
396         int                      n;
397
398         if (a->altq.qid == 0)
399                 return;
400
401         qs = &a->qstats;
402         n = qs->avgn;
403
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;
425         }
426
427         if (n == 0) {
428                 qs->prev_bytes = b;
429                 qs->prev_packets = p;
430                 qs->avgn++;
431                 return;
432         }
433
434         if (b >= qs->prev_bytes)
435                 qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
436                     (b - qs->prev_bytes)) / n;
437
438         if (p >= qs->prev_packets)
439                 qs->avg_packets = ((qs->avg_packets * (n - 1)) +
440                     (p - qs->prev_packets)) / n;
441
442         qs->prev_bytes = b;
443         qs->prev_packets = p;
444         if (n < AVGN_MAX)
445                 qs->avgn++;
446 }