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 |
|
|
} |