pfctl_qstats.c revision 130617
1/* $OpenBSD: pfctl_qstats.c,v 1.29 2004/03/15 15:25:44 dhartmei 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 130617 2004-06-16 23:39:33Z 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 for (node = root; node != NULL; node = node->next) { 101 if (iface != NULL && strcmp(node->altq.ifname, iface)) 102 continue; 103 if (dotitle) { 104 pfctl_print_title("ALTQ:"); 105 dotitle = 0; 106 } 107 pfctl_print_altq_node(dev, node, 0, opts); 108 } 109 110 while (verbose2) { 111 printf("\n"); 112 fflush(stdout); 113 sleep(STAT_INTERVAL); 114 if (pfctl_update_qstats(dev, &root) == -1) 115 return (-1); 116 for (node = root; node != NULL; node = node->next) { 117 if (iface != NULL && strcmp(node->altq.ifname, iface)) 118 continue; 119 pfctl_print_altq_node(dev, node, 0, opts); 120 } 121 } 122 pfctl_free_altq_node(root); 123 return (0); 124} 125 126int 127pfctl_update_qstats(int dev, struct pf_altq_node **root) 128{ 129 struct pf_altq_node *node; 130 struct pfioc_altq pa; 131 struct pfioc_qstats pq; 132 u_int32_t mnr, nr; 133 struct queue_stats qstats; 134 static u_int32_t last_ticket; 135 136 memset(&pa, 0, sizeof(pa)); 137 memset(&pq, 0, sizeof(pq)); 138 memset(&qstats, 0, sizeof(qstats)); 139 if (ioctl(dev, DIOCGETALTQS, &pa)) { 140 warn("DIOCGETALTQS"); 141 return (-1); 142 } 143 144 /* if a new set is found, start over */ 145 if (pa.ticket != last_ticket && *root != NULL) { 146 pfctl_free_altq_node(*root); 147 *root = NULL; 148 } 149 last_ticket = pa.ticket; 150 151 mnr = pa.nr; 152 for (nr = 0; nr < mnr; ++nr) { 153 pa.nr = nr; 154 if (ioctl(dev, DIOCGETALTQ, &pa)) { 155 warn("DIOCGETALTQ"); 156 return (-1); 157 } 158 if (pa.altq.qid > 0) { 159 pq.nr = nr; 160 pq.ticket = pa.ticket; 161 pq.buf = &qstats.data; 162 pq.nbytes = sizeof(qstats.data); 163 if (ioctl(dev, DIOCGETQSTATS, &pq)) { 164 warn("DIOCGETQSTATS"); 165 return (-1); 166 } 167 if ((node = pfctl_find_altq_node(*root, pa.altq.qname, 168 pa.altq.ifname)) != NULL) { 169 memcpy(&node->qstats.data, &qstats.data, 170 sizeof(qstats.data)); 171 update_avg(node); 172 } else { 173 pfctl_insert_altq_node(root, pa.altq, qstats); 174 } 175 } 176 } 177 return (mnr); 178} 179 180void 181pfctl_insert_altq_node(struct pf_altq_node **root, 182 const struct pf_altq altq, const struct queue_stats qstats) 183{ 184 struct pf_altq_node *node; 185 186 node = calloc(1, sizeof(struct pf_altq_node)); 187 if (node == NULL) 188 err(1, "pfctl_insert_altq_node: calloc"); 189 memcpy(&node->altq, &altq, sizeof(struct pf_altq)); 190 memcpy(&node->qstats, &qstats, sizeof(qstats)); 191 node->next = node->children = NULL; 192 193 if (*root == NULL) 194 *root = node; 195 else if (!altq.parent[0]) { 196 struct pf_altq_node *prev = *root; 197 198 while (prev->next != NULL) 199 prev = prev->next; 200 prev->next = node; 201 } else { 202 struct pf_altq_node *parent; 203 204 parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname); 205 if (parent == NULL) 206 errx(1, "parent %s not found", altq.parent); 207 if (parent->children == NULL) 208 parent->children = node; 209 else { 210 struct pf_altq_node *prev = parent->children; 211 212 while (prev->next != NULL) 213 prev = prev->next; 214 prev->next = node; 215 } 216 } 217 update_avg(node); 218} 219 220struct pf_altq_node * 221pfctl_find_altq_node(struct pf_altq_node *root, const char *qname, 222 const char *ifname) 223{ 224 struct pf_altq_node *node, *child; 225 226 for (node = root; node != NULL; node = node->next) { 227 if (!strcmp(node->altq.qname, qname) 228 && !(strcmp(node->altq.ifname, ifname))) 229 return (node); 230 if (node->children != NULL) { 231 child = pfctl_find_altq_node(node->children, qname, 232 ifname); 233 if (child != NULL) 234 return (child); 235 } 236 } 237 return (NULL); 238} 239 240void 241pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level, 242 int opts) 243{ 244 const struct pf_altq_node *child; 245 246 if (node == NULL) 247 return; 248 249 print_altq(&node->altq, level, NULL, NULL); 250 251 if (node->children != NULL) { 252 printf("{"); 253 for (child = node->children; child != NULL; 254 child = child->next) { 255 printf("%s", child->altq.qname); 256 if (child->next != NULL) 257 printf(", "); 258 } 259 printf("}"); 260 } 261 printf("\n"); 262 263 if (opts & PF_OPT_VERBOSE) 264 pfctl_print_altq_nodestat(dev, node); 265 266 if (opts & PF_OPT_DEBUG) 267 printf(" [ qid=%u ifname=%s ifbandwidth=%s ]\n", 268 node->altq.qid, node->altq.ifname, 269 rate2str((double)(node->altq.ifbandwidth))); 270 271 for (child = node->children; child != NULL; 272 child = child->next) 273 pfctl_print_altq_node(dev, child, level + 1, opts); 274} 275 276void 277pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a) 278{ 279 if (a->altq.qid == 0) 280 return; 281 282 switch (a->altq.scheduler) { 283 case ALTQT_CBQ: 284 print_cbqstats(a->qstats); 285 break; 286 case ALTQT_PRIQ: 287 print_priqstats(a->qstats); 288 break; 289 case ALTQT_HFSC: 290 print_hfscstats(a->qstats); 291 break; 292 } 293} 294 295void 296print_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 316void 317print_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 336void 337print_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 356void 357pfctl_free_altq_node(struct pf_altq_node *node) 358{ 359 while (node != NULL) { 360 struct pf_altq_node *prev; 361 362 if (node->children != NULL) 363 pfctl_free_altq_node(node->children); 364 prev = node; 365 node = node->next; 366 free(prev); 367 } 368} 369 370void 371update_avg(struct pf_altq_node *a) 372{ 373 struct queue_stats *qs; 374 u_int64_t b, p; 375 int n; 376 377 if (a->altq.qid == 0) 378 return; 379 380 qs = &a->qstats; 381 n = qs->avgn; 382 383 switch (a->altq.scheduler) { 384 case ALTQT_CBQ: 385 b = qs->data.cbq_stats.xmit_cnt.bytes; 386 p = qs->data.cbq_stats.xmit_cnt.packets; 387 break; 388 case ALTQT_PRIQ: 389 b = qs->data.priq_stats.xmitcnt.bytes; 390 p = qs->data.priq_stats.xmitcnt.packets; 391 break; 392 case ALTQT_HFSC: 393 b = qs->data.hfsc_stats.xmit_cnt.bytes; 394 p = qs->data.hfsc_stats.xmit_cnt.packets; 395 break; 396 default: 397 b = 0; 398 p = 0; 399 break; 400 } 401 402 if (n == 0) { 403 qs->prev_bytes = b; 404 qs->prev_packets = p; 405 qs->avgn++; 406 return; 407 } 408 409 if (b >= qs->prev_bytes) 410 qs->avg_bytes = ((qs->avg_bytes * (n - 1)) + 411 (b - qs->prev_bytes)) / n; 412 413 if (p >= qs->prev_packets) 414 qs->avg_packets = ((qs->avg_packets * (n - 1)) + 415 (p - qs->prev_packets)) / n; 416 417 qs->prev_bytes = b; 418 qs->prev_packets = p; 419 if (n < AVGN_MAX) 420 qs->avgn++; 421} 422