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