[BACK]Return to pfctl_queue.c CVS log [TXT][DIR] Up to [local] / src / sbin / pfctl

File: [local] / src / sbin / pfctl / pfctl_queue.c (download)

Revision 1.8, Sun May 19 10:39:40 2024 UTC (3 weeks, 1 day ago) by jsg
Branch: MAIN
CVS Tags: HEAD
Changes since 1.7: +1 -3 lines

remove prototypes with no matching function

/*	$OpenBSD: pfctl_queue.c,v 1.8 2024/05/19 10:39:40 jsg Exp $ */

/*
 * Copyright (c) 2003 - 2013 Henning Brauer <henning@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

#include <net/if.h>
#include <netinet/in.h>
#include <net/pfvar.h>
#include <arpa/inet.h>

#include <err.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <net/hfsc.h>
#include <net/fq_codel.h>

#include "pfctl.h"
#include "pfctl_parser.h"

#define AVGN_MAX	8
#define STAT_INTERVAL	5

struct queue_stats {
	union {
		struct hfsc_class_stats	hfsc;
		struct fqcodel_stats	fqc;
	}			 data;
	int			 avgn;
	double			 avg_bytes;
	double			 avg_packets;
	u_int64_t		 prev_bytes;
	u_int64_t		 prev_packets;
};

struct pfctl_queue_node {
	TAILQ_ENTRY(pfctl_queue_node)	entries;
	struct pf_queuespec		qs;
	struct queue_stats		qstats;
};
TAILQ_HEAD(qnodes, pfctl_queue_node) qnodes = TAILQ_HEAD_INITIALIZER(qnodes);

int			 pfctl_update_qstats(int);
void			 pfctl_insert_queue_node(const struct pf_queuespec,
			    const struct queue_stats);
struct pfctl_queue_node	*pfctl_find_queue_node(const char *, const char *);
void			 pfctl_print_queue_node(int, struct pfctl_queue_node *,
			    int);
void			 pfctl_print_queue_nodestat(int,
			    const struct pfctl_queue_node *);
void			 update_avg(struct queue_stats *);
char			*rate2str(double);

int
pfctl_show_queues(int dev, const char *iface, int opts, int verbose2)
{
	struct pfctl_queue_node	*node;
	int			 nodes, dotitle = (opts & PF_OPT_SHOWALL);


	if ((nodes = pfctl_update_qstats(dev)) <= 0)
		return (nodes);

	TAILQ_FOREACH(node, &qnodes, entries) {
		if (iface != NULL && strcmp(node->qs.ifname, iface))
			continue;
		if (dotitle) {
			pfctl_print_title("QUEUES:");
			dotitle = 0;
		}
		pfctl_print_queue_node(dev, node, opts);
	}

	while (verbose2 && nodes > 0) {
		printf("\n");
		fflush(stdout);
		sleep(STAT_INTERVAL);
		if ((nodes = pfctl_update_qstats(dev)) == -1)
			return (-1);
		TAILQ_FOREACH(node, &qnodes, entries) {
			if (iface != NULL && strcmp(node->qs.ifname, iface))
				continue;
			pfctl_print_queue_node(dev, node, opts);
		}
	}
	while ((node = TAILQ_FIRST(&qnodes)) != NULL)
		TAILQ_REMOVE(&qnodes, node, entries);
	return (0);
}

int
pfctl_update_qstats(int dev)
{
	struct pfctl_queue_node	*node;
	struct pfioc_queue	 pq;
	struct pfioc_qstats	 pqs;
	u_int32_t		 mnr, nr;
	struct queue_stats	 qstats;
	static u_int32_t	 last_ticket;

	memset(&pq, 0, sizeof(pq));
	memset(&pqs, 0, sizeof(pqs));
	memset(&qstats, 0, sizeof(qstats));
	if (ioctl(dev, DIOCGETQUEUES, &pq) == -1) {
		warn("DIOCGETQUEUES");
		return (-1);
	}

	/* if a new set is found, start over */
	if (pq.ticket != last_ticket)
		while ((node = TAILQ_FIRST(&qnodes)) != NULL)
			TAILQ_REMOVE(&qnodes, node, entries);
	last_ticket = pq.ticket;

	mnr = pq.nr;
	for (nr = 0; nr < mnr; ++nr) {
		pqs.nr = nr;
		pqs.ticket = pq.ticket;
		pqs.buf = &qstats.data;
		pqs.nbytes = sizeof(qstats.data);
		if (ioctl(dev, DIOCGETQSTATS, &pqs) == -1) {
			warn("DIOCGETQSTATS");
			return (-1);
		}
		if ((node = pfctl_find_queue_node(pqs.queue.qname,
		    pqs.queue.ifname)) != NULL) {
			memcpy(&node->qstats.data, &qstats.data,
			    sizeof(qstats.data));
			update_avg(&node->qstats);
		} else {
			pfctl_insert_queue_node(pqs.queue, qstats);
		}
	}
	return (mnr);
}

