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