X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/b0d623f7f2ae71ed96e60569f61f9a9a27016e80..fe8ab488e9161c46dd9885d58fc52996dc0249ff:/bsd/net/pf_ioctl.c diff --git a/bsd/net/pf_ioctl.c b/bsd/net/pf_ioctl.c index 8145fed94..bfad2e2ae 100644 --- a/bsd/net/pf_ioctl.c +++ b/bsd/net/pf_ioctl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2007-2014 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -26,7 +26,7 @@ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ -/* $apfw: pf_ioctl.c,v 1.16 2008/08/27 00:01:32 jhw Exp $ */ +/* $apfw: git commit b6bf13f8321283cd7ee82b1795e86506084b1b95 $ */ /* $OpenBSD: pf_ioctl.c,v 1.175 2007/02/26 22:47:43 deraadt Exp $ */ /* @@ -79,9 +79,11 @@ #include #include #include +#include #include +#include #include #include #include @@ -94,8 +96,16 @@ #include #include +#if DUMMYNET +#include +#else +struct ip_fw_args; +#endif /* DUMMYNET */ + #include +#include + #include #include @@ -113,9 +123,16 @@ #include #endif /* INET6 */ -#if ALTQ -#include -#endif /* ALTQ */ +#if PF_ALTQ +#include +#include +#include +#include +#include +#include +#endif /* PF_ALTQ */ + +#include #if 0 static void pfdetach(void); @@ -123,32 +140,68 @@ static void pfdetach(void); static int pfopen(dev_t, int, int, struct proc *); static int pfclose(dev_t, int, int, struct proc *); static int pfioctl(dev_t, u_long, caddr_t, int, struct proc *); +static int pfioctl_ioc_table(u_long, struct pfioc_table_32 *, + struct pfioc_table_64 *, struct proc *); +static int pfioctl_ioc_tokens(u_long, struct pfioc_tokens_32 *, + struct pfioc_tokens_64 *, struct proc *); +static int pfioctl_ioc_rule(u_long, int, struct pfioc_rule *, struct proc *); +static int pfioctl_ioc_state_kill(u_long, struct pfioc_state_kill *, + struct proc *); +static int pfioctl_ioc_state(u_long, struct pfioc_state *, struct proc *); +static int pfioctl_ioc_states(u_long, struct pfioc_states_32 *, + struct pfioc_states_64 *, struct proc *); +static int pfioctl_ioc_natlook(u_long, struct pfioc_natlook *, struct proc *); +static int pfioctl_ioc_tm(u_long, struct pfioc_tm *, struct proc *); +static int pfioctl_ioc_limit(u_long, struct pfioc_limit *, struct proc *); +static int pfioctl_ioc_pooladdr(u_long, struct pfioc_pooladdr *, struct proc *); +static int pfioctl_ioc_ruleset(u_long, struct pfioc_ruleset *, struct proc *); +static int pfioctl_ioc_trans(u_long, struct pfioc_trans_32 *, + struct pfioc_trans_64 *, struct proc *); +static int pfioctl_ioc_src_nodes(u_long, struct pfioc_src_nodes_32 *, + struct pfioc_src_nodes_64 *, struct proc *); +static int pfioctl_ioc_src_node_kill(u_long, struct pfioc_src_node_kill *, + struct proc *); +static int pfioctl_ioc_iface(u_long, struct pfioc_iface_32 *, + struct pfioc_iface_64 *, struct proc *); static 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); - static void pf_mv_pool(struct pf_palist *, struct pf_palist *); static void pf_empty_pool(struct pf_palist *); -#if ALTQ +#if PF_ALTQ static int pf_begin_altq(u_int32_t *); static int pf_rollback_altq(u_int32_t); static int pf_commit_altq(u_int32_t); static int pf_enable_altq(struct pf_altq *); static int pf_disable_altq(struct pf_altq *); -#endif /* ALTQ */ +static void pf_altq_copyin(struct pf_altq *, struct pf_altq *); +static void pf_altq_copyout(struct pf_altq *, struct pf_altq *); +#endif /* PF_ALTQ */ static int pf_begin_rules(u_int32_t *, int, const char *); static int pf_rollback_rules(u_int32_t, int, char *); static int pf_setup_pfsync_matching(struct pf_ruleset *); static void pf_hash_rule(MD5_CTX *, struct pf_rule *); -#ifndef NO_APPLE_EXTENSIONS static void pf_hash_rule_addr(MD5_CTX *, struct pf_rule_addr *, u_int8_t); -#else -static void pf_hash_rule_addr(MD5_CTX *, struct pf_rule_addr *); -#endif static int pf_commit_rules(u_int32_t, int, char *); +static void pf_rule_copyin(struct pf_rule *, struct pf_rule *, struct proc *, + int); +static void pf_rule_copyout(struct pf_rule *, struct pf_rule *); static void pf_state_export(struct pfsync_state *, struct pf_state_key *, struct pf_state *); static void pf_state_import(struct pfsync_state *, struct pf_state_key *, struct pf_state *); +static void pf_pooladdr_copyin(struct pf_pooladdr *, struct pf_pooladdr *); +static void pf_pooladdr_copyout(struct pf_pooladdr *, struct pf_pooladdr *); +static void pf_expire_states_and_src_nodes(struct pf_rule *); +static void pf_delete_rule_from_ruleset(struct pf_ruleset *, + int, struct pf_rule *); +static void pf_addrwrap_setup(struct pf_addr_wrap *); +static int pf_rule_setup(struct pfioc_rule *, struct pf_rule *, + struct pf_ruleset *); +static void pf_delete_rule_by_owner(char *, u_int32_t); +static int pf_delete_rule_by_ticket(struct pfioc_rule *, u_int32_t); +static void pf_ruleset_cleanup(struct pf_ruleset *, int); +static void pf_deleterule_anchor_step_out(struct pf_ruleset **, + int, struct pf_rule **); #define PF_CDEV_MAJOR (-1) @@ -170,21 +223,49 @@ static struct cdevsw pf_cdevsw = { }; static void pf_attach_hooks(void); +#if 0 +/* currently unused along with pfdetach() */ static void pf_detach_hooks(void); -static int pf_hooks_attached = 0; +#endif + +/* + * This is set during DIOCSTART/DIOCSTOP with pf_perim_lock held as writer, + * and used in pf_af_hook() for performance optimization, such that packets + * will enter pf_test() or pf_test6() only when PF is running. + */ +int pf_is_enabled = 0; + +#if PF_ALTQ +u_int32_t altq_allowed = 0; +#endif /* PF_ALTQ */ + +u_int32_t pf_hash_seed; + +/* + * These are the pf enabled reference counting variables + */ +static u_int64_t pf_enabled_ref_count; +static u_int32_t nr_tokens = 0; +static u_int64_t pffwrules; +static u_int32_t pfdevcnt; + +SLIST_HEAD(list_head, pfioc_kernel_token); +static struct list_head token_list_head; struct pf_rule pf_default_rule; -#if ALTQ +#if PF_ALTQ static int pf_altq_running; -#endif /* ALTQ */ +#endif /* PF_ALTQ */ #define TAGID_MAX 50000 +#if !PF_ALTQ static TAILQ_HEAD(pf_tags, pf_tagname) pf_tags = TAILQ_HEAD_INITIALIZER(pf_tags); -#if ALTQ -static TAILQ_HEAD(pf_tags, pf_tagname) pf_qids = - TAILQ_HEAD_INITIALIZER(pf_qids); -#endif /* ALTQ */ +#else /* PF_ALTQ */ +static TAILQ_HEAD(pf_tags, pf_tagname) + pf_tags = TAILQ_HEAD_INITIALIZER(pf_tags), + pf_qids = TAILQ_HEAD_INITIALIZER(pf_qids); +#endif /* PF_ALTQ */ #if (PF_QNAME_SIZE != PF_TAG_NAME_SIZE) #error PF_QNAME_SIZE must be equal to PF_TAG_NAME_SIZE @@ -197,13 +278,77 @@ static void pf_rtlabel_remove(struct pf_addr_wrap *); static void pf_rtlabel_copyout(struct pf_addr_wrap *); #if INET -static int pf_inet_hook(struct ifnet *, struct mbuf **, int); +static int pf_inet_hook(struct ifnet *, struct mbuf **, int, + struct ip_fw_args *); #endif /* INET */ #if INET6 -static int pf_inet6_hook(struct ifnet *, struct mbuf **, int); +static int pf_inet6_hook(struct ifnet *, struct mbuf **, int, + struct ip_fw_args *); #endif /* INET6 */ -#define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x +#define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x + +/* + * Helper macros for ioctl structures which vary in size (32-bit vs. 64-bit) + */ +#define PFIOCX_STRUCT_DECL(s) \ +struct { \ + union { \ + struct s##_32 _s##_32; \ + struct s##_64 _s##_64; \ + } _u; \ +} *s##_un = NULL \ + +#define PFIOCX_STRUCT_BEGIN(a, s, _action) { \ + VERIFY(s##_un == NULL); \ + s##_un = _MALLOC(sizeof (*s##_un), M_TEMP, M_WAITOK|M_ZERO); \ + if (s##_un == NULL) { \ + _action \ + } else { \ + if (p64) \ + bcopy(a, &s##_un->_u._s##_64, \ + sizeof (struct s##_64)); \ + else \ + bcopy(a, &s##_un->_u._s##_32, \ + sizeof (struct s##_32)); \ + } \ +} + +#define PFIOCX_STRUCT_END(s, a) { \ + VERIFY(s##_un != NULL); \ + if (p64) \ + bcopy(&s##_un->_u._s##_64, a, sizeof (struct s##_64)); \ + else \ + bcopy(&s##_un->_u._s##_32, a, sizeof (struct s##_32)); \ + _FREE(s##_un, M_TEMP); \ + s##_un = NULL; \ +} + +#define PFIOCX_STRUCT_ADDR32(s) (&s##_un->_u._s##_32) +#define PFIOCX_STRUCT_ADDR64(s) (&s##_un->_u._s##_64) + +/* + * Helper macros for regular ioctl structures. + */ +#define PFIOC_STRUCT_BEGIN(a, v, _action) { \ + VERIFY((v) == NULL); \ + (v) = _MALLOC(sizeof (*(v)), M_TEMP, M_WAITOK|M_ZERO); \ + if ((v) == NULL) { \ + _action \ + } else { \ + bcopy(a, v, sizeof (*(v))); \ + } \ +} + +#define PFIOC_STRUCT_END(v, a) { \ + VERIFY((v) != NULL); \ + bcopy(v, a, sizeof (*(v))); \ + _FREE(v, M_TEMP); \ + (v) = NULL; \ +} + +#define PFIOC_STRUCT_ADDR32(s) (&s##_un->_u._s##_32) +#define PFIOC_STRUCT_ADDR64(s) (&s##_un->_u._s##_64) static lck_attr_t *pf_perim_lock_attr; static lck_grp_t *pf_perim_lock_grp; @@ -217,6 +362,78 @@ struct thread *pf_purge_thread; extern void pfi_kifaddr_update(void *); +/* pf enable ref-counting helper functions */ +static u_int64_t generate_token(struct proc *); +static int remove_token(struct pfioc_remove_token *); +static void invalidate_all_tokens(void); + +static u_int64_t +generate_token(struct proc *p) +{ + u_int64_t token_value; + struct pfioc_kernel_token *new_token; + + new_token = _MALLOC(sizeof (struct pfioc_kernel_token), M_TEMP, + M_WAITOK|M_ZERO); + + lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED); + + if (new_token == NULL) { + /* malloc failed! bail! */ + printf("%s: unable to allocate pf token structure!", __func__); + return (0); + } + + token_value = VM_KERNEL_ADDRPERM((u_int64_t)(uintptr_t)new_token); + + new_token->token.token_value = token_value; + new_token->token.pid = proc_pid(p); + proc_name(new_token->token.pid, new_token->token.proc_name, + sizeof (new_token->token.proc_name)); + new_token->token.timestamp = pf_calendar_time_second(); + + SLIST_INSERT_HEAD(&token_list_head, new_token, next); + nr_tokens++; + + return (token_value); +} + +static int +remove_token(struct pfioc_remove_token *tok) +{ + struct pfioc_kernel_token *entry, *tmp; + + lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED); + + SLIST_FOREACH_SAFE(entry, &token_list_head, next, tmp) { + if (tok->token_value == entry->token.token_value) { + SLIST_REMOVE(&token_list_head, entry, + pfioc_kernel_token, next); + _FREE(entry, M_TEMP); + nr_tokens--; + return (0); /* success */ + } + } + + printf("pf : remove failure\n"); + return (ESRCH); /* failure */ +} + +static void +invalidate_all_tokens(void) +{ + struct pfioc_kernel_token *entry, *tmp; + + lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED); + + SLIST_FOREACH_SAFE(entry, &token_list_head, next, tmp) { + SLIST_REMOVE(&token_list_head, entry, pfioc_kernel_token, next); + _FREE(entry, M_TEMP); + } + + nr_tokens = 0; +} + void pfinit(void) { @@ -227,13 +444,12 @@ pfinit(void) pf_perim_lock_grp = lck_grp_alloc_init("pf_perim", pf_perim_lock_grp_attr); pf_perim_lock_attr = lck_attr_alloc_init(); - pf_perim_lock = lck_rw_alloc_init(pf_perim_lock_grp, - pf_perim_lock_attr); + lck_rw_init(pf_perim_lock, pf_perim_lock_grp, pf_perim_lock_attr); pf_lock_grp_attr = lck_grp_attr_alloc_init(); pf_lock_grp = lck_grp_alloc_init("pf", pf_lock_grp_attr); pf_lock_attr = lck_attr_alloc_init(); - pf_lock = lck_mtx_alloc_init(pf_lock_grp, pf_lock_attr); + lck_mtx_init(pf_lock, pf_lock_grp, pf_lock_attr); pool_init(&pf_rule_pl, sizeof (struct pf_rule), 0, 0, 0, "pfrulepl", NULL); @@ -243,14 +459,12 @@ pfinit(void) NULL); pool_init(&pf_state_key_pl, sizeof (struct pf_state_key), 0, 0, 0, "pfstatekeypl", NULL); -#ifndef NO_APPLE_EXTENSIONS pool_init(&pf_app_state_pl, sizeof (struct pf_app_state), 0, 0, 0, "pfappstatepl", NULL); -#endif -#if ALTQ +#if PF_ALTQ pool_init(&pf_altq_pl, sizeof (struct pf_altq), 0, 0, 0, "pfaltqpl", NULL); -#endif /* ALTQ */ +#endif /* PF_ALTQ */ pool_init(&pf_pooladdr_pl, sizeof (struct pf_pooladdr), 0, 0, 0, "pfpooladdrpl", NULL); pfr_initialize(); @@ -269,12 +483,32 @@ pfinit(void) pf_init_ruleset(&pf_main_ruleset); TAILQ_INIT(&pf_pabuf); TAILQ_INIT(&state_list); -#if ALTQ +#if PF_ALTQ TAILQ_INIT(&pf_altqs[0]); TAILQ_INIT(&pf_altqs[1]); pf_altqs_active = &pf_altqs[0]; pf_altqs_inactive = &pf_altqs[1]; -#endif /* ALTQ */ + + PE_parse_boot_argn("altq", &altq_allowed, sizeof (altq_allowed)); + + _CASSERT(ALTRQ_PURGE == CLASSQRQ_PURGE); + _CASSERT(ALTRQ_PURGE_SC == CLASSQRQ_PURGE_SC); + _CASSERT(ALTRQ_EVENT == CLASSQRQ_EVENT); + + _CASSERT(ALTDQ_REMOVE == CLASSQDQ_REMOVE); + _CASSERT(ALTDQ_POLL == CLASSQDQ_POLL); +#endif /* PF_ALTQ */ + + _CASSERT((SC_BE & SCIDX_MASK) == SCIDX_BE); + _CASSERT((SC_BK_SYS & SCIDX_MASK) == SCIDX_BK_SYS); + _CASSERT((SC_BK & SCIDX_MASK) == SCIDX_BK); + _CASSERT((SC_RD & SCIDX_MASK) == SCIDX_RD); + _CASSERT((SC_OAM & SCIDX_MASK) == SCIDX_OAM); + _CASSERT((SC_AV & SCIDX_MASK) == SCIDX_AV); + _CASSERT((SC_RV & SCIDX_MASK) == SCIDX_RV); + _CASSERT((SC_VI & SCIDX_MASK) == SCIDX_VI); + _CASSERT((SC_VO & SCIDX_MASK) == SCIDX_VO); + _CASSERT((SC_CTL & SCIDX_MASK) == SCIDX_CTL); /* default rule should never be garbage collected */ pf_default_rule.entries.tqe_prev = &pf_default_rule.entries.tqe_next; @@ -294,14 +528,12 @@ pfinit(void) t[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL; t[PFTM_ICMP_FIRST_PACKET] = PFTM_ICMP_FIRST_PACKET_VAL; t[PFTM_ICMP_ERROR_REPLY] = PFTM_ICMP_ERROR_REPLY_VAL; -#ifndef NO_APPLE_EXTENSIONS t[PFTM_GREv1_FIRST_PACKET] = PFTM_GREv1_FIRST_PACKET_VAL; t[PFTM_GREv1_INITIATING] = PFTM_GREv1_INITIATING_VAL; t[PFTM_GREv1_ESTABLISHED] = PFTM_GREv1_ESTABLISHED_VAL; t[PFTM_ESP_FIRST_PACKET] = PFTM_ESP_FIRST_PACKET_VAL; t[PFTM_ESP_INITIATING] = PFTM_ESP_INITIATING_VAL; t[PFTM_ESP_ESTABLISHED] = PFTM_ESP_ESTABLISHED_VAL; -#endif t[PFTM_OTHER_FIRST_PACKET] = PFTM_OTHER_FIRST_PACKET_VAL; t[PFTM_OTHER_SINGLE] = PFTM_OTHER_SINGLE_VAL; t[PFTM_OTHER_MULTIPLE] = PFTM_OTHER_MULTIPLE_VAL; @@ -315,6 +547,7 @@ pfinit(void) pf_normalize_init(); bzero(&pf_status, sizeof (pf_status)); pf_status.debug = PF_DEBUG_URGENT; + pf_hash_seed = RandomULong(); /* XXX do our best to avoid a conflict */ pf_status.hostid = random(); @@ -330,8 +563,13 @@ pfinit(void) printf("%s: failed to allocate major number!\n", __func__); return; } - (void) devfs_make_node(makedev(maj, 0), DEVFS_CHAR, + (void) devfs_make_node(makedev(maj, PFDEV_PF), DEVFS_CHAR, UID_ROOT, GID_WHEEL, 0600, "pf", 0); + + (void) devfs_make_node(makedev(maj, PFDEV_PFM), DEVFS_CHAR, + UID_ROOT, GID_WHEEL, 0600, "pfm", 0); + + pf_attach_hooks(); } #if 0 @@ -346,6 +584,8 @@ pfdetach(void) int i; char r = '\0'; + pf_detach_hooks(); + pf_status.running = 0; wakeup(pf_purge_thread_fn); @@ -353,10 +593,10 @@ pfdetach(void) for (i = 0; i < PF_RULESET_MAX; i++) if (pf_begin_rules(&ticket, i, &r) == 0) pf_commit_rules(ticket, i, &r); -#if ALTQ +#if PF_ALTQ if (pf_begin_altq(&ticket) == 0) pf_commit_altq(ticket); -#endif /* ALTQ */ +#endif /* PF_ALTQ */ /* clear states */ RB_FOREACH(state, pf_state_tree_id, &tree_id) { @@ -398,9 +638,9 @@ pfdetach(void) /* destroy the pools */ pool_destroy(&pf_pooladdr_pl); -#if ALTQ +#if PF_ALTQ pool_destroy(&pf_altq_pl); -#endif /* ALTQ */ +#endif /* PF_ALTQ */ pool_destroy(&pf_state_pl); pool_destroy(&pf_rule_pl); pool_destroy(&pf_src_tree_pl); @@ -417,8 +657,18 @@ static int pfopen(dev_t dev, int flags, int fmt, struct proc *p) { #pragma unused(flags, fmt, p) - if (minor(dev) >= 1) + if (minor(dev) >= PFDEV_MAX) return (ENXIO); + + if (minor(dev) == PFDEV_PFM) { + lck_mtx_lock(pf_lock); + if (pfdevcnt != 0) { + lck_mtx_unlock(pf_lock); + return (EBUSY); + } + pfdevcnt++; + lck_mtx_unlock(pf_lock); + } return (0); } @@ -426,8 +676,15 @@ static int pfclose(dev_t dev, int flags, int fmt, struct proc *p) { #pragma unused(flags, fmt, p) - if (minor(dev) >= 1) + if (minor(dev) >= PFDEV_MAX) return (ENXIO); + + if (minor(dev) == PFDEV_PFM) { + lck_mtx_lock(pf_lock); + VERIFY(pfdevcnt > 0); + pfdevcnt--; + lck_mtx_unlock(pf_lock); + } return (0); } @@ -525,11 +782,13 @@ pf_rm_rule(struct pf_rulequeue *rulequeue, struct pf_rule *rule) return; pf_tag_unref(rule->tag); pf_tag_unref(rule->match_tag); -#if ALTQ - if (rule->pqid != rule->qid) - pf_qid_unref(rule->pqid); - pf_qid_unref(rule->qid); -#endif /* ALTQ */ +#if PF_ALTQ + if (altq_allowed) { + if (rule->pqid != rule->qid) + pf_qid_unref(rule->pqid); + pf_qid_unref(rule->qid); + } +#endif /* PF_ALTQ */ pf_rtlabel_remove(&rule->src.addr); pf_rtlabel_remove(&rule->dst.addr); pfi_dynaddr_remove(&rule->src.addr); @@ -670,22 +929,28 @@ pf_rtlabel_copyout(struct pf_addr_wrap *a) #pragma unused(a) } -#if ALTQ +#if PF_ALTQ u_int32_t pf_qname2qid(char *qname) { + lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED); + return ((u_int32_t)tagname2tag(&pf_qids, qname)); } void pf_qid2qname(u_int32_t qid, char *p) { + lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED); + tag2tagname(&pf_qids, (u_int16_t)qid, p); } void pf_qid_unref(u_int32_t qid) { + lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED); + tag_unref(&pf_qids, (u_int16_t)qid); } @@ -695,10 +960,12 @@ pf_begin_altq(u_int32_t *ticket) struct pf_altq *altq; int error = 0; + lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED); + /* Purge the old altq list */ while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { TAILQ_REMOVE(pf_altqs_inactive, altq, entries); - if (altq->qname[0] == 0) { + if (altq->qname[0] == '\0') { /* detach and destroy the discipline */ error = altq_remove(altq); } else @@ -718,12 +985,14 @@ pf_rollback_altq(u_int32_t ticket) struct pf_altq *altq; int error = 0; + lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED); + 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); - if (altq->qname[0] == 0) { + if (altq->qname[0] == '\0') { /* detach and destroy the discipline */ error = altq_remove(altq); } else @@ -739,13 +1008,14 @@ pf_commit_altq(u_int32_t ticket) { struct pf_altqqueue *old_altqs; struct pf_altq *altq; - int s, err, error = 0; + int err, error = 0; + + lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED); if (!altqs_inactive_open || ticket != ticket_altqs_inactive) return (EBUSY); /* swap altqs, keep the old. */ - s = splnet(); old_altqs = pf_altqs_active; pf_altqs_active = pf_altqs_inactive; pf_altqs_inactive = old_altqs; @@ -753,13 +1023,12 @@ pf_commit_altq(u_int32_t ticket) /* Attach new disciplines */ TAILQ_FOREACH(altq, pf_altqs_active, entries) { - if (altq->qname[0] == 0) { + if (altq->qname[0] == '\0') { /* 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); } } @@ -768,7 +1037,7 @@ pf_commit_altq(u_int32_t ticket) /* Purge the old altq list */ while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { TAILQ_REMOVE(pf_altqs_inactive, altq, entries); - if (altq->qname[0] == 0) { + if (altq->qname[0] == '\0') { /* detach and destroy the discipline */ if (pf_altq_running) error = pf_disable_altq(altq); @@ -782,7 +1051,6 @@ pf_commit_altq(u_int32_t ticket) pf_qid_unref(altq->qid); pool_put(&pf_altq_pl, altq); } - splx(s); altqs_inactive_open = 0; return (error); @@ -792,23 +1060,40 @@ static int pf_enable_altq(struct pf_altq *altq) { struct ifnet *ifp; - struct tb_profile tb; - int s, error = 0; + struct ifclassq *ifq; + int error = 0; + + lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED); if ((ifp = ifunit(altq->ifname)) == NULL) return (EINVAL); - if (ifp->if_snd.altq_type != ALTQT_NONE) - error = altq_enable(&ifp->if_snd); + ifq = &ifp->if_snd; + IFCQ_LOCK(ifq); + if (IFCQ_ALTQ(ifq)->altq_type != ALTQT_NONE) + error = altq_enable(IFCQ_ALTQ(ifq)); + + /* set or clear tokenbucket regulator */ + if (error == 0 && ifp != NULL && ALTQ_IS_ENABLED(IFCQ_ALTQ(ifq))) { + struct tb_profile tb = { 0, 0, 0 }; - /* set tokenbucket regulator */ - if (error == 0 && ifp != NULL && ALTQ_IS_ENABLED(&ifp->if_snd)) { - tb.rate = altq->ifbandwidth; - tb.depth = altq->tbrsize; - s = splnet(); - error = tbr_set(&ifp->if_snd, &tb); - splx(s); + if (altq->aflags & PF_ALTQF_TBR) { + if (altq->bwtype != PF_ALTQ_BW_ABSOLUTE && + altq->bwtype != PF_ALTQ_BW_PERCENT) { + error = EINVAL; + } else { + if (altq->bwtype == PF_ALTQ_BW_ABSOLUTE) + tb.rate = altq->ifbandwidth; + else + tb.percent = altq->ifbandwidth; + tb.depth = altq->tbrsize; + error = ifclassq_tbr_set(ifq, &tb, TRUE); + } + } else if (IFCQ_TBR_IS_ENABLED(ifq)) { + error = ifclassq_tbr_set(ifq, &tb, TRUE); + } } + IFCQ_UNLOCK(ifq); return (error); } @@ -817,8 +1102,10 @@ static int pf_disable_altq(struct pf_altq *altq) { struct ifnet *ifp; - struct tb_profile tb; - int s, error; + struct ifclassq *ifq; + int error; + + lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED); if ((ifp = ifunit(altq->ifname)) == NULL) return (EINVAL); @@ -827,22 +1114,50 @@ pf_disable_altq(struct pf_altq *altq) * 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) + ifq = &ifp->if_snd; + IFCQ_LOCK(ifq); + if (altq->altq_disc != IFCQ_ALTQ(ifq)->altq_disc) { + IFCQ_UNLOCK(ifq); return (0); + } - error = altq_disable(&ifp->if_snd); + error = altq_disable(IFCQ_ALTQ(ifq)); - if (error == 0) { + if (error == 0 && IFCQ_TBR_IS_ENABLED(ifq)) { /* clear tokenbucket regulator */ - tb.rate = 0; - s = splnet(); - error = tbr_set(&ifp->if_snd, &tb); - splx(s); + struct tb_profile tb = { 0, 0, 0 }; + error = ifclassq_tbr_set(ifq, &tb, TRUE); } + IFCQ_UNLOCK(ifq); return (error); } -#endif /* ALTQ */ + +static void +pf_altq_copyin(struct pf_altq *src, struct pf_altq *dst) +{ + bcopy(src, dst, sizeof (struct pf_altq)); + + dst->ifname[sizeof (dst->ifname) - 1] = '\0'; + dst->qname[sizeof (dst->qname) - 1] = '\0'; + dst->parent[sizeof (dst->parent) - 1] = '\0'; + dst->altq_disc = NULL; + dst->entries.tqe_next = NULL; + dst->entries.tqe_prev = NULL; +} + +static void +pf_altq_copyout(struct pf_altq *src, struct pf_altq *dst) +{ + struct pf_altq pa; + + bcopy(src, &pa, sizeof (struct pf_altq)); + pa.altq_disc = NULL; + pa.entries.tqe_next = NULL; + pa.entries.tqe_prev = NULL; + bcopy(&pa, dst, sizeof (struct pf_altq)); +} +#endif /* PF_ALTQ */ static int pf_begin_rules(u_int32_t *ticket, int rs_num, const char *anchor) @@ -884,29 +1199,24 @@ pf_rollback_rules(u_int32_t ticket, int rs_num, char *anchor) return (0); } -#define PF_MD5_UPD(st, elm) \ +#define PF_MD5_UPD(st, elm) \ MD5Update(ctx, (u_int8_t *)&(st)->elm, sizeof ((st)->elm)) -#define PF_MD5_UPD_STR(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 { \ +#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 { \ +#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) -#ifndef NO_APPLE_EXTENSIONS static void pf_hash_rule_addr(MD5_CTX *ctx, struct pf_rule_addr *pfr, u_int8_t proto) -#else -static void -pf_hash_rule_addr(MD5_CTX *ctx, struct pf_rule_addr *pfr) -#endif { PF_MD5_UPD(pfr, addr.type); switch (pfr->addr.type) { @@ -927,26 +1237,19 @@ pf_hash_rule_addr(MD5_CTX *ctx, struct pf_rule_addr *pfr) break; } -#ifndef NO_APPLE_EXTENSIONS switch (proto) { case IPPROTO_TCP: case IPPROTO_UDP: PF_MD5_UPD(pfr, xport.range.port[0]); PF_MD5_UPD(pfr, xport.range.port[1]); PF_MD5_UPD(pfr, xport.range.op); - break; + break; default: break; } PF_MD5_UPD(pfr, neg); -#else - PF_MD5_UPD(pfr, port[0]); - PF_MD5_UPD(pfr, port[1]); - PF_MD5_UPD(pfr, neg); - PF_MD5_UPD(pfr, port_op); -#endif } static void @@ -955,13 +1258,8 @@ pf_hash_rule(MD5_CTX *ctx, struct pf_rule *rule) u_int16_t x; u_int32_t y; -#ifndef NO_APPLE_EXTENSIONS pf_hash_rule_addr(ctx, &rule->src, rule->proto); pf_hash_rule_addr(ctx, &rule->dst, rule->proto); -#else - pf_hash_rule_addr(ctx, &rule->src); - pf_hash_rule_addr(ctx, &rule->dst); -#endif PF_MD5_UPD_STR(rule, label); PF_MD5_UPD_STR(rule, ifname); PF_MD5_UPD_STR(rule, match_tagname); @@ -997,7 +1295,7 @@ static 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_rule *rule, **old_array, *r; struct pf_rulequeue *old_rules; int error; u_int32_t old_rcount; @@ -1023,6 +1321,16 @@ pf_commit_rules(u_int32_t ticket, int rs_num, char *anchor) old_rcount = rs->rules[rs_num].active.rcount; old_array = rs->rules[rs_num].active.ptr_array; + if(old_rcount != 0) { + r = TAILQ_FIRST(rs->rules[rs_num].active.ptr); + while (r) { + if (r->rule_flag & PFRULE_PFM) + pffwrules--; + r = TAILQ_NEXT(r, entries); + } + } + + rs->rules[rs_num].active.ptr = rs->rules[rs_num].inactive.ptr; rs->rules[rs_num].active.ptr_array = @@ -1050,6 +1358,56 @@ pf_commit_rules(u_int32_t ticket, int rs_num, char *anchor) return (0); } +static void +pf_rule_copyin(struct pf_rule *src, struct pf_rule *dst, struct proc *p, + int minordev) +{ + bcopy(src, dst, sizeof (struct pf_rule)); + + dst->label[sizeof (dst->label) - 1] = '\0'; + dst->ifname[sizeof (dst->ifname) - 1] = '\0'; + dst->qname[sizeof (dst->qname) - 1] = '\0'; + dst->pqname[sizeof (dst->pqname) - 1] = '\0'; + dst->tagname[sizeof (dst->tagname) - 1] = '\0'; + dst->match_tagname[sizeof (dst->match_tagname) - 1] = '\0'; + dst->overload_tblname[sizeof (dst->overload_tblname) - 1] = '\0'; + + dst->cuid = kauth_cred_getuid(p->p_ucred); + dst->cpid = p->p_pid; + + dst->anchor = NULL; + dst->kif = NULL; + dst->overload_tbl = NULL; + + TAILQ_INIT(&dst->rpool.list); + dst->rpool.cur = NULL; + + /* initialize refcounting */ + dst->states = 0; + dst->src_nodes = 0; + + dst->entries.tqe_prev = NULL; + dst->entries.tqe_next = NULL; + if ((uint8_t)minordev == PFDEV_PFM) + dst->rule_flag |= PFRULE_PFM; +} + +static void +pf_rule_copyout(struct pf_rule *src, struct pf_rule *dst) +{ + bcopy(src, dst, sizeof (struct pf_rule)); + + dst->anchor = NULL; + dst->kif = NULL; + dst->overload_tbl = NULL; + + TAILQ_INIT(&dst->rpool.list); + dst->rpool.cur = NULL; + + dst->entries.tqe_prev = NULL; + dst->entries.tqe_next = NULL; +} + static void pf_state_export(struct pfsync_state *sp, struct pf_state_key *sk, struct pf_state *s) @@ -1058,7 +1416,6 @@ pf_state_export(struct pfsync_state *sp, struct pf_state_key *sk, bzero(sp, sizeof (struct pfsync_state)); /* copy from state key */ -#ifndef NO_APPLE_EXTENSIONS sp->lan.addr = sk->lan.addr; sp->lan.xport = sk->lan.xport; sp->gwy.addr = sk->gwy.addr; @@ -1067,17 +1424,10 @@ pf_state_export(struct pfsync_state *sp, struct pf_state_key *sk, sp->ext.xport = sk->ext.xport; sp->proto_variant = sk->proto_variant; sp->tag = s->tag; -#else - sp->lan.addr = sk->lan.addr; - sp->lan.port = sk->lan.port; - sp->gwy.addr = sk->gwy.addr; - sp->gwy.port = sk->gwy.port; - sp->ext.addr = sk->ext.addr; - sp->ext.port = sk->ext.port; -#endif sp->proto = sk->proto; sp->af = sk->af; sp->direction = sk->direction; + sp->flowhash = sk->flowhash; /* copy from state */ memcpy(&sp->id, &s->id, sizeof (sp->id)); @@ -1119,7 +1469,6 @@ pf_state_import(struct pfsync_state *sp, struct pf_state_key *sk, struct pf_state *s) { /* copy to state key */ -#ifndef NO_APPLE_EXTENSIONS sk->lan.addr = sp->lan.addr; sk->lan.xport = sp->lan.xport; sk->gwy.addr = sp->gwy.addr; @@ -1128,17 +1477,10 @@ pf_state_import(struct pfsync_state *sp, struct pf_state_key *sk, sk->ext.xport = sp->ext.xport; sk->proto_variant = sp->proto_variant; s->tag = sp->tag; -#else - sk->lan.addr = sp->lan.addr; - sk->lan.port = sp->lan.port; - sk->gwy.addr = sp->gwy.addr; - sk->gwy.port = sp->gwy.port; - sk->ext.addr = sp->ext.addr; - sk->ext.port = sp->ext.port; -#endif sk->proto = sp->proto; sk->af = sp->af; sk->direction = sp->direction; + sk->flowhash = pf_calc_state_key_flowhash(sk); /* copy to state */ memcpy(&s->id, &sp->id, sizeof (sp->id)); @@ -1159,6 +1501,27 @@ pf_state_import(struct pfsync_state *sp, struct pf_state_key *sk, s->bytes[0] = s->bytes[1] = 0; } +static void +pf_pooladdr_copyin(struct pf_pooladdr *src, struct pf_pooladdr *dst) +{ + bcopy(src, dst, sizeof (struct pf_pooladdr)); + + dst->entries.tqe_prev = NULL; + dst->entries.tqe_next = NULL; + dst->ifname[sizeof (dst->ifname) - 1] = '\0'; + dst->kif = NULL; +} + +static void +pf_pooladdr_copyout(struct pf_pooladdr *src, struct pf_pooladdr *dst) +{ + bcopy(src, dst, sizeof (struct pf_pooladdr)); + + dst->entries.tqe_prev = NULL; + dst->entries.tqe_next = NULL; + dst->kif = NULL; +} + static int pf_setup_pfsync_matching(struct pf_ruleset *rs) { @@ -1199,13 +1562,45 @@ pf_setup_pfsync_matching(struct pf_ruleset *rs) return (0); } +static void +pf_start(void) +{ + lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED); + + VERIFY(pf_is_enabled == 0); + + pf_is_enabled = 1; + pf_status.running = 1; + pf_status.since = pf_calendar_time_second(); + if (pf_status.stateid == 0) { + pf_status.stateid = pf_time_second(); + pf_status.stateid = pf_status.stateid << 32; + } + wakeup(pf_purge_thread_fn); + DPFPRINTF(PF_DEBUG_MISC, ("pf: started\n")); +} + +static void +pf_stop(void) +{ + lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED); + + VERIFY(pf_is_enabled); + + pf_status.running = 0; + pf_is_enabled = 0; + pf_status.since = pf_calendar_time_second(); + wakeup(pf_purge_thread_fn); + DPFPRINTF(PF_DEBUG_MISC, ("pf: stopped\n")); +} + static int pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) { #pragma unused(dev) - struct pf_pooladdr *pa = NULL; - struct pf_pool *pool = NULL; - int error = 0; + int p64 = proc_is64bit(p); + int error = 0; + int minordev = minor(dev); if (kauth_cred_issuser(kauth_cred_get()) == 0) return (EPERM); @@ -1224,6 +1619,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCNATLOOK: case DIOCSETDEBUG: case DIOCGETSTATES: + case DIOCINSERTRULE: + case DIOCDELETERULE: case DIOCGETTIMEOUT: case DIOCCLRRULECTRS: case DIOCGETLIMIT: @@ -1247,17 +1644,23 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCGETSRCNODES: case DIOCCLRSRCNODES: case DIOCIGETIFACES: + case DIOCGIFSPEED: case DIOCSETIFFLAG: case DIOCCLRIFFLAG: break; case DIOCRCLRTABLES: case DIOCRADDTABLES: case DIOCRDELTABLES: - case DIOCRSETTFLAGS: - if (((struct pfioc_table *)addr)->pfrio_flags & - PFR_FLAG_DUMMY) + case DIOCRSETTFLAGS: { + int pfrio_flags; + + bcopy(&((struct pfioc_table *)(void *)addr)-> + pfrio_flags, &pfrio_flags, sizeof (pfrio_flags)); + + if (pfrio_flags & PFR_FLAG_DUMMY) break; /* dummy operation ok */ return (EPERM); + } default: return (EPERM); } @@ -1265,13 +1668,18 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) if (!(flags & FWRITE)) switch (cmd) { case DIOCSTART: + case DIOCSTARTREF: case DIOCSTOP: + case DIOCSTOPREF: + case DIOCGETSTARTERS: case DIOCGETRULES: case DIOCGETADDRS: case DIOCGETADDR: case DIOCGETSTATE: case DIOCGETSTATUS: case DIOCGETSTATES: + case DIOCINSERTRULE: + case DIOCDELETERULE: case DIOCGETTIMEOUT: case DIOCGETLIMIT: case DIOCGETALTQS: @@ -1288,6 +1696,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCOSFPGET: case DIOCGETSRCNODES: case DIOCIGETIFACES: + case DIOCGIFSPEED: break; case DIOCRCLRTABLES: case DIOCRADDTABLES: @@ -1297,22 +1706,48 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCRADDADDRS: case DIOCRDELADDRS: case DIOCRSETADDRS: - case DIOCRSETTFLAGS: - if (((struct pfioc_table *)addr)->pfrio_flags & - PFR_FLAG_DUMMY) { + case DIOCRSETTFLAGS: { + int pfrio_flags; + + bcopy(&((struct pfioc_table *)(void *)addr)-> + pfrio_flags, &pfrio_flags, sizeof (pfrio_flags)); + + if (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) + } + case DIOCGETRULE: { + u_int32_t action; + + bcopy(&((struct pfioc_rule *)(void *)addr)->action, + &action, sizeof (action)); + + if (action == PF_GET_CLR_CNTR) return (EACCES); break; + } default: return (EACCES); } +#if PF_ALTQ + switch (cmd) { + case DIOCSTARTALTQ: + case DIOCSTOPALTQ: + case DIOCADDALTQ: + case DIOCGETALTQS: + case DIOCGETALTQ: + case DIOCCHANGEALTQ: + case DIOCGETQSTATS: + /* fail if ALTQ is disabled */ + if (!altq_allowed) + return (ENODEV); + break; + } +#endif /* PF_ALTQ */ + if (flags & FWRITE) lck_rw_lock_exclusive(pf_perim_lock); else @@ -1324,20 +1759,45 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCSTART: if (pf_status.running) { + /* + * Increment the reference for a simple -e enable, so + * that even if other processes drop their references, + * pf will still be available to processes that turned + * it on without taking a reference + */ + if (nr_tokens == pf_enabled_ref_count) { + pf_enabled_ref_count++; + VERIFY(pf_enabled_ref_count != 0); + } error = EEXIST; } else if (pf_purge_thread == NULL) { error = ENOMEM; } else { - pf_status.running = 1; - pf_status.since = pf_time_second(); - if (pf_status.stateid == 0) { - pf_status.stateid = pf_time_second(); - pf_status.stateid = pf_status.stateid << 32; + pf_start(); + pf_enabled_ref_count++; + VERIFY(pf_enabled_ref_count != 0); + } + break; + + case DIOCSTARTREF: /* u_int64_t */ + if (pf_purge_thread == NULL) { + error = ENOMEM; + } else { + u_int64_t token; + + /* small enough to be on stack */ + if ((token = generate_token(p)) != 0) { + if (pf_is_enabled == 0) { + pf_start(); + } + pf_enabled_ref_count++; + VERIFY(pf_enabled_ref_count != 0); + } else { + error = ENOMEM; + DPFPRINTF(PF_DEBUG_URGENT, + ("pf: unable to generate token\n")); } - mbuf_growth_aggressive(); - pf_attach_hooks(); - wakeup(pf_purge_thread_fn); - DPFPRINTF(PF_DEBUG_MISC, ("pf: started\n")); + bcopy(&token, addr, sizeof (token)); } break; @@ -1345,30 +1805,1326 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) if (!pf_status.running) { error = ENOENT; } else { - mbuf_growth_normal(); - pf_detach_hooks(); - pf_status.running = 0; - pf_status.since = pf_time_second(); - wakeup(pf_purge_thread_fn); - DPFPRINTF(PF_DEBUG_MISC, ("pf: stopped\n")); + pf_stop(); + pf_enabled_ref_count = 0; + invalidate_all_tokens(); } break; - case DIOCADDRULE: { - struct pfioc_rule *pr = (struct pfioc_rule *)addr; - struct pf_ruleset *ruleset; - struct pf_rule *rule, *tail; - struct pf_pooladdr *apa; - int rs_num; + case DIOCSTOPREF: /* struct pfioc_remove_token */ + if (!pf_status.running) { + error = ENOENT; + } else { + struct pfioc_remove_token pfrt; + + /* small enough to be on stack */ + bcopy(addr, &pfrt, sizeof (pfrt)); + if ((error = remove_token(&pfrt)) == 0) { + VERIFY(pf_enabled_ref_count != 0); + pf_enabled_ref_count--; + /* return currently held references */ + pfrt.refcount = pf_enabled_ref_count; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: enabled refcount decremented\n")); + } else { + error = EINVAL; + DPFPRINTF(PF_DEBUG_URGENT, + ("pf: token mismatch\n")); + } + bcopy(&pfrt, addr, sizeof (pfrt)); - pr->anchor[sizeof (pr->anchor) - 1] = 0; - ruleset = pf_find_ruleset(pr->anchor); - if (ruleset == NULL) { - error = EINVAL; + if (error == 0 && pf_enabled_ref_count == 0) + pf_stop(); + } + break; + + case DIOCGETSTARTERS: { /* struct pfioc_tokens */ + PFIOCX_STRUCT_DECL(pfioc_tokens); + + PFIOCX_STRUCT_BEGIN(addr, pfioc_tokens, error = ENOMEM; break;); + error = pfioctl_ioc_tokens(cmd, + PFIOCX_STRUCT_ADDR32(pfioc_tokens), + PFIOCX_STRUCT_ADDR64(pfioc_tokens), p); + PFIOCX_STRUCT_END(pfioc_tokens, addr); + break; + } + + case DIOCADDRULE: /* struct pfioc_rule */ + case DIOCGETRULES: /* struct pfioc_rule */ + case DIOCGETRULE: /* struct pfioc_rule */ + case DIOCCHANGERULE: /* struct pfioc_rule */ + case DIOCINSERTRULE: /* struct pfioc_rule */ + case DIOCDELETERULE: { /* struct pfioc_rule */ + struct pfioc_rule *pr = NULL; + + PFIOC_STRUCT_BEGIN(addr, pr, error = ENOMEM; break;); + error = pfioctl_ioc_rule(cmd, minordev, pr, p); + PFIOC_STRUCT_END(pr, addr); + break; + } + + case DIOCCLRSTATES: /* struct pfioc_state_kill */ + case DIOCKILLSTATES: { /* struct pfioc_state_kill */ + struct pfioc_state_kill *psk = NULL; + + PFIOC_STRUCT_BEGIN(addr, psk, error = ENOMEM; break;); + error = pfioctl_ioc_state_kill(cmd, psk, p); + PFIOC_STRUCT_END(psk, addr); + break; + } + + case DIOCADDSTATE: /* struct pfioc_state */ + case DIOCGETSTATE: { /* struct pfioc_state */ + struct pfioc_state *ps = NULL; + + PFIOC_STRUCT_BEGIN(addr, ps, error = ENOMEM; break;); + error = pfioctl_ioc_state(cmd, ps, p); + PFIOC_STRUCT_END(ps, addr); + break; + } + + case DIOCGETSTATES: { /* struct pfioc_states */ + PFIOCX_STRUCT_DECL(pfioc_states); + + PFIOCX_STRUCT_BEGIN(addr, pfioc_states, error = ENOMEM; break;); + error = pfioctl_ioc_states(cmd, + PFIOCX_STRUCT_ADDR32(pfioc_states), + PFIOCX_STRUCT_ADDR64(pfioc_states), p); + PFIOCX_STRUCT_END(pfioc_states, addr); + break; + } + + case DIOCGETSTATUS: { /* struct pf_status */ + struct pf_status *s = NULL; + + PFIOC_STRUCT_BEGIN(&pf_status, s, error = ENOMEM; break;); + pfi_update_status(s->ifname, s); + PFIOC_STRUCT_END(s, addr); + break; + } + + case DIOCSETSTATUSIF: { /* struct pfioc_if */ + struct pfioc_if *pi = (struct pfioc_if *)(void *)addr; + + /* OK for unaligned accesses */ + if (pi->ifname[0] == 0) { + bzero(pf_status.ifname, IFNAMSIZ); break; } - rs_num = pf_get_ruleset_number(pr->rule.action); - if (rs_num >= PF_RULESET_MAX) { + 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 = pf_calendar_time_second(); + if (*pf_status.ifname) + pfi_update_status(pf_status.ifname, NULL); + break; + } + + case DIOCNATLOOK: { /* struct pfioc_natlook */ + struct pfioc_natlook *pnl = NULL; + + PFIOC_STRUCT_BEGIN(addr, pnl, error = ENOMEM; break;); + error = pfioctl_ioc_natlook(cmd, pnl, p); + PFIOC_STRUCT_END(pnl, addr); + break; + } + + case DIOCSETTIMEOUT: /* struct pfioc_tm */ + case DIOCGETTIMEOUT: { /* struct pfioc_tm */ + struct pfioc_tm pt; + + /* small enough to be on stack */ + bcopy(addr, &pt, sizeof (pt)); + error = pfioctl_ioc_tm(cmd, &pt, p); + bcopy(&pt, addr, sizeof (pt)); + break; + } + + case DIOCGETLIMIT: /* struct pfioc_limit */ + case DIOCSETLIMIT: { /* struct pfioc_limit */ + struct pfioc_limit pl; + + /* small enough to be on stack */ + bcopy(addr, &pl, sizeof (pl)); + error = pfioctl_ioc_limit(cmd, &pl, p); + bcopy(&pl, addr, sizeof (pl)); + break; + } + + case DIOCSETDEBUG: { /* u_int32_t */ + bcopy(addr, &pf_status.debug, sizeof (u_int32_t)); + 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; + } + + case DIOCGIFSPEED: { + struct pf_ifspeed *psp = (struct pf_ifspeed *)(void *)addr; + struct pf_ifspeed ps; + struct ifnet *ifp; + u_int64_t baudrate; + + if (psp->ifname[0] != '\0') { + /* Can we completely trust user-land? */ + strlcpy(ps.ifname, psp->ifname, IFNAMSIZ); + ps.ifname[IFNAMSIZ - 1] = '\0'; + ifp = ifunit(ps.ifname); + if (ifp != NULL) { + baudrate = ifp->if_output_bw.max_bw; + bcopy(&baudrate, &psp->baudrate, + sizeof (baudrate)); + } else { + error = EINVAL; + } + } else { + error = EINVAL; + } + break; + } + +#if PF_ALTQ + case DIOCSTARTALTQ: { + struct pf_altq *altq; + + VERIFY(altq_allowed); + /* enable all altq interfaces on active list */ + TAILQ_FOREACH(altq, pf_altqs_active, entries) { + if (altq->qname[0] == '\0') { + 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; + + VERIFY(altq_allowed); + /* disable all altq interfaces on active list */ + TAILQ_FOREACH(altq, pf_altqs_active, entries) { + if (altq->qname[0] == '\0') { + 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 */ + struct pfioc_altq *pa = (struct pfioc_altq *)(void *)addr; + struct pf_altq *altq, *a; + u_int32_t ticket; + + VERIFY(altq_allowed); + bcopy(&pa->ticket, &ticket, sizeof (ticket)); + if (ticket != ticket_altqs_inactive) { + error = EBUSY; + break; + } + altq = pool_get(&pf_altq_pl, PR_WAITOK); + if (altq == NULL) { + error = ENOMEM; + break; + } + pf_altq_copyin(&pa->altq, altq); + + /* + * 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; + } + } + } + + error = altq_add(altq); + if (error) { + pool_put(&pf_altq_pl, altq); + break; + } + + TAILQ_INSERT_TAIL(pf_altqs_inactive, altq, entries); + pf_altq_copyout(altq, &pa->altq); + break; + } + + case DIOCGETALTQS: { + struct pfioc_altq *pa = (struct pfioc_altq *)(void *)addr; + struct pf_altq *altq; + u_int32_t nr; + + VERIFY(altq_allowed); + nr = 0; + TAILQ_FOREACH(altq, pf_altqs_active, entries) + nr++; + bcopy(&nr, &pa->nr, sizeof (nr)); + bcopy(&ticket_altqs_active, &pa->ticket, sizeof (pa->ticket)); + break; + } + + case DIOCGETALTQ: { + struct pfioc_altq *pa = (struct pfioc_altq *)(void *)addr; + struct pf_altq *altq; + u_int32_t nr, pa_nr, ticket; + + VERIFY(altq_allowed); + bcopy(&pa->ticket, &ticket, sizeof (ticket)); + if (ticket != ticket_altqs_active) { + error = EBUSY; + break; + } + bcopy(&pa->nr, &pa_nr, sizeof (pa_nr)); + 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; + } + pf_altq_copyout(altq, &pa->altq); + break; + } + + case DIOCCHANGEALTQ: + VERIFY(altq_allowed); + /* CHANGEALTQ not supported yet! */ + error = ENODEV; + break; + + case DIOCGETQSTATS: { + struct pfioc_qstats *pq = (struct pfioc_qstats *)(void *)addr; + struct pf_altq *altq; + u_int32_t nr, pq_nr, ticket; + int nbytes; + + VERIFY(altq_allowed); + bcopy(&pq->ticket, &ticket, sizeof (ticket)); + if (ticket != ticket_altqs_active) { + error = EBUSY; + break; + } + bcopy(&pq->nr, &pq_nr, sizeof (pq_nr)); + 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; + } + bcopy(&pq->nbytes, &nbytes, sizeof (nbytes)); + error = altq_getqstats(altq, pq->buf, &nbytes); + if (error == 0) { + pq->scheduler = altq->scheduler; + bcopy(&nbytes, &pq->nbytes, sizeof (nbytes)); + } + break; + } +#endif /* PF_ALTQ */ + + case DIOCBEGINADDRS: /* struct pfioc_pooladdr */ + case DIOCADDADDR: /* struct pfioc_pooladdr */ + case DIOCGETADDRS: /* struct pfioc_pooladdr */ + case DIOCGETADDR: /* struct pfioc_pooladdr */ + case DIOCCHANGEADDR: { /* struct pfioc_pooladdr */ + struct pfioc_pooladdr *pp = NULL; + + PFIOC_STRUCT_BEGIN(addr, pp, error = ENOMEM; break;) + error = pfioctl_ioc_pooladdr(cmd, pp, p); + PFIOC_STRUCT_END(pp, addr); + break; + } + + case DIOCGETRULESETS: /* struct pfioc_ruleset */ + case DIOCGETRULESET: { /* struct pfioc_ruleset */ + struct pfioc_ruleset *pr = NULL; + + PFIOC_STRUCT_BEGIN(addr, pr, error = ENOMEM; break;); + error = pfioctl_ioc_ruleset(cmd, pr, p); + PFIOC_STRUCT_END(pr, addr); + break; + } + + case DIOCRCLRTABLES: /* struct pfioc_table */ + case DIOCRADDTABLES: /* struct pfioc_table */ + case DIOCRDELTABLES: /* struct pfioc_table */ + case DIOCRGETTABLES: /* struct pfioc_table */ + case DIOCRGETTSTATS: /* struct pfioc_table */ + case DIOCRCLRTSTATS: /* struct pfioc_table */ + case DIOCRSETTFLAGS: /* struct pfioc_table */ + case DIOCRCLRADDRS: /* struct pfioc_table */ + case DIOCRADDADDRS: /* struct pfioc_table */ + case DIOCRDELADDRS: /* struct pfioc_table */ + case DIOCRSETADDRS: /* struct pfioc_table */ + case DIOCRGETADDRS: /* struct pfioc_table */ + case DIOCRGETASTATS: /* struct pfioc_table */ + case DIOCRCLRASTATS: /* struct pfioc_table */ + case DIOCRTSTADDRS: /* struct pfioc_table */ + case DIOCRINADEFINE: { /* struct pfioc_table */ + PFIOCX_STRUCT_DECL(pfioc_table); + + PFIOCX_STRUCT_BEGIN(addr, pfioc_table, error = ENOMEM; break;); + error = pfioctl_ioc_table(cmd, + PFIOCX_STRUCT_ADDR32(pfioc_table), + PFIOCX_STRUCT_ADDR64(pfioc_table), p); + PFIOCX_STRUCT_END(pfioc_table, addr); + break; + } + + case DIOCOSFPADD: /* struct pf_osfp_ioctl */ + case DIOCOSFPGET: { /* struct pf_osfp_ioctl */ + struct pf_osfp_ioctl *io = NULL; + + PFIOC_STRUCT_BEGIN(addr, io, error = ENOMEM; break;); + if (cmd == DIOCOSFPADD) { + error = pf_osfp_add(io); + } else { + VERIFY(cmd == DIOCOSFPGET); + error = pf_osfp_get(io); + } + PFIOC_STRUCT_END(io, addr); + break; + } + + case DIOCXBEGIN: /* struct pfioc_trans */ + case DIOCXROLLBACK: /* struct pfioc_trans */ + case DIOCXCOMMIT: { /* struct pfioc_trans */ + PFIOCX_STRUCT_DECL(pfioc_trans); + + PFIOCX_STRUCT_BEGIN(addr, pfioc_trans, error = ENOMEM; break;); + error = pfioctl_ioc_trans(cmd, + PFIOCX_STRUCT_ADDR32(pfioc_trans), + PFIOCX_STRUCT_ADDR64(pfioc_trans), p); + PFIOCX_STRUCT_END(pfioc_trans, addr); + break; + } + + case DIOCGETSRCNODES: { /* struct pfioc_src_nodes */ + PFIOCX_STRUCT_DECL(pfioc_src_nodes); + + PFIOCX_STRUCT_BEGIN(addr, pfioc_src_nodes, + error = ENOMEM; break;); + error = pfioctl_ioc_src_nodes(cmd, + PFIOCX_STRUCT_ADDR32(pfioc_src_nodes), + PFIOCX_STRUCT_ADDR64(pfioc_src_nodes), p); + PFIOCX_STRUCT_END(pfioc_src_nodes, addr); + 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(); + pf_status.src_nodes = 0; + break; + } + + case DIOCKILLSRCNODES: { /* struct pfioc_src_node_kill */ + struct pfioc_src_node_kill *psnk = NULL; + + PFIOC_STRUCT_BEGIN(addr, psnk, error = ENOMEM; break;); + error = pfioctl_ioc_src_node_kill(cmd, psnk, p); + PFIOC_STRUCT_END(psnk, addr); + break; + } + + case DIOCSETHOSTID: { /* u_int32_t */ + u_int32_t hid; + + /* small enough to be on stack */ + bcopy(addr, &hid, sizeof (hid)); + if (hid == 0) + pf_status.hostid = random(); + else + pf_status.hostid = hid; + break; + } + + case DIOCOSFPFLUSH: + pf_osfp_flush(); + break; + + case DIOCIGETIFACES: /* struct pfioc_iface */ + case DIOCSETIFFLAG: /* struct pfioc_iface */ + case DIOCCLRIFFLAG: { /* struct pfioc_iface */ + PFIOCX_STRUCT_DECL(pfioc_iface); + + PFIOCX_STRUCT_BEGIN(addr, pfioc_iface, error = ENOMEM; break;); + error = pfioctl_ioc_iface(cmd, + PFIOCX_STRUCT_ADDR32(pfioc_iface), + PFIOCX_STRUCT_ADDR64(pfioc_iface), p); + PFIOCX_STRUCT_END(pfioc_iface, addr); + break; + } + + default: + error = ENODEV; + break; + } + + lck_mtx_unlock(pf_lock); + lck_rw_done(pf_perim_lock); + + return (error); +} + +static int +pfioctl_ioc_table(u_long cmd, struct pfioc_table_32 *io32, + struct pfioc_table_64 *io64, struct proc *p) +{ + int p64 = proc_is64bit(p); + int error = 0; + + if (!p64) + goto struct32; + + /* + * 64-bit structure processing + */ + switch (cmd) { + case DIOCRCLRTABLES: + if (io64->pfrio_esize != 0) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io64->pfrio_table); + error = pfr_clr_tables(&io64->pfrio_table, &io64->pfrio_ndel, + io64->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRADDTABLES: + if (io64->pfrio_esize != sizeof (struct pfr_table)) { + error = ENODEV; + break; + } + error = pfr_add_tables(io64->pfrio_buffer, io64->pfrio_size, + &io64->pfrio_nadd, io64->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRDELTABLES: + if (io64->pfrio_esize != sizeof (struct pfr_table)) { + error = ENODEV; + break; + } + error = pfr_del_tables(io64->pfrio_buffer, io64->pfrio_size, + &io64->pfrio_ndel, io64->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRGETTABLES: + if (io64->pfrio_esize != sizeof (struct pfr_table)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io64->pfrio_table); + error = pfr_get_tables(&io64->pfrio_table, io64->pfrio_buffer, + &io64->pfrio_size, io64->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRGETTSTATS: + if (io64->pfrio_esize != sizeof (struct pfr_tstats)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io64->pfrio_table); + error = pfr_get_tstats(&io64->pfrio_table, io64->pfrio_buffer, + &io64->pfrio_size, io64->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRCLRTSTATS: + if (io64->pfrio_esize != sizeof (struct pfr_table)) { + error = ENODEV; + break; + } + error = pfr_clr_tstats(io64->pfrio_buffer, io64->pfrio_size, + &io64->pfrio_nzero, io64->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRSETTFLAGS: + if (io64->pfrio_esize != sizeof (struct pfr_table)) { + error = ENODEV; + break; + } + error = pfr_set_tflags(io64->pfrio_buffer, io64->pfrio_size, + io64->pfrio_setflag, io64->pfrio_clrflag, + &io64->pfrio_nchange, &io64->pfrio_ndel, + io64->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRCLRADDRS: + if (io64->pfrio_esize != 0) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io64->pfrio_table); + error = pfr_clr_addrs(&io64->pfrio_table, &io64->pfrio_ndel, + io64->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRADDADDRS: + if (io64->pfrio_esize != sizeof (struct pfr_addr)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io64->pfrio_table); + error = pfr_add_addrs(&io64->pfrio_table, io64->pfrio_buffer, + io64->pfrio_size, &io64->pfrio_nadd, io64->pfrio_flags | + PFR_FLAG_USERIOCTL); + break; + + case DIOCRDELADDRS: + if (io64->pfrio_esize != sizeof (struct pfr_addr)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io64->pfrio_table); + error = pfr_del_addrs(&io64->pfrio_table, io64->pfrio_buffer, + io64->pfrio_size, &io64->pfrio_ndel, io64->pfrio_flags | + PFR_FLAG_USERIOCTL); + break; + + case DIOCRSETADDRS: + if (io64->pfrio_esize != sizeof (struct pfr_addr)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io64->pfrio_table); + error = pfr_set_addrs(&io64->pfrio_table, io64->pfrio_buffer, + io64->pfrio_size, &io64->pfrio_size2, &io64->pfrio_nadd, + &io64->pfrio_ndel, &io64->pfrio_nchange, io64->pfrio_flags | + PFR_FLAG_USERIOCTL, 0); + break; + + case DIOCRGETADDRS: + if (io64->pfrio_esize != sizeof (struct pfr_addr)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io64->pfrio_table); + error = pfr_get_addrs(&io64->pfrio_table, io64->pfrio_buffer, + &io64->pfrio_size, io64->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRGETASTATS: + if (io64->pfrio_esize != sizeof (struct pfr_astats)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io64->pfrio_table); + error = pfr_get_astats(&io64->pfrio_table, io64->pfrio_buffer, + &io64->pfrio_size, io64->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRCLRASTATS: + if (io64->pfrio_esize != sizeof (struct pfr_addr)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io64->pfrio_table); + error = pfr_clr_astats(&io64->pfrio_table, io64->pfrio_buffer, + io64->pfrio_size, &io64->pfrio_nzero, io64->pfrio_flags | + PFR_FLAG_USERIOCTL); + break; + + case DIOCRTSTADDRS: + if (io64->pfrio_esize != sizeof (struct pfr_addr)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io64->pfrio_table); + error = pfr_tst_addrs(&io64->pfrio_table, io64->pfrio_buffer, + io64->pfrio_size, &io64->pfrio_nmatch, io64->pfrio_flags | + PFR_FLAG_USERIOCTL); + break; + + case DIOCRINADEFINE: + if (io64->pfrio_esize != sizeof (struct pfr_addr)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io64->pfrio_table); + error = pfr_ina_define(&io64->pfrio_table, io64->pfrio_buffer, + io64->pfrio_size, &io64->pfrio_nadd, &io64->pfrio_naddr, + io64->pfrio_ticket, io64->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + default: + VERIFY(0); + /* NOTREACHED */ + } + goto done; + +struct32: + /* + * 32-bit structure processing + */ + switch (cmd) { + case DIOCRCLRTABLES: + if (io32->pfrio_esize != 0) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io32->pfrio_table); + error = pfr_clr_tables(&io32->pfrio_table, &io32->pfrio_ndel, + io32->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRADDTABLES: + if (io32->pfrio_esize != sizeof (struct pfr_table)) { + error = ENODEV; + break; + } + error = pfr_add_tables(io32->pfrio_buffer, io32->pfrio_size, + &io32->pfrio_nadd, io32->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRDELTABLES: + if (io32->pfrio_esize != sizeof (struct pfr_table)) { + error = ENODEV; + break; + } + error = pfr_del_tables(io32->pfrio_buffer, io32->pfrio_size, + &io32->pfrio_ndel, io32->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRGETTABLES: + if (io32->pfrio_esize != sizeof (struct pfr_table)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io32->pfrio_table); + error = pfr_get_tables(&io32->pfrio_table, io32->pfrio_buffer, + &io32->pfrio_size, io32->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRGETTSTATS: + if (io32->pfrio_esize != sizeof (struct pfr_tstats)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io32->pfrio_table); + error = pfr_get_tstats(&io32->pfrio_table, io32->pfrio_buffer, + &io32->pfrio_size, io32->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRCLRTSTATS: + if (io32->pfrio_esize != sizeof (struct pfr_table)) { + error = ENODEV; + break; + } + error = pfr_clr_tstats(io32->pfrio_buffer, io32->pfrio_size, + &io32->pfrio_nzero, io32->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRSETTFLAGS: + if (io32->pfrio_esize != sizeof (struct pfr_table)) { + error = ENODEV; + break; + } + error = pfr_set_tflags(io32->pfrio_buffer, io32->pfrio_size, + io32->pfrio_setflag, io32->pfrio_clrflag, + &io32->pfrio_nchange, &io32->pfrio_ndel, + io32->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRCLRADDRS: + if (io32->pfrio_esize != 0) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io32->pfrio_table); + error = pfr_clr_addrs(&io32->pfrio_table, &io32->pfrio_ndel, + io32->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRADDADDRS: + if (io32->pfrio_esize != sizeof (struct pfr_addr)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io32->pfrio_table); + error = pfr_add_addrs(&io32->pfrio_table, io32->pfrio_buffer, + io32->pfrio_size, &io32->pfrio_nadd, io32->pfrio_flags | + PFR_FLAG_USERIOCTL); + break; + + case DIOCRDELADDRS: + if (io32->pfrio_esize != sizeof (struct pfr_addr)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io32->pfrio_table); + error = pfr_del_addrs(&io32->pfrio_table, io32->pfrio_buffer, + io32->pfrio_size, &io32->pfrio_ndel, io32->pfrio_flags | + PFR_FLAG_USERIOCTL); + break; + + case DIOCRSETADDRS: + if (io32->pfrio_esize != sizeof (struct pfr_addr)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io32->pfrio_table); + error = pfr_set_addrs(&io32->pfrio_table, io32->pfrio_buffer, + io32->pfrio_size, &io32->pfrio_size2, &io32->pfrio_nadd, + &io32->pfrio_ndel, &io32->pfrio_nchange, io32->pfrio_flags | + PFR_FLAG_USERIOCTL, 0); + break; + + case DIOCRGETADDRS: + if (io32->pfrio_esize != sizeof (struct pfr_addr)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io32->pfrio_table); + error = pfr_get_addrs(&io32->pfrio_table, io32->pfrio_buffer, + &io32->pfrio_size, io32->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRGETASTATS: + if (io32->pfrio_esize != sizeof (struct pfr_astats)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io32->pfrio_table); + error = pfr_get_astats(&io32->pfrio_table, io32->pfrio_buffer, + &io32->pfrio_size, io32->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + case DIOCRCLRASTATS: + if (io32->pfrio_esize != sizeof (struct pfr_addr)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io32->pfrio_table); + error = pfr_clr_astats(&io32->pfrio_table, io32->pfrio_buffer, + io32->pfrio_size, &io32->pfrio_nzero, io32->pfrio_flags | + PFR_FLAG_USERIOCTL); + break; + + case DIOCRTSTADDRS: + if (io32->pfrio_esize != sizeof (struct pfr_addr)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io32->pfrio_table); + error = pfr_tst_addrs(&io32->pfrio_table, io32->pfrio_buffer, + io32->pfrio_size, &io32->pfrio_nmatch, io32->pfrio_flags | + PFR_FLAG_USERIOCTL); + break; + + case DIOCRINADEFINE: + if (io32->pfrio_esize != sizeof (struct pfr_addr)) { + error = ENODEV; + break; + } + pfr_table_copyin_cleanup(&io32->pfrio_table); + error = pfr_ina_define(&io32->pfrio_table, io32->pfrio_buffer, + io32->pfrio_size, &io32->pfrio_nadd, &io32->pfrio_naddr, + io32->pfrio_ticket, io32->pfrio_flags | PFR_FLAG_USERIOCTL); + break; + + default: + VERIFY(0); + /* NOTREACHED */ + } + +done: + return (error); +} + +static int +pfioctl_ioc_tokens(u_long cmd, struct pfioc_tokens_32 *tok32, + struct pfioc_tokens_64 *tok64, struct proc *p) +{ + struct pfioc_token *tokens; + struct pfioc_kernel_token *entry, *tmp; + user_addr_t token_buf; + int ocnt, cnt, error = 0, p64 = proc_is64bit(p); + char *ptr; + + switch (cmd) { + case DIOCGETSTARTERS: { + int size; + + if (nr_tokens == 0) { + error = ENOENT; + break; + } + + size = sizeof (struct pfioc_token) * nr_tokens; + ocnt = cnt = (p64 ? tok64->size : tok32->size); + if (cnt == 0) { + if (p64) + tok64->size = size; + else + tok32->size = size; + break; + } + + token_buf = (p64 ? tok64->pgt_buf : tok32->pgt_buf); + tokens = _MALLOC(size, M_TEMP, M_WAITOK|M_ZERO); + if (tokens == NULL) { + error = ENOMEM; + break; + } + + ptr = (void *)tokens; + SLIST_FOREACH_SAFE(entry, &token_list_head, next, tmp) { + struct pfioc_token *t; + + if ((unsigned)cnt < sizeof (*tokens)) + break; /* no more buffer space left */ + + t = (struct pfioc_token *)(void *)ptr; + t->token_value = entry->token.token_value; + t->timestamp = entry->token.timestamp; + t->pid = entry->token.pid; + bcopy(entry->token.proc_name, t->proc_name, + PFTOK_PROCNAME_LEN); + ptr += sizeof (struct pfioc_token); + + cnt -= sizeof (struct pfioc_token); + } + + if (cnt < ocnt) + error = copyout(tokens, token_buf, ocnt - cnt); + + if (p64) + tok64->size = ocnt - cnt; + else + tok32->size = ocnt - cnt; + + _FREE(tokens, M_TEMP); + break; + } + + default: + VERIFY(0); + /* NOTREACHED */ + } + + return (error); +} + +static void +pf_expire_states_and_src_nodes(struct pf_rule *rule) +{ + struct pf_state *state; + struct pf_src_node *sn; + int killed = 0; + + /* expire the states */ + state = TAILQ_FIRST(&state_list); + while (state) { + if (state->rule.ptr == rule) + state->timeout = PFTM_PURGE; + state = TAILQ_NEXT(state, entry_list); + } + pf_purge_expired_states(pf_status.states); + + /* expire the src_nodes */ + RB_FOREACH(sn, pf_src_tree, &tree_src_tracking) { + if (sn->rule.ptr != rule) + continue; + if (sn->states != 0) { + RB_FOREACH(state, pf_state_tree_id, + &tree_id) { + if (state->src_node == sn) + state->src_node = NULL; + if (state->nat_src_node == sn) + state->nat_src_node = NULL; + } + sn->states = 0; + } + sn->expire = 1; + killed++; + } + if (killed) + pf_purge_expired_src_nodes(); +} + +static void +pf_delete_rule_from_ruleset(struct pf_ruleset *ruleset, int rs_num, + struct pf_rule *rule) +{ + struct pf_rule *r; + int nr = 0; + + pf_expire_states_and_src_nodes(rule); + + pf_rm_rule(ruleset->rules[rs_num].active.ptr, rule); + if (ruleset->rules[rs_num].active.rcount-- == 0) + panic("%s: rcount value broken!", __func__); + r = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr); + + while (r) { + r->nr = nr++; + r = TAILQ_NEXT(r, entries); + } +} + + +static void +pf_ruleset_cleanup(struct pf_ruleset *ruleset, int rs) +{ + pf_calc_skip_steps(ruleset->rules[rs].active.ptr); + ruleset->rules[rs].active.ticket = + ++ruleset->rules[rs].inactive.ticket; +} + +/* + * req_dev encodes the PF interface. Currently, possible values are + * 0 or PFRULE_PFM + */ +static int +pf_delete_rule_by_ticket(struct pfioc_rule *pr, u_int32_t req_dev) +{ + struct pf_ruleset *ruleset; + struct pf_rule *rule = NULL; + int is_anchor; + int error; + int i; + + is_anchor = (pr->anchor_call[0] != '\0'); + if ((ruleset = pf_find_ruleset_with_owner(pr->anchor, + pr->rule.owner, is_anchor, &error)) == NULL) + return (error); + + for (i = 0; i < PF_RULESET_MAX && rule == NULL; i++) { + rule = TAILQ_FIRST(ruleset->rules[i].active.ptr); + while (rule && (rule->ticket != pr->rule.ticket)) + rule = TAILQ_NEXT(rule, entries); + } + if (rule == NULL) + return (ENOENT); + else + i--; + + if (strcmp(rule->owner, pr->rule.owner)) + return (EACCES); + +delete_rule: + if (rule->anchor && (ruleset != &pf_main_ruleset) && + ((strcmp(ruleset->anchor->owner, "")) == 0) && + ((ruleset->rules[i].active.rcount - 1) == 0)) { + /* set rule & ruleset to parent and repeat */ + struct pf_rule *delete_rule = rule; + struct pf_ruleset *delete_ruleset = ruleset; + +#define parent_ruleset ruleset->anchor->parent->ruleset + if (ruleset->anchor->parent == NULL) + ruleset = &pf_main_ruleset; + else + ruleset = &parent_ruleset; + + rule = TAILQ_FIRST(ruleset->rules[i].active.ptr); + while (rule && + (rule->anchor != delete_ruleset->anchor)) + rule = TAILQ_NEXT(rule, entries); + if (rule == NULL) + panic("%s: rule not found!", __func__); + + /* + * if reqest device != rule's device, bail : + * with error if ticket matches; + * without error if ticket doesn't match (i.e. its just cleanup) + */ + if ((rule->rule_flag & PFRULE_PFM) ^ req_dev) { + if (rule->ticket != pr->rule.ticket) { + return (0); + } else { + return EACCES; + } + } + + if (delete_rule->rule_flag & PFRULE_PFM) { + pffwrules--; + } + + pf_delete_rule_from_ruleset(delete_ruleset, + i, delete_rule); + delete_ruleset->rules[i].active.ticket = + ++delete_ruleset->rules[i].inactive.ticket; + goto delete_rule; + } else { + /* + * process deleting rule only if device that added the + * rule matches device that issued the request + */ + if ((rule->rule_flag & PFRULE_PFM) ^ req_dev) + return EACCES; + if (rule->rule_flag & PFRULE_PFM) + pffwrules--; + pf_delete_rule_from_ruleset(ruleset, i, + rule); + pf_ruleset_cleanup(ruleset, i); + } + + return (0); +} + +/* + * req_dev encodes the PF interface. Currently, possible values are + * 0 or PFRULE_PFM + */ +static void +pf_delete_rule_by_owner(char *owner, u_int32_t req_dev) +{ + struct pf_ruleset *ruleset; + struct pf_rule *rule, *next; + int deleted = 0; + + for (int rs = 0; rs < PF_RULESET_MAX; rs++) { + rule = TAILQ_FIRST(pf_main_ruleset.rules[rs].active.ptr); + ruleset = &pf_main_ruleset; + while (rule) { + next = TAILQ_NEXT(rule, entries); + /* + * process deleting rule only if device that added the + * rule matches device that issued the request + */ + if ((rule->rule_flag & PFRULE_PFM) ^ req_dev) { + rule = next; + continue; + } + if (rule->anchor) { + if (((strcmp(rule->owner, owner)) == 0) || + ((strcmp(rule->owner, "")) == 0)) { + if (rule->anchor->ruleset.rules[rs].active.rcount > 0) { + if (deleted) { + pf_ruleset_cleanup(ruleset, rs); + deleted = 0; + } + /* step into anchor */ + ruleset = + &rule->anchor->ruleset; + rule = TAILQ_FIRST(ruleset->rules[rs].active.ptr); + continue; + } else { + if (rule->rule_flag & + PFRULE_PFM) + pffwrules--; + pf_delete_rule_from_ruleset(ruleset, rs, rule); + deleted = 1; + rule = next; + } + } else + rule = next; + } else { + if (((strcmp(rule->owner, owner)) == 0)) { + /* delete rule */ + if (rule->rule_flag & PFRULE_PFM) + pffwrules--; + pf_delete_rule_from_ruleset(ruleset, + rs, rule); + deleted = 1; + } + rule = next; + } + if (rule == NULL) { + if (deleted) { + pf_ruleset_cleanup(ruleset, rs); + deleted = 0; + } + if (ruleset != &pf_main_ruleset) + pf_deleterule_anchor_step_out(&ruleset, + rs, &rule); + } + } + } +} + +static void +pf_deleterule_anchor_step_out(struct pf_ruleset **ruleset_ptr, + int rs, struct pf_rule **rule_ptr) +{ + struct pf_ruleset *ruleset = *ruleset_ptr; + struct pf_rule *rule = *rule_ptr; + + /* step out of anchor */ + struct pf_ruleset *rs_copy = ruleset; + ruleset = ruleset->anchor->parent? + &ruleset->anchor->parent->ruleset:&pf_main_ruleset; + + rule = TAILQ_FIRST(ruleset->rules[rs].active.ptr); + while (rule && (rule->anchor != rs_copy->anchor)) + rule = TAILQ_NEXT(rule, entries); + if (rule == NULL) + panic("%s: parent rule of anchor not found!", __func__); + if (rule->anchor->ruleset.rules[rs].active.rcount > 0) + rule = TAILQ_NEXT(rule, entries); + + *ruleset_ptr = ruleset; + *rule_ptr = rule; +} + +static void +pf_addrwrap_setup(struct pf_addr_wrap *aw) +{ + VERIFY(aw); + bzero(&aw->p, sizeof aw->p); +} + +static int +pf_rule_setup(struct pfioc_rule *pr, struct pf_rule *rule, + struct pf_ruleset *ruleset) { + struct pf_pooladdr *apa; + int error = 0; + + if (rule->ifname[0]) { + rule->kif = pfi_kif_get(rule->ifname); + if (rule->kif == NULL) { + pool_put(&pf_rule_pl, rule); + return (EINVAL); + } + pfi_kif_ref(rule->kif, PFI_KIF_REF_RULE); + } +#if PF_ALTQ + /* set queue IDs */ + if (altq_allowed && 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 /* PF_ALTQ */ + 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 PFLOG + if (!rule->log) + rule->logif = 0; + if (rule->logif >= PFLOGIFS_MAX) + error = EINVAL; +#endif /* PFLOG */ + pf_addrwrap_setup(&rule->src.addr); + pf_addrwrap_setup(&rule->dst.addr); + 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(apa, &pf_pabuf, entries) + if (pf_tbladdr_setup(ruleset, &apa->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); + return (error); + } + rule->rpool.cur = TAILQ_FIRST(&rule->rpool.list); + rule->evaluations = rule->packets[0] = rule->packets[1] = + rule->bytes[0] = rule->bytes[1] = 0; + + return (0); +} + +static int +pfioctl_ioc_rule(u_long cmd, int minordev, struct pfioc_rule *pr, struct proc *p) +{ + int error = 0; + u_int32_t req_dev = 0; + + switch (cmd) { + case DIOCADDRULE: { + struct pf_ruleset *ruleset; + struct pf_rule *rule, *tail; + int rs_num; + + pr->anchor[sizeof (pr->anchor) - 1] = '\0'; + pr->anchor_call[sizeof (pr->anchor_call) - 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; } @@ -1389,16 +3145,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = ENOMEM; break; } - bcopy(&pr->rule, rule, sizeof (struct pf_rule)); - rule->cuid = kauth_cred_getuid(p->p_ucred); - rule->cpid = p->p_pid; - 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; + pf_rule_copyin(&pr->rule, rule, p, minordev); #if !INET if (rule->af == AF_INET) { pool_put(&pf_rule_pl, rule); @@ -1419,97 +3166,25 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) 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); - } - -#if 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 /* ALTQ */ - 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 PFLOG - if (!rule->log) - rule->logif = 0; - if (rule->logif >= PFLOGIFS_MAX) - error = EINVAL; -#endif /* PFLOG */ - 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(apa, &pf_pabuf, entries) - if (pf_tbladdr_setup(ruleset, &apa->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); + if ((error = pf_rule_setup(pr, rule, ruleset))) break; - } - 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++; + if (rule->rule_flag & PFRULE_PFM) + pffwrules++; 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; + pr->anchor[sizeof (pr->anchor) - 1] = '\0'; + pr->anchor_call[sizeof (pr->anchor_call) - 1] = '\0'; ruleset = pf_find_ruleset(pr->anchor); if (ruleset == NULL) { error = EINVAL; @@ -1531,12 +3206,12 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } 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; + pr->anchor[sizeof (pr->anchor) - 1] = '\0'; + pr->anchor_call[sizeof (pr->anchor_call) - 1] = '\0'; ruleset = pf_find_ruleset(pr->anchor); if (ruleset == NULL) { error = EINVAL; @@ -1558,7 +3233,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = EBUSY; break; } - bcopy(rule, &pr->rule, sizeof (struct pf_rule)); + pf_rule_copyout(rule, &pr->rule); if (pf_anchor_copyout(ruleset, rule, pr)) { error = EBUSY; break; @@ -1585,9 +3260,10 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } case DIOCCHANGERULE: { - struct pfioc_rule *pcr = (struct pfioc_rule *)addr; + struct pfioc_rule *pcr = pr; struct pf_ruleset *ruleset; struct pf_rule *oldrule = NULL, *newrule = NULL; + struct pf_pooladdr *pa; u_int32_t nr = 0; int rs_num; @@ -1603,6 +3279,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = EINVAL; break; } + pcr->anchor[sizeof (pcr->anchor) - 1] = '\0'; + pcr->anchor_call[sizeof (pcr->anchor_call) - 1] = '\0'; ruleset = pf_find_ruleset(pcr->anchor); if (ruleset == NULL) { error = EINVAL; @@ -1635,13 +3313,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = ENOMEM; break; } - bcopy(&pcr->rule, newrule, sizeof (struct pf_rule)); - newrule->cuid = kauth_cred_getuid(p->p_ucred); - newrule->cpid = p->p_pid; - TAILQ_INIT(&newrule->rpool.list); - /* initialize refcounting */ - newrule->states = 0; - newrule->entries.tqe_prev = NULL; + pf_rule_copyin(&pcr->rule, newrule, p, minordev); #if !INET if (newrule->af == AF_INET) { pool_put(&pf_rule_pl, newrule); @@ -1667,20 +3339,20 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } else newrule->kif = NULL; -#if ALTQ +#if PF_ALTQ /* set queue IDs */ - if (newrule->qname[0] != 0) { + if (altq_allowed && newrule->qname[0] != '\0') { if ((newrule->qid = pf_qname2qid(newrule->qname)) == 0) error = EBUSY; - else if (newrule->pqname[0] != 0) { + else if (newrule->pqname[0] != '\0') { if ((newrule->pqid = pf_qname2qid(newrule->pqname)) == 0) error = EBUSY; } else newrule->pqid = newrule->qid; } -#endif /* ALTQ */ +#endif /* PF_ALTQ */ if (newrule->tagname[0]) if ((newrule->tag = pf_tagname2tag(newrule->tagname)) == 0) @@ -1697,6 +3369,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) if (newrule->logif >= PFLOGIFS_MAX) error = EINVAL; #endif /* PFLOG */ + pf_addrwrap_setup(&newrule->src.addr); + pf_addrwrap_setup(&newrule->dst.addr); if (pf_rtlabel_add(&newrule->src.addr) || pf_rtlabel_add(&newrule->dst.addr)) error = EBUSY; @@ -1781,29 +3455,201 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) ruleset->rules[rs_num].active.rcount++; } - nr = 0; - TAILQ_FOREACH(oldrule, - ruleset->rules[rs_num].active.ptr, entries) - oldrule->nr = nr++; + 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 DIOCINSERTRULE: { + struct pf_ruleset *ruleset; + struct pf_rule *rule, *tail, *r; + int rs_num; + int is_anchor; + + pr->anchor[sizeof (pr->anchor) - 1] = '\0'; + pr->anchor_call[sizeof (pr->anchor_call) - 1] = '\0'; + is_anchor = (pr->anchor_call[0] != '\0'); + + if ((ruleset = pf_find_ruleset_with_owner(pr->anchor, + pr->rule.owner, is_anchor, &error)) == NULL) + 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; + } + + /* make sure this anchor rule doesn't exist already */ + if (is_anchor) { + r = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr); + while (r) { + if (r->anchor && + ((strcmp(r->anchor->name, + pr->anchor_call)) == 0)) { + if (((strcmp(pr->rule.owner, + r->owner)) == 0) || + ((strcmp(r->owner, "")) == 0)) + error = EEXIST; + else + error = EPERM; + break; + } + r = TAILQ_NEXT(r, entries); + } + if (error != 0) + return (error); + } + + rule = pool_get(&pf_rule_pl, PR_WAITOK); + if (rule == NULL) { + error = ENOMEM; + break; + } + pf_rule_copyin(&pr->rule, rule, p, minordev); +#if !INET + if (rule->af == AF_INET) { + pool_put(&pf_rule_pl, rule); + error = EAFNOSUPPORT; + break; + } +#endif /* INET */ +#if !INET6 + if (rule->af == AF_INET6) { + pool_put(&pf_rule_pl, rule); + error = EAFNOSUPPORT; + break; + } + +#endif /* INET6 */ + r = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr); + while ((r != NULL) && (rule->priority >= (unsigned)r->priority)) + r = TAILQ_NEXT(r, entries); + if (r == NULL) { + if ((tail = + TAILQ_LAST(ruleset->rules[rs_num].active.ptr, + pf_rulequeue)) != NULL) + rule->nr = tail->nr + 1; + else + rule->nr = 0; + } else { + rule->nr = r->nr; + } + + if ((error = pf_rule_setup(pr, rule, ruleset))) + break; + + if (rule->anchor != NULL) + strlcpy(rule->anchor->owner, rule->owner, + PF_OWNER_NAME_SIZE); + + if (r) { + TAILQ_INSERT_BEFORE(r, rule, entries); + while (r && ++r->nr) + r = TAILQ_NEXT(r, entries); + } else + TAILQ_INSERT_TAIL(ruleset->rules[rs_num].active.ptr, + rule, entries); + ruleset->rules[rs_num].active.rcount++; + + /* Calculate checksum for the main ruleset */ + if (ruleset == &pf_main_ruleset) + error = pf_setup_pfsync_matching(ruleset); + + pf_ruleset_cleanup(ruleset, rs_num); + rule->ticket = VM_KERNEL_ADDRPERM((u_int64_t)(uintptr_t)rule); - ruleset->rules[rs_num].active.ticket++; + pr->rule.ticket = rule->ticket; + pf_rule_copyout(rule, &pr->rule); + if (rule->rule_flag & PFRULE_PFM) + pffwrules++; + break; + } - pf_calc_skip_steps(ruleset->rules[rs_num].active.ptr); - pf_remove_if_empty_ruleset(ruleset); + case DIOCDELETERULE: { + pr->anchor[sizeof (pr->anchor) - 1] = '\0'; + pr->anchor_call[sizeof (pr->anchor_call) - 1] = '\0'; + + if (pr->rule.return_icmp >> 8 > ICMP_MAXTYPE) { + error = EINVAL; + break; + } + /* get device through which request is made */ + if ((uint8_t)minordev == PFDEV_PFM) + req_dev |= PFRULE_PFM; + + if (pr->rule.ticket) { + if ((error = pf_delete_rule_by_ticket(pr, req_dev))) + break; + } else + pf_delete_rule_by_owner(pr->rule.owner, req_dev); + pr->nr = pffwrules; break; } + default: + VERIFY(0); + /* NOTREACHED */ + } + + return (error); +} + +static int +pfioctl_ioc_state_kill(u_long cmd, struct pfioc_state_kill *psk, struct proc *p) +{ +#pragma unused(p) + int error = 0; + + psk->psk_ifname[sizeof (psk->psk_ifname) - 1] = '\0'; + psk->psk_ownername[sizeof(psk->psk_ownername) - 1] = '\0'; + + bool ifname_matched = true; + bool owner_matched = true; + + switch (cmd) { case DIOCCLRSTATES: { struct pf_state *s, *nexts; - struct pfioc_state_kill *psk = (struct pfioc_state_kill *)addr; int killed = 0; for (s = RB_MIN(pf_state_tree_id, &tree_id); s; s = nexts) { nexts = RB_NEXT(pf_state_tree_id, &tree_id, s); + /* + * Purge all states only when neither ifname + * or owner is provided. If any of these are provided + * we purge only the states with meta data that match + */ + bool unlink_state = false; + ifname_matched = true; + owner_matched = true; + + if (psk->psk_ifname[0] && + strcmp(psk->psk_ifname, s->kif->pfik_name)) { + ifname_matched = false; + } + + if (psk->psk_ownername[0] && + ((NULL == s->rule.ptr) || + strcmp(psk->psk_ownername, s->rule.ptr->owner))) { + owner_matched = false; + } + + unlink_state = ifname_matched && owner_matched; - if (!psk->psk_ifname[0] || strcmp(psk->psk_ifname, - s->kif->pfik_name) == 0) { + if (unlink_state) { #if NPFSYNC /* don't send out individual delete messages */ s->sync_flags = PFSTATE_NOSYNC; @@ -1823,13 +3669,25 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) struct pf_state *s, *nexts; struct pf_state_key *sk; struct pf_state_host *src, *dst; - struct pfioc_state_kill *psk = (struct pfioc_state_kill *)addr; int killed = 0; for (s = RB_MIN(pf_state_tree_id, &tree_id); s; s = nexts) { nexts = RB_NEXT(pf_state_tree_id, &tree_id, s); sk = s->state_key; + ifname_matched = true; + owner_matched = true; + + if (psk->psk_ifname[0] && + strcmp(psk->psk_ifname, s->kif->pfik_name)) { + ifname_matched = false; + } + + if (psk->psk_ownername[0] && + ((NULL == s->rule.ptr) || + strcmp(psk->psk_ownername, s->rule.ptr->owner))) { + owner_matched = false; + } if (sk->direction == PF_OUT) { src = &sk->lan; @@ -1848,25 +3706,14 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) &psk->psk_dst.addr.v.a.addr, &psk->psk_dst.addr.v.a.mask, &dst->addr, sk->af) && -#ifndef NO_APPLE_EXTENSIONS (pf_match_xport(psk->psk_proto, psk->psk_proto_variant, &psk->psk_src.xport, &src->xport)) && (pf_match_xport(psk->psk_proto, psk->psk_proto_variant, &psk->psk_dst.xport, &dst->xport)) && -#else - (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)) && -#endif - (!psk->psk_ifname[0] || strcmp(psk->psk_ifname, - s->kif->pfik_name) == 0)) { + ifname_matched && + owner_matched) { #if NPFSYNC /* send immediate delete of state */ pfsync_delete_state(s); @@ -1880,15 +3727,28 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } + default: + VERIFY(0); + /* NOTREACHED */ + } + + return (error); +} + +static int +pfioctl_ioc_state(u_long cmd, struct pfioc_state *ps, struct proc *p) +{ +#pragma unused(p) + int error = 0; + + switch (cmd) { case DIOCADDSTATE: { - struct pfioc_state *ps = (struct pfioc_state *)addr; - struct pfsync_state *sp = &ps->state; + struct pfsync_state *sp = &ps->state; struct pf_state *s; struct pf_state_key *sk; struct pfi_kif *kif; - if (sp->timeout >= PFTM_MAX && - sp->timeout != PFTM_UNTIL_PACKET) { + if (sp->timeout >= PFTM_MAX) { error = EINVAL; break; } @@ -1898,7 +3758,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } bzero(s, sizeof (struct pf_state)); - if ((sk = pf_alloc_state_key(s)) == NULL) { + if ((sk = pf_alloc_state_key(s, NULL)) == NULL) { pool_put(&pf_state_pl, s); error = ENOMEM; break; @@ -1911,10 +3771,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = ENOENT; break; } -#ifndef NO_APPLE_EXTENSIONS TAILQ_INIT(&s->unlink_hooks); s->state_key->app_state = 0; -#endif if (pf_insert_state(kif, s)) { pfi_kif_unref(kif, PFI_KIF_REF_NONE); pool_put(&pf_state_pl, s); @@ -1922,11 +3780,11 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } pf_default_rule.states++; + VERIFY(pf_default_rule.states != 0); break; } case DIOCGETSTATE: { - struct pfioc_state *ps = (struct pfioc_state *)addr; struct pf_state *s; struct pf_state_cmp id_key; @@ -1943,78 +3801,91 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } - case DIOCGETSTATES: { - struct pfioc_states *ps = (struct pfioc_states *)addr; + default: + VERIFY(0); + /* NOTREACHED */ + } + + return (error); +} + +static int +pfioctl_ioc_states(u_long cmd, struct pfioc_states_32 *ps32, + struct pfioc_states_64 *ps64, struct proc *p) +{ + int p64 = proc_is64bit(p); + int error = 0; + + switch (cmd) { + case DIOCGETSTATES: { /* struct pfioc_states */ struct pf_state *state; - struct pfsync_state *y, *pstore; + struct pfsync_state *pstore; + user_addr_t buf; u_int32_t nr = 0; + int len, size; - if (ps->ps_len == 0) { - nr = pf_status.states; - ps->ps_len = sizeof (struct pfsync_state) * nr; + len = (p64 ? ps64->ps_len : ps32->ps_len); + if (len == 0) { + size = sizeof (struct pfsync_state) * pf_status.states; + if (p64) + ps64->ps_len = size; + else + ps32->ps_len = size; break; } pstore = _MALLOC(sizeof (*pstore), M_TEMP, M_WAITOK); - - y = ps->ps_states; + if (pstore == NULL) { + error = ENOMEM; + break; + } + buf = (p64 ? ps64->ps_buf : ps32->ps_buf); state = TAILQ_FIRST(&state_list); while (state) { if (state->timeout != PFTM_UNLINKED) { - if ((nr+1) * sizeof (*y) > (unsigned)ps->ps_len) + if ((nr + 1) * sizeof (*pstore) > (unsigned)len) break; pf_state_export(pstore, state->state_key, state); - error = copyout(pstore, CAST_USER_ADDR_T(y), - sizeof (*y)); + error = copyout(pstore, buf, sizeof (*pstore)); if (error) { _FREE(pstore, M_TEMP); goto fail; } - y++; + buf += sizeof (*pstore); nr++; } state = TAILQ_NEXT(state, entry_list); } - ps->ps_len = sizeof (struct pfsync_state) * nr; + size = sizeof (struct pfsync_state) * nr; + if (p64) + ps64->ps_len = size; + else + ps32->ps_len = size; _FREE(pstore, M_TEMP); break; } - case DIOCGETSTATUS: { - struct pf_status *s = (struct pf_status *)addr; - bcopy(&pf_status, s, sizeof (struct pf_status)); - pfi_update_status(s->ifname, s); - break; - } - - case DIOCSETSTATUSIF: { - struct pfioc_if *pi = (struct pfioc_if *)addr; - - if (pi->ifname[0] == 0) { - bzero(pf_status.ifname, IFNAMSIZ); - break; - } - strlcpy(pf_status.ifname, pi->ifname, IFNAMSIZ); - break; + default: + VERIFY(0); + /* NOTREACHED */ } +fail: + return (error); +} - 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 = pf_time_second(); - if (*pf_status.ifname) - pfi_update_status(pf_status.ifname, NULL); - break; - } +static int +pfioctl_ioc_natlook(u_long cmd, struct pfioc_natlook *pnl, struct proc *p) +{ +#pragma unused(p) + int error = 0; + switch (cmd) { case DIOCNATLOOK: { - struct pfioc_natlook *pnl = (struct pfioc_natlook *)addr; struct pf_state_key *sk; struct pf_state *state; struct pf_state_key_cmp key; @@ -2022,21 +3893,14 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) key.af = pnl->af; key.proto = pnl->proto; - -#ifndef NO_APPLE_EXTENSIONS key.proto_variant = pnl->proto_variant; -#endif if (!pnl->proto || PF_AZERO(&pnl->saddr, pnl->af) || PF_AZERO(&pnl->daddr, pnl->af) || ((pnl->proto == IPPROTO_TCP || pnl->proto == IPPROTO_UDP) && -#ifndef NO_APPLE_EXTENSIONS (!pnl->dxport.port || !pnl->sxport.port))) -#else - (!pnl->dport || !pnl->sport))) -#endif error = EINVAL; else { /* @@ -2047,35 +3911,19 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) */ if (direction == PF_IN) { PF_ACPY(&key.ext.addr, &pnl->daddr, pnl->af); -#ifndef NO_APPLE_EXTENSIONS memcpy(&key.ext.xport, &pnl->dxport, sizeof (key.ext.xport)); -#else - key.ext.port = pnl->dport; -#endif PF_ACPY(&key.gwy.addr, &pnl->saddr, pnl->af); -#ifndef NO_APPLE_EXTENSIONS memcpy(&key.gwy.xport, &pnl->sxport, sizeof (key.gwy.xport)); -#else - key.gwy.port = pnl->sport; -#endif state = pf_find_state_all(&key, PF_IN, &m); } else { PF_ACPY(&key.lan.addr, &pnl->daddr, pnl->af); -#ifndef NO_APPLE_EXTENSIONS memcpy(&key.lan.xport, &pnl->dxport, sizeof (key.lan.xport)); -#else - key.lan.port = pnl->dport; -#endif PF_ACPY(&key.ext.addr, &pnl->saddr, pnl->af); -#ifndef NO_APPLE_EXTENSIONS memcpy(&key.ext.xport, &pnl->sxport, sizeof (key.ext.xport)); -#else - key.ext.port = pnl->sport; -#endif state = pf_find_state_all(&key, PF_OUT, &m); } if (m > 1) @@ -2085,37 +3933,21 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) if (direction == PF_IN) { PF_ACPY(&pnl->rsaddr, &sk->lan.addr, sk->af); -#ifndef NO_APPLE_EXTENSIONS memcpy(&pnl->rsxport, &sk->lan.xport, sizeof (pnl->rsxport)); -#else - pnl->rsport = sk->lan.port; -#endif PF_ACPY(&pnl->rdaddr, &pnl->daddr, pnl->af); -#ifndef NO_APPLE_EXTENSIONS memcpy(&pnl->rdxport, &pnl->dxport, sizeof (pnl->rdxport)); -#else - pnl->rdport = pnl->dport; -#endif } else { PF_ACPY(&pnl->rdaddr, &sk->gwy.addr, sk->af); -#ifndef NO_APPLE_EXTENSIONS memcpy(&pnl->rdxport, &sk->gwy.xport, sizeof (pnl->rdxport)); -#else - pnl->rdport = sk->gwy.port; -#endif PF_ACPY(&pnl->rsaddr, &pnl->saddr, pnl->af); -#ifndef NO_APPLE_EXTENSIONS memcpy(&pnl->rsxport, &pnl->sxport, sizeof (pnl->rsxport)); -#else - pnl->rsport = pnl->sport; -#endif } } else error = ENOENT; @@ -2123,246 +3955,114 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) 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_fn); - 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; - } - pool_sethardlimit(pf_pool_limits[pl->index].pp, - pl->limit, NULL, 0); - 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; - } - -#if ALTQ - case DIOCSTARTALTQ: { - struct pf_altq *altq; - - /* enable all altq interfaces on active list */ - TAILQ_FOREACH(altq, pf_altqs_active, entries) { - if (altq->qname[0] == 0) { - 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) { - if (altq->qname[0] == 0) { - error = pf_disable_altq(altq); - if (error != 0) - break; - } - } - if (error == 0) - pf_altq_running = 0; - DPFPRINTF(PF_DEBUG_MISC, ("altq: stopped\n")); - break; + default: + VERIFY(0); + /* NOTREACHED */ } - 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_WAITOK); - if (altq == NULL) { - error = ENOMEM; - break; - } - bcopy(&pa->altq, altq, sizeof (struct pf_altq)); + return (error); +} - /* - * 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; - } - } - } +static int +pfioctl_ioc_tm(u_long cmd, struct pfioc_tm *pt, struct proc *p) +{ +#pragma unused(p) + int error = 0; - error = altq_add(altq); - if (error) { - pool_put(&pf_altq_pl, altq); - break; - } + switch (cmd) { + case DIOCSETTIMEOUT: { + int old; - TAILQ_INSERT_TAIL(pf_altqs_inactive, altq, entries); - bcopy(altq, &pa->altq, sizeof (struct pf_altq)); + 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_fn); + pt->seconds = old; 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; + case DIOCGETTIMEOUT: { + if (pt->timeout < 0 || pt->timeout >= PFTM_MAX) { + error = EINVAL; + goto fail; + } + pt->seconds = pf_default_rule.timeout[pt->timeout]; break; } - case DIOCGETALTQ: { - struct pfioc_altq *pa = (struct pfioc_altq *)addr; - struct pf_altq *altq; - u_int32_t nr; + default: + VERIFY(0); + /* NOTREACHED */ + } +fail: + return (error); +} - 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; +static int +pfioctl_ioc_limit(u_long cmd, struct pfioc_limit *pl, struct proc *p) +{ +#pragma unused(p) + int error = 0; + + switch (cmd) { + case DIOCGETLIMIT: { + + if (pl->index < 0 || pl->index >= PF_LIMIT_MAX) { + error = EINVAL; + goto fail; } - bcopy(altq, &pa->altq, sizeof (struct pf_altq)); + pl->limit = pf_pool_limits[pl->index].limit; 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; + case DIOCSETLIMIT: { + int old_limit; - 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; - } - error = altq_getqstats(altq, pq->buf, &nbytes); - if (error == 0) { - pq->scheduler = altq->scheduler; - pq->nbytes = nbytes; + if (pl->index < 0 || pl->index >= PF_LIMIT_MAX || + pf_pool_limits[pl->index].pp == NULL) { + error = EINVAL; + goto fail; } + pool_sethardlimit(pf_pool_limits[pl->index].pp, + pl->limit, NULL, 0); + old_limit = pf_pool_limits[pl->index].limit; + pf_pool_limits[pl->index].limit = pl->limit; + pl->limit = old_limit; break; } -#endif /* ALTQ */ - case DIOCBEGINADDRS: { - struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; + default: + VERIFY(0); + /* NOTREACHED */ + } +fail: + return (error); +} + +static int +pfioctl_ioc_pooladdr(u_long cmd, struct pfioc_pooladdr *pp, struct proc *p) +{ +#pragma unused(p) + struct pf_pooladdr *pa = NULL; + struct pf_pool *pool = NULL; + int error = 0; + switch (cmd) { + case DIOCBEGINADDRS: { pf_empty_pool(&pf_pabuf); pp->ticket = ++ticket_pabuf; break; } case DIOCADDADDR: { - struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; - + pp->anchor[sizeof (pp->anchor) - 1] = '\0'; if (pp->ticket != ticket_pabuf) { error = EBUSY; break; @@ -2390,7 +4090,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = ENOMEM; break; } - bcopy(&pp->addr, pa, sizeof (struct pf_pooladdr)); + pf_pooladdr_copyin(&pp->addr, pa); if (pa->ifname[0]) { pa->kif = pfi_kif_get(pa->ifname); if (pa->kif == NULL) { @@ -2400,6 +4100,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } pfi_kif_ref(pa->kif, PFI_KIF_REF_RULE); } + pf_addrwrap_setup(&pa->addr); if (pfi_dynaddr_setup(&pa->addr, pp->af)) { pfi_dynaddr_remove(&pa->addr); pfi_kif_unref(pa->kif, PFI_KIF_REF_RULE); @@ -2412,9 +4113,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } case DIOCGETADDRS: { - struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; - pp->nr = 0; + pp->anchor[sizeof (pp->anchor) - 1] = '\0'; pool = pf_get_pool(pp->anchor, pp->ticket, pp->r_action, pp->r_num, 0, 1, 0); if (pool == NULL) { @@ -2427,9 +4127,9 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } case DIOCGETADDR: { - struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; u_int32_t nr = 0; + pp->anchor[sizeof (pp->anchor) - 1] = '\0'; pool = pf_get_pool(pp->anchor, pp->ticket, pp->r_action, pp->r_num, 0, 1, 1); if (pool == NULL) { @@ -2445,7 +4145,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = EBUSY; break; } - bcopy(pa, &pp->addr, sizeof (struct pf_pooladdr)); + pf_pooladdr_copyout(pa, &pp->addr); pfi_dynaddr_copyout(&pp->addr.addr); pf_tbladdr_copyout(&pp->addr.addr); pf_rtlabel_copyout(&pp->addr.addr); @@ -2453,7 +4153,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } case DIOCCHANGEADDR: { - struct pfioc_pooladdr *pca = (struct pfioc_pooladdr *)addr; + struct pfioc_pooladdr *pca = pp; struct pf_pooladdr *oldpa = NULL, *newpa = NULL; struct pf_ruleset *ruleset; @@ -2469,6 +4169,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } + pca->anchor[sizeof (pca->anchor) - 1] = '\0'; ruleset = pf_find_ruleset(pca->anchor); if (ruleset == NULL) { error = EBUSY; @@ -2486,7 +4187,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = ENOMEM; break; } - bcopy(&pca->addr, newpa, sizeof (struct pf_pooladdr)); + pf_pooladdr_copyin(&pca->addr, newpa); #if !INET if (pca->af == AF_INET) { pool_put(&pf_pooladdr_pl, newpa); @@ -2511,6 +4212,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) pfi_kif_ref(newpa->kif, PFI_KIF_REF_RULE); } else newpa->kif = NULL; + pf_addrwrap_setup(&newpa->addr); if (pfi_dynaddr_setup(&newpa->addr, pca->af) || pf_tbladdr_setup(ruleset, &newpa->addr)) { pfi_dynaddr_remove(&newpa->addr); @@ -2552,321 +4254,146 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) 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; + TAILQ_INSERT_AFTER(&pool->list, oldpa, + newpa, entries); } - error = pfr_get_addrs(&io->pfrio_table, io->pfrio_buffer, - &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); + + pool->cur = TAILQ_FIRST(&pool->list); + PF_ACPY(&pool->counter, &pool->cur->addr.v.a.addr, + pca->af); 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; + default: + VERIFY(0); + /* NOTREACHED */ } - case DIOCRCLRASTATS: { - struct pfioc_table *io = (struct pfioc_table *)addr; + return (error); +} - 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; - } +static int +pfioctl_ioc_ruleset(u_long cmd, struct pfioc_ruleset *pr, struct proc *p) +{ +#pragma unused(p) + int error = 0; - case DIOCRTSTADDRS: { - struct pfioc_table *io = (struct pfioc_table *)addr; + switch (cmd) { + case DIOCGETRULESETS: { + struct pf_ruleset *ruleset; + struct pf_anchor *anchor; - if (io->pfrio_esize != sizeof (struct pfr_addr)) { - error = ENODEV; + pr->path[sizeof (pr->path) - 1] = '\0'; + pr->name[sizeof (pr->name) - 1] = '\0'; + if ((ruleset = pf_find_ruleset(pr->path)) == NULL) { + error = EINVAL; break; } - error = pfr_tst_addrs(&io->pfrio_table, io->pfrio_buffer, - io->pfrio_size, &io->pfrio_nmatch, io->pfrio_flags | - PFR_FLAG_USERIOCTL); + 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 DIOCRINADEFINE: { - struct pfioc_table *io = (struct pfioc_table *)addr; + case DIOCGETRULESET: { + struct pf_ruleset *ruleset; + struct pf_anchor *anchor; + u_int32_t nr = 0; - if (io->pfrio_esize != sizeof (struct pfr_addr)) { - error = ENODEV; + pr->path[sizeof (pr->path) - 1] = '\0'; + if ((ruleset = pf_find_ruleset(pr->path)) == NULL) { + error = EINVAL; 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); + 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 DIOCOSFPADD: { - struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr; - error = pf_osfp_add(io); - break; + default: + VERIFY(0); + /* NOTREACHED */ } - case DIOCOSFPGET: { - struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr; - error = pf_osfp_get(io); - break; - } + return (error); +} + +static int +pfioctl_ioc_trans(u_long cmd, struct pfioc_trans_32 *io32, + struct pfioc_trans_64 *io64, struct proc *p) +{ + int p64 = proc_is64bit(p); + int error = 0, esize, size; + user_addr_t buf; + esize = (p64 ? io64->esize : io32->esize); + size = (p64 ? io64->size : io32->size); + buf = (p64 ? io64->array : io32->array); + + switch (cmd) { 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)) { + if (esize != sizeof (*ioe)) { error = ENODEV; goto fail; } ioe = _MALLOC(sizeof (*ioe), M_TEMP, M_WAITOK); table = _MALLOC(sizeof (*table), M_TEMP, M_WAITOK); - for (i = 0; i < io->size; i++) { - if (copyin(CAST_USER_ADDR_T(io->array+i), ioe, - sizeof (*ioe))) { + for (i = 0; i < size; i++, buf += sizeof (*ioe)) { + if (copyin(buf, ioe, sizeof (*ioe))) { _FREE(table, M_TEMP); _FREE(ioe, M_TEMP); error = EFAULT; goto fail; } + ioe->anchor[sizeof (ioe->anchor) - 1] = '\0'; switch (ioe->rs_num) { case PF_RULESET_ALTQ: -#if 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; +#if PF_ALTQ + if (altq_allowed) { + if (ioe->anchor[0]) { + _FREE(table, M_TEMP); + _FREE(ioe, M_TEMP); + error = EINVAL; + goto fail; + } + error = pf_begin_altq(&ioe->ticket); + if (error != 0) { + _FREE(table, M_TEMP); + _FREE(ioe, M_TEMP); + goto fail; + } } -#endif /* ALTQ */ +#endif /* PF_ALTQ */ break; case PF_RULESET_TABLE: bzero(table, sizeof (*table)); @@ -2888,8 +4415,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } break; } - if (copyout(ioe, CAST_USER_ADDR_T(io->array+i), - sizeof (io->array[i]))) { + if (copyout(ioe, buf, sizeof (*ioe))) { _FREE(table, M_TEMP); _FREE(ioe, M_TEMP); error = EFAULT; @@ -2902,40 +4428,42 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } 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)) { + if (esize != sizeof (*ioe)) { error = ENODEV; goto fail; } ioe = _MALLOC(sizeof (*ioe), M_TEMP, M_WAITOK); table = _MALLOC(sizeof (*table), M_TEMP, M_WAITOK); - for (i = 0; i < io->size; i++) { - if (copyin(CAST_USER_ADDR_T(io->array+i), ioe, - sizeof (*ioe))) { + for (i = 0; i < size; i++, buf += sizeof (*ioe)) { + if (copyin(buf, ioe, sizeof (*ioe))) { _FREE(table, M_TEMP); _FREE(ioe, M_TEMP); error = EFAULT; goto fail; } + ioe->anchor[sizeof (ioe->anchor) - 1] = '\0'; switch (ioe->rs_num) { case PF_RULESET_ALTQ: -#if 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 */ +#if PF_ALTQ + if (altq_allowed) { + if (ioe->anchor[0]) { + _FREE(table, M_TEMP); + _FREE(ioe, M_TEMP); + error = EINVAL; + goto fail; + } + error = pf_rollback_altq(ioe->ticket); + if (error != 0) { + _FREE(table, M_TEMP); + _FREE(ioe, M_TEMP); + goto fail; /* really bad */ + } } -#endif /* ALTQ */ +#endif /* PF_ALTQ */ break; case PF_RULESET_TABLE: bzero(table, sizeof (*table)); @@ -2964,44 +4492,47 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } case DIOCXCOMMIT: { - struct pfioc_trans *io = (struct pfioc_trans *)addr; struct pfioc_trans_e *ioe; struct pfr_table *table; struct pf_ruleset *rs; + user_addr_t _buf = buf; int i; - if (io->esize != sizeof (*ioe)) { + if (esize != sizeof (*ioe)) { error = ENODEV; goto fail; } ioe = _MALLOC(sizeof (*ioe), M_TEMP, M_WAITOK); table = _MALLOC(sizeof (*table), M_TEMP, M_WAITOK); /* first makes sure everything will succeed */ - for (i = 0; i < io->size; i++) { - if (copyin(CAST_USER_ADDR_T(io->array+i), ioe, - sizeof (*ioe))) { + for (i = 0; i < size; i++, buf += sizeof (*ioe)) { + if (copyin(buf, ioe, sizeof (*ioe))) { _FREE(table, M_TEMP); _FREE(ioe, M_TEMP); error = EFAULT; goto fail; } + ioe->anchor[sizeof (ioe->anchor) - 1] = '\0'; switch (ioe->rs_num) { case PF_RULESET_ALTQ: -#if 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; +#if PF_ALTQ + if (altq_allowed) { + 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; + } } -#endif /* ALTQ */ +#endif /* PF_ALTQ */ break; case PF_RULESET_TABLE: rs = pf_find_ruleset(ioe->anchor); @@ -3034,24 +4565,26 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } } + buf = _buf; /* now do the commit - no errors should happen here */ - for (i = 0; i < io->size; i++) { - if (copyin(CAST_USER_ADDR_T(io->array+i), ioe, - sizeof (*ioe))) { + for (i = 0; i < size; i++, buf += sizeof (*ioe)) { + if (copyin(buf, ioe, sizeof (*ioe))) { _FREE(table, M_TEMP); _FREE(ioe, M_TEMP); error = EFAULT; goto fail; } + ioe->anchor[sizeof (ioe->anchor) - 1] = '\0'; switch (ioe->rs_num) { case PF_RULESET_ALTQ: -#if ALTQ - if ((error = pf_commit_altq(ioe->ticket))) { +#if PF_ALTQ + if (altq_allowed && + (error = pf_commit_altq(ioe->ticket))) { _FREE(table, M_TEMP); _FREE(ioe, M_TEMP); goto fail; /* really bad */ } -#endif /* ALTQ */ +#endif /* PF_ALTQ */ break; case PF_RULESET_TABLE: bzero(table, sizeof (*table)); @@ -3079,26 +4612,52 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } + default: + VERIFY(0); + /* NOTREACHED */ + } +fail: + return (error); +} + +static int +pfioctl_ioc_src_nodes(u_long cmd, struct pfioc_src_nodes_32 *psn32, + struct pfioc_src_nodes_64 *psn64, struct proc *p) +{ + int p64 = proc_is64bit(p); + int error = 0; + + switch (cmd) { case DIOCGETSRCNODES: { - struct pfioc_src_nodes *psn = (struct pfioc_src_nodes *)addr; - struct pf_src_node *n, *sn, *pstore; + struct pf_src_node *n, *pstore; + user_addr_t buf; u_int32_t nr = 0; - int space = psn->psn_len; + int space, size; + space = (p64 ? psn64->psn_len : psn32->psn_len); if (space == 0) { RB_FOREACH(n, pf_src_tree, &tree_src_tracking) nr++; - psn->psn_len = sizeof (struct pf_src_node) * nr; + + size = sizeof (struct pf_src_node) * nr; + if (p64) + psn64->psn_len = size; + else + psn32->psn_len = size; break; } pstore = _MALLOC(sizeof (*pstore), M_TEMP, M_WAITOK); + if (pstore == NULL) { + error = ENOMEM; + break; + } + buf = (p64 ? psn64->psn_buf : psn32->psn_buf); - sn = psn->psn_src_nodes; RB_FOREACH(n, pf_src_tree, &tree_src_tracking) { uint64_t secs = pf_time_second(), diff; - if ((nr + 1) * sizeof (*sn) > (unsigned)psn->psn_len) + if ((nr + 1) * sizeof (*pstore) > (unsigned)space) break; bcopy(n, pstore, sizeof (*pstore)); @@ -3119,43 +4678,49 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) n->conn_rate.count * diff / n->conn_rate.seconds; - error = copyout(pstore, CAST_USER_ADDR_T(sn), - sizeof (*sn)); + _RB_PARENT(pstore, entry) = NULL; + RB_LEFT(pstore, entry) = RB_RIGHT(pstore, entry) = NULL; + pstore->kif = NULL; + + error = copyout(pstore, buf, sizeof (*pstore)); if (error) { _FREE(pstore, M_TEMP); goto fail; } - sn++; + buf += sizeof (*pstore); nr++; } - psn->psn_len = sizeof (struct pf_src_node) * nr; + + size = sizeof (struct pf_src_node) * nr; + if (p64) + psn64->psn_len = size; + else + psn32->psn_len = size; _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(); - pf_status.src_nodes = 0; - break; + default: + VERIFY(0); + /* NOTREACHED */ } +fail: + return (error); + +} + +static int +pfioctl_ioc_src_node_kill(u_long cmd, struct pfioc_src_node_kill *psnk, + struct proc *p) +{ +#pragma unused(p) + int error = 0; + switch (cmd) { 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) { @@ -3190,73 +4755,92 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } - case DIOCSETHOSTID: { - u_int32_t *hid = (u_int32_t *)addr; - - if (*hid == 0) - pf_status.hostid = random(); - else - pf_status.hostid = *hid; - break; + default: + VERIFY(0); + /* NOTREACHED */ } - case DIOCOSFPFLUSH: - pf_osfp_flush(); - break; + return (error); +} + +static int +pfioctl_ioc_iface(u_long cmd, struct pfioc_iface_32 *io32, + struct pfioc_iface_64 *io64, struct proc *p) +{ + int p64 = proc_is64bit(p); + int error = 0; + switch (cmd) { case DIOCIGETIFACES: { - struct pfioc_iface *io = (struct pfioc_iface *)addr; + user_addr_t buf; + int esize; - if (io->pfiio_esize != sizeof (struct pfi_kif)) { + buf = (p64 ? io64->pfiio_buffer : io32->pfiio_buffer); + esize = (p64 ? io64->pfiio_esize : io32->pfiio_esize); + + /* esize must be that of the user space version of pfi_kif */ + if (esize != sizeof (struct pfi_uif)) { error = ENODEV; break; } - error = pfi_get_ifaces(io->pfiio_name, io->pfiio_buffer, - &io->pfiio_size); + if (p64) + io64->pfiio_name[sizeof (io64->pfiio_name) - 1] = '\0'; + else + io32->pfiio_name[sizeof (io32->pfiio_name) - 1] = '\0'; + error = pfi_get_ifaces( + p64 ? io64->pfiio_name : io32->pfiio_name, buf, + p64 ? &io64->pfiio_size : &io32->pfiio_size); break; } case DIOCSETIFFLAG: { - struct pfioc_iface *io = (struct pfioc_iface *)addr; + if (p64) + io64->pfiio_name[sizeof (io64->pfiio_name) - 1] = '\0'; + else + io32->pfiio_name[sizeof (io32->pfiio_name) - 1] = '\0'; - error = pfi_set_flags(io->pfiio_name, io->pfiio_flags); + error = pfi_set_flags( + p64 ? io64->pfiio_name : io32->pfiio_name, + p64 ? io64->pfiio_flags : io32->pfiio_flags); break; } case DIOCCLRIFFLAG: { - struct pfioc_iface *io = (struct pfioc_iface *)addr; + if (p64) + io64->pfiio_name[sizeof (io64->pfiio_name) - 1] = '\0'; + else + io32->pfiio_name[sizeof (io32->pfiio_name) - 1] = '\0'; - error = pfi_clear_flags(io->pfiio_name, io->pfiio_flags); + error = pfi_clear_flags( + p64 ? io64->pfiio_name : io32->pfiio_name, + p64 ? io64->pfiio_flags : io32->pfiio_flags); break; } default: - error = ENODEV; - break; + VERIFY(0); + /* NOTREACHED */ } -fail: - lck_mtx_unlock(pf_lock); - lck_rw_done(pf_perim_lock); return (error); } int pf_af_hook(struct ifnet *ifp, struct mbuf **mppn, struct mbuf **mp, - unsigned int af, int input) + unsigned int af, int input, struct ip_fw_args *fwa) { - int error = 0, reentry; - struct thread *curthread = current_thread(); + int error = 0; struct mbuf *nextpkt; + net_thread_marks_t marks; + struct ifnet * pf_ifp = ifp; + + marks = net_thread_marks_push(NET_THREAD_HELD_PF); - reentry = (ifp->if_pf_curthread == curthread); - if (!reentry) { + if (marks != net_thread_marks_none) { lck_rw_lock_shared(pf_perim_lock); - if (!pf_hooks_attached) + if (!pf_is_enabled) goto done; - lck_mtx_lock(pf_lock); - ifp->if_pf_curthread = curthread; } if (mppn != NULL && *mppn != NULL) @@ -3264,50 +4848,69 @@ pf_af_hook(struct ifnet *ifp, struct mbuf **mppn, struct mbuf **mp, if ((nextpkt = (*mp)->m_nextpkt) != NULL) (*mp)->m_nextpkt = NULL; + /* + * For packets destined to locally hosted IP address + * ip_output_list sets Mbuf's pkt header's rcvif to + * the interface hosting the IP address. + * While on the output path ifp passed to pf_af_hook + * to such local communication is the loopback interface, + * the input path derives ifp from mbuf packet header's + * rcvif. + * This asymmetry caues issues with PF. + * To handle that case, we have a limited change here to + * pass interface as loopback if packets are looped in. + */ + if (input && ((*mp)->m_pkthdr.pkt_flags & PKTF_LOOP)) { + pf_ifp = lo_ifp; + } + switch (af) { #if INET case AF_INET: { - error = pf_inet_hook(ifp, mp, input); + error = pf_inet_hook(pf_ifp, mp, input, fwa); break; } #endif /* INET */ #if INET6 case AF_INET6: - error = pf_inet6_hook(ifp, mp, input); + error = pf_inet6_hook(pf_ifp, mp, input, fwa); break; #endif /* INET6 */ default: break; } - if (nextpkt != NULL) { - if (*mp != NULL) { - struct mbuf *m = *mp; - while (m->m_nextpkt != NULL) - m = m->m_nextpkt; - m->m_nextpkt = nextpkt; - } else { - *mp = nextpkt; - } + /* When packet valid, link to the next packet */ + if (*mp != NULL && nextpkt != NULL) { + struct mbuf *m = *mp; + while (m->m_nextpkt != NULL) + m = m->m_nextpkt; + m->m_nextpkt = nextpkt; + } + /* Fix up linkage of previous packet in the chain */ + if (mppn != NULL) { + if (*mp != NULL) + *mppn = *mp; + else + *mppn = nextpkt; } - if (mppn != NULL && *mppn != NULL) - *mppn = *mp; - if (!reentry) { - ifp->if_pf_curthread = NULL; + if (marks != net_thread_marks_none) lck_mtx_unlock(pf_lock); - } + done: - if (!reentry) + if (marks != net_thread_marks_none) lck_rw_done(pf_perim_lock); + net_thread_marks_pop(marks); return (error); } #if INET static int -pf_inet_hook(struct ifnet *ifp, struct mbuf **mp, int input) +pf_inet_hook(struct ifnet *ifp, struct mbuf **mp, int input, + struct ip_fw_args *fwa) { struct mbuf *m = *mp; #if BYTE_ORDER != BIG_ENDIAN @@ -3337,7 +4940,7 @@ pf_inet_hook(struct ifnet *ifp, struct mbuf **mp, int input) HTONS(ip->ip_len); HTONS(ip->ip_off); #endif - if (pf_test(input ? PF_IN : PF_OUT, ifp, mp, NULL) != PF_PASS) { + if (pf_test(input ? PF_IN : PF_OUT, ifp, mp, NULL, fwa) != PF_PASS) { if (*mp != NULL) { m_freem(*mp); *mp = NULL; @@ -3348,9 +4951,11 @@ pf_inet_hook(struct ifnet *ifp, struct mbuf **mp, int input) } #if BYTE_ORDER != BIG_ENDIAN else { - ip = mtod(*mp, struct ip *); - NTOHS(ip->ip_len); - NTOHS(ip->ip_off); + if (*mp != NULL) { + ip = mtod(*mp, struct ip *); + NTOHS(ip->ip_len); + NTOHS(ip->ip_off); + } } #endif return (error); @@ -3359,14 +4964,11 @@ pf_inet_hook(struct ifnet *ifp, struct mbuf **mp, int input) #if INET6 int -pf_inet6_hook(struct ifnet *ifp, struct mbuf **mp, int input) +pf_inet6_hook(struct ifnet *ifp, struct mbuf **mp, int input, + struct ip_fw_args *fwa) { int error = 0; -#if 0 - /* - * TODO: once we support IPv6 hardware checksum offload - */ /* * If the packet is outbound, is originated locally, is flagged for * delayed UDP/TCP checksum calculation, and is about to be processed @@ -3375,18 +4977,21 @@ pf_inet6_hook(struct ifnet *ifp, struct mbuf **mp, int input) * it properly. */ if (!input && (*mp)->m_pkthdr.rcvif == NULL) { - static const int mask = CSUM_DELAY_DATA; + static const int mask = CSUM_DELAY_IPV6_DATA; const int flags = (*mp)->m_pkthdr.csum_flags & ~IF_HWASSIST_CSUM_FLAGS(ifp->if_hwassist); if (flags & mask) { + /* + * Checksum offload should not have been enabled + * when extension headers exist, thus 0 for optlen. + */ in6_delayed_cksum(*mp); (*mp)->m_pkthdr.csum_flags &= ~mask; } } -#endif - if (pf_test6(input ? PF_IN : PF_OUT, ifp, mp, NULL) != PF_PASS) { + if (pf_test6(input ? PF_IN : PF_OUT, ifp, mp, NULL, fwa) != PF_PASS) { if (*mp != NULL) { m_freem(*mp); *mp = NULL; @@ -3400,33 +5005,19 @@ pf_inet6_hook(struct ifnet *ifp, struct mbuf **mp, int input) #endif /* INET6 */ int -pf_ifaddr_hook(struct ifnet *ifp, unsigned long cmd) +pf_ifaddr_hook(struct ifnet *ifp) { - lck_rw_lock_shared(pf_perim_lock); - if (!pf_hooks_attached) - goto done; + struct pfi_kif *kif = ifp->if_pf_kif; - lck_mtx_lock(pf_lock); + if (kif != NULL) { + lck_rw_lock_shared(pf_perim_lock); + lck_mtx_lock(pf_lock); - switch (cmd) { - case SIOCSIFADDR: - case SIOCAIFADDR: - case SIOCDIFADDR: -#if INET6 - case SIOCAIFADDR_IN6: - case SIOCDIFADDR_IN6: -#endif /* INET6 */ - if (ifp->if_pf_kif != NULL) - pfi_kifaddr_update(ifp->if_pf_kif); - break; - default: - panic("%s: unexpected ioctl %lu", __func__, cmd); - /* NOTREACHED */ - } + pfi_kifaddr_update(kif); - lck_mtx_unlock(pf_lock); -done: - lck_rw_done(pf_perim_lock); + lck_mtx_unlock(pf_lock); + lck_rw_done(pf_perim_lock); + } return (0); } @@ -3437,53 +5028,149 @@ void pf_ifnet_hook(struct ifnet *ifp, int attach) { lck_rw_lock_shared(pf_perim_lock); - if (!pf_hooks_attached) - goto done; - lck_mtx_lock(pf_lock); if (attach) pfi_attach_ifnet(ifp); else pfi_detach_ifnet(ifp); lck_mtx_unlock(pf_lock); -done: lck_rw_done(pf_perim_lock); } static void pf_attach_hooks(void) { - int i; - - if (pf_hooks_attached) - return; - ifnet_head_lock_shared(); - for (i = 0; i <= if_index; i++) { - struct ifnet *ifp = ifindex2ifnet[i]; - if (ifp != NULL) { - pfi_attach_ifnet(ifp); + /* + * Check against ifnet_addrs[] before proceeding, in case this + * is called very early on, e.g. during dlil_init() before any + * network interface is attached. + */ + if (ifnet_addrs != NULL) { + int i; + + for (i = 0; i <= if_index; i++) { + struct ifnet *ifp = ifindex2ifnet[i]; + if (ifp != NULL) { + pfi_attach_ifnet(ifp); + } } } ifnet_head_done(); - pf_hooks_attached = 1; } +#if 0 +/* currently unused along with pfdetach() */ static void pf_detach_hooks(void) { - int i; - - if (!pf_hooks_attached) - return; - ifnet_head_lock_shared(); - for (i = 0; i <= if_index; i++) { - struct ifnet *ifp = ifindex2ifnet[i]; - if (ifp != NULL && ifp->if_pf_kif != NULL) { - pfi_detach_ifnet(ifp); + if (ifnet_addrs != NULL) { + for (i = 0; i <= if_index; i++) { + int i; + + struct ifnet *ifp = ifindex2ifnet[i]; + if (ifp != NULL && ifp->if_pf_kif != NULL) { + pfi_detach_ifnet(ifp); + } } } ifnet_head_done(); - pf_hooks_attached = 0; +} +#endif + +/* + * 'D' group ioctls. + * + * The switch statement below does nothing at runtime, as it serves as a + * compile time check to ensure that all of the socket 'D' ioctls (those + * in the 'D' group going thru soo_ioctl) that are made available by the + * networking stack is unique. This works as long as this routine gets + * updated each time a new interface ioctl gets added. + * + * Any failures at compile time indicates duplicated ioctl values. + */ +static __attribute__((unused)) void +pfioctl_cassert(void) +{ + /* + * This is equivalent to _CASSERT() and the compiler wouldn't + * generate any instructions, thus for compile time only. + */ + switch ((u_long)0) { + case 0: + + /* bsd/net/pfvar.h */ + case DIOCSTART: + case DIOCSTOP: + case DIOCADDRULE: + case DIOCGETSTARTERS: + case DIOCGETRULES: + case DIOCGETRULE: + case DIOCSTARTREF: + case DIOCSTOPREF: + case DIOCCLRSTATES: + case DIOCGETSTATE: + case DIOCSETSTATUSIF: + case DIOCGETSTATUS: + case DIOCCLRSTATUS: + case DIOCNATLOOK: + case DIOCSETDEBUG: + case DIOCGETSTATES: + case DIOCCHANGERULE: + case DIOCINSERTRULE: + case DIOCDELETERULE: + case DIOCSETTIMEOUT: + case DIOCGETTIMEOUT: + case DIOCADDSTATE: + case DIOCCLRRULECTRS: + case DIOCGETLIMIT: + case DIOCSETLIMIT: + case DIOCKILLSTATES: + case DIOCSTARTALTQ: + case DIOCSTOPALTQ: + case DIOCADDALTQ: + case DIOCGETALTQS: + case DIOCGETALTQ: + case DIOCCHANGEALTQ: + case DIOCGETQSTATS: + case DIOCBEGINADDRS: + case DIOCADDADDR: + case DIOCGETADDRS: + case DIOCGETADDR: + case DIOCCHANGEADDR: + case DIOCGETRULESETS: + case DIOCGETRULESET: + case DIOCRCLRTABLES: + case DIOCRADDTABLES: + case DIOCRDELTABLES: + case DIOCRGETTABLES: + case DIOCRGETTSTATS: + case DIOCRCLRTSTATS: + case DIOCRCLRADDRS: + case DIOCRADDADDRS: + case DIOCRDELADDRS: + case DIOCRSETADDRS: + case DIOCRGETADDRS: + case DIOCRGETASTATS: + case DIOCRCLRASTATS: + case DIOCRTSTADDRS: + case DIOCRSETTFLAGS: + case DIOCRINADEFINE: + case DIOCOSFPFLUSH: + case DIOCOSFPADD: + case DIOCOSFPGET: + case DIOCXBEGIN: + case DIOCXCOMMIT: + case DIOCXROLLBACK: + case DIOCGETSRCNODES: + case DIOCCLRSRCNODES: + case DIOCSETHOSTID: + case DIOCIGETIFACES: + case DIOCSETIFFLAG: + case DIOCCLRIFFLAG: + case DIOCKILLSRCNODES: + case DIOCGIFSPEED: + ; + } }