pfctl_qstats.c revision 1.4 1 1.4 yamt /* $NetBSD: pfctl_qstats.c,v 1.4 2004/11/14 11:26:48 yamt Exp $ */
2 1.4 yamt /* $OpenBSD: pfctl_qstats.c,v 1.30 2004/04/27 21:47:32 kjc Exp $ */
3 1.1 itojun
4 1.1 itojun /*
5 1.1 itojun * Copyright (c) Henning Brauer <henning (at) openbsd.org>
6 1.1 itojun *
7 1.1 itojun * Permission to use, copy, modify, and distribute this software for any
8 1.1 itojun * purpose with or without fee is hereby granted, provided that the above
9 1.1 itojun * copyright notice and this permission notice appear in all copies.
10 1.1 itojun *
11 1.1 itojun * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 1.1 itojun * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 1.1 itojun * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 1.1 itojun * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 1.1 itojun * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 1.1 itojun * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 1.1 itojun * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 1.1 itojun */
19 1.1 itojun
20 1.1 itojun #include <sys/types.h>
21 1.1 itojun #include <sys/ioctl.h>
22 1.1 itojun #include <sys/socket.h>
23 1.1 itojun
24 1.1 itojun #include <net/if.h>
25 1.1 itojun #include <netinet/in.h>
26 1.1 itojun #include <net/pfvar.h>
27 1.1 itojun #include <arpa/inet.h>
28 1.1 itojun
29 1.1 itojun #include <err.h>
30 1.1 itojun #include <stdio.h>
31 1.1 itojun #include <stdlib.h>
32 1.1 itojun #include <string.h>
33 1.1 itojun #include <unistd.h>
34 1.1 itojun
35 1.1 itojun #include <altq/altq.h>
36 1.1 itojun #include <altq/altq_cbq.h>
37 1.1 itojun #include <altq/altq_priq.h>
38 1.1 itojun #include <altq/altq_hfsc.h>
39 1.1 itojun
40 1.1 itojun #include "pfctl.h"
41 1.1 itojun #include "pfctl_parser.h"
42 1.1 itojun
43 1.1 itojun union class_stats {
44 1.1 itojun class_stats_t cbq_stats;
45 1.3 itojun #ifdef __OpenBSD__
46 1.1 itojun struct priq_classstats priq_stats;
47 1.1 itojun struct hfsc_classstats hfsc_stats;
48 1.3 itojun #endif
49 1.1 itojun };
50 1.1 itojun
51 1.1 itojun #define AVGN_MAX 8
52 1.1 itojun #define STAT_INTERVAL 5
53 1.1 itojun
54 1.1 itojun struct queue_stats {
55 1.1 itojun union class_stats data;
56 1.1 itojun int avgn;
57 1.1 itojun double avg_bytes;
58 1.1 itojun double avg_packets;
59 1.1 itojun u_int64_t prev_bytes;
60 1.1 itojun u_int64_t prev_packets;
61 1.1 itojun };
62 1.1 itojun
63 1.1 itojun struct pf_altq_node {
64 1.1 itojun struct pf_altq altq;
65 1.1 itojun struct pf_altq_node *next;
66 1.1 itojun struct pf_altq_node *children;
67 1.1 itojun struct queue_stats qstats;
68 1.1 itojun };
69 1.1 itojun
70 1.1 itojun int pfctl_update_qstats(int, struct pf_altq_node **);
71 1.1 itojun void pfctl_insert_altq_node(struct pf_altq_node **,
72 1.1 itojun const struct pf_altq, const struct queue_stats);
73 1.1 itojun struct pf_altq_node *pfctl_find_altq_node(struct pf_altq_node *,
74 1.1 itojun const char *, const char *);
75 1.1 itojun void pfctl_print_altq_node(int, const struct pf_altq_node *,
76 1.1 itojun unsigned, int);
77 1.1 itojun void print_cbqstats(struct queue_stats);
78 1.1 itojun void print_priqstats(struct queue_stats);
79 1.1 itojun void print_hfscstats(struct queue_stats);
80 1.1 itojun void pfctl_free_altq_node(struct pf_altq_node *);
81 1.1 itojun void pfctl_print_altq_nodestat(int,
82 1.1 itojun const struct pf_altq_node *);
83 1.1 itojun
84 1.1 itojun void update_avg(struct pf_altq_node *);
85 1.1 itojun
86 1.1 itojun int
87 1.1 itojun pfctl_show_altq(int dev, const char *iface, int opts, int verbose2)
88 1.1 itojun {
89 1.1 itojun struct pf_altq_node *root = NULL, *node;
90 1.1 itojun int nodes, dotitle = (opts & PF_OPT_SHOWALL);
91 1.1 itojun
92 1.1 itojun
93 1.1 itojun if ((nodes = pfctl_update_qstats(dev, &root)) < 0)
94 1.1 itojun return (-1);
95 1.1 itojun
96 1.4 yamt if (nodes == 0)
97 1.4 yamt printf("No queue in use\n");
98 1.1 itojun for (node = root; node != NULL; node = node->next) {
99 1.1 itojun if (iface != NULL && strcmp(node->altq.ifname, iface))
100 1.1 itojun continue;
101 1.1 itojun if (dotitle) {
102 1.1 itojun pfctl_print_title("ALTQ:");
103 1.1 itojun dotitle = 0;
104 1.1 itojun }
105 1.1 itojun pfctl_print_altq_node(dev, node, 0, opts);
106 1.1 itojun }
107 1.1 itojun
108 1.4 yamt while (verbose2 && nodes > 0) {
109 1.1 itojun printf("\n");
110 1.1 itojun fflush(stdout);
111 1.1 itojun sleep(STAT_INTERVAL);
112 1.4 yamt if ((nodes = pfctl_update_qstats(dev, &root)) == -1)
113 1.1 itojun return (-1);
114 1.1 itojun for (node = root; node != NULL; node = node->next) {
115 1.1 itojun if (iface != NULL && strcmp(node->altq.ifname, iface))
116 1.1 itojun continue;
117 1.1 itojun pfctl_print_altq_node(dev, node, 0, opts);
118 1.1 itojun }
119 1.1 itojun }
120 1.1 itojun pfctl_free_altq_node(root);
121 1.1 itojun return (0);
122 1.1 itojun }
123 1.1 itojun
124 1.1 itojun int
125 1.1 itojun pfctl_update_qstats(int dev, struct pf_altq_node **root)
126 1.1 itojun {
127 1.1 itojun struct pf_altq_node *node;
128 1.1 itojun struct pfioc_altq pa;
129 1.1 itojun struct pfioc_qstats pq;
130 1.1 itojun u_int32_t mnr, nr;
131 1.1 itojun struct queue_stats qstats;
132 1.1 itojun static u_int32_t last_ticket;
133 1.1 itojun
134 1.1 itojun memset(&pa, 0, sizeof(pa));
135 1.1 itojun memset(&pq, 0, sizeof(pq));
136 1.1 itojun memset(&qstats, 0, sizeof(qstats));
137 1.1 itojun if (ioctl(dev, DIOCGETALTQS, &pa)) {
138 1.1 itojun warn("DIOCGETALTQS");
139 1.1 itojun return (-1);
140 1.1 itojun }
141 1.1 itojun
142 1.1 itojun /* if a new set is found, start over */
143 1.1 itojun if (pa.ticket != last_ticket && *root != NULL) {
144 1.1 itojun pfctl_free_altq_node(*root);
145 1.1 itojun *root = NULL;
146 1.1 itojun }
147 1.1 itojun last_ticket = pa.ticket;
148 1.1 itojun
149 1.1 itojun mnr = pa.nr;
150 1.1 itojun for (nr = 0; nr < mnr; ++nr) {
151 1.1 itojun pa.nr = nr;
152 1.1 itojun if (ioctl(dev, DIOCGETALTQ, &pa)) {
153 1.1 itojun warn("DIOCGETALTQ");
154 1.1 itojun return (-1);
155 1.1 itojun }
156 1.1 itojun if (pa.altq.qid > 0) {
157 1.1 itojun pq.nr = nr;
158 1.1 itojun pq.ticket = pa.ticket;
159 1.1 itojun pq.buf = &qstats.data;
160 1.1 itojun pq.nbytes = sizeof(qstats.data);
161 1.1 itojun if (ioctl(dev, DIOCGETQSTATS, &pq)) {
162 1.1 itojun warn("DIOCGETQSTATS");
163 1.1 itojun return (-1);
164 1.1 itojun }
165 1.1 itojun if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
166 1.1 itojun pa.altq.ifname)) != NULL) {
167 1.1 itojun memcpy(&node->qstats.data, &qstats.data,
168 1.1 itojun sizeof(qstats.data));
169 1.1 itojun update_avg(node);
170 1.1 itojun } else {
171 1.1 itojun pfctl_insert_altq_node(root, pa.altq, qstats);
172 1.1 itojun }
173 1.1 itojun }
174 1.1 itojun }
175 1.1 itojun return (mnr);
176 1.1 itojun }
177 1.1 itojun
178 1.1 itojun void
179 1.1 itojun pfctl_insert_altq_node(struct pf_altq_node **root,
180 1.1 itojun const struct pf_altq altq, const struct queue_stats qstats)
181 1.1 itojun {
182 1.1 itojun struct pf_altq_node *node;
183 1.1 itojun
184 1.1 itojun node = calloc(1, sizeof(struct pf_altq_node));
185 1.1 itojun if (node == NULL)
186 1.1 itojun err(1, "pfctl_insert_altq_node: calloc");
187 1.1 itojun memcpy(&node->altq, &altq, sizeof(struct pf_altq));
188 1.1 itojun memcpy(&node->qstats, &qstats, sizeof(qstats));
189 1.1 itojun node->next = node->children = NULL;
190 1.1 itojun
191 1.1 itojun if (*root == NULL)
192 1.1 itojun *root = node;
193 1.1 itojun else if (!altq.parent[0]) {
194 1.1 itojun struct pf_altq_node *prev = *root;
195 1.1 itojun
196 1.1 itojun while (prev->next != NULL)
197 1.1 itojun prev = prev->next;
198 1.1 itojun prev->next = node;
199 1.1 itojun } else {
200 1.1 itojun struct pf_altq_node *parent;
201 1.1 itojun
202 1.1 itojun parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
203 1.1 itojun if (parent == NULL)
204 1.1 itojun errx(1, "parent %s not found", altq.parent);
205 1.1 itojun if (parent->children == NULL)
206 1.1 itojun parent->children = node;
207 1.1 itojun else {
208 1.1 itojun struct pf_altq_node *prev = parent->children;
209 1.1 itojun
210 1.1 itojun while (prev->next != NULL)
211 1.1 itojun prev = prev->next;
212 1.1 itojun prev->next = node;
213 1.1 itojun }
214 1.1 itojun }
215 1.1 itojun update_avg(node);
216 1.1 itojun }
217 1.1 itojun
218 1.1 itojun struct pf_altq_node *
219 1.1 itojun pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
220 1.1 itojun const char *ifname)
221 1.1 itojun {
222 1.1 itojun struct pf_altq_node *node, *child;
223 1.1 itojun
224 1.1 itojun for (node = root; node != NULL; node = node->next) {
225 1.1 itojun if (!strcmp(node->altq.qname, qname)
226 1.1 itojun && !(strcmp(node->altq.ifname, ifname)))
227 1.1 itojun return (node);
228 1.1 itojun if (node->children != NULL) {
229 1.1 itojun child = pfctl_find_altq_node(node->children, qname,
230 1.1 itojun ifname);
231 1.1 itojun if (child != NULL)
232 1.1 itojun return (child);
233 1.1 itojun }
234 1.1 itojun }
235 1.1 itojun return (NULL);
236 1.1 itojun }
237 1.1 itojun
238 1.1 itojun void
239 1.1 itojun pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level,
240 1.1 itojun int opts)
241 1.1 itojun {
242 1.1 itojun const struct pf_altq_node *child;
243 1.1 itojun
244 1.1 itojun if (node == NULL)
245 1.1 itojun return;
246 1.1 itojun
247 1.1 itojun print_altq(&node->altq, level, NULL, NULL);
248 1.1 itojun
249 1.1 itojun if (node->children != NULL) {
250 1.1 itojun printf("{");
251 1.1 itojun for (child = node->children; child != NULL;
252 1.1 itojun child = child->next) {
253 1.1 itojun printf("%s", child->altq.qname);
254 1.1 itojun if (child->next != NULL)
255 1.1 itojun printf(", ");
256 1.1 itojun }
257 1.1 itojun printf("}");
258 1.1 itojun }
259 1.1 itojun printf("\n");
260 1.1 itojun
261 1.1 itojun if (opts & PF_OPT_VERBOSE)
262 1.1 itojun pfctl_print_altq_nodestat(dev, node);
263 1.1 itojun
264 1.1 itojun if (opts & PF_OPT_DEBUG)
265 1.1 itojun printf(" [ qid=%u ifname=%s ifbandwidth=%s ]\n",
266 1.1 itojun node->altq.qid, node->altq.ifname,
267 1.1 itojun rate2str((double)(node->altq.ifbandwidth)));
268 1.1 itojun
269 1.1 itojun for (child = node->children; child != NULL;
270 1.1 itojun child = child->next)
271 1.1 itojun pfctl_print_altq_node(dev, child, level + 1, opts);
272 1.1 itojun }
273 1.1 itojun
274 1.1 itojun void
275 1.1 itojun pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a)
276 1.1 itojun {
277 1.1 itojun if (a->altq.qid == 0)
278 1.1 itojun return;
279 1.1 itojun
280 1.1 itojun switch (a->altq.scheduler) {
281 1.1 itojun case ALTQT_CBQ:
282 1.1 itojun print_cbqstats(a->qstats);
283 1.1 itojun break;
284 1.1 itojun case ALTQT_PRIQ:
285 1.1 itojun print_priqstats(a->qstats);
286 1.1 itojun break;
287 1.1 itojun case ALTQT_HFSC:
288 1.1 itojun print_hfscstats(a->qstats);
289 1.1 itojun break;
290 1.1 itojun }
291 1.1 itojun }
292 1.1 itojun
293 1.1 itojun void
294 1.1 itojun print_cbqstats(struct queue_stats cur)
295 1.1 itojun {
296 1.1 itojun printf(" [ pkts: %10llu bytes: %10llu "
297 1.1 itojun "dropped pkts: %6llu bytes: %6llu ]\n",
298 1.1 itojun (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
299 1.1 itojun (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
300 1.1 itojun (unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
301 1.1 itojun (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
302 1.1 itojun printf(" [ qlength: %3d/%3d borrows: %6u suspends: %6u ]\n",
303 1.1 itojun cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
304 1.1 itojun cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
305 1.1 itojun
306 1.1 itojun if (cur.avgn < 2)
307 1.1 itojun return;
308 1.1 itojun
309 1.1 itojun printf(" [ measured: %7.1f packets/s, %s/s ]\n",
310 1.1 itojun cur.avg_packets / STAT_INTERVAL,
311 1.1 itojun rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
312 1.1 itojun }
313 1.1 itojun
314 1.1 itojun void
315 1.1 itojun print_priqstats(struct queue_stats cur)
316 1.1 itojun {
317 1.3 itojun #ifdef __OpenBSD__
318 1.1 itojun printf(" [ pkts: %10llu bytes: %10llu "
319 1.1 itojun "dropped pkts: %6llu bytes: %6llu ]\n",
320 1.1 itojun (unsigned long long)cur.data.priq_stats.xmitcnt.packets,
321 1.1 itojun (unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
322 1.1 itojun (unsigned long long)cur.data.priq_stats.dropcnt.packets,
323 1.1 itojun (unsigned long long)cur.data.priq_stats.dropcnt.bytes);
324 1.1 itojun printf(" [ qlength: %3d/%3d ]\n",
325 1.1 itojun cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
326 1.1 itojun
327 1.1 itojun if (cur.avgn < 2)
328 1.1 itojun return;
329 1.1 itojun
330 1.1 itojun printf(" [ measured: %7.1f packets/s, %s/s ]\n",
331 1.1 itojun cur.avg_packets / STAT_INTERVAL,
332 1.1 itojun rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
333 1.3 itojun #endif
334 1.1 itojun }
335 1.1 itojun
336 1.1 itojun void
337 1.1 itojun print_hfscstats(struct queue_stats cur)
338 1.1 itojun {
339 1.3 itojun #ifdef __OpenBSD__
340 1.1 itojun printf(" [ pkts: %10llu bytes: %10llu "
341 1.1 itojun "dropped pkts: %6llu bytes: %6llu ]\n",
342 1.1 itojun (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
343 1.1 itojun (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
344 1.1 itojun (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
345 1.1 itojun (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
346 1.1 itojun printf(" [ qlength: %3d/%3d ]\n",
347 1.1 itojun cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
348 1.1 itojun
349 1.1 itojun if (cur.avgn < 2)
350 1.1 itojun return;
351 1.1 itojun
352 1.1 itojun printf(" [ measured: %7.1f packets/s, %s/s ]\n",
353 1.1 itojun cur.avg_packets / STAT_INTERVAL,
354 1.1 itojun rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
355 1.3 itojun #endif
356 1.1 itojun }
357 1.1 itojun
358 1.1 itojun void
359 1.1 itojun pfctl_free_altq_node(struct pf_altq_node *node)
360 1.1 itojun {
361 1.1 itojun while (node != NULL) {
362 1.1 itojun struct pf_altq_node *prev;
363 1.1 itojun
364 1.1 itojun if (node->children != NULL)
365 1.1 itojun pfctl_free_altq_node(node->children);
366 1.1 itojun prev = node;
367 1.1 itojun node = node->next;
368 1.1 itojun free(prev);
369 1.1 itojun }
370 1.1 itojun }
371 1.1 itojun
372 1.1 itojun void
373 1.1 itojun update_avg(struct pf_altq_node *a)
374 1.1 itojun {
375 1.1 itojun struct queue_stats *qs;
376 1.1 itojun u_int64_t b, p;
377 1.1 itojun int n;
378 1.1 itojun
379 1.1 itojun if (a->altq.qid == 0)
380 1.1 itojun return;
381 1.1 itojun
382 1.1 itojun qs = &a->qstats;
383 1.1 itojun n = qs->avgn;
384 1.1 itojun
385 1.1 itojun switch (a->altq.scheduler) {
386 1.1 itojun case ALTQT_CBQ:
387 1.1 itojun b = qs->data.cbq_stats.xmit_cnt.bytes;
388 1.1 itojun p = qs->data.cbq_stats.xmit_cnt.packets;
389 1.1 itojun break;
390 1.3 itojun #ifdef __OpenBSD__
391 1.1 itojun case ALTQT_PRIQ:
392 1.1 itojun b = qs->data.priq_stats.xmitcnt.bytes;
393 1.1 itojun p = qs->data.priq_stats.xmitcnt.packets;
394 1.1 itojun break;
395 1.1 itojun case ALTQT_HFSC:
396 1.1 itojun b = qs->data.hfsc_stats.xmit_cnt.bytes;
397 1.1 itojun p = qs->data.hfsc_stats.xmit_cnt.packets;
398 1.1 itojun break;
399 1.3 itojun #endif
400 1.1 itojun default:
401 1.1 itojun b = 0;
402 1.1 itojun p = 0;
403 1.1 itojun break;
404 1.1 itojun }
405 1.1 itojun
406 1.1 itojun if (n == 0) {
407 1.1 itojun qs->prev_bytes = b;
408 1.1 itojun qs->prev_packets = p;
409 1.1 itojun qs->avgn++;
410 1.1 itojun return;
411 1.1 itojun }
412 1.1 itojun
413 1.1 itojun if (b >= qs->prev_bytes)
414 1.1 itojun qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
415 1.1 itojun (b - qs->prev_bytes)) / n;
416 1.1 itojun
417 1.1 itojun if (p >= qs->prev_packets)
418 1.1 itojun qs->avg_packets = ((qs->avg_packets * (n - 1)) +
419 1.1 itojun (p - qs->prev_packets)) / n;
420 1.1 itojun
421 1.1 itojun qs->prev_bytes = b;
422 1.1 itojun qs->prev_packets = p;
423 1.1 itojun if (n < AVGN_MAX)
424 1.1 itojun qs->avgn++;
425 1.1 itojun }
426