xref: /openbsd-src/sbin/pfctl/pfctl_queue.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
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