1*67686547Sjsg /* $OpenBSD: pfctl_queue.c,v 1.8 2024/05/19 10:39:40 jsg Exp $ */
2f8d11d7cShenning
3f8d11d7cShenning /*
4f8d11d7cShenning * Copyright (c) 2003 - 2013 Henning Brauer <henning@openbsd.org>
5f8d11d7cShenning *
6f8d11d7cShenning * Permission to use, copy, modify, and distribute this software for any
7f8d11d7cShenning * purpose with or without fee is hereby granted, provided that the above
8f8d11d7cShenning * copyright notice and this permission notice appear in all copies.
9f8d11d7cShenning *
10f8d11d7cShenning * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11f8d11d7cShenning * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12f8d11d7cShenning * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13f8d11d7cShenning * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14f8d11d7cShenning * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15f8d11d7cShenning * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16f8d11d7cShenning * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17f8d11d7cShenning */
18f8d11d7cShenning
19f8d11d7cShenning #include <sys/types.h>
20f8d11d7cShenning #include <sys/ioctl.h>
21f8d11d7cShenning #include <sys/socket.h>
22f8d11d7cShenning
23f8d11d7cShenning #include <net/if.h>
24f8d11d7cShenning #include <netinet/in.h>
25f8d11d7cShenning #include <net/pfvar.h>
26f8d11d7cShenning #include <arpa/inet.h>
27f8d11d7cShenning
28f8d11d7cShenning #include <err.h>
2970b55ec8Smikeb #include <math.h>
30f8d11d7cShenning #include <stdio.h>
31f8d11d7cShenning #include <stdlib.h>
32f8d11d7cShenning #include <string.h>
33f8d11d7cShenning #include <unistd.h>
34f8d11d7cShenning
35f8d11d7cShenning #include <net/hfsc.h>
3670b55ec8Smikeb #include <net/fq_codel.h>
37f8d11d7cShenning
38f8d11d7cShenning #include "pfctl.h"
39f8d11d7cShenning #include "pfctl_parser.h"
40f8d11d7cShenning
41f8d11d7cShenning #define AVGN_MAX 8
42f8d11d7cShenning #define STAT_INTERVAL 5
43f8d11d7cShenning
44f8d11d7cShenning struct queue_stats {
4570b55ec8Smikeb union {
4670b55ec8Smikeb struct hfsc_class_stats hfsc;
4770b55ec8Smikeb struct fqcodel_stats fqc;
4870b55ec8Smikeb } data;
49f8d11d7cShenning int avgn;
50f8d11d7cShenning double avg_bytes;
51f8d11d7cShenning double avg_packets;
52f8d11d7cShenning u_int64_t prev_bytes;
53f8d11d7cShenning u_int64_t prev_packets;
54f8d11d7cShenning };
55f8d11d7cShenning
56f8d11d7cShenning struct pfctl_queue_node {
57f8d11d7cShenning TAILQ_ENTRY(pfctl_queue_node) entries;
58f8d11d7cShenning struct pf_queuespec qs;
59f8d11d7cShenning struct queue_stats qstats;
60f8d11d7cShenning };
61f8d11d7cShenning TAILQ_HEAD(qnodes, pfctl_queue_node) qnodes = TAILQ_HEAD_INITIALIZER(qnodes);
62f8d11d7cShenning
63f8d11d7cShenning int pfctl_update_qstats(int);
64f8d11d7cShenning void pfctl_insert_queue_node(const struct pf_queuespec,
65f8d11d7cShenning const struct queue_stats);
66f8d11d7cShenning struct pfctl_queue_node *pfctl_find_queue_node(const char *, const char *);
67f8d11d7cShenning void pfctl_print_queue_node(int, struct pfctl_queue_node *,
68f8d11d7cShenning int);
69f8d11d7cShenning void pfctl_print_queue_nodestat(int,
70f8d11d7cShenning const struct pfctl_queue_node *);
71f8d11d7cShenning void update_avg(struct queue_stats *);
7212aebd24Shenning char *rate2str(double);
73f8d11d7cShenning
74f8d11d7cShenning int
pfctl_show_queues(int dev,const char * iface,int opts,int verbose2)75f8d11d7cShenning pfctl_show_queues(int dev, const char *iface, int opts, int verbose2)
76f8d11d7cShenning {
77f8d11d7cShenning struct pfctl_queue_node *node;
78f8d11d7cShenning int nodes, dotitle = (opts & PF_OPT_SHOWALL);
79f8d11d7cShenning
80f8d11d7cShenning
81f8d11d7cShenning if ((nodes = pfctl_update_qstats(dev)) <= 0)
82f8d11d7cShenning return (nodes);
83f8d11d7cShenning
84f8d11d7cShenning TAILQ_FOREACH(node, &qnodes, entries) {
85f8d11d7cShenning if (iface != NULL && strcmp(node->qs.ifname, iface))
86f8d11d7cShenning continue;
87f8d11d7cShenning if (dotitle) {
88f8d11d7cShenning pfctl_print_title("QUEUES:");
89f8d11d7cShenning dotitle = 0;
90f8d11d7cShenning }
91f8d11d7cShenning pfctl_print_queue_node(dev, node, opts);
92f8d11d7cShenning }
93f8d11d7cShenning
94f8d11d7cShenning while (verbose2 && nodes > 0) {
95f8d11d7cShenning printf("\n");
96f8d11d7cShenning fflush(stdout);
97f8d11d7cShenning sleep(STAT_INTERVAL);
98f8d11d7cShenning if ((nodes = pfctl_update_qstats(dev)) == -1)
99f8d11d7cShenning return (-1);
100f8d11d7cShenning TAILQ_FOREACH(node, &qnodes, entries) {
101f8d11d7cShenning if (iface != NULL && strcmp(node->qs.ifname, iface))
102f8d11d7cShenning continue;
103f8d11d7cShenning pfctl_print_queue_node(dev, node, opts);
104f8d11d7cShenning }
105f8d11d7cShenning }
106f8d11d7cShenning while ((node = TAILQ_FIRST(&qnodes)) != NULL)
107f8d11d7cShenning TAILQ_REMOVE(&qnodes, node, entries);
108f8d11d7cShenning return (0);
109f8d11d7cShenning }
110f8d11d7cShenning
111f8d11d7cShenning int
pfctl_update_qstats(int dev)112f8d11d7cShenning pfctl_update_qstats(int dev)
113f8d11d7cShenning {
114f8d11d7cShenning struct pfctl_queue_node *node;
115f8d11d7cShenning struct pfioc_queue pq;
116f8d11d7cShenning struct pfioc_qstats pqs;
117f8d11d7cShenning u_int32_t mnr, nr;
118f8d11d7cShenning struct queue_stats qstats;
119f8d11d7cShenning static u_int32_t last_ticket;
120f8d11d7cShenning
121f8d11d7cShenning memset(&pq, 0, sizeof(pq));
122f8d11d7cShenning memset(&pqs, 0, sizeof(pqs));
123f8d11d7cShenning memset(&qstats, 0, sizeof(qstats));
124df69c215Sderaadt if (ioctl(dev, DIOCGETQUEUES, &pq) == -1) {
125f8d11d7cShenning warn("DIOCGETQUEUES");
126f8d11d7cShenning return (-1);
127f8d11d7cShenning }
128f8d11d7cShenning
129f8d11d7cShenning /* if a new set is found, start over */
130f8d11d7cShenning if (pq.ticket != last_ticket)
131f8d11d7cShenning while ((node = TAILQ_FIRST(&qnodes)) != NULL)
132f8d11d7cShenning TAILQ_REMOVE(&qnodes, node, entries);
133f8d11d7cShenning last_ticket = pq.ticket;
134f8d11d7cShenning
135f8d11d7cShenning mnr = pq.nr;
136f8d11d7cShenning for (nr = 0; nr < mnr; ++nr) {
137f8d11d7cShenning pqs.nr = nr;
138f8d11d7cShenning pqs.ticket = pq.ticket;
139f8d11d7cShenning pqs.buf = &qstats.data;
140f8d11d7cShenning pqs.nbytes = sizeof(qstats.data);
141df69c215Sderaadt if (ioctl(dev, DIOCGETQSTATS, &pqs) == -1) {
142f8d11d7cShenning warn("DIOCGETQSTATS");
143f8d11d7cShenning return (-1);
144f8d11d7cShenning }
145f8d11d7cShenning if ((node = pfctl_find_queue_node(pqs.queue.qname,
146f8d11d7cShenning pqs.queue.ifname)) != NULL) {
147f8d11d7cShenning memcpy(&node->qstats.data, &qstats.data,
148f8d11d7cShenning sizeof(qstats.data));
149f8d11d7cShenning update_avg(&node->qstats);
150f8d11d7cShenning } else {
151f8d11d7cShenning pfctl_insert_queue_node(pqs.queue, qstats);
152f8d11d7cShenning }
153f8d11d7cShenning }
154f8d11d7cShenning return (mnr);
155f8d11d7cShenning }
156f8d11d7cShenning
157f8d11d7cShenning void
pfctl_insert_queue_node(const struct pf_queuespec qs,const struct queue_stats qstats)158f8d11d7cShenning pfctl_insert_queue_node(const struct pf_queuespec qs,
159f8d11d7cShenning const struct queue_stats qstats)
160f8d11d7cShenning {
161f8d11d7cShenning struct pfctl_queue_node *node;
162f8d11d7cShenning
163f8d11d7cShenning node = calloc(1, sizeof(struct pfctl_queue_node));
164f8d11d7cShenning if (node == NULL)
165f8d11d7cShenning err(1, "pfctl_insert_queue_node: calloc");
166f8d11d7cShenning memcpy(&node->qs, &qs, sizeof(qs));
167f8d11d7cShenning memcpy(&node->qstats, &qstats, sizeof(qstats));
168f8d11d7cShenning TAILQ_INSERT_TAIL(&qnodes, node, entries);
169f8d11d7cShenning update_avg(&node->qstats);
170f8d11d7cShenning }
171f8d11d7cShenning
172f8d11d7cShenning struct pfctl_queue_node *
pfctl_find_queue_node(const char * qname,const char * ifname)173f8d11d7cShenning pfctl_find_queue_node(const char *qname, const char *ifname)
174f8d11d7cShenning {
175f8d11d7cShenning struct pfctl_queue_node *node;
176f8d11d7cShenning
177f8d11d7cShenning TAILQ_FOREACH(node, &qnodes, entries)
178f8d11d7cShenning if (!strcmp(node->qs.qname, qname)
179f8d11d7cShenning && !(strcmp(node->qs.ifname, ifname)))
180f8d11d7cShenning return (node);
181f8d11d7cShenning return (NULL);
182f8d11d7cShenning }
183f8d11d7cShenning
184f8d11d7cShenning void
pfctl_print_queue_node(int dev,struct pfctl_queue_node * node,int opts)185f8d11d7cShenning pfctl_print_queue_node(int dev, struct pfctl_queue_node *node, int opts)
186f8d11d7cShenning {
187f8d11d7cShenning if (node == NULL)
188f8d11d7cShenning return;
189f8d11d7cShenning
190f8d11d7cShenning print_queuespec(&node->qs);
191f8d11d7cShenning if (opts & PF_OPT_VERBOSE)
192f8d11d7cShenning pfctl_print_queue_nodestat(dev, node);
193f8d11d7cShenning
194f8d11d7cShenning if (opts & PF_OPT_DEBUG)
195f8d11d7cShenning printf(" [ qid=%u parent_qid=%u ifname=%s]\n",
196f8d11d7cShenning node->qs.qid, node->qs.parent_qid, node->qs.ifname);
197f8d11d7cShenning }
198f8d11d7cShenning
199f8d11d7cShenning void
pfctl_print_queue_nodestat(int dev,const struct pfctl_queue_node * node)200f8d11d7cShenning pfctl_print_queue_nodestat(int dev, const struct pfctl_queue_node *node)
201f8d11d7cShenning {
20270b55ec8Smikeb struct hfsc_class_stats *stats =
20370b55ec8Smikeb (struct hfsc_class_stats *)&node->qstats.data.hfsc;
20470b55ec8Smikeb struct fqcodel_stats *fqstats =
20570b55ec8Smikeb (struct fqcodel_stats *)&node->qstats.data.fqc;
20670b55ec8Smikeb
207f8d11d7cShenning printf(" [ pkts: %10llu bytes: %10llu "
208f8d11d7cShenning "dropped pkts: %6llu bytes: %6llu ]\n",
20970b55ec8Smikeb (unsigned long long)stats->xmit_cnt.packets,
21070b55ec8Smikeb (unsigned long long)stats->xmit_cnt.bytes,
21170b55ec8Smikeb (unsigned long long)stats->drop_cnt.packets,
21270b55ec8Smikeb (unsigned long long)stats->drop_cnt.bytes);
213b28abc64Smikeb if (node->qs.parent_qid == 0 && (node->qs.flags & PFQS_FLOWQUEUE) &&
214b28abc64Smikeb !(node->qs.flags & PFQS_ROOTCLASS)) {
21570b55ec8Smikeb double avg = 0, dev = 0;
21670b55ec8Smikeb
21770b55ec8Smikeb if (fqstats->flows > 0) {
21870b55ec8Smikeb avg = (double)fqstats->delaysum /
21970b55ec8Smikeb (double)fqstats->flows;
22070b55ec8Smikeb dev = sqrt(fmax(0, (double)fqstats->delaysumsq /
221aec39f4bSmikeb (double)fqstats->flows - avg * avg));
22270b55ec8Smikeb }
22370b55ec8Smikeb
22470b55ec8Smikeb printf(" [ qlength: %3d/%3d avg delay: %.3fms std-dev: %.3fms"
22570b55ec8Smikeb " flows: %3d ]\n", stats->qlength, stats->qlimit,
22670b55ec8Smikeb avg / 1000, dev / 1000, fqstats->flows);
22770b55ec8Smikeb } else
22870b55ec8Smikeb printf(" [ qlength: %3d/%3d ]\n", stats->qlength,
22970b55ec8Smikeb stats->qlimit);
230f8d11d7cShenning
231f8d11d7cShenning if (node->qstats.avgn < 2)
232f8d11d7cShenning return;
233f8d11d7cShenning
234f8d11d7cShenning printf(" [ measured: %7.1f packets/s, %s/s ]\n",
235f8d11d7cShenning node->qstats.avg_packets / STAT_INTERVAL,
236f8d11d7cShenning rate2str((8 * node->qstats.avg_bytes) / STAT_INTERVAL));
237f8d11d7cShenning }
238f8d11d7cShenning
239f8d11d7cShenning void
update_avg(struct queue_stats * s)240f8d11d7cShenning update_avg(struct queue_stats *s)
241f8d11d7cShenning {
24270b55ec8Smikeb struct hfsc_class_stats *stats =
24370b55ec8Smikeb (struct hfsc_class_stats *)&s->data;
24470b55ec8Smikeb
245f8d11d7cShenning if (s->avgn > 0) {
24670b55ec8Smikeb if (stats->xmit_cnt.bytes >= s->prev_bytes)
247f8d11d7cShenning s->avg_bytes = ((s->avg_bytes * (s->avgn - 1)) +
24870b55ec8Smikeb (stats->xmit_cnt.bytes - s->prev_bytes)) /
249f8d11d7cShenning s->avgn;
25070b55ec8Smikeb if (stats->xmit_cnt.packets >= s->prev_packets)
251f8d11d7cShenning s->avg_packets = ((s->avg_packets * (s->avgn - 1)) +
25270b55ec8Smikeb (stats->xmit_cnt.packets - s->prev_packets)) /
253f8d11d7cShenning s->avgn;
254f8d11d7cShenning }
255f8d11d7cShenning
25670b55ec8Smikeb s->prev_bytes = stats->xmit_cnt.bytes;
25770b55ec8Smikeb s->prev_packets = stats->xmit_cnt.packets;
258f8d11d7cShenning if (s->avgn < AVGN_MAX)
259f8d11d7cShenning s->avgn++;
260f8d11d7cShenning }
26112aebd24Shenning
26212aebd24Shenning #define R2S_BUFS 8
26312aebd24Shenning #define RATESTR_MAX 16
26412aebd24Shenning
26512aebd24Shenning char *
rate2str(double rate)26612aebd24Shenning rate2str(double rate)
26712aebd24Shenning {
26812aebd24Shenning char *buf;
26912aebd24Shenning static char r2sbuf[R2S_BUFS][RATESTR_MAX]; /* ring bufer */
27012aebd24Shenning static int idx = 0;
27112aebd24Shenning int i;
27212aebd24Shenning static const char unit[] = " KMG";
27312aebd24Shenning
27412aebd24Shenning buf = r2sbuf[idx++];
27512aebd24Shenning if (idx == R2S_BUFS)
27612aebd24Shenning idx = 0;
27712aebd24Shenning
27812aebd24Shenning for (i = 0; rate >= 1000 && i <= 3; i++)
27912aebd24Shenning rate /= 1000;
28012aebd24Shenning
28112aebd24Shenning if ((int)(rate * 100) % 100)
28212aebd24Shenning snprintf(buf, RATESTR_MAX, "%.2f%cb", rate, unit[i]);
28312aebd24Shenning else
28412aebd24Shenning snprintf(buf, RATESTR_MAX, "%d%cb", (int)rate, unit[i]);
28512aebd24Shenning
28612aebd24Shenning return (buf);
28712aebd24Shenning }
288