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