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