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