diff options
Diffstat (limited to 'sys/contrib/pf/net/pf.c')
-rw-r--r-- | sys/contrib/pf/net/pf.c | 5632 |
1 files changed, 2736 insertions, 2896 deletions
diff --git a/sys/contrib/pf/net/pf.c b/sys/contrib/pf/net/pf.c index 135d734..5efd651 100644 --- a/sys/contrib/pf/net/pf.c +++ b/sys/contrib/pf/net/pf.c @@ -1,9 +1,8 @@ -/* $OpenBSD: pf.c,v 1.527 2007/02/22 15:23:23 pyr Exp $ */ -/* add: $OpenBSD: pf.c,v 1.559 2007/09/18 18:45:59 markus Exp $ */ +/* $OpenBSD: pf.c,v 1.634 2009/02/27 12:37:45 henning Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier - * Copyright (c) 2002,2003 Henning Brauer + * Copyright (c) 2002 - 2008 Henning Brauer * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -66,10 +65,17 @@ __FBSDID("$FreeBSD$"); #define NPFSYNC 0 #endif +#ifdef DEV_PFLOW +#define NPFLOW DEV_PFLOW +#else +#define NPFLOW 0 +#endif + #else #include "bpfilter.h" #include "pflog.h" #include "pfsync.h" +#include "pflow.h" #endif #include <sys/param.h> @@ -81,8 +87,10 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/time.h> #ifdef __FreeBSD__ +#include <sys/random.h> #include <sys/sysctl.h> #include <sys/endian.h> +#define betoh64 be64toh #else #include <sys/pool.h> #endif @@ -95,11 +103,21 @@ __FBSDID("$FreeBSD$"); #include <sys/rwlock.h> #endif +#ifdef __FreeBSD__ +#include <sys/md5.h> +#else +#include <crypto/md5.h> +#endif + #include <net/if.h> #include <net/if_types.h> #include <net/bpf.h> #include <net/route.h> -#ifndef __FreeBSD__ +#ifdef __FreeBSD__ +#ifdef RADIX_MPATH +#include <net/radix_mpath.h> +#endif +#else #include <net/radix_mpath.h> #endif @@ -118,16 +136,18 @@ __FBSDID("$FreeBSD$"); #include <netinet/udp_var.h> #include <netinet/icmp_var.h> #include <netinet/if_ether.h> +#ifdef __FreeBSD__ +#include <netinet/ip_fw.h> +#include <netinet/ipfw/ip_fw_private.h> /* XXX: only for DIR_IN/DIR_OUT */ +#endif #ifndef __FreeBSD__ #include <dev/rndvar.h> #endif #include <net/pfvar.h> #include <net/if_pflog.h> - -#if NPFSYNC > 0 +#include <net/if_pflow.h> #include <net/if_pfsync.h> -#endif /* NPFSYNC > 0 */ #ifdef INET6 #include <netinet/ip6.h> @@ -147,15 +167,61 @@ __FBSDID("$FreeBSD$"); #include <security/mac/mac_framework.h> extern int ip_optcopy(struct ip *, struct ip *); -extern int debug_pfugidhack; #endif -#define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x +#ifdef __FreeBSD__ +#define DPFPRINTF(n, x) if (V_pf_status.debug >= (n)) printf x +#else +#define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x +#endif /* * Global variables */ +/* state tables */ +#ifdef __FreeBSD__ +VNET_DEFINE(struct pf_state_tree, pf_statetbl); + +VNET_DEFINE(struct pf_altqqueue, pf_altqs[2]); +VNET_DEFINE(struct pf_palist, pf_pabuf); +VNET_DEFINE(struct pf_altqqueue *, pf_altqs_active); +VNET_DEFINE(struct pf_altqqueue *, pf_altqs_inactive); +VNET_DEFINE(struct pf_status, pf_status); + +VNET_DEFINE(u_int32_t, ticket_altqs_active); +VNET_DEFINE(u_int32_t, ticket_altqs_inactive); +VNET_DEFINE(int, altqs_inactive_open); +VNET_DEFINE(u_int32_t, ticket_pabuf); + +VNET_DEFINE(MD5_CTX, pf_tcp_secret_ctx); +#define V_pf_tcp_secret_ctx VNET(pf_tcp_secret_ctx) +VNET_DEFINE(u_char, pf_tcp_secret[16]); +#define V_pf_tcp_secret VNET(pf_tcp_secret) +VNET_DEFINE(int, pf_tcp_secret_init); +#define V_pf_tcp_secret_init VNET(pf_tcp_secret_init) +VNET_DEFINE(int, pf_tcp_iss_off); +#define V_pf_tcp_iss_off VNET(pf_tcp_iss_off) + +struct pf_anchor_stackframe { + struct pf_ruleset *rs; + struct pf_rule *r; + struct pf_anchor_node *parent; + struct pf_anchor *child; +}; +VNET_DEFINE(struct pf_anchor_stackframe, pf_anchor_stack[64]); +#define V_pf_anchor_stack VNET(pf_anchor_stack) + +VNET_DEFINE(uma_zone_t, pf_src_tree_pl); +VNET_DEFINE(uma_zone_t, pf_rule_pl); +VNET_DEFINE(uma_zone_t, pf_pooladdr_pl); +VNET_DEFINE(uma_zone_t, pf_state_pl); +VNET_DEFINE(uma_zone_t, pf_state_key_pl); +VNET_DEFINE(uma_zone_t, pf_state_item_pl); +VNET_DEFINE(uma_zone_t, pf_altq_pl); +#else +struct pf_state_tree pf_statetbl; + struct pf_altqqueue pf_altqs[2]; struct pf_palist pf_pabuf; struct pf_altqqueue *pf_altqs_active; @@ -167,6 +233,11 @@ u_int32_t ticket_altqs_inactive; int altqs_inactive_open; u_int32_t ticket_pabuf; +MD5_CTX pf_tcp_secret_ctx; +u_char pf_tcp_secret[16]; +int pf_tcp_secret_init; +int pf_tcp_iss_off; + struct pf_anchor_stackframe { struct pf_ruleset *rs; struct pf_rule *r; @@ -174,16 +245,11 @@ struct pf_anchor_stackframe { struct pf_anchor *child; } pf_anchor_stack[64]; -#ifdef __FreeBSD__ -uma_zone_t pf_src_tree_pl, pf_rule_pl; -uma_zone_t pf_state_pl, pf_altq_pl, pf_pooladdr_pl; -#else -struct pool pf_src_tree_pl, pf_rule_pl; -struct pool pf_state_pl, pf_altq_pl, pf_pooladdr_pl; +struct pool pf_src_tree_pl, pf_rule_pl, pf_pooladdr_pl; +struct pool pf_state_pl, pf_state_key_pl, pf_state_item_pl; +struct pool pf_altq_pl; #endif -void pf_print_host(struct pf_addr *, u_int16_t, u_int8_t); - void pf_init_threshold(struct pf_threshold *, u_int32_t, u_int32_t); void pf_add_threshold(struct pf_threshold *); @@ -212,27 +278,12 @@ void pf_send_tcp(const struct pf_rule *, sa_family_t, u_int16_t, u_int16_t, u_int32_t, u_int32_t, u_int8_t, u_int16_t, u_int16_t, u_int8_t, int, u_int16_t, struct ether_header *, struct ifnet *); -void pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t, +static void pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t, sa_family_t, struct pf_rule *); -struct pf_rule *pf_match_translation(struct pf_pdesc *, struct mbuf *, - int, int, struct pfi_kif *, - struct pf_addr *, u_int16_t, struct pf_addr *, - u_int16_t, int); -struct pf_rule *pf_get_translation(struct pf_pdesc *, struct mbuf *, - int, int, struct pfi_kif *, struct pf_src_node **, - struct pf_addr *, u_int16_t, - struct pf_addr *, u_int16_t, - struct pf_addr *, u_int16_t *); -int pf_test_tcp(struct pf_rule **, struct pf_state **, - int, struct pfi_kif *, struct mbuf *, int, - void *, struct pf_pdesc *, struct pf_rule **, -#ifdef __FreeBSD__ - struct pf_ruleset **, struct ifqueue *, - struct inpcb *); -#else - struct pf_ruleset **, struct ifqueue *); -#endif -int pf_test_udp(struct pf_rule **, struct pf_state **, +void pf_detach_state(struct pf_state *); +void pf_state_key_detach(struct pf_state *, int); +u_int32_t pf_tcp_iss(struct pf_pdesc *); +int pf_test_rule(struct pf_rule **, struct pf_state **, int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *, struct pf_rule **, #ifdef __FreeBSD__ @@ -241,14 +292,14 @@ int pf_test_udp(struct pf_rule **, struct pf_state **, #else struct pf_ruleset **, struct ifqueue *); #endif -int pf_test_icmp(struct pf_rule **, struct pf_state **, - int, struct pfi_kif *, struct mbuf *, int, - void *, struct pf_pdesc *, struct pf_rule **, - struct pf_ruleset **, struct ifqueue *); -int pf_test_other(struct pf_rule **, struct pf_state **, - int, struct pfi_kif *, struct mbuf *, int, void *, - struct pf_pdesc *, struct pf_rule **, - struct pf_ruleset **, struct ifqueue *); +static __inline int pf_create_state(struct pf_rule *, struct pf_rule *, + struct pf_rule *, struct pf_pdesc *, + struct pf_src_node *, struct pf_state_key *, + struct pf_state_key *, struct pf_state_key *, + struct pf_state_key *, struct mbuf *, int, + u_int16_t, u_int16_t, int *, struct pfi_kif *, + struct pf_state **, int, u_int16_t, u_int16_t, + int); int pf_test_fragment(struct pf_rule **, int, struct pfi_kif *, struct mbuf *, void *, struct pf_pdesc *, struct pf_rule **, @@ -257,7 +308,7 @@ int pf_tcp_track_full(struct pf_state_peer *, struct pf_state_peer *, struct pf_state **, struct pfi_kif *, struct mbuf *, int, struct pf_pdesc *, u_short *, int *); -int pf_tcp_track_sloppy(struct pf_state_peer *, +int pf_tcp_track_sloppy(struct pf_state_peer *, struct pf_state_peer *, struct pf_state **, struct pf_pdesc *, u_short *); int pf_test_state_tcp(struct pf_state **, int, @@ -270,30 +321,14 @@ int pf_test_state_icmp(struct pf_state **, int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *, u_short *); int pf_test_state_other(struct pf_state **, int, - struct pfi_kif *, struct pf_pdesc *); -int pf_match_tag(struct mbuf *, struct pf_rule *, - struct pf_mtag *, int *); -int pf_step_out_of_anchor(int *, struct pf_ruleset **, - int, struct pf_rule **, struct pf_rule **, - int *); -void pf_hash(struct pf_addr *, struct pf_addr *, - struct pf_poolhashkey *, sa_family_t); -int pf_map_addr(u_int8_t, struct pf_rule *, - struct pf_addr *, struct pf_addr *, - struct pf_addr *, struct pf_src_node **); -int pf_get_sport(sa_family_t, u_int8_t, struct pf_rule *, - struct pf_addr *, struct pf_addr *, u_int16_t, - struct pf_addr *, u_int16_t*, u_int16_t, u_int16_t, - struct pf_src_node **); + struct pfi_kif *, struct mbuf *, struct pf_pdesc *); void pf_route(struct mbuf **, struct pf_rule *, int, struct ifnet *, struct pf_state *, struct pf_pdesc *); void pf_route6(struct mbuf **, struct pf_rule *, int, struct ifnet *, struct pf_state *, struct pf_pdesc *); -#ifdef __FreeBSD__ -/* XXX: import */ -#else +#ifndef __FreeBSD__ int pf_socket_lookup(int, struct pf_pdesc *); #endif u_int8_t pf_get_wscale(struct mbuf *, int, u_int16_t, @@ -306,19 +341,32 @@ void pf_set_rt_ifp(struct pf_state *, struct pf_addr *); int pf_check_proto_cksum(struct mbuf *, int, int, u_int8_t, sa_family_t); +#ifndef __FreeBSD__ +struct pf_divert *pf_get_divert(struct mbuf *); +#endif +void pf_print_state_parts(struct pf_state *, + struct pf_state_key *, struct pf_state_key *); int pf_addr_wrap_neq(struct pf_addr_wrap *, struct pf_addr_wrap *); -struct pf_state *pf_find_state_recurse(struct pfi_kif *, - struct pf_state_cmp *, u_int8_t); +int pf_compare_state_keys(struct pf_state_key *, + struct pf_state_key *, struct pfi_kif *, u_int); +#ifdef __FreeBSD__ +struct pf_state *pf_find_state(struct pfi_kif *, + struct pf_state_key_cmp *, u_int, struct mbuf *, + struct pf_mtag *); +#else +struct pf_state *pf_find_state(struct pfi_kif *, + struct pf_state_key_cmp *, u_int, struct mbuf *); +#endif int pf_src_connlimit(struct pf_state **); int pf_check_congestion(struct ifqueue *); #ifdef __FreeBSD__ int in4_cksum(struct mbuf *m, u_int8_t nxt, int off, int len); -extern int pf_end_threads; +VNET_DECLARE(int, pf_end_threads); -struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX]; +VNET_DEFINE(struct pf_pool_limit, pf_pool_limits[PF_LIMIT_MAX]); #else extern struct pool pfr_ktable_pl; extern struct pool pfr_kentry_pl; @@ -332,80 +380,101 @@ struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] = { }; #endif -#define STATE_LOOKUP() \ +#ifdef __FreeBSD__ +#define PPACKET_LOOPED() \ + (pd->pf_mtag->flags & PF_PACKET_LOOPED) + +#define PACKET_LOOPED() \ + (pd.pf_mtag->flags & PF_PACKET_LOOPED) + +#define STATE_LOOKUP(i, k, d, s, m, pt) \ do { \ - if (direction == PF_IN) \ - *state = pf_find_state_recurse( \ - kif, &key, PF_EXT_GWY); \ - else \ - *state = pf_find_state_recurse( \ - kif, &key, PF_LAN_EXT); \ - if (*state == NULL || (*state)->timeout == PFTM_PURGE) \ + s = pf_find_state(i, k, d, m, pt); \ + if (s == NULL || (s)->timeout == PFTM_PURGE) \ return (PF_DROP); \ - if (direction == PF_OUT && \ - (((*state)->rule.ptr->rt == PF_ROUTETO && \ - (*state)->rule.ptr->direction == PF_OUT) || \ - ((*state)->rule.ptr->rt == PF_REPLYTO && \ - (*state)->rule.ptr->direction == PF_IN)) && \ - (*state)->rt_kif != NULL && \ - (*state)->rt_kif != kif) \ + if (PPACKET_LOOPED()) \ + return (PF_PASS); \ + if (d == PF_OUT && \ + (((s)->rule.ptr->rt == PF_ROUTETO && \ + (s)->rule.ptr->direction == PF_OUT) || \ + ((s)->rule.ptr->rt == PF_REPLYTO && \ + (s)->rule.ptr->direction == PF_IN)) && \ + (s)->rt_kif != NULL && \ + (s)->rt_kif != i) \ return (PF_PASS); \ } while (0) +#else +#define STATE_LOOKUP(i, k, d, s, m) \ + do { \ + s = pf_find_state(i, k, d, m); \ + if (s == NULL || (s)->timeout == PFTM_PURGE) \ + return (PF_DROP); \ + if (d == PF_OUT && \ + (((s)->rule.ptr->rt == PF_ROUTETO && \ + (s)->rule.ptr->direction == PF_OUT) || \ + ((s)->rule.ptr->rt == PF_REPLYTO && \ + (s)->rule.ptr->direction == PF_IN)) && \ + (s)->rt_kif != NULL && \ + (s)->rt_kif != i) \ + return (PF_PASS); \ + } while (0) +#endif -#define STATE_TRANSLATE(s) \ - (s)->lan.addr.addr32[0] != (s)->gwy.addr.addr32[0] || \ - ((s)->af == AF_INET6 && \ - ((s)->lan.addr.addr32[1] != (s)->gwy.addr.addr32[1] || \ - (s)->lan.addr.addr32[2] != (s)->gwy.addr.addr32[2] || \ - (s)->lan.addr.addr32[3] != (s)->gwy.addr.addr32[3])) || \ - (s)->lan.port != (s)->gwy.port - -#define BOUND_IFACE(r, k) \ +#ifdef __FreeBSD__ +#define BOUND_IFACE(r, k) \ + ((r)->rule_flag & PFRULE_IFBOUND) ? (k) : V_pfi_all +#else +#define BOUND_IFACE(r, k) \ ((r)->rule_flag & PFRULE_IFBOUND) ? (k) : pfi_all +#endif -#define STATE_INC_COUNTERS(s) \ +#define STATE_INC_COUNTERS(s) \ do { \ - s->rule.ptr->states++; \ - if (s->anchor.ptr != NULL) \ - s->anchor.ptr->states++; \ - if (s->nat_rule.ptr != NULL) \ - s->nat_rule.ptr->states++; \ + 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++; \ + } \ } while (0) -#define STATE_DEC_COUNTERS(s) \ +#define STATE_DEC_COUNTERS(s) \ do { \ if (s->nat_rule.ptr != NULL) \ - s->nat_rule.ptr->states--; \ + s->nat_rule.ptr->states_cur--; \ if (s->anchor.ptr != NULL) \ - s->anchor.ptr->states--; \ - s->rule.ptr->states--; \ + s->anchor.ptr->states_cur--; \ + s->rule.ptr->states_cur--; \ } while (0) +static __inline int pf_src_compare(struct pf_src_node *, struct pf_src_node *); +static __inline int pf_state_compare_key(struct pf_state_key *, + struct pf_state_key *); +static __inline int pf_state_compare_id(struct pf_state *, + struct pf_state *); + +#ifdef __FreeBSD__ +VNET_DEFINE(struct pf_src_tree, tree_src_tracking); + +VNET_DEFINE(struct pf_state_tree_id, tree_id); +VNET_DEFINE(struct pf_state_queue, state_list); +#else struct pf_src_tree tree_src_tracking; struct pf_state_tree_id tree_id; struct pf_state_queue state_list; - -#ifdef __FreeBSD__ -static int pf_src_compare(struct pf_src_node *, struct pf_src_node *); -static int pf_state_compare_lan_ext(struct pf_state *, struct pf_state *); -static int pf_state_compare_ext_gwy(struct pf_state *, struct pf_state *); -static int pf_state_compare_id(struct pf_state *, struct pf_state *); #endif RB_GENERATE(pf_src_tree, pf_src_node, entry, pf_src_compare); -RB_GENERATE(pf_state_tree_lan_ext, pf_state, - u.s.entry_lan_ext, pf_state_compare_lan_ext); -RB_GENERATE(pf_state_tree_ext_gwy, pf_state, - u.s.entry_ext_gwy, pf_state_compare_ext_gwy); +RB_GENERATE(pf_state_tree, pf_state_key, entry, pf_state_compare_key); RB_GENERATE(pf_state_tree_id, pf_state, - u.s.entry_id, pf_state_compare_id); + entry_id, pf_state_compare_id); -#ifdef __FreeBSD__ -static int -#else static __inline int -#endif pf_src_compare(struct pf_src_node *a, struct pf_src_node *b) { int diff; @@ -449,169 +518,6 @@ pf_src_compare(struct pf_src_node *a, struct pf_src_node *b) return (0); } -#ifdef __FreeBSD__ -static int -#else -static __inline int -#endif -pf_state_compare_lan_ext(struct pf_state *a, struct pf_state *b) -{ - int diff; - - if ((diff = a->proto - b->proto) != 0) - return (diff); - if ((diff = a->af - b->af) != 0) - return (diff); - switch (a->af) { -#ifdef INET - case AF_INET: - if (a->lan.addr.addr32[0] > b->lan.addr.addr32[0]) - return (1); - if (a->lan.addr.addr32[0] < b->lan.addr.addr32[0]) - return (-1); - if (a->ext.addr.addr32[0] > b->ext.addr.addr32[0]) - return (1); - if (a->ext.addr.addr32[0] < b->ext.addr.addr32[0]) - return (-1); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (a->lan.addr.addr32[3] > b->lan.addr.addr32[3]) - return (1); - if (a->lan.addr.addr32[3] < b->lan.addr.addr32[3]) - return (-1); - if (a->ext.addr.addr32[3] > b->ext.addr.addr32[3]) - return (1); - if (a->ext.addr.addr32[3] < b->ext.addr.addr32[3]) - return (-1); - if (a->lan.addr.addr32[2] > b->lan.addr.addr32[2]) - return (1); - if (a->lan.addr.addr32[2] < b->lan.addr.addr32[2]) - return (-1); - if (a->ext.addr.addr32[2] > b->ext.addr.addr32[2]) - return (1); - if (a->ext.addr.addr32[2] < b->ext.addr.addr32[2]) - return (-1); - if (a->lan.addr.addr32[1] > b->lan.addr.addr32[1]) - return (1); - if (a->lan.addr.addr32[1] < b->lan.addr.addr32[1]) - return (-1); - if (a->ext.addr.addr32[1] > b->ext.addr.addr32[1]) - return (1); - if (a->ext.addr.addr32[1] < b->ext.addr.addr32[1]) - return (-1); - if (a->lan.addr.addr32[0] > b->lan.addr.addr32[0]) - return (1); - if (a->lan.addr.addr32[0] < b->lan.addr.addr32[0]) - return (-1); - if (a->ext.addr.addr32[0] > b->ext.addr.addr32[0]) - return (1); - if (a->ext.addr.addr32[0] < b->ext.addr.addr32[0]) - return (-1); - break; -#endif /* INET6 */ - } - - if ((diff = a->lan.port - b->lan.port) != 0) - return (diff); - if ((diff = a->ext.port - b->ext.port) != 0) - return (diff); - - return (0); -} - -#ifdef __FreeBSD__ -static int -#else -static __inline int -#endif -pf_state_compare_ext_gwy(struct pf_state *a, struct pf_state *b) -{ - int diff; - - if ((diff = a->proto - b->proto) != 0) - return (diff); - if ((diff = a->af - b->af) != 0) - return (diff); - switch (a->af) { -#ifdef INET - case AF_INET: - if (a->ext.addr.addr32[0] > b->ext.addr.addr32[0]) - return (1); - if (a->ext.addr.addr32[0] < b->ext.addr.addr32[0]) - return (-1); - if (a->gwy.addr.addr32[0] > b->gwy.addr.addr32[0]) - return (1); - if (a->gwy.addr.addr32[0] < b->gwy.addr.addr32[0]) - return (-1); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (a->ext.addr.addr32[3] > b->ext.addr.addr32[3]) - return (1); - if (a->ext.addr.addr32[3] < b->ext.addr.addr32[3]) - return (-1); - if (a->gwy.addr.addr32[3] > b->gwy.addr.addr32[3]) - return (1); - if (a->gwy.addr.addr32[3] < b->gwy.addr.addr32[3]) - return (-1); - if (a->ext.addr.addr32[2] > b->ext.addr.addr32[2]) - return (1); - if (a->ext.addr.addr32[2] < b->ext.addr.addr32[2]) - return (-1); - if (a->gwy.addr.addr32[2] > b->gwy.addr.addr32[2]) - return (1); - if (a->gwy.addr.addr32[2] < b->gwy.addr.addr32[2]) - return (-1); - if (a->ext.addr.addr32[1] > b->ext.addr.addr32[1]) - return (1); - if (a->ext.addr.addr32[1] < b->ext.addr.addr32[1]) - return (-1); - if (a->gwy.addr.addr32[1] > b->gwy.addr.addr32[1]) - return (1); - if (a->gwy.addr.addr32[1] < b->gwy.addr.addr32[1]) - return (-1); - if (a->ext.addr.addr32[0] > b->ext.addr.addr32[0]) - return (1); - if (a->ext.addr.addr32[0] < b->ext.addr.addr32[0]) - return (-1); - if (a->gwy.addr.addr32[0] > b->gwy.addr.addr32[0]) - return (1); - if (a->gwy.addr.addr32[0] < b->gwy.addr.addr32[0]) - return (-1); - break; -#endif /* INET6 */ - } - - if ((diff = a->ext.port - b->ext.port) != 0) - return (diff); - if ((diff = a->gwy.port - b->gwy.port) != 0) - return (diff); - - return (0); -} - -#ifdef __FreeBSD__ -static int -#else -static __inline int -#endif -pf_state_compare_id(struct pf_state *a, struct pf_state *b) -{ - if (a->id > b->id) - return (1); - if (a->id < b->id) - return (-1); - if (a->creatorid > b->creatorid) - return (1); - if (a->creatorid < b->creatorid) - return (-1); - - return (0); -} - #ifdef INET6 void pf_addrcpy(struct pf_addr *dst, struct pf_addr *src, sa_family_t af) @@ -632,80 +538,6 @@ pf_addrcpy(struct pf_addr *dst, struct pf_addr *src, sa_family_t af) } #endif /* INET6 */ -struct pf_state * -pf_find_state_byid(struct pf_state_cmp *key) -{ - pf_status.fcounters[FCNT_STATE_SEARCH]++; - return (RB_FIND(pf_state_tree_id, &tree_id, (struct pf_state *)key)); -} - -struct pf_state * -pf_find_state_recurse(struct pfi_kif *kif, struct pf_state_cmp *key, u_int8_t tree) -{ - struct pf_state *s; - - pf_status.fcounters[FCNT_STATE_SEARCH]++; - - switch (tree) { - case PF_LAN_EXT: - if ((s = RB_FIND(pf_state_tree_lan_ext, &kif->pfik_lan_ext, - (struct pf_state *)key)) != NULL) - return (s); - if ((s = RB_FIND(pf_state_tree_lan_ext, &pfi_all->pfik_lan_ext, - (struct pf_state *)key)) != NULL) - return (s); - return (NULL); - case PF_EXT_GWY: - if ((s = RB_FIND(pf_state_tree_ext_gwy, &kif->pfik_ext_gwy, - (struct pf_state *)key)) != NULL) - return (s); - if ((s = RB_FIND(pf_state_tree_ext_gwy, &pfi_all->pfik_ext_gwy, - (struct pf_state *)key)) != NULL) - return (s); - return (NULL); - default: - panic("pf_find_state_recurse"); - } -} - -struct pf_state * -pf_find_state_all(struct pf_state_cmp *key, u_int8_t tree, int *more) -{ - struct pf_state *s, *ss = NULL; - struct pfi_kif *kif; - - pf_status.fcounters[FCNT_STATE_SEARCH]++; - - switch (tree) { - case PF_LAN_EXT: - TAILQ_FOREACH(kif, &pfi_statehead, pfik_w_states) { - s = RB_FIND(pf_state_tree_lan_ext, - &kif->pfik_lan_ext, (struct pf_state *)key); - if (s == NULL) - continue; - if (more == NULL) - return (s); - ss = s; - (*more)++; - } - return (ss); - case PF_EXT_GWY: - TAILQ_FOREACH(kif, &pfi_statehead, pfik_w_states) { - s = RB_FIND(pf_state_tree_ext_gwy, - &kif->pfik_ext_gwy, (struct pf_state *)key); - if (s == NULL) - continue; - if (more == NULL) - return (s); - ss = s; - (*more)++; - } - return (ss); - default: - panic("pf_find_state_all"); - } -} - void pf_init_threshold(struct pf_threshold *threshold, u_int32_t limit, u_int32_t seconds) @@ -739,7 +571,6 @@ pf_check_threshold(struct pf_threshold *threshold) int pf_src_connlimit(struct pf_state **state) { - struct pf_state *s; int bad = 0; (*state)->src_node->conn++; @@ -749,13 +580,21 @@ pf_src_connlimit(struct pf_state **state) if ((*state)->rule.ptr->max_src_conn && (*state)->rule.ptr->max_src_conn < (*state)->src_node->conn) { +#ifdef __FreeBSD__ + V_pf_status.lcounters[LCNT_SRCCONN]++; +#else pf_status.lcounters[LCNT_SRCCONN]++; +#endif bad++; } if ((*state)->rule.ptr->max_src_conn_rate.limit && pf_check_threshold(&(*state)->src_node->conn_rate)) { +#ifdef __FreeBSD__ + V_pf_status.lcounters[LCNT_SRCCONNRATE]++; +#else pf_status.lcounters[LCNT_SRCCONNRATE]++; +#endif bad++; } @@ -766,16 +605,21 @@ pf_src_connlimit(struct pf_state **state) struct pfr_addr p; u_int32_t killed = 0; +#ifdef __FreeBSD__ + V_pf_status.lcounters[LCNT_OVERLOAD_TABLE]++; + if (V_pf_status.debug >= PF_DEBUG_MISC) { +#else pf_status.lcounters[LCNT_OVERLOAD_TABLE]++; if (pf_status.debug >= PF_DEBUG_MISC) { +#endif printf("pf_src_connlimit: blocking address "); pf_print_host(&(*state)->src_node->addr, 0, - (*state)->af); + (*state)->key[PF_SK_WIRE]->af); } bzero(&p, sizeof(p)); - p.pfra_af = (*state)->af; - switch ((*state)->af) { + p.pfra_af = (*state)->key[PF_SK_WIRE]->af; + switch ((*state)->key[PF_SK_WIRE]->af) { #ifdef INET case AF_INET: p.pfra_net = 32; @@ -795,34 +639,51 @@ pf_src_connlimit(struct pf_state **state) /* kill existing states if that's required. */ if ((*state)->rule.ptr->flush) { - pf_status.lcounters[LCNT_OVERLOAD_FLUSH]++; + struct pf_state_key *sk; + struct pf_state *st; - RB_FOREACH(s, pf_state_tree_id, &tree_id) { +#ifdef __FreeBSD__ + V_pf_status.lcounters[LCNT_OVERLOAD_FLUSH]++; + RB_FOREACH(st, pf_state_tree_id, &V_tree_id) { +#else + pf_status.lcounters[LCNT_OVERLOAD_FLUSH]++; + RB_FOREACH(st, pf_state_tree_id, &tree_id) { +#endif + sk = st->key[PF_SK_WIRE]; /* * Kill states from this source. (Only those * from the same rule if PF_FLUSH_GLOBAL is not * set) */ - if (s->af == (*state)->af && + if (sk->af == + (*state)->key[PF_SK_WIRE]->af && (((*state)->direction == PF_OUT && PF_AEQ(&(*state)->src_node->addr, - &s->lan.addr, s->af)) || + &sk->addr[0], sk->af)) || ((*state)->direction == PF_IN && PF_AEQ(&(*state)->src_node->addr, - &s->ext.addr, s->af))) && + &sk->addr[1], sk->af))) && ((*state)->rule.ptr->flush & PF_FLUSH_GLOBAL || - (*state)->rule.ptr == s->rule.ptr)) { - s->timeout = PFTM_PURGE; - s->src.state = s->dst.state = + (*state)->rule.ptr == st->rule.ptr)) { + st->timeout = PFTM_PURGE; + st->src.state = st->dst.state = TCPS_CLOSED; killed++; } } +#ifdef __FreeBSD__ + if (V_pf_status.debug >= PF_DEBUG_MISC) +#else if (pf_status.debug >= PF_DEBUG_MISC) +#endif printf(", %u states killed", killed); } +#ifdef __FreeBSD__ + if (V_pf_status.debug >= PF_DEBUG_MISC) +#else if (pf_status.debug >= PF_DEBUG_MISC) +#endif printf("\n"); } @@ -846,18 +707,30 @@ pf_insert_src_node(struct pf_src_node **sn, struct pf_rule *rule, k.rule.ptr = rule; else k.rule.ptr = NULL; +#ifdef __FreeBSD__ + V_pf_status.scounters[SCNT_SRC_NODE_SEARCH]++; + *sn = RB_FIND(pf_src_tree, &V_tree_src_tracking, &k); +#else pf_status.scounters[SCNT_SRC_NODE_SEARCH]++; *sn = RB_FIND(pf_src_tree, &tree_src_tracking, &k); +#endif } if (*sn == NULL) { if (!rule->max_src_nodes || rule->src_nodes < rule->max_src_nodes) - (*sn) = pool_get(&pf_src_tree_pl, PR_NOWAIT); +#ifdef __FreeBSD__ + (*sn) = pool_get(&V_pf_src_tree_pl, PR_NOWAIT | PR_ZERO); +#else + (*sn) = pool_get(&pf_src_tree_pl, PR_NOWAIT | PR_ZERO); +#endif else +#ifdef __FreeBSD__ + V_pf_status.lcounters[LCNT_SRCNODES]++; +#else pf_status.lcounters[LCNT_SRCNODES]++; +#endif if ((*sn) == NULL) return (-1); - bzero(*sn, sizeof(struct pf_src_node)); pf_init_threshold(&(*sn)->conn_rate, rule->max_src_conn_rate.limit, @@ -871,109 +744,590 @@ pf_insert_src_node(struct pf_src_node **sn, struct pf_rule *rule, (*sn)->rule.ptr = NULL; PF_ACPY(&(*sn)->addr, src, af); if (RB_INSERT(pf_src_tree, +#ifdef __FreeBSD__ + &V_tree_src_tracking, *sn) != NULL) { + if (V_pf_status.debug >= PF_DEBUG_MISC) { +#else &tree_src_tracking, *sn) != NULL) { if (pf_status.debug >= PF_DEBUG_MISC) { +#endif printf("pf: src_tree insert failed: "); pf_print_host(&(*sn)->addr, 0, af); printf("\n"); } +#ifdef __FreeBSD__ + pool_put(&V_pf_src_tree_pl, *sn); +#else pool_put(&pf_src_tree_pl, *sn); +#endif return (-1); } (*sn)->creation = time_second; (*sn)->ruletype = rule->action; if ((*sn)->rule.ptr != NULL) (*sn)->rule.ptr->src_nodes++; +#ifdef __FreeBSD__ + V_pf_status.scounters[SCNT_SRC_NODE_INSERT]++; + V_pf_status.src_nodes++; +#else pf_status.scounters[SCNT_SRC_NODE_INSERT]++; pf_status.src_nodes++; +#endif } else { if (rule->max_src_states && (*sn)->states >= rule->max_src_states) { +#ifdef __FreeBSD__ + V_pf_status.lcounters[LCNT_SRCSTATES]++; +#else pf_status.lcounters[LCNT_SRCSTATES]++; +#endif return (-1); } } return (0); } +/* state table stuff */ + +static __inline int +pf_state_compare_key(struct pf_state_key *a, struct pf_state_key *b) +{ + int diff; + + if ((diff = a->proto - b->proto) != 0) + return (diff); + if ((diff = a->af - b->af) != 0) + return (diff); + switch (a->af) { +#ifdef INET + case AF_INET: + if (a->addr[0].addr32[0] > b->addr[0].addr32[0]) + return (1); + if (a->addr[0].addr32[0] < b->addr[0].addr32[0]) + return (-1); + if (a->addr[1].addr32[0] > b->addr[1].addr32[0]) + return (1); + if (a->addr[1].addr32[0] < b->addr[1].addr32[0]) + return (-1); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (a->addr[0].addr32[3] > b->addr[0].addr32[3]) + return (1); + if (a->addr[0].addr32[3] < b->addr[0].addr32[3]) + return (-1); + if (a->addr[1].addr32[3] > b->addr[1].addr32[3]) + return (1); + if (a->addr[1].addr32[3] < b->addr[1].addr32[3]) + return (-1); + if (a->addr[0].addr32[2] > b->addr[0].addr32[2]) + return (1); + if (a->addr[0].addr32[2] < b->addr[0].addr32[2]) + return (-1); + if (a->addr[1].addr32[2] > b->addr[1].addr32[2]) + return (1); + if (a->addr[1].addr32[2] < b->addr[1].addr32[2]) + return (-1); + if (a->addr[0].addr32[1] > b->addr[0].addr32[1]) + return (1); + if (a->addr[0].addr32[1] < b->addr[0].addr32[1]) + return (-1); + if (a->addr[1].addr32[1] > b->addr[1].addr32[1]) + return (1); + if (a->addr[1].addr32[1] < b->addr[1].addr32[1]) + return (-1); + if (a->addr[0].addr32[0] > b->addr[0].addr32[0]) + return (1); + if (a->addr[0].addr32[0] < b->addr[0].addr32[0]) + return (-1); + if (a->addr[1].addr32[0] > b->addr[1].addr32[0]) + return (1); + if (a->addr[1].addr32[0] < b->addr[1].addr32[0]) + return (-1); + break; +#endif /* INET6 */ + } + + if ((diff = a->port[0] - b->port[0]) != 0) + return (diff); + if ((diff = a->port[1] - b->port[1]) != 0) + return (diff); + + return (0); +} + +static __inline int +pf_state_compare_id(struct pf_state *a, struct pf_state *b) +{ + if (a->id > b->id) + return (1); + if (a->id < b->id) + return (-1); + if (a->creatorid > b->creatorid) + return (1); + if (a->creatorid < b->creatorid) + return (-1); + + return (0); +} + int -pf_insert_state(struct pfi_kif *kif, struct pf_state *state) +pf_state_key_attach(struct pf_state_key *sk, struct pf_state *s, int idx) { - /* Thou MUST NOT insert multiple duplicate keys */ - state->u.s.kif = kif; - if (RB_INSERT(pf_state_tree_lan_ext, &kif->pfik_lan_ext, state)) { - if (pf_status.debug >= PF_DEBUG_MISC) { - printf("pf: state insert failed: tree_lan_ext"); - printf(" lan: "); - pf_print_host(&state->lan.addr, state->lan.port, - state->af); - printf(" gwy: "); - pf_print_host(&state->gwy.addr, state->gwy.port, - state->af); - printf(" ext: "); - pf_print_host(&state->ext.addr, state->ext.port, - state->af); - if (state->sync_flags & PFSTATE_FROMSYNC) - printf(" (from sync)"); - printf("\n"); - } + struct pf_state_item *si; + struct pf_state_key *cur; + struct pf_state *olds = NULL; + +#ifdef __FreeBSD__ + KASSERT(s->key[idx] == NULL, ("%s: key is null!", __FUNCTION__)); +#else + KASSERT(s->key[idx] == NULL); /* XXX handle this? */ +#endif + +#ifdef __FreeBSD__ + if ((cur = RB_INSERT(pf_state_tree, &V_pf_statetbl, sk)) != NULL) { +#else + if ((cur = RB_INSERT(pf_state_tree, &pf_statetbl, sk)) != NULL) { +#endif + /* key exists. check for same kif, if none, add to key */ + TAILQ_FOREACH(si, &cur->states, entry) + if (si->s->kif == s->kif && + si->s->direction == s->direction) { + if (sk->proto == IPPROTO_TCP && + si->s->src.state >= TCPS_FIN_WAIT_2 && + si->s->dst.state >= TCPS_FIN_WAIT_2) { + si->s->src.state = si->s->dst.state = + TCPS_CLOSED; + /* unlink late or sks can go away */ + olds = si->s; + } else { +#ifdef __FreeBSD__ + if (V_pf_status.debug >= PF_DEBUG_MISC) { +#else + if (pf_status.debug >= PF_DEBUG_MISC) { +#endif + printf("pf: %s key attach " + "failed on %s: ", + (idx == PF_SK_WIRE) ? + "wire" : "stack", + s->kif->pfik_name); + pf_print_state_parts(s, + (idx == PF_SK_WIRE) ? + sk : NULL, + (idx == PF_SK_STACK) ? + sk : NULL); + printf(", existing: "); + pf_print_state_parts(si->s, + (idx == PF_SK_WIRE) ? + sk : NULL, + (idx == PF_SK_STACK) ? + sk : NULL); + printf("\n"); + } +#ifdef __FreeBSD__ + pool_put(&V_pf_state_key_pl, sk); +#else + pool_put(&pf_state_key_pl, sk); +#endif + return (-1); /* collision! */ + } + } +#ifdef __FreeBSD__ + pool_put(&V_pf_state_key_pl, sk); +#else + pool_put(&pf_state_key_pl, sk); +#endif + s->key[idx] = cur; + } else + s->key[idx] = sk; + +#ifdef __FreeBSD__ + if ((si = pool_get(&V_pf_state_item_pl, PR_NOWAIT)) == NULL) { +#else + if ((si = pool_get(&pf_state_item_pl, PR_NOWAIT)) == NULL) { +#endif + pf_state_key_detach(s, idx); return (-1); } + si->s = s; - if (RB_INSERT(pf_state_tree_ext_gwy, &kif->pfik_ext_gwy, state)) { - if (pf_status.debug >= PF_DEBUG_MISC) { - printf("pf: state insert failed: tree_ext_gwy"); - printf(" lan: "); - pf_print_host(&state->lan.addr, state->lan.port, - state->af); - printf(" gwy: "); - pf_print_host(&state->gwy.addr, state->gwy.port, - state->af); - printf(" ext: "); - pf_print_host(&state->ext.addr, state->ext.port, - state->af); - if (state->sync_flags & PFSTATE_FROMSYNC) - printf(" (from sync)"); - printf("\n"); + /* list is sorted, if-bound states before floating */ +#ifdef __FreeBSD__ + if (s->kif == V_pfi_all) +#else + if (s->kif == pfi_all) +#endif + TAILQ_INSERT_TAIL(&s->key[idx]->states, si, entry); + else + TAILQ_INSERT_HEAD(&s->key[idx]->states, si, entry); + + if (olds) + pf_unlink_state(olds); + + return (0); +} + +void +pf_detach_state(struct pf_state *s) +{ + if (s->key[PF_SK_WIRE] == s->key[PF_SK_STACK]) + s->key[PF_SK_WIRE] = NULL; + + if (s->key[PF_SK_STACK] != NULL) + pf_state_key_detach(s, PF_SK_STACK); + + if (s->key[PF_SK_WIRE] != NULL) + pf_state_key_detach(s, PF_SK_WIRE); +} + +void +pf_state_key_detach(struct pf_state *s, int idx) +{ + struct pf_state_item *si; + + si = TAILQ_FIRST(&s->key[idx]->states); + while (si && si->s != s) + si = TAILQ_NEXT(si, entry); + + if (si) { + TAILQ_REMOVE(&s->key[idx]->states, si, entry); +#ifdef __FreeBSD__ + pool_put(&V_pf_state_item_pl, si); +#else + pool_put(&pf_state_item_pl, si); +#endif + } + + if (TAILQ_EMPTY(&s->key[idx]->states)) { +#ifdef __FreeBSD__ + RB_REMOVE(pf_state_tree, &V_pf_statetbl, s->key[idx]); +#else + RB_REMOVE(pf_state_tree, &pf_statetbl, s->key[idx]); +#endif + if (s->key[idx]->reverse) + s->key[idx]->reverse->reverse = NULL; +#ifdef __FreeBSD__ + /* XXX: implement this */ +#else + if (s->key[idx]->inp) + s->key[idx]->inp->inp_pf_sk = NULL; +#endif +#ifdef __FreeBSD__ + pool_put(&V_pf_state_key_pl, s->key[idx]); +#else + pool_put(&pf_state_key_pl, s->key[idx]); +#endif + } + s->key[idx] = NULL; +} + +struct pf_state_key * +pf_alloc_state_key(int pool_flags) +{ + struct pf_state_key *sk; + +#ifdef __FreeBSD__ + if ((sk = pool_get(&V_pf_state_key_pl, pool_flags)) == NULL) +#else + if ((sk = pool_get(&pf_state_key_pl, pool_flags)) == NULL) +#endif + return (NULL); + TAILQ_INIT(&sk->states); + + return (sk); +} + +int +pf_state_key_setup(struct pf_pdesc *pd, struct pf_rule *nr, + struct pf_state_key **skw, struct pf_state_key **sks, + struct pf_state_key **skp, struct pf_state_key **nkp, + struct pf_addr *saddr, struct pf_addr *daddr, + u_int16_t sport, u_int16_t dport) +{ +#ifdef __FreeBSD__ + KASSERT((*skp == NULL && *nkp == NULL), + ("%s: skp == NULL && nkp == NULL", __FUNCTION__)); +#else + KASSERT((*skp == NULL && *nkp == NULL)); +#endif + + if ((*skp = pf_alloc_state_key(PR_NOWAIT | PR_ZERO)) == NULL) + return (ENOMEM); + + PF_ACPY(&(*skp)->addr[pd->sidx], saddr, pd->af); + PF_ACPY(&(*skp)->addr[pd->didx], daddr, pd->af); + (*skp)->port[pd->sidx] = sport; + (*skp)->port[pd->didx] = dport; + (*skp)->proto = pd->proto; + (*skp)->af = pd->af; + + if (nr != NULL) { + if ((*nkp = pf_alloc_state_key(PR_NOWAIT | PR_ZERO)) == NULL) + return (ENOMEM); /* caller must handle cleanup */ + + /* XXX maybe just bcopy and TAILQ_INIT(&(*nkp)->states) */ + PF_ACPY(&(*nkp)->addr[0], &(*skp)->addr[0], pd->af); + PF_ACPY(&(*nkp)->addr[1], &(*skp)->addr[1], pd->af); + (*nkp)->port[0] = (*skp)->port[0]; + (*nkp)->port[1] = (*skp)->port[1]; + (*nkp)->proto = pd->proto; + (*nkp)->af = pd->af; + } else + *nkp = *skp; + + if (pd->dir == PF_IN) { + *skw = *skp; + *sks = *nkp; + } else { + *sks = *skp; + *skw = *nkp; + } + return (0); +} + + +int +pf_state_insert(struct pfi_kif *kif, struct pf_state_key *skw, + struct pf_state_key *sks, struct pf_state *s) +{ +#ifndef __FreeBSD__ + splassert(IPL_SOFTNET); +#endif + + s->kif = kif; + + if (skw == sks) { + if (pf_state_key_attach(skw, s, PF_SK_WIRE)) + return (-1); + s->key[PF_SK_STACK] = s->key[PF_SK_WIRE]; + } else { + if (pf_state_key_attach(skw, s, PF_SK_WIRE)) { +#ifdef __FreeBSD__ + pool_put(&V_pf_state_key_pl, sks); +#else + pool_put(&pf_state_key_pl, sks); +#endif + return (-1); + } + if (pf_state_key_attach(sks, s, PF_SK_STACK)) { + pf_state_key_detach(s, PF_SK_WIRE); + return (-1); } - RB_REMOVE(pf_state_tree_lan_ext, &kif->pfik_lan_ext, state); - return (-1); } - if (state->id == 0 && state->creatorid == 0) { - state->id = htobe64(pf_status.stateid++); - state->creatorid = pf_status.hostid; + if (s->id == 0 && s->creatorid == 0) { +#ifdef __FreeBSD__ + s->id = htobe64(V_pf_status.stateid++); + s->creatorid = V_pf_status.hostid; +#else + s->id = htobe64(pf_status.stateid++); + s->creatorid = pf_status.hostid; +#endif } - if (RB_INSERT(pf_state_tree_id, &tree_id, state) != NULL) { - if (pf_status.debug >= PF_DEBUG_MISC) { #ifdef __FreeBSD__ - printf("pf: state insert failed: " - "id: %016llx creatorid: %08x", - (long long)be64toh(state->id), - ntohl(state->creatorid)); + if (RB_INSERT(pf_state_tree_id, &V_tree_id, s) != NULL) { + if (V_pf_status.debug >= PF_DEBUG_MISC) { #else + if (RB_INSERT(pf_state_tree_id, &tree_id, s) != NULL) { + if (pf_status.debug >= PF_DEBUG_MISC) { +#endif printf("pf: state insert failed: " "id: %016llx creatorid: %08x", - betoh64(state->id), ntohl(state->creatorid)); +#ifdef __FreeBSD__ + (unsigned long long)betoh64(s->id), ntohl(s->creatorid)); +#else + betoh64(s->id), ntohl(s->creatorid)); #endif - if (state->sync_flags & PFSTATE_FROMSYNC) - printf(" (from sync)"); printf("\n"); } - RB_REMOVE(pf_state_tree_lan_ext, &kif->pfik_lan_ext, state); - RB_REMOVE(pf_state_tree_ext_gwy, &kif->pfik_ext_gwy, state); + pf_detach_state(s); return (-1); } - TAILQ_INSERT_TAIL(&state_list, state, u.s.entry_list); +#ifdef __FreeBSD__ + TAILQ_INSERT_TAIL(&V_state_list, s, entry_list); + V_pf_status.fcounters[FCNT_STATE_INSERT]++; + V_pf_status.states++; +#else + TAILQ_INSERT_TAIL(&state_list, s, entry_list); pf_status.fcounters[FCNT_STATE_INSERT]++; pf_status.states++; +#endif pfi_kif_ref(kif, PFI_KIF_REF_STATE); -#if NPFSYNC - pfsync_insert_state(state); +#if NPFSYNC > 0 +#ifdef __FreeBSD__ + if (pfsync_insert_state_ptr != NULL) + pfsync_insert_state_ptr(s); +#else + pfsync_insert_state(s); +#endif #endif return (0); } +struct pf_state * +pf_find_state_byid(struct pf_state_cmp *key) +{ +#ifdef __FreeBSD__ + V_pf_status.fcounters[FCNT_STATE_SEARCH]++; + + return (RB_FIND(pf_state_tree_id, &V_tree_id, (struct pf_state *)key)); +#else + pf_status.fcounters[FCNT_STATE_SEARCH]++; + + return (RB_FIND(pf_state_tree_id, &tree_id, (struct pf_state *)key)); +#endif +} + +/* XXX debug function, intended to be removed one day */ +int +pf_compare_state_keys(struct pf_state_key *a, struct pf_state_key *b, + struct pfi_kif *kif, u_int dir) +{ + /* a (from hdr) and b (new) must be exact opposites of each other */ + if (a->af == b->af && a->proto == b->proto && + PF_AEQ(&a->addr[0], &b->addr[1], a->af) && + PF_AEQ(&a->addr[1], &b->addr[0], a->af) && + a->port[0] == b->port[1] && + a->port[1] == b->port[0]) + return (0); + else { + /* mismatch. must not happen. */ + printf("pf: state key linking mismatch! dir=%s, " + "if=%s, stored af=%u, a0: ", + dir == PF_OUT ? "OUT" : "IN", kif->pfik_name, a->af); + pf_print_host(&a->addr[0], a->port[0], a->af); + printf(", a1: "); + pf_print_host(&a->addr[1], a->port[1], a->af); + printf(", proto=%u", a->proto); + printf(", found af=%u, a0: ", b->af); + pf_print_host(&b->addr[0], b->port[0], b->af); + printf(", a1: "); + pf_print_host(&b->addr[1], b->port[1], b->af); + printf(", proto=%u", b->proto); + printf(".\n"); + return (-1); + } +} + +struct pf_state * +#ifdef __FreeBSD__ +pf_find_state(struct pfi_kif *kif, struct pf_state_key_cmp *key, u_int dir, + struct mbuf *m, struct pf_mtag *pftag) +#else +pf_find_state(struct pfi_kif *kif, struct pf_state_key_cmp *key, u_int dir, + struct mbuf *m) +#endif +{ + struct pf_state_key *sk; + struct pf_state_item *si; + +#ifdef __FreeBSD__ + V_pf_status.fcounters[FCNT_STATE_SEARCH]++; +#else + pf_status.fcounters[FCNT_STATE_SEARCH]++; +#endif + +#ifdef __FreeBSD__ + if (dir == PF_OUT && pftag->statekey && + ((struct pf_state_key *)pftag->statekey)->reverse) + sk = ((struct pf_state_key *)pftag->statekey)->reverse; + else { +#ifdef __FreeBSD__ + if ((sk = RB_FIND(pf_state_tree, &V_pf_statetbl, +#else + if ((sk = RB_FIND(pf_state_tree, &pf_statetbl, +#endif + (struct pf_state_key *)key)) == NULL) + return (NULL); + if (dir == PF_OUT && pftag->statekey && + pf_compare_state_keys(pftag->statekey, sk, + kif, dir) == 0) { + ((struct pf_state_key *) + pftag->statekey)->reverse = sk; + sk->reverse = pftag->statekey; + } + } +#else + if (dir == PF_OUT && m->m_pkthdr.pf.statekey && + ((struct pf_state_key *)m->m_pkthdr.pf.statekey)->reverse) + sk = ((struct pf_state_key *)m->m_pkthdr.pf.statekey)->reverse; + else { +#ifdef __FreeBSD__ + if ((sk = RB_FIND(pf_state_tree, &V_pf_statetbl, +#else + if ((sk = RB_FIND(pf_state_tree, &pf_statetbl, +#endif + (struct pf_state_key *)key)) == NULL) + return (NULL); + if (dir == PF_OUT && m->m_pkthdr.pf.statekey && + pf_compare_state_keys(m->m_pkthdr.pf.statekey, sk, + kif, dir) == 0) { + ((struct pf_state_key *) + m->m_pkthdr.pf.statekey)->reverse = sk; + sk->reverse = m->m_pkthdr.pf.statekey; + } + } +#endif + + if (dir == PF_OUT) +#ifdef __FreeBSD__ + pftag->statekey = NULL; +#else + m->m_pkthdr.pf.statekey = NULL; +#endif + + /* list is sorted, if-bound states before floating ones */ + TAILQ_FOREACH(si, &sk->states, entry) +#ifdef __FreeBSD__ + if ((si->s->kif == V_pfi_all || si->s->kif == kif) && +#else + if ((si->s->kif == pfi_all || si->s->kif == kif) && +#endif + sk == (dir == PF_IN ? si->s->key[PF_SK_WIRE] : + si->s->key[PF_SK_STACK])) + return (si->s); + + return (NULL); +} + +struct pf_state * +pf_find_state_all(struct pf_state_key_cmp *key, u_int dir, int *more) +{ + struct pf_state_key *sk; + struct pf_state_item *si, *ret = NULL; + +#ifdef __FreeBSD__ + V_pf_status.fcounters[FCNT_STATE_SEARCH]++; +#else + pf_status.fcounters[FCNT_STATE_SEARCH]++; +#endif + +#ifdef __FreeBSD__ + sk = RB_FIND(pf_state_tree, &V_pf_statetbl, (struct pf_state_key *)key); +#else + sk = RB_FIND(pf_state_tree, &pf_statetbl, (struct pf_state_key *)key); +#endif + if (sk != NULL) { + TAILQ_FOREACH(si, &sk->states, entry) + if (dir == PF_INOUT || + (sk == (dir == PF_IN ? si->s->key[PF_SK_WIRE] : + si->s->key[PF_SK_STACK]))) { + if (more == NULL) + return (si->s); + + if (ret) + (*more)++; + else + ret = si; + } + } + return (ret ? ret->s : NULL); +} + +/* END state table stuff */ + + void pf_purge_thread(void *v) { @@ -982,61 +1336,61 @@ pf_purge_thread(void *v) int locked; #endif + CURVNET_SET((struct vnet *)v); + for (;;) { tsleep(pf_purge_thread, PWAIT, "pftm", 1 * hz); #ifdef __FreeBSD__ - sx_slock(&pf_consistency_lock); + sx_slock(&V_pf_consistency_lock); + PF_LOCK(); + locked = 0; + + if (V_pf_end_threads) { + PF_UNLOCK(); + sx_sunlock(&V_pf_consistency_lock); + sx_xlock(&V_pf_consistency_lock); PF_LOCK(); - locked = 0; - if (pf_end_threads) { - PF_UNLOCK(); - sx_sunlock(&pf_consistency_lock); - sx_xlock(&pf_consistency_lock); - PF_LOCK(); - pf_purge_expired_states(pf_status.states, 1); - pf_purge_expired_fragments(); - pf_purge_expired_src_nodes(1); - pf_end_threads++; + pf_purge_expired_states(V_pf_status.states, 1); + pf_purge_expired_fragments(); + pf_purge_expired_src_nodes(1); + V_pf_end_threads++; - sx_xunlock(&pf_consistency_lock); - PF_UNLOCK(); - wakeup(pf_purge_thread); - kproc_exit(0); - } + sx_xunlock(&V_pf_consistency_lock); + PF_UNLOCK(); + wakeup(pf_purge_thread); + kproc_exit(0); + } #endif s = splsoftnet(); /* process a fraction of the state table every second */ #ifdef __FreeBSD__ - if(!pf_purge_expired_states(1 + (pf_status.states - / pf_default_rule.timeout[PFTM_INTERVAL]), 0)) { - PF_UNLOCK(); - sx_sunlock(&pf_consistency_lock); - sx_xlock(&pf_consistency_lock); - PF_LOCK(); - locked = 1; + if (!pf_purge_expired_states(1 + (V_pf_status.states / + V_pf_default_rule.timeout[PFTM_INTERVAL]), 0)) { + PF_UNLOCK(); + sx_sunlock(&V_pf_consistency_lock); + sx_xlock(&V_pf_consistency_lock); + PF_LOCK(); + locked = 1; - pf_purge_expired_states(1 + (pf_status.states - / pf_default_rule.timeout[PFTM_INTERVAL]), 1); - } + pf_purge_expired_states(1 + (V_pf_status.states / + V_pf_default_rule.timeout[PFTM_INTERVAL]), 1); + } #else pf_purge_expired_states(1 + (pf_status.states / pf_default_rule.timeout[PFTM_INTERVAL])); #endif /* purge other expired types every PFTM_INTERVAL seconds */ +#ifdef __FreeBSD__ + if (++nloops >= V_pf_default_rule.timeout[PFTM_INTERVAL]) { +#else if (++nloops >= pf_default_rule.timeout[PFTM_INTERVAL]) { +#endif pf_purge_expired_fragments(); - if (!pf_purge_expired_src_nodes(locked)) { - PF_UNLOCK(); - sx_sunlock(&pf_consistency_lock); - sx_xlock(&pf_consistency_lock); - PF_LOCK(); - locked = 1; - pf_purge_expired_src_nodes(1); - } + pf_purge_expired_src_nodes(0); nloops = 0; } @@ -1044,11 +1398,12 @@ pf_purge_thread(void *v) #ifdef __FreeBSD__ PF_UNLOCK(); if (locked) - sx_xunlock(&pf_consistency_lock); + sx_xunlock(&V_pf_consistency_lock); else - sx_sunlock(&pf_consistency_lock); + sx_sunlock(&V_pf_consistency_lock); #endif } + CURVNET_RESTORE(); } u_int32_t @@ -1064,7 +1419,7 @@ pf_state_expires(const struct pf_state *state) return (time_second); if (state->timeout == PFTM_UNTIL_PACKET) return (0); -#ifdef __FreeBSD__ +#ifdef __FreeBSD__ KASSERT(state->timeout != PFTM_UNLINKED, ("pf_state_expires: timeout == PFTM_UNLINKED")); KASSERT((state->timeout < PFTM_MAX), @@ -1075,15 +1430,25 @@ pf_state_expires(const struct pf_state *state) #endif timeout = state->rule.ptr->timeout[state->timeout]; if (!timeout) +#ifdef __FreeBSD__ + timeout = V_pf_default_rule.timeout[state->timeout]; +#else timeout = pf_default_rule.timeout[state->timeout]; +#endif start = state->rule.ptr->timeout[PFTM_ADAPTIVE_START]; if (start) { end = state->rule.ptr->timeout[PFTM_ADAPTIVE_END]; - states = state->rule.ptr->states; + states = state->rule.ptr->states_cur; } else { +#ifdef __FreeBSD__ + start = V_pf_default_rule.timeout[PFTM_ADAPTIVE_START]; + end = V_pf_default_rule.timeout[PFTM_ADAPTIVE_END]; + states = V_pf_status.states; +#else start = pf_default_rule.timeout[PFTM_ADAPTIVE_START]; end = pf_default_rule.timeout[PFTM_ADAPTIVE_END]; states = pf_status.states; +#endif } if (end && states > start && start < end) { if (states < end) @@ -1103,46 +1468,61 @@ void pf_purge_expired_src_nodes(int waslocked) #endif { - struct pf_src_node *cur, *next; - int locked = waslocked; - - for (cur = RB_MIN(pf_src_tree, &tree_src_tracking); cur; cur = next) { - next = RB_NEXT(pf_src_tree, &tree_src_tracking, cur); + struct pf_src_node *cur, *next; + int locked = waslocked; - if (cur->states <= 0 && cur->expire <= time_second) { - if (! locked) { #ifdef __FreeBSD__ - if (!sx_try_upgrade(&pf_consistency_lock)) - return (0); + for (cur = RB_MIN(pf_src_tree, &V_tree_src_tracking); cur; cur = next) { + next = RB_NEXT(pf_src_tree, &V_tree_src_tracking, cur); #else - rw_enter_write(&pf_consistency_lock); + for (cur = RB_MIN(pf_src_tree, &tree_src_tracking); cur; cur = next) { + next = RB_NEXT(pf_src_tree, &tree_src_tracking, cur); #endif - next = RB_NEXT(pf_src_tree, - &tree_src_tracking, cur); - locked = 1; - } - if (cur->rule.ptr != NULL) { - cur->rule.ptr->src_nodes--; - if (cur->rule.ptr->states <= 0 && - cur->rule.ptr->max_src_nodes <= 0) - pf_rm_rule(NULL, cur->rule.ptr); - } - RB_REMOVE(pf_src_tree, &tree_src_tracking, cur); - pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; - pf_status.src_nodes--; - pool_put(&pf_src_tree_pl, cur); - } - } - if (locked && !waslocked) + if (cur->states <= 0 && cur->expire <= time_second) { + if (! locked) { #ifdef __FreeBSD__ - sx_downgrade(&pf_consistency_lock); + if (!sx_try_upgrade(&V_pf_consistency_lock)) + return (0); #else - rw_exit_write(&pf_consistency_lock); + rw_enter_write(&pf_consistency_lock); +#endif + next = RB_NEXT(pf_src_tree, +#ifdef __FreeBSD__ + &V_tree_src_tracking, cur); +#else + &tree_src_tracking, cur); +#endif + locked = 1; + } + if (cur->rule.ptr != NULL) { + cur->rule.ptr->src_nodes--; + if (cur->rule.ptr->states_cur <= 0 && + cur->rule.ptr->max_src_nodes <= 0) + pf_rm_rule(NULL, cur->rule.ptr); + } +#ifdef __FreeBSD__ + RB_REMOVE(pf_src_tree, &V_tree_src_tracking, cur); + V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + V_pf_status.src_nodes--; + pool_put(&V_pf_src_tree_pl, cur); +#else + RB_REMOVE(pf_src_tree, &tree_src_tracking, cur); + pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + pf_status.src_nodes--; + pool_put(&pf_src_tree_pl, cur); #endif + } + } + if (locked && !waslocked) #ifdef __FreeBSD__ + { + sx_downgrade(&V_pf_consistency_lock); + } return (1); +#else + rw_exit_write(&pf_consistency_lock); #endif } @@ -1152,15 +1532,17 @@ pf_src_tree_remove_state(struct pf_state *s) u_int32_t timeout; if (s->src_node != NULL) { - if (s->proto == IPPROTO_TCP) { - if (s->src.tcp_est) - --s->src_node->conn; - } + if (s->src.tcp_est) + --s->src_node->conn; if (--s->src_node->states <= 0) { timeout = s->rule.ptr->timeout[PFTM_SRC_NODE]; if (!timeout) timeout = +#ifdef __FreeBSD__ + V_pf_default_rule.timeout[PFTM_SRC_NODE]; +#else pf_default_rule.timeout[PFTM_SRC_NODE]; +#endif s->src_node->expire = time_second + timeout; } } @@ -1169,7 +1551,11 @@ pf_src_tree_remove_state(struct pf_state *s) timeout = s->rule.ptr->timeout[PFTM_SRC_NODE]; if (!timeout) timeout = +#ifdef __FreeBSD__ + V_pf_default_rule.timeout[PFTM_SRC_NODE]; +#else pf_default_rule.timeout[PFTM_SRC_NODE]; +#endif s->nat_src_node->expire = time_second + timeout; } } @@ -1184,29 +1570,49 @@ pf_unlink_state(struct pf_state *cur) if (cur->local_flags & PFSTATE_EXPIRING) return; cur->local_flags |= PFSTATE_EXPIRING; +#else + splassert(IPL_SOFTNET); #endif + if (cur->src.state == PF_TCPS_PROXY_DST) { + /* XXX wire key the right one? */ #ifdef __FreeBSD__ - pf_send_tcp(NULL, cur->rule.ptr, cur->af, + pf_send_tcp(NULL, cur->rule.ptr, cur->key[PF_SK_WIRE]->af, #else - pf_send_tcp(cur->rule.ptr, cur->af, + pf_send_tcp(cur->rule.ptr, cur->key[PF_SK_WIRE]->af, #endif - &cur->ext.addr, &cur->lan.addr, - cur->ext.port, cur->lan.port, + &cur->key[PF_SK_WIRE]->addr[1], + &cur->key[PF_SK_WIRE]->addr[0], + cur->key[PF_SK_WIRE]->port[1], + cur->key[PF_SK_WIRE]->port[0], cur->src.seqhi, cur->src.seqlo + 1, TH_RST|TH_ACK, 0, 0, 0, 1, cur->tag, NULL, NULL); } - RB_REMOVE(pf_state_tree_ext_gwy, - &cur->u.s.kif->pfik_ext_gwy, cur); - RB_REMOVE(pf_state_tree_lan_ext, - &cur->u.s.kif->pfik_lan_ext, cur); +#ifdef __FreeBSD__ + RB_REMOVE(pf_state_tree_id, &V_tree_id, cur); +#else RB_REMOVE(pf_state_tree_id, &tree_id, cur); -#if NPFSYNC - if (cur->creatorid == pf_status.hostid) - pfsync_delete_state(cur); +#endif +#if NPFLOW > 0 + if (cur->state_flags & PFSTATE_PFLOW) +#ifdef __FreeBSD__ + if (export_pflow_ptr != NULL) + export_pflow_ptr(cur); +#else + export_pflow(cur); +#endif +#endif +#if NPFSYNC > 0 +#ifdef __FreeBSD__ + if (pfsync_delete_state_ptr != NULL) + pfsync_delete_state_ptr(cur); +#else + pfsync_delete_state(cur); +#endif #endif cur->timeout = PFTM_UNLINKED; pf_src_tree_remove_state(cur); + pf_detach_state(cur); } /* callers should be at splsoftnet and hold the @@ -1214,10 +1620,17 @@ pf_unlink_state(struct pf_state *cur) void pf_free_state(struct pf_state *cur) { -#if NPFSYNC - if (pfsyncif != NULL && - (pfsyncif->sc_bulk_send_next == cur || - pfsyncif->sc_bulk_terminator == cur)) +#ifndef __FreeBSD__ + splassert(IPL_SOFTNET); +#endif + +#if NPFSYNC > 0 +#ifdef __FreeBSD__ + if (pfsync_state_in_use_ptr != NULL) + pfsync_state_in_use_ptr(cur); +#else + if (pfsync_state_in_use(cur)) +#endif return; #endif #ifdef __FreeBSD__ @@ -1226,24 +1639,34 @@ pf_free_state(struct pf_state *cur) #else KASSERT(cur->timeout == PFTM_UNLINKED); #endif - if (--cur->rule.ptr->states <= 0 && + if (--cur->rule.ptr->states_cur <= 0 && cur->rule.ptr->src_nodes <= 0) pf_rm_rule(NULL, cur->rule.ptr); if (cur->nat_rule.ptr != NULL) - if (--cur->nat_rule.ptr->states <= 0 && + if (--cur->nat_rule.ptr->states_cur <= 0 && cur->nat_rule.ptr->src_nodes <= 0) pf_rm_rule(NULL, cur->nat_rule.ptr); if (cur->anchor.ptr != NULL) - if (--cur->anchor.ptr->states <= 0) + if (--cur->anchor.ptr->states_cur <= 0) pf_rm_rule(NULL, cur->anchor.ptr); pf_normalize_tcp_cleanup(cur); - pfi_kif_unref(cur->u.s.kif, PFI_KIF_REF_STATE); - TAILQ_REMOVE(&state_list, cur, u.s.entry_list); + pfi_kif_unref(cur->kif, PFI_KIF_REF_STATE); +#ifdef __FreeBSD__ + TAILQ_REMOVE(&V_state_list, cur, entry_list); +#else + TAILQ_REMOVE(&state_list, cur, entry_list); +#endif if (cur->tag) pf_tag_unref(cur->tag); +#ifdef __FreeBSD__ + pool_put(&V_pf_state_pl, cur); + V_pf_status.fcounters[FCNT_STATE_REMOVALS]++; + V_pf_status.states--; +#else pool_put(&pf_state_pl, cur); pf_status.fcounters[FCNT_STATE_REMOVALS]++; pf_status.states--; +#endif } #ifdef __FreeBSD__ @@ -1257,28 +1680,32 @@ pf_purge_expired_states(u_int32_t maxcheck) static struct pf_state *cur = NULL; struct pf_state *next; #ifdef __FreeBSD__ - int locked = waslocked; + int locked = waslocked; #else - int locked = 0; + int locked = 0; #endif while (maxcheck--) { /* wrap to start of list when we hit the end */ if (cur == NULL) { +#ifdef __FreeBSD__ + cur = TAILQ_FIRST(&V_state_list); +#else cur = TAILQ_FIRST(&state_list); +#endif if (cur == NULL) break; /* list empty */ } /* get next state, as cur may get deleted */ - next = TAILQ_NEXT(cur, u.s.entry_list); + next = TAILQ_NEXT(cur, entry_list); if (cur->timeout == PFTM_UNLINKED) { /* free unlinked state */ if (! locked) { #ifdef __FreeBSD__ - if (!sx_try_upgrade(&pf_consistency_lock)) - return (0); + if (!sx_try_upgrade(&V_pf_consistency_lock)) + return (0); #else rw_enter_write(&pf_consistency_lock); #endif @@ -1290,8 +1717,8 @@ pf_purge_expired_states(u_int32_t maxcheck) pf_unlink_state(cur); if (! locked) { #ifdef __FreeBSD__ - if (!sx_try_upgrade(&pf_consistency_lock)) - return (0); + if (!sx_try_upgrade(&V_pf_consistency_lock)) + return (0); #else rw_enter_write(&pf_consistency_lock); #endif @@ -1304,7 +1731,7 @@ pf_purge_expired_states(u_int32_t maxcheck) #ifdef __FreeBSD__ if (!waslocked && locked) - sx_downgrade(&pf_consistency_lock); + sx_downgrade(&V_pf_consistency_lock); return (1); #else @@ -1318,7 +1745,7 @@ pf_tbladdr_setup(struct pf_ruleset *rs, struct pf_addr_wrap *aw) { if (aw->type != PF_ADDR_TABLE) return (0); - if ((aw->p.tbl = pfr_attach_table(rs, aw->v.tblname)) == NULL) + if ((aw->p.tbl = pfr_attach_table(rs, aw->v.tblname, 1)) == NULL) return (1); return (0); } @@ -1365,34 +1792,33 @@ pf_print_host(struct pf_addr *addr, u_int16_t p, sa_family_t af) #ifdef INET6 case AF_INET6: { u_int16_t b; - u_int8_t i, curstart = 255, curend = 0, - maxstart = 0, maxend = 0; + u_int8_t i, curstart, curend, maxstart, maxend; + curstart = curend = maxstart = maxend = 255; for (i = 0; i < 8; i++) { if (!addr->addr16[i]) { if (curstart == 255) curstart = i; - else - curend = i; + curend = i; } else { - if (curstart) { - if ((curend - curstart) > - (maxend - maxstart)) { - maxstart = curstart; - maxend = curend; - curstart = 255; - } + if ((curend - curstart) > + (maxend - maxstart)) { + maxstart = curstart; + maxend = curend; } + curstart = curend = 255; } } + if ((curend - curstart) > + (maxend - maxstart)) { + maxstart = curstart; + maxend = curend; + } for (i = 0; i < 8; i++) { if (i >= maxstart && i <= maxend) { - if (maxend != 7) { - if (i == maxstart) - printf(":"); - } else { - if (i == maxend) - printf(":"); - } + if (i == 0) + printf(":"); + if (i == maxend) + printf(":"); } else { b = ntohs(addr->addr16[i]); printf("%x", b); @@ -1413,39 +1839,87 @@ pf_print_host(struct pf_addr *addr, u_int16_t p, sa_family_t af) void pf_print_state(struct pf_state *s) { - switch (s->proto) { + pf_print_state_parts(s, NULL, NULL); +} + +void +pf_print_state_parts(struct pf_state *s, + struct pf_state_key *skwp, struct pf_state_key *sksp) +{ + struct pf_state_key *skw, *sks; + u_int8_t proto, dir; + + /* Do our best to fill these, but they're skipped if NULL */ + skw = skwp ? skwp : (s ? s->key[PF_SK_WIRE] : NULL); + sks = sksp ? sksp : (s ? s->key[PF_SK_STACK] : NULL); + proto = skw ? skw->proto : (sks ? sks->proto : 0); + dir = s ? s->direction : 0; + + switch (proto) { + case IPPROTO_IPV4: + printf("IPv4"); + break; + case IPPROTO_IPV6: + printf("IPv6"); + break; case IPPROTO_TCP: - printf("TCP "); + printf("TCP"); break; case IPPROTO_UDP: - printf("UDP "); + printf("UDP"); break; case IPPROTO_ICMP: - printf("ICMP "); + printf("ICMP"); break; case IPPROTO_ICMPV6: - printf("ICMPV6 "); + printf("ICMPv6"); break; default: - printf("%u ", s->proto); + printf("%u", skw->proto); break; } - pf_print_host(&s->lan.addr, s->lan.port, s->af); - printf(" "); - pf_print_host(&s->gwy.addr, s->gwy.port, s->af); - printf(" "); - pf_print_host(&s->ext.addr, s->ext.port, s->af); - printf(" [lo=%u high=%u win=%u modulator=%u", s->src.seqlo, - s->src.seqhi, s->src.max_win, s->src.seqdiff); - if (s->src.wscale && s->dst.wscale) - printf(" wscale=%u", s->src.wscale & PF_WSCALE_MASK); - printf("]"); - printf(" [lo=%u high=%u win=%u modulator=%u", s->dst.seqlo, - s->dst.seqhi, s->dst.max_win, s->dst.seqdiff); - if (s->src.wscale && s->dst.wscale) - printf(" wscale=%u", s->dst.wscale & PF_WSCALE_MASK); - printf("]"); - printf(" %u:%u", s->src.state, s->dst.state); + switch (dir) { + case PF_IN: + printf(" in"); + break; + case PF_OUT: + printf(" out"); + break; + } + if (skw) { + printf(" wire: "); + pf_print_host(&skw->addr[0], skw->port[0], skw->af); + printf(" "); + pf_print_host(&skw->addr[1], skw->port[1], skw->af); + } + if (sks) { + printf(" stack: "); + if (sks != skw) { + pf_print_host(&sks->addr[0], sks->port[0], sks->af); + printf(" "); + pf_print_host(&sks->addr[1], sks->port[1], sks->af); + } else + printf("-"); + } + if (s) { + if (proto == IPPROTO_TCP) { + printf(" [lo=%u high=%u win=%u modulator=%u", + s->src.seqlo, s->src.seqhi, + s->src.max_win, s->src.seqdiff); + if (s->src.wscale && s->dst.wscale) + printf(" wscale=%u", + s->src.wscale & PF_WSCALE_MASK); + printf("]"); + printf(" [lo=%u high=%u win=%u modulator=%u", + s->dst.seqlo, s->dst.seqhi, + s->dst.max_win, s->dst.seqdiff); + if (s->src.wscale && s->dst.wscale) + printf(" wscale=%u", + s->dst.wscale & PF_WSCALE_MASK); + printf("]"); + } + printf(" %u:%u", s->src.state, s->dst.state); + } } void @@ -1528,6 +2002,7 @@ pf_addr_wrap_neq(struct pf_addr_wrap *aw1, struct pf_addr_wrap *aw2) return (1); switch (aw1->type) { case PF_ADDR_ADDRMASK: + case PF_ADDR_RANGE: if (PF_ANEQ(&aw1->v.a.addr, &aw2->v.a.addr, 0)) return (1); if (PF_ANEQ(&aw1->v.a.mask, &aw2->v.a.mask, 0)) @@ -1651,12 +2126,13 @@ pf_change_icmp(struct pf_addr *ia, u_int16_t *ip, struct pf_addr *oa, struct pf_addr oia, ooa; PF_ACPY(&oia, ia, af); - PF_ACPY(&ooa, oa, af); + if (oa) + PF_ACPY(&ooa, oa, af); /* Change inner protocol port, fix inner protocol checksum. */ if (ip != NULL) { u_int16_t oip = *ip; - u_int32_t opc = 0; /* make the compiler happy */ + u_int32_t opc; if (pc != NULL) opc = *pc; @@ -1700,31 +2176,33 @@ pf_change_icmp(struct pf_addr *ia, u_int16_t *ip, struct pf_addr *oa, break; #endif /* INET6 */ } - /* Change outer ip address, fix outer ip or icmpv6 checksum. */ - PF_ACPY(oa, na, af); - switch (af) { + /* Outer ip address, fix outer ip or icmpv6 checksum, if necessary. */ + if (oa) { + PF_ACPY(oa, na, af); + switch (af) { #ifdef INET - case AF_INET: - *hc = pf_cksum_fixup(pf_cksum_fixup(*hc, - ooa.addr16[0], oa->addr16[0], 0), - ooa.addr16[1], oa->addr16[1], 0); - break; + case AF_INET: + *hc = pf_cksum_fixup(pf_cksum_fixup(*hc, + ooa.addr16[0], oa->addr16[0], 0), + ooa.addr16[1], oa->addr16[1], 0); + break; #endif /* INET */ #ifdef INET6 - case AF_INET6: - *ic = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(*ic, - ooa.addr16[0], oa->addr16[0], u), - ooa.addr16[1], oa->addr16[1], u), - ooa.addr16[2], oa->addr16[2], u), - ooa.addr16[3], oa->addr16[3], u), - ooa.addr16[4], oa->addr16[4], u), - ooa.addr16[5], oa->addr16[5], u), - ooa.addr16[6], oa->addr16[6], u), - ooa.addr16[7], oa->addr16[7], u); - break; + case AF_INET6: + *ic = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(*ic, + ooa.addr16[0], oa->addr16[0], u), + ooa.addr16[1], oa->addr16[1], u), + ooa.addr16[2], oa->addr16[2], u), + ooa.addr16[3], oa->addr16[3], u), + ooa.addr16[4], oa->addr16[4], u), + ooa.addr16[5], oa->addr16[5], u), + ooa.addr16[6], oa->addr16[6], u), + ooa.addr16[7], oa->addr16[7], u); + break; #endif /* INET6 */ + } } } @@ -1746,7 +2224,7 @@ pf_modulate_sack(struct mbuf *m, int off, struct pf_pdesc *pd, int copyback = 0, i, olen; struct sackblk sack; -#define TCPOLEN_SACKLEN (TCPOLEN_SACK + 2) +#define TCPOLEN_SACKLEN (TCPOLEN_SACK + 2) if (hlen < TCPOLEN_SACKLEN || !pf_pull_hdr(m, off + sizeof(*th), opts, hlen, NULL, NULL, pd->af)) return 0; @@ -1815,9 +2293,9 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af, #endif /* INET6 */ struct tcphdr *th; char *opt; - struct pf_mtag *pf_mtag; - #ifdef __FreeBSD__ + struct pf_mtag *pf_mtag; + KASSERT( #ifdef INET af == AF_INET @@ -1839,7 +2317,7 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af, #ifdef INET6 h6 = NULL; #endif -#endif +#endif /* __FreeBSD__ */ /* maximum segment size tcp option */ tlen = sizeof(struct tcphdr); @@ -1865,42 +2343,46 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af, return; #ifdef __FreeBSD__ #ifdef MAC - if (replyto) - mac_netinet_firewall_reply(replyto, m); - else - mac_netinet_firewall_send(m); -#else - (void)replyto; -#endif + mac_netinet_firewall_send(m); #endif if ((pf_mtag = pf_get_mtag(m)) == NULL) { m_freem(m); return; } +#endif if (tag) #ifdef __FreeBSD__ m->m_flags |= M_SKIP_FIREWALL; + pf_mtag->tag = rtag; #else - pf_mtag->flags |= PF_TAG_GENERATED; + m->m_pkthdr.pf.flags |= PF_TAG_GENERATED; + m->m_pkthdr.pf.tag = rtag; #endif - pf_mtag->tag = rtag; - if (r != NULL && r->rtableid >= 0) #ifdef __FreeBSD__ { M_SETFIB(m, r->rtableid); -#endif pf_mtag->rtableid = r->rtableid; +#else + m->m_pkthdr.pf.rtableid = r->rtableid; +#endif #ifdef __FreeBSD__ } #endif + #ifdef ALTQ if (r != NULL && r->qid) { +#ifdef __FreeBSD__ pf_mtag->qid = r->qid; + /* add hints for ecn */ - pf_mtag->af = af; pf_mtag->hdr = mtod(m, struct ip *); +#else + m->m_pkthdr.pf.qid = r->qid; + /* add hints for ecn */ + m->m_pkthdr.pf.hdr = mtod(m, struct ip *); +#endif } #endif /* ALTQ */ m->m_data += max_linkhdr; @@ -1966,18 +2448,19 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af, #ifdef __FreeBSD__ h->ip_off = V_path_mtu_discovery ? IP_DF : 0; h->ip_len = len; + h->ip_ttl = ttl ? ttl : V_ip_defttl; #else - h->ip_off = htons(ip_mtudisc ? IP_DF : 0); h->ip_len = htons(len); + h->ip_off = htons(ip_mtudisc ? IP_DF : 0); + h->ip_ttl = ttl ? ttl : ip_defttl; #endif - h->ip_ttl = ttl ? ttl : V_ip_defttl; h->ip_sum = 0; if (eh == NULL) { #ifdef __FreeBSD__ - PF_UNLOCK(); - ip_output(m, (void *)NULL, (void *)NULL, 0, - (void *)NULL, (void *)NULL); - PF_LOCK(); + PF_UNLOCK(); + ip_output(m, (void *)NULL, (void *)NULL, 0, + (void *)NULL, (void *)NULL); + PF_LOCK(); #else /* ! __FreeBSD__ */ ip_output(m, (void *)NULL, (void *)NULL, 0, (void *)NULL, (void *)NULL); @@ -2025,23 +2508,23 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af, ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); PF_LOCK(); #else - ip6_output(m, NULL, NULL, 0, NULL, NULL); + ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); #endif break; #endif /* INET6 */ } } -void +static void pf_send_icmp(struct mbuf *m, u_int8_t type, u_int8_t code, sa_family_t af, struct pf_rule *r) { - struct pf_mtag *pf_mtag; struct mbuf *m0; #ifdef __FreeBSD__ #ifdef INET struct ip *ip; #endif + struct pf_mtag *pf_mtag; #endif #ifdef __FreeBSD__ @@ -2049,33 +2532,42 @@ pf_send_icmp(struct mbuf *m, u_int8_t type, u_int8_t code, sa_family_t af, if (m0 == NULL) return; #else - m0 = m_copy(m, 0, M_COPYALL); + if ((m0 = m_copy(m, 0, M_COPYALL)) == NULL) + return; #endif + +#ifdef __FreeBSD__ if ((pf_mtag = pf_get_mtag(m0)) == NULL) return; -#ifdef __FreeBSD__ /* XXX: revisit */ m0->m_flags |= M_SKIP_FIREWALL; #else - pf_mtag->flags |= PF_TAG_GENERATED; + m0->m_pkthdr.pf.flags |= PF_TAG_GENERATED; #endif if (r->rtableid >= 0) #ifdef __FreeBSD__ { M_SETFIB(m0, r->rtableid); -#endif pf_mtag->rtableid = r->rtableid; +#else + m0->m_pkthdr.pf.rtableid = r->rtableid; +#endif #ifdef __FreeBSD__ } #endif #ifdef ALTQ if (r->qid) { +#ifdef __FreeBSD__ pf_mtag->qid = r->qid; /* add hints for ecn */ - pf_mtag->af = af; pf_mtag->hdr = mtod(m0, struct ip *); +#else + m0->m_pkthdr.pf.qid = r->qid; + /* add hints for ecn */ + m0->m_pkthdr.pf.hdr = mtod(m0, struct ip *); +#endif } #endif /* ALTQ */ @@ -2155,6 +2647,44 @@ pf_match_addr(u_int8_t n, struct pf_addr *a, struct pf_addr *m, } } +/* + * Return 1 if b <= a <= e, otherwise return 0. + */ +int +pf_match_addr_range(struct pf_addr *b, struct pf_addr *e, + struct pf_addr *a, sa_family_t af) +{ + switch (af) { +#ifdef INET + case AF_INET: + if ((a->addr32[0] < b->addr32[0]) || + (a->addr32[0] > e->addr32[0])) + return (0); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: { + int i; + + /* check a >= b */ + for (i = 0; i < 4; ++i) + if (a->addr32[i] > b->addr32[i]) + break; + else if (a->addr32[i] < b->addr32[i]) + return (0); + /* check a <= e */ + for (i = 0; i < 4; ++i) + if (a->addr32[i] < e->addr32[i]) + break; + else if (a->addr32[i] > e->addr32[i]) + return (0); + break; + } +#endif /* INET6 */ + } + return (1); +} + int pf_match(u_int8_t op, u_int32_t a1, u_int32_t a2, u_int32_t p) { @@ -2206,88 +2736,80 @@ pf_match_gid(u_int8_t op, gid_t a1, gid_t a2, gid_t g) return (pf_match(op, a1, a2, g)); } -#ifndef __FreeBSD__ -struct pf_mtag * -pf_find_mtag(struct mbuf *m) -{ - struct m_tag *mtag; - - if ((mtag = m_tag_find(m, PACKET_TAG_PF, NULL)) == NULL) - return (NULL); - - return ((struct pf_mtag *)(mtag + 1)); -} - -struct pf_mtag * -pf_get_mtag(struct mbuf *m) -{ - struct m_tag *mtag; - - if ((mtag = m_tag_find(m, PACKET_TAG_PF, NULL)) == NULL) { - mtag = m_tag_get(PACKET_TAG_PF, sizeof(struct pf_mtag), - M_NOWAIT); - if (mtag == NULL) - return (NULL); - bzero(mtag + 1, sizeof(struct pf_mtag)); - m_tag_prepend(m, mtag); - } - - return ((struct pf_mtag *)(mtag + 1)); -} -#endif - int -pf_match_tag(struct mbuf *m, struct pf_rule *r, struct pf_mtag *pf_mtag, - int *tag) +#ifdef __FreeBSD__ +pf_match_tag(struct mbuf *m, struct pf_rule *r, int *tag, + struct pf_mtag *pf_mtag) +#else +pf_match_tag(struct mbuf *m, struct pf_rule *r, int *tag) +#endif { if (*tag == -1) +#ifdef __FreeBSD__ *tag = pf_mtag->tag; +#else + *tag = m->m_pkthdr.pf.tag; +#endif return ((!r->match_tag_not && r->match_tag == *tag) || (r->match_tag_not && r->match_tag != *tag)); } int -pf_tag_packet(struct mbuf *m, struct pf_mtag *pf_mtag, int tag, int rtableid) +#ifdef __FreeBSD__ +pf_tag_packet(struct mbuf *m, int tag, int rtableid, + struct pf_mtag *pf_mtag) +#else +pf_tag_packet(struct mbuf *m, int tag, int rtableid) +#endif { if (tag <= 0 && rtableid < 0) return (0); - if (pf_mtag == NULL) - if ((pf_mtag = pf_get_mtag(m)) == NULL) - return (1); if (tag > 0) +#ifdef __FreeBSD__ pf_mtag->tag = tag; +#else + m->m_pkthdr.pf.tag = tag; +#endif if (rtableid >= 0) #ifdef __FreeBSD__ { M_SETFIB(m, rtableid); -#endif - pf_mtag->rtableid = rtableid; -#ifdef __FreeBSD__ } +#else + m->m_pkthdr.pf.rtableid = rtableid; #endif return (0); } -static void +void pf_step_into_anchor(int *depth, struct pf_ruleset **rs, int n, - struct pf_rule **r, struct pf_rule **a, int *match) + struct pf_rule **r, struct pf_rule **a, int *match) { struct pf_anchor_stackframe *f; (*r)->anchor->match = 0; if (match) *match = 0; +#ifdef __FreeBSD__ + if (*depth >= sizeof(V_pf_anchor_stack) / + sizeof(V_pf_anchor_stack[0])) { +#else if (*depth >= sizeof(pf_anchor_stack) / sizeof(pf_anchor_stack[0])) { +#endif printf("pf_step_into_anchor: stack overflow\n"); *r = TAILQ_NEXT(*r, entries); return; } else if (*depth == 0 && a != NULL) *a = *r; +#ifdef __FreeBSD__ + f = V_pf_anchor_stack + (*depth)++; +#else f = pf_anchor_stack + (*depth)++; +#endif f->rs = *rs; f->r = *r; if ((*r)->anchor_wildcard) { @@ -2316,7 +2838,11 @@ pf_step_out_of_anchor(int *depth, struct pf_ruleset **rs, int n, do { if (*depth <= 0) break; +#ifdef __FreeBSD__ + f = V_pf_anchor_stack + *depth - 1; +#else f = pf_anchor_stack + *depth - 1; +#endif if (f->parent != NULL && f->child != NULL) { if (f->child->match || (match != NULL && *match)) { @@ -2337,7 +2863,7 @@ pf_step_out_of_anchor(int *depth, struct pf_ruleset **rs, int n, if (*depth == 0 && a != NULL) *a = NULL; *rs = f->rs; - if (f->r->anchor->match || (match != NULL && *match)) + if (f->r->anchor->match || (match != NULL && *match)) quick = f->r->quick; *r = TAILQ_NEXT(f->r, entries); } while (*r == NULL); @@ -2402,567 +2928,6 @@ pf_addr_inc(struct pf_addr *addr, sa_family_t af) } #endif /* INET6 */ -#define mix(a,b,c) \ - do { \ - a -= b; a -= c; a ^= (c >> 13); \ - b -= c; b -= a; b ^= (a << 8); \ - c -= a; c -= b; c ^= (b >> 13); \ - a -= b; a -= c; a ^= (c >> 12); \ - b -= c; b -= a; b ^= (a << 16); \ - c -= a; c -= b; c ^= (b >> 5); \ - a -= b; a -= c; a ^= (c >> 3); \ - b -= c; b -= a; b ^= (a << 10); \ - c -= a; c -= b; c ^= (b >> 15); \ - } while (0) - -/* - * hash function based on bridge_hash in if_bridge.c - */ -void -pf_hash(struct pf_addr *inaddr, struct pf_addr *hash, - struct pf_poolhashkey *key, sa_family_t af) -{ - u_int32_t a = 0x9e3779b9, b = 0x9e3779b9, c = key->key32[0]; - - switch (af) { -#ifdef INET - case AF_INET: - a += inaddr->addr32[0]; - b += key->key32[1]; - mix(a, b, c); - hash->addr32[0] = c + key->key32[2]; - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - a += inaddr->addr32[0]; - b += inaddr->addr32[2]; - mix(a, b, c); - hash->addr32[0] = c; - a += inaddr->addr32[1]; - b += inaddr->addr32[3]; - c += key->key32[1]; - mix(a, b, c); - hash->addr32[1] = c; - a += inaddr->addr32[2]; - b += inaddr->addr32[1]; - c += key->key32[2]; - mix(a, b, c); - hash->addr32[2] = c; - a += inaddr->addr32[3]; - b += inaddr->addr32[0]; - c += key->key32[3]; - mix(a, b, c); - hash->addr32[3] = c; - break; -#endif /* INET6 */ - } -} - -int -pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr, - struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_src_node **sn) -{ - unsigned char hash[16]; - struct pf_pool *rpool = &r->rpool; - struct pf_addr *raddr = &rpool->cur->addr.v.a.addr; - struct pf_addr *rmask = &rpool->cur->addr.v.a.mask; - struct pf_pooladdr *acur = rpool->cur; - struct pf_src_node k; - - if (*sn == NULL && r->rpool.opts & PF_POOL_STICKYADDR && - (r->rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) { - k.af = af; - PF_ACPY(&k.addr, saddr, af); - if (r->rule_flag & PFRULE_RULESRCTRACK || - r->rpool.opts & PF_POOL_STICKYADDR) - k.rule.ptr = r; - else - k.rule.ptr = NULL; - pf_status.scounters[SCNT_SRC_NODE_SEARCH]++; - *sn = RB_FIND(pf_src_tree, &tree_src_tracking, &k); - if (*sn != NULL && !PF_AZERO(&(*sn)->raddr, af)) { - PF_ACPY(naddr, &(*sn)->raddr, af); - if (pf_status.debug >= PF_DEBUG_MISC) { - printf("pf_map_addr: src tracking maps "); - pf_print_host(&k.addr, 0, af); - printf(" to "); - pf_print_host(naddr, 0, af); - printf("\n"); - } - return (0); - } - } - - if (rpool->cur->addr.type == PF_ADDR_NOROUTE) - return (1); - if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { - switch (af) { -#ifdef INET - case AF_INET: - if (rpool->cur->addr.p.dyn->pfid_acnt4 < 1 && - (rpool->opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN) - return (1); - raddr = &rpool->cur->addr.p.dyn->pfid_addr4; - rmask = &rpool->cur->addr.p.dyn->pfid_mask4; - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (rpool->cur->addr.p.dyn->pfid_acnt6 < 1 && - (rpool->opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN) - return (1); - raddr = &rpool->cur->addr.p.dyn->pfid_addr6; - rmask = &rpool->cur->addr.p.dyn->pfid_mask6; - break; -#endif /* INET6 */ - } - } else if (rpool->cur->addr.type == PF_ADDR_TABLE) { - if ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) - return (1); /* unsupported */ - } else { - raddr = &rpool->cur->addr.v.a.addr; - rmask = &rpool->cur->addr.v.a.mask; - } - - switch (rpool->opts & PF_POOL_TYPEMASK) { - case PF_POOL_NONE: - PF_ACPY(naddr, raddr, af); - break; - case PF_POOL_BITMASK: - PF_POOLMASK(naddr, raddr, rmask, saddr, af); - break; - case PF_POOL_RANDOM: - if (init_addr != NULL && PF_AZERO(init_addr, af)) { - switch (af) { -#ifdef INET - case AF_INET: - rpool->counter.addr32[0] = htonl(arc4random()); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (rmask->addr32[3] != 0xffffffff) - rpool->counter.addr32[3] = - htonl(arc4random()); - else - break; - if (rmask->addr32[2] != 0xffffffff) - rpool->counter.addr32[2] = - htonl(arc4random()); - else - break; - if (rmask->addr32[1] != 0xffffffff) - rpool->counter.addr32[1] = - htonl(arc4random()); - else - break; - if (rmask->addr32[0] != 0xffffffff) - rpool->counter.addr32[0] = - htonl(arc4random()); - break; -#endif /* INET6 */ - } - PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af); - PF_ACPY(init_addr, naddr, af); - - } else { - PF_AINC(&rpool->counter, af); - PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af); - } - break; - case PF_POOL_SRCHASH: - pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, af); - PF_POOLMASK(naddr, raddr, rmask, (struct pf_addr *)&hash, af); - break; - case PF_POOL_ROUNDROBIN: - if (rpool->cur->addr.type == PF_ADDR_TABLE) { - if (!pfr_pool_get(rpool->cur->addr.p.tbl, - &rpool->tblidx, &rpool->counter, - &raddr, &rmask, af)) - goto get_addr; - } else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { - if (!pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt, - &rpool->tblidx, &rpool->counter, - &raddr, &rmask, af)) - goto get_addr; - } else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af)) - goto get_addr; - - try_next: - if ((rpool->cur = TAILQ_NEXT(rpool->cur, entries)) == NULL) - rpool->cur = TAILQ_FIRST(&rpool->list); - if (rpool->cur->addr.type == PF_ADDR_TABLE) { - rpool->tblidx = -1; - if (pfr_pool_get(rpool->cur->addr.p.tbl, - &rpool->tblidx, &rpool->counter, - &raddr, &rmask, af)) { - /* table contains no address of type 'af' */ - if (rpool->cur != acur) - goto try_next; - return (1); - } - } else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { - rpool->tblidx = -1; - if (pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt, - &rpool->tblidx, &rpool->counter, - &raddr, &rmask, af)) { - /* table contains no address of type 'af' */ - if (rpool->cur != acur) - goto try_next; - return (1); - } - } else { - raddr = &rpool->cur->addr.v.a.addr; - rmask = &rpool->cur->addr.v.a.mask; - PF_ACPY(&rpool->counter, raddr, af); - } - - get_addr: - PF_ACPY(naddr, &rpool->counter, af); - if (init_addr != NULL && PF_AZERO(init_addr, af)) - PF_ACPY(init_addr, naddr, af); - PF_AINC(&rpool->counter, af); - break; - } - if (*sn != NULL) - PF_ACPY(&(*sn)->raddr, naddr, af); - - if (pf_status.debug >= PF_DEBUG_MISC && - (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) { - printf("pf_map_addr: selected address "); - pf_print_host(naddr, 0, af); - printf("\n"); - } - - return (0); -} - -int -pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, - struct pf_addr *saddr, struct pf_addr *daddr, u_int16_t dport, - struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, u_int16_t high, - struct pf_src_node **sn) -{ - struct pf_state_cmp key; - struct pf_addr init_addr; - u_int16_t cut; - - bzero(&init_addr, sizeof(init_addr)); - if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn)) - return (1); - - if (proto == IPPROTO_ICMP) { - low = 1; - high = 65535; - } - - do { - key.af = af; - key.proto = proto; - PF_ACPY(&key.ext.addr, daddr, key.af); - PF_ACPY(&key.gwy.addr, naddr, key.af); - key.ext.port = dport; - - /* - * port search; start random, step; - * similar 2 portloop in in_pcbbind - */ - if (!(proto == IPPROTO_TCP || proto == IPPROTO_UDP || - proto == IPPROTO_ICMP)) { - key.gwy.port = dport; - if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL) - return (0); - } else if (low == 0 && high == 0) { - key.gwy.port = *nport; - if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL) - return (0); - } else if (low == high) { - key.gwy.port = htons(low); - if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL) { - *nport = htons(low); - return (0); - } - } else { - u_int16_t tmp; - - if (low > high) { - tmp = low; - low = high; - high = tmp; - } - /* low < high */ - cut = htonl(arc4random()) % (1 + high - low) + low; - /* low <= cut <= high */ - for (tmp = cut; tmp <= high; ++(tmp)) { - key.gwy.port = htons(tmp); - if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == - NULL) { - *nport = htons(tmp); - return (0); - } - } - for (tmp = cut - 1; tmp >= low; --(tmp)) { - key.gwy.port = htons(tmp); - if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == - NULL) { - *nport = htons(tmp); - return (0); - } - } - } - - switch (r->rpool.opts & PF_POOL_TYPEMASK) { - case PF_POOL_RANDOM: - case PF_POOL_ROUNDROBIN: - if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn)) - return (1); - break; - case PF_POOL_NONE: - case PF_POOL_SRCHASH: - case PF_POOL_BITMASK: - default: - return (1); - } - } while (! PF_AEQ(&init_addr, naddr, af) ); - - return (1); /* none available */ -} - -struct pf_rule * -pf_match_translation(struct pf_pdesc *pd, struct mbuf *m, int off, - int direction, struct pfi_kif *kif, struct pf_addr *saddr, u_int16_t sport, - struct pf_addr *daddr, u_int16_t dport, int rs_num) -{ - struct pf_rule *r, *rm = NULL; - struct pf_ruleset *ruleset = NULL; - int tag = -1; - int rtableid = -1; - int asd = 0; - - r = TAILQ_FIRST(pf_main_ruleset.rules[rs_num].active.ptr); - while (r && rm == NULL) { - struct pf_rule_addr *src = NULL, *dst = NULL; - struct pf_addr_wrap *xdst = NULL; - - if (r->action == PF_BINAT && direction == PF_IN) { - src = &r->dst; - if (r->rpool.cur != NULL) - xdst = &r->rpool.cur->addr; - } else { - src = &r->src; - dst = &r->dst; - } - - r->evaluations++; - if (pfi_kif_match(r->kif, kif) == r->ifnot) - r = r->skip[PF_SKIP_IFP].ptr; - else if (r->direction && r->direction != direction) - r = r->skip[PF_SKIP_DIR].ptr; - else if (r->af && r->af != pd->af) - r = r->skip[PF_SKIP_AF].ptr; - else if (r->proto && r->proto != pd->proto) - r = r->skip[PF_SKIP_PROTO].ptr; - else if (PF_MISMATCHAW(&src->addr, saddr, pd->af, - src->neg, kif)) - r = r->skip[src == &r->src ? PF_SKIP_SRC_ADDR : - PF_SKIP_DST_ADDR].ptr; - else if (src->port_op && !pf_match_port(src->port_op, - src->port[0], src->port[1], sport)) - r = r->skip[src == &r->src ? PF_SKIP_SRC_PORT : - PF_SKIP_DST_PORT].ptr; - else if (dst != NULL && - PF_MISMATCHAW(&dst->addr, daddr, pd->af, dst->neg, NULL)) - r = r->skip[PF_SKIP_DST_ADDR].ptr; - else if (xdst != NULL && PF_MISMATCHAW(xdst, daddr, pd->af, - 0, NULL)) - r = TAILQ_NEXT(r, entries); - else if (dst != NULL && dst->port_op && - !pf_match_port(dst->port_op, dst->port[0], - dst->port[1], dport)) - r = r->skip[PF_SKIP_DST_PORT].ptr; - else if (r->match_tag && !pf_match_tag(m, r, pd->pf_mtag, &tag)) - r = TAILQ_NEXT(r, entries); - else if (r->os_fingerprint != PF_OSFP_ANY && (pd->proto != - IPPROTO_TCP || !pf_osfp_match(pf_osfp_fingerprint(pd, m, - off, pd->hdr.tcp), r->os_fingerprint))) - r = TAILQ_NEXT(r, entries); - else { - if (r->tag) - tag = r->tag; - if (r->rtableid >= 0) - rtableid = r->rtableid; - if (r->anchor == NULL) { - rm = r; - } else - pf_step_into_anchor(&asd, &ruleset, rs_num, - &r, NULL, NULL); - } - if (r == NULL) - pf_step_out_of_anchor(&asd, &ruleset, rs_num, &r, - NULL, NULL); - } - if (pf_tag_packet(m, pd->pf_mtag, tag, rtableid)) - return (NULL); - if (rm != NULL && (rm->action == PF_NONAT || - rm->action == PF_NORDR || rm->action == PF_NOBINAT)) - return (NULL); - return (rm); -} - -struct pf_rule * -pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, - struct pfi_kif *kif, struct pf_src_node **sn, - struct pf_addr *saddr, u_int16_t sport, - struct pf_addr *daddr, u_int16_t dport, - struct pf_addr *naddr, u_int16_t *nport) -{ - struct pf_rule *r = NULL; - - if (direction == PF_OUT) { - r = pf_match_translation(pd, m, off, direction, kif, saddr, - sport, daddr, dport, PF_RULESET_BINAT); - if (r == NULL) - r = pf_match_translation(pd, m, off, direction, kif, - saddr, sport, daddr, dport, PF_RULESET_NAT); - } else { - r = pf_match_translation(pd, m, off, direction, kif, saddr, - sport, daddr, dport, PF_RULESET_RDR); - if (r == NULL) - r = pf_match_translation(pd, m, off, direction, kif, - saddr, sport, daddr, dport, PF_RULESET_BINAT); - } - - if (r != NULL) { - switch (r->action) { - case PF_NONAT: - case PF_NOBINAT: - case PF_NORDR: - return (NULL); - case PF_NAT: - if (pf_get_sport(pd->af, pd->proto, r, saddr, - daddr, dport, naddr, nport, r->rpool.proxy_port[0], - r->rpool.proxy_port[1], sn)) { - DPFPRINTF(PF_DEBUG_MISC, - ("pf: NAT proxy port allocation " - "(%u-%u) failed\n", - r->rpool.proxy_port[0], - r->rpool.proxy_port[1])); - return (NULL); - } - break; - case PF_BINAT: - switch (direction) { - case PF_OUT: - if (r->rpool.cur->addr.type == PF_ADDR_DYNIFTL){ - switch (pd->af) { -#ifdef INET - case AF_INET: - if (r->rpool.cur->addr.p.dyn-> - pfid_acnt4 < 1) - return (NULL); - PF_POOLMASK(naddr, - &r->rpool.cur->addr.p.dyn-> - pfid_addr4, - &r->rpool.cur->addr.p.dyn-> - pfid_mask4, - saddr, AF_INET); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (r->rpool.cur->addr.p.dyn-> - pfid_acnt6 < 1) - return (NULL); - PF_POOLMASK(naddr, - &r->rpool.cur->addr.p.dyn-> - pfid_addr6, - &r->rpool.cur->addr.p.dyn-> - pfid_mask6, - saddr, AF_INET6); - break; -#endif /* INET6 */ - } - } else - PF_POOLMASK(naddr, - &r->rpool.cur->addr.v.a.addr, - &r->rpool.cur->addr.v.a.mask, - saddr, pd->af); - break; - case PF_IN: - if (r->src.addr.type == PF_ADDR_DYNIFTL) { - switch (pd->af) { -#ifdef INET - case AF_INET: - if (r->src.addr.p.dyn-> - pfid_acnt4 < 1) - return (NULL); - PF_POOLMASK(naddr, - &r->src.addr.p.dyn-> - pfid_addr4, - &r->src.addr.p.dyn-> - pfid_mask4, - daddr, AF_INET); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - if (r->src.addr.p.dyn-> - pfid_acnt6 < 1) - return (NULL); - PF_POOLMASK(naddr, - &r->src.addr.p.dyn-> - pfid_addr6, - &r->src.addr.p.dyn-> - pfid_mask6, - daddr, AF_INET6); - break; -#endif /* INET6 */ - } - } else - PF_POOLMASK(naddr, - &r->src.addr.v.a.addr, - &r->src.addr.v.a.mask, daddr, - pd->af); - break; - } - break; - case PF_RDR: { - if (pf_map_addr(pd->af, r, saddr, naddr, NULL, sn)) - return (NULL); - if ((r->rpool.opts & PF_POOL_TYPEMASK) == - PF_POOL_BITMASK) - PF_POOLMASK(naddr, naddr, - &r->rpool.cur->addr.v.a.mask, daddr, - pd->af); - - if (r->rpool.proxy_port[1]) { - u_int32_t tmp_nport; - - tmp_nport = ((ntohs(dport) - - ntohs(r->dst.port[0])) % - (r->rpool.proxy_port[1] - - r->rpool.proxy_port[0] + 1)) + - r->rpool.proxy_port[0]; - - /* wrap around if necessary */ - if (tmp_nport > 65535) - tmp_nport -= 65535; - *nport = htons((u_int16_t)tmp_nport); - } else if (r->rpool.proxy_port[0]) - *nport = htons(r->rpool.proxy_port[0]); - break; - } - default: - return (NULL); - } - } - - return (r); -} - int #ifdef __FreeBSD__ pf_socket_lookup(int direction, struct pf_pdesc *pd, struct inpcb *inp_arg) @@ -2983,7 +2948,8 @@ pf_socket_lookup(int direction, struct pf_pdesc *pd) return (-1); pd->lookup.uid = UID_MAX; pd->lookup.gid = GID_MAX; - pd->lookup.pid = NO_PID; /* XXX: revisit */ + pd->lookup.pid = NO_PID; + #ifdef __FreeBSD__ if (inp_arg != NULL) { INP_LOCK_ASSERT(inp_arg); @@ -2992,6 +2958,7 @@ pf_socket_lookup(int direction, struct pf_pdesc *pd) return (1); } #endif + switch (pd->proto) { case IPPROTO_TCP: if (pd->hdr.tcp == NULL) @@ -3050,7 +3017,8 @@ pf_socket_lookup(int direction, struct pf_pdesc *pd) #else inp = in_pcbhashlookup(tb, saddr->v4, sport, daddr->v4, dport); if (inp == NULL) { - inp = in_pcblookup_listen(tb, daddr->v4, dport, 0); + inp = in_pcblookup_listen(tb, daddr->v4, dport, 0, + NULL); if (inp == NULL) return (-1); } @@ -3077,7 +3045,8 @@ pf_socket_lookup(int direction, struct pf_pdesc *pd) inp = in6_pcbhashlookup(tb, &saddr->v6, sport, &daddr->v6, dport); if (inp == NULL) { - inp = in6_pcblookup_listen(tb, &daddr->v6, dport, 0); + inp = in6_pcblookup_listen(tb, &daddr->v6, dport, 0, + NULL); if (inp == NULL) return (-1); } @@ -3147,7 +3116,11 @@ pf_get_mss(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) int hlen; u_int8_t hdr[60]; u_int8_t *opt, optlen; +#ifdef __FreeBSD__ u_int16_t mss = V_tcp_mssdflt; +#else + u_int16_t mss = tcp_mssdflt; +#endif hlen = th_off << 2; /* hlen <= sizeof(hdr) */ if (hlen <= sizeof(struct tcphdr)) @@ -3191,8 +3164,13 @@ pf_calc_mss(struct pf_addr *addr, sa_family_t af, u_int16_t offer) struct route_in6 ro6; #endif /* INET6 */ struct rtentry *rt = NULL; - int hlen = 0; /* make the compiler happy */ +#ifdef __FreeBSD__ + int hlen = 0; u_int16_t mss = V_tcp_mssdflt; +#else + int hlen; + u_int16_t mss = tcp_mssdflt; +#endif switch (af) { #ifdef INET @@ -3240,7 +3218,11 @@ pf_calc_mss(struct pf_addr *addr, sa_family_t af, u_int16_t offer) if (rt && rt->rt_ifp) { mss = rt->rt_ifp->if_mtu - hlen - sizeof(struct tcphdr); +#ifdef __FreeBSD__ mss = max(V_tcp_mssdflt, mss); +#else + mss = max(tcp_mssdflt, mss); +#endif RTFREE(rt); } mss = min(mss, offer); @@ -3252,55 +3234,113 @@ void pf_set_rt_ifp(struct pf_state *s, struct pf_addr *saddr) { struct pf_rule *r = s->rule.ptr; + struct pf_src_node *sn = NULL; s->rt_kif = NULL; if (!r->rt || r->rt == PF_FASTROUTE) return; - switch (s->af) { + switch (s->key[PF_SK_WIRE]->af) { #ifdef INET case AF_INET: - pf_map_addr(AF_INET, r, saddr, &s->rt_addr, NULL, - &s->nat_src_node); + pf_map_addr(AF_INET, r, saddr, &s->rt_addr, NULL, &sn); s->rt_kif = r->rpool.cur->kif; break; #endif /* INET */ #ifdef INET6 case AF_INET6: - pf_map_addr(AF_INET6, r, saddr, &s->rt_addr, NULL, - &s->nat_src_node); + pf_map_addr(AF_INET6, r, saddr, &s->rt_addr, NULL, &sn); s->rt_kif = r->rpool.cur->kif; break; #endif /* INET6 */ } } +u_int32_t +pf_tcp_iss(struct pf_pdesc *pd) +{ + MD5_CTX ctx; + u_int32_t digest[4]; + +#ifdef __FreeBSD__ + if (V_pf_tcp_secret_init == 0) { + read_random(&V_pf_tcp_secret, sizeof(V_pf_tcp_secret)); + MD5Init(&V_pf_tcp_secret_ctx); + MD5Update(&V_pf_tcp_secret_ctx, V_pf_tcp_secret, + sizeof(V_pf_tcp_secret)); + V_pf_tcp_secret_init = 1; + } + + ctx = V_pf_tcp_secret_ctx; +#else + if (pf_tcp_secret_init == 0) { + arc4random_buf(pf_tcp_secret, sizeof(pf_tcp_secret)); + MD5Init(&pf_tcp_secret_ctx); + MD5Update(&pf_tcp_secret_ctx, pf_tcp_secret, + sizeof(pf_tcp_secret)); + pf_tcp_secret_init = 1; + } + + ctx = pf_tcp_secret_ctx; +#endif + + MD5Update(&ctx, (char *)&pd->hdr.tcp->th_sport, sizeof(u_short)); + MD5Update(&ctx, (char *)&pd->hdr.tcp->th_dport, sizeof(u_short)); + if (pd->af == AF_INET6) { + MD5Update(&ctx, (char *)&pd->src->v6, sizeof(struct in6_addr)); + MD5Update(&ctx, (char *)&pd->dst->v6, sizeof(struct in6_addr)); + } else { + MD5Update(&ctx, (char *)&pd->src->v4, sizeof(struct in_addr)); + MD5Update(&ctx, (char *)&pd->dst->v4, sizeof(struct in_addr)); + } + MD5Final((u_char *)digest, &ctx); +#ifdef __FreeBSD__ + V_pf_tcp_iss_off += 4096; +#define ISN_RANDOM_INCREMENT (4096 - 1) + return (digest[0] + (arc4random() & ISN_RANDOM_INCREMENT) + + V_pf_tcp_iss_off); +#undef ISN_RANDOM_INCREMENT +#else + pf_tcp_iss_off += 4096; + return (digest[0] + tcp_iss + pf_tcp_iss_off); +#endif +} + int -pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, +pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, struct pfi_kif *kif, struct mbuf *m, int off, void *h, -#ifdef __FreeBSD__ struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm, +#ifdef __FreeBSD__ struct ifqueue *ifq, struct inpcb *inp) #else - struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm, struct ifqueue *ifq) #endif { struct pf_rule *nr = NULL; struct pf_addr *saddr = pd->src, *daddr = pd->dst; - struct tcphdr *th = pd->hdr.tcp; - u_int16_t bport, nport = 0; sa_family_t af = pd->af; struct pf_rule *r, *a = NULL; struct pf_ruleset *ruleset = NULL; struct pf_src_node *nsn = NULL; + struct tcphdr *th = pd->hdr.tcp; + struct pf_state_key *skw = NULL, *sks = NULL; + struct pf_state_key *sk = NULL, *nk = NULL; u_short reason; - int rewrite = 0; + int rewrite = 0, hdrlen = 0; int tag = -1, rtableid = -1; - u_int16_t mss = V_tcp_mssdflt; int asd = 0; int match = 0; + int state_icmp = 0; +#ifdef __FreeBSD__ + u_int16_t sport = 0, dport = 0; + u_int16_t bproto_sum = 0, bip_sum = 0; +#else + u_int16_t sport, dport; + u_int16_t bproto_sum = 0, bip_sum; +#endif + u_int8_t icmptype = 0, icmpcode = 0; - if (pf_check_congestion(ifq)) { + + if (direction == PF_IN && pf_check_congestion(ifq)) { REASON_SET(&reason, PFRES_CONGEST); return (PF_DROP); } @@ -3308,44 +3348,193 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, #ifdef __FreeBSD__ if (inp != NULL) pd->lookup.done = pf_socket_lookup(direction, pd, inp); - else if (debug_pfugidhack) { + else if (V_debug_pfugidhack) { PF_UNLOCK(); DPFPRINTF(PF_DEBUG_MISC, ("pf: unlocked lookup\n")); - pd->lookup.done = pf_socket_lookup(direction, pd, inp); + pd->lookup.done = pf_socket_lookup(direction, pd, inp); PF_LOCK(); } #endif + switch (pd->proto) { + case IPPROTO_TCP: + sport = th->th_sport; + dport = th->th_dport; + hdrlen = sizeof(*th); + break; + case IPPROTO_UDP: + sport = pd->hdr.udp->uh_sport; + dport = pd->hdr.udp->uh_dport; + hdrlen = sizeof(*pd->hdr.udp); + break; +#ifdef INET + case IPPROTO_ICMP: + if (pd->af != AF_INET) + break; + sport = dport = pd->hdr.icmp->icmp_id; + hdrlen = sizeof(*pd->hdr.icmp); + icmptype = pd->hdr.icmp->icmp_type; + icmpcode = pd->hdr.icmp->icmp_code; + + if (icmptype == ICMP_UNREACH || + icmptype == ICMP_SOURCEQUENCH || + icmptype == ICMP_REDIRECT || + icmptype == ICMP_TIMXCEED || + icmptype == ICMP_PARAMPROB) + state_icmp++; + break; +#endif /* INET */ +#ifdef INET6 + case IPPROTO_ICMPV6: + if (af != AF_INET6) + break; + sport = dport = pd->hdr.icmp6->icmp6_id; + hdrlen = sizeof(*pd->hdr.icmp6); + icmptype = pd->hdr.icmp6->icmp6_type; + icmpcode = pd->hdr.icmp6->icmp6_code; + + if (icmptype == ICMP6_DST_UNREACH || + icmptype == ICMP6_PACKET_TOO_BIG || + icmptype == ICMP6_TIME_EXCEEDED || + icmptype == ICMP6_PARAM_PROB) + state_icmp++; + break; +#endif /* INET6 */ + default: + sport = dport = hdrlen = 0; + break; + } + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); - if (direction == PF_OUT) { - bport = nport = th->th_sport; - /* check outgoing packet for BINAT/NAT */ - if ((nr = pf_get_translation(pd, m, off, PF_OUT, kif, &nsn, - saddr, th->th_sport, daddr, th->th_dport, - &pd->naddr, &nport)) != NULL) { - PF_ACPY(&pd->baddr, saddr, af); - pf_change_ap(saddr, &th->th_sport, pd->ip_sum, - &th->th_sum, &pd->naddr, nport, 0, af); - rewrite++; - if (nr->natpass) - r = NULL; - pd->nat_rule = nr; + /* check packet for BINAT/NAT/RDR */ + if ((nr = pf_get_translation(pd, m, off, direction, kif, &nsn, + &skw, &sks, &sk, &nk, saddr, daddr, sport, dport)) != NULL) { + if (nk == NULL || sk == NULL) { + REASON_SET(&reason, PFRES_MEMORY); + goto cleanup; } - } else { - bport = nport = th->th_dport; - /* check incoming packet for BINAT/RDR */ - if ((nr = pf_get_translation(pd, m, off, PF_IN, kif, &nsn, - saddr, th->th_sport, daddr, th->th_dport, - &pd->naddr, &nport)) != NULL) { - PF_ACPY(&pd->baddr, daddr, af); - pf_change_ap(daddr, &th->th_dport, pd->ip_sum, - &th->th_sum, &pd->naddr, nport, 0, af); + + if (pd->ip_sum) + bip_sum = *pd->ip_sum; + + switch (pd->proto) { + case IPPROTO_TCP: + bproto_sum = th->th_sum; + pd->proto_sum = &th->th_sum; + + if (PF_ANEQ(saddr, &nk->addr[pd->sidx], af) || + nk->port[pd->sidx] != sport) { + pf_change_ap(saddr, &th->th_sport, pd->ip_sum, + &th->th_sum, &nk->addr[pd->sidx], + nk->port[pd->sidx], 0, af); + pd->sport = &th->th_sport; + sport = th->th_sport; + } + + if (PF_ANEQ(daddr, &nk->addr[pd->didx], af) || + nk->port[pd->didx] != dport) { + pf_change_ap(daddr, &th->th_dport, pd->ip_sum, + &th->th_sum, &nk->addr[pd->didx], + nk->port[pd->didx], 0, af); + dport = th->th_dport; + pd->dport = &th->th_dport; + } + rewrite++; + break; + case IPPROTO_UDP: + bproto_sum = pd->hdr.udp->uh_sum; + pd->proto_sum = &pd->hdr.udp->uh_sum; + + if (PF_ANEQ(saddr, &nk->addr[pd->sidx], af) || + nk->port[pd->sidx] != sport) { + pf_change_ap(saddr, &pd->hdr.udp->uh_sport, + pd->ip_sum, &pd->hdr.udp->uh_sum, + &nk->addr[pd->sidx], + nk->port[pd->sidx], 1, af); + sport = pd->hdr.udp->uh_sport; + pd->sport = &pd->hdr.udp->uh_sport; + } + + if (PF_ANEQ(daddr, &nk->addr[pd->didx], af) || + nk->port[pd->didx] != dport) { + pf_change_ap(daddr, &pd->hdr.udp->uh_dport, + pd->ip_sum, &pd->hdr.udp->uh_sum, + &nk->addr[pd->didx], + nk->port[pd->didx], 1, af); + dport = pd->hdr.udp->uh_dport; + pd->dport = &pd->hdr.udp->uh_dport; + } rewrite++; - if (nr->natpass) - r = NULL; - pd->nat_rule = nr; + break; +#ifdef INET + case IPPROTO_ICMP: + nk->port[0] = nk->port[1]; + if (PF_ANEQ(saddr, &nk->addr[pd->sidx], AF_INET)) + pf_change_a(&saddr->v4.s_addr, pd->ip_sum, + nk->addr[pd->sidx].v4.s_addr, 0); + + if (PF_ANEQ(daddr, &nk->addr[pd->didx], AF_INET)) + pf_change_a(&daddr->v4.s_addr, pd->ip_sum, + nk->addr[pd->didx].v4.s_addr, 0); + + if (nk->port[1] != pd->hdr.icmp->icmp_id) { + pd->hdr.icmp->icmp_cksum = pf_cksum_fixup( + pd->hdr.icmp->icmp_cksum, sport, + nk->port[1], 0); + pd->hdr.icmp->icmp_id = nk->port[1]; + pd->sport = &pd->hdr.icmp->icmp_id; + } + m_copyback(m, off, ICMP_MINLEN, (caddr_t)pd->hdr.icmp); + break; +#endif /* INET */ +#ifdef INET6 + case IPPROTO_ICMPV6: + nk->port[0] = nk->port[1]; + if (PF_ANEQ(saddr, &nk->addr[pd->sidx], AF_INET6)) + pf_change_a6(saddr, &pd->hdr.icmp6->icmp6_cksum, + &nk->addr[pd->sidx], 0); + + if (PF_ANEQ(daddr, &nk->addr[pd->didx], AF_INET6)) + pf_change_a6(daddr, &pd->hdr.icmp6->icmp6_cksum, + &nk->addr[pd->didx], 0); + rewrite++; + break; +#endif /* INET */ + default: + switch (af) { +#ifdef INET + case AF_INET: + if (PF_ANEQ(saddr, + &nk->addr[pd->sidx], AF_INET)) + pf_change_a(&saddr->v4.s_addr, + pd->ip_sum, + nk->addr[pd->sidx].v4.s_addr, 0); + + if (PF_ANEQ(daddr, + &nk->addr[pd->didx], AF_INET)) + pf_change_a(&daddr->v4.s_addr, + pd->ip_sum, + nk->addr[pd->didx].v4.s_addr, 0); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (PF_ANEQ(saddr, + &nk->addr[pd->sidx], AF_INET6)) + PF_ACPY(saddr, &nk->addr[pd->sidx], af); + + if (PF_ANEQ(daddr, + &nk->addr[pd->didx], AF_INET6)) + PF_ACPY(saddr, &nk->addr[pd->didx], af); + break; +#endif /* INET */ + } + break; } + if (nr->natpass) + r = NULL; + pd->nat_rule = nr; } while (r != NULL) { @@ -3356,26 +3545,36 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, r = r->skip[PF_SKIP_DIR].ptr; else if (r->af && r->af != af) r = r->skip[PF_SKIP_AF].ptr; - else if (r->proto && r->proto != IPPROTO_TCP) + else if (r->proto && r->proto != pd->proto) r = r->skip[PF_SKIP_PROTO].ptr; else if (PF_MISMATCHAW(&r->src.addr, saddr, af, r->src.neg, kif)) r = r->skip[PF_SKIP_SRC_ADDR].ptr; + /* tcp/udp only. port_op always 0 in other cases */ else if (r->src.port_op && !pf_match_port(r->src.port_op, - r->src.port[0], r->src.port[1], th->th_sport)) + r->src.port[0], r->src.port[1], sport)) r = r->skip[PF_SKIP_SRC_PORT].ptr; else if (PF_MISMATCHAW(&r->dst.addr, daddr, af, r->dst.neg, NULL)) r = r->skip[PF_SKIP_DST_ADDR].ptr; + /* tcp/udp only. port_op always 0 in other cases */ else if (r->dst.port_op && !pf_match_port(r->dst.port_op, - r->dst.port[0], r->dst.port[1], th->th_dport)) + r->dst.port[0], r->dst.port[1], dport)) r = r->skip[PF_SKIP_DST_PORT].ptr; + /* icmp only. type always 0 in other cases */ + else if (r->type && r->type != icmptype + 1) + r = TAILQ_NEXT(r, entries); + /* icmp only. type always 0 in other cases */ + else if (r->code && r->code != icmpcode + 1) + r = TAILQ_NEXT(r, entries); else if (r->tos && !(r->tos == pd->tos)) r = TAILQ_NEXT(r, entries); else if (r->rule_flag & PFRULE_FRAGMENT) r = TAILQ_NEXT(r, entries); - else if ((r->flagset & th->th_flags) != r->flags) + else if (pd->proto == IPPROTO_TCP && + (r->flagset & th->th_flags) != r->flags) r = TAILQ_NEXT(r, entries); + /* tcp/udp only. uid.op always 0 in other cases */ else if (r->uid.op && (pd->lookup.done || (pd->lookup.done = #ifdef __FreeBSD__ pf_socket_lookup(direction, pd, inp), 1)) && @@ -3385,6 +3584,7 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, !pf_match_uid(r->uid.op, r->uid.uid[0], r->uid.uid[1], pd->lookup.uid)) r = TAILQ_NEXT(r, entries); + /* tcp/udp only. gid.op always 0 in other cases */ else if (r->gid.op && (pd->lookup.done || (pd->lookup.done = #ifdef __FreeBSD__ pf_socket_lookup(direction, pd, inp), 1)) && @@ -3394,12 +3594,23 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1], pd->lookup.gid)) r = TAILQ_NEXT(r, entries); - else if (r->prob && r->prob <= arc4random()) + else if (r->prob && +#ifdef __FreeBSD__ + r->prob <= arc4random()) +#else + r->prob <= arc4random_uniform(UINT_MAX - 1) + 1) +#endif r = TAILQ_NEXT(r, entries); - else if (r->match_tag && !pf_match_tag(m, r, pd->pf_mtag, &tag)) +#ifdef __FreeBSD__ + else if (r->match_tag && !pf_match_tag(m, r, &tag, pd->pf_mtag)) +#else + else if (r->match_tag && !pf_match_tag(m, r, &tag)) +#endif r = TAILQ_NEXT(r, entries); - else if (r->os_fingerprint != PF_OSFP_ANY && !pf_osfp_match( - pf_osfp_fingerprint(pd, m, off, th), r->os_fingerprint)) + else if (r->os_fingerprint != PF_OSFP_ANY && + (pd->proto != IPPROTO_TCP || !pf_osfp_match( + pf_osfp_fingerprint(pd, m, off, th), + r->os_fingerprint))) r = TAILQ_NEXT(r, entries); else { if (r->tag) @@ -3428,13 +3639,9 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, REASON_SET(&reason, PFRES_MATCH); - if (r->log || (nr != NULL && nr->natpass && nr->log)) { + if (r->log || (nr != NULL && nr->log)) { if (rewrite) -#ifdef __FreeBSD__ - m_copyback(m, off, sizeof(*th), (caddr_t)th); -#else - m_copyback(m, off, sizeof(*th), th); -#endif + m_copyback(m, off, hdrlen, pd->hdr.any); PFLOG_PACKET(kif, h, m, af, direction, reason, r->log ? r : nr, a, ruleset, pd); } @@ -3445,161 +3652,233 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, (r->rule_flag & PFRULE_RETURN))) { /* undo NAT changes, if they have taken place */ if (nr != NULL) { - if (direction == PF_OUT) { - pf_change_ap(saddr, &th->th_sport, pd->ip_sum, - &th->th_sum, &pd->baddr, bport, 0, af); - rewrite++; - } else { - pf_change_ap(daddr, &th->th_dport, pd->ip_sum, - &th->th_sum, &pd->baddr, bport, 0, af); - rewrite++; - } - } - if (((r->rule_flag & PFRULE_RETURNRST) || + PF_ACPY(saddr, &sk->addr[pd->sidx], af); + PF_ACPY(daddr, &sk->addr[pd->didx], af); + if (pd->sport) + *pd->sport = sk->port[pd->sidx]; + if (pd->dport) + *pd->dport = sk->port[pd->didx]; + if (pd->proto_sum) + *pd->proto_sum = bproto_sum; + if (pd->ip_sum) + *pd->ip_sum = bip_sum; + m_copyback(m, off, hdrlen, pd->hdr.any); + } + if (pd->proto == IPPROTO_TCP && + ((r->rule_flag & PFRULE_RETURNRST) || (r->rule_flag & PFRULE_RETURN)) && !(th->th_flags & TH_RST)) { - u_int32_t ack = ntohl(th->th_seq) + pd->p_len; + u_int32_t ack = ntohl(th->th_seq) + pd->p_len; + int len = 0; +#ifdef INET + struct ip *h4; +#endif +#ifdef INET6 + struct ip6_hdr *h6; +#endif + + switch (af) { +#ifdef INET + case AF_INET: + h4 = mtod(m, struct ip *); + len = ntohs(h4->ip_len) - off; + break; +#endif +#ifdef INET6 + case AF_INET6: + h6 = mtod(m, struct ip6_hdr *); + len = ntohs(h6->ip6_plen) - (off - sizeof(*h6)); + break; +#endif + } - if (th->th_flags & TH_SYN) - ack++; - if (th->th_flags & TH_FIN) - ack++; + if (pf_check_proto_cksum(m, off, len, IPPROTO_TCP, af)) + REASON_SET(&reason, PFRES_PROTCKSUM); + else { + if (th->th_flags & TH_SYN) + ack++; + if (th->th_flags & TH_FIN) + ack++; #ifdef __FreeBSD__ - pf_send_tcp(m, r, af, pd->dst, + pf_send_tcp(m, r, af, pd->dst, #else - pf_send_tcp(r, af, pd->dst, + pf_send_tcp(r, af, pd->dst, #endif - pd->src, th->th_dport, th->th_sport, - ntohl(th->th_ack), ack, TH_RST|TH_ACK, 0, 0, - r->return_ttl, 1, 0, pd->eh, kif->pfik_ifp); - } else if ((af == AF_INET) && r->return_icmp) + pd->src, th->th_dport, th->th_sport, + ntohl(th->th_ack), ack, TH_RST|TH_ACK, 0, 0, + r->return_ttl, 1, 0, pd->eh, kif->pfik_ifp); + } + } else if (pd->proto != IPPROTO_ICMP && af == AF_INET && + r->return_icmp) pf_send_icmp(m, r->return_icmp >> 8, r->return_icmp & 255, af, r); - else if ((af == AF_INET6) && r->return_icmp6) + else if (pd->proto != IPPROTO_ICMPV6 && af == AF_INET6 && + r->return_icmp6) pf_send_icmp(m, r->return_icmp6 >> 8, r->return_icmp6 & 255, af, r); } if (r->action == PF_DROP) - return (PF_DROP); + goto cleanup; - if (pf_tag_packet(m, pd->pf_mtag, tag, rtableid)) { +#ifdef __FreeBSD__ + if (pf_tag_packet(m, tag, rtableid, pd->pf_mtag)) { +#else + if (pf_tag_packet(m, tag, rtableid)) { +#endif REASON_SET(&reason, PFRES_MEMORY); - return (PF_DROP); + goto cleanup; } - if (r->keep_state || nr != NULL || - (pd->flags & PFDESC_TCP_NORM)) { - /* create new state */ - u_int16_t len; - struct pf_state *s = NULL; - struct pf_src_node *sn = NULL; + if (!state_icmp && (r->keep_state || nr != NULL || + (pd->flags & PFDESC_TCP_NORM))) { + int action; + action = pf_create_state(r, nr, a, pd, nsn, skw, sks, nk, sk, m, + off, sport, dport, &rewrite, kif, sm, tag, bproto_sum, + bip_sum, hdrlen); + if (action != PF_PASS) + return (action); + } else { +#ifdef __FreeBSD__ + if (sk != NULL) + pool_put(&V_pf_state_key_pl, sk); + if (nk != NULL) + pool_put(&V_pf_state_key_pl, nk); +#else + if (sk != NULL) + pool_put(&pf_state_key_pl, sk); + if (nk != NULL) + pool_put(&pf_state_key_pl, nk); +#endif + } - len = pd->tot_len - off - (th->th_off << 2); + /* copy back packet headers if we performed NAT operations */ + if (rewrite) + m_copyback(m, off, hdrlen, pd->hdr.any); + +#if NPFSYNC > 0 + if (*sm != NULL && !ISSET((*sm)->state_flags, PFSTATE_NOSYNC) && +#ifdef __FreeBSD__ + direction == PF_OUT && pfsync_up_ptr != NULL && pfsync_up_ptr()) { +#else + direction == PF_OUT && pfsync_up()) { +#endif + /* + * We want the state created, but we dont + * want to send this in case a partner + * firewall has to know about it to allow + * replies through it. + */ +#ifdef __FreeBSD__ + if (pfsync_defer_ptr != NULL) + pfsync_defer_ptr(*sm, m); +#else + if (pfsync_defer(*sm, m)) +#endif + return (PF_DEFER); + } +#endif + + return (PF_PASS); - /* check maximums */ - if (r->max_states && (r->states >= r->max_states)) { - pf_status.lcounters[LCNT_STATES]++; - REASON_SET(&reason, PFRES_MAXSTATES); - goto cleanup; - } - /* src node for filter rule */ - if ((r->rule_flag & PFRULE_SRCTRACK || - r->rpool.opts & PF_POOL_STICKYADDR) && - pf_insert_src_node(&sn, r, saddr, af) != 0) { - REASON_SET(&reason, PFRES_SRCLIMIT); - goto cleanup; - } - /* src node for translation rule */ - if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) && - ((direction == PF_OUT && - pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) || - (pf_insert_src_node(&nsn, nr, saddr, af) != 0))) { - REASON_SET(&reason, PFRES_SRCLIMIT); - goto cleanup; - } - s = pool_get(&pf_state_pl, PR_NOWAIT); - if (s == NULL) { - REASON_SET(&reason, PFRES_MEMORY); cleanup: - if (sn != NULL && sn->states == 0 && sn->expire == 0) { - RB_REMOVE(pf_src_tree, &tree_src_tracking, sn); - pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; - pf_status.src_nodes--; - pool_put(&pf_src_tree_pl, sn); - } - if (nsn != sn && nsn != NULL && nsn->states == 0 && - nsn->expire == 0) { - RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn); - pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; - pf_status.src_nodes--; - pool_put(&pf_src_tree_pl, nsn); - } - return (PF_DROP); - } - bzero(s, sizeof(*s)); - s->rule.ptr = r; - s->nat_rule.ptr = nr; - s->anchor.ptr = a; - STATE_INC_COUNTERS(s); - if (r->allow_opts) - s->state_flags |= PFSTATE_ALLOWOPTS; - if (r->rule_flag & PFRULE_STATESLOPPY) - s->state_flags |= PFSTATE_SLOPPY; - s->log = r->log & PF_LOG_ALL; - if (nr != NULL) - s->log |= nr->log & PF_LOG_ALL; - s->proto = IPPROTO_TCP; - s->direction = direction; - s->af = af; - if (direction == PF_OUT) { - PF_ACPY(&s->gwy.addr, saddr, af); - s->gwy.port = th->th_sport; /* sport */ - PF_ACPY(&s->ext.addr, daddr, af); - s->ext.port = th->th_dport; - if (nr != NULL) { - PF_ACPY(&s->lan.addr, &pd->baddr, af); - s->lan.port = bport; - } else { - PF_ACPY(&s->lan.addr, &s->gwy.addr, af); - s->lan.port = s->gwy.port; - } - } else { - PF_ACPY(&s->lan.addr, daddr, af); - s->lan.port = th->th_dport; - PF_ACPY(&s->ext.addr, saddr, af); - s->ext.port = th->th_sport; - if (nr != NULL) { - PF_ACPY(&s->gwy.addr, &pd->baddr, af); - s->gwy.port = bport; - } else { - PF_ACPY(&s->gwy.addr, &s->lan.addr, af); - s->gwy.port = s->lan.port; - } - } +#ifdef __FreeBSD__ + if (sk != NULL) + pool_put(&V_pf_state_key_pl, sk); + if (nk != NULL) + pool_put(&V_pf_state_key_pl, nk); +#else + if (sk != NULL) + pool_put(&pf_state_key_pl, sk); + if (nk != NULL) + pool_put(&pf_state_key_pl, nk); +#endif + return (PF_DROP); +} +static __inline int +pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a, + struct pf_pdesc *pd, struct pf_src_node *nsn, struct pf_state_key *skw, + struct pf_state_key *sks, struct pf_state_key *nk, struct pf_state_key *sk, + struct mbuf *m, int off, u_int16_t sport, u_int16_t dport, int *rewrite, + struct pfi_kif *kif, struct pf_state **sm, int tag, u_int16_t bproto_sum, + u_int16_t bip_sum, int hdrlen) +{ + struct pf_state *s = NULL; + struct pf_src_node *sn = NULL; + struct tcphdr *th = pd->hdr.tcp; +#ifdef __FreeBSD__ + u_int16_t mss = V_tcp_mssdflt; +#else + u_int16_t mss = tcp_mssdflt; +#endif + u_short reason; + + /* check maximums */ + if (r->max_states && (r->states_cur >= r->max_states)) { +#ifdef __FreeBSD__ + V_pf_status.lcounters[LCNT_STATES]++; +#else + pf_status.lcounters[LCNT_STATES]++; +#endif + REASON_SET(&reason, PFRES_MAXSTATES); + return (PF_DROP); + } + /* src node for filter rule */ + if ((r->rule_flag & PFRULE_SRCTRACK || + r->rpool.opts & PF_POOL_STICKYADDR) && + pf_insert_src_node(&sn, r, pd->src, pd->af) != 0) { + REASON_SET(&reason, PFRES_SRCLIMIT); + goto csfailed; + } + /* src node for translation rule */ + if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) && + pf_insert_src_node(&nsn, nr, &sk->addr[pd->sidx], pd->af)) { + REASON_SET(&reason, PFRES_SRCLIMIT); + goto csfailed; + } +#ifdef __FreeBSD__ + s = pool_get(&V_pf_state_pl, PR_NOWAIT | PR_ZERO); +#else + s = pool_get(&pf_state_pl, PR_NOWAIT | PR_ZERO); +#endif + if (s == NULL) { + REASON_SET(&reason, PFRES_MEMORY); + goto csfailed; + } + s->rule.ptr = r; + s->nat_rule.ptr = nr; + s->anchor.ptr = a; + STATE_INC_COUNTERS(s); + if (r->allow_opts) + s->state_flags |= PFSTATE_ALLOWOPTS; + if (r->rule_flag & PFRULE_STATESLOPPY) + s->state_flags |= PFSTATE_SLOPPY; + if (r->rule_flag & PFRULE_PFLOW) + s->state_flags |= PFSTATE_PFLOW; + s->log = r->log & PF_LOG_ALL; + s->sync_state = PFSYNC_S_NONE; + if (nr != NULL) + s->log |= nr->log & PF_LOG_ALL; + switch (pd->proto) { + case IPPROTO_TCP: s->src.seqlo = ntohl(th->th_seq); - s->src.seqhi = s->src.seqlo + len + 1; + s->src.seqhi = s->src.seqlo + pd->p_len + 1; if ((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN && r->keep_state == PF_STATE_MODULATE) { /* Generate sequence number modulator */ -#ifdef __FreeBSD__ - while ((s->src.seqdiff = - pf_new_isn(s) - s->src.seqlo) == 0) - ; -#else - while ((s->src.seqdiff = - tcp_rndiss_next() - s->src.seqlo) == 0) - ; -#endif + if ((s->src.seqdiff = pf_tcp_iss(pd) - s->src.seqlo) == + 0) + s->src.seqdiff = 1; pf_change_a(&th->th_seq, &th->th_sum, htonl(s->src.seqlo + s->src.seqdiff), 0); - rewrite = 1; + *rewrite = 1; } else s->src.seqdiff = 0; if (th->th_flags & TH_SYN) { s->src.seqhi++; - s->src.wscale = pf_get_wscale(m, off, th->th_off, af); + s->src.wscale = pf_get_wscale(m, off, + th->th_off, pd->af); } s->src.max_win = MAX(ntohs(th->th_win), 1); if (s->src.wscale & PF_WSCALE_MASK) { @@ -3615,994 +3894,173 @@ cleanup: s->dst.max_win = 1; s->src.state = TCPS_SYN_SENT; s->dst.state = TCPS_CLOSED; - s->creation = time_second; - s->expire = time_second; s->timeout = PFTM_TCP_FIRST_PACKET; - pf_set_rt_ifp(s, saddr); - if (sn != NULL) { - s->src_node = sn; - s->src_node->states++; - } - if (nsn != NULL) { - PF_ACPY(&nsn->raddr, &pd->naddr, af); - s->nat_src_node = nsn; - s->nat_src_node->states++; - } + break; + case IPPROTO_UDP: + s->src.state = PFUDPS_SINGLE; + s->dst.state = PFUDPS_NO_TRAFFIC; + s->timeout = PFTM_UDP_FIRST_PACKET; + break; + case IPPROTO_ICMP: +#ifdef INET6 + case IPPROTO_ICMPV6: +#endif + s->timeout = PFTM_ICMP_FIRST_PACKET; + break; + default: + s->src.state = PFOTHERS_SINGLE; + s->dst.state = PFOTHERS_NO_TRAFFIC; + s->timeout = PFTM_OTHER_FIRST_PACKET; + } + + s->creation = time_second; + s->expire = time_second; + + if (sn != NULL) { + s->src_node = sn; + s->src_node->states++; + } + if (nsn != NULL) { + /* XXX We only modify one side for now. */ + PF_ACPY(&nsn->raddr, &nk->addr[1], pd->af); + s->nat_src_node = nsn; + s->nat_src_node->states++; + } + if (pd->proto == IPPROTO_TCP) { if ((pd->flags & PFDESC_TCP_NORM) && pf_normalize_tcp_init(m, off, pd, th, &s->src, &s->dst)) { REASON_SET(&reason, PFRES_MEMORY); pf_src_tree_remove_state(s); STATE_DEC_COUNTERS(s); +#ifdef __FreeBSD__ + pool_put(&V_pf_state_pl, s); +#else pool_put(&pf_state_pl, s); +#endif return (PF_DROP); } if ((pd->flags & PFDESC_TCP_NORM) && s->src.scrub && pf_normalize_tcp_stateful(m, off, pd, &reason, th, s, - &s->src, &s->dst, &rewrite)) { + &s->src, &s->dst, rewrite)) { /* This really shouldn't happen!!! */ DPFPRINTF(PF_DEBUG_URGENT, ("pf_normalize_tcp_stateful failed on first pkt")); pf_normalize_tcp_cleanup(s); pf_src_tree_remove_state(s); STATE_DEC_COUNTERS(s); - pool_put(&pf_state_pl, s); - return (PF_DROP); - } - if (pf_insert_state(BOUND_IFACE(r, kif), s)) { - pf_normalize_tcp_cleanup(s); - REASON_SET(&reason, PFRES_STATEINS); - pf_src_tree_remove_state(s); - STATE_DEC_COUNTERS(s); - pool_put(&pf_state_pl, s); - return (PF_DROP); - } else - *sm = s; - if (tag > 0) { - pf_tag_ref(tag); - s->tag = tag; - } - if ((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN && - r->keep_state == PF_STATE_SYNPROXY) { - s->src.state = PF_TCPS_PROXY_SRC; - if (nr != NULL) { - if (direction == PF_OUT) { - pf_change_ap(saddr, &th->th_sport, - pd->ip_sum, &th->th_sum, &pd->baddr, - bport, 0, af); - } else { - pf_change_ap(daddr, &th->th_dport, - pd->ip_sum, &th->th_sum, &pd->baddr, - bport, 0, af); - } - } - s->src.seqhi = htonl(arc4random()); - /* Find mss option */ - mss = pf_get_mss(m, off, th->th_off, af); - mss = pf_calc_mss(saddr, af, mss); - mss = pf_calc_mss(daddr, af, mss); - s->src.mss = mss; #ifdef __FreeBSD__ - pf_send_tcp(NULL, r, af, daddr, saddr, th->th_dport, + pool_put(&V_pf_state_pl, s); #else - pf_send_tcp(r, af, daddr, saddr, th->th_dport, + pool_put(&pf_state_pl, s); #endif - th->th_sport, s->src.seqhi, ntohl(th->th_seq) + 1, - TH_SYN|TH_ACK, 0, s->src.mss, 0, 1, 0, NULL, NULL); - REASON_SET(&reason, PFRES_SYNPROXY); - return (PF_SYNPROXY_DROP); + return (PF_DROP); } } + s->direction = pd->dir; - /* copy back packet headers if we performed NAT operations */ - if (rewrite) - m_copyback(m, off, sizeof(*th), (caddr_t)th); - - return (PF_PASS); -} + if (sk == NULL && pf_state_key_setup(pd, nr, &skw, &sks, &sk, &nk, + pd->src, pd->dst, sport, dport)) + goto csfailed; -int -pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, - struct pfi_kif *kif, struct mbuf *m, int off, void *h, + if (pf_state_insert(BOUND_IFACE(r, kif), skw, sks, s)) { + if (pd->proto == IPPROTO_TCP) + pf_normalize_tcp_cleanup(s); + REASON_SET(&reason, PFRES_STATEINS); + pf_src_tree_remove_state(s); + STATE_DEC_COUNTERS(s); #ifdef __FreeBSD__ - struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm, - struct ifqueue *ifq, struct inpcb *inp) + pool_put(&V_pf_state_pl, s); #else - struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm, - struct ifqueue *ifq) + pool_put(&pf_state_pl, s); #endif -{ - struct pf_rule *nr = NULL; - struct pf_addr *saddr = pd->src, *daddr = pd->dst; - struct udphdr *uh = pd->hdr.udp; - u_int16_t bport, nport = 0; - sa_family_t af = pd->af; - struct pf_rule *r, *a = NULL; - struct pf_ruleset *ruleset = NULL; - struct pf_src_node *nsn = NULL; - u_short reason; - int rewrite = 0; - int tag = -1, rtableid = -1; - int asd = 0; - int match = 0; - - if (pf_check_congestion(ifq)) { - REASON_SET(&reason, PFRES_CONGEST); return (PF_DROP); - } + } else + *sm = s; -#ifdef __FreeBSD__ - if (inp != NULL) - pd->lookup.done = pf_socket_lookup(direction, pd, inp); - else if (debug_pfugidhack) { - PF_UNLOCK(); - DPFPRINTF(PF_DEBUG_MISC, ("pf: unlocked lookup\n")); - pd->lookup.done = pf_socket_lookup(direction, pd, inp); - PF_LOCK(); + pf_set_rt_ifp(s, pd->src); /* needs s->state_key set */ + if (tag > 0) { + pf_tag_ref(tag); + s->tag = tag; } + if (pd->proto == IPPROTO_TCP && (th->th_flags & (TH_SYN|TH_ACK)) == + TH_SYN && r->keep_state == PF_STATE_SYNPROXY) { + s->src.state = PF_TCPS_PROXY_SRC; + /* undo NAT changes, if they have taken place */ + if (nr != NULL) { + struct pf_state_key *skt = s->key[PF_SK_WIRE]; + if (pd->dir == PF_OUT) + skt = s->key[PF_SK_STACK]; + PF_ACPY(pd->src, &skt->addr[pd->sidx], pd->af); + PF_ACPY(pd->dst, &skt->addr[pd->didx], pd->af); + if (pd->sport) + *pd->sport = skt->port[pd->sidx]; + if (pd->dport) + *pd->dport = skt->port[pd->didx]; + if (pd->proto_sum) + *pd->proto_sum = bproto_sum; + if (pd->ip_sum) + *pd->ip_sum = bip_sum; + m_copyback(m, off, hdrlen, pd->hdr.any); + } + s->src.seqhi = htonl(arc4random()); + /* Find mss option */ + mss = pf_get_mss(m, off, th->th_off, pd->af); + mss = pf_calc_mss(pd->src, pd->af, mss); + mss = pf_calc_mss(pd->dst, pd->af, mss); + s->src.mss = mss; +#ifdef __FreeBSD__ + pf_send_tcp(NULL, r, pd->af, pd->dst, pd->src, th->th_dport, +#else + pf_send_tcp(r, pd->af, pd->dst, pd->src, th->th_dport, #endif - - r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); - - if (direction == PF_OUT) { - bport = nport = uh->uh_sport; - /* check outgoing packet for BINAT/NAT */ - if ((nr = pf_get_translation(pd, m, off, PF_OUT, kif, &nsn, - saddr, uh->uh_sport, daddr, uh->uh_dport, - &pd->naddr, &nport)) != NULL) { - PF_ACPY(&pd->baddr, saddr, af); - pf_change_ap(saddr, &uh->uh_sport, pd->ip_sum, - &uh->uh_sum, &pd->naddr, nport, 1, af); - rewrite++; - if (nr->natpass) - r = NULL; - pd->nat_rule = nr; - } - } else { - bport = nport = uh->uh_dport; - /* check incoming packet for BINAT/RDR */ - if ((nr = pf_get_translation(pd, m, off, PF_IN, kif, &nsn, - saddr, uh->uh_sport, daddr, uh->uh_dport, &pd->naddr, - &nport)) != NULL) { - PF_ACPY(&pd->baddr, daddr, af); - pf_change_ap(daddr, &uh->uh_dport, pd->ip_sum, - &uh->uh_sum, &pd->naddr, nport, 1, af); - rewrite++; - if (nr->natpass) - r = NULL; - pd->nat_rule = nr; - } + th->th_sport, s->src.seqhi, ntohl(th->th_seq) + 1, + TH_SYN|TH_ACK, 0, s->src.mss, 0, 1, 0, NULL, NULL); + REASON_SET(&reason, PFRES_SYNPROXY); + return (PF_SYNPROXY_DROP); } - while (r != NULL) { - r->evaluations++; - if (pfi_kif_match(r->kif, kif) == r->ifnot) - r = r->skip[PF_SKIP_IFP].ptr; - else if (r->direction && r->direction != direction) - r = r->skip[PF_SKIP_DIR].ptr; - else if (r->af && r->af != af) - r = r->skip[PF_SKIP_AF].ptr; - else if (r->proto && r->proto != IPPROTO_UDP) - r = r->skip[PF_SKIP_PROTO].ptr; - else if (PF_MISMATCHAW(&r->src.addr, saddr, af, - r->src.neg, kif)) - r = r->skip[PF_SKIP_SRC_ADDR].ptr; - else if (r->src.port_op && !pf_match_port(r->src.port_op, - r->src.port[0], r->src.port[1], uh->uh_sport)) - r = r->skip[PF_SKIP_SRC_PORT].ptr; - else if (PF_MISMATCHAW(&r->dst.addr, daddr, af, - r->dst.neg, NULL)) - r = r->skip[PF_SKIP_DST_ADDR].ptr; - else if (r->dst.port_op && !pf_match_port(r->dst.port_op, - r->dst.port[0], r->dst.port[1], uh->uh_dport)) - r = r->skip[PF_SKIP_DST_PORT].ptr; - else if (r->tos && !(r->tos == pd->tos)) - r = TAILQ_NEXT(r, entries); - else if (r->rule_flag & PFRULE_FRAGMENT) - r = TAILQ_NEXT(r, entries); - else if (r->uid.op && (pd->lookup.done || (pd->lookup.done = + return (PF_PASS); + +csfailed: #ifdef __FreeBSD__ - pf_socket_lookup(direction, pd, inp), 1)) && + if (sk != NULL) + pool_put(&V_pf_state_key_pl, sk); + if (nk != NULL) + pool_put(&V_pf_state_key_pl, nk); #else - pf_socket_lookup(direction, pd), 1)) && + if (sk != NULL) + pool_put(&pf_state_key_pl, sk); + if (nk != NULL) + pool_put(&pf_state_key_pl, nk); #endif - !pf_match_uid(r->uid.op, r->uid.uid[0], r->uid.uid[1], - pd->lookup.uid)) - r = TAILQ_NEXT(r, entries); - else if (r->gid.op && (pd->lookup.done || (pd->lookup.done = + + if (sn != NULL && sn->states == 0 && sn->expire == 0) { #ifdef __FreeBSD__ - pf_socket_lookup(direction, pd, inp), 1)) && + RB_REMOVE(pf_src_tree, &V_tree_src_tracking, sn); + V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + V_pf_status.src_nodes--; + pool_put(&V_pf_src_tree_pl, sn); #else - pf_socket_lookup(direction, pd), 1)) && + RB_REMOVE(pf_src_tree, &tree_src_tracking, sn); + pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + pf_status.src_nodes--; + pool_put(&pf_src_tree_pl, sn); #endif - !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1], - pd->lookup.gid)) - r = TAILQ_NEXT(r, entries); - else if (r->prob && r->prob <= arc4random()) - r = TAILQ_NEXT(r, entries); - else if (r->match_tag && !pf_match_tag(m, r, pd->pf_mtag, &tag)) - r = TAILQ_NEXT(r, entries); - else if (r->os_fingerprint != PF_OSFP_ANY) - r = TAILQ_NEXT(r, entries); - else { - if (r->tag) - tag = r->tag; - if (r->rtableid >= 0) - rtableid = r->rtableid; - if (r->anchor == NULL) { - match = 1; - *rm = r; - *am = a; - *rsm = ruleset; - if ((*rm)->quick) - break; - r = TAILQ_NEXT(r, entries); - } else - pf_step_into_anchor(&asd, &ruleset, - PF_RULESET_FILTER, &r, &a, &match); - } - if (r == NULL && pf_step_out_of_anchor(&asd, &ruleset, - PF_RULESET_FILTER, &r, &a, &match)) - break; } - r = *rm; - a = *am; - ruleset = *rsm; - - REASON_SET(&reason, PFRES_MATCH); - - if (r->log || (nr != NULL && nr->natpass && nr->log)) { - if (rewrite) + if (nsn != sn && nsn != NULL && nsn->states == 0 && nsn->expire == 0) { #ifdef __FreeBSD__ - m_copyback(m, off, sizeof(*uh), (caddr_t)uh); + RB_REMOVE(pf_src_tree, &V_tree_src_tracking, nsn); + V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + V_pf_status.src_nodes--; + pool_put(&V_pf_src_tree_pl, nsn); #else - m_copyback(m, off, sizeof(*uh), uh); + RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn); + pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + pf_status.src_nodes--; + pool_put(&pf_src_tree_pl, nsn); #endif - PFLOG_PACKET(kif, h, m, af, direction, reason, r->log ? r : nr, - a, ruleset, pd); - } - - if ((r->action == PF_DROP) && - ((r->rule_flag & PFRULE_RETURNICMP) || - (r->rule_flag & PFRULE_RETURN))) { - /* undo NAT changes, if they have taken place */ - if (nr != NULL) { - if (direction == PF_OUT) { - pf_change_ap(saddr, &uh->uh_sport, pd->ip_sum, - &uh->uh_sum, &pd->baddr, bport, 1, af); - rewrite++; - } else { - pf_change_ap(daddr, &uh->uh_dport, pd->ip_sum, - &uh->uh_sum, &pd->baddr, bport, 1, af); - rewrite++; - } - } - if ((af == AF_INET) && r->return_icmp) - pf_send_icmp(m, r->return_icmp >> 8, - r->return_icmp & 255, af, r); - else if ((af == AF_INET6) && r->return_icmp6) - pf_send_icmp(m, r->return_icmp6 >> 8, - r->return_icmp6 & 255, af, r); - } - - if (r->action == PF_DROP) - return (PF_DROP); - - if (pf_tag_packet(m, pd->pf_mtag, tag, rtableid)) { - REASON_SET(&reason, PFRES_MEMORY); - return (PF_DROP); - } - - if (r->keep_state || nr != NULL) { - /* create new state */ - struct pf_state *s = NULL; - struct pf_src_node *sn = NULL; - - /* check maximums */ - if (r->max_states && (r->states >= r->max_states)) { - pf_status.lcounters[LCNT_STATES]++; - REASON_SET(&reason, PFRES_MAXSTATES); - goto cleanup; - } - /* src node for filter rule */ - if ((r->rule_flag & PFRULE_SRCTRACK || - r->rpool.opts & PF_POOL_STICKYADDR) && - pf_insert_src_node(&sn, r, saddr, af) != 0) { - REASON_SET(&reason, PFRES_SRCLIMIT); - goto cleanup; - } - /* src node for translation rule */ - if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) && - ((direction == PF_OUT && - pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) || - (pf_insert_src_node(&nsn, nr, saddr, af) != 0))) { - REASON_SET(&reason, PFRES_SRCLIMIT); - goto cleanup; - } - s = pool_get(&pf_state_pl, PR_NOWAIT); - if (s == NULL) { - REASON_SET(&reason, PFRES_MEMORY); -cleanup: - if (sn != NULL && sn->states == 0 && sn->expire == 0) { - RB_REMOVE(pf_src_tree, &tree_src_tracking, sn); - pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; - pf_status.src_nodes--; - pool_put(&pf_src_tree_pl, sn); - } - if (nsn != sn && nsn != NULL && nsn->states == 0 && - nsn->expire == 0) { - RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn); - pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; - pf_status.src_nodes--; - pool_put(&pf_src_tree_pl, nsn); - } - return (PF_DROP); - } - bzero(s, sizeof(*s)); - s->rule.ptr = r; - s->nat_rule.ptr = nr; - s->anchor.ptr = a; - STATE_INC_COUNTERS(s); - if (r->allow_opts) - s->state_flags |= PFSTATE_ALLOWOPTS; - if (r->rule_flag & PFRULE_STATESLOPPY) - s->state_flags |= PFSTATE_SLOPPY; - s->log = r->log & PF_LOG_ALL; - if (nr != NULL) - s->log |= nr->log & PF_LOG_ALL; - s->proto = IPPROTO_UDP; - s->direction = direction; - s->af = af; - if (direction == PF_OUT) { - PF_ACPY(&s->gwy.addr, saddr, af); - s->gwy.port = uh->uh_sport; - PF_ACPY(&s->ext.addr, daddr, af); - s->ext.port = uh->uh_dport; - if (nr != NULL) { - PF_ACPY(&s->lan.addr, &pd->baddr, af); - s->lan.port = bport; - } else { - PF_ACPY(&s->lan.addr, &s->gwy.addr, af); - s->lan.port = s->gwy.port; - } - } else { - PF_ACPY(&s->lan.addr, daddr, af); - s->lan.port = uh->uh_dport; - PF_ACPY(&s->ext.addr, saddr, af); - s->ext.port = uh->uh_sport; - if (nr != NULL) { - PF_ACPY(&s->gwy.addr, &pd->baddr, af); - s->gwy.port = bport; - } else { - PF_ACPY(&s->gwy.addr, &s->lan.addr, af); - s->gwy.port = s->lan.port; - } - } - s->src.state = PFUDPS_SINGLE; - s->dst.state = PFUDPS_NO_TRAFFIC; - s->creation = time_second; - s->expire = time_second; - s->timeout = PFTM_UDP_FIRST_PACKET; - pf_set_rt_ifp(s, saddr); - if (sn != NULL) { - s->src_node = sn; - s->src_node->states++; - } - if (nsn != NULL) { - PF_ACPY(&nsn->raddr, &pd->naddr, af); - s->nat_src_node = nsn; - s->nat_src_node->states++; - } - if (pf_insert_state(BOUND_IFACE(r, kif), s)) { - REASON_SET(&reason, PFRES_STATEINS); - pf_src_tree_remove_state(s); - STATE_DEC_COUNTERS(s); - pool_put(&pf_state_pl, s); - return (PF_DROP); - } else - *sm = s; - if (tag > 0) { - pf_tag_ref(tag); - s->tag = tag; - } - } - - /* copy back packet headers if we performed NAT operations */ - if (rewrite) - m_copyback(m, off, sizeof(*uh), (caddr_t)uh); - - return (PF_PASS); -} - -int -pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, - struct pfi_kif *kif, struct mbuf *m, int off, void *h, - struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm, - struct ifqueue *ifq) -{ - struct pf_rule *nr = NULL; - struct pf_addr *saddr = pd->src, *daddr = pd->dst; - struct pf_rule *r, *a = NULL; - struct pf_ruleset *ruleset = NULL; - struct pf_src_node *nsn = NULL; - u_short reason; - u_int16_t icmpid = 0, bport, nport = 0; - sa_family_t af = pd->af; - u_int8_t icmptype = 0; /* make the compiler happy */ - u_int8_t icmpcode = 0; /* make the compiler happy */ - int state_icmp = 0; - int tag = -1, rtableid = -1; -#ifdef INET6 - int rewrite = 0; -#endif /* INET6 */ - int asd = 0; - int match = 0; - - if (pf_check_congestion(ifq)) { - REASON_SET(&reason, PFRES_CONGEST); - return (PF_DROP); - } - - switch (pd->proto) { -#ifdef INET - case IPPROTO_ICMP: - icmptype = pd->hdr.icmp->icmp_type; - icmpcode = pd->hdr.icmp->icmp_code; - icmpid = pd->hdr.icmp->icmp_id; - - if (icmptype == ICMP_UNREACH || - icmptype == ICMP_SOURCEQUENCH || - icmptype == ICMP_REDIRECT || - icmptype == ICMP_TIMXCEED || - icmptype == ICMP_PARAMPROB) - state_icmp++; - break; -#endif /* INET */ -#ifdef INET6 - case IPPROTO_ICMPV6: - icmptype = pd->hdr.icmp6->icmp6_type; - icmpcode = pd->hdr.icmp6->icmp6_code; - icmpid = pd->hdr.icmp6->icmp6_id; - - if (icmptype == ICMP6_DST_UNREACH || - icmptype == ICMP6_PACKET_TOO_BIG || - icmptype == ICMP6_TIME_EXCEEDED || - icmptype == ICMP6_PARAM_PROB) - state_icmp++; - break; -#endif /* INET6 */ - } - - r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); - - if (direction == PF_OUT) { - bport = nport = icmpid; - /* check outgoing packet for BINAT/NAT */ - if ((nr = pf_get_translation(pd, m, off, PF_OUT, kif, &nsn, - saddr, icmpid, daddr, icmpid, &pd->naddr, &nport)) != - NULL) { - PF_ACPY(&pd->baddr, saddr, af); - switch (af) { -#ifdef INET - case AF_INET: - pf_change_a(&saddr->v4.s_addr, pd->ip_sum, - pd->naddr.v4.s_addr, 0); - pd->hdr.icmp->icmp_cksum = pf_cksum_fixup( - pd->hdr.icmp->icmp_cksum, icmpid, nport, 0); - pd->hdr.icmp->icmp_id = nport; - m_copyback(m, off, ICMP_MINLEN, - (caddr_t)pd->hdr.icmp); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - pf_change_a6(saddr, &pd->hdr.icmp6->icmp6_cksum, - &pd->naddr, 0); - rewrite++; - break; -#endif /* INET6 */ - } - if (nr->natpass) - r = NULL; - pd->nat_rule = nr; - } - } else { - bport = nport = icmpid; - /* check incoming packet for BINAT/RDR */ - if ((nr = pf_get_translation(pd, m, off, PF_IN, kif, &nsn, - saddr, icmpid, daddr, icmpid, &pd->naddr, &nport)) != - NULL) { - PF_ACPY(&pd->baddr, daddr, af); - switch (af) { -#ifdef INET - case AF_INET: - pf_change_a(&daddr->v4.s_addr, - pd->ip_sum, pd->naddr.v4.s_addr, 0); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - pf_change_a6(daddr, &pd->hdr.icmp6->icmp6_cksum, - &pd->naddr, 0); - rewrite++; - break; -#endif /* INET6 */ - } - if (nr->natpass) - r = NULL; - pd->nat_rule = nr; - } - } - - while (r != NULL) { - r->evaluations++; - if (pfi_kif_match(r->kif, kif) == r->ifnot) - r = r->skip[PF_SKIP_IFP].ptr; - else if (r->direction && r->direction != direction) - r = r->skip[PF_SKIP_DIR].ptr; - else if (r->af && r->af != af) - r = r->skip[PF_SKIP_AF].ptr; - else if (r->proto && r->proto != pd->proto) - r = r->skip[PF_SKIP_PROTO].ptr; - else if (PF_MISMATCHAW(&r->src.addr, saddr, af, - r->src.neg, kif)) - r = r->skip[PF_SKIP_SRC_ADDR].ptr; - else if (PF_MISMATCHAW(&r->dst.addr, daddr, af, - r->dst.neg, NULL)) - r = r->skip[PF_SKIP_DST_ADDR].ptr; - else if (r->type && r->type != icmptype + 1) - r = TAILQ_NEXT(r, entries); - else if (r->code && r->code != icmpcode + 1) - r = TAILQ_NEXT(r, entries); - else if (r->tos && !(r->tos == pd->tos)) - r = TAILQ_NEXT(r, entries); - else if (r->rule_flag & PFRULE_FRAGMENT) - r = TAILQ_NEXT(r, entries); - else if (r->prob && r->prob <= arc4random()) - r = TAILQ_NEXT(r, entries); - else if (r->match_tag && !pf_match_tag(m, r, pd->pf_mtag, &tag)) - r = TAILQ_NEXT(r, entries); - else if (r->os_fingerprint != PF_OSFP_ANY) - r = TAILQ_NEXT(r, entries); - else { - if (r->tag) - tag = r->tag; - if (r->rtableid >= 0) - rtableid = r->rtableid; - if (r->anchor == NULL) { - match = 1; - *rm = r; - *am = a; - *rsm = ruleset; - if ((*rm)->quick) - break; - r = TAILQ_NEXT(r, entries); - } else - pf_step_into_anchor(&asd, &ruleset, - PF_RULESET_FILTER, &r, &a, &match); - } - if (r == NULL && pf_step_out_of_anchor(&asd, &ruleset, - PF_RULESET_FILTER, &r, &a, &match)) - break; - } - r = *rm; - a = *am; - ruleset = *rsm; - - REASON_SET(&reason, PFRES_MATCH); - - if (r->log || (nr != NULL && nr->natpass && nr->log)) { -#ifdef INET6 - if (rewrite) - m_copyback(m, off, sizeof(struct icmp6_hdr), - (caddr_t)pd->hdr.icmp6); -#endif /* INET6 */ - PFLOG_PACKET(kif, h, m, af, direction, reason, r->log ? r : nr, - a, ruleset, pd); - } - - if (r->action != PF_PASS) - return (PF_DROP); - - if (pf_tag_packet(m, pd->pf_mtag, tag, rtableid)) { - REASON_SET(&reason, PFRES_MEMORY); - return (PF_DROP); - } - - if (!state_icmp && (r->keep_state || nr != NULL)) { - /* create new state */ - struct pf_state *s = NULL; - struct pf_src_node *sn = NULL; - - /* check maximums */ - if (r->max_states && (r->states >= r->max_states)) { - pf_status.lcounters[LCNT_STATES]++; - REASON_SET(&reason, PFRES_MAXSTATES); - goto cleanup; - } - /* src node for filter rule */ - if ((r->rule_flag & PFRULE_SRCTRACK || - r->rpool.opts & PF_POOL_STICKYADDR) && - pf_insert_src_node(&sn, r, saddr, af) != 0) { - REASON_SET(&reason, PFRES_SRCLIMIT); - goto cleanup; - } - /* src node for translation rule */ - if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) && - ((direction == PF_OUT && - pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) || - (pf_insert_src_node(&nsn, nr, saddr, af) != 0))) { - REASON_SET(&reason, PFRES_SRCLIMIT); - goto cleanup; - } - s = pool_get(&pf_state_pl, PR_NOWAIT); - if (s == NULL) { - REASON_SET(&reason, PFRES_MEMORY); -cleanup: - if (sn != NULL && sn->states == 0 && sn->expire == 0) { - RB_REMOVE(pf_src_tree, &tree_src_tracking, sn); - pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; - pf_status.src_nodes--; - pool_put(&pf_src_tree_pl, sn); - } - if (nsn != sn && nsn != NULL && nsn->states == 0 && - nsn->expire == 0) { - RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn); - pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; - pf_status.src_nodes--; - pool_put(&pf_src_tree_pl, nsn); - } - return (PF_DROP); - } - bzero(s, sizeof(*s)); - s->rule.ptr = r; - s->nat_rule.ptr = nr; - s->anchor.ptr = a; - STATE_INC_COUNTERS(s); - if (r->allow_opts) - s->state_flags |= PFSTATE_ALLOWOPTS; - if (r->rule_flag & PFRULE_STATESLOPPY) - s->state_flags |= PFSTATE_SLOPPY; - s->log = r->log & PF_LOG_ALL; - if (nr != NULL) - s->log |= nr->log & PF_LOG_ALL; - s->proto = pd->proto; - s->direction = direction; - s->af = af; - if (direction == PF_OUT) { - PF_ACPY(&s->gwy.addr, saddr, af); - s->gwy.port = nport; - PF_ACPY(&s->ext.addr, daddr, af); - s->ext.port = 0; - if (nr != NULL) { - PF_ACPY(&s->lan.addr, &pd->baddr, af); - s->lan.port = bport; - } else { - PF_ACPY(&s->lan.addr, &s->gwy.addr, af); - s->lan.port = s->gwy.port; - } - } else { - PF_ACPY(&s->lan.addr, daddr, af); - s->lan.port = nport; - PF_ACPY(&s->ext.addr, saddr, af); - s->ext.port = 0; - if (nr != NULL) { - PF_ACPY(&s->gwy.addr, &pd->baddr, af); - s->gwy.port = bport; - } else { - PF_ACPY(&s->gwy.addr, &s->lan.addr, af); - s->gwy.port = s->lan.port; - } - } - s->creation = time_second; - s->expire = time_second; - s->timeout = PFTM_ICMP_FIRST_PACKET; - pf_set_rt_ifp(s, saddr); - if (sn != NULL) { - s->src_node = sn; - s->src_node->states++; - } - if (nsn != NULL) { - PF_ACPY(&nsn->raddr, &pd->naddr, af); - s->nat_src_node = nsn; - s->nat_src_node->states++; - } - if (pf_insert_state(BOUND_IFACE(r, kif), s)) { - REASON_SET(&reason, PFRES_STATEINS); - pf_src_tree_remove_state(s); - STATE_DEC_COUNTERS(s); - pool_put(&pf_state_pl, s); - return (PF_DROP); - } else - *sm = s; - if (tag > 0) { - pf_tag_ref(tag); - s->tag = tag; - } - } - -#ifdef INET6 - /* copy back packet headers if we performed IPv6 NAT operations */ - if (rewrite) - m_copyback(m, off, sizeof(struct icmp6_hdr), - (caddr_t)pd->hdr.icmp6); -#endif /* INET6 */ - - return (PF_PASS); -} - -int -pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, - struct pfi_kif *kif, struct mbuf *m, int off, void *h, struct pf_pdesc *pd, - struct pf_rule **am, struct pf_ruleset **rsm, struct ifqueue *ifq) -{ - struct pf_rule *nr = NULL; - struct pf_rule *r, *a = NULL; - struct pf_ruleset *ruleset = NULL; - struct pf_src_node *nsn = NULL; - struct pf_addr *saddr = pd->src, *daddr = pd->dst; - sa_family_t af = pd->af; - u_short reason; - int tag = -1, rtableid = -1; - int asd = 0; - int match = 0; - - if (pf_check_congestion(ifq)) { - REASON_SET(&reason, PFRES_CONGEST); - return (PF_DROP); - } - - r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); - - if (direction == PF_OUT) { - /* check outgoing packet for BINAT/NAT */ - if ((nr = pf_get_translation(pd, m, off, PF_OUT, kif, &nsn, - saddr, 0, daddr, 0, &pd->naddr, NULL)) != NULL) { - PF_ACPY(&pd->baddr, saddr, af); - switch (af) { -#ifdef INET - case AF_INET: - pf_change_a(&saddr->v4.s_addr, pd->ip_sum, - pd->naddr.v4.s_addr, 0); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - PF_ACPY(saddr, &pd->naddr, af); - break; -#endif /* INET6 */ - } - if (nr->natpass) - r = NULL; - pd->nat_rule = nr; - } - } else { - /* check incoming packet for BINAT/RDR */ - if ((nr = pf_get_translation(pd, m, off, PF_IN, kif, &nsn, - saddr, 0, daddr, 0, &pd->naddr, NULL)) != NULL) { - PF_ACPY(&pd->baddr, daddr, af); - switch (af) { -#ifdef INET - case AF_INET: - pf_change_a(&daddr->v4.s_addr, - pd->ip_sum, pd->naddr.v4.s_addr, 0); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - PF_ACPY(daddr, &pd->naddr, af); - break; -#endif /* INET6 */ - } - if (nr->natpass) - r = NULL; - pd->nat_rule = nr; - } - } - - while (r != NULL) { - r->evaluations++; - if (pfi_kif_match(r->kif, kif) == r->ifnot) - r = r->skip[PF_SKIP_IFP].ptr; - else if (r->direction && r->direction != direction) - r = r->skip[PF_SKIP_DIR].ptr; - else if (r->af && r->af != af) - r = r->skip[PF_SKIP_AF].ptr; - else if (r->proto && r->proto != pd->proto) - r = r->skip[PF_SKIP_PROTO].ptr; - else if (PF_MISMATCHAW(&r->src.addr, pd->src, af, - r->src.neg, kif)) - r = r->skip[PF_SKIP_SRC_ADDR].ptr; - else if (PF_MISMATCHAW(&r->dst.addr, pd->dst, af, - r->dst.neg, NULL)) - r = r->skip[PF_SKIP_DST_ADDR].ptr; - else if (r->tos && !(r->tos == pd->tos)) - r = TAILQ_NEXT(r, entries); - else if (r->rule_flag & PFRULE_FRAGMENT) - r = TAILQ_NEXT(r, entries); - else if (r->prob && r->prob <= arc4random()) - r = TAILQ_NEXT(r, entries); - else if (r->match_tag && !pf_match_tag(m, r, pd->pf_mtag, &tag)) - r = TAILQ_NEXT(r, entries); - else if (r->os_fingerprint != PF_OSFP_ANY) - r = TAILQ_NEXT(r, entries); - else { - if (r->tag) - tag = r->tag; - if (r->rtableid >= 0) - rtableid = r->rtableid; - if (r->anchor == NULL) { - match = 1; - *rm = r; - *am = a; - *rsm = ruleset; - if ((*rm)->quick) - break; - r = TAILQ_NEXT(r, entries); - } else - pf_step_into_anchor(&asd, &ruleset, - PF_RULESET_FILTER, &r, &a, &match); - } - if (r == NULL && pf_step_out_of_anchor(&asd, &ruleset, - PF_RULESET_FILTER, &r, &a, &match)) - break; - } - r = *rm; - a = *am; - ruleset = *rsm; - - REASON_SET(&reason, PFRES_MATCH); - - if (r->log || (nr != NULL && nr->natpass && nr->log)) - PFLOG_PACKET(kif, h, m, af, direction, reason, r->log ? r : nr, - a, ruleset, pd); - - if ((r->action == PF_DROP) && - ((r->rule_flag & PFRULE_RETURNICMP) || - (r->rule_flag & PFRULE_RETURN))) { - struct pf_addr *a = NULL; - - if (nr != NULL) { - if (direction == PF_OUT) - a = saddr; - else - a = daddr; - } - if (a != NULL) { - switch (af) { -#ifdef INET - case AF_INET: - pf_change_a(&a->v4.s_addr, pd->ip_sum, - pd->baddr.v4.s_addr, 0); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - PF_ACPY(a, &pd->baddr, af); - break; -#endif /* INET6 */ - } - } - if ((af == AF_INET) && r->return_icmp) - pf_send_icmp(m, r->return_icmp >> 8, - r->return_icmp & 255, af, r); - else if ((af == AF_INET6) && r->return_icmp6) - pf_send_icmp(m, r->return_icmp6 >> 8, - r->return_icmp6 & 255, af, r); - } - - if (r->action != PF_PASS) - return (PF_DROP); - - if (pf_tag_packet(m, pd->pf_mtag, tag, rtableid)) { - REASON_SET(&reason, PFRES_MEMORY); - return (PF_DROP); - } - - if (r->keep_state || nr != NULL) { - /* create new state */ - struct pf_state *s = NULL; - struct pf_src_node *sn = NULL; - - /* check maximums */ - if (r->max_states && (r->states >= r->max_states)) { - pf_status.lcounters[LCNT_STATES]++; - REASON_SET(&reason, PFRES_MAXSTATES); - goto cleanup; - } - /* src node for filter rule */ - if ((r->rule_flag & PFRULE_SRCTRACK || - r->rpool.opts & PF_POOL_STICKYADDR) && - pf_insert_src_node(&sn, r, saddr, af) != 0) { - REASON_SET(&reason, PFRES_SRCLIMIT); - goto cleanup; - } - /* src node for translation rule */ - if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) && - ((direction == PF_OUT && - pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) || - (pf_insert_src_node(&nsn, nr, saddr, af) != 0))) { - REASON_SET(&reason, PFRES_SRCLIMIT); - goto cleanup; - } - s = pool_get(&pf_state_pl, PR_NOWAIT); - if (s == NULL) { - REASON_SET(&reason, PFRES_MEMORY); -cleanup: - if (sn != NULL && sn->states == 0 && sn->expire == 0) { - RB_REMOVE(pf_src_tree, &tree_src_tracking, sn); - pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; - pf_status.src_nodes--; - pool_put(&pf_src_tree_pl, sn); - } - if (nsn != sn && nsn != NULL && nsn->states == 0 && - nsn->expire == 0) { - RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn); - pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; - pf_status.src_nodes--; - pool_put(&pf_src_tree_pl, nsn); - } - return (PF_DROP); - } - bzero(s, sizeof(*s)); - s->rule.ptr = r; - s->nat_rule.ptr = nr; - s->anchor.ptr = a; - STATE_INC_COUNTERS(s); - if (r->allow_opts) - s->state_flags |= PFSTATE_ALLOWOPTS; - if (r->rule_flag & PFRULE_STATESLOPPY) - s->state_flags |= PFSTATE_SLOPPY; - s->log = r->log & PF_LOG_ALL; - if (nr != NULL) - s->log |= nr->log & PF_LOG_ALL; - s->proto = pd->proto; - s->direction = direction; - s->af = af; - if (direction == PF_OUT) { - PF_ACPY(&s->gwy.addr, saddr, af); - PF_ACPY(&s->ext.addr, daddr, af); - if (nr != NULL) - PF_ACPY(&s->lan.addr, &pd->baddr, af); - else - PF_ACPY(&s->lan.addr, &s->gwy.addr, af); - } else { - PF_ACPY(&s->lan.addr, daddr, af); - PF_ACPY(&s->ext.addr, saddr, af); - if (nr != NULL) - PF_ACPY(&s->gwy.addr, &pd->baddr, af); - else - PF_ACPY(&s->gwy.addr, &s->lan.addr, af); - } - s->src.state = PFOTHERS_SINGLE; - s->dst.state = PFOTHERS_NO_TRAFFIC; - s->creation = time_second; - s->expire = time_second; - s->timeout = PFTM_OTHER_FIRST_PACKET; - pf_set_rt_ifp(s, saddr); - if (sn != NULL) { - s->src_node = sn; - s->src_node->states++; - } - if (nsn != NULL) { - PF_ACPY(&nsn->raddr, &pd->naddr, af); - s->nat_src_node = nsn; - s->nat_src_node->states++; - } - if (pf_insert_state(BOUND_IFACE(r, kif), s)) { - REASON_SET(&reason, PFRES_STATEINS); - pf_src_tree_remove_state(s); - STATE_DEC_COUNTERS(s); - pool_put(&pf_state_pl, s); - return (PF_DROP); - } else - *sm = s; - if (tag > 0) { - pf_tag_ref(tag); - s->tag = tag; - } } - - return (PF_PASS); + return (PF_DROP); } int @@ -4649,9 +4107,14 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif, pd->proto == IPPROTO_ICMPV6) && (r->type || r->code)) r = TAILQ_NEXT(r, entries); - else if (r->prob && r->prob <= arc4random()) + else if (r->prob && r->prob <= + (arc4random() % (UINT_MAX - 1) + 1)) r = TAILQ_NEXT(r, entries); - else if (r->match_tag && !pf_match_tag(m, r, pd->pf_mtag, &tag)) +#ifdef __FreeBSD__ + else if (r->match_tag && !pf_match_tag(m, r, &tag, pd->pf_mtag)) +#else + else if (r->match_tag && !pf_match_tag(m, r, &tag)) +#endif r = TAILQ_NEXT(r, entries); else { if (r->anchor == NULL) { @@ -4683,7 +4146,11 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif, if (r->action != PF_PASS) return (PF_DROP); - if (pf_tag_packet(m, pd->pf_mtag, tag, -1)) { +#ifdef __FreeBSD__ + if (pf_tag_packet(m, tag, -1, pd->pf_mtag)) { +#else + if (pf_tag_packet(m, tag, -1)) { +#endif REASON_SET(&reason, PFRES_MEMORY); return (PF_DROP); } @@ -4696,11 +4163,11 @@ pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, struct pf_state **state, struct pfi_kif *kif, struct mbuf *m, int off, struct pf_pdesc *pd, u_short *reason, int *copyback) { - struct tcphdr *th = pd->hdr.tcp; - u_int16_t win = ntohs(th->th_win); - u_int32_t ack, end, seq, orig_seq; - u_int8_t sws, dws; - int ackskew; + struct tcphdr *th = pd->hdr.tcp; + u_int16_t win = ntohs(th->th_win); + u_int32_t ack, end, seq, orig_seq; + u_int8_t sws, dws; + int ackskew; if (src->wscale && dst->wscale && !(th->th_flags & TH_SYN)) { sws = src->wscale & PF_WSCALE_MASK; @@ -4728,13 +4195,9 @@ pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, /* Deferred generation of sequence number modulator */ if (dst->seqdiff && !src->seqdiff) { -#ifdef __FreeBSD__ - while ((src->seqdiff = pf_new_isn(*state) - seq) == 0) - ; -#else - while ((src->seqdiff = tcp_rndiss_next() - seq) == 0) + /* use random iss for the TCP server */ + while ((src->seqdiff = arc4random() - seq) == 0) ; -#endif ack = ntohl(th->th_ack) - dst->seqdiff; pf_change_a(&th->th_seq, &th->th_sum, htonl(seq + src->seqdiff), 0); @@ -4841,7 +4304,7 @@ pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, } -#define MAXACKWINDOW (0xffff + 1500) /* 1500 is an arbitrary fudge factor */ +#define MAXACKWINDOW (0xffff + 1500) /* 1500 is an arbitrary fudge factor */ if (SEQ_GEQ(src->seqhi, end) && /* Last octet inside other's window space */ SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)) && @@ -4851,7 +4314,8 @@ pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, (ackskew <= (MAXACKWINDOW << sws)) && /* Acking not more than one window forward */ ((th->th_flags & TH_RST) == 0 || orig_seq == src->seqlo || - (orig_seq == src->seqlo + 1) || (pd->flags & PFDESC_IP_REAS) == 0)) { + (orig_seq == src->seqlo + 1) || (orig_seq + 1 == src->seqlo) || + (pd->flags & PFDESC_IP_REAS) == 0)) { /* Require an exact/+1 sequence match on resets when possible */ if (dst->scrub || src->scrub) { @@ -4941,19 +4405,25 @@ pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, * and keep updating the state TTL. */ +#ifdef __FreeBSD__ + if (V_pf_status.debug >= PF_DEBUG_MISC) { +#else if (pf_status.debug >= PF_DEBUG_MISC) { +#endif printf("pf: loose state match: "); pf_print_state(*state); pf_print_flags(th->th_flags); printf(" seq=%u (%u) ack=%u len=%u ackskew=%d " - "pkts=%llu:%llu\n", seq, orig_seq, ack, pd->p_len, + "pkts=%llu:%llu dir=%s,%s\n", seq, orig_seq, ack, #ifdef __FreeBSD__ - ackskew, (unsigned long long)(*state)->packets[0], - (unsigned long long)(*state)->packets[1]); + pd->p_len, ackskew, (unsigned long long)(*state)->packets[0], + (unsigned long long)(*state)->packets[1], #else - ackskew, (*state)->packets[0], - (*state)->packets[1]); + pd->p_len, ackskew, (*state)->packets[0], + (*state)->packets[1], #endif + pd->dir == PF_IN ? "in" : "out", + pd->dir == (*state)->direction ? "fwd" : "rev"); } if (dst->scrub || src->scrub) { @@ -4991,7 +4461,7 @@ pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, /* Send RST for state mismatches during handshake */ if (!(th->th_flags & TH_RST)) #ifdef __FreeBSD__ - pf_send_tcp(m, (*state)->rule.ptr, pd->af, + pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, #else pf_send_tcp((*state)->rule.ptr, pd->af, #endif @@ -5003,16 +4473,16 @@ pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, src->seqlo = 0; src->seqhi = 1; src->max_win = 1; +#ifdef __FreeBSD__ + } else if (V_pf_status.debug >= PF_DEBUG_MISC) { +#else } else if (pf_status.debug >= PF_DEBUG_MISC) { +#endif printf("pf: BAD state: "); pf_print_state(*state); pf_print_flags(th->th_flags); printf(" seq=%u (%u) ack=%u len=%u ackskew=%d " -#ifdef notyet "pkts=%llu:%llu dir=%s,%s\n", -#else - "pkts=%llu:%llu%s\n", -#endif seq, orig_seq, ack, pd->p_len, ackskew, #ifdef __FreeBSD__ (unsigned long long)(*state)->packets[0], @@ -5020,12 +4490,8 @@ pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, #else (*state)->packets[0], (*state)->packets[1], #endif -#ifdef notyet - direction == PF_IN ? "in" : "out", - direction == (*state)->direction ? "fwd" : "rev"); -#else - ""); -#endif + pd->dir == PF_IN ? "in" : "out", + pd->dir == (*state)->direction ? "fwd" : "rev"); printf("pf: State failure on: %c %c %c %c | %c %c\n", SEQ_GEQ(src->seqhi, end) ? ' ' : '1', SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)) ? @@ -5039,7 +4505,6 @@ pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, return (PF_DROP); } - /* Any packets which have gotten here are to be passed */ return (PF_PASS); } @@ -5114,32 +4579,36 @@ pf_tcp_track_sloppy(struct pf_state_peer *src, struct pf_state_peer *dst, return (PF_PASS); } - int pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, struct mbuf *m, int off, void *h, struct pf_pdesc *pd, u_short *reason) { - struct pf_state_cmp key; + struct pf_state_key_cmp key; struct tcphdr *th = pd->hdr.tcp; int copyback = 0; struct pf_state_peer *src, *dst; + struct pf_state_key *sk; key.af = pd->af; key.proto = IPPROTO_TCP; - if (direction == PF_IN) { - PF_ACPY(&key.ext.addr, pd->src, key.af); - PF_ACPY(&key.gwy.addr, pd->dst, key.af); - key.ext.port = th->th_sport; - key.gwy.port = th->th_dport; - } else { - PF_ACPY(&key.lan.addr, pd->src, key.af); - PF_ACPY(&key.ext.addr, pd->dst, key.af); - key.lan.port = th->th_sport; - key.ext.port = th->th_dport; + if (direction == PF_IN) { /* wire side, straight */ + PF_ACPY(&key.addr[0], pd->src, key.af); + PF_ACPY(&key.addr[1], pd->dst, key.af); + key.port[0] = th->th_sport; + key.port[1] = th->th_dport; + } else { /* stack side, reverse */ + PF_ACPY(&key.addr[1], pd->src, key.af); + PF_ACPY(&key.addr[0], pd->dst, key.af); + key.port[1] = th->th_sport; + key.port[0] = th->th_dport; } - STATE_LOOKUP(); +#ifdef __FreeBSD__ + STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); +#else + STATE_LOOKUP(kif, &key, direction, *state, m); +#endif if (direction == (*state)->direction) { src = &(*state)->src; @@ -5149,6 +4618,8 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, dst = &(*state)->src; } + sk = (*state)->key[pd->didx]; + if ((*state)->src.state == PF_TCPS_PROXY_SRC) { if (direction != (*state)->direction) { REASON_SET(reason, PFRES_SYNPROXY); @@ -5183,15 +4654,6 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, (*state)->src.state = PF_TCPS_PROXY_DST; } if ((*state)->src.state == PF_TCPS_PROXY_DST) { - struct pf_state_host *src, *dst; - - if (direction == PF_OUT) { - src = &(*state)->gwy; - dst = &(*state)->ext; - } else { - src = &(*state)->ext; - dst = &(*state)->lan; - } if (direction == (*state)->direction) { if (((th->th_flags & (TH_SYN|TH_ACK)) != TH_ACK) || (ntohl(th->th_ack) != (*state)->src.seqhi + 1) || @@ -5204,11 +4666,11 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, (*state)->dst.seqhi = htonl(arc4random()); #ifdef __FreeBSD__ pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, - &src->addr, #else - pf_send_tcp((*state)->rule.ptr, pd->af, &src->addr, + pf_send_tcp((*state)->rule.ptr, pd->af, #endif - &dst->addr, src->port, dst->port, + &sk->addr[pd->sidx], &sk->addr[pd->didx], + sk->port[pd->sidx], sk->port[pd->didx], (*state)->dst.seqhi, 0, TH_SYN, 0, (*state)->src.mss, 0, 0, (*state)->tag, NULL, NULL); REASON_SET(reason, PFRES_SYNPROXY); @@ -5232,11 +4694,11 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, (*state)->tag, NULL, NULL); #ifdef __FreeBSD__ pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, - &src->addr, #else - pf_send_tcp((*state)->rule.ptr, pd->af, &src->addr, + pf_send_tcp((*state)->rule.ptr, pd->af, #endif - &dst->addr, src->port, dst->port, + &sk->addr[pd->sidx], &sk->addr[pd->didx], + sk->port[pd->sidx], sk->port[pd->didx], (*state)->src.seqhi + 1, (*state)->src.seqlo + 1, TH_ACK, (*state)->dst.max_win, 0, 0, 1, 0, NULL, NULL); @@ -5259,7 +4721,11 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, if (((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN) && dst->state >= TCPS_FIN_WAIT_2 && src->state >= TCPS_FIN_WAIT_2) { +#ifdef __FreeBSD__ + if (V_pf_status.debug >= PF_DEBUG_MISC) { +#else if (pf_status.debug >= PF_DEBUG_MISC) { +#endif printf("pf: state reuse "); pf_print_state(*state); pf_print_flags(th->th_flags); @@ -5282,21 +4748,31 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, } /* translate source/destination address, if necessary */ - if (STATE_TRANSLATE(*state)) { - if (direction == PF_OUT) + if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = (*state)->key[pd->didx]; + + if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) || + nk->port[pd->sidx] != th->th_sport) pf_change_ap(pd->src, &th->th_sport, pd->ip_sum, - &th->th_sum, &(*state)->gwy.addr, - (*state)->gwy.port, 0, pd->af); - else + &th->th_sum, &nk->addr[pd->sidx], + nk->port[pd->sidx], 0, pd->af); + + if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) || + nk->port[pd->didx] != th->th_dport) pf_change_ap(pd->dst, &th->th_dport, pd->ip_sum, - &th->th_sum, &(*state)->lan.addr, - (*state)->lan.port, 0, pd->af); - m_copyback(m, off, sizeof(*th), (caddr_t)th); - } else if (copyback) { - /* Copyback sequence modulation or stateful scrub changes */ - m_copyback(m, off, sizeof(*th), (caddr_t)th); + &th->th_sum, &nk->addr[pd->didx], + nk->port[pd->didx], 0, pd->af); + copyback = 1; } + /* Copyback sequence modulation or stateful scrub changes if needed */ + if (copyback) +#ifdef __FreeBSD__ + m_copyback(m, off, sizeof(*th), (caddr_t)th); +#else + m_copyback(m, off, sizeof(*th), th); +#endif + return (PF_PASS); } @@ -5305,24 +4781,28 @@ pf_test_state_udp(struct pf_state **state, int direction, struct pfi_kif *kif, struct mbuf *m, int off, void *h, struct pf_pdesc *pd) { struct pf_state_peer *src, *dst; - struct pf_state_cmp key; + struct pf_state_key_cmp key; struct udphdr *uh = pd->hdr.udp; key.af = pd->af; key.proto = IPPROTO_UDP; - if (direction == PF_IN) { - PF_ACPY(&key.ext.addr, pd->src, key.af); - PF_ACPY(&key.gwy.addr, pd->dst, key.af); - key.ext.port = uh->uh_sport; - key.gwy.port = uh->uh_dport; - } else { - PF_ACPY(&key.lan.addr, pd->src, key.af); - PF_ACPY(&key.ext.addr, pd->dst, key.af); - key.lan.port = uh->uh_sport; - key.ext.port = uh->uh_dport; + if (direction == PF_IN) { /* wire side, straight */ + PF_ACPY(&key.addr[0], pd->src, key.af); + PF_ACPY(&key.addr[1], pd->dst, key.af); + key.port[0] = uh->uh_sport; + key.port[1] = uh->uh_dport; + } else { /* stack side, reverse */ + PF_ACPY(&key.addr[1], pd->src, key.af); + PF_ACPY(&key.addr[0], pd->dst, key.af); + key.port[1] = uh->uh_sport; + key.port[0] = uh->uh_dport; } - STATE_LOOKUP(); +#ifdef __FreeBSD__ + STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); +#else + STATE_LOOKUP(kif, &key, direction, *state, m); +#endif if (direction == (*state)->direction) { src = &(*state)->src; @@ -5346,16 +4826,25 @@ pf_test_state_udp(struct pf_state **state, int direction, struct pfi_kif *kif, (*state)->timeout = PFTM_UDP_SINGLE; /* translate source/destination address, if necessary */ - if (STATE_TRANSLATE(*state)) { - if (direction == PF_OUT) + if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = (*state)->key[pd->didx]; + + if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) || + nk->port[pd->sidx] != uh->uh_sport) pf_change_ap(pd->src, &uh->uh_sport, pd->ip_sum, - &uh->uh_sum, &(*state)->gwy.addr, - (*state)->gwy.port, 1, pd->af); - else + &uh->uh_sum, &nk->addr[pd->sidx], + nk->port[pd->sidx], 1, pd->af); + + if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) || + nk->port[pd->didx] != uh->uh_dport) pf_change_ap(pd->dst, &uh->uh_dport, pd->ip_sum, - &uh->uh_sum, &(*state)->lan.addr, - (*state)->lan.port, 1, pd->af); + &uh->uh_sum, &nk->addr[pd->didx], + nk->port[pd->didx], 1, pd->af); +#ifdef __FreeBSD__ m_copyback(m, off, sizeof(*uh), (caddr_t)uh); +#else + m_copyback(m, off, sizeof(*uh), uh); +#endif } return (PF_PASS); @@ -5365,12 +4854,15 @@ int pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, struct mbuf *m, int off, void *h, struct pf_pdesc *pd, u_short *reason) { - struct pf_addr *saddr = pd->src, *daddr = pd->dst; - u_int16_t icmpid = 0; /* make the compiler happy */ - u_int16_t *icmpsum = NULL; /* make the compiler happy */ - u_int8_t icmptype = 0; /* make the compiler happy */ + struct pf_addr *saddr = pd->src, *daddr = pd->dst; +#ifdef __FreeBSD__ + u_int16_t icmpid = 0, *icmpsum; +#else + u_int16_t icmpid, *icmpsum; +#endif + u_int8_t icmptype; int state_icmp = 0; - struct pf_state_cmp key; + struct pf_state_key_cmp key; switch (pd->proto) { #ifdef INET @@ -5410,84 +4902,84 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, */ key.af = pd->af; key.proto = pd->proto; - if (direction == PF_IN) { - PF_ACPY(&key.ext.addr, pd->src, key.af); - PF_ACPY(&key.gwy.addr, pd->dst, key.af); - key.ext.port = 0; - key.gwy.port = icmpid; - } else { - PF_ACPY(&key.lan.addr, pd->src, key.af); - PF_ACPY(&key.ext.addr, pd->dst, key.af); - key.lan.port = icmpid; - key.ext.port = 0; + key.port[0] = key.port[1] = icmpid; + if (direction == PF_IN) { /* wire side, straight */ + PF_ACPY(&key.addr[0], pd->src, key.af); + PF_ACPY(&key.addr[1], pd->dst, key.af); + } else { /* stack side, reverse */ + PF_ACPY(&key.addr[1], pd->src, key.af); + PF_ACPY(&key.addr[0], pd->dst, key.af); } - STATE_LOOKUP(); +#ifdef __FreeBSD__ + STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); +#else + STATE_LOOKUP(kif, &key, direction, *state, m); +#endif (*state)->expire = time_second; (*state)->timeout = PFTM_ICMP_ERROR_REPLY; /* translate source/destination address, if necessary */ - if (STATE_TRANSLATE(*state)) { - if (direction == PF_OUT) { - switch (pd->af) { + if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = (*state)->key[pd->didx]; + + switch (pd->af) { #ifdef INET - case AF_INET: + case AF_INET: + if (PF_ANEQ(pd->src, + &nk->addr[pd->sidx], AF_INET)) pf_change_a(&saddr->v4.s_addr, pd->ip_sum, - (*state)->gwy.addr.v4.s_addr, 0); - pd->hdr.icmp->icmp_cksum = - pf_cksum_fixup( - pd->hdr.icmp->icmp_cksum, icmpid, - (*state)->gwy.port, 0); - pd->hdr.icmp->icmp_id = - (*state)->gwy.port; - m_copyback(m, off, ICMP_MINLEN, - (caddr_t)pd->hdr.icmp); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - pf_change_a6(saddr, - &pd->hdr.icmp6->icmp6_cksum, - &(*state)->gwy.addr, 0); - m_copyback(m, off, - sizeof(struct icmp6_hdr), - (caddr_t)pd->hdr.icmp6); - break; -#endif /* INET6 */ - } - } else { - switch (pd->af) { -#ifdef INET - case AF_INET: + nk->addr[pd->sidx].v4.s_addr, 0); + + if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], + AF_INET)) pf_change_a(&daddr->v4.s_addr, pd->ip_sum, - (*state)->lan.addr.v4.s_addr, 0); + nk->addr[pd->didx].v4.s_addr, 0); + + if (nk->port[0] != + pd->hdr.icmp->icmp_id) { pd->hdr.icmp->icmp_cksum = pf_cksum_fixup( pd->hdr.icmp->icmp_cksum, icmpid, - (*state)->lan.port, 0); + nk->port[pd->sidx], 0); pd->hdr.icmp->icmp_id = - (*state)->lan.port; - m_copyback(m, off, ICMP_MINLEN, - (caddr_t)pd->hdr.icmp); - break; + nk->port[pd->sidx]; + } + + m_copyback(m, off, ICMP_MINLEN, +#ifdef __FreeBSD__ + (caddr_t) +#endif + pd->hdr.icmp); + break; #endif /* INET */ #ifdef INET6 - case AF_INET6: + case AF_INET6: + if (PF_ANEQ(pd->src, + &nk->addr[pd->sidx], AF_INET6)) + pf_change_a6(saddr, + &pd->hdr.icmp6->icmp6_cksum, + &nk->addr[pd->sidx], 0); + + if (PF_ANEQ(pd->dst, + &nk->addr[pd->didx], AF_INET6)) pf_change_a6(daddr, &pd->hdr.icmp6->icmp6_cksum, - &(*state)->lan.addr, 0); - m_copyback(m, off, - sizeof(struct icmp6_hdr), - (caddr_t)pd->hdr.icmp6); - break; + &nk->addr[pd->didx], 0); + + m_copyback(m, off, + sizeof(struct icmp6_hdr), +#ifdef __FreeBSD__ + (caddr_t) +#endif + pd->hdr.icmp6); + break; #endif /* INET6 */ - } } } - return (PF_PASS); } else { @@ -5497,6 +4989,9 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, */ struct pf_pdesc pd2; +#ifdef __FreeBSD__ + bzero(&pd2, sizeof pd2); +#endif #ifdef INET struct ip h2; #endif /* INET */ @@ -5504,10 +4999,18 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, struct ip6_hdr h2_6; int terminal = 0; #endif /* INET6 */ - int ipoff2 = 0; /* make the compiler happy */ - int off2 = 0; /* make the compiler happy */ +#ifdef __FreeBSD__ + int ipoff2 = 0; + int off2 = 0; +#else + int ipoff2; + int off2; +#endif pd2.af = pd->af; + /* Payload packet is from the opposite direction. */ + pd2.sidx = (direction == PF_IN) ? 1 : 0; + pd2.didx = (direction == PF_IN) ? 0 : 1; switch (pd->af) { #ifdef INET case AF_INET: @@ -5593,10 +5096,6 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, } while (!terminal); break; #endif /* INET6 */ -#ifdef __FreeBSD__ - default: - panic("AF not supported: %d", pd->af); -#endif } switch (pd2.proto) { @@ -5622,19 +5121,16 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, key.af = pd2.af; key.proto = IPPROTO_TCP; - if (direction == PF_IN) { - PF_ACPY(&key.ext.addr, pd2.dst, key.af); - PF_ACPY(&key.gwy.addr, pd2.src, key.af); - key.ext.port = th.th_dport; - key.gwy.port = th.th_sport; - } else { - PF_ACPY(&key.lan.addr, pd2.dst, key.af); - PF_ACPY(&key.ext.addr, pd2.src, key.af); - key.lan.port = th.th_dport; - key.ext.port = th.th_sport; - } + PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); + PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); + key.port[pd2.sidx] = th.th_sport; + key.port[pd2.didx] = th.th_dport; - STATE_LOOKUP(); +#ifdef __FreeBSD__ + STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); +#else + STATE_LOOKUP(kif, &key, direction, *state, m); +#endif if (direction == (*state)->direction) { src = &(*state)->dst; @@ -5660,7 +5156,11 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, if (!((*state)->state_flags & PFSTATE_SLOPPY) && (!SEQ_GEQ(src->seqhi, seq) || !SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)))) { +#ifdef __FreeBSD__ + if (V_pf_status.debug >= PF_DEBUG_MISC) { +#else if (pf_status.debug >= PF_DEBUG_MISC) { +#endif printf("pf: BAD ICMP %d:%d ", icmptype, pd->hdr.icmp->icmp_code); pf_print_host(pd->src, 0, pd->af); @@ -5672,22 +5172,47 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, } REASON_SET(reason, PFRES_BADSTATE); return (PF_DROP); + } else { +#ifdef __FreeBSD__ + if (V_pf_status.debug >= PF_DEBUG_MISC) { +#else + if (pf_status.debug >= PF_DEBUG_MISC) { +#endif + printf("pf: OK ICMP %d:%d ", + icmptype, pd->hdr.icmp->icmp_code); + pf_print_host(pd->src, 0, pd->af); + printf(" -> "); + pf_print_host(pd->dst, 0, pd->af); + printf(" state: "); + pf_print_state(*state); + printf(" seq=%u\n", seq); + } } - if (STATE_TRANSLATE(*state)) { - if (direction == PF_IN) { + /* translate source/destination address, if necessary */ + if ((*state)->key[PF_SK_WIRE] != + (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = + (*state)->key[pd->didx]; + + if (PF_ANEQ(pd2.src, + &nk->addr[pd2.sidx], pd2.af) || + nk->port[pd2.sidx] != th.th_sport) pf_change_icmp(pd2.src, &th.th_sport, - daddr, &(*state)->lan.addr, - (*state)->lan.port, NULL, + daddr, &nk->addr[pd2.sidx], + nk->port[pd2.sidx], NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, pd2.af); - } else { + + if (PF_ANEQ(pd2.dst, + &nk->addr[pd2.didx], pd2.af) || + nk->port[pd2.didx] != th.th_dport) pf_change_icmp(pd2.dst, &th.th_dport, - saddr, &(*state)->gwy.addr, - (*state)->gwy.port, NULL, + NULL, /* XXX Inbound NAT? */ + &nk->addr[pd2.didx], + nk->port[pd2.didx], NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, pd2.af); - } copyback = 1; } @@ -5696,22 +5221,38 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, #ifdef INET case AF_INET: m_copyback(m, off, ICMP_MINLEN, - (caddr_t)pd->hdr.icmp); +#ifdef __FreeBSD__ + (caddr_t) +#endif + pd->hdr.icmp); m_copyback(m, ipoff2, sizeof(h2), - (caddr_t)&h2); +#ifdef __FreeBSD__ + (caddr_t) +#endif + &h2); break; #endif /* INET */ #ifdef INET6 case AF_INET6: m_copyback(m, off, sizeof(struct icmp6_hdr), - (caddr_t)pd->hdr.icmp6); +#ifdef __FreeBSD__ + (caddr_t) +#endif + pd->hdr.icmp6); m_copyback(m, ipoff2, sizeof(h2_6), - (caddr_t)&h2_6); +#ifdef __FreeBSD__ + (caddr_t) +#endif + &h2_6); break; #endif /* INET6 */ } +#ifdef __FreeBSD__ m_copyback(m, off2, 8, (caddr_t)&th); +#else + m_copyback(m, off2, 8, &th); +#endif } return (PF_PASS); @@ -5730,57 +5271,79 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, key.af = pd2.af; key.proto = IPPROTO_UDP; - if (direction == PF_IN) { - PF_ACPY(&key.ext.addr, pd2.dst, key.af); - PF_ACPY(&key.gwy.addr, pd2.src, key.af); - key.ext.port = uh.uh_dport; - key.gwy.port = uh.uh_sport; - } else { - PF_ACPY(&key.lan.addr, pd2.dst, key.af); - PF_ACPY(&key.ext.addr, pd2.src, key.af); - key.lan.port = uh.uh_dport; - key.ext.port = uh.uh_sport; - } + PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); + PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); + key.port[pd2.sidx] = uh.uh_sport; + key.port[pd2.didx] = uh.uh_dport; - STATE_LOOKUP(); +#ifdef __FreeBSD__ + STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); +#else + STATE_LOOKUP(kif, &key, direction, *state, m); +#endif - if (STATE_TRANSLATE(*state)) { - if (direction == PF_IN) { + /* translate source/destination address, if necessary */ + if ((*state)->key[PF_SK_WIRE] != + (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = + (*state)->key[pd->didx]; + + if (PF_ANEQ(pd2.src, + &nk->addr[pd2.sidx], pd2.af) || + nk->port[pd2.sidx] != uh.uh_sport) pf_change_icmp(pd2.src, &uh.uh_sport, - daddr, &(*state)->lan.addr, - (*state)->lan.port, &uh.uh_sum, + daddr, &nk->addr[pd2.sidx], + nk->port[pd2.sidx], &uh.uh_sum, pd2.ip_sum, icmpsum, pd->ip_sum, 1, pd2.af); - } else { + + if (PF_ANEQ(pd2.dst, + &nk->addr[pd2.didx], pd2.af) || + nk->port[pd2.didx] != uh.uh_dport) pf_change_icmp(pd2.dst, &uh.uh_dport, - saddr, &(*state)->gwy.addr, - (*state)->gwy.port, &uh.uh_sum, + NULL, /* XXX Inbound NAT? */ + &nk->addr[pd2.didx], + nk->port[pd2.didx], &uh.uh_sum, pd2.ip_sum, icmpsum, pd->ip_sum, 1, pd2.af); - } + switch (pd2.af) { #ifdef INET case AF_INET: m_copyback(m, off, ICMP_MINLEN, - (caddr_t)pd->hdr.icmp); - m_copyback(m, ipoff2, sizeof(h2), - (caddr_t)&h2); +#ifdef __FreeBSD__ + (caddr_t) +#endif + pd->hdr.icmp); +#ifdef __FreeBSD__ + m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); +#else + m_copyback(m, ipoff2, sizeof(h2), &h2); +#endif break; #endif /* INET */ #ifdef INET6 case AF_INET6: m_copyback(m, off, sizeof(struct icmp6_hdr), - (caddr_t)pd->hdr.icmp6); +#ifdef __FreeBSD__ + (caddr_t) +#endif + pd->hdr.icmp6); m_copyback(m, ipoff2, sizeof(h2_6), - (caddr_t)&h2_6); +#ifdef __FreeBSD__ + (caddr_t) +#endif + &h2_6); break; #endif /* INET6 */ } - m_copyback(m, off2, sizeof(uh), - (caddr_t)&uh); +#ifdef __FreeBSD__ + m_copyback(m, off2, sizeof(uh), (caddr_t)&uh); +#else + m_copyback(m, off2, sizeof(uh), &uh); +#endif } - return (PF_PASS); break; } @@ -5798,42 +5361,51 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, key.af = pd2.af; key.proto = IPPROTO_ICMP; - if (direction == PF_IN) { - PF_ACPY(&key.ext.addr, pd2.dst, key.af); - PF_ACPY(&key.gwy.addr, pd2.src, key.af); - key.ext.port = 0; - key.gwy.port = iih.icmp_id; - } else { - PF_ACPY(&key.lan.addr, pd2.dst, key.af); - PF_ACPY(&key.ext.addr, pd2.src, key.af); - key.lan.port = iih.icmp_id; - key.ext.port = 0; - } + PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); + PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); + key.port[0] = key.port[1] = iih.icmp_id; - STATE_LOOKUP(); +#ifdef __FreeBSD__ + STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); +#else + STATE_LOOKUP(kif, &key, direction, *state, m); +#endif - if (STATE_TRANSLATE(*state)) { - if (direction == PF_IN) { + /* translate source/destination address, if necessary */ + if ((*state)->key[PF_SK_WIRE] != + (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = + (*state)->key[pd->didx]; + + if (PF_ANEQ(pd2.src, + &nk->addr[pd2.sidx], pd2.af) || + nk->port[pd2.sidx] != iih.icmp_id) pf_change_icmp(pd2.src, &iih.icmp_id, - daddr, &(*state)->lan.addr, - (*state)->lan.port, NULL, + daddr, &nk->addr[pd2.sidx], + nk->port[pd2.sidx], NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, AF_INET); - } else { + + if (PF_ANEQ(pd2.dst, + &nk->addr[pd2.didx], pd2.af) || + nk->port[pd2.didx] != iih.icmp_id) pf_change_icmp(pd2.dst, &iih.icmp_id, - saddr, &(*state)->gwy.addr, - (*state)->gwy.port, NULL, + NULL, /* XXX Inbound NAT? */ + &nk->addr[pd2.didx], + nk->port[pd2.didx], NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, AF_INET); - } - m_copyback(m, off, ICMP_MINLEN, - (caddr_t)pd->hdr.icmp); - m_copyback(m, ipoff2, sizeof(h2), - (caddr_t)&h2); - m_copyback(m, off2, ICMP_MINLEN, - (caddr_t)&iih); - } +#ifdef __FreeBSD__ + m_copyback(m, off, ICMP_MINLEN, (caddr_t)pd->hdr.icmp); + m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); + m_copyback(m, off2, ICMP_MINLEN, (caddr_t)&iih); +#else + m_copyback(m, off, ICMP_MINLEN, pd->hdr.icmp); + m_copyback(m, ipoff2, sizeof(h2), &h2); + m_copyback(m, off2, ICMP_MINLEN, &iih); +#endif + } return (PF_PASS); break; } @@ -5852,42 +5424,55 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, key.af = pd2.af; key.proto = IPPROTO_ICMPV6; - if (direction == PF_IN) { - PF_ACPY(&key.ext.addr, pd2.dst, key.af); - PF_ACPY(&key.gwy.addr, pd2.src, key.af); - key.ext.port = 0; - key.gwy.port = iih.icmp6_id; - } else { - PF_ACPY(&key.lan.addr, pd2.dst, key.af); - PF_ACPY(&key.ext.addr, pd2.src, key.af); - key.lan.port = iih.icmp6_id; - key.ext.port = 0; - } + PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); + PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); + key.port[0] = key.port[1] = iih.icmp6_id; - STATE_LOOKUP(); +#ifdef __FreeBSD__ + STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); +#else + STATE_LOOKUP(kif, &key, direction, *state, m); +#endif + + /* translate source/destination address, if necessary */ + if ((*state)->key[PF_SK_WIRE] != + (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = + (*state)->key[pd->didx]; - if (STATE_TRANSLATE(*state)) { - if (direction == PF_IN) { + if (PF_ANEQ(pd2.src, + &nk->addr[pd2.sidx], pd2.af) || + nk->port[pd2.sidx] != iih.icmp6_id) pf_change_icmp(pd2.src, &iih.icmp6_id, - daddr, &(*state)->lan.addr, - (*state)->lan.port, NULL, + daddr, &nk->addr[pd2.sidx], + nk->port[pd2.sidx], NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, AF_INET6); - } else { + + if (PF_ANEQ(pd2.dst, + &nk->addr[pd2.didx], pd2.af) || + nk->port[pd2.didx] != iih.icmp6_id) pf_change_icmp(pd2.dst, &iih.icmp6_id, - saddr, &(*state)->gwy.addr, - (*state)->gwy.port, NULL, + NULL, /* XXX Inbound NAT? */ + &nk->addr[pd2.didx], + nk->port[pd2.didx], NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, AF_INET6); - } + +#ifdef __FreeBSD__ m_copyback(m, off, sizeof(struct icmp6_hdr), (caddr_t)pd->hdr.icmp6); - m_copyback(m, ipoff2, sizeof(h2_6), - (caddr_t)&h2_6); + m_copyback(m, ipoff2, sizeof(h2_6), (caddr_t)&h2_6); m_copyback(m, off2, sizeof(struct icmp6_hdr), (caddr_t)&iih); +#else + m_copyback(m, off, sizeof(struct icmp6_hdr), + pd->hdr.icmp6); + m_copyback(m, ipoff2, sizeof(h2_6), &h2_6); + m_copyback(m, off2, sizeof(struct icmp6_hdr), + &iih); +#endif } - return (PF_PASS); break; } @@ -5895,55 +5480,68 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, default: { key.af = pd2.af; key.proto = pd2.proto; - if (direction == PF_IN) { - PF_ACPY(&key.ext.addr, pd2.dst, key.af); - PF_ACPY(&key.gwy.addr, pd2.src, key.af); - key.ext.port = 0; - key.gwy.port = 0; - } else { - PF_ACPY(&key.lan.addr, pd2.dst, key.af); - PF_ACPY(&key.ext.addr, pd2.src, key.af); - key.lan.port = 0; - key.ext.port = 0; - } + PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); + PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); + key.port[0] = key.port[1] = 0; + +#ifdef __FreeBSD__ + STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); +#else + STATE_LOOKUP(kif, &key, direction, *state, m); +#endif - STATE_LOOKUP(); + /* translate source/destination address, if necessary */ + if ((*state)->key[PF_SK_WIRE] != + (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = + (*state)->key[pd->didx]; - if (STATE_TRANSLATE(*state)) { - if (direction == PF_IN) { - pf_change_icmp(pd2.src, NULL, - daddr, &(*state)->lan.addr, - 0, NULL, + if (PF_ANEQ(pd2.src, + &nk->addr[pd2.sidx], pd2.af)) + pf_change_icmp(pd2.src, NULL, daddr, + &nk->addr[pd2.sidx], 0, NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, pd2.af); - } else { - pf_change_icmp(pd2.dst, NULL, - saddr, &(*state)->gwy.addr, - 0, NULL, + + if (PF_ANEQ(pd2.dst, + &nk->addr[pd2.didx], pd2.af)) + pf_change_icmp(pd2.src, NULL, + NULL, /* XXX Inbound NAT? */ + &nk->addr[pd2.didx], 0, NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, pd2.af); - } + switch (pd2.af) { #ifdef INET case AF_INET: +#ifdef __FreeBSD__ m_copyback(m, off, ICMP_MINLEN, (caddr_t)pd->hdr.icmp); - m_copyback(m, ipoff2, sizeof(h2), - (caddr_t)&h2); + m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); +#else + m_copyback(m, off, ICMP_MINLEN, + pd->hdr.icmp); + m_copyback(m, ipoff2, sizeof(h2), &h2); +#endif break; #endif /* INET */ #ifdef INET6 case AF_INET6: m_copyback(m, off, sizeof(struct icmp6_hdr), - (caddr_t)pd->hdr.icmp6); +#ifdef __FreeBSD__ + (caddr_t) +#endif + pd->hdr.icmp6); m_copyback(m, ipoff2, sizeof(h2_6), - (caddr_t)&h2_6); +#ifdef __FreeBSD__ + (caddr_t) +#endif + &h2_6); break; #endif /* INET6 */ } } - return (PF_PASS); break; } @@ -5953,26 +5551,28 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, int pf_test_state_other(struct pf_state **state, int direction, struct pfi_kif *kif, - struct pf_pdesc *pd) + struct mbuf *m, struct pf_pdesc *pd) { struct pf_state_peer *src, *dst; - struct pf_state_cmp key; + struct pf_state_key_cmp key; key.af = pd->af; key.proto = pd->proto; if (direction == PF_IN) { - PF_ACPY(&key.ext.addr, pd->src, key.af); - PF_ACPY(&key.gwy.addr, pd->dst, key.af); - key.ext.port = 0; - key.gwy.port = 0; + PF_ACPY(&key.addr[0], pd->src, key.af); + PF_ACPY(&key.addr[1], pd->dst, key.af); + key.port[0] = key.port[1] = 0; } else { - PF_ACPY(&key.lan.addr, pd->src, key.af); - PF_ACPY(&key.ext.addr, pd->dst, key.af); - key.lan.port = 0; - key.ext.port = 0; + PF_ACPY(&key.addr[1], pd->src, key.af); + PF_ACPY(&key.addr[0], pd->dst, key.af); + key.port[1] = key.port[0] = 0; } - STATE_LOOKUP(); +#ifdef __FreeBSD__ + STATE_LOOKUP(kif, &key, direction, *state, m, pd->pf_mtag); +#else + STATE_LOOKUP(kif, &key, direction, *state, m); +#endif if (direction == (*state)->direction) { src = &(*state)->src; @@ -5996,39 +5596,48 @@ pf_test_state_other(struct pf_state **state, int direction, struct pfi_kif *kif, (*state)->timeout = PFTM_OTHER_SINGLE; /* translate source/destination address, if necessary */ - if (STATE_TRANSLATE(*state)) { - if (direction == PF_OUT) - switch (pd->af) { + if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { + struct pf_state_key *nk = (*state)->key[pd->didx]; + +#ifdef __FreeBSD__ + KASSERT(nk, ("%s: nk is null", __FUNCTION__)); + KASSERT(pd, ("%s: pd is null", __FUNCTION__)); + KASSERT(pd->src, ("%s: pd->src is null", __FUNCTION__)); + KASSERT(pd->dst, ("%s: pd->dst is null", __FUNCTION__)); +#else + KASSERT(nk); + KASSERT(pd); + KASSERT(pd->src); + KASSERT(pd->dst); +#endif + switch (pd->af) { #ifdef INET - case AF_INET: + case AF_INET: + if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], AF_INET)) pf_change_a(&pd->src->v4.s_addr, - pd->ip_sum, (*state)->gwy.addr.v4.s_addr, + pd->ip_sum, + nk->addr[pd->sidx].v4.s_addr, 0); - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - PF_ACPY(pd->src, &(*state)->gwy.addr, pd->af); - break; -#endif /* INET6 */ - } - else - switch (pd->af) { -#ifdef INET - case AF_INET: + + + if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], AF_INET)) pf_change_a(&pd->dst->v4.s_addr, - pd->ip_sum, (*state)->lan.addr.v4.s_addr, + pd->ip_sum, + nk->addr[pd->didx].v4.s_addr, 0); + break; #endif /* INET */ #ifdef INET6 - case AF_INET6: - PF_ACPY(pd->dst, &(*state)->lan.addr, pd->af); - break; + case AF_INET6: + if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], AF_INET)) + PF_ACPY(pd->src, &nk->addr[pd->sidx], pd->af); + + if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], AF_INET)) + PF_ACPY(pd->dst, &nk->addr[pd->didx], pd->af); #endif /* INET6 */ - } + } } - return (PF_PASS); } @@ -6086,6 +5695,11 @@ pf_pull_hdr(struct mbuf *m, int off, void *p, int len, int pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kif *kif) { +#ifdef __FreeBSD__ +#ifdef RADIX_MPATH + struct radix_node_head *rnh; +#endif +#endif struct sockaddr_in *dst; int ret = 1; int check_mpath; @@ -6106,6 +5720,14 @@ pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kif *kif) struct ifnet *ifp; check_mpath = 0; +#ifdef __FreeBSD__ +#ifdef RADIX_MPATH + /* XXX: stick to table 0 for now */ + rnh = rt_tables_get_rnh(0, af); + if (rnh != NULL && rn_mpath_capable(rnh)) + check_mpath = 1; +#endif +#endif bzero(&ro, sizeof(ro)); switch (af) { case AF_INET: @@ -6113,18 +5735,24 @@ pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kif *kif) dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr = addr->v4; -#ifndef __FreeBSD__ /* MULTIPATH_ROUTING */ +#ifndef __FreeBSD__ if (ipmultipath) check_mpath = 1; #endif break; #ifdef INET6 case AF_INET6: + /* + * Skip check for addresses with embedded interface scope, + * as they would always match anyway. + */ + if (IN6_IS_SCOPE_EMBED(&addr->v6)) + goto out; dst6 = (struct sockaddr_in6 *)&ro.ro_dst; dst6->sin6_family = AF_INET6; dst6->sin6_len = sizeof(*dst6); dst6->sin6_addr = addr->v6; -#ifndef __FreeBSD__ /* MULTIPATH_ROUTING */ +#ifndef __FreeBSD__ if (ip6_multipath) check_mpath = 1; #endif @@ -6174,11 +5802,13 @@ pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kif *kif) if (kif->pfik_ifp == ifp) ret = 1; -#ifdef __FreeBSD__ /* MULTIPATH_ROUTING */ - rn = NULL; -#else +#ifdef __FreeBSD__ +#ifdef RADIX_MPATH rn = rn_mpath_next(rn); #endif +#else + rn = rn_mpath_next(rn, 0); +#endif } while (check_mpath == 1 && rn != NULL && ret == 0); } else ret = 0; @@ -6249,7 +5879,6 @@ pf_rtlabel_match(struct pf_addr *addr, sa_family_t af, struct pf_addr_wrap *aw) } #ifdef INET - void pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, struct pf_state *s, struct pf_pdesc *pd) @@ -6274,7 +5903,11 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, (dir != PF_IN && dir != PF_OUT) || oifp == NULL) panic("pf_route: invalid parameters"); +#ifdef __FreeBSD__ if (pd->pf_mtag->routed++ > 3) { +#else + if ((*m)->m_pkthdr.pf.routed++ > 3) { +#endif m0 = *m; *m = NULL; goto bad; @@ -6309,9 +5942,17 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, dst->sin_addr = ip->ip_dst; if (r->rt == PF_FASTROUTE) { +#ifdef __FreeBSD__ in_rtalloc(ro, 0); +#else + rtalloc(ro); +#endif if (ro->ro_rt == 0) { +#ifdef __FreeBSD__ KMOD_IPSTAT_INC(ips_noroute); +#else + ipstat.ips_noroute++; +#endif goto bad; } @@ -6377,7 +6018,7 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, * XXX: in_delayed_cksum assumes HBO for ip->ip_len (at least) */ NTOHS(ip->ip_len); - NTOHS(ip->ip_off); /* XXX: needed? */ + NTOHS(ip->ip_off); /* XXX: needed? */ in_delayed_cksum(m0); HTONS(ip->ip_len); HTONS(ip->ip_off); @@ -6386,9 +6027,8 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, m0->m_pkthdr.csum_flags &= ifp->if_hwassist; if (ntohs(ip->ip_len) <= ifp->if_mtu || - (m0->m_pkthdr.csum_flags & ifp->if_hwassist & CSUM_TSO) != 0 || (ifp->if_hwassist & CSUM_FRAGMENT && - ((ip->ip_off & htons(IP_DF)) == 0))) { + ((ip->ip_off & htons(IP_DF)) == 0))) { /* * ip->ip_len = htons(ip->ip_len); * ip->ip_off = htons(ip->ip_off); @@ -6408,7 +6048,6 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, PF_LOCK(); goto done; } - #else /* Copied from ip_output. */ #ifdef IPSEC @@ -6429,25 +6068,28 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, if (!(ifp->if_capabilities & IFCAP_CSUM_TCPv4) || ifp->if_bridge != NULL) { in_delayed_cksum(m0); - m0->m_pkthdr.csum_flags &= ~M_TCPV4_CSUM_OUT; /* Clear */ + m0->m_pkthdr.csum_flags &= ~M_TCPV4_CSUM_OUT; /* Clr */ } } else if (m0->m_pkthdr.csum_flags & M_UDPV4_CSUM_OUT) { if (!(ifp->if_capabilities & IFCAP_CSUM_UDPv4) || ifp->if_bridge != NULL) { in_delayed_cksum(m0); - m0->m_pkthdr.csum_flags &= ~M_UDPV4_CSUM_OUT; /* Clear */ + m0->m_pkthdr.csum_flags &= ~M_UDPV4_CSUM_OUT; /* Clr */ } } if (ntohs(ip->ip_len) <= ifp->if_mtu) { + ip->ip_sum = 0; if ((ifp->if_capabilities & IFCAP_CSUM_IPv4) && ifp->if_bridge == NULL) { m0->m_pkthdr.csum_flags |= M_IPV4_CSUM_OUT; +#ifdef __FreeBSD__ KMOD_IPSTAT_INC(ips_outhwcsum); - } else { - ip->ip_sum = 0; +#else + ipstat.ips_outhwcsum++; +#endif + } else ip->ip_sum = in_cksum(m0, ip->ip_hl << 2); - } /* Update relevant hardware checksum stats for TCP/UDP */ if (m0->m_pkthdr.csum_flags & M_TCPV4_CSUM_OUT) KMOD_TCPSTAT_INC(tcps_outhwcsum); @@ -6457,12 +6099,17 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, goto done; } #endif + /* * Too large for interface; fragment if possible. * Must be able to put at least 8 bytes per fragment. */ - if (ip->ip_off & htons(IP_DF) || (m0->m_pkthdr.csum_flags & CSUM_TSO)) { + if (ip->ip_off & htons(IP_DF)) { +#ifdef __FreeBSD__ KMOD_IPSTAT_INC(ips_cantfrag); +#else + ipstat.ips_cantfrag++; +#endif if (r->rt != PF_DUPTO) { #ifdef __FreeBSD__ /* icmp_error() expects host byte ordering */ @@ -6493,7 +6140,7 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, error = ip_fragment(m0, ifp, ifp->if_mtu); #endif if (error) { -#ifndef __FreeBSD__ /* ip_fragment does not do m_freem() on FreeBSD */ +#ifndef __FreeBSD__ /* ip_fragment does not do m_freem() on FreeBSD */ m0 = NULL; #endif goto bad; @@ -6519,7 +6166,11 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, } if (error == 0) +#ifdef __FreeBSD__ KMOD_IPSTAT_INC(ips_fragmented); +#else + ipstat.ips_fragmented++; +#endif done: if (r->rt != PF_DUPTO) @@ -6547,13 +6198,16 @@ pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, struct ifnet *ifp = NULL; struct pf_addr naddr; struct pf_src_node *sn = NULL; - int error = 0; if (m == NULL || *m == NULL || r == NULL || (dir != PF_IN && dir != PF_OUT) || oifp == NULL) panic("pf_route6: invalid parameters"); +#ifdef __FreeBSD__ if (pd->pf_mtag->routed++ > 3) { +#else + if ((*m)->m_pkthdr.pf.routed++ > 3) { +#endif m0 = *m; *m = NULL; goto bad; @@ -6592,14 +6246,9 @@ pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, m0->m_flags |= M_SKIP_FIREWALL; PF_UNLOCK(); ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL); - PF_LOCK(); #else - mtag = m_tag_get(PACKET_TAG_PF_GENERATED, 0, M_NOWAIT); - if (mtag == NULL) - goto bad; - m_tag_prepend(m0, mtag); - pd->pf_mtag->flags |= PF_TAG_GENERATED; - ip6_output(m0, NULL, NULL, 0, NULL, NULL); + m0->m_pkthdr.pf.flags |= PF_TAG_GENERATED; + ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL); #endif return; } @@ -6660,7 +6309,7 @@ pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, #ifdef __FreeBSD__ PF_UNLOCK(); #endif - error = nd6_output(ifp, ifp, m0, dst, NULL); + nd6_output(ifp, ifp, m0, dst, NULL); #ifdef __FreeBSD__ PF_LOCK(); #endif @@ -6671,7 +6320,7 @@ pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, PF_UNLOCK(); icmp6_error(m0, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu); PF_LOCK(); - } else + } else #else if (r->rt != PF_DUPTO) icmp6_error(m0, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu); @@ -6691,7 +6340,6 @@ bad: } #endif /* INET6 */ - #ifdef __FreeBSD__ /* * FreeBSD supports cksum offloads for the following drivers. @@ -6729,10 +6377,10 @@ pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, sa_family_t a if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) { sum = m->m_pkthdr.csum_data; } else { - ip = mtod(m, struct ip *); + ip = mtod(m, struct ip *); sum = in_pseudo(ip->ip_src.s_addr, - ip->ip_dst.s_addr, htonl((u_short)len + - m->m_pkthdr.csum_data + IPPROTO_TCP)); + ip->ip_dst.s_addr, htonl((u_short)len + + m->m_pkthdr.csum_data + IPPROTO_TCP)); } sum ^= 0xffff; ++hw_assist; @@ -6743,14 +6391,14 @@ pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, sa_family_t a if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) { sum = m->m_pkthdr.csum_data; } else { - ip = mtod(m, struct ip *); + ip = mtod(m, struct ip *); sum = in_pseudo(ip->ip_src.s_addr, - ip->ip_dst.s_addr, htonl((u_short)len + - m->m_pkthdr.csum_data + IPPROTO_UDP)); + ip->ip_dst.s_addr, htonl((u_short)len + + m->m_pkthdr.csum_data + IPPROTO_UDP)); } sum ^= 0xffff; ++hw_assist; - } + } break; case IPPROTO_ICMP: #ifdef INET6 @@ -6827,6 +6475,7 @@ pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, sa_family_t a return (0); } #else /* !__FreeBSD__ */ + /* * check protocol (tcp/udp/icmp/icmp6) checksum and set mbuf flag * off is the offset where the protocol header starts @@ -6919,7 +6568,37 @@ pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, m->m_pkthdr.csum_flags |= flag_ok; return (0); } -#endif /* __FreeBSD__ */ +#endif + +#ifndef __FreeBSD__ +struct pf_divert * +pf_find_divert(struct mbuf *m) +{ + struct m_tag *mtag; + + if ((mtag = m_tag_find(m, PACKET_TAG_PF_DIVERT, NULL)) == NULL) + return (NULL); + + return ((struct pf_divert *)(mtag + 1)); +} + +struct pf_divert * +pf_get_divert(struct mbuf *m) +{ + struct m_tag *mtag; + + if ((mtag = m_tag_find(m, PACKET_TAG_PF_DIVERT, NULL)) == NULL) { + mtag = m_tag_get(PACKET_TAG_PF_DIVERT, sizeof(struct pf_divert), + M_NOWAIT); + if (mtag == NULL) + return (NULL); + bzero(mtag + 1, sizeof(struct pf_divert)); + m_tag_prepend(m, mtag); + } + + return ((struct pf_divert *)(mtag + 1)); +} +#endif #ifdef INET int @@ -6934,8 +6613,14 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct pfi_kif *kif; u_short action, reason = 0, log = 0; struct mbuf *m = *m0; - struct ip *h = NULL; /* make the compiler happy */ +#ifdef __FreeBSD__ + struct ip *h = NULL; + struct m_tag *ipfwtag; + struct pf_rule *a = NULL, *r = &V_pf_default_rule, *tr, *nr; +#else + struct ip *h; struct pf_rule *a = NULL, *r = &pf_default_rule, *tr, *nr; +#endif struct pf_state *s = NULL; struct pf_ruleset *ruleset = NULL; struct pf_pdesc pd; @@ -6943,44 +6628,32 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, #ifdef __FreeBSD__ PF_LOCK(); -#endif - if (!pf_status.running) -#ifdef __FreeBSD__ + if (!V_pf_status.running) { PF_UNLOCK(); -#endif return (PF_PASS); -#ifdef __FreeBSD__ } +#else + if (!pf_status.running) + return (PF_PASS); #endif memset(&pd, 0, sizeof(pd)); - if ((pd.pf_mtag = pf_get_mtag(m)) == NULL) { #ifdef __FreeBSD__ + if ((pd.pf_mtag = pf_get_mtag(m)) == NULL) { PF_UNLOCK(); -#endif DPFPRINTF(PF_DEBUG_URGENT, ("pf_test: pf_get_mtag returned NULL\n")); return (PF_DROP); } -#ifdef __FreeBSD__ - if (m->m_flags & M_SKIP_FIREWALL) { - PF_UNLOCK(); - return (PF_PASS); - } -#else - if (pd.pf_mtag->flags & PF_TAG_GENERATED) - return (PF_PASS); #endif - -#ifdef __FreeBSD__ - /* XXX_IMPORT: later */ -#else +#ifndef __FreeBSD__ if (ifp->if_type == IFT_CARP && ifp->if_carpdev) - ifp = ifp->if_carpdev; + kif = (struct pfi_kif *)ifp->if_carpdev->if_pf_kif; + else #endif + kif = (struct pfi_kif *)ifp->if_pf_kif; - kif = (struct pfi_kif *)ifp->if_pf_kif; if (kif == NULL) { #ifdef __FreeBSD__ PF_UNLOCK(); @@ -6989,12 +6662,15 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, ("pf_test: kif == NULL, if_xname %s\n", ifp->if_xname)); return (PF_DROP); } - if (kif->pfik_flags & PFI_IFLAG_SKIP) { + if (kif->pfik_flags & PFI_IFLAG_SKIP) #ifdef __FreeBSD__ + { PF_UNLOCK(); #endif return (PF_PASS); +#ifdef __FreeBSD__ } +#endif #ifdef __FreeBSD__ M_ASSERTPKTHDR(m); @@ -7003,7 +6679,7 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, if ((m->m_flags & M_PKTHDR) == 0) panic("non-M_PKTHDR is passed to pf_test"); #endif /* DIAGNOSTIC */ -#endif /* __FreeBSD__ */ +#endif if (m->m_pkthdr.len < (int)sizeof(*h)) { action = PF_DROP; @@ -7012,12 +6688,36 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, goto done; } +#ifdef __FreeBSD__ + if (m->m_flags & M_SKIP_FIREWALL) { + PF_UNLOCK(); + return (PF_PASS); + } +#else + if (m->m_pkthdr.pf.flags & PF_TAG_GENERATED) + return (PF_PASS); +#endif + +#ifdef __FreeBSD__ + if (ip_divert_ptr != NULL && + ((ipfwtag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL)) != NULL)) { + struct ipfw_rule_ref *rr = (struct ipfw_rule_ref *)(ipfwtag+1); + if (rr->info & IPFW_IS_DIVERT && rr->rulenum == 0) { + pd.pf_mtag->flags |= PF_PACKET_LOOPED; + m_tag_delete(m, ipfwtag); + } + if (pd.pf_mtag->flags & PF_FASTFWD_OURS_PRESENT) { + m->m_flags |= M_FASTFWD_OURS; + pd.pf_mtag->flags &= ~PF_FASTFWD_OURS_PRESENT; + } + } else +#endif /* We do IP header normalization and packet reassembly here */ if (pf_normalize_ip(m0, dir, kif, &reason, &pd) != PF_PASS) { action = PF_DROP; goto done; } - m = *m0; + m = *m0; /* pf_normalize messes with m0 */ h = mtod(m, struct ip *); off = h->ip_hl << 2; @@ -7030,9 +6730,13 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, pd.src = (struct pf_addr *)&h->ip_src; pd.dst = (struct pf_addr *)&h->ip_dst; - PF_ACPY(&pd.baddr, dir == PF_OUT ? pd.src : pd.dst, AF_INET); + pd.sport = pd.dport = NULL; pd.ip_sum = &h->ip_sum; + pd.proto_sum = NULL; pd.proto = h->ip_p; + pd.dir = dir; + pd.sidx = (dir == PF_IN) ? 0 : 1; + pd.didx = (dir == PF_IN) ? 1 : 0; pd.af = AF_INET; pd.tos = h->ip_tos; pd.tot_len = ntohs(h->ip_len); @@ -7056,12 +6760,6 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, log = action != PF_PASS; goto done; } - if (dir == PF_IN && pf_check_proto_cksum(m, off, - ntohs(h->ip_len) - off, IPPROTO_TCP, AF_INET)) { - REASON_SET(&reason, PFRES_PROTCKSUM); - action = PF_DROP; - goto done; - } pd.p_len = pd.tot_len - off - (th.th_off << 2); if ((th.th_flags & TH_ACK) && pd.p_len == 0) pqid = 1; @@ -7071,18 +6769,23 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, action = pf_test_state_tcp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { -#if NPFSYNC +#if NPFSYNC > 0 +#ifdef __FreeBSD__ + if (pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); +#else pfsync_update_state(s); +#endif #endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) #ifdef __FreeBSD__ - action = pf_test_tcp(&r, &s, dir, kif, + action = pf_test_rule(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, NULL, inp); #else - action = pf_test_tcp(&r, &s, dir, kif, + action = pf_test_rule(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, &ipintrq); #endif break; @@ -7097,12 +6800,6 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, log = action != PF_PASS; goto done; } - if (dir == PF_IN && uh.uh_sum && pf_check_proto_cksum(m, - off, ntohs(h->ip_len) - off, IPPROTO_UDP, AF_INET)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_PROTCKSUM); - goto done; - } if (uh.uh_dport == 0 || ntohs(uh.uh_ulen) > m->m_pkthdr.len - off || ntohs(uh.uh_ulen) < sizeof(struct udphdr)) { @@ -7112,18 +6809,23 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, } action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd); if (action == PF_PASS) { -#if NPFSYNC +#if NPFSYNC > 0 +#ifdef __FreeBSD__ + if (pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); +#else pfsync_update_state(s); +#endif #endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) #ifdef __FreeBSD__ - action = pf_test_udp(&r, &s, dir, kif, + action = pf_test_rule(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, NULL, inp); #else - action = pf_test_udp(&r, &s, dir, kif, + action = pf_test_rule(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, &ipintrq); #endif break; @@ -7138,47 +6840,60 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, log = action != PF_PASS; goto done; } - if (dir == PF_IN && pf_check_proto_cksum(m, off, - ntohs(h->ip_len) - off, IPPROTO_ICMP, AF_INET)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_PROTCKSUM); - goto done; - } action = pf_test_state_icmp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { -#if NPFSYNC +#if NPFSYNC > 0 +#ifdef __FreeBSD__ + if (pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); +#else pfsync_update_state(s); +#endif #endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) #ifdef __FreeBSD__ - action = pf_test_icmp(&r, &s, dir, kif, - m, off, h, &pd, &a, &ruleset, NULL); + action = pf_test_rule(&r, &s, dir, kif, + m, off, h, &pd, &a, &ruleset, NULL, inp); #else - action = pf_test_icmp(&r, &s, dir, kif, + action = pf_test_rule(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, &ipintrq); #endif break; } +#ifdef INET6 + case IPPROTO_ICMPV6: { + action = PF_DROP; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: dropping IPv4 packet with ICMPv6 payload\n")); + goto done; + } +#endif + default: - action = pf_test_state_other(&s, dir, kif, &pd); + action = pf_test_state_other(&s, dir, kif, m, &pd); if (action == PF_PASS) { -#if NPFSYNC +#if NPFSYNC > 0 +#ifdef __FreeBSD__ + if (pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); +#else pfsync_update_state(s); +#endif #endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) #ifdef __FreeBSD__ - action = pf_test_other(&r, &s, dir, kif, m, off, h, - &pd, &a, &ruleset, NULL); + action = pf_test_rule(&r, &s, dir, kif, m, off, h, + &pd, &a, &ruleset, NULL, inp); #else - action = pf_test_other(&r, &s, dir, kif, m, off, h, + action = pf_test_rule(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, &ipintrq); #endif break; @@ -7195,17 +6910,37 @@ done: } if ((s && s->tag) || r->rtableid) - pf_tag_packet(m, pd.pf_mtag, s ? s->tag : 0, r->rtableid); +#ifdef __FreeBSD__ + pf_tag_packet(m, s ? s->tag : 0, r->rtableid, pd.pf_mtag); +#else + pf_tag_packet(m, s ? s->tag : 0, r->rtableid); +#endif + + if (dir == PF_IN && s && s->key[PF_SK_STACK]) +#ifdef __FreeBSD__ + pd.pf_mtag->statekey = s->key[PF_SK_STACK]; +#else + m->m_pkthdr.pf.statekey = s->key[PF_SK_STACK]; +#endif #ifdef ALTQ if (action == PF_PASS && r->qid) { +#ifdef __FreeBSD__ if (pqid || (pd.tos & IPTOS_LOWDELAY)) pd.pf_mtag->qid = r->pqid; else pd.pf_mtag->qid = r->qid; /* add hints for ecn */ - pd.pf_mtag->af = AF_INET; pd.pf_mtag->hdr = h; + +#else + if (pqid || (pd.tos & IPTOS_LOWDELAY)) + m->m_pkthdr.pf.qid = r->pqid; + else + m->m_pkthdr.pf.qid = r->qid; + /* add hints for ecn */ + m->m_pkthdr.pf.hdr = h; +#endif } #endif /* ALTQ */ @@ -7219,7 +6954,55 @@ done: (s->nat_rule.ptr->action == PF_RDR || s->nat_rule.ptr->action == PF_BINAT) && (ntohl(pd.dst->v4.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) - pd.pf_mtag->flags |= PF_TAG_TRANSLATE_LOCALHOST; +#ifdef __FreeBSD__ + m->m_flags |= M_SKIP_FIREWALL; +#else + m->m_pkthdr.pf.flags |= PF_TAG_TRANSLATE_LOCALHOST; +#endif + +#ifdef __FreeBSD__ + if (action == PF_PASS && r->divert.port && + ip_divert_ptr != NULL && !PACKET_LOOPED()) { + + ipfwtag = m_tag_alloc(MTAG_IPFW_RULE, 0, + sizeof(struct ipfw_rule_ref), M_NOWAIT | M_ZERO); + if (ipfwtag != NULL) { + ((struct ipfw_rule_ref *)(ipfwtag+1))->info = r->divert.port; + ((struct ipfw_rule_ref *)(ipfwtag+1))->rulenum = dir; + + m_tag_prepend(m, ipfwtag); + + PF_UNLOCK(); + + if (m->m_flags & M_FASTFWD_OURS) { + pd.pf_mtag->flags |= PF_FASTFWD_OURS_PRESENT; + m->m_flags &= ~M_FASTFWD_OURS; + } + + ip_divert_ptr(*m0, + dir == PF_IN ? DIR_IN : DIR_OUT); + *m0 = NULL; + return (action); + } else { + /* XXX: ipfw has the same behaviour! */ + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: failed to allocate divert tag\n")); + } + } +#else + if (dir == PF_IN && action == PF_PASS && r->divert.port) { + struct pf_divert *divert; + + if ((divert = pf_get_divert(m))) { + m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED; + divert->port = r->divert.port; + divert->addr.ipv4 = r->divert.addr.v4; + } + } +#endif if (log) { struct pf_rule *lr; @@ -7263,53 +7046,44 @@ done: } tr = r; nr = (s != NULL) ? s->nat_rule.ptr : pd.nat_rule; - if (nr != NULL) { - struct pf_addr *x; - /* - * XXX: we need to make sure that the addresses - * passed to pfr_update_stats() are the same than - * the addresses used during matching (pfr_match) - */ - if (r == &pf_default_rule) { - tr = nr; - x = (s == NULL || s->direction == dir) ? - &pd.baddr : &pd.naddr; - } else - x = (s == NULL || s->direction == dir) ? - &pd.naddr : &pd.baddr; - if (x == &pd.baddr || s == NULL) { - /* we need to change the address */ - if (dir == PF_OUT) - pd.src = x; - else - pd.dst = x; - } - } +#ifdef __FreeBSD__ + if (nr != NULL && r == &V_pf_default_rule) +#else + if (nr != NULL && r == &pf_default_rule) +#endif + tr = nr; if (tr->src.addr.type == PF_ADDR_TABLE) - pfr_update_stats(tr->src.addr.p.tbl, (s == NULL || - s->direction == dir) ? pd.src : pd.dst, pd.af, - pd.tot_len, dir == PF_OUT, r->action == PF_PASS, - tr->src.neg); + pfr_update_stats(tr->src.addr.p.tbl, + (s == NULL) ? pd.src : + &s->key[(s->direction == PF_IN)]-> + addr[(s->direction == PF_OUT)], + pd.af, pd.tot_len, dir == PF_OUT, + r->action == PF_PASS, tr->src.neg); if (tr->dst.addr.type == PF_ADDR_TABLE) - pfr_update_stats(tr->dst.addr.p.tbl, (s == NULL || - s->direction == dir) ? pd.dst : pd.src, pd.af, - pd.tot_len, dir == PF_OUT, r->action == PF_PASS, - tr->dst.neg); + pfr_update_stats(tr->dst.addr.p.tbl, + (s == NULL) ? pd.dst : + &s->key[(s->direction == PF_IN)]-> + addr[(s->direction == PF_IN)], + pd.af, pd.tot_len, dir == PF_OUT, + r->action == PF_PASS, tr->dst.neg); } - - if (action == PF_SYNPROXY_DROP) { + switch (action) { + case PF_SYNPROXY_DROP: m_freem(*m0); + case PF_DEFER: *m0 = NULL; action = PF_PASS; - } else if (r->rt) + break; + default: /* pf_route can free the mbuf causing *m0 to become NULL */ - pf_route(m0, r, dir, ifp, s, &pd); - + if (r->rt) + pf_route(m0, r, dir, kif->pfik_ifp, s, &pd); + break; + } #ifdef __FreeBSD__ PF_UNLOCK(); #endif - return (action); } #endif /* INET */ @@ -7327,8 +7101,13 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct pfi_kif *kif; u_short action, reason = 0, log = 0; struct mbuf *m = *m0, *n = NULL; +#ifdef __FreeBSD__ + struct ip6_hdr *h = NULL; + struct pf_rule *a = NULL, *r = &V_pf_default_rule, *tr, *nr; +#else struct ip6_hdr *h; struct pf_rule *a = NULL, *r = &pf_default_rule, *tr, *nr; +#endif struct pf_state *s = NULL; struct pf_ruleset *ruleset = NULL; struct pf_pdesc pd; @@ -7336,38 +7115,31 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, #ifdef __FreeBSD__ PF_LOCK(); -#endif - - if (!pf_status.running) -#ifdef __FreeBSD__ - { + if (!V_pf_status.running) { PF_UNLOCK(); -#endif return (PF_PASS); -#ifdef __FreeBSD__ } +#else + if (!pf_status.running) + return (PF_PASS); #endif memset(&pd, 0, sizeof(pd)); - if ((pd.pf_mtag = pf_get_mtag(m)) == NULL) { #ifdef __FreeBSD__ + if ((pd.pf_mtag = pf_get_mtag(m)) == NULL) { PF_UNLOCK(); -#endif DPFPRINTF(PF_DEBUG_URGENT, - ("pf_test6: pf_get_mtag returned NULL\n")); + ("pf_test: pf_get_mtag returned NULL\n")); return (PF_DROP); } - if (pd.pf_mtag->flags & PF_TAG_GENERATED) - return (PF_PASS); - -#ifdef __FreeBSD__ - /* XXX_IMPORT: later */ -#else +#endif +#ifndef __FreeBSD__ if (ifp->if_type == IFT_CARP && ifp->if_carpdev) - ifp = ifp->if_carpdev; + kif = (struct pfi_kif *)ifp->if_carpdev->if_pf_kif; + else #endif + kif = (struct pfi_kif *)ifp->if_pf_kif; - kif = (struct pfi_kif *)ifp->if_pf_kif; if (kif == NULL) { #ifdef __FreeBSD__ PF_UNLOCK(); @@ -7376,12 +7148,15 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, ("pf_test6: kif == NULL, if_xname %s\n", ifp->if_xname)); return (PF_DROP); } - if (kif->pfik_flags & PFI_IFLAG_SKIP) { + if (kif->pfik_flags & PFI_IFLAG_SKIP) #ifdef __FreeBSD__ + { PF_UNLOCK(); #endif return (PF_PASS); +#ifdef __FreeBSD__ } +#endif #ifdef __FreeBSD__ M_ASSERTPKTHDR(m); @@ -7392,10 +7167,6 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, #endif /* DIAGNOSTIC */ #endif -#ifdef __FreeBSD__ - h = NULL; /* make the compiler happy */ -#endif - if (m->m_pkthdr.len < (int)sizeof(*h)) { action = PF_DROP; REASON_SET(&reason, PFRES_SHORT); @@ -7403,12 +7174,19 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, goto done; } +#ifdef __FreeBSD__ + if (pd.pf_mtag->flags & PF_TAG_GENERATED) +#else + if (m->m_pkthdr.pf.flags & PF_TAG_GENERATED) +#endif + return (PF_PASS); + /* We do IP header normalization and packet reassembly here */ if (pf_normalize_ip6(m0, dir, kif, &reason, &pd) != PF_PASS) { action = PF_DROP; goto done; } - m = *m0; + m = *m0; /* pf_normalize messes with m0 */ h = mtod(m, struct ip6_hdr *); #if 1 @@ -7425,8 +7203,12 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, pd.src = (struct pf_addr *)&h->ip6_src; pd.dst = (struct pf_addr *)&h->ip6_dst; - PF_ACPY(&pd.baddr, dir == PF_OUT ? pd.src : pd.dst, AF_INET6); + pd.sport = pd.dport = NULL; pd.ip_sum = NULL; + pd.proto_sum = NULL; + pd.dir = dir; + pd.sidx = (dir == PF_IN) ? 0 : 1; + pd.didx = (dir == PF_IN) ? 1 : 0; pd.af = AF_INET6; pd.tos = 0; pd.tot_len = ntohs(h->ip6_plen) + sizeof(struct ip6_hdr); @@ -7470,7 +7252,7 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, log = 1; goto done; } - /* fallthrough */ + /* FALLTHROUGH */ } case IPPROTO_AH: case IPPROTO_HOPOPTS: @@ -7515,13 +7297,6 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, log = action != PF_PASS; goto done; } - if (dir == PF_IN && pf_check_proto_cksum(n, off, - ntohs(h->ip6_plen) - (off - sizeof(struct ip6_hdr)), - IPPROTO_TCP, AF_INET6)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_PROTCKSUM); - goto done; - } pd.p_len = pd.tot_len - off - (th.th_off << 2); action = pf_normalize_tcp(dir, kif, m, 0, off, h, &pd); if (action == PF_DROP) @@ -7529,18 +7304,23 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, action = pf_test_state_tcp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { -#if NPFSYNC +#if NPFSYNC > 0 +#ifdef __FreeBSD__ + if (pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); +#else pfsync_update_state(s); +#endif #endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) #ifdef __FreeBSD__ - action = pf_test_tcp(&r, &s, dir, kif, + action = pf_test_rule(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, NULL, inp); #else - action = pf_test_tcp(&r, &s, dir, kif, + action = pf_test_rule(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, &ip6intrq); #endif break; @@ -7555,13 +7335,6 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, log = action != PF_PASS; goto done; } - if (dir == PF_IN && uh.uh_sum && pf_check_proto_cksum(n, - off, ntohs(h->ip6_plen) - (off - sizeof(struct ip6_hdr)), - IPPROTO_UDP, AF_INET6)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_PROTCKSUM); - goto done; - } if (uh.uh_dport == 0 || ntohs(uh.uh_ulen) > m->m_pkthdr.len - off || ntohs(uh.uh_ulen) < sizeof(struct udphdr)) { @@ -7571,23 +7344,35 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, } action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd); if (action == PF_PASS) { -#if NPFSYNC +#if NPFSYNC > 0 +#ifdef __FreeBSD__ + if (pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); +#else pfsync_update_state(s); +#endif #endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) #ifdef __FreeBSD__ - action = pf_test_udp(&r, &s, dir, kif, + action = pf_test_rule(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, NULL, inp); #else - action = pf_test_udp(&r, &s, dir, kif, + action = pf_test_rule(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, &ip6intrq); #endif break; } + case IPPROTO_ICMP: { + action = PF_DROP; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: dropping IPv6 packet with ICMPv4 payload\n")); + goto done; + } + case IPPROTO_ICMPV6: { struct icmp6_hdr ih; @@ -7597,54 +7382,62 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, log = action != PF_PASS; goto done; } - if (dir == PF_IN && pf_check_proto_cksum(n, off, - ntohs(h->ip6_plen) - (off - sizeof(struct ip6_hdr)), - IPPROTO_ICMPV6, AF_INET6)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_PROTCKSUM); - goto done; - } action = pf_test_state_icmp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { -#if NPFSYNC +#if NPFSYNC > 0 +#ifdef __FreeBSD__ + if (pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); +#else pfsync_update_state(s); +#endif #endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) #ifdef __FreeBSD__ - action = pf_test_icmp(&r, &s, dir, kif, - m, off, h, &pd, &a, &ruleset, NULL); + action = pf_test_rule(&r, &s, dir, kif, + m, off, h, &pd, &a, &ruleset, NULL, inp); #else - action = pf_test_icmp(&r, &s, dir, kif, + action = pf_test_rule(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, &ip6intrq); #endif break; } default: - action = pf_test_state_other(&s, dir, kif, &pd); + action = pf_test_state_other(&s, dir, kif, m, &pd); if (action == PF_PASS) { -#if NPFSYNC +#if NPFSYNC > 0 +#ifdef __FreeBSD__ + if (pfsync_update_state_ptr != NULL) + pfsync_update_state_ptr(s); +#else pfsync_update_state(s); +#endif #endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) #ifdef __FreeBSD__ - action = pf_test_other(&r, &s, dir, kif, m, off, h, - &pd, &a, &ruleset, NULL); + action = pf_test_rule(&r, &s, dir, kif, m, off, h, + &pd, &a, &ruleset, NULL, inp); #else - action = pf_test_other(&r, &s, dir, kif, m, off, h, + action = pf_test_rule(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, &ip6intrq); #endif break; } done: + if (n != m) { + m_freem(n); + n = NULL; + } + /* handle dangerous IPv6 extension headers. */ if (action == PF_PASS && rh_cnt && !((s && s->state_flags & PFSTATE_ALLOWOPTS) || r->allow_opts)) { @@ -7656,17 +7449,36 @@ done: } if ((s && s->tag) || r->rtableid) - pf_tag_packet(m, pd.pf_mtag, s ? s->tag : 0, r->rtableid); +#ifdef __FreeBSD__ + pf_tag_packet(m, s ? s->tag : 0, r->rtableid, pd.pf_mtag); +#else + pf_tag_packet(m, s ? s->tag : 0, r->rtableid); +#endif + + if (dir == PF_IN && s && s->key[PF_SK_STACK]) +#ifdef __FreeBSD__ + pd.pf_mtag->statekey = s->key[PF_SK_STACK]; +#else + m->m_pkthdr.pf.statekey = s->key[PF_SK_STACK]; +#endif #ifdef ALTQ if (action == PF_PASS && r->qid) { +#ifdef __FreeBSD__ if (pd.tos & IPTOS_LOWDELAY) pd.pf_mtag->qid = r->pqid; else pd.pf_mtag->qid = r->qid; /* add hints for ecn */ - pd.pf_mtag->af = AF_INET6; pd.pf_mtag->hdr = h; +#else + if (pd.tos & IPTOS_LOWDELAY) + m->m_pkthdr.pf.qid = r->pqid; + else + m->m_pkthdr.pf.qid = r->qid; + /* add hints for ecn */ + m->m_pkthdr.pf.hdr = h; +#endif } #endif /* ALTQ */ @@ -7675,7 +7487,27 @@ done: (s->nat_rule.ptr->action == PF_RDR || s->nat_rule.ptr->action == PF_BINAT) && IN6_IS_ADDR_LOOPBACK(&pd.dst->v6)) - pd.pf_mtag->flags |= PF_TAG_TRANSLATE_LOCALHOST; +#ifdef __FreeBSD__ + m->m_flags |= M_SKIP_FIREWALL; +#else + m->m_pkthdr.pf.flags |= PF_TAG_TRANSLATE_LOCALHOST; +#endif + +#ifdef __FreeBSD__ + /* XXX: Anybody working on it?! */ + if (r->divert.port) + printf("pf: divert(9) is not supported for IPv6\n"); +#else + if (dir == PF_IN && action == PF_PASS && r->divert.port) { + struct pf_divert *divert; + + if ((divert = pf_get_divert(m))) { + m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED; + divert->port = r->divert.port; + divert->addr.ipv6 = r->divert.addr.v6; + } + } +#endif if (log) { struct pf_rule *lr; @@ -7719,48 +7551,39 @@ done: } tr = r; nr = (s != NULL) ? s->nat_rule.ptr : pd.nat_rule; - if (nr != NULL) { - struct pf_addr *x; - /* - * XXX: we need to make sure that the addresses - * passed to pfr_update_stats() are the same than - * the addresses used during matching (pfr_match) - */ - if (r == &pf_default_rule) { - tr = nr; - x = (s == NULL || s->direction == dir) ? - &pd.baddr : &pd.naddr; - } else { - x = (s == NULL || s->direction == dir) ? - &pd.naddr : &pd.baddr; - } - if (x == &pd.baddr || s == NULL) { - if (dir == PF_OUT) - pd.src = x; - else - pd.dst = x; - } - } +#ifdef __FreeBSD__ + if (nr != NULL && r == &V_pf_default_rule) +#else + if (nr != NULL && r == &pf_default_rule) +#endif + tr = nr; if (tr->src.addr.type == PF_ADDR_TABLE) - pfr_update_stats(tr->src.addr.p.tbl, (s == NULL || - s->direction == dir) ? pd.src : pd.dst, pd.af, - pd.tot_len, dir == PF_OUT, r->action == PF_PASS, - tr->src.neg); + pfr_update_stats(tr->src.addr.p.tbl, + (s == NULL) ? pd.src : + &s->key[(s->direction == PF_IN)]->addr[0], + pd.af, pd.tot_len, dir == PF_OUT, + r->action == PF_PASS, tr->src.neg); if (tr->dst.addr.type == PF_ADDR_TABLE) - pfr_update_stats(tr->dst.addr.p.tbl, (s == NULL || - s->direction == dir) ? pd.dst : pd.src, pd.af, - pd.tot_len, dir == PF_OUT, r->action == PF_PASS, - tr->dst.neg); + pfr_update_stats(tr->dst.addr.p.tbl, + (s == NULL) ? pd.dst : + &s->key[(s->direction == PF_IN)]->addr[1], + pd.af, pd.tot_len, dir == PF_OUT, + r->action == PF_PASS, tr->dst.neg); } - - if (action == PF_SYNPROXY_DROP) { + switch (action) { + case PF_SYNPROXY_DROP: m_freem(*m0); + case PF_DEFER: *m0 = NULL; action = PF_PASS; - } else if (r->rt) + break; + default: /* pf_route6 can free the mbuf causing *m0 to become NULL */ - pf_route6(m0, r, dir, ifp, s, &pd); + if (r->rt) + pf_route6(m0, r, dir, kif->pfik_ifp, s, &pd); + break; + } #ifdef __FreeBSD__ PF_UNLOCK(); @@ -7782,3 +7605,20 @@ pf_check_congestion(struct ifqueue *ifq) return (0); #endif } + +/* + * must be called whenever any addressing information such as + * address, port, protocol has changed + */ +void +pf_pkt_addr_changed(struct mbuf *m) +{ +#ifdef __FreeBSD__ + struct pf_mtag *pf_tag; + + if ((pf_tag = pf_find_mtag(m)) != NULL) + pf_tag->statekey = NULL; +#else + m->m_pkthdr.pf.statekey = NULL; +#endif +} |