1 |
|
|
/* $OpenBSD: pfctl_queue.c,v 1.6 2017/07/19 12:51:30 mikeb 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)) { |
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)) { |
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 |
|
|
} |