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