pfctl_qstats.c revision 1.2 1 /* $NetBSD: pfctl_qstats.c,v 1.2 2004/06/22 15:16:30 itojun Exp $ */
2 /* $OpenBSD: pfctl_qstats.c,v 1.29 2004/03/15 15:25:44 dhartmei 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 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, 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 }
416