]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/pf/pfctl/pfctl_qstats.c
This commit was generated by cvs2svn to compensate for changes in r171164,
[FreeBSD/FreeBSD.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                         pfctl_print_altq_node(dev, node, 0, opts);
122                 }
123         }
124         pfctl_free_altq_node(root);
125         return (0);
126 }
127
128 int
129 pfctl_update_qstats(int dev, struct pf_altq_node **root)
130 {
131         struct pf_altq_node     *node;
132         struct pfioc_altq        pa;
133         struct pfioc_qstats      pq;
134         u_int32_t                mnr, nr;
135         struct queue_stats       qstats;
136         static  u_int32_t        last_ticket;
137
138         memset(&pa, 0, sizeof(pa));
139         memset(&pq, 0, sizeof(pq));
140         memset(&qstats, 0, sizeof(qstats));
141         if (ioctl(dev, DIOCGETALTQS, &pa)) {
142                 warn("DIOCGETALTQS");
143                 return (-1);
144         }
145
146         /* if a new set is found, start over */
147         if (pa.ticket != last_ticket && *root != NULL) {
148                 pfctl_free_altq_node(*root);
149                 *root = NULL;
150         }
151         last_ticket = pa.ticket;
152
153         mnr = pa.nr;
154         for (nr = 0; nr < mnr; ++nr) {
155                 pa.nr = nr;
156                 if (ioctl(dev, DIOCGETALTQ, &pa)) {
157                         warn("DIOCGETALTQ");
158                         return (-1);
159                 }
160                 if (pa.altq.qid > 0) {
161                         pq.nr = nr;
162                         pq.ticket = pa.ticket;
163                         pq.buf = &qstats.data;
164                         pq.nbytes = sizeof(qstats.data);
165                         if (ioctl(dev, DIOCGETQSTATS, &pq)) {
166                                 warn("DIOCGETQSTATS");
167                                 return (-1);
168                         }
169                         if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
170                             pa.altq.ifname)) != NULL) {
171                                 memcpy(&node->qstats.data, &qstats.data,
172                                     sizeof(qstats.data));
173                                 update_avg(node);
174                         } else {
175                                 pfctl_insert_altq_node(root, pa.altq, qstats);
176                         }
177                 }
178         }
179         return (mnr);
180 }
181
182 void
183 pfctl_insert_altq_node(struct pf_altq_node **root,
184     const struct pf_altq altq, const struct queue_stats qstats)
185 {
186         struct pf_altq_node     *node;
187
188         node = calloc(1, sizeof(struct pf_altq_node));
189         if (node == NULL)
190                 err(1, "pfctl_insert_altq_node: calloc");
191         memcpy(&node->altq, &altq, sizeof(struct pf_altq));
192         memcpy(&node->qstats, &qstats, sizeof(qstats));
193         node->next = node->children = NULL;
194
195         if (*root == NULL)
196                 *root = node;
197         else if (!altq.parent[0]) {
198                 struct pf_altq_node     *prev = *root;
199
200                 while (prev->next != NULL)
201                         prev = prev->next;
202                 prev->next = node;
203         } else {
204                 struct pf_altq_node     *parent;
205
206                 parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
207                 if (parent == NULL)
208                         errx(1, "parent %s not found", altq.parent);
209                 if (parent->children == NULL)
210                         parent->children = node;
211                 else {
212                         struct pf_altq_node *prev = parent->children;
213
214                         while (prev->next != NULL)
215                                 prev = prev->next;
216                         prev->next = node;
217                 }
218         }
219         update_avg(node);
220 }
221
222 struct pf_altq_node *
223 pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
224     const char *ifname)
225 {
226         struct pf_altq_node     *node, *child;
227
228         for (node = root; node != NULL; node = node->next) {
229                 if (!strcmp(node->altq.qname, qname)
230                     && !(strcmp(node->altq.ifname, ifname)))
231                         return (node);
232                 if (node->children != NULL) {
233                         child = pfctl_find_altq_node(node->children, qname,
234                             ifname);
235                         if (child != NULL)
236                                 return (child);
237                 }
238         }
239         return (NULL);
240 }
241
242 void
243 pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level,
244     int opts)
245 {
246         const struct pf_altq_node       *child;
247
248         if (node == NULL)
249                 return;
250
251         print_altq(&node->altq, level, NULL, NULL);
252
253         if (node->children != NULL) {
254                 printf("{");
255                 for (child = node->children; child != NULL;
256                     child = child->next) {
257                         printf("%s", child->altq.qname);
258                         if (child->next != NULL)
259                                 printf(", ");
260                 }
261                 printf("}");
262         }
263         printf("\n");
264
265         if (opts & PF_OPT_VERBOSE)
266                 pfctl_print_altq_nodestat(dev, node);
267
268         if (opts & PF_OPT_DEBUG)
269                 printf("  [ qid=%u ifname=%s ifbandwidth=%s ]\n",
270                     node->altq.qid, node->altq.ifname,
271                     rate2str((double)(node->altq.ifbandwidth)));
272
273         for (child = node->children; child != NULL;
274             child = child->next)
275                 pfctl_print_altq_node(dev, child, level + 1, opts);
276 }
277
278 void
279 pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a)
280 {
281         if (a->altq.qid == 0)
282                 return;
283
284         switch (a->altq.scheduler) {
285         case ALTQT_CBQ:
286                 print_cbqstats(a->qstats);
287                 break;
288         case ALTQT_PRIQ:
289                 print_priqstats(a->qstats);
290                 break;
291         case ALTQT_HFSC:
292                 print_hfscstats(a->qstats);
293                 break;
294         }
295 }
296
297 void
298 print_cbqstats(struct queue_stats cur)
299 {
300         printf("  [ pkts: %10llu  bytes: %10llu  "
301             "dropped pkts: %6llu bytes: %6llu ]\n",
302             (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
303             (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
304             (unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
305             (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
306         printf("  [ qlength: %3d/%3d  borrows: %6u  suspends: %6u ]\n",
307             cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
308             cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
309
310         if (cur.avgn < 2)
311                 return;
312
313         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
314             cur.avg_packets / STAT_INTERVAL,
315             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
316 }
317
318 void
319 print_priqstats(struct queue_stats cur)
320 {
321         printf("  [ pkts: %10llu  bytes: %10llu  "
322             "dropped pkts: %6llu bytes: %6llu ]\n",
323             (unsigned long long)cur.data.priq_stats.xmitcnt.packets,
324             (unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
325             (unsigned long long)cur.data.priq_stats.dropcnt.packets,
326             (unsigned long long)cur.data.priq_stats.dropcnt.bytes);
327         printf("  [ qlength: %3d/%3d ]\n",
328             cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
329
330         if (cur.avgn < 2)
331                 return;
332
333         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
334             cur.avg_packets / STAT_INTERVAL,
335             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
336 }
337
338 void
339 print_hfscstats(struct queue_stats cur)
340 {
341         printf("  [ pkts: %10llu  bytes: %10llu  "
342             "dropped pkts: %6llu bytes: %6llu ]\n",
343             (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
344             (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
345             (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
346             (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
347         printf("  [ qlength: %3d/%3d ]\n",
348             cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
349
350         if (cur.avgn < 2)
351                 return;
352
353         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
354             cur.avg_packets / STAT_INTERVAL,
355             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
356 }
357
358 void
359 pfctl_free_altq_node(struct pf_altq_node *node)
360 {
361         while (node != NULL) {
362                 struct pf_altq_node     *prev;
363
364                 if (node->children != NULL)
365                         pfctl_free_altq_node(node->children);
366                 prev = node;
367                 node = node->next;
368                 free(prev);
369         }
370 }
371
372 void
373 update_avg(struct pf_altq_node *a)
374 {
375         struct queue_stats      *qs;
376         u_int64_t                b, p;
377         int                      n;
378
379         if (a->altq.qid == 0)
380                 return;
381
382         qs = &a->qstats;
383         n = qs->avgn;
384
385         switch (a->altq.scheduler) {
386         case ALTQT_CBQ:
387                 b = qs->data.cbq_stats.xmit_cnt.bytes;
388                 p = qs->data.cbq_stats.xmit_cnt.packets;
389                 break;
390         case ALTQT_PRIQ:
391                 b = qs->data.priq_stats.xmitcnt.bytes;
392                 p = qs->data.priq_stats.xmitcnt.packets;
393                 break;
394         case ALTQT_HFSC:
395                 b = qs->data.hfsc_stats.xmit_cnt.bytes;
396                 p = qs->data.hfsc_stats.xmit_cnt.packets;
397                 break;
398         default:
399                 b = 0;
400                 p = 0;
401                 break;
402         }
403
404         if (n == 0) {
405                 qs->prev_bytes = b;
406                 qs->prev_packets = p;
407                 qs->avgn++;
408                 return;
409         }
410
411         if (b >= qs->prev_bytes)
412                 qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
413                     (b - qs->prev_bytes)) / n;
414
415         if (p >= qs->prev_packets)
416                 qs->avg_packets = ((qs->avg_packets * (n - 1)) +
417                     (p - qs->prev_packets)) / n;
418
419         qs->prev_bytes = b;
420         qs->prev_packets = p;
421         if (n < AVGN_MAX)
422                 qs->avgn++;
423 }