summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpb <pb@openbsd.org>2002-06-09 03:57:12 +0000
committerpb <pb@openbsd.org>2002-06-09 03:57:12 +0000
commitbf40458b7c516d51364d7a518ed6a250d9ed8a07 (patch)
tree061d315eedb73c98f7b0950bf74fe41de4cd8fb7
parentsync (diff)
downloadwireguard-openbsd-bf40458b7c516d51364d7a518ed6a250d9ed8a07.tar.xz
wireguard-openbsd-bf40458b7c516d51364d7a518ed6a250d9ed8a07.zip
new file sys/net/pf_ioctl.c
functions moved from pf.c to there ok dhartmei@, frantzen@ testing myself + henning@, kernel & userland utils fine
-rw-r--r--sys/conf/files3
-rw-r--r--sys/net/pf.c1445
-rw-r--r--sys/net/pf_ioctl.c1483
3 files changed, 1496 insertions, 1435 deletions
diff --git a/sys/conf/files b/sys/conf/files
index b0e26ae8d7f..16370e819d4 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.249 2002/06/08 21:22:02 itojun Exp $
+# $OpenBSD: files,v 1.250 2002/06/09 03:57:12 pb Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -335,6 +335,7 @@ file dev/ksyms.c ksyms needs-flag
pseudo-device pf: ifnet
file net/pf.c pf needs-flag
file net/pf_norm.c pf
+file net/pf_ioctl.c pf
# XXX machine-independent SCSI files should live somewhere here, maybe
diff --git a/sys/net/pf.c b/sys/net/pf.c
index f6a41c18a18..3fb3f63fe1b 100644
--- a/sys/net/pf.c
+++ b/sys/net/pf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf.c,v 1.222 2002/06/09 01:49:42 jasoni Exp $ */
+/* $OpenBSD: pf.c,v 1.223 2002/06/09 03:57:18 pb Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -38,7 +38,6 @@
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/filio.h>
-#include <sys/fcntl.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/kernel.h>
@@ -83,19 +82,10 @@
#define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x
struct pf_state_tree;
-struct pf_port_node {
- LIST_ENTRY(pf_port_node) next;
- u_int16_t port;
-};
-LIST_HEAD(pf_port_list, pf_port_node);
-
/*
* Global variables
*/
-TAILQ_HEAD(pf_natqueue, pf_nat) pf_nats[2];
-TAILQ_HEAD(pf_binatqueue, pf_binat) pf_binats[2];
-TAILQ_HEAD(pf_rdrqueue, pf_rdr) pf_rdrs[2];
struct pf_rulequeue pf_rules[2];
struct pf_rulequeue *pf_rules_active;
struct pf_rulequeue *pf_rules_inactive;
@@ -156,12 +146,6 @@ int *pftm_timeouts[PFTM_MAX] = { &pftm_tcp_first_packet,
struct pool pf_tree_pl, pf_rule_pl, pf_nat_pl, pf_sport_pl;
struct pool pf_rdr_pl, pf_state_pl, pf_binat_pl, pf_addr_pl;
-struct pf_pool_limit {
- void *pp;
- unsigned limit;
-} pf_pool_limits[PF_LIMIT_MAX] = { { &pf_state_pl, UINT_MAX },
- { &pf_frent_pl, PFFRAG_FRENT_HIWAT } };
-
void pf_addrcpy(struct pf_addr *, struct pf_addr *,
u_int8_t);
int pf_compare_rules(struct pf_rule *,
@@ -184,11 +168,6 @@ void pf_print_host(struct pf_addr *, u_int16_t, u_int8_t);
void pf_print_state(struct pf_state *);
void pf_print_flags(u_int8_t);
-void pfattach(int);
-int pfopen(dev_t, int, int, struct proc *);
-int pfclose(dev_t, int, int, struct proc *);
-int pfioctl(dev_t, u_long, caddr_t, int, struct proc *);
-
u_int16_t pf_cksum_fixup(u_int16_t, u_int16_t, u_int16_t,
u_int8_t);
void pf_change_ap(struct pf_addr *, u_int16_t *,
@@ -250,6 +229,9 @@ void pf_route(struct mbuf **, struct pf_rule *, int);
void pf_route6(struct mbuf **, struct pf_rule *, int);
int pf_socket_lookup(uid_t *, gid_t *, int, int, int,
struct pf_pdesc *);
+struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] = { { &pf_state_pl, UINT_MAX },
+ { &pf_frent_pl, PFFRAG_FRENT_HIWAT } };
+
#if NPFLOG > 0
@@ -282,10 +264,15 @@ int pf_socket_lookup(uid_t *, gid_t *, int, int, int,
static __inline int pf_state_compare(struct pf_tree_node *,
struct pf_tree_node *);
-RB_HEAD(pf_state_tree, pf_tree_node) tree_lan_ext, tree_ext_gwy;
-RB_PROTOTYPE(pf_state_tree, pf_tree_node, entry, pf_state_compare);
+
+struct pf_state_tree tree_lan_ext, tree_ext_gwy;
RB_GENERATE(pf_state_tree, pf_tree_node, entry, pf_state_compare);
+struct pf_rulequeue pf_rules[2];
+struct pf_natqueue pf_nats[2];
+struct pf_binatqueue pf_binats[2];
+struct pf_rdrqueue pf_rdrs[2];
+
static __inline int
pf_state_compare(struct pf_tree_node *a, struct pf_tree_node *b)
{
@@ -852,1416 +839,6 @@ pf_print_flags(u_int8_t f)
printf("W");
}
-void
-pfattach(int num)
-{
- pool_init(&pf_tree_pl, sizeof(struct pf_tree_node), 0, 0, 0, "pftrpl",
- NULL);
- pool_init(&pf_rule_pl, sizeof(struct pf_rule), 0, 0, 0, "pfrulepl",
- &pool_allocator_nointr);
- pool_init(&pf_nat_pl, sizeof(struct pf_nat), 0, 0, 0, "pfnatpl",
- &pool_allocator_nointr);
- pool_init(&pf_binat_pl, sizeof(struct pf_binat), 0, 0, 0, "pfbinatpl",
- &pool_allocator_nointr);
- pool_init(&pf_rdr_pl, sizeof(struct pf_rdr), 0, 0, 0, "pfrdrpl",
- &pool_allocator_nointr);
- pool_init(&pf_state_pl, sizeof(struct pf_state), 0, 0, 0, "pfstatepl",
- NULL);
- pool_init(&pf_sport_pl, sizeof(struct pf_port_node), 0, 0, 0, "pfsport",
- NULL);
- pool_init(&pf_addr_pl, sizeof(struct pf_addr_dyn), 0, 0, 0, "pfaddr",
- NULL);
-
- TAILQ_INIT(&pf_rules[0]);
- TAILQ_INIT(&pf_rules[1]);
- TAILQ_INIT(&pf_nats[0]);
- TAILQ_INIT(&pf_nats[1]);
- TAILQ_INIT(&pf_binats[0]);
- TAILQ_INIT(&pf_binats[1]);
- TAILQ_INIT(&pf_rdrs[0]);
- TAILQ_INIT(&pf_rdrs[1]);
- pf_rules_active = &pf_rules[0];
- pf_rules_inactive = &pf_rules[1];
- pf_nats_active = &pf_nats[0];
- pf_nats_inactive = &pf_nats[1];
- pf_binats_active = &pf_binats[0];
- pf_binats_inactive = &pf_binats[1];
- pf_rdrs_active = &pf_rdrs[0];
- pf_rdrs_inactive = &pf_rdrs[1];
-
- LIST_INIT(&pf_tcp_ports);
- LIST_INIT(&pf_udp_ports);
-
- timeout_set(&pf_expire_to, pf_purge_timeout, &pf_expire_to);
- timeout_add(&pf_expire_to, pftm_interval * hz);
-
- pf_normalize_init();
- pf_status.debug = PF_DEBUG_URGENT;
-}
-
-int
-pfopen(dev_t dev, int flags, int fmt, struct proc *p)
-{
- if (minor(dev) >= 1)
- return (ENXIO);
- return (0);
-}
-
-int
-pfclose(dev_t dev, int flags, int fmt, struct proc *p)
-{
- if (minor(dev) >= 1)
- return (ENXIO);
- return (0);
-}
-
-int
-pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
-{
- int error = 0;
- int s;
-
- /* XXX keep in sync with switch() below */
- if (securelevel > 1)
- switch (cmd) {
- case DIOCGETRULES:
- case DIOCGETRULE:
- case DIOCGETNATS:
- case DIOCGETNAT:
- case DIOCGETBINATS:
- case DIOCGETBINAT:
- case DIOCGETRDRS:
- case DIOCGETRDR:
- case DIOCGETSTATE:
- case DIOCSETSTATUSIF:
- case DIOCGETSTATUS:
- case DIOCCLRSTATUS:
- case DIOCNATLOOK:
- case DIOCSETDEBUG:
- case DIOCGETSTATES:
- case DIOCGETTIMEOUT:
- case DIOCCLRRULECTRS:
- case DIOCGETLIMIT:
- break;
- default:
- return (EPERM);
- }
-
- if (!(flags & FWRITE))
- switch (cmd) {
- case DIOCGETRULES:
- case DIOCGETRULE:
- case DIOCGETNATS:
- case DIOCGETNAT:
- case DIOCGETRDRS:
- case DIOCGETRDR:
- case DIOCGETSTATE:
- case DIOCGETSTATUS:
- case DIOCGETSTATES:
- case DIOCGETTIMEOUT:
- case DIOCGETBINATS:
- case DIOCGETBINAT:
- case DIOCGETLIMIT:
- break;
- default:
- return (EACCES);
- }
-
- switch (cmd) {
-
- case DIOCSTART:
- if (pf_status.running)
- error = EEXIST;
- else {
- u_int32_t states = pf_status.states;
- bzero(&pf_status, sizeof(struct pf_status));
- pf_status.running = 1;
- pf_status.states = states;
- pf_status.since = time.tv_sec;
- DPFPRINTF(PF_DEBUG_MISC, ("pf: started\n"));
- }
- break;
-
- case DIOCSTOP:
- if (!pf_status.running)
- error = ENOENT;
- else {
- pf_status.running = 0;
- DPFPRINTF(PF_DEBUG_MISC, ("pf: stopped\n"));
- }
- break;
-
- case DIOCBEGINRULES: {
- u_int32_t *ticket = (u_int32_t *)addr;
- struct pf_rule *rule;
-
- while ((rule = TAILQ_FIRST(pf_rules_inactive)) != NULL) {
- TAILQ_REMOVE(pf_rules_inactive, rule, entries);
- pf_dynaddr_remove(&rule->src.addr);
- pf_dynaddr_remove(&rule->dst.addr);
- pool_put(&pf_rule_pl, rule);
- }
- *ticket = ++ticket_rules_inactive;
- break;
- }
-
- case DIOCADDRULE: {
- struct pfioc_rule *pr = (struct pfioc_rule *)addr;
- struct pf_rule *rule, *tail;
-
- if (pr->ticket != ticket_rules_inactive) {
- error = EBUSY;
- break;
- }
- rule = pool_get(&pf_rule_pl, PR_NOWAIT);
- if (rule == NULL) {
- error = ENOMEM;
- break;
- }
- bcopy(&pr->rule, rule, sizeof(struct pf_rule));
-#ifndef INET
- if (rule->af == AF_INET) {
- pool_put(&pf_rule_pl, rule);
- error = EAFNOSUPPORT;
- break;
- }
-#endif /* INET */
-#ifndef INET6
- if (rule->af == AF_INET6) {
- pool_put(&pf_rule_pl, rule);
- error = EAFNOSUPPORT;
- break;
- }
-#endif /* INET6 */
- tail = TAILQ_LAST(pf_rules_inactive, pf_rulequeue);
- if (tail)
- rule->nr = tail->nr + 1;
- else
- rule->nr = 0;
- if (rule->ifname[0]) {
- rule->ifp = ifunit(rule->ifname);
- if (rule->ifp == NULL) {
- pool_put(&pf_rule_pl, rule);
- error = EINVAL;
- break;
- }
- } else
- rule->ifp = NULL;
- if (rule->rt_ifname[0]) {
- rule->rt_ifp = ifunit(rule->rt_ifname);
- if (rule->rt_ifname == NULL) {
- pool_put(&pf_rule_pl, rule);
- error = EINVAL;
- break;
- }
- } else
- rule->rt_ifp = NULL;
- if (pf_dynaddr_setup(&rule->src.addr, rule->af) ||
- pf_dynaddr_setup(&rule->dst.addr, rule->af)) {
- pf_dynaddr_remove(&rule->src.addr);
- pf_dynaddr_remove(&rule->dst.addr);
- pool_put(&pf_rule_pl, rule);
- error = EINVAL;
- break;
- }
- rule->evaluations = rule->packets = rule->bytes = 0;
- TAILQ_INSERT_TAIL(pf_rules_inactive, rule, entries);
- break;
- }
-
- case DIOCCOMMITRULES: {
- u_int32_t *ticket = (u_int32_t *)addr;
- struct pf_rulequeue *old_rules;
- struct pf_rule *rule;
- struct pf_tree_node *n;
-
- if (*ticket != ticket_rules_inactive) {
- error = EBUSY;
- break;
- }
-
- /* Swap rules, keep the old. */
- s = splsoftnet();
- /*
- * Rules are about to get freed, clear rule pointers in states
- */
- RB_FOREACH(n, pf_state_tree, &tree_ext_gwy)
- n->state->rule.ptr = NULL;
- old_rules = pf_rules_active;
- pf_rules_active = pf_rules_inactive;
- pf_rules_inactive = old_rules;
- ticket_rules_active = ticket_rules_inactive;
- pf_calc_skip_steps(pf_rules_active);
- splx(s);
-
- /* Purge the old rule list. */
- while ((rule = TAILQ_FIRST(old_rules)) != NULL) {
- TAILQ_REMOVE(old_rules, rule, entries);
- pf_dynaddr_remove(&rule->src.addr);
- pf_dynaddr_remove(&rule->dst.addr);
- pool_put(&pf_rule_pl, rule);
- }
- break;
- }
-
- case DIOCGETRULES: {
- struct pfioc_rule *pr = (struct pfioc_rule *)addr;
- struct pf_rule *tail;
-
- s = splsoftnet();
- tail = TAILQ_LAST(pf_rules_active, pf_rulequeue);
- if (tail)
- pr->nr = tail->nr + 1;
- else
- pr->nr = 0;
- pr->ticket = ticket_rules_active;
- splx(s);
- break;
- }
-
- case DIOCGETRULE: {
- struct pfioc_rule *pr = (struct pfioc_rule *)addr;
- struct pf_rule *rule;
-
- if (pr->ticket != ticket_rules_active) {
- error = EBUSY;
- break;
- }
- s = splsoftnet();
- rule = TAILQ_FIRST(pf_rules_active);
- while ((rule != NULL) && (rule->nr != pr->nr))
- rule = TAILQ_NEXT(rule, entries);
- if (rule == NULL) {
- error = EBUSY;
- splx(s);
- break;
- }
- bcopy(rule, &pr->rule, sizeof(struct pf_rule));
- pf_dynaddr_copyout(&pr->rule.src.addr);
- pf_dynaddr_copyout(&pr->rule.dst.addr);
- splx(s);
- break;
- }
-
- case DIOCCHANGERULE: {
- struct pfioc_changerule *pcr = (struct pfioc_changerule *)addr;
- struct pf_rule *oldrule = NULL, *newrule = NULL;
- u_int32_t nr = 0;
-
- if (pcr->action < PF_CHANGE_ADD_HEAD ||
- pcr->action > PF_CHANGE_REMOVE) {
- error = EINVAL;
- break;
- }
-
- if (pcr->action != PF_CHANGE_REMOVE) {
- newrule = pool_get(&pf_rule_pl, PR_NOWAIT);
- if (newrule == NULL) {
- error = ENOMEM;
- break;
- }
- bcopy(&pcr->newrule, newrule, sizeof(struct pf_rule));
-#ifndef INET
- if (newrule->af == AF_INET) {
- pool_put(&pf_rule_pl, newrule);
- error = EAFNOSUPPORT;
- break;
- }
-#endif /* INET */
-#ifndef INET6
- if (newrule->af == AF_INET6) {
- pool_put(&pf_rule_pl, newrule);
- error = EAFNOSUPPORT;
- break;
- }
-#endif /* INET6 */
- if (newrule->ifname[0]) {
- newrule->ifp = ifunit(newrule->ifname);
- if (newrule->ifp == NULL) {
- pool_put(&pf_rule_pl, newrule);
- error = EINVAL;
- break;
- }
- } else
- newrule->ifp = NULL;
- if (newrule->rt_ifname[0]) {
- newrule->rt_ifp = ifunit(newrule->rt_ifname);
- if (newrule->rt_ifname == NULL) {
- pool_put(&pf_rule_pl, newrule);
- error = EINVAL;
- break;
- }
- } else
- newrule->rt_ifp = NULL;
- if (pf_dynaddr_setup(&newrule->src.addr, newrule->af) ||
- pf_dynaddr_setup(&newrule->dst.addr, newrule->af)) {
- pf_dynaddr_remove(&newrule->src.addr);
- pf_dynaddr_remove(&newrule->dst.addr);
- pool_put(&pf_rule_pl, newrule);
- error = EINVAL;
- break;
- }
- newrule->evaluations = newrule->packets = 0;
- newrule->bytes = 0;
- }
-
- s = splsoftnet();
-
- if (pcr->action == PF_CHANGE_ADD_HEAD)
- oldrule = TAILQ_FIRST(pf_rules_active);
- else if (pcr->action == PF_CHANGE_ADD_TAIL)
- oldrule = TAILQ_LAST(pf_rules_active, pf_rulequeue);
- else {
- oldrule = TAILQ_FIRST(pf_rules_active);
- while ((oldrule != NULL) && pf_compare_rules(oldrule,
- &pcr->oldrule))
- oldrule = TAILQ_NEXT(oldrule, entries);
- if (oldrule == NULL) {
- error = EINVAL;
- splx(s);
- break;
- }
- }
-
- if (pcr->action == PF_CHANGE_REMOVE) {
- struct pf_tree_node *n;
-
- RB_FOREACH(n, pf_state_tree, &tree_ext_gwy)
- if (n->state->rule.ptr == oldrule)
- n->state->rule.ptr = NULL;
- TAILQ_REMOVE(pf_rules_active, oldrule, entries);
- pf_dynaddr_remove(&oldrule->src.addr);
- pf_dynaddr_remove(&oldrule->dst.addr);
- pool_put(&pf_rule_pl, oldrule);
- } else {
- if (oldrule == NULL)
- TAILQ_INSERT_TAIL(pf_rules_active, newrule,
- entries);
- else if (pcr->action == PF_CHANGE_ADD_HEAD ||
- pcr->action == PF_CHANGE_ADD_BEFORE)
- TAILQ_INSERT_BEFORE(oldrule, newrule, entries);
- else
- TAILQ_INSERT_AFTER(pf_rules_active, oldrule,
- newrule, entries);
- }
-
- TAILQ_FOREACH(oldrule, pf_rules_active, entries)
- oldrule->nr = nr++;
-
- pf_calc_skip_steps(pf_rules_active);
-
- ticket_rules_active++;
- splx(s);
- break;
- }
-
- case DIOCBEGINNATS: {
- u_int32_t *ticket = (u_int32_t *)addr;
- struct pf_nat *nat;
-
- while ((nat = TAILQ_FIRST(pf_nats_inactive)) != NULL) {
- pf_dynaddr_remove(&nat->saddr);
- pf_dynaddr_remove(&nat->daddr);
- pf_dynaddr_remove(&nat->raddr);
- TAILQ_REMOVE(pf_nats_inactive, nat, entries);
- pool_put(&pf_nat_pl, nat);
- }
- *ticket = ++ticket_nats_inactive;
- break;
- }
-
- case DIOCADDNAT: {
- struct pfioc_nat *pn = (struct pfioc_nat *)addr;
- struct pf_nat *nat;
-
- if (pn->ticket != ticket_nats_inactive) {
- error = EBUSY;
- break;
- }
- nat = pool_get(&pf_nat_pl, PR_NOWAIT);
- if (nat == NULL) {
- error = ENOMEM;
- break;
- }
- bcopy(&pn->nat, nat, sizeof(struct pf_nat));
-#ifndef INET
- if (nat->af == AF_INET) {
- pool_put(&pf_nat_pl, nat);
- error = EAFNOSUPPORT;
- break;
- }
-#endif /* INET */
-#ifndef INET6
- if (nat->af == AF_INET6) {
- pool_put(&pf_nat_pl, nat);
- error = EAFNOSUPPORT;
- break;
- }
-#endif /* INET6 */
- if (nat->ifname[0]) {
- nat->ifp = ifunit(nat->ifname);
- if (nat->ifp == NULL) {
- pool_put(&pf_nat_pl, nat);
- error = EINVAL;
- break;
- }
- } else
- nat->ifp = NULL;
- if (pf_dynaddr_setup(&nat->saddr, nat->af) ||
- pf_dynaddr_setup(&nat->daddr, nat->af) ||
- pf_dynaddr_setup(&nat->raddr, nat->af)) {
- pf_dynaddr_remove(&nat->saddr);
- pf_dynaddr_remove(&nat->daddr);
- pf_dynaddr_remove(&nat->raddr);
- pool_put(&pf_nat_pl, nat);
- error = EINVAL;
- break;
- }
- TAILQ_INSERT_TAIL(pf_nats_inactive, nat, entries);
- break;
- }
-
- case DIOCCOMMITNATS: {
- u_int32_t *ticket = (u_int32_t *)addr;
- struct pf_natqueue *old_nats;
- struct pf_nat *nat;
-
- if (*ticket != ticket_nats_inactive) {
- error = EBUSY;
- break;
- }
-
- /* Swap nats, keep the old. */
- s = splsoftnet();
- old_nats = pf_nats_active;
- pf_nats_active = pf_nats_inactive;
- pf_nats_inactive = old_nats;
- ticket_nats_active = ticket_nats_inactive;
- splx(s);
-
- /* Purge the old nat list */
- while ((nat = TAILQ_FIRST(old_nats)) != NULL) {
- pf_dynaddr_remove(&nat->saddr);
- pf_dynaddr_remove(&nat->daddr);
- pf_dynaddr_remove(&nat->raddr);
- TAILQ_REMOVE(old_nats, nat, entries);
- pool_put(&pf_nat_pl, nat);
- }
- break;
- }
-
- case DIOCGETNATS: {
- struct pfioc_nat *pn = (struct pfioc_nat *)addr;
- struct pf_nat *nat;
-
- pn->nr = 0;
- s = splsoftnet();
- TAILQ_FOREACH(nat, pf_nats_active, entries)
- pn->nr++;
- pn->ticket = ticket_nats_active;
- splx(s);
- break;
- }
-
- case DIOCGETNAT: {
- struct pfioc_nat *pn = (struct pfioc_nat *)addr;
- struct pf_nat *nat;
- u_int32_t nr;
-
- if (pn->ticket != ticket_nats_active) {
- error = EBUSY;
- break;
- }
- nr = 0;
- s = splsoftnet();
- nat = TAILQ_FIRST(pf_nats_active);
- while ((nat != NULL) && (nr < pn->nr)) {
- nat = TAILQ_NEXT(nat, entries);
- nr++;
- }
- if (nat == NULL) {
- error = EBUSY;
- splx(s);
- break;
- }
- bcopy(nat, &pn->nat, sizeof(struct pf_nat));
- pf_dynaddr_copyout(&pn->nat.saddr);
- pf_dynaddr_copyout(&pn->nat.daddr);
- pf_dynaddr_copyout(&pn->nat.raddr);
- splx(s);
- break;
- }
-
- case DIOCCHANGENAT: {
- struct pfioc_changenat *pcn = (struct pfioc_changenat *)addr;
- struct pf_nat *oldnat = NULL, *newnat = NULL;
-
- if (pcn->action < PF_CHANGE_ADD_HEAD ||
- pcn->action > PF_CHANGE_REMOVE) {
- error = EINVAL;
- break;
- }
-
- if (pcn->action != PF_CHANGE_REMOVE) {
- newnat = pool_get(&pf_nat_pl, PR_NOWAIT);
- if (newnat == NULL) {
- error = ENOMEM;
- break;
- }
- bcopy(&pcn->newnat, newnat, sizeof(struct pf_nat));
-#ifndef INET
- if (newnat->af == AF_INET) {
- pool_put(&pf_nat_pl, newnat);
- error = EAFNOSUPPORT;
- break;
- }
-#endif /* INET */
-#ifndef INET6
- if (newnat->af == AF_INET6) {
- pool_put(&pf_nat_pl, newnat);
- error = EAFNOSUPPORT;
- break;
- }
-#endif /* INET6 */
- if (newnat->ifname[0]) {
- newnat->ifp = ifunit(newnat->ifname);
- if (newnat->ifp == NULL) {
- pool_put(&pf_nat_pl, newnat);
- error = EINVAL;
- break;
- }
- } else
- newnat->ifp = NULL;
- if (pf_dynaddr_setup(&newnat->saddr, newnat->af) ||
- pf_dynaddr_setup(&newnat->daddr, newnat->af) ||
- pf_dynaddr_setup(&newnat->raddr, newnat->af)) {
- pf_dynaddr_remove(&newnat->saddr);
- pf_dynaddr_remove(&newnat->daddr);
- pf_dynaddr_remove(&newnat->raddr);
- pool_put(&pf_nat_pl, newnat);
- error = EINVAL;
- break;
- }
- }
-
- s = splsoftnet();
-
- if (pcn->action == PF_CHANGE_ADD_HEAD)
- oldnat = TAILQ_FIRST(pf_nats_active);
- else if (pcn->action == PF_CHANGE_ADD_TAIL)
- oldnat = TAILQ_LAST(pf_nats_active, pf_natqueue);
- else {
- oldnat = TAILQ_FIRST(pf_nats_active);
- while ((oldnat != NULL) && pf_compare_nats(oldnat,
- &pcn->oldnat))
- oldnat = TAILQ_NEXT(oldnat, entries);
- if (oldnat == NULL) {
- error = EINVAL;
- splx(s);
- break;
- }
- }
-
- if (pcn->action == PF_CHANGE_REMOVE) {
- pf_dynaddr_remove(&oldnat->saddr);
- pf_dynaddr_remove(&oldnat->daddr);
- pf_dynaddr_remove(&oldnat->raddr);
- TAILQ_REMOVE(pf_nats_active, oldnat, entries);
- pool_put(&pf_nat_pl, oldnat);
- } else {
- if (oldnat == NULL)
- TAILQ_INSERT_TAIL(pf_nats_active, newnat,
- entries);
- else if (pcn->action == PF_CHANGE_ADD_HEAD ||
- pcn->action == PF_CHANGE_ADD_BEFORE)
- TAILQ_INSERT_BEFORE(oldnat, newnat, entries);
- else
- TAILQ_INSERT_AFTER(pf_nats_active, oldnat,
- newnat, entries);
- }
-
- ticket_nats_active++;
- splx(s);
- break;
- }
-
- case DIOCBEGINBINATS: {
- u_int32_t *ticket = (u_int32_t *)addr;
- struct pf_binat *binat;
-
- while ((binat = TAILQ_FIRST(pf_binats_inactive)) != NULL) {
- TAILQ_REMOVE(pf_binats_inactive, binat, entries);
- pf_dynaddr_remove(&binat->saddr);
- pf_dynaddr_remove(&binat->daddr);
- pf_dynaddr_remove(&binat->raddr);
- pool_put(&pf_binat_pl, binat);
- }
- *ticket = ++ticket_binats_inactive;
- break;
- }
-
- case DIOCADDBINAT: {
- struct pfioc_binat *pb = (struct pfioc_binat *)addr;
- struct pf_binat *binat;
-
- if (pb->ticket != ticket_binats_inactive) {
- error = EBUSY;
- break;
- }
- binat = pool_get(&pf_binat_pl, PR_NOWAIT);
- if (binat == NULL) {
- error = ENOMEM;
- break;
- }
- bcopy(&pb->binat, binat, sizeof(struct pf_binat));
-#ifndef INET
- if (binat->af == AF_INET) {
- pool_put(&pf_binat_pl, binat);
- error = EAFNOSUPPORT;
- break;
- }
-#endif /* INET */
-#ifndef INET6
- if (binat->af == AF_INET6) {
- pool_put(&pf_binat_pl, binat);
- error = EAFNOSUPPORT;
- break;
- }
-#endif /* INET6 */
- if (binat->ifname[0]) {
- binat->ifp = ifunit(binat->ifname);
- if (binat->ifp == NULL) {
- pool_put(&pf_binat_pl, binat);
- error = EINVAL;
- break;
- }
- } else
- binat->ifp = NULL;
- if (pf_dynaddr_setup(&binat->saddr, binat->af) ||
- pf_dynaddr_setup(&binat->daddr, binat->af) ||
- pf_dynaddr_setup(&binat->raddr, binat->af)) {
- pf_dynaddr_remove(&binat->saddr);
- pf_dynaddr_remove(&binat->daddr);
- pf_dynaddr_remove(&binat->raddr);
- pool_put(&pf_binat_pl, binat);
- error = EINVAL;
- break;
- }
- TAILQ_INSERT_TAIL(pf_binats_inactive, binat, entries);
- break;
- }
-
- case DIOCCOMMITBINATS: {
- u_int32_t *ticket = (u_int32_t *)addr;
- struct pf_binatqueue *old_binats;
- struct pf_binat *binat;
-
- if (*ticket != ticket_binats_inactive) {
- error = EBUSY;
- break;
- }
-
- /* Swap binats, keep the old. */
- s = splsoftnet();
- old_binats = pf_binats_active;
- pf_binats_active = pf_binats_inactive;
- pf_binats_inactive = old_binats;
- ticket_binats_active = ticket_binats_inactive;
- splx(s);
-
- /* Purge the old binat list */
- while ((binat = TAILQ_FIRST(old_binats)) != NULL) {
- TAILQ_REMOVE(old_binats, binat, entries);
- pf_dynaddr_remove(&binat->saddr);
- pf_dynaddr_remove(&binat->daddr);
- pf_dynaddr_remove(&binat->raddr);
- pool_put(&pf_binat_pl, binat);
- }
- break;
- }
-
- case DIOCGETBINATS: {
- struct pfioc_binat *pb = (struct pfioc_binat *)addr;
- struct pf_binat *binat;
-
- pb->nr = 0;
- s = splsoftnet();
- TAILQ_FOREACH(binat, pf_binats_active, entries)
- pb->nr++;
- pb->ticket = ticket_binats_active;
- splx(s);
- break;
- }
-
- case DIOCGETBINAT: {
- struct pfioc_binat *pb = (struct pfioc_binat *)addr;
- struct pf_binat *binat;
- u_int32_t nr;
-
- if (pb->ticket != ticket_binats_active) {
- error = EBUSY;
- break;
- }
- nr = 0;
- s = splsoftnet();
- binat = TAILQ_FIRST(pf_binats_active);
- while ((binat != NULL) && (nr < pb->nr)) {
- binat = TAILQ_NEXT(binat, entries);
- nr++;
- }
- if (binat == NULL) {
- error = EBUSY;
- splx(s);
- break;
- }
- bcopy(binat, &pb->binat, sizeof(struct pf_binat));
- pf_dynaddr_copyout(&pb->binat.saddr);
- pf_dynaddr_copyout(&pb->binat.daddr);
- pf_dynaddr_copyout(&pb->binat.raddr);
- splx(s);
- break;
- }
-
- case DIOCCHANGEBINAT: {
- struct pfioc_changebinat *pcn = (struct pfioc_changebinat *)addr;
- struct pf_binat *oldbinat = NULL, *newbinat = NULL;
-
- if (pcn->action < PF_CHANGE_ADD_HEAD ||
- pcn->action > PF_CHANGE_REMOVE) {
- error = EINVAL;
- break;
- }
-
- if (pcn->action != PF_CHANGE_REMOVE) {
- newbinat = pool_get(&pf_binat_pl, PR_NOWAIT);
- if (newbinat == NULL) {
- error = ENOMEM;
- break;
- }
- bcopy(&pcn->newbinat, newbinat,
- sizeof(struct pf_binat));
-#ifndef INET
- if (newbinat->af == AF_INET) {
- pool_put(&pf_binat_pl, newbinat);
- error = EAFNOSUPPORT;
- break;
- }
-#endif /* INET */
-#ifndef INET6
- if (newbinat->af == AF_INET6) {
- pool_put(&pf_binat_pl, newbinat);
- error = EAFNOSUPPORT;
- break;
- }
-#endif /* INET6 */
- if (newbinat->ifname[0]) {
- newbinat->ifp = ifunit(newbinat->ifname);
- if (newbinat->ifp == NULL) {
- pool_put(&pf_binat_pl, newbinat);
- error = EINVAL;
- break;
- }
- } else
- newbinat->ifp = NULL;
- if (pf_dynaddr_setup(&newbinat->saddr, newbinat->af) ||
- pf_dynaddr_setup(&newbinat->daddr, newbinat->af) ||
- pf_dynaddr_setup(&newbinat->raddr, newbinat->af)) {
- pf_dynaddr_remove(&newbinat->saddr);
- pf_dynaddr_remove(&newbinat->daddr);
- pf_dynaddr_remove(&newbinat->raddr);
- pool_put(&pf_binat_pl, newbinat);
- error = EINVAL;
- break;
- }
- }
-
- s = splsoftnet();
-
- if (pcn->action == PF_CHANGE_ADD_HEAD)
- oldbinat = TAILQ_FIRST(pf_binats_active);
- else if (pcn->action == PF_CHANGE_ADD_TAIL)
- oldbinat = TAILQ_LAST(pf_binats_active, pf_binatqueue);
- else {
- oldbinat = TAILQ_FIRST(pf_binats_active);
- while ((oldbinat != NULL) && pf_compare_binats(oldbinat,
- &pcn->oldbinat))
- oldbinat = TAILQ_NEXT(oldbinat, entries);
- if (oldbinat == NULL) {
- error = EINVAL;
- splx(s);
- break;
- }
- }
-
- if (pcn->action == PF_CHANGE_REMOVE) {
- TAILQ_REMOVE(pf_binats_active, oldbinat, entries);
- pf_dynaddr_remove(&oldbinat->saddr);
- pf_dynaddr_remove(&oldbinat->daddr);
- pf_dynaddr_remove(&oldbinat->raddr);
- pool_put(&pf_binat_pl, oldbinat);
- } else {
- if (oldbinat == NULL)
- TAILQ_INSERT_TAIL(pf_binats_active, newbinat,
- entries);
- else if (pcn->action == PF_CHANGE_ADD_HEAD ||
- pcn->action == PF_CHANGE_ADD_BEFORE)
- TAILQ_INSERT_BEFORE(oldbinat, newbinat,
- entries);
- else
- TAILQ_INSERT_AFTER(pf_binats_active, oldbinat,
- newbinat, entries);
- }
-
- ticket_binats_active++;
- splx(s);
- break;
- }
-
- case DIOCBEGINRDRS: {
- u_int32_t *ticket = (u_int32_t *)addr;
- struct pf_rdr *rdr;
-
- while ((rdr = TAILQ_FIRST(pf_rdrs_inactive)) != NULL) {
- TAILQ_REMOVE(pf_rdrs_inactive, rdr, entries);
- pf_dynaddr_remove(&rdr->saddr);
- pf_dynaddr_remove(&rdr->daddr);
- pf_dynaddr_remove(&rdr->raddr);
- pool_put(&pf_rdr_pl, rdr);
- }
- *ticket = ++ticket_rdrs_inactive;
- break;
- }
-
- case DIOCADDRDR: {
- struct pfioc_rdr *pr = (struct pfioc_rdr *)addr;
- struct pf_rdr *rdr;
-
- if (pr->ticket != ticket_rdrs_inactive) {
- error = EBUSY;
- break;
- }
- rdr = pool_get(&pf_rdr_pl, PR_NOWAIT);
- if (rdr == NULL) {
- error = ENOMEM;
- break;
- }
- bcopy(&pr->rdr, rdr, sizeof(struct pf_rdr));
-#ifndef INET
- if (rdr->af == AF_INET) {
- pool_put(&pf_rdr_pl, rdr);
- error = EAFNOSUPPORT;
- break;
- }
-#endif /* INET */
-#ifndef INET6
- if (rdr->af == AF_INET6) {
- pool_put(&pf_rdr_pl, rdr);
- error = EAFNOSUPPORT;
- break;
- }
-#endif /* INET6 */
- if (rdr->ifname[0]) {
- rdr->ifp = ifunit(rdr->ifname);
- if (rdr->ifp == NULL) {
- pool_put(&pf_rdr_pl, rdr);
- error = EINVAL;
- break;
- }
- } else
- rdr->ifp = NULL;
- if (pf_dynaddr_setup(&rdr->saddr, rdr->af) ||
- pf_dynaddr_setup(&rdr->daddr, rdr->af) ||
- pf_dynaddr_setup(&rdr->raddr, rdr->af)) {
- pf_dynaddr_remove(&rdr->saddr);
- pf_dynaddr_remove(&rdr->daddr);
- pf_dynaddr_remove(&rdr->raddr);
- pool_put(&pf_rdr_pl, rdr);
- error = EINVAL;
- break;
- }
- TAILQ_INSERT_TAIL(pf_rdrs_inactive, rdr, entries);
- break;
- }
-
- case DIOCCOMMITRDRS: {
- u_int32_t *ticket = (u_int32_t *)addr;
- struct pf_rdrqueue *old_rdrs;
- struct pf_rdr *rdr;
-
- if (*ticket != ticket_rdrs_inactive) {
- error = EBUSY;
- break;
- }
-
- /* Swap rdrs, keep the old. */
- s = splsoftnet();
- old_rdrs = pf_rdrs_active;
- pf_rdrs_active = pf_rdrs_inactive;
- pf_rdrs_inactive = old_rdrs;
- ticket_rdrs_active = ticket_rdrs_inactive;
- splx(s);
-
- /* Purge the old rdr list */
- while ((rdr = TAILQ_FIRST(old_rdrs)) != NULL) {
- TAILQ_REMOVE(old_rdrs, rdr, entries);
- pf_dynaddr_remove(&rdr->saddr);
- pf_dynaddr_remove(&rdr->daddr);
- pf_dynaddr_remove(&rdr->raddr);
- pool_put(&pf_rdr_pl, rdr);
- }
- break;
- }
-
- case DIOCGETRDRS: {
- struct pfioc_rdr *pr = (struct pfioc_rdr *)addr;
- struct pf_rdr *rdr;
-
- pr->nr = 0;
- s = splsoftnet();
- TAILQ_FOREACH(rdr, pf_rdrs_active, entries)
- pr->nr++;
- pr->ticket = ticket_rdrs_active;
- splx(s);
- break;
- }
-
- case DIOCGETRDR: {
- struct pfioc_rdr *pr = (struct pfioc_rdr *)addr;
- struct pf_rdr *rdr;
- u_int32_t nr;
-
- if (pr->ticket != ticket_rdrs_active) {
- error = EBUSY;
- break;
- }
- nr = 0;
- s = splsoftnet();
- rdr = TAILQ_FIRST(pf_rdrs_active);
- while ((rdr != NULL) && (nr < pr->nr)) {
- rdr = TAILQ_NEXT(rdr, entries);
- nr++;
- }
- if (rdr == NULL) {
- error = EBUSY;
- splx(s);
- break;
- }
- bcopy(rdr, &pr->rdr, sizeof(struct pf_rdr));
- pf_dynaddr_copyout(&pr->rdr.saddr);
- pf_dynaddr_copyout(&pr->rdr.daddr);
- pf_dynaddr_copyout(&pr->rdr.raddr);
- splx(s);
- break;
- }
-
- case DIOCCHANGERDR: {
- struct pfioc_changerdr *pcn = (struct pfioc_changerdr *)addr;
- struct pf_rdr *oldrdr = NULL, *newrdr = NULL;
-
- if (pcn->action < PF_CHANGE_ADD_HEAD ||
- pcn->action > PF_CHANGE_REMOVE) {
- error = EINVAL;
- break;
- }
-
- if (pcn->action != PF_CHANGE_REMOVE) {
- newrdr = pool_get(&pf_rdr_pl, PR_NOWAIT);
- if (newrdr == NULL) {
- error = ENOMEM;
- break;
- }
- bcopy(&pcn->newrdr, newrdr, sizeof(struct pf_rdr));
-#ifndef INET
- if (newrdr->af == AF_INET) {
- pool_put(&pf_rdr_pl, newrdr);
- error = EAFNOSUPPORT;
- break;
- }
-#endif /* INET */
-#ifndef INET6
- if (newrdr->af == AF_INET6) {
- pool_put(&pf_rdr_pl, newrdr);
- error = EAFNOSUPPORT;
- break;
- }
-#endif /* INET6 */
- if (newrdr->ifname[0]) {
- newrdr->ifp = ifunit(newrdr->ifname);
- if (newrdr->ifp == NULL) {
- pool_put(&pf_rdr_pl, newrdr);
- error = EINVAL;
- break;
- }
- } else
- newrdr->ifp = NULL;
- if (pf_dynaddr_setup(&newrdr->saddr, newrdr->af) ||
- pf_dynaddr_setup(&newrdr->daddr, newrdr->af) ||
- pf_dynaddr_setup(&newrdr->raddr, newrdr->af)) {
- pf_dynaddr_remove(&newrdr->saddr);
- pf_dynaddr_remove(&newrdr->daddr);
- pf_dynaddr_remove(&newrdr->raddr);
- pool_put(&pf_rdr_pl, newrdr);
- error = EINVAL;
- break;
- }
- }
-
- s = splsoftnet();
-
- if (pcn->action == PF_CHANGE_ADD_HEAD)
- oldrdr = TAILQ_FIRST(pf_rdrs_active);
- else if (pcn->action == PF_CHANGE_ADD_TAIL)
- oldrdr = TAILQ_LAST(pf_rdrs_active, pf_rdrqueue);
- else {
- oldrdr = TAILQ_FIRST(pf_rdrs_active);
- while ((oldrdr != NULL) && pf_compare_rdrs(oldrdr,
- &pcn->oldrdr))
- oldrdr = TAILQ_NEXT(oldrdr, entries);
- if (oldrdr == NULL) {
- error = EINVAL;
- splx(s);
- break;
- }
- }
-
- if (pcn->action == PF_CHANGE_REMOVE) {
- TAILQ_REMOVE(pf_rdrs_active, oldrdr, entries);
- pf_dynaddr_remove(&oldrdr->saddr);
- pf_dynaddr_remove(&oldrdr->daddr);
- pf_dynaddr_remove(&oldrdr->raddr);
- pool_put(&pf_rdr_pl, oldrdr);
- } else {
- if (oldrdr == NULL)
- TAILQ_INSERT_TAIL(pf_rdrs_active, newrdr,
- entries);
- else if (pcn->action == PF_CHANGE_ADD_HEAD ||
- pcn->action == PF_CHANGE_ADD_BEFORE)
- TAILQ_INSERT_BEFORE(oldrdr, newrdr, entries);
- else
- TAILQ_INSERT_AFTER(pf_rdrs_active, oldrdr,
- newrdr, entries);
- }
-
- ticket_rdrs_active++;
- splx(s);
- break;
- }
-
- case DIOCCLRSTATES: {
- struct pf_tree_node *n;
-
- s = splsoftnet();
- RB_FOREACH(n, pf_state_tree, &tree_ext_gwy)
- n->state->expire = 0;
- pf_purge_expired_states();
- pf_status.states = 0;
- splx(s);
- break;
- }
-
- case DIOCKILLSTATES: {
- struct pf_tree_node *n;
- struct pf_state *st;
- struct pfioc_state_kill *psk =
- (struct pfioc_state_kill *)addr;
- int killed = 0;
-
- s = splsoftnet();
- RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) {
- st = n->state;
- if ((!psk->psk_af || st->af == psk->psk_af) &&
- (!psk->psk_proto || psk->psk_proto == st->proto) &&
- PF_MATCHA(psk->psk_src.not, &psk->psk_src.addr.addr,
- &psk->psk_src.mask, &st->lan.addr, st->af) &&
- PF_MATCHA(psk->psk_dst.not, &psk->psk_dst.addr.addr,
- &psk->psk_dst.mask, &st->ext.addr, st->af) &&
- (psk->psk_src.port_op == 0 ||
- pf_match_port(psk->psk_src.port_op,
- psk->psk_src.port[0], psk->psk_src.port[1],
- st->lan.port)) &&
- (psk->psk_dst.port_op == 0 ||
- pf_match_port(psk->psk_dst.port_op,
- psk->psk_dst.port[0], psk->psk_dst.port[1],
- st->ext.port))) {
- st->expire = 0;
- killed++;
- }
- }
- pf_purge_expired_states();
- splx(s);
- psk->psk_af = killed;
- break;
- }
-
- case DIOCADDSTATE: {
- struct pfioc_state *ps = (struct pfioc_state *)addr;
- struct pf_state *state;
-
- state = pool_get(&pf_state_pl, PR_NOWAIT);
- if (state == NULL) {
- error = ENOMEM;
- break;
- }
- s = splsoftnet();
- bcopy(&ps->state, state, sizeof(struct pf_state));
- state->rule.ptr = NULL;
- state->creation = time.tv_sec;
- state->expire += state->creation;
- state->packets = 0;
- state->bytes = 0;
- if (pf_insert_state(state)) {
- pool_put(&pf_state_pl, state);
- error = ENOMEM;
- }
- splx(s);
- }
-
- case DIOCGETSTATE: {
- struct pfioc_state *ps = (struct pfioc_state *)addr;
- struct pf_tree_node *n;
- u_int32_t nr;
- int secs;
-
- nr = 0;
- s = splsoftnet();
- RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) {
- if (nr >= ps->nr)
- break;
- nr++;
- }
- if (n == NULL) {
- error = EBUSY;
- splx(s);
- break;
- }
- bcopy(n->state, &ps->state, sizeof(struct pf_state));
- if (n->state->rule.ptr == NULL)
- ps->state.rule.nr = -1;
- else
- ps->state.rule.nr = n->state->rule.ptr->nr;
- splx(s);
- secs = time.tv_sec;
- ps->state.creation = secs - ps->state.creation;
- if (ps->state.expire <= secs)
- ps->state.expire = 0;
- else
- ps->state.expire -= secs;
- break;
- }
-
- case DIOCGETSTATES: {
- struct pfioc_states *ps = (struct pfioc_states *)addr;
- struct pf_tree_node *n;
- struct pf_state *p, pstore;
- u_int32_t nr = 0;
- int space = ps->ps_len;
-
- if (space == 0) {
- s = splsoftnet();
- RB_FOREACH(n, pf_state_tree, &tree_ext_gwy)
- nr++;
- splx(s);
- ps->ps_len = sizeof(struct pf_state) * nr;
- return (0);
- }
-
- s = splsoftnet();
- p = ps->ps_states;
- RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) {
- int secs = time.tv_sec;
-
- if ((nr + 1) * sizeof(*p) > ps->ps_len)
- break;
-
- bcopy(n->state, &pstore, sizeof(pstore));
- if (n->state->rule.ptr == NULL)
- pstore.rule.nr = -1;
- else
- pstore.rule.nr = n->state->rule.ptr->nr;
- pstore.creation = secs - pstore.creation;
- if (pstore.expire <= secs)
- pstore.expire = 0;
- else
- pstore.expire -= secs;
- error = copyout(&pstore, p, sizeof(*p));
- if (error) {
- splx(s);
- goto fail;
- }
- p++;
- nr++;
- }
- ps->ps_len = sizeof(struct pf_state) * nr;
- splx(s);
- break;
- }
-
- case DIOCSETSTATUSIF: {
- struct pfioc_if *pi = (struct pfioc_if *)addr;
- struct ifnet *ifp;
-
- if ((ifp = ifunit(pi->ifname)) == NULL)
- error = EINVAL;
- else
- status_ifp = ifp;
- break;
- }
-
- case DIOCGETSTATUS: {
- struct pf_status *s = (struct pf_status *)addr;
- bcopy(&pf_status, s, sizeof(struct pf_status));
- break;
- }
-
- case DIOCCLRSTATUS: {
- u_int32_t running = pf_status.running;
- u_int32_t states = pf_status.states;
- u_int32_t since = pf_status.since;
- u_int32_t debug = pf_status.debug;
-
- bzero(&pf_status, sizeof(struct pf_status));
- pf_status.running = running;
- pf_status.states = states;
- pf_status.since = since;
- pf_status.debug = debug;
- break;
- }
-
- case DIOCNATLOOK: {
- struct pfioc_natlook *pnl = (struct pfioc_natlook *)addr;
- struct pf_state *st;
- struct pf_tree_node key;
- int direction = pnl->direction;
-
- key.af = pnl->af;
- key.proto = pnl->proto;
-
- /*
- * userland gives us source and dest of connetion, reverse
- * the lookup so we ask for what happens with the return
- * traffic, enabling us to find it in the state tree.
- */
- PF_ACPY(&key.addr[1], &pnl->saddr, pnl->af);
- key.port[1] = pnl->sport;
- PF_ACPY(&key.addr[0], &pnl->daddr, pnl->af);
- key.port[0] = pnl->dport;
-
- if (!pnl->proto ||
- PF_AZERO(&pnl->saddr, pnl->af) ||
- PF_AZERO(&pnl->daddr, pnl->af) ||
- !pnl->dport || !pnl->sport)
- error = EINVAL;
- else {
- s = splsoftnet();
- if (direction == PF_IN)
- st = pf_find_state(&tree_ext_gwy, &key);
- else
- st = pf_find_state(&tree_lan_ext, &key);
- if (st != NULL) {
- if (direction == PF_IN) {
- PF_ACPY(&pnl->rsaddr, &st->lan.addr,
- st->af);
- pnl->rsport = st->lan.port;
- PF_ACPY(&pnl->rdaddr, &pnl->daddr,
- pnl->af);
- pnl->rdport = pnl->dport;
- } else {
- PF_ACPY(&pnl->rdaddr, &st->gwy.addr,
- st->af);
- pnl->rdport = st->gwy.port;
- PF_ACPY(&pnl->rsaddr, &pnl->saddr,
- pnl->af);
- pnl->rsport = pnl->sport;
- }
- } else
- error = ENOENT;
- splx(s);
- }
- break;
- }
-
- case DIOCSETTIMEOUT: {
- struct pfioc_tm *pt = (struct pfioc_tm *)addr;
- int old;
-
- if (pt->timeout < 0 || pt->timeout >= PFTM_MAX ||
- pt->seconds < 0) {
- error = EINVAL;
- goto fail;
- }
- old = *pftm_timeouts[pt->timeout];
- *pftm_timeouts[pt->timeout] = pt->seconds;
- pt->seconds = old;
- break;
- }
-
- case DIOCGETTIMEOUT: {
- struct pfioc_tm *pt = (struct pfioc_tm *)addr;
-
- if (pt->timeout < 0 || pt->timeout >= PFTM_MAX) {
- error = EINVAL;
- goto fail;
- }
- pt->seconds = *pftm_timeouts[pt->timeout];
- break;
- }
-
- case DIOCGETLIMIT: {
- struct pfioc_limit *pl = (struct pfioc_limit *)addr;
-
- if (pl->index < 0 || pl->index >= PF_LIMIT_MAX) {
- error = EINVAL;
- goto fail;
- }
- pl->limit = pf_pool_limits[pl->index].limit;
- break;
- }
-
- case DIOCSETLIMIT: {
- struct pfioc_limit *pl = (struct pfioc_limit *)addr;
- int old_limit;
-
- if (pl->index < 0 || pl->index >= PF_LIMIT_MAX) {
- error = EINVAL;
- goto fail;
- }
- if (pool_sethardlimit(pf_pool_limits[pl->index].pp,
- pl->limit, NULL, 0) != 0) {
- error = EBUSY;
- goto fail;
- }
- old_limit = pf_pool_limits[pl->index].limit;
- pf_pool_limits[pl->index].limit = pl->limit;
- pl->limit = old_limit;
- break;
- }
-
- case DIOCSETDEBUG: {
- u_int32_t *level = (u_int32_t *)addr;
- pf_status.debug = *level;
- break;
- }
-
- case DIOCCLRRULECTRS: {
- struct pf_rule *rule;
-
- s = splsoftnet();
- TAILQ_FOREACH(rule, pf_rules_active, entries)
- rule->evaluations = rule->packets =
- rule->bytes = 0;
- splx(s);
- break;
- }
-
- default:
- error = ENODEV;
- break;
- }
-fail:
-
- return (error);
-}
-
#define PF_CALC_SKIP_STEP(i, c) \
do { \
if (a & 1 << i) { \
diff --git a/sys/net/pf_ioctl.c b/sys/net/pf_ioctl.c
new file mode 100644
index 00000000000..ba8591dd64c
--- /dev/null
+++ b/sys/net/pf_ioctl.c
@@ -0,0 +1,1483 @@
+/* $OpenBSD: pf_ioctl.c,v 1.1 2002/06/09 03:57:18 pb 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.
+ *
+ * Effort sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F30602-01-2-0537.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/filio.h>
+#include <sys/fcntl.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/time.h>
+#include <sys/pool.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+
+#include <net/pfvar.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet/in_pcb.h>
+#endif /* INET6 */
+
+void pfattach(int);
+int pfopen(dev_t, int, int, struct proc *);
+int pfclose(dev_t, int, int, struct proc *);
+int pfioctl(dev_t, u_long, caddr_t, int, struct proc *);
+
+extern struct timeout pf_expire_to;
+
+#define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x
+
+void
+pfattach(int num)
+{
+ pool_init(&pf_tree_pl, sizeof(struct pf_tree_node), 0, 0, 0, "pftrpl",
+ NULL);
+ pool_init(&pf_rule_pl, sizeof(struct pf_rule), 0, 0, 0, "pfrulepl",
+ &pool_allocator_nointr);
+ pool_init(&pf_nat_pl, sizeof(struct pf_nat), 0, 0, 0, "pfnatpl",
+ &pool_allocator_nointr);
+ pool_init(&pf_binat_pl, sizeof(struct pf_binat), 0, 0, 0, "pfbinatpl",
+ &pool_allocator_nointr);
+ pool_init(&pf_rdr_pl, sizeof(struct pf_rdr), 0, 0, 0, "pfrdrpl",
+ &pool_allocator_nointr);
+ pool_init(&pf_state_pl, sizeof(struct pf_state), 0, 0, 0, "pfstatepl",
+ NULL);
+ pool_init(&pf_sport_pl, sizeof(struct pf_port_node), 0, 0, 0, "pfsport",
+ NULL);
+ pool_init(&pf_addr_pl, sizeof(struct pf_addr_dyn), 0, 0, 0, "pfaddr",
+ NULL);
+
+ TAILQ_INIT(&pf_rules[0]);
+ TAILQ_INIT(&pf_rules[1]);
+ TAILQ_INIT(&pf_nats[0]);
+ TAILQ_INIT(&pf_nats[1]);
+ TAILQ_INIT(&pf_binats[0]);
+ TAILQ_INIT(&pf_binats[1]);
+ TAILQ_INIT(&pf_rdrs[0]);
+ TAILQ_INIT(&pf_rdrs[1]);
+ pf_rules_active = &pf_rules[0];
+ pf_rules_inactive = &pf_rules[1];
+ pf_nats_active = &pf_nats[0];
+ pf_nats_inactive = &pf_nats[1];
+ pf_binats_active = &pf_binats[0];
+ pf_binats_inactive = &pf_binats[1];
+ pf_rdrs_active = &pf_rdrs[0];
+ pf_rdrs_inactive = &pf_rdrs[1];
+
+ LIST_INIT(&pf_tcp_ports);
+ LIST_INIT(&pf_udp_ports);
+
+ timeout_set(&pf_expire_to, pf_purge_timeout, &pf_expire_to);
+ timeout_add(&pf_expire_to, pftm_interval * hz);
+
+ pf_normalize_init();
+ pf_status.debug = PF_DEBUG_URGENT;
+}
+
+int
+pfopen(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ if (minor(dev) >= 1)
+ return (ENXIO);
+ return (0);
+}
+
+int
+pfclose(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ if (minor(dev) >= 1)
+ return (ENXIO);
+ return (0);
+}
+
+int
+pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
+{
+ int error = 0;
+ int s;
+
+ /* XXX keep in sync with switch() below */
+ if (securelevel > 1)
+ switch (cmd) {
+ case DIOCGETRULES:
+ case DIOCGETRULE:
+ case DIOCGETNATS:
+ case DIOCGETNAT:
+ case DIOCGETBINATS:
+ case DIOCGETBINAT:
+ case DIOCGETRDRS:
+ case DIOCGETRDR:
+ case DIOCGETSTATE:
+ case DIOCSETSTATUSIF:
+ case DIOCGETSTATUS:
+ case DIOCCLRSTATUS:
+ case DIOCNATLOOK:
+ case DIOCSETDEBUG:
+ case DIOCGETSTATES:
+ case DIOCGETTIMEOUT:
+ case DIOCCLRRULECTRS:
+ case DIOCGETLIMIT:
+ break;
+ default:
+ return (EPERM);
+ }
+
+ if (!(flags & FWRITE))
+ switch (cmd) {
+ case DIOCGETRULES:
+ case DIOCGETRULE:
+ case DIOCGETNATS:
+ case DIOCGETNAT:
+ case DIOCGETRDRS:
+ case DIOCGETRDR:
+ case DIOCGETSTATE:
+ case DIOCGETSTATUS:
+ case DIOCGETSTATES:
+ case DIOCGETTIMEOUT:
+ case DIOCGETBINATS:
+ case DIOCGETBINAT:
+ case DIOCGETLIMIT:
+ break;
+ default:
+ return (EACCES);
+ }
+
+ switch (cmd) {
+
+ case DIOCSTART:
+ if (pf_status.running)
+ error = EEXIST;
+ else {
+ u_int32_t states = pf_status.states;
+ bzero(&pf_status, sizeof(struct pf_status));
+ pf_status.running = 1;
+ pf_status.states = states;
+ pf_status.since = time.tv_sec;
+ DPFPRINTF(PF_DEBUG_MISC, ("pf: started\n"));
+ }
+ break;
+
+ case DIOCSTOP:
+ if (!pf_status.running)
+ error = ENOENT;
+ else {
+ pf_status.running = 0;
+ DPFPRINTF(PF_DEBUG_MISC, ("pf: stopped\n"));
+ }
+ break;
+
+ case DIOCBEGINRULES: {
+ u_int32_t *ticket = (u_int32_t *)addr;
+ struct pf_rule *rule;
+
+ while ((rule = TAILQ_FIRST(pf_rules_inactive)) != NULL) {
+ TAILQ_REMOVE(pf_rules_inactive, rule, entries);
+ pf_dynaddr_remove(&rule->src.addr);
+ pf_dynaddr_remove(&rule->dst.addr);
+ pool_put(&pf_rule_pl, rule);
+ }
+ *ticket = ++ticket_rules_inactive;
+ break;
+ }
+
+ case DIOCADDRULE: {
+ struct pfioc_rule *pr = (struct pfioc_rule *)addr;
+ struct pf_rule *rule, *tail;
+
+ if (pr->ticket != ticket_rules_inactive) {
+ error = EBUSY;
+ break;
+ }
+ rule = pool_get(&pf_rule_pl, PR_NOWAIT);
+ if (rule == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ bcopy(&pr->rule, rule, sizeof(struct pf_rule));
+#ifndef INET
+ if (rule->af == AF_INET) {
+ pool_put(&pf_rule_pl, rule);
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /* INET */
+#ifndef INET6
+ if (rule->af == AF_INET6) {
+ pool_put(&pf_rule_pl, rule);
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /* INET6 */
+ tail = TAILQ_LAST(pf_rules_inactive, pf_rulequeue);
+ if (tail)
+ rule->nr = tail->nr + 1;
+ else
+ rule->nr = 0;
+ if (rule->ifname[0]) {
+ rule->ifp = ifunit(rule->ifname);
+ if (rule->ifp == NULL) {
+ pool_put(&pf_rule_pl, rule);
+ error = EINVAL;
+ break;
+ }
+ } else
+ rule->ifp = NULL;
+ if (rule->rt_ifname[0]) {
+ rule->rt_ifp = ifunit(rule->rt_ifname);
+ if (rule->rt_ifname == NULL) {
+ pool_put(&pf_rule_pl, rule);
+ error = EINVAL;
+ break;
+ }
+ } else
+ rule->rt_ifp = NULL;
+ if (pf_dynaddr_setup(&rule->src.addr, rule->af) ||
+ pf_dynaddr_setup(&rule->dst.addr, rule->af)) {
+ pf_dynaddr_remove(&rule->src.addr);
+ pf_dynaddr_remove(&rule->dst.addr);
+ pool_put(&pf_rule_pl, rule);
+ error = EINVAL;
+ break;
+ }
+ rule->evaluations = rule->packets = rule->bytes = 0;
+ TAILQ_INSERT_TAIL(pf_rules_inactive, rule, entries);
+ break;
+ }
+
+ case DIOCCOMMITRULES: {
+ u_int32_t *ticket = (u_int32_t *)addr;
+ struct pf_rulequeue *old_rules;
+ struct pf_rule *rule;
+ struct pf_tree_node *n;
+
+ if (*ticket != ticket_rules_inactive) {
+ error = EBUSY;
+ break;
+ }
+
+ /* Swap rules, keep the old. */
+ s = splsoftnet();
+ /*
+ * Rules are about to get freed, clear rule pointers in states
+ */
+ RB_FOREACH(n, pf_state_tree, &tree_ext_gwy)
+ n->state->rule.ptr = NULL;
+ old_rules = pf_rules_active;
+ pf_rules_active = pf_rules_inactive;
+ pf_rules_inactive = old_rules;
+ ticket_rules_active = ticket_rules_inactive;
+ pf_calc_skip_steps(pf_rules_active);
+ splx(s);
+
+ /* Purge the old rule list. */
+ while ((rule = TAILQ_FIRST(old_rules)) != NULL) {
+ TAILQ_REMOVE(old_rules, rule, entries);
+ pf_dynaddr_remove(&rule->src.addr);
+ pf_dynaddr_remove(&rule->dst.addr);
+ pool_put(&pf_rule_pl, rule);
+ }
+ break;
+ }
+
+ case DIOCGETRULES: {
+ struct pfioc_rule *pr = (struct pfioc_rule *)addr;
+ struct pf_rule *tail;
+
+ s = splsoftnet();
+ tail = TAILQ_LAST(pf_rules_active, pf_rulequeue);
+ if (tail)
+ pr->nr = tail->nr + 1;
+ else
+ pr->nr = 0;
+ pr->ticket = ticket_rules_active;
+ splx(s);
+ break;
+ }
+
+ case DIOCGETRULE: {
+ struct pfioc_rule *pr = (struct pfioc_rule *)addr;
+ struct pf_rule *rule;
+
+ if (pr->ticket != ticket_rules_active) {
+ error = EBUSY;
+ break;
+ }
+ s = splsoftnet();
+ rule = TAILQ_FIRST(pf_rules_active);
+ while ((rule != NULL) && (rule->nr != pr->nr))
+ rule = TAILQ_NEXT(rule, entries);
+ if (rule == NULL) {
+ error = EBUSY;
+ splx(s);
+ break;
+ }
+ bcopy(rule, &pr->rule, sizeof(struct pf_rule));
+ pf_dynaddr_copyout(&pr->rule.src.addr);
+ pf_dynaddr_copyout(&pr->rule.dst.addr);
+ splx(s);
+ break;
+ }
+
+ case DIOCCHANGERULE: {
+ struct pfioc_changerule *pcr = (struct pfioc_changerule *)addr;
+ struct pf_rule *oldrule = NULL, *newrule = NULL;
+ u_int32_t nr = 0;
+
+ if (pcr->action < PF_CHANGE_ADD_HEAD ||
+ pcr->action > PF_CHANGE_REMOVE) {
+ error = EINVAL;
+ break;
+ }
+
+ if (pcr->action != PF_CHANGE_REMOVE) {
+ newrule = pool_get(&pf_rule_pl, PR_NOWAIT);
+ if (newrule == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ bcopy(&pcr->newrule, newrule, sizeof(struct pf_rule));
+#ifndef INET
+ if (newrule->af == AF_INET) {
+ pool_put(&pf_rule_pl, newrule);
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /* INET */
+#ifndef INET6
+ if (newrule->af == AF_INET6) {
+ pool_put(&pf_rule_pl, newrule);
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /* INET6 */
+ if (newrule->ifname[0]) {
+ newrule->ifp = ifunit(newrule->ifname);
+ if (newrule->ifp == NULL) {
+ pool_put(&pf_rule_pl, newrule);
+ error = EINVAL;
+ break;
+ }
+ } else
+ newrule->ifp = NULL;
+ if (newrule->rt_ifname[0]) {
+ newrule->rt_ifp = ifunit(newrule->rt_ifname);
+ if (newrule->rt_ifname == NULL) {
+ pool_put(&pf_rule_pl, newrule);
+ error = EINVAL;
+ break;
+ }
+ } else
+ newrule->rt_ifp = NULL;
+ if (pf_dynaddr_setup(&newrule->src.addr, newrule->af) ||
+ pf_dynaddr_setup(&newrule->dst.addr, newrule->af)) {
+ pf_dynaddr_remove(&newrule->src.addr);
+ pf_dynaddr_remove(&newrule->dst.addr);
+ pool_put(&pf_rule_pl, newrule);
+ error = EINVAL;
+ break;
+ }
+ newrule->evaluations = newrule->packets = 0;
+ newrule->bytes = 0;
+ }
+
+ s = splsoftnet();
+
+ if (pcr->action == PF_CHANGE_ADD_HEAD)
+ oldrule = TAILQ_FIRST(pf_rules_active);
+ else if (pcr->action == PF_CHANGE_ADD_TAIL)
+ oldrule = TAILQ_LAST(pf_rules_active, pf_rulequeue);
+ else {
+ oldrule = TAILQ_FIRST(pf_rules_active);
+ while ((oldrule != NULL) && pf_compare_rules(oldrule,
+ &pcr->oldrule))
+ oldrule = TAILQ_NEXT(oldrule, entries);
+ if (oldrule == NULL) {
+ error = EINVAL;
+ splx(s);
+ break;
+ }
+ }
+
+ if (pcr->action == PF_CHANGE_REMOVE) {
+ struct pf_tree_node *n;
+
+ RB_FOREACH(n, pf_state_tree, &tree_ext_gwy)
+ if (n->state->rule.ptr == oldrule)
+ n->state->rule.ptr = NULL;
+ TAILQ_REMOVE(pf_rules_active, oldrule, entries);
+ pf_dynaddr_remove(&oldrule->src.addr);
+ pf_dynaddr_remove(&oldrule->dst.addr);
+ pool_put(&pf_rule_pl, oldrule);
+ } else {
+ if (oldrule == NULL)
+ TAILQ_INSERT_TAIL(pf_rules_active, newrule,
+ entries);
+ else if (pcr->action == PF_CHANGE_ADD_HEAD ||
+ pcr->action == PF_CHANGE_ADD_BEFORE)
+ TAILQ_INSERT_BEFORE(oldrule, newrule, entries);
+ else
+ TAILQ_INSERT_AFTER(pf_rules_active, oldrule,
+ newrule, entries);
+ }
+
+ TAILQ_FOREACH(oldrule, pf_rules_active, entries)
+ oldrule->nr = nr++;
+
+ pf_calc_skip_steps(pf_rules_active);
+
+ ticket_rules_active++;
+ splx(s);
+ break;
+ }
+
+ case DIOCBEGINNATS: {
+ u_int32_t *ticket = (u_int32_t *)addr;
+ struct pf_nat *nat;
+
+ while ((nat = TAILQ_FIRST(pf_nats_inactive)) != NULL) {
+ pf_dynaddr_remove(&nat->saddr);
+ pf_dynaddr_remove(&nat->daddr);
+ pf_dynaddr_remove(&nat->raddr);
+ TAILQ_REMOVE(pf_nats_inactive, nat, entries);
+ pool_put(&pf_nat_pl, nat);
+ }
+ *ticket = ++ticket_nats_inactive;
+ break;
+ }
+
+ case DIOCADDNAT: {
+ struct pfioc_nat *pn = (struct pfioc_nat *)addr;
+ struct pf_nat *nat;
+
+ if (pn->ticket != ticket_nats_inactive) {
+ error = EBUSY;
+ break;
+ }
+ nat = pool_get(&pf_nat_pl, PR_NOWAIT);
+ if (nat == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ bcopy(&pn->nat, nat, sizeof(struct pf_nat));
+#ifndef INET
+ if (nat->af == AF_INET) {
+ pool_put(&pf_nat_pl, nat);
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /* INET */
+#ifndef INET6
+ if (nat->af == AF_INET6) {
+ pool_put(&pf_nat_pl, nat);
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /* INET6 */
+ if (nat->ifname[0]) {
+ nat->ifp = ifunit(nat->ifname);
+ if (nat->ifp == NULL) {
+ pool_put(&pf_nat_pl, nat);
+ error = EINVAL;
+ break;
+ }
+ } else
+ nat->ifp = NULL;
+ if (pf_dynaddr_setup(&nat->saddr, nat->af) ||
+ pf_dynaddr_setup(&nat->daddr, nat->af) ||
+ pf_dynaddr_setup(&nat->raddr, nat->af)) {
+ pf_dynaddr_remove(&nat->saddr);
+ pf_dynaddr_remove(&nat->daddr);
+ pf_dynaddr_remove(&nat->raddr);
+ pool_put(&pf_nat_pl, nat);
+ error = EINVAL;
+ break;
+ }
+ TAILQ_INSERT_TAIL(pf_nats_inactive, nat, entries);
+ break;
+ }
+
+ case DIOCCOMMITNATS: {
+ u_int32_t *ticket = (u_int32_t *)addr;
+ struct pf_natqueue *old_nats;
+ struct pf_nat *nat;
+
+ if (*ticket != ticket_nats_inactive) {
+ error = EBUSY;
+ break;
+ }
+
+ /* Swap nats, keep the old. */
+ s = splsoftnet();
+ old_nats = pf_nats_active;
+ pf_nats_active = pf_nats_inactive;
+ pf_nats_inactive = old_nats;
+ ticket_nats_active = ticket_nats_inactive;
+ splx(s);
+
+ /* Purge the old nat list */
+ while ((nat = TAILQ_FIRST(old_nats)) != NULL) {
+ pf_dynaddr_remove(&nat->saddr);
+ pf_dynaddr_remove(&nat->daddr);
+ pf_dynaddr_remove(&nat->raddr);
+ TAILQ_REMOVE(old_nats, nat, entries);
+ pool_put(&pf_nat_pl, nat);
+ }
+ break;
+ }
+
+ case DIOCGETNATS: {
+ struct pfioc_nat *pn = (struct pfioc_nat *)addr;
+ struct pf_nat *nat;
+
+ pn->nr = 0;
+ s = splsoftnet();
+ TAILQ_FOREACH(nat, pf_nats_active, entries)
+ pn->nr++;
+ pn->ticket = ticket_nats_active;
+ splx(s);
+ break;
+ }
+
+ case DIOCGETNAT: {
+ struct pfioc_nat *pn = (struct pfioc_nat *)addr;
+ struct pf_nat *nat;
+ u_int32_t nr;
+
+ if (pn->ticket != ticket_nats_active) {
+ error = EBUSY;
+ break;
+ }
+ nr = 0;
+ s = splsoftnet();
+ nat = TAILQ_FIRST(pf_nats_active);
+ while ((nat != NULL) && (nr < pn->nr)) {
+ nat = TAILQ_NEXT(nat, entries);
+ nr++;
+ }
+ if (nat == NULL) {
+ error = EBUSY;
+ splx(s);
+ break;
+ }
+ bcopy(nat, &pn->nat, sizeof(struct pf_nat));
+ pf_dynaddr_copyout(&pn->nat.saddr);
+ pf_dynaddr_copyout(&pn->nat.daddr);
+ pf_dynaddr_copyout(&pn->nat.raddr);
+ splx(s);
+ break;
+ }
+
+ case DIOCCHANGENAT: {
+ struct pfioc_changenat *pcn = (struct pfioc_changenat *)addr;
+ struct pf_nat *oldnat = NULL, *newnat = NULL;
+
+ if (pcn->action < PF_CHANGE_ADD_HEAD ||
+ pcn->action > PF_CHANGE_REMOVE) {
+ error = EINVAL;
+ break;
+ }
+
+ if (pcn->action != PF_CHANGE_REMOVE) {
+ newnat = pool_get(&pf_nat_pl, PR_NOWAIT);
+ if (newnat == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ bcopy(&pcn->newnat, newnat, sizeof(struct pf_nat));
+#ifndef INET
+ if (newnat->af == AF_INET) {
+ pool_put(&pf_nat_pl, newnat);
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /* INET */
+#ifndef INET6
+ if (newnat->af == AF_INET6) {
+ pool_put(&pf_nat_pl, newnat);
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /* INET6 */
+ if (newnat->ifname[0]) {
+ newnat->ifp = ifunit(newnat->ifname);
+ if (newnat->ifp == NULL) {
+ pool_put(&pf_nat_pl, newnat);
+ error = EINVAL;
+ break;
+ }
+ } else
+ newnat->ifp = NULL;
+ if (pf_dynaddr_setup(&newnat->saddr, newnat->af) ||
+ pf_dynaddr_setup(&newnat->daddr, newnat->af) ||
+ pf_dynaddr_setup(&newnat->raddr, newnat->af)) {
+ pf_dynaddr_remove(&newnat->saddr);
+ pf_dynaddr_remove(&newnat->daddr);
+ pf_dynaddr_remove(&newnat->raddr);
+ pool_put(&pf_nat_pl, newnat);
+ error = EINVAL;
+ break;
+ }
+ }
+
+ s = splsoftnet();
+
+ if (pcn->action == PF_CHANGE_ADD_HEAD)
+ oldnat = TAILQ_FIRST(pf_nats_active);
+ else if (pcn->action == PF_CHANGE_ADD_TAIL)
+ oldnat = TAILQ_LAST(pf_nats_active, pf_natqueue);
+ else {
+ oldnat = TAILQ_FIRST(pf_nats_active);
+ while ((oldnat != NULL) && pf_compare_nats(oldnat,
+ &pcn->oldnat))
+ oldnat = TAILQ_NEXT(oldnat, entries);
+ if (oldnat == NULL) {
+ error = EINVAL;
+ splx(s);
+ break;
+ }
+ }
+
+ if (pcn->action == PF_CHANGE_REMOVE) {
+ pf_dynaddr_remove(&oldnat->saddr);
+ pf_dynaddr_remove(&oldnat->daddr);
+ pf_dynaddr_remove(&oldnat->raddr);
+ TAILQ_REMOVE(pf_nats_active, oldnat, entries);
+ pool_put(&pf_nat_pl, oldnat);
+ } else {
+ if (oldnat == NULL)
+ TAILQ_INSERT_TAIL(pf_nats_active, newnat,
+ entries);
+ else if (pcn->action == PF_CHANGE_ADD_HEAD ||
+ pcn->action == PF_CHANGE_ADD_BEFORE)
+ TAILQ_INSERT_BEFORE(oldnat, newnat, entries);
+ else
+ TAILQ_INSERT_AFTER(pf_nats_active, oldnat,
+ newnat, entries);
+ }
+
+ ticket_nats_active++;
+ splx(s);
+ break;
+ }
+
+ case DIOCBEGINBINATS: {
+ u_int32_t *ticket = (u_int32_t *)addr;
+ struct pf_binat *binat;
+
+ while ((binat = TAILQ_FIRST(pf_binats_inactive)) != NULL) {
+ TAILQ_REMOVE(pf_binats_inactive, binat, entries);
+ pf_dynaddr_remove(&binat->saddr);
+ pf_dynaddr_remove(&binat->daddr);
+ pf_dynaddr_remove(&binat->raddr);
+ pool_put(&pf_binat_pl, binat);
+ }
+ *ticket = ++ticket_binats_inactive;
+ break;
+ }
+
+ case DIOCADDBINAT: {
+ struct pfioc_binat *pb = (struct pfioc_binat *)addr;
+ struct pf_binat *binat;
+
+ if (pb->ticket != ticket_binats_inactive) {
+ error = EBUSY;
+ break;
+ }
+ binat = pool_get(&pf_binat_pl, PR_NOWAIT);
+ if (binat == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ bcopy(&pb->binat, binat, sizeof(struct pf_binat));
+#ifndef INET
+ if (binat->af == AF_INET) {
+ pool_put(&pf_binat_pl, binat);
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /* INET */
+#ifndef INET6
+ if (binat->af == AF_INET6) {
+ pool_put(&pf_binat_pl, binat);
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /* INET6 */
+ if (binat->ifname[0]) {
+ binat->ifp = ifunit(binat->ifname);
+ if (binat->ifp == NULL) {
+ pool_put(&pf_binat_pl, binat);
+ error = EINVAL;
+ break;
+ }
+ } else
+ binat->ifp = NULL;
+ if (pf_dynaddr_setup(&binat->saddr, binat->af) ||
+ pf_dynaddr_setup(&binat->daddr, binat->af) ||
+ pf_dynaddr_setup(&binat->raddr, binat->af)) {
+ pf_dynaddr_remove(&binat->saddr);
+ pf_dynaddr_remove(&binat->daddr);
+ pf_dynaddr_remove(&binat->raddr);
+ pool_put(&pf_binat_pl, binat);
+ error = EINVAL;
+ break;
+ }
+ TAILQ_INSERT_TAIL(pf_binats_inactive, binat, entries);
+ break;
+ }
+
+ case DIOCCOMMITBINATS: {
+ u_int32_t *ticket = (u_int32_t *)addr;
+ struct pf_binatqueue *old_binats;
+ struct pf_binat *binat;
+
+ if (*ticket != ticket_binats_inactive) {
+ error = EBUSY;
+ break;
+ }
+
+ /* Swap binats, keep the old. */
+ s = splsoftnet();
+ old_binats = pf_binats_active;
+ pf_binats_active = pf_binats_inactive;
+ pf_binats_inactive = old_binats;
+ ticket_binats_active = ticket_binats_inactive;
+ splx(s);
+
+ /* Purge the old binat list */
+ while ((binat = TAILQ_FIRST(old_binats)) != NULL) {
+ TAILQ_REMOVE(old_binats, binat, entries);
+ pf_dynaddr_remove(&binat->saddr);
+ pf_dynaddr_remove(&binat->daddr);
+ pf_dynaddr_remove(&binat->raddr);
+ pool_put(&pf_binat_pl, binat);
+ }
+ break;
+ }
+
+ case DIOCGETBINATS: {
+ struct pfioc_binat *pb = (struct pfioc_binat *)addr;
+ struct pf_binat *binat;
+
+ pb->nr = 0;
+ s = splsoftnet();
+ TAILQ_FOREACH(binat, pf_binats_active, entries)
+ pb->nr++;
+ pb->ticket = ticket_binats_active;
+ splx(s);
+ break;
+ }
+
+ case DIOCGETBINAT: {
+ struct pfioc_binat *pb = (struct pfioc_binat *)addr;
+ struct pf_binat *binat;
+ u_int32_t nr;
+
+ if (pb->ticket != ticket_binats_active) {
+ error = EBUSY;
+ break;
+ }
+ nr = 0;
+ s = splsoftnet();
+ binat = TAILQ_FIRST(pf_binats_active);
+ while ((binat != NULL) && (nr < pb->nr)) {
+ binat = TAILQ_NEXT(binat, entries);
+ nr++;
+ }
+ if (binat == NULL) {
+ error = EBUSY;
+ splx(s);
+ break;
+ }
+ bcopy(binat, &pb->binat, sizeof(struct pf_binat));
+ pf_dynaddr_copyout(&pb->binat.saddr);
+ pf_dynaddr_copyout(&pb->binat.daddr);
+ pf_dynaddr_copyout(&pb->binat.raddr);
+ splx(s);
+ break;
+ }
+
+ case DIOCCHANGEBINAT: {
+ struct pfioc_changebinat *pcn = (struct pfioc_changebinat *)addr;
+ struct pf_binat *oldbinat = NULL, *newbinat = NULL;
+
+ if (pcn->action < PF_CHANGE_ADD_HEAD ||
+ pcn->action > PF_CHANGE_REMOVE) {
+ error = EINVAL;
+ break;
+ }
+
+ if (pcn->action != PF_CHANGE_REMOVE) {
+ newbinat = pool_get(&pf_binat_pl, PR_NOWAIT);
+ if (newbinat == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ bcopy(&pcn->newbinat, newbinat,
+ sizeof(struct pf_binat));
+#ifndef INET
+ if (newbinat->af == AF_INET) {
+ pool_put(&pf_binat_pl, newbinat);
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /* INET */
+#ifndef INET6
+ if (newbinat->af == AF_INET6) {
+ pool_put(&pf_binat_pl, newbinat);
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /* INET6 */
+ if (newbinat->ifname[0]) {
+ newbinat->ifp = ifunit(newbinat->ifname);
+ if (newbinat->ifp == NULL) {
+ pool_put(&pf_binat_pl, newbinat);
+ error = EINVAL;
+ break;
+ }
+ } else
+ newbinat->ifp = NULL;
+ if (pf_dynaddr_setup(&newbinat->saddr, newbinat->af) ||
+ pf_dynaddr_setup(&newbinat->daddr, newbinat->af) ||
+ pf_dynaddr_setup(&newbinat->raddr, newbinat->af)) {
+ pf_dynaddr_remove(&newbinat->saddr);
+ pf_dynaddr_remove(&newbinat->daddr);
+ pf_dynaddr_remove(&newbinat->raddr);
+ pool_put(&pf_binat_pl, newbinat);
+ error = EINVAL;
+ break;
+ }
+ }
+
+ s = splsoftnet();
+
+ if (pcn->action == PF_CHANGE_ADD_HEAD)
+ oldbinat = TAILQ_FIRST(pf_binats_active);
+ else if (pcn->action == PF_CHANGE_ADD_TAIL)
+ oldbinat = TAILQ_LAST(pf_binats_active, pf_binatqueue);
+ else {
+ oldbinat = TAILQ_FIRST(pf_binats_active);
+ while ((oldbinat != NULL) && pf_compare_binats(oldbinat,
+ &pcn->oldbinat))
+ oldbinat = TAILQ_NEXT(oldbinat, entries);
+ if (oldbinat == NULL) {
+ error = EINVAL;
+ splx(s);
+ break;
+ }
+ }
+
+ if (pcn->action == PF_CHANGE_REMOVE) {
+ TAILQ_REMOVE(pf_binats_active, oldbinat, entries);
+ pf_dynaddr_remove(&oldbinat->saddr);
+ pf_dynaddr_remove(&oldbinat->daddr);
+ pf_dynaddr_remove(&oldbinat->raddr);
+ pool_put(&pf_binat_pl, oldbinat);
+ } else {
+ if (oldbinat == NULL)
+ TAILQ_INSERT_TAIL(pf_binats_active, newbinat,
+ entries);
+ else if (pcn->action == PF_CHANGE_ADD_HEAD ||
+ pcn->action == PF_CHANGE_ADD_BEFORE)
+ TAILQ_INSERT_BEFORE(oldbinat, newbinat,
+ entries);
+ else
+ TAILQ_INSERT_AFTER(pf_binats_active, oldbinat,
+ newbinat, entries);
+ }
+
+ ticket_binats_active++;
+ splx(s);
+ break;
+ }
+
+ case DIOCBEGINRDRS: {
+ u_int32_t *ticket = (u_int32_t *)addr;
+ struct pf_rdr *rdr;
+
+ while ((rdr = TAILQ_FIRST(pf_rdrs_inactive)) != NULL) {
+ TAILQ_REMOVE(pf_rdrs_inactive, rdr, entries);
+ pf_dynaddr_remove(&rdr->saddr);
+ pf_dynaddr_remove(&rdr->daddr);
+ pf_dynaddr_remove(&rdr->raddr);
+ pool_put(&pf_rdr_pl, rdr);
+ }
+ *ticket = ++ticket_rdrs_inactive;
+ break;
+ }
+
+ case DIOCADDRDR: {
+ struct pfioc_rdr *pr = (struct pfioc_rdr *)addr;
+ struct pf_rdr *rdr;
+
+ if (pr->ticket != ticket_rdrs_inactive) {
+ error = EBUSY;
+ break;
+ }
+ rdr = pool_get(&pf_rdr_pl, PR_NOWAIT);
+ if (rdr == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ bcopy(&pr->rdr, rdr, sizeof(struct pf_rdr));
+#ifndef INET
+ if (rdr->af == AF_INET) {
+ pool_put(&pf_rdr_pl, rdr);
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /* INET */
+#ifndef INET6
+ if (rdr->af == AF_INET6) {
+ pool_put(&pf_rdr_pl, rdr);
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /* INET6 */
+ if (rdr->ifname[0]) {
+ rdr->ifp = ifunit(rdr->ifname);
+ if (rdr->ifp == NULL) {
+ pool_put(&pf_rdr_pl, rdr);
+ error = EINVAL;
+ break;
+ }
+ } else
+ rdr->ifp = NULL;
+ if (pf_dynaddr_setup(&rdr->saddr, rdr->af) ||
+ pf_dynaddr_setup(&rdr->daddr, rdr->af) ||
+ pf_dynaddr_setup(&rdr->raddr, rdr->af)) {
+ pf_dynaddr_remove(&rdr->saddr);
+ pf_dynaddr_remove(&rdr->daddr);
+ pf_dynaddr_remove(&rdr->raddr);
+ pool_put(&pf_rdr_pl, rdr);
+ error = EINVAL;
+ break;
+ }
+ TAILQ_INSERT_TAIL(pf_rdrs_inactive, rdr, entries);
+ break;
+ }
+
+ case DIOCCOMMITRDRS: {
+ u_int32_t *ticket = (u_int32_t *)addr;
+ struct pf_rdrqueue *old_rdrs;
+ struct pf_rdr *rdr;
+
+ if (*ticket != ticket_rdrs_inactive) {
+ error = EBUSY;
+ break;
+ }
+
+ /* Swap rdrs, keep the old. */
+ s = splsoftnet();
+ old_rdrs = pf_rdrs_active;
+ pf_rdrs_active = pf_rdrs_inactive;
+ pf_rdrs_inactive = old_rdrs;
+ ticket_rdrs_active = ticket_rdrs_inactive;
+ splx(s);
+
+ /* Purge the old rdr list */
+ while ((rdr = TAILQ_FIRST(old_rdrs)) != NULL) {
+ TAILQ_REMOVE(old_rdrs, rdr, entries);
+ pf_dynaddr_remove(&rdr->saddr);
+ pf_dynaddr_remove(&rdr->daddr);
+ pf_dynaddr_remove(&rdr->raddr);
+ pool_put(&pf_rdr_pl, rdr);
+ }
+ break;
+ }
+
+ case DIOCGETRDRS: {
+ struct pfioc_rdr *pr = (struct pfioc_rdr *)addr;
+ struct pf_rdr *rdr;
+
+ pr->nr = 0;
+ s = splsoftnet();
+ TAILQ_FOREACH(rdr, pf_rdrs_active, entries)
+ pr->nr++;
+ pr->ticket = ticket_rdrs_active;
+ splx(s);
+ break;
+ }
+
+ case DIOCGETRDR: {
+ struct pfioc_rdr *pr = (struct pfioc_rdr *)addr;
+ struct pf_rdr *rdr;
+ u_int32_t nr;
+
+ if (pr->ticket != ticket_rdrs_active) {
+ error = EBUSY;
+ break;
+ }
+ nr = 0;
+ s = splsoftnet();
+ rdr = TAILQ_FIRST(pf_rdrs_active);
+ while ((rdr != NULL) && (nr < pr->nr)) {
+ rdr = TAILQ_NEXT(rdr, entries);
+ nr++;
+ }
+ if (rdr == NULL) {
+ error = EBUSY;
+ splx(s);
+ break;
+ }
+ bcopy(rdr, &pr->rdr, sizeof(struct pf_rdr));
+ pf_dynaddr_copyout(&pr->rdr.saddr);
+ pf_dynaddr_copyout(&pr->rdr.daddr);
+ pf_dynaddr_copyout(&pr->rdr.raddr);
+ splx(s);
+ break;
+ }
+
+ case DIOCCHANGERDR: {
+ struct pfioc_changerdr *pcn = (struct pfioc_changerdr *)addr;
+ struct pf_rdr *oldrdr = NULL, *newrdr = NULL;
+
+ if (pcn->action < PF_CHANGE_ADD_HEAD ||
+ pcn->action > PF_CHANGE_REMOVE) {
+ error = EINVAL;
+ break;
+ }
+
+ if (pcn->action != PF_CHANGE_REMOVE) {
+ newrdr = pool_get(&pf_rdr_pl, PR_NOWAIT);
+ if (newrdr == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ bcopy(&pcn->newrdr, newrdr, sizeof(struct pf_rdr));
+#ifndef INET
+ if (newrdr->af == AF_INET) {
+ pool_put(&pf_rdr_pl, newrdr);
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /* INET */
+#ifndef INET6
+ if (newrdr->af == AF_INET6) {
+ pool_put(&pf_rdr_pl, newrdr);
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /* INET6 */
+ if (newrdr->ifname[0]) {
+ newrdr->ifp = ifunit(newrdr->ifname);
+ if (newrdr->ifp == NULL) {
+ pool_put(&pf_rdr_pl, newrdr);
+ error = EINVAL;
+ break;
+ }
+ } else
+ newrdr->ifp = NULL;
+ if (pf_dynaddr_setup(&newrdr->saddr, newrdr->af) ||
+ pf_dynaddr_setup(&newrdr->daddr, newrdr->af) ||
+ pf_dynaddr_setup(&newrdr->raddr, newrdr->af)) {
+ pf_dynaddr_remove(&newrdr->saddr);
+ pf_dynaddr_remove(&newrdr->daddr);
+ pf_dynaddr_remove(&newrdr->raddr);
+ pool_put(&pf_rdr_pl, newrdr);
+ error = EINVAL;
+ break;
+ }
+ }
+
+ s = splsoftnet();
+
+ if (pcn->action == PF_CHANGE_ADD_HEAD)
+ oldrdr = TAILQ_FIRST(pf_rdrs_active);
+ else if (pcn->action == PF_CHANGE_ADD_TAIL)
+ oldrdr = TAILQ_LAST(pf_rdrs_active, pf_rdrqueue);
+ else {
+ oldrdr = TAILQ_FIRST(pf_rdrs_active);
+ while ((oldrdr != NULL) && pf_compare_rdrs(oldrdr,
+ &pcn->oldrdr))
+ oldrdr = TAILQ_NEXT(oldrdr, entries);
+ if (oldrdr == NULL) {
+ error = EINVAL;
+ splx(s);
+ break;
+ }
+ }
+
+ if (pcn->action == PF_CHANGE_REMOVE) {
+ TAILQ_REMOVE(pf_rdrs_active, oldrdr, entries);
+ pf_dynaddr_remove(&oldrdr->saddr);
+ pf_dynaddr_remove(&oldrdr->daddr);
+ pf_dynaddr_remove(&oldrdr->raddr);
+ pool_put(&pf_rdr_pl, oldrdr);
+ } else {
+ if (oldrdr == NULL)
+ TAILQ_INSERT_TAIL(pf_rdrs_active, newrdr,
+ entries);
+ else if (pcn->action == PF_CHANGE_ADD_HEAD ||
+ pcn->action == PF_CHANGE_ADD_BEFORE)
+ TAILQ_INSERT_BEFORE(oldrdr, newrdr, entries);
+ else
+ TAILQ_INSERT_AFTER(pf_rdrs_active, oldrdr,
+ newrdr, entries);
+ }
+
+ ticket_rdrs_active++;
+ splx(s);
+ break;
+ }
+
+ case DIOCCLRSTATES: {
+ struct pf_tree_node *n;
+
+ s = splsoftnet();
+ RB_FOREACH(n, pf_state_tree, &tree_ext_gwy)
+ n->state->expire = 0;
+ pf_purge_expired_states();
+ pf_status.states = 0;
+ splx(s);
+ break;
+ }
+
+ case DIOCKILLSTATES: {
+ struct pf_tree_node *n;
+ struct pf_state *st;
+ struct pfioc_state_kill *psk =
+ (struct pfioc_state_kill *)addr;
+ int killed = 0;
+
+ s = splsoftnet();
+ RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) {
+ st = n->state;
+ if ((!psk->psk_af || st->af == psk->psk_af) &&
+ (!psk->psk_proto || psk->psk_proto == st->proto) &&
+ PF_MATCHA(psk->psk_src.not, &psk->psk_src.addr.addr,
+ &psk->psk_src.mask, &st->lan.addr, st->af) &&
+ PF_MATCHA(psk->psk_dst.not, &psk->psk_dst.addr.addr,
+ &psk->psk_dst.mask, &st->ext.addr, st->af) &&
+ (psk->psk_src.port_op == 0 ||
+ pf_match_port(psk->psk_src.port_op,
+ psk->psk_src.port[0], psk->psk_src.port[1],
+ st->lan.port)) &&
+ (psk->psk_dst.port_op == 0 ||
+ pf_match_port(psk->psk_dst.port_op,
+ psk->psk_dst.port[0], psk->psk_dst.port[1],
+ st->ext.port))) {
+ st->expire = 0;
+ killed++;
+ }
+ }
+ pf_purge_expired_states();
+ splx(s);
+ psk->psk_af = killed;
+ break;
+ }
+
+ case DIOCADDSTATE: {
+ struct pfioc_state *ps = (struct pfioc_state *)addr;
+ struct pf_state *state;
+
+ state = pool_get(&pf_state_pl, PR_NOWAIT);
+ if (state == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ s = splsoftnet();
+ bcopy(&ps->state, state, sizeof(struct pf_state));
+ state->rule.ptr = NULL;
+ state->creation = time.tv_sec;
+ state->expire += state->creation;
+ state->packets = 0;
+ state->bytes = 0;
+ if (pf_insert_state(state)) {
+ pool_put(&pf_state_pl, state);
+ error = ENOMEM;
+ }
+ splx(s);
+ }
+
+ case DIOCGETSTATE: {
+ struct pfioc_state *ps = (struct pfioc_state *)addr;
+ struct pf_tree_node *n;
+ u_int32_t nr;
+ int secs;
+
+ nr = 0;
+ s = splsoftnet();
+ RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) {
+ if (nr >= ps->nr)
+ break;
+ nr++;
+ }
+ if (n == NULL) {
+ error = EBUSY;
+ splx(s);
+ break;
+ }
+ bcopy(n->state, &ps->state, sizeof(struct pf_state));
+ if (n->state->rule.ptr == NULL)
+ ps->state.rule.nr = -1;
+ else
+ ps->state.rule.nr = n->state->rule.ptr->nr;
+ splx(s);
+ secs = time.tv_sec;
+ ps->state.creation = secs - ps->state.creation;
+ if (ps->state.expire <= secs)
+ ps->state.expire = 0;
+ else
+ ps->state.expire -= secs;
+ break;
+ }
+
+ case DIOCGETSTATES: {
+ struct pfioc_states *ps = (struct pfioc_states *)addr;
+ struct pf_tree_node *n;
+ struct pf_state *p, pstore;
+ u_int32_t nr = 0;
+ int space = ps->ps_len;
+
+ if (space == 0) {
+ s = splsoftnet();
+ RB_FOREACH(n, pf_state_tree, &tree_ext_gwy)
+ nr++;
+ splx(s);
+ ps->ps_len = sizeof(struct pf_state) * nr;
+ return (0);
+ }
+
+ s = splsoftnet();
+ p = ps->ps_states;
+ RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) {
+ int secs = time.tv_sec;
+
+ if ((nr + 1) * sizeof(*p) > ps->ps_len)
+ break;
+
+ bcopy(n->state, &pstore, sizeof(pstore));
+ if (n->state->rule.ptr == NULL)
+ pstore.rule.nr = -1;
+ else
+ pstore.rule.nr = n->state->rule.ptr->nr;
+ pstore.creation = secs - pstore.creation;
+ if (pstore.expire <= secs)
+ pstore.expire = 0;
+ else
+ pstore.expire -= secs;
+ error = copyout(&pstore, p, sizeof(*p));
+ if (error) {
+ splx(s);
+ goto fail;
+ }
+ p++;
+ nr++;
+ }
+ ps->ps_len = sizeof(struct pf_state) * nr;
+ splx(s);
+ break;
+ }
+
+ case DIOCSETSTATUSIF: {
+ struct pfioc_if *pi = (struct pfioc_if *)addr;
+ struct ifnet *ifp;
+
+ if ((ifp = ifunit(pi->ifname)) == NULL)
+ error = EINVAL;
+ else
+ status_ifp = ifp;
+ break;
+ }
+
+ case DIOCGETSTATUS: {
+ struct pf_status *s = (struct pf_status *)addr;
+ bcopy(&pf_status, s, sizeof(struct pf_status));
+ break;
+ }
+
+ case DIOCCLRSTATUS: {
+ u_int32_t running = pf_status.running;
+ u_int32_t states = pf_status.states;
+ u_int32_t since = pf_status.since;
+ u_int32_t debug = pf_status.debug;
+
+ bzero(&pf_status, sizeof(struct pf_status));
+ pf_status.running = running;
+ pf_status.states = states;
+ pf_status.since = since;
+ pf_status.debug = debug;
+ break;
+ }
+
+ case DIOCNATLOOK: {
+ struct pfioc_natlook *pnl = (struct pfioc_natlook *)addr;
+ struct pf_state *st;
+ struct pf_tree_node key;
+ int direction = pnl->direction;
+
+ key.af = pnl->af;
+ key.proto = pnl->proto;
+
+ /*
+ * userland gives us source and dest of connetion, reverse
+ * the lookup so we ask for what happens with the return
+ * traffic, enabling us to find it in the state tree.
+ */
+ PF_ACPY(&key.addr[1], &pnl->saddr, pnl->af);
+ key.port[1] = pnl->sport;
+ PF_ACPY(&key.addr[0], &pnl->daddr, pnl->af);
+ key.port[0] = pnl->dport;
+
+ if (!pnl->proto ||
+ PF_AZERO(&pnl->saddr, pnl->af) ||
+ PF_AZERO(&pnl->daddr, pnl->af) ||
+ !pnl->dport || !pnl->sport)
+ error = EINVAL;
+ else {
+ s = splsoftnet();
+ if (direction == PF_IN)
+ st = pf_find_state(&tree_ext_gwy, &key);
+ else
+ st = pf_find_state(&tree_lan_ext, &key);
+ if (st != NULL) {
+ if (direction == PF_IN) {
+ PF_ACPY(&pnl->rsaddr, &st->lan.addr,
+ st->af);
+ pnl->rsport = st->lan.port;
+ PF_ACPY(&pnl->rdaddr, &pnl->daddr,
+ pnl->af);
+ pnl->rdport = pnl->dport;
+ } else {
+ PF_ACPY(&pnl->rdaddr, &st->gwy.addr,
+ st->af);
+ pnl->rdport = st->gwy.port;
+ PF_ACPY(&pnl->rsaddr, &pnl->saddr,
+ pnl->af);
+ pnl->rsport = pnl->sport;
+ }
+ } else
+ error = ENOENT;
+ splx(s);
+ }
+ break;
+ }
+
+ case DIOCSETTIMEOUT: {
+ struct pfioc_tm *pt = (struct pfioc_tm *)addr;
+ int old;
+
+ if (pt->timeout < 0 || pt->timeout >= PFTM_MAX ||
+ pt->seconds < 0) {
+ error = EINVAL;
+ goto fail;
+ }
+ old = *pftm_timeouts[pt->timeout];
+ *pftm_timeouts[pt->timeout] = pt->seconds;
+ pt->seconds = old;
+ break;
+ }
+
+ case DIOCGETTIMEOUT: {
+ struct pfioc_tm *pt = (struct pfioc_tm *)addr;
+
+ if (pt->timeout < 0 || pt->timeout >= PFTM_MAX) {
+ error = EINVAL;
+ goto fail;
+ }
+ pt->seconds = *pftm_timeouts[pt->timeout];
+ break;
+ }
+
+ case DIOCGETLIMIT: {
+ struct pfioc_limit *pl = (struct pfioc_limit *)addr;
+
+ if (pl->index < 0 || pl->index >= PF_LIMIT_MAX) {
+ error = EINVAL;
+ goto fail;
+ }
+ pl->limit = pf_pool_limits[pl->index].limit;
+ break;
+ }
+
+ case DIOCSETLIMIT: {
+ struct pfioc_limit *pl = (struct pfioc_limit *)addr;
+ int old_limit;
+
+ if (pl->index < 0 || pl->index >= PF_LIMIT_MAX) {
+ error = EINVAL;
+ goto fail;
+ }
+ if (pool_sethardlimit(pf_pool_limits[pl->index].pp,
+ pl->limit, NULL, 0) != 0) {
+ error = EBUSY;
+ goto fail;
+ }
+ old_limit = pf_pool_limits[pl->index].limit;
+ pf_pool_limits[pl->index].limit = pl->limit;
+ pl->limit = old_limit;
+ break;
+ }
+
+ case DIOCSETDEBUG: {
+ u_int32_t *level = (u_int32_t *)addr;
+ pf_status.debug = *level;
+ break;
+ }
+
+ case DIOCCLRRULECTRS: {
+ struct pf_rule *rule;
+
+ s = splsoftnet();
+ TAILQ_FOREACH(rule, pf_rules_active, entries)
+ rule->evaluations = rule->packets =
+ rule->bytes = 0;
+ splx(s);
+ break;
+ }
+
+ default:
+ error = ENODEV;
+ break;
+ }
+fail:
+
+ return (error);
+}