void
pfctl_insert_queue_node(const struct pf_queuespec qs,
    const struct queue_stats qstats)
{
	struct pfctl_queue_node	*node;

	node = calloc(1, sizeof(struct pfctl_queue_node));
	if (node == NULL)
		err(1, "pfctl_insert_queue_node: calloc");
	memcpy(&node->qs, &qs, sizeof(qs));
	memcpy(&node->qstats, &qstats, sizeof(qstats));
	TAILQ_INSERT_TAIL(&qnodes, node, entries);
	update_avg(&node->qstats);
}

struct pfctl_queue_node *
pfctl_find_queue_node(const char *qname, const char *ifname)
{
	struct pfctl_queue_node	*node;

	TAILQ_FOREACH(node, &qnodes, entries)
		if (!strcmp(node->qs.qname, qname)
		    && !(strcmp(node->qs.ifname, ifname)))
			return (node);
	return (NULL);
}

void
pfctl_print_queue_node(int dev, struct pfctl_queue_node *node, int opts)
{
	if (node == NULL)
		return;

	print_queuespec(&node->qs);
	if (opts & PF_OPT_VERBOSE)
		pfctl_print_queue_nodestat(dev, node);

	if (opts & PF_OPT_DEBUG)
		printf("  [ qid=%u parent_qid=%u ifname=%s]\n",
		    node->qs.qid, node->qs.parent_qid, node->qs.ifname);
}

void
pfctl_print_queue_nodestat(int dev, const struct pfctl_queue_node *node)
{
	struct hfsc_class_stats *stats =
	    (struct hfsc_class_stats *)&node->qstats.data.hfsc;
	struct fqcodel_stats *fqstats =
	    (struct fqcodel_stats *)&node->qstats.data.fqc;

	printf("  [ pkts: %10llu  bytes: %10llu  "
	    "dropped pkts: %6llu bytes: %6llu ]\n",
	    (unsigned long long)stats->xmit_cnt.packets,
	    (unsigned long long)stats->xmit_cnt.bytes,
	    (unsigned long long)stats->drop_cnt.packets,
	    (unsigned long long)stats->drop_cnt.bytes);
	if (node->qs.parent_qid == 0 && (node->qs.flags & PFQS_FLOWQUEUE) &&
	    !(node->qs.flags & PFQS_ROOTCLASS)) {
		double avg = 0, dev = 0;

		if (fqstats->flows > 0) {
			avg = (double)fqstats->delaysum /
			    (double)fqstats->flows;
			dev = sqrt(fmax(0, (double)fqstats->delaysumsq /
			    (double)fqstats->flows - avg * avg));
		}

		printf("  [ qlength: %3d/%3d  avg delay: %.3fms std-dev: %.3fms"
		    "  flows: %3d ]\n", stats->qlength, stats->qlimit,
		    avg / 1000, dev / 1000, fqstats->flows);
	} else
		printf("  [ qlength: %3d/%3d ]\n", stats->qlength,
		    stats->qlimit);

	if (node->qstats.avgn < 2)
		return;

	printf("  [ measured: %7.1f packets/s, %s/s ]\n",
	    node->qstats.avg_packets / STAT_INTERVAL,
	    rate2str((8 * node->qstats.avg_bytes) / STAT_INTERVAL));
}

void
update_avg(struct queue_stats *s)
{
	struct hfsc_class_stats *stats =
	    (struct hfsc_class_stats *)&s->data;

	if (s->avgn > 0) {
		if (stats->xmit_cnt.bytes >= s->prev_bytes)
			s->avg_bytes = ((s->avg_bytes * (s->avgn - 1)) +
			    (stats->xmit_cnt.bytes - s->prev_bytes)) /
			    s->avgn;
		if (stats->xmit_cnt.packets >= s->prev_packets)
			s->avg_packets = ((s->avg_packets * (s->avgn - 1)) +
			    (stats->xmit_cnt.packets - s->prev_packets)) /
			    s->avgn;
	}

	s->prev_bytes = stats->xmit_cnt.bytes;
	s->prev_packets = stats->xmit_cnt.packets;
	if (s->avgn < AVGN_MAX)
		s->avgn++;
}

#define	R2S_BUFS	8
#define	RATESTR_MAX	16

char *
rate2str(double rate)
{
	char		*buf;
	static char	 r2sbuf[R2S_BUFS][RATESTR_MAX];  /* ring bufer */
	static int	 idx = 0;
	int		 i;
	static const char unit[] = " KMG";

	buf = r2sbuf[idx++];
	if (idx == R2S_BUFS)
		idx = 0;

	for (i = 0; rate >= 1000 && i <= 3; i++)
		rate /= 1000;

	if ((int)(rate * 100) % 100)
		snprintf(buf, RATESTR_MAX, "%.2f%cb", rate, unit[i]);
	else
		snprintf(buf, RATESTR_MAX, "%d%cb", (int)rate, unit[i]);

	return (buf);
}