1 1.5 peter /* $NetBSD: pfctl_qstats.c,v 1.5 2006/10/12 19:59:08 peter Exp $ */ 2 1.4 yamt /* $OpenBSD: pfctl_qstats.c,v 1.30 2004/04/27 21:47:32 kjc Exp $ */ 3 1.1 itojun 4 1.1 itojun /* 5 1.1 itojun * Copyright (c) Henning Brauer <henning (at) openbsd.org> 6 1.1 itojun * 7 1.1 itojun * Permission to use, copy, modify, and distribute this software for any 8 1.1 itojun * purpose with or without fee is hereby granted, provided that the above 9 1.1 itojun * copyright notice and this permission notice appear in all copies. 10 1.1 itojun * 11 1.1 itojun * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 1.1 itojun * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 1.1 itojun * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 1.1 itojun * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 1.1 itojun * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 1.1 itojun * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 1.1 itojun * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 1.1 itojun */ 19 1.1 itojun 20 1.1 itojun #include <sys/types.h> 21 1.1 itojun #include <sys/ioctl.h> 22 1.1 itojun #include <sys/socket.h> 23 1.1 itojun 24 1.1 itojun #include <net/if.h> 25 1.1 itojun #include <netinet/in.h> 26 1.1 itojun #include <net/pfvar.h> 27 1.1 itojun #include <arpa/inet.h> 28 1.1 itojun 29 1.1 itojun #include <err.h> 30 1.1 itojun #include <stdio.h> 31 1.1 itojun #include <stdlib.h> 32 1.1 itojun #include <string.h> 33 1.1 itojun #include <unistd.h> 34 1.1 itojun 35 1.1 itojun #include <altq/altq.h> 36 1.1 itojun #include <altq/altq_cbq.h> 37 1.1 itojun #include <altq/altq_priq.h> 38 1.1 itojun #include <altq/altq_hfsc.h> 39 1.1 itojun 40 1.1 itojun #include "pfctl.h" 41 1.1 itojun #include "pfctl_parser.h" 42 1.1 itojun 43 1.1 itojun union class_stats { 44 1.1 itojun class_stats_t cbq_stats; 45 1.1 itojun struct priq_classstats priq_stats; 46 1.1 itojun struct hfsc_classstats hfsc_stats; 47 1.1 itojun }; 48 1.1 itojun 49 1.1 itojun #define AVGN_MAX 8 50 1.1 itojun #define STAT_INTERVAL 5 51 1.1 itojun 52 1.1 itojun struct queue_stats { 53 1.1 itojun union class_stats data; 54 1.1 itojun int avgn; 55 1.1 itojun double avg_bytes; 56 1.1 itojun double avg_packets; 57 1.1 itojun u_int64_t prev_bytes; 58 1.1 itojun u_int64_t prev_packets; 59 1.1 itojun }; 60 1.1 itojun 61 1.1 itojun struct pf_altq_node { 62 1.1 itojun struct pf_altq altq; 63 1.1 itojun struct pf_altq_node *next; 64 1.1 itojun struct pf_altq_node *children; 65 1.1 itojun struct queue_stats qstats; 66 1.1 itojun }; 67 1.1 itojun 68 1.1 itojun int pfctl_update_qstats(int, struct pf_altq_node **); 69 1.1 itojun void pfctl_insert_altq_node(struct pf_altq_node **, 70 1.1 itojun const struct pf_altq, const struct queue_stats); 71 1.1 itojun struct pf_altq_node *pfctl_find_altq_node(struct pf_altq_node *, 72 1.1 itojun const char *, const char *); 73 1.1 itojun void pfctl_print_altq_node(int, const struct pf_altq_node *, 74 1.1 itojun unsigned, int); 75 1.1 itojun void print_cbqstats(struct queue_stats); 76 1.1 itojun void print_priqstats(struct queue_stats); 77 1.1 itojun void print_hfscstats(struct queue_stats); 78 1.1 itojun void pfctl_free_altq_node(struct pf_altq_node *); 79 1.1 itojun void pfctl_print_altq_nodestat(int, 80 1.1 itojun const struct pf_altq_node *); 81 1.1 itojun 82 1.1 itojun void update_avg(struct pf_altq_node *); 83 1.1 itojun 84 1.1 itojun int 85 1.1 itojun pfctl_show_altq(int dev, const char *iface, int opts, int verbose2) 86 1.1 itojun { 87 1.1 itojun struct pf_altq_node *root = NULL, *node; 88 1.1 itojun int nodes, dotitle = (opts & PF_OPT_SHOWALL); 89 1.1 itojun 90 1.1 itojun 91 1.1 itojun if ((nodes = pfctl_update_qstats(dev, &root)) < 0) 92 1.1 itojun return (-1); 93 1.1 itojun 94 1.4 yamt if (nodes == 0) 95 1.4 yamt printf("No queue in use\n"); 96 1.1 itojun for (node = root; node != NULL; node = node->next) { 97 1.1 itojun if (iface != NULL && strcmp(node->altq.ifname, iface)) 98 1.1 itojun continue; 99 1.1 itojun if (dotitle) { 100 1.1 itojun pfctl_print_title("ALTQ:"); 101 1.1 itojun dotitle = 0; 102 1.1 itojun } 103 1.1 itojun pfctl_print_altq_node(dev, node, 0, opts); 104 1.1 itojun } 105 1.1 itojun 106 1.4 yamt while (verbose2 && nodes > 0) { 107 1.1 itojun printf("\n"); 108 1.1 itojun fflush(stdout); 109 1.1 itojun sleep(STAT_INTERVAL); 110 1.4 yamt if ((nodes = pfctl_update_qstats(dev, &root)) == -1) 111 1.1 itojun return (-1); 112 1.1 itojun for (node = root; node != NULL; node = node->next) { 113 1.1 itojun if (iface != NULL && strcmp(node->altq.ifname, iface)) 114 1.1 itojun continue; 115 1.1 itojun pfctl_print_altq_node(dev, node, 0, opts); 116 1.1 itojun } 117 1.1 itojun } 118 1.1 itojun pfctl_free_altq_node(root); 119 1.1 itojun return (0); 120 1.1 itojun } 121 1.1 itojun 122 1.1 itojun int 123 1.1 itojun pfctl_update_qstats(int dev, struct pf_altq_node **root) 124 1.1 itojun { 125 1.1 itojun struct pf_altq_node *node; 126 1.1 itojun struct pfioc_altq pa; 127 1.1 itojun struct pfioc_qstats pq; 128 1.1 itojun u_int32_t mnr, nr; 129 1.1 itojun struct queue_stats qstats; 130 1.1 itojun static u_int32_t last_ticket; 131 1.1 itojun 132 1.1 itojun memset(&pa, 0, sizeof(pa)); 133 1.1 itojun memset(&pq, 0, sizeof(pq)); 134 1.1 itojun memset(&qstats, 0, sizeof(qstats)); 135 1.1 itojun if (ioctl(dev, DIOCGETALTQS, &pa)) { 136 1.1 itojun warn("DIOCGETALTQS"); 137 1.1 itojun return (-1); 138 1.1 itojun } 139 1.1 itojun 140 1.1 itojun /* if a new set is found, start over */ 141 1.1 itojun if (pa.ticket != last_ticket && *root != NULL) { 142 1.1 itojun pfctl_free_altq_node(*root); 143 1.1 itojun *root = NULL; 144 1.1 itojun } 145 1.1 itojun last_ticket = pa.ticket; 146 1.1 itojun 147 1.1 itojun mnr = pa.nr; 148 1.1 itojun for (nr = 0; nr < mnr; ++nr) { 149 1.1 itojun pa.nr = nr; 150 1.1 itojun if (ioctl(dev, DIOCGETALTQ, &pa)) { 151 1.1 itojun warn("DIOCGETALTQ"); 152 1.1 itojun return (-1); 153 1.1 itojun } 154 1.1 itojun if (pa.altq.qid > 0) { 155 1.1 itojun pq.nr = nr; 156 1.1 itojun pq.ticket = pa.ticket; 157 1.1 itojun pq.buf = &qstats.data; 158 1.1 itojun pq.nbytes = sizeof(qstats.data); 159 1.1 itojun if (ioctl(dev, DIOCGETQSTATS, &pq)) { 160 1.1 itojun warn("DIOCGETQSTATS"); 161 1.1 itojun return (-1); 162 1.1 itojun } 163 1.1 itojun if ((node = pfctl_find_altq_node(*root, pa.altq.qname, 164 1.1 itojun pa.altq.ifname)) != NULL) { 165 1.1 itojun memcpy(&node->qstats.data, &qstats.data, 166 1.1 itojun sizeof(qstats.data)); 167 1.1 itojun update_avg(node); 168 1.1 itojun } else { 169 1.1 itojun pfctl_insert_altq_node(root, pa.altq, qstats); 170 1.1 itojun } 171 1.1 itojun } 172 1.1 itojun } 173 1.1 itojun return (mnr); 174 1.1 itojun } 175 1.1 itojun 176 1.1 itojun void 177 1.1 itojun pfctl_insert_altq_node(struct pf_altq_node **root, 178 1.1 itojun const struct pf_altq altq, const struct queue_stats qstats) 179 1.1 itojun { 180 1.1 itojun struct pf_altq_node *node; 181 1.1 itojun 182 1.1 itojun node = calloc(1, sizeof(struct pf_altq_node)); 183 1.1 itojun if (node == NULL) 184 1.1 itojun err(1, "pfctl_insert_altq_node: calloc"); 185 1.1 itojun memcpy(&node->altq, &altq, sizeof(struct pf_altq)); 186 1.1 itojun memcpy(&node->qstats, &qstats, sizeof(qstats)); 187 1.1 itojun node->next = node->children = NULL; 188 1.1 itojun 189 1.1 itojun if (*root == NULL) 190 1.1 itojun *root = node; 191 1.1 itojun else if (!altq.parent[0]) { 192 1.1 itojun struct pf_altq_node *prev = *root; 193 1.1 itojun 194 1.1 itojun while (prev->next != NULL) 195 1.1 itojun prev = prev->next; 196 1.1 itojun prev->next = node; 197 1.1 itojun } else { 198 1.1 itojun struct pf_altq_node *parent; 199 1.1 itojun 200 1.1 itojun parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname); 201 1.1 itojun if (parent == NULL) 202 1.1 itojun errx(1, "parent %s not found", altq.parent); 203 1.1 itojun if (parent->children == NULL) 204 1.1 itojun parent->children = node; 205 1.1 itojun else { 206 1.1 itojun struct pf_altq_node *prev = parent->children; 207 1.1 itojun 208 1.1 itojun while (prev->next != NULL) 209 1.1 itojun prev = prev->next; 210 1.1 itojun prev->next = node; 211 1.1 itojun } 212 1.1 itojun } 213 1.1 itojun update_avg(node); 214 1.1 itojun } 215 1.1 itojun 216 1.1 itojun struct pf_altq_node * 217 1.1 itojun pfctl_find_altq_node(struct pf_altq_node *root, const char *qname, 218 1.1 itojun const char *ifname) 219 1.1 itojun { 220 1.1 itojun struct pf_altq_node *node, *child; 221 1.1 itojun 222 1.1 itojun for (node = root; node != NULL; node = node->next) { 223 1.1 itojun if (!strcmp(node->altq.qname, qname) 224 1.1 itojun && !(strcmp(node->altq.ifname, ifname))) 225 1.1 itojun return (node); 226 1.1 itojun if (node->children != NULL) { 227 1.1 itojun child = pfctl_find_altq_node(node->children, qname, 228 1.1 itojun ifname); 229 1.1 itojun if (child != NULL) 230 1.1 itojun return (child); 231 1.1 itojun } 232 1.1 itojun } 233 1.1 itojun return (NULL); 234 1.1 itojun } 235 1.1 itojun 236 1.1 itojun void 237 1.1 itojun pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level, 238 1.1 itojun int opts) 239 1.1 itojun { 240 1.1 itojun const struct pf_altq_node *child; 241 1.1 itojun 242 1.1 itojun if (node == NULL) 243 1.1 itojun return; 244 1.1 itojun 245 1.1 itojun print_altq(&node->altq, level, NULL, NULL); 246 1.1 itojun 247 1.1 itojun if (node->children != NULL) { 248 1.1 itojun printf("{"); 249 1.1 itojun for (child = node->children; child != NULL; 250 1.1 itojun child = child->next) { 251 1.1 itojun printf("%s", child->altq.qname); 252 1.1 itojun if (child->next != NULL) 253 1.1 itojun printf(", "); 254 1.1 itojun } 255 1.1 itojun printf("}"); 256 1.1 itojun } 257 1.1 itojun printf("\n"); 258 1.1 itojun 259 1.1 itojun if (opts & PF_OPT_VERBOSE) 260 1.1 itojun pfctl_print_altq_nodestat(dev, node); 261 1.1 itojun 262 1.1 itojun if (opts & PF_OPT_DEBUG) 263 1.1 itojun printf(" [ qid=%u ifname=%s ifbandwidth=%s ]\n", 264 1.1 itojun node->altq.qid, node->altq.ifname, 265 1.1 itojun rate2str((double)(node->altq.ifbandwidth))); 266 1.1 itojun 267 1.1 itojun for (child = node->children; child != NULL; 268 1.1 itojun child = child->next) 269 1.1 itojun pfctl_print_altq_node(dev, child, level + 1, opts); 270 1.1 itojun } 271 1.1 itojun 272 1.1 itojun void 273 1.1 itojun pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a) 274 1.1 itojun { 275 1.1 itojun if (a->altq.qid == 0) 276 1.1 itojun return; 277 1.1 itojun 278 1.1 itojun switch (a->altq.scheduler) { 279 1.1 itojun case ALTQT_CBQ: 280 1.1 itojun print_cbqstats(a->qstats); 281 1.1 itojun break; 282 1.1 itojun case ALTQT_PRIQ: 283 1.1 itojun print_priqstats(a->qstats); 284 1.1 itojun break; 285 1.1 itojun case ALTQT_HFSC: 286 1.1 itojun print_hfscstats(a->qstats); 287 1.1 itojun break; 288 1.1 itojun } 289 1.1 itojun } 290 1.1 itojun 291 1.1 itojun void 292 1.1 itojun print_cbqstats(struct queue_stats cur) 293 1.1 itojun { 294 1.1 itojun printf(" [ pkts: %10llu bytes: %10llu " 295 1.1 itojun "dropped pkts: %6llu bytes: %6llu ]\n", 296 1.1 itojun (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets, 297 1.1 itojun (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes, 298 1.1 itojun (unsigned long long)cur.data.cbq_stats.drop_cnt.packets, 299 1.1 itojun (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes); 300 1.1 itojun printf(" [ qlength: %3d/%3d borrows: %6u suspends: %6u ]\n", 301 1.1 itojun cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax, 302 1.1 itojun cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays); 303 1.1 itojun 304 1.1 itojun if (cur.avgn < 2) 305 1.1 itojun return; 306 1.1 itojun 307 1.1 itojun printf(" [ measured: %7.1f packets/s, %s/s ]\n", 308 1.1 itojun cur.avg_packets / STAT_INTERVAL, 309 1.1 itojun rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 310 1.1 itojun } 311 1.1 itojun 312 1.1 itojun void 313 1.1 itojun print_priqstats(struct queue_stats cur) 314 1.1 itojun { 315 1.1 itojun printf(" [ pkts: %10llu bytes: %10llu " 316 1.1 itojun "dropped pkts: %6llu bytes: %6llu ]\n", 317 1.1 itojun (unsigned long long)cur.data.priq_stats.xmitcnt.packets, 318 1.1 itojun (unsigned long long)cur.data.priq_stats.xmitcnt.bytes, 319 1.1 itojun (unsigned long long)cur.data.priq_stats.dropcnt.packets, 320 1.1 itojun (unsigned long long)cur.data.priq_stats.dropcnt.bytes); 321 1.1 itojun printf(" [ qlength: %3d/%3d ]\n", 322 1.1 itojun cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit); 323 1.1 itojun 324 1.1 itojun if (cur.avgn < 2) 325 1.1 itojun return; 326 1.1 itojun 327 1.1 itojun printf(" [ measured: %7.1f packets/s, %s/s ]\n", 328 1.1 itojun cur.avg_packets / STAT_INTERVAL, 329 1.1 itojun rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 330 1.1 itojun } 331 1.1 itojun 332 1.1 itojun void 333 1.1 itojun print_hfscstats(struct queue_stats cur) 334 1.1 itojun { 335 1.1 itojun printf(" [ pkts: %10llu bytes: %10llu " 336 1.1 itojun "dropped pkts: %6llu bytes: %6llu ]\n", 337 1.1 itojun (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets, 338 1.1 itojun (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes, 339 1.1 itojun (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets, 340 1.1 itojun (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes); 341 1.1 itojun printf(" [ qlength: %3d/%3d ]\n", 342 1.1 itojun cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit); 343 1.1 itojun 344 1.1 itojun if (cur.avgn < 2) 345 1.1 itojun return; 346 1.1 itojun 347 1.1 itojun printf(" [ measured: %7.1f packets/s, %s/s ]\n", 348 1.1 itojun cur.avg_packets / STAT_INTERVAL, 349 1.1 itojun rate2str((8 * cur.avg_bytes) / STAT_INTERVAL)); 350 1.1 itojun } 351 1.1 itojun 352 1.1 itojun void 353 1.1 itojun pfctl_free_altq_node(struct pf_altq_node *node) 354 1.1 itojun { 355 1.1 itojun while (node != NULL) { 356 1.1 itojun struct pf_altq_node *prev; 357 1.1 itojun 358 1.1 itojun if (node->children != NULL) 359 1.1 itojun pfctl_free_altq_node(node->children); 360 1.1 itojun prev = node; 361 1.1 itojun node = node->next; 362 1.1 itojun free(prev); 363 1.1 itojun } 364 1.1 itojun } 365 1.1 itojun 366 1.1 itojun void 367 1.1 itojun update_avg(struct pf_altq_node *a) 368 1.1 itojun { 369 1.1 itojun struct queue_stats *qs; 370 1.1 itojun u_int64_t b, p; 371 1.1 itojun int n; 372 1.1 itojun 373 1.1 itojun if (a->altq.qid == 0) 374 1.1 itojun return; 375 1.1 itojun 376 1.1 itojun qs = &a->qstats; 377 1.1 itojun n = qs->avgn; 378 1.1 itojun 379 1.1 itojun switch (a->altq.scheduler) { 380 1.1 itojun case ALTQT_CBQ: 381 1.1 itojun b = qs->data.cbq_stats.xmit_cnt.bytes; 382 1.1 itojun p = qs->data.cbq_stats.xmit_cnt.packets; 383 1.1 itojun break; 384 1.1 itojun case ALTQT_PRIQ: 385 1.1 itojun b = qs->data.priq_stats.xmitcnt.bytes; 386 1.1 itojun p = qs->data.priq_stats.xmitcnt.packets; 387 1.1 itojun break; 388 1.1 itojun case ALTQT_HFSC: 389 1.1 itojun b = qs->data.hfsc_stats.xmit_cnt.bytes; 390 1.1 itojun p = qs->data.hfsc_stats.xmit_cnt.packets; 391 1.1 itojun break; 392 1.1 itojun default: 393 1.1 itojun b = 0; 394 1.1 itojun p = 0; 395 1.1 itojun break; 396 1.1 itojun } 397 1.1 itojun 398 1.1 itojun if (n == 0) { 399 1.1 itojun qs->prev_bytes = b; 400 1.1 itojun qs->prev_packets = p; 401 1.1 itojun qs->avgn++; 402 1.1 itojun return; 403 1.1 itojun } 404 1.1 itojun 405 1.1 itojun if (b >= qs->prev_bytes) 406 1.1 itojun qs->avg_bytes = ((qs->avg_bytes * (n - 1)) + 407 1.1 itojun (b - qs->prev_bytes)) / n; 408 1.1 itojun 409 1.1 itojun if (p >= qs->prev_packets) 410 1.1 itojun qs->avg_packets = ((qs->avg_packets * (n - 1)) + 411 1.1 itojun (p - qs->prev_packets)) / n; 412 1.1 itojun 413 1.1 itojun qs->prev_bytes = b; 414 1.1 itojun qs->prev_packets = p; 415 1.1 itojun if (n < AVGN_MAX) 416 1.1 itojun qs->avgn++; 417 1.1 itojun } 418