mirror of
https://github.com/lgcosta/pf-linux-port.git
synced 2026-11-15 04:44:44 -03:00
M2: live netfilter data path (pf_test per direction), pfattach bring-up, NET_LOCK reentrancy fix, static pfctl
- pfk_hooks: PRE→PF_IN, LOCAL_OUT/POST→PF_OUT v4+v6; replacement/buffer handling with NF_STOLEN + re-injection (documented divergence: re-entry) - pfk_mod: pf_status_init/pfattach + pfi attach via pfk_if event callback - pfk_kern: NET_LOCK holder reentrancy (fix deadlock when notifier fires under the write lock — observed as full-VM network hang) - tools: static pfctl link for musl VMs
This commit is contained in:
1 parent
0119e70960
commit
5323ee2c71
49 files changed
+20028
-28
No files matched your search
+6548
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,381 @@
|
||||
/* $OpenBSD: pf_print_state.c,v 1.69 2019/02/02 15:43:18 yasuoka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001 Daniel Hartmeier
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#define TCPSTATES
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp_fsm.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/pfvar.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pfctl_parser.h"
|
||||
#include "pfctl.h"
|
||||
|
||||
void print_name(struct pf_addr *, sa_family_t);
|
||||
|
||||
void
|
||||
print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose)
|
||||
{
|
||||
switch (addr->type) {
|
||||
case PF_ADDR_DYNIFTL:
|
||||
printf("(%s", addr->v.ifname);
|
||||
if (addr->iflags & PFI_AFLAG_NETWORK)
|
||||
printf(":network");
|
||||
if (addr->iflags & PFI_AFLAG_BROADCAST)
|
||||
printf(":broadcast");
|
||||
if (addr->iflags & PFI_AFLAG_PEER)
|
||||
printf(":peer");
|
||||
if (addr->iflags & PFI_AFLAG_NOALIAS)
|
||||
printf(":0");
|
||||
if (verbose) {
|
||||
if (addr->p.dyncnt <= 0)
|
||||
printf(":*");
|
||||
else
|
||||
printf(":%d", addr->p.dyncnt);
|
||||
}
|
||||
printf(")");
|
||||
break;
|
||||
case PF_ADDR_TABLE:
|
||||
if (verbose)
|
||||
if (addr->p.tblcnt == -1)
|
||||
printf("<%s:*>", addr->v.tblname);
|
||||
else
|
||||
printf("<%s:%d>", addr->v.tblname,
|
||||
addr->p.tblcnt);
|
||||
else
|
||||
printf("<%s>", addr->v.tblname);
|
||||
return;
|
||||
case PF_ADDR_RANGE: {
|
||||
print_addr_str(af, &addr->v.a.addr);
|
||||
printf(" - ");
|
||||
print_addr_str(af, &addr->v.a.mask);
|
||||
break;
|
||||
}
|
||||
case PF_ADDR_ADDRMASK:
|
||||
if (PF_AZERO(&addr->v.a.addr, AF_INET6) &&
|
||||
PF_AZERO(&addr->v.a.mask, AF_INET6))
|
||||
printf("any");
|
||||
else
|
||||
print_addr_str(af, &addr->v.a.addr);
|
||||
break;
|
||||
case PF_ADDR_NOROUTE:
|
||||
printf("no-route");
|
||||
return;
|
||||
case PF_ADDR_URPFFAILED:
|
||||
printf("urpf-failed");
|
||||
return;
|
||||
case PF_ADDR_RTLABEL:
|
||||
printf("route \"%s\"", addr->v.rtlabelname);
|
||||
return;
|
||||
default:
|
||||
printf("?");
|
||||
return;
|
||||
}
|
||||
|
||||
/* mask if not _both_ address and mask are zero */
|
||||
if (addr->type != PF_ADDR_RANGE &&
|
||||
!(PF_AZERO(&addr->v.a.addr, AF_INET6) &&
|
||||
PF_AZERO(&addr->v.a.mask, AF_INET6))) {
|
||||
int bits = unmask(&addr->v.a.mask);
|
||||
|
||||
if (bits < (af == AF_INET ? 32 : 128))
|
||||
printf("/%d", bits);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_addr_str(sa_family_t af, struct pf_addr *addr)
|
||||
{
|
||||
static char buf[48];
|
||||
|
||||
if (inet_ntop(af, addr, buf, sizeof(buf)) == NULL)
|
||||
printf("?");
|
||||
else
|
||||
printf("%s", buf);
|
||||
}
|
||||
|
||||
void
|
||||
print_name(struct pf_addr *addr, sa_family_t af)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
struct sockaddr_in *sin;
|
||||
struct sockaddr_in6 *sin6;
|
||||
char host[NI_MAXHOST];
|
||||
socklen_t slen; /* PFLINUX: sa_len replacement */
|
||||
|
||||
memset(&ss, 0, sizeof(ss));
|
||||
ss.ss_family = af;
|
||||
if (ss.ss_family == AF_INET) {
|
||||
sin = (struct sockaddr_in *)&ss;
|
||||
/* PFLINUX: Linux struct sockaddr(_in) has no sa_len;
|
||||
* getnameinfo(3) takes an explicit length instead. */
|
||||
sin->sin_addr = addr->v4;
|
||||
slen = sizeof(*sin);
|
||||
} else {
|
||||
sin6 = (struct sockaddr_in6 *)&ss;
|
||||
sin6->sin6_addr = addr->v6;
|
||||
slen = sizeof(*sin6);
|
||||
}
|
||||
|
||||
if (getnameinfo((struct sockaddr *)&ss, slen, host, sizeof(host),
|
||||
NULL, 0, NI_NOFQDN) != 0)
|
||||
printf("?");
|
||||
else
|
||||
printf("%s", host);
|
||||
}
|
||||
|
||||
void
|
||||
print_host(struct pf_addr *addr, u_int16_t port, sa_family_t af, u_int16_t rdom,
|
||||
const char *proto, int opts)
|
||||
{
|
||||
struct pf_addr_wrap aw;
|
||||
struct servent *s = NULL;
|
||||
char ps[6];
|
||||
|
||||
if (rdom)
|
||||
printf("(%u) ", ntohs(rdom));
|
||||
|
||||
if (opts & PF_OPT_USEDNS)
|
||||
print_name(addr, af);
|
||||
else {
|
||||
memset(&aw, 0, sizeof(aw));
|
||||
aw.v.a.addr = *addr;
|
||||
memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask));
|
||||
print_addr(&aw, af, opts & PF_OPT_VERBOSE2);
|
||||
}
|
||||
|
||||
if (port) {
|
||||
snprintf(ps, sizeof(ps), "%u", ntohs(port));
|
||||
if (opts & PF_OPT_PORTNAMES)
|
||||
s = getservbyport(port, proto);
|
||||
if (af == AF_INET)
|
||||
printf(":%s", s ? s->s_name : ps);
|
||||
else
|
||||
printf("[%s]", s ? s->s_name : ps);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_seq(struct pfsync_state_peer *p)
|
||||
{
|
||||
if (p->seqdiff)
|
||||
printf("[%u + %u](+%u)", ntohl(p->seqlo),
|
||||
ntohl(p->seqhi) - ntohl(p->seqlo), ntohl(p->seqdiff));
|
||||
else
|
||||
printf("[%u + %u]", ntohl(p->seqlo),
|
||||
ntohl(p->seqhi) - ntohl(p->seqlo));
|
||||
}
|
||||
|
||||
void
|
||||
print_state(struct pfsync_state *s, int opts)
|
||||
{
|
||||
struct pfsync_state_peer *src, *dst;
|
||||
struct pfsync_state_key *sk, *nk;
|
||||
struct protoent *p;
|
||||
char *pn = NULL;
|
||||
int min, sec;
|
||||
int afto = (s->key[PF_SK_STACK].af != s->key[PF_SK_WIRE].af);
|
||||
int idx;
|
||||
|
||||
if (s->direction == PF_OUT) {
|
||||
src = &s->src;
|
||||
dst = &s->dst;
|
||||
sk = &s->key[PF_SK_STACK];
|
||||
nk = &s->key[PF_SK_WIRE];
|
||||
if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6)
|
||||
sk->port[0] = nk->port[0];
|
||||
} else {
|
||||
src = &s->dst;
|
||||
dst = &s->src;
|
||||
sk = &s->key[PF_SK_WIRE];
|
||||
nk = &s->key[PF_SK_STACK];
|
||||
if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6)
|
||||
sk->port[1] = nk->port[1];
|
||||
}
|
||||
printf("%s ", s->ifname);
|
||||
if ((p = getprotobynumber(s->proto)) != NULL) {
|
||||
pn = p->p_name;
|
||||
printf("%s ", pn);
|
||||
} else
|
||||
printf("%u ", s->proto);
|
||||
|
||||
print_host(&nk->addr[1], nk->port[1], nk->af, nk->rdomain, pn, opts);
|
||||
if (nk->af != sk->af || PF_ANEQ(&nk->addr[1], &sk->addr[1], nk->af) ||
|
||||
nk->port[1] != sk->port[1] ||
|
||||
nk->rdomain != sk->rdomain) {
|
||||
idx = afto ? 0 : 1;
|
||||
printf(" (");
|
||||
print_host(&sk->addr[idx], sk->port[idx], sk->af,
|
||||
sk->rdomain, pn, opts);
|
||||
printf(")");
|
||||
}
|
||||
if (s->direction == PF_IN && !PF_AZERO(&s->rt_addr, sk->af)) {
|
||||
printf(" {");
|
||||
print_addr_str(sk->af, &s->rt_addr);
|
||||
printf("}");
|
||||
}
|
||||
if (s->direction == PF_OUT || (afto && s->direction == PF_IN))
|
||||
printf(" -> ");
|
||||
else
|
||||
printf(" <- ");
|
||||
print_host(&nk->addr[0], nk->port[0], nk->af, nk->rdomain, pn, opts);
|
||||
if (nk->af != sk->af || PF_ANEQ(&nk->addr[0], &sk->addr[0], nk->af) ||
|
||||
nk->port[0] != sk->port[0] ||
|
||||
nk->rdomain != sk->rdomain) {
|
||||
idx = afto ? 1 : 0;
|
||||
printf(" (");
|
||||
print_host(&sk->addr[idx], sk->port[idx], sk->af,
|
||||
sk->rdomain, pn, opts);
|
||||
printf(")");
|
||||
}
|
||||
if (s->direction == PF_OUT && !PF_AZERO(&s->rt_addr, nk->af)) {
|
||||
printf(" {");
|
||||
print_addr_str(nk->af, &s->rt_addr);
|
||||
printf("}");
|
||||
}
|
||||
|
||||
printf(" ");
|
||||
if (s->proto == IPPROTO_TCP) {
|
||||
if (src->state <= TCPS_TIME_WAIT &&
|
||||
dst->state <= TCPS_TIME_WAIT)
|
||||
printf(" %s:%s\n", tcpstates[src->state],
|
||||
tcpstates[dst->state]);
|
||||
else if (src->state == PF_TCPS_PROXY_SRC ||
|
||||
dst->state == PF_TCPS_PROXY_SRC)
|
||||
printf(" PROXY:SRC\n");
|
||||
else if (src->state == PF_TCPS_PROXY_DST ||
|
||||
dst->state == PF_TCPS_PROXY_DST)
|
||||
printf(" PROXY:DST\n");
|
||||
else
|
||||
printf(" <BAD STATE LEVELS %u:%u>\n",
|
||||
src->state, dst->state);
|
||||
if (opts & PF_OPT_VERBOSE) {
|
||||
printf(" ");
|
||||
print_seq(src);
|
||||
if (src->wscale && dst->wscale)
|
||||
printf(" wscale %u",
|
||||
src->wscale & PF_WSCALE_MASK);
|
||||
printf(" ");
|
||||
print_seq(dst);
|
||||
if (src->wscale && dst->wscale)
|
||||
printf(" wscale %u",
|
||||
dst->wscale & PF_WSCALE_MASK);
|
||||
printf("\n");
|
||||
}
|
||||
} else if (s->proto == IPPROTO_UDP && src->state < PFUDPS_NSTATES &&
|
||||
dst->state < PFUDPS_NSTATES) {
|
||||
const char *states[] = PFUDPS_NAMES;
|
||||
|
||||
printf(" %s:%s\n", states[src->state], states[dst->state]);
|
||||
} else if (s->proto != IPPROTO_ICMP && s->proto != IPPROTO_ICMPV6 &&
|
||||
src->state < PFOTHERS_NSTATES && dst->state < PFOTHERS_NSTATES) {
|
||||
/* XXX ICMP doesn't really have state levels */
|
||||
const char *states[] = PFOTHERS_NAMES;
|
||||
|
||||
printf(" %s:%s\n", states[src->state], states[dst->state]);
|
||||
} else {
|
||||
printf(" %u:%u\n", src->state, dst->state);
|
||||
}
|
||||
|
||||
if (opts & PF_OPT_VERBOSE) {
|
||||
u_int64_t packets[2];
|
||||
u_int64_t bytes[2];
|
||||
u_int32_t creation = ntohl(s->creation);
|
||||
u_int32_t expire = ntohl(s->expire);
|
||||
|
||||
sec = creation % 60;
|
||||
creation /= 60;
|
||||
min = creation % 60;
|
||||
creation /= 60;
|
||||
printf(" age %.2u:%.2u:%.2u", creation, min, sec);
|
||||
sec = expire % 60;
|
||||
expire /= 60;
|
||||
min = expire % 60;
|
||||
expire /= 60;
|
||||
printf(", expires in %.2u:%.2u:%.2u", expire, min, sec);
|
||||
|
||||
bcopy(s->packets[0], &packets[0], sizeof(u_int64_t));
|
||||
bcopy(s->packets[1], &packets[1], sizeof(u_int64_t));
|
||||
bcopy(s->bytes[0], &bytes[0], sizeof(u_int64_t));
|
||||
bcopy(s->bytes[1], &bytes[1], sizeof(u_int64_t));
|
||||
printf(", %llu:%llu pkts, %llu:%llu bytes",
|
||||
betoh64(packets[0]),
|
||||
betoh64(packets[1]),
|
||||
betoh64(bytes[0]),
|
||||
betoh64(bytes[1]));
|
||||
if (ntohl(s->anchor) != -1)
|
||||
printf(", anchor %u", ntohl(s->anchor));
|
||||
if (ntohl(s->rule) != -1)
|
||||
printf(", rule %u", ntohl(s->rule));
|
||||
if (ntohs(s->state_flags) & PFSTATE_SLOPPY)
|
||||
printf(", sloppy");
|
||||
if (ntohs(s->state_flags) & PFSTATE_PFLOW)
|
||||
printf(", pflow");
|
||||
if (s->sync_flags & PFSYNC_FLAG_SRCNODE)
|
||||
printf(", source-track");
|
||||
if (s->sync_flags & PFSYNC_FLAG_NATSRCNODE)
|
||||
printf(", sticky-address");
|
||||
printf("\n");
|
||||
}
|
||||
if (opts & PF_OPT_VERBOSE2) {
|
||||
u_int64_t id;
|
||||
|
||||
bcopy(&s->id, &id, sizeof(u_int64_t));
|
||||
printf(" id: %016llx creatorid: %08x",
|
||||
betoh64(id), ntohl(s->creatorid));
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
unmask(struct pf_addr *m)
|
||||
{
|
||||
int i = 31, j = 0, b = 0;
|
||||
u_int32_t tmp;
|
||||
|
||||
while (j < 4 && m->addr32[j] == 0xffffffff) {
|
||||
b += 32;
|
||||
j++;
|
||||
}
|
||||
if (j < 4) {
|
||||
tmp = ntohl(m->addr32[j]);
|
||||
for (i = 31; tmp & (1 << i); --i)
|
||||
b++;
|
||||
}
|
||||
return (b);
|
||||
}
|
||||
+3683
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,137 @@
|
||||
/* $OpenBSD: pfctl.h,v 1.66 2026/02/03 10:25:28 sashan Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001 Daniel Hartmeier
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PFCTL_H_
|
||||
#define _PFCTL_H_
|
||||
|
||||
#ifdef PFCTL_DEBUG
|
||||
#define DBGPRINT(...) fprintf(stderr, __VA_ARGS__)
|
||||
#else
|
||||
#define DBGPRINT(...) (void)(0)
|
||||
#endif
|
||||
|
||||
enum pfctl_show { PFCTL_SHOW_RULES, PFCTL_SHOW_LABELS, PFCTL_SHOW_NOTHING };
|
||||
|
||||
enum { PFRB_TABLES = 1, PFRB_TSTATS, PFRB_ADDRS, PFRB_ASTATS,
|
||||
PFRB_IFACES, PFRB_TRANS, PFRB_MAX };
|
||||
struct pfr_buffer {
|
||||
int pfrb_type; /* type of content, see enum above */
|
||||
int pfrb_size; /* number of objects in buffer */
|
||||
int pfrb_msize; /* maximum number of objects in buffer */
|
||||
void *pfrb_caddr; /* malloc'ated memory area */
|
||||
};
|
||||
#define PFRB_FOREACH(var, buf) \
|
||||
for ((var) = pfr_buf_next((buf), NULL); \
|
||||
(var) != NULL; \
|
||||
(var) = pfr_buf_next((buf), (var)))
|
||||
|
||||
|
||||
struct pfr_anchoritem {
|
||||
SLIST_ENTRY(pfr_anchoritem) pfra_sle;
|
||||
char *pfra_anchorname;
|
||||
};
|
||||
|
||||
struct pfr_uktable {
|
||||
struct pfr_ktable pfrukt_kt;
|
||||
struct pfr_buffer pfrukt_addrs;
|
||||
int pfrukt_init_addr;
|
||||
SLIST_ENTRY(pfr_uktable)
|
||||
pfrukt_entry;
|
||||
};
|
||||
|
||||
#define pfrukt_t pfrukt_kt.pfrkt_ts.pfrts_t
|
||||
#define pfrukt_name pfrukt_kt.pfrkt_t.pfrt_name
|
||||
#define pfrukt_anchor pfrukt_kt.pfrkt_t.pfrt_anchor
|
||||
|
||||
extern struct pfr_ktablehead pfr_ktables;
|
||||
|
||||
SLIST_HEAD(pfr_anchors, pfr_anchoritem);
|
||||
|
||||
struct pfctl;
|
||||
|
||||
int pfr_clr_tables(struct pfr_table *, int *, int);
|
||||
int pfr_add_tables(struct pfr_table *, int, int *, int);
|
||||
int pfr_del_tables(struct pfr_table *, int, int *, int);
|
||||
int pfr_get_tables(struct pfr_table *, struct pfr_table *, int *, int);
|
||||
int pfr_get_tstats(struct pfr_table *, struct pfr_tstats *, int *, int);
|
||||
int pfr_clr_tstats(struct pfr_table *, int, int *, int);
|
||||
int pfr_clr_astats(struct pfr_table *, struct pfr_addr *, int, int *, int);
|
||||
int pfr_clr_addrs(struct pfr_table *, int *, int);
|
||||
int pfr_add_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
|
||||
int pfr_del_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
|
||||
int pfr_set_addrs(struct pfr_table *, struct pfr_addr *, int, int *,
|
||||
int *, int *, int *, int);
|
||||
int pfr_get_addrs(struct pfr_table *, struct pfr_addr *, int *, int);
|
||||
int pfr_get_astats(struct pfr_table *, struct pfr_astats *, int *, int);
|
||||
int pfr_tst_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
|
||||
int pfr_ina_define(struct pfr_table *, struct pfr_addr *, int, int *,
|
||||
int *, int, int);
|
||||
void pfr_buf_clear(struct pfr_buffer *);
|
||||
int pfr_buf_add(struct pfr_buffer *, const void *);
|
||||
void *pfr_buf_next(struct pfr_buffer *, const void *);
|
||||
int pfr_buf_grow(struct pfr_buffer *, int);
|
||||
int pfr_buf_load(struct pfr_buffer *, char *, int, int);
|
||||
char *pf_strerror(int);
|
||||
int pfi_get_ifaces(const char *, struct pfi_kif *, int *);
|
||||
|
||||
void pfctl_print_title(char *);
|
||||
int pfctl_clear_tables(const char *, int);
|
||||
void pfctl_show_tables(const char *, int);
|
||||
int pfctl_table(int, char *[], char *, const char *, char *,
|
||||
const char *, int);
|
||||
void warn_duplicate_tables(const char *, const char *);
|
||||
void pfctl_show_ifaces(const char *, int);
|
||||
FILE *pfctl_fopen(const char *, const char *);
|
||||
|
||||
void print_addr(struct pf_addr_wrap *, sa_family_t, int);
|
||||
void print_addr_str(sa_family_t, struct pf_addr *);
|
||||
void print_host(struct pf_addr *, u_int16_t p, sa_family_t, u_int16_t, const char *, int);
|
||||
void print_seq(struct pfsync_state_peer *);
|
||||
void print_state(struct pfsync_state *, int);
|
||||
|
||||
int pfctl_cmdline_symset(char *);
|
||||
int pfctl_add_trans(struct pfr_buffer *, int, const char *);
|
||||
u_int32_t
|
||||
pfctl_get_ticket(struct pfr_buffer *, int, const char *);
|
||||
int pfctl_trans(int, struct pfr_buffer *, u_long, int);
|
||||
|
||||
int pfctl_show_queues(int, const char *, int, int);
|
||||
|
||||
void pfctl_err(int, int, const char *, ...);
|
||||
void pfctl_errx(int, int, const char *, ...);
|
||||
|
||||
const char
|
||||
*pfctl_statelim_id2name(struct pfctl *, u_int8_t);
|
||||
const char
|
||||
*pfctl_sourcelim_id2name(struct pfctl *, u_int8_t);
|
||||
|
||||
#endif /* _PFCTL_H_ */
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,346 @@
|
||||
/* $OpenBSD: pfctl_parser.h,v 1.124 2026/04/09 06:10:38 dlg Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001 Daniel Hartmeier
|
||||
* Copyright (c) 2002 - 2013 Henning Brauer <henning@openbsd.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PFCTL_PARSER_H_
|
||||
#define _PFCTL_PARSER_H_
|
||||
|
||||
#define PF_OSFP_FILE "/etc/pf.os"
|
||||
|
||||
#define PF_OPT_DISABLE 0x00001
|
||||
#define PF_OPT_ENABLE 0x00002
|
||||
#define PF_OPT_VERBOSE 0x00004
|
||||
#define PF_OPT_NOACTION 0x00008
|
||||
#define PF_OPT_QUIET 0x00010
|
||||
#define PF_OPT_CLRRULECTRS 0x00020
|
||||
#define PF_OPT_USEDNS 0x00040
|
||||
#define PF_OPT_VERBOSE2 0x00080
|
||||
#define PF_OPT_DUMMYACTION 0x00100
|
||||
#define PF_OPT_DEBUG 0x00200
|
||||
#define PF_OPT_SHOWALL 0x00400
|
||||
#define PF_OPT_OPTIMIZE 0x00800
|
||||
#define PF_OPT_NODNS 0x01000
|
||||
#define PF_OPT_RECURSE 0x04000
|
||||
#define PF_OPT_PORTNAMES 0x08000
|
||||
#define PF_OPT_IGNFAIL 0x10000
|
||||
#define PF_OPT_CALLSHOW 0x20000
|
||||
|
||||
#define PF_TH_ALL 0xFF
|
||||
|
||||
#define PF_NAT_PROXY_PORT_LOW 50001
|
||||
#define PF_NAT_PROXY_PORT_HIGH 65535
|
||||
|
||||
#define PF_OPTIMIZE_BASIC 0x0001
|
||||
#define PF_OPTIMIZE_PROFILE 0x0002
|
||||
|
||||
#define FCNT_NAMES { \
|
||||
"searches", \
|
||||
"inserts", \
|
||||
"removals", \
|
||||
NULL \
|
||||
}
|
||||
|
||||
struct pfr_buffer; /* forward definition */
|
||||
|
||||
struct pfctl_statelim {
|
||||
struct pfioc_statelim ioc;
|
||||
RBT_ENTRY(pfctl_statelim) id_entry;
|
||||
RBT_ENTRY(pfctl_statelim) nm_entry;
|
||||
};
|
||||
|
||||
RBT_HEAD(pfctl_statelim_ids, pfctl_statelim);
|
||||
RBT_HEAD(pfctl_statelim_nms, pfctl_statelim);
|
||||
|
||||
struct pfctl_sourcelim {
|
||||
struct pfioc_sourcelim ioc;
|
||||
RBT_ENTRY(pfctl_sourcelim) id_entry;
|
||||
RBT_ENTRY(pfctl_sourcelim) nm_entry;
|
||||
};
|
||||
|
||||
RBT_HEAD(pfctl_sourcelim_ids, pfctl_sourcelim);
|
||||
RBT_HEAD(pfctl_sourcelim_nms, pfctl_sourcelim);
|
||||
|
||||
struct pfctl {
|
||||
int dev;
|
||||
int opts;
|
||||
int optimize;
|
||||
int asd; /* anchor stack depth */
|
||||
int bn; /* brace number */
|
||||
int brace;
|
||||
int tdirty; /* kernel dirty */
|
||||
#define PFCTL_ANCHOR_STACK_DEPTH 64
|
||||
struct pf_anchor *astack[PFCTL_ANCHOR_STACK_DEPTH];
|
||||
struct pfioc_queue *pqueue;
|
||||
struct pfr_buffer *trans;
|
||||
struct pf_anchor *anchor, *alast;
|
||||
struct pfr_ktablehead pfr_ktlast;
|
||||
const char *ruleset;
|
||||
|
||||
struct pfctl_statelim_ids statelim_ids;
|
||||
struct pfctl_statelim_nms statelim_nms;
|
||||
struct pfctl_sourcelim_ids sourcelim_ids;
|
||||
struct pfctl_sourcelim_nms sourcelim_nms;
|
||||
|
||||
/* 'set foo' options */
|
||||
u_int32_t timeout[PFTM_MAX];
|
||||
u_int32_t limit[PF_LIMIT_MAX];
|
||||
u_int32_t debug;
|
||||
u_int32_t hostid;
|
||||
u_int32_t reassemble;
|
||||
u_int8_t syncookies;
|
||||
u_int8_t syncookieswat[2]; /* lowat, hiwat */
|
||||
char *ifname;
|
||||
|
||||
u_int8_t timeout_set[PFTM_MAX];
|
||||
u_int8_t limit_set[PF_LIMIT_MAX];
|
||||
u_int8_t debug_set;
|
||||
u_int8_t hostid_set;
|
||||
u_int8_t ifname_set;
|
||||
u_int8_t reass_set;
|
||||
u_int8_t syncookies_set;
|
||||
u_int8_t syncookieswat_set;
|
||||
};
|
||||
|
||||
struct node_if {
|
||||
char ifname[IFNAMSIZ];
|
||||
u_int8_t not;
|
||||
u_int8_t dynamic; /* antispoof */
|
||||
u_int8_t use_rdomain;
|
||||
u_int ifa_flags;
|
||||
int rdomain;
|
||||
struct node_if *next;
|
||||
struct node_if *tail;
|
||||
};
|
||||
|
||||
struct node_host {
|
||||
struct pf_addr_wrap addr;
|
||||
struct pf_addr bcast;
|
||||
struct pf_addr peer;
|
||||
sa_family_t af;
|
||||
u_int8_t not;
|
||||
u_int32_t ifindex; /* link-local IPv6 addrs */
|
||||
u_int16_t weight; /* load balancing weight */
|
||||
char *ifname;
|
||||
u_int ifa_flags;
|
||||
struct node_host *next;
|
||||
struct node_host *tail;
|
||||
};
|
||||
void freehostlist(struct node_host *);
|
||||
|
||||
struct node_os {
|
||||
char *os;
|
||||
pf_osfp_t fingerprint;
|
||||
struct node_os *next;
|
||||
struct node_os *tail;
|
||||
};
|
||||
|
||||
struct node_queue_bw {
|
||||
u_int64_t bw_absolute;
|
||||
u_int16_t bw_percent;
|
||||
};
|
||||
|
||||
struct node_hfsc_sc {
|
||||
struct node_queue_bw m1; /* slope of 1st segment; bps */
|
||||
u_int d; /* x-projection of m1; msec */
|
||||
struct node_queue_bw m2; /* slope of 2nd segment; bps */
|
||||
u_int8_t used;
|
||||
};
|
||||
|
||||
struct node_hfsc_opts {
|
||||
struct node_hfsc_sc realtime;
|
||||
struct node_hfsc_sc linkshare;
|
||||
struct node_hfsc_sc upperlimit;
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct node_queue_opt {
|
||||
int qtype;
|
||||
union {
|
||||
struct priq_opts priq_opts;
|
||||
struct node_hfsc_opts hfsc_opts;
|
||||
} data;
|
||||
};
|
||||
|
||||
SIMPLEQ_HEAD(node_tinithead, node_tinit);
|
||||
struct node_tinit { /* table initializer */
|
||||
SIMPLEQ_ENTRY(node_tinit) entries;
|
||||
struct node_host *host;
|
||||
char *file;
|
||||
};
|
||||
|
||||
|
||||
/* optimizer created tables */
|
||||
struct pf_opt_tbl {
|
||||
char pt_name[PF_TABLE_NAME_SIZE];
|
||||
int pt_rulecount;
|
||||
int pt_generated;
|
||||
u_int32_t pt_flags;
|
||||
u_int32_t pt_refcnt;
|
||||
struct node_tinithead pt_nodes;
|
||||
struct pfr_buffer *pt_buf;
|
||||
};
|
||||
|
||||
/* optimizer pf_rule container */
|
||||
struct pf_opt_rule {
|
||||
struct pf_rule por_rule;
|
||||
struct pf_opt_tbl *por_src_tbl;
|
||||
struct pf_opt_tbl *por_dst_tbl;
|
||||
u_int64_t por_profile_count;
|
||||
TAILQ_ENTRY(pf_opt_rule) por_entry;
|
||||
TAILQ_ENTRY(pf_opt_rule) por_skip_entry[PF_SKIP_COUNT];
|
||||
};
|
||||
|
||||
TAILQ_HEAD(pf_opt_queue, pf_opt_rule);
|
||||
|
||||
extern TAILQ_HEAD(pf_qihead, pfctl_qsitem) qspecs, rootqs;
|
||||
struct pfctl_qsitem {
|
||||
TAILQ_ENTRY(pfctl_qsitem) entries;
|
||||
struct pf_queuespec qs;
|
||||
struct pf_qihead children;
|
||||
int matches;
|
||||
};
|
||||
|
||||
struct pfctl_watermarks {
|
||||
u_int32_t hi;
|
||||
u_int32_t lo;
|
||||
};
|
||||
|
||||
struct pfr_uktable;
|
||||
|
||||
void copy_satopfaddr(struct pf_addr *, struct sockaddr *);
|
||||
|
||||
int pfctl_rules(int, char *, int, int, char *, struct pfr_buffer *);
|
||||
int pfctl_optimize_ruleset(struct pfctl *, struct pf_ruleset *);
|
||||
int pf_opt_create_table(struct pfctl *, struct pf_opt_tbl *);
|
||||
int add_opt_table(struct pfctl *, struct pf_opt_tbl **, sa_family_t,
|
||||
struct pf_rule_addr *, char *);
|
||||
|
||||
void pfctl_add_rule(struct pfctl *, struct pf_rule *);
|
||||
|
||||
int pfctl_add_statelim(struct pfctl *, struct pfctl_statelim *);
|
||||
struct pfctl_statelim *
|
||||
pfctl_get_statelim_id(struct pfctl *, uint32_t);
|
||||
struct pfctl_statelim *
|
||||
pfctl_get_statelim_nm(struct pfctl *, const char *);
|
||||
int pfctl_add_sourcelim(struct pfctl *, struct pfctl_sourcelim *);
|
||||
struct pfctl_sourcelim *
|
||||
pfctl_get_sourcelim_id(struct pfctl *, uint32_t);
|
||||
struct pfctl_sourcelim *
|
||||
pfctl_get_sourcelim_nm(struct pfctl *, const char *);
|
||||
|
||||
int pfctl_set_timeout(struct pfctl *, const char *, int, int);
|
||||
int pfctl_set_reassembly(struct pfctl *, int, int);
|
||||
int pfctl_set_syncookies(struct pfctl *, u_int8_t,
|
||||
struct pfctl_watermarks *);
|
||||
int pfctl_set_optimization(struct pfctl *, const char *);
|
||||
int pfctl_set_limit(struct pfctl *, const char *, unsigned int);
|
||||
int pfctl_set_logif(struct pfctl *, char *);
|
||||
void pfctl_set_hostid(struct pfctl *, u_int32_t);
|
||||
int pfctl_set_debug(struct pfctl *, char *);
|
||||
int pfctl_set_interface_flags(struct pfctl *, char *, int, int);
|
||||
|
||||
int parse_config(char *, struct pfctl *);
|
||||
int parse_flags(char *);
|
||||
int pfctl_load_anchors(int, struct pfctl *);
|
||||
|
||||
int pfctl_load_queues(struct pfctl *);
|
||||
int pfctl_add_queue(struct pfctl *, struct pf_queuespec *);
|
||||
struct pfctl_qsitem * pfctl_find_queue(char *, struct pf_qihead *);
|
||||
|
||||
void print_pool(struct pf_pool *, u_int16_t, u_int16_t, sa_family_t, int, int);
|
||||
void print_src_node(struct pf_src_node *, int);
|
||||
void print_statelim(const struct pfioc_statelim *);
|
||||
void print_sourcelim(const struct pfioc_sourcelim *);
|
||||
void print_rule(struct pfctl *pf, struct pf_rule *, const char *, int);
|
||||
void print_tabledef(const char *, int, int, struct node_tinithead *);
|
||||
void print_status(struct pf_status *, struct pfctl_watermarks *, int);
|
||||
void print_queuespec(struct pf_queuespec *);
|
||||
|
||||
int pfctl_define_table(char *, int, int, const char *, struct pfr_buffer *,
|
||||
u_int32_t, struct pfr_uktable *);
|
||||
void pfctl_expand_label_nr(struct pf_rule *, unsigned int);
|
||||
|
||||
void pfctl_clear_fingerprints(int, int);
|
||||
int pfctl_file_fingerprints(int, int, const char *);
|
||||
pf_osfp_t pfctl_get_fingerprint(const char *);
|
||||
int pfctl_load_fingerprints(int, int);
|
||||
char *pfctl_lookup_fingerprint(pf_osfp_t, char *, size_t);
|
||||
void pfctl_show_fingerprints(int);
|
||||
|
||||
struct icmptypeent {
|
||||
const char *name;
|
||||
u_int8_t type;
|
||||
};
|
||||
|
||||
struct icmpcodeent {
|
||||
const char *name;
|
||||
u_int8_t type;
|
||||
u_int8_t code;
|
||||
};
|
||||
|
||||
/* PFLINUX: last arg is the address family; the definitions below use
|
||||
* sa_family_t, which on Linux is `unsigned short` (OpenBSD: u_int8_t).
|
||||
* Header and definition must agree. */
|
||||
const struct icmptypeent *geticmptypebynumber(u_int8_t, sa_family_t);
|
||||
const struct icmptypeent *geticmptypebyname(char *, sa_family_t);
|
||||
const struct icmpcodeent *geticmpcodebynumber(u_int8_t, u_int8_t, sa_family_t);
|
||||
const struct icmpcodeent *geticmpcodebyname(u_long, char *, sa_family_t);
|
||||
|
||||
int string_to_loglevel(const char *);
|
||||
const char *loglevel_to_string(int);
|
||||
|
||||
struct pf_timeout {
|
||||
const char *name;
|
||||
int timeout;
|
||||
};
|
||||
|
||||
extern const struct pf_timeout pf_timeouts[];
|
||||
|
||||
void set_ipmask(struct node_host *, int);
|
||||
int check_netmask(struct node_host *, sa_family_t);
|
||||
int unmask(struct pf_addr *);
|
||||
struct node_host *gen_dynnode(struct node_host *, sa_family_t);
|
||||
void ifa_load(void);
|
||||
unsigned int ifa_nametoindex(const char *);
|
||||
char *ifa_indextoname(unsigned int, char *);
|
||||
struct node_host *ifa_exists(const char *);
|
||||
struct node_host *ifa_lookup(const char *, int);
|
||||
struct node_host *host(const char *, int);
|
||||
|
||||
int append_addr(struct pfr_buffer *, char *, int, int);
|
||||
int append_addr_host(struct pfr_buffer *,
|
||||
struct node_host *, int, int);
|
||||
int pfr_ktable_compare(struct pfr_ktable *,
|
||||
struct pfr_ktable *);
|
||||
RB_PROTOTYPE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare);
|
||||
|
||||
#endif /* _PFCTL_PARSER_H_ */
|
||||
@@ -0,0 +1,287 @@
|
||||
/* $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);
|
||||
}
|
||||
@@ -0,0 +1,598 @@
|
||||
/* $OpenBSD: pfctl_radix.c,v 1.40 2024/11/20 13:57:29 kirill Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002 Cedric Berger
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
#include <net/pfvar.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "pfctl.h"
|
||||
#include "pfctl_parser.h"
|
||||
|
||||
#define BUF_SIZE 256
|
||||
|
||||
extern int dev;
|
||||
|
||||
static int pfr_next_token(char buf[BUF_SIZE], FILE *);
|
||||
|
||||
struct pfr_ktablehead pfr_ktables = { 0 };
|
||||
RB_GENERATE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare);
|
||||
|
||||
int
|
||||
pfr_ktable_compare(struct pfr_ktable *p, struct pfr_ktable *q)
|
||||
{
|
||||
int d;
|
||||
|
||||
if ((d = strncmp(p->pfrkt_name, q->pfrkt_name, PF_TABLE_NAME_SIZE)))
|
||||
return (d);
|
||||
return (strcmp(p->pfrkt_anchor, q->pfrkt_anchor));
|
||||
}
|
||||
|
||||
int
|
||||
pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags)
|
||||
{
|
||||
struct pfioc_table io;
|
||||
|
||||
bzero(&io, sizeof io);
|
||||
io.pfrio_flags = flags;
|
||||
if (filter != NULL)
|
||||
io.pfrio_table = *filter;
|
||||
if (ioctl(dev, DIOCRCLRTABLES, &io) == -1)
|
||||
return (-1);
|
||||
if (ndel != NULL)
|
||||
*ndel = io.pfrio_ndel;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags)
|
||||
{
|
||||
struct pfioc_table io;
|
||||
|
||||
if (size < 0 || (size && tbl == NULL)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
bzero(&io, sizeof io);
|
||||
io.pfrio_flags = flags;
|
||||
io.pfrio_buffer = tbl;
|
||||
io.pfrio_esize = sizeof(*tbl);
|
||||
io.pfrio_size = size;
|
||||
if (ioctl(dev, DIOCRADDTABLES, &io) == -1)
|
||||
return (-1);
|
||||
if (nadd != NULL)
|
||||
*nadd = io.pfrio_nadd;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags)
|
||||
{
|
||||
struct pfioc_table io;
|
||||
|
||||
if (size < 0 || (size && tbl == NULL)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
bzero(&io, sizeof io);
|
||||
io.pfrio_flags = flags;
|
||||
io.pfrio_buffer = tbl;
|
||||
io.pfrio_esize = sizeof(*tbl);
|
||||
io.pfrio_size = size;
|
||||
if (ioctl(dev, DIOCRDELTABLES, &io) == -1)
|
||||
return (-1);
|
||||
if (ndel != NULL)
|
||||
*ndel = io.pfrio_ndel;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size,
|
||||
int flags)
|
||||
{
|
||||
struct pfioc_table io;
|
||||
|
||||
if (size == NULL || *size < 0 || (*size && tbl == NULL)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
bzero(&io, sizeof io);
|
||||
io.pfrio_flags = flags;
|
||||
if (filter != NULL)
|
||||
io.pfrio_table = *filter;
|
||||
io.pfrio_buffer = tbl;
|
||||
io.pfrio_esize = sizeof(*tbl);
|
||||
io.pfrio_size = *size;
|
||||
if (ioctl(dev, DIOCRGETTABLES, &io) == -1)
|
||||
return (-1);
|
||||
*size = io.pfrio_size;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size,
|
||||
int flags)
|
||||
{
|
||||
struct pfioc_table io;
|
||||
|
||||
if (size == NULL || *size < 0 || (*size && tbl == NULL)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
bzero(&io, sizeof io);
|
||||
io.pfrio_flags = flags;
|
||||
if (filter != NULL)
|
||||
io.pfrio_table = *filter;
|
||||
io.pfrio_buffer = tbl;
|
||||
io.pfrio_esize = sizeof(*tbl);
|
||||
io.pfrio_size = *size;
|
||||
if (ioctl(dev, DIOCRGETTSTATS, &io) == -1)
|
||||
return (-1);
|
||||
*size = io.pfrio_size;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags)
|
||||
{
|
||||
struct pfioc_table io;
|
||||
|
||||
if (tbl == NULL) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
bzero(&io, sizeof io);
|
||||
io.pfrio_flags = flags;
|
||||
io.pfrio_table = *tbl;
|
||||
if (ioctl(dev, DIOCRCLRADDRS, &io) == -1)
|
||||
return (-1);
|
||||
if (ndel != NULL)
|
||||
*ndel = io.pfrio_ndel;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
|
||||
int *nadd, int flags)
|
||||
{
|
||||
struct pfioc_table io;
|
||||
|
||||
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
bzero(&io, sizeof io);
|
||||
io.pfrio_flags = flags;
|
||||
io.pfrio_table = *tbl;
|
||||
io.pfrio_buffer = addr;
|
||||
io.pfrio_esize = sizeof(*addr);
|
||||
io.pfrio_size = size;
|
||||
if (ioctl(dev, DIOCRADDADDRS, &io) == -1)
|
||||
return (-1);
|
||||
if (nadd != NULL)
|
||||
*nadd = io.pfrio_nadd;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
|
||||
int *ndel, int flags)
|
||||
{
|
||||
struct pfioc_table io;
|
||||
|
||||
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
bzero(&io, sizeof io);
|
||||
io.pfrio_flags = flags;
|
||||
io.pfrio_table = *tbl;
|
||||
io.pfrio_buffer = addr;
|
||||
io.pfrio_esize = sizeof(*addr);
|
||||
io.pfrio_size = size;
|
||||
if (ioctl(dev, DIOCRDELADDRS, &io) == -1)
|
||||
return (-1);
|
||||
if (ndel != NULL)
|
||||
*ndel = io.pfrio_ndel;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
|
||||
int *size2, int *nadd, int *ndel, int *nchange, int flags)
|
||||
{
|
||||
struct pfioc_table io;
|
||||
|
||||
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
bzero(&io, sizeof io);
|
||||
io.pfrio_flags = flags;
|
||||
io.pfrio_table = *tbl;
|
||||
io.pfrio_buffer = addr;
|
||||
io.pfrio_esize = sizeof(*addr);
|
||||
io.pfrio_size = size;
|
||||
io.pfrio_size2 = (size2 != NULL) ? *size2 : 0;
|
||||
if (ioctl(dev, DIOCRSETADDRS, &io) == -1)
|
||||
return (-1);
|
||||
if (nadd != NULL)
|
||||
*nadd = io.pfrio_nadd;
|
||||
if (ndel != NULL)
|
||||
*ndel = io.pfrio_ndel;
|
||||
if (nchange != NULL)
|
||||
*nchange = io.pfrio_nchange;
|
||||
if (size2 != NULL)
|
||||
*size2 = io.pfrio_size2;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size,
|
||||
int flags)
|
||||
{
|
||||
struct pfioc_table io;
|
||||
|
||||
if (tbl == NULL || size == NULL || *size < 0 ||
|
||||
(*size && addr == NULL)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
bzero(&io, sizeof io);
|
||||
io.pfrio_flags = flags;
|
||||
io.pfrio_table = *tbl;
|
||||
io.pfrio_buffer = addr;
|
||||
io.pfrio_esize = sizeof(*addr);
|
||||
io.pfrio_size = *size;
|
||||
if (ioctl(dev, DIOCRGETADDRS, &io) == -1)
|
||||
return (-1);
|
||||
*size = io.pfrio_size;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size,
|
||||
int flags)
|
||||
{
|
||||
struct pfioc_table io;
|
||||
|
||||
if (tbl == NULL || size == NULL || *size < 0 ||
|
||||
(*size && addr == NULL)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
bzero(&io, sizeof io);
|
||||
io.pfrio_flags = flags;
|
||||
io.pfrio_table = *tbl;
|
||||
io.pfrio_buffer = addr;
|
||||
io.pfrio_esize = sizeof(*addr);
|
||||
io.pfrio_size = *size;
|
||||
if (ioctl(dev, DIOCRGETASTATS, &io) == -1)
|
||||
return (-1);
|
||||
*size = io.pfrio_size;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size,
|
||||
int *nzero, int flags)
|
||||
{
|
||||
struct pfioc_table io;
|
||||
|
||||
if (size < 0 || (size && !tbl) || addr == NULL) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
bzero(&io, sizeof io);
|
||||
io.pfrio_flags = flags;
|
||||
io.pfrio_table = *tbl;
|
||||
io.pfrio_buffer = addr;
|
||||
io.pfrio_esize = sizeof(*addr);
|
||||
io.pfrio_size = size;
|
||||
if (ioctl(dev, DIOCRCLRASTATS, &io) == -1)
|
||||
return (-1);
|
||||
if (nzero)
|
||||
*nzero = io.pfrio_nzero;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags)
|
||||
{
|
||||
struct pfioc_table io;
|
||||
|
||||
if (size < 0 || (size && !tbl)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
bzero(&io, sizeof io);
|
||||
io.pfrio_flags = flags;
|
||||
io.pfrio_buffer = tbl;
|
||||
io.pfrio_esize = sizeof(*tbl);
|
||||
io.pfrio_size = size;
|
||||
if (ioctl(dev, DIOCRCLRTSTATS, &io) == -1)
|
||||
return (-1);
|
||||
if (nzero)
|
||||
*nzero = io.pfrio_nzero;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
|
||||
int *nmatch, int flags)
|
||||
{
|
||||
struct pfioc_table io;
|
||||
|
||||
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
bzero(&io, sizeof io);
|
||||
io.pfrio_flags = flags;
|
||||
io.pfrio_table = *tbl;
|
||||
io.pfrio_buffer = addr;
|
||||
io.pfrio_esize = sizeof(*addr);
|
||||
io.pfrio_size = size;
|
||||
if (ioctl(dev, DIOCRTSTADDRS, &io) == -1)
|
||||
return (-1);
|
||||
if (nmatch)
|
||||
*nmatch = io.pfrio_nmatch;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size,
|
||||
int *nadd, int *naddr, int ticket, int flags)
|
||||
{
|
||||
struct pfioc_table io;
|
||||
|
||||
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
|
||||
DBGPRINT("%s %p %d %p\n", __func__, tbl, size, addr);
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
bzero(&io, sizeof io);
|
||||
io.pfrio_flags = flags;
|
||||
io.pfrio_table = *tbl;
|
||||
io.pfrio_buffer = addr;
|
||||
io.pfrio_esize = sizeof(*addr);
|
||||
io.pfrio_size = size;
|
||||
io.pfrio_ticket = ticket;
|
||||
if (ioctl(dev, DIOCRINADEFINE, &io) == -1)
|
||||
return (-1);
|
||||
if (nadd != NULL)
|
||||
*nadd = io.pfrio_nadd;
|
||||
if (naddr != NULL)
|
||||
*naddr = io.pfrio_naddr;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* interface management code */
|
||||
|
||||
int
|
||||
pfi_get_ifaces(const char *filter, struct pfi_kif *buf, int *size)
|
||||
{
|
||||
struct pfioc_iface io;
|
||||
|
||||
if (size == NULL || *size < 0 || (*size && buf == NULL)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
bzero(&io, sizeof io);
|
||||
if (filter != NULL)
|
||||
if (strlcpy(io.pfiio_name, filter, sizeof(io.pfiio_name)) >=
|
||||
sizeof(io.pfiio_name)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
io.pfiio_buffer = buf;
|
||||
io.pfiio_esize = sizeof(*buf);
|
||||
io.pfiio_size = *size;
|
||||
if (ioctl(dev, DIOCIGETIFACES, &io) == -1)
|
||||
return (-1);
|
||||
*size = io.pfiio_size;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* buffer management code */
|
||||
|
||||
size_t buf_esize[PFRB_MAX] = { 0,
|
||||
sizeof(struct pfr_table), sizeof(struct pfr_tstats),
|
||||
sizeof(struct pfr_addr), sizeof(struct pfr_astats),
|
||||
sizeof(struct pfi_kif), sizeof(struct pfioc_trans_e)
|
||||
};
|
||||
|
||||
/*
|
||||
* add one element to the buffer
|
||||
*/
|
||||
int
|
||||
pfr_buf_add(struct pfr_buffer *b, const void *e)
|
||||
{
|
||||
size_t bs;
|
||||
|
||||
if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX ||
|
||||
e == NULL) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
bs = buf_esize[b->pfrb_type];
|
||||
if (b->pfrb_size == b->pfrb_msize)
|
||||
if (pfr_buf_grow(b, 0))
|
||||
return (-1);
|
||||
memcpy(((caddr_t)b->pfrb_caddr) + bs * b->pfrb_size, e, bs);
|
||||
b->pfrb_size++;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* return next element of the buffer (or first one if prev is NULL)
|
||||
* see PFRB_FOREACH macro
|
||||
*/
|
||||
void *
|
||||
pfr_buf_next(struct pfr_buffer *b, const void *prev)
|
||||
{
|
||||
size_t bs;
|
||||
|
||||
if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX)
|
||||
return (NULL);
|
||||
if (b->pfrb_size == 0)
|
||||
return (NULL);
|
||||
if (prev == NULL)
|
||||
return (b->pfrb_caddr);
|
||||
bs = buf_esize[b->pfrb_type];
|
||||
if ((((caddr_t)prev)-((caddr_t)b->pfrb_caddr)) / bs >= b->pfrb_size-1)
|
||||
return (NULL);
|
||||
return (((caddr_t)prev) + bs);
|
||||
}
|
||||
|
||||
/*
|
||||
* minsize:
|
||||
* 0: make the buffer somewhat bigger
|
||||
* n: make room for "n" entries in the buffer
|
||||
*/
|
||||
int
|
||||
pfr_buf_grow(struct pfr_buffer *b, int minsize)
|
||||
{
|
||||
caddr_t p;
|
||||
size_t bs;
|
||||
|
||||
if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
if (minsize != 0 && minsize <= b->pfrb_msize)
|
||||
return (0);
|
||||
bs = buf_esize[b->pfrb_type];
|
||||
if (!b->pfrb_msize) {
|
||||
if (minsize < 64)
|
||||
minsize = 64;
|
||||
}
|
||||
if (minsize == 0)
|
||||
minsize = b->pfrb_msize * 2;
|
||||
p = reallocarray(b->pfrb_caddr, minsize, bs);
|
||||
if (p == NULL)
|
||||
return (-1);
|
||||
bzero(p + b->pfrb_msize * bs, (minsize - b->pfrb_msize) * bs);
|
||||
b->pfrb_caddr = p;
|
||||
b->pfrb_msize = minsize;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* reset buffer and free memory.
|
||||
*/
|
||||
void
|
||||
pfr_buf_clear(struct pfr_buffer *b)
|
||||
{
|
||||
if (b == NULL)
|
||||
return;
|
||||
free(b->pfrb_caddr);
|
||||
b->pfrb_caddr = NULL;
|
||||
b->pfrb_size = b->pfrb_msize = 0;
|
||||
}
|
||||
|
||||
int
|
||||
pfr_buf_load(struct pfr_buffer *b, char *file, int nonetwork, int opts)
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[BUF_SIZE];
|
||||
int rv;
|
||||
int ev = 0;
|
||||
|
||||
if (file == NULL)
|
||||
return (0);
|
||||
if (!strcmp(file, "-"))
|
||||
fp = stdin;
|
||||
else {
|
||||
fp = pfctl_fopen(file, "r");
|
||||
if (fp == NULL)
|
||||
return (-1);
|
||||
}
|
||||
while ((rv = pfr_next_token(buf, fp)) == 1)
|
||||
if ((ev = append_addr(b, buf, nonetwork, opts)) == -1) {
|
||||
rv = -1;
|
||||
break;
|
||||
}
|
||||
if (ev == 1) /* expected further append_addr call */
|
||||
rv = -1;
|
||||
if (fp != stdin)
|
||||
fclose(fp);
|
||||
return (rv);
|
||||
}
|
||||
|
||||
int
|
||||
pfr_next_token(char buf[BUF_SIZE], FILE *fp)
|
||||
{
|
||||
static char next_ch = ' ';
|
||||
int i = 0;
|
||||
|
||||
for (;;) {
|
||||
/* skip spaces */
|
||||
while (isspace((unsigned char)next_ch) && !feof(fp))
|
||||
next_ch = fgetc(fp);
|
||||
/* remove from '#' until end of line */
|
||||
if (next_ch == '#')
|
||||
while (!feof(fp)) {
|
||||
next_ch = fgetc(fp);
|
||||
if (next_ch == '\n')
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (feof(fp)) {
|
||||
next_ch = ' ';
|
||||
return (0);
|
||||
}
|
||||
do {
|
||||
if (i < BUF_SIZE)
|
||||
buf[i++] = next_ch;
|
||||
next_ch = fgetc(fp);
|
||||
} while (!feof(fp) && !isspace((unsigned char)next_ch));
|
||||
if (i >= BUF_SIZE) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
buf[i] = '\0';
|
||||
return (1);
|
||||
}
|
||||
@@ -0,0 +1,692 @@
|
||||
/* $OpenBSD: pfctl_table.c,v 1.91 2024/11/20 13:57:29 kirill Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002 Cedric Berger
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <net/pfvar.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "pfctl_parser.h"
|
||||
#include "pfctl.h"
|
||||
|
||||
extern void usage(void);
|
||||
static void print_table(struct pfr_table *, int, int);
|
||||
static void print_tstats(struct pfr_tstats *, int);
|
||||
static int load_addr(struct pfr_buffer *, int, char *[], char *, int, int);
|
||||
static void print_addrx(struct pfr_addr *, struct pfr_addr *, int);
|
||||
static void print_astats(struct pfr_astats *, int);
|
||||
static void xprintf(int, const char *, ...);
|
||||
static void print_iface(struct pfi_kif *, int);
|
||||
|
||||
static const char *stats_text[PFR_DIR_MAX][PFR_OP_TABLE_MAX] = {
|
||||
{ "In/Block:", "In/Match:", "In/Pass:", "In/XPass:" },
|
||||
{ "Out/Block:", "Out/Match:", "Out/Pass:", "Out/XPass:" }
|
||||
};
|
||||
|
||||
static const char *istats_text[2][2][2] = {
|
||||
{ { "In4/Pass:", "In4/Block:" }, { "Out4/Pass:", "Out4/Block:" } },
|
||||
{ { "In6/Pass:", "In6/Block:" }, { "Out6/Pass:", "Out6/Block:" } }
|
||||
};
|
||||
|
||||
#define RVTEST(fct) do { \
|
||||
if ((!(opts & PF_OPT_NOACTION) || \
|
||||
(opts & PF_OPT_DUMMYACTION)) && \
|
||||
(fct)) { \
|
||||
if ((opts & PF_OPT_RECURSE) == 0) \
|
||||
warnx("%s", pf_strerror(errno)); \
|
||||
goto _error; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CREATE_TABLE do { \
|
||||
warn_duplicate_tables(table.pfrt_name, \
|
||||
table.pfrt_anchor); \
|
||||
table.pfrt_flags |= PFR_TFLAG_PERSIST; \
|
||||
if ((!(opts & PF_OPT_NOACTION) || \
|
||||
(opts & PF_OPT_DUMMYACTION)) && \
|
||||
(pfr_add_tables(&table, 1, &nadd, flags)) && \
|
||||
(errno != EPERM)) { \
|
||||
warnx("%s", pf_strerror(errno)); \
|
||||
goto _error; \
|
||||
} \
|
||||
if (nadd) { \
|
||||
xprintf(opts, "%d table created", nadd); \
|
||||
if (opts & PF_OPT_NOACTION) \
|
||||
return (0); \
|
||||
} \
|
||||
table.pfrt_flags &= ~PFR_TFLAG_PERSIST; \
|
||||
} while(0)
|
||||
|
||||
int
|
||||
pfctl_clear_tables(const char *anchor, int opts)
|
||||
{
|
||||
int rv;
|
||||
|
||||
if ((rv = pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts)) == -1) {
|
||||
if ((opts & PF_OPT_IGNFAIL) == 0)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return (rv);
|
||||
}
|
||||
|
||||
void
|
||||
pfctl_show_tables(const char *anchor, int opts)
|
||||
{
|
||||
if (pfctl_table(0, NULL, NULL, "-s", NULL, anchor, opts) == -1)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int
|
||||
pfctl_table(int argc, char *argv[], char *tname, const char *command,
|
||||
char *file, const char *anchor, int opts)
|
||||
{
|
||||
struct pfr_table table;
|
||||
struct pfr_buffer b, b2;
|
||||
struct pfr_addr *a, *a2;
|
||||
int nadd = 0, ndel = 0, nchange = 0, nzero = 0;
|
||||
int rv = 0, flags = 0, nmatch = 0;
|
||||
void *p;
|
||||
|
||||
if (command == NULL)
|
||||
usage();
|
||||
if (opts & PF_OPT_NOACTION)
|
||||
flags |= PFR_FLAG_DUMMY;
|
||||
|
||||
bzero(&b, sizeof(b));
|
||||
bzero(&b2, sizeof(b2));
|
||||
bzero(&table, sizeof(table));
|
||||
if (tname != NULL) {
|
||||
if (strlen(tname) >= PF_TABLE_NAME_SIZE)
|
||||
usage();
|
||||
if (strlcpy(table.pfrt_name, tname,
|
||||
sizeof(table.pfrt_name)) >= sizeof(table.pfrt_name))
|
||||
errx(1, "pfctl_table: strlcpy");
|
||||
}
|
||||
if (strlcpy(table.pfrt_anchor, anchor,
|
||||
sizeof(table.pfrt_anchor)) >= sizeof(table.pfrt_anchor))
|
||||
errx(1, "pfctl_table: strlcpy");
|
||||
|
||||
if (!strcmp(command, "-F")) {
|
||||
if (argc || file != NULL)
|
||||
usage();
|
||||
RVTEST(pfr_clr_tables(&table, &ndel, flags));
|
||||
xprintf(opts, "%d tables deleted", ndel);
|
||||
} else if (!strcmp(command, "-s")) {
|
||||
b.pfrb_type = (opts & PF_OPT_VERBOSE2) ?
|
||||
PFRB_TSTATS : PFRB_TABLES;
|
||||
if (argc || file != NULL)
|
||||
usage();
|
||||
for (;;) {
|
||||
pfr_buf_grow(&b, b.pfrb_size);
|
||||
b.pfrb_size = b.pfrb_msize;
|
||||
if (opts & PF_OPT_VERBOSE2)
|
||||
RVTEST(pfr_get_tstats(&table,
|
||||
b.pfrb_caddr, &b.pfrb_size, flags));
|
||||
else
|
||||
RVTEST(pfr_get_tables(&table,
|
||||
b.pfrb_caddr, &b.pfrb_size, flags));
|
||||
if (b.pfrb_size <= b.pfrb_msize)
|
||||
break;
|
||||
}
|
||||
|
||||
if ((opts & PF_OPT_SHOWALL) && b.pfrb_size > 0)
|
||||
pfctl_print_title("TABLES:");
|
||||
|
||||
PFRB_FOREACH(p, &b)
|
||||
if (opts & PF_OPT_VERBOSE2)
|
||||
print_tstats(p, opts & PF_OPT_DEBUG);
|
||||
else
|
||||
print_table(p, opts & PF_OPT_VERBOSE,
|
||||
opts & PF_OPT_DEBUG);
|
||||
} else if (!strcmp(command, "kill")) {
|
||||
if (argc || file != NULL)
|
||||
usage();
|
||||
RVTEST(pfr_del_tables(&table, 1, &ndel, flags));
|
||||
xprintf(opts, "%d table deleted", ndel);
|
||||
} else if (!strcmp(command, "flush")) {
|
||||
if (argc || file != NULL)
|
||||
usage();
|
||||
RVTEST(pfr_clr_addrs(&table, &ndel, flags));
|
||||
xprintf(opts, "%d addresses deleted", ndel);
|
||||
} else if (!strcmp(command, "add")) {
|
||||
b.pfrb_type = PFRB_ADDRS;
|
||||
if (load_addr(&b, argc, argv, file, 0, opts))
|
||||
goto _error;
|
||||
CREATE_TABLE;
|
||||
if (opts & PF_OPT_VERBOSE)
|
||||
flags |= PFR_FLAG_FEEDBACK;
|
||||
RVTEST(pfr_add_addrs(&table, b.pfrb_caddr, b.pfrb_size,
|
||||
&nadd, flags));
|
||||
xprintf(opts, "%d/%d addresses added", nadd, b.pfrb_size);
|
||||
if (opts & PF_OPT_VERBOSE)
|
||||
PFRB_FOREACH(a, &b)
|
||||
if (opts & PF_OPT_VERBOSE2 ||
|
||||
a->pfra_fback != PFR_FB_NONE)
|
||||
print_addrx(a, NULL,
|
||||
opts & PF_OPT_USEDNS);
|
||||
} else if (!strcmp(command, "delete")) {
|
||||
b.pfrb_type = PFRB_ADDRS;
|
||||
if (load_addr(&b, argc, argv, file, 0, opts))
|
||||
goto _error;
|
||||
if (opts & PF_OPT_VERBOSE)
|
||||
flags |= PFR_FLAG_FEEDBACK;
|
||||
RVTEST(pfr_del_addrs(&table, b.pfrb_caddr, b.pfrb_size,
|
||||
&ndel, flags));
|
||||
xprintf(opts, "%d/%d addresses deleted", ndel, b.pfrb_size);
|
||||
if (opts & PF_OPT_VERBOSE)
|
||||
PFRB_FOREACH(a, &b)
|
||||
if (opts & PF_OPT_VERBOSE2 ||
|
||||
a->pfra_fback != PFR_FB_NONE)
|
||||
print_addrx(a, NULL,
|
||||
opts & PF_OPT_USEDNS);
|
||||
} else if (!strcmp(command, "replace")) {
|
||||
b.pfrb_type = PFRB_ADDRS;
|
||||
if (load_addr(&b, argc, argv, file, 0, opts))
|
||||
goto _error;
|
||||
CREATE_TABLE;
|
||||
if (opts & PF_OPT_VERBOSE)
|
||||
flags |= PFR_FLAG_FEEDBACK;
|
||||
for (;;) {
|
||||
int sz2 = b.pfrb_msize;
|
||||
|
||||
RVTEST(pfr_set_addrs(&table, b.pfrb_caddr, b.pfrb_size,
|
||||
&sz2, &nadd, &ndel, &nchange, flags));
|
||||
if (sz2 <= b.pfrb_msize) {
|
||||
b.pfrb_size = sz2;
|
||||
break;
|
||||
} else
|
||||
pfr_buf_grow(&b, sz2);
|
||||
}
|
||||
if (nadd)
|
||||
xprintf(opts, "%d addresses added", nadd);
|
||||
if (ndel)
|
||||
xprintf(opts, "%d addresses deleted", ndel);
|
||||
if (nchange)
|
||||
xprintf(opts, "%d addresses changed", nchange);
|
||||
if (!nadd && !ndel && !nchange)
|
||||
xprintf(opts, "no changes");
|
||||
if (opts & PF_OPT_VERBOSE)
|
||||
PFRB_FOREACH(a, &b)
|
||||
if (opts & PF_OPT_VERBOSE2 ||
|
||||
a->pfra_fback != PFR_FB_NONE)
|
||||
print_addrx(a, NULL,
|
||||
opts & PF_OPT_USEDNS);
|
||||
} else if (!strcmp(command, "expire")) {
|
||||
const char *errstr;
|
||||
u_int lifetime;
|
||||
|
||||
b.pfrb_type = PFRB_ASTATS;
|
||||
b2.pfrb_type = PFRB_ADDRS;
|
||||
if (argc != 1 || file != NULL)
|
||||
usage();
|
||||
lifetime = strtonum(*argv, 0, UINT_MAX, &errstr);
|
||||
if (errstr)
|
||||
errx(1, "expiry time: %s", errstr);
|
||||
for (;;) {
|
||||
pfr_buf_grow(&b, b.pfrb_size);
|
||||
b.pfrb_size = b.pfrb_msize;
|
||||
RVTEST(pfr_get_astats(&table, b.pfrb_caddr,
|
||||
&b.pfrb_size, flags));
|
||||
if (b.pfrb_size <= b.pfrb_msize)
|
||||
break;
|
||||
}
|
||||
PFRB_FOREACH(p, &b) {
|
||||
((struct pfr_astats *)p)->pfras_a.pfra_fback = PFR_FB_NONE;
|
||||
if (time(NULL) - ((struct pfr_astats *)p)->pfras_tzero >
|
||||
lifetime)
|
||||
if (pfr_buf_add(&b2,
|
||||
&((struct pfr_astats *)p)->pfras_a))
|
||||
err(1, "duplicate buffer");
|
||||
}
|
||||
|
||||
if (opts & PF_OPT_VERBOSE)
|
||||
flags |= PFR_FLAG_FEEDBACK;
|
||||
RVTEST(pfr_del_addrs(&table, b2.pfrb_caddr, b2.pfrb_size,
|
||||
&ndel, flags));
|
||||
xprintf(opts, "%d/%d addresses expired", ndel, b2.pfrb_size);
|
||||
if (opts & PF_OPT_VERBOSE)
|
||||
PFRB_FOREACH(a, &b2)
|
||||
if (opts & PF_OPT_VERBOSE2 ||
|
||||
a->pfra_fback != PFR_FB_NONE)
|
||||
print_addrx(a, NULL,
|
||||
opts & PF_OPT_USEDNS);
|
||||
} else if (!strcmp(command, "show")) {
|
||||
b.pfrb_type = (opts & PF_OPT_VERBOSE) ?
|
||||
PFRB_ASTATS : PFRB_ADDRS;
|
||||
if (argc || file != NULL)
|
||||
usage();
|
||||
for (;;) {
|
||||
pfr_buf_grow(&b, b.pfrb_size);
|
||||
b.pfrb_size = b.pfrb_msize;
|
||||
if (opts & PF_OPT_VERBOSE)
|
||||
RVTEST(pfr_get_astats(&table, b.pfrb_caddr,
|
||||
&b.pfrb_size, flags));
|
||||
else
|
||||
RVTEST(pfr_get_addrs(&table, b.pfrb_caddr,
|
||||
&b.pfrb_size, flags));
|
||||
if (b.pfrb_size <= b.pfrb_msize)
|
||||
break;
|
||||
}
|
||||
PFRB_FOREACH(p, &b)
|
||||
if (opts & PF_OPT_VERBOSE)
|
||||
print_astats(p, opts & PF_OPT_USEDNS);
|
||||
else
|
||||
print_addrx(p, NULL, opts & PF_OPT_USEDNS);
|
||||
} else if (!strcmp(command, "test")) {
|
||||
b.pfrb_type = PFRB_ADDRS;
|
||||
b2.pfrb_type = PFRB_ADDRS;
|
||||
|
||||
if (load_addr(&b, argc, argv, file, 1, opts))
|
||||
goto _error;
|
||||
if (opts & PF_OPT_VERBOSE2) {
|
||||
flags |= PFR_FLAG_REPLACE;
|
||||
PFRB_FOREACH(a, &b)
|
||||
if (pfr_buf_add(&b2, a))
|
||||
err(1, "duplicate buffer");
|
||||
}
|
||||
RVTEST(pfr_tst_addrs(&table, b.pfrb_caddr, b.pfrb_size,
|
||||
&nmatch, flags));
|
||||
xprintf(opts, "%d/%d addresses match", nmatch, b.pfrb_size);
|
||||
if ((opts & PF_OPT_VERBOSE) && !(opts & PF_OPT_VERBOSE2))
|
||||
PFRB_FOREACH(a, &b)
|
||||
if (a->pfra_fback == PFR_FB_MATCH)
|
||||
print_addrx(a, NULL,
|
||||
opts & PF_OPT_USEDNS);
|
||||
if (opts & PF_OPT_VERBOSE2) {
|
||||
a2 = NULL;
|
||||
PFRB_FOREACH(a, &b) {
|
||||
a2 = pfr_buf_next(&b2, a2);
|
||||
print_addrx(a2, a, opts & PF_OPT_USEDNS);
|
||||
}
|
||||
}
|
||||
if (nmatch < b.pfrb_size)
|
||||
rv = 2;
|
||||
} else if (!strcmp(command, "zero") && (argc || file != NULL)) {
|
||||
b.pfrb_type = PFRB_ADDRS;
|
||||
if (load_addr(&b, argc, argv, file, 0, opts))
|
||||
goto _error;
|
||||
if (opts & PF_OPT_VERBOSE)
|
||||
flags |= PFR_FLAG_FEEDBACK;
|
||||
RVTEST(pfr_clr_astats(&table, b.pfrb_caddr, b.pfrb_size,
|
||||
&nzero, flags));
|
||||
xprintf(opts, "%d/%d addresses cleared", nzero, b.pfrb_size);
|
||||
if (opts & PF_OPT_VERBOSE)
|
||||
PFRB_FOREACH(a, &b)
|
||||
if (opts & PF_OPT_VERBOSE2 ||
|
||||
a->pfra_fback != PFR_FB_NONE)
|
||||
print_addrx(a, NULL,
|
||||
opts & PF_OPT_USEDNS);
|
||||
} else if (!strcmp(command, "zero")) {
|
||||
flags |= PFR_FLAG_ADDRSTOO;
|
||||
RVTEST(pfr_clr_tstats(&table, 1, &nzero, flags));
|
||||
xprintf(opts, "%d table/stats cleared", nzero);
|
||||
} else
|
||||
warnx("pfctl_table: unknown command '%s'", command);
|
||||
goto _cleanup;
|
||||
|
||||
_error:
|
||||
rv = -1;
|
||||
_cleanup:
|
||||
pfr_buf_clear(&b);
|
||||
pfr_buf_clear(&b2);
|
||||
return (rv);
|
||||
}
|
||||
|
||||
void
|
||||
print_table(struct pfr_table *ta, int verbose, int debug)
|
||||
{
|
||||
if (!debug && !(ta->pfrt_flags & PFR_TFLAG_ACTIVE))
|
||||
return;
|
||||
if (verbose)
|
||||
printf("%c%c%c%c%c%c%c\t",
|
||||
(ta->pfrt_flags & PFR_TFLAG_CONST) ? 'c' : '-',
|
||||
(ta->pfrt_flags & PFR_TFLAG_PERSIST) ? 'p' : '-',
|
||||
(ta->pfrt_flags & PFR_TFLAG_ACTIVE) ? 'a' : '-',
|
||||
(ta->pfrt_flags & PFR_TFLAG_INACTIVE) ? 'i' : '-',
|
||||
(ta->pfrt_flags & PFR_TFLAG_REFERENCED) ? 'r' : '-',
|
||||
(ta->pfrt_flags & PFR_TFLAG_REFDANCHOR) ? 'h' : '-',
|
||||
(ta->pfrt_flags & PFR_TFLAG_COUNTERS) ? 'C' : '-');
|
||||
|
||||
printf("%s", ta->pfrt_name);
|
||||
if (ta->pfrt_anchor[0] != '\0')
|
||||
printf("@%s", ta->pfrt_anchor);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void
|
||||
print_tstats(struct pfr_tstats *ts, int debug)
|
||||
{
|
||||
time_t time = ts->pfrts_tzero;
|
||||
int dir, op;
|
||||
char *ct;
|
||||
|
||||
if (!debug && !(ts->pfrts_flags & PFR_TFLAG_ACTIVE))
|
||||
return;
|
||||
ct = ctime(&time);
|
||||
print_table(&ts->pfrts_t, 1, debug);
|
||||
printf("\tAddresses: %d\n", ts->pfrts_cnt);
|
||||
if (ct)
|
||||
printf("\tCleared: %s", ct);
|
||||
else
|
||||
printf("\tCleared: %lld\n", time);
|
||||
printf("\tReferences: [ Anchors: %-18d Rules: %-18d ]\n",
|
||||
ts->pfrts_refcnt[PFR_REFCNT_ANCHOR],
|
||||
ts->pfrts_refcnt[PFR_REFCNT_RULE]);
|
||||
printf("\tEvaluations: [ NoMatch: %-18llu Match: %-18llu ]\n",
|
||||
(unsigned long long)ts->pfrts_nomatch,
|
||||
(unsigned long long)ts->pfrts_match);
|
||||
for (dir = 0; dir < PFR_DIR_MAX; dir++)
|
||||
for (op = 0; op < PFR_OP_TABLE_MAX; op++)
|
||||
printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
|
||||
stats_text[dir][op],
|
||||
(unsigned long long)ts->pfrts_packets[dir][op],
|
||||
(unsigned long long)ts->pfrts_bytes[dir][op]);
|
||||
}
|
||||
|
||||
int
|
||||
load_addr(struct pfr_buffer *b, int argc, char *argv[], char *file,
|
||||
int nonetwork, int opts)
|
||||
{
|
||||
int ev = 0;
|
||||
while (argc--)
|
||||
if ((ev = append_addr(b, *argv++, nonetwork, opts)) == -1) {
|
||||
if (errno)
|
||||
warn("cannot decode %s", argv[-1]);
|
||||
return (-1);
|
||||
}
|
||||
if (ev == 1) { /* expected further append_addr call */
|
||||
warnx("failed to decode %s", argv[-1]);
|
||||
return (-1);
|
||||
}
|
||||
if (pfr_buf_load(b, file, nonetwork, opts)) {
|
||||
warn("cannot load %s", file);
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
print_addrx(struct pfr_addr *ad, struct pfr_addr *rad, int dns)
|
||||
{
|
||||
char ch, buf[256] = "{error}";
|
||||
char fb[] = { ' ', 'M', 'A', 'D', 'C', 'Z', 'X', ' ', 'Y', ' ' };
|
||||
unsigned int fback, hostnet;
|
||||
|
||||
fback = (rad != NULL) ? rad->pfra_fback : ad->pfra_fback;
|
||||
ch = (fback < sizeof(fb)/sizeof(*fb)) ? fb[fback] : '?';
|
||||
hostnet = (ad->pfra_af == AF_INET6) ? 128 : 32;
|
||||
inet_ntop(ad->pfra_af, &ad->pfra_u, buf, sizeof(buf));
|
||||
printf("%c %c%s", ch, (ad->pfra_not?'!':' '), buf);
|
||||
if (ad->pfra_net < hostnet)
|
||||
printf("/%d", ad->pfra_net);
|
||||
if (rad != NULL && fback != PFR_FB_NONE) {
|
||||
if (strlcpy(buf, "{error}", sizeof(buf)) >= sizeof(buf))
|
||||
errx(1, "print_addrx: strlcpy");
|
||||
inet_ntop(rad->pfra_af, &rad->pfra_u, buf, sizeof(buf));
|
||||
printf("\t%c%s", (rad->pfra_not?'!':' '), buf);
|
||||
if (rad->pfra_net < hostnet)
|
||||
printf("/%d", rad->pfra_net);
|
||||
}
|
||||
if (rad != NULL && fback == PFR_FB_NONE)
|
||||
printf("\t nomatch");
|
||||
if (dns && ad->pfra_net == hostnet) {
|
||||
char host[NI_MAXHOST];
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t slen; /* PFLINUX: sa_len replacement */
|
||||
|
||||
strlcpy(host, "?", sizeof(host));
|
||||
bzero(&ss, sizeof(ss));
|
||||
ss.ss_family = ad->pfra_af;
|
||||
if (ss.ss_family == AF_INET) {
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
|
||||
|
||||
/* PFLINUX: no sa_len on Linux; getnameinfo(3)
|
||||
* takes an explicit length instead. */
|
||||
sin->sin_addr = ad->pfra_ip4addr;
|
||||
slen = sizeof(*sin);
|
||||
} else {
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
|
||||
|
||||
sin6->sin6_addr = ad->pfra_ip6addr;
|
||||
slen = sizeof(*sin6);
|
||||
}
|
||||
if (getnameinfo((struct sockaddr *)&ss, slen, host,
|
||||
sizeof(host), NULL, 0, NI_NAMEREQD) == 0)
|
||||
printf("\t(%s)", host);
|
||||
}
|
||||
if (ad->pfra_ifname[0] != '\0')
|
||||
printf("@%s", ad->pfra_ifname);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void
|
||||
print_astats(struct pfr_astats *as, int dns)
|
||||
{
|
||||
time_t time = as->pfras_tzero;
|
||||
int dir, op;
|
||||
char *ct;
|
||||
|
||||
ct = ctime(&time);
|
||||
print_addrx(&as->pfras_a, NULL, dns);
|
||||
if (ct)
|
||||
printf("\tCleared: %s", ctime(&time));
|
||||
else
|
||||
printf("\tCleared: %lld\n", time);
|
||||
if (as->pfras_a.pfra_states)
|
||||
printf("\tActive States: %d\n", as->pfras_a.pfra_states);
|
||||
if (as->pfras_a.pfra_type == PFRKE_COST)
|
||||
printf("\tWeight: %d\n", as->pfras_a.pfra_weight);
|
||||
if (as->pfras_a.pfra_ifname[0])
|
||||
printf("\tInterface: %s\n", as->pfras_a.pfra_ifname);
|
||||
if (as->pfras_a.pfra_fback == PFR_FB_NOCOUNT)
|
||||
return;
|
||||
for (dir = 0; dir < PFR_DIR_MAX; dir++)
|
||||
for (op = 0; op < PFR_OP_ADDR_MAX; op++)
|
||||
printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
|
||||
stats_text[dir][op],
|
||||
(unsigned long long)as->pfras_packets[dir][op],
|
||||
(unsigned long long)as->pfras_bytes[dir][op]);
|
||||
}
|
||||
|
||||
int
|
||||
pfctl_define_table(char *name, int flags, int addrs, const char *anchor,
|
||||
struct pfr_buffer *ab, u_int32_t ticket, struct pfr_uktable *ukt)
|
||||
{
|
||||
struct pfr_table tbl_buf;
|
||||
struct pfr_table *tbl;
|
||||
|
||||
if (ukt == NULL) {
|
||||
bzero(&tbl_buf, sizeof(tbl_buf));
|
||||
tbl = &tbl_buf;
|
||||
} else {
|
||||
if (ab->pfrb_size != 0) {
|
||||
/*
|
||||
* copy IP addresses which come with table from
|
||||
* temporal buffer to buffer attached to table.
|
||||
*/
|
||||
ukt->pfrukt_addrs = *ab;
|
||||
ab->pfrb_size = 0;
|
||||
ab->pfrb_msize = 0;
|
||||
ab->pfrb_caddr = NULL;
|
||||
} else
|
||||
memset(&ukt->pfrukt_addrs, 0,
|
||||
sizeof(struct pfr_buffer));
|
||||
|
||||
tbl = &ukt->pfrukt_t;
|
||||
}
|
||||
|
||||
if (strlcpy(tbl->pfrt_name, name, sizeof(tbl->pfrt_name)) >=
|
||||
sizeof(tbl->pfrt_name) || strlcpy(tbl->pfrt_anchor, anchor,
|
||||
sizeof(tbl->pfrt_anchor)) >= sizeof(tbl->pfrt_anchor))
|
||||
errx(1, "%s: strlcpy", __func__);
|
||||
tbl->pfrt_flags = flags;
|
||||
DBGPRINT("%s %s@%s [%x]\n", __func__, tbl->pfrt_name,
|
||||
tbl->pfrt_anchor, tbl->pfrt_flags);
|
||||
|
||||
/*
|
||||
* non-root anchors processed by parse.y are loaded to kernel later.
|
||||
* Here we load tables, which are either created for root anchor
|
||||
* or by 'pfctl -t ... -T ...' command.
|
||||
*/
|
||||
if (ukt != NULL)
|
||||
return (0);
|
||||
|
||||
return pfr_ina_define(tbl, ab->pfrb_caddr, ab->pfrb_size, NULL,
|
||||
NULL, ticket, addrs ? PFR_FLAG_ADDRSTOO : 0);
|
||||
}
|
||||
|
||||
void
|
||||
warn_duplicate_tables(const char *tablename, const char *anchorname)
|
||||
{
|
||||
struct pfr_buffer b;
|
||||
struct pfr_table *t;
|
||||
|
||||
bzero(&b, sizeof(b));
|
||||
b.pfrb_type = PFRB_TABLES;
|
||||
for (;;) {
|
||||
pfr_buf_grow(&b, b.pfrb_size);
|
||||
b.pfrb_size = b.pfrb_msize;
|
||||
if (pfr_get_tables(NULL, b.pfrb_caddr,
|
||||
&b.pfrb_size, PFR_FLAG_ALLRSETS))
|
||||
err(1, "pfr_get_tables");
|
||||
if (b.pfrb_size <= b.pfrb_msize)
|
||||
break;
|
||||
}
|
||||
PFRB_FOREACH(t, &b) {
|
||||
if (!(t->pfrt_flags & PFR_TFLAG_ACTIVE))
|
||||
continue;
|
||||
if (!strcmp(anchorname, t->pfrt_anchor))
|
||||
continue;
|
||||
if (!strcmp(tablename, t->pfrt_name))
|
||||
warnx("warning: table <%s> already defined"
|
||||
" in anchor \"%s\"", tablename,
|
||||
t->pfrt_anchor[0] ? t->pfrt_anchor : "/");
|
||||
}
|
||||
pfr_buf_clear(&b);
|
||||
}
|
||||
|
||||
void
|
||||
xprintf(int opts, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
if (opts & PF_OPT_QUIET)
|
||||
return;
|
||||
|
||||
va_start(args, fmt);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (opts & PF_OPT_DUMMYACTION)
|
||||
fprintf(stderr, " (dummy).\n");
|
||||
else if (opts & PF_OPT_NOACTION)
|
||||
fprintf(stderr, " (syntax only).\n");
|
||||
else
|
||||
fprintf(stderr, ".\n");
|
||||
}
|
||||
|
||||
|
||||
/* interface stuff */
|
||||
|
||||
void
|
||||
pfctl_show_ifaces(const char *filter, int opts)
|
||||
{
|
||||
struct pfr_buffer b;
|
||||
struct pfi_kif *p;
|
||||
|
||||
bzero(&b, sizeof(b));
|
||||
b.pfrb_type = PFRB_IFACES;
|
||||
for (;;) {
|
||||
pfr_buf_grow(&b, 0);
|
||||
b.pfrb_size = b.pfrb_msize;
|
||||
if (pfi_get_ifaces(filter, b.pfrb_caddr, &b.pfrb_size))
|
||||
errx(1, "%s", pf_strerror(errno));
|
||||
if (b.pfrb_size < b.pfrb_msize)
|
||||
break;
|
||||
}
|
||||
if (opts & PF_OPT_SHOWALL)
|
||||
pfctl_print_title("INTERFACES:");
|
||||
PFRB_FOREACH(p, &b)
|
||||
print_iface(p, opts);
|
||||
}
|
||||
|
||||
void
|
||||
print_iface(struct pfi_kif *p, int opts)
|
||||
{
|
||||
time_t tzero = p->pfik_tzero;
|
||||
int i, af, dir, act;
|
||||
char *ct;
|
||||
|
||||
printf("%s", p->pfik_name);
|
||||
if (opts & PF_OPT_VERBOSE) {
|
||||
if (p->pfik_flags & PFI_IFLAG_SKIP)
|
||||
printf(" (skip)");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
if (!(opts & PF_OPT_VERBOSE2))
|
||||
return;
|
||||
|
||||
ct = ctime(&tzero);
|
||||
if (ct)
|
||||
printf("\tCleared: %s", ct);
|
||||
else
|
||||
printf("\tCleared: %lld\n", tzero);
|
||||
printf("\tReferences: [ States: %-18d Rules: %-18d ]\n",
|
||||
p->pfik_states, p->pfik_rules);
|
||||
for (i = 0; i < 8; i++) {
|
||||
af = (i>>2) & 1;
|
||||
dir = (i>>1) &1;
|
||||
act = i & 1;
|
||||
printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
|
||||
istats_text[af][dir][act],
|
||||
(unsigned long long)p->pfik_packets[af][dir][act],
|
||||
(unsigned long long)p->pfik_bytes[af][dir][act]);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user