1 /* $OpenBSD: pfctl_qstats.c,v 1.29 2004/03/15 15:25:44 dhartmei Exp $ */ 2 /* $DragonFly: src/usr.sbin/pfctl/pfctl_qstats.c,v 1.3 2008/04/06 18:58:14 dillon Exp $ */ 3 4 /* 5 * Copyright (c) Henning Brauer <henning@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/types.h> 21 #include <sys/ioctl.h> 22 #include <sys/socket.h> 23 24 #include <net/if.h> 25 #include <netinet/in.h> 26 #include <net/pf/pfvar.h> 27 #include <arpa/inet.h> 28 29 #include <err.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 35 #include <net/altq/altq.h> 36 #include <net/altq/altq_cbq.h> 37 #include <net/altq/altq_priq.h> 38 #include <net/altq/altq_hfsc.h> 39 #include <net/altq/altq_fairq.h> 40 41 #include "pfctl.h" 42 #include "pfctl_parser.h" 43 44 union class_stats { 45 class_stats_t cbq_stats; 46 struct priq_classstats priq_stats; 47 struct hfsc_classstats hfsc_stats; 48 struct fairq_classstats fairq_stats; 49 }; 50 51 #define AVGN_MAX 8 52 #define STAT_INTERVAL 5 53 54 struct queue_stats { 55 union class_stats data; 56 int avgn; 57 double avg_bytes; 58 double avg_packets; 59 u_int64_t prev_bytes; 60 u_int64_t prev_packets; 61 }; 62 63 struct pf_altq_node { 64 struct pf_altq altq; 65 struct pf_altq_node *next; 66 struct pf_altq_node *children; 67 struct queue_stats qstats; 68 }; 69 70 int pfctl_update_qstats(int, struct pf_altq_node **); 71 void pfctl_insert_altq_node(struct pf_altq_node **, 72 const struct pf_altq, const struct queue_stats); 73 struct pf_altq_node *pfctl_find_altq_node(struct pf_altq_node *, 74 const char *, const char *); 75 void pfctl_print_altq_node(int, const struct pf_altq_node *, 76 unsigned, int); 77 void print_cbqstats(struct queue_stats); 78 void print_priqstats(struct queue_stats); 79 void print_hfscstats(struct queue_stats); 80 void print_fairqstats(struct queue_stats); 81 void pfctl_free_altq_node(struct pf_altq_node *); 82 void pfctl_print_altq_nodestat(int, 83 const struct pf_altq_node *); 84 85 void update_avg(struct pf_altq_node *); 86 87 int 88 pfctl_show_altq(int dev, const char *iface, int opts, int verbose2) 89 { 90 struct pf_altq_node *root = NULL, *node; 91 int nodes, dotitle = (opts & PF_OPT_SHOWALL); 92 93 94 if ((nodes = pfctl_update_qstats(dev, &root)) < 0) 95 return (-1); 96 97 for (node = root; node != NULL; node = node->next) { 98 if (iface != NULL && strcmp(node->altq.ifname, iface)) 99 continue; 100 if (dotitle) { 101 pfctl_print_title("ALTQ:"); 102 dotitle = 0; 103 } 104 pfctl_print_altq_node(dev, node, 0, opts); 105 } 106 107 while (verbose2) { 108 printf("\n"); 109 fflush(stdout); 110 sleep(STAT_INTERVAL); 111 if (pfctl_update_qstats(dev, &root) == -1) 112 return (-1); 113 for (node = root; node != NULL; node = node->next) { 114 if (iface != NULL && strcmp(node->altq.ifname, iface)) 115 continue; 116 pfctl_print_altq_node(dev, node, 0, opts); 117 } 118 } 119 pfctl_free_altq_node(root); 120 return (0); 121 } 122 123 int 124 pfctl_update_qstats(int dev, struct pf_altq_node **root) 125 { 126 struct pf_altq_node *node; 127 struct pfioc_altq pa; 128 struct pfioc_qstats pq; 129 u_int32_t mnr, nr; 130 struct queue_stats qstats; 131 static u_int32_t last_ticket; 132 133 memset(&pa, 0, sizeof(pa)); 134 memset(&pq, 0, sizeof(pq)); 135 memset(&qstats, 0, sizeof(qstats)); 136 if (ioctl(dev, DIOCGETALTQS, &pa)) { 137 warn("DIOCGETALTQS"); 138 return (-1); 139 } 140 141 /* if a new set is found, start over */ 142 if (pa.ticket != last_ticket && *root != NULL) { 143 pfctl_free_altq_node(*root); 144 *root = NULL; 145 } 146 last_ticket = pa.ticket; 147 148 mnr = pa.nr; 149 for (nr = 0; nr < mnr; ++nr) { 150 pa.nr = nr; 151 if (ioctl(dev, DIOCGETALTQ, &pa)) { 152 warn("DIOCGETALTQ"); 153 return (-1); 154 } 155 if (pa.altq.qid > 0) { 156 pq.nr = nr; 157 pq.ticket = pa.ticket; 158 pq.buf = &qstats.data; 159 pq.nbytes = sizeof(qstats.data); 160 if (ioctl(dev, DIOCGETQSTATS, &pq)) { 161 warn("DIOCGETQSTATS"); 162 return (-1); 163 } 164 if ((node = pfctl_find_altq_node(*root, pa.altq.qname, 165 pa.altq.ifname)) != NULL) { 166 memcpy(&node->qstats.data, &qstats.data, 167 sizeof(qstats.data)); 168 update_avg(node); 169 } else { 170 pfctl_insert_altq_node(root, pa.altq, qstats); 171 } 172 } 173 } 174 return (mnr); 175 } 176 177 void 178 pfctl_insert_altq_node(struct pf_altq_node **root, 179 const struct pf_altq altq, const struct queue_stats qstats) 180 { 181 struct pf_altq_node *node; 182 183 node = calloc(1, sizeof(struct pf_altq_node)); 184 if (node == NULL) 185 err(1, "pfctl_insert_altq_node: calloc"); 186 memcpy(&node->altq, &altq, sizeof(struct pf_altq)); 187 memcpy(&node->qstats, &qstats, sizeof(qstats)); 188 node->next = node->children = NULL; 189 190 if (*root == NULL) 191 *root = node; 192 else if (!altq.parent[0]) { 193 struct pf_altq_node *prev = *root; 194 195 while (prev->next != NULL) 196 prev = prev->next; 197 prev->next = node; 198 } else { 199 struct pf_altq_node *parent; 200 201 parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname); 202 if (parent == NULL) 203 errx(1, "parent %s not found", altq.parent); 204 if (parent->children == NULL) 205 parent->children = node; 206 else { 207 struct pf_altq_node *prev = parent->children; 208 209 while (prev->next != NULL) 210 prev = prev->next; 211 prev->next = node; 212 } 213 } 214 update_avg(node); 215 } 216 217 struct pf_altq_node * 218 pfctl_find_altq_node(struct pf_altq_node *root, const char *qname, 219 const char *ifname) 220 { 221 struct pf_altq_node *node, *child; 222 223 for (node = root; node != NULL; node = node->next) { 224 if (!strcmp(node->altq.qname, qname) 225 && !(strcmp(node->altq.ifname, ifname))) 226 return (node); 227 if (node->children != NULL) { 228 child = pfctl_find_altq_node(node->children, qname, 229 ifname); 230 if (child != NULL) 231 return (child); 232 } 233 } 234 return (NULL); 235 } 236 237 void 238 pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level, 239 int opts) 240 { 241 const struct pf_altq_node *child; 242 243 if (node == NULL) 244 return; 245 246 print_altq(&node->altq, level, NULL, NULL); 247 248 if (node->children != NULL) { 249 printf("{"); 250 for (child = node->children; child != NULL; 251 child = child->next) { 252 printf("%s", child->altq.qname); 253 if (child->next != NULL) 254 printf(", "); 255 } 256 printf("}"); 257 } 258 printf("\n"); 259 260 if (opts & PF_OPT_VERBOSE) 261 pfctl_print_altq_nodestat(dev, node); 262 263 if (opts & PF_OPT_DEBUG) 264 printf(" [ qid=%u ifname=%s ifbandwidth=%s ]\n", 265 node->altq.qid, node->altq.ifname, 266 rate2str((double)(node->altq.ifbandwidth))); 267 268 for (child = node->children; child != NULL; 269 child = child->next) 270 pfctl_print_altq_node(dev, child, level + 1, opts); 271 } 272 273 void 274 pfctl_print_altq_nodestat(int dev __unused, const struct pf_altq_node *a) 275 { 276 if (a->altq.qid == 0) 277 return; 278 279 switch (a->altq.scheduler) { 280 case ALTQT_CBQ: 281 print_cbqstats(a->qstats); 282 break; 283 case ALTQT_PRIQ: 284 print_priqstats(a->qstats); 285 break; 286 case ALTQT_HFSC: 287 print_hfscstats(a->qstats); 288 break; 289 case ALTQT_FAIRQ: 290 print_fairqstats(a->qstats); 291 break; 292 } 293 } 294 295 void 296 print_cbqstats(struct queue_stats cur) 297 { 298 printf(" [ pkts: %10llu bytes: %10llu " 299 "dropped pkts: %6llu bytes: %6llu ]\n", 300 (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets, 301 (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes, 302 (unsigned long long)cur.data.cbq_stats.drop_cnt.packets, 303 (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes); 304 printf(" [ qlength: %3d/%3d borrows: %6u suspends: %6u ]\n", 305 cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax, 306 cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays); 307 308 if (cur.avgn < 2) 309 return; 310 311 printf(" [ measured: %7.1f packets/s, %s/s ]\n", 312 cur.avg_packets / STAT_INTERVAL, 313 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 314 } 315 316 void 317 print_priqstats(struct queue_stats cur) 318 { 319 printf(" [ pkts: %10llu bytes: %10llu " 320 "dropped pkts: %6llu bytes: %6llu ]\n", 321 (unsigned long long)cur.data.priq_stats.xmitcnt.packets, 322 (unsigned long long)cur.data.priq_stats.xmitcnt.bytes, 323 (unsigned long long)cur.data.priq_stats.dropcnt.packets, 324 (unsigned long long)cur.data.priq_stats.dropcnt.bytes); 325 printf(" [ qlength: %3d/%3d ]\n", 326 cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit); 327 328 if (cur.avgn < 2) 329 return; 330 331 printf(" [ measured: %7.1f packets/s, %s/s ]\n", 332 cur.avg_packets / STAT_INTERVAL, 333 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 334 } 335 336 void 337 print_hfscstats(struct queue_stats cur) 338 { 339 printf(" [ pkts: %10llu bytes: %10llu " 340 "dropped pkts: %6llu bytes: %6llu ]\n", 341 (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets, 342 (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes, 343 (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets, 344 (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes); 345 printf(" [ qlength: %3d/%3d ]\n", 346 cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit); 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_fairqstats(struct queue_stats cur) 358 { 359 printf(" [ pkts: %10llu bytes: %10llu " 360 "dropped pkts: %6llu bytes: %6llu ]\n", 361 (unsigned long long)cur.data.fairq_stats.xmit_cnt.packets, 362 (unsigned long long)cur.data.fairq_stats.xmit_cnt.bytes, 363 (unsigned long long)cur.data.fairq_stats.drop_cnt.packets, 364 (unsigned long long)cur.data.fairq_stats.drop_cnt.bytes); 365 printf(" [ qlength: %3d/%3d ]\n", 366 cur.data.fairq_stats.qlength, cur.data.fairq_stats.qlimit); 367 368 if (cur.avgn < 2) 369 return; 370 371 printf(" [ measured: %7.1f packets/s, %s/s ]\n", 372 cur.avg_packets / STAT_INTERVAL, 373 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 374 } 375 376 377 void 378 pfctl_free_altq_node(struct pf_altq_node *node) 379 { 380 while (node != NULL) { 381 struct pf_altq_node *prev; 382 383 if (node->children != NULL) 384 pfctl_free_altq_node(node->children); 385 prev = node; 386 node = node->next; 387 free(prev); 388 } 389 } 390 391 void 392 update_avg(struct pf_altq_node *a) 393 { 394 struct queue_stats *qs; 395 u_int64_t b, p; 396 int n; 397 398 if (a->altq.qid == 0) 399 return; 400 401 qs = &a->qstats; 402 n = qs->avgn; 403 404 switch (a->altq.scheduler) { 405 case ALTQT_CBQ: 406 b = qs->data.cbq_stats.xmit_cnt.bytes; 407 p = qs->data.cbq_stats.xmit_cnt.packets; 408 break; 409 case ALTQT_PRIQ: 410 b = qs->data.priq_stats.xmitcnt.bytes; 411 p = qs->data.priq_stats.xmitcnt.packets; 412 break; 413 case ALTQT_HFSC: 414 b = qs->data.hfsc_stats.xmit_cnt.bytes; 415 p = qs->data.hfsc_stats.xmit_cnt.packets; 416 break; 417 case ALTQT_FAIRQ: 418 b = qs->data.fairq_stats.xmit_cnt.bytes; 419 p = qs->data.fairq_stats.xmit_cnt.packets; 420 break; 421 default: 422 b = 0; 423 p = 0; 424 break; 425 } 426 427 if (n == 0) { 428 qs->prev_bytes = b; 429 qs->prev_packets = p; 430 qs->avgn++; 431 return; 432 } 433 434 if (b >= qs->prev_bytes) 435 qs->avg_bytes = ((qs->avg_bytes * (n - 1)) + 436 (b - qs->prev_bytes)) / n; 437 438 if (p >= qs->prev_packets) 439 qs->avg_packets = ((qs->avg_packets * (n - 1)) + 440 (p - qs->prev_packets)) / n; 441 442 qs->prev_bytes = b; 443 qs->prev_packets = p; 444 if (n < AVGN_MAX) 445 qs->avgn++; 446 } 447