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