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:
lgcosta committed 2026-11-11 15:20:01 -03:00
1 parent 0119e70960
commit 5323ee2c71
49 files changed
+20028 -28

No files matched your search

+6548
View File
File diff suppressed because it is too large. Load diff
+381
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+137
View File
@@ -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
+346
View File
@@ -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_ */
+287
View File
@@ -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);
}
+598
View File
@@ -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);
}
+692
View File
@@ -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]);
}
}