pfctl_qstats.c revision 127082
117706Sjulian/* $OpenBSD: pfctl_qstats.c,v 1.24 2003/07/31 09:46:08 kjc Exp $ */ 217706Sjulian 317706Sjulian/* 417706Sjulian * Copyright (c) Henning Brauer <henning@openbsd.org> 517706Sjulian * 617706Sjulian * Permission to use, copy, modify, and distribute this software for any 717706Sjulian * purpose with or without fee is hereby granted, provided that the above 817706Sjulian * copyright notice and this permission notice appear in all copies. 917706Sjulian * 1017706Sjulian * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1117706Sjulian * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1217706Sjulian * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13165967Simp * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1417706Sjulian * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1517706Sjulian * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1617706Sjulian * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1717706Sjulian */ 1817706Sjulian 1917706Sjulian#include <sys/cdefs.h> 2049439Sdeischen__FBSDID("$FreeBSD: head/contrib/pf/pfctl/pfctl_qstats.c 127082 2004-03-16 17:24:06Z obrien $"); 2117706Sjulian 2217706Sjulian#include <sys/types.h> 2317706Sjulian#include <sys/ioctl.h> 2417706Sjulian#include <sys/socket.h> 2517706Sjulian 2617706Sjulian#include <net/if.h> 2717706Sjulian#include <netinet/in.h> 2817706Sjulian#include <net/pfvar.h> 2950476Speter#include <arpa/inet.h> 3017706Sjulian 31174112Sdeischen#include <err.h> 32174112Sdeischen#include <stdio.h> 3317706Sjulian#include <stdlib.h> 3417706Sjulian#include <string.h> 3517706Sjulian#include <unistd.h> 3617706Sjulian 37174112Sdeischen#include <altq/altq.h> 38103388Smini#include <altq/altq_cbq.h> 3917706Sjulian#include <altq/altq_priq.h> 40156611Sdeischen#include <altq/altq_hfsc.h> 41156611Sdeischen 42156611Sdeischen#include "pfctl.h" 4375369Sdeischen#include "pfctl_parser.h" 4471581Sdeischen 4571581Sdeischenunion class_stats { 4671581Sdeischen class_stats_t cbq_stats; 4717706Sjulian struct priq_classstats priq_stats; 4817706Sjulian struct hfsc_classstats hfsc_stats; 4917706Sjulian}; 5022315Sjulian 5122315Sjulian#define AVGN_MAX 8 5222315Sjulian#define STAT_INTERVAL 5 5322315Sjulian 5422315Sjulianstruct queue_stats { 5522315Sjulian union class_stats data; 5622315Sjulian int avgn; 57113658Sdeischen double avg_bytes; 58113658Sdeischen double avg_packets; 59117715Sdeischen u_int64_t prev_bytes; 60141822Sdeischen u_int64_t prev_packets; 6122315Sjulian}; 6222315Sjulian 6317706Sjulianstruct pf_altq_node { 6417706Sjulian struct pf_altq altq; 6517706Sjulian struct pf_altq_node *next; 6617706Sjulian struct pf_altq_node *children; 6717706Sjulian 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, int opts, int verbose2) 88{ 89 struct pf_altq_node *root = NULL, *node; 90 91#ifdef __FreeBSD__ 92 if (!altqsupport) 93 return (-1); 94#endif 95 if (pfctl_update_qstats(dev, &root)) 96 return (-1); 97 98 for (node = root; node != NULL; node = node->next) 99 pfctl_print_altq_node(dev, node, 0, opts); 100 101 while (verbose2) { 102 printf("\n"); 103 fflush(stdout); 104 sleep(STAT_INTERVAL); 105 if (pfctl_update_qstats(dev, &root)) 106 return (-1); 107 for (node = root; node != NULL; node = node->next) 108 pfctl_print_altq_node(dev, node, 0, opts); 109 } 110 pfctl_free_altq_node(root); 111 return (0); 112} 113 114int 115pfctl_update_qstats(int dev, struct pf_altq_node **root) 116{ 117 struct pf_altq_node *node; 118 struct pfioc_altq pa; 119 struct pfioc_qstats pq; 120 u_int32_t mnr, nr; 121 struct queue_stats qstats; 122 static u_int32_t last_ticket; 123 124 memset(&pa, 0, sizeof(pa)); 125 memset(&pq, 0, sizeof(pq)); 126 memset(&qstats, 0, sizeof(qstats)); 127 if (ioctl(dev, DIOCGETALTQS, &pa)) { 128 warn("DIOCGETALTQS"); 129 return (-1); 130 } 131 132 /* if a new set is found, start over */ 133 if (pa.ticket != last_ticket && *root != NULL) { 134 pfctl_free_altq_node(*root); 135 *root = NULL; 136 } 137 last_ticket = pa.ticket; 138 139 mnr = pa.nr; 140 for (nr = 0; nr < mnr; ++nr) { 141 pa.nr = nr; 142 if (ioctl(dev, DIOCGETALTQ, &pa)) { 143 warn("DIOCGETALTQ"); 144 return (-1); 145 } 146 if (pa.altq.qid > 0) { 147 pq.nr = nr; 148 pq.ticket = pa.ticket; 149 pq.buf = &qstats.data; 150 pq.nbytes = sizeof(qstats.data); 151 if (ioctl(dev, DIOCGETQSTATS, &pq)) { 152 warn("DIOCGETQSTATS"); 153 return (-1); 154 } 155 if ((node = pfctl_find_altq_node(*root, pa.altq.qname, 156 pa.altq.ifname)) != NULL) { 157 memcpy(&node->qstats.data, &qstats.data, 158 sizeof(qstats.data)); 159 update_avg(node); 160 } else { 161 pfctl_insert_altq_node(root, pa.altq, qstats); 162 } 163 } 164 } 165 return (0); 166} 167 168void 169pfctl_insert_altq_node(struct pf_altq_node **root, 170 const struct pf_altq altq, const struct queue_stats qstats) 171{ 172 struct pf_altq_node *node; 173 174 node = calloc(1, sizeof(struct pf_altq_node)); 175 if (node == NULL) 176 err(1, "pfctl_insert_altq_node: calloc"); 177 memcpy(&node->altq, &altq, sizeof(struct pf_altq)); 178 memcpy(&node->qstats, &qstats, sizeof(qstats)); 179 node->next = node->children = NULL; 180 181 if (*root == NULL) 182 *root = node; 183 else if (!altq.parent[0]) { 184 struct pf_altq_node *prev = *root; 185 186 while (prev->next != NULL) 187 prev = prev->next; 188 prev->next = node; 189 } else { 190 struct pf_altq_node *parent; 191 192 parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname); 193 if (parent == NULL) 194 errx(1, "parent %s not found", altq.parent); 195 if (parent->children == NULL) 196 parent->children = node; 197 else { 198 struct pf_altq_node *prev = parent->children; 199 200 while (prev->next != NULL) 201 prev = prev->next; 202 prev->next = node; 203 } 204 } 205 update_avg(node); 206} 207 208struct pf_altq_node * 209pfctl_find_altq_node(struct pf_altq_node *root, const char *qname, 210 const char *ifname) 211{ 212 struct pf_altq_node *node, *child; 213 214 for (node = root; node != NULL; node = node->next) { 215 if (!strcmp(node->altq.qname, qname) 216 && !(strcmp(node->altq.ifname, ifname))) 217 return (node); 218 if (node->children != NULL) { 219 child = pfctl_find_altq_node(node->children, qname, 220 ifname); 221 if (child != NULL) 222 return (child); 223 } 224 } 225 return (NULL); 226} 227 228void 229pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level, 230 int opts) 231{ 232 const struct pf_altq_node *child; 233 234 if (node == NULL) 235 return; 236 237 print_altq(&node->altq, level, NULL, NULL); 238 239 if (node->children != NULL) { 240 printf("{"); 241 for (child = node->children; child != NULL; 242 child = child->next) { 243 printf("%s", child->altq.qname); 244 if (child->next != NULL) 245 printf(", "); 246 } 247 printf("}"); 248 } 249 printf("\n"); 250 251 if (opts & PF_OPT_VERBOSE) 252 pfctl_print_altq_nodestat(dev, node); 253 254 if (opts & PF_OPT_DEBUG) 255 printf(" [ qid=%u ifname=%s ifbandwidth=%s ]\n", node->altq.qid, 256 node->altq.ifname, rate2str((double)(node->altq.ifbandwidth))); 257 258 for (child = node->children; child != NULL; 259 child = child->next) 260 pfctl_print_altq_node(dev, child, level+1, opts); 261} 262 263void 264pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a) 265{ 266 if (a->altq.qid == 0) 267 return; 268 269 switch (a->altq.scheduler) { 270 case ALTQT_CBQ: 271 print_cbqstats(a->qstats); 272 break; 273 case ALTQT_PRIQ: 274 print_priqstats(a->qstats); 275 break; 276 case ALTQT_HFSC: 277 print_hfscstats(a->qstats); 278 break; 279 } 280} 281 282void 283print_cbqstats(struct queue_stats cur) 284{ 285 printf(" [ pkts: %10llu bytes: %10llu " 286 "dropped pkts: %6llu bytes: %6llu ]\n", 287 (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets, 288 (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes, 289 (unsigned long long)cur.data.cbq_stats.drop_cnt.packets, 290 (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes); 291 printf(" [ qlength: %3d/%3d borrows: %6u suspends: %6u ]\n", 292 cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax, 293 cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays); 294 295 if (cur.avgn < 2) 296 return; 297 298 printf(" [ measured: %7.1f packets/s, %s/s ]\n", 299 cur.avg_packets / STAT_INTERVAL, 300 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 301} 302 303void 304print_priqstats(struct queue_stats cur) 305{ 306 printf(" [ pkts: %10llu bytes: %10llu " 307 "dropped pkts: %6llu bytes: %6llu ]\n", 308 (unsigned long long)cur.data.priq_stats.xmitcnt.packets, 309 (unsigned long long)cur.data.priq_stats.xmitcnt.bytes, 310 (unsigned long long)cur.data.priq_stats.dropcnt.packets, 311 (unsigned long long)cur.data.priq_stats.dropcnt.bytes); 312 printf(" [ qlength: %3d/%3d ]\n", 313 cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit); 314 315 if (cur.avgn < 2) 316 return; 317 318 printf(" [ measured: %7.1f packets/s, %s/s ]\n", 319 cur.avg_packets / STAT_INTERVAL, 320 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 321} 322 323void 324print_hfscstats(struct queue_stats cur) 325{ 326 printf(" [ pkts: %10llu bytes: %10llu " 327 "dropped pkts: %6llu bytes: %6llu ]\n", 328 (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets, 329 (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes, 330 (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets, 331 (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes); 332 printf(" [ qlength: %3d/%3d ]\n", 333 cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit); 334 335 if (cur.avgn < 2) 336 return; 337 338 printf(" [ measured: %7.1f packets/s, %s/s ]\n", 339 cur.avg_packets / STAT_INTERVAL, 340 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 341} 342 343void 344pfctl_free_altq_node(struct pf_altq_node *node) 345{ 346 while (node != NULL) { 347 struct pf_altq_node *prev; 348 349 if (node->children != NULL) 350 pfctl_free_altq_node(node->children); 351 prev = node; 352 node = node->next; 353 free(prev); 354 } 355} 356 357void 358update_avg(struct pf_altq_node *a) 359{ 360 struct queue_stats *qs; 361 u_int64_t b, p; 362 int n; 363 364 if (a->altq.qid == 0) 365 return; 366 367 qs = &a->qstats; 368 n = qs->avgn; 369 370 switch (a->altq.scheduler) { 371 case ALTQT_CBQ: 372 b = qs->data.cbq_stats.xmit_cnt.bytes; 373 p = qs->data.cbq_stats.xmit_cnt.packets; 374 break; 375 case ALTQT_PRIQ: 376 b = qs->data.priq_stats.xmitcnt.bytes; 377 p = qs->data.priq_stats.xmitcnt.packets; 378 break; 379 case ALTQT_HFSC: 380 b = qs->data.hfsc_stats.xmit_cnt.bytes; 381 p = qs->data.hfsc_stats.xmit_cnt.packets; 382 break; 383 default: 384 b = 0; 385 p = 0; 386 break; 387 } 388 389 if (n == 0) { 390 qs->prev_bytes = b; 391 qs->prev_packets = p; 392 qs->avgn++; 393 return; 394 } 395 396 if (b >= qs->prev_bytes) 397 qs->avg_bytes = ((qs->avg_bytes * (n - 1)) + 398 (b - qs->prev_bytes)) / n; 399 400 if (p >= qs->prev_packets) 401 qs->avg_packets = ((qs->avg_packets * (n - 1)) + 402 (p - qs->prev_packets)) / n; 403 404 qs->prev_bytes = b; 405 qs->prev_packets = p; 406 if (n < AVGN_MAX) 407 qs->avgn++; 408} 409