pfctl_qstats.c revision 1.4 1 /* $NetBSD: pfctl_qstats.c,v 1.4 2004/11/14 11:26:48 yamt Exp $ */
2 /* $OpenBSD: pfctl_qstats.c,v 1.30 2004/04/27 21:47:32 kjc Exp $ */
3
4 /*
5 * Copyright (c) Henning Brauer <henning (at) 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/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 <altq/altq.h>
36 #include <altq/altq_cbq.h>
37 #include <altq/altq_priq.h>
38 #include <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 #ifdef __OpenBSD__
46 struct priq_classstats priq_stats;
47 struct hfsc_classstats hfsc_stats;
48 #endif
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
93 if ((nodes = pfctl_update_qstats(dev, &root)) < 0)
94 return (-1);
95
96 if (nodes == 0)
97 printf("No queue in use\n");
98 for (node = root; node != NULL; node = node->next) {
99 if (iface != NULL && strcmp(node->altq.ifname, iface))
100 continue;
101 if (dotitle) {
102 pfctl_print_title("ALTQ:");
103 dotitle = 0;
104 }
105 pfctl_print_altq_node(dev, node, 0, opts);
106 }
107
108 while (verbose2 && nodes > 0) {
109 printf("\n");
110 fflush(stdout);
111 sleep(STAT_INTERVAL);
112 if ((nodes = pfctl_update_qstats(dev, &root)) == -1)
113 return (-1);
114 for (node = root; node != NULL; node = node->next) {
115 if (iface != NULL && strcmp(node->altq.ifname, iface))
116 continue;
117 pfctl_print_altq_node(dev, node, 0, opts);
118 }
119 }
120 pfctl_free_altq_node(root);
121 return (0);
122 }
123
124 int
125 pfctl_update_qstats(int dev, struct pf_altq_node **root)
126 {
127 struct pf_altq_node *node;
128 struct pfioc_altq pa;
129 struct pfioc_qstats pq;
130 u_int32_t mnr, nr;
131 struct queue_stats qstats;
132 static u_int32_t last_ticket;
133
134 memset(&pa, 0, sizeof(pa));
135 memset(&pq, 0, sizeof(pq));
136 memset(&qstats, 0, sizeof(qstats));
137 if (ioctl(dev, DIOCGETALTQS, &pa)) {
138 warn("DIOCGETALTQS");
139 return (-1);
140 }
141
142 /* if a new set is found, start over */
143 if (pa.ticket != last_ticket && *root != NULL) {
144 pfctl_free_altq_node(*root);
145 *root = NULL;
146 }
147 last_ticket = pa.ticket;
148
149 mnr = pa.nr;
150 for (nr = 0; nr < mnr; ++nr) {
151 pa.nr = nr;
152 if (ioctl(dev, DIOCGETALTQ, &pa)) {
153 warn("DIOCGETALTQ");
154 return (-1);
155 }
156 if (pa.altq.qid > 0) {
157 pq.nr = nr;
158 pq.ticket = pa.ticket;
159 pq.buf = &qstats.data;
160 pq.nbytes = sizeof(qstats.data);
161 if (ioctl(dev, DIOCGETQSTATS, &pq)) {
162 warn("DIOCGETQSTATS");
163 return (-1);
164 }
165 if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
166 pa.altq.ifname)) != NULL) {
167 memcpy(&node->qstats.data, &qstats.data,
168 sizeof(qstats.data));
169 update_avg(node);
170 } else {
171 pfctl_insert_altq_node(root, pa.altq, qstats);
172 }
173 }
174 }
175 return (mnr);
176 }
177
178 void
179 pfctl_insert_altq_node(struct pf_altq_node **root,
180 const struct pf_altq altq, const struct queue_stats qstats)
181 {
182 struct pf_altq_node *node;
183
184 node = calloc(1, sizeof(struct pf_altq_node));
185 if (node == NULL)
186 err(1, "pfctl_insert_altq_node: calloc");
187 memcpy(&node->altq, &altq, sizeof(struct pf_altq));
188 memcpy(&node->qstats, &qstats, sizeof(qstats));
189 node->next = node->children = NULL;
190
191 if (*root == NULL)
192 *root = node;
193 else if (!altq.parent[0]) {
194 struct pf_altq_node *prev = *root;
195
196 while (prev->next != NULL)
197 prev = prev->next;
198 prev->next = node;
199 } else {
200 struct pf_altq_node *parent;
201
202 parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
203 if (parent == NULL)
204 errx(1, "parent %s not found", altq.parent);
205 if (parent->children == NULL)
206 parent->children = node;
207 else {
208 struct pf_altq_node *prev = parent->children;
209
210 while (prev->next != NULL)
211 prev = prev->next;
212 prev->next = node;
213 }
214 }
215 update_avg(node);
216 }
217
218 struct pf_altq_node *
219 pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
220 const char *ifname)
221 {
222 struct pf_altq_node *node, *child;
223
224 for (node = root; node != NULL; node = node->next) {
225 if (!strcmp(node->altq.qname, qname)
226 && !(strcmp(node->altq.ifname, ifname)))
227 return (node);
228 if (node->children != NULL) {
229 child = pfctl_find_altq_node(node->children, qname,
230 ifname);
231 if (child != NULL)
232 return (child);
233 }
234 }
235 return (NULL);
236 }
237
238 void
239 pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level,
240 int opts)
241 {
242 const struct pf_altq_node *child;
243
244 if (node == NULL)
245 return;
246
247 print_altq(&node->altq, level, NULL, NULL);
248
249 if (node->children != NULL) {
250 printf("{");
251 for (child = node->children; child != NULL;
252 child = child->next) {
253 printf("%s", child->altq.qname);
254 if (child->next != NULL)
255 printf(", ");
256 }
257 printf("}");
258 }
259 printf("\n");
260
261 if (opts & PF_OPT_VERBOSE)
262 pfctl_print_altq_nodestat(dev, node);
263
264 if (opts & PF_OPT_DEBUG)
265 printf(" [ qid=%u ifname=%s ifbandwidth=%s ]\n",
266 node->altq.qid, node->altq.ifname,
267 rate2str((double)(node->altq.ifbandwidth)));
268
269 for (child = node->children; child != NULL;
270 child = child->next)
271 pfctl_print_altq_node(dev, child, level + 1, opts);
272 }
273
274 void
275 pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a)
276 {
277 if (a->altq.qid == 0)
278 return;
279
280 switch (a->altq.scheduler) {
281 case ALTQT_CBQ:
282 print_cbqstats(a->qstats);
283 break;
284 case ALTQT_PRIQ:
285 print_priqstats(a->qstats);
286 break;
287 case ALTQT_HFSC:
288 print_hfscstats(a->qstats);
289 break;
290 }
291 }
292
293 void
294 print_cbqstats(struct queue_stats cur)
295 {
296 printf(" [ pkts: %10llu bytes: %10llu "
297 "dropped pkts: %6llu bytes: %6llu ]\n",
298 (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
299 (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
300 (unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
301 (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
302 printf(" [ qlength: %3d/%3d borrows: %6u suspends: %6u ]\n",
303 cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
304 cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
305
306 if (cur.avgn < 2)
307 return;
308
309 printf(" [ measured: %7.1f packets/s, %s/s ]\n",
310 cur.avg_packets / STAT_INTERVAL,
311 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
312 }
313
314 void
315 print_priqstats(struct queue_stats cur)
316 {
317 #ifdef __OpenBSD__
318 printf(" [ pkts: %10llu bytes: %10llu "
319 "dropped pkts: %6llu bytes: %6llu ]\n",
320 (unsigned long long)cur.data.priq_stats.xmitcnt.packets,
321 (unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
322 (unsigned long long)cur.data.priq_stats.dropcnt.packets,
323 (unsigned long long)cur.data.priq_stats.dropcnt.bytes);
324 printf(" [ qlength: %3d/%3d ]\n",
325 cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
326
327 if (cur.avgn < 2)
328 return;
329
330 printf(" [ measured: %7.1f packets/s, %s/s ]\n",
331 cur.avg_packets / STAT_INTERVAL,
332 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
333 #endif
334 }
335
336 void
337 print_hfscstats(struct queue_stats cur)
338 {
339 #ifdef __OpenBSD__
340 printf(" [ pkts: %10llu bytes: %10llu "
341 "dropped pkts: %6llu bytes: %6llu ]\n",
342 (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
343 (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
344 (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
345 (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
346 printf(" [ qlength: %3d/%3d ]\n",
347 cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
348
349 if (cur.avgn < 2)
350 return;
351
352 printf(" [ measured: %7.1f packets/s, %s/s ]\n",
353 cur.avg_packets / STAT_INTERVAL,
354 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
355 #endif
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 #ifdef __OpenBSD__
391 case ALTQT_PRIQ:
392 b = qs->data.priq_stats.xmitcnt.bytes;
393 p = qs->data.priq_stats.xmitcnt.packets;
394 break;
395 case ALTQT_HFSC:
396 b = qs->data.hfsc_stats.xmit_cnt.bytes;
397 p = qs->data.hfsc_stats.xmit_cnt.packets;
398 break;
399 #endif
400 default:
401 b = 0;
402 p = 0;
403 break;
404 }
405
406 if (n == 0) {
407 qs->prev_bytes = b;
408 qs->prev_packets = p;
409 qs->avgn++;
410 return;
411 }
412
413 if (b >= qs->prev_bytes)
414 qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
415 (b - qs->prev_bytes)) / n;
416
417 if (p >= qs->prev_packets)
418 qs->avg_packets = ((qs->avg_packets * (n - 1)) +
419 (p - qs->prev_packets)) / n;
420
421 qs->prev_bytes = b;
422 qs->prev_packets = p;
423 if (n < AVGN_MAX)
424 qs->avgn++;
425 }
426