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 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 (node->altq.local_flags & PFALTQ_FLAG_IF_REMOVED) 100 continue; 101 if (dotitle) { 102 pfctl_print_title("ALTQ:"); 103 dotitle = 0; 104 } 105 pfctl_print_altq_node(dev, node, 0, opts); 106 } 107 108 while (verbose2) { 109 printf("\n"); 110 fflush(stdout); 111 sleep(STAT_INTERVAL); 112 if (pfctl_update_qstats(dev, &root) == -1) 113 return (-1); 114 for (node = root; node != NULL; node = node->next) { 115 if (iface != NULL && strcmp(node->altq.ifname, iface)) 116 continue; 117 pfctl_print_altq_node(dev, node, 0, opts); 118 } 119 } 120 pfctl_free_altq_node(root); 121 return (0); 122 } 123 124 int 125 pfctl_update_qstats(int dev, struct pf_altq_node **root) 126 { 127 struct pf_altq_node *node; 128 struct pfioc_altq pa; 129 struct pfioc_qstats pq; 130 u_int32_t mnr, nr; 131 struct queue_stats qstats; 132 static u_int32_t last_ticket; 133 134 memset(&pa, 0, sizeof(pa)); 135 memset(&pq, 0, sizeof(pq)); 136 memset(&qstats, 0, sizeof(qstats)); 137 if (ioctl(dev, DIOCGETALTQS, &pa)) { 138 warn("DIOCGETALTQS"); 139 return (-1); 140 } 141 142 /* if a new set is found, start over */ 143 if (pa.ticket != last_ticket && *root != NULL) { 144 pfctl_free_altq_node(*root); 145 *root = NULL; 146 } 147 last_ticket = pa.ticket; 148 149 mnr = pa.nr; 150 for (nr = 0; nr < mnr; ++nr) { 151 pa.nr = nr; 152 if (ioctl(dev, DIOCGETALTQ, &pa)) { 153 warn("DIOCGETALTQ"); 154 return (-1); 155 } 156 if ((pa.altq.qid > 0) && 157 !(pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED)) { 158 pq.nr = nr; 159 pq.ticket = pa.ticket; 160 pq.buf = &qstats.data; 161 pq.nbytes = sizeof(qstats.data); 162 if (ioctl(dev, DIOCGETQSTATS, &pq)) { 163 warn("DIOCGETQSTATS"); 164 return (-1); 165 } 166 if ((node = pfctl_find_altq_node(*root, pa.altq.qname, 167 pa.altq.ifname)) != NULL) { 168 memcpy(&node->qstats.data, &qstats.data, 169 sizeof(qstats.data)); 170 update_avg(node); 171 } else { 172 pfctl_insert_altq_node(root, pa.altq, qstats); 173 } 174 } else if (pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED) { 175 memset(&qstats.data, 0, sizeof(qstats.data)); 176 if ((node = pfctl_find_altq_node(*root, pa.altq.qname, 177 pa.altq.ifname)) != NULL) { 178 memcpy(&node->qstats.data, &qstats.data, 179 sizeof(qstats.data)); 180 update_avg(node); 181 } else { 182 pfctl_insert_altq_node(root, pa.altq, qstats); 183 } 184 } 185 } 186 return (mnr); 187 } 188 189 void 190 pfctl_insert_altq_node(struct pf_altq_node **root, 191 const struct pf_altq altq, const struct queue_stats qstats) 192 { 193 struct pf_altq_node *node; 194 195 node = calloc(1, sizeof(struct pf_altq_node)); 196 if (node == NULL) 197 err(1, "pfctl_insert_altq_node: calloc"); 198 memcpy(&node->altq, &altq, sizeof(struct pf_altq)); 199 memcpy(&node->qstats, &qstats, sizeof(qstats)); 200 node->next = node->children = NULL; 201 202 if (*root == NULL) 203 *root = node; 204 else if (!altq.parent[0]) { 205 struct pf_altq_node *prev = *root; 206 207 while (prev->next != NULL) 208 prev = prev->next; 209 prev->next = node; 210 } else { 211 struct pf_altq_node *parent; 212 213 parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname); 214 if (parent == NULL) 215 errx(1, "parent %s not found", altq.parent); 216 if (parent->children == NULL) 217 parent->children = node; 218 else { 219 struct pf_altq_node *prev = parent->children; 220 221 while (prev->next != NULL) 222 prev = prev->next; 223 prev->next = node; 224 } 225 } 226 update_avg(node); 227 } 228 229 struct pf_altq_node * 230 pfctl_find_altq_node(struct pf_altq_node *root, const char *qname, 231 const char *ifname) 232 { 233 struct pf_altq_node *node, *child; 234 235 for (node = root; node != NULL; node = node->next) { 236 if (!strcmp(node->altq.qname, qname) 237 && !(strcmp(node->altq.ifname, ifname))) 238 return (node); 239 if (node->children != NULL) { 240 child = pfctl_find_altq_node(node->children, qname, 241 ifname); 242 if (child != NULL) 243 return (child); 244 } 245 } 246 return (NULL); 247 } 248 249 void 250 pfctl_print_altq_node(int dev, const struct pf_altq_node *node, 251 unsigned int level, int opts) 252 { 253 const struct pf_altq_node *child; 254 255 if (node == NULL) 256 return; 257 258 print_altq(&node->altq, level, NULL, NULL); 259 260 if (node->children != NULL) { 261 printf("{"); 262 for (child = node->children; child != NULL; 263 child = child->next) { 264 printf("%s", child->altq.qname); 265 if (child->next != NULL) 266 printf(", "); 267 } 268 printf("}"); 269 } 270 printf("\n"); 271 272 if (opts & PF_OPT_VERBOSE) 273 pfctl_print_altq_nodestat(dev, node); 274 275 if (opts & PF_OPT_DEBUG) 276 printf(" [ qid=%u ifname=%s ifbandwidth=%s ]\n", 277 node->altq.qid, node->altq.ifname, 278 rate2str((double)(node->altq.ifbandwidth))); 279 280 for (child = node->children; child != NULL; 281 child = child->next) 282 pfctl_print_altq_node(dev, child, level + 1, opts); 283 } 284 285 void 286 pfctl_print_altq_nodestat(int dev __unused, const struct pf_altq_node *a) 287 { 288 if (a->altq.qid == 0) 289 return; 290 if (a->altq.local_flags & PFALTQ_FLAG_IF_REMOVED) 291 return; 292 293 switch (a->altq.scheduler) { 294 case ALTQT_CBQ: 295 print_cbqstats(a->qstats); 296 break; 297 case ALTQT_PRIQ: 298 print_priqstats(a->qstats); 299 break; 300 case ALTQT_HFSC: 301 print_hfscstats(a->qstats); 302 break; 303 case ALTQT_FAIRQ: 304 print_fairqstats(a->qstats); 305 break; 306 } 307 } 308 309 void 310 print_cbqstats(struct queue_stats cur) 311 { 312 printf(" [ pkts: %10llu bytes: %10llu " 313 "dropped pkts: %6llu bytes: %6llu ]\n", 314 (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets, 315 (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes, 316 (unsigned long long)cur.data.cbq_stats.drop_cnt.packets, 317 (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes); 318 printf(" [ qlength: %3d/%3d borrows: %6u suspends: %6u ]\n", 319 cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax, 320 cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays); 321 322 if (cur.avgn < 2) 323 return; 324 325 printf(" [ measured: %7.1f packets/s, %s/s ]\n", 326 cur.avg_packets / STAT_INTERVAL, 327 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 328 } 329 330 void 331 print_priqstats(struct queue_stats cur) 332 { 333 printf(" [ pkts: %10llu bytes: %10llu " 334 "dropped pkts: %6llu bytes: %6llu ]\n", 335 (unsigned long long)cur.data.priq_stats.xmitcnt.packets, 336 (unsigned long long)cur.data.priq_stats.xmitcnt.bytes, 337 (unsigned long long)cur.data.priq_stats.dropcnt.packets, 338 (unsigned long long)cur.data.priq_stats.dropcnt.bytes); 339 printf(" [ qlength: %3d/%3d ]\n", 340 cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit); 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_hfscstats(struct queue_stats cur) 352 { 353 printf(" [ pkts: %10llu bytes: %10llu " 354 "dropped pkts: %6llu bytes: %6llu ]\n", 355 (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets, 356 (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes, 357 (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets, 358 (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes); 359 printf(" [ qlength: %3d/%3d ]\n", 360 cur.data.hfsc_stats.qlength, cur.data.hfsc_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_fairqstats(struct queue_stats cur) 372 { 373 printf(" [ pkts: %10llu bytes: %10llu " 374 "dropped pkts: %6llu bytes: %6llu ]\n", 375 (unsigned long long)cur.data.fairq_stats.xmit_cnt.packets, 376 (unsigned long long)cur.data.fairq_stats.xmit_cnt.bytes, 377 (unsigned long long)cur.data.fairq_stats.drop_cnt.packets, 378 (unsigned long long)cur.data.fairq_stats.drop_cnt.bytes); 379 printf(" [ qlength: %3d/%3d ]\n", 380 cur.data.fairq_stats.qlength, cur.data.fairq_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 391 void 392 pfctl_free_altq_node(struct pf_altq_node *node) 393 { 394 while (node != NULL) { 395 struct pf_altq_node *prev; 396 397 if (node->children != NULL) 398 pfctl_free_altq_node(node->children); 399 prev = node; 400 node = node->next; 401 free(prev); 402 } 403 } 404 405 void 406 update_avg(struct pf_altq_node *a) 407 { 408 struct queue_stats *qs; 409 u_int64_t b, p; 410 int n; 411 412 if (a->altq.qid == 0) 413 return; 414 415 qs = &a->qstats; 416 n = qs->avgn; 417 418 switch (a->altq.scheduler) { 419 case ALTQT_CBQ: 420 b = qs->data.cbq_stats.xmit_cnt.bytes; 421 p = qs->data.cbq_stats.xmit_cnt.packets; 422 break; 423 case ALTQT_PRIQ: 424 b = qs->data.priq_stats.xmitcnt.bytes; 425 p = qs->data.priq_stats.xmitcnt.packets; 426 break; 427 case ALTQT_HFSC: 428 b = qs->data.hfsc_stats.xmit_cnt.bytes; 429 p = qs->data.hfsc_stats.xmit_cnt.packets; 430 break; 431 case ALTQT_FAIRQ: 432 b = qs->data.fairq_stats.xmit_cnt.bytes; 433 p = qs->data.fairq_stats.xmit_cnt.packets; 434 break; 435 default: 436 b = 0; 437 p = 0; 438 break; 439 } 440 441 if (n == 0) { 442 qs->prev_bytes = b; 443 qs->prev_packets = p; 444 qs->avgn++; 445 return; 446 } 447 448 if (b >= qs->prev_bytes) 449 qs->avg_bytes = ((qs->avg_bytes * (n - 1)) + 450 (b - qs->prev_bytes)) / n; 451 452 if (p >= qs->prev_packets) 453 qs->avg_packets = ((qs->avg_packets * (n - 1)) + 454 (p - qs->prev_packets)) / n; 455 456 qs->prev_bytes = b; 457 qs->prev_packets = p; 458 if (n < AVGN_MAX) 459 qs->avgn++; 460 } 461