GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/pfctl/pfctl_queue.c Lines: 0 116 0.0 %
Date: 2017-11-07 Branches: 0 76 0.0 %

Line Branch Exec Source
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
}