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