diff options
author | Martin Matuska <mm@FreeBSD.org> | 2014-02-19 22:02:15 +0000 |
---|---|---|
committer | Martin Matuska <mm@FreeBSD.org> | 2014-02-19 22:02:15 +0000 |
commit | 5748b897da441d1f10e1fe0c39155ea33d6d383a (patch) | |
tree | 7c8754b66fd2a59523982c11a17a367cbfcb1520 /sys/netpfil | |
parent | 654957c2c861b1483df873678439699c0dfeeb94 (diff) | |
parent | 2067168264b9ab5abb628187a3c56180e7767e7b (diff) | |
download | src-5748b897da441d1f10e1fe0c39155ea33d6d383a.tar.gz src-5748b897da441d1f10e1fe0c39155ea33d6d383a.zip |
Merge head up to r262222 (last merge was incomplete).
Notes
Notes:
svn path=/projects/pf/head/; revision=262234
Diffstat (limited to 'sys/netpfil')
-rw-r--r-- | sys/netpfil/ipfw/dn_sched.h | 1 | ||||
-rw-r--r-- | sys/netpfil/ipfw/dummynet.txt | 4 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_dn_io.c | 30 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_dn_private.h | 1 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_fw2.c | 49 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_fw_dynamic.c | 37 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_fw_log.c | 7 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_fw_nat.c | 2 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_fw_pfil.c | 8 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_fw_private.h | 18 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_fw_sockopt.c | 4 | ||||
-rw-r--r-- | sys/netpfil/ipfw/ip_fw_table.c | 8 | ||||
-rw-r--r-- | sys/netpfil/pf/if_pfsync.c | 16 | ||||
-rw-r--r-- | sys/netpfil/pf/pf.c | 129 | ||||
-rw-r--r-- | sys/netpfil/pf/pf.h | 2 | ||||
-rw-r--r-- | sys/netpfil/pf/pf_ioctl.c | 189 | ||||
-rw-r--r-- | sys/netpfil/pf/pf_lb.c | 1 |
17 files changed, 332 insertions, 174 deletions
diff --git a/sys/netpfil/ipfw/dn_sched.h b/sys/netpfil/ipfw/dn_sched.h index ab823fe7c806..0d2845427ab9 100644 --- a/sys/netpfil/ipfw/dn_sched.h +++ b/sys/netpfil/ipfw/dn_sched.h @@ -166,6 +166,7 @@ dn_dequeue(struct dn_queue *q) if (m == NULL) return NULL; q->mq.head = m->m_nextpkt; + q->mq.count--; /* Update stats for the queue */ q->ni.length--; diff --git a/sys/netpfil/ipfw/dummynet.txt b/sys/netpfil/ipfw/dummynet.txt index e8c97255aeff..b3b2ee9880e3 100644 --- a/sys/netpfil/ipfw/dummynet.txt +++ b/sys/netpfil/ipfw/dummynet.txt @@ -202,10 +202,10 @@ Almost all objects in this implementation are preceded by a structure Files ----- The dummynet code is split in several files. -All kernel code is in sys/netinet/ipfw except ip_dummynet.h +All kernel code is in sys/netpfil/ipfw except ip_dummynet.h All userland code is in sbin/ipfw. Files are -- sys/netinet/ip_dummynet.h defines the kernel-userland API +- sys/netpfil/ip_dummynet.h defines the kernel-userland API - ip_dn_private.h contains the kernel-specific APIs and data structures - dn_sched.h defines the scheduler API diff --git a/sys/netpfil/ipfw/ip_dn_io.c b/sys/netpfil/ipfw/ip_dn_io.c index 7afa1e20bea8..1748194b1e25 100644 --- a/sys/netpfil/ipfw/ip_dn_io.c +++ b/sys/netpfil/ipfw/ip_dn_io.c @@ -260,10 +260,39 @@ dn_tag_get(struct mbuf *m) static inline void mq_append(struct mq *q, struct mbuf *m) { +#ifdef USERSPACE + // buffers from netmap need to be copied + // XXX note that the routine is not expected to fail + ND("append %p to %p", m, q); + if (m->m_flags & M_STACK) { + struct mbuf *m_new; + void *p; + int l, ofs; + + ofs = m->m_data - m->__m_extbuf; + // XXX allocate + MGETHDR(m_new, M_NOWAIT, MT_DATA); + ND("*** WARNING, volatile buf %p ext %p %d dofs %d m_new %p", + m, m->__m_extbuf, m->__m_extlen, ofs, m_new); + p = m_new->__m_extbuf; /* new pointer */ + l = m_new->__m_extlen; /* new len */ + if (l <= m->__m_extlen) { + panic("extlen too large"); + } + + *m_new = *m; // copy + m_new->m_flags &= ~M_STACK; + m_new->__m_extbuf = p; // point to new buffer + pkt_copy(m->__m_extbuf, p, m->__m_extlen); + m_new->m_data = p + ofs; + m = m_new; + } +#endif /* USERSPACE */ if (q->head == NULL) q->head = m; else q->tail->m_nextpkt = m; + q->count++; q->tail = m; m->m_nextpkt = NULL; } @@ -455,6 +484,7 @@ transmit_event(struct mq *q, struct delay_line *dline, uint64_t now) if (!DN_KEY_LEQ(pkt->output_time, now)) break; dline->mq.head = m->m_nextpkt; + dline->mq.count--; mq_append(q, m); } if (m != NULL) { diff --git a/sys/netpfil/ipfw/ip_dn_private.h b/sys/netpfil/ipfw/ip_dn_private.h index 159ddc9ab8b2..958c8201412c 100644 --- a/sys/netpfil/ipfw/ip_dn_private.h +++ b/sys/netpfil/ipfw/ip_dn_private.h @@ -83,6 +83,7 @@ SLIST_HEAD(dn_alg_head, dn_alg); struct mq { /* a basic queue of packets*/ struct mbuf *head, *tail; + int count; }; static inline void diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index aa6d9e376d72..ecb0f2dc8169 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -370,7 +370,7 @@ iface_match(struct ifnet *ifp, ipfw_insn_if *cmd, struct ip_fw_chain *chain, uin return(1); } } else { -#ifdef __FreeBSD__ /* and OSX too ? */ +#if !defined(USERSPACE) && defined(__FreeBSD__) /* and OSX too ? */ struct ifaddr *ia; if_addr_rlock(ifp); @@ -413,7 +413,7 @@ iface_match(struct ifnet *ifp, ipfw_insn_if *cmd, struct ip_fw_chain *chain, uin static int verify_path(struct in_addr src, struct ifnet *ifp, u_int fib) { -#ifndef __FreeBSD__ +#if defined(USERSPACE) || !defined(__FreeBSD__) return 0; #else struct route ro; @@ -664,6 +664,9 @@ static int check_uidgid(ipfw_insn_u32 *insn, struct ip_fw_args *args, int *ugid_lookupp, struct ucred **uc) { +#if defined(USERSPACE) + return 0; // not supported in userspace +#else #ifndef __FreeBSD__ /* XXX */ return cred_check(insn, proto, oif, @@ -766,6 +769,7 @@ check_uidgid(ipfw_insn_u32 *insn, struct ip_fw_args *args, int *ugid_lookupp, match = ((*uc)->cr_prison->pr_id == (int)insn->d[0]); return (match); #endif /* __FreeBSD__ */ +#endif /* not supported in userspace */ } /* @@ -1464,6 +1468,7 @@ do { \ key = htonl(dst_port); else if (v == 3) key = htonl(src_port); +#ifndef USERSPACE else if (v == 4 || v == 5) { check_uidgid( (ipfw_insn_u32 *)cmd, @@ -1483,6 +1488,7 @@ do { \ #endif /* !__FreeBSD__ */ key = htonl(key); } else +#endif /* !USERSPACE */ break; } match = ipfw_lookup_table(chain, @@ -1946,6 +1952,7 @@ do { \ break; case O_SOCKARG: { +#ifndef USERSPACE /* not supported in userspace */ struct inpcb *inp = args->inp; struct inpcbinfo *pi; @@ -1986,6 +1993,7 @@ do { \ match = 1; } } +#endif /* !USERSPACE */ break; } @@ -2405,38 +2413,35 @@ do { \ } case O_NAT: + l = 0; /* exit inner loop */ + done = 1; /* exit outer loop */ if (!IPFW_NAT_LOADED) { retval = IP_FW_DENY; - } else { - struct cfg_nat *t; - int nat_id; + break; + } - set_match(args, f_pos, chain); - /* Check if this is 'global' nat rule */ - if (cmd->arg1 == 0) { - retval = ipfw_nat_ptr(args, NULL, m); - l = 0; - done = 1; - break; - } - t = ((ipfw_insn_nat *)cmd)->nat; - if (t == NULL) { + struct cfg_nat *t; + int nat_id; + + set_match(args, f_pos, chain); + /* Check if this is 'global' nat rule */ + if (cmd->arg1 == 0) { + retval = ipfw_nat_ptr(args, NULL, m); + break; + } + t = ((ipfw_insn_nat *)cmd)->nat; + if (t == NULL) { nat_id = IP_FW_ARG_TABLEARG(cmd->arg1); t = (*lookup_nat_ptr)(&chain->nat, nat_id); if (t == NULL) { retval = IP_FW_DENY; - l = 0; /* exit inner loop */ - done = 1; /* exit outer loop */ break; } if (cmd->arg1 != IP_FW_TABLEARG) ((ipfw_insn_nat *)cmd)->nat = t; - } - retval = ipfw_nat_ptr(args, t, m); } - l = 0; /* exit inner loop */ - done = 1; /* exit outer loop */ + retval = ipfw_nat_ptr(args, t, m); break; case O_REASS: { @@ -2661,7 +2666,7 @@ vnet_ipfw_init(const void *unused) rule->set = RESVD_SET; rule->cmd[0].len = 1; rule->cmd[0].opcode = default_to_accept ? O_ACCEPT : O_DENY; - chain->rules = chain->default_rule = chain->map[0] = rule; + chain->default_rule = chain->map[0] = rule; chain->id = rule->id = 1; IPFW_LOCK_INIT(chain); diff --git a/sys/netpfil/ipfw/ip_fw_dynamic.c b/sys/netpfil/ipfw/ip_fw_dynamic.c index 787fb15906fd..af546f43fc20 100644 --- a/sys/netpfil/ipfw/ip_fw_dynamic.c +++ b/sys/netpfil/ipfw/ip_fw_dynamic.c @@ -106,7 +106,8 @@ __FBSDID("$FreeBSD$"); * * Each dynamic rule holds a pointer to the parent ipfw rule so * we know what action to perform. Dynamic rules are removed when - * the parent rule is deleted. XXX we should make them survive. + * the parent rule is deleted. This can be changed by dyn_keep_states + * sysctl. * * There are some limitations with dynamic rules -- we do not * obey the 'randomized match', and we do not do multiple @@ -141,6 +142,10 @@ static VNET_DEFINE(uma_zone_t, ipfw_dyn_rule_zone); #define IPFW_BUCK_UNLOCK(i) mtx_unlock(&V_ipfw_dyn_v[(i)].mtx) #define IPFW_BUCK_ASSERT(i) mtx_assert(&V_ipfw_dyn_v[(i)].mtx, MA_OWNED) + +static VNET_DEFINE(int, dyn_keep_states); +#define V_dyn_keep_states VNET(dyn_keep_states) + /* * Timeouts for various events in handing dynamic rules. */ @@ -181,6 +186,13 @@ static VNET_DEFINE(u_int32_t, dyn_max); /* max # of dynamic rules */ #define DYN_COUNT uma_zone_get_cur(V_ipfw_dyn_rule_zone) #define V_dyn_max VNET(dyn_max) +/* for userspace, we emulate the uma_zone_counter with ipfw_dyn_count */ +static int ipfw_dyn_count; /* number of objects */ + +#ifdef USERSPACE /* emulation of UMA object counters for userspace */ +#define uma_zone_get_cur(x) ipfw_dyn_count +#endif /* USERSPACE */ + static int last_log; /* Log ratelimiting */ static void ipfw_dyn_tick(void *vnetx); @@ -227,12 +239,16 @@ SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_short_lifetime, SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_keepalive, CTLFLAG_RW, &VNET_NAME(dyn_keepalive), 0, "Enable keepalives for dyn. rules"); +SYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_keep_states, + CTLFLAG_RW, &VNET_NAME(dyn_keep_states), 0, + "Do not flush dynamic states on rule deletion"); SYSEND #endif /* SYSCTL_NODE */ +#ifdef INET6 static __inline int hash_packet6(struct ipfw_flow_id *id) { @@ -244,6 +260,7 @@ hash_packet6(struct ipfw_flow_id *id) (id->dst_port) ^ (id->src_port); return i; } +#endif /* * IMPORTANT: the hash function for dynamic rules must be commutative @@ -300,6 +317,7 @@ print_dyn_rule_flags(struct ipfw_flow_id *id, int dyn_type, int log_flags, print_dyn_rule_flags(id, dtype, LOG_DEBUG, prefix, postfix) #define TIME_LEQ(a,b) ((int)((a)-(b)) <= 0) +#define TIME_LE(a,b) ((int)((a)-(b)) < 0) /* * Lookup a dynamic rule, locked version. @@ -579,6 +597,7 @@ add_dyn_rule(struct ipfw_flow_id *id, int i, u_int8_t dyn_type, struct ip_fw *ru } return NULL; } + ipfw_dyn_count++; /* * refcount on parent is already incremented, so @@ -1092,6 +1111,20 @@ check_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule, if ((TIME_LEQ(q->expire, time_uptime)) || ((rule != NULL) && (q->rule == rule)) || ((set != RESVD_SET) && (q->rule->set == set))) { + if (TIME_LE(time_uptime, q->expire) && + q->dyn_type == O_KEEP_STATE && + V_dyn_keep_states != 0) { + /* + * Do not delete state if + * it is not expired and + * dyn_keep_states is ON. + * However we need to re-link it + * to any other stable rule + */ + q->rule = chain->default_rule; + NEXT_RULE(); + } + /* Unlink q from current list */ q_next = q->next; if (q == V_ipfw_dyn_v[i].head) @@ -1253,11 +1286,13 @@ check_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule, for (q = exp_head; q != NULL; q = q_next) { q_next = q->next; uma_zfree(V_ipfw_dyn_rule_zone, q); + ipfw_dyn_count--; } for (q = exp_lhead; q != NULL; q = q_next) { q_next = q->next; uma_zfree(V_ipfw_dyn_rule_zone, q); + ipfw_dyn_count--; } /* diff --git a/sys/netpfil/ipfw/ip_fw_log.c b/sys/netpfil/ipfw/ip_fw_log.c index 13aa3e43c8ac..b9ef96dc0f99 100644 --- a/sys/netpfil/ipfw/ip_fw_log.c +++ b/sys/netpfil/ipfw/ip_fw_log.c @@ -85,8 +85,15 @@ __FBSDID("$FreeBSD$"); #define ICMP(p) ((struct icmphdr *)(p)) #define ICMP6(p) ((struct icmp6_hdr *)(p)) +#ifdef __APPLE__ +#undef snprintf +#define snprintf sprintf +#define SNPARGS(buf, len) buf + len +#define SNP(buf) buf +#else /* !__APPLE__ */ #define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0 #define SNP(buf) buf, sizeof(buf) +#endif /* !__APPLE__ */ #ifdef WITHOUT_BPF void diff --git a/sys/netpfil/ipfw/ip_fw_nat.c b/sys/netpfil/ipfw/ip_fw_nat.c index 237b560978ea..427a55e46f6a 100644 --- a/sys/netpfil/ipfw/ip_fw_nat.c +++ b/sys/netpfil/ipfw/ip_fw_nat.c @@ -676,7 +676,7 @@ static moduledata_t ipfw_nat_mod = { }; /* Define startup order. */ -#define IPFW_NAT_SI_SUB_FIREWALL SI_SUB_PROTO_IFATTACHDOMAIN +#define IPFW_NAT_SI_SUB_FIREWALL (SI_SUB_PROTO_IFATTACHDOMAIN + 1) #define IPFW_NAT_MODEVENT_ORDER (SI_ORDER_ANY - 255) #define IPFW_NAT_MODULE_ORDER (IPFW_NAT_MODEVENT_ORDER + 1) #define IPFW_NAT_VNET_ORDER (IPFW_NAT_MODEVENT_ORDER + 2) diff --git a/sys/netpfil/ipfw/ip_fw_pfil.c b/sys/netpfil/ipfw/ip_fw_pfil.c index 79973b1f05c7..dcef12a21669 100644 --- a/sys/netpfil/ipfw/ip_fw_pfil.c +++ b/sys/netpfil/ipfw/ip_fw_pfil.c @@ -82,9 +82,9 @@ int ipfw_chg_hook(SYSCTL_HANDLER_ARGS); /* Forward declarations. */ static int ipfw_divert(struct mbuf **, int, struct ipfw_rule_ref *, int); -static int ipfw_check_packet(void *, struct mbuf **, struct ifnet *, int, +int ipfw_check_packet(void *, struct mbuf **, struct ifnet *, int, struct inpcb *); -static int ipfw_check_frame(void *, struct mbuf **, struct ifnet *, int, +int ipfw_check_frame(void *, struct mbuf **, struct ifnet *, int, struct inpcb *); #ifdef SYSCTL_NODE @@ -116,7 +116,7 @@ SYSEND * dummynet, divert, netgraph or other modules. * The packet may be consumed. */ -static int +int ipfw_check_packet(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, struct inpcb *inp) { @@ -292,7 +292,7 @@ again: * Inteface is NULL from ether_demux, and ifp from * ether_output_frame. */ -static int +int ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir, struct inpcb *inp) { diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h index a8d7eea4edfd..e4a2f3195de5 100644 --- a/sys/netpfil/ipfw/ip_fw_private.h +++ b/sys/netpfil/ipfw/ip_fw_private.h @@ -213,25 +213,27 @@ VNET_DECLARE(unsigned int, fw_tables_max); #define V_fw_tables_max VNET(fw_tables_max) struct ip_fw_chain { - struct ip_fw *rules; /* list of rules */ - struct ip_fw *reap; /* list of rules to reap */ - struct ip_fw *default_rule; - int n_rules; /* number of static rules */ - int static_len; /* total len of static rules */ struct ip_fw **map; /* array of rule ptrs to ease lookup */ + uint32_t id; /* ruleset id */ + int n_rules; /* number of static rules */ LIST_HEAD(nat_list, cfg_nat) nat; /* list of nat entries */ struct radix_node_head **tables; /* IPv4 tables */ struct radix_node_head **xtables; /* extended tables */ uint8_t *tabletype; /* Array of table types */ #if defined( __linux__ ) || defined( _WIN32 ) spinlock_t rwmtx; - spinlock_t uh_lock; #else struct rwlock rwmtx; +#endif + int static_len; /* total len of static rules */ + uint32_t gencnt; /* NAT generation count */ + struct ip_fw *reap; /* list of rules to reap */ + struct ip_fw *default_rule; +#if defined( __linux__ ) || defined( _WIN32 ) + spinlock_t uh_lock; +#else struct rwlock uh_lock; /* lock for upper half */ #endif - uint32_t id; /* ruleset id */ - uint32_t gencnt; /* generation count */ }; struct sockopt; /* used by tcp_var.h */ diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c index 64f09a56a2a9..0d619bc64a1c 100644 --- a/sys/netpfil/ipfw/ip_fw_sockopt.c +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -159,7 +159,7 @@ ipfw_add_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule) int i, l, insert_before; struct ip_fw **map; /* the new array of pointers */ - if (chain->rules == NULL || input_rule->rulenum > IPFW_DEFAULT_RULE-1) + if (chain->map == NULL || input_rule->rulenum > IPFW_DEFAULT_RULE - 1) return (EINVAL); l = RULESIZE(input_rule); @@ -653,7 +653,7 @@ check_ipfw_struct(struct ip_fw *rule, int size) case O_IP_SRC_LOOKUP: case O_IP_DST_LOOKUP: - if (cmd->arg1 >= IPFW_TABLES_MAX) { + if (cmd->arg1 >= V_fw_tables_max) { printf("ipfw: invalid table number %d\n", cmd->arg1); return (EINVAL); diff --git a/sys/netpfil/ipfw/ip_fw_table.c b/sys/netpfil/ipfw/ip_fw_table.c index a22fff9240f2..31eebfe802ac 100644 --- a/sys/netpfil/ipfw/ip_fw_table.c +++ b/sys/netpfil/ipfw/ip_fw_table.c @@ -123,6 +123,7 @@ struct table_xentry { #define OFF_LEN_IFACE (8 * offsetof(struct xaddr_iface, ifname)) +#ifdef INET6 static inline void ipv6_writemask(struct in6_addr *addr6, uint8_t mask) { @@ -132,6 +133,7 @@ ipv6_writemask(struct in6_addr *addr6, uint8_t mask) *cp++ = 0xFFFFFFFF; *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0); } +#endif int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, @@ -542,7 +544,7 @@ ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, return (0); KEY_LEN(sa) = KEY_LEN_INET; sa.sin_addr.s_addr = addr; - ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh)); + ent = (struct table_entry *)(rnh->rnh_matchaddr(&sa, rnh)); if (ent != NULL) { *val = ent->value; return (1); @@ -568,7 +570,7 @@ ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, case IPFW_TABLE_CIDR: KEY_LEN(sa6) = KEY_LEN_INET6; memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); - xent = (struct table_xentry *)(rnh->rnh_lookup(&sa6, NULL, rnh)); + xent = (struct table_xentry *)(rnh->rnh_matchaddr(&sa6, rnh)); break; case IPFW_TABLE_INTERFACE: @@ -576,7 +578,7 @@ ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, strlcpy(iface.ifname, (char *)paddr, IF_NAMESIZE) + 1; /* Assume direct match */ /* FIXME: Add interface pattern matching */ - xent = (struct table_xentry *)(rnh->rnh_lookup(&iface, NULL, rnh)); + xent = (struct table_xentry *)(rnh->rnh_matchaddr(&iface, rnh)); break; default: diff --git a/sys/netpfil/pf/if_pfsync.c b/sys/netpfil/pf/if_pfsync.c index 52ff02cb59fa..6ab0596adc5e 100644 --- a/sys/netpfil/pf/if_pfsync.c +++ b/sys/netpfil/pf/if_pfsync.c @@ -438,7 +438,8 @@ pfsync_state_import(struct pfsync_state *sp, u_int8_t flags) else r = &V_pf_default_rule; - if ((r->max_states && r->states_cur >= r->max_states)) + if ((r->max_states && + counter_u64_fetch(r->states_cur) >= r->max_states)) goto cleanup; /* @@ -516,18 +517,15 @@ pfsync_state_import(struct pfsync_state *sp, u_int8_t flags) st->pfsync_time = time_uptime; st->sync_state = PFSYNC_S_NONE; - /* XXX when we have nat_rule/anchors, use STATE_INC_COUNTERS */ - r->states_cur++; - r->states_tot++; - if (!(flags & PFSYNC_SI_IOCTL)) st->state_flags |= PFSTATE_NOSYNC; - if ((error = pf_state_insert(kif, skw, sks, st)) != 0) { - /* XXX when we have nat_rule/anchors, use STATE_DEC_COUNTERS */ - r->states_cur--; + if ((error = pf_state_insert(kif, skw, sks, st)) != 0) goto cleanup_state; - } + + /* XXX when we have nat_rule/anchors, use STATE_INC_COUNTERS */ + counter_u64_add(r->states_cur, 1); + counter_u64_add(r->states_tot, 1); if (!(flags & PFSYNC_SI_IOCTL)) { st->state_flags &= ~PFSTATE_NOSYNC; diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 2f13e3b04476..ed0a35f8bc13 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -340,27 +340,27 @@ enum { PF_ICMP_MULTI_NONE, PF_ICMP_MULTI_SOLICITED, PF_ICMP_MULTI_LINK }; #define BOUND_IFACE(r, k) \ ((r)->rule_flag & PFRULE_IFBOUND) ? (k) : V_pfi_all -#define STATE_INC_COUNTERS(s) \ - do { \ - s->rule.ptr->states_cur++; \ - s->rule.ptr->states_tot++; \ - if (s->anchor.ptr != NULL) { \ - s->anchor.ptr->states_cur++; \ - s->anchor.ptr->states_tot++; \ - } \ - if (s->nat_rule.ptr != NULL) { \ - s->nat_rule.ptr->states_cur++; \ - s->nat_rule.ptr->states_tot++; \ - } \ +#define STATE_INC_COUNTERS(s) \ + do { \ + counter_u64_add(s->rule.ptr->states_cur, 1); \ + counter_u64_add(s->rule.ptr->states_tot, 1); \ + if (s->anchor.ptr != NULL) { \ + counter_u64_add(s->anchor.ptr->states_cur, 1); \ + counter_u64_add(s->anchor.ptr->states_tot, 1); \ + } \ + if (s->nat_rule.ptr != NULL) { \ + counter_u64_add(s->nat_rule.ptr->states_cur, 1);\ + counter_u64_add(s->nat_rule.ptr->states_tot, 1);\ + } \ } while (0) -#define STATE_DEC_COUNTERS(s) \ - do { \ - if (s->nat_rule.ptr != NULL) \ - s->nat_rule.ptr->states_cur--; \ - if (s->anchor.ptr != NULL) \ - s->anchor.ptr->states_cur--; \ - s->rule.ptr->states_cur--; \ +#define STATE_DEC_COUNTERS(s) \ + do { \ + if (s->nat_rule.ptr != NULL) \ + counter_u64_add(s->nat_rule.ptr->states_cur, -1);\ + if (s->anchor.ptr != NULL) \ + counter_u64_add(s->anchor.ptr->states_cur, -1); \ + counter_u64_add(s->rule.ptr->states_cur, -1); \ } while (0) static MALLOC_DEFINE(M_PFHASH, "pf_hash", "pf(4) hash header structures"); @@ -650,7 +650,7 @@ pf_insert_src_node(struct pf_src_node **sn, struct pf_rule *rule, PF_HASHROW_ASSERT(sh); if (!rule->max_src_nodes || - rule->src_nodes < rule->max_src_nodes) + counter_u64_fetch(rule->src_nodes) < rule->max_src_nodes) (*sn) = uma_zalloc(V_pf_sources_z, M_NOWAIT | M_ZERO); else V_pf_status.lcounters[LCNT_SRCNODES]++; @@ -670,7 +670,7 @@ pf_insert_src_node(struct pf_src_node **sn, struct pf_rule *rule, (*sn)->creation = time_uptime; (*sn)->ruletype = rule->action; if ((*sn)->rule.ptr != NULL) - (*sn)->rule.ptr->src_nodes++; + counter_u64_add((*sn)->rule.ptr->src_nodes, 1); PF_HASHROW_UNLOCK(sh); V_pf_status.scounters[SCNT_SRC_NODE_INSERT]++; V_pf_status.src_nodes++; @@ -684,20 +684,53 @@ pf_insert_src_node(struct pf_src_node **sn, struct pf_rule *rule, return (0); } -static void -pf_remove_src_node(struct pf_src_node *src) +void +pf_unlink_src_node_locked(struct pf_src_node *src) { +#ifdef INVARIANTS struct pf_srchash *sh; sh = &V_pf_srchash[pf_hashsrc(&src->addr, src->af)]; - PF_HASHROW_LOCK(sh); + PF_HASHROW_ASSERT(sh); +#endif LIST_REMOVE(src, entry); - PF_HASHROW_UNLOCK(sh); - + if (src->rule.ptr) + counter_u64_add(src->rule.ptr->src_nodes, -1); V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; V_pf_status.src_nodes--; +} + +void +pf_unlink_src_node(struct pf_src_node *src) +{ + struct pf_srchash *sh; - uma_zfree(V_pf_sources_z, src); + sh = &V_pf_srchash[pf_hashsrc(&src->addr, src->af)]; + PF_HASHROW_LOCK(sh); + pf_unlink_src_node_locked(src); + PF_HASHROW_UNLOCK(sh); +} + +static void +pf_free_src_node(struct pf_src_node *sn) +{ + + KASSERT(sn->states == 0, ("%s: %p has refs", __func__, sn)); + uma_zfree(V_pf_sources_z, sn); +} + +u_int +pf_free_src_nodes(struct pf_src_node_list *head) +{ + struct pf_src_node *sn, *tmp; + u_int count = 0; + + LIST_FOREACH_SAFE(sn, head, entry, tmp) { + pf_free_src_node(sn); + count++; + } + + return (count); } /* Data storage structures initialization. */ @@ -1440,7 +1473,7 @@ pf_state_expires(const struct pf_state *state) start = state->rule.ptr->timeout[PFTM_ADAPTIVE_START]; if (start) { end = state->rule.ptr->timeout[PFTM_ADAPTIVE_END]; - states = state->rule.ptr->states_cur; /* XXXGL */ + states = counter_u64_fetch(state->rule.ptr->states_cur); } else { start = V_pf_default_rule.timeout[PFTM_ADAPTIVE_START]; end = V_pf_default_rule.timeout[PFTM_ADAPTIVE_END]; @@ -1459,24 +1492,24 @@ pf_state_expires(const struct pf_state *state) void pf_purge_expired_src_nodes() { + struct pf_src_node_list freelist; struct pf_srchash *sh; struct pf_src_node *cur, *next; int i; + LIST_INIT(&freelist); for (i = 0, sh = V_pf_srchash; i <= pf_srchashmask; i++, sh++) { PF_HASHROW_LOCK(sh); LIST_FOREACH_SAFE(cur, &sh->nodes, entry, next) - if (cur->states <= 0 && cur->expire <= time_uptime) { - if (cur->rule.ptr != NULL) - cur->rule.ptr->src_nodes--; - LIST_REMOVE(cur, entry); - V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; - V_pf_status.src_nodes--; - uma_zfree(V_pf_sources_z, cur); + if (cur->states == 0 && cur->expire <= time_uptime) { + pf_unlink_src_node_locked(cur); + LIST_INSERT_HEAD(&freelist, cur, entry); } else if (cur->rule.ptr != NULL) cur->rule.ptr->rule_flag |= PFRULE_REFS; PF_HASHROW_UNLOCK(sh); } + + pf_free_src_nodes(&freelist); } static void @@ -1487,7 +1520,7 @@ pf_src_tree_remove_state(struct pf_state *s) if (s->src_node != NULL) { if (s->src.tcp_est) --s->src_node->conn; - if (--s->src_node->states <= 0) { + if (--s->src_node->states == 0) { timeout = s->rule.ptr->timeout[PFTM_SRC_NODE]; if (!timeout) timeout = @@ -1496,7 +1529,7 @@ pf_src_tree_remove_state(struct pf_state *s) } } if (s->nat_src_node != s->src_node && s->nat_src_node != NULL) { - if (--s->nat_src_node->states <= 0) { + if (--s->nat_src_node->states == 0) { timeout = s->rule.ptr->timeout[PFTM_SRC_NODE]; if (!timeout) timeout = @@ -1549,11 +1582,7 @@ pf_unlink_state(struct pf_state *s, u_int flags) if (pfsync_delete_state_ptr != NULL) pfsync_delete_state_ptr(s); - --s->rule.ptr->states_cur; - if (s->nat_rule.ptr != NULL) - --s->nat_rule.ptr->states_cur; - if (s->anchor.ptr != NULL) - --s->anchor.ptr->states_cur; + STATE_DEC_COUNTERS(s); s->timeout = PFTM_UNLINKED; @@ -3568,7 +3597,8 @@ pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a, u_short reason; /* check maximums */ - if (r->max_states && (r->states_cur >= r->max_states)) { + if (r->max_states && + (counter_u64_fetch(r->states_cur) >= r->max_states)) { V_pf_status.lcounters[LCNT_STATES]++; REASON_SET(&reason, PFRES_MAXSTATES); return (PF_DROP); @@ -3766,11 +3796,15 @@ csfailed: if (nk != NULL) uma_zfree(V_pf_state_key_z, nk); - if (sn != NULL && sn->states == 0 && sn->expire == 0) - pf_remove_src_node(sn); + if (sn != NULL && sn->states == 0 && sn->expire == 0) { + pf_unlink_src_node(sn); + pf_free_src_node(sn); + } - if (nsn != sn && nsn != NULL && nsn->states == 0 && nsn->expire == 0) - pf_remove_src_node(nsn); + if (nsn != sn && nsn != NULL && nsn->states == 0 && nsn->expire == 0) { + pf_unlink_src_node(nsn); + pf_free_src_node(nsn); + } return (PF_DROP); } @@ -5451,7 +5485,6 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, PF_STATE_UNLOCK(s); rt = rtalloc1_fib(sintosa(&dst), 0, 0, M_GETFIB(m0)); if (rt == NULL) { - RTFREE_LOCKED(rt); KMOD_IPSTAT_INC(ips_noroute); error = EHOSTUNREACH; goto bad; diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h index 8d2c0f73c7cc..d2d9f2ba49c9 100644 --- a/sys/netpfil/pf/pf.h +++ b/sys/netpfil/pf/pf.h @@ -31,7 +31,7 @@ */ #ifndef _NET_PF_H_ -#define _NET_PFAR_H_ +#define _NET_PF_H_ #define PF_TCPS_PROXY_SRC ((TCP_NSTATES)+0) #define PF_TCPS_PROXY_DST ((TCP_NSTATES)+1) diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c index 7ecc016728a9..4541a5741b72 100644 --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -155,6 +155,7 @@ struct cdev *pf_dev; static void pf_clear_states(void); static int pf_clear_tables(void); static void pf_clear_srcnodes(struct pf_src_node *); +static void pf_kill_srcnodes(struct pfioc_src_node_kill *); static void pf_tbladdr_copyout(struct pf_addr_wrap *); /* @@ -231,6 +232,10 @@ pf_vnet_init(void) V_pf_default_rule.nr = -1; V_pf_default_rule.rtableid = -1; + V_pf_default_rule.states_cur = counter_u64_alloc(M_WAITOK); + V_pf_default_rule.states_tot = counter_u64_alloc(M_WAITOK); + V_pf_default_rule.src_nodes = counter_u64_alloc(M_WAITOK); + /* initialize default timeouts */ my_timeout[PFTM_TCP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL; my_timeout[PFTM_TCP_OPENING] = PFTM_TCP_OPENING_VAL; @@ -400,6 +405,9 @@ pf_free_rule(struct pf_rule *rule) pfi_kif_unref(rule->kif); pf_anchor_remove(rule); pf_empty_pool(&rule->rpool.list); + counter_u64_free(rule->states_cur); + counter_u64_free(rule->states_tot); + counter_u64_free(rule->src_nodes); free(rule, M_PFRULE); } @@ -1151,6 +1159,9 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td bcopy(&pr->rule, rule, sizeof(struct pf_rule)); if (rule->ifname[0]) kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + rule->states_cur = counter_u64_alloc(M_WAITOK); + rule->states_tot = counter_u64_alloc(M_WAITOK); + rule->src_nodes = counter_u64_alloc(M_WAITOK); rule->cuid = td->td_ucred->cr_ruid; rule->cpid = td->td_proc ? td->td_proc->p_pid : 0; TAILQ_INIT(&rule->rpool.list); @@ -1267,6 +1278,9 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td #undef ERROUT DIOCADDRULE_error: PF_RULES_WUNLOCK(); + counter_u64_free(rule->states_cur); + counter_u64_free(rule->states_tot); + counter_u64_free(rule->src_nodes); free(rule, M_PFRULE); if (kif) free(kif, PFI_MTYPE); @@ -1338,6 +1352,16 @@ DIOCADDRULE_error: break; } bcopy(rule, &pr->rule, sizeof(struct pf_rule)); + /* + * XXXGL: this is what happens when internal kernel + * structures are used as ioctl API structures. + */ + pr->rule.states_cur = + (counter_u64_t )counter_u64_fetch(rule->states_cur); + pr->rule.states_tot = + (counter_u64_t )counter_u64_fetch(rule->states_tot); + pr->rule.src_nodes = + (counter_u64_t )counter_u64_fetch(rule->src_nodes); if (pf_anchor_copyout(ruleset, rule, pr)) { PF_RULES_WUNLOCK(); error = EBUSY; @@ -1356,7 +1380,7 @@ DIOCADDRULE_error: rule->evaluations = 0; rule->packets[0] = rule->packets[1] = 0; rule->bytes[0] = rule->bytes[1] = 0; - rule->states_tot = 0; + counter_u64_zero(rule->states_tot); } PF_RULES_WUNLOCK(); break; @@ -1396,15 +1420,14 @@ DIOCADDRULE_error: #endif /* INET6 */ newrule = malloc(sizeof(*newrule), M_PFRULE, M_WAITOK); bcopy(&pcr->rule, newrule, sizeof(struct pf_rule)); + if (newrule->ifname[0]) + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + newrule->states_cur = counter_u64_alloc(M_WAITOK); + newrule->states_tot = counter_u64_alloc(M_WAITOK); + newrule->src_nodes = counter_u64_alloc(M_WAITOK); newrule->cuid = td->td_ucred->cr_ruid; newrule->cpid = td->td_proc ? td->td_proc->p_pid : 0; TAILQ_INIT(&newrule->rpool.list); - /* Initialize refcounting. */ - newrule->states_cur = 0; - newrule->entries.tqe_prev = NULL; - - if (newrule->ifname[0]) - kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); } #define ERROUT(x) { error = (x); goto DIOCCHANGERULE_error; } @@ -1572,8 +1595,12 @@ DIOCADDRULE_error: #undef ERROUT DIOCCHANGERULE_error: PF_RULES_WUNLOCK(); - if (newrule != NULL) + if (newrule != NULL) { + counter_u64_free(newrule->states_cur); + counter_u64_free(newrule->states_tot); + counter_u64_free(newrule->src_nodes); free(newrule, M_PFRULE); + } if (kif != NULL) free(kif, PFI_MTYPE); break; @@ -2283,6 +2310,7 @@ DIOCGETSTATES_full: bcopy(&pca->addr, newpa, sizeof(struct pf_pooladdr)); if (newpa->ifname[0]) kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + newpa->kif = NULL; } #define ERROUT(x) { error = (x); goto DIOCCHANGEADDR_error; } @@ -2300,8 +2328,8 @@ DIOCGETSTATES_full: if (newpa->ifname[0]) { newpa->kif = pfi_kif_attach(kif, newpa->ifname); pfi_kif_ref(newpa->kif); - } else - newpa->kif = NULL; + kif = NULL; + } switch (newpa->addr.type) { case PF_ADDR_DYNIFTL: @@ -2315,32 +2343,24 @@ DIOCGETSTATES_full: error = ENOMEM; break; } - if (error) { - if (newpa->kif) - pfi_kif_unref(newpa->kif); - PF_RULES_WUNLOCK(); - free(newpa, M_PFRULE); - break; - } + if (error) + goto DIOCCHANGEADDR_error; } - if (pca->action == PF_CHANGE_ADD_HEAD) + switch (pca->action) { + case PF_CHANGE_ADD_HEAD: oldpa = TAILQ_FIRST(&pool->list); - else if (pca->action == PF_CHANGE_ADD_TAIL) + break; + case PF_CHANGE_ADD_TAIL: oldpa = TAILQ_LAST(&pool->list, pf_palist); - else { - int i = 0; - + break; + default: oldpa = TAILQ_FIRST(&pool->list); - while ((oldpa != NULL) && (i < pca->nr)) { + for (int i = 0; oldpa && i < pca->nr; i++) oldpa = TAILQ_NEXT(oldpa, entries); - i++; - } - if (oldpa == NULL) { - PF_RULES_WUNLOCK(); - error = EINVAL; - break; - } + + if (oldpa == NULL) + ERROUT(EINVAL); } if (pca->action == PF_CHANGE_REMOVE) { @@ -2368,13 +2388,14 @@ DIOCGETSTATES_full: } pool->cur = TAILQ_FIRST(&pool->list); - PF_ACPY(&pool->counter, &pool->cur->addr.v.a.addr, - pca->af); + PF_ACPY(&pool->counter, &pool->cur->addr.v.a.addr, pca->af); PF_RULES_WUNLOCK(); break; #undef ERROUT DIOCCHANGEADDR_error: + if (newpa->kif) + pfi_kif_unref(newpa->kif); PF_RULES_WUNLOCK(); if (newpa != NULL) free(newpa, M_PFRULE); @@ -3084,7 +3105,7 @@ DIOCCHANGEADDR_error: uint32_t i, nr = 0; if (psn->psn_len == 0) { - for (i = 0, sh = V_pf_srchash; i < pf_srchashmask; + for (i = 0, sh = V_pf_srchash; i <= pf_srchashmask; i++, sh++) { PF_HASHROW_LOCK(sh); LIST_FOREACH(n, &sh->nodes, entry) @@ -3096,7 +3117,7 @@ DIOCCHANGEADDR_error: } p = pstore = malloc(psn->psn_len, M_TEMP, M_WAITOK); - for (i = 0, sh = V_pf_srchash; i < pf_srchashmask; + for (i = 0, sh = V_pf_srchash; i <= pf_srchashmask; i++, sh++) { PF_HASHROW_LOCK(sh); LIST_FOREACH(n, &sh->nodes, entry) { @@ -3146,45 +3167,9 @@ DIOCCHANGEADDR_error: break; } - case DIOCKILLSRCNODES: { - struct pfioc_src_node_kill *psnk = - (struct pfioc_src_node_kill *)addr; - struct pf_srchash *sh; - struct pf_src_node *sn; - u_int i, killed = 0; - - for (i = 0, sh = V_pf_srchash; i < pf_srchashmask; - i++, sh++) { - /* - * XXXGL: we don't ever acquire sources hash lock - * but if we ever do, the below call to pf_clear_srcnodes() - * would lead to a LOR. - */ - PF_HASHROW_LOCK(sh); - LIST_FOREACH(sn, &sh->nodes, entry) - if (PF_MATCHA(psnk->psnk_src.neg, - &psnk->psnk_src.addr.v.a.addr, - &psnk->psnk_src.addr.v.a.mask, - &sn->addr, sn->af) && - PF_MATCHA(psnk->psnk_dst.neg, - &psnk->psnk_dst.addr.v.a.addr, - &psnk->psnk_dst.addr.v.a.mask, - &sn->raddr, sn->af)) { - /* Handle state to src_node linkage */ - if (sn->states != 0) - pf_clear_srcnodes(sn); - sn->expire = 1; - killed++; - } - PF_HASHROW_UNLOCK(sh); - } - - if (killed > 0) - pf_purge_expired_src_nodes(); - - psnk->psnk_killed = killed; + case DIOCKILLSRCNODES: + pf_kill_srcnodes((struct pfioc_src_node_kill *)addr); break; - } case DIOCSETHOSTID: { u_int32_t *hostid = (u_int32_t *)addr; @@ -3388,7 +3373,7 @@ pf_clear_srcnodes(struct pf_src_node *n) if (n == NULL) { struct pf_srchash *sh; - for (i = 0, sh = V_pf_srchash; i < pf_srchashmask; + for (i = 0, sh = V_pf_srchash; i <= pf_srchashmask; i++, sh++) { PF_HASHROW_LOCK(sh); LIST_FOREACH(n, &sh->nodes, entry) { @@ -3403,6 +3388,59 @@ pf_clear_srcnodes(struct pf_src_node *n) n->states = 0; } } + +static void +pf_kill_srcnodes(struct pfioc_src_node_kill *psnk) +{ + struct pf_src_node_list kill; + + LIST_INIT(&kill); + for (int i = 0; i <= pf_srchashmask; i++) { + struct pf_srchash *sh = &V_pf_srchash[i]; + struct pf_src_node *sn, *tmp; + + PF_HASHROW_LOCK(sh); + LIST_FOREACH_SAFE(sn, &sh->nodes, entry, tmp) + if (PF_MATCHA(psnk->psnk_src.neg, + &psnk->psnk_src.addr.v.a.addr, + &psnk->psnk_src.addr.v.a.mask, + &sn->addr, sn->af) && + PF_MATCHA(psnk->psnk_dst.neg, + &psnk->psnk_dst.addr.v.a.addr, + &psnk->psnk_dst.addr.v.a.mask, + &sn->raddr, sn->af)) { + pf_unlink_src_node_locked(sn); + LIST_INSERT_HEAD(&kill, sn, entry); + sn->expire = 1; + } + PF_HASHROW_UNLOCK(sh); + } + + for (int i = 0; i <= pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + struct pf_state *s; + + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + if (s->src_node && s->src_node->expire == 1) { +#ifdef INVARIANTS + s->src_node->states--; +#endif + s->src_node = NULL; + } + if (s->nat_src_node && s->nat_src_node->expire == 1) { +#ifdef INVARIANTS + s->nat_src_node->states--; +#endif + s->nat_src_node = NULL; + } + } + PF_HASHROW_UNLOCK(ih); + } + + psnk->psnk_killed = pf_free_src_nodes(&kill); +} + /* * XXX - Check for version missmatch!!! */ @@ -3418,6 +3456,11 @@ shutdown_pf(void) char nn = '\0'; V_pf_status.running = 0; + + counter_u64_free(V_pf_default_rule.states_cur); + counter_u64_free(V_pf_default_rule.states_tot); + counter_u64_free(V_pf_default_rule.src_nodes); + do { if ((error = pf_begin_rules(&t[0], PF_RULESET_SCRUB, &nn)) != 0) { diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c index bc7cc09be5c0..62789f5da2b5 100644 --- a/sys/netpfil/pf/pf_lb.c +++ b/sys/netpfil/pf/pf_lb.c @@ -686,6 +686,7 @@ notrans: uma_zfree(V_pf_state_key_z, *nkp); uma_zfree(V_pf_state_key_z, *skp); *skp = *nkp = NULL; + *sn = NULL; return (NULL); } |