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