xref: /openbsd/sbin/pfctl/pfctl_queue.c (revision 91f110e0)
1 /*	$OpenBSD: pfctl_queue.c,v 1.1 2013/10/12 12:16:12 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 
70 int
71 pfctl_show_queues(int dev, const char *iface, int opts, int verbose2)
72 {
73 	struct pfctl_queue_node	*node;
74 	int			 nodes, dotitle = (opts & PF_OPT_SHOWALL);
75 
76 
77 	if ((nodes = pfctl_update_qstats(dev)) <= 0)
78 		return (nodes);
79 
80 	TAILQ_FOREACH(node, &qnodes, entries) {
81 		if (iface != NULL && strcmp(node->qs.ifname, iface))
82 			continue;
83 		if (dotitle) {
84 			pfctl_print_title("QUEUES:");
85 			dotitle = 0;
86 		}
87 		pfctl_print_queue_node(dev, node, opts);
88 	}
89 
90 	while (verbose2 && nodes > 0) {
91 		printf("\n");
92 		fflush(stdout);
93 		sleep(STAT_INTERVAL);
94 		if ((nodes = pfctl_update_qstats(dev)) == -1)
95 			return (-1);
96 		TAILQ_FOREACH(node, &qnodes, entries) {
97 			if (iface != NULL && strcmp(node->qs.ifname, iface))
98 				continue;
99 			pfctl_print_queue_node(dev, node, opts);
100 		}
101 	}
102 	while ((node = TAILQ_FIRST(&qnodes)) != NULL)
103 		TAILQ_REMOVE(&qnodes, node, entries);
104 	return (0);
105 }
106 
107 int
108 pfctl_update_qstats(int dev)
109 {
110 	struct pfctl_queue_node	*node;
111 	struct pfioc_queue	 pq;
112 	struct pfioc_qstats	 pqs;
113 	u_int32_t		 mnr, nr;
114 	struct queue_stats	 qstats;
115 	static u_int32_t	 last_ticket;
116 
117 	memset(&pq, 0, sizeof(pq));
118 	memset(&pqs, 0, sizeof(pqs));
119 	memset(&qstats, 0, sizeof(qstats));
120 	if (ioctl(dev, DIOCGETQUEUES, &pq)) {
121 		warn("DIOCGETQUEUES");
122 		return (-1);
123 	}
124 
125 	/* if a new set is found, start over */
126 	if (pq.ticket != last_ticket)
127 		while ((node = TAILQ_FIRST(&qnodes)) != NULL)
128 			TAILQ_REMOVE(&qnodes, node, entries);
129 	last_ticket = pq.ticket;
130 
131 	mnr = pq.nr;
132 	for (nr = 0; nr < mnr; ++nr) {
133 		pqs.nr = nr;
134 		pqs.ticket = pq.ticket;
135 		pqs.buf = &qstats.data;
136 		pqs.nbytes = sizeof(qstats.data);
137 		if (ioctl(dev, DIOCGETQSTATS, &pqs)) {
138 			warn("DIOCGETQSTATS");
139 			return (-1);
140 		}
141 //		if (pqs.queue.qname[0] != '_') {
142 //			if (pqs.queue.parent[0] && pqs.queue.parent[0] == '_')
143 //				pqs.queue.parent[0] = 0;
144 			if ((node = pfctl_find_queue_node(pqs.queue.qname,
145 			    pqs.queue.ifname)) != NULL) {
146 				memcpy(&node->qstats.data, &qstats.data,
147 				    sizeof(qstats.data));
148 				update_avg(&node->qstats);
149 			} else {
150 				pfctl_insert_queue_node(pqs.queue, qstats);
151 			}
152 //		}
153 	}
154 	return (mnr);
155 }
156 
157 void
158 pfctl_insert_queue_node(const struct pf_queuespec qs,
159     const struct queue_stats qstats)
160 {
161 	struct pfctl_queue_node	*node;
162 
163 	node = calloc(1, sizeof(struct pfctl_queue_node));
164 	if (node == NULL)
165 		err(1, "pfctl_insert_queue_node: calloc");
166 	memcpy(&node->qs, &qs, sizeof(qs));
167 	memcpy(&node->qstats, &qstats, sizeof(qstats));
168 	TAILQ_INSERT_TAIL(&qnodes, node, entries);
169 	update_avg(&node->qstats);
170 }
171 
172 struct pfctl_queue_node *
173 pfctl_find_queue_node(const char *qname, const char *ifname)
174 {
175 	struct pfctl_queue_node	*node;
176 
177 	TAILQ_FOREACH(node, &qnodes, entries)
178 		if (!strcmp(node->qs.qname, qname)
179 		    && !(strcmp(node->qs.ifname, ifname)))
180 			return (node);
181 	return (NULL);
182 }
183 
184 void
185 pfctl_print_queue_node(int dev, struct pfctl_queue_node *node, int opts)
186 {
187 	if (node == NULL)
188 		return;
189 
190 	print_queuespec(&node->qs);
191 	if (opts & PF_OPT_VERBOSE)
192 		pfctl_print_queue_nodestat(dev, node);
193 
194 	if (opts & PF_OPT_DEBUG)
195 		printf("  [ qid=%u parent_qid=%u ifname=%s]\n",
196 		    node->qs.qid, node->qs.parent_qid, node->qs.ifname);
197 }
198 
199 void
200 pfctl_print_queue_nodestat(int dev, const struct pfctl_queue_node *node)
201 {
202 	printf("  [ pkts: %10llu  bytes: %10llu  "
203 	    "dropped pkts: %6llu bytes: %6llu ]\n",
204 	    (unsigned long long)node->qstats.data.xmit_cnt.packets,
205 	    (unsigned long long)node->qstats.data.xmit_cnt.bytes,
206 	    (unsigned long long)node->qstats.data.drop_cnt.packets,
207 	    (unsigned long long)node->qstats.data.drop_cnt.bytes);
208 	printf("  [ qlength: %3d/%3d ]\n", node->qstats.data.qlength,
209 	    node->qstats.data.qlimit);
210 
211 	if (node->qstats.avgn < 2)
212 		return;
213 
214 	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
215 	    node->qstats.avg_packets / STAT_INTERVAL,
216 	    rate2str((8 * node->qstats.avg_bytes) / STAT_INTERVAL));
217 }
218 
219 void
220 update_avg(struct queue_stats *s)
221 {
222 	if (s->avgn > 0) {
223 		if (s->data.xmit_cnt.bytes >= s->prev_bytes)
224 			s->avg_bytes = ((s->avg_bytes * (s->avgn - 1)) +
225 			    (s->data.xmit_cnt.bytes - s->prev_bytes)) /
226 			    s->avgn;
227 		if (s->data.xmit_cnt.packets >= s->prev_packets)
228 			s->avg_packets = ((s->avg_packets * (s->avgn - 1)) +
229 			    (s->data.xmit_cnt.packets - s->prev_packets)) /
230 			    s->avgn;
231 	}
232 
233 	s->prev_bytes = s->data.xmit_cnt.bytes;
234 	s->prev_packets = s->data.xmit_cnt.packets;
235 	if (s->avgn < AVGN_MAX)
236 		s->avgn++;
237 }
238