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