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