pfctl_qstats.c revision 127024
1/* $FreeBSD: head/contrib/pf/pfctl/pfctl_qstats.c 127024 2004-03-15 13:41:17Z mlaier $ */ 2/* $OpenBSD: pfctl_qstats.c,v 1.24 2003/07/31 09:46:08 kjc 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/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 <altq/altq.h> 36#include <altq/altq_cbq.h> 37#include <altq/altq_priq.h> 38#include <altq/altq_hfsc.h> 39 40#include "pfctl.h" 41#include "pfctl_parser.h" 42 43union class_stats { 44 class_stats_t cbq_stats; 45 struct priq_classstats priq_stats; 46 struct hfsc_classstats hfsc_stats; 47}; 48 49#define AVGN_MAX 8 50#define STAT_INTERVAL 5 51 52struct queue_stats { 53 union class_stats data; 54 int avgn; 55 double avg_bytes; 56 double avg_packets; 57 u_int64_t prev_bytes; 58 u_int64_t prev_packets; 59}; 60 61struct pf_altq_node { 62 struct pf_altq altq; 63 struct pf_altq_node *next; 64 struct pf_altq_node *children; 65 struct queue_stats qstats; 66}; 67 68int pfctl_update_qstats(int, struct pf_altq_node **); 69void pfctl_insert_altq_node(struct pf_altq_node **, 70 const struct pf_altq, const struct queue_stats); 71struct pf_altq_node *pfctl_find_altq_node(struct pf_altq_node *, 72 const char *, const char *); 73void pfctl_print_altq_node(int, const struct pf_altq_node *, 74 unsigned, int); 75void print_cbqstats(struct queue_stats); 76void print_priqstats(struct queue_stats); 77void print_hfscstats(struct queue_stats); 78void pfctl_free_altq_node(struct pf_altq_node *); 79void pfctl_print_altq_nodestat(int, 80 const struct pf_altq_node *); 81 82void update_avg(struct pf_altq_node *); 83 84int 85pfctl_show_altq(int dev, int opts, int verbose2) 86{ 87 struct pf_altq_node *root = NULL, *node; 88 89#ifdef __FreeBSD__ 90 if (!altqsupport) 91 return (-1); 92#endif 93 if (pfctl_update_qstats(dev, &root)) 94 return (-1); 95 96 for (node = root; node != NULL; node = node->next) 97 pfctl_print_altq_node(dev, node, 0, opts); 98 99 while (verbose2) { 100 printf("\n"); 101 fflush(stdout); 102 sleep(STAT_INTERVAL); 103 if (pfctl_update_qstats(dev, &root)) 104 return (-1); 105 for (node = root; node != NULL; node = node->next) 106 pfctl_print_altq_node(dev, node, 0, opts); 107 } 108 pfctl_free_altq_node(root); 109 return (0); 110} 111 112int 113pfctl_update_qstats(int dev, struct pf_altq_node **root) 114{ 115 struct pf_altq_node *node; 116 struct pfioc_altq pa; 117 struct pfioc_qstats pq; 118 u_int32_t mnr, nr; 119 struct queue_stats qstats; 120 static u_int32_t last_ticket; 121 122 memset(&pa, 0, sizeof(pa)); 123 memset(&pq, 0, sizeof(pq)); 124 memset(&qstats, 0, sizeof(qstats)); 125 if (ioctl(dev, DIOCGETALTQS, &pa)) { 126 warn("DIOCGETALTQS"); 127 return (-1); 128 } 129 130 /* if a new set is found, start over */ 131 if (pa.ticket != last_ticket && *root != NULL) { 132 pfctl_free_altq_node(*root); 133 *root = NULL; 134 } 135 last_ticket = pa.ticket; 136 137 mnr = pa.nr; 138 for (nr = 0; nr < mnr; ++nr) { 139 pa.nr = nr; 140 if (ioctl(dev, DIOCGETALTQ, &pa)) { 141 warn("DIOCGETALTQ"); 142 return (-1); 143 } 144 if (pa.altq.qid > 0) { 145 pq.nr = nr; 146 pq.ticket = pa.ticket; 147 pq.buf = &qstats.data; 148 pq.nbytes = sizeof(qstats.data); 149 if (ioctl(dev, DIOCGETQSTATS, &pq)) { 150 warn("DIOCGETQSTATS"); 151 return (-1); 152 } 153 if ((node = pfctl_find_altq_node(*root, pa.altq.qname, 154 pa.altq.ifname)) != NULL) { 155 memcpy(&node->qstats.data, &qstats.data, 156 sizeof(qstats.data)); 157 update_avg(node); 158 } else { 159 pfctl_insert_altq_node(root, pa.altq, qstats); 160 } 161 } 162 } 163 return (0); 164} 165 166void 167pfctl_insert_altq_node(struct pf_altq_node **root, 168 const struct pf_altq altq, const struct queue_stats qstats) 169{ 170 struct pf_altq_node *node; 171 172 node = calloc(1, sizeof(struct pf_altq_node)); 173 if (node == NULL) 174 err(1, "pfctl_insert_altq_node: calloc"); 175 memcpy(&node->altq, &altq, sizeof(struct pf_altq)); 176 memcpy(&node->qstats, &qstats, sizeof(qstats)); 177 node->next = node->children = NULL; 178 179 if (*root == NULL) 180 *root = node; 181 else if (!altq.parent[0]) { 182 struct pf_altq_node *prev = *root; 183 184 while (prev->next != NULL) 185 prev = prev->next; 186 prev->next = node; 187 } else { 188 struct pf_altq_node *parent; 189 190 parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname); 191 if (parent == NULL) 192 errx(1, "parent %s not found", altq.parent); 193 if (parent->children == NULL) 194 parent->children = node; 195 else { 196 struct pf_altq_node *prev = parent->children; 197 198 while (prev->next != NULL) 199 prev = prev->next; 200 prev->next = node; 201 } 202 } 203 update_avg(node); 204} 205 206struct pf_altq_node * 207pfctl_find_altq_node(struct pf_altq_node *root, const char *qname, 208 const char *ifname) 209{ 210 struct pf_altq_node *node, *child; 211 212 for (node = root; node != NULL; node = node->next) { 213 if (!strcmp(node->altq.qname, qname) 214 && !(strcmp(node->altq.ifname, ifname))) 215 return (node); 216 if (node->children != NULL) { 217 child = pfctl_find_altq_node(node->children, qname, 218 ifname); 219 if (child != NULL) 220 return (child); 221 } 222 } 223 return (NULL); 224} 225 226void 227pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level, 228 int opts) 229{ 230 const struct pf_altq_node *child; 231 232 if (node == NULL) 233 return; 234 235 print_altq(&node->altq, level, NULL, NULL); 236 237 if (node->children != NULL) { 238 printf("{"); 239 for (child = node->children; child != NULL; 240 child = child->next) { 241 printf("%s", child->altq.qname); 242 if (child->next != NULL) 243 printf(", "); 244 } 245 printf("}"); 246 } 247 printf("\n"); 248 249 if (opts & PF_OPT_VERBOSE) 250 pfctl_print_altq_nodestat(dev, node); 251 252 if (opts & PF_OPT_DEBUG) 253 printf(" [ qid=%u ifname=%s ifbandwidth=%s ]\n", node->altq.qid, 254 node->altq.ifname, rate2str((double)(node->altq.ifbandwidth))); 255 256 for (child = node->children; child != NULL; 257 child = child->next) 258 pfctl_print_altq_node(dev, child, level+1, opts); 259} 260 261void 262pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a) 263{ 264 if (a->altq.qid == 0) 265 return; 266 267 switch (a->altq.scheduler) { 268 case ALTQT_CBQ: 269 print_cbqstats(a->qstats); 270 break; 271 case ALTQT_PRIQ: 272 print_priqstats(a->qstats); 273 break; 274 case ALTQT_HFSC: 275 print_hfscstats(a->qstats); 276 break; 277 } 278} 279 280void 281print_cbqstats(struct queue_stats cur) 282{ 283 printf(" [ pkts: %10llu bytes: %10llu " 284 "dropped pkts: %6llu bytes: %6llu ]\n", 285 (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets, 286 (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes, 287 (unsigned long long)cur.data.cbq_stats.drop_cnt.packets, 288 (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes); 289 printf(" [ qlength: %3d/%3d borrows: %6u suspends: %6u ]\n", 290 cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax, 291 cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays); 292 293 if (cur.avgn < 2) 294 return; 295 296 printf(" [ measured: %7.1f packets/s, %s/s ]\n", 297 cur.avg_packets / STAT_INTERVAL, 298 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 299} 300 301void 302print_priqstats(struct queue_stats cur) 303{ 304 printf(" [ pkts: %10llu bytes: %10llu " 305 "dropped pkts: %6llu bytes: %6llu ]\n", 306 (unsigned long long)cur.data.priq_stats.xmitcnt.packets, 307 (unsigned long long)cur.data.priq_stats.xmitcnt.bytes, 308 (unsigned long long)cur.data.priq_stats.dropcnt.packets, 309 (unsigned long long)cur.data.priq_stats.dropcnt.bytes); 310 printf(" [ qlength: %3d/%3d ]\n", 311 cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit); 312 313 if (cur.avgn < 2) 314 return; 315 316 printf(" [ measured: %7.1f packets/s, %s/s ]\n", 317 cur.avg_packets / STAT_INTERVAL, 318 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 319} 320 321void 322print_hfscstats(struct queue_stats cur) 323{ 324 printf(" [ pkts: %10llu bytes: %10llu " 325 "dropped pkts: %6llu bytes: %6llu ]\n", 326 (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets, 327 (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes, 328 (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets, 329 (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes); 330 printf(" [ qlength: %3d/%3d ]\n", 331 cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit); 332 333 if (cur.avgn < 2) 334 return; 335 336 printf(" [ measured: %7.1f packets/s, %s/s ]\n", 337 cur.avg_packets / STAT_INTERVAL, 338 rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 339} 340 341void 342pfctl_free_altq_node(struct pf_altq_node *node) 343{ 344 while (node != NULL) { 345 struct pf_altq_node *prev; 346 347 if (node->children != NULL) 348 pfctl_free_altq_node(node->children); 349 prev = node; 350 node = node->next; 351 free(prev); 352 } 353} 354 355void 356update_avg(struct pf_altq_node *a) 357{ 358 struct queue_stats *qs; 359 u_int64_t b, p; 360 int n; 361 362 if (a->altq.qid == 0) 363 return; 364 365 qs = &a->qstats; 366 n = qs->avgn; 367 368 switch (a->altq.scheduler) { 369 case ALTQT_CBQ: 370 b = qs->data.cbq_stats.xmit_cnt.bytes; 371 p = qs->data.cbq_stats.xmit_cnt.packets; 372 break; 373 case ALTQT_PRIQ: 374 b = qs->data.priq_stats.xmitcnt.bytes; 375 p = qs->data.priq_stats.xmitcnt.packets; 376 break; 377 case ALTQT_HFSC: 378 b = qs->data.hfsc_stats.xmit_cnt.bytes; 379 p = qs->data.hfsc_stats.xmit_cnt.packets; 380 break; 381 default: 382 b = 0; 383 p = 0; 384 break; 385 } 386 387 if (n == 0) { 388 qs->prev_bytes = b; 389 qs->prev_packets = p; 390 qs->avgn++; 391 return; 392 } 393 394 if (b >= qs->prev_bytes) 395 qs->avg_bytes = ((qs->avg_bytes * (n - 1)) + 396 (b - qs->prev_bytes)) / n; 397 398 if (p >= qs->prev_packets) 399 qs->avg_packets = ((qs->avg_packets * (n - 1)) + 400 (p - qs->prev_packets)) / n; 401 402 qs->prev_bytes = b; 403 qs->prev_packets = p; 404 if (n < AVGN_MAX) 405 qs->avgn++; 406} 407