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