Merge from vendor branch LIBARCHIVE:
[dragonfly.git] / usr.sbin / pfctl / pfctl_qstats.c
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.2 2005/02/11 22:31:45 joerg Exp $ */
3
4 /*
5  * Copyright (c) Henning Brauer <henning@openbsd.org>
6  *
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.
10  *
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.
18  */
19
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
40 #include "pfctl.h"
41 #include "pfctl_parser.h"
42
43 union class_stats {
44         class_stats_t           cbq_stats;
45         struct priq_classstats  priq_stats;
46         struct hfsc_classstats  hfsc_stats;
47 };
48
49 #define AVGN_MAX        8
50 #define STAT_INTERVAL   5
51
52 struct queue_stats {
53         union class_stats        data;
54         int                      avgn;
55         double                   avg_bytes;
56         double                   avg_packets;
57         u_int64_t                prev_bytes;
58         u_int64_t                prev_packets;
59 };
60
61 struct pf_altq_node {
62         struct pf_altq           altq;
63         struct pf_altq_node     *next;
64         struct pf_altq_node     *children;
65         struct queue_stats       qstats;
66 };
67
68 int                      pfctl_update_qstats(int, struct pf_altq_node **);
69 void                     pfctl_insert_altq_node(struct pf_altq_node **,
70                             const struct pf_altq, const struct queue_stats);
71 struct pf_altq_node     *pfctl_find_altq_node(struct pf_altq_node *,
72                             const char *, const char *);
73 void                     pfctl_print_altq_node(int, const struct pf_altq_node *,
74                              unsigned, int);
75 void                     print_cbqstats(struct queue_stats);
76 void                     print_priqstats(struct queue_stats);
77 void                     print_hfscstats(struct queue_stats);
78 void                     pfctl_free_altq_node(struct pf_altq_node *);
79 void                     pfctl_print_altq_nodestat(int,
80                             const struct pf_altq_node *);
81
82 void                     update_avg(struct pf_altq_node *);
83
84 int
85 pfctl_show_altq(int dev, const char *iface, int opts, int verbose2)
86 {
87         struct pf_altq_node     *root = NULL, *node;
88         int                      nodes, dotitle = (opts & PF_OPT_SHOWALL);
89
90
91         if ((nodes = pfctl_update_qstats(dev, &root)) < 0)
92                 return (-1);
93
94         for (node = root; node != NULL; node = node->next) {
95                 if (iface != NULL && strcmp(node->altq.ifname, iface))
96                         continue;
97                 if (dotitle) {
98                         pfctl_print_title("ALTQ:");
99                         dotitle = 0;
100                 }
101                 pfctl_print_altq_node(dev, node, 0, opts);
102         }
103
104         while (verbose2) {
105                 printf("\n");
106                 fflush(stdout);
107                 sleep(STAT_INTERVAL);
108                 if (pfctl_update_qstats(dev, &root) == -1)
109                         return (-1);
110                 for (node = root; node != NULL; node = node->next) {
111                         if (iface != NULL && strcmp(node->altq.ifname, iface))
112                                 continue;
113                         pfctl_print_altq_node(dev, node, 0, opts);
114                 }
115         }
116         pfctl_free_altq_node(root);
117         return (0);
118 }
119
120 int
121 pfctl_update_qstats(int dev, struct pf_altq_node **root)
122 {
123         struct pf_altq_node     *node;
124         struct pfioc_altq        pa;
125         struct pfioc_qstats      pq;
126         u_int32_t                mnr, nr;
127         struct queue_stats       qstats;
128         static  u_int32_t        last_ticket;
129
130         memset(&pa, 0, sizeof(pa));
131         memset(&pq, 0, sizeof(pq));
132         memset(&qstats, 0, sizeof(qstats));
133         if (ioctl(dev, DIOCGETALTQS, &pa)) {
134                 warn("DIOCGETALTQS");
135                 return (-1);
136         }
137
138         /* if a new set is found, start over */
139         if (pa.ticket != last_ticket && *root != NULL) {
140                 pfctl_free_altq_node(*root);
141                 *root = NULL;
142         }
143         last_ticket = pa.ticket;
144
145         mnr = pa.nr;
146         for (nr = 0; nr < mnr; ++nr) {
147                 pa.nr = nr;
148                 if (ioctl(dev, DIOCGETALTQ, &pa)) {
149                         warn("DIOCGETALTQ");
150                         return (-1);
151                 }
152                 if (pa.altq.qid > 0) {
153                         pq.nr = nr;
154                         pq.ticket = pa.ticket;
155                         pq.buf = &qstats.data;
156                         pq.nbytes = sizeof(qstats.data);
157                         if (ioctl(dev, DIOCGETQSTATS, &pq)) {
158                                 warn("DIOCGETQSTATS");
159                                 return (-1);
160                         }
161                         if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
162                             pa.altq.ifname)) != NULL) {
163                                 memcpy(&node->qstats.data, &qstats.data,
164                                     sizeof(qstats.data));
165                                 update_avg(node);
166                         } else {
167                                 pfctl_insert_altq_node(root, pa.altq, qstats);
168                         }
169                 }
170         }
171         return (mnr);
172 }
173
174 void
175 pfctl_insert_altq_node(struct pf_altq_node **root,
176     const struct pf_altq altq, const struct queue_stats qstats)
177 {
178         struct pf_altq_node     *node;
179
180         node = calloc(1, sizeof(struct pf_altq_node));
181         if (node == NULL)
182                 err(1, "pfctl_insert_altq_node: calloc");
183         memcpy(&node->altq, &altq, sizeof(struct pf_altq));
184         memcpy(&node->qstats, &qstats, sizeof(qstats));
185         node->next = node->children = NULL;
186
187         if (*root == NULL)
188                 *root = node;
189         else if (!altq.parent[0]) {
190                 struct pf_altq_node     *prev = *root;
191
192                 while (prev->next != NULL)
193                         prev = prev->next;
194                 prev->next = node;
195         } else {
196                 struct pf_altq_node     *parent;
197
198                 parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
199                 if (parent == NULL)
200                         errx(1, "parent %s not found", altq.parent);
201                 if (parent->children == NULL)
202                         parent->children = node;
203                 else {
204                         struct pf_altq_node *prev = parent->children;
205
206                         while (prev->next != NULL)
207                                 prev = prev->next;
208                         prev->next = node;
209                 }
210         }
211         update_avg(node);
212 }
213
214 struct pf_altq_node *
215 pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
216     const char *ifname)
217 {
218         struct pf_altq_node     *node, *child;
219
220         for (node = root; node != NULL; node = node->next) {
221                 if (!strcmp(node->altq.qname, qname)
222                     && !(strcmp(node->altq.ifname, ifname)))
223                         return (node);
224                 if (node->children != NULL) {
225                         child = pfctl_find_altq_node(node->children, qname,
226                             ifname);
227                         if (child != NULL)
228                                 return (child);
229                 }
230         }
231         return (NULL);
232 }
233
234 void
235 pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level,
236     int opts)
237 {
238         const struct pf_altq_node       *child;
239
240         if (node == NULL)
241                 return;
242
243         print_altq(&node->altq, level, NULL, NULL);
244
245         if (node->children != NULL) {
246                 printf("{");
247                 for (child = node->children; child != NULL;
248                     child = child->next) {
249                         printf("%s", child->altq.qname);
250                         if (child->next != NULL)
251                                 printf(", ");
252                 }
253                 printf("}");
254         }
255         printf("\n");
256
257         if (opts & PF_OPT_VERBOSE)
258                 pfctl_print_altq_nodestat(dev, node);
259
260         if (opts & PF_OPT_DEBUG)
261                 printf("  [ qid=%u ifname=%s ifbandwidth=%s ]\n",
262                     node->altq.qid, node->altq.ifname,
263                     rate2str((double)(node->altq.ifbandwidth)));
264
265         for (child = node->children; child != NULL;
266             child = child->next)
267                 pfctl_print_altq_node(dev, child, level + 1, opts);
268 }
269
270 void
271 pfctl_print_altq_nodestat(int dev __unused, const struct pf_altq_node *a)
272 {
273         if (a->altq.qid == 0)
274                 return;
275
276         switch (a->altq.scheduler) {
277         case ALTQT_CBQ:
278                 print_cbqstats(a->qstats);
279                 break;
280         case ALTQT_PRIQ:
281                 print_priqstats(a->qstats);
282                 break;
283         case ALTQT_HFSC:
284                 print_hfscstats(a->qstats);
285                 break;
286         }
287 }
288
289 void
290 print_cbqstats(struct queue_stats cur)
291 {
292         printf("  [ pkts: %10llu  bytes: %10llu  "
293             "dropped pkts: %6llu bytes: %6llu ]\n",
294             (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
295             (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
296             (unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
297             (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
298         printf("  [ qlength: %3d/%3d  borrows: %6u  suspends: %6u ]\n",
299             cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
300             cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
301
302         if (cur.avgn < 2)
303                 return;
304
305         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
306             cur.avg_packets / STAT_INTERVAL,
307             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
308 }
309
310 void
311 print_priqstats(struct queue_stats cur)
312 {
313         printf("  [ pkts: %10llu  bytes: %10llu  "
314             "dropped pkts: %6llu bytes: %6llu ]\n",
315             (unsigned long long)cur.data.priq_stats.xmitcnt.packets,
316             (unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
317             (unsigned long long)cur.data.priq_stats.dropcnt.packets,
318             (unsigned long long)cur.data.priq_stats.dropcnt.bytes);
319         printf("  [ qlength: %3d/%3d ]\n",
320             cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
321
322         if (cur.avgn < 2)
323                 return;
324
325         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
326             cur.avg_packets / STAT_INTERVAL,
327             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
328 }
329
330 void
331 print_hfscstats(struct queue_stats cur)
332 {
333         printf("  [ pkts: %10llu  bytes: %10llu  "
334             "dropped pkts: %6llu bytes: %6llu ]\n",
335             (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
336             (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
337             (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
338             (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
339         printf("  [ qlength: %3d/%3d ]\n",
340             cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
341
342         if (cur.avgn < 2)
343                 return;
344
345         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
346             cur.avg_packets / STAT_INTERVAL,
347             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
348 }
349
350 void
351 pfctl_free_altq_node(struct pf_altq_node *node)
352 {
353         while (node != NULL) {
354                 struct pf_altq_node     *prev;
355
356                 if (node->children != NULL)
357                         pfctl_free_altq_node(node->children);
358                 prev = node;
359                 node = node->next;
360                 free(prev);
361         }
362 }
363
364 void
365 update_avg(struct pf_altq_node *a)
366 {
367         struct queue_stats      *qs;
368         u_int64_t                b, p;
369         int                      n;
370
371         if (a->altq.qid == 0)
372                 return;
373
374         qs = &a->qstats;
375         n = qs->avgn;
376
377         switch (a->altq.scheduler) {
378         case ALTQT_CBQ:
379                 b = qs->data.cbq_stats.xmit_cnt.bytes;
380                 p = qs->data.cbq_stats.xmit_cnt.packets;
381                 break;
382         case ALTQT_PRIQ:
383                 b = qs->data.priq_stats.xmitcnt.bytes;
384                 p = qs->data.priq_stats.xmitcnt.packets;
385                 break;
386         case ALTQT_HFSC:
387                 b = qs->data.hfsc_stats.xmit_cnt.bytes;
388                 p = qs->data.hfsc_stats.xmit_cnt.packets;
389                 break;
390         default:
391                 b = 0;
392                 p = 0;
393                 break;
394         }
395
396         if (n == 0) {
397                 qs->prev_bytes = b;
398                 qs->prev_packets = p;
399                 qs->avgn++;
400                 return;
401         }
402
403         if (b >= qs->prev_bytes)
404                 qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
405                     (b - qs->prev_bytes)) / n;
406
407         if (p >= qs->prev_packets)
408                 qs->avg_packets = ((qs->avg_packets * (n - 1)) +
409                     (p - qs->prev_packets)) / n;
410
411         qs->prev_bytes = b;
412         qs->prev_packets = p;
413         if (n < AVGN_MAX)
414                 qs->avgn++;
415 }