1 /* $OpenBSD: pfctl_queue.c,v 1.7 2019/06/28 13:32:45 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2003 - 2013 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/types.h> 20 #include <sys/ioctl.h> 21 #include <sys/socket.h> 22 23 #include <net/if.h> 24 #include <netinet/in.h> 25 #include <net/pfvar.h> 26 #include <arpa/inet.h> 27 28 #include <err.h> 29 #include <math.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 35 #include <net/hfsc.h> 36 #include <net/fq_codel.h> 37 38 #include "pfctl.h" 39 #include "pfctl_parser.h" 40 41 #define AVGN_MAX 8 42 #define STAT_INTERVAL 5 43 44 struct queue_stats { 45 union { 46 struct hfsc_class_stats hfsc; 47 struct fqcodel_stats fqc; 48 } data; 49 int avgn; 50 double avg_bytes; 51 double avg_packets; 52 u_int64_t prev_bytes; 53 u_int64_t prev_packets; 54 }; 55 56 struct pfctl_queue_node { 57 TAILQ_ENTRY(pfctl_queue_node) entries; 58 struct pf_queuespec qs; 59 struct queue_stats qstats; 60 }; 61 TAILQ_HEAD(qnodes, pfctl_queue_node) qnodes = TAILQ_HEAD_INITIALIZER(qnodes); 62 63 int pfctl_update_qstats(int); 64 void pfctl_insert_queue_node(const struct pf_queuespec, 65 const struct queue_stats); 66 struct pfctl_queue_node *pfctl_find_queue_node(const char *, const char *); 67 void pfctl_print_queue_node(int, struct pfctl_queue_node *, 68 int); 69 void print_qstats(struct queue_stats); 70 void pfctl_free_queue_node(struct pfctl_queue_node *); 71 void pfctl_print_queue_nodestat(int, 72 const struct pfctl_queue_node *); 73 void update_avg(struct queue_stats *); 74 char *rate2str(double); 75 76 int 77 pfctl_show_queues(int dev, const char *iface, int opts, int verbose2) 78 { 79 struct pfctl_queue_node *node; 80 int nodes, dotitle = (opts & PF_OPT_SHOWALL); 81 82 83 if ((nodes = pfctl_update_qstats(dev)) <= 0) 84 return (nodes); 85 86 TAILQ_FOREACH(node, &qnodes, entries) { 87 if (iface != NULL && strcmp(node->qs.ifname, iface)) 88 continue; 89 if (dotitle) { 90 pfctl_print_title("QUEUES:"); 91 dotitle = 0; 92 } 93 pfctl_print_queue_node(dev, node, opts); 94 } 95 96 while (verbose2 && nodes > 0) { 97 printf("\n"); 98 fflush(stdout); 99 sleep(STAT_INTERVAL); 100 if ((nodes = pfctl_update_qstats(dev)) == -1) 101 return (-1); 102 TAILQ_FOREACH(node, &qnodes, entries) { 103 if (iface != NULL && strcmp(node->qs.ifname, iface)) 104 continue; 105 pfctl_print_queue_node(dev, node, opts); 106 } 107 } 108 while ((node = TAILQ_FIRST(&qnodes)) != NULL) 109 TAILQ_REMOVE(&qnodes, node, entries); 110 return (0); 111 } 112 113 int 114 pfctl_update_qstats(int dev) 115 { 116 struct pfctl_queue_node *node; 117 struct pfioc_queue pq; 118 struct pfioc_qstats pqs; 119 u_int32_t mnr, nr; 120 struct queue_stats qstats; 121 static u_int32_t last_ticket; 122 123 memset(&pq, 0, sizeof(pq)); 124 memset(&pqs, 0, sizeof(pqs)); 125 memset(&qstats, 0, sizeof(qstats)); 126 if (ioctl(dev, DIOCGETQUEUES, &pq) == -1) { 127 warn("DIOCGETQUEUES"); 128 return (-1); 129 } 130 131 /* if a new set is found, start over */ 132 if (pq.ticket != last_ticket) 133 while ((node = TAILQ_FIRST(&qnodes)) != NULL) 134 TAILQ_REMOVE(&qnodes, node, entries); 135 last_ticket = pq.ticket; 136 137 mnr = pq.nr; 138 for (nr = 0; nr < mnr; ++nr) { 139 pqs.nr = nr; 140 pqs.ticket = pq.ticket; 141 pqs.buf = &qstats.data; 142 pqs.nbytes = sizeof(qstats.data); 143 if (ioctl(dev, DIOCGETQSTATS, &pqs) == -1) { 144 warn("DIOCGETQSTATS"); 145 return (-1); 146 } 147 if ((node = pfctl_find_queue_node(pqs.queue.qname, 148 pqs.queue.ifname)) != NULL) { 149 memcpy(&node->qstats.data, &qstats.data, 150 sizeof(qstats.data)); 151 update_avg(&node->qstats); 152 } else { 153 pfctl_insert_queue_node(pqs.queue, qstats); 154 } 155 } 156 return (mnr); 157 } 158 159 void 160 pfctl_insert_queue_node(const struct pf_queuespec qs, 161 const struct queue_stats qstats) 162 { 163 struct pfctl_queue_node *node; 164 165 node = calloc(1, sizeof(struct pfctl_queue_node)); 166 if (node == NULL) 167 err(1, "pfctl_insert_queue_node: calloc"); 168 memcpy(&node->qs, &qs, sizeof(qs)); 169 memcpy(&node->qstats, &qstats, sizeof(qstats)); 170 TAILQ_INSERT_TAIL(&qnodes, node, entries); 171 update_avg(&node->qstats); 172 } 173 174 struct pfctl_queue_node * 175 pfctl_find_queue_node(const char *qname, const char *ifname) 176 { 177 struct pfctl_queue_node *node; 178 179 TAILQ_FOREACH(node, &qnodes, entries) 180 if (!strcmp(node->qs.qname, qname) 181 && !(strcmp(node->qs.ifname, ifname))) 182 return (node); 183 return (NULL); 184 } 185 186 void 187 pfctl_print_queue_node(int dev, struct pfctl_queue_node *node, int opts) 188 { 189 if (node == NULL) 190 return; 191 192 print_queuespec(&node->qs); 193 if (opts & PF_OPT_VERBOSE) 194 pfctl_print_queue_nodestat(dev, node); 195 196 if (opts & PF_OPT_DEBUG) 197 printf(" [ qid=%u parent_qid=%u ifname=%s]\n", 198 node->qs.qid, node->qs.parent_qid, node->qs.ifname); 199 } 200 201 void 202 pfctl_print_queue_nodestat(int dev, const struct pfctl_queue_node *node) 203 { 204 struct hfsc_class_stats *stats = 205 (struct hfsc_class_stats *)&node->qstats.data.hfsc; 206 struct fqcodel_stats *fqstats = 207 (struct fqcodel_stats *)&node->qstats.data.fqc; 208 209 printf(" [ pkts: %10llu bytes: %10llu " 210 "dropped pkts: %6llu bytes: %6llu ]\n", 211 (unsigned long long)stats->xmit_cnt.packets, 212 (unsigned long long)stats->xmit_cnt.bytes, 213 (unsigned long long)stats->drop_cnt.packets, 214 (unsigned long long)stats->drop_cnt.bytes); 215 if (node->qs.parent_qid == 0 && (node->qs.flags & PFQS_FLOWQUEUE) && 216 !(node->qs.flags & PFQS_ROOTCLASS)) { 217 double avg = 0, dev = 0; 218 219 if (fqstats->flows > 0) { 220 avg = (double)fqstats->delaysum / 221 (double)fqstats->flows; 222 dev = sqrt(fmax(0, (double)fqstats->delaysumsq / 223 (double)fqstats->flows - avg * avg)); 224 } 225 226 printf(" [ qlength: %3d/%3d avg delay: %.3fms std-dev: %.3fms" 227 " flows: %3d ]\n", stats->qlength, stats->qlimit, 228 avg / 1000, dev / 1000, fqstats->flows); 229 } else 230 printf(" [ qlength: %3d/%3d ]\n", stats->qlength, 231 stats->qlimit); 232 233 if (node->qstats.avgn < 2) 234 return; 235 236 printf(" [ measured: %7.1f packets/s, %s/s ]\n", 237 node->qstats.avg_packets / STAT_INTERVAL, 238 rate2str((8 * node->qstats.avg_bytes) / STAT_INTERVAL)); 239 } 240 241 void 242 update_avg(struct queue_stats *s) 243 { 244 struct hfsc_class_stats *stats = 245 (struct hfsc_class_stats *)&s->data; 246 247 if (s->avgn > 0) { 248 if (stats->xmit_cnt.bytes >= s->prev_bytes) 249 s->avg_bytes = ((s->avg_bytes * (s->avgn - 1)) + 250 (stats->xmit_cnt.bytes - s->prev_bytes)) / 251 s->avgn; 252 if (stats->xmit_cnt.packets >= s->prev_packets) 253 s->avg_packets = ((s->avg_packets * (s->avgn - 1)) + 254 (stats->xmit_cnt.packets - s->prev_packets)) / 255 s->avgn; 256 } 257 258 s->prev_bytes = stats->xmit_cnt.bytes; 259 s->prev_packets = stats->xmit_cnt.packets; 260 if (s->avgn < AVGN_MAX) 261 s->avgn++; 262 } 263 264 #define R2S_BUFS 8 265 #define RATESTR_MAX 16 266 267 char * 268 rate2str(double rate) 269 { 270 char *buf; 271 static char r2sbuf[R2S_BUFS][RATESTR_MAX]; /* ring bufer */ 272 static int idx = 0; 273 int i; 274 static const char unit[] = " KMG"; 275 276 buf = r2sbuf[idx++]; 277 if (idx == R2S_BUFS) 278 idx = 0; 279 280 for (i = 0; rate >= 1000 && i <= 3; i++) 281 rate /= 1000; 282 283 if ((int)(rate * 100) % 100) 284 snprintf(buf, RATESTR_MAX, "%.2f%cb", rate, unit[i]); 285 else 286 snprintf(buf, RATESTR_MAX, "%d%cb", (int)rate, unit[i]); 287 288 return (buf); 289 } 290