/* $OpenBSD: pfvar_priv.h,v 1.30 2023/01/06 17:44:34 sashan Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier * Copyright (c) 2002 - 2013 Henning Brauer * Copyright (c) 2016 Alexander Bluhm * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #ifndef _NET_PFVAR_PRIV_H_ #define _NET_PFVAR_PRIV_H_ #ifdef _KERNEL #include #include #include struct pf_state_item { TAILQ_ENTRY(pf_state_item) si_entry; struct pf_state *si_st; }; TAILQ_HEAD(pf_statelisthead, pf_state_item); struct pf_state_key { struct pf_addr addr[2]; u_int16_t port[2]; u_int16_t rdomain; u_int16_t hash; sa_family_t af; u_int8_t proto; RB_ENTRY(pf_state_key) sk_entry; struct pf_statelisthead sk_states; struct pf_state_key *sk_reverse; struct inpcb *sk_inp; pf_refcnt_t sk_refcnt; u_int8_t sk_removed; }; RBT_HEAD(pf_state_tree, pf_state_key); RBT_PROTOTYPE(pf_state_tree, pf_state_key, sk_entry, pf_state_compare_key); #define PF_REVERSED_KEY(key, family) \ ((key[PF_SK_WIRE]->af != key[PF_SK_STACK]->af) && \ (key[PF_SK_WIRE]->af != (family))) /* * Protection/ownership of pf_state members: * I immutable after pf_state_insert() * M pf_state mtx * P PF_STATE_LOCK * S pfsync mutex * L pf_state_list * g pf_purge gc */ struct pf_state { u_int64_t id; /* [I] */ u_int32_t creatorid; /* [I] */ u_int8_t direction; /* [I] */ u_int8_t pad[3]; TAILQ_ENTRY(pf_state) sync_list; /* [S] */ TAILQ_ENTRY(pf_state) sync_snap; /* [S] */ TAILQ_ENTRY(pf_state) entry_list; /* [L] */ SLIST_ENTRY(pf_state) gc_list; /* [g] */ RB_ENTRY(pf_state) entry_id; /* [P] */ struct pf_state_peer src; struct pf_state_peer dst; struct pf_rule_slist match_rules; /* [I] */ union pf_rule_ptr rule; /* [I] */ union pf_rule_ptr anchor; /* [I] */ union pf_rule_ptr natrule; /* [I] */ struct pf_addr rt_addr; /* [I] */ struct pf_sn_head src_nodes; /* [I] */ struct pf_state_key *key[2]; /* [I] stack and wire */ struct pfi_kif *kif; /* [I] */ struct mutex mtx; pf_refcnt_t refcnt; u_int64_t packets[2]; u_int64_t bytes[2]; int32_t creation; /* [I] */ int32_t expire; int32_t pfsync_time; int rtableid[2]; /* [I] rtables stack and wire */ u_int16_t qid; /* [I] */ u_int16_t pqid; /* [I] */ u_int16_t tag; /* [I] */ u_int16_t state_flags; u_int8_t log; /* [I] */ u_int8_t timeout; u_int8_t sync_state; /* PFSYNC_S_x */ u_int8_t sync_updates; u_int8_t min_ttl; /* [I] */ u_int8_t set_tos; /* [I] */ u_int8_t set_prio[2]; /* [I] */ u_int16_t max_mss; /* [I] */ u_int16_t if_index_in; /* [I] */ u_int16_t if_index_out; /* [I] */ u_int16_t delay; /* [I] */ u_int8_t rt; /* [I] */ u_int8_t snapped; /* [S] */ }; RBT_HEAD(pf_state_tree_id, pf_state); RBT_PROTOTYPE(pf_state_tree_id, pf_state, entry_id, pf_state_compare_id); extern struct pf_state_tree_id tree_id; /* * * states are linked into a global list to support the following * functionality: * * - garbage collection * - pfsync bulk send operations * - bulk state fetches via the DIOCGETSTATES ioctl * - bulk state clearing via the DIOCCLRSTATES ioctl * * states are inserted into the global pf_state_list once it has also * been successfully added to the various trees that make up the state * table. states are only removed from the pf_state_list by the garbage * collection process. * the pf_state_list head and tail pointers (ie, the pfs_list TAILQ_HEAD * structure) and the pointers between the entries on the pf_state_list * are locked separately. at a high level, this allows for insertion * of new states into the pf_state_list while other contexts (eg, the * ioctls) are traversing the state items in the list. for garbage * collection to remove items from the pf_state_list, it has to exclude * both modifications to the list head and tail pointers, and traversal * of the links between the states. * * the head and tail pointers are protected by a mutex. the pointers * between states are protected by an rwlock. * * because insertions are only made to the end of the list, if we get * a snapshot of the head and tail of the list and prevent modifications * to the links between states, we can safely traverse between the * head and tail entries. subsequent insertions can add entries after * our view of the tail, but we don't look past our view. * * if both locks must be taken, the rwlock protecting the links between * states is taken before the mutex protecting the head and tail * pointer. * * insertion into the list follows this pattern: * * // serialise list head/tail modifications * mtx_enter(&pf_state_list.pfs_mtx); * TAILQ_INSERT_TAIL(&pf_state_list.pfs_list, state, entry_list); * mtx_leave(&pf_state_list.pfs_mtx); * * traversal of the list: * * // lock against the gc removing an item from the list * rw_enter_read(&pf_state_list.pfs_rwl); * * // get a snapshot view of the ends of the list * mtx_enter(&pf_state_list.pfs_mtx); * head = TAILQ_FIRST(&pf_state_list.pfs_list); * tail = TAILQ_LAST(&pf_state_list.pfs_list, pf_state_queue); * mtx_leave(&pf_state_list.pfs_mtx); * * state = NULL; * next = head; * * while (state != tail) { * state = next; * next = TAILQ_NEXT(state, entry_list); * * // look at the state * } * * rw_exit_read(&pf_state_list.pfs_rwl); * * removing an item from the list: * * // wait for iterators (readers) to get out * rw_enter_write(&pf_state_list.pfs_rwl); * * // serialise list head/tail modifications * mtx_enter(&pf_state_list.pfs_mtx); * TAILQ_REMOVE(&pf_state_list.pfs_list, state, entry_list); * mtx_leave(&pf_state_list.pfs_mtx); * * rw_exit_write(&pf_state_list.pfs_rwl); * * the lock ordering for pf_state_list locks and the rest of the pf * locks are: * * 1. KERNEL_LOCK * 2. NET_LOCK * 3. pf_state_list.pfs_rwl * 4. PF_LOCK * 5. PF_STATE_LOCK * 6. pf_state_list.pfs_mtx */ struct pf_state_list { /* the list of states in the system */ struct pf_state_queue pfs_list; /* serialise pfs_list head/tail access */ struct mutex pfs_mtx; /* serialise access to pointers between pfs_list entries */ struct rwlock pfs_rwl; }; #define PF_STATE_LIST_INITIALIZER(_pfs) { \ .pfs_list = TAILQ_HEAD_INITIALIZER(_pfs.pfs_list), \ .pfs_mtx = MUTEX_INITIALIZER(IPL_SOFTNET), \ .pfs_rwl = RWLOCK_INITIALIZER("pfstates"), \ } extern struct rwlock pf_lock; struct pf_pdesc { struct { int done; uid_t uid; gid_t gid; pid_t pid; } lookup; u_int64_t tot_len; /* Make Mickey money */ struct pf_addr nsaddr; /* src address after NAT */ struct pf_addr ndaddr; /* dst address after NAT */ struct pfi_kif *kif; /* incoming interface */ struct mbuf *m; /* mbuf containing the packet */ struct pf_addr *src; /* src address */ struct pf_addr *dst; /* dst address */ u_int16_t *pcksum; /* proto cksum */ u_int16_t *sport; u_int16_t *dport; u_int16_t osport; u_int16_t odport; u_int16_t hash; u_int16_t nsport; /* src port after NAT */ u_int16_t ndport; /* dst port after NAT */ u_int32_t off; /* protocol header offset */ u_int32_t hdrlen; /* protocol header length */ u_int32_t p_len; /* length of protocol payload */ u_int32_t extoff; /* extension header offset */ u_int32_t fragoff; /* fragment header offset */ u_int32_t jumbolen; /* length from v6 jumbo header */ u_int32_t badopts; /* v4 options or v6 routing headers */ #define PF_OPT_OTHER 0x0001 #define PF_OPT_JUMBO 0x0002 #define PF_OPT_ROUTER_ALERT 0x0004 u_int16_t rdomain; /* original routing domain */ u_int16_t virtual_proto; #define PF_VPROTO_FRAGMENT 256 sa_family_t af; sa_family_t naf; u_int8_t proto; u_int8_t tos; u_int8_t ttl; u_int8_t dir; /* direction */ u_int8_t sidx; /* key index for source */ u_int8_t didx; /* key index for destination */ u_int8_t destchg; /* flag set when destination changed */ u_int8_t pflog; /* flags for packet logging */ union { struct tcphdr tcp; struct udphdr udp; struct icmp icmp; #ifdef INET6 struct icmp6_hdr icmp6; struct mld_hdr mld; struct nd_neighbor_solicit nd_ns; #endif /* INET6 */ } hdr; }; struct pf_anchor_stackframe { struct pf_ruleset *sf_rs; union { struct pf_rule *u_r; struct pf_anchor_stackframe *u_stack_top; } u; struct pf_anchor *sf_child; int sf_jump_target; }; #define sf_r u.u_r #define sf_stack_top u.u_stack_top enum { PF_NEXT_RULE, PF_NEXT_CHILD }; extern struct cpumem *pf_anchor_stack; extern struct task pf_purge_task; extern struct timeout pf_purge_to; struct pf_state *pf_state_ref(struct pf_state *); void pf_state_unref(struct pf_state *); extern struct rwlock pf_lock; extern struct rwlock pf_state_lock; #define PF_LOCK() do { \ rw_enter_write(&pf_lock); \ } while (0) #define PF_UNLOCK() do { \ PF_ASSERT_LOCKED(); \ rw_exit_write(&pf_lock); \ } while (0) #define PF_ASSERT_LOCKED() do { \ if (rw_status(&pf_lock) != RW_WRITE) \ splassert_fail(RW_WRITE, \ rw_status(&pf_lock),__func__);\ } while (0) #define PF_ASSERT_UNLOCKED() do { \ if (rw_status(&pf_lock) == RW_WRITE) \ splassert_fail(0, rw_status(&pf_lock), __func__);\ } while (0) #define PF_STATE_ENTER_READ() do { \ rw_enter_read(&pf_state_lock); \ } while (0) #define PF_STATE_EXIT_READ() do { \ rw_exit_read(&pf_state_lock); \ } while (0) #define PF_STATE_ENTER_WRITE() do { \ rw_enter_write(&pf_state_lock); \ } while (0) #define PF_STATE_EXIT_WRITE() do { \ PF_STATE_ASSERT_LOCKED(); \ rw_exit_write(&pf_state_lock); \ } while (0) #define PF_STATE_ASSERT_LOCKED() do { \ if (rw_status(&pf_state_lock) != RW_WRITE)\ splassert_fail(RW_WRITE, \ rw_status(&pf_state_lock), __func__);\ } while (0) extern void pf_purge_timeout(void *); extern void pf_purge(void *); /* for copies to/from network byte order */ void pf_state_peer_hton(const struct pf_state_peer *, struct pfsync_state_peer *); void pf_state_peer_ntoh(const struct pfsync_state_peer *, struct pf_state_peer *); #endif /* _KERNEL */ #endif /* _NET_PFVAR_PRIV_H_ */