freebsd-dev/sys/contrib/pf/net/pf_ioctl.c
Ed Schouten 55b043392b Revert my previous two changes.
Even though the code seems to be FreeBSD kernel code, it isn't compiled
on FreeBSD. I could have known this, because I was a little amazed that
I couldn't find a prototype of pfopen()/pfclose() somewhere else,
because it isn't marked as static.

Apart from that, removing these functions wouldn't have been harmful
anyway, because there are some other strange things about them (the
implementation isn't consistent with the prototype at the top). Still,
it's better to leave it, because it makes merging code back to older
branches a little harder.

Requested by:	mlaier
2009-01-25 16:52:41 +00:00

3901 lines
92 KiB
C

/* $OpenBSD: pf_ioctl.c,v 1.175 2007/02/26 22:47:43 deraadt Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
* Copyright (c) 2002,2003 Henning Brauer
* 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.
*
*/
#ifdef __FreeBSD__
#include "opt_inet.h"
#include "opt_inet6.h"
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#endif
#ifdef __FreeBSD__
#include "opt_bpf.h"
#include "opt_pf.h"
#ifdef DEV_BPF
#define NBPFILTER DEV_BPF
#else
#define NBPFILTER 0
#endif
#ifdef DEV_PFLOG
#define NPFLOG DEV_PFLOG
#else
#define NPFLOG 0
#endif
#ifdef DEV_PFSYNC
#define NPFSYNC DEV_PFSYNC
#else
#define NPFSYNC 0
#endif
#else
#include "bpfilter.h"
#include "pflog.h"
#include "pfsync.h"
#endif
#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/time.h>
#include <sys/malloc.h>
#ifdef __FreeBSD__
#include <sys/module.h>
#include <sys/conf.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/vimage.h>
#else
#include <sys/timeout.h>
#include <sys/pool.h>
#endif
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/kthread.h>
#ifndef __FreeBSD__
#include <sys/rwlock.h>
#include <uvm/uvm_extern.h>
#endif
#include <net/if.h>
#include <net/if_types.h>
#include <net/route.h>
#ifdef __FreeBSD__
#include <net/vnet.h>
#endif
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#ifdef __FreeBSD__
#include <sys/md5.h>
#else
#include <dev/rndvar.h>
#include <crypto/md5.h>
#endif
#include <net/pfvar.h>
#if NPFSYNC > 0
#include <net/if_pfsync.h>
#endif /* NPFSYNC > 0 */
#include <net/if_pflog.h>
#ifdef INET6
#include <netinet/ip6.h>
#include <netinet/in_pcb.h>
#endif /* INET6 */
#ifdef ALTQ
#include <altq/altq.h>
#endif
#ifdef __FreeBSD__
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <net/pfil.h>
#endif /* __FreeBSD__ */
#ifdef __FreeBSD__
void init_zone_var(void);
void cleanup_pf_zone(void);
int pfattach(void);
#else
void pfattach(int);
void pf_thread_create(void *);
int pfopen(dev_t, int, int, struct proc *);
int pfclose(dev_t, int, int, struct proc *);
#endif
struct pf_pool *pf_get_pool(char *, u_int32_t, u_int8_t, u_int32_t,
u_int8_t, u_int8_t, u_int8_t);
void pf_mv_pool(struct pf_palist *, struct pf_palist *);
void pf_empty_pool(struct pf_palist *);
#ifdef __FreeBSD__
int pfioctl(struct cdev *, u_long, caddr_t, int, struct thread *);
#else
int pfioctl(struct cdev *, u_long, caddr_t, int, struct proc *);
#endif
#ifdef ALTQ
int pf_begin_altq(u_int32_t *);
int pf_rollback_altq(u_int32_t);
int pf_commit_altq(u_int32_t);
int pf_enable_altq(struct pf_altq *);
int pf_disable_altq(struct pf_altq *);
#endif /* ALTQ */
int pf_begin_rules(u_int32_t *, int, const char *);
int pf_rollback_rules(u_int32_t, int, char *);
int pf_setup_pfsync_matching(struct pf_ruleset *);
void pf_hash_rule(MD5_CTX *, struct pf_rule *);
void pf_hash_rule_addr(MD5_CTX *, struct pf_rule_addr *);
int pf_commit_rules(u_int32_t, int, char *);
struct pf_rule pf_default_rule;
#ifdef __FreeBSD__
struct sx pf_consistency_lock;
SX_SYSINIT(pf_consistency_lock, &pf_consistency_lock, "pf_statetbl_lock");
#else
struct rwlock pf_consistency_lock = RWLOCK_INITIALIZER;
#endif
#ifdef ALTQ
static int pf_altq_running;
#endif
#define TAGID_MAX 50000
TAILQ_HEAD(pf_tags, pf_tagname) pf_tags = TAILQ_HEAD_INITIALIZER(pf_tags),
pf_qids = TAILQ_HEAD_INITIALIZER(pf_qids);
#if (PF_QNAME_SIZE != PF_TAG_NAME_SIZE)
#error PF_QNAME_SIZE must be equal to PF_TAG_NAME_SIZE
#endif
u_int16_t tagname2tag(struct pf_tags *, char *);
void tag2tagname(struct pf_tags *, u_int16_t, char *);
void tag_unref(struct pf_tags *, u_int16_t);
int pf_rtlabel_add(struct pf_addr_wrap *);
void pf_rtlabel_remove(struct pf_addr_wrap *);
void pf_rtlabel_copyout(struct pf_addr_wrap *);
#define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x
#ifdef __FreeBSD__
static struct cdev *pf_dev;
/*
* XXX - These are new and need to be checked when moveing to a new version
*/
static void pf_clear_states(void);
static int pf_clear_tables(void);
static void pf_clear_srcnodes(void);
/*
* XXX - These are new and need to be checked when moveing to a new version
*/
/*
* Wrapper functions for pfil(9) hooks
*/
static int pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp,
int dir, struct inpcb *inp);
static int pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp,
int dir, struct inpcb *inp);
#ifdef INET6
static int pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp,
int dir, struct inpcb *inp);
static int pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp,
int dir, struct inpcb *inp);
#endif
static int hook_pf(void);
static int dehook_pf(void);
static int shutdown_pf(void);
static int pf_load(void);
static int pf_unload(void);
static struct cdevsw pf_cdevsw = {
.d_ioctl = pfioctl,
.d_name = PF_NAME,
.d_version = D_VERSION,
};
static volatile int pf_pfil_hooked = 0;
int pf_end_threads = 0;
struct mtx pf_task_mtx;
pflog_packet_t *pflog_packet_ptr = NULL;
int debug_pfugidhack = 0;
SYSCTL_INT(_debug, OID_AUTO, pfugidhack, CTLFLAG_RW, &debug_pfugidhack, 0,
"Enable/disable pf user/group rules mpsafe hack");
void
init_pf_mutex(void)
{
mtx_init(&pf_task_mtx, "pf task mtx", NULL, MTX_DEF);
}
void
destroy_pf_mutex(void)
{
mtx_destroy(&pf_task_mtx);
}
void
init_zone_var(void)
{
pf_src_tree_pl = pf_rule_pl = NULL;
pf_state_pl = pf_altq_pl = pf_pooladdr_pl = NULL;
pf_frent_pl = pf_frag_pl = pf_cache_pl = pf_cent_pl = NULL;
pf_state_scrub_pl = NULL;
pfr_ktable_pl = pfr_kentry_pl = NULL;
}
void
cleanup_pf_zone(void)
{
UMA_DESTROY(pf_src_tree_pl);
UMA_DESTROY(pf_rule_pl);
UMA_DESTROY(pf_state_pl);
UMA_DESTROY(pf_altq_pl);
UMA_DESTROY(pf_pooladdr_pl);
UMA_DESTROY(pf_frent_pl);
UMA_DESTROY(pf_frag_pl);
UMA_DESTROY(pf_cache_pl);
UMA_DESTROY(pf_cent_pl);
UMA_DESTROY(pfr_ktable_pl);
UMA_DESTROY(pfr_kentry_pl2);
UMA_DESTROY(pfr_kentry_pl);
UMA_DESTROY(pf_state_scrub_pl);
UMA_DESTROY(pfi_addr_pl);
}
int
pfattach(void)
{
u_int32_t *my_timeout = pf_default_rule.timeout;
int error = 1;
do {
UMA_CREATE(pf_src_tree_pl,struct pf_src_node, "pfsrctrpl");
UMA_CREATE(pf_rule_pl, struct pf_rule, "pfrulepl");
UMA_CREATE(pf_state_pl, struct pf_state, "pfstatepl");
UMA_CREATE(pf_altq_pl, struct pf_altq, "pfaltqpl");
UMA_CREATE(pf_pooladdr_pl, struct pf_pooladdr, "pfpooladdrpl");
UMA_CREATE(pfr_ktable_pl, struct pfr_ktable, "pfrktable");
UMA_CREATE(pfr_kentry_pl, struct pfr_kentry, "pfrkentry");
UMA_CREATE(pfr_kentry_pl2, struct pfr_kentry, "pfrkentry2");
UMA_CREATE(pf_frent_pl, struct pf_frent, "pffrent");
UMA_CREATE(pf_frag_pl, struct pf_fragment, "pffrag");
UMA_CREATE(pf_cache_pl, struct pf_fragment, "pffrcache");
UMA_CREATE(pf_cent_pl, struct pf_frcache, "pffrcent");
UMA_CREATE(pf_state_scrub_pl, struct pf_state_scrub,
"pfstatescrub");
UMA_CREATE(pfi_addr_pl, struct pfi_dynaddr, "pfiaddrpl");
error = 0;
} while(0);
if (error) {
cleanup_pf_zone();
return (error);
}
pfr_initialize();
pfi_initialize();
if ( (error = pf_osfp_initialize()) ) {
cleanup_pf_zone();
pf_osfp_cleanup();
return (error);
}
pf_pool_limits[PF_LIMIT_STATES].pp = pf_state_pl;
pf_pool_limits[PF_LIMIT_STATES].limit = PFSTATE_HIWAT;
pf_pool_limits[PF_LIMIT_SRC_NODES].pp = pf_src_tree_pl;
pf_pool_limits[PF_LIMIT_SRC_NODES].limit = PFSNODE_HIWAT;
pf_pool_limits[PF_LIMIT_FRAGS].pp = pf_frent_pl;
pf_pool_limits[PF_LIMIT_FRAGS].limit = PFFRAG_FRENT_HIWAT;
pf_pool_limits[PF_LIMIT_TABLES].pp = pfr_ktable_pl;
pf_pool_limits[PF_LIMIT_TABLES].limit = PFR_KTABLE_HIWAT;
pf_pool_limits[PF_LIMIT_TABLE_ENTRIES].pp = pfr_kentry_pl;
pf_pool_limits[PF_LIMIT_TABLE_ENTRIES].limit = PFR_KENTRY_HIWAT;
uma_zone_set_max(pf_pool_limits[PF_LIMIT_STATES].pp,
pf_pool_limits[PF_LIMIT_STATES].limit);
RB_INIT(&tree_src_tracking);
RB_INIT(&pf_anchors);
pf_init_ruleset(&pf_main_ruleset);
TAILQ_INIT(&pf_altqs[0]);
TAILQ_INIT(&pf_altqs[1]);
TAILQ_INIT(&pf_pabuf);
pf_altqs_active = &pf_altqs[0];
pf_altqs_inactive = &pf_altqs[1];
TAILQ_INIT(&state_list);
/* default rule should never be garbage collected */
pf_default_rule.entries.tqe_prev = &pf_default_rule.entries.tqe_next;
pf_default_rule.action = PF_PASS;
pf_default_rule.nr = -1;
pf_default_rule.rtableid = -1;
/* initialize default timeouts */
my_timeout[PFTM_TCP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL;
my_timeout[PFTM_TCP_OPENING] = PFTM_TCP_OPENING_VAL;
my_timeout[PFTM_TCP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL;
my_timeout[PFTM_TCP_CLOSING] = PFTM_TCP_CLOSING_VAL;
my_timeout[PFTM_TCP_FIN_WAIT] = PFTM_TCP_FIN_WAIT_VAL;
my_timeout[PFTM_TCP_CLOSED] = PFTM_TCP_CLOSED_VAL;
my_timeout[PFTM_UDP_FIRST_PACKET] = PFTM_UDP_FIRST_PACKET_VAL;
my_timeout[PFTM_UDP_SINGLE] = PFTM_UDP_SINGLE_VAL;
my_timeout[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL;
my_timeout[PFTM_ICMP_FIRST_PACKET] = PFTM_ICMP_FIRST_PACKET_VAL;
my_timeout[PFTM_ICMP_ERROR_REPLY] = PFTM_ICMP_ERROR_REPLY_VAL;
my_timeout[PFTM_OTHER_FIRST_PACKET] = PFTM_OTHER_FIRST_PACKET_VAL;
my_timeout[PFTM_OTHER_SINGLE] = PFTM_OTHER_SINGLE_VAL;
my_timeout[PFTM_OTHER_MULTIPLE] = PFTM_OTHER_MULTIPLE_VAL;
my_timeout[PFTM_FRAG] = PFTM_FRAG_VAL;
my_timeout[PFTM_INTERVAL] = PFTM_INTERVAL_VAL;
my_timeout[PFTM_SRC_NODE] = PFTM_SRC_NODE_VAL;
my_timeout[PFTM_TS_DIFF] = PFTM_TS_DIFF_VAL;
my_timeout[PFTM_ADAPTIVE_START] = PFSTATE_ADAPT_START;
my_timeout[PFTM_ADAPTIVE_END] = PFSTATE_ADAPT_END;
pf_normalize_init();
bzero(&pf_status, sizeof(pf_status));
pf_status.debug = PF_DEBUG_URGENT;
pf_pfil_hooked = 0;
/* XXX do our best to avoid a conflict */
pf_status.hostid = arc4random();
if (kproc_create(pf_purge_thread, NULL, NULL, 0, 0, "pfpurge"))
return (ENXIO);
return (error);
}
#else /* !__FreeBSD__ */
void
pfattach(int num)
{
u_int32_t *timeout = pf_default_rule.timeout;
pool_init(&pf_rule_pl, sizeof(struct pf_rule), 0, 0, 0, "pfrulepl",
&pool_allocator_nointr);
pool_init(&pf_src_tree_pl, sizeof(struct pf_src_node), 0, 0, 0,
"pfsrctrpl", NULL);
pool_init(&pf_state_pl, sizeof(struct pf_state), 0, 0, 0, "pfstatepl",
NULL);
pool_init(&pf_altq_pl, sizeof(struct pf_altq), 0, 0, 0, "pfaltqpl",
&pool_allocator_nointr);
pool_init(&pf_pooladdr_pl, sizeof(struct pf_pooladdr), 0, 0, 0,
"pfpooladdrpl", &pool_allocator_nointr);
pfr_initialize();
pfi_initialize();
pf_osfp_initialize();
pool_sethardlimit(pf_pool_limits[PF_LIMIT_STATES].pp,
pf_pool_limits[PF_LIMIT_STATES].limit, NULL, 0);
if (ctob(physmem) <= 100*1024*1024)
pf_pool_limits[PF_LIMIT_TABLE_ENTRIES].limit =
PFR_KENTRY_HIWAT_SMALL;
RB_INIT(&tree_src_tracking);
RB_INIT(&pf_anchors);
pf_init_ruleset(&pf_main_ruleset);
TAILQ_INIT(&pf_altqs[0]);
TAILQ_INIT(&pf_altqs[1]);
TAILQ_INIT(&pf_pabuf);
pf_altqs_active = &pf_altqs[0];
pf_altqs_inactive = &pf_altqs[1];
TAILQ_INIT(&state_list);
/* default rule should never be garbage collected */
pf_default_rule.entries.tqe_prev = &pf_default_rule.entries.tqe_next;
pf_default_rule.action = PF_PASS;
pf_default_rule.nr = -1;
pf_default_rule.rtableid = -1;
/* initialize default timeouts */
timeout[PFTM_TCP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL;
timeout[PFTM_TCP_OPENING] = PFTM_TCP_OPENING_VAL;
timeout[PFTM_TCP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL;
timeout[PFTM_TCP_CLOSING] = PFTM_TCP_CLOSING_VAL;
timeout[PFTM_TCP_FIN_WAIT] = PFTM_TCP_FIN_WAIT_VAL;
timeout[PFTM_TCP_CLOSED] = PFTM_TCP_CLOSED_VAL;
timeout[PFTM_UDP_FIRST_PACKET] = PFTM_UDP_FIRST_PACKET_VAL;
timeout[PFTM_UDP_SINGLE] = PFTM_UDP_SINGLE_VAL;
timeout[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL;
timeout[PFTM_ICMP_FIRST_PACKET] = PFTM_ICMP_FIRST_PACKET_VAL;
timeout[PFTM_ICMP_ERROR_REPLY] = PFTM_ICMP_ERROR_REPLY_VAL;
timeout[PFTM_OTHER_FIRST_PACKET] = PFTM_OTHER_FIRST_PACKET_VAL;
timeout[PFTM_OTHER_SINGLE] = PFTM_OTHER_SINGLE_VAL;
timeout[PFTM_OTHER_MULTIPLE] = PFTM_OTHER_MULTIPLE_VAL;
timeout[PFTM_FRAG] = PFTM_FRAG_VAL;
timeout[PFTM_INTERVAL] = PFTM_INTERVAL_VAL;
timeout[PFTM_SRC_NODE] = PFTM_SRC_NODE_VAL;
timeout[PFTM_TS_DIFF] = PFTM_TS_DIFF_VAL;
timeout[PFTM_ADAPTIVE_START] = PFSTATE_ADAPT_START;
timeout[PFTM_ADAPTIVE_END] = PFSTATE_ADAPT_END;
pf_normalize_init();
bzero(&pf_status, sizeof(pf_status));
pf_status.debug = PF_DEBUG_URGENT;
/* XXX do our best to avoid a conflict */
pf_status.hostid = arc4random();
/* require process context to purge states, so perform in a thread */
kproc_create_deferred(pf_thread_create, NULL);
}
void
pf_thread_create(void *v)
{
if (kproc_create(pf_purge_thread, NULL, NULL, "pfpurge"))
panic("pfpurge thread");
}
int
pfopen(struct cdev *dev, int flags, int fmt, struct proc *p)
{
if (dev2unit(dev) >= 1)
return (ENXIO);
return (0);
}
int
pfclose(struct cdev *dev, int flags, int fmt, struct proc *p)
{
if (dev2unit(dev) >= 1)
return (ENXIO);
return (0);
}
#endif /* __FreeBSD__ */
struct pf_pool *
pf_get_pool(char *anchor, u_int32_t ticket, u_int8_t rule_action,
u_int32_t rule_number, u_int8_t r_last, u_int8_t active,
u_int8_t check_ticket)
{
struct pf_ruleset *ruleset;
struct pf_rule *rule;
int rs_num;
ruleset = pf_find_ruleset(anchor);
if (ruleset == NULL)
return (NULL);
rs_num = pf_get_ruleset_number(rule_action);
if (rs_num >= PF_RULESET_MAX)
return (NULL);
if (active) {
if (check_ticket && ticket !=
ruleset->rules[rs_num].active.ticket)
return (NULL);
if (r_last)
rule = TAILQ_LAST(ruleset->rules[rs_num].active.ptr,
pf_rulequeue);
else
rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr);
} else {
if (check_ticket && ticket !=
ruleset->rules[rs_num].inactive.ticket)
return (NULL);
if (r_last)
rule = TAILQ_LAST(ruleset->rules[rs_num].inactive.ptr,
pf_rulequeue);
else
rule = TAILQ_FIRST(ruleset->rules[rs_num].inactive.ptr);
}
if (!r_last) {
while ((rule != NULL) && (rule->nr != rule_number))
rule = TAILQ_NEXT(rule, entries);
}
if (rule == NULL)
return (NULL);
return (&rule->rpool);
}
void
pf_mv_pool(struct pf_palist *poola, struct pf_palist *poolb)
{
struct pf_pooladdr *mv_pool_pa;
while ((mv_pool_pa = TAILQ_FIRST(poola)) != NULL) {
TAILQ_REMOVE(poola, mv_pool_pa, entries);
TAILQ_INSERT_TAIL(poolb, mv_pool_pa, entries);
}
}
void
pf_empty_pool(struct pf_palist *poola)
{
struct pf_pooladdr *empty_pool_pa;
while ((empty_pool_pa = TAILQ_FIRST(poola)) != NULL) {
pfi_dynaddr_remove(&empty_pool_pa->addr);
pf_tbladdr_remove(&empty_pool_pa->addr);
pfi_kif_unref(empty_pool_pa->kif, PFI_KIF_REF_RULE);
TAILQ_REMOVE(poola, empty_pool_pa, entries);
pool_put(&pf_pooladdr_pl, empty_pool_pa);
}
}
void
pf_rm_rule(struct pf_rulequeue *rulequeue, struct pf_rule *rule)
{
if (rulequeue != NULL) {
if (rule->states <= 0) {
/*
* XXX - we need to remove the table *before* detaching
* the rule to make sure the table code does not delete
* the anchor under our feet.
*/
pf_tbladdr_remove(&rule->src.addr);
pf_tbladdr_remove(&rule->dst.addr);
if (rule->overload_tbl)
pfr_detach_table(rule->overload_tbl);
}
TAILQ_REMOVE(rulequeue, rule, entries);
rule->entries.tqe_prev = NULL;
rule->nr = -1;
}
if (rule->states > 0 || rule->src_nodes > 0 ||
rule->entries.tqe_prev != NULL)
return;
pf_tag_unref(rule->tag);
pf_tag_unref(rule->match_tag);
#ifdef ALTQ
if (rule->pqid != rule->qid)
pf_qid_unref(rule->pqid);
pf_qid_unref(rule->qid);
#endif
pf_rtlabel_remove(&rule->src.addr);
pf_rtlabel_remove(&rule->dst.addr);
pfi_dynaddr_remove(&rule->src.addr);
pfi_dynaddr_remove(&rule->dst.addr);
if (rulequeue == NULL) {
pf_tbladdr_remove(&rule->src.addr);
pf_tbladdr_remove(&rule->dst.addr);
if (rule->overload_tbl)
pfr_detach_table(rule->overload_tbl);
}
pfi_kif_unref(rule->kif, PFI_KIF_REF_RULE);
pf_anchor_remove(rule);
pf_empty_pool(&rule->rpool.list);
pool_put(&pf_rule_pl, rule);
}
u_int16_t
tagname2tag(struct pf_tags *head, char *tagname)
{
struct pf_tagname *tag, *p = NULL;
u_int16_t new_tagid = 1;
TAILQ_FOREACH(tag, head, entries)
if (strcmp(tagname, tag->name) == 0) {
tag->ref++;
return (tag->tag);
}
/*
* to avoid fragmentation, we do a linear search from the beginning
* and take the first free slot we find. if there is none or the list
* is empty, append a new entry at the end.
*/
/* new entry */
if (!TAILQ_EMPTY(head))
for (p = TAILQ_FIRST(head); p != NULL &&
p->tag == new_tagid; p = TAILQ_NEXT(p, entries))
new_tagid = p->tag + 1;
if (new_tagid > TAGID_MAX)
return (0);
/* allocate and fill new struct pf_tagname */
tag = (struct pf_tagname *)malloc(sizeof(struct pf_tagname),
M_TEMP, M_NOWAIT);
if (tag == NULL)
return (0);
bzero(tag, sizeof(struct pf_tagname));
strlcpy(tag->name, tagname, sizeof(tag->name));
tag->tag = new_tagid;
tag->ref++;
if (p != NULL) /* insert new entry before p */
TAILQ_INSERT_BEFORE(p, tag, entries);
else /* either list empty or no free slot in between */
TAILQ_INSERT_TAIL(head, tag, entries);
return (tag->tag);
}
void
tag2tagname(struct pf_tags *head, u_int16_t tagid, char *p)
{
struct pf_tagname *tag;
TAILQ_FOREACH(tag, head, entries)
if (tag->tag == tagid) {
strlcpy(p, tag->name, PF_TAG_NAME_SIZE);
return;
}
}
void
tag_unref(struct pf_tags *head, u_int16_t tag)
{
struct pf_tagname *p, *next;
if (tag == 0)
return;
for (p = TAILQ_FIRST(head); p != NULL; p = next) {
next = TAILQ_NEXT(p, entries);
if (tag == p->tag) {
if (--p->ref == 0) {
TAILQ_REMOVE(head, p, entries);
free(p, M_TEMP);
}
break;
}
}
}
u_int16_t
pf_tagname2tag(char *tagname)
{
return (tagname2tag(&pf_tags, tagname));
}
void
pf_tag2tagname(u_int16_t tagid, char *p)
{
tag2tagname(&pf_tags, tagid, p);
}
void
pf_tag_ref(u_int16_t tag)
{
struct pf_tagname *t;
TAILQ_FOREACH(t, &pf_tags, entries)
if (t->tag == tag)
break;
if (t != NULL)
t->ref++;
}
void
pf_tag_unref(u_int16_t tag)
{
tag_unref(&pf_tags, tag);
}
int
pf_rtlabel_add(struct pf_addr_wrap *a)
{
#ifdef __FreeBSD__
/* XXX_IMPORT: later */
return (0);
#else
if (a->type == PF_ADDR_RTLABEL &&
(a->v.rtlabel = rtlabel_name2id(a->v.rtlabelname)) == 0)
return (-1);
return (0);
#endif
}
void
pf_rtlabel_remove(struct pf_addr_wrap *a)
{
#ifdef __FreeBSD__
/* XXX_IMPORT: later */
#else
if (a->type == PF_ADDR_RTLABEL)
rtlabel_unref(a->v.rtlabel);
#endif
}
void
pf_rtlabel_copyout(struct pf_addr_wrap *a)
{
#ifdef __FreeBSD__
/* XXX_IMPORT: later */
if (a->type == PF_ADDR_RTLABEL && a->v.rtlabel)
strlcpy(a->v.rtlabelname, "?", sizeof(a->v.rtlabelname));
#else
const char *name;
if (a->type == PF_ADDR_RTLABEL && a->v.rtlabel) {
if ((name = rtlabel_id2name(a->v.rtlabel)) == NULL)
strlcpy(a->v.rtlabelname, "?",
sizeof(a->v.rtlabelname));
else
strlcpy(a->v.rtlabelname, name,
sizeof(a->v.rtlabelname));
}
#endif
}
#ifdef ALTQ
u_int32_t
pf_qname2qid(char *qname)
{
return ((u_int32_t)tagname2tag(&pf_qids, qname));
}
void
pf_qid2qname(u_int32_t qid, char *p)
{
tag2tagname(&pf_qids, (u_int16_t)qid, p);
}
void
pf_qid_unref(u_int32_t qid)
{
tag_unref(&pf_qids, (u_int16_t)qid);
}
int
pf_begin_altq(u_int32_t *ticket)
{
struct pf_altq *altq;
int error = 0;
/* Purge the old altq list */
while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) {
TAILQ_REMOVE(pf_altqs_inactive, altq, entries);
#ifdef __FreeBSD__
if (altq->qname[0] == 0 &&
(altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) {
#else
if (altq->qname[0] == 0) {
#endif
/* detach and destroy the discipline */
error = altq_remove(altq);
} else
pf_qid_unref(altq->qid);
pool_put(&pf_altq_pl, altq);
}
if (error)
return (error);
*ticket = ++ticket_altqs_inactive;
altqs_inactive_open = 1;
return (0);
}
int
pf_rollback_altq(u_int32_t ticket)
{
struct pf_altq *altq;
int error = 0;
if (!altqs_inactive_open || ticket != ticket_altqs_inactive)
return (0);
/* Purge the old altq list */
while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) {
TAILQ_REMOVE(pf_altqs_inactive, altq, entries);
#ifdef __FreeBSD__
if (altq->qname[0] == 0 &&
(altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) {
#else
if (altq->qname[0] == 0) {
#endif
/* detach and destroy the discipline */
error = altq_remove(altq);
} else
pf_qid_unref(altq->qid);
pool_put(&pf_altq_pl, altq);
}
altqs_inactive_open = 0;
return (error);
}
int
pf_commit_altq(u_int32_t ticket)
{
struct pf_altqqueue *old_altqs;
struct pf_altq *altq;
int s, err, error = 0;
if (!altqs_inactive_open || ticket != ticket_altqs_inactive)
return (EBUSY);
/* swap altqs, keep the old. */
s = splsoftnet();
old_altqs = pf_altqs_active;
pf_altqs_active = pf_altqs_inactive;
pf_altqs_inactive = old_altqs;
ticket_altqs_active = ticket_altqs_inactive;
/* Attach new disciplines */
TAILQ_FOREACH(altq, pf_altqs_active, entries) {
#ifdef __FreeBSD__
if (altq->qname[0] == 0 &&
(altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) {
#else
if (altq->qname[0] == 0) {
#endif
/* attach the discipline */
error = altq_pfattach(altq);
if (error == 0 && pf_altq_running)
error = pf_enable_altq(altq);
if (error != 0) {
splx(s);
return (error);
}
}
}
/* Purge the old altq list */
while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) {
TAILQ_REMOVE(pf_altqs_inactive, altq, entries);
#ifdef __FreeBSD__
if (altq->qname[0] == 0 &&
(altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) {
#else
if (altq->qname[0] == 0) {
#endif
/* detach and destroy the discipline */
if (pf_altq_running)
error = pf_disable_altq(altq);
err = altq_pfdetach(altq);
if (err != 0 && error == 0)
error = err;
err = altq_remove(altq);
if (err != 0 && error == 0)
error = err;
} else
pf_qid_unref(altq->qid);
pool_put(&pf_altq_pl, altq);
}
splx(s);
altqs_inactive_open = 0;
return (error);
}
int
pf_enable_altq(struct pf_altq *altq)
{
struct ifnet *ifp;
struct tb_profile tb;
int s, error = 0;
if ((ifp = ifunit(altq->ifname)) == NULL)
return (EINVAL);
if (ifp->if_snd.altq_type != ALTQT_NONE)
error = altq_enable(&ifp->if_snd);
/* set tokenbucket regulator */
if (error == 0 && ifp != NULL && ALTQ_IS_ENABLED(&ifp->if_snd)) {
tb.rate = altq->ifbandwidth;
tb.depth = altq->tbrsize;
s = splnet();
#ifdef __FreeBSD__
PF_UNLOCK();
#endif
error = tbr_set(&ifp->if_snd, &tb);
#ifdef __FreeBSD__
PF_LOCK();
#endif
splx(s);
}
return (error);
}
int
pf_disable_altq(struct pf_altq *altq)
{
struct ifnet *ifp;
struct tb_profile tb;
int s, error;
if ((ifp = ifunit(altq->ifname)) == NULL)
return (EINVAL);
/*
* when the discipline is no longer referenced, it was overridden
* by a new one. if so, just return.
*/
if (altq->altq_disc != ifp->if_snd.altq_disc)
return (0);
error = altq_disable(&ifp->if_snd);
if (error == 0) {
/* clear tokenbucket regulator */
tb.rate = 0;
s = splnet();
#ifdef __FreeBSD__
PF_UNLOCK();
#endif
error = tbr_set(&ifp->if_snd, &tb);
#ifdef __FreeBSD__
PF_LOCK();
#endif
splx(s);
}
return (error);
}
#ifdef __FreeBSD__
void
pf_altq_ifnet_event(struct ifnet *ifp, int remove)
{
struct ifnet *ifp1;
struct pf_altq *a1, *a2, *a3;
u_int32_t ticket;
int error = 0;
/* Interrupt userland queue modifications */
if (altqs_inactive_open)
pf_rollback_altq(ticket_altqs_inactive);
/* Start new altq ruleset */
if (pf_begin_altq(&ticket))
return;
/* Copy the current active set */
TAILQ_FOREACH(a1, pf_altqs_active, entries) {
a2 = pool_get(&pf_altq_pl, PR_NOWAIT);
if (a2 == NULL) {
error = ENOMEM;
break;
}
bcopy(a1, a2, sizeof(struct pf_altq));
if (a2->qname[0] != 0) {
if ((a2->qid = pf_qname2qid(a2->qname)) == 0) {
error = EBUSY;
pool_put(&pf_altq_pl, a2);
break;
}
a2->altq_disc = NULL;
TAILQ_FOREACH(a3, pf_altqs_inactive, entries) {
if (strncmp(a3->ifname, a2->ifname,
IFNAMSIZ) == 0 && a3->qname[0] == 0) {
a2->altq_disc = a3->altq_disc;
break;
}
}
}
/* Deactivate the interface in question */
a2->local_flags &= ~PFALTQ_FLAG_IF_REMOVED;
if ((ifp1 = ifunit(a2->ifname)) == NULL ||
(remove && ifp1 == ifp)) {
a2->local_flags |= PFALTQ_FLAG_IF_REMOVED;
} else {
PF_UNLOCK();
error = altq_add(a2);
PF_LOCK();
if (ticket != ticket_altqs_inactive)
error = EBUSY;
if (error) {
pool_put(&pf_altq_pl, a2);
break;
}
}
TAILQ_INSERT_TAIL(pf_altqs_inactive, a2, entries);
}
if (error != 0)
pf_rollback_altq(ticket);
else
pf_commit_altq(ticket);
}
#endif
#endif /* ALTQ */
int
pf_begin_rules(u_int32_t *ticket, int rs_num, const char *anchor)
{
struct pf_ruleset *rs;
struct pf_rule *rule;
if (rs_num < 0 || rs_num >= PF_RULESET_MAX)
return (EINVAL);
rs = pf_find_or_create_ruleset(anchor);
if (rs == NULL)
return (EINVAL);
while ((rule = TAILQ_FIRST(rs->rules[rs_num].inactive.ptr)) != NULL) {
pf_rm_rule(rs->rules[rs_num].inactive.ptr, rule);
rs->rules[rs_num].inactive.rcount--;
}
*ticket = ++rs->rules[rs_num].inactive.ticket;
rs->rules[rs_num].inactive.open = 1;
return (0);
}
int
pf_rollback_rules(u_int32_t ticket, int rs_num, char *anchor)
{
struct pf_ruleset *rs;
struct pf_rule *rule;
if (rs_num < 0 || rs_num >= PF_RULESET_MAX)
return (EINVAL);
rs = pf_find_ruleset(anchor);
if (rs == NULL || !rs->rules[rs_num].inactive.open ||
rs->rules[rs_num].inactive.ticket != ticket)
return (0);
while ((rule = TAILQ_FIRST(rs->rules[rs_num].inactive.ptr)) != NULL) {
pf_rm_rule(rs->rules[rs_num].inactive.ptr, rule);
rs->rules[rs_num].inactive.rcount--;
}
rs->rules[rs_num].inactive.open = 0;
return (0);
}
#define PF_MD5_UPD(st, elm) \
MD5Update(ctx, (u_int8_t *) &(st)->elm, sizeof((st)->elm))
#define PF_MD5_UPD_STR(st, elm) \
MD5Update(ctx, (u_int8_t *) (st)->elm, strlen((st)->elm))
#define PF_MD5_UPD_HTONL(st, elm, stor) do { \
(stor) = htonl((st)->elm); \
MD5Update(ctx, (u_int8_t *) &(stor), sizeof(u_int32_t));\
} while (0)
#define PF_MD5_UPD_HTONS(st, elm, stor) do { \
(stor) = htons((st)->elm); \
MD5Update(ctx, (u_int8_t *) &(stor), sizeof(u_int16_t));\
} while (0)
void
pf_hash_rule_addr(MD5_CTX *ctx, struct pf_rule_addr *pfr)
{
PF_MD5_UPD(pfr, addr.type);
switch (pfr->addr.type) {
case PF_ADDR_DYNIFTL:
PF_MD5_UPD(pfr, addr.v.ifname);
PF_MD5_UPD(pfr, addr.iflags);
break;
case PF_ADDR_TABLE:
PF_MD5_UPD(pfr, addr.v.tblname);
break;
case PF_ADDR_ADDRMASK:
/* XXX ignore af? */
PF_MD5_UPD(pfr, addr.v.a.addr.addr32);
PF_MD5_UPD(pfr, addr.v.a.mask.addr32);
break;
case PF_ADDR_RTLABEL:
PF_MD5_UPD(pfr, addr.v.rtlabelname);
break;
}
PF_MD5_UPD(pfr, port[0]);
PF_MD5_UPD(pfr, port[1]);
PF_MD5_UPD(pfr, neg);
PF_MD5_UPD(pfr, port_op);
}
void
pf_hash_rule(MD5_CTX *ctx, struct pf_rule *rule)
{
u_int16_t x;
u_int32_t y;
pf_hash_rule_addr(ctx, &rule->src);
pf_hash_rule_addr(ctx, &rule->dst);
PF_MD5_UPD_STR(rule, label);
PF_MD5_UPD_STR(rule, ifname);
PF_MD5_UPD_STR(rule, match_tagname);
PF_MD5_UPD_HTONS(rule, match_tag, x); /* dup? */
PF_MD5_UPD_HTONL(rule, os_fingerprint, y);
PF_MD5_UPD_HTONL(rule, prob, y);
PF_MD5_UPD_HTONL(rule, uid.uid[0], y);
PF_MD5_UPD_HTONL(rule, uid.uid[1], y);
PF_MD5_UPD(rule, uid.op);
PF_MD5_UPD_HTONL(rule, gid.gid[0], y);
PF_MD5_UPD_HTONL(rule, gid.gid[1], y);
PF_MD5_UPD(rule, gid.op);
PF_MD5_UPD_HTONL(rule, rule_flag, y);
PF_MD5_UPD(rule, action);
PF_MD5_UPD(rule, direction);
PF_MD5_UPD(rule, af);
PF_MD5_UPD(rule, quick);
PF_MD5_UPD(rule, ifnot);
PF_MD5_UPD(rule, match_tag_not);
PF_MD5_UPD(rule, natpass);
PF_MD5_UPD(rule, keep_state);
PF_MD5_UPD(rule, proto);
PF_MD5_UPD(rule, type);
PF_MD5_UPD(rule, code);
PF_MD5_UPD(rule, flags);
PF_MD5_UPD(rule, flagset);
PF_MD5_UPD(rule, allow_opts);
PF_MD5_UPD(rule, rt);
PF_MD5_UPD(rule, tos);
}
int
pf_commit_rules(u_int32_t ticket, int rs_num, char *anchor)
{
struct pf_ruleset *rs;
struct pf_rule *rule, **old_array;
struct pf_rulequeue *old_rules;
int s, error;
u_int32_t old_rcount;
if (rs_num < 0 || rs_num >= PF_RULESET_MAX)
return (EINVAL);
rs = pf_find_ruleset(anchor);
if (rs == NULL || !rs->rules[rs_num].inactive.open ||
ticket != rs->rules[rs_num].inactive.ticket)
return (EBUSY);
/* Calculate checksum for the main ruleset */
if (rs == &pf_main_ruleset) {
error = pf_setup_pfsync_matching(rs);
if (error != 0)
return (error);
}
/* Swap rules, keep the old. */
s = splsoftnet();
old_rules = rs->rules[rs_num].active.ptr;
old_rcount = rs->rules[rs_num].active.rcount;
old_array = rs->rules[rs_num].active.ptr_array;
rs->rules[rs_num].active.ptr =
rs->rules[rs_num].inactive.ptr;
rs->rules[rs_num].active.ptr_array =
rs->rules[rs_num].inactive.ptr_array;
rs->rules[rs_num].active.rcount =
rs->rules[rs_num].inactive.rcount;
rs->rules[rs_num].inactive.ptr = old_rules;
rs->rules[rs_num].inactive.ptr_array = old_array;
rs->rules[rs_num].inactive.rcount = old_rcount;
rs->rules[rs_num].active.ticket =
rs->rules[rs_num].inactive.ticket;
pf_calc_skip_steps(rs->rules[rs_num].active.ptr);
/* Purge the old rule list. */
while ((rule = TAILQ_FIRST(old_rules)) != NULL)
pf_rm_rule(old_rules, rule);
if (rs->rules[rs_num].inactive.ptr_array)
free(rs->rules[rs_num].inactive.ptr_array, M_TEMP);
rs->rules[rs_num].inactive.ptr_array = NULL;
rs->rules[rs_num].inactive.rcount = 0;
rs->rules[rs_num].inactive.open = 0;
pf_remove_if_empty_ruleset(rs);
splx(s);
return (0);
}
int
pf_setup_pfsync_matching(struct pf_ruleset *rs)
{
MD5_CTX ctx;
struct pf_rule *rule;
int rs_cnt;
u_int8_t digest[PF_MD5_DIGEST_LENGTH];
MD5Init(&ctx);
for (rs_cnt = 0; rs_cnt < PF_RULESET_MAX; rs_cnt++) {
/* XXX PF_RULESET_SCRUB as well? */
if (rs_cnt == PF_RULESET_SCRUB)
continue;
if (rs->rules[rs_cnt].inactive.ptr_array)
free(rs->rules[rs_cnt].inactive.ptr_array, M_TEMP);
rs->rules[rs_cnt].inactive.ptr_array = NULL;
if (rs->rules[rs_cnt].inactive.rcount) {
rs->rules[rs_cnt].inactive.ptr_array =
malloc(sizeof(caddr_t) *
rs->rules[rs_cnt].inactive.rcount,
M_TEMP, M_NOWAIT);
if (!rs->rules[rs_cnt].inactive.ptr_array)
return (ENOMEM);
}
TAILQ_FOREACH(rule, rs->rules[rs_cnt].inactive.ptr,
entries) {
pf_hash_rule(&ctx, rule);
(rs->rules[rs_cnt].inactive.ptr_array)[rule->nr] = rule;
}
}
MD5Final(digest, &ctx);
memcpy(pf_status.pf_chksum, digest, sizeof(pf_status.pf_chksum));
return (0);
}
int
#ifdef __FreeBSD__
pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td)
#else
pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
#endif
{
struct pf_pooladdr *pa = NULL;
struct pf_pool *pool = NULL;
#ifndef __FreeBSD__
int s;
#endif
int error = 0;
/* XXX keep in sync with switch() below */
#ifdef __FreeBSD__
if (securelevel_gt(td->td_ucred, 2))
#else
if (securelevel > 1)
#endif
switch (cmd) {
case DIOCGETRULES:
case DIOCGETRULE:
case DIOCGETADDRS:
case DIOCGETADDR:
case DIOCGETSTATE:
case DIOCSETSTATUSIF:
case DIOCGETSTATUS:
case DIOCCLRSTATUS:
case DIOCNATLOOK:
case DIOCSETDEBUG:
case DIOCGETSTATES:
case DIOCGETTIMEOUT:
case DIOCCLRRULECTRS:
case DIOCGETLIMIT:
case DIOCGETALTQS:
case DIOCGETALTQ:
case DIOCGETQSTATS:
case DIOCGETRULESETS:
case DIOCGETRULESET:
case DIOCRGETTABLES:
case DIOCRGETTSTATS:
case DIOCRCLRTSTATS:
case DIOCRCLRADDRS:
case DIOCRADDADDRS:
case DIOCRDELADDRS:
case DIOCRSETADDRS:
case DIOCRGETADDRS:
case DIOCRGETASTATS:
case DIOCRCLRASTATS:
case DIOCRTSTADDRS:
case DIOCOSFPGET:
case DIOCGETSRCNODES:
case DIOCCLRSRCNODES:
case DIOCIGETIFACES:
#ifdef __FreeBSD__
case DIOCGIFSPEED:
#endif
case DIOCSETIFFLAG:
case DIOCCLRIFFLAG:
break;
case DIOCRCLRTABLES:
case DIOCRADDTABLES:
case DIOCRDELTABLES:
case DIOCRSETTFLAGS:
if (((struct pfioc_table *)addr)->pfrio_flags &
PFR_FLAG_DUMMY)
break; /* dummy operation ok */
return (EPERM);
default:
return (EPERM);
}
if (!(flags & FWRITE))
switch (cmd) {
case DIOCGETRULES:
case DIOCGETADDRS:
case DIOCGETADDR:
case DIOCGETSTATE:
case DIOCGETSTATUS:
case DIOCGETSTATES:
case DIOCGETTIMEOUT:
case DIOCGETLIMIT:
case DIOCGETALTQS:
case DIOCGETALTQ:
case DIOCGETQSTATS:
case DIOCGETRULESETS:
case DIOCGETRULESET:
case DIOCNATLOOK:
case DIOCRGETTABLES:
case DIOCRGETTSTATS:
case DIOCRGETADDRS:
case DIOCRGETASTATS:
case DIOCRTSTADDRS:
case DIOCOSFPGET:
case DIOCGETSRCNODES:
case DIOCIGETIFACES:
#ifdef __FreeBSD__
case DIOCGIFSPEED:
#endif
break;
case DIOCRCLRTABLES:
case DIOCRADDTABLES:
case DIOCRDELTABLES:
case DIOCRCLRTSTATS:
case DIOCRCLRADDRS:
case DIOCRADDADDRS:
case DIOCRDELADDRS:
case DIOCRSETADDRS:
case DIOCRSETTFLAGS:
if (((struct pfioc_table *)addr)->pfrio_flags &
PFR_FLAG_DUMMY) {
flags |= FWRITE; /* need write lock for dummy */
break; /* dummy operation ok */
}
return (EACCES);
case DIOCGETRULE:
if (((struct pfioc_rule *)addr)->action == PF_GET_CLR_CNTR)
return (EACCES);
break;
default:
return (EACCES);
}
if (flags & FWRITE)
#ifdef __FreeBSD__
sx_xlock(&pf_consistency_lock);
else
sx_slock(&pf_consistency_lock);
#else
rw_enter_write(&pf_consistency_lock);
else
rw_enter_read(&pf_consistency_lock);
#endif
#ifdef __FreeBSD__
PF_LOCK();
#else
s = splsoftnet();
#endif
switch (cmd) {
case DIOCSTART:
if (pf_status.running)
error = EEXIST;
else {
#ifdef __FreeBSD__
PF_UNLOCK();
error = hook_pf();
PF_LOCK();
if (error) {
DPFPRINTF(PF_DEBUG_MISC,
("pf: pfil registeration fail\n"));
break;
}
#endif
pf_status.running = 1;
pf_status.since = time_second;
if (pf_status.stateid == 0) {
pf_status.stateid = time_second;
pf_status.stateid = pf_status.stateid << 32;
}
DPFPRINTF(PF_DEBUG_MISC, ("pf: started\n"));
}
break;
case DIOCSTOP:
if (!pf_status.running)
error = ENOENT;
else {
pf_status.running = 0;
#ifdef __FreeBSD__
PF_UNLOCK();
error = dehook_pf();
PF_LOCK();
if (error) {
pf_status.running = 1;
DPFPRINTF(PF_DEBUG_MISC,
("pf: pfil unregisteration failed\n"));
}
#endif
pf_status.since = time_second;
DPFPRINTF(PF_DEBUG_MISC, ("pf: stopped\n"));
}
break;
case DIOCADDRULE: {
struct pfioc_rule *pr = (struct pfioc_rule *)addr;
struct pf_ruleset *ruleset;
struct pf_rule *rule, *tail;
struct pf_pooladdr *pa;
int rs_num;
pr->anchor[sizeof(pr->anchor) - 1] = 0;
ruleset = pf_find_ruleset(pr->anchor);
if (ruleset == NULL) {
error = EINVAL;
break;
}
rs_num = pf_get_ruleset_number(pr->rule.action);
if (rs_num >= PF_RULESET_MAX) {
error = EINVAL;
break;
}
if (pr->rule.return_icmp >> 8 > ICMP_MAXTYPE) {
error = EINVAL;
break;
}
if (pr->ticket != ruleset->rules[rs_num].inactive.ticket) {
#ifdef __FreeBSD__
DPFPRINTF(PF_DEBUG_MISC,
("ticket: %d != [%d]%d\n", pr->ticket, rs_num,
ruleset->rules[rs_num].inactive.ticket));
#endif
error = EBUSY;
break;
}
if (pr->pool_ticket != ticket_pabuf) {
#ifdef __FreeBSD__
DPFPRINTF(PF_DEBUG_MISC,
("pool_ticket: %d != %d\n", pr->pool_ticket,
ticket_pabuf));
#endif
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));
#ifdef __FreeBSD__
rule->cuid = td->td_ucred->cr_ruid;
rule->cpid = td->td_proc ? td->td_proc->p_pid : 0;
#else
rule->cuid = p->p_cred->p_ruid;
rule->cpid = p->p_pid;
#endif
rule->anchor = NULL;
rule->kif = NULL;
TAILQ_INIT(&rule->rpool.list);
/* initialize refcounting */
rule->states = 0;
rule->src_nodes = 0;
rule->entries.tqe_prev = NULL;
#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(ruleset->rules[rs_num].inactive.ptr,
pf_rulequeue);
if (tail)
rule->nr = tail->nr + 1;
else
rule->nr = 0;
if (rule->ifname[0]) {
rule->kif = pfi_kif_get(rule->ifname);
if (rule->kif == NULL) {
pool_put(&pf_rule_pl, rule);
error = EINVAL;
break;
}
pfi_kif_ref(rule->kif, PFI_KIF_REF_RULE);
}
#ifdef __FreeBSD__ /* ROUTING */
if (rule->rtableid > 0 && rule->rtableid > rt_numfibs)
#else
if (rule->rtableid > 0 && !rtable_exists(rule->rtableid))
#endif
error = EBUSY;
#ifdef ALTQ
/* set queue IDs */
if (rule->qname[0] != 0) {
if ((rule->qid = pf_qname2qid(rule->qname)) == 0)
error = EBUSY;
else if (rule->pqname[0] != 0) {
if ((rule->pqid =
pf_qname2qid(rule->pqname)) == 0)
error = EBUSY;
} else
rule->pqid = rule->qid;
}
#endif
if (rule->tagname[0])
if ((rule->tag = pf_tagname2tag(rule->tagname)) == 0)
error = EBUSY;
if (rule->match_tagname[0])
if ((rule->match_tag =
pf_tagname2tag(rule->match_tagname)) == 0)
error = EBUSY;
if (rule->rt && !rule->direction)
error = EINVAL;
#if NPFLOG > 0
#ifdef __FreeBSD__
if (!rule->log)
rule->logif = 0;
#endif
if (rule->logif >= PFLOGIFS_MAX)
error = EINVAL;
#endif
if (pf_rtlabel_add(&rule->src.addr) ||
pf_rtlabel_add(&rule->dst.addr))
error = EBUSY;
if (pfi_dynaddr_setup(&rule->src.addr, rule->af))
error = EINVAL;
if (pfi_dynaddr_setup(&rule->dst.addr, rule->af))
error = EINVAL;
if (pf_tbladdr_setup(ruleset, &rule->src.addr))
error = EINVAL;
if (pf_tbladdr_setup(ruleset, &rule->dst.addr))
error = EINVAL;
if (pf_anchor_setup(rule, ruleset, pr->anchor_call))
error = EINVAL;
TAILQ_FOREACH(pa, &pf_pabuf, entries)
if (pf_tbladdr_setup(ruleset, &pa->addr))
error = EINVAL;
if (rule->overload_tblname[0]) {
if ((rule->overload_tbl = pfr_attach_table(ruleset,
rule->overload_tblname)) == NULL)
error = EINVAL;
else
rule->overload_tbl->pfrkt_flags |=
PFR_TFLAG_ACTIVE;
}
pf_mv_pool(&pf_pabuf, &rule->rpool.list);
if (((((rule->action == PF_NAT) || (rule->action == PF_RDR) ||
(rule->action == PF_BINAT)) && rule->anchor == NULL) ||
(rule->rt > PF_FASTROUTE)) &&
(TAILQ_FIRST(&rule->rpool.list) == NULL))
error = EINVAL;
if (error) {
pf_rm_rule(NULL, rule);
break;
}
#ifdef __FreeBSD__
if (!debug_pfugidhack && (rule->uid.op || rule->gid.op ||
rule->log & PF_LOG_SOCKET_LOOKUP)) {
DPFPRINTF(PF_DEBUG_MISC,
("pf: debug.pfugidhack enabled\n"));
debug_pfugidhack = 1;
}
#endif
rule->rpool.cur = TAILQ_FIRST(&rule->rpool.list);
rule->evaluations = rule->packets[0] = rule->packets[1] =
rule->bytes[0] = rule->bytes[1] = 0;
TAILQ_INSERT_TAIL(ruleset->rules[rs_num].inactive.ptr,
rule, entries);
ruleset->rules[rs_num].inactive.rcount++;
break;
}
case DIOCGETRULES: {
struct pfioc_rule *pr = (struct pfioc_rule *)addr;
struct pf_ruleset *ruleset;
struct pf_rule *tail;
int rs_num;
pr->anchor[sizeof(pr->anchor) - 1] = 0;
ruleset = pf_find_ruleset(pr->anchor);
if (ruleset == NULL) {
error = EINVAL;
break;
}
rs_num = pf_get_ruleset_number(pr->rule.action);
if (rs_num >= PF_RULESET_MAX) {
error = EINVAL;
break;
}
tail = TAILQ_LAST(ruleset->rules[rs_num].active.ptr,
pf_rulequeue);
if (tail)
pr->nr = tail->nr + 1;
else
pr->nr = 0;
pr->ticket = ruleset->rules[rs_num].active.ticket;
break;
}
case DIOCGETRULE: {
struct pfioc_rule *pr = (struct pfioc_rule *)addr;
struct pf_ruleset *ruleset;
struct pf_rule *rule;
int rs_num, i;
pr->anchor[sizeof(pr->anchor) - 1] = 0;
ruleset = pf_find_ruleset(pr->anchor);
if (ruleset == NULL) {
error = EINVAL;
break;
}
rs_num = pf_get_ruleset_number(pr->rule.action);
if (rs_num >= PF_RULESET_MAX) {
error = EINVAL;
break;
}
if (pr->ticket != ruleset->rules[rs_num].active.ticket) {
error = EBUSY;
break;
}
rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr);
while ((rule != NULL) && (rule->nr != pr->nr))
rule = TAILQ_NEXT(rule, entries);
if (rule == NULL) {
error = EBUSY;
break;
}
bcopy(rule, &pr->rule, sizeof(struct pf_rule));
if (pf_anchor_copyout(ruleset, rule, pr)) {
error = EBUSY;
break;
}
pfi_dynaddr_copyout(&pr->rule.src.addr);
pfi_dynaddr_copyout(&pr->rule.dst.addr);
pf_tbladdr_copyout(&pr->rule.src.addr);
pf_tbladdr_copyout(&pr->rule.dst.addr);
pf_rtlabel_copyout(&pr->rule.src.addr);
pf_rtlabel_copyout(&pr->rule.dst.addr);
for (i = 0; i < PF_SKIP_COUNT; ++i)
if (rule->skip[i].ptr == NULL)
pr->rule.skip[i].nr = -1;
else
pr->rule.skip[i].nr =
rule->skip[i].ptr->nr;
if (pr->action == PF_GET_CLR_CNTR) {
rule->evaluations = 0;
rule->packets[0] = rule->packets[1] = 0;
rule->bytes[0] = rule->bytes[1] = 0;
}
break;
}
case DIOCCHANGERULE: {
struct pfioc_rule *pcr = (struct pfioc_rule *)addr;
struct pf_ruleset *ruleset;
struct pf_rule *oldrule = NULL, *newrule = NULL;
u_int32_t nr = 0;
int rs_num;
if (!(pcr->action == PF_CHANGE_REMOVE ||
pcr->action == PF_CHANGE_GET_TICKET) &&
pcr->pool_ticket != ticket_pabuf) {
error = EBUSY;
break;
}
if (pcr->action < PF_CHANGE_ADD_HEAD ||
pcr->action > PF_CHANGE_GET_TICKET) {
error = EINVAL;
break;
}
ruleset = pf_find_ruleset(pcr->anchor);
if (ruleset == NULL) {
error = EINVAL;
break;
}
rs_num = pf_get_ruleset_number(pcr->rule.action);
if (rs_num >= PF_RULESET_MAX) {
error = EINVAL;
break;
}
if (pcr->action == PF_CHANGE_GET_TICKET) {
pcr->ticket = ++ruleset->rules[rs_num].active.ticket;
break;
} else {
if (pcr->ticket !=
ruleset->rules[rs_num].active.ticket) {
error = EINVAL;
break;
}
if (pcr->rule.return_icmp >> 8 > ICMP_MAXTYPE) {
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->rule, newrule, sizeof(struct pf_rule));
#ifdef __FreeBSD__
newrule->cuid = td->td_ucred->cr_ruid;
newrule->cpid = td->td_proc ? td->td_proc->p_pid : 0;
#else
newrule->cuid = p->p_cred->p_ruid;
newrule->cpid = p->p_pid;
#endif
TAILQ_INIT(&newrule->rpool.list);
/* initialize refcounting */
newrule->states = 0;
newrule->entries.tqe_prev = NULL;
#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->kif = pfi_kif_get(newrule->ifname);
if (newrule->kif == NULL) {
pool_put(&pf_rule_pl, newrule);
error = EINVAL;
break;
}
pfi_kif_ref(newrule->kif, PFI_KIF_REF_RULE);
} else
newrule->kif = NULL;
if (newrule->rtableid > 0 &&
#ifdef __FreeBSD__ /* ROUTING */
newrule->rtableid > rt_numfibs)
#else
!rtable_exists(newrule->rtableid))
#endif
error = EBUSY;
#ifdef ALTQ
/* set queue IDs */
if (newrule->qname[0] != 0) {
if ((newrule->qid =
pf_qname2qid(newrule->qname)) == 0)
error = EBUSY;
else if (newrule->pqname[0] != 0) {
if ((newrule->pqid =
pf_qname2qid(newrule->pqname)) == 0)
error = EBUSY;
} else
newrule->pqid = newrule->qid;
}
#endif /* ALTQ */
if (newrule->tagname[0])
if ((newrule->tag =
pf_tagname2tag(newrule->tagname)) == 0)
error = EBUSY;
if (newrule->match_tagname[0])
if ((newrule->match_tag = pf_tagname2tag(
newrule->match_tagname)) == 0)
error = EBUSY;
if (newrule->rt && !newrule->direction)
error = EINVAL;
#ifdef __FreeBSD__
#if NPFLOG > 0
if (!newrule->log)
newrule->logif = 0;
if (newrule->logif >= PFLOGIFS_MAX)
error = EINVAL;
#endif
#endif
if (pf_rtlabel_add(&newrule->src.addr) ||
pf_rtlabel_add(&newrule->dst.addr))
error = EBUSY;
if (pfi_dynaddr_setup(&newrule->src.addr, newrule->af))
error = EINVAL;
if (pfi_dynaddr_setup(&newrule->dst.addr, newrule->af))
error = EINVAL;
if (pf_tbladdr_setup(ruleset, &newrule->src.addr))
error = EINVAL;
if (pf_tbladdr_setup(ruleset, &newrule->dst.addr))
error = EINVAL;
if (pf_anchor_setup(newrule, ruleset, pcr->anchor_call))
error = EINVAL;
TAILQ_FOREACH(pa, &pf_pabuf, entries)
if (pf_tbladdr_setup(ruleset, &pa->addr))
error = EINVAL;
if (newrule->overload_tblname[0]) {
if ((newrule->overload_tbl = pfr_attach_table(
ruleset, newrule->overload_tblname)) ==
NULL)
error = EINVAL;
else
newrule->overload_tbl->pfrkt_flags |=
PFR_TFLAG_ACTIVE;
}
pf_mv_pool(&pf_pabuf, &newrule->rpool.list);
if (((((newrule->action == PF_NAT) ||
(newrule->action == PF_RDR) ||
(newrule->action == PF_BINAT) ||
(newrule->rt > PF_FASTROUTE)) &&
!newrule->anchor)) &&
(TAILQ_FIRST(&newrule->rpool.list) == NULL))
error = EINVAL;
if (error) {
pf_rm_rule(NULL, newrule);
break;
}
#ifdef __FreeBSD__
if (!debug_pfugidhack && (newrule->uid.op ||
newrule->gid.op ||
newrule->log & PF_LOG_SOCKET_LOOKUP)) {
DPFPRINTF(PF_DEBUG_MISC,
("pf: debug.pfugidhack enabled\n"));
debug_pfugidhack = 1;
}
#endif
newrule->rpool.cur = TAILQ_FIRST(&newrule->rpool.list);
newrule->evaluations = 0;
newrule->packets[0] = newrule->packets[1] = 0;
newrule->bytes[0] = newrule->bytes[1] = 0;
}
pf_empty_pool(&pf_pabuf);
if (pcr->action == PF_CHANGE_ADD_HEAD)
oldrule = TAILQ_FIRST(
ruleset->rules[rs_num].active.ptr);
else if (pcr->action == PF_CHANGE_ADD_TAIL)
oldrule = TAILQ_LAST(
ruleset->rules[rs_num].active.ptr, pf_rulequeue);
else {
oldrule = TAILQ_FIRST(
ruleset->rules[rs_num].active.ptr);
while ((oldrule != NULL) && (oldrule->nr != pcr->nr))
oldrule = TAILQ_NEXT(oldrule, entries);
if (oldrule == NULL) {
if (newrule != NULL)
pf_rm_rule(NULL, newrule);
error = EINVAL;
break;
}
}
if (pcr->action == PF_CHANGE_REMOVE) {
pf_rm_rule(ruleset->rules[rs_num].active.ptr, oldrule);
ruleset->rules[rs_num].active.rcount--;
} else {
if (oldrule == NULL)
TAILQ_INSERT_TAIL(
ruleset->rules[rs_num].active.ptr,
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(
ruleset->rules[rs_num].active.ptr,
oldrule, newrule, entries);
ruleset->rules[rs_num].active.rcount++;
}
nr = 0;
TAILQ_FOREACH(oldrule,
ruleset->rules[rs_num].active.ptr, entries)
oldrule->nr = nr++;
ruleset->rules[rs_num].active.ticket++;
pf_calc_skip_steps(ruleset->rules[rs_num].active.ptr);
pf_remove_if_empty_ruleset(ruleset);
break;
}
case DIOCCLRSTATES: {
struct pf_state *state, *nexts;
struct pfioc_state_kill *psk = (struct pfioc_state_kill *)addr;
int killed = 0;
for (state = RB_MIN(pf_state_tree_id, &tree_id); state;
state = nexts) {
nexts = RB_NEXT(pf_state_tree_id, &tree_id, state);
if (!psk->psk_ifname[0] || !strcmp(psk->psk_ifname,
state->u.s.kif->pfik_name)) {
#if NPFSYNC
/* don't send out individual delete messages */
state->sync_flags = PFSTATE_NOSYNC;
#endif
pf_unlink_state(state);
killed++;
}
}
psk->psk_af = killed;
#if NPFSYNC
pfsync_clear_states(pf_status.hostid, psk->psk_ifname);
#endif
break;
}
case DIOCKILLSTATES: {
struct pf_state *state, *nexts;
struct pf_state_host *src, *dst;
struct pfioc_state_kill *psk = (struct pfioc_state_kill *)addr;
int killed = 0;
for (state = RB_MIN(pf_state_tree_id, &tree_id); state;
state = nexts) {
nexts = RB_NEXT(pf_state_tree_id, &tree_id, state);
if (state->direction == PF_OUT) {
src = &state->lan;
dst = &state->ext;
} else {
src = &state->ext;
dst = &state->lan;
}
if ((!psk->psk_af || state->af == psk->psk_af)
&& (!psk->psk_proto || psk->psk_proto ==
state->proto) &&
PF_MATCHA(psk->psk_src.neg,
&psk->psk_src.addr.v.a.addr,
&psk->psk_src.addr.v.a.mask,
&src->addr, state->af) &&
PF_MATCHA(psk->psk_dst.neg,
&psk->psk_dst.addr.v.a.addr,
&psk->psk_dst.addr.v.a.mask,
&dst->addr, state->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],
src->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],
dst->port)) &&
(!psk->psk_ifname[0] || !strcmp(psk->psk_ifname,
state->u.s.kif->pfik_name))) {
#if NPFSYNC > 0
/* send immediate delete of state */
pfsync_delete_state(state);
state->sync_flags |= PFSTATE_NOSYNC;
#endif
pf_unlink_state(state);
killed++;
}
}
psk->psk_af = killed;
break;
}
case DIOCADDSTATE: {
struct pfioc_state *ps = (struct pfioc_state *)addr;
struct pf_state *state;
struct pfi_kif *kif;
if (ps->state.timeout >= PFTM_MAX &&
ps->state.timeout != PFTM_UNTIL_PACKET) {
error = EINVAL;
break;
}
state = pool_get(&pf_state_pl, PR_NOWAIT);
if (state == NULL) {
error = ENOMEM;
break;
}
kif = pfi_kif_get(ps->state.u.ifname);
if (kif == NULL) {
pool_put(&pf_state_pl, state);
error = ENOENT;
break;
}
bcopy(&ps->state, state, sizeof(struct pf_state));
bzero(&state->u, sizeof(state->u));
state->rule.ptr = &pf_default_rule;
state->nat_rule.ptr = NULL;
state->anchor.ptr = NULL;
state->rt_kif = NULL;
state->creation = time_second;
state->pfsync_time = 0;
state->packets[0] = state->packets[1] = 0;
state->bytes[0] = state->bytes[1] = 0;
if (pf_insert_state(kif, state)) {
pfi_kif_unref(kif, PFI_KIF_REF_NONE);
pool_put(&pf_state_pl, state);
error = ENOMEM;
}
break;
}
case DIOCGETSTATE: {
struct pfioc_state *ps = (struct pfioc_state *)addr;
struct pf_state *state;
u_int32_t nr;
int secs;
nr = 0;
RB_FOREACH(state, pf_state_tree_id, &tree_id) {
if (nr >= ps->nr)
break;
nr++;
}
if (state == NULL) {
error = EBUSY;
break;
}
secs = time_second;
bcopy(state, &ps->state, sizeof(ps->state));
strlcpy(ps->state.u.ifname, state->u.s.kif->pfik_name,
sizeof(ps->state.u.ifname));
ps->state.rule.nr = state->rule.ptr->nr;
ps->state.nat_rule.nr = (state->nat_rule.ptr == NULL) ?
-1 : state->nat_rule.ptr->nr;
ps->state.anchor.nr = (state->anchor.ptr == NULL) ?
-1 : state->anchor.ptr->nr;
ps->state.creation = secs - ps->state.creation;
ps->state.expire = pf_state_expires(state);
if (ps->state.expire > secs)
ps->state.expire -= secs;
else
ps->state.expire = 0;
break;
}
case DIOCGETSTATES: {
struct pfioc_states *ps = (struct pfioc_states *)addr;
struct pf_state *state;
struct pf_state *p, *pstore;
u_int32_t nr = 0;
int space = ps->ps_len;
if (space == 0) {
nr = pf_status.states;
ps->ps_len = sizeof(struct pf_state) * nr;
break;
}
#ifdef __FreeBSD__
PF_UNLOCK();
#endif
pstore = malloc(sizeof(*pstore), M_TEMP, M_WAITOK);
#ifdef __FreeBSD__
PF_LOCK();
#endif
p = ps->ps_states;
state = TAILQ_FIRST(&state_list);
while (state) {
if (state->timeout != PFTM_UNLINKED) {
int secs = time_second;
if ((nr+1) * sizeof(*p) > (unsigned)ps->ps_len)
break;
bcopy(state, pstore, sizeof(*pstore));
strlcpy(pstore->u.ifname,
state->u.s.kif->pfik_name,
sizeof(pstore->u.ifname));
pstore->rule.nr = state->rule.ptr->nr;
pstore->nat_rule.nr = (state->nat_rule.ptr ==
NULL) ? -1 : state->nat_rule.ptr->nr;
pstore->anchor.nr = (state->anchor.ptr ==
NULL) ? -1 : state->anchor.ptr->nr;
pstore->creation = secs - pstore->creation;
pstore->expire = pf_state_expires(state);
if (pstore->expire > secs)
pstore->expire -= secs;
else
pstore->expire = 0;
#ifdef __FreeBSD__
PF_COPYOUT(pstore, p, sizeof(*p), error);
#else
error = copyout(pstore, p, sizeof(*p));
#endif
if (error) {
free(pstore, M_TEMP);
goto fail;
}
p++;
nr++;
}
state = TAILQ_NEXT(state, u.s.entry_list);
}
ps->ps_len = sizeof(struct pf_state) * nr;
free(pstore, M_TEMP);
break;
}
case DIOCGETSTATUS: {
struct pf_status *s = (struct pf_status *)addr;
bcopy(&pf_status, s, sizeof(struct pf_status));
pfi_fill_oldstatus(s);
break;
}
case DIOCSETSTATUSIF: {
struct pfioc_if *pi = (struct pfioc_if *)addr;
if (pi->ifname[0] == 0) {
bzero(pf_status.ifname, IFNAMSIZ);
break;
}
if (ifunit(pi->ifname) == NULL) {
error = EINVAL;
break;
}
strlcpy(pf_status.ifname, pi->ifname, IFNAMSIZ);
break;
}
case DIOCCLRSTATUS: {
bzero(pf_status.counters, sizeof(pf_status.counters));
bzero(pf_status.fcounters, sizeof(pf_status.fcounters));
bzero(pf_status.scounters, sizeof(pf_status.scounters));
pf_status.since = time_second;
if (*pf_status.ifname)
pfi_clr_istats(pf_status.ifname);
break;
}
case DIOCNATLOOK: {
struct pfioc_natlook *pnl = (struct pfioc_natlook *)addr;
struct pf_state *state;
struct pf_state_cmp key;
int m = 0, direction = pnl->direction;
key.af = pnl->af;
key.proto = pnl->proto;
if (!pnl->proto ||
PF_AZERO(&pnl->saddr, pnl->af) ||
PF_AZERO(&pnl->daddr, pnl->af) ||
((pnl->proto == IPPROTO_TCP ||
pnl->proto == IPPROTO_UDP) &&
(!pnl->dport || !pnl->sport)))
error = EINVAL;
else {
/*
* userland gives us source and dest of connection,
* reverse the lookup so we ask for what happens with
* the return traffic, enabling us to find it in the
* state tree.
*/
if (direction == PF_IN) {
PF_ACPY(&key.ext.addr, &pnl->daddr, pnl->af);
key.ext.port = pnl->dport;
PF_ACPY(&key.gwy.addr, &pnl->saddr, pnl->af);
key.gwy.port = pnl->sport;
state = pf_find_state_all(&key, PF_EXT_GWY, &m);
} else {
PF_ACPY(&key.lan.addr, &pnl->daddr, pnl->af);
key.lan.port = pnl->dport;
PF_ACPY(&key.ext.addr, &pnl->saddr, pnl->af);
key.ext.port = pnl->sport;
state = pf_find_state_all(&key, PF_LAN_EXT, &m);
}
if (m > 1)
error = E2BIG; /* more than one state */
else if (state != NULL) {
if (direction == PF_IN) {
PF_ACPY(&pnl->rsaddr, &state->lan.addr,
state->af);
pnl->rsport = state->lan.port;
PF_ACPY(&pnl->rdaddr, &pnl->daddr,
pnl->af);
pnl->rdport = pnl->dport;
} else {
PF_ACPY(&pnl->rdaddr, &state->gwy.addr,
state->af);
pnl->rdport = state->gwy.port;
PF_ACPY(&pnl->rsaddr, &pnl->saddr,
pnl->af);
pnl->rsport = pnl->sport;
}
} else
error = ENOENT;
}
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 = pf_default_rule.timeout[pt->timeout];
if (pt->timeout == PFTM_INTERVAL && pt->seconds == 0)
pt->seconds = 1;
pf_default_rule.timeout[pt->timeout] = pt->seconds;
if (pt->timeout == PFTM_INTERVAL && pt->seconds < old)
wakeup(pf_purge_thread);
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 = pf_default_rule.timeout[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 ||
pf_pool_limits[pl->index].pp == NULL) {
error = EINVAL;
goto fail;
}
#ifdef __FreeBSD__
uma_zone_set_max(pf_pool_limits[pl->index].pp, pl->limit);
#else
if (pool_sethardlimit(pf_pool_limits[pl->index].pp,
pl->limit, NULL, 0) != 0) {
error = EBUSY;
goto fail;
}
#endif
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: {
/* obsoleted by DIOCGETRULE with action=PF_GET_CLR_CNTR */
struct pf_ruleset *ruleset = &pf_main_ruleset;
struct pf_rule *rule;
TAILQ_FOREACH(rule,
ruleset->rules[PF_RULESET_FILTER].active.ptr, entries) {
rule->evaluations = 0;
rule->packets[0] = rule->packets[1] = 0;
rule->bytes[0] = rule->bytes[1] = 0;
}
break;
}
#ifdef __FreeBSD__
case DIOCGIFSPEED: {
struct pf_ifspeed *psp = (struct pf_ifspeed *)addr;
struct pf_ifspeed ps;
struct ifnet *ifp;
if (psp->ifname[0] != 0) {
/* Can we completely trust user-land? */
strlcpy(ps.ifname, psp->ifname, IFNAMSIZ);
ifp = ifunit(ps.ifname);
if (ifp != NULL)
psp->baudrate = ifp->if_baudrate;
else
error = EINVAL;
} else
error = EINVAL;
break;
}
#endif /* __FreeBSD__ */
#ifdef ALTQ
case DIOCSTARTALTQ: {
struct pf_altq *altq;
/* enable all altq interfaces on active list */
TAILQ_FOREACH(altq, pf_altqs_active, entries) {
#ifdef __FreeBSD__
if (altq->qname[0] == 0 && (altq->local_flags &
PFALTQ_FLAG_IF_REMOVED) == 0) {
#else
if (altq->qname[0] == 0) {
#endif
error = pf_enable_altq(altq);
if (error != 0)
break;
}
}
if (error == 0)
pf_altq_running = 1;
DPFPRINTF(PF_DEBUG_MISC, ("altq: started\n"));
break;
}
case DIOCSTOPALTQ: {
struct pf_altq *altq;
/* disable all altq interfaces on active list */
TAILQ_FOREACH(altq, pf_altqs_active, entries) {
#ifdef __FreeBSD__
if (altq->qname[0] == 0 && (altq->local_flags &
PFALTQ_FLAG_IF_REMOVED) == 0) {
#else
if (altq->qname[0] == 0) {
#endif
error = pf_disable_altq(altq);
if (error != 0)
break;
}
}
if (error == 0)
pf_altq_running = 0;
DPFPRINTF(PF_DEBUG_MISC, ("altq: stopped\n"));
break;
}
case DIOCADDALTQ: {
struct pfioc_altq *pa = (struct pfioc_altq *)addr;
struct pf_altq *altq, *a;
if (pa->ticket != ticket_altqs_inactive) {
error = EBUSY;
break;
}
altq = pool_get(&pf_altq_pl, PR_NOWAIT);
if (altq == NULL) {
error = ENOMEM;
break;
}
bcopy(&pa->altq, altq, sizeof(struct pf_altq));
#ifdef __FreeBSD__
altq->local_flags = 0;
#endif
/*
* if this is for a queue, find the discipline and
* copy the necessary fields
*/
if (altq->qname[0] != 0) {
if ((altq->qid = pf_qname2qid(altq->qname)) == 0) {
error = EBUSY;
pool_put(&pf_altq_pl, altq);
break;
}
altq->altq_disc = NULL;
TAILQ_FOREACH(a, pf_altqs_inactive, entries) {
if (strncmp(a->ifname, altq->ifname,
IFNAMSIZ) == 0 && a->qname[0] == 0) {
altq->altq_disc = a->altq_disc;
break;
}
}
}
#ifdef __FreeBSD__
struct ifnet *ifp;
if ((ifp = ifunit(altq->ifname)) == NULL) {
altq->local_flags |= PFALTQ_FLAG_IF_REMOVED;
} else {
PF_UNLOCK();
#endif
error = altq_add(altq);
#ifdef __FreeBSD__
PF_LOCK();
}
#endif
if (error) {
pool_put(&pf_altq_pl, altq);
break;
}
TAILQ_INSERT_TAIL(pf_altqs_inactive, altq, entries);
bcopy(altq, &pa->altq, sizeof(struct pf_altq));
break;
}
case DIOCGETALTQS: {
struct pfioc_altq *pa = (struct pfioc_altq *)addr;
struct pf_altq *altq;
pa->nr = 0;
TAILQ_FOREACH(altq, pf_altqs_active, entries)
pa->nr++;
pa->ticket = ticket_altqs_active;
break;
}
case DIOCGETALTQ: {
struct pfioc_altq *pa = (struct pfioc_altq *)addr;
struct pf_altq *altq;
u_int32_t nr;
if (pa->ticket != ticket_altqs_active) {
error = EBUSY;
break;
}
nr = 0;
altq = TAILQ_FIRST(pf_altqs_active);
while ((altq != NULL) && (nr < pa->nr)) {
altq = TAILQ_NEXT(altq, entries);
nr++;
}
if (altq == NULL) {
error = EBUSY;
break;
}
bcopy(altq, &pa->altq, sizeof(struct pf_altq));
break;
}
case DIOCCHANGEALTQ:
/* CHANGEALTQ not supported yet! */
error = ENODEV;
break;
case DIOCGETQSTATS: {
struct pfioc_qstats *pq = (struct pfioc_qstats *)addr;
struct pf_altq *altq;
u_int32_t nr;
int nbytes;
if (pq->ticket != ticket_altqs_active) {
error = EBUSY;
break;
}
nbytes = pq->nbytes;
nr = 0;
altq = TAILQ_FIRST(pf_altqs_active);
while ((altq != NULL) && (nr < pq->nr)) {
altq = TAILQ_NEXT(altq, entries);
nr++;
}
if (altq == NULL) {
error = EBUSY;
break;
}
#ifdef __FreeBSD__
if ((altq->local_flags & PFALTQ_FLAG_IF_REMOVED) != 0) {
error = ENXIO;
break;
}
PF_UNLOCK();
#endif
error = altq_getqstats(altq, pq->buf, &nbytes);
#ifdef __FreeBSD__
PF_LOCK();
#endif
if (error == 0) {
pq->scheduler = altq->scheduler;
pq->nbytes = nbytes;
}
break;
}
#endif /* ALTQ */
case DIOCBEGINADDRS: {
struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr;
pf_empty_pool(&pf_pabuf);
pp->ticket = ++ticket_pabuf;
break;
}
case DIOCADDADDR: {
struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr;
if (pp->ticket != ticket_pabuf) {
error = EBUSY;
break;
}
#ifndef INET
if (pp->af == AF_INET) {
error = EAFNOSUPPORT;
break;
}
#endif /* INET */
#ifndef INET6
if (pp->af == AF_INET6) {
error = EAFNOSUPPORT;
break;
}
#endif /* INET6 */
if (pp->addr.addr.type != PF_ADDR_ADDRMASK &&
pp->addr.addr.type != PF_ADDR_DYNIFTL &&
pp->addr.addr.type != PF_ADDR_TABLE) {
error = EINVAL;
break;
}
pa = pool_get(&pf_pooladdr_pl, PR_NOWAIT);
if (pa == NULL) {
error = ENOMEM;
break;
}
bcopy(&pp->addr, pa, sizeof(struct pf_pooladdr));
if (pa->ifname[0]) {
pa->kif = pfi_kif_get(pa->ifname);
if (pa->kif == NULL) {
pool_put(&pf_pooladdr_pl, pa);
error = EINVAL;
break;
}
pfi_kif_ref(pa->kif, PFI_KIF_REF_RULE);
}
if (pfi_dynaddr_setup(&pa->addr, pp->af)) {
pfi_dynaddr_remove(&pa->addr);
pfi_kif_unref(pa->kif, PFI_KIF_REF_RULE);
pool_put(&pf_pooladdr_pl, pa);
error = EINVAL;
break;
}
TAILQ_INSERT_TAIL(&pf_pabuf, pa, entries);
break;
}
case DIOCGETADDRS: {
struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr;
pp->nr = 0;
pool = pf_get_pool(pp->anchor, pp->ticket, pp->r_action,
pp->r_num, 0, 1, 0);
if (pool == NULL) {
error = EBUSY;
break;
}
TAILQ_FOREACH(pa, &pool->list, entries)
pp->nr++;
break;
}
case DIOCGETADDR: {
struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr;
u_int32_t nr = 0;
pool = pf_get_pool(pp->anchor, pp->ticket, pp->r_action,
pp->r_num, 0, 1, 1);
if (pool == NULL) {
error = EBUSY;
break;
}
pa = TAILQ_FIRST(&pool->list);
while ((pa != NULL) && (nr < pp->nr)) {
pa = TAILQ_NEXT(pa, entries);
nr++;
}
if (pa == NULL) {
error = EBUSY;
break;
}
bcopy(pa, &pp->addr, sizeof(struct pf_pooladdr));
pfi_dynaddr_copyout(&pp->addr.addr);
pf_tbladdr_copyout(&pp->addr.addr);
pf_rtlabel_copyout(&pp->addr.addr);
break;
}
case DIOCCHANGEADDR: {
struct pfioc_pooladdr *pca = (struct pfioc_pooladdr *)addr;
struct pf_pooladdr *oldpa = NULL, *newpa = NULL;
struct pf_ruleset *ruleset;
if (pca->action < PF_CHANGE_ADD_HEAD ||
pca->action > PF_CHANGE_REMOVE) {
error = EINVAL;
break;
}
if (pca->addr.addr.type != PF_ADDR_ADDRMASK &&
pca->addr.addr.type != PF_ADDR_DYNIFTL &&
pca->addr.addr.type != PF_ADDR_TABLE) {
error = EINVAL;
break;
}
ruleset = pf_find_ruleset(pca->anchor);
if (ruleset == NULL) {
error = EBUSY;
break;
}
pool = pf_get_pool(pca->anchor, pca->ticket, pca->r_action,
pca->r_num, pca->r_last, 1, 1);
if (pool == NULL) {
error = EBUSY;
break;
}
if (pca->action != PF_CHANGE_REMOVE) {
newpa = pool_get(&pf_pooladdr_pl, PR_NOWAIT);
if (newpa == NULL) {
error = ENOMEM;
break;
}
bcopy(&pca->addr, newpa, sizeof(struct pf_pooladdr));
#ifndef INET
if (pca->af == AF_INET) {
pool_put(&pf_pooladdr_pl, newpa);
error = EAFNOSUPPORT;
break;
}
#endif /* INET */
#ifndef INET6
if (pca->af == AF_INET6) {
pool_put(&pf_pooladdr_pl, newpa);
error = EAFNOSUPPORT;
break;
}
#endif /* INET6 */
if (newpa->ifname[0]) {
newpa->kif = pfi_kif_get(newpa->ifname);
if (newpa->kif == NULL) {
pool_put(&pf_pooladdr_pl, newpa);
error = EINVAL;
break;
}
pfi_kif_ref(newpa->kif, PFI_KIF_REF_RULE);
} else
newpa->kif = NULL;
if (pfi_dynaddr_setup(&newpa->addr, pca->af) ||
pf_tbladdr_setup(ruleset, &newpa->addr)) {
pfi_dynaddr_remove(&newpa->addr);
pfi_kif_unref(newpa->kif, PFI_KIF_REF_RULE);
pool_put(&pf_pooladdr_pl, newpa);
error = EINVAL;
break;
}
}
if (pca->action == PF_CHANGE_ADD_HEAD)
oldpa = TAILQ_FIRST(&pool->list);
else if (pca->action == PF_CHANGE_ADD_TAIL)
oldpa = TAILQ_LAST(&pool->list, pf_palist);
else {
int i = 0;
oldpa = TAILQ_FIRST(&pool->list);
while ((oldpa != NULL) && (i < pca->nr)) {
oldpa = TAILQ_NEXT(oldpa, entries);
i++;
}
if (oldpa == NULL) {
error = EINVAL;
break;
}
}
if (pca->action == PF_CHANGE_REMOVE) {
TAILQ_REMOVE(&pool->list, oldpa, entries);
pfi_dynaddr_remove(&oldpa->addr);
pf_tbladdr_remove(&oldpa->addr);
pfi_kif_unref(oldpa->kif, PFI_KIF_REF_RULE);
pool_put(&pf_pooladdr_pl, oldpa);
} else {
if (oldpa == NULL)
TAILQ_INSERT_TAIL(&pool->list, newpa, entries);
else if (pca->action == PF_CHANGE_ADD_HEAD ||
pca->action == PF_CHANGE_ADD_BEFORE)
TAILQ_INSERT_BEFORE(oldpa, newpa, entries);
else
TAILQ_INSERT_AFTER(&pool->list, oldpa,
newpa, entries);
}
pool->cur = TAILQ_FIRST(&pool->list);
PF_ACPY(&pool->counter, &pool->cur->addr.v.a.addr,
pca->af);
break;
}
case DIOCGETRULESETS: {
struct pfioc_ruleset *pr = (struct pfioc_ruleset *)addr;
struct pf_ruleset *ruleset;
struct pf_anchor *anchor;
pr->path[sizeof(pr->path) - 1] = 0;
if ((ruleset = pf_find_ruleset(pr->path)) == NULL) {
error = EINVAL;
break;
}
pr->nr = 0;
if (ruleset->anchor == NULL) {
/* XXX kludge for pf_main_ruleset */
RB_FOREACH(anchor, pf_anchor_global, &pf_anchors)
if (anchor->parent == NULL)
pr->nr++;
} else {
RB_FOREACH(anchor, pf_anchor_node,
&ruleset->anchor->children)
pr->nr++;
}
break;
}
case DIOCGETRULESET: {
struct pfioc_ruleset *pr = (struct pfioc_ruleset *)addr;
struct pf_ruleset *ruleset;
struct pf_anchor *anchor;
u_int32_t nr = 0;
pr->path[sizeof(pr->path) - 1] = 0;
if ((ruleset = pf_find_ruleset(pr->path)) == NULL) {
error = EINVAL;
break;
}
pr->name[0] = 0;
if (ruleset->anchor == NULL) {
/* XXX kludge for pf_main_ruleset */
RB_FOREACH(anchor, pf_anchor_global, &pf_anchors)
if (anchor->parent == NULL && nr++ == pr->nr) {
strlcpy(pr->name, anchor->name,
sizeof(pr->name));
break;
}
} else {
RB_FOREACH(anchor, pf_anchor_node,
&ruleset->anchor->children)
if (nr++ == pr->nr) {
strlcpy(pr->name, anchor->name,
sizeof(pr->name));
break;
}
}
if (!pr->name[0])
error = EBUSY;
break;
}
case DIOCRCLRTABLES: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != 0) {
error = ENODEV;
break;
}
error = pfr_clr_tables(&io->pfrio_table, &io->pfrio_ndel,
io->pfrio_flags | PFR_FLAG_USERIOCTL);
break;
}
case DIOCRADDTABLES: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_table)) {
error = ENODEV;
break;
}
error = pfr_add_tables(io->pfrio_buffer, io->pfrio_size,
&io->pfrio_nadd, io->pfrio_flags | PFR_FLAG_USERIOCTL);
break;
}
case DIOCRDELTABLES: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_table)) {
error = ENODEV;
break;
}
error = pfr_del_tables(io->pfrio_buffer, io->pfrio_size,
&io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL);
break;
}
case DIOCRGETTABLES: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_table)) {
error = ENODEV;
break;
}
error = pfr_get_tables(&io->pfrio_table, io->pfrio_buffer,
&io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL);
break;
}
case DIOCRGETTSTATS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_tstats)) {
error = ENODEV;
break;
}
error = pfr_get_tstats(&io->pfrio_table, io->pfrio_buffer,
&io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL);
break;
}
case DIOCRCLRTSTATS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_table)) {
error = ENODEV;
break;
}
error = pfr_clr_tstats(io->pfrio_buffer, io->pfrio_size,
&io->pfrio_nzero, io->pfrio_flags | PFR_FLAG_USERIOCTL);
break;
}
case DIOCRSETTFLAGS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_table)) {
error = ENODEV;
break;
}
error = pfr_set_tflags(io->pfrio_buffer, io->pfrio_size,
io->pfrio_setflag, io->pfrio_clrflag, &io->pfrio_nchange,
&io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL);
break;
}
case DIOCRCLRADDRS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != 0) {
error = ENODEV;
break;
}
error = pfr_clr_addrs(&io->pfrio_table, &io->pfrio_ndel,
io->pfrio_flags | PFR_FLAG_USERIOCTL);
break;
}
case DIOCRADDADDRS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_addr)) {
error = ENODEV;
break;
}
error = pfr_add_addrs(&io->pfrio_table, io->pfrio_buffer,
io->pfrio_size, &io->pfrio_nadd, io->pfrio_flags |
PFR_FLAG_USERIOCTL);
break;
}
case DIOCRDELADDRS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_addr)) {
error = ENODEV;
break;
}
error = pfr_del_addrs(&io->pfrio_table, io->pfrio_buffer,
io->pfrio_size, &io->pfrio_ndel, io->pfrio_flags |
PFR_FLAG_USERIOCTL);
break;
}
case DIOCRSETADDRS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_addr)) {
error = ENODEV;
break;
}
error = pfr_set_addrs(&io->pfrio_table, io->pfrio_buffer,
io->pfrio_size, &io->pfrio_size2, &io->pfrio_nadd,
&io->pfrio_ndel, &io->pfrio_nchange, io->pfrio_flags |
PFR_FLAG_USERIOCTL, 0);
break;
}
case DIOCRGETADDRS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_addr)) {
error = ENODEV;
break;
}
error = pfr_get_addrs(&io->pfrio_table, io->pfrio_buffer,
&io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL);
break;
}
case DIOCRGETASTATS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_astats)) {
error = ENODEV;
break;
}
error = pfr_get_astats(&io->pfrio_table, io->pfrio_buffer,
&io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL);
break;
}
case DIOCRCLRASTATS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_addr)) {
error = ENODEV;
break;
}
error = pfr_clr_astats(&io->pfrio_table, io->pfrio_buffer,
io->pfrio_size, &io->pfrio_nzero, io->pfrio_flags |
PFR_FLAG_USERIOCTL);
break;
}
case DIOCRTSTADDRS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_addr)) {
error = ENODEV;
break;
}
error = pfr_tst_addrs(&io->pfrio_table, io->pfrio_buffer,
io->pfrio_size, &io->pfrio_nmatch, io->pfrio_flags |
PFR_FLAG_USERIOCTL);
break;
}
case DIOCRINADEFINE: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_addr)) {
error = ENODEV;
break;
}
error = pfr_ina_define(&io->pfrio_table, io->pfrio_buffer,
io->pfrio_size, &io->pfrio_nadd, &io->pfrio_naddr,
io->pfrio_ticket, io->pfrio_flags | PFR_FLAG_USERIOCTL);
break;
}
case DIOCOSFPADD: {
struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr;
error = pf_osfp_add(io);
break;
}
case DIOCOSFPGET: {
struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr;
error = pf_osfp_get(io);
break;
}
case DIOCXBEGIN: {
struct pfioc_trans *io = (struct pfioc_trans *)addr;
struct pfioc_trans_e *ioe;
struct pfr_table *table;
int i;
if (io->esize != sizeof(*ioe)) {
error = ENODEV;
goto fail;
}
#ifdef __FreeBSD__
PF_UNLOCK();
#endif
ioe = (struct pfioc_trans_e *)malloc(sizeof(*ioe),
M_TEMP, M_WAITOK);
table = (struct pfr_table *)malloc(sizeof(*table),
M_TEMP, M_WAITOK);
#ifdef __FreeBSD__
PF_LOCK();
#endif
for (i = 0; i < io->size; i++) {
#ifdef __FreeBSD__
PF_COPYIN(io->array+i, ioe, sizeof(*ioe), error);
if (error) {
#else
if (copyin(io->array+i, ioe, sizeof(*ioe))) {
#endif
free(table, M_TEMP);
free(ioe, M_TEMP);
error = EFAULT;
goto fail;
}
switch (ioe->rs_num) {
#ifdef ALTQ
case PF_RULESET_ALTQ:
if (ioe->anchor[0]) {
free(table, M_TEMP);
free(ioe, M_TEMP);
error = EINVAL;
goto fail;
}
if ((error = pf_begin_altq(&ioe->ticket))) {
free(table, M_TEMP);
free(ioe, M_TEMP);
goto fail;
}
break;
#endif /* ALTQ */
case PF_RULESET_TABLE:
bzero(table, sizeof(*table));
strlcpy(table->pfrt_anchor, ioe->anchor,
sizeof(table->pfrt_anchor));
if ((error = pfr_ina_begin(table,
&ioe->ticket, NULL, 0))) {
free(table, M_TEMP);
free(ioe, M_TEMP);
goto fail;
}
break;
default:
if ((error = pf_begin_rules(&ioe->ticket,
ioe->rs_num, ioe->anchor))) {
free(table, M_TEMP);
free(ioe, M_TEMP);
goto fail;
}
break;
}
#ifdef __FreeBSD__
PF_COPYOUT(ioe, io->array+i, sizeof(io->array[i]),
error);
if (error) {
#else
if (copyout(ioe, io->array+i, sizeof(io->array[i]))) {
#endif
free(table, M_TEMP);
free(ioe, M_TEMP);
error = EFAULT;
goto fail;
}
}
free(table, M_TEMP);
free(ioe, M_TEMP);
break;
}
case DIOCXROLLBACK: {
struct pfioc_trans *io = (struct pfioc_trans *)addr;
struct pfioc_trans_e *ioe;
struct pfr_table *table;
int i;
if (io->esize != sizeof(*ioe)) {
error = ENODEV;
goto fail;
}
#ifdef __FreeBSD__
PF_UNLOCK();
#endif
ioe = (struct pfioc_trans_e *)malloc(sizeof(*ioe),
M_TEMP, M_WAITOK);
table = (struct pfr_table *)malloc(sizeof(*table),
M_TEMP, M_WAITOK);
#ifdef __FreeBSD__
PF_LOCK();
#endif
for (i = 0; i < io->size; i++) {
#ifdef __FreeBSD__
PF_COPYIN(io->array+i, ioe, sizeof(*ioe), error);
if (error) {
#else
if (copyin(io->array+i, ioe, sizeof(*ioe))) {
#endif
free(table, M_TEMP);
free(ioe, M_TEMP);
error = EFAULT;
goto fail;
}
switch (ioe->rs_num) {
#ifdef ALTQ
case PF_RULESET_ALTQ:
if (ioe->anchor[0]) {
free(table, M_TEMP);
free(ioe, M_TEMP);
error = EINVAL;
goto fail;
}
if ((error = pf_rollback_altq(ioe->ticket))) {
free(table, M_TEMP);
free(ioe, M_TEMP);
goto fail; /* really bad */
}
break;
#endif /* ALTQ */
case PF_RULESET_TABLE:
bzero(table, sizeof(*table));
strlcpy(table->pfrt_anchor, ioe->anchor,
sizeof(table->pfrt_anchor));
if ((error = pfr_ina_rollback(table,
ioe->ticket, NULL, 0))) {
free(table, M_TEMP);
free(ioe, M_TEMP);
goto fail; /* really bad */
}
break;
default:
if ((error = pf_rollback_rules(ioe->ticket,
ioe->rs_num, ioe->anchor))) {
free(table, M_TEMP);
free(ioe, M_TEMP);
goto fail; /* really bad */
}
break;
}
}
free(table, M_TEMP);
free(ioe, M_TEMP);
break;
}
case DIOCXCOMMIT: {
struct pfioc_trans *io = (struct pfioc_trans *)addr;
struct pfioc_trans_e *ioe;
struct pfr_table *table;
struct pf_ruleset *rs;
int i;
if (io->esize != sizeof(*ioe)) {
error = ENODEV;
goto fail;
}
#ifdef __FreeBSD__
PF_UNLOCK();
#endif
ioe = (struct pfioc_trans_e *)malloc(sizeof(*ioe),
M_TEMP, M_WAITOK);
table = (struct pfr_table *)malloc(sizeof(*table),
M_TEMP, M_WAITOK);
#ifdef __FreeBSD__
PF_LOCK();
#endif
/* first makes sure everything will succeed */
for (i = 0; i < io->size; i++) {
#ifdef __FreeBSD__
PF_COPYIN(io->array+i, ioe, sizeof(*ioe), error);
if (error) {
#else
if (copyin(io->array+i, ioe, sizeof(*ioe))) {
#endif
free(table, M_TEMP);
free(ioe, M_TEMP);
error = EFAULT;
goto fail;
}
switch (ioe->rs_num) {
#ifdef ALTQ
case PF_RULESET_ALTQ:
if (ioe->anchor[0]) {
free(table, M_TEMP);
free(ioe, M_TEMP);
error = EINVAL;
goto fail;
}
if (!altqs_inactive_open || ioe->ticket !=
ticket_altqs_inactive) {
free(table, M_TEMP);
free(ioe, M_TEMP);
error = EBUSY;
goto fail;
}
break;
#endif /* ALTQ */
case PF_RULESET_TABLE:
rs = pf_find_ruleset(ioe->anchor);
if (rs == NULL || !rs->topen || ioe->ticket !=
rs->tticket) {
free(table, M_TEMP);
free(ioe, M_TEMP);
error = EBUSY;
goto fail;
}
break;
default:
if (ioe->rs_num < 0 || ioe->rs_num >=
PF_RULESET_MAX) {
free(table, M_TEMP);
free(ioe, M_TEMP);
error = EINVAL;
goto fail;
}
rs = pf_find_ruleset(ioe->anchor);
if (rs == NULL ||
!rs->rules[ioe->rs_num].inactive.open ||
rs->rules[ioe->rs_num].inactive.ticket !=
ioe->ticket) {
free(table, M_TEMP);
free(ioe, M_TEMP);
error = EBUSY;
goto fail;
}
break;
}
}
/* now do the commit - no errors should happen here */
for (i = 0; i < io->size; i++) {
#ifdef __FreeBSD__
PF_COPYIN(io->array+i, ioe, sizeof(*ioe), error);
if (error) {
#else
if (copyin(io->array+i, ioe, sizeof(*ioe))) {
#endif
free(table, M_TEMP);
free(ioe, M_TEMP);
error = EFAULT;
goto fail;
}
switch (ioe->rs_num) {
#ifdef ALTQ
case PF_RULESET_ALTQ:
if ((error = pf_commit_altq(ioe->ticket))) {
free(table, M_TEMP);
free(ioe, M_TEMP);
goto fail; /* really bad */
}
break;
#endif /* ALTQ */
case PF_RULESET_TABLE:
bzero(table, sizeof(*table));
strlcpy(table->pfrt_anchor, ioe->anchor,
sizeof(table->pfrt_anchor));
if ((error = pfr_ina_commit(table, ioe->ticket,
NULL, NULL, 0))) {
free(table, M_TEMP);
free(ioe, M_TEMP);
goto fail; /* really bad */
}
break;
default:
if ((error = pf_commit_rules(ioe->ticket,
ioe->rs_num, ioe->anchor))) {
free(table, M_TEMP);
free(ioe, M_TEMP);
goto fail; /* really bad */
}
break;
}
}
free(table, M_TEMP);
free(ioe, M_TEMP);
break;
}
case DIOCGETSRCNODES: {
struct pfioc_src_nodes *psn = (struct pfioc_src_nodes *)addr;
struct pf_src_node *n, *p, *pstore;
u_int32_t nr = 0;
int space = psn->psn_len;
if (space == 0) {
RB_FOREACH(n, pf_src_tree, &tree_src_tracking)
nr++;
psn->psn_len = sizeof(struct pf_src_node) * nr;
break;
}
#ifdef __FreeBSD__
PF_UNLOCK();
#endif
pstore = malloc(sizeof(*pstore), M_TEMP, M_WAITOK);
#ifdef __FreeBSD__
PF_LOCK();
#endif
p = psn->psn_src_nodes;
RB_FOREACH(n, pf_src_tree, &tree_src_tracking) {
int secs = time_second, diff;
if ((nr + 1) * sizeof(*p) > (unsigned)psn->psn_len)
break;
bcopy(n, pstore, sizeof(*pstore));
if (n->rule.ptr != NULL)
pstore->rule.nr = n->rule.ptr->nr;
pstore->creation = secs - pstore->creation;
if (pstore->expire > secs)
pstore->expire -= secs;
else
pstore->expire = 0;
/* adjust the connection rate estimate */
diff = secs - n->conn_rate.last;
if (diff >= n->conn_rate.seconds)
pstore->conn_rate.count = 0;
else
pstore->conn_rate.count -=
n->conn_rate.count * diff /
n->conn_rate.seconds;
#ifdef __FreeBSD__
PF_COPYOUT(pstore, p, sizeof(*p), error);
#else
error = copyout(pstore, p, sizeof(*p));
#endif
if (error) {
free(pstore, M_TEMP);
goto fail;
}
p++;
nr++;
}
psn->psn_len = sizeof(struct pf_src_node) * nr;
free(pstore, M_TEMP);
break;
}
case DIOCCLRSRCNODES: {
struct pf_src_node *n;
struct pf_state *state;
RB_FOREACH(state, pf_state_tree_id, &tree_id) {
state->src_node = NULL;
state->nat_src_node = NULL;
}
RB_FOREACH(n, pf_src_tree, &tree_src_tracking) {
n->expire = 1;
n->states = 0;
}
pf_purge_expired_src_nodes(1);
pf_status.src_nodes = 0;
break;
}
case DIOCKILLSRCNODES: {
struct pf_src_node *sn;
struct pf_state *s;
struct pfioc_src_node_kill *psnk = \
(struct pfioc_src_node_kill *) addr;
int killed = 0;
RB_FOREACH(sn, pf_src_tree, &tree_src_tracking) {
if (PF_MATCHA(psnk->psnk_src.neg, \
&psnk->psnk_src.addr.v.a.addr, \
&psnk->psnk_src.addr.v.a.mask, \
&sn->addr, sn->af) &&
PF_MATCHA(psnk->psnk_dst.neg, \
&psnk->psnk_dst.addr.v.a.addr, \
&psnk->psnk_dst.addr.v.a.mask, \
&sn->raddr, sn->af)) {
/* Handle state to src_node linkage */
if (sn->states != 0) {
RB_FOREACH(s, pf_state_tree_id,
&tree_id) {
if (s->src_node == sn)
s->src_node = NULL;
if (s->nat_src_node == sn)
s->nat_src_node = NULL;
}
sn->states = 0;
}
sn->expire = 1;
killed++;
}
}
if (killed > 0)
pf_purge_expired_src_nodes(1);
psnk->psnk_af = killed;
break;
}
case DIOCSETHOSTID: {
u_int32_t *hostid = (u_int32_t *)addr;
if (*hostid == 0)
pf_status.hostid = arc4random();
else
pf_status.hostid = *hostid;
break;
}
case DIOCOSFPFLUSH:
pf_osfp_flush();
break;
case DIOCIGETIFACES: {
struct pfioc_iface *io = (struct pfioc_iface *)addr;
if (io->pfiio_esize != sizeof(struct pfi_kif)) {
error = ENODEV;
break;
}
error = pfi_get_ifaces(io->pfiio_name, io->pfiio_buffer,
&io->pfiio_size);
break;
}
case DIOCSETIFFLAG: {
struct pfioc_iface *io = (struct pfioc_iface *)addr;
error = pfi_set_flags(io->pfiio_name, io->pfiio_flags);
break;
}
case DIOCCLRIFFLAG: {
struct pfioc_iface *io = (struct pfioc_iface *)addr;
error = pfi_clear_flags(io->pfiio_name, io->pfiio_flags);
break;
}
default:
error = ENODEV;
break;
}
fail:
#ifdef __FreeBSD__
PF_UNLOCK();
if (flags & FWRITE)
sx_xunlock(&pf_consistency_lock);
else
sx_sunlock(&pf_consistency_lock);
#else
splx(s);
/* XXX: Lock order? */
if (flags & FWRITE)
rw_exit_write(&pf_consistency_lock);
else
rw_exit_read(&pf_consistency_lock);
#endif
return (error);
}
#ifdef __FreeBSD__
/*
* XXX - Check for version missmatch!!!
*/
static void
pf_clear_states(void)
{
struct pf_state *state;
RB_FOREACH(state, pf_state_tree_id, &tree_id) {
state->timeout = PFTM_PURGE;
#if NPFSYNC
/* don't send out individual delete messages */
state->sync_flags = PFSTATE_NOSYNC;
#endif
pf_unlink_state(state);
}
#if 0 /* NPFSYNC */
/*
* XXX This is called on module unload, we do not want to sync that over? */
*/
pfsync_clear_states(pf_status.hostid, psk->psk_ifname);
#endif
}
static int
pf_clear_tables(void)
{
struct pfioc_table io;
int error;
bzero(&io, sizeof(io));
error = pfr_clr_tables(&io.pfrio_table, &io.pfrio_ndel,
io.pfrio_flags);
return (error);
}
static void
pf_clear_srcnodes(void)
{
struct pf_src_node *n;
struct pf_state *state;
RB_FOREACH(state, pf_state_tree_id, &tree_id) {
state->src_node = NULL;
state->nat_src_node = NULL;
}
RB_FOREACH(n, pf_src_tree, &tree_src_tracking) {
n->expire = 1;
n->states = 0;
}
}
/*
* XXX - Check for version missmatch!!!
*/
/*
* Duplicate pfctl -Fa operation to get rid of as much as we can.
*/
static int
shutdown_pf(void)
{
int error = 0;
u_int32_t t[5];
char nn = '\0';
pf_status.running = 0;
do {
if ((error = pf_begin_rules(&t[0], PF_RULESET_SCRUB, &nn))
!= 0) {
DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: SCRUB\n"));
break;
}
if ((error = pf_begin_rules(&t[1], PF_RULESET_FILTER, &nn))
!= 0) {
DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: FILTER\n"));
break; /* XXX: rollback? */
}
if ((error = pf_begin_rules(&t[2], PF_RULESET_NAT, &nn))
!= 0) {
DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: NAT\n"));
break; /* XXX: rollback? */
}
if ((error = pf_begin_rules(&t[3], PF_RULESET_BINAT, &nn))
!= 0) {
DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: BINAT\n"));
break; /* XXX: rollback? */
}
if ((error = pf_begin_rules(&t[4], PF_RULESET_RDR, &nn))
!= 0) {
DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: RDR\n"));
break; /* XXX: rollback? */
}
/* XXX: these should always succeed here */
pf_commit_rules(t[0], PF_RULESET_SCRUB, &nn);
pf_commit_rules(t[1], PF_RULESET_FILTER, &nn);
pf_commit_rules(t[2], PF_RULESET_NAT, &nn);
pf_commit_rules(t[3], PF_RULESET_BINAT, &nn);
pf_commit_rules(t[4], PF_RULESET_RDR, &nn);
if ((error = pf_clear_tables()) != 0)
break;
#ifdef ALTQ
if ((error = pf_begin_altq(&t[0])) != 0) {
DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: ALTQ\n"));
break;
}
pf_commit_altq(t[0]);
#endif
pf_clear_states();
pf_clear_srcnodes();
/* status does not use malloced mem so no need to cleanup */
/* fingerprints and interfaces have thier own cleanup code */
} while(0);
return (error);
}
static int
pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
struct inpcb *inp)
{
/*
* XXX Wed Jul 9 22:03:16 2003 UTC
* OpenBSD has changed its byte ordering convention on ip_len/ip_off
* in network stack. OpenBSD's network stack have converted
* ip_len/ip_off to host byte order frist as FreeBSD.
* Now this is not true anymore , so we should convert back to network
* byte order.
*/
struct ip *h = NULL;
int chk;
if ((*m)->m_pkthdr.len >= (int)sizeof(struct ip)) {
/* if m_pkthdr.len is less than ip header, pf will handle. */
h = mtod(*m, struct ip *);
HTONS(h->ip_len);
HTONS(h->ip_off);
}
chk = pf_test(PF_IN, ifp, m, NULL, inp);
if (chk && *m) {
m_freem(*m);
*m = NULL;
}
if (*m != NULL) {
/* pf_test can change ip header location */
h = mtod(*m, struct ip *);
NTOHS(h->ip_len);
NTOHS(h->ip_off);
}
return chk;
}
static int
pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
struct inpcb *inp)
{
/*
* XXX Wed Jul 9 22:03:16 2003 UTC
* OpenBSD has changed its byte ordering convention on ip_len/ip_off
* in network stack. OpenBSD's network stack have converted
* ip_len/ip_off to host byte order frist as FreeBSD.
* Now this is not true anymore , so we should convert back to network
* byte order.
*/
struct ip *h = NULL;
int chk;
/* We need a proper CSUM befor we start (s. OpenBSD ip_output) */
if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
in_delayed_cksum(*m);
(*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
}
if ((*m)->m_pkthdr.len >= (int)sizeof(*h)) {
/* if m_pkthdr.len is less than ip header, pf will handle. */
h = mtod(*m, struct ip *);
HTONS(h->ip_len);
HTONS(h->ip_off);
}
chk = pf_test(PF_OUT, ifp, m, NULL, inp);
if (chk && *m) {
m_freem(*m);
*m = NULL;
}
if (*m != NULL) {
/* pf_test can change ip header location */
h = mtod(*m, struct ip *);
NTOHS(h->ip_len);
NTOHS(h->ip_off);
}
return chk;
}
#ifdef INET6
static int
pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
struct inpcb *inp)
{
INIT_VNET_NET(curvnet);
/*
* IPv6 is not affected by ip_len/ip_off byte order changes.
*/
int chk;
/*
* In case of loopback traffic IPv6 uses the real interface in
* order to support scoped addresses. In order to support stateful
* filtering we have change this to lo0 as it is the case in IPv4.
*/
chk = pf_test6(PF_IN, (*m)->m_flags & M_LOOP ? &V_loif[0] : ifp, m,
NULL, inp);
if (chk && *m) {
m_freem(*m);
*m = NULL;
}
return chk;
}
static int
pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
struct inpcb *inp)
{
/*
* IPv6 does not affected ip_len/ip_off byte order changes.
*/
int chk;
/* We need a proper CSUM befor we start (s. OpenBSD ip_output) */
if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
in_delayed_cksum(*m);
(*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
}
chk = pf_test6(PF_OUT, ifp, m, NULL, inp);
if (chk && *m) {
m_freem(*m);
*m = NULL;
}
return chk;
}
#endif /* INET6 */
static int
hook_pf(void)
{
struct pfil_head *pfh_inet;
#ifdef INET6
struct pfil_head *pfh_inet6;
#endif
PF_ASSERT(MA_NOTOWNED);
if (pf_pfil_hooked)
return (0);
pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
if (pfh_inet == NULL)
return (ESRCH); /* XXX */
pfil_add_hook(pf_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
pfil_add_hook(pf_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
#ifdef INET6
pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
if (pfh_inet6 == NULL) {
pfil_remove_hook(pf_check_in, NULL, PFIL_IN | PFIL_WAITOK,
pfh_inet);
pfil_remove_hook(pf_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
pfh_inet);
return (ESRCH); /* XXX */
}
pfil_add_hook(pf_check6_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6);
pfil_add_hook(pf_check6_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6);
#endif
pf_pfil_hooked = 1;
return (0);
}
static int
dehook_pf(void)
{
struct pfil_head *pfh_inet;
#ifdef INET6
struct pfil_head *pfh_inet6;
#endif
PF_ASSERT(MA_NOTOWNED);
if (pf_pfil_hooked == 0)
return (0);
pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
if (pfh_inet == NULL)
return (ESRCH); /* XXX */
pfil_remove_hook(pf_check_in, NULL, PFIL_IN | PFIL_WAITOK,
pfh_inet);
pfil_remove_hook(pf_check_out, NULL, PFIL_OUT | PFIL_WAITOK,
pfh_inet);
#ifdef INET6
pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
if (pfh_inet6 == NULL)
return (ESRCH); /* XXX */
pfil_remove_hook(pf_check6_in, NULL, PFIL_IN | PFIL_WAITOK,
pfh_inet6);
pfil_remove_hook(pf_check6_out, NULL, PFIL_OUT | PFIL_WAITOK,
pfh_inet6);
#endif
pf_pfil_hooked = 0;
return (0);
}
static int
pf_load(void)
{
init_zone_var();
init_pf_mutex();
pf_dev = make_dev(&pf_cdevsw, 0, 0, 0, 0600, PF_NAME);
if (pfattach() < 0) {
destroy_dev(pf_dev);
destroy_pf_mutex();
return (ENOMEM);
}
return (0);
}
static int
pf_unload(void)
{
int error = 0;
PF_LOCK();
pf_status.running = 0;
PF_UNLOCK();
error = dehook_pf();
if (error) {
/*
* Should not happen!
* XXX Due to error code ESRCH, kldunload will show
* a message like 'No such process'.
*/
printf("%s : pfil unregisteration fail\n", __FUNCTION__);
return error;
}
PF_LOCK();
shutdown_pf();
pf_end_threads = 1;
while (pf_end_threads < 2) {
wakeup_one(pf_purge_thread);
msleep(pf_purge_thread, &pf_task_mtx, 0, "pftmo", hz);
}
pfi_cleanup();
pf_osfp_flush();
pf_osfp_cleanup();
cleanup_pf_zone();
PF_UNLOCK();
destroy_dev(pf_dev);
destroy_pf_mutex();
return error;
}
static int
pf_modevent(module_t mod, int type, void *data)
{
int error = 0;
switch(type) {
case MOD_LOAD:
error = pf_load();
break;
case MOD_UNLOAD:
error = pf_unload();
break;
default:
error = EINVAL;
break;
}
return error;
}
static moduledata_t pf_mod = {
"pf",
pf_modevent,
0
};
DECLARE_MODULE(pf, pf_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_FIRST);
MODULE_VERSION(pf, PF_MODVER);
#endif /* __FreeBSD__ */