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_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 if (ioctl(dev, DIOCGETALTQS, &pa)) { 152 warn("DIOCGETALTQS"); 153 return (-1); 154 } 155 156 /* if a new set is found, start over */ 157 if (pa.ticket != last_ticket && *root != NULL) { 158 pfctl_free_altq_node(*root); 159 *root = NULL; 160 } 161 last_ticket = pa.ticket; 162 163 mnr = pa.nr; 164 for (nr = 0; nr < mnr; ++nr) { 165 pa.nr = nr; 166 if (ioctl(dev, DIOCGETALTQ, &pa)) { 167 warn("DIOCGETALTQ"); 168 return (-1); 169 } 170 #ifdef __FreeBSD__ 171 if ((pa.altq.qid > 0 || pa.altq.scheduler == ALTQT_CODEL) && 172 !(pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED)) { 173 #else 174 if (pa.altq.qid > 0) { 175 #endif 176 pq.nr = nr; 177 pq.ticket = pa.ticket; 178 pq.buf = &qstats.data; 179 pq.nbytes = sizeof(qstats.data); 180 if (ioctl(dev, DIOCGETQSTATS, &pq)) { 181 warn("DIOCGETQSTATS"); 182 return (-1); 183 } 184 if ((node = pfctl_find_altq_node(*root, pa.altq.qname, 185 pa.altq.ifname)) != NULL) { 186 memcpy(&node->qstats.data, &qstats.data, 187 sizeof(qstats.data)); 188 update_avg(node); 189 } else { 190 pfctl_insert_altq_node(root, pa.altq, qstats); 191 } 192 } 193 #ifdef __FreeBSD__ 194 else if (pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED) { 195 memset(&qstats.data, 0, sizeof(qstats.data)); 196 if ((node = pfctl_find_altq_node(*root, pa.altq.qname, 197 pa.altq.ifname)) != NULL) { 198 memcpy(&node->qstats.data, &qstats.data, 199 sizeof(qstats.data)); 200 update_avg(node); 201 } else { 202 pfctl_insert_altq_node(root, pa.altq, qstats); 203 } 204 } 205 #endif 206 } 207 return (mnr); 208 } 209 210 void 211 pfctl_insert_altq_node(struct pf_altq_node **root, 212 const struct pf_altq altq, const struct queue_stats qstats) 213 { 214 struct pf_altq_node *node; 215 216 node = calloc(1, sizeof(struct pf_altq_node)); 217 if (node == NULL) 218 err(1, "pfctl_insert_altq_node: calloc"); 219 memcpy(&node->altq, &altq, sizeof(struct pf_altq)); 220 memcpy(&node->qstats, &qstats, sizeof(qstats)); 221 node->next = node->children = NULL; 222 223 if (*root == NULL) 224 *root = node; 225 else if (!altq.parent[0]) { 226 struct pf_altq_node *prev = *root; 227 228 while (prev->next != NULL) 229 prev = prev->next; 230 prev->next = node; 231 } else { 232 struct pf_altq_node *parent; 233 234 parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname); 235 if (parent == NULL) 236 errx(1, "parent %s not found", altq.parent); 237 if (parent->children == NULL) 238 parent->children = node; 239 else { 240 struct pf_altq_node *prev = parent->children; 241 242 while (prev->next != NULL) 243 prev = prev->next; 244 prev->next = node; 245 } 246 } 247 update_avg(node); 248 } 249 250 struct pf_altq_node * 251 pfctl_find_altq_node(struct pf_altq_node *root, const char *qname, 252 const char *ifname) 253 { 254 struct pf_altq_node *node, *child; 255 256 for (node = root; node != NULL; node = node->next) { 257 if (!strcmp(node->altq.qname, qname) 258 && !(strcmp(node->altq.ifname, ifname))) 259 return (node); 260 if (node->children != NULL) { 261 child = pfctl_find_altq_node(node->children, qname, 262 ifname); 263 if (child != NULL) 264 return (child); 265 } 266 } 267 return (NULL); 268 } 269 270 void 271 pfctl_print_altq_node(int dev, const struct pf_altq_node *node, 272 unsigned int level, int opts) 273 { 274 const struct pf_altq_node *child; 275 276 if (node == NULL) 277 return; 278 279 print_altq(&node->altq, level, NULL, NULL); 280 281 if (node->children != NULL) { 282 printf("{"); 283 for (child = node->children; child != NULL; 284 child = child->next) { 285 printf("%s", child->altq.qname); 286 if (child->next != NULL) 287 printf(", "); 288 } 289 printf("}"); 290 } 291 printf("\n"); 292 293 if (opts & PF_OPT_VERBOSE) 294 pfctl_print_altq_nodestat(dev, node); 295 296 if (opts & PF_OPT_DEBUG) 297 printf(" [ qid=%u ifname=%s ifbandwidth=%s ]\n", 298 node->altq.qid, node->altq.ifname, 299 rate2str((double)(node->altq.ifbandwidth))); 300 301 for (child = node->children; child != NULL; 302 child = child->next) 303 pfctl_print_altq_node(dev, child, level + 1, opts); 304 } 305 306 void 307 pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a) 308 { 309 if (a->altq.qid == 0 && a->altq.scheduler != ALTQT_CODEL) 310 return; 311 312 #ifdef __FreeBSD__ 313 if (a->altq.local_flags & PFALTQ_FLAG_IF_REMOVED) 314 return; 315 #endif 316 switch (a->altq.scheduler) { 317 case ALTQT_CBQ: 318 print_cbqstats(a->qstats); 319 break; 320 case ALTQT_PRIQ: 321 print_priqstats(a->qstats); 322 break; 323 case ALTQT_HFSC: 324 print_hfscstats(a->qstats); 325 break; 326 case ALTQT_FAIRQ: 327 print_fairqstats(a->qstats); 328 break; 329 case ALTQT_CODEL: 330 print_codelstats(a->qstats); 331 break; 332 } 333 } 334 335 void 336 print_cbqstats(struct queue_stats cur) 337 { 338 printf(" [ pkts: %10llu bytes: %10llu " 339 "dropped pkts: %6llu bytes: %6llu ]\n", 340 (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets, 341 (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes, 342 (unsigned long long)cur.data.cbq_stats.drop_cnt.packets, 343 (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes); 344 printf(" [ qlength: %3d/%3d borrows: %6u suspends: %6u ]\n", 345 cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax, 346 cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays); 347 348 if (cur.avgn < 2) 349 return; 350 351 printf(" [ measured: %7.1f packets/s, %s/s ]\n", 352 cur.avg_packets / STAT_INTERVAL, 353 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 354 } 355 356 void 357 print_codelstats(struct queue_stats cur) 358 { 359 printf(" [ pkts: %10llu bytes: %10llu " 360 "dropped pkts: %6llu bytes: %6llu ]\n", 361 (unsigned long long)cur.data.codel_stats.cl_xmitcnt.packets, 362 (unsigned long long)cur.data.codel_stats.cl_xmitcnt.bytes, 363 (unsigned long long)cur.data.codel_stats.cl_dropcnt.packets + 364 cur.data.codel_stats.stats.drop_cnt.packets, 365 (unsigned long long)cur.data.codel_stats.cl_dropcnt.bytes + 366 cur.data.codel_stats.stats.drop_cnt.bytes); 367 printf(" [ qlength: %3d/%3d ]\n", 368 cur.data.codel_stats.qlength, cur.data.codel_stats.qlimit); 369 370 if (cur.avgn < 2) 371 return; 372 373 printf(" [ measured: %7.1f packets/s, %s/s ]\n", 374 cur.avg_packets / STAT_INTERVAL, 375 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 376 } 377 378 void 379 print_priqstats(struct queue_stats cur) 380 { 381 printf(" [ pkts: %10llu bytes: %10llu " 382 "dropped pkts: %6llu bytes: %6llu ]\n", 383 (unsigned long long)cur.data.priq_stats.xmitcnt.packets, 384 (unsigned long long)cur.data.priq_stats.xmitcnt.bytes, 385 (unsigned long long)cur.data.priq_stats.dropcnt.packets, 386 (unsigned long long)cur.data.priq_stats.dropcnt.bytes); 387 printf(" [ qlength: %3d/%3d ]\n", 388 cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit); 389 390 if (cur.avgn < 2) 391 return; 392 393 printf(" [ measured: %7.1f packets/s, %s/s ]\n", 394 cur.avg_packets / STAT_INTERVAL, 395 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 396 } 397 398 void 399 print_hfscstats(struct queue_stats cur) 400 { 401 printf(" [ pkts: %10llu bytes: %10llu " 402 "dropped pkts: %6llu bytes: %6llu ]\n", 403 (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets, 404 (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes, 405 (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets, 406 (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes); 407 printf(" [ qlength: %3d/%3d ]\n", 408 cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit); 409 410 if (cur.avgn < 2) 411 return; 412 413 printf(" [ measured: %7.1f packets/s, %s/s ]\n", 414 cur.avg_packets / STAT_INTERVAL, 415 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 416 } 417 418 void 419 print_fairqstats(struct queue_stats cur) 420 { 421 printf(" [ pkts: %10llu bytes: %10llu " 422 "dropped pkts: %6llu bytes: %6llu ]\n", 423 (unsigned long long)cur.data.fairq_stats.xmit_cnt.packets, 424 (unsigned long long)cur.data.fairq_stats.xmit_cnt.bytes, 425 (unsigned long long)cur.data.fairq_stats.drop_cnt.packets, 426 (unsigned long long)cur.data.fairq_stats.drop_cnt.bytes); 427 printf(" [ qlength: %3d/%3d ]\n", 428 cur.data.fairq_stats.qlength, cur.data.fairq_stats.qlimit); 429 430 if (cur.avgn < 2) 431 return; 432 433 printf(" [ measured: %7.1f packets/s, %s/s ]\n", 434 cur.avg_packets / STAT_INTERVAL, 435 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 436 } 437 438 void 439 pfctl_free_altq_node(struct pf_altq_node *node) 440 { 441 while (node != NULL) { 442 struct pf_altq_node *prev; 443 444 if (node->children != NULL) 445 pfctl_free_altq_node(node->children); 446 prev = node; 447 node = node->next; 448 free(prev); 449 } 450 } 451 452 void 453 update_avg(struct pf_altq_node *a) 454 { 455 struct queue_stats *qs; 456 u_int64_t b, p; 457 int n; 458 459 if (a->altq.qid == 0 && a->altq.scheduler != ALTQT_CODEL) 460 return; 461 462 qs = &a->qstats; 463 n = qs->avgn; 464 465 switch (a->altq.scheduler) { 466 case ALTQT_CBQ: 467 b = qs->data.cbq_stats.xmit_cnt.bytes; 468 p = qs->data.cbq_stats.xmit_cnt.packets; 469 break; 470 case ALTQT_PRIQ: 471 b = qs->data.priq_stats.xmitcnt.bytes; 472 p = qs->data.priq_stats.xmitcnt.packets; 473 break; 474 case ALTQT_HFSC: 475 b = qs->data.hfsc_stats.xmit_cnt.bytes; 476 p = qs->data.hfsc_stats.xmit_cnt.packets; 477 break; 478 case ALTQT_FAIRQ: 479 b = qs->data.fairq_stats.xmit_cnt.bytes; 480 p = qs->data.fairq_stats.xmit_cnt.packets; 481 break; 482 case ALTQT_CODEL: 483 b = qs->data.codel_stats.cl_xmitcnt.bytes; 484 p = qs->data.codel_stats.cl_xmitcnt.packets; 485 break; 486 default: 487 b = 0; 488 p = 0; 489 break; 490 } 491 492 if (n == 0) { 493 qs->prev_bytes = b; 494 qs->prev_packets = p; 495 qs->avgn++; 496 return; 497 } 498 499 if (b >= qs->prev_bytes) 500 qs->avg_bytes = ((qs->avg_bytes * (n - 1)) + 501 (b - qs->prev_bytes)) / n; 502 503 if (p >= qs->prev_packets) 504 qs->avg_packets = ((qs->avg_packets * (n - 1)) + 505 (p - qs->prev_packets)) / n; 506 507 qs->prev_bytes = b; 508 qs->prev_packets = p; 509 if (n < AVGN_MAX) 510 qs->avgn++; 511 } 512