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