diff --git a/sys/netpfil/ipfilter/netinet/fil.c b/sys/netpfil/ipfilter/netinet/fil.c index 9addc0f4e00f..51200315b58c 100644 --- a/sys/netpfil/ipfilter/netinet/fil.c +++ b/sys/netpfil/ipfilter/netinet/fil.c @@ -1,10274 +1,9954 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * Copyright 2008 Sun Microsystems. * * $Id$ * */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #if defined(_KERNEL) && defined(__FreeBSD__) # if !defined(IPFILTER_LKM) # include "opt_inet6.h" # endif # include #else # include #endif #if defined(__SVR4) || defined(sun) /* SOLARIS */ # include #endif # include #if defined(_KERNEL) # include # include #else # include # include # include # include # include # define _KERNEL # include # undef _KERNEL #endif #if !defined(__SVR4) # include #else # include # if (SOLARIS2 < 5) && defined(sun) # include # endif #endif # include #include #include #ifdef sun # include #endif #include #include #include #include # include # include #include "netinet/ip_compat.h" #ifdef USE_INET6 # include # if !SOLARIS && defined(_KERNEL) # include # endif #endif #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #include "netinet/ip_auth.h" #ifdef IPFILTER_SCAN # include "netinet/ip_scan.h" #endif #include "netinet/ip_sync.h" #include "netinet/ip_lookup.h" #include "netinet/ip_pool.h" #include "netinet/ip_htable.h" #ifdef IPFILTER_COMPILED # include "netinet/ip_rules.h" #endif #if defined(IPFILTER_BPF) && defined(_KERNEL) # include #endif #if defined(__FreeBSD__) # include #endif #include "netinet/ipl.h" #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000) # include extern struct callout ipf_slowtimer_ch; #endif /* END OF INCLUDES */ #if !defined(lint) static const char sccsid[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-2000 Darren Reed"; static const char rcsid[] = "@(#)$FreeBSD$"; /* static const char rcsid[] = "@(#)$Id: fil.c,v 2.243.2.125 2007/10/10 09:27:20 darrenr Exp $"; */ #endif #ifndef _KERNEL # include "ipf.h" # include "ipt.h" extern int opts; extern int blockreason; #endif /* _KERNEL */ #define FASTROUTE_RECURSION #define LBUMP(x) softc->x++ #define LBUMPD(x, y) do { softc->x.y++; DT(y); } while (0) static INLINE int ipf_check_ipf(fr_info_t *, frentry_t *, int); static u_32_t ipf_checkcipso(fr_info_t *, u_char *, int); static u_32_t ipf_checkripso(u_char *); static u_32_t ipf_decaps(fr_info_t *, u_32_t, int); #ifdef IPFILTER_LOG static frentry_t *ipf_dolog(fr_info_t *, u_32_t *); #endif static int ipf_flushlist(ipf_main_softc_t *, int *, frentry_t **); static int ipf_flush_groups(ipf_main_softc_t *, frgroup_t **, int); static ipfunc_t ipf_findfunc(ipfunc_t); static void *ipf_findlookup(ipf_main_softc_t *, int, frentry_t *, i6addr_t *, i6addr_t *); static frentry_t *ipf_firewall(fr_info_t *, u_32_t *); static int ipf_fr_matcharray(fr_info_t *, int *); static int ipf_frruleiter(ipf_main_softc_t *, void *, int, void *); static void ipf_funcfini(ipf_main_softc_t *, frentry_t *); static int ipf_funcinit(ipf_main_softc_t *, frentry_t *); static int ipf_geniter(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *); static void ipf_getstat(ipf_main_softc_t *, struct friostat *, int); static int ipf_group_flush(ipf_main_softc_t *, frgroup_t *); static void ipf_group_free(frgroup_t *); static int ipf_grpmapfini(struct ipf_main_softc_s *, frentry_t *); static int ipf_grpmapinit(struct ipf_main_softc_s *, frentry_t *); static frentry_t *ipf_nextrule(ipf_main_softc_t *, int, int, frentry_t *, int); static int ipf_portcheck(frpcmp_t *, u_32_t); static INLINE int ipf_pr_ah(fr_info_t *); static INLINE void ipf_pr_esp(fr_info_t *); static INLINE void ipf_pr_gre(fr_info_t *); static INLINE void ipf_pr_udp(fr_info_t *); static INLINE void ipf_pr_tcp(fr_info_t *); static INLINE void ipf_pr_icmp(fr_info_t *); static INLINE void ipf_pr_ipv4hdr(fr_info_t *); static INLINE void ipf_pr_short(fr_info_t *, int); static INLINE int ipf_pr_tcpcommon(fr_info_t *); static INLINE int ipf_pr_udpcommon(fr_info_t *); static void ipf_rule_delete(ipf_main_softc_t *, frentry_t *f, int, int); static void ipf_rule_expire_insert(ipf_main_softc_t *, frentry_t *, int); static int ipf_synclist(ipf_main_softc_t *, frentry_t *, void *); static void ipf_token_flush(ipf_main_softc_t *); static void ipf_token_unlink(ipf_main_softc_t *, ipftoken_t *); static ipftuneable_t *ipf_tune_findbyname(ipftuneable_t *, const char *); static ipftuneable_t *ipf_tune_findbycookie(ipftuneable_t **, void *, void **); static int ipf_updateipid(fr_info_t *); static int ipf_settimeout(struct ipf_main_softc_s *, struct ipftuneable *, ipftuneval_t *); #if !defined(_KERNEL) || SOLARIS static int ppsratecheck(struct timeval *, int *, int); #endif /* * bit values for identifying presence of individual IP options * All of these tables should be ordered by increasing key value on the left * hand side to allow for binary searching of the array and include a trailer * with a 0 for the bitmask for linear searches to easily find the end with. */ static const struct optlist ipopts[] = { { IPOPT_NOP, 0x000001 }, { IPOPT_RR, 0x000002 }, { IPOPT_ZSU, 0x000004 }, { IPOPT_MTUP, 0x000008 }, { IPOPT_MTUR, 0x000010 }, { IPOPT_ENCODE, 0x000020 }, { IPOPT_TS, 0x000040 }, { IPOPT_TR, 0x000080 }, { IPOPT_SECURITY, 0x000100 }, { IPOPT_LSRR, 0x000200 }, { IPOPT_E_SEC, 0x000400 }, { IPOPT_CIPSO, 0x000800 }, { IPOPT_SATID, 0x001000 }, { IPOPT_SSRR, 0x002000 }, { IPOPT_ADDEXT, 0x004000 }, { IPOPT_VISA, 0x008000 }, { IPOPT_IMITD, 0x010000 }, { IPOPT_EIP, 0x020000 }, { IPOPT_FINN, 0x040000 }, { 0, 0x000000 } }; #ifdef USE_INET6 static const struct optlist ip6exthdr[] = { { IPPROTO_HOPOPTS, 0x000001 }, { IPPROTO_IPV6, 0x000002 }, { IPPROTO_ROUTING, 0x000004 }, { IPPROTO_FRAGMENT, 0x000008 }, { IPPROTO_ESP, 0x000010 }, { IPPROTO_AH, 0x000020 }, { IPPROTO_NONE, 0x000040 }, { IPPROTO_DSTOPTS, 0x000080 }, { IPPROTO_MOBILITY, 0x000100 }, { 0, 0 } }; #endif /* * bit values for identifying presence of individual IP security options */ static const struct optlist secopt[] = { { IPSO_CLASS_RES4, 0x01 }, { IPSO_CLASS_TOPS, 0x02 }, { IPSO_CLASS_SECR, 0x04 }, { IPSO_CLASS_RES3, 0x08 }, { IPSO_CLASS_CONF, 0x10 }, { IPSO_CLASS_UNCL, 0x20 }, { IPSO_CLASS_RES2, 0x40 }, { IPSO_CLASS_RES1, 0x80 } }; char ipfilter_version[] = IPL_VERSION; int ipf_features = 0 #ifdef IPFILTER_LKM | IPF_FEAT_LKM #endif #ifdef IPFILTER_LOG | IPF_FEAT_LOG #endif | IPF_FEAT_LOOKUP #ifdef IPFILTER_BPF | IPF_FEAT_BPF #endif #ifdef IPFILTER_COMPILED | IPF_FEAT_COMPILED #endif #ifdef IPFILTER_CKSUM | IPF_FEAT_CKSUM #endif | IPF_FEAT_SYNC #ifdef IPFILTER_SCAN | IPF_FEAT_SCAN #endif #ifdef USE_INET6 | IPF_FEAT_IPV6 #endif ; /* * Table of functions available for use with call rules. */ static ipfunc_resolve_t ipf_availfuncs[] = { { "srcgrpmap", ipf_srcgrpmap, ipf_grpmapinit, ipf_grpmapfini }, { "dstgrpmap", ipf_dstgrpmap, ipf_grpmapinit, ipf_grpmapfini }, { "", NULL, NULL, NULL } }; static ipftuneable_t ipf_main_tuneables[] = { { { (void *)offsetof(struct ipf_main_softc_s, ipf_flags) }, "ipf_flags", 0, 0xffffffff, stsizeof(ipf_main_softc_t, ipf_flags), 0, NULL, NULL }, { { (void *)offsetof(struct ipf_main_softc_s, ipf_active) }, "active", 0, 0, stsizeof(ipf_main_softc_t, ipf_active), IPFT_RDONLY, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_control_forwarding) }, "control_forwarding", 0, 1, stsizeof(ipf_main_softc_t, ipf_control_forwarding), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_update_ipid) }, "update_ipid", 0, 1, stsizeof(ipf_main_softc_t, ipf_update_ipid), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_chksrc) }, "chksrc", 0, 1, stsizeof(ipf_main_softc_t, ipf_chksrc), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_minttl) }, "min_ttl", 0, 1, stsizeof(ipf_main_softc_t, ipf_minttl), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_icmpminfragmtu) }, "icmp_minfragmtu", 0, 1, stsizeof(ipf_main_softc_t, ipf_icmpminfragmtu), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_pass) }, "default_pass", 0, 0xffffffff, stsizeof(ipf_main_softc_t, ipf_pass), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpidletimeout) }, "tcp_idle_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpidletimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpclosewait) }, "tcp_close_wait", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpclosewait), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcplastack) }, "tcp_last_ack", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcplastack), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcptimeout) }, "tcp_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcptimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpsynsent) }, "tcp_syn_sent", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpsynsent), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpsynrecv) }, "tcp_syn_received", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpsynrecv), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpclosed) }, "tcp_closed", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpclosed), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcphalfclosed) }, "tcp_half_closed", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcphalfclosed), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcptimewait) }, "tcp_time_wait", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcptimewait), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_udptimeout) }, "udp_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_udptimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_udpacktimeout) }, "udp_ack_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_udpacktimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_icmptimeout) }, "icmp_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_icmptimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_icmpacktimeout) }, "icmp_ack_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_icmpacktimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_iptimeout) }, "ip_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_iptimeout), 0, NULL, ipf_settimeout }, #if defined(INSTANCES) && defined(_KERNEL) { { (void *)offsetof(ipf_main_softc_t, ipf_get_loopback) }, "intercept_loopback", 0, 1, stsizeof(ipf_main_softc_t, ipf_get_loopback), 0, NULL, ipf_set_loopback }, #endif { { 0 }, NULL, 0, 0, 0, 0, NULL, NULL } }; /* * The next section of code is a collection of small routines that set * fields in the fr_info_t structure passed based on properties of the * current packet. There are different routines for the same protocol * for each of IPv4 and IPv6. Adding a new protocol, for which there * will "special" inspection for setup, is now more easily done by adding * a new routine and expanding the ipf_pr_ipinit*() function rather than by * adding more code to a growing switch statement. */ #ifdef USE_INET6 static INLINE int ipf_pr_ah6(fr_info_t *); static INLINE void ipf_pr_esp6(fr_info_t *); static INLINE void ipf_pr_gre6(fr_info_t *); static INLINE void ipf_pr_udp6(fr_info_t *); static INLINE void ipf_pr_tcp6(fr_info_t *); static INLINE void ipf_pr_icmp6(fr_info_t *); static INLINE void ipf_pr_ipv6hdr(fr_info_t *); static INLINE void ipf_pr_short6(fr_info_t *, int); static INLINE int ipf_pr_hopopts6(fr_info_t *); static INLINE int ipf_pr_mobility6(fr_info_t *); static INLINE int ipf_pr_routing6(fr_info_t *); static INLINE int ipf_pr_dstopts6(fr_info_t *); static INLINE int ipf_pr_fragment6(fr_info_t *); static INLINE struct ip6_ext *ipf_pr_ipv6exthdr(fr_info_t *, int, int); /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_short6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* xmin(I) - minimum header size */ /* */ /* IPv6 Only */ /* This is function enforces the 'is a packet too short to be legit' rule */ /* for IPv6 and marks the packet with FI_SHORT if so. See function comment */ /* for ipf_pr_short() for more details. */ /* ------------------------------------------------------------------------ */ static INLINE void -ipf_pr_short6(fin, xmin) - fr_info_t *fin; - int xmin; +ipf_pr_short6(fr_info_t *fin, int xmin) { if (fin->fin_dlen < xmin) fin->fin_flx |= FI_SHORT; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ipv6hdr */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Copy values from the IPv6 header into the fr_info_t struct and call the */ /* per-protocol analyzer if it exists. In validating the packet, a protocol*/ /* analyzer may pullup or free the packet itself so we need to be vigiliant */ /* of that possibility arising. */ /* ------------------------------------------------------------------------ */ static INLINE void -ipf_pr_ipv6hdr(fin) - fr_info_t *fin; +ipf_pr_ipv6hdr(fr_info_t *fin) { ip6_t *ip6 = (ip6_t *)fin->fin_ip; int p, go = 1, i, hdrcount; fr_ip_t *fi = &fin->fin_fi; fin->fin_off = 0; fi->fi_tos = 0; fi->fi_optmsk = 0; fi->fi_secmsk = 0; fi->fi_auth = 0; p = ip6->ip6_nxt; fin->fin_crc = p; fi->fi_ttl = ip6->ip6_hlim; fi->fi_src.in6 = ip6->ip6_src; fin->fin_crc += fi->fi_src.i6[0]; fin->fin_crc += fi->fi_src.i6[1]; fin->fin_crc += fi->fi_src.i6[2]; fin->fin_crc += fi->fi_src.i6[3]; fi->fi_dst.in6 = ip6->ip6_dst; fin->fin_crc += fi->fi_dst.i6[0]; fin->fin_crc += fi->fi_dst.i6[1]; fin->fin_crc += fi->fi_dst.i6[2]; fin->fin_crc += fi->fi_dst.i6[3]; fin->fin_id = 0; if (IN6_IS_ADDR_MULTICAST(&fi->fi_dst.in6)) fin->fin_flx |= FI_MULTICAST|FI_MBCAST; hdrcount = 0; while (go && !(fin->fin_flx & FI_SHORT)) { switch (p) { case IPPROTO_UDP : ipf_pr_udp6(fin); go = 0; break; case IPPROTO_TCP : ipf_pr_tcp6(fin); go = 0; break; case IPPROTO_ICMPV6 : ipf_pr_icmp6(fin); go = 0; break; case IPPROTO_GRE : ipf_pr_gre6(fin); go = 0; break; case IPPROTO_HOPOPTS : p = ipf_pr_hopopts6(fin); break; case IPPROTO_MOBILITY : p = ipf_pr_mobility6(fin); break; case IPPROTO_DSTOPTS : p = ipf_pr_dstopts6(fin); break; case IPPROTO_ROUTING : p = ipf_pr_routing6(fin); break; case IPPROTO_AH : p = ipf_pr_ah6(fin); break; case IPPROTO_ESP : ipf_pr_esp6(fin); go = 0; break; case IPPROTO_IPV6 : for (i = 0; ip6exthdr[i].ol_bit != 0; i++) if (ip6exthdr[i].ol_val == p) { fin->fin_flx |= ip6exthdr[i].ol_bit; break; } go = 0; break; case IPPROTO_NONE : go = 0; break; case IPPROTO_FRAGMENT : p = ipf_pr_fragment6(fin); /* * Given that the only fragments we want to let through * (where fin_off != 0) are those where the non-first * fragments only have data, we can safely stop looking * at headers if this is a non-leading fragment. */ if (fin->fin_off != 0) go = 0; break; default : go = 0; break; } hdrcount++; /* * It is important to note that at this point, for the * extension headers (go != 0), the entire header may not have * been pulled up when the code gets to this point. This is * only done for "go != 0" because the other header handlers * will all pullup their complete header. The other indicator * of an incomplete packet is that this was just an extension * header. */ if ((go != 0) && (p != IPPROTO_NONE) && (ipf_pr_pullup(fin, 0) == -1)) { p = IPPROTO_NONE; break; } } /* * Some of the above functions, like ipf_pr_esp6(), can call ipf_pullup * and destroy whatever packet was here. The caller of this function * expects us to return if there is a problem with ipf_pullup. */ if (fin->fin_m == NULL) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_bad); return; } fi->fi_p = p; /* * IPv6 fragment case 1 - see comment for ipf_pr_fragment6(). * "go != 0" implies the above loop hasn't arrived at a layer 4 header. */ if ((go != 0) && (fin->fin_flx & FI_FRAG) && (fin->fin_off == 0)) { ipf_main_softc_t *softc = fin->fin_main_soft; fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_ipv6_frag_1, fr_info_t *, fin, int, go); LBUMPD(ipf_stats[fin->fin_out], fr_v6_badfrag); LBUMP(ipf_stats[fin->fin_out].fr_v6_bad); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ipv6exthdr */ /* Returns: struct ip6_ext * - pointer to the start of the next header */ /* or NULL if there is a prolblem. */ /* Parameters: fin(I) - pointer to packet information */ /* multiple(I) - flag indicating yes/no if multiple occurances */ /* of this extension header are allowed. */ /* proto(I) - protocol number for this extension header */ /* */ /* IPv6 Only */ /* This function embodies a number of common checks that all IPv6 extension */ /* headers must be subjected to. For example, making sure the packet is */ /* big enough for it to be in, checking if it is repeated and setting a */ /* flag to indicate its presence. */ /* ------------------------------------------------------------------------ */ static INLINE struct ip6_ext * -ipf_pr_ipv6exthdr(fin, multiple, proto) - fr_info_t *fin; - int multiple, proto; +ipf_pr_ipv6exthdr(fr_info_t *fin, int multiple, int proto) { ipf_main_softc_t *softc = fin->fin_main_soft; struct ip6_ext *hdr; u_short shift; int i; fin->fin_flx |= FI_V6EXTHDR; /* 8 is default length of extension hdr */ if ((fin->fin_dlen - 8) < 0) { fin->fin_flx |= FI_SHORT; LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_short); return NULL; } if (ipf_pr_pullup(fin, 8) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_pullup); return NULL; } hdr = fin->fin_dp; switch (proto) { case IPPROTO_FRAGMENT : shift = 8; break; default : shift = 8 + (hdr->ip6e_len << 3); break; } if (shift > fin->fin_dlen) { /* Nasty extension header length? */ fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_pr_ipv6exthdr_len, fr_info_t *, fin, u_short, shift, u_short, fin->fin_dlen); LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_hlen); return NULL; } fin->fin_dp = (char *)fin->fin_dp + shift; fin->fin_dlen -= shift; /* * If we have seen a fragment header, do not set any flags to indicate * the presence of this extension header as it has no impact on the * end result until after it has been defragmented. */ if (fin->fin_flx & FI_FRAG) return hdr; for (i = 0; ip6exthdr[i].ol_bit != 0; i++) if (ip6exthdr[i].ol_val == proto) { /* * Most IPv6 extension headers are only allowed once. */ if ((multiple == 0) && ((fin->fin_optmsk & ip6exthdr[i].ol_bit) != 0)) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_ipv6exthdr_once, fr_info_t *, fin, u_int, (fin->fin_optmsk & ip6exthdr[i].ol_bit)); } else fin->fin_optmsk |= ip6exthdr[i].ol_bit; break; } return hdr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_hopopts6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks pending hop by hop options extension header */ /* ------------------------------------------------------------------------ */ static INLINE int -ipf_pr_hopopts6(fin) - fr_info_t *fin; +ipf_pr_hopopts6(fr_info_t *fin) { struct ip6_ext *hdr; hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); if (hdr == NULL) return IPPROTO_NONE; return hdr->ip6e_nxt; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_mobility6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks the IPv6 mobility extension header */ /* ------------------------------------------------------------------------ */ static INLINE int -ipf_pr_mobility6(fin) - fr_info_t *fin; +ipf_pr_mobility6(fr_info_t *fin) { struct ip6_ext *hdr; hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_MOBILITY); if (hdr == NULL) return IPPROTO_NONE; return hdr->ip6e_nxt; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_routing6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks pending routing extension header */ /* ------------------------------------------------------------------------ */ static INLINE int -ipf_pr_routing6(fin) - fr_info_t *fin; +ipf_pr_routing6(fr_info_t *fin) { struct ip6_routing *hdr; hdr = (struct ip6_routing *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_ROUTING); if (hdr == NULL) return IPPROTO_NONE; switch (hdr->ip6r_type) { case 0 : /* * Nasty extension header length? */ if (((hdr->ip6r_len >> 1) < hdr->ip6r_segleft) || (hdr->ip6r_segleft && (hdr->ip6r_len & 1))) { ipf_main_softc_t *softc = fin->fin_main_soft; fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_routing6, fr_info_t *, fin); LBUMPD(ipf_stats[fin->fin_out], fr_v6_rh_bad); return IPPROTO_NONE; } break; default : break; } return hdr->ip6r_nxt; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_fragment6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Examine the IPv6 fragment header and extract fragment offset information.*/ /* */ /* Fragments in IPv6 are extraordinarily difficult to deal with - much more */ /* so than in IPv4. There are 5 cases of fragments with IPv6 that all */ /* packets with a fragment header can fit into. They are as follows: */ /* */ /* 1. [IPv6][0-n EH][FH][0-n EH] (no L4HDR present) */ /* 2. [IPV6][0-n EH][FH][0-n EH][L4HDR part] (short) */ /* 3. [IPV6][0-n EH][FH][L4HDR part][0-n data] (short) */ /* 4. [IPV6][0-n EH][FH][0-n EH][L4HDR][0-n data] */ /* 5. [IPV6][0-n EH][FH][data] */ /* */ /* IPV6 = IPv6 header, FH = Fragment Header, */ /* 0-n EH = 0 or more extension headers, 0-n data = 0 or more bytes of data */ /* */ /* Packets that match 1, 2, 3 will be dropped as the only reasonable */ /* scenario in which they happen is in extreme circumstances that are most */ /* likely to be an indication of an attack rather than normal traffic. */ /* A type 3 packet may be sent by an attacked after a type 4 packet. There */ /* are two rules that can be used to guard against type 3 packets: L4 */ /* headers must always be in a packet that has the offset field set to 0 */ /* and no packet is allowed to overlay that where offset = 0. */ /* ------------------------------------------------------------------------ */ static INLINE int -ipf_pr_fragment6(fin) - fr_info_t *fin; +ipf_pr_fragment6(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; struct ip6_frag *frag; fin->fin_flx |= FI_FRAG; frag = (struct ip6_frag *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_FRAGMENT); if (frag == NULL) { LBUMPD(ipf_stats[fin->fin_out], fr_v6_frag_bad); return IPPROTO_NONE; } if ((frag->ip6f_offlg & IP6F_MORE_FRAG) != 0) { /* * Any fragment that isn't the last fragment must have its * length as a multiple of 8. */ if ((fin->fin_plen & 7) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_frag_not_8, fr_info_t *, fin, u_int, (fin->fin_plen & 7)); } } fin->fin_fraghdr = frag; fin->fin_id = frag->ip6f_ident; fin->fin_off = ntohs(frag->ip6f_offlg & IP6F_OFF_MASK); if (fin->fin_off != 0) fin->fin_flx |= FI_FRAGBODY; /* * Jumbograms aren't handled, so the max. length is 64k */ if ((fin->fin_off << 3) + fin->fin_dlen > 65535) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_jumbogram, fr_info_t *, fin, u_int, ((fin->fin_off << 3) + fin->fin_dlen)); } /* * We don't know where the transport layer header (or whatever is next * is), as it could be behind destination options (amongst others) so * return the fragment header as the type of packet this is. Note that * this effectively disables the fragment cache for > 1 protocol at a * time. */ return frag->ip6f_nxt; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_dstopts6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks pending destination options extension header */ /* ------------------------------------------------------------------------ */ static INLINE int -ipf_pr_dstopts6(fin) - fr_info_t *fin; +ipf_pr_dstopts6(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; struct ip6_ext *hdr; hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_DSTOPTS); if (hdr == NULL) { LBUMPD(ipf_stats[fin->fin_out], fr_v6_dst_bad); return IPPROTO_NONE; } return hdr->ip6e_nxt; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_icmp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This routine is mainly concerned with determining the minimum valid size */ /* for an ICMPv6 packet. */ /* ------------------------------------------------------------------------ */ static INLINE void -ipf_pr_icmp6(fin) - fr_info_t *fin; +ipf_pr_icmp6(fr_info_t *fin) { int minicmpsz = sizeof(struct icmp6_hdr); struct icmp6_hdr *icmp6; if (ipf_pr_pullup(fin, ICMP6ERR_MINPKTLEN - sizeof(ip6_t)) == -1) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_icmp6_pullup); return; } if (fin->fin_dlen > 1) { ip6_t *ip6; icmp6 = fin->fin_dp; fin->fin_data[0] = *(u_short *)icmp6; if ((icmp6->icmp6_type & ICMP6_INFOMSG_MASK) != 0) fin->fin_flx |= FI_ICMPQUERY; switch (icmp6->icmp6_type) { case ICMP6_ECHO_REPLY : case ICMP6_ECHO_REQUEST : if (fin->fin_dlen >= 6) fin->fin_data[1] = icmp6->icmp6_id; minicmpsz = ICMP6ERR_MINPKTLEN - sizeof(ip6_t); break; case ICMP6_DST_UNREACH : case ICMP6_PACKET_TOO_BIG : case ICMP6_TIME_EXCEEDED : case ICMP6_PARAM_PROB : fin->fin_flx |= FI_ICMPERR; minicmpsz = ICMP6ERR_IPICMPHLEN - sizeof(ip6_t); if (fin->fin_plen < ICMP6ERR_IPICMPHLEN) break; if (M_LEN(fin->fin_m) < fin->fin_plen) { if (ipf_coalesce(fin) != 1) return; } if (ipf_pr_pullup(fin, ICMP6ERR_MINPKTLEN) == -1) return; /* * If the destination of this packet doesn't match the * source of the original packet then this packet is * not correct. */ icmp6 = fin->fin_dp; ip6 = (ip6_t *)((char *)icmp6 + ICMPERR_ICMPHLEN); if (IP6_NEQ(&fin->fin_fi.fi_dst, (i6addr_t *)&ip6->ip6_src)) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_icmp6, fr_info_t *, fin); } break; default : break; } } ipf_pr_short6(fin, minicmpsz); if ((fin->fin_flx & (FI_SHORT|FI_BAD)) == 0) { u_char p = fin->fin_p; fin->fin_p = IPPROTO_ICMPV6; ipf_checkv6sum(fin); fin->fin_p = p; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_udp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Analyse the packet for IPv6/UDP properties. */ /* Is not expected to be called for fragmented packets. */ /* ------------------------------------------------------------------------ */ static INLINE void -ipf_pr_udp6(fin) - fr_info_t *fin; +ipf_pr_udp6(fr_info_t *fin) { if (ipf_pr_udpcommon(fin) == 0) { u_char p = fin->fin_p; fin->fin_p = IPPROTO_UDP; ipf_checkv6sum(fin); fin->fin_p = p; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_tcp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Analyse the packet for IPv6/TCP properties. */ /* Is not expected to be called for fragmented packets. */ /* ------------------------------------------------------------------------ */ static INLINE void -ipf_pr_tcp6(fin) - fr_info_t *fin; +ipf_pr_tcp6(fr_info_t *fin) { if (ipf_pr_tcpcommon(fin) == 0) { u_char p = fin->fin_p; fin->fin_p = IPPROTO_TCP; ipf_checkv6sum(fin); fin->fin_p = p; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_esp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Analyse the packet for ESP properties. */ /* The minimum length is taken to be the SPI (32bits) plus a tail (32bits) */ /* even though the newer ESP packets must also have a sequence number that */ /* is 32bits as well, it is not possible(?) to determine the version from a */ /* simple packet header. */ /* ------------------------------------------------------------------------ */ static INLINE void -ipf_pr_esp6(fin) - fr_info_t *fin; +ipf_pr_esp6(fr_info_t *fin) { if ((fin->fin_off == 0) && (ipf_pr_pullup(fin, 8) == -1)) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_esp_pullup); return; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ah6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Analyse the packet for AH properties. */ /* The minimum length is taken to be the combination of all fields in the */ /* header being present and no authentication data (null algorithm used.) */ /* ------------------------------------------------------------------------ */ static INLINE int -ipf_pr_ah6(fin) - fr_info_t *fin; +ipf_pr_ah6(fr_info_t *fin) { authhdr_t *ah; fin->fin_flx |= FI_AH; ah = (authhdr_t *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); if (ah == NULL) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_ah_bad); return IPPROTO_NONE; } ipf_pr_short6(fin, sizeof(*ah)); /* * No need for another pullup, ipf_pr_ipv6exthdr() will pullup * enough data to satisfy ah_next (the very first one.) */ return ah->ah_next; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_gre6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for GRE properties. */ /* ------------------------------------------------------------------------ */ static INLINE void -ipf_pr_gre6(fin) - fr_info_t *fin; +ipf_pr_gre6(fr_info_t *fin) { grehdr_t *gre; if (ipf_pr_pullup(fin, sizeof(grehdr_t)) == -1) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_gre_pullup); return; } gre = fin->fin_dp; if (GRE_REV(gre->gr_flags) == 1) fin->fin_data[0] = gre->gr_call; } #endif /* USE_INET6 */ /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_pullup */ /* Returns: int - 0 == pullup succeeded, -1 == failure */ /* Parameters: fin(I) - pointer to packet information */ /* plen(I) - length (excluding L3 header) to pullup */ /* */ /* Short inline function to cut down on code duplication to perform a call */ /* to ipf_pullup to ensure there is the required amount of data, */ /* consecutively in the packet buffer. */ /* */ /* This function pulls up 'extra' data at the location of fin_dp. fin_dp */ /* points to the first byte after the complete layer 3 header, which will */ /* include all of the known extension headers for IPv6 or options for IPv4. */ /* */ /* Since fr_pullup() expects the total length of bytes to be pulled up, it */ /* is necessary to add those we can already assume to be pulled up (fin_dp */ /* - fin_ip) to what is passed through. */ /* ------------------------------------------------------------------------ */ int -ipf_pr_pullup(fin, plen) - fr_info_t *fin; - int plen; +ipf_pr_pullup(fr_info_t *fin, int plen) { ipf_main_softc_t *softc = fin->fin_main_soft; if (fin->fin_m != NULL) { if (fin->fin_dp != NULL) plen += (char *)fin->fin_dp - ((char *)fin->fin_ip + fin->fin_hlen); plen += fin->fin_hlen; if (M_LEN(fin->fin_m) < plen + fin->fin_ipoff) { #if defined(_KERNEL) if (ipf_pullup(fin->fin_m, fin, plen) == NULL) { DT(ipf_pullup_fail); LBUMP(ipf_stats[fin->fin_out].fr_pull[1]); return -1; } LBUMP(ipf_stats[fin->fin_out].fr_pull[0]); #else LBUMP(ipf_stats[fin->fin_out].fr_pull[1]); /* * Fake ipf_pullup failing */ fin->fin_reason = FRB_PULLUP; *fin->fin_mp = NULL; fin->fin_m = NULL; fin->fin_ip = NULL; return -1; #endif } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_short */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* xmin(I) - minimum header size */ /* */ /* Check if a packet is "short" as defined by xmin. The rule we are */ /* applying here is that the packet must not be fragmented within the layer */ /* 4 header. That is, it must not be a fragment that has its offset set to */ /* start within the layer 4 header (hdrmin) or if it is at offset 0, the */ /* entire layer 4 header must be present (min). */ /* ------------------------------------------------------------------------ */ static INLINE void -ipf_pr_short(fin, xmin) - fr_info_t *fin; - int xmin; +ipf_pr_short(fr_info_t *fin, int xmin) { if (fin->fin_off == 0) { if (fin->fin_dlen < xmin) fin->fin_flx |= FI_SHORT; } else if (fin->fin_off < xmin) { fin->fin_flx |= FI_SHORT; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_icmp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Do a sanity check on the packet for ICMP (v4). In nearly all cases, */ /* except extrememly bad packets, both type and code will be present. */ /* The expected minimum size of an ICMP packet is very much dependent on */ /* the type of it. */ /* */ /* XXX - other ICMP sanity checks? */ /* ------------------------------------------------------------------------ */ static INLINE void -ipf_pr_icmp(fin) - fr_info_t *fin; +ipf_pr_icmp(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; int minicmpsz = sizeof(struct icmp); icmphdr_t *icmp; ip_t *oip; ipf_pr_short(fin, ICMPERR_ICMPHLEN); if (fin->fin_off != 0) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_icmp_frag); return; } if (ipf_pr_pullup(fin, ICMPERR_ICMPHLEN) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_icmp_pullup); return; } icmp = fin->fin_dp; fin->fin_data[0] = *(u_short *)icmp; fin->fin_data[1] = icmp->icmp_id; switch (icmp->icmp_type) { case ICMP_ECHOREPLY : case ICMP_ECHO : /* Router discovery messaes - RFC 1256 */ case ICMP_ROUTERADVERT : case ICMP_ROUTERSOLICIT : fin->fin_flx |= FI_ICMPQUERY; minicmpsz = ICMP_MINLEN; break; /* * type(1) + code(1) + cksum(2) + id(2) seq(2) + * 3 * timestamp(3 * 4) */ case ICMP_TSTAMP : case ICMP_TSTAMPREPLY : fin->fin_flx |= FI_ICMPQUERY; minicmpsz = 20; break; /* * type(1) + code(1) + cksum(2) + id(2) seq(2) + * mask(4) */ case ICMP_IREQ : case ICMP_IREQREPLY : case ICMP_MASKREQ : case ICMP_MASKREPLY : fin->fin_flx |= FI_ICMPQUERY; minicmpsz = 12; break; /* * type(1) + code(1) + cksum(2) + id(2) seq(2) + ip(20+) */ case ICMP_UNREACH : #ifdef icmp_nextmtu if (icmp->icmp_code == ICMP_UNREACH_NEEDFRAG) { if (icmp->icmp_nextmtu < softc->ipf_icmpminfragmtu) { fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_icmp_nextmtu, fr_info_t *, fin, u_int, icmp->icmp_nextmtu, u_int, softc->ipf_icmpminfragmtu); } } #endif /* FALLTHROUGH */ case ICMP_SOURCEQUENCH : case ICMP_REDIRECT : case ICMP_TIMXCEED : case ICMP_PARAMPROB : fin->fin_flx |= FI_ICMPERR; if (ipf_coalesce(fin) != 1) { LBUMPD(ipf_stats[fin->fin_out], fr_icmp_coalesce); return; } /* * ICMP error packets should not be generated for IP * packets that are a fragment that isn't the first * fragment. */ oip = (ip_t *)((char *)fin->fin_dp + ICMPERR_ICMPHLEN); if ((ntohs(oip->ip_off) & IP_OFFMASK) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_icmp_err, fr_info_t, fin, u_int, (ntohs(oip->ip_off) & IP_OFFMASK)); } /* * If the destination of this packet doesn't match the * source of the original packet then this packet is * not correct. */ if (oip->ip_src.s_addr != fin->fin_daddr) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_src_ne_dst, fr_info_t *, fin); } break; default : break; } ipf_pr_short(fin, minicmpsz); ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_tcpcommon */ /* Returns: int - 0 = header ok, 1 = bad packet, -1 = buffer error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* TCP header sanity checking. Look for bad combinations of TCP flags, */ /* and make some checks with how they interact with other fields. */ /* If compiled with IPFILTER_CKSUM, check to see if the TCP checksum is */ /* valid and mark the packet as bad if not. */ /* ------------------------------------------------------------------------ */ static INLINE int -ipf_pr_tcpcommon(fin) - fr_info_t *fin; +ipf_pr_tcpcommon(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; int flags, tlen; tcphdr_t *tcp; fin->fin_flx |= FI_TCPUDP; if (fin->fin_off != 0) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_frag); return 0; } if (ipf_pr_pullup(fin, sizeof(*tcp)) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_pullup); return -1; } tcp = fin->fin_dp; if (fin->fin_dlen > 3) { fin->fin_sport = ntohs(tcp->th_sport); fin->fin_dport = ntohs(tcp->th_dport); } if ((fin->fin_flx & FI_SHORT) != 0) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_short); return 1; } /* * Use of the TCP data offset *must* result in a value that is at * least the same size as the TCP header. */ tlen = TCP_OFF(tcp) << 2; if (tlen < sizeof(tcphdr_t)) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_small); fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_tlen, fr_info_t, fin, u_int, tlen, u_int, sizeof(tcphdr_t)); return 1; } flags = tcp->th_flags; fin->fin_tcpf = tcp->th_flags; /* * If the urgent flag is set, then the urgent pointer must * also be set and vice versa. Good TCP packets do not have * just one of these set. */ if ((flags & TH_URG) != 0 && (tcp->th_urp == 0)) { fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_th_urg, fr_info_t*, fin, u_int, (flags & TH_URG), u_int, tcp->th_urp); #if 0 } else if ((flags & TH_URG) == 0 && (tcp->th_urp != 0)) { /* * Ignore this case (#if 0) as it shows up in "real" * traffic with bogus values in the urgent pointer field. */ fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_th_urg0, fr_info_t *, fin, u_int, (flags & TH_URG), u_int, tcp->th_urp); #endif } else if (((flags & (TH_SYN|TH_FIN)) != 0) && ((flags & (TH_RST|TH_ACK)) == TH_RST)) { /* TH_FIN|TH_RST|TH_ACK seems to appear "naturally" */ fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_th_fin_rst_ack, fr_info_t, fin); #if 1 } else if (((flags & TH_SYN) != 0) && ((flags & (TH_URG|TH_PUSH)) != 0)) { /* * SYN with URG and PUSH set is not for normal TCP but it is * possible(?) with T/TCP...but who uses T/TCP? */ fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_th_syn_urg_psh, fr_info_t *, fin); #endif } else if (!(flags & TH_ACK)) { /* * If the ack bit isn't set, then either the SYN or * RST bit must be set. If the SYN bit is set, then * we expect the ACK field to be 0. If the ACK is * not set and if URG, PSH or FIN are set, consdier * that to indicate a bad TCP packet. */ if ((flags == TH_SYN) && (tcp->th_ack != 0)) { /* * Cisco PIX sets the ACK field to a random value. * In light of this, do not set FI_BAD until a patch * is available from Cisco to ensure that * interoperability between existing systems is * achieved. */ /*fin->fin_flx |= FI_BAD*/; /*DT1(ipf_fi_bad_th_syn_ack, fr_info_t *, fin);*/ } else if (!(flags & (TH_RST|TH_SYN))) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_th_rst_syn, fr_info_t *, fin); } else if ((flags & (TH_URG|TH_PUSH|TH_FIN)) != 0) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_th_urg_push_fin, fr_info_t *, fin); } } if (fin->fin_flx & FI_BAD) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_bad_flags); return 1; } /* * At this point, it's not exactly clear what is to be gained by * marking up which TCP options are and are not present. The one we * are most interested in is the TCP window scale. This is only in * a SYN packet [RFC1323] so we don't need this here...? * Now if we were to analyse the header for passive fingerprinting, * then that might add some weight to adding this... */ if (tlen == sizeof(tcphdr_t)) { return 0; } if (ipf_pr_pullup(fin, tlen) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_pullup); return -1; } #if 0 tcp = fin->fin_dp; ip = fin->fin_ip; s = (u_char *)(tcp + 1); off = IP_HL(ip) << 2; # ifdef _KERNEL if (fin->fin_mp != NULL) { mb_t *m = *fin->fin_mp; if (off + tlen > M_LEN(m)) return; } # endif for (tlen -= (int)sizeof(*tcp); tlen > 0; ) { opt = *s; if (opt == '\0') break; else if (opt == TCPOPT_NOP) ol = 1; else { if (tlen < 2) break; ol = (int)*(s + 1); if (ol < 2 || ol > tlen) break; } for (i = 9, mv = 4; mv >= 0; ) { op = ipopts + i; if (opt == (u_char)op->ol_val) { optmsk |= op->ol_bit; break; } } tlen -= ol; s += ol; } #endif /* 0 */ return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_udpcommon */ /* Returns: int - 0 = header ok, 1 = bad packet */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Extract the UDP source and destination ports, if present. If compiled */ /* with IPFILTER_CKSUM, check to see if the UDP checksum is valid. */ /* ------------------------------------------------------------------------ */ static INLINE int -ipf_pr_udpcommon(fin) - fr_info_t *fin; +ipf_pr_udpcommon(fr_info_t *fin) { udphdr_t *udp; fin->fin_flx |= FI_TCPUDP; if (!fin->fin_off && (fin->fin_dlen > 3)) { if (ipf_pr_pullup(fin, sizeof(*udp)) == -1) { ipf_main_softc_t *softc = fin->fin_main_soft; fin->fin_flx |= FI_SHORT; LBUMPD(ipf_stats[fin->fin_out], fr_udp_pullup); return 1; } udp = fin->fin_dp; fin->fin_sport = ntohs(udp->uh_sport); fin->fin_dport = ntohs(udp->uh_dport); } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_tcp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Analyse the packet for IPv4/TCP properties. */ /* ------------------------------------------------------------------------ */ static INLINE void -ipf_pr_tcp(fin) - fr_info_t *fin; +ipf_pr_tcp(fr_info_t *fin) { ipf_pr_short(fin, sizeof(tcphdr_t)); if (ipf_pr_tcpcommon(fin) == 0) ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_udp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Analyse the packet for IPv4/UDP properties. */ /* ------------------------------------------------------------------------ */ static INLINE void -ipf_pr_udp(fin) - fr_info_t *fin; +ipf_pr_udp(fr_info_t *fin) { ipf_pr_short(fin, sizeof(udphdr_t)); if (ipf_pr_udpcommon(fin) == 0) ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_esp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for ESP properties. */ /* The minimum length is taken to be the SPI (32bits) plus a tail (32bits) */ /* even though the newer ESP packets must also have a sequence number that */ /* is 32bits as well, it is not possible(?) to determine the version from a */ /* simple packet header. */ /* ------------------------------------------------------------------------ */ static INLINE void -ipf_pr_esp(fin) - fr_info_t *fin; +ipf_pr_esp(fr_info_t *fin) { if (fin->fin_off == 0) { ipf_pr_short(fin, 8); if (ipf_pr_pullup(fin, 8) == -1) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v4_esp_pullup); } } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ah */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for AH properties. */ /* The minimum length is taken to be the combination of all fields in the */ /* header being present and no authentication data (null algorithm used.) */ /* ------------------------------------------------------------------------ */ static INLINE int -ipf_pr_ah(fin) - fr_info_t *fin; +ipf_pr_ah(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; authhdr_t *ah; int len; fin->fin_flx |= FI_AH; ipf_pr_short(fin, sizeof(*ah)); if (((fin->fin_flx & FI_SHORT) != 0) || (fin->fin_off != 0)) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_ah_bad); return IPPROTO_NONE; } if (ipf_pr_pullup(fin, sizeof(*ah)) == -1) { DT(fr_v4_ah_pullup_1); LBUMP(ipf_stats[fin->fin_out].fr_v4_ah_pullup); return IPPROTO_NONE; } ah = (authhdr_t *)fin->fin_dp; len = (ah->ah_plen + 2) << 2; ipf_pr_short(fin, len); if (ipf_pr_pullup(fin, len) == -1) { DT(fr_v4_ah_pullup_2); LBUMP(ipf_stats[fin->fin_out].fr_v4_ah_pullup); return IPPROTO_NONE; } /* * Adjust fin_dp and fin_dlen for skipping over the authentication * header. */ fin->fin_dp = (char *)fin->fin_dp + len; fin->fin_dlen -= len; return ah->ah_next; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_gre */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for GRE properties. */ /* ------------------------------------------------------------------------ */ static INLINE void -ipf_pr_gre(fin) - fr_info_t *fin; +ipf_pr_gre(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; grehdr_t *gre; ipf_pr_short(fin, sizeof(grehdr_t)); if (fin->fin_off != 0) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_gre_frag); return; } if (ipf_pr_pullup(fin, sizeof(grehdr_t)) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_gre_pullup); return; } gre = fin->fin_dp; if (GRE_REV(gre->gr_flags) == 1) fin->fin_data[0] = gre->gr_call; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ipv4hdr */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Analyze the IPv4 header and set fields in the fr_info_t structure. */ /* Check all options present and flag their presence if any exist. */ /* ------------------------------------------------------------------------ */ static INLINE void -ipf_pr_ipv4hdr(fin) - fr_info_t *fin; +ipf_pr_ipv4hdr(fr_info_t *fin) { u_short optmsk = 0, secmsk = 0, auth = 0; int hlen, ol, mv, p, i; const struct optlist *op; u_char *s, opt; u_short off; fr_ip_t *fi; ip_t *ip; fi = &fin->fin_fi; hlen = fin->fin_hlen; ip = fin->fin_ip; p = ip->ip_p; fi->fi_p = p; fin->fin_crc = p; fi->fi_tos = ip->ip_tos; fin->fin_id = ntohs(ip->ip_id); off = ntohs(ip->ip_off); /* Get both TTL and protocol */ fi->fi_p = ip->ip_p; fi->fi_ttl = ip->ip_ttl; /* Zero out bits not used in IPv6 address */ fi->fi_src.i6[1] = 0; fi->fi_src.i6[2] = 0; fi->fi_src.i6[3] = 0; fi->fi_dst.i6[1] = 0; fi->fi_dst.i6[2] = 0; fi->fi_dst.i6[3] = 0; fi->fi_saddr = ip->ip_src.s_addr; fin->fin_crc += fi->fi_saddr; fi->fi_daddr = ip->ip_dst.s_addr; fin->fin_crc += fi->fi_daddr; if (IN_CLASSD(ntohl(fi->fi_daddr))) fin->fin_flx |= FI_MULTICAST|FI_MBCAST; /* * set packet attribute flags based on the offset and * calculate the byte offset that it represents. */ off &= IP_MF|IP_OFFMASK; if (off != 0) { int morefrag = off & IP_MF; fi->fi_flx |= FI_FRAG; off &= IP_OFFMASK; if (off == 1 && p == IPPROTO_TCP) { fin->fin_flx |= FI_SHORT; /* RFC 3128 */ DT1(ipf_fi_tcp_frag_off_1, fr_info_t *, fin); } if (off != 0) { fin->fin_flx |= FI_FRAGBODY; off <<= 3; if ((off + fin->fin_dlen > 65535) || (fin->fin_dlen == 0) || ((morefrag != 0) && ((fin->fin_dlen & 7) != 0))) { /* * The length of the packet, starting at its * offset cannot exceed 65535 (0xffff) as the * length of an IP packet is only 16 bits. * * Any fragment that isn't the last fragment * must have a length greater than 0 and it * must be an even multiple of 8. */ fi->fi_flx |= FI_BAD; DT1(ipf_fi_bad_fragbody_gt_65535, fr_info_t *, fin); } } } fin->fin_off = off; /* * Call per-protocol setup and checking */ if (p == IPPROTO_AH) { /* * Treat AH differently because we expect there to be another * layer 4 header after it. */ p = ipf_pr_ah(fin); } switch (p) { case IPPROTO_UDP : ipf_pr_udp(fin); break; case IPPROTO_TCP : ipf_pr_tcp(fin); break; case IPPROTO_ICMP : ipf_pr_icmp(fin); break; case IPPROTO_ESP : ipf_pr_esp(fin); break; case IPPROTO_GRE : ipf_pr_gre(fin); break; } ip = fin->fin_ip; if (ip == NULL) return; /* * If it is a standard IP header (no options), set the flag fields * which relate to options to 0. */ if (hlen == sizeof(*ip)) { fi->fi_optmsk = 0; fi->fi_secmsk = 0; fi->fi_auth = 0; return; } /* * So the IP header has some IP options attached. Walk the entire * list of options present with this packet and set flags to indicate * which ones are here and which ones are not. For the somewhat out * of date and obscure security classification options, set a flag to * represent which classification is present. */ fi->fi_flx |= FI_OPTIONS; for (s = (u_char *)(ip + 1), hlen -= (int)sizeof(*ip); hlen > 0; ) { opt = *s; if (opt == '\0') break; else if (opt == IPOPT_NOP) ol = 1; else { if (hlen < 2) break; ol = (int)*(s + 1); if (ol < 2 || ol > hlen) break; } for (i = 9, mv = 4; mv >= 0; ) { op = ipopts + i; if ((opt == (u_char)op->ol_val) && (ol > 4)) { u_32_t doi; switch (opt) { case IPOPT_SECURITY : if (optmsk & op->ol_bit) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_ipopt_security, fr_info_t *, fin, u_short, (optmsk & op->ol_bit)); } else { doi = ipf_checkripso(s); secmsk = doi >> 16; auth = doi & 0xffff; } break; case IPOPT_CIPSO : if (optmsk & op->ol_bit) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_ipopt_cipso, fr_info_t *, fin, u_short, (optmsk & op->ol_bit)); } else { doi = ipf_checkcipso(fin, s, ol); secmsk = doi >> 16; auth = doi & 0xffff; } break; } optmsk |= op->ol_bit; } if (opt < op->ol_val) i -= mv; else i += mv; mv--; } hlen -= ol; s += ol; } /* * */ if (auth && !(auth & 0x0100)) auth &= 0xff00; fi->fi_optmsk = optmsk; fi->fi_secmsk = secmsk; fi->fi_auth = auth; } /* ------------------------------------------------------------------------ */ /* Function: ipf_checkripso */ /* Returns: void */ /* Parameters: s(I) - pointer to start of RIPSO option */ /* */ /* ------------------------------------------------------------------------ */ static u_32_t -ipf_checkripso(s) - u_char *s; +ipf_checkripso(u_char *s) { const struct optlist *sp; u_short secmsk = 0, auth = 0; u_char sec; int j, m; sec = *(s + 2); /* classification */ for (j = 3, m = 2; m >= 0; ) { sp = secopt + j; if (sec == sp->ol_val) { secmsk |= sp->ol_bit; auth = *(s + 3); auth *= 256; auth += *(s + 4); break; } if (sec < sp->ol_val) j -= m; else j += m; m--; } return (secmsk << 16) | auth; } /* ------------------------------------------------------------------------ */ /* Function: ipf_checkcipso */ /* Returns: u_32_t - 0 = failure, else the doi from the header */ /* Parameters: fin(IO) - pointer to packet information */ /* s(I) - pointer to start of CIPSO option */ /* ol(I) - length of CIPSO option field */ /* */ /* This function returns the domain of integrity (DOI) field from the CIPSO */ /* header and returns that whilst also storing the highest sensitivity */ /* value found in the fr_info_t structure. */ /* */ /* No attempt is made to extract the category bitmaps as these are defined */ /* by the user (rather than the protocol) and can be rather numerous on the */ /* end nodes. */ /* ------------------------------------------------------------------------ */ static u_32_t -ipf_checkcipso(fin, s, ol) - fr_info_t *fin; - u_char *s; - int ol; +ipf_checkcipso(fr_info_t *fin, u_char *s, int ol) { ipf_main_softc_t *softc = fin->fin_main_soft; fr_ip_t *fi; u_32_t doi; u_char *t, tag, tlen, sensitivity; int len; if (ol < 6 || ol > 40) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_cipso_bad); fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_ol, fr_info_t *, fin, u_int, ol); return 0; } fi = &fin->fin_fi; fi->fi_sensitivity = 0; /* * The DOI field MUST be there. */ bcopy(s + 2, &doi, sizeof(doi)); t = (u_char *)s + 6; for (len = ol - 6; len >= 2; len -= tlen, t+= tlen) { tag = *t; tlen = *(t + 1); if (tlen > len || tlen < 4 || tlen > 34) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_cipso_tlen); fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tlen, fr_info_t *, fin, u_int, tlen); return 0; } sensitivity = 0; /* * Tag numbers 0, 1, 2, 5 are laid out in the CIPSO Internet * draft (16 July 1992) that has expired. */ if (tag == 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag, fr_info_t *, fin, u_int, tag); continue; } else if (tag == 1) { if (*(t + 2) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag1_t2, fr_info_t *, fin, u_int, (*t + 2)); continue; } sensitivity = *(t + 3); /* Category bitmap for categories 0-239 */ } else if (tag == 4) { if (*(t + 2) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag4_t2, fr_info_t *, fin, u_int, (*t + 2)); continue; } sensitivity = *(t + 3); /* Enumerated categories, 16bits each, upto 15 */ } else if (tag == 5) { if (*(t + 2) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag5_t2, fr_info_t *, fin, u_int, (*t + 2)); continue; } sensitivity = *(t + 3); /* Range of categories (2*16bits), up to 7 pairs */ } else if (tag > 127) { /* Custom defined DOI */ ; } else { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag127, fr_info_t *, fin, u_int, tag); continue; } if (sensitivity > fi->fi_sensitivity) fi->fi_sensitivity = sensitivity; } return doi; } /* ------------------------------------------------------------------------ */ /* Function: ipf_makefrip */ /* Returns: int - 0 == packet ok, -1 == packet freed */ /* Parameters: hlen(I) - length of IP packet header */ /* ip(I) - pointer to the IP header */ /* fin(IO) - pointer to packet information */ /* */ /* Compact the IP header into a structure which contains just the info. */ /* which is useful for comparing IP headers with and store this information */ /* in the fr_info_t structure pointer to by fin. At present, it is assumed */ /* this function will be called with either an IPv4 or IPv6 packet. */ /* ------------------------------------------------------------------------ */ int -ipf_makefrip(hlen, ip, fin) - int hlen; - ip_t *ip; - fr_info_t *fin; +ipf_makefrip(int hlen, ip_t *ip, fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; int v; fin->fin_depth = 0; fin->fin_hlen = (u_short)hlen; fin->fin_ip = ip; fin->fin_rule = 0xffffffff; fin->fin_group[0] = -1; fin->fin_group[1] = '\0'; fin->fin_dp = (char *)ip + hlen; v = fin->fin_v; if (v == 4) { fin->fin_plen = ntohs(ip->ip_len); fin->fin_dlen = fin->fin_plen - hlen; ipf_pr_ipv4hdr(fin); #ifdef USE_INET6 } else if (v == 6) { fin->fin_plen = ntohs(((ip6_t *)ip)->ip6_plen); fin->fin_dlen = fin->fin_plen; fin->fin_plen += hlen; ipf_pr_ipv6hdr(fin); #endif } if (fin->fin_ip == NULL) { LBUMP(ipf_stats[fin->fin_out].fr_ip_freed); return -1; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_portcheck */ /* Returns: int - 1 == port matched, 0 == port match failed */ /* Parameters: frp(I) - pointer to port check `expression' */ /* pop(I) - port number to evaluate */ /* */ /* Perform a comparison of a port number against some other(s), using a */ /* structure with compare information stored in it. */ /* ------------------------------------------------------------------------ */ static INLINE int -ipf_portcheck(frp, pop) - frpcmp_t *frp; - u_32_t pop; +ipf_portcheck(frpcmp_t *frp, u_32_t pop) { int err = 1; u_32_t po; po = frp->frp_port; /* * Do opposite test to that required and continue if that succeeds. */ switch (frp->frp_cmp) { case FR_EQUAL : if (pop != po) /* EQUAL */ err = 0; break; case FR_NEQUAL : if (pop == po) /* NOTEQUAL */ err = 0; break; case FR_LESST : if (pop >= po) /* LESSTHAN */ err = 0; break; case FR_GREATERT : if (pop <= po) /* GREATERTHAN */ err = 0; break; case FR_LESSTE : if (pop > po) /* LT or EQ */ err = 0; break; case FR_GREATERTE : if (pop < po) /* GT or EQ */ err = 0; break; case FR_OUTRANGE : if (pop >= po && pop <= frp->frp_top) /* Out of range */ err = 0; break; case FR_INRANGE : if (pop <= po || pop >= frp->frp_top) /* In range */ err = 0; break; case FR_INCRANGE : if (pop < po || pop > frp->frp_top) /* Inclusive range */ err = 0; break; default : break; } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tcpudpchk */ /* Returns: int - 1 == protocol matched, 0 == check failed */ /* Parameters: fda(I) - pointer to packet information */ /* ft(I) - pointer to structure with comparison data */ /* */ /* Compares the current pcket (assuming it is TCP/UDP) information with a */ /* structure containing information that we want to match against. */ /* ------------------------------------------------------------------------ */ int -ipf_tcpudpchk(fi, ft) - fr_ip_t *fi; - frtuc_t *ft; +ipf_tcpudpchk(fr_ip_t *fi, frtuc_t *ft) { int err = 1; /* * Both ports should *always* be in the first fragment. * So far, I cannot find any cases where they can not be. * * compare destination ports */ if (ft->ftu_dcmp) err = ipf_portcheck(&ft->ftu_dst, fi->fi_ports[1]); /* * compare source ports */ if (err && ft->ftu_scmp) err = ipf_portcheck(&ft->ftu_src, fi->fi_ports[0]); /* * If we don't have all the TCP/UDP header, then how can we * expect to do any sort of match on it ? If we were looking for * TCP flags, then NO match. If not, then match (which should * satisfy the "short" class too). */ if (err && (fi->fi_p == IPPROTO_TCP)) { if (fi->fi_flx & FI_SHORT) return !(ft->ftu_tcpf | ft->ftu_tcpfm); /* * Match the flags ? If not, abort this match. */ if (ft->ftu_tcpfm && ft->ftu_tcpf != (fi->fi_tcpf & ft->ftu_tcpfm)) { FR_DEBUG(("f. %#x & %#x != %#x\n", fi->fi_tcpf, ft->ftu_tcpfm, ft->ftu_tcpf)); err = 0; } } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_check_ipf */ /* Returns: int - 0 == match, else no match */ /* Parameters: fin(I) - pointer to packet information */ /* fr(I) - pointer to filter rule */ /* portcmp(I) - flag indicating whether to attempt matching on */ /* TCP/UDP port data. */ /* */ /* Check to see if a packet matches an IPFilter rule. Checks of addresses, */ /* port numbers, etc, for "standard" IPFilter rules are all orchestrated in */ /* this function. */ /* ------------------------------------------------------------------------ */ static INLINE int -ipf_check_ipf(fin, fr, portcmp) - fr_info_t *fin; - frentry_t *fr; - int portcmp; +ipf_check_ipf(fr_info_t *fin, frentry_t *fr, int portcmp) { u_32_t *ld, *lm, *lip; fripf_t *fri; fr_ip_t *fi; int i; fi = &fin->fin_fi; fri = fr->fr_ipf; lip = (u_32_t *)fi; lm = (u_32_t *)&fri->fri_mip; ld = (u_32_t *)&fri->fri_ip; /* * first 32 bits to check coversion: * IP version, TOS, TTL, protocol */ i = ((*lip & *lm) != *ld); FR_DEBUG(("0. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (i) return 1; /* * Next 32 bits is a constructed bitmask indicating which IP options * are present (if any) in this packet. */ lip++, lm++, ld++; i = ((*lip & *lm) != *ld); FR_DEBUG(("1. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (i != 0) return 1; lip++, lm++, ld++; /* * Unrolled loops (4 each, for 32 bits) for address checks. */ /* * Check the source address. */ if (fr->fr_satype == FRI_LOOKUP) { i = (*fr->fr_srcfunc)(fin->fin_main_soft, fr->fr_srcptr, fi->fi_v, lip, fin->fin_plen); if (i == -1) return 1; lip += 3; lm += 3; ld += 3; } else { i = ((*lip & *lm) != *ld); FR_DEBUG(("2a. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (fi->fi_v == 6) { lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("2b. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("2c. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("2d. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); } else { lip += 3; lm += 3; ld += 3; } } i ^= (fr->fr_flags & FR_NOTSRCIP) >> 6; if (i != 0) return 1; /* * Check the destination address. */ lip++, lm++, ld++; if (fr->fr_datype == FRI_LOOKUP) { i = (*fr->fr_dstfunc)(fin->fin_main_soft, fr->fr_dstptr, fi->fi_v, lip, fin->fin_plen); if (i == -1) return 1; lip += 3; lm += 3; ld += 3; } else { i = ((*lip & *lm) != *ld); FR_DEBUG(("3a. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (fi->fi_v == 6) { lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3b. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3c. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3d. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); } else { lip += 3; lm += 3; ld += 3; } } i ^= (fr->fr_flags & FR_NOTDSTIP) >> 7; if (i != 0) return 1; /* * IP addresses matched. The next 32bits contains: * mast of old IP header security & authentication bits. */ lip++, lm++, ld++; i = (*ld - (*lip & *lm)); FR_DEBUG(("4. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); /* * Next we have 32 bits of packet flags. */ lip++, lm++, ld++; i |= (*ld - (*lip & *lm)); FR_DEBUG(("5. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); if (i == 0) { /* * If a fragment, then only the first has what we're * looking for here... */ if (portcmp) { if (!ipf_tcpudpchk(&fin->fin_fi, &fr->fr_tuc)) i = 1; } else { if (fr->fr_dcmp || fr->fr_scmp || fr->fr_tcpf || fr->fr_tcpfm) i = 1; if (fr->fr_icmpm || fr->fr_icmp) { if (((fi->fi_p != IPPROTO_ICMP) && (fi->fi_p != IPPROTO_ICMPV6)) || fin->fin_off || (fin->fin_dlen < 2)) i = 1; else if ((fin->fin_data[0] & fr->fr_icmpm) != fr->fr_icmp) { FR_DEBUG(("i. %#x & %#x != %#x\n", fin->fin_data[0], fr->fr_icmpm, fr->fr_icmp)); i = 1; } } } } return i; } /* ------------------------------------------------------------------------ */ /* Function: ipf_scanlist */ /* Returns: int - result flags of scanning filter list */ /* Parameters: fin(I) - pointer to packet information */ /* pass(I) - default result to return for filtering */ /* */ /* Check the input/output list of rules for a match to the current packet. */ /* If a match is found, the value of fr_flags from the rule becomes the */ /* return value and fin->fin_fr points to the matched rule. */ /* */ /* This function may be called recursively upto 16 times (limit inbuilt.) */ /* When unwinding, it should finish up with fin_depth as 0. */ /* */ /* Could be per interface, but this gets real nasty when you don't have, */ /* or can't easily change, the kernel source code to . */ /* ------------------------------------------------------------------------ */ int -ipf_scanlist(fin, pass) - fr_info_t *fin; - u_32_t pass; +ipf_scanlist(fr_info_t *fin, u_32_t pass) { ipf_main_softc_t *softc = fin->fin_main_soft; int rulen, portcmp, off, skip; struct frentry *fr, *fnext; u_32_t passt, passo; /* * Do not allow nesting deeper than 16 levels. */ if (fin->fin_depth >= 16) return pass; fr = fin->fin_fr; /* * If there are no rules in this list, return now. */ if (fr == NULL) return pass; skip = 0; portcmp = 0; fin->fin_depth++; fin->fin_fr = NULL; off = fin->fin_off; if ((fin->fin_flx & FI_TCPUDP) && (fin->fin_dlen > 3) && !off) portcmp = 1; for (rulen = 0; fr; fr = fnext, rulen++) { fnext = fr->fr_next; if (skip != 0) { FR_VERBOSE(("SKIP %d (%#x)\n", skip, fr->fr_flags)); skip--; continue; } /* * In all checks below, a null (zero) value in the * filter struture is taken to mean a wildcard. * * check that we are working for the right interface */ #ifdef _KERNEL if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp) continue; #else if (opts & (OPT_VERBOSE|OPT_DEBUG)) printf("\n"); FR_VERBOSE(("%c", FR_ISSKIP(pass) ? 's' : FR_ISPASS(pass) ? 'p' : FR_ISACCOUNT(pass) ? 'A' : FR_ISAUTH(pass) ? 'a' : (pass & FR_NOMATCH) ? 'n' :'b')); if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp) continue; FR_VERBOSE((":i")); #endif switch (fr->fr_type) { case FR_T_IPF : case FR_T_IPF_BUILTIN : if (ipf_check_ipf(fin, fr, portcmp)) continue; break; #if defined(IPFILTER_BPF) case FR_T_BPFOPC : case FR_T_BPFOPC_BUILTIN : { u_char *mc; int wlen; if (*fin->fin_mp == NULL) continue; if (fin->fin_family != fr->fr_family) continue; mc = (u_char *)fin->fin_m; wlen = fin->fin_dlen + fin->fin_hlen; if (!bpf_filter(fr->fr_data, mc, wlen, 0)) continue; break; } #endif case FR_T_CALLFUNC_BUILTIN : { frentry_t *f; f = (*fr->fr_func)(fin, &pass); if (f != NULL) fr = f; else continue; break; } case FR_T_IPFEXPR : case FR_T_IPFEXPR_BUILTIN : if (fin->fin_family != fr->fr_family) continue; if (ipf_fr_matcharray(fin, fr->fr_data) == 0) continue; break; default : break; } if ((fin->fin_out == 0) && (fr->fr_nattag.ipt_num[0] != 0)) { if (fin->fin_nattag == NULL) continue; if (ipf_matchtag(&fr->fr_nattag, fin->fin_nattag) == 0) continue; } FR_VERBOSE(("=%d/%d.%d *", fr->fr_grhead, fr->fr_group, rulen)); passt = fr->fr_flags; /* * If the rule is a "call now" rule, then call the function * in the rule, if it exists and use the results from that. * If the function pointer is bad, just make like we ignore * it, except for increasing the hit counter. */ if ((passt & FR_CALLNOW) != 0) { frentry_t *frs; ATOMIC_INC64(fr->fr_hits); if ((fr->fr_func == NULL) || (fr->fr_func == (ipfunc_t)-1)) continue; frs = fin->fin_fr; fin->fin_fr = fr; fr = (*fr->fr_func)(fin, &passt); if (fr == NULL) { fin->fin_fr = frs; continue; } passt = fr->fr_flags; } fin->fin_fr = fr; #ifdef IPFILTER_LOG /* * Just log this packet... */ if ((passt & FR_LOGMASK) == FR_LOG) { if (ipf_log_pkt(fin, passt) == -1) { if (passt & FR_LOGORBLOCK) { DT(frb_logfail); passt &= ~FR_CMDMASK; passt |= FR_BLOCK|FR_QUICK; fin->fin_reason = FRB_LOGFAIL; } } } #endif /* IPFILTER_LOG */ MUTEX_ENTER(&fr->fr_lock); fr->fr_bytes += (U_QUAD_T)fin->fin_plen; fr->fr_hits++; MUTEX_EXIT(&fr->fr_lock); fin->fin_rule = rulen; passo = pass; if (FR_ISSKIP(passt)) { skip = fr->fr_arg; continue; } else if (((passt & FR_LOGMASK) != FR_LOG) && ((passt & FR_LOGMASK) != FR_DECAPSULATE)) { pass = passt; } if (passt & (FR_RETICMP|FR_FAKEICMP)) fin->fin_icode = fr->fr_icode; if (fr->fr_group != -1) { (void) strncpy(fin->fin_group, FR_NAME(fr, fr_group), strlen(FR_NAME(fr, fr_group))); } else { fin->fin_group[0] = '\0'; } FR_DEBUG(("pass %#x/%#x/%x\n", passo, pass, passt)); if (fr->fr_grphead != NULL) { fin->fin_fr = fr->fr_grphead->fg_start; FR_VERBOSE(("group %s\n", FR_NAME(fr, fr_grhead))); if (FR_ISDECAPS(passt)) passt = ipf_decaps(fin, pass, fr->fr_icode); else passt = ipf_scanlist(fin, pass); if (fin->fin_fr == NULL) { fin->fin_rule = rulen; if (fr->fr_group != -1) (void) strncpy(fin->fin_group, fr->fr_names + fr->fr_group, strlen(fr->fr_names + fr->fr_group)); fin->fin_fr = fr; passt = pass; } pass = passt; } if (pass & FR_QUICK) { /* * Finally, if we've asked to track state for this * packet, set it up. Add state for "quick" rules * here so that if the action fails we can consider * the rule to "not match" and keep on processing * filter rules. */ if ((pass & FR_KEEPSTATE) && !FR_ISAUTH(pass) && !(fin->fin_flx & FI_STATE)) { int out = fin->fin_out; fin->fin_fr = fr; if (ipf_state_add(softc, fin, NULL, 0) == 0) { LBUMPD(ipf_stats[out], fr_ads); } else { LBUMPD(ipf_stats[out], fr_bads); pass = passo; continue; } } break; } } fin->fin_depth--; return pass; } /* ------------------------------------------------------------------------ */ /* Function: ipf_acctpkt */ /* Returns: frentry_t* - always returns NULL */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Checks a packet against accounting rules, if there are any for the given */ /* IP protocol version. */ /* */ /* N.B.: this function returns NULL to match the prototype used by other */ /* functions called from the IPFilter "mainline" in ipf_check(). */ /* ------------------------------------------------------------------------ */ frentry_t * -ipf_acctpkt(fin, passp) - fr_info_t *fin; - u_32_t *passp; +ipf_acctpkt(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc = fin->fin_main_soft; char group[FR_GROUPLEN]; frentry_t *fr, *frsave; u_32_t pass, rulen; passp = passp; fr = softc->ipf_acct[fin->fin_out][softc->ipf_active]; if (fr != NULL) { frsave = fin->fin_fr; bcopy(fin->fin_group, group, FR_GROUPLEN); rulen = fin->fin_rule; fin->fin_fr = fr; pass = ipf_scanlist(fin, FR_NOMATCH); if (FR_ISACCOUNT(pass)) { LBUMPD(ipf_stats[0], fr_acct); } fin->fin_fr = frsave; bcopy(group, fin->fin_group, FR_GROUPLEN); fin->fin_rule = rulen; } return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_firewall */ /* Returns: frentry_t* - returns pointer to matched rule, if no matches */ /* were found, returns NULL. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Applies an appropriate set of firewall rules to the packet, to see if */ /* there are any matches. The first check is to see if a match can be seen */ /* in the cache. If not, then search an appropriate list of rules. Once a */ /* matching rule is found, take any appropriate actions as defined by the */ /* rule - except logging. */ /* ------------------------------------------------------------------------ */ static frentry_t * -ipf_firewall(fin, passp) - fr_info_t *fin; - u_32_t *passp; +ipf_firewall(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc = fin->fin_main_soft; frentry_t *fr; u_32_t pass; int out; out = fin->fin_out; pass = *passp; /* * This rule cache will only affect packets that are not being * statefully filtered. */ fin->fin_fr = softc->ipf_rules[out][softc->ipf_active]; if (fin->fin_fr != NULL) pass = ipf_scanlist(fin, softc->ipf_pass); if ((pass & FR_NOMATCH)) { LBUMPD(ipf_stats[out], fr_nom); } fr = fin->fin_fr; /* * Apply packets per second rate-limiting to a rule as required. */ if ((fr != NULL) && (fr->fr_pps != 0) && !ppsratecheck(&fr->fr_lastpkt, &fr->fr_curpps, fr->fr_pps)) { DT2(frb_ppsrate, fr_info_t *, fin, frentry_t *, fr); pass &= ~(FR_CMDMASK|FR_RETICMP|FR_RETRST); pass |= FR_BLOCK; LBUMPD(ipf_stats[out], fr_ppshit); fin->fin_reason = FRB_PPSRATE; } /* * If we fail to add a packet to the authorization queue, then we * drop the packet later. However, if it was added then pretend * we've dropped it already. */ if (FR_ISAUTH(pass)) { if (ipf_auth_new(fin->fin_m, fin) != 0) { DT1(frb_authnew, fr_info_t *, fin); fin->fin_m = *fin->fin_mp = NULL; fin->fin_reason = FRB_AUTHNEW; fin->fin_error = 0; } else { IPFERROR(1); fin->fin_error = ENOSPC; } } if ((fr != NULL) && (fr->fr_func != NULL) && (fr->fr_func != (ipfunc_t)-1) && !(pass & FR_CALLNOW)) (void) (*fr->fr_func)(fin, &pass); /* * If a rule is a pre-auth rule, check again in the list of rules * loaded for authenticated use. It does not particulary matter * if this search fails because a "preauth" result, from a rule, * is treated as "not a pass", hence the packet is blocked. */ if (FR_ISPREAUTH(pass)) { pass = ipf_auth_pre_scanlist(softc, fin, pass); } /* * If the rule has "keep frag" and the packet is actually a fragment, * then create a fragment state entry. */ if (pass & FR_KEEPFRAG) { if (fin->fin_flx & FI_FRAG) { if (ipf_frag_new(softc, fin, pass) == -1) { LBUMP(ipf_stats[out].fr_bnfr); } else { LBUMP(ipf_stats[out].fr_nfr); } } else { LBUMP(ipf_stats[out].fr_cfr); } } fr = fin->fin_fr; *passp = pass; return fr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_check */ /* Returns: int - 0 == packet allowed through, */ /* User space: */ /* -1 == packet blocked */ /* 1 == packet not matched */ /* -2 == requires authentication */ /* Kernel: */ /* > 0 == filter error # for packet */ /* Parameters: ctx(I) - pointer to the instance context */ /* ip(I) - pointer to start of IPv4/6 packet */ /* hlen(I) - length of header */ /* ifp(I) - pointer to interface this packet is on */ /* out(I) - 0 == packet going in, 1 == packet going out */ /* mp(IO) - pointer to caller's buffer pointer that holds this */ /* IP packet. */ /* Solaris: */ /* qpi(I) - pointer to STREAMS queue information for this */ /* interface & direction. */ /* */ /* ipf_check() is the master function for all IPFilter packet processing. */ /* It orchestrates: Network Address Translation (NAT), checking for packet */ /* authorisation (or pre-authorisation), presence of related state info., */ /* generating log entries, IP packet accounting, routing of packets as */ /* directed by firewall rules and of course whether or not to allow the */ /* packet to be further processed by the kernel. */ /* */ /* For packets blocked, the contents of "mp" will be NULL'd and the buffer */ /* freed. Packets passed may be returned with the pointer pointed to by */ /* by "mp" changed to a new buffer. */ /* ------------------------------------------------------------------------ */ int -ipf_check(ctx, ip, hlen, ifp, out +ipf_check(void *ctx, ip_t *ip, int hlen, struct ifnet *ifp, int out #if defined(_KERNEL) && SOLARIS - , qif, mp) - void *qif; + , void* qif, mb_t **mp) #else - , mp) + , mb_t **mp) #endif - mb_t **mp; - ip_t *ip; - int hlen; - struct ifnet *ifp; - int out; - void *ctx; { /* * The above really sucks, but short of writing a diff */ ipf_main_softc_t *softc = ctx; fr_info_t frinfo; fr_info_t *fin = &frinfo; u_32_t pass = softc->ipf_pass; frentry_t *fr = NULL; int v = IP_V(ip); mb_t *mc = NULL; mb_t *m; /* * The first part of ipf_check() deals with making sure that what goes * into the filtering engine makes some sense. Information about the * the packet is distilled, collected into a fr_info_t structure and * the an attempt to ensure the buffer the packet is in is big enough * to hold all the required packet headers. */ #ifdef _KERNEL # if SOLARIS qpktinfo_t *qpi = qif; # ifdef __sparc if ((u_int)ip & 0x3) return 2; # endif # else SPL_INT(s); # endif if (softc->ipf_running <= 0) { return 0; } bzero((char *)fin, sizeof(*fin)); # if SOLARIS if (qpi->qpi_flags & QF_BROADCAST) fin->fin_flx |= FI_MBCAST|FI_BROADCAST; if (qpi->qpi_flags & QF_MULTICAST) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; m = qpi->qpi_m; fin->fin_qfm = m; fin->fin_qpi = qpi; # else /* SOLARIS */ m = *mp; # if defined(M_MCAST) if ((m->m_flags & M_MCAST) != 0) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; # endif # if defined(M_MLOOP) if ((m->m_flags & M_MLOOP) != 0) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; # endif # if defined(M_BCAST) if ((m->m_flags & M_BCAST) != 0) fin->fin_flx |= FI_MBCAST|FI_BROADCAST; # endif # ifdef M_CANFASTFWD /* * XXX For now, IP Filter and fast-forwarding of cached flows * XXX are mutually exclusive. Eventually, IP Filter should * XXX get a "can-fast-forward" filter rule. */ m->m_flags &= ~M_CANFASTFWD; # endif /* M_CANFASTFWD */ # if defined(CSUM_DELAY_DATA) && !defined(__FreeBSD__) /* * disable delayed checksums. */ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { in_delayed_cksum(m); m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } # endif /* CSUM_DELAY_DATA */ # endif /* SOLARIS */ #else bzero((char *)fin, sizeof(*fin)); m = *mp; # if defined(M_MCAST) if ((m->m_flags & M_MCAST) != 0) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; # endif # if defined(M_MLOOP) if ((m->m_flags & M_MLOOP) != 0) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; # endif # if defined(M_BCAST) if ((m->m_flags & M_BCAST) != 0) fin->fin_flx |= FI_MBCAST|FI_BROADCAST; # endif #endif /* _KERNEL */ fin->fin_v = v; fin->fin_m = m; fin->fin_ip = ip; fin->fin_mp = mp; fin->fin_out = out; fin->fin_ifp = ifp; fin->fin_error = ENETUNREACH; fin->fin_hlen = (u_short)hlen; fin->fin_dp = (char *)ip + hlen; fin->fin_main_soft = softc; fin->fin_ipoff = (char *)ip - MTOD(m, char *); SPL_NET(s); #ifdef USE_INET6 if (v == 6) { LBUMP(ipf_stats[out].fr_ipv6); /* * Jumbo grams are quite likely too big for internal buffer * structures to handle comfortably, for now, so just drop * them. */ if (((ip6_t *)ip)->ip6_plen == 0) { DT1(frb_jumbo, ip6_t *, (ip6_t *)ip); pass = FR_BLOCK|FR_NOMATCH; fin->fin_reason = FRB_JUMBO; goto finished; } fin->fin_family = AF_INET6; } else #endif { fin->fin_family = AF_INET; } if (ipf_makefrip(hlen, ip, fin) == -1) { DT1(frb_makefrip, fr_info_t *, fin); pass = FR_BLOCK|FR_NOMATCH; fin->fin_reason = FRB_MAKEFRIP; goto finished; } /* * For at least IPv6 packets, if a m_pullup() fails then this pointer * becomes NULL and so we have no packet to free. */ if (*fin->fin_mp == NULL) goto finished; if (!out) { if (v == 4) { if (softc->ipf_chksrc && !ipf_verifysrc(fin)) { LBUMPD(ipf_stats[0], fr_v4_badsrc); fin->fin_flx |= FI_BADSRC; } if (fin->fin_ip->ip_ttl < softc->ipf_minttl) { LBUMPD(ipf_stats[0], fr_v4_badttl); fin->fin_flx |= FI_LOWTTL; } } #ifdef USE_INET6 else if (v == 6) { if (((ip6_t *)ip)->ip6_hlim < softc->ipf_minttl) { LBUMPD(ipf_stats[0], fr_v6_badttl); fin->fin_flx |= FI_LOWTTL; } } #endif } if (fin->fin_flx & FI_SHORT) { LBUMPD(ipf_stats[out], fr_short); } READ_ENTER(&softc->ipf_mutex); if (!out) { switch (fin->fin_v) { case 4 : if (ipf_nat_checkin(fin, &pass) == -1) { goto filterdone; } break; #ifdef USE_INET6 case 6 : if (ipf_nat6_checkin(fin, &pass) == -1) { goto filterdone; } break; #endif default : break; } } /* * Check auth now. * If a packet is found in the auth table, then skip checking * the access lists for permission but we do need to consider * the result as if it were from the ACL's. In addition, being * found in the auth table means it has been seen before, so do * not pass it through accounting (again), lest it be counted twice. */ fr = ipf_auth_check(fin, &pass); if (!out && (fr == NULL)) (void) ipf_acctpkt(fin, NULL); if (fr == NULL) { if ((fin->fin_flx & FI_FRAG) != 0) fr = ipf_frag_known(fin, &pass); if (fr == NULL) fr = ipf_state_check(fin, &pass); } if ((pass & FR_NOMATCH) || (fr == NULL)) fr = ipf_firewall(fin, &pass); /* * If we've asked to track state for this packet, set it up. * Here rather than ipf_firewall because ipf_checkauth may decide * to return a packet for "keep state" */ if ((pass & FR_KEEPSTATE) && (fin->fin_m != NULL) && !(fin->fin_flx & FI_STATE)) { if (ipf_state_add(softc, fin, NULL, 0) == 0) { LBUMP(ipf_stats[out].fr_ads); } else { LBUMP(ipf_stats[out].fr_bads); if (FR_ISPASS(pass)) { DT(frb_stateadd); pass &= ~FR_CMDMASK; pass |= FR_BLOCK; fin->fin_reason = FRB_STATEADD; } } } fin->fin_fr = fr; if ((fr != NULL) && !(fin->fin_flx & FI_STATE)) { fin->fin_dif = &fr->fr_dif; fin->fin_tif = &fr->fr_tifs[fin->fin_rev]; } /* * Only count/translate packets which will be passed on, out the * interface. */ if (out && FR_ISPASS(pass)) { (void) ipf_acctpkt(fin, NULL); switch (fin->fin_v) { case 4 : if (ipf_nat_checkout(fin, &pass) == -1) { ; } else if ((softc->ipf_update_ipid != 0) && (v == 4)) { if (ipf_updateipid(fin) == -1) { DT(frb_updateipid); LBUMP(ipf_stats[1].fr_ipud); pass &= ~FR_CMDMASK; pass |= FR_BLOCK; fin->fin_reason = FRB_UPDATEIPID; } else { LBUMP(ipf_stats[0].fr_ipud); } } break; #ifdef USE_INET6 case 6 : (void) ipf_nat6_checkout(fin, &pass); break; #endif default : break; } } filterdone: #ifdef IPFILTER_LOG if ((softc->ipf_flags & FF_LOGGING) || (pass & FR_LOGMASK)) { (void) ipf_dolog(fin, &pass); } #endif /* * The FI_STATE flag is cleared here so that calling ipf_state_check * will work when called from inside of fr_fastroute. Although * there is a similar flag, FI_NATED, for NAT, it does have the same * impact on code execution. */ fin->fin_flx &= ~FI_STATE; #if defined(FASTROUTE_RECURSION) /* * Up the reference on fr_lock and exit ipf_mutex. The generation of * a packet below can sometimes cause a recursive call into IPFilter. * On those platforms where that does happen, we need to hang onto * the filter rule just in case someone decides to remove or flush it * in the meantime. */ if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } RWLOCK_EXIT(&softc->ipf_mutex); #endif if ((pass & FR_RETMASK) != 0) { /* * Should we return an ICMP packet to indicate error * status passing through the packet filter ? * WARNING: ICMP error packets AND TCP RST packets should * ONLY be sent in repsonse to incoming packets. Sending * them in response to outbound packets can result in a * panic on some operating systems. */ if (!out) { if (pass & FR_RETICMP) { int dst; if ((pass & FR_RETMASK) == FR_FAKEICMP) dst = 1; else dst = 0; (void) ipf_send_icmp_err(ICMP_UNREACH, fin, dst); LBUMP(ipf_stats[0].fr_ret); } else if (((pass & FR_RETMASK) == FR_RETRST) && !(fin->fin_flx & FI_SHORT)) { if (((fin->fin_flx & FI_OOW) != 0) || (ipf_send_reset(fin) == 0)) { LBUMP(ipf_stats[1].fr_ret); } } /* * When using return-* with auth rules, the auth code * takes over disposing of this packet. */ if (FR_ISAUTH(pass) && (fin->fin_m != NULL)) { DT1(frb_authcapture, fr_info_t *, fin); fin->fin_m = *fin->fin_mp = NULL; fin->fin_reason = FRB_AUTHCAPTURE; m = NULL; } } else { if (pass & FR_RETRST) { fin->fin_error = ECONNRESET; } } } /* * After the above so that ICMP unreachables and TCP RSTs get * created properly. */ if (FR_ISBLOCK(pass) && (fin->fin_flx & FI_NEWNAT)) ipf_nat_uncreate(fin); /* * If we didn't drop off the bottom of the list of rules (and thus * the 'current' rule fr is not NULL), then we may have some extra * instructions about what to do with a packet. * Once we're finished return to our caller, freeing the packet if * we are dropping it. */ if (fr != NULL) { frdest_t *fdp; /* * Generate a duplicated packet first because ipf_fastroute * can lead to fin_m being free'd... not good. */ fdp = fin->fin_dif; if ((fdp != NULL) && (fdp->fd_ptr != NULL) && (fdp->fd_ptr != (void *)-1)) { mc = M_COPY(fin->fin_m); if (mc != NULL) ipf_fastroute(mc, &mc, fin, fdp); } fdp = fin->fin_tif; if (!out && (pass & FR_FASTROUTE)) { /* * For fastroute rule, no destination interface defined * so pass NULL as the frdest_t parameter */ (void) ipf_fastroute(fin->fin_m, mp, fin, NULL); m = *mp = NULL; } else if ((fdp != NULL) && (fdp->fd_ptr != NULL) && (fdp->fd_ptr != (struct ifnet *)-1)) { /* this is for to rules: */ ipf_fastroute(fin->fin_m, mp, fin, fdp); m = *mp = NULL; } #if defined(FASTROUTE_RECURSION) (void) ipf_derefrule(softc, &fr); #endif } #if !defined(FASTROUTE_RECURSION) RWLOCK_EXIT(&softc->ipf_mutex); #endif finished: if (!FR_ISPASS(pass)) { LBUMP(ipf_stats[out].fr_block); if (*mp != NULL) { #ifdef _KERNEL FREE_MB_T(*mp); #endif m = *mp = NULL; } } else { LBUMP(ipf_stats[out].fr_pass); } SPL_X(s); #ifdef _KERNEL if (FR_ISPASS(pass)) return 0; LBUMP(ipf_stats[out].fr_blocked[fin->fin_reason]); return fin->fin_error; #else /* _KERNEL */ if (*mp != NULL) (*mp)->mb_ifp = fin->fin_ifp; blockreason = fin->fin_reason; FR_VERBOSE(("fin_flx %#x pass %#x ", fin->fin_flx, pass)); /*if ((pass & FR_CMDMASK) == (softc->ipf_pass & FR_CMDMASK))*/ if ((pass & FR_NOMATCH) != 0) return 1; if ((pass & FR_RETMASK) != 0) switch (pass & FR_RETMASK) { case FR_RETRST : return 3; case FR_RETICMP : return 4; case FR_FAKEICMP : return 5; } switch (pass & FR_CMDMASK) { case FR_PASS : return 0; case FR_BLOCK : return -1; case FR_AUTH : return -2; case FR_ACCOUNT : return -3; case FR_PREAUTH : return -4; } return 2; #endif /* _KERNEL */ } #ifdef IPFILTER_LOG /* ------------------------------------------------------------------------ */ /* Function: ipf_dolog */ /* Returns: frentry_t* - returns contents of fin_fr (no change made) */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Checks flags set to see how a packet should be logged, if it is to be */ /* logged. Adjust statistics based on its success or not. */ /* ------------------------------------------------------------------------ */ frentry_t * -ipf_dolog(fin, passp) - fr_info_t *fin; - u_32_t *passp; +ipf_dolog(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc = fin->fin_main_soft; u_32_t pass; int out; out = fin->fin_out; pass = *passp; if ((softc->ipf_flags & FF_LOGNOMATCH) && (pass & FR_NOMATCH)) { pass |= FF_LOGNOMATCH; LBUMPD(ipf_stats[out], fr_npkl); goto logit; } else if (((pass & FR_LOGMASK) == FR_LOGP) || (FR_ISPASS(pass) && (softc->ipf_flags & FF_LOGPASS))) { if ((pass & FR_LOGMASK) != FR_LOGP) pass |= FF_LOGPASS; LBUMPD(ipf_stats[out], fr_ppkl); goto logit; } else if (((pass & FR_LOGMASK) == FR_LOGB) || (FR_ISBLOCK(pass) && (softc->ipf_flags & FF_LOGBLOCK))) { if ((pass & FR_LOGMASK) != FR_LOGB) pass |= FF_LOGBLOCK; LBUMPD(ipf_stats[out], fr_bpkl); logit: if (ipf_log_pkt(fin, pass) == -1) { /* * If the "or-block" option has been used then * block the packet if we failed to log it. */ if ((pass & FR_LOGORBLOCK) && FR_ISPASS(pass)) { DT1(frb_logfail2, u_int, pass); pass &= ~FR_CMDMASK; pass |= FR_BLOCK; fin->fin_reason = FRB_LOGFAIL2; } } *passp = pass; } return fin->fin_fr; } #endif /* IPFILTER_LOG */ /* ------------------------------------------------------------------------ */ /* Function: ipf_cksum */ /* Returns: u_short - IP header checksum */ /* Parameters: addr(I) - pointer to start of buffer to checksum */ /* len(I) - length of buffer in bytes */ /* */ /* Calculate the two's complement 16 bit checksum of the buffer passed. */ /* */ /* N.B.: addr should be 16bit aligned. */ /* ------------------------------------------------------------------------ */ u_short -ipf_cksum(addr, len) - u_short *addr; - int len; +ipf_cksum(u_short *addr, int len) { u_32_t sum = 0; for (sum = 0; len > 1; len -= 2) sum += *addr++; /* mop up an odd byte, if necessary */ if (len == 1) sum += *(u_char *)addr; /* * add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ return (u_short)(~sum); } /* ------------------------------------------------------------------------ */ /* Function: fr_cksum */ /* Returns: u_short - layer 4 checksum */ /* Parameters: fin(I) - pointer to packet information */ /* ip(I) - pointer to IP header */ /* l4proto(I) - protocol to caclulate checksum for */ /* l4hdr(I) - pointer to layer 4 header */ /* */ /* Calculates the TCP checksum for the packet held in "m", using the data */ /* in the IP header "ip" to seed it. */ /* */ /* NB: This function assumes we've pullup'd enough for all of the IP header */ /* and the TCP header. We also assume that data blocks aren't allocated in */ /* odd sizes. */ /* */ /* Expects ip_len and ip_off to be in network byte order when called. */ /* ------------------------------------------------------------------------ */ u_short -fr_cksum(fin, ip, l4proto, l4hdr) - fr_info_t *fin; - ip_t *ip; - int l4proto; - void *l4hdr; +fr_cksum(fr_info_t *fin, ip_t *ip, int l4proto, void *l4hdr) { u_short *sp, slen, sumsave, *csump; u_int sum, sum2; int hlen; int off; #ifdef USE_INET6 ip6_t *ip6; #endif csump = NULL; sumsave = 0; sp = NULL; slen = 0; hlen = 0; sum = 0; sum = htons((u_short)l4proto); /* * Add up IP Header portion */ #ifdef USE_INET6 if (IP_V(ip) == 4) { #endif hlen = IP_HL(ip) << 2; off = hlen; sp = (u_short *)&ip->ip_src; sum += *sp++; /* ip_src */ sum += *sp++; sum += *sp++; /* ip_dst */ sum += *sp++; slen = fin->fin_plen - off; sum += htons(slen); #ifdef USE_INET6 } else if (IP_V(ip) == 6) { mb_t *m; m = fin->fin_m; ip6 = (ip6_t *)ip; off = ((caddr_t)ip6 - m->m_data) + sizeof(struct ip6_hdr); int len = ntohs(ip6->ip6_plen) - (off - sizeof(*ip6)); return(ipf_pcksum6(m, ip6, off, len)); } else { return 0xffff; } #endif switch (l4proto) { case IPPROTO_UDP : csump = &((udphdr_t *)l4hdr)->uh_sum; break; case IPPROTO_TCP : csump = &((tcphdr_t *)l4hdr)->th_sum; break; case IPPROTO_ICMP : csump = &((icmphdr_t *)l4hdr)->icmp_cksum; sum = 0; /* Pseudo-checksum is not included */ break; #ifdef USE_INET6 case IPPROTO_ICMPV6 : csump = &((struct icmp6_hdr *)l4hdr)->icmp6_cksum; break; #endif default : break; } if (csump != NULL) { sumsave = *csump; *csump = 0; } sum2 = ipf_pcksum(fin, off, sum); if (csump != NULL) *csump = sumsave; return sum2; } /* ------------------------------------------------------------------------ */ /* Function: ipf_findgroup */ /* Returns: frgroup_t * - NULL = group not found, else pointer to group */ /* Parameters: softc(I) - pointer to soft context main structure */ /* group(I) - group name to search for */ /* unit(I) - device to which this group belongs */ /* set(I) - which set of rules (inactive/inactive) this is */ /* fgpp(O) - pointer to place to store pointer to the pointer */ /* to where to add the next (last) group or where */ /* to delete group from. */ /* */ /* Search amongst the defined groups for a particular group number. */ /* ------------------------------------------------------------------------ */ frgroup_t * -ipf_findgroup(softc, group, unit, set, fgpp) - ipf_main_softc_t *softc; - char *group; - minor_t unit; - int set; - frgroup_t ***fgpp; +ipf_findgroup(ipf_main_softc_t *softc, char *group, minor_t unit, int set, + frgroup_t ***fgpp) { frgroup_t *fg, **fgp; /* * Which list of groups to search in is dependent on which list of * rules are being operated on. */ fgp = &softc->ipf_groups[unit][set]; while ((fg = *fgp) != NULL) { if (strncmp(group, fg->fg_name, FR_GROUPLEN) == 0) break; else fgp = &fg->fg_next; } if (fgpp != NULL) *fgpp = fgp; return fg; } /* ------------------------------------------------------------------------ */ /* Function: ipf_group_add */ /* Returns: frgroup_t * - NULL == did not create group, */ /* != NULL == pointer to the group */ /* Parameters: softc(I) - pointer to soft context main structure */ /* num(I) - group number to add */ /* head(I) - rule pointer that is using this as the head */ /* flags(I) - rule flags which describe the type of rule it is */ /* unit(I) - device to which this group will belong to */ /* set(I) - which set of rules (inactive/inactive) this is */ /* Write Locks: ipf_mutex */ /* */ /* Add a new group head, or if it already exists, increase the reference */ /* count to it. */ /* ------------------------------------------------------------------------ */ frgroup_t * -ipf_group_add(softc, group, head, flags, unit, set) - ipf_main_softc_t *softc; - char *group; - void *head; - u_32_t flags; - minor_t unit; - int set; +ipf_group_add(ipf_main_softc_t *softc, char *group, void *head, u_32_t flags, + minor_t unit, int set) { frgroup_t *fg, **fgp; u_32_t gflags; if (group == NULL) return NULL; if (unit == IPL_LOGIPF && *group == '\0') return NULL; fgp = NULL; gflags = flags & FR_INOUT; fg = ipf_findgroup(softc, group, unit, set, &fgp); if (fg != NULL) { if (fg->fg_head == NULL && head != NULL) fg->fg_head = head; if (fg->fg_flags == 0) fg->fg_flags = gflags; else if (gflags != fg->fg_flags) return NULL; fg->fg_ref++; return fg; } KMALLOC(fg, frgroup_t *); if (fg != NULL) { fg->fg_head = head; fg->fg_start = NULL; fg->fg_next = *fgp; bcopy(group, fg->fg_name, strlen(group) + 1); fg->fg_flags = gflags; fg->fg_ref = 1; fg->fg_set = &softc->ipf_groups[unit][set]; *fgp = fg; } return fg; } /* ------------------------------------------------------------------------ */ /* Function: ipf_group_del */ /* Returns: int - number of rules deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* group(I) - group name to delete */ /* fr(I) - filter rule from which group is referenced */ /* Write Locks: ipf_mutex */ /* */ /* This function is called whenever a reference to a group is to be dropped */ /* and thus its reference count needs to be lowered and the group free'd if */ /* the reference count reaches zero. Passing in fr is really for the sole */ /* purpose of knowing when the head rule is being deleted. */ /* ------------------------------------------------------------------------ */ void -ipf_group_del(softc, group, fr) - ipf_main_softc_t *softc; - frgroup_t *group; - frentry_t *fr; +ipf_group_del(ipf_main_softc_t *softc, frgroup_t *group, frentry_t *fr) { if (group->fg_head == fr) group->fg_head = NULL; group->fg_ref--; if ((group->fg_ref == 0) && (group->fg_start == NULL)) ipf_group_free(group); } /* ------------------------------------------------------------------------ */ /* Function: ipf_group_free */ /* Returns: Nil */ /* Parameters: group(I) - pointer to filter rule group */ /* */ /* Remove the group from the list of groups and free it. */ /* ------------------------------------------------------------------------ */ static void -ipf_group_free(group) - frgroup_t *group; +ipf_group_free(frgroup_t *group) { frgroup_t **gp; for (gp = group->fg_set; *gp != NULL; gp = &(*gp)->fg_next) { if (*gp == group) { *gp = group->fg_next; break; } } KFREE(group); } /* ------------------------------------------------------------------------ */ /* Function: ipf_group_flush */ /* Returns: int - number of rules flush from group */ /* Parameters: softc(I) - pointer to soft context main structure */ /* Parameters: group(I) - pointer to filter rule group */ /* */ /* Remove all of the rules that currently are listed under the given group. */ /* ------------------------------------------------------------------------ */ static int -ipf_group_flush(softc, group) - ipf_main_softc_t *softc; - frgroup_t *group; +ipf_group_flush(ipf_main_softc_t *softc, frgroup_t *group) { int gone = 0; (void) ipf_flushlist(softc, &gone, &group->fg_start); return gone; } /* ------------------------------------------------------------------------ */ /* Function: ipf_getrulen */ /* Returns: frentry_t * - NULL == not found, else pointer to rule n */ /* Parameters: softc(I) - pointer to soft context main structure */ /* Parameters: unit(I) - device for which to count the rule's number */ /* flags(I) - which set of rules to find the rule in */ /* group(I) - group name */ /* n(I) - rule number to find */ /* */ /* Find rule # n in group # g and return a pointer to it. Return NULl if */ /* group # g doesn't exist or there are less than n rules in the group. */ /* ------------------------------------------------------------------------ */ frentry_t * -ipf_getrulen(softc, unit, group, n) - ipf_main_softc_t *softc; - int unit; - char *group; - u_32_t n; +ipf_getrulen(ipf_main_softc_t *softc, int unit, char *group, u_32_t n) { frentry_t *fr; frgroup_t *fg; fg = ipf_findgroup(softc, group, unit, softc->ipf_active, NULL); if (fg == NULL) return NULL; for (fr = fg->fg_start; fr && n; fr = fr->fr_next, n--) ; if (n != 0) return NULL; return fr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_flushlist */ /* Returns: int - >= 0 - number of flushed rules */ /* Parameters: softc(I) - pointer to soft context main structure */ /* nfreedp(O) - pointer to int where flush count is stored */ /* listp(I) - pointer to list to flush pointer */ /* Write Locks: ipf_mutex */ /* */ /* Recursively flush rules from the list, descending groups as they are */ /* encountered. if a rule is the head of a group and it has lost all its */ /* group members, then also delete the group reference. nfreedp is needed */ /* to store the accumulating count of rules removed, whereas the returned */ /* value is just the number removed from the current list. The latter is */ /* needed to correctly adjust reference counts on rules that define groups. */ /* */ /* NOTE: Rules not loaded from user space cannot be flushed. */ /* ------------------------------------------------------------------------ */ static int -ipf_flushlist(softc, nfreedp, listp) - ipf_main_softc_t *softc; - int *nfreedp; - frentry_t **listp; +ipf_flushlist(ipf_main_softc_t *softc, int *nfreedp, frentry_t **listp) { int freed = 0; frentry_t *fp; while ((fp = *listp) != NULL) { if ((fp->fr_type & FR_T_BUILTIN) || !(fp->fr_flags & FR_COPIED)) { listp = &fp->fr_next; continue; } *listp = fp->fr_next; if (fp->fr_next != NULL) fp->fr_next->fr_pnext = fp->fr_pnext; fp->fr_pnext = NULL; if (fp->fr_grphead != NULL) { freed += ipf_group_flush(softc, fp->fr_grphead); fp->fr_names[fp->fr_grhead] = '\0'; } if (fp->fr_icmpgrp != NULL) { freed += ipf_group_flush(softc, fp->fr_icmpgrp); fp->fr_names[fp->fr_icmphead] = '\0'; } if (fp->fr_srctrack.ht_max_nodes) ipf_rb_ht_flush(&fp->fr_srctrack); fp->fr_next = NULL; ASSERT(fp->fr_ref > 0); if (ipf_derefrule(softc, &fp) == 0) freed++; } *nfreedp += freed; return freed; } /* ------------------------------------------------------------------------ */ /* Function: ipf_flush */ /* Returns: int - >= 0 - number of flushed rules */ /* Parameters: softc(I) - pointer to soft context main structure */ /* unit(I) - device for which to flush rules */ /* flags(I) - which set of rules to flush */ /* */ /* Calls flushlist() for all filter rules (accounting, firewall - both IPv4 */ /* and IPv6) as defined by the value of flags. */ /* ------------------------------------------------------------------------ */ int -ipf_flush(softc, unit, flags) - ipf_main_softc_t *softc; - minor_t unit; - int flags; +ipf_flush(ipf_main_softc_t *softc, minor_t unit, int flags) { int flushed = 0, set; WRITE_ENTER(&softc->ipf_mutex); set = softc->ipf_active; if ((flags & FR_INACTIVE) == FR_INACTIVE) set = 1 - set; if (flags & FR_OUTQUE) { ipf_flushlist(softc, &flushed, &softc->ipf_rules[1][set]); ipf_flushlist(softc, &flushed, &softc->ipf_acct[1][set]); } if (flags & FR_INQUE) { ipf_flushlist(softc, &flushed, &softc->ipf_rules[0][set]); ipf_flushlist(softc, &flushed, &softc->ipf_acct[0][set]); } flushed += ipf_flush_groups(softc, &softc->ipf_groups[unit][set], flags & (FR_INQUE|FR_OUTQUE)); RWLOCK_EXIT(&softc->ipf_mutex); if (unit == IPL_LOGIPF) { int tmp; tmp = ipf_flush(softc, IPL_LOGCOUNT, flags); if (tmp >= 0) flushed += tmp; } return flushed; } /* ------------------------------------------------------------------------ */ /* Function: ipf_flush_groups */ /* Returns: int - >= 0 - number of flushed rules */ /* Parameters: softc(I) - soft context pointerto work with */ /* grhead(I) - pointer to the start of the group list to flush */ /* flags(I) - which set of rules to flush */ /* */ /* Walk through all of the groups under the given group head and remove all */ /* of those that match the flags passed in. The for loop here is bit more */ /* complicated than usual because the removal of a rule with ipf_derefrule */ /* may end up removing not only the structure pointed to by "fg" but also */ /* what is fg_next and fg_next after that. So if a filter rule is actually */ /* removed from the group then it is necessary to start again. */ /* ------------------------------------------------------------------------ */ static int -ipf_flush_groups(softc, grhead, flags) - ipf_main_softc_t *softc; - frgroup_t **grhead; - int flags; +ipf_flush_groups(ipf_main_softc_t *softc, frgroup_t **grhead, int flags) { frentry_t *fr, **frp; frgroup_t *fg, **fgp; int flushed = 0; int removed = 0; for (fgp = grhead; (fg = *fgp) != NULL; ) { while ((fg != NULL) && ((fg->fg_flags & flags) == 0)) fg = fg->fg_next; if (fg == NULL) break; removed = 0; frp = &fg->fg_start; while ((removed == 0) && ((fr = *frp) != NULL)) { if ((fr->fr_flags & flags) == 0) { frp = &fr->fr_next; } else { if (fr->fr_next != NULL) fr->fr_next->fr_pnext = fr->fr_pnext; *frp = fr->fr_next; fr->fr_pnext = NULL; fr->fr_next = NULL; (void) ipf_derefrule(softc, &fr); flushed++; removed++; } } if (removed == 0) fgp = &fg->fg_next; } return flushed; } /* ------------------------------------------------------------------------ */ /* Function: memstr */ /* Returns: char * - NULL if failed, != NULL pointer to matching bytes */ /* Parameters: src(I) - pointer to byte sequence to match */ /* dst(I) - pointer to byte sequence to search */ /* slen(I) - match length */ /* dlen(I) - length available to search in */ /* */ /* Search dst for a sequence of bytes matching those at src and extend for */ /* slen bytes. */ /* ------------------------------------------------------------------------ */ char * -memstr(src, dst, slen, dlen) - const char *src; - char *dst; - size_t slen, dlen; +memstr(const char *src, char *dst, size_t slen, size_t dlen) { char *s = NULL; while (dlen >= slen) { if (bcmp(src, dst, slen) == 0) { s = dst; break; } dst++; dlen--; } return s; } /* ------------------------------------------------------------------------ */ /* Function: ipf_fixskip */ /* Returns: Nil */ /* Parameters: listp(IO) - pointer to start of list with skip rule */ /* rp(I) - rule added/removed with skip in it. */ /* addremove(I) - adjustment (-1/+1) to make to skip count, */ /* depending on whether a rule was just added */ /* or removed. */ /* */ /* Adjust all the rules in a list which would have skip'd past the position */ /* where we are inserting to skip to the right place given the change. */ /* ------------------------------------------------------------------------ */ void -ipf_fixskip(listp, rp, addremove) - frentry_t **listp, *rp; - int addremove; +ipf_fixskip(frentry_t **listp, frentry_t *rp, int addremove) { int rules, rn; frentry_t *fp; rules = 0; for (fp = *listp; (fp != NULL) && (fp != rp); fp = fp->fr_next) rules++; if (fp == NULL) return; for (rn = 0, fp = *listp; fp && (fp != rp); fp = fp->fr_next, rn++) if (FR_ISSKIP(fp->fr_flags) && (rn + fp->fr_arg >= rules)) fp->fr_arg += addremove; } #ifdef _KERNEL /* ------------------------------------------------------------------------ */ /* Function: count4bits */ /* Returns: int - >= 0 - number of consecutive bits in input */ /* Parameters: ip(I) - 32bit IP address */ /* */ /* IPv4 ONLY */ /* count consecutive 1's in bit mask. If the mask generated by counting */ /* consecutive 1's is different to that passed, return -1, else return # */ /* of bits. */ /* ------------------------------------------------------------------------ */ int -count4bits(ip) - u_32_t ip; +count4bits(u_32_t ip) { u_32_t ipn; int cnt = 0, i, j; ip = ipn = ntohl(ip); for (i = 32; i; i--, ipn *= 2) if (ipn & 0x80000000) cnt++; else break; ipn = 0; for (i = 32, j = cnt; i; i--, j--) { ipn *= 2; if (j > 0) ipn++; } if (ipn == ip) return cnt; return -1; } /* ------------------------------------------------------------------------ */ /* Function: count6bits */ /* Returns: int - >= 0 - number of consecutive bits in input */ /* Parameters: msk(I) - pointer to start of IPv6 bitmask */ /* */ /* IPv6 ONLY */ /* count consecutive 1's in bit mask. */ /* ------------------------------------------------------------------------ */ # ifdef USE_INET6 int -count6bits(msk) - u_32_t *msk; +count6bits(u_32_t *msk) { int i = 0, k; u_32_t j; for (k = 3; k >= 0; k--) if (msk[k] == 0xffffffff) i += 32; else { for (j = msk[k]; j; j <<= 1) if (j & 0x80000000) i++; } return i; } # endif #endif /* _KERNEL */ /* ------------------------------------------------------------------------ */ /* Function: ipf_synclist */ /* Returns: int - 0 = no failures, else indication of first failure */ /* Parameters: fr(I) - start of filter list to sync interface names for */ /* ifp(I) - interface pointer for limiting sync lookups */ /* Write Locks: ipf_mutex */ /* */ /* Walk through a list of filter rules and resolve any interface names into */ /* pointers. Where dynamic addresses are used, also update the IP address */ /* used in the rule. The interface pointer is used to limit the lookups to */ /* a specific set of matching names if it is non-NULL. */ /* Errors can occur when resolving the destination name of to/dup-to fields */ /* when the name points to a pool and that pool doest not exist. If this */ /* does happen then it is necessary to check if there are any lookup refs */ /* that need to be dropped before returning with an error. */ /* ------------------------------------------------------------------------ */ static int -ipf_synclist(softc, fr, ifp) - ipf_main_softc_t *softc; - frentry_t *fr; - void *ifp; +ipf_synclist(ipf_main_softc_t *softc, frentry_t *fr, void *ifp) { frentry_t *frt, *start = fr; frdest_t *fdp; char *name; int error; void *ifa; int v, i; error = 0; for (; fr; fr = fr->fr_next) { if (fr->fr_family == AF_INET) v = 4; else if (fr->fr_family == AF_INET6) v = 6; else v = 0; /* * Lookup all the interface names that are part of the rule. */ for (i = 0; i < FR_NUM(fr->fr_ifas); i++) { if ((ifp != NULL) && (fr->fr_ifas[i] != ifp)) continue; if (fr->fr_ifnames[i] == -1) continue; name = FR_NAME(fr, fr_ifnames[i]); fr->fr_ifas[i] = ipf_resolvenic(softc, name, v); } if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) { if (fr->fr_satype != FRI_NORMAL && fr->fr_satype != FRI_LOOKUP) { ifa = ipf_resolvenic(softc, fr->fr_names + fr->fr_sifpidx, v); ipf_ifpaddr(softc, v, fr->fr_satype, ifa, &fr->fr_src6, &fr->fr_smsk6); } if (fr->fr_datype != FRI_NORMAL && fr->fr_datype != FRI_LOOKUP) { ifa = ipf_resolvenic(softc, fr->fr_names + fr->fr_sifpidx, v); ipf_ifpaddr(softc, v, fr->fr_datype, ifa, &fr->fr_dst6, &fr->fr_dmsk6); } } fdp = &fr->fr_tifs[0]; if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { error = ipf_resolvedest(softc, fr->fr_names, fdp, v); if (error != 0) goto unwind; } fdp = &fr->fr_tifs[1]; if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { error = ipf_resolvedest(softc, fr->fr_names, fdp, v); if (error != 0) goto unwind; } fdp = &fr->fr_dif; if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { error = ipf_resolvedest(softc, fr->fr_names, fdp, v); if (error != 0) goto unwind; } if (((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && (fr->fr_satype == FRI_LOOKUP) && (fr->fr_srcptr == NULL)) { fr->fr_srcptr = ipf_lookup_res_num(softc, fr->fr_srctype, IPL_LOGIPF, fr->fr_srcnum, &fr->fr_srcfunc); } if (((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && (fr->fr_datype == FRI_LOOKUP) && (fr->fr_dstptr == NULL)) { fr->fr_dstptr = ipf_lookup_res_num(softc, fr->fr_dsttype, IPL_LOGIPF, fr->fr_dstnum, &fr->fr_dstfunc); } } return 0; unwind: for (frt = start; frt != fr; fr = fr->fr_next) { if (((frt->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && (frt->fr_satype == FRI_LOOKUP) && (frt->fr_srcptr != NULL)) ipf_lookup_deref(softc, frt->fr_srctype, frt->fr_srcptr); if (((frt->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && (frt->fr_datype == FRI_LOOKUP) && (frt->fr_dstptr != NULL)) ipf_lookup_deref(softc, frt->fr_dsttype, frt->fr_dstptr); } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync */ /* Returns: void */ /* Parameters: Nil */ /* */ /* ipf_sync() is called when we suspect that the interface list or */ /* information about interfaces (like IP#) has changed. Go through all */ /* filter rules, NAT entries and the state table and check if anything */ /* needs to be changed/updated. */ /* ------------------------------------------------------------------------ */ int -ipf_sync(softc, ifp) - ipf_main_softc_t *softc; - void *ifp; +ipf_sync(ipf_main_softc_t *softc, void *ifp) { int i; #if !SOLARIS ipf_nat_sync(softc, ifp); ipf_state_sync(softc, ifp); ipf_lookup_sync(softc, ifp); #endif WRITE_ENTER(&softc->ipf_mutex); (void) ipf_synclist(softc, softc->ipf_acct[0][softc->ipf_active], ifp); (void) ipf_synclist(softc, softc->ipf_acct[1][softc->ipf_active], ifp); (void) ipf_synclist(softc, softc->ipf_rules[0][softc->ipf_active], ifp); (void) ipf_synclist(softc, softc->ipf_rules[1][softc->ipf_active], ifp); for (i = 0; i < IPL_LOGSIZE; i++) { frgroup_t *g; for (g = softc->ipf_groups[i][0]; g != NULL; g = g->fg_next) (void) ipf_synclist(softc, g->fg_start, ifp); for (g = softc->ipf_groups[i][1]; g != NULL; g = g->fg_next) (void) ipf_synclist(softc, g->fg_start, ifp); } RWLOCK_EXIT(&softc->ipf_mutex); return 0; } /* * In the functions below, bcopy() is called because the pointer being * copied _from_ in this instance is a pointer to a char buf (which could * end up being unaligned) and on the kernel's local stack. */ /* ------------------------------------------------------------------------ */ /* Function: copyinptr */ /* Returns: int - 0 = success, else failure */ /* Parameters: src(I) - pointer to the source address */ /* dst(I) - destination address */ /* size(I) - number of bytes to copy */ /* */ /* Copy a block of data in from user space, given a pointer to the pointer */ /* to start copying from (src) and a pointer to where to store it (dst). */ /* NB: src - pointer to user space pointer, dst - kernel space pointer */ /* ------------------------------------------------------------------------ */ int -copyinptr(softc, src, dst, size) - ipf_main_softc_t *softc; - void *src, *dst; - size_t size; +copyinptr(ipf_main_softc_t *softc, void *src, void *dst, size_t size) { caddr_t ca; int error; #if SOLARIS error = COPYIN(src, &ca, sizeof(ca)); if (error != 0) return error; #else bcopy(src, (caddr_t)&ca, sizeof(ca)); #endif error = COPYIN(ca, dst, size); if (error != 0) { IPFERROR(3); error = EFAULT; } return error; } /* ------------------------------------------------------------------------ */ /* Function: copyoutptr */ /* Returns: int - 0 = success, else failure */ /* Parameters: src(I) - pointer to the source address */ /* dst(I) - destination address */ /* size(I) - number of bytes to copy */ /* */ /* Copy a block of data out to user space, given a pointer to the pointer */ /* to start copying from (src) and a pointer to where to store it (dst). */ /* NB: src - kernel space pointer, dst - pointer to user space pointer. */ /* ------------------------------------------------------------------------ */ int -copyoutptr(softc, src, dst, size) - ipf_main_softc_t *softc; - void *src, *dst; - size_t size; +copyoutptr(ipf_main_softc_t *softc, void *src, void *dst, size_t size) { caddr_t ca; int error; bcopy(dst, (caddr_t)&ca, sizeof(ca)); error = COPYOUT(src, ca, size); if (error != 0) { IPFERROR(4); error = EFAULT; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lock */ /* Returns: int - 0 = success, else error */ /* Parameters: data(I) - pointer to lock value to set */ /* lockp(O) - pointer to location to store old lock value */ /* */ /* Get the new value for the lock integer, set it and return the old value */ /* in *lockp. */ /* ------------------------------------------------------------------------ */ int -ipf_lock(data, lockp) - caddr_t data; - int *lockp; +ipf_lock(caddr_t data, int *lockp) { int arg, err; err = BCOPYIN(data, &arg, sizeof(arg)); if (err != 0) return EFAULT; err = BCOPYOUT(lockp, data, sizeof(*lockp)); if (err != 0) return EFAULT; *lockp = arg; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_getstat */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fiop(I) - pointer to ipfilter stats structure */ /* rev(I) - version claim by program doing ioctl */ /* */ /* Stores a copy of current pointers, counters, etc, in the friostat */ /* structure. */ /* If IPFILTER_COMPAT is compiled, we pretend to be whatever version the */ /* program is looking for. This ensure that validation of the version it */ /* expects will always succeed. Thus kernels with IPFILTER_COMPAT will */ /* allow older binaries to work but kernels without it will not. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ static void -ipf_getstat(softc, fiop, rev) - ipf_main_softc_t *softc; - friostat_t *fiop; - int rev; +ipf_getstat(ipf_main_softc_t *softc, friostat_t *fiop, int rev) { int i; bcopy((char *)softc->ipf_stats, (char *)fiop->f_st, sizeof(ipf_statistics_t) * 2); fiop->f_locks[IPL_LOGSTATE] = -1; fiop->f_locks[IPL_LOGNAT] = -1; fiop->f_locks[IPL_LOGIPF] = -1; fiop->f_locks[IPL_LOGAUTH] = -1; fiop->f_ipf[0][0] = softc->ipf_rules[0][0]; fiop->f_acct[0][0] = softc->ipf_acct[0][0]; fiop->f_ipf[0][1] = softc->ipf_rules[0][1]; fiop->f_acct[0][1] = softc->ipf_acct[0][1]; fiop->f_ipf[1][0] = softc->ipf_rules[1][0]; fiop->f_acct[1][0] = softc->ipf_acct[1][0]; fiop->f_ipf[1][1] = softc->ipf_rules[1][1]; fiop->f_acct[1][1] = softc->ipf_acct[1][1]; fiop->f_ticks = softc->ipf_ticks; fiop->f_active = softc->ipf_active; fiop->f_froute[0] = softc->ipf_frouteok[0]; fiop->f_froute[1] = softc->ipf_frouteok[1]; fiop->f_rb_no_mem = softc->ipf_rb_no_mem; fiop->f_rb_node_max = softc->ipf_rb_node_max; fiop->f_running = softc->ipf_running; for (i = 0; i < IPL_LOGSIZE; i++) { fiop->f_groups[i][0] = softc->ipf_groups[i][0]; fiop->f_groups[i][1] = softc->ipf_groups[i][1]; } #ifdef IPFILTER_LOG fiop->f_log_ok = ipf_log_logok(softc, IPL_LOGIPF); fiop->f_log_fail = ipf_log_failures(softc, IPL_LOGIPF); fiop->f_logging = 1; #else fiop->f_log_ok = 0; fiop->f_log_fail = 0; fiop->f_logging = 0; #endif fiop->f_defpass = softc->ipf_pass; fiop->f_features = ipf_features; #ifdef IPFILTER_COMPAT snprintf(fiop->f_version, sizeof(friostat.f_version), "IP Filter: v%d.%d.%d", (rev / 1000000) % 100, (rev / 10000) % 100, (rev / 100) % 100); #else rev = rev; (void) strncpy(fiop->f_version, ipfilter_version, sizeof(fiop->f_version)); #endif } #ifdef USE_INET6 int icmptoicmp6types[ICMP_MAXTYPE+1] = { ICMP6_ECHO_REPLY, /* 0: ICMP_ECHOREPLY */ -1, /* 1: UNUSED */ -1, /* 2: UNUSED */ ICMP6_DST_UNREACH, /* 3: ICMP_UNREACH */ -1, /* 4: ICMP_SOURCEQUENCH */ ND_REDIRECT, /* 5: ICMP_REDIRECT */ -1, /* 6: UNUSED */ -1, /* 7: UNUSED */ ICMP6_ECHO_REQUEST, /* 8: ICMP_ECHO */ -1, /* 9: UNUSED */ -1, /* 10: UNUSED */ ICMP6_TIME_EXCEEDED, /* 11: ICMP_TIMXCEED */ ICMP6_PARAM_PROB, /* 12: ICMP_PARAMPROB */ -1, /* 13: ICMP_TSTAMP */ -1, /* 14: ICMP_TSTAMPREPLY */ -1, /* 15: ICMP_IREQ */ -1, /* 16: ICMP_IREQREPLY */ -1, /* 17: ICMP_MASKREQ */ -1, /* 18: ICMP_MASKREPLY */ }; int icmptoicmp6unreach[ICMP_MAX_UNREACH] = { ICMP6_DST_UNREACH_ADDR, /* 0: ICMP_UNREACH_NET */ ICMP6_DST_UNREACH_ADDR, /* 1: ICMP_UNREACH_HOST */ -1, /* 2: ICMP_UNREACH_PROTOCOL */ ICMP6_DST_UNREACH_NOPORT, /* 3: ICMP_UNREACH_PORT */ -1, /* 4: ICMP_UNREACH_NEEDFRAG */ ICMP6_DST_UNREACH_NOTNEIGHBOR, /* 5: ICMP_UNREACH_SRCFAIL */ ICMP6_DST_UNREACH_ADDR, /* 6: ICMP_UNREACH_NET_UNKNOWN */ ICMP6_DST_UNREACH_ADDR, /* 7: ICMP_UNREACH_HOST_UNKNOWN */ -1, /* 8: ICMP_UNREACH_ISOLATED */ ICMP6_DST_UNREACH_ADMIN, /* 9: ICMP_UNREACH_NET_PROHIB */ ICMP6_DST_UNREACH_ADMIN, /* 10: ICMP_UNREACH_HOST_PROHIB */ -1, /* 11: ICMP_UNREACH_TOSNET */ -1, /* 12: ICMP_UNREACH_TOSHOST */ ICMP6_DST_UNREACH_ADMIN, /* 13: ICMP_UNREACH_ADMIN_PROHIBIT */ }; int icmpreplytype6[ICMP6_MAXTYPE + 1]; #endif int icmpreplytype4[ICMP_MAXTYPE + 1]; /* ------------------------------------------------------------------------ */ /* Function: ipf_matchicmpqueryreply */ /* Returns: int - 1 if "icmp" is a valid reply to "ic" else 0. */ /* Parameters: v(I) - IP protocol version (4 or 6) */ /* ic(I) - ICMP information */ /* icmp(I) - ICMP packet header */ /* rev(I) - direction (0 = forward/1 = reverse) of packet */ /* */ /* Check if the ICMP packet defined by the header pointed to by icmp is a */ /* reply to one as described by what's in ic. If it is a match, return 1, */ /* else return 0 for no match. */ /* ------------------------------------------------------------------------ */ int -ipf_matchicmpqueryreply(v, ic, icmp, rev) - int v; - icmpinfo_t *ic; - icmphdr_t *icmp; - int rev; +ipf_matchicmpqueryreply(int v, icmpinfo_t *ic, icmphdr_t *icmp, int rev) { int ictype; ictype = ic->ici_type; if (v == 4) { /* * If we matched its type on the way in, then when going out * it will still be the same type. */ if ((!rev && (icmp->icmp_type == ictype)) || (rev && (icmpreplytype4[ictype] == icmp->icmp_type))) { if (icmp->icmp_type != ICMP_ECHOREPLY) return 1; if (icmp->icmp_id == ic->ici_id) return 1; } } #ifdef USE_INET6 else if (v == 6) { if ((!rev && (icmp->icmp_type == ictype)) || (rev && (icmpreplytype6[ictype] == icmp->icmp_type))) { if (icmp->icmp_type != ICMP6_ECHO_REPLY) return 1; if (icmp->icmp_id == ic->ici_id) return 1; } } #endif return 0; } /* * IFNAMES are located in the variable length field starting at * frentry.fr_names. As pointers within the struct cannot be passed * to the kernel from ipf(8), an offset is used. An offset of -1 means it * is unused (invalid). If it is used (valid) it is an offset to the * character string of an interface name or a comment. The following * macros will assist those who follow to understand the code. */ #define IPF_IFNAME_VALID(_a) (_a != -1) #define IPF_IFNAME_INVALID(_a) (_a == -1) #define IPF_IFNAMES_DIFFERENT(_a) \ !((IPF_IFNAME_INVALID(fr1->_a) && \ IPF_IFNAME_INVALID(fr2->_a)) || \ (IPF_IFNAME_VALID(fr1->_a) && \ IPF_IFNAME_VALID(fr2->_a) && \ !strcmp(FR_NAME(fr1, _a), FR_NAME(fr2, _a)))) #define IPF_FRDEST_DIFFERENT(_a) \ (memcmp(&fr1->_a.fd_addr, &fr2->_a.fd_addr, \ offsetof(frdest_t, fd_name) - offsetof(frdest_t, fd_addr)) || \ IPF_IFNAMES_DIFFERENT(_a.fd_name)) /* ------------------------------------------------------------------------ */ /* Function: ipf_rule_compare */ /* Parameters: fr1(I) - first rule structure to compare */ /* fr2(I) - second rule structure to compare */ /* Returns: int - 0 == rules are the same, else mismatch */ /* */ /* Compare two rules and return 0 if they match or a number indicating */ /* which of the individual checks failed. */ /* ------------------------------------------------------------------------ */ static int ipf_rule_compare(frentry_t *fr1, frentry_t *fr2) { int i; if (fr1->fr_cksum != fr2->fr_cksum) return (1); if (fr1->fr_size != fr2->fr_size) return (2); if (fr1->fr_dsize != fr2->fr_dsize) return (3); if (bcmp((char *)&fr1->fr_func, (char *)&fr2->fr_func, FR_CMPSIZ) != 0) return (4); /* * XXX: There is still a bug here as different rules with the * the same interfaces but in a different order will compare * differently. But since multiple interfaces in a rule doesn't * work anyway a simple straightforward compare is performed * here. Ultimately frentry_t creation will need to be * revisited in ipf_y.y. While the other issue, recognition * of only the first interface in a list of interfaces will * need to be separately addressed along with why only four. */ for (i = 0; i < FR_NUM(fr1->fr_ifnames); i++) { /* * XXX: It's either the same index or uninitialized. * We assume this because multiple interfaces * referenced by the same rule doesn't work anyway. */ if (IPF_IFNAMES_DIFFERENT(fr_ifnames[i])) return(5); } if (IPF_FRDEST_DIFFERENT(fr_tif)) return (6); if (IPF_FRDEST_DIFFERENT(fr_rif)) return (7); if (IPF_FRDEST_DIFFERENT(fr_dif)) return (8); if (!fr1->fr_data && !fr2->fr_data) return (0); /* move along, nothing to see here */ if (fr1->fr_data && fr2->fr_data) { if (bcmp(fr1->fr_caddr, fr2->fr_caddr, fr1->fr_dsize) == 0) return (0); /* same */ } return (9); } /* ------------------------------------------------------------------------ */ /* Function: frrequest */ /* Returns: int - 0 == success, > 0 == errno value */ /* Parameters: unit(I) - device for which this is for */ /* req(I) - ioctl command (SIOC*) */ /* data(I) - pointr to ioctl data */ /* set(I) - 1 or 0 (filter set) */ /* makecopy(I) - flag indicating whether data points to a rule */ /* in kernel space & hence doesn't need copying. */ /* */ /* This function handles all the requests which operate on the list of */ /* filter rules. This includes adding, deleting, insertion. It is also */ /* responsible for creating groups when a "head" rule is loaded. Interface */ /* names are resolved here and other sanity checks are made on the content */ /* of the rule structure being loaded. If a rule has user defined timeouts */ /* then make sure they are created and initialised before exiting. */ /* ------------------------------------------------------------------------ */ int -frrequest(softc, unit, req, data, set, makecopy) - ipf_main_softc_t *softc; - int unit; - ioctlcmd_t req; - int set, makecopy; - caddr_t data; +frrequest(ipf_main_softc_t *softc, int unit, ioctlcmd_t req, caddr_t data, + int set, int makecopy) { int error = 0, in, family, need_free = 0; enum { OP_ADD, /* add rule */ OP_REM, /* remove rule */ OP_ZERO /* zero statistics and counters */ } addrem = OP_ADD; frentry_t frd, *fp, *f, **fprev, **ftail; void *ptr, *uptr, *cptr; u_int *p, *pp; frgroup_t *fg; char *group; ptr = NULL; cptr = NULL; fg = NULL; fp = &frd; if (makecopy != 0) { bzero(fp, sizeof(frd)); error = ipf_inobj(softc, data, NULL, fp, IPFOBJ_FRENTRY); if (error) { return error; } if ((fp->fr_type & FR_T_BUILTIN) != 0) { IPFERROR(6); return EINVAL; } KMALLOCS(f, frentry_t *, fp->fr_size); if (f == NULL) { IPFERROR(131); return ENOMEM; } bzero(f, fp->fr_size); error = ipf_inobjsz(softc, data, f, IPFOBJ_FRENTRY, fp->fr_size); if (error) { KFREES(f, fp->fr_size); return error; } fp = f; f = NULL; fp->fr_next = NULL; fp->fr_dnext = NULL; fp->fr_pnext = NULL; fp->fr_pdnext = NULL; fp->fr_grp = NULL; fp->fr_grphead = NULL; fp->fr_icmpgrp = NULL; fp->fr_isc = (void *)-1; fp->fr_ptr = NULL; fp->fr_ref = 0; fp->fr_flags |= FR_COPIED; } else { fp = (frentry_t *)data; if ((fp->fr_type & FR_T_BUILTIN) == 0) { IPFERROR(7); return EINVAL; } fp->fr_flags &= ~FR_COPIED; } if (((fp->fr_dsize == 0) && (fp->fr_data != NULL)) || ((fp->fr_dsize != 0) && (fp->fr_data == NULL))) { IPFERROR(8); error = EINVAL; goto donenolock; } family = fp->fr_family; uptr = fp->fr_data; if (req == (ioctlcmd_t)SIOCINAFR || req == (ioctlcmd_t)SIOCINIFR || req == (ioctlcmd_t)SIOCADAFR || req == (ioctlcmd_t)SIOCADIFR) addrem = OP_ADD; /* Add rule */ else if (req == (ioctlcmd_t)SIOCRMAFR || req == (ioctlcmd_t)SIOCRMIFR) addrem = OP_REM; /* Remove rule */ else if (req == (ioctlcmd_t)SIOCZRLST) addrem = OP_ZERO; /* Zero statistics and counters */ else { IPFERROR(9); error = EINVAL; goto donenolock; } /* * Only filter rules for IPv4 or IPv6 are accepted. */ if (family == AF_INET) { /*EMPTY*/; #ifdef USE_INET6 } else if (family == AF_INET6) { /*EMPTY*/; #endif } else if (family != 0) { IPFERROR(10); error = EINVAL; goto donenolock; } /* * If the rule is being loaded from user space, i.e. we had to copy it * into kernel space, then do not trust the function pointer in the * rule. */ if ((makecopy == 1) && (fp->fr_func != NULL)) { if (ipf_findfunc(fp->fr_func) == NULL) { IPFERROR(11); error = ESRCH; goto donenolock; } if (addrem == OP_ADD) { error = ipf_funcinit(softc, fp); if (error != 0) goto donenolock; } } if ((fp->fr_flags & FR_CALLNOW) && ((fp->fr_func == NULL) || (fp->fr_func == (ipfunc_t)-1))) { IPFERROR(142); error = ESRCH; goto donenolock; } if (((fp->fr_flags & FR_CMDMASK) == FR_CALL) && ((fp->fr_func == NULL) || (fp->fr_func == (ipfunc_t)-1))) { IPFERROR(143); error = ESRCH; goto donenolock; } ptr = NULL; cptr = NULL; if (FR_ISACCOUNT(fp->fr_flags)) unit = IPL_LOGCOUNT; /* * Check that each group name in the rule has a start index that * is valid. */ if (fp->fr_icmphead != -1) { if ((fp->fr_icmphead < 0) || (fp->fr_icmphead >= fp->fr_namelen)) { IPFERROR(136); error = EINVAL; goto donenolock; } if (!strcmp(FR_NAME(fp, fr_icmphead), "0")) fp->fr_names[fp->fr_icmphead] = '\0'; } if (fp->fr_grhead != -1) { if ((fp->fr_grhead < 0) || (fp->fr_grhead >= fp->fr_namelen)) { IPFERROR(137); error = EINVAL; goto donenolock; } if (!strcmp(FR_NAME(fp, fr_grhead), "0")) fp->fr_names[fp->fr_grhead] = '\0'; } if (fp->fr_group != -1) { if ((fp->fr_group < 0) || (fp->fr_group >= fp->fr_namelen)) { IPFERROR(138); error = EINVAL; goto donenolock; } if ((req != (int)SIOCZRLST) && (fp->fr_group != -1)) { /* * Allow loading rules that are in groups to cause * them to be created if they don't already exit. */ group = FR_NAME(fp, fr_group); if (addrem == OP_ADD) { fg = ipf_group_add(softc, group, NULL, fp->fr_flags, unit, set); fp->fr_grp = fg; } else { fg = ipf_findgroup(softc, group, unit, set, NULL); if (fg == NULL) { IPFERROR(12); error = ESRCH; goto donenolock; } } if (fg->fg_flags == 0) { fg->fg_flags = fp->fr_flags & FR_INOUT; } else if (fg->fg_flags != (fp->fr_flags & FR_INOUT)) { IPFERROR(13); error = ESRCH; goto donenolock; } } } else { /* * If a rule is going to be part of a group then it does * not matter whether it is an in or out rule, but if it * isn't in a group, then it does... */ if ((fp->fr_flags & (FR_INQUE|FR_OUTQUE)) == 0) { IPFERROR(14); error = EINVAL; goto donenolock; } } in = (fp->fr_flags & FR_INQUE) ? 0 : 1; /* * Work out which rule list this change is being applied to. */ ftail = NULL; fprev = NULL; if (unit == IPL_LOGAUTH) { if ((fp->fr_tifs[0].fd_ptr != NULL) || (fp->fr_tifs[1].fd_ptr != NULL) || (fp->fr_dif.fd_ptr != NULL) || (fp->fr_flags & FR_FASTROUTE)) { softc->ipf_interror = 145; error = EINVAL; goto donenolock; } fprev = ipf_auth_rulehead(softc); } else { if (FR_ISACCOUNT(fp->fr_flags)) fprev = &softc->ipf_acct[in][set]; else if ((fp->fr_flags & (FR_OUTQUE|FR_INQUE)) != 0) fprev = &softc->ipf_rules[in][set]; } if (fprev == NULL) { IPFERROR(15); error = ESRCH; goto donenolock; } if (fg != NULL) fprev = &fg->fg_start; /* * Copy in extra data for the rule. */ if (fp->fr_dsize != 0) { if (makecopy != 0) { KMALLOCS(ptr, void *, fp->fr_dsize); if (ptr == NULL) { IPFERROR(16); error = ENOMEM; goto donenolock; } /* * The bcopy case is for when the data is appended * to the rule by ipf_in_compat(). */ if (uptr >= (void *)fp && uptr < (void *)((char *)fp + fp->fr_size)) { bcopy(uptr, ptr, fp->fr_dsize); error = 0; } else { error = COPYIN(uptr, ptr, fp->fr_dsize); if (error != 0) { IPFERROR(17); error = EFAULT; goto donenolock; } } } else { ptr = uptr; } fp->fr_data = ptr; } else { fp->fr_data = NULL; } /* * Perform per-rule type sanity checks of their members. * All code after this needs to be aware that allocated memory * may need to be free'd before exiting. */ switch (fp->fr_type & ~FR_T_BUILTIN) { #if defined(IPFILTER_BPF) case FR_T_BPFOPC : if (fp->fr_dsize == 0) { IPFERROR(19); error = EINVAL; break; } if (!bpf_validate(ptr, fp->fr_dsize/sizeof(struct bpf_insn))) { IPFERROR(20); error = EINVAL; break; } break; #endif case FR_T_IPF : /* * Preparation for error case at the bottom of this function. */ if (fp->fr_datype == FRI_LOOKUP) fp->fr_dstptr = NULL; if (fp->fr_satype == FRI_LOOKUP) fp->fr_srcptr = NULL; if (fp->fr_dsize != sizeof(fripf_t)) { IPFERROR(21); error = EINVAL; break; } /* * Allowing a rule with both "keep state" and "with oow" is * pointless because adding a state entry to the table will * fail with the out of window (oow) flag set. */ if ((fp->fr_flags & FR_KEEPSTATE) && (fp->fr_flx & FI_OOW)) { IPFERROR(22); error = EINVAL; break; } switch (fp->fr_satype) { case FRI_BROADCAST : case FRI_DYNAMIC : case FRI_NETWORK : case FRI_NETMASKED : case FRI_PEERADDR : if (fp->fr_sifpidx < 0) { IPFERROR(23); error = EINVAL; } break; case FRI_LOOKUP : fp->fr_srcptr = ipf_findlookup(softc, unit, fp, &fp->fr_src6, &fp->fr_smsk6); if (fp->fr_srcfunc == NULL) { IPFERROR(132); error = ESRCH; break; } break; case FRI_NORMAL : break; default : IPFERROR(133); error = EINVAL; break; } if (error != 0) break; switch (fp->fr_datype) { case FRI_BROADCAST : case FRI_DYNAMIC : case FRI_NETWORK : case FRI_NETMASKED : case FRI_PEERADDR : if (fp->fr_difpidx < 0) { IPFERROR(24); error = EINVAL; } break; case FRI_LOOKUP : fp->fr_dstptr = ipf_findlookup(softc, unit, fp, &fp->fr_dst6, &fp->fr_dmsk6); if (fp->fr_dstfunc == NULL) { IPFERROR(134); error = ESRCH; } break; case FRI_NORMAL : break; default : IPFERROR(135); error = EINVAL; } break; case FR_T_NONE : case FR_T_CALLFUNC : case FR_T_COMPIPF : break; case FR_T_IPFEXPR : if (ipf_matcharray_verify(fp->fr_data, fp->fr_dsize) == -1) { IPFERROR(25); error = EINVAL; } break; default : IPFERROR(26); error = EINVAL; break; } if (error != 0) goto donenolock; if (fp->fr_tif.fd_name != -1) { if ((fp->fr_tif.fd_name < 0) || (fp->fr_tif.fd_name >= fp->fr_namelen)) { IPFERROR(139); error = EINVAL; goto donenolock; } } if (fp->fr_dif.fd_name != -1) { if ((fp->fr_dif.fd_name < 0) || (fp->fr_dif.fd_name >= fp->fr_namelen)) { IPFERROR(140); error = EINVAL; goto donenolock; } } if (fp->fr_rif.fd_name != -1) { if ((fp->fr_rif.fd_name < 0) || (fp->fr_rif.fd_name >= fp->fr_namelen)) { IPFERROR(141); error = EINVAL; goto donenolock; } } /* * Lookup all the interface names that are part of the rule. */ error = ipf_synclist(softc, fp, NULL); if (error != 0) goto donenolock; fp->fr_statecnt = 0; if (fp->fr_srctrack.ht_max_nodes != 0) ipf_rb_ht_init(&fp->fr_srctrack); /* * Look for an existing matching filter rule, but don't include the * next or interface pointer in the comparison (fr_next, fr_ifa). * This elminates rules which are indentical being loaded. Checksum * the constant part of the filter rule to make comparisons quicker * (this meaning no pointers are included). */ pp = (u_int *)(fp->fr_caddr + fp->fr_dsize); for (fp->fr_cksum = 0, p = (u_int *)fp->fr_data; p < pp; p++) fp->fr_cksum += *p; WRITE_ENTER(&softc->ipf_mutex); /* * Now that the filter rule lists are locked, we can walk the * chain of them without fear. */ ftail = fprev; for (f = *ftail; (f = *ftail) != NULL; ftail = &f->fr_next) { if (fp->fr_collect <= f->fr_collect) { ftail = fprev; f = NULL; break; } fprev = ftail; } for (; (f = *ftail) != NULL; ftail = &f->fr_next) { if (ipf_rule_compare(fp, f) == 0) break; } /* * If zero'ing statistics, copy current to caller and zero. */ if (addrem == OP_ZERO) { if (f == NULL) { IPFERROR(27); error = ESRCH; } else { /* * Copy and reduce lock because of impending copyout. * Well we should, but if we do then the atomicity of * this call and the correctness of fr_hits and * fr_bytes cannot be guaranteed. As it is, this code * only resets them to 0 if they are successfully * copied out into user space. */ bcopy((char *)f, (char *)fp, f->fr_size); /* MUTEX_DOWNGRADE(&softc->ipf_mutex); */ /* * When we copy this rule back out, set the data * pointer to be what it was in user space. */ fp->fr_data = uptr; error = ipf_outobj(softc, data, fp, IPFOBJ_FRENTRY); if (error == 0) { if ((f->fr_dsize != 0) && (uptr != NULL)) { error = COPYOUT(f->fr_data, uptr, f->fr_dsize); if (error == 0) { f->fr_hits = 0; f->fr_bytes = 0; } else { IPFERROR(28); error = EFAULT; } } } } if (makecopy != 0) { if (ptr != NULL) { KFREES(ptr, fp->fr_dsize); } KFREES(fp, fp->fr_size); } RWLOCK_EXIT(&softc->ipf_mutex); return error; } if (f == NULL) { /* * At the end of this, ftail must point to the place where the * new rule is to be saved/inserted/added. * For SIOCAD*FR, this should be the last rule in the group of * rules that have equal fr_collect fields. * For SIOCIN*FR, ... */ if (req == (ioctlcmd_t)SIOCADAFR || req == (ioctlcmd_t)SIOCADIFR) { for (ftail = fprev; (f = *ftail) != NULL; ) { if (f->fr_collect > fp->fr_collect) break; ftail = &f->fr_next; fprev = ftail; } ftail = fprev; f = NULL; ptr = NULL; } else if (req == (ioctlcmd_t)SIOCINAFR || req == (ioctlcmd_t)SIOCINIFR) { while ((f = *fprev) != NULL) { if (f->fr_collect >= fp->fr_collect) break; fprev = &f->fr_next; } ftail = fprev; if (fp->fr_hits != 0) { while (fp->fr_hits && (f = *ftail)) { if (f->fr_collect != fp->fr_collect) break; fprev = ftail; ftail = &f->fr_next; fp->fr_hits--; } } f = NULL; ptr = NULL; } } /* * Request to remove a rule. */ if (addrem == OP_REM) { if (f == NULL) { IPFERROR(29); error = ESRCH; } else { /* * Do not allow activity from user space to interfere * with rules not loaded that way. */ if ((makecopy == 1) && !(f->fr_flags & FR_COPIED)) { IPFERROR(30); error = EPERM; goto done; } /* * Return EBUSY if the rule is being reference by * something else (eg state information.) */ if (f->fr_ref > 1) { IPFERROR(31); error = EBUSY; goto done; } #ifdef IPFILTER_SCAN if (f->fr_isctag != -1 && (f->fr_isc != (struct ipscan *)-1)) ipf_scan_detachfr(f); #endif if (unit == IPL_LOGAUTH) { error = ipf_auth_precmd(softc, req, f, ftail); goto done; } ipf_rule_delete(softc, f, unit, set); need_free = makecopy; } } else { /* * Not removing, so we must be adding/inserting a rule. */ if (f != NULL) { IPFERROR(32); error = EEXIST; goto done; } if (unit == IPL_LOGAUTH) { error = ipf_auth_precmd(softc, req, fp, ftail); goto done; } MUTEX_NUKE(&fp->fr_lock); MUTEX_INIT(&fp->fr_lock, "filter rule lock"); if (fp->fr_die != 0) ipf_rule_expire_insert(softc, fp, set); fp->fr_hits = 0; if (makecopy != 0) fp->fr_ref = 1; fp->fr_pnext = ftail; fp->fr_next = *ftail; if (fp->fr_next != NULL) fp->fr_next->fr_pnext = &fp->fr_next; *ftail = fp; ipf_fixskip(ftail, fp, 1); fp->fr_icmpgrp = NULL; if (fp->fr_icmphead != -1) { group = FR_NAME(fp, fr_icmphead); fg = ipf_group_add(softc, group, fp, 0, unit, set); fp->fr_icmpgrp = fg; } fp->fr_grphead = NULL; if (fp->fr_grhead != -1) { group = FR_NAME(fp, fr_grhead); fg = ipf_group_add(softc, group, fp, fp->fr_flags, unit, set); fp->fr_grphead = fg; } } done: RWLOCK_EXIT(&softc->ipf_mutex); donenolock: if (need_free || (error != 0)) { if ((fp->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) { if ((fp->fr_satype == FRI_LOOKUP) && (fp->fr_srcptr != NULL)) ipf_lookup_deref(softc, fp->fr_srctype, fp->fr_srcptr); if ((fp->fr_datype == FRI_LOOKUP) && (fp->fr_dstptr != NULL)) ipf_lookup_deref(softc, fp->fr_dsttype, fp->fr_dstptr); } if (fp->fr_grp != NULL) { WRITE_ENTER(&softc->ipf_mutex); ipf_group_del(softc, fp->fr_grp, fp); RWLOCK_EXIT(&softc->ipf_mutex); } if ((ptr != NULL) && (makecopy != 0)) { KFREES(ptr, fp->fr_dsize); } KFREES(fp, fp->fr_size); } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rule_delete */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* f(I) - pointer to the rule being deleted */ /* ftail(I) - pointer to the pointer to f */ /* unit(I) - device for which this is for */ /* set(I) - 1 or 0 (filter set) */ /* */ /* This function attempts to do what it can to delete a filter rule: remove */ /* it from any linked lists and remove any groups it is responsible for. */ /* But in the end, removing a rule can only drop the reference count - we */ /* must use that as the guide for whether or not it can be freed. */ /* ------------------------------------------------------------------------ */ static void -ipf_rule_delete(softc, f, unit, set) - ipf_main_softc_t *softc; - frentry_t *f; - int unit, set; +ipf_rule_delete(ipf_main_softc_t *softc, frentry_t *f, int unit, int set) { /* * If fr_pdnext is set, then the rule is on the expire list, so * remove it from there. */ if (f->fr_pdnext != NULL) { *f->fr_pdnext = f->fr_dnext; if (f->fr_dnext != NULL) f->fr_dnext->fr_pdnext = f->fr_pdnext; f->fr_pdnext = NULL; f->fr_dnext = NULL; } ipf_fixskip(f->fr_pnext, f, -1); if (f->fr_pnext != NULL) *f->fr_pnext = f->fr_next; if (f->fr_next != NULL) f->fr_next->fr_pnext = f->fr_pnext; f->fr_pnext = NULL; f->fr_next = NULL; (void) ipf_derefrule(softc, &f); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rule_expire_insert */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* f(I) - pointer to rule to be added to expire list */ /* set(I) - 1 or 0 (filter set) */ /* */ /* If the new rule has a given expiration time, insert it into the list of */ /* expiring rules with the ones to be removed first added to the front of */ /* the list. The insertion is O(n) but it is kept sorted for quick scans at */ /* expiration interval checks. */ /* ------------------------------------------------------------------------ */ static void -ipf_rule_expire_insert(softc, f, set) - ipf_main_softc_t *softc; - frentry_t *f; - int set; +ipf_rule_expire_insert(ipf_main_softc_t *softc, frentry_t *f, int set) { frentry_t *fr; /* */ f->fr_die = softc->ipf_ticks + IPF_TTLVAL(f->fr_die); for (fr = softc->ipf_rule_explist[set]; fr != NULL; fr = fr->fr_dnext) { if (f->fr_die < fr->fr_die) break; if (fr->fr_dnext == NULL) { /* * We've got to the last rule and everything * wanted to be expired before this new node, * so we have to tack it on the end... */ fr->fr_dnext = f; f->fr_pdnext = &fr->fr_dnext; fr = NULL; break; } } if (softc->ipf_rule_explist[set] == NULL) { softc->ipf_rule_explist[set] = f; f->fr_pdnext = &softc->ipf_rule_explist[set]; } else if (fr != NULL) { f->fr_dnext = fr; f->fr_pdnext = fr->fr_pdnext; fr->fr_pdnext = &f->fr_dnext; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_findlookup */ /* Returns: NULL = failure, else success */ /* Parameters: softc(I) - pointer to soft context main structure */ /* unit(I) - ipf device we want to find match for */ /* fp(I) - rule for which lookup is for */ /* addrp(I) - pointer to lookup information in address struct */ /* maskp(O) - pointer to lookup information for storage */ /* */ /* When using pools and hash tables to store addresses for matching in */ /* rules, it is necessary to resolve both the object referred to by the */ /* name or address (and return that pointer) and also provide the means by */ /* which to determine if an address belongs to that object to make the */ /* packet matching quicker. */ /* ------------------------------------------------------------------------ */ static void * -ipf_findlookup(softc, unit, fr, addrp, maskp) - ipf_main_softc_t *softc; - int unit; - frentry_t *fr; - i6addr_t *addrp, *maskp; +ipf_findlookup(ipf_main_softc_t *softc, int unit, frentry_t *fr, + i6addr_t *addrp, i6addr_t *maskp) { void *ptr = NULL; switch (addrp->iplookupsubtype) { case 0 : ptr = ipf_lookup_res_num(softc, unit, addrp->iplookuptype, addrp->iplookupnum, &maskp->iplookupfunc); break; case 1 : if (addrp->iplookupname < 0) break; if (addrp->iplookupname >= fr->fr_namelen) break; ptr = ipf_lookup_res_name(softc, unit, addrp->iplookuptype, fr->fr_names + addrp->iplookupname, &maskp->iplookupfunc); break; default : break; } return ptr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_funcinit */ /* Returns: int - 0 == success, else ESRCH: cannot resolve rule details */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fr(I) - pointer to filter rule */ /* */ /* If a rule is a call rule, then check if the function it points to needs */ /* an init function to be called now the rule has been loaded. */ /* ------------------------------------------------------------------------ */ static int -ipf_funcinit(softc, fr) - ipf_main_softc_t *softc; - frentry_t *fr; +ipf_funcinit(ipf_main_softc_t *softc, frentry_t *fr) { ipfunc_resolve_t *ft; int err; IPFERROR(34); err = ESRCH; for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (ft->ipfu_addr == fr->fr_func) { err = 0; if (ft->ipfu_init != NULL) err = (*ft->ipfu_init)(softc, fr); break; } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_funcfini */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fr(I) - pointer to filter rule */ /* */ /* For a given filter rule, call the matching "fini" function if the rule */ /* is using a known function that would have resulted in the "init" being */ /* called for ealier. */ /* ------------------------------------------------------------------------ */ static void -ipf_funcfini(softc, fr) - ipf_main_softc_t *softc; - frentry_t *fr; +ipf_funcfini(ipf_main_softc_t *softc, frentry_t *fr) { ipfunc_resolve_t *ft; for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (ft->ipfu_addr == fr->fr_func) { if (ft->ipfu_fini != NULL) (void) (*ft->ipfu_fini)(softc, fr); break; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_findfunc */ /* Returns: ipfunc_t - pointer to function if found, else NULL */ /* Parameters: funcptr(I) - function pointer to lookup */ /* */ /* Look for a function in the table of known functions. */ /* ------------------------------------------------------------------------ */ static ipfunc_t -ipf_findfunc(funcptr) - ipfunc_t funcptr; +ipf_findfunc(ipfunc_t funcptr) { ipfunc_resolve_t *ft; for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (ft->ipfu_addr == funcptr) return funcptr; return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_resolvefunc */ /* Returns: int - 0 == success, else error */ /* Parameters: data(IO) - ioctl data pointer to ipfunc_resolve_t struct */ /* */ /* Copy in a ipfunc_resolve_t structure and then fill in the missing field. */ /* This will either be the function name (if the pointer is set) or the */ /* function pointer if the name is set. When found, fill in the other one */ /* so that the entire, complete, structure can be copied back to user space.*/ /* ------------------------------------------------------------------------ */ int -ipf_resolvefunc(softc, data) - ipf_main_softc_t *softc; - void *data; +ipf_resolvefunc(ipf_main_softc_t *softc, void *data) { ipfunc_resolve_t res, *ft; int error; error = BCOPYIN(data, &res, sizeof(res)); if (error != 0) { IPFERROR(123); return EFAULT; } if (res.ipfu_addr == NULL && res.ipfu_name[0] != '\0') { for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (strncmp(res.ipfu_name, ft->ipfu_name, sizeof(res.ipfu_name)) == 0) { res.ipfu_addr = ft->ipfu_addr; res.ipfu_init = ft->ipfu_init; if (COPYOUT(&res, data, sizeof(res)) != 0) { IPFERROR(35); return EFAULT; } return 0; } } if (res.ipfu_addr != NULL && res.ipfu_name[0] == '\0') { for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (ft->ipfu_addr == res.ipfu_addr) { (void) strncpy(res.ipfu_name, ft->ipfu_name, sizeof(res.ipfu_name)); res.ipfu_init = ft->ipfu_init; if (COPYOUT(&res, data, sizeof(res)) != 0) { IPFERROR(36); return EFAULT; } return 0; } } IPFERROR(37); return ESRCH; } #if !defined(_KERNEL) || SOLARIS /* * From: NetBSD * ppsratecheck(): packets (or events) per second limitation. */ int -ppsratecheck(lasttime, curpps, maxpps) - struct timeval *lasttime; - int *curpps; - int maxpps; /* maximum pps allowed */ +ppsratecheck(struct timeval *lasttime, int *curpps, int maxpps) + /* maxpps: maximum pps allowed */ { struct timeval tv, delta; int rv; GETKTIME(&tv); delta.tv_sec = tv.tv_sec - lasttime->tv_sec; delta.tv_usec = tv.tv_usec - lasttime->tv_usec; if (delta.tv_usec < 0) { delta.tv_sec--; delta.tv_usec += 1000000; } /* * check for 0,0 is so that the message will be seen at least once. * if more than one second have passed since the last update of * lasttime, reset the counter. * * we do increment *curpps even in *curpps < maxpps case, as some may * try to use *curpps for stat purposes as well. */ if ((lasttime->tv_sec == 0 && lasttime->tv_usec == 0) || delta.tv_sec >= 1) { *lasttime = tv; *curpps = 0; rv = 1; } else if (maxpps < 0) rv = 1; else if (*curpps < maxpps) rv = 1; else rv = 0; *curpps = *curpps + 1; return (rv); } #endif /* ------------------------------------------------------------------------ */ /* Function: ipf_derefrule */ /* Returns: int - 0 == rule freed up, else rule not freed */ /* Parameters: fr(I) - pointer to filter rule */ /* */ /* Decrement the reference counter to a rule by one. If it reaches zero, */ /* free it and any associated storage space being used by it. */ /* ------------------------------------------------------------------------ */ int -ipf_derefrule(softc, frp) - ipf_main_softc_t *softc; - frentry_t **frp; +ipf_derefrule(ipf_main_softc_t *softc, frentry_t **frp) { frentry_t *fr; frdest_t *fdp; fr = *frp; *frp = NULL; MUTEX_ENTER(&fr->fr_lock); fr->fr_ref--; if (fr->fr_ref == 0) { MUTEX_EXIT(&fr->fr_lock); MUTEX_DESTROY(&fr->fr_lock); ipf_funcfini(softc, fr); fdp = &fr->fr_tif; if (fdp->fd_type == FRD_DSTLIST) ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); fdp = &fr->fr_rif; if (fdp->fd_type == FRD_DSTLIST) ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); fdp = &fr->fr_dif; if (fdp->fd_type == FRD_DSTLIST) ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF && fr->fr_satype == FRI_LOOKUP) ipf_lookup_deref(softc, fr->fr_srctype, fr->fr_srcptr); if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF && fr->fr_datype == FRI_LOOKUP) ipf_lookup_deref(softc, fr->fr_dsttype, fr->fr_dstptr); if (fr->fr_grp != NULL) ipf_group_del(softc, fr->fr_grp, fr); if (fr->fr_grphead != NULL) ipf_group_del(softc, fr->fr_grphead, fr); if (fr->fr_icmpgrp != NULL) ipf_group_del(softc, fr->fr_icmpgrp, fr); if ((fr->fr_flags & FR_COPIED) != 0) { if (fr->fr_dsize) { KFREES(fr->fr_data, fr->fr_dsize); } KFREES(fr, fr->fr_size); return 0; } return 1; } else { MUTEX_EXIT(&fr->fr_lock); } return -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_grpmapinit */ /* Returns: int - 0 == success, else ESRCH because table entry not found*/ /* Parameters: fr(I) - pointer to rule to find hash table for */ /* */ /* Looks for group hash table fr_arg and stores a pointer to it in fr_ptr. */ /* fr_ptr is later used by ipf_srcgrpmap and ipf_dstgrpmap. */ /* ------------------------------------------------------------------------ */ static int -ipf_grpmapinit(softc, fr) - ipf_main_softc_t *softc; - frentry_t *fr; +ipf_grpmapinit(ipf_main_softc_t *softc, frentry_t *fr) { char name[FR_GROUPLEN]; iphtable_t *iph; (void) snprintf(name, sizeof(name), "%d", fr->fr_arg); iph = ipf_lookup_find_htable(softc, IPL_LOGIPF, name); if (iph == NULL) { IPFERROR(38); return ESRCH; } if ((iph->iph_flags & FR_INOUT) != (fr->fr_flags & FR_INOUT)) { IPFERROR(39); return ESRCH; } iph->iph_ref++; fr->fr_ptr = iph; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_grpmapfini */ /* Returns: int - 0 == success, else ESRCH because table entry not found*/ /* Parameters: softc(I) - pointer to soft context main structure */ /* fr(I) - pointer to rule to release hash table for */ /* */ /* For rules that have had ipf_grpmapinit called, ipf_lookup_deref needs to */ /* be called to undo what ipf_grpmapinit caused to be done. */ /* ------------------------------------------------------------------------ */ static int -ipf_grpmapfini(softc, fr) - ipf_main_softc_t *softc; - frentry_t *fr; +ipf_grpmapfini(ipf_main_softc_t *softc, frentry_t *fr) { iphtable_t *iph; iph = fr->fr_ptr; if (iph != NULL) ipf_lookup_deref(softc, IPLT_HASH, iph); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_srcgrpmap */ /* Returns: frentry_t * - pointer to "new last matching" rule or NULL */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Look for a rule group head in a hash table, using the source address as */ /* the key, and descend into that group and continue matching rules against */ /* the packet. */ /* ------------------------------------------------------------------------ */ frentry_t * -ipf_srcgrpmap(fin, passp) - fr_info_t *fin; - u_32_t *passp; +ipf_srcgrpmap(fr_info_t *fin, u_32_t *passp) { frgroup_t *fg; void *rval; rval = ipf_iphmfindgroup(fin->fin_main_soft, fin->fin_fr->fr_ptr, &fin->fin_src); if (rval == NULL) return NULL; fg = rval; fin->fin_fr = fg->fg_start; (void) ipf_scanlist(fin, *passp); return fin->fin_fr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstgrpmap */ /* Returns: frentry_t * - pointer to "new last matching" rule or NULL */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Look for a rule group head in a hash table, using the destination */ /* address as the key, and descend into that group and continue matching */ /* rules against the packet. */ /* ------------------------------------------------------------------------ */ frentry_t * -ipf_dstgrpmap(fin, passp) - fr_info_t *fin; - u_32_t *passp; +ipf_dstgrpmap(fr_info_t *fin, u_32_t *passp) { frgroup_t *fg; void *rval; rval = ipf_iphmfindgroup(fin->fin_main_soft, fin->fin_fr->fr_ptr, &fin->fin_dst); if (rval == NULL) return NULL; fg = rval; fin->fin_fr = fg->fg_start; (void) ipf_scanlist(fin, *passp); return fin->fin_fr; } /* * Queue functions * =============== * These functions manage objects on queues for efficient timeouts. There * are a number of system defined queues as well as user defined timeouts. * It is expected that a lock is held in the domain in which the queue * belongs (i.e. either state or NAT) when calling any of these functions * that prevents ipf_freetimeoutqueue() from being called at the same time * as any other. */ /* ------------------------------------------------------------------------ */ /* Function: ipf_addtimeoutqueue */ /* Returns: struct ifqtq * - NULL if malloc fails, else pointer to */ /* timeout queue with given interval. */ /* Parameters: parent(I) - pointer to pointer to parent node of this list */ /* of interface queues. */ /* seconds(I) - timeout value in seconds for this queue. */ /* */ /* This routine first looks for a timeout queue that matches the interval */ /* being requested. If it finds one, increments the reference counter and */ /* returns a pointer to it. If none are found, it allocates a new one and */ /* inserts it at the top of the list. */ /* */ /* Locking. */ /* It is assumed that the caller of this function has an appropriate lock */ /* held (exclusively) in the domain that encompases 'parent'. */ /* ------------------------------------------------------------------------ */ ipftq_t * -ipf_addtimeoutqueue(softc, parent, seconds) - ipf_main_softc_t *softc; - ipftq_t **parent; - u_int seconds; +ipf_addtimeoutqueue(ipf_main_softc_t *softc, ipftq_t **parent, u_int seconds) { ipftq_t *ifq; u_int period; period = seconds * IPF_HZ_DIVIDE; MUTEX_ENTER(&softc->ipf_timeoutlock); for (ifq = *parent; ifq != NULL; ifq = ifq->ifq_next) { if (ifq->ifq_ttl == period) { /* * Reset the delete flag, if set, so the structure * gets reused rather than freed and reallocated. */ MUTEX_ENTER(&ifq->ifq_lock); ifq->ifq_flags &= ~IFQF_DELETE; ifq->ifq_ref++; MUTEX_EXIT(&ifq->ifq_lock); MUTEX_EXIT(&softc->ipf_timeoutlock); return ifq; } } KMALLOC(ifq, ipftq_t *); if (ifq != NULL) { MUTEX_NUKE(&ifq->ifq_lock); IPFTQ_INIT(ifq, period, "ipftq mutex"); ifq->ifq_next = *parent; ifq->ifq_pnext = parent; ifq->ifq_flags = IFQF_USER; ifq->ifq_ref++; *parent = ifq; softc->ipf_userifqs++; } MUTEX_EXIT(&softc->ipf_timeoutlock); return ifq; } /* ------------------------------------------------------------------------ */ /* Function: ipf_deletetimeoutqueue */ /* Returns: int - new reference count value of the timeout queue */ /* Parameters: ifq(I) - timeout queue which is losing a reference. */ /* Locks: ifq->ifq_lock */ /* */ /* This routine must be called when we're discarding a pointer to a timeout */ /* queue object, taking care of the reference counter. */ /* */ /* Now that this just sets a DELETE flag, it requires the expire code to */ /* check the list of user defined timeout queues and call the free function */ /* below (currently commented out) to stop memory leaking. It is done this */ /* way because the locking may not be sufficient to safely do a free when */ /* this function is called. */ /* ------------------------------------------------------------------------ */ int -ipf_deletetimeoutqueue(ifq) - ipftq_t *ifq; +ipf_deletetimeoutqueue(ipftq_t *ifq) { ifq->ifq_ref--; if ((ifq->ifq_ref == 0) && ((ifq->ifq_flags & IFQF_USER) != 0)) { ifq->ifq_flags |= IFQF_DELETE; } return ifq->ifq_ref; } /* ------------------------------------------------------------------------ */ /* Function: ipf_freetimeoutqueue */ /* Parameters: ifq(I) - timeout queue which is losing a reference. */ /* Returns: Nil */ /* */ /* Locking: */ /* It is assumed that the caller of this function has an appropriate lock */ /* held (exclusively) in the domain that encompases the callers "domain". */ /* The ifq_lock for this structure should not be held. */ /* */ /* Remove a user defined timeout queue from the list of queues it is in and */ /* tidy up after this is done. */ /* ------------------------------------------------------------------------ */ void -ipf_freetimeoutqueue(softc, ifq) - ipf_main_softc_t *softc; - ipftq_t *ifq; +ipf_freetimeoutqueue(ipf_main_softc_t *softc, ipftq_t *ifq) { if (((ifq->ifq_flags & IFQF_DELETE) == 0) || (ifq->ifq_ref != 0) || ((ifq->ifq_flags & IFQF_USER) == 0)) { printf("ipf_freetimeoutqueue(%lx) flags 0x%x ttl %d ref %d\n", (u_long)ifq, ifq->ifq_flags, ifq->ifq_ttl, ifq->ifq_ref); return; } /* * Remove from its position in the list. */ *ifq->ifq_pnext = ifq->ifq_next; if (ifq->ifq_next != NULL) ifq->ifq_next->ifq_pnext = ifq->ifq_pnext; ifq->ifq_next = NULL; ifq->ifq_pnext = NULL; MUTEX_DESTROY(&ifq->ifq_lock); ATOMIC_DEC(softc->ipf_userifqs); KFREE(ifq); } /* ------------------------------------------------------------------------ */ /* Function: ipf_deletequeueentry */ /* Returns: Nil */ /* Parameters: tqe(I) - timeout queue entry to delete */ /* */ /* Remove a tail queue entry from its queue and make it an orphan. */ /* ipf_deletetimeoutqueue is called to make sure the reference count on the */ /* queue is correct. We can't, however, call ipf_freetimeoutqueue because */ /* the correct lock(s) may not be held that would make it safe to do so. */ /* ------------------------------------------------------------------------ */ void -ipf_deletequeueentry(tqe) - ipftqent_t *tqe; +ipf_deletequeueentry(ipftqent_t *tqe) { ipftq_t *ifq; ifq = tqe->tqe_ifq; MUTEX_ENTER(&ifq->ifq_lock); if (tqe->tqe_pnext != NULL) { *tqe->tqe_pnext = tqe->tqe_next; if (tqe->tqe_next != NULL) tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; else /* we must be the tail anyway */ ifq->ifq_tail = tqe->tqe_pnext; tqe->tqe_pnext = NULL; tqe->tqe_ifq = NULL; } (void) ipf_deletetimeoutqueue(ifq); ASSERT(ifq->ifq_ref > 0); MUTEX_EXIT(&ifq->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_queuefront */ /* Returns: Nil */ /* Parameters: tqe(I) - pointer to timeout queue entry */ /* */ /* Move a queue entry to the front of the queue, if it isn't already there. */ /* ------------------------------------------------------------------------ */ void -ipf_queuefront(tqe) - ipftqent_t *tqe; +ipf_queuefront(ipftqent_t *tqe) { ipftq_t *ifq; ifq = tqe->tqe_ifq; if (ifq == NULL) return; MUTEX_ENTER(&ifq->ifq_lock); if (ifq->ifq_head != tqe) { *tqe->tqe_pnext = tqe->tqe_next; if (tqe->tqe_next) tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; else ifq->ifq_tail = tqe->tqe_pnext; tqe->tqe_next = ifq->ifq_head; ifq->ifq_head->tqe_pnext = &tqe->tqe_next; ifq->ifq_head = tqe; tqe->tqe_pnext = &ifq->ifq_head; } MUTEX_EXIT(&ifq->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_queueback */ /* Returns: Nil */ /* Parameters: ticks(I) - ipf tick time to use with this call */ /* tqe(I) - pointer to timeout queue entry */ /* */ /* Move a queue entry to the back of the queue, if it isn't already there. */ /* We use use ticks to calculate the expiration and mark for when we last */ /* touched the structure. */ /* ------------------------------------------------------------------------ */ void -ipf_queueback(ticks, tqe) - u_long ticks; - ipftqent_t *tqe; +ipf_queueback(u_long ticks, ipftqent_t *tqe) { ipftq_t *ifq; ifq = tqe->tqe_ifq; if (ifq == NULL) return; tqe->tqe_die = ticks + ifq->ifq_ttl; tqe->tqe_touched = ticks; MUTEX_ENTER(&ifq->ifq_lock); if (tqe->tqe_next != NULL) { /* at the end already ? */ /* * Remove from list */ *tqe->tqe_pnext = tqe->tqe_next; tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; /* * Make it the last entry. */ tqe->tqe_next = NULL; tqe->tqe_pnext = ifq->ifq_tail; *ifq->ifq_tail = tqe; ifq->ifq_tail = &tqe->tqe_next; } MUTEX_EXIT(&ifq->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_queueappend */ /* Returns: Nil */ /* Parameters: ticks(I) - ipf tick time to use with this call */ /* tqe(I) - pointer to timeout queue entry */ /* ifq(I) - pointer to timeout queue */ /* parent(I) - owing object pointer */ /* */ /* Add a new item to this queue and put it on the very end. */ /* We use use ticks to calculate the expiration and mark for when we last */ /* touched the structure. */ /* ------------------------------------------------------------------------ */ void -ipf_queueappend(ticks, tqe, ifq, parent) - u_long ticks; - ipftqent_t *tqe; - ipftq_t *ifq; - void *parent; +ipf_queueappend(u_long ticks, ipftqent_t *tqe, ipftq_t *ifq, void *parent) { MUTEX_ENTER(&ifq->ifq_lock); tqe->tqe_parent = parent; tqe->tqe_pnext = ifq->ifq_tail; *ifq->ifq_tail = tqe; ifq->ifq_tail = &tqe->tqe_next; tqe->tqe_next = NULL; tqe->tqe_ifq = ifq; tqe->tqe_die = ticks + ifq->ifq_ttl; tqe->tqe_touched = ticks; ifq->ifq_ref++; MUTEX_EXIT(&ifq->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_movequeue */ /* Returns: Nil */ /* Parameters: tq(I) - pointer to timeout queue information */ /* oifp(I) - old timeout queue entry was on */ /* nifp(I) - new timeout queue to put entry on */ /* */ /* Move a queue entry from one timeout queue to another timeout queue. */ /* If it notices that the current entry is already last and does not need */ /* to move queue, the return. */ /* ------------------------------------------------------------------------ */ void -ipf_movequeue(ticks, tqe, oifq, nifq) - u_long ticks; - ipftqent_t *tqe; - ipftq_t *oifq, *nifq; +ipf_movequeue(u_long ticks, ipftqent_t *tqe, ipftq_t *oifq, ipftq_t *nifq) { /* * If the queue hasn't changed and we last touched this entry at the * same ipf time, then we're not going to achieve anything by either * changing the ttl or moving it on the queue. */ if (oifq == nifq && tqe->tqe_touched == ticks) return; /* * For any of this to be outside the lock, there is a risk that two * packets entering simultaneously, with one changing to a different * queue and one not, could end up with things in a bizarre state. */ MUTEX_ENTER(&oifq->ifq_lock); tqe->tqe_touched = ticks; tqe->tqe_die = ticks + nifq->ifq_ttl; /* * Is the operation here going to be a no-op ? */ if (oifq == nifq) { if ((tqe->tqe_next == NULL) || (tqe->tqe_next->tqe_die == tqe->tqe_die)) { MUTEX_EXIT(&oifq->ifq_lock); return; } } /* * Remove from the old queue */ *tqe->tqe_pnext = tqe->tqe_next; if (tqe->tqe_next) tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; else oifq->ifq_tail = tqe->tqe_pnext; tqe->tqe_next = NULL; /* * If we're moving from one queue to another, release the * lock on the old queue and get a lock on the new queue. * For user defined queues, if we're moving off it, call * delete in case it can now be freed. */ if (oifq != nifq) { tqe->tqe_ifq = NULL; (void) ipf_deletetimeoutqueue(oifq); MUTEX_EXIT(&oifq->ifq_lock); MUTEX_ENTER(&nifq->ifq_lock); tqe->tqe_ifq = nifq; nifq->ifq_ref++; } /* * Add to the bottom of the new queue */ tqe->tqe_pnext = nifq->ifq_tail; *nifq->ifq_tail = tqe; nifq->ifq_tail = &tqe->tqe_next; MUTEX_EXIT(&nifq->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_updateipid */ /* Returns: int - 0 == success, -1 == error (packet should be droppped) */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* When we are doing NAT, change the IP of every packet to represent a */ /* single sequence of packets coming from the host, hiding any host */ /* specific sequencing that might otherwise be revealed. If the packet is */ /* a fragment, then store the 'new' IPid in the fragment cache and look up */ /* the fragment cache for non-leading fragments. If a non-leading fragment */ /* has no match in the cache, return an error. */ /* ------------------------------------------------------------------------ */ static int -ipf_updateipid(fin) - fr_info_t *fin; +ipf_updateipid(fr_info_t *fin) { u_short id, ido, sums; u_32_t sumd, sum; ip_t *ip; ip = fin->fin_ip; ido = ntohs(ip->ip_id); if (fin->fin_off != 0) { sum = ipf_frag_ipidknown(fin); if (sum == 0xffffffff) return -1; sum &= 0xffff; id = (u_short)sum; ip->ip_id = htons(id); } else { ip_fillid(ip); id = ntohs(ip->ip_id); if ((fin->fin_flx & FI_FRAG) != 0) (void) ipf_frag_ipidnew(fin, (u_32_t)id); } if (id == ido) return 0; CALC_SUMD(ido, id, sumd); /* DESTRUCTIVE MACRO! id,ido change */ sum = (~ntohs(ip->ip_sum)) & 0xffff; sum += sumd; sum = (sum >> 16) + (sum & 0xffff); sum = (sum >> 16) + (sum & 0xffff); sums = ~(u_short)sum; ip->ip_sum = htons(sums); return 0; } #ifdef NEED_FRGETIFNAME /* ------------------------------------------------------------------------ */ /* Function: ipf_getifname */ /* Returns: char * - pointer to interface name */ /* Parameters: ifp(I) - pointer to network interface */ /* buffer(O) - pointer to where to store interface name */ /* */ /* Constructs an interface name in the buffer passed. The buffer passed is */ /* expected to be at least LIFNAMSIZ in bytes big. If buffer is passed in */ /* as a NULL pointer then return a pointer to a static array. */ /* ------------------------------------------------------------------------ */ char * -ipf_getifname(ifp, buffer) - struct ifnet *ifp; - char *buffer; +ipf_getifname(struct ifnet *ifp, char *buffer) { static char namebuf[LIFNAMSIZ]; # if SOLARIS || defined(__FreeBSD__) int unit, space; char temp[20]; char *s; # endif if (buffer == NULL) buffer = namebuf; (void) strncpy(buffer, ifp->if_name, LIFNAMSIZ); buffer[LIFNAMSIZ - 1] = '\0'; # if SOLARIS || defined(__FreeBSD__) for (s = buffer; *s; s++) ; unit = ifp->if_unit; space = LIFNAMSIZ - (s - buffer); if ((space > 0) && (unit >= 0)) { (void) snprintf(temp, sizeof(name), "%d", unit); (void) strncpy(s, temp, space); } # endif return buffer; } #endif /* ------------------------------------------------------------------------ */ /* Function: ipf_ioctlswitch */ /* Returns: int - -1 continue processing, else ioctl return value */ /* Parameters: unit(I) - device unit opened */ /* data(I) - pointer to ioctl data */ /* cmd(I) - ioctl command */ /* mode(I) - mode value */ /* uid(I) - uid making the ioctl call */ /* ctx(I) - pointer to context data */ /* */ /* Based on the value of unit, call the appropriate ioctl handler or return */ /* EIO if ipfilter is not running. Also checks if write perms are req'd */ /* for the device in order to execute the ioctl. A special case is made */ /* SIOCIPFINTERROR so that the same code isn't required in every handler. */ /* The context data pointer is passed through as this is used as the key */ /* for locating a matching token for continued access for walking lists, */ /* etc. */ /* ------------------------------------------------------------------------ */ int -ipf_ioctlswitch(softc, unit, data, cmd, mode, uid, ctx) - ipf_main_softc_t *softc; - int unit, mode, uid; - ioctlcmd_t cmd; - void *data, *ctx; +ipf_ioctlswitch(ipf_main_softc_t *softc, int unit, void *data, ioctlcmd_t cmd, + int mode, int uid, void *ctx) { int error = 0; switch (cmd) { case SIOCIPFINTERROR : error = BCOPYOUT(&softc->ipf_interror, data, sizeof(softc->ipf_interror)); if (error != 0) { IPFERROR(40); error = EFAULT; } return error; default : break; } switch (unit) { case IPL_LOGIPF : error = ipf_ipf_ioctl(softc, data, cmd, mode, uid, ctx); break; case IPL_LOGNAT : if (softc->ipf_running > 0) { error = ipf_nat_ioctl(softc, data, cmd, mode, uid, ctx); } else { IPFERROR(42); error = EIO; } break; case IPL_LOGSTATE : if (softc->ipf_running > 0) { error = ipf_state_ioctl(softc, data, cmd, mode, uid, ctx); } else { IPFERROR(43); error = EIO; } break; case IPL_LOGAUTH : if (softc->ipf_running > 0) { error = ipf_auth_ioctl(softc, data, cmd, mode, uid, ctx); } else { IPFERROR(44); error = EIO; } break; case IPL_LOGSYNC : if (softc->ipf_running > 0) { error = ipf_sync_ioctl(softc, data, cmd, mode, uid, ctx); } else { error = EIO; IPFERROR(45); } break; case IPL_LOGSCAN : #ifdef IPFILTER_SCAN if (softc->ipf_running > 0) error = ipf_scan_ioctl(softc, data, cmd, mode, uid, ctx); else #endif { error = EIO; IPFERROR(46); } break; case IPL_LOGLOOKUP : if (softc->ipf_running > 0) { error = ipf_lookup_ioctl(softc, data, cmd, mode, uid, ctx); } else { error = EIO; IPFERROR(47); } break; default : IPFERROR(48); error = EIO; break; } return error; } /* * This array defines the expected size of objects coming into the kernel * for the various recognised object types. The first column is flags (see * below), 2nd column is current size, 3rd column is the version number of * when the current size became current. * Flags: * 1 = minimum size, not absolute size */ static const int ipf_objbytes[IPFOBJ_COUNT][3] = { { 1, sizeof(struct frentry), 5010000 }, /* 0 */ { 1, sizeof(struct friostat), 5010000 }, { 0, sizeof(struct fr_info), 5010000 }, { 0, sizeof(struct ipf_authstat), 4010100 }, { 0, sizeof(struct ipfrstat), 5010000 }, { 1, sizeof(struct ipnat), 5010000 }, /* 5 */ { 0, sizeof(struct natstat), 5010000 }, { 0, sizeof(struct ipstate_save), 5010000 }, { 1, sizeof(struct nat_save), 5010000 }, { 0, sizeof(struct natlookup), 5010000 }, { 1, sizeof(struct ipstate), 5010000 }, /* 10 */ { 0, sizeof(struct ips_stat), 5010000 }, { 0, sizeof(struct frauth), 5010000 }, { 0, sizeof(struct ipftune), 4010100 }, { 0, sizeof(struct nat), 5010000 }, { 0, sizeof(struct ipfruleiter), 4011400 }, /* 15 */ { 0, sizeof(struct ipfgeniter), 4011400 }, { 0, sizeof(struct ipftable), 4011400 }, { 0, sizeof(struct ipflookupiter), 4011400 }, { 0, sizeof(struct ipftq) * IPF_TCP_NSTATES }, { 1, 0, 0 }, /* IPFEXPR */ { 0, 0, 0 }, /* PROXYCTL */ { 0, sizeof (struct fripf), 5010000 } }; /* ------------------------------------------------------------------------ */ /* Function: ipf_inobj */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - soft context pointerto work with */ /* data(I) - pointer to ioctl data */ /* objp(O) - where to store ipfobj structure */ /* ptr(I) - pointer to data to copy out */ /* type(I) - type of structure being moved */ /* */ /* Copy in the contents of what the ipfobj_t points to. In future, we */ /* add things to check for version numbers, sizes, etc, to make it backward */ /* compatible at the ABI for user land. */ /* If objp is not NULL then we assume that the caller wants to see what is */ /* in the ipfobj_t structure being copied in. As an example, this can tell */ /* the caller what version of ipfilter the ioctl program was written to. */ /* ------------------------------------------------------------------------ */ int -ipf_inobj(softc, data, objp, ptr, type) - ipf_main_softc_t *softc; - void *data; - ipfobj_t *objp; - void *ptr; - int type; +ipf_inobj(ipf_main_softc_t *softc, void *data, ipfobj_t *objp, void *ptr, + int type) { ipfobj_t obj; int error; int size; if ((type < 0) || (type >= IPFOBJ_COUNT)) { IPFERROR(49); return EINVAL; } if (objp == NULL) objp = &obj; error = BCOPYIN(data, objp, sizeof(*objp)); if (error != 0) { IPFERROR(124); return EFAULT; } if (objp->ipfo_type != type) { IPFERROR(50); return EINVAL; } if (objp->ipfo_rev >= ipf_objbytes[type][2]) { if ((ipf_objbytes[type][0] & 1) != 0) { if (objp->ipfo_size < ipf_objbytes[type][1]) { IPFERROR(51); return EINVAL; } size = ipf_objbytes[type][1]; } else if (objp->ipfo_size == ipf_objbytes[type][1]) { size = objp->ipfo_size; } else { IPFERROR(52); return EINVAL; } error = COPYIN(objp->ipfo_ptr, ptr, size); if (error != 0) { IPFERROR(55); error = EFAULT; } } else { #ifdef IPFILTER_COMPAT error = ipf_in_compat(softc, objp, ptr, 0); #else IPFERROR(54); error = EINVAL; #endif } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_inobjsz */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - soft context pointerto work with */ /* data(I) - pointer to ioctl data */ /* ptr(I) - pointer to store real data in */ /* type(I) - type of structure being moved */ /* sz(I) - size of data to copy */ /* */ /* As per ipf_inobj, except the size of the object to copy in is passed in */ /* but it must not be smaller than the size defined for the type and the */ /* type must allow for varied sized objects. The extra requirement here is */ /* that sz must match the size of the object being passed in - this is not */ /* not possible nor required in ipf_inobj(). */ /* ------------------------------------------------------------------------ */ int -ipf_inobjsz(softc, data, ptr, type, sz) - ipf_main_softc_t *softc; - void *data; - void *ptr; - int type, sz; +ipf_inobjsz(ipf_main_softc_t *softc, void *data, void *ptr, int type, int sz) { ipfobj_t obj; int error; if ((type < 0) || (type >= IPFOBJ_COUNT)) { IPFERROR(56); return EINVAL; } error = BCOPYIN(data, &obj, sizeof(obj)); if (error != 0) { IPFERROR(125); return EFAULT; } if (obj.ipfo_type != type) { IPFERROR(58); return EINVAL; } if (obj.ipfo_rev >= ipf_objbytes[type][2]) { if (((ipf_objbytes[type][0] & 1) == 0) || (sz < ipf_objbytes[type][1])) { IPFERROR(57); return EINVAL; } error = COPYIN(obj.ipfo_ptr, ptr, sz); if (error != 0) { IPFERROR(61); error = EFAULT; } } else { #ifdef IPFILTER_COMPAT error = ipf_in_compat(softc, &obj, ptr, sz); #else IPFERROR(60); error = EINVAL; #endif } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_outobjsz */ /* Returns: int - 0 = success, else failure */ /* Parameters: data(I) - pointer to ioctl data */ /* ptr(I) - pointer to store real data in */ /* type(I) - type of structure being moved */ /* sz(I) - size of data to copy */ /* */ /* As per ipf_outobj, except the size of the object to copy out is passed in*/ /* but it must not be smaller than the size defined for the type and the */ /* type must allow for varied sized objects. The extra requirement here is */ /* that sz must match the size of the object being passed in - this is not */ /* not possible nor required in ipf_outobj(). */ /* ------------------------------------------------------------------------ */ int -ipf_outobjsz(softc, data, ptr, type, sz) - ipf_main_softc_t *softc; - void *data; - void *ptr; - int type, sz; +ipf_outobjsz(ipf_main_softc_t *softc, void *data, void *ptr, int type, int sz) { ipfobj_t obj; int error; if ((type < 0) || (type >= IPFOBJ_COUNT)) { IPFERROR(62); return EINVAL; } error = BCOPYIN(data, &obj, sizeof(obj)); if (error != 0) { IPFERROR(127); return EFAULT; } if (obj.ipfo_type != type) { IPFERROR(63); return EINVAL; } if (obj.ipfo_rev >= ipf_objbytes[type][2]) { if (((ipf_objbytes[type][0] & 1) == 0) || (sz < ipf_objbytes[type][1])) { IPFERROR(146); return EINVAL; } error = COPYOUT(ptr, obj.ipfo_ptr, sz); if (error != 0) { IPFERROR(66); error = EFAULT; } } else { #ifdef IPFILTER_COMPAT error = ipf_out_compat(softc, &obj, ptr); #else IPFERROR(65); error = EINVAL; #endif } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_outobj */ /* Returns: int - 0 = success, else failure */ /* Parameters: data(I) - pointer to ioctl data */ /* ptr(I) - pointer to store real data in */ /* type(I) - type of structure being moved */ /* */ /* Copy out the contents of what ptr is to where ipfobj points to. In */ /* future, we add things to check for version numbers, sizes, etc, to make */ /* it backward compatible at the ABI for user land. */ /* ------------------------------------------------------------------------ */ int -ipf_outobj(softc, data, ptr, type) - ipf_main_softc_t *softc; - void *data; - void *ptr; - int type; +ipf_outobj(ipf_main_softc_t *softc, void *data, void *ptr, int type) { ipfobj_t obj; int error; if ((type < 0) || (type >= IPFOBJ_COUNT)) { IPFERROR(67); return EINVAL; } error = BCOPYIN(data, &obj, sizeof(obj)); if (error != 0) { IPFERROR(126); return EFAULT; } if (obj.ipfo_type != type) { IPFERROR(68); return EINVAL; } if (obj.ipfo_rev >= ipf_objbytes[type][2]) { if ((ipf_objbytes[type][0] & 1) != 0) { if (obj.ipfo_size < ipf_objbytes[type][1]) { IPFERROR(69); return EINVAL; } } else if (obj.ipfo_size != ipf_objbytes[type][1]) { IPFERROR(70); return EINVAL; } error = COPYOUT(ptr, obj.ipfo_ptr, obj.ipfo_size); if (error != 0) { IPFERROR(73); error = EFAULT; } } else { #ifdef IPFILTER_COMPAT error = ipf_out_compat(softc, &obj, ptr); #else IPFERROR(72); error = EINVAL; #endif } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_outobjk */ /* Returns: int - 0 = success, else failure */ /* Parameters: obj(I) - pointer to data description structure */ /* ptr(I) - pointer to kernel data to copy out */ /* */ /* In the above functions, the ipfobj_t structure is copied into the kernel,*/ /* telling ipfilter how to copy out data. In this instance, the ipfobj_t is */ /* already populated with information and now we just need to use it. */ /* There is no need for this function to have a "type" parameter as there */ /* is no point in validating information that comes from the kernel with */ /* itself. */ /* ------------------------------------------------------------------------ */ int -ipf_outobjk(softc, obj, ptr) - ipf_main_softc_t *softc; - ipfobj_t *obj; - void *ptr; +ipf_outobjk(ipf_main_softc_t *softc, ipfobj_t *obj, void *ptr) { int type = obj->ipfo_type; int error; if ((type < 0) || (type >= IPFOBJ_COUNT)) { IPFERROR(147); return EINVAL; } if (obj->ipfo_rev >= ipf_objbytes[type][2]) { if ((ipf_objbytes[type][0] & 1) != 0) { if (obj->ipfo_size < ipf_objbytes[type][1]) { IPFERROR(148); return EINVAL; } } else if (obj->ipfo_size != ipf_objbytes[type][1]) { IPFERROR(149); return EINVAL; } error = COPYOUT(ptr, obj->ipfo_ptr, obj->ipfo_size); if (error != 0) { IPFERROR(150); error = EFAULT; } } else { #ifdef IPFILTER_COMPAT error = ipf_out_compat(softc, obj, ptr); #else IPFERROR(151); error = EINVAL; #endif } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_checkl4sum */ /* Returns: int - 0 = good, -1 = bad, 1 = cannot check */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* If possible, calculate the layer 4 checksum for the packet. If this is */ /* not possible, return without indicating a failure or success but in a */ /* way that is ditinguishable. This function should only be called by the */ /* ipf_checkv6sum() for each platform. */ /* ------------------------------------------------------------------------ */ INLINE int -ipf_checkl4sum(fin) - fr_info_t *fin; +ipf_checkl4sum(fr_info_t *fin) { u_short sum, hdrsum, *csump; udphdr_t *udp; int dosum; /* * If the TCP packet isn't a fragment, isn't too short and otherwise * isn't already considered "bad", then validate the checksum. If * this check fails then considered the packet to be "bad". */ if ((fin->fin_flx & (FI_FRAG|FI_SHORT|FI_BAD)) != 0) return 1; DT2(l4sumo, int, fin->fin_out, int, (int)fin->fin_p); if (fin->fin_out == 1) { fin->fin_cksum = FI_CK_SUMOK; return 0; } csump = NULL; hdrsum = 0; dosum = 0; sum = 0; switch (fin->fin_p) { case IPPROTO_TCP : csump = &((tcphdr_t *)fin->fin_dp)->th_sum; dosum = 1; break; case IPPROTO_UDP : udp = fin->fin_dp; if (udp->uh_sum != 0) { csump = &udp->uh_sum; dosum = 1; } break; #ifdef USE_INET6 case IPPROTO_ICMPV6 : csump = &((struct icmp6_hdr *)fin->fin_dp)->icmp6_cksum; dosum = 1; break; #endif case IPPROTO_ICMP : csump = &((struct icmp *)fin->fin_dp)->icmp_cksum; dosum = 1; break; default : return 1; /*NOTREACHED*/ } if (csump != NULL) { hdrsum = *csump; if (fin->fin_p == IPPROTO_UDP && hdrsum == 0xffff) hdrsum = 0x0000; } if (dosum) { sum = fr_cksum(fin, fin->fin_ip, fin->fin_p, fin->fin_dp); } #if !defined(_KERNEL) if (sum == hdrsum) { FR_DEBUG(("checkl4sum: %hx == %hx\n", sum, hdrsum)); } else { FR_DEBUG(("checkl4sum: %hx != %hx\n", sum, hdrsum)); } #endif DT3(l4sums, u_short, hdrsum, u_short, sum, fr_info_t *, fin); #ifdef USE_INET6 if (hdrsum == sum || (sum == 0 && IP_V(fin->fin_ip) == 6)) { #else if (hdrsum == sum) { #endif fin->fin_cksum = FI_CK_SUMOK; return 0; } fin->fin_cksum = FI_CK_BAD; return -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_ifpfillv4addr */ /* Returns: int - 0 = address update, -1 = address not updated */ /* Parameters: atype(I) - type of network address update to perform */ /* sin(I) - pointer to source of address information */ /* mask(I) - pointer to source of netmask information */ /* inp(I) - pointer to destination address store */ /* inpmask(I) - pointer to destination netmask store */ /* */ /* Given a type of network address update (atype) to perform, copy */ /* information from sin/mask into inp/inpmask. If ipnmask is NULL then no */ /* netmask update is performed unless FRI_NETMASKED is passed as atype, in */ /* which case the operation fails. For all values of atype other than */ /* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s */ /* value. */ /* ------------------------------------------------------------------------ */ int -ipf_ifpfillv4addr(atype, sin, mask, inp, inpmask) - int atype; - struct sockaddr_in *sin, *mask; - struct in_addr *inp, *inpmask; +ipf_ifpfillv4addr(int atype, struct sockaddr_in *sin, struct sockaddr_in *mask, + struct in_addr *inp, struct in_addr *inpmask) { if (inpmask != NULL && atype != FRI_NETMASKED) inpmask->s_addr = 0xffffffff; if (atype == FRI_NETWORK || atype == FRI_NETMASKED) { if (atype == FRI_NETMASKED) { if (inpmask == NULL) return -1; inpmask->s_addr = mask->sin_addr.s_addr; } inp->s_addr = sin->sin_addr.s_addr & mask->sin_addr.s_addr; } else { inp->s_addr = sin->sin_addr.s_addr; } return 0; } #ifdef USE_INET6 /* ------------------------------------------------------------------------ */ /* Function: ipf_ifpfillv6addr */ /* Returns: int - 0 = address update, -1 = address not updated */ /* Parameters: atype(I) - type of network address update to perform */ /* sin(I) - pointer to source of address information */ /* mask(I) - pointer to source of netmask information */ /* inp(I) - pointer to destination address store */ /* inpmask(I) - pointer to destination netmask store */ /* */ /* Given a type of network address update (atype) to perform, copy */ /* information from sin/mask into inp/inpmask. If ipnmask is NULL then no */ /* netmask update is performed unless FRI_NETMASKED is passed as atype, in */ /* which case the operation fails. For all values of atype other than */ /* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s */ /* value. */ /* ------------------------------------------------------------------------ */ int -ipf_ifpfillv6addr(atype, sin, mask, inp, inpmask) - int atype; - struct sockaddr_in6 *sin, *mask; - i6addr_t *inp, *inpmask; +ipf_ifpfillv6addr(int atype, struct sockaddr_in6 *sin, + struct sockaddr_in6 *mask, i6addr_t *inp, i6addr_t *inpmask) { i6addr_t *src, *and; src = (i6addr_t *)&sin->sin6_addr; and = (i6addr_t *)&mask->sin6_addr; if (inpmask != NULL && atype != FRI_NETMASKED) { inpmask->i6[0] = 0xffffffff; inpmask->i6[1] = 0xffffffff; inpmask->i6[2] = 0xffffffff; inpmask->i6[3] = 0xffffffff; } if (atype == FRI_NETWORK || atype == FRI_NETMASKED) { if (atype == FRI_NETMASKED) { if (inpmask == NULL) return -1; inpmask->i6[0] = and->i6[0]; inpmask->i6[1] = and->i6[1]; inpmask->i6[2] = and->i6[2]; inpmask->i6[3] = and->i6[3]; } inp->i6[0] = src->i6[0] & and->i6[0]; inp->i6[1] = src->i6[1] & and->i6[1]; inp->i6[2] = src->i6[2] & and->i6[2]; inp->i6[3] = src->i6[3] & and->i6[3]; } else { inp->i6[0] = src->i6[0]; inp->i6[1] = src->i6[1]; inp->i6[2] = src->i6[2]; inp->i6[3] = src->i6[3]; } return 0; } #endif /* ------------------------------------------------------------------------ */ /* Function: ipf_matchtag */ /* Returns: 0 == mismatch, 1 == match. */ /* Parameters: tag1(I) - pointer to first tag to compare */ /* tag2(I) - pointer to second tag to compare */ /* */ /* Returns true (non-zero) or false(0) if the two tag structures can be */ /* considered to be a match or not match, respectively. The tag is 16 */ /* bytes long (16 characters) but that is overlayed with 4 32bit ints so */ /* compare the ints instead, for speed. tag1 is the master of the */ /* comparison. This function should only be called with both tag1 and tag2 */ /* as non-NULL pointers. */ /* ------------------------------------------------------------------------ */ int -ipf_matchtag(tag1, tag2) - ipftag_t *tag1, *tag2; +ipf_matchtag(ipftag_t *tag1, ipftag_t *tag2) { if (tag1 == tag2) return 1; if ((tag1->ipt_num[0] == 0) && (tag2->ipt_num[0] == 0)) return 1; if ((tag1->ipt_num[0] == tag2->ipt_num[0]) && (tag1->ipt_num[1] == tag2->ipt_num[1]) && (tag1->ipt_num[2] == tag2->ipt_num[2]) && (tag1->ipt_num[3] == tag2->ipt_num[3])) return 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_coalesce */ /* Returns: 1 == success, -1 == failure, 0 == no change */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Attempt to get all of the packet data into a single, contiguous buffer. */ /* If this call returns a failure then the buffers have also been freed. */ /* ------------------------------------------------------------------------ */ int -ipf_coalesce(fin) - fr_info_t *fin; +ipf_coalesce(fr_info_t *fin) { if ((fin->fin_flx & FI_COALESCE) != 0) return 1; /* * If the mbuf pointers indicate that there is no mbuf to work with, * return but do not indicate success or failure. */ if (fin->fin_m == NULL || fin->fin_mp == NULL) return 0; #if defined(_KERNEL) if (ipf_pullup(fin->fin_m, fin, fin->fin_plen) == NULL) { ipf_main_softc_t *softc = fin->fin_main_soft; DT1(frb_coalesce, fr_info_t *, fin); LBUMP(ipf_stats[fin->fin_out].fr_badcoalesces); # if SOLARIS FREE_MB_T(*fin->fin_mp); # endif fin->fin_reason = FRB_COALESCE; *fin->fin_mp = NULL; fin->fin_m = NULL; return -1; } #else fin = fin; /* LINT */ #endif return 1; } /* * The following table lists all of the tunable variables that can be * accessed via SIOCIPFGET/SIOCIPFSET/SIOCIPFGETNEXt. The format of each row * in the table below is as follows: * * pointer to value, name of value, minimum, maximum, size of the value's * container, value attribute flags * * For convienience, IPFT_RDONLY means the value is read-only, IPFT_WRDISABLED * means the value can only be written to when IPFilter is loaded but disabled. * The obvious implication is if neither of these are set then the value can be * changed at any time without harm. */ /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_findbycookie */ /* Returns: NULL = search failed, else pointer to tune struct */ /* Parameters: cookie(I) - cookie value to search for amongst tuneables */ /* next(O) - pointer to place to store the cookie for the */ /* "next" tuneable, if it is desired. */ /* */ /* This function is used to walk through all of the existing tunables with */ /* successive calls. It searches the known tunables for the one which has */ /* a matching value for "cookie" - ie its address. When returning a match, */ /* the next one to be found may be returned inside next. */ /* ------------------------------------------------------------------------ */ static ipftuneable_t * -ipf_tune_findbycookie(ptop, cookie, next) - ipftuneable_t **ptop; - void *cookie, **next; +ipf_tune_findbycookie(ipftuneable_t **ptop, void *cookie, void **next) { ipftuneable_t *ta, **tap; for (ta = *ptop; ta->ipft_name != NULL; ta++) if (ta == cookie) { if (next != NULL) { /* * If the next entry in the array has a name * present, then return a pointer to it for * where to go next, else return a pointer to * the dynaminc list as a key to search there * next. This facilitates a weak linking of * the two "lists" together. */ if ((ta + 1)->ipft_name != NULL) *next = ta + 1; else *next = ptop; } return ta; } for (tap = ptop; (ta = *tap) != NULL; tap = &ta->ipft_next) if (tap == cookie) { if (next != NULL) *next = &ta->ipft_next; return ta; } if (next != NULL) *next = NULL; return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_findbyname */ /* Returns: NULL = search failed, else pointer to tune struct */ /* Parameters: name(I) - name of the tuneable entry to find. */ /* */ /* Search the static array of tuneables and the list of dynamic tuneables */ /* for an entry with a matching name. If we can find one, return a pointer */ /* to the matching structure. */ /* ------------------------------------------------------------------------ */ static ipftuneable_t * -ipf_tune_findbyname(top, name) - ipftuneable_t *top; - const char *name; +ipf_tune_findbyname(ipftuneable_t *top, const char *name) { ipftuneable_t *ta; for (ta = top; ta != NULL; ta = ta->ipft_next) if (!strcmp(ta->ipft_name, name)) { return ta; } return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_add_array */ /* Returns: int - 0 == success, else failure */ /* Parameters: newtune - pointer to new tune array to add to tuneables */ /* */ /* Appends tune structures from the array passed in (newtune) to the end of */ /* the current list of "dynamic" tuneable parameters. */ /* If any entry to be added is already present (by name) then the operation */ /* is aborted - entries that have been added are removed before returning. */ /* An entry with no name (NULL) is used as the indication that the end of */ /* the array has been reached. */ /* ------------------------------------------------------------------------ */ int -ipf_tune_add_array(softc, newtune) - ipf_main_softc_t *softc; - ipftuneable_t *newtune; +ipf_tune_add_array(ipf_main_softc_t *softc, ipftuneable_t *newtune) { ipftuneable_t *nt, *dt; int error = 0; for (nt = newtune; nt->ipft_name != NULL; nt++) { error = ipf_tune_add(softc, nt); if (error != 0) { for (dt = newtune; dt != nt; dt++) { (void) ipf_tune_del(softc, dt); } } } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_array_link */ /* Returns: 0 == success, -1 == failure */ /* Parameters: softc(I) - soft context pointerto work with */ /* array(I) - pointer to an array of tuneables */ /* */ /* Given an array of tunables (array), append them to the current list of */ /* tuneables for this context (softc->ipf_tuners.) To properly prepare the */ /* the array for being appended to the list, initialise all of the next */ /* pointers so we don't need to walk parts of it with ++ and others with */ /* next. The array is expected to have an entry with a NULL name as the */ /* terminator. Trying to add an array with no non-NULL names will return as */ /* a failure. */ /* ------------------------------------------------------------------------ */ int -ipf_tune_array_link(softc, array) - ipf_main_softc_t *softc; - ipftuneable_t *array; +ipf_tune_array_link(ipf_main_softc_t *softc, ipftuneable_t *array) { ipftuneable_t *t, **p; t = array; if (t->ipft_name == NULL) return -1; for (; t[1].ipft_name != NULL; t++) t[0].ipft_next = &t[1]; t->ipft_next = NULL; /* * Since a pointer to the last entry isn't kept, we need to find it * each time we want to add new variables to the list. */ for (p = &softc->ipf_tuners; (t = *p) != NULL; p = &t->ipft_next) if (t->ipft_name == NULL) break; *p = array; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_array_unlink */ /* Returns: 0 == success, -1 == failure */ /* Parameters: softc(I) - soft context pointerto work with */ /* array(I) - pointer to an array of tuneables */ /* */ /* ------------------------------------------------------------------------ */ int -ipf_tune_array_unlink(softc, array) - ipf_main_softc_t *softc; - ipftuneable_t *array; +ipf_tune_array_unlink(ipf_main_softc_t *softc, ipftuneable_t *array) { ipftuneable_t *t, **p; for (p = &softc->ipf_tuners; (t = *p) != NULL; p = &t->ipft_next) if (t == array) break; if (t == NULL) return -1; for (; t[1].ipft_name != NULL; t++) ; *p = t->ipft_next; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_array_copy */ /* Returns: NULL = failure, else pointer to new array */ /* Parameters: base(I) - pointer to structure base */ /* size(I) - size of the array at template */ /* template(I) - original array to copy */ /* */ /* Allocate memory for a new set of tuneable values and copy everything */ /* from template into the new region of memory. The new region is full of */ /* uninitialised pointers (ipft_next) so set them up. Now, ipftp_offset... */ /* */ /* NOTE: the following assumes that sizeof(long) == sizeof(void *) */ /* In the array template, ipftp_offset is the offset (in bytes) of the */ /* location of the tuneable value inside the structure pointed to by base. */ /* As ipftp_offset is a union over the pointers to the tuneable values, if */ /* we add base to the copy's ipftp_offset, copy ends up with a pointer in */ /* ipftp_void that points to the stored value. */ /* ------------------------------------------------------------------------ */ ipftuneable_t * -ipf_tune_array_copy(base, size, template) - void *base; - size_t size; - ipftuneable_t *template; +ipf_tune_array_copy(void *base, size_t size, ipftuneable_t *template) { ipftuneable_t *copy; int i; KMALLOCS(copy, ipftuneable_t *, size); if (copy == NULL) { return NULL; } bcopy(template, copy, size); for (i = 0; copy[i].ipft_name; i++) { copy[i].ipft_una.ipftp_offset += (u_long)base; copy[i].ipft_next = copy + i + 1; } return copy; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_add */ /* Returns: int - 0 == success, else failure */ /* Parameters: newtune - pointer to new tune entry to add to tuneables */ /* */ /* Appends tune structures from the array passed in (newtune) to the end of */ /* the current list of "dynamic" tuneable parameters. Once added, the */ /* owner of the object is not expected to ever change "ipft_next". */ /* ------------------------------------------------------------------------ */ int -ipf_tune_add(softc, newtune) - ipf_main_softc_t *softc; - ipftuneable_t *newtune; +ipf_tune_add(ipf_main_softc_t *softc, ipftuneable_t *newtune) { ipftuneable_t *ta, **tap; ta = ipf_tune_findbyname(softc->ipf_tuners, newtune->ipft_name); if (ta != NULL) { IPFERROR(74); return EEXIST; } for (tap = &softc->ipf_tuners; *tap != NULL; tap = &(*tap)->ipft_next) ; newtune->ipft_next = NULL; *tap = newtune; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_del */ /* Returns: int - 0 == success, else failure */ /* Parameters: oldtune - pointer to tune entry to remove from the list of */ /* current dynamic tuneables */ /* */ /* Search for the tune structure, by pointer, in the list of those that are */ /* dynamically added at run time. If found, adjust the list so that this */ /* structure is no longer part of it. */ /* ------------------------------------------------------------------------ */ int -ipf_tune_del(softc, oldtune) - ipf_main_softc_t *softc; - ipftuneable_t *oldtune; +ipf_tune_del(ipf_main_softc_t *softc, ipftuneable_t *oldtune) { ipftuneable_t *ta, **tap; int error = 0; for (tap = &softc->ipf_tuners; (ta = *tap) != NULL; tap = &ta->ipft_next) { if (ta == oldtune) { *tap = oldtune->ipft_next; oldtune->ipft_next = NULL; break; } } if (ta == NULL) { error = ESRCH; IPFERROR(75); } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_del_array */ /* Returns: int - 0 == success, else failure */ /* Parameters: oldtune - pointer to tuneables array */ /* */ /* Remove each tuneable entry in the array from the list of "dynamic" */ /* tunables. If one entry should fail to be found, an error will be */ /* returned and no further ones removed. */ /* An entry with a NULL name is used as the indicator of the last entry in */ /* the array. */ /* ------------------------------------------------------------------------ */ int -ipf_tune_del_array(softc, oldtune) - ipf_main_softc_t *softc; - ipftuneable_t *oldtune; +ipf_tune_del_array(ipf_main_softc_t *softc, ipftuneable_t *oldtune) { ipftuneable_t *ot; int error = 0; for (ot = oldtune; ot->ipft_name != NULL; ot++) { error = ipf_tune_del(softc, ot); if (error != 0) break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune */ /* Returns: int - 0 == success, else failure */ /* Parameters: cmd(I) - ioctl command number */ /* data(I) - pointer to ioctl data structure */ /* */ /* Implement handling of SIOCIPFGETNEXT, SIOCIPFGET and SIOCIPFSET. These */ /* three ioctls provide the means to access and control global variables */ /* within IPFilter, allowing (for example) timeouts and table sizes to be */ /* changed without rebooting, reloading or recompiling. The initialisation */ /* and 'destruction' routines of the various components of ipfilter are all */ /* each responsible for handling their own values being too big. */ /* ------------------------------------------------------------------------ */ int -ipf_ipftune(softc, cmd, data) - ipf_main_softc_t *softc; - ioctlcmd_t cmd; - void *data; +ipf_ipftune(ipf_main_softc_t *softc, ioctlcmd_t cmd, void *data) { ipftuneable_t *ta; ipftune_t tu; void *cookie; int error; error = ipf_inobj(softc, data, NULL, &tu, IPFOBJ_TUNEABLE); if (error != 0) return error; tu.ipft_name[sizeof(tu.ipft_name) - 1] = '\0'; cookie = tu.ipft_cookie; ta = NULL; switch (cmd) { case SIOCIPFGETNEXT : /* * If cookie is non-NULL, assume it to be a pointer to the last * entry we looked at, so find it (if possible) and return a * pointer to the next one after it. The last entry in the * the table is a NULL entry, so when we get to it, set cookie * to NULL and return that, indicating end of list, erstwhile * if we come in with cookie set to NULL, we are starting anew * at the front of the list. */ if (cookie != NULL) { ta = ipf_tune_findbycookie(&softc->ipf_tuners, cookie, &tu.ipft_cookie); } else { ta = softc->ipf_tuners; tu.ipft_cookie = ta + 1; } if (ta != NULL) { /* * Entry found, but does the data pointed to by that * row fit in what we can return? */ if (ta->ipft_sz > sizeof(tu.ipft_un)) { IPFERROR(76); return EINVAL; } tu.ipft_vlong = 0; if (ta->ipft_sz == sizeof(u_long)) tu.ipft_vlong = *ta->ipft_plong; else if (ta->ipft_sz == sizeof(u_int)) tu.ipft_vint = *ta->ipft_pint; else if (ta->ipft_sz == sizeof(u_short)) tu.ipft_vshort = *ta->ipft_pshort; else if (ta->ipft_sz == sizeof(u_char)) tu.ipft_vchar = *ta->ipft_pchar; tu.ipft_sz = ta->ipft_sz; tu.ipft_min = ta->ipft_min; tu.ipft_max = ta->ipft_max; tu.ipft_flags = ta->ipft_flags; bcopy(ta->ipft_name, tu.ipft_name, MIN(sizeof(tu.ipft_name), strlen(ta->ipft_name) + 1)); } error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); break; case SIOCIPFGET : case SIOCIPFSET : /* * Search by name or by cookie value for a particular entry * in the tuning paramter table. */ IPFERROR(77); error = ESRCH; if (cookie != NULL) { ta = ipf_tune_findbycookie(&softc->ipf_tuners, cookie, NULL); if (ta != NULL) error = 0; } else if (tu.ipft_name[0] != '\0') { ta = ipf_tune_findbyname(softc->ipf_tuners, tu.ipft_name); if (ta != NULL) error = 0; } if (error != 0) break; if (cmd == (ioctlcmd_t)SIOCIPFGET) { /* * Fetch the tuning parameters for a particular value */ tu.ipft_vlong = 0; if (ta->ipft_sz == sizeof(u_long)) tu.ipft_vlong = *ta->ipft_plong; else if (ta->ipft_sz == sizeof(u_int)) tu.ipft_vint = *ta->ipft_pint; else if (ta->ipft_sz == sizeof(u_short)) tu.ipft_vshort = *ta->ipft_pshort; else if (ta->ipft_sz == sizeof(u_char)) tu.ipft_vchar = *ta->ipft_pchar; tu.ipft_cookie = ta; tu.ipft_sz = ta->ipft_sz; tu.ipft_min = ta->ipft_min; tu.ipft_max = ta->ipft_max; tu.ipft_flags = ta->ipft_flags; error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); } else if (cmd == (ioctlcmd_t)SIOCIPFSET) { /* * Set an internal parameter. The hard part here is * getting the new value safely and correctly out of * the kernel (given we only know its size, not type.) */ u_long in; if (((ta->ipft_flags & IPFT_WRDISABLED) != 0) && (softc->ipf_running > 0)) { IPFERROR(78); error = EBUSY; break; } in = tu.ipft_vlong; if (in < ta->ipft_min || in > ta->ipft_max) { IPFERROR(79); error = EINVAL; break; } if (ta->ipft_func != NULL) { SPL_INT(s); SPL_NET(s); error = (*ta->ipft_func)(softc, ta, &tu.ipft_un); SPL_X(s); } else if (ta->ipft_sz == sizeof(u_long)) { tu.ipft_vlong = *ta->ipft_plong; *ta->ipft_plong = in; } else if (ta->ipft_sz == sizeof(u_int)) { tu.ipft_vint = *ta->ipft_pint; *ta->ipft_pint = (u_int)(in & 0xffffffff); } else if (ta->ipft_sz == sizeof(u_short)) { tu.ipft_vshort = *ta->ipft_pshort; *ta->ipft_pshort = (u_short)(in & 0xffff); } else if (ta->ipft_sz == sizeof(u_char)) { tu.ipft_vchar = *ta->ipft_pchar; *ta->ipft_pchar = (u_char)(in & 0xff); } error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); } break; default : IPFERROR(80); error = EINVAL; break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_zerostats */ /* Returns: int - 0 = success, else failure */ /* Parameters: data(O) - pointer to pointer for copying data back to */ /* */ /* Copies the current statistics out to userspace and then zero's the */ /* current ones in the kernel. The lock is only held across the bzero() as */ /* the copyout may result in paging (ie network activity.) */ /* ------------------------------------------------------------------------ */ int -ipf_zerostats(softc, data) - ipf_main_softc_t *softc; - caddr_t data; +ipf_zerostats(ipf_main_softc_t *softc, caddr_t data) { friostat_t fio; ipfobj_t obj; int error; error = ipf_inobj(softc, data, &obj, &fio, IPFOBJ_IPFSTAT); if (error != 0) return error; ipf_getstat(softc, &fio, obj.ipfo_rev); error = ipf_outobj(softc, data, &fio, IPFOBJ_IPFSTAT); if (error != 0) return error; WRITE_ENTER(&softc->ipf_mutex); bzero(&softc->ipf_stats, sizeof(softc->ipf_stats)); RWLOCK_EXIT(&softc->ipf_mutex); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_resolvedest */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* base(I) - where strings are stored */ /* fdp(IO) - pointer to destination information to resolve */ /* v(I) - IP protocol version to match */ /* */ /* Looks up an interface name in the frdest structure pointed to by fdp and */ /* if a matching name can be found for the particular IP protocol version */ /* then store the interface pointer in the frdest struct. If no match is */ /* found, then set the interface pointer to be -1 as NULL is considered to */ /* indicate there is no information at all in the structure. */ /* ------------------------------------------------------------------------ */ int -ipf_resolvedest(softc, base, fdp, v) - ipf_main_softc_t *softc; - char *base; - frdest_t *fdp; - int v; +ipf_resolvedest(ipf_main_softc_t *softc, char *base, frdest_t *fdp, int v) { int errval = 0; void *ifp; ifp = NULL; if (fdp->fd_name != -1) { if (fdp->fd_type == FRD_DSTLIST) { ifp = ipf_lookup_res_name(softc, IPL_LOGIPF, IPLT_DSTLIST, base + fdp->fd_name, NULL); if (ifp == NULL) { IPFERROR(144); errval = ESRCH; } } else { ifp = GETIFP(base + fdp->fd_name, v); if (ifp == NULL) ifp = (void *)-1; } } fdp->fd_ptr = ifp; return errval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_resolvenic */ /* Returns: void* - NULL = wildcard name, -1 = failed to find NIC, else */ /* pointer to interface structure for NIC */ /* Parameters: softc(I)- pointer to soft context main structure */ /* name(I) - complete interface name */ /* v(I) - IP protocol version */ /* */ /* Look for a network interface structure that firstly has a matching name */ /* to that passed in and that is also being used for that IP protocol */ /* version (necessary on some platforms where there are separate listings */ /* for both IPv4 and IPv6 on the same physical NIC. */ /* ------------------------------------------------------------------------ */ void * -ipf_resolvenic(softc, name, v) - ipf_main_softc_t *softc; - char *name; - int v; +ipf_resolvenic(ipf_main_softc_t *softc, char *name, int v) { void *nic; softc = softc; /* gcc -Wextra */ if (name[0] == '\0') return NULL; if ((name[1] == '\0') && ((name[0] == '-') || (name[0] == '*'))) { return NULL; } nic = GETIFP(name, v); if (nic == NULL) nic = (void *)-1; return nic; } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_expire */ /* Returns: None. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* This function is run every ipf tick to see if there are any tokens that */ /* have been held for too long and need to be freed up. */ /* ------------------------------------------------------------------------ */ void -ipf_token_expire(softc) - ipf_main_softc_t *softc; +ipf_token_expire(ipf_main_softc_t *softc) { ipftoken_t *it; WRITE_ENTER(&softc->ipf_tokens); while ((it = softc->ipf_token_head) != NULL) { if (it->ipt_die > softc->ipf_ticks) break; ipf_token_deref(softc, it); } RWLOCK_EXIT(&softc->ipf_tokens); } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_flush */ /* Returns: None. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Loop through all of the existing tokens and call deref to see if they */ /* can be freed. Normally a function like this might just loop on */ /* ipf_token_head but there is a chance that a token might have a ref count */ /* of greater than one and in that case the the reference would drop twice */ /* by code that is only entitled to drop it once. */ /* ------------------------------------------------------------------------ */ static void -ipf_token_flush(softc) - ipf_main_softc_t *softc; +ipf_token_flush(ipf_main_softc_t *softc) { ipftoken_t *it, *next; WRITE_ENTER(&softc->ipf_tokens); for (it = softc->ipf_token_head; it != NULL; it = next) { next = it->ipt_next; (void) ipf_token_deref(softc, it); } RWLOCK_EXIT(&softc->ipf_tokens); } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I)- pointer to soft context main structure */ /* type(I) - the token type to match */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ /* This function looks for a a token in the current list that matches up */ /* the fields (type, uid, ptr). If none is found, ESRCH is returned, else */ /* call ipf_token_dewref() to remove it from the list. In the event that */ /* the token has a reference held elsewhere, setting ipt_complete to 2 */ /* enables debugging to distinguish between the two paths that ultimately */ /* lead to a token to be deleted. */ /* ------------------------------------------------------------------------ */ int -ipf_token_del(softc, type, uid, ptr) - ipf_main_softc_t *softc; - int type, uid; - void *ptr; +ipf_token_del(ipf_main_softc_t *softc, int type, int uid, void *ptr) { ipftoken_t *it; int error; IPFERROR(82); error = ESRCH; WRITE_ENTER(&softc->ipf_tokens); for (it = softc->ipf_token_head; it != NULL; it = it->ipt_next) { if (ptr == it->ipt_ctx && type == it->ipt_type && uid == it->ipt_uid) { it->ipt_complete = 2; ipf_token_deref(softc, it); error = 0; break; } } RWLOCK_EXIT(&softc->ipf_tokens); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_mark_complete */ /* Returns: None. */ /* Parameters: token(I) - pointer to token structure */ /* */ /* Mark a token as being ineligable for being found with ipf_token_find. */ /* ------------------------------------------------------------------------ */ void -ipf_token_mark_complete(token) - ipftoken_t *token; +ipf_token_mark_complete(ipftoken_t *token) { if (token->ipt_complete == 0) token->ipt_complete = 1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_find */ /* Returns: ipftoken_t * - NULL if no memory, else pointer to token */ /* Parameters: softc(I)- pointer to soft context main structure */ /* type(I) - the token type to match */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ /* This function looks for a live token in the list of current tokens that */ /* matches the tuple (type, uid, ptr). If one cannot be found then one is */ /* allocated. If one is found then it is moved to the top of the list of */ /* currently active tokens. */ /* ------------------------------------------------------------------------ */ ipftoken_t * -ipf_token_find(softc, type, uid, ptr) - ipf_main_softc_t *softc; - int type, uid; - void *ptr; +ipf_token_find(ipf_main_softc_t *softc, int type, int uid, void *ptr) { ipftoken_t *it, *new; KMALLOC(new, ipftoken_t *); if (new != NULL) bzero((char *)new, sizeof(*new)); WRITE_ENTER(&softc->ipf_tokens); for (it = softc->ipf_token_head; it != NULL; it = it->ipt_next) { if ((ptr == it->ipt_ctx) && (type == it->ipt_type) && (uid == it->ipt_uid) && (it->ipt_complete < 2)) break; } if (it == NULL) { it = new; new = NULL; if (it == NULL) { RWLOCK_EXIT(&softc->ipf_tokens); return NULL; } it->ipt_ctx = ptr; it->ipt_uid = uid; it->ipt_type = type; it->ipt_ref = 1; } else { if (new != NULL) { KFREE(new); new = NULL; } if (it->ipt_complete > 0) it = NULL; else ipf_token_unlink(softc, it); } if (it != NULL) { it->ipt_pnext = softc->ipf_token_tail; *softc->ipf_token_tail = it; softc->ipf_token_tail = &it->ipt_next; it->ipt_next = NULL; it->ipt_ref++; it->ipt_die = softc->ipf_ticks + 20; } RWLOCK_EXIT(&softc->ipf_tokens); return it; } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_unlink */ /* Returns: None. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to token structure */ /* Write Locks: ipf_tokens */ /* */ /* This function unlinks a token structure from the linked list of tokens */ /* that "own" it. The head pointer never needs to be explicitly adjusted */ /* but the tail does due to the linked list implementation. */ /* ------------------------------------------------------------------------ */ static void -ipf_token_unlink(softc, token) - ipf_main_softc_t *softc; - ipftoken_t *token; +ipf_token_unlink(ipf_main_softc_t *softc, ipftoken_t *token) { if (softc->ipf_token_tail == &token->ipt_next) softc->ipf_token_tail = token->ipt_pnext; *token->ipt_pnext = token->ipt_next; if (token->ipt_next != NULL) token->ipt_next->ipt_pnext = token->ipt_pnext; token->ipt_next = NULL; token->ipt_pnext = NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_deref */ /* Returns: int - 0 == token freed, else reference count */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to token structure */ /* Write Locks: ipf_tokens */ /* */ /* Drop the reference count on the token structure and if it drops to zero, */ /* call the dereference function for the token type because it is then */ /* possible to free the token data structure. */ /* ------------------------------------------------------------------------ */ int -ipf_token_deref(softc, token) - ipf_main_softc_t *softc; - ipftoken_t *token; +ipf_token_deref(ipf_main_softc_t *softc, ipftoken_t *token) { void *data, **datap; ASSERT(token->ipt_ref > 0); token->ipt_ref--; if (token->ipt_ref > 0) return token->ipt_ref; data = token->ipt_data; datap = &data; if ((data != NULL) && (data != (void *)-1)) { switch (token->ipt_type) { case IPFGENITER_IPF : (void) ipf_derefrule(softc, (frentry_t **)datap); break; case IPFGENITER_IPNAT : WRITE_ENTER(&softc->ipf_nat); ipf_nat_rule_deref(softc, (ipnat_t **)datap); RWLOCK_EXIT(&softc->ipf_nat); break; case IPFGENITER_NAT : ipf_nat_deref(softc, (nat_t **)datap); break; case IPFGENITER_STATE : ipf_state_deref(softc, (ipstate_t **)datap); break; case IPFGENITER_FRAG : ipf_frag_pkt_deref(softc, (ipfr_t **)datap); break; case IPFGENITER_NATFRAG : ipf_frag_nat_deref(softc, (ipfr_t **)datap); break; case IPFGENITER_HOSTMAP : WRITE_ENTER(&softc->ipf_nat); ipf_nat_hostmapdel(softc, (hostmap_t **)datap); RWLOCK_EXIT(&softc->ipf_nat); break; default : ipf_lookup_iterderef(softc, token->ipt_type, data); break; } } ipf_token_unlink(softc, token); KFREE(token); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nextrule */ /* Returns: frentry_t * - NULL == no more rules, else pointer to next */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fr(I) - pointer to filter rule */ /* out(I) - 1 == out rules, 0 == input rules */ /* */ /* Starting with "fr", find the next rule to visit. This includes visiting */ /* the list of rule groups if either fr is NULL (empty list) or it is the */ /* last rule in the list. When walking rule lists, it is either input or */ /* output rules that are returned, never both. */ /* ------------------------------------------------------------------------ */ static frentry_t * -ipf_nextrule(softc, active, unit, fr, out) - ipf_main_softc_t *softc; - int active, unit; - frentry_t *fr; - int out; +ipf_nextrule(ipf_main_softc_t *softc, int active, int unit, frentry_t *fr, + int out) { frentry_t *next; frgroup_t *fg; if (fr != NULL && fr->fr_group != -1) { fg = ipf_findgroup(softc, fr->fr_names + fr->fr_group, unit, active, NULL); if (fg != NULL) fg = fg->fg_next; } else { fg = softc->ipf_groups[unit][active]; } while (fg != NULL) { next = fg->fg_start; while (next != NULL) { if (out) { if (next->fr_flags & FR_OUTQUE) return next; } else if (next->fr_flags & FR_INQUE) { return next; } next = next->fr_next; } if (next == NULL) fg = fg->fg_next; } return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_getnextrule */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I)- pointer to soft context main structure */ /* t(I) - pointer to destination information to resolve */ /* ptr(I) - pointer to ipfobj_t to copyin from user space */ /* */ /* This function's first job is to bring in the ipfruleiter_t structure via */ /* the ipfobj_t structure to determine what should be the next rule to */ /* return. Once the ipfruleiter_t has been brought in, it then tries to */ /* find the 'next rule'. This may include searching rule group lists or */ /* just be as simple as looking at the 'next' field in the rule structure. */ /* When we have found the rule to return, increase its reference count and */ /* if we used an existing rule to get here, decrease its reference count. */ /* ------------------------------------------------------------------------ */ int -ipf_getnextrule(softc, t, ptr) - ipf_main_softc_t *softc; - ipftoken_t *t; - void *ptr; +ipf_getnextrule(ipf_main_softc_t *softc, ipftoken_t *t, void *ptr) { frentry_t *fr, *next, zero; ipfruleiter_t it; int error, out; frgroup_t *fg; ipfobj_t obj; int predict; char *dst; int unit; if (t == NULL || ptr == NULL) { IPFERROR(84); return EFAULT; } error = ipf_inobj(softc, ptr, &obj, &it, IPFOBJ_IPFITER); if (error != 0) return error; if ((it.iri_inout < 0) || (it.iri_inout > 3)) { IPFERROR(85); return EINVAL; } if ((it.iri_active != 0) && (it.iri_active != 1)) { IPFERROR(86); return EINVAL; } if (it.iri_nrules == 0) { IPFERROR(87); return ENOSPC; } if (it.iri_rule == NULL) { IPFERROR(88); return EFAULT; } fg = NULL; fr = t->ipt_data; if ((it.iri_inout & F_OUT) != 0) out = 1; else out = 0; if ((it.iri_inout & F_ACIN) != 0) unit = IPL_LOGCOUNT; else unit = IPL_LOGIPF; READ_ENTER(&softc->ipf_mutex); if (fr == NULL) { if (*it.iri_group == '\0') { if (unit == IPL_LOGCOUNT) { next = softc->ipf_acct[out][it.iri_active]; } else { next = softc->ipf_rules[out][it.iri_active]; } if (next == NULL) next = ipf_nextrule(softc, it.iri_active, unit, NULL, out); } else { fg = ipf_findgroup(softc, it.iri_group, unit, it.iri_active, NULL); if (fg != NULL) next = fg->fg_start; else next = NULL; } } else { next = fr->fr_next; if (next == NULL) next = ipf_nextrule(softc, it.iri_active, unit, fr, out); } if (next != NULL && next->fr_next != NULL) predict = 1; else if (ipf_nextrule(softc, it.iri_active, unit, next, out) != NULL) predict = 1; else predict = 0; if (fr != NULL) (void) ipf_derefrule(softc, &fr); obj.ipfo_type = IPFOBJ_FRENTRY; dst = (char *)it.iri_rule; if (next != NULL) { obj.ipfo_size = next->fr_size; MUTEX_ENTER(&next->fr_lock); next->fr_ref++; MUTEX_EXIT(&next->fr_lock); t->ipt_data = next; } else { obj.ipfo_size = sizeof(frentry_t); bzero(&zero, sizeof(zero)); next = &zero; t->ipt_data = NULL; } it.iri_rule = predict ? next : NULL; if (predict == 0) ipf_token_mark_complete(t); RWLOCK_EXIT(&softc->ipf_mutex); obj.ipfo_ptr = dst; error = ipf_outobjk(softc, &obj, next); if (error == 0 && t->ipt_data != NULL) { dst += obj.ipfo_size; if (next->fr_data != NULL) { ipfobj_t dobj; if (next->fr_type == FR_T_IPFEXPR) dobj.ipfo_type = IPFOBJ_IPFEXPR; else dobj.ipfo_type = IPFOBJ_FRIPF; dobj.ipfo_size = next->fr_dsize; dobj.ipfo_rev = obj.ipfo_rev; dobj.ipfo_ptr = dst; error = ipf_outobjk(softc, &dobj, next->fr_data); } } if ((fr != NULL) && (next == &zero)) (void) ipf_derefrule(softc, &fr); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frruleiter */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I)- pointer to soft context main structure */ /* data(I) - the token type to match */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ /* This function serves as a stepping stone between ipf_ipf_ioctl and */ /* ipf_getnextrule. It's role is to find the right token in the kernel for */ /* the process doing the ioctl and use that to ask for the next rule. */ /* ------------------------------------------------------------------------ */ static int -ipf_frruleiter(softc, data, uid, ctx) - ipf_main_softc_t *softc; - void *data, *ctx; - int uid; +ipf_frruleiter(ipf_main_softc_t *softc, void *data, int uid, void *ctx) { ipftoken_t *token; ipfruleiter_t it; ipfobj_t obj; int error; token = ipf_token_find(softc, IPFGENITER_IPF, uid, ctx); if (token != NULL) { error = ipf_getnextrule(softc, token, data); WRITE_ENTER(&softc->ipf_tokens); ipf_token_deref(softc, token); RWLOCK_EXIT(&softc->ipf_tokens); } else { error = ipf_inobj(softc, data, &obj, &it, IPFOBJ_IPFITER); if (error != 0) return error; it.iri_rule = NULL; error = ipf_outobj(softc, data, &it, IPFOBJ_IPFITER); } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_geniter */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to ipftoken_t structure */ /* itp(I) - pointer to iterator data */ /* */ /* Decide which iterator function to call using information passed through */ /* the ipfgeniter_t structure at itp. */ /* ------------------------------------------------------------------------ */ static int -ipf_geniter(softc, token, itp) - ipf_main_softc_t *softc; - ipftoken_t *token; - ipfgeniter_t *itp; +ipf_geniter(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp) { int error; switch (itp->igi_type) { case IPFGENITER_FRAG : error = ipf_frag_pkt_next(softc, token, itp); break; default : IPFERROR(92); error = EINVAL; break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_genericiter */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I)- pointer to soft context main structure */ /* data(I) - the token type to match */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ /* Handle the SIOCGENITER ioctl for the ipfilter device. The primary role */ /* ------------------------------------------------------------------------ */ int -ipf_genericiter(softc, data, uid, ctx) - ipf_main_softc_t *softc; - void *data, *ctx; - int uid; +ipf_genericiter(ipf_main_softc_t *softc, void *data, int uid, void *ctx) { ipftoken_t *token; ipfgeniter_t iter; int error; error = ipf_inobj(softc, data, NULL, &iter, IPFOBJ_GENITER); if (error != 0) return error; token = ipf_token_find(softc, iter.igi_type, uid, ctx); if (token != NULL) { token->ipt_subtype = iter.igi_type; error = ipf_geniter(softc, token, &iter); WRITE_ENTER(&softc->ipf_tokens); ipf_token_deref(softc, token); RWLOCK_EXIT(&softc->ipf_tokens); } else { IPFERROR(93); error = 0; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_ipf_ioctl */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I)- pointer to soft context main structure */ /* data(I) - the token type to match */ /* cmd(I) - the ioctl command number */ /* mode(I) - mode flags for the ioctl */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ /* This function handles all of the ioctl command that are actually isssued */ /* to the /dev/ipl device. */ /* ------------------------------------------------------------------------ */ int -ipf_ipf_ioctl(softc, data, cmd, mode, uid, ctx) - ipf_main_softc_t *softc; - caddr_t data; - ioctlcmd_t cmd; - int mode, uid; - void *ctx; +ipf_ipf_ioctl(ipf_main_softc_t *softc, caddr_t data, ioctlcmd_t cmd, int mode, + int uid, void *ctx) { friostat_t fio; int error, tmp; ipfobj_t obj; SPL_INT(s); switch (cmd) { case SIOCFRENB : if (!(mode & FWRITE)) { IPFERROR(94); error = EPERM; } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (error != 0) { IPFERROR(95); error = EFAULT; break; } WRITE_ENTER(&softc->ipf_global); if (tmp) { if (softc->ipf_running > 0) error = 0; else error = ipfattach(softc); if (error == 0) softc->ipf_running = 1; else (void) ipfdetach(softc); } else { if (softc->ipf_running == 1) error = ipfdetach(softc); else error = 0; if (error == 0) softc->ipf_running = -1; } RWLOCK_EXIT(&softc->ipf_global); } break; case SIOCIPFSET : if (!(mode & FWRITE)) { IPFERROR(96); error = EPERM; break; } /* FALLTHRU */ case SIOCIPFGETNEXT : case SIOCIPFGET : error = ipf_ipftune(softc, cmd, (void *)data); break; case SIOCSETFF : if (!(mode & FWRITE)) { IPFERROR(97); error = EPERM; } else { error = BCOPYIN(data, &softc->ipf_flags, sizeof(softc->ipf_flags)); if (error != 0) { IPFERROR(98); error = EFAULT; } } break; case SIOCGETFF : error = BCOPYOUT(&softc->ipf_flags, data, sizeof(softc->ipf_flags)); if (error != 0) { IPFERROR(99); error = EFAULT; } break; case SIOCFUNCL : error = ipf_resolvefunc(softc, (void *)data); break; case SIOCINAFR : case SIOCRMAFR : case SIOCADAFR : case SIOCZRLST : if (!(mode & FWRITE)) { IPFERROR(100); error = EPERM; } else { error = frrequest(softc, IPL_LOGIPF, cmd, (caddr_t)data, softc->ipf_active, 1); } break; case SIOCINIFR : case SIOCRMIFR : case SIOCADIFR : if (!(mode & FWRITE)) { IPFERROR(101); error = EPERM; } else { error = frrequest(softc, IPL_LOGIPF, cmd, (caddr_t)data, 1 - softc->ipf_active, 1); } break; case SIOCSWAPA : if (!(mode & FWRITE)) { IPFERROR(102); error = EPERM; } else { WRITE_ENTER(&softc->ipf_mutex); error = BCOPYOUT(&softc->ipf_active, data, sizeof(softc->ipf_active)); if (error != 0) { IPFERROR(103); error = EFAULT; } else { softc->ipf_active = 1 - softc->ipf_active; } RWLOCK_EXIT(&softc->ipf_mutex); } break; case SIOCGETFS : error = ipf_inobj(softc, (void *)data, &obj, &fio, IPFOBJ_IPFSTAT); if (error != 0) break; ipf_getstat(softc, &fio, obj.ipfo_rev); error = ipf_outobj(softc, (void *)data, &fio, IPFOBJ_IPFSTAT); break; case SIOCFRZST : if (!(mode & FWRITE)) { IPFERROR(104); error = EPERM; } else error = ipf_zerostats(softc, (caddr_t)data); break; case SIOCIPFFL : if (!(mode & FWRITE)) { IPFERROR(105); error = EPERM; } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (!error) { tmp = ipf_flush(softc, IPL_LOGIPF, tmp); error = BCOPYOUT(&tmp, data, sizeof(tmp)); if (error != 0) { IPFERROR(106); error = EFAULT; } } else { IPFERROR(107); error = EFAULT; } } break; #ifdef USE_INET6 case SIOCIPFL6 : if (!(mode & FWRITE)) { IPFERROR(108); error = EPERM; } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (!error) { tmp = ipf_flush(softc, IPL_LOGIPF, tmp); error = BCOPYOUT(&tmp, data, sizeof(tmp)); if (error != 0) { IPFERROR(109); error = EFAULT; } } else { IPFERROR(110); error = EFAULT; } } break; #endif case SIOCSTLCK : if (!(mode & FWRITE)) { IPFERROR(122); error = EPERM; } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (error == 0) { ipf_state_setlock(softc->ipf_state_soft, tmp); ipf_nat_setlock(softc->ipf_nat_soft, tmp); ipf_frag_setlock(softc->ipf_frag_soft, tmp); ipf_auth_setlock(softc->ipf_auth_soft, tmp); } else { IPFERROR(111); error = EFAULT; } } break; #ifdef IPFILTER_LOG case SIOCIPFFB : if (!(mode & FWRITE)) { IPFERROR(112); error = EPERM; } else { tmp = ipf_log_clear(softc, IPL_LOGIPF); error = BCOPYOUT(&tmp, data, sizeof(tmp)); if (error) { IPFERROR(113); error = EFAULT; } } break; #endif /* IPFILTER_LOG */ case SIOCFRSYN : if (!(mode & FWRITE)) { IPFERROR(114); error = EPERM; } else { WRITE_ENTER(&softc->ipf_global); #if (SOLARIS && defined(_KERNEL)) && !defined(INSTANCES) error = ipfsync(); #else ipf_sync(softc, NULL); error = 0; #endif RWLOCK_EXIT(&softc->ipf_global); } break; case SIOCGFRST : error = ipf_outobj(softc, (void *)data, ipf_frag_stats(softc->ipf_frag_soft), IPFOBJ_FRAGSTAT); break; #ifdef IPFILTER_LOG case FIONREAD : tmp = ipf_log_bytesused(softc, IPL_LOGIPF); error = BCOPYOUT(&tmp, data, sizeof(tmp)); break; #endif case SIOCIPFITER : SPL_SCHED(s); error = ipf_frruleiter(softc, data, uid, ctx); SPL_X(s); break; case SIOCGENITER : SPL_SCHED(s); error = ipf_genericiter(softc, data, uid, ctx); SPL_X(s); break; case SIOCIPFDELTOK : error = BCOPYIN(data, &tmp, sizeof(tmp)); if (error == 0) { SPL_SCHED(s); error = ipf_token_del(softc, tmp, uid, ctx); SPL_X(s); } break; default : IPFERROR(115); error = EINVAL; break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_decaps */ /* Returns: int - -1 == decapsulation failed, else bit mask of */ /* flags indicating packet filtering decision. */ /* Parameters: fin(I) - pointer to packet information */ /* pass(I) - IP protocol version to match */ /* l5proto(I) - layer 5 protocol to decode UDP data as. */ /* */ /* This function is called for packets that are wrapt up in other packets, */ /* for example, an IP packet that is the entire data segment for another IP */ /* packet. If the basic constraints for this are satisfied, change the */ /* buffer to point to the start of the inner packet and start processing */ /* rules belonging to the head group this rule specifies. */ /* ------------------------------------------------------------------------ */ u_32_t -ipf_decaps(fin, pass, l5proto) - fr_info_t *fin; - u_32_t pass; - int l5proto; +ipf_decaps(fr_info_t *fin, u_32_t pass, int l5proto) { fr_info_t fin2, *fino = NULL; int elen, hlen, nh; grehdr_t gre; ip_t *ip; mb_t *m; if ((fin->fin_flx & FI_COALESCE) == 0) if (ipf_coalesce(fin) == -1) goto cantdecaps; m = fin->fin_m; hlen = fin->fin_hlen; switch (fin->fin_p) { case IPPROTO_UDP : /* * In this case, the specific protocol being decapsulated * inside UDP frames comes from the rule. */ nh = fin->fin_fr->fr_icode; break; case IPPROTO_GRE : /* 47 */ bcopy(fin->fin_dp, (char *)&gre, sizeof(gre)); hlen += sizeof(grehdr_t); if (gre.gr_R|gre.gr_s) goto cantdecaps; if (gre.gr_C) hlen += 4; if (gre.gr_K) hlen += 4; if (gre.gr_S) hlen += 4; nh = IPPROTO_IP; /* * If the routing options flag is set, validate that it is * there and bounce over it. */ #if 0 /* This is really heavy weight and lots of room for error, */ /* so for now, put it off and get the simple stuff right. */ if (gre.gr_R) { u_char off, len, *s; u_short af; int end; end = 0; s = fin->fin_dp; s += hlen; aplen = fin->fin_plen - hlen; while (aplen > 3) { af = (s[0] << 8) | s[1]; off = s[2]; len = s[3]; aplen -= 4; s += 4; if (af == 0 && len == 0) { end = 1; break; } if (aplen < len) break; s += len; aplen -= len; } if (end != 1) goto cantdecaps; hlen = s - (u_char *)fin->fin_dp; } #endif break; #ifdef IPPROTO_IPIP case IPPROTO_IPIP : /* 4 */ #endif nh = IPPROTO_IP; break; default : /* Includes ESP, AH is special for IPv4 */ goto cantdecaps; } switch (nh) { case IPPROTO_IP : case IPPROTO_IPV6 : break; default : goto cantdecaps; } bcopy((char *)fin, (char *)&fin2, sizeof(fin2)); fino = fin; fin = &fin2; elen = hlen; #if SOLARIS && defined(_KERNEL) m->b_rptr += elen; #else m->m_data += elen; m->m_len -= elen; #endif fin->fin_plen -= elen; ip = (ip_t *)((char *)fin->fin_ip + elen); /* * Make sure we have at least enough data for the network layer * header. */ if (IP_V(ip) == 4) hlen = IP_HL(ip) << 2; #ifdef USE_INET6 else if (IP_V(ip) == 6) hlen = sizeof(ip6_t); #endif else goto cantdecaps2; if (fin->fin_plen < hlen) goto cantdecaps2; fin->fin_dp = (char *)ip + hlen; if (IP_V(ip) == 4) { /* * Perform IPv4 header checksum validation. */ if (ipf_cksum((u_short *)ip, hlen)) goto cantdecaps2; } if (ipf_makefrip(hlen, ip, fin) == -1) { cantdecaps2: if (m != NULL) { #if SOLARIS && defined(_KERNEL) m->b_rptr -= elen; #else m->m_data -= elen; m->m_len += elen; #endif } cantdecaps: DT1(frb_decapfrip, fr_info_t *, fin); pass &= ~FR_CMDMASK; pass |= FR_BLOCK|FR_QUICK; fin->fin_reason = FRB_DECAPFRIP; return -1; } pass = ipf_scanlist(fin, pass); /* * Copy the packet filter "result" fields out of the fr_info_t struct * that is local to the decapsulation processing and back into the * one we were called with. */ fino->fin_flx = fin->fin_flx; fino->fin_rev = fin->fin_rev; fino->fin_icode = fin->fin_icode; fino->fin_rule = fin->fin_rule; (void) strncpy(fino->fin_group, fin->fin_group, FR_GROUPLEN); fino->fin_fr = fin->fin_fr; fino->fin_error = fin->fin_error; fino->fin_mp = fin->fin_mp; fino->fin_m = fin->fin_m; m = fin->fin_m; if (m != NULL) { #if SOLARIS && defined(_KERNEL) m->b_rptr -= elen; #else m->m_data -= elen; m->m_len += elen; #endif } return pass; } /* ------------------------------------------------------------------------ */ /* Function: ipf_matcharray_load */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to ioctl data */ /* objp(I) - ipfobj_t structure to load data into */ /* arrayptr(I) - pointer to location to store array pointer */ /* */ /* This function loads in a mathing array through the ipfobj_t struct that */ /* describes it. Sanity checking and array size limitations are enforced */ /* in this function to prevent userspace from trying to load in something */ /* that is insanely big. Once the size of the array is known, the memory */ /* required is malloc'd and returned through changing *arrayptr. The */ /* contents of the array are verified before returning. Only in the event */ /* of a successful call is the caller required to free up the malloc area. */ /* ------------------------------------------------------------------------ */ int -ipf_matcharray_load(softc, data, objp, arrayptr) - ipf_main_softc_t *softc; - caddr_t data; - ipfobj_t *objp; - int **arrayptr; +ipf_matcharray_load(ipf_main_softc_t *softc, caddr_t data, ipfobj_t *objp, + int **arrayptr) { int arraysize, *array, error; *arrayptr = NULL; error = BCOPYIN(data, objp, sizeof(*objp)); if (error != 0) { IPFERROR(116); return EFAULT; } if (objp->ipfo_type != IPFOBJ_IPFEXPR) { IPFERROR(117); return EINVAL; } if (((objp->ipfo_size & 3) != 0) || (objp->ipfo_size == 0) || (objp->ipfo_size > 1024)) { IPFERROR(118); return EINVAL; } arraysize = objp->ipfo_size * sizeof(*array); KMALLOCS(array, int *, arraysize); if (array == NULL) { IPFERROR(119); return ENOMEM; } error = COPYIN(objp->ipfo_ptr, array, arraysize); if (error != 0) { KFREES(array, arraysize); IPFERROR(120); return EFAULT; } if (ipf_matcharray_verify(array, arraysize) != 0) { KFREES(array, arraysize); IPFERROR(121); return EINVAL; } *arrayptr = array; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_matcharray_verify */ /* Returns: Nil */ /* Parameters: array(I) - pointer to matching array */ /* arraysize(I) - number of elements in the array */ /* */ /* Verify the contents of a matching array by stepping through each element */ /* in it. The actual commands in the array are not verified for */ /* correctness, only that all of the sizes are correctly within limits. */ /* ------------------------------------------------------------------------ */ int -ipf_matcharray_verify(array, arraysize) - int *array, arraysize; +ipf_matcharray_verify(int *array, int arraysize) { int i, nelem, maxidx; ipfexp_t *e; nelem = arraysize / sizeof(*array); /* * Currently, it makes no sense to have an array less than 6 * elements long - the initial size at the from, a single operation * (minimum 4 in length) and a trailer, for a total of 6. */ if ((array[0] < 6) || (arraysize < 24) || (arraysize > 4096)) { return -1; } /* * Verify the size of data pointed to by array with how long * the array claims to be itself. */ if (array[0] * sizeof(*array) != arraysize) { return -1; } maxidx = nelem - 1; /* * The last opcode in this array should be an IPF_EXP_END. */ if (array[maxidx] != IPF_EXP_END) { return -1; } for (i = 1; i < maxidx; ) { e = (ipfexp_t *)(array + i); /* * The length of the bits to check must be at least 1 * (or else there is nothing to comapre with!) and it * cannot exceed the length of the data present. */ if ((e->ipfe_size < 1 ) || (e->ipfe_size + i > maxidx)) { return -1; } i += e->ipfe_size; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_fr_matcharray */ /* Returns: int - 0 = match failed, else positive match */ /* Parameters: fin(I) - pointer to packet information */ /* array(I) - pointer to matching array */ /* */ /* This function is used to apply a matching array against a packet and */ /* return an indication of whether or not the packet successfully matches */ /* all of the commands in it. */ /* ------------------------------------------------------------------------ */ static int -ipf_fr_matcharray(fin, array) - fr_info_t *fin; - int *array; +ipf_fr_matcharray(fr_info_t *fin, int *array) { int i, n, *x, rv, p; ipfexp_t *e; rv = 0; n = array[0]; x = array + 1; for (; n > 0; x += 3 + x[3], rv = 0) { e = (ipfexp_t *)x; if (e->ipfe_cmd == IPF_EXP_END) break; n -= e->ipfe_size; /* * The upper 16 bits currently store the protocol value. * This is currently used with TCP and UDP port compares and * allows "tcp.port = 80" without requiring an explicit " "ip.pr = tcp" first. */ p = e->ipfe_cmd >> 16; if ((p != 0) && (p != fin->fin_p)) break; switch (e->ipfe_cmd) { case IPF_EXP_IP_PR : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (fin->fin_p == e->ipfe_arg0[i]); } break; case IPF_EXP_IP_SRCADDR : if (fin->fin_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((fin->fin_saddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; case IPF_EXP_IP_DSTADDR : if (fin->fin_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((fin->fin_daddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; case IPF_EXP_IP_ADDR : if (fin->fin_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((fin->fin_saddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]) || ((fin->fin_daddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; #ifdef USE_INET6 case IPF_EXP_IP6_SRCADDR : if (fin->fin_v != 6) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= IP6_MASKEQ(&fin->fin_src6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; case IPF_EXP_IP6_DSTADDR : if (fin->fin_v != 6) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= IP6_MASKEQ(&fin->fin_dst6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; case IPF_EXP_IP6_ADDR : if (fin->fin_v != 6) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= IP6_MASKEQ(&fin->fin_src6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]) || IP6_MASKEQ(&fin->fin_dst6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; #endif case IPF_EXP_UDP_PORT : case IPF_EXP_TCP_PORT : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (fin->fin_sport == e->ipfe_arg0[i]) || (fin->fin_dport == e->ipfe_arg0[i]); } break; case IPF_EXP_UDP_SPORT : case IPF_EXP_TCP_SPORT : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (fin->fin_sport == e->ipfe_arg0[i]); } break; case IPF_EXP_UDP_DPORT : case IPF_EXP_TCP_DPORT : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (fin->fin_dport == e->ipfe_arg0[i]); } break; case IPF_EXP_TCP_FLAGS : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((fin->fin_tcpf & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; } rv ^= e->ipfe_not; if (rv == 0) break; } return rv; } /* ------------------------------------------------------------------------ */ /* Function: ipf_queueflush */ /* Returns: int - number of entries flushed (0 = none) */ /* Parameters: softc(I) - pointer to soft context main structure */ /* deletefn(I) - function to call to delete entry */ /* ipfqs(I) - top of the list of ipf internal queues */ /* userqs(I) - top of the list of user defined timeouts */ /* */ /* This fucntion gets called when the state/NAT hash tables fill up and we */ /* need to try a bit harder to free up some space. The algorithm used here */ /* split into two parts but both halves have the same goal: to reduce the */ /* number of connections considered to be "active" to the low watermark. */ /* There are two steps in doing this: */ /* 1) Remove any TCP connections that are already considered to be "closed" */ /* but have not yet been removed from the state table. The two states */ /* TCPS_TIME_WAIT and TCPS_CLOSED are considered to be the perfect */ /* candidates for this style of removal. If freeing up entries in */ /* CLOSED or both CLOSED and TIME_WAIT brings us to the low watermark, */ /* we do not go on to step 2. */ /* */ /* 2) Look for the oldest entries on each timeout queue and free them if */ /* they are within the given window we are considering. Where the */ /* window starts and the steps taken to increase its size depend upon */ /* how long ipf has been running (ipf_ticks.) Anything modified in the */ /* last 30 seconds is not touched. */ /* touched */ /* die ipf_ticks 30*1.5 1800*1.5 | 43200*1.5 */ /* | | | | | | */ /* future <--+----------+--------+-----------+-----+-----+-----------> past */ /* now \_int=30s_/ \_int=1hr_/ \_int=12hr */ /* */ /* Points to note: */ /* - tqe_die is the time, in the future, when entries die. */ /* - tqe_die - ipf_ticks is how long left the connection has to live in ipf */ /* ticks. */ /* - tqe_touched is when the entry was last used by NAT/state */ /* - the closer tqe_touched is to ipf_ticks, the further tqe_die will be */ /* ipf_ticks any given timeout queue and vice versa. */ /* - both tqe_die and tqe_touched increase over time */ /* - timeout queues are sorted with the highest value of tqe_die at the */ /* bottom and therefore the smallest values of each are at the top */ /* - the pointer passed in as ipfqs should point to an array of timeout */ /* queues representing each of the TCP states */ /* */ /* We start by setting up a maximum range to scan for things to move of */ /* iend (newest) to istart (oldest) in chunks of "interval". If nothing is */ /* found in that range, "interval" is adjusted (so long as it isn't 30) and */ /* we start again with a new value for "iend" and "istart". This is */ /* continued until we either finish the scan of 30 second intervals or the */ /* low water mark is reached. */ /* ------------------------------------------------------------------------ */ int -ipf_queueflush(softc, deletefn, ipfqs, userqs, activep, size, low) - ipf_main_softc_t *softc; - ipftq_delete_fn_t deletefn; - ipftq_t *ipfqs, *userqs; - u_int *activep; - int size, low; +ipf_queueflush(ipf_main_softc_t *softc, ipftq_delete_fn_t deletefn, + ipftq_t *ipfqs, ipftq_t *userqs, u_int *activep, int size, int low) { u_long interval, istart, iend; ipftq_t *ifq, *ifqnext; ipftqent_t *tqe, *tqn; int removed = 0; for (tqn = ipfqs[IPF_TCPS_CLOSED].ifq_head; ((tqe = tqn) != NULL); ) { tqn = tqe->tqe_next; if ((*deletefn)(softc, tqe->tqe_parent) == 0) removed++; } if ((*activep * 100 / size) > low) { for (tqn = ipfqs[IPF_TCPS_TIME_WAIT].ifq_head; ((tqe = tqn) != NULL); ) { tqn = tqe->tqe_next; if ((*deletefn)(softc, tqe->tqe_parent) == 0) removed++; } } if ((*activep * 100 / size) <= low) { return removed; } /* * NOTE: Use of "* 15 / 10" is required here because if "* 1.5" is * used then the operations are upgraded to floating point * and kernels don't like floating point... */ if (softc->ipf_ticks > IPF_TTLVAL(43200 * 15 / 10)) { istart = IPF_TTLVAL(86400 * 4); interval = IPF_TTLVAL(43200); } else if (softc->ipf_ticks > IPF_TTLVAL(1800 * 15 / 10)) { istart = IPF_TTLVAL(43200); interval = IPF_TTLVAL(1800); } else if (softc->ipf_ticks > IPF_TTLVAL(30 * 15 / 10)) { istart = IPF_TTLVAL(1800); interval = IPF_TTLVAL(30); } else { return 0; } if (istart > softc->ipf_ticks) { if (softc->ipf_ticks - interval < interval) istart = interval; else istart = (softc->ipf_ticks / interval) * interval; } iend = softc->ipf_ticks - interval; while ((*activep * 100 / size) > low) { u_long try; try = softc->ipf_ticks - istart; for (ifq = ipfqs; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { if (try < tqe->tqe_touched) break; tqn = tqe->tqe_next; if ((*deletefn)(softc, tqe->tqe_parent) == 0) removed++; } } for (ifq = userqs; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { if (try < tqe->tqe_touched) break; tqn = tqe->tqe_next; if ((*deletefn)(softc, tqe->tqe_parent) == 0) removed++; } } if (try >= iend) { if (interval == IPF_TTLVAL(43200)) { interval = IPF_TTLVAL(1800); } else if (interval == IPF_TTLVAL(1800)) { interval = IPF_TTLVAL(30); } else { break; } if (interval >= softc->ipf_ticks) break; iend = softc->ipf_ticks - interval; } istart -= interval; } return removed; } /* ------------------------------------------------------------------------ */ /* Function: ipf_deliverlocal */ /* Returns: int - 1 = local address, 0 = non-local address */ /* Parameters: softc(I) - pointer to soft context main structure */ /* ipversion(I) - IP protocol version (4 or 6) */ /* ifp(I) - network interface pointer */ /* ipaddr(I) - IPv4/6 destination address */ /* */ /* This fucntion is used to determine in the address "ipaddr" belongs to */ /* the network interface represented by ifp. */ /* ------------------------------------------------------------------------ */ int -ipf_deliverlocal(softc, ipversion, ifp, ipaddr) - ipf_main_softc_t *softc; - int ipversion; - void *ifp; - i6addr_t *ipaddr; +ipf_deliverlocal(ipf_main_softc_t *softc, int ipversion, void *ifp, + i6addr_t *ipaddr) { i6addr_t addr; int islocal = 0; if (ipversion == 4) { if (ipf_ifpaddr(softc, 4, FRI_NORMAL, ifp, &addr, NULL) == 0) { if (addr.in4.s_addr == ipaddr->in4.s_addr) islocal = 1; } #ifdef USE_INET6 } else if (ipversion == 6) { if (ipf_ifpaddr(softc, 6, FRI_NORMAL, ifp, &addr, NULL) == 0) { if (IP6_EQ(&addr, ipaddr)) islocal = 1; } #endif } return islocal; } /* ------------------------------------------------------------------------ */ /* Function: ipf_settimeout */ /* Returns: int - 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to tuneable array entry */ /* p(I) - pointer to values passed in to apply */ /* */ /* This function is called to set the timeout values for each distinct */ /* queue timeout that is available. When called, it calls into both the */ /* state and NAT code, telling them to update their timeout queues. */ /* ------------------------------------------------------------------------ */ static int -ipf_settimeout(softc, t, p) - struct ipf_main_softc_s *softc; - ipftuneable_t *t; - ipftuneval_t *p; +ipf_settimeout(struct ipf_main_softc_s *softc, ipftuneable_t *t, + ipftuneval_t *p) { /* * ipf_interror should be set by the functions called here, not * by this function - it's just a middle man. */ if (ipf_state_settimeout(softc, t, p) == -1) return -1; if (ipf_nat_settimeout(softc, t, p) == -1) return -1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_apply_timeout */ /* Returns: int - 0 = success, -1 = failure */ /* Parameters: head(I) - pointer to tuneable array entry */ /* seconds(I) - pointer to values passed in to apply */ /* */ /* This function applies a timeout of "seconds" to the timeout queue that */ /* is pointed to by "head". All entries on this list have an expiration */ /* set to be the current tick value of ipf plus the ttl. Given that this */ /* function should only be called when the delta is non-zero, the task is */ /* to walk the entire list and apply the change. The sort order will not */ /* change. The only catch is that this is O(n) across the list, so if the */ /* queue has lots of entries (10s of thousands or 100s of thousands), it */ /* could take a relatively long time to work through them all. */ /* ------------------------------------------------------------------------ */ void -ipf_apply_timeout(head, seconds) - ipftq_t *head; - u_int seconds; +ipf_apply_timeout(ipftq_t *head, u_int seconds) { u_int oldtimeout, newtimeout; ipftqent_t *tqe; int delta; MUTEX_ENTER(&head->ifq_lock); oldtimeout = head->ifq_ttl; newtimeout = IPF_TTLVAL(seconds); delta = oldtimeout - newtimeout; head->ifq_ttl = newtimeout; for (tqe = head->ifq_head; tqe != NULL; tqe = tqe->tqe_next) { tqe->tqe_die += delta; } MUTEX_EXIT(&head->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_settimeout_tcp */ /* Returns: int - 0 = successfully applied, -1 = failed */ /* Parameters: t(I) - pointer to tuneable to change */ /* p(I) - pointer to new timeout information */ /* tab(I) - pointer to table of TCP queues */ /* */ /* This function applies the new timeout (p) to the TCP tunable (t) and */ /* updates all of the entries on the relevant timeout queue by calling */ /* ipf_apply_timeout(). */ /* ------------------------------------------------------------------------ */ int -ipf_settimeout_tcp(t, p, tab) - ipftuneable_t *t; - ipftuneval_t *p; - ipftq_t *tab; +ipf_settimeout_tcp(ipftuneable_t *t, ipftuneval_t *p, ipftq_t *tab) { if (!strcmp(t->ipft_name, "tcp_idle_timeout") || !strcmp(t->ipft_name, "tcp_established")) { ipf_apply_timeout(&tab[IPF_TCPS_ESTABLISHED], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_close_wait")) { ipf_apply_timeout(&tab[IPF_TCPS_CLOSE_WAIT], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_last_ack")) { ipf_apply_timeout(&tab[IPF_TCPS_LAST_ACK], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_timeout")) { ipf_apply_timeout(&tab[IPF_TCPS_LISTEN], p->ipftu_int); ipf_apply_timeout(&tab[IPF_TCPS_HALF_ESTAB], p->ipftu_int); ipf_apply_timeout(&tab[IPF_TCPS_CLOSING], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_listen")) { ipf_apply_timeout(&tab[IPF_TCPS_LISTEN], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_half_established")) { ipf_apply_timeout(&tab[IPF_TCPS_HALF_ESTAB], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_closing")) { ipf_apply_timeout(&tab[IPF_TCPS_CLOSING], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_syn_received")) { ipf_apply_timeout(&tab[IPF_TCPS_SYN_RECEIVED], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_syn_sent")) { ipf_apply_timeout(&tab[IPF_TCPS_SYN_SENT], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_closed")) { ipf_apply_timeout(&tab[IPF_TCPS_CLOSED], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_half_closed")) { ipf_apply_timeout(&tab[IPF_TCPS_CLOSED], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_time_wait")) { ipf_apply_timeout(&tab[IPF_TCPS_TIME_WAIT], p->ipftu_int); } else { /* * ipf_interror isn't set here because it should be set * by whatever called this function. */ return -1; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_soft_create */ /* Returns: NULL = failure, else success */ /* Parameters: arg(I) - pointer to soft context structure if already allocd */ /* */ /* Create the foundation soft context structure. In circumstances where it */ /* is not required to dynamically allocate the context, a pointer can be */ /* passed in (rather than NULL) to a structure to be initialised. */ /* The main thing of interest is that a number of locks are initialised */ /* here instead of in the where might be expected - in the relevant create */ /* function elsewhere. This is done because the current locking design has */ /* some areas where these locks are used outside of their module. */ /* Possibly the most important exercise that is done here is setting of all */ /* the timeout values, allowing them to be changed before init(). */ /* ------------------------------------------------------------------------ */ void * -ipf_main_soft_create(arg) - void *arg; +ipf_main_soft_create(void *arg) { ipf_main_softc_t *softc; if (arg == NULL) { KMALLOC(softc, ipf_main_softc_t *); if (softc == NULL) return NULL; } else { softc = arg; } bzero((char *)softc, sizeof(*softc)); /* * This serves as a flag as to whether or not the softc should be * free'd when _destroy is called. */ softc->ipf_dynamic_softc = (arg == NULL) ? 1 : 0; softc->ipf_tuners = ipf_tune_array_copy(softc, sizeof(ipf_main_tuneables), ipf_main_tuneables); if (softc->ipf_tuners == NULL) { ipf_main_soft_destroy(softc); return NULL; } MUTEX_INIT(&softc->ipf_rw, "ipf rw mutex"); MUTEX_INIT(&softc->ipf_timeoutlock, "ipf timeout lock"); RWLOCK_INIT(&softc->ipf_global, "ipf filter load/unload mutex"); RWLOCK_INIT(&softc->ipf_mutex, "ipf filter rwlock"); RWLOCK_INIT(&softc->ipf_tokens, "ipf token rwlock"); RWLOCK_INIT(&softc->ipf_state, "ipf state rwlock"); RWLOCK_INIT(&softc->ipf_nat, "ipf IP NAT rwlock"); RWLOCK_INIT(&softc->ipf_poolrw, "ipf pool rwlock"); RWLOCK_INIT(&softc->ipf_frag, "ipf frag rwlock"); softc->ipf_token_head = NULL; softc->ipf_token_tail = &softc->ipf_token_head; softc->ipf_tcpidletimeout = FIVE_DAYS; softc->ipf_tcpclosewait = IPF_TTLVAL(2 * TCP_MSL); softc->ipf_tcplastack = IPF_TTLVAL(30); softc->ipf_tcptimewait = IPF_TTLVAL(2 * TCP_MSL); softc->ipf_tcptimeout = IPF_TTLVAL(2 * TCP_MSL); softc->ipf_tcpsynsent = IPF_TTLVAL(2 * TCP_MSL); softc->ipf_tcpsynrecv = IPF_TTLVAL(2 * TCP_MSL); softc->ipf_tcpclosed = IPF_TTLVAL(30); softc->ipf_tcphalfclosed = IPF_TTLVAL(2 * 3600); softc->ipf_udptimeout = IPF_TTLVAL(120); softc->ipf_udpacktimeout = IPF_TTLVAL(12); softc->ipf_icmptimeout = IPF_TTLVAL(60); softc->ipf_icmpacktimeout = IPF_TTLVAL(6); softc->ipf_iptimeout = IPF_TTLVAL(60); #if defined(IPFILTER_DEFAULT_BLOCK) softc->ipf_pass = FR_BLOCK|FR_NOMATCH; #else softc->ipf_pass = (IPF_DEFAULT_PASS)|FR_NOMATCH; #endif softc->ipf_minttl = 4; softc->ipf_icmpminfragmtu = 68; softc->ipf_flags = IPF_LOGGING; #ifdef LARGE_NAT softc->ipf_large_nat = 1; #endif ipf_fbsd_kenv_get(softc); return softc; } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_soft_init */ /* Returns: 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ int -ipf_main_soft_init(softc) - ipf_main_softc_t *softc; +ipf_main_soft_init(ipf_main_softc_t *softc) { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_soft_destroy */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Undo everything that we did in ipf_main_soft_create. */ /* */ /* The most important check that needs to be made here is whether or not */ /* the structure was allocated by ipf_main_soft_create() by checking what */ /* value is stored in ipf_dynamic_main. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ void -ipf_main_soft_destroy(softc) - ipf_main_softc_t *softc; +ipf_main_soft_destroy(ipf_main_softc_t *softc) { RW_DESTROY(&softc->ipf_frag); RW_DESTROY(&softc->ipf_poolrw); RW_DESTROY(&softc->ipf_nat); RW_DESTROY(&softc->ipf_state); RW_DESTROY(&softc->ipf_tokens); RW_DESTROY(&softc->ipf_mutex); RW_DESTROY(&softc->ipf_global); MUTEX_DESTROY(&softc->ipf_timeoutlock); MUTEX_DESTROY(&softc->ipf_rw); if (softc->ipf_tuners != NULL) { KFREES(softc->ipf_tuners, sizeof(ipf_main_tuneables)); } if (softc->ipf_dynamic_softc == 1) { KFREE(softc); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_soft_fini */ /* Returns: 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Clean out the rules which have been added since _init was last called, */ /* the only dynamic part of the mainline. */ /* ------------------------------------------------------------------------ */ int -ipf_main_soft_fini(softc) - ipf_main_softc_t *softc; +ipf_main_soft_fini(ipf_main_softc_t *softc) { (void) ipf_flush(softc, IPL_LOGIPF, FR_INQUE|FR_OUTQUE|FR_INACTIVE); (void) ipf_flush(softc, IPL_LOGIPF, FR_INQUE|FR_OUTQUE); (void) ipf_flush(softc, IPL_LOGCOUNT, FR_INQUE|FR_OUTQUE|FR_INACTIVE); (void) ipf_flush(softc, IPL_LOGCOUNT, FR_INQUE|FR_OUTQUE); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_load */ /* Returns: 0 = success, -1 = failure */ /* Parameters: none */ /* */ /* Handle global initialisation that needs to be done for the base part of */ /* IPFilter. At present this just amounts to initialising some ICMP lookup */ /* arrays that get used by the state/NAT code. */ /* ------------------------------------------------------------------------ */ int -ipf_main_load() +ipf_main_load(void) { int i; /* fill icmp reply type table */ for (i = 0; i <= ICMP_MAXTYPE; i++) icmpreplytype4[i] = -1; icmpreplytype4[ICMP_ECHO] = ICMP_ECHOREPLY; icmpreplytype4[ICMP_TSTAMP] = ICMP_TSTAMPREPLY; icmpreplytype4[ICMP_IREQ] = ICMP_IREQREPLY; icmpreplytype4[ICMP_MASKREQ] = ICMP_MASKREPLY; #ifdef USE_INET6 /* fill icmp reply type table */ for (i = 0; i <= ICMP6_MAXTYPE; i++) icmpreplytype6[i] = -1; icmpreplytype6[ICMP6_ECHO_REQUEST] = ICMP6_ECHO_REPLY; icmpreplytype6[ICMP6_MEMBERSHIP_QUERY] = ICMP6_MEMBERSHIP_REPORT; icmpreplytype6[ICMP6_NI_QUERY] = ICMP6_NI_REPLY; icmpreplytype6[ND_ROUTER_SOLICIT] = ND_ROUTER_ADVERT; icmpreplytype6[ND_NEIGHBOR_SOLICIT] = ND_NEIGHBOR_ADVERT; #endif return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_unload */ /* Returns: 0 = success, -1 = failure */ /* Parameters: none */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int -ipf_main_unload() +ipf_main_unload(void) { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_load_all */ /* Returns: 0 = success, -1 = failure */ /* Parameters: none */ /* */ /* Work through all of the subsystems inside IPFilter and call the load */ /* function for each in an order that won't lead to a crash :) */ /* ------------------------------------------------------------------------ */ int -ipf_load_all() +ipf_load_all(void) { if (ipf_main_load() == -1) return -1; if (ipf_state_main_load() == -1) return -1; if (ipf_nat_main_load() == -1) return -1; if (ipf_frag_main_load() == -1) return -1; if (ipf_auth_main_load() == -1) return -1; if (ipf_proxy_main_load() == -1) return -1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_unload_all */ /* Returns: 0 = success, -1 = failure */ /* Parameters: none */ /* */ /* Work through all of the subsystems inside IPFilter and call the unload */ /* function for each in an order that won't lead to a crash :) */ /* ------------------------------------------------------------------------ */ int -ipf_unload_all() +ipf_unload_all(void) { if (ipf_proxy_main_unload() == -1) return -1; if (ipf_auth_main_unload() == -1) return -1; if (ipf_frag_main_unload() == -1) return -1; if (ipf_nat_main_unload() == -1) return -1; if (ipf_state_main_unload() == -1) return -1; if (ipf_main_unload() == -1) return -1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_create_all */ /* Returns: NULL = failure, else success */ /* Parameters: arg(I) - pointer to soft context main structure */ /* */ /* Work through all of the subsystems inside IPFilter and call the create */ /* function for each in an order that won't lead to a crash :) */ /* ------------------------------------------------------------------------ */ ipf_main_softc_t * -ipf_create_all(arg) - void *arg; +ipf_create_all(void *arg) { ipf_main_softc_t *softc; softc = ipf_main_soft_create(arg); if (softc == NULL) return NULL; #ifdef IPFILTER_LOG softc->ipf_log_soft = ipf_log_soft_create(softc); if (softc->ipf_log_soft == NULL) { ipf_destroy_all(softc); return NULL; } #endif softc->ipf_lookup_soft = ipf_lookup_soft_create(softc); if (softc->ipf_lookup_soft == NULL) { ipf_destroy_all(softc); return NULL; } softc->ipf_sync_soft = ipf_sync_soft_create(softc); if (softc->ipf_sync_soft == NULL) { ipf_destroy_all(softc); return NULL; } softc->ipf_state_soft = ipf_state_soft_create(softc); if (softc->ipf_state_soft == NULL) { ipf_destroy_all(softc); return NULL; } softc->ipf_nat_soft = ipf_nat_soft_create(softc); if (softc->ipf_nat_soft == NULL) { ipf_destroy_all(softc); return NULL; } softc->ipf_frag_soft = ipf_frag_soft_create(softc); if (softc->ipf_frag_soft == NULL) { ipf_destroy_all(softc); return NULL; } softc->ipf_auth_soft = ipf_auth_soft_create(softc); if (softc->ipf_auth_soft == NULL) { ipf_destroy_all(softc); return NULL; } softc->ipf_proxy_soft = ipf_proxy_soft_create(softc); if (softc->ipf_proxy_soft == NULL) { ipf_destroy_all(softc); return NULL; } return softc; } /* ------------------------------------------------------------------------ */ /* Function: ipf_destroy_all */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Work through all of the subsystems inside IPFilter and call the destroy */ /* function for each in an order that won't lead to a crash :) */ /* */ /* Every one of these functions is expected to succeed, so there is no */ /* checking of return values. */ /* ------------------------------------------------------------------------ */ void -ipf_destroy_all(softc) - ipf_main_softc_t *softc; +ipf_destroy_all(ipf_main_softc_t *softc) { if (softc->ipf_state_soft != NULL) { ipf_state_soft_destroy(softc, softc->ipf_state_soft); softc->ipf_state_soft = NULL; } if (softc->ipf_nat_soft != NULL) { ipf_nat_soft_destroy(softc, softc->ipf_nat_soft); softc->ipf_nat_soft = NULL; } if (softc->ipf_frag_soft != NULL) { ipf_frag_soft_destroy(softc, softc->ipf_frag_soft); softc->ipf_frag_soft = NULL; } if (softc->ipf_auth_soft != NULL) { ipf_auth_soft_destroy(softc, softc->ipf_auth_soft); softc->ipf_auth_soft = NULL; } if (softc->ipf_proxy_soft != NULL) { ipf_proxy_soft_destroy(softc, softc->ipf_proxy_soft); softc->ipf_proxy_soft = NULL; } if (softc->ipf_sync_soft != NULL) { ipf_sync_soft_destroy(softc, softc->ipf_sync_soft); softc->ipf_sync_soft = NULL; } if (softc->ipf_lookup_soft != NULL) { ipf_lookup_soft_destroy(softc, softc->ipf_lookup_soft); softc->ipf_lookup_soft = NULL; } #ifdef IPFILTER_LOG if (softc->ipf_log_soft != NULL) { ipf_log_soft_destroy(softc, softc->ipf_log_soft); softc->ipf_log_soft = NULL; } #endif ipf_main_soft_destroy(softc); } /* ------------------------------------------------------------------------ */ /* Function: ipf_init_all */ /* Returns: 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Work through all of the subsystems inside IPFilter and call the init */ /* function for each in an order that won't lead to a crash :) */ /* ------------------------------------------------------------------------ */ int -ipf_init_all(softc) - ipf_main_softc_t *softc; +ipf_init_all(ipf_main_softc_t *softc) { if (ipf_main_soft_init(softc) == -1) return -1; #ifdef IPFILTER_LOG if (ipf_log_soft_init(softc, softc->ipf_log_soft) == -1) return -1; #endif if (ipf_lookup_soft_init(softc, softc->ipf_lookup_soft) == -1) return -1; if (ipf_sync_soft_init(softc, softc->ipf_sync_soft) == -1) return -1; if (ipf_state_soft_init(softc, softc->ipf_state_soft) == -1) return -1; if (ipf_nat_soft_init(softc, softc->ipf_nat_soft) == -1) return -1; if (ipf_frag_soft_init(softc, softc->ipf_frag_soft) == -1) return -1; if (ipf_auth_soft_init(softc, softc->ipf_auth_soft) == -1) return -1; if (ipf_proxy_soft_init(softc, softc->ipf_proxy_soft) == -1) return -1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_fini_all */ /* Returns: 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Work through all of the subsystems inside IPFilter and call the fini */ /* function for each in an order that won't lead to a crash :) */ /* ------------------------------------------------------------------------ */ int -ipf_fini_all(softc) - ipf_main_softc_t *softc; +ipf_fini_all(ipf_main_softc_t *softc) { ipf_token_flush(softc); if (ipf_proxy_soft_fini(softc, softc->ipf_proxy_soft) == -1) return -1; if (ipf_auth_soft_fini(softc, softc->ipf_auth_soft) == -1) return -1; if (ipf_frag_soft_fini(softc, softc->ipf_frag_soft) == -1) return -1; if (ipf_nat_soft_fini(softc, softc->ipf_nat_soft) == -1) return -1; if (ipf_state_soft_fini(softc, softc->ipf_state_soft) == -1) return -1; if (ipf_sync_soft_fini(softc, softc->ipf_sync_soft) == -1) return -1; if (ipf_lookup_soft_fini(softc, softc->ipf_lookup_soft) == -1) return -1; #ifdef IPFILTER_LOG if (ipf_log_soft_fini(softc, softc->ipf_log_soft) == -1) return -1; #endif if (ipf_main_soft_fini(softc) == -1) return -1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rule_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* At present this function exists just to support temporary addition of */ /* firewall rules. Both inactive and active lists are scanned for items to */ /* purge, as by rights, the expiration is computed as soon as the rule is */ /* loaded in. */ /* ------------------------------------------------------------------------ */ void -ipf_rule_expire(softc) - ipf_main_softc_t *softc; +ipf_rule_expire(ipf_main_softc_t *softc) { frentry_t *fr; if ((softc->ipf_rule_explist[0] == NULL) && (softc->ipf_rule_explist[1] == NULL)) return; WRITE_ENTER(&softc->ipf_mutex); while ((fr = softc->ipf_rule_explist[0]) != NULL) { /* * Because the list is kept sorted on insertion, the fist * one that dies in the future means no more work to do. */ if (fr->fr_die > softc->ipf_ticks) break; ipf_rule_delete(softc, fr, IPL_LOGIPF, 0); } while ((fr = softc->ipf_rule_explist[1]) != NULL) { /* * Because the list is kept sorted on insertion, the fist * one that dies in the future means no more work to do. */ if (fr->fr_die > softc->ipf_ticks) break; ipf_rule_delete(softc, fr, IPL_LOGIPF, 1); } RWLOCK_EXIT(&softc->ipf_mutex); } static int ipf_ht_node_cmp(struct host_node_s *, struct host_node_s *); static void ipf_ht_node_make_key(host_track_t *, host_node_t *, int, i6addr_t *); host_node_t RBI_ZERO(ipf_rb); RBI_CODE(ipf_rb, host_node_t, hn_entry, ipf_ht_node_cmp) /* ------------------------------------------------------------------------ */ /* Function: ipf_ht_node_cmp */ /* Returns: int - 0 == nodes are the same, .. */ /* Parameters: k1(I) - pointer to first key to compare */ /* k2(I) - pointer to second key to compare */ /* */ /* The "key" for the node is a combination of two fields: the address */ /* family and the address itself. */ /* */ /* Because we're not actually interpreting the address data, it isn't */ /* necessary to convert them to/from network/host byte order. The mask is */ /* just used to remove bits that aren't significant - it doesn't matter */ /* where they are, as long as they're always in the same place. */ /* */ /* As with IP6_EQ, comparing IPv6 addresses starts at the bottom because */ /* this is where individual ones will differ the most - but not true for */ /* for /48's, etc. */ /* ------------------------------------------------------------------------ */ static int -ipf_ht_node_cmp(k1, k2) - struct host_node_s *k1, *k2; +ipf_ht_node_cmp(struct host_node_s *k1, struct host_node_s *k2) { int i; i = (k2->hn_addr.adf_family - k1->hn_addr.adf_family); if (i != 0) return i; if (k1->hn_addr.adf_family == AF_INET) return (k2->hn_addr.adf_addr.in4.s_addr - k1->hn_addr.adf_addr.in4.s_addr); i = k2->hn_addr.adf_addr.i6[3] - k1->hn_addr.adf_addr.i6[3]; if (i != 0) return i; i = k2->hn_addr.adf_addr.i6[2] - k1->hn_addr.adf_addr.i6[2]; if (i != 0) return i; i = k2->hn_addr.adf_addr.i6[1] - k1->hn_addr.adf_addr.i6[1]; if (i != 0) return i; i = k2->hn_addr.adf_addr.i6[0] - k1->hn_addr.adf_addr.i6[0]; return i; } /* ------------------------------------------------------------------------ */ /* Function: ipf_ht_node_make_key */ /* Returns: Nil */ /* parameters: htp(I) - pointer to address tracking structure */ /* key(I) - where to store masked address for lookup */ /* family(I) - protocol family of address */ /* addr(I) - pointer to network address */ /* */ /* Using the "netmask" (number of bits) stored parent host tracking struct, */ /* copy the address passed in into the key structure whilst masking out the */ /* bits that we don't want. */ /* */ /* Because the parser will set ht_netmask to 128 if there is no protocol */ /* specified (the parser doesn't know if it should be a v4 or v6 rule), we */ /* have to be wary of that and not allow 32-128 to happen. */ /* ------------------------------------------------------------------------ */ static void -ipf_ht_node_make_key(htp, key, family, addr) - host_track_t *htp; - host_node_t *key; - int family; - i6addr_t *addr; +ipf_ht_node_make_key(host_track_t *htp, host_node_t *key, int family, + i6addr_t *addr) { key->hn_addr.adf_family = family; if (family == AF_INET) { u_32_t mask; int bits; key->hn_addr.adf_len = sizeof(key->hn_addr.adf_addr.in4); bits = htp->ht_netmask; if (bits >= 32) { mask = 0xffffffff; } else { mask = htonl(0xffffffff << (32 - bits)); } key->hn_addr.adf_addr.in4.s_addr = addr->in4.s_addr & mask; #ifdef USE_INET6 } else { int bits = htp->ht_netmask; key->hn_addr.adf_len = sizeof(key->hn_addr.adf_addr.in6); if (bits > 96) { key->hn_addr.adf_addr.i6[3] = addr->i6[3] & htonl(0xffffffff << (128 - bits)); key->hn_addr.adf_addr.i6[2] = addr->i6[2]; key->hn_addr.adf_addr.i6[1] = addr->i6[2]; key->hn_addr.adf_addr.i6[0] = addr->i6[2]; } else if (bits > 64) { key->hn_addr.adf_addr.i6[3] = 0; key->hn_addr.adf_addr.i6[2] = addr->i6[2] & htonl(0xffffffff << (96 - bits)); key->hn_addr.adf_addr.i6[1] = addr->i6[1]; key->hn_addr.adf_addr.i6[0] = addr->i6[0]; } else if (bits > 32) { key->hn_addr.adf_addr.i6[3] = 0; key->hn_addr.adf_addr.i6[2] = 0; key->hn_addr.adf_addr.i6[1] = addr->i6[1] & htonl(0xffffffff << (64 - bits)); key->hn_addr.adf_addr.i6[0] = addr->i6[0]; } else { key->hn_addr.adf_addr.i6[3] = 0; key->hn_addr.adf_addr.i6[2] = 0; key->hn_addr.adf_addr.i6[1] = 0; key->hn_addr.adf_addr.i6[0] = addr->i6[0] & htonl(0xffffffff << (32 - bits)); } #endif } } /* ------------------------------------------------------------------------ */ /* Function: ipf_ht_node_add */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* htp(I) - pointer to address tracking structure */ /* family(I) - protocol family of address */ /* addr(I) - pointer to network address */ /* */ /* NOTE: THIS FUNCTION MUST BE CALLED WITH AN EXCLUSIVE LOCK THAT PREVENTS */ /* ipf_ht_node_del FROM RUNNING CONCURRENTLY ON THE SAME htp. */ /* */ /* After preparing the key with the address information to find, look in */ /* the red-black tree to see if the address is known. A successful call to */ /* this function can mean one of two things: a new node was added to the */ /* tree or a matching node exists and we're able to bump up its activity. */ /* ------------------------------------------------------------------------ */ int -ipf_ht_node_add(softc, htp, family, addr) - ipf_main_softc_t *softc; - host_track_t *htp; - int family; - i6addr_t *addr; +ipf_ht_node_add(ipf_main_softc_t *softc, host_track_t *htp, int family, + i6addr_t *addr) { host_node_t *h; host_node_t k; ipf_ht_node_make_key(htp, &k, family, addr); h = RBI_SEARCH(ipf_rb, &htp->ht_root, &k); if (h == NULL) { if (htp->ht_cur_nodes >= htp->ht_max_nodes) return -1; KMALLOC(h, host_node_t *); if (h == NULL) { DT(ipf_rb_no_mem); LBUMP(ipf_rb_no_mem); return -1; } /* * If there was a macro to initialise the RB node then that * would get used here, but there isn't... */ bzero((char *)h, sizeof(*h)); h->hn_addr = k.hn_addr; h->hn_addr.adf_family = k.hn_addr.adf_family; RBI_INSERT(ipf_rb, &htp->ht_root, h); htp->ht_cur_nodes++; } else { if ((htp->ht_max_per_node != 0) && (h->hn_active >= htp->ht_max_per_node)) { DT(ipf_rb_node_max); LBUMP(ipf_rb_node_max); return -1; } } h->hn_active++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_ht_node_del */ /* Returns: int - 0 == success, -1 == failure */ /* parameters: htp(I) - pointer to address tracking structure */ /* family(I) - protocol family of address */ /* addr(I) - pointer to network address */ /* */ /* NOTE: THIS FUNCTION MUST BE CALLED WITH AN EXCLUSIVE LOCK THAT PREVENTS */ /* ipf_ht_node_add FROM RUNNING CONCURRENTLY ON THE SAME htp. */ /* */ /* Try and find the address passed in amongst the leavese on this tree to */ /* be friend. If found then drop the active account for that node drops by */ /* one. If that count reaches 0, it is time to free it all up. */ /* ------------------------------------------------------------------------ */ int -ipf_ht_node_del(htp, family, addr) - host_track_t *htp; - int family; - i6addr_t *addr; +ipf_ht_node_del(host_track_t *htp, int family, i6addr_t *addr) { host_node_t *h; host_node_t k; ipf_ht_node_make_key(htp, &k, family, addr); h = RBI_SEARCH(ipf_rb, &htp->ht_root, &k); if (h == NULL) { return -1; } else { h->hn_active--; if (h->hn_active == 0) { (void) RBI_DELETE(ipf_rb, &htp->ht_root, h); htp->ht_cur_nodes--; KFREE(h); } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rb_ht_init */ /* Returns: Nil */ /* Parameters: head(I) - pointer to host tracking structure */ /* */ /* Initialise the host tracking structure to be ready for use above. */ /* ------------------------------------------------------------------------ */ void -ipf_rb_ht_init(head) - host_track_t *head; +ipf_rb_ht_init(host_track_t *head) { RBI_INIT(ipf_rb, &head->ht_root); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rb_ht_freenode */ /* Returns: Nil */ /* Parameters: head(I) - pointer to host tracking structure */ /* arg(I) - additional argument from walk caller */ /* */ /* Free an actual host_node_t structure. */ /* ------------------------------------------------------------------------ */ void -ipf_rb_ht_freenode(node, arg) - host_node_t *node; - void *arg; +ipf_rb_ht_freenode(host_node_t *node, void *arg) { KFREE(node); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rb_ht_flush */ /* Returns: Nil */ /* Parameters: head(I) - pointer to host tracking structure */ /* */ /* Remove all of the nodes in the tree tracking hosts by calling a walker */ /* and free'ing each one. */ /* ------------------------------------------------------------------------ */ void -ipf_rb_ht_flush(head) - host_track_t *head; +ipf_rb_ht_flush(host_track_t *head) { RBI_WALK(ipf_rb, &head->ht_root, ipf_rb_ht_freenode, NULL); } /* ------------------------------------------------------------------------ */ /* Function: ipf_slowtimer */ /* Returns: Nil */ /* Parameters: ptr(I) - pointer to main ipf soft context structure */ /* */ /* Slowly expire held state for fragments. Timeouts are set * in */ /* expectation of this being called twice per second. */ /* ------------------------------------------------------------------------ */ void -ipf_slowtimer(softc) - ipf_main_softc_t *softc; +ipf_slowtimer(ipf_main_softc_t *softc) { ipf_token_expire(softc); ipf_frag_expire(softc); ipf_state_expire(softc); ipf_nat_expire(softc); ipf_auth_expire(softc); ipf_lookup_expire(softc); ipf_rule_expire(softc); ipf_sync_expire(softc); softc->ipf_ticks++; } /* ------------------------------------------------------------------------ */ /* Function: ipf_inet_mask_add */ /* Returns: Nil */ /* Parameters: bits(I) - pointer to nat context information */ /* mtab(I) - pointer to mask hash table structure */ /* */ /* When called, bits represents the mask of a new NAT rule that has just */ /* been added. This function inserts a bitmask into the array of masks to */ /* search when searching for a matching NAT rule for a packet. */ /* Prevention of duplicate masks is achieved by checking the use count for */ /* a given netmask. */ /* ------------------------------------------------------------------------ */ void -ipf_inet_mask_add(bits, mtab) - int bits; - ipf_v4_masktab_t *mtab; +ipf_inet_mask_add(int bits, ipf_v4_masktab_t *mtab) { u_32_t mask; int i, j; mtab->imt4_masks[bits]++; if (mtab->imt4_masks[bits] > 1) return; if (bits == 0) mask = 0; else mask = 0xffffffff << (32 - bits); for (i = 0; i < 33; i++) { if (ntohl(mtab->imt4_active[i]) < mask) { for (j = 32; j > i; j--) mtab->imt4_active[j] = mtab->imt4_active[j - 1]; mtab->imt4_active[i] = htonl(mask); break; } } mtab->imt4_max++; } /* ------------------------------------------------------------------------ */ /* Function: ipf_inet_mask_del */ /* Returns: Nil */ /* Parameters: bits(I) - number of bits set in the netmask */ /* mtab(I) - pointer to mask hash table structure */ /* */ /* Remove the 32bit bitmask represented by "bits" from the collection of */ /* netmasks stored inside of mtab. */ /* ------------------------------------------------------------------------ */ void -ipf_inet_mask_del(bits, mtab) - int bits; - ipf_v4_masktab_t *mtab; +ipf_inet_mask_del(int bits, ipf_v4_masktab_t *mtab) { u_32_t mask; int i, j; mtab->imt4_masks[bits]--; if (mtab->imt4_masks[bits] > 0) return; mask = htonl(0xffffffff << (32 - bits)); for (i = 0; i < 33; i++) { if (mtab->imt4_active[i] == mask) { for (j = i + 1; j < 33; j++) mtab->imt4_active[j - 1] = mtab->imt4_active[j]; break; } } mtab->imt4_max--; ASSERT(mtab->imt4_max >= 0); } #ifdef USE_INET6 /* ------------------------------------------------------------------------ */ /* Function: ipf_inet6_mask_add */ /* Returns: Nil */ /* Parameters: bits(I) - number of bits set in mask */ /* mask(I) - pointer to mask to add */ /* mtab(I) - pointer to mask hash table structure */ /* */ /* When called, bitcount represents the mask of a IPv6 NAT map rule that */ /* has just been added. This function inserts a bitmask into the array of */ /* masks to search when searching for a matching NAT rule for a packet. */ /* Prevention of duplicate masks is achieved by checking the use count for */ /* a given netmask. */ /* ------------------------------------------------------------------------ */ void -ipf_inet6_mask_add(bits, mask, mtab) - int bits; - i6addr_t *mask; - ipf_v6_masktab_t *mtab; +ipf_inet6_mask_add(int bits, i6addr_t *mask, ipf_v6_masktab_t *mtab) { i6addr_t zero; int i, j; mtab->imt6_masks[bits]++; if (mtab->imt6_masks[bits] > 1) return; if (bits == 0) { mask = &zero; zero.i6[0] = 0; zero.i6[1] = 0; zero.i6[2] = 0; zero.i6[3] = 0; } for (i = 0; i < 129; i++) { if (IP6_LT(&mtab->imt6_active[i], mask)) { for (j = 128; j > i; j--) mtab->imt6_active[j] = mtab->imt6_active[j - 1]; mtab->imt6_active[i] = *mask; break; } } mtab->imt6_max++; } /* ------------------------------------------------------------------------ */ /* Function: ipf_inet6_mask_del */ /* Returns: Nil */ /* Parameters: bits(I) - number of bits set in mask */ /* mask(I) - pointer to mask to remove */ /* mtab(I) - pointer to mask hash table structure */ /* */ /* Remove the 128bit bitmask represented by "bits" from the collection of */ /* netmasks stored inside of mtab. */ /* ------------------------------------------------------------------------ */ void -ipf_inet6_mask_del(bits, mask, mtab) - int bits; - i6addr_t *mask; - ipf_v6_masktab_t *mtab; +ipf_inet6_mask_del(int bits, i6addr_t *mask, ipf_v6_masktab_t *mtab) { i6addr_t zero; int i, j; mtab->imt6_masks[bits]--; if (mtab->imt6_masks[bits] > 0) return; if (bits == 0) mask = &zero; zero.i6[0] = 0; zero.i6[1] = 0; zero.i6[2] = 0; zero.i6[3] = 0; for (i = 0; i < 129; i++) { if (IP6_EQ(&mtab->imt6_active[i], mask)) { for (j = i + 1; j < 129; j++) { mtab->imt6_active[j - 1] = mtab->imt6_active[j]; if (IP6_EQ(&mtab->imt6_active[j - 1], &zero)) break; } break; } } mtab->imt6_max--; ASSERT(mtab->imt6_max >= 0); } #endif diff --git a/sys/netpfil/ipfilter/netinet/ip_auth.c b/sys/netpfil/ipfilter/netinet/ip_auth.c index 881dab1aaea8..e216c197f666 100644 --- a/sys/netpfil/ipfilter/netinet/ip_auth.c +++ b/sys/netpfil/ipfilter/netinet/ip_auth.c @@ -1,1246 +1,1208 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if !defined(_KERNEL) # include # include # ifdef _STDC_C99 # include # endif # include # define _KERNEL # include # undef _KERNEL #endif #if defined(_KERNEL) && defined(__FreeBSD__) # include # include #else # include #endif # include #include #if defined(_KERNEL) # include # if !defined(__SVR4) # include # endif #endif #if defined(__SVR4) # include # include # ifdef _KERNEL # include # endif # include # include #endif #if defined(__FreeBSD__) # include #endif #if defined(__NetBSD__) # include #endif #if defined(_KERNEL) && defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000) # include #endif #if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 400000) && \ !defined(_KERNEL) # include #endif #include #ifdef sun # include #endif #include #include #include # include #if !defined(_KERNEL) # define KERNEL # define _KERNEL # define NOT_KERNEL #endif #ifdef NOT_KERNEL # undef _KERNEL # undef KERNEL #endif #include #if defined(__FreeBSD__) # include # define IF_QFULL _IF_QFULL # define IF_DROP _IF_DROP #endif #include #include #include #include #include "netinet/ip_compat.h" #include #include "netinet/ip_fil.h" #include "netinet/ip_auth.h" #if !SOLARIS # include # ifdef __FreeBSD__ # include # endif #endif #if defined(__FreeBSD__) # include # if defined(_KERNEL) && !defined(IPFILTER_LKM) # include # include # endif #endif /* END OF INCLUDES */ #if !defined(lint) static const char rcsid[] = "@(#)$FreeBSD$"; /* static const char rcsid[] = "@(#)$Id: ip_auth.c,v 2.73.2.24 2007/09/09 11:32:04 darrenr Exp $"; */ #endif static void ipf_auth_deref(frauthent_t **); static void ipf_auth_deref_unlocked(ipf_auth_softc_t *, frauthent_t **); static int ipf_auth_geniter(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, ipfobj_t *); static int ipf_auth_reply(ipf_main_softc_t *, ipf_auth_softc_t *, char *); static int ipf_auth_wait(ipf_main_softc_t *, ipf_auth_softc_t *, char *); static int ipf_auth_flush(void *); /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_main_load */ /* Returns: int - 0 == success, else error */ /* Parameters: None */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int -ipf_auth_main_load() +ipf_auth_main_load(void) { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_main_unload */ /* Returns: int - 0 == success, else error */ /* Parameters: None */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int -ipf_auth_main_unload() +ipf_auth_main_unload(void) { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_soft_create */ /* Returns: int - NULL = failure, else success */ /* Parameters: softc(I) - pointer to soft context data */ /* */ /* Create a structre to store all of the run-time data for packet auth in */ /* and initialise some fields to their defaults. */ /* ------------------------------------------------------------------------ */ void * -ipf_auth_soft_create(softc) - ipf_main_softc_t *softc; +ipf_auth_soft_create(ipf_main_softc_t *softc) { ipf_auth_softc_t *softa; KMALLOC(softa, ipf_auth_softc_t *); if (softa == NULL) return NULL; bzero((char *)softa, sizeof(*softa)); softa->ipf_auth_size = FR_NUMAUTH; softa->ipf_auth_defaultage = 600; RWLOCK_INIT(&softa->ipf_authlk, "ipf IP User-Auth rwlock"); MUTEX_INIT(&softa->ipf_auth_mx, "ipf auth log mutex"); #if SOLARIS && defined(_KERNEL) cv_init(&softa->ipf_auth_wait, "ipf auth condvar", CV_DRIVER, NULL); #endif return softa; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_soft_init */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context data */ /* arg(I) - opaque pointer to auth context data */ /* */ /* Allocate memory and initialise data structures used in handling auth */ /* rules. */ /* ------------------------------------------------------------------------ */ int -ipf_auth_soft_init(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_auth_soft_init(ipf_main_softc_t *softc, void *arg) { ipf_auth_softc_t *softa = arg; KMALLOCS(softa->ipf_auth, frauth_t *, softa->ipf_auth_size * sizeof(*softa->ipf_auth)); if (softa->ipf_auth == NULL) return -1; bzero((char *)softa->ipf_auth, softa->ipf_auth_size * sizeof(*softa->ipf_auth)); KMALLOCS(softa->ipf_auth_pkts, mb_t **, softa->ipf_auth_size * sizeof(*softa->ipf_auth_pkts)); if (softa->ipf_auth_pkts == NULL) return -2; bzero((char *)softa->ipf_auth_pkts, softa->ipf_auth_size * sizeof(*softa->ipf_auth_pkts)); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_soft_fini */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context data */ /* arg(I) - opaque pointer to auth context data */ /* */ /* Free all network buffer memory used to keep saved packets that have been */ /* connectedd to the soft soft context structure *but* do not free that: it */ /* is free'd by _destroy(). */ /* ------------------------------------------------------------------------ */ int -ipf_auth_soft_fini(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_auth_soft_fini(ipf_main_softc_t *softc, void *arg) { ipf_auth_softc_t *softa = arg; frauthent_t *fae, **faep; frentry_t *fr, **frp; mb_t *m; int i; if (softa->ipf_auth != NULL) { KFREES(softa->ipf_auth, softa->ipf_auth_size * sizeof(*softa->ipf_auth)); softa->ipf_auth = NULL; } if (softa->ipf_auth_pkts != NULL) { for (i = 0; i < softa->ipf_auth_size; i++) { m = softa->ipf_auth_pkts[i]; if (m != NULL) { FREE_MB_T(m); softa->ipf_auth_pkts[i] = NULL; } } KFREES(softa->ipf_auth_pkts, softa->ipf_auth_size * sizeof(*softa->ipf_auth_pkts)); softa->ipf_auth_pkts = NULL; } faep = &softa->ipf_auth_entries; while ((fae = *faep) != NULL) { *faep = fae->fae_next; KFREE(fae); } softa->ipf_auth_ip = NULL; if (softa->ipf_auth_rules != NULL) { for (frp = &softa->ipf_auth_rules; ((fr = *frp) != NULL); ) { if (fr->fr_ref == 1) { *frp = fr->fr_next; MUTEX_DESTROY(&fr->fr_lock); KFREE(fr); } else frp = &fr->fr_next; } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_soft_destroy */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context data */ /* arg(I) - opaque pointer to auth context data */ /* */ /* Undo what was done in _create() - i.e. free the soft context data. */ /* ------------------------------------------------------------------------ */ void -ipf_auth_soft_destroy(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_auth_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_auth_softc_t *softa = arg; #if SOLARIS && defined(_KERNEL) cv_destroy(&softa->ipf_auth_wait); #endif MUTEX_DESTROY(&softa->ipf_auth_mx); RW_DESTROY(&softa->ipf_authlk); KFREE(softa); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_setlock */ /* Returns: void */ /* Paramters: arg(I) - pointer to soft context data */ /* tmp(I) - value to assign to auth lock */ /* */ /* ------------------------------------------------------------------------ */ void -ipf_auth_setlock(arg, tmp) - void *arg; - int tmp; +ipf_auth_setlock(void *arg, int tmp) { ipf_auth_softc_t *softa = arg; softa->ipf_auth_lock = tmp; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_check */ /* Returns: frentry_t* - pointer to ipf rule if match found, else NULL */ /* Parameters: fin(I) - pointer to ipftoken structure */ /* passp(I) - pointer to ipfgeniter structure */ /* */ /* Check if a packet has authorization. If the packet is found to match an */ /* authorization result and that would result in a feedback loop (i.e. it */ /* will end up returning FR_AUTH) then return FR_BLOCK instead. */ /* ------------------------------------------------------------------------ */ frentry_t * -ipf_auth_check(fin, passp) - fr_info_t *fin; - u_32_t *passp; +ipf_auth_check(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_auth_softc_t *softa = softc->ipf_auth_soft; frentry_t *fr; frauth_t *fra; u_32_t pass; u_short id; ip_t *ip; int i; if (softa->ipf_auth_lock || !softa->ipf_auth_used) return NULL; ip = fin->fin_ip; id = ip->ip_id; READ_ENTER(&softa->ipf_authlk); for (i = softa->ipf_auth_start; i != softa->ipf_auth_end; ) { /* * index becomes -2 only after an SIOCAUTHW. Check this in * case the same packet gets sent again and it hasn't yet been * auth'd. */ fra = softa->ipf_auth + i; if ((fra->fra_index == -2) && (id == fra->fra_info.fin_id) && !bcmp((char *)fin, (char *)&fra->fra_info, FI_CSIZE)) { /* * Avoid feedback loop. */ if (!(pass = fra->fra_pass) || (FR_ISAUTH(pass))) { pass = FR_BLOCK; fin->fin_reason = FRB_AUTHFEEDBACK; } /* * Create a dummy rule for the stateful checking to * use and return. Zero out any values we don't * trust from userland! */ if ((pass & FR_KEEPSTATE) || ((pass & FR_KEEPFRAG) && (fin->fin_flx & FI_FRAG))) { KMALLOC(fr, frentry_t *); if (fr) { bcopy((char *)fra->fra_info.fin_fr, (char *)fr, sizeof(*fr)); fr->fr_grp = NULL; fr->fr_ifa = fin->fin_ifp; fr->fr_func = NULL; fr->fr_ref = 1; fr->fr_flags = pass; fr->fr_ifas[1] = NULL; fr->fr_ifas[2] = NULL; fr->fr_ifas[3] = NULL; MUTEX_INIT(&fr->fr_lock, "ipf auth rule"); } } else fr = fra->fra_info.fin_fr; fin->fin_fr = fr; fin->fin_flx |= fra->fra_flx; RWLOCK_EXIT(&softa->ipf_authlk); WRITE_ENTER(&softa->ipf_authlk); /* * ipf_auth_rules is populated with the rules malloc'd * above and only those. */ if ((fr != NULL) && (fr != fra->fra_info.fin_fr)) { fr->fr_next = softa->ipf_auth_rules; softa->ipf_auth_rules = fr; } softa->ipf_auth_stats.fas_hits++; fra->fra_index = -1; softa->ipf_auth_used--; softa->ipf_auth_replies--; if (i == softa->ipf_auth_start) { while (fra->fra_index == -1) { i++; fra++; if (i == softa->ipf_auth_size) { i = 0; fra = softa->ipf_auth; } softa->ipf_auth_start = i; if (i == softa->ipf_auth_end) break; } if (softa->ipf_auth_start == softa->ipf_auth_end) { softa->ipf_auth_next = 0; softa->ipf_auth_start = 0; softa->ipf_auth_end = 0; } } RWLOCK_EXIT(&softa->ipf_authlk); if (passp != NULL) *passp = pass; softa->ipf_auth_stats.fas_hits++; return fr; } i++; if (i == softa->ipf_auth_size) i = 0; } RWLOCK_EXIT(&softa->ipf_authlk); softa->ipf_auth_stats.fas_miss++; return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_new */ /* Returns: int - 1 == success, 0 = did not put packet on auth queue */ /* Parameters: m(I) - pointer to mb_t with packet in it */ /* fin(I) - pointer to packet information */ /* */ /* Check if we have room in the auth array to hold details for another */ /* packet. If we do, store it and wake up any user programs which are */ /* waiting to hear about these events. */ /* ------------------------------------------------------------------------ */ int -ipf_auth_new(m, fin) - mb_t *m; - fr_info_t *fin; +ipf_auth_new(mb_t *m, fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_auth_softc_t *softa = softc->ipf_auth_soft; #if defined(_KERNEL) && SOLARIS qpktinfo_t *qpi = fin->fin_qpi; #endif frauth_t *fra; #if !defined(sparc) && !defined(m68k) ip_t *ip; #endif int i; if (softa->ipf_auth_lock) return 0; WRITE_ENTER(&softa->ipf_authlk); if (((softa->ipf_auth_end + 1) % softa->ipf_auth_size) == softa->ipf_auth_start) { softa->ipf_auth_stats.fas_nospace++; RWLOCK_EXIT(&softa->ipf_authlk); return 0; } softa->ipf_auth_stats.fas_added++; softa->ipf_auth_used++; i = softa->ipf_auth_end++; if (softa->ipf_auth_end == softa->ipf_auth_size) softa->ipf_auth_end = 0; fra = softa->ipf_auth + i; fra->fra_index = i; if (fin->fin_fr != NULL) fra->fra_pass = fin->fin_fr->fr_flags; else fra->fra_pass = 0; fra->fra_age = softa->ipf_auth_defaultage; bcopy((char *)fin, (char *)&fra->fra_info, sizeof(*fin)); fra->fra_flx = fra->fra_info.fin_flx & (FI_STATE|FI_NATED); fra->fra_info.fin_flx &= ~(FI_STATE|FI_NATED); #if !defined(sparc) && !defined(m68k) /* * No need to copyback here as we want to undo the changes, not keep * them. */ ip = fin->fin_ip; # if SOLARIS && defined(_KERNEL) if ((ip == (ip_t *)m->b_rptr) && (fin->fin_v == 4)) # endif { register u_short bo; bo = ip->ip_len; ip->ip_len = htons(bo); bo = ip->ip_off; ip->ip_off = htons(bo); } #endif #if SOLARIS && defined(_KERNEL) COPYIFNAME(fin->fin_v, fin->fin_ifp, fra->fra_info.fin_ifname); m->b_rptr -= qpi->qpi_off; fra->fra_q = qpi->qpi_q; /* The queue can disappear! */ fra->fra_m = *fin->fin_mp; fra->fra_info.fin_mp = &fra->fra_m; softa->ipf_auth_pkts[i] = *(mblk_t **)fin->fin_mp; RWLOCK_EXIT(&softa->ipf_authlk); cv_signal(&softa->ipf_auth_wait); pollwakeup(&softc->ipf_poll_head[IPL_LOGAUTH], POLLIN|POLLRDNORM); #else softa->ipf_auth_pkts[i] = m; RWLOCK_EXIT(&softa->ipf_authlk); WAKEUP(&softa->ipf_auth_next, 0); #endif return 1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_ioctl */ /* Returns: int - 0 == success, else error */ /* Parameters: data(IO) - pointer to ioctl data */ /* cmd(I) - ioctl command */ /* mode(I) - mode flags associated with open descriptor */ /* uid(I) - uid associatd with application making the call */ /* ctx(I) - pointer for context */ /* */ /* This function handles all of the ioctls recognised by the auth component */ /* in IPFilter - ie ioctls called on an open fd for /dev/ipf_auth */ /* ------------------------------------------------------------------------ */ int -ipf_auth_ioctl(softc, data, cmd, mode, uid, ctx) - ipf_main_softc_t *softc; - caddr_t data; - ioctlcmd_t cmd; - int mode, uid; - void *ctx; +ipf_auth_ioctl(ipf_main_softc_t *softc, caddr_t data, ioctlcmd_t cmd, + int mode, int uid, void *ctx) { ipf_auth_softc_t *softa = softc->ipf_auth_soft; int error = 0, i; SPL_INT(s); switch (cmd) { case SIOCGENITER : { ipftoken_t *token; ipfgeniter_t iter; ipfobj_t obj; error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); if (error != 0) break; SPL_SCHED(s); token = ipf_token_find(softc, IPFGENITER_AUTH, uid, ctx); if (token != NULL) error = ipf_auth_geniter(softc, token, &iter, &obj); else { WRITE_ENTER(&softc->ipf_tokens); ipf_token_deref(softc, token); RWLOCK_EXIT(&softc->ipf_tokens); IPFERROR(10001); error = ESRCH; } SPL_X(s); break; } case SIOCADAFR : case SIOCRMAFR : if (!(mode & FWRITE)) { IPFERROR(10002); error = EPERM; } else error = frrequest(softc, IPL_LOGAUTH, cmd, data, softc->ipf_active, 1); break; case SIOCSTLCK : if (!(mode & FWRITE)) { IPFERROR(10003); error = EPERM; } else { error = ipf_lock(data, &softa->ipf_auth_lock); } break; case SIOCATHST: softa->ipf_auth_stats.fas_faelist = softa->ipf_auth_entries; error = ipf_outobj(softc, data, &softa->ipf_auth_stats, IPFOBJ_AUTHSTAT); break; case SIOCIPFFL: SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); i = ipf_auth_flush(softa); RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); error = BCOPYOUT(&i, data, sizeof(i)); if (error != 0) { IPFERROR(10004); error = EFAULT; } break; case SIOCAUTHW: error = ipf_auth_wait(softc, softa, data); break; case SIOCAUTHR: error = ipf_auth_reply(softc, softa, data); break; default : IPFERROR(10005); error = EINVAL; break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_expire */ /* Returns: None */ /* Parameters: None */ /* */ /* Slowly expire held auth records. Timeouts are set in expectation of */ /* this being called twice per second. */ /* ------------------------------------------------------------------------ */ void -ipf_auth_expire(softc) - ipf_main_softc_t *softc; +ipf_auth_expire(ipf_main_softc_t *softc) { ipf_auth_softc_t *softa = softc->ipf_auth_soft; frauthent_t *fae, **faep; frentry_t *fr, **frp; frauth_t *fra; mb_t *m; int i; SPL_INT(s); if (softa->ipf_auth_lock) return; SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); for (i = 0, fra = softa->ipf_auth; i < softa->ipf_auth_size; i++, fra++) { fra->fra_age--; if ((fra->fra_age == 0) && (softa->ipf_auth[i].fra_index != -1)) { if ((m = softa->ipf_auth_pkts[i]) != NULL) { FREE_MB_T(m); softa->ipf_auth_pkts[i] = NULL; } else if (softa->ipf_auth[i].fra_index == -2) { softa->ipf_auth_replies--; } softa->ipf_auth[i].fra_index = -1; softa->ipf_auth_stats.fas_expire++; softa->ipf_auth_used--; } } /* * Expire pre-auth rules */ for (faep = &softa->ipf_auth_entries; ((fae = *faep) != NULL); ) { fae->fae_age--; if (fae->fae_age == 0) { ipf_auth_deref(&fae); softa->ipf_auth_stats.fas_expire++; } else faep = &fae->fae_next; } if (softa->ipf_auth_entries != NULL) softa->ipf_auth_ip = &softa->ipf_auth_entries->fae_fr; else softa->ipf_auth_ip = NULL; for (frp = &softa->ipf_auth_rules; ((fr = *frp) != NULL); ) { if (fr->fr_ref == 1) { *frp = fr->fr_next; MUTEX_DESTROY(&fr->fr_lock); KFREE(fr); } else frp = &fr->fr_next; } RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_precmd */ /* Returns: int - 0 == success, else error */ /* Parameters: cmd(I) - ioctl command for rule */ /* fr(I) - pointer to ipf rule */ /* fptr(I) - pointer to caller's 'fr' */ /* */ /* ------------------------------------------------------------------------ */ int -ipf_auth_precmd(softc, cmd, fr, frptr) - ipf_main_softc_t *softc; - ioctlcmd_t cmd; - frentry_t *fr, **frptr; +ipf_auth_precmd(ipf_main_softc_t *softc, ioctlcmd_t cmd, frentry_t *fr, + frentry_t **frptr) { ipf_auth_softc_t *softa = softc->ipf_auth_soft; frauthent_t *fae, **faep; int error = 0; SPL_INT(s); if ((cmd != SIOCADAFR) && (cmd != SIOCRMAFR)) { IPFERROR(10006); return EIO; } for (faep = &softa->ipf_auth_entries; ((fae = *faep) != NULL); ) { if (&fae->fae_fr == fr) break; else faep = &fae->fae_next; } if (cmd == (ioctlcmd_t)SIOCRMAFR) { if (fr == NULL || frptr == NULL) { IPFERROR(10007); error = EINVAL; } else if (fae == NULL) { IPFERROR(10008); error = ESRCH; } else { SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); *faep = fae->fae_next; if (softa->ipf_auth_ip == &fae->fae_fr) softa->ipf_auth_ip = softa->ipf_auth_entries ? &softa->ipf_auth_entries->fae_fr : NULL; RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); KFREE(fae); } } else if (fr != NULL && frptr != NULL) { KMALLOC(fae, frauthent_t *); if (fae != NULL) { bcopy((char *)fr, (char *)&fae->fae_fr, sizeof(*fr)); SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); fae->fae_age = softa->ipf_auth_defaultage; fae->fae_fr.fr_hits = 0; fae->fae_fr.fr_next = *frptr; fae->fae_ref = 1; *frptr = &fae->fae_fr; fae->fae_next = *faep; *faep = fae; softa->ipf_auth_ip = &softa->ipf_auth_entries->fae_fr; RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); } else { IPFERROR(10009); error = ENOMEM; } } else { IPFERROR(10010); error = EINVAL; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_flush */ /* Returns: int - number of auth entries flushed */ /* Parameters: None */ /* Locks: WRITE(ipf_authlk) */ /* */ /* This function flushs the ipf_auth_pkts array of any packet data with */ /* references still there. */ /* It is expected that the caller has already acquired the correct locks or */ /* set the priority level correctly for this to block out other code paths */ /* into these data structures. */ /* ------------------------------------------------------------------------ */ static int -ipf_auth_flush(arg) - void *arg; +ipf_auth_flush(void *arg) { ipf_auth_softc_t *softa = arg; int i, num_flushed; mb_t *m; if (softa->ipf_auth_lock) return -1; num_flushed = 0; for (i = 0 ; i < softa->ipf_auth_size; i++) { if (softa->ipf_auth[i].fra_index != -1) { m = softa->ipf_auth_pkts[i]; if (m != NULL) { FREE_MB_T(m); softa->ipf_auth_pkts[i] = NULL; } softa->ipf_auth[i].fra_index = -1; /* perhaps add & use a flush counter inst.*/ softa->ipf_auth_stats.fas_expire++; num_flushed++; } } softa->ipf_auth_start = 0; softa->ipf_auth_end = 0; softa->ipf_auth_next = 0; softa->ipf_auth_used = 0; softa->ipf_auth_replies = 0; return num_flushed; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_waiting */ /* Returns: int - number of packets in the auth queue */ /* Parameters: None */ /* */ /* Simple truth check to see if there are any packets waiting in the auth */ /* queue. */ /* ------------------------------------------------------------------------ */ int -ipf_auth_waiting(softc) - ipf_main_softc_t *softc; +ipf_auth_waiting(ipf_main_softc_t *softc) { ipf_auth_softc_t *softa = softc->ipf_auth_soft; return (softa->ipf_auth_used != 0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_geniter */ /* Returns: int - 0 == success, else error */ /* Parameters: token(I) - pointer to ipftoken structure */ /* itp(I) - pointer to ipfgeniter structure */ /* objp(I) - pointer to ipf object destription */ /* */ /* Iterate through the list of entries in the auth queue list. */ /* objp is used here to get the location of where to do the copy out to. */ /* Stomping over various fields with new information will not harm anything */ /* ------------------------------------------------------------------------ */ static int -ipf_auth_geniter(softc, token, itp, objp) - ipf_main_softc_t *softc; - ipftoken_t *token; - ipfgeniter_t *itp; - ipfobj_t *objp; +ipf_auth_geniter(ipf_main_softc_t *softc, ipftoken_t *token, + ipfgeniter_t *itp, ipfobj_t *objp) { ipf_auth_softc_t *softa = softc->ipf_auth_soft; frauthent_t *fae, *next, zero; int error; if (itp->igi_data == NULL) { IPFERROR(10011); return EFAULT; } if (itp->igi_type != IPFGENITER_AUTH) { IPFERROR(10012); return EINVAL; } objp->ipfo_type = IPFOBJ_FRAUTH; objp->ipfo_ptr = itp->igi_data; objp->ipfo_size = sizeof(frauth_t); READ_ENTER(&softa->ipf_authlk); fae = token->ipt_data; if (fae == NULL) { next = softa->ipf_auth_entries; } else { next = fae->fae_next; } /* * If we found an auth entry to use, bump its reference count * so that it can be used for is_next when we come back. */ if (next != NULL) { ATOMIC_INC(next->fae_ref); token->ipt_data = next; } else { bzero(&zero, sizeof(zero)); next = &zero; token->ipt_data = NULL; } RWLOCK_EXIT(&softa->ipf_authlk); error = ipf_outobjk(softc, objp, next); if (fae != NULL) ipf_auth_deref_unlocked(softa, &fae); if (next->fae_next == NULL) ipf_token_mark_complete(token); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_deref_unlocked */ /* Returns: None */ /* Parameters: faep(IO) - pointer to caller's frauthent_t pointer */ /* */ /* Wrapper for ipf_auth_deref for when a write lock on ipf_authlk is not */ /* held. */ /* ------------------------------------------------------------------------ */ static void -ipf_auth_deref_unlocked(softa, faep) - ipf_auth_softc_t *softa; - frauthent_t **faep; +ipf_auth_deref_unlocked(ipf_auth_softc_t *softa, frauthent_t **faep) { WRITE_ENTER(&softa->ipf_authlk); ipf_auth_deref(faep); RWLOCK_EXIT(&softa->ipf_authlk); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_deref */ /* Returns: None */ /* Parameters: faep(IO) - pointer to caller's frauthent_t pointer */ /* Locks: WRITE(ipf_authlk) */ /* */ /* This function unconditionally sets the pointer in the caller to NULL, */ /* to make it clear that it should no longer use that pointer, and drops */ /* the reference count on the structure by 1. If it reaches 0, free it up. */ /* ------------------------------------------------------------------------ */ static void -ipf_auth_deref(faep) - frauthent_t **faep; +ipf_auth_deref(frauthent_t **faep) { frauthent_t *fae; fae = *faep; *faep = NULL; fae->fae_ref--; if (fae->fae_ref == 0) { KFREE(fae); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_wait_pkt */ /* Returns: int - 0 == success, else error */ /* Parameters: data(I) - pointer to data from ioctl call */ /* */ /* This function is called when an application is waiting for a packet to */ /* match an "auth" rule by issuing an SIOCAUTHW ioctl. If there is already */ /* a packet waiting on the queue then we will return that _one_ immediately.*/ /* If there are no packets present in the queue (ipf_auth_pkts) then we go */ /* to sleep. */ /* ------------------------------------------------------------------------ */ static int -ipf_auth_wait(softc, softa, data) - ipf_main_softc_t *softc; - ipf_auth_softc_t *softa; - char *data; +ipf_auth_wait(ipf_main_softc_t *softc, ipf_auth_softc_t *softa, char *data) { frauth_t auth, *au = &auth; int error, len, i; mb_t *m; char *t; SPL_INT(s); ipf_auth_ioctlloop: error = ipf_inobj(softc, data, NULL, au, IPFOBJ_FRAUTH); if (error != 0) return error; /* * XXX Locks are held below over calls to copyout...a better * solution needs to be found so this isn't necessary. The situation * we are trying to guard against here is an error in the copyout * steps should not cause the packet to "disappear" from the queue. */ SPL_NET(s); READ_ENTER(&softa->ipf_authlk); /* * If ipf_auth_next is not equal to ipf_auth_end it will be because * there is a packet waiting to be delt with in the ipf_auth_pkts * array. We copy as much of that out to user space as requested. */ if (softa->ipf_auth_used > 0) { while (softa->ipf_auth_pkts[softa->ipf_auth_next] == NULL) { softa->ipf_auth_next++; if (softa->ipf_auth_next == softa->ipf_auth_size) softa->ipf_auth_next = 0; } error = ipf_outobj(softc, data, &softa->ipf_auth[softa->ipf_auth_next], IPFOBJ_FRAUTH); if (error != 0) { RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); return error; } if (auth.fra_len != 0 && auth.fra_buf != NULL) { /* * Copy packet contents out to user space if * requested. Bail on an error. */ m = softa->ipf_auth_pkts[softa->ipf_auth_next]; len = MSGDSIZE(m); if (len > auth.fra_len) len = auth.fra_len; auth.fra_len = len; for (t = auth.fra_buf; m && (len > 0); ) { i = MIN(M_LEN(m), len); error = copyoutptr(softc, MTOD(m, char *), &t, i); len -= i; t += i; if (error != 0) { RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); return error; } m = m->m_next; } } RWLOCK_EXIT(&softa->ipf_authlk); SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); softa->ipf_auth_next++; if (softa->ipf_auth_next == softa->ipf_auth_size) softa->ipf_auth_next = 0; RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); return 0; } RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); MUTEX_ENTER(&softa->ipf_auth_mx); #ifdef _KERNEL # if SOLARIS error = 0; if (!cv_wait_sig(&softa->ipf_auth_wait, &softa->ipf_auth_mx.ipf_lk)) { IPFERROR(10014); error = EINTR; } # else /* SOLARIS */ error = SLEEP(&softa->ipf_auth_next, "ipf_auth_next"); # endif /* SOLARIS */ #endif MUTEX_EXIT(&softa->ipf_auth_mx); if (error == 0) goto ipf_auth_ioctlloop; return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_reply */ /* Returns: int - 0 == success, else error */ /* Parameters: data(I) - pointer to data from ioctl call */ /* */ /* This function is called by an application when it wants to return a */ /* decision on a packet using the SIOCAUTHR ioctl. This is after it has */ /* received information using an SIOCAUTHW. The decision returned in the */ /* form of flags, the same as those used in each rule. */ /* ------------------------------------------------------------------------ */ static int -ipf_auth_reply(softc, softa, data) - ipf_main_softc_t *softc; - ipf_auth_softc_t *softa; - char *data; +ipf_auth_reply(ipf_main_softc_t *softc, ipf_auth_softc_t *softa, char *data) { frauth_t auth, *au = &auth, *fra; fr_info_t fin; int error, i; mb_t *m; SPL_INT(s); error = ipf_inobj(softc, data, NULL, &auth, IPFOBJ_FRAUTH); if (error != 0) return error; SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); i = au->fra_index; fra = softa->ipf_auth + i; error = 0; /* * Check the validity of the information being returned with two simple * checks. First, the auth index value should be within the size of * the array and second the packet id being returned should also match. */ if ((i < 0) || (i >= softa->ipf_auth_size)) { RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); IPFERROR(10015); return ESRCH; } if (fra->fra_info.fin_id != au->fra_info.fin_id) { RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); IPFERROR(10019); return ESRCH; } m = softa->ipf_auth_pkts[i]; fra->fra_index = -2; fra->fra_pass = au->fra_pass; softa->ipf_auth_pkts[i] = NULL; softa->ipf_auth_replies++; bcopy(&fra->fra_info, &fin, sizeof(fin)); RWLOCK_EXIT(&softa->ipf_authlk); /* * Re-insert the packet back into the packet stream flowing through * the kernel in a manner that will mean IPFilter sees the packet * again. This is not the same as is done with fastroute, * deliberately, as we want to resume the normal packet processing * path for it. */ #ifdef _KERNEL if ((m != NULL) && (au->fra_info.fin_out != 0)) { error = ipf_inject(&fin, m); if (error != 0) { IPFERROR(10016); error = ENOBUFS; softa->ipf_auth_stats.fas_sendfail++; } else { softa->ipf_auth_stats.fas_sendok++; } } else if (m) { error = ipf_inject(&fin, m); if (error != 0) { IPFERROR(10017); error = ENOBUFS; softa->ipf_auth_stats.fas_quefail++; } else { softa->ipf_auth_stats.fas_queok++; } } else { IPFERROR(10018); error = EINVAL; } /* * If we experience an error which will result in the packet * not being processed, make sure we advance to the next one. */ if (error == ENOBUFS) { WRITE_ENTER(&softa->ipf_authlk); softa->ipf_auth_used--; fra->fra_index = -1; fra->fra_pass = 0; if (i == softa->ipf_auth_start) { while (fra->fra_index == -1) { i++; if (i == softa->ipf_auth_size) i = 0; softa->ipf_auth_start = i; if (i == softa->ipf_auth_end) break; } if (softa->ipf_auth_start == softa->ipf_auth_end) { softa->ipf_auth_next = 0; softa->ipf_auth_start = 0; softa->ipf_auth_end = 0; } } RWLOCK_EXIT(&softa->ipf_authlk); } #endif /* _KERNEL */ SPL_X(s); return 0; } u_32_t -ipf_auth_pre_scanlist(softc, fin, pass) - ipf_main_softc_t *softc; - fr_info_t *fin; - u_32_t pass; +ipf_auth_pre_scanlist(ipf_main_softc_t *softc, fr_info_t *fin, u_32_t pass) { ipf_auth_softc_t *softa = softc->ipf_auth_soft; if (softa->ipf_auth_ip != NULL) return ipf_scanlist(fin, softc->ipf_pass); return pass; } frentry_t ** -ipf_auth_rulehead(softc) - ipf_main_softc_t *softc; +ipf_auth_rulehead(ipf_main_softc_t *softc) { ipf_auth_softc_t *softa = softc->ipf_auth_soft; return &softa->ipf_auth_ip; } diff --git a/sys/netpfil/ipfilter/netinet/ip_dns_pxy.c b/sys/netpfil/ipfilter/netinet/ip_dns_pxy.c index 22871a7ba02b..209dc3112a22 100644 --- a/sys/netpfil/ipfilter/netinet/ip_dns_pxy.c +++ b/sys/netpfil/ipfilter/netinet/ip_dns_pxy.c @@ -1,401 +1,373 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id: ip_dns_pxy.c,v 1.1.2.10 2012/07/22 08:04:23 darren_r Exp $ */ #define IPF_DNS_PROXY /* * map ... proxy port dns/udp 53 { block .cnn.com; } */ typedef struct ipf_dns_filter { struct ipf_dns_filter *idns_next; char *idns_name; int idns_namelen; int idns_pass; } ipf_dns_filter_t; typedef struct ipf_dns_softc_s { ipf_dns_filter_t *ipf_p_dns_list; ipfrwlock_t ipf_p_dns_rwlock; u_long ipf_p_dns_compress; u_long ipf_p_dns_toolong; u_long ipf_p_dns_nospace; } ipf_dns_softc_t; int ipf_p_dns_allow_query(ipf_dns_softc_t *, dnsinfo_t *); int ipf_p_dns_ctl(ipf_main_softc_t *, void *, ap_ctl_t *); void ipf_p_dns_del(ipf_main_softc_t *, ap_session_t *); int ipf_p_dns_get_name(ipf_dns_softc_t *, char *, int, char *, int); int ipf_p_dns_inout(void *, fr_info_t *, ap_session_t *, nat_t *); int ipf_p_dns_match(fr_info_t *, ap_session_t *, nat_t *); int ipf_p_dns_match_names(ipf_dns_filter_t *, char *, int); int ipf_p_dns_new(void *, fr_info_t *, ap_session_t *, nat_t *); void *ipf_p_dns_soft_create(ipf_main_softc_t *); void ipf_p_dns_soft_destroy(ipf_main_softc_t *, void *); typedef struct { u_char dns_id[2]; u_short dns_ctlword; u_short dns_qdcount; u_short dns_ancount; u_short dns_nscount; u_short dns_arcount; } ipf_dns_hdr_t; #define DNS_QR(x) ((ntohs(x) & 0x8000) >> 15) #define DNS_OPCODE(x) ((ntohs(x) & 0x7800) >> 11) #define DNS_AA(x) ((ntohs(x) & 0x0400) >> 10) #define DNS_TC(x) ((ntohs(x) & 0x0200) >> 9) #define DNS_RD(x) ((ntohs(x) & 0x0100) >> 8) #define DNS_RA(x) ((ntohs(x) & 0x0080) >> 7) #define DNS_Z(x) ((ntohs(x) & 0x0070) >> 4) #define DNS_RCODE(x) ((ntohs(x) & 0x000f) >> 0) void * -ipf_p_dns_soft_create(softc) - ipf_main_softc_t *softc; +ipf_p_dns_soft_create(ipf_main_softc_t *softc) { ipf_dns_softc_t *softd; KMALLOC(softd, ipf_dns_softc_t *); if (softd == NULL) return NULL; bzero((char *)softd, sizeof(*softd)); RWLOCK_INIT(&softd->ipf_p_dns_rwlock, "ipf dns rwlock"); return softd; } void -ipf_p_dns_soft_destroy(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_p_dns_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_dns_softc_t *softd = arg; ipf_dns_filter_t *idns; while ((idns = softd->ipf_p_dns_list) != NULL) { KFREES(idns->idns_name, idns->idns_namelen); idns->idns_name = NULL; idns->idns_namelen = 0; softd->ipf_p_dns_list = idns->idns_next; KFREE(idns); } RW_DESTROY(&softd->ipf_p_dns_rwlock); KFREE(softd); } int -ipf_p_dns_ctl(softc, arg, ctl) - ipf_main_softc_t *softc; - void *arg; - ap_ctl_t *ctl; +ipf_p_dns_ctl(ipf_main_softc_t *softc, void *arg, ap_ctl_t *ctl) { ipf_dns_softc_t *softd = arg; ipf_dns_filter_t *tmp, *idns, **idnsp; int error = 0; /* * To make locking easier. */ KMALLOC(tmp, ipf_dns_filter_t *); WRITE_ENTER(&softd->ipf_p_dns_rwlock); for (idnsp = &softd->ipf_p_dns_list; (idns = *idnsp) != NULL; idnsp = &idns->idns_next) { if (idns->idns_namelen != ctl->apc_dsize) continue; if (!strncmp(ctl->apc_data, idns->idns_name, idns->idns_namelen)) break; } switch (ctl->apc_cmd) { case APC_CMD_DEL : if (idns == NULL) { IPFERROR(80006); error = ESRCH; break; } *idnsp = idns->idns_next; idns->idns_next = NULL; KFREES(idns->idns_name, idns->idns_namelen); idns->idns_name = NULL; idns->idns_namelen = 0; KFREE(idns); break; case APC_CMD_ADD : if (idns != NULL) { IPFERROR(80007); error = EEXIST; break; } if (tmp == NULL) { IPFERROR(80008); error = ENOMEM; break; } idns = tmp; tmp = NULL; idns->idns_namelen = ctl->apc_dsize; idns->idns_name = ctl->apc_data; idns->idns_pass = ctl->apc_arg; idns->idns_next = NULL; *idnsp = idns; ctl->apc_data = NULL; ctl->apc_dsize = 0; break; default : IPFERROR(80009); error = EINVAL; break; } RWLOCK_EXIT(&softd->ipf_p_dns_rwlock); if (tmp != NULL) { KFREE(tmp); tmp = NULL; } return error; } /* ARGSUSED */ int -ipf_p_dns_new(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_dns_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { dnsinfo_t *di; int dlen; if (fin->fin_v != 4) return -1; dlen = fin->fin_dlen - sizeof(udphdr_t); if (dlen < sizeof(ipf_dns_hdr_t)) { /* * No real DNS packet is smaller than that. */ return -1; } aps->aps_psiz = sizeof(dnsinfo_t); KMALLOCS(di, dnsinfo_t *, sizeof(dnsinfo_t)); if (di == NULL) { printf("ipf_dns_new:KMALLOCS(%d) failed\n", sizeof(*di)); return -1; } MUTEX_INIT(&di->dnsi_lock, "dns lock"); aps->aps_data = di; dlen = fin->fin_dlen - sizeof(udphdr_t); COPYDATA(fin->fin_m, fin->fin_hlen + sizeof(udphdr_t), MIN(dlen, sizeof(di->dnsi_buffer)), di->dnsi_buffer); di->dnsi_id = (di->dnsi_buffer[0] << 8) | di->dnsi_buffer[1]; return 0; } /* ARGSUSED */ void -ipf_p_dns_del(softc, aps) - ipf_main_softc_t *softc; - ap_session_t *aps; +ipf_p_dns_del(ipf_main_softc_t *softc, ap_session_t *aps) { #ifdef USE_MUTEXES dnsinfo_t *di = aps->aps_data; MUTEX_DESTROY(&di->dnsi_lock); #endif KFREES(aps->aps_data, aps->aps_psiz); aps->aps_data = NULL; aps->aps_psiz = 0; } /* * Tries to match the base string (in our ACL) with the query from a packet. */ int -ipf_p_dns_match_names(idns, query, qlen) - ipf_dns_filter_t *idns; - char *query; - int qlen; +ipf_p_dns_match_names(ipf_dns_filter_t *idns, char *query, int qlen) { int blen; char *base; blen = idns->idns_namelen; base = idns->idns_name; if (blen > qlen) return 1; if (blen == qlen) return strncasecmp(base, query, qlen); /* * If the base string string is shorter than the query, allow the * tail of the base to match the same length tail of the query *if*: * - the base string starts with a '*' (*cnn.com) * - the base string represents a domain (.cnn.com) * as otherwise it would not be possible to block just "cnn.com" * without also impacting "foocnn.com", etc. */ if (*base == '*') { base++; blen--; } else if (*base != '.') return 1; return strncasecmp(base, query + qlen - blen, blen); } int -ipf_p_dns_get_name(softd, start, len, buffer, buflen) - ipf_dns_softc_t *softd; - char *start; - int len; - char *buffer; - int buflen; +ipf_p_dns_get_name(ipf_dns_softc_t *softd, char *start, int len, + char *buffer, int buflen) { char *s, *t, clen; int slen, blen; s = start; t = buffer; slen = len; blen = buflen - 1; /* Always make room for trailing \0 */ while (*s != '\0') { clen = *s; if ((clen & 0xc0) == 0xc0) { /* Doesn't do compression */ softd->ipf_p_dns_compress++; return 0; } if (clen > slen) { softd->ipf_p_dns_toolong++; return 0; /* Does the name run off the end? */ } if ((clen + 1) > blen) { softd->ipf_p_dns_nospace++; return 0; /* Enough room for name+.? */ } s++; bcopy(s, t, clen); t += clen; s += clen; *t++ = '.'; slen -= clen; blen -= (clen + 1); } *(t - 1) = '\0'; return s - start; } int -ipf_p_dns_allow_query(softd, dnsi) - ipf_dns_softc_t *softd; - dnsinfo_t *dnsi; +ipf_p_dns_allow_query(ipf_dns_softc_t *softd, dnsinfo_t *dnsi) { ipf_dns_filter_t *idns; int len; len = strlen(dnsi->dnsi_buffer); for (idns = softd->ipf_p_dns_list; idns != NULL; idns = idns->idns_next) if (ipf_p_dns_match_names(idns, dnsi->dnsi_buffer, len) == 0) return idns->idns_pass; return 0; } /* ARGSUSED */ int -ipf_p_dns_inout(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_dns_inout(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { ipf_dns_softc_t *softd = arg; ipf_dns_hdr_t *dns; dnsinfo_t *di; char *data; int dlen, q, rc = 0; if (fin->fin_dlen < sizeof(*dns)) return APR_ERR(1); dns = (ipf_dns_hdr_t *)((char *)fin->fin_dp + sizeof(udphdr_t)); q = dns->dns_qdcount; data = (char *)(dns + 1); dlen = fin->fin_dlen - sizeof(*dns) - sizeof(udphdr_t); di = aps->aps_data; READ_ENTER(&softd->ipf_p_dns_rwlock); MUTEX_ENTER(&di->dnsi_lock); for (; (dlen > 0) && (q > 0); q--) { int len; len = ipf_p_dns_get_name(softd, data, dlen, di->dnsi_buffer, sizeof(di->dnsi_buffer)); if (len == 0) { rc = 1; break; } rc = ipf_p_dns_allow_query(softd, di); if (rc != 0) break; data += len; dlen -= len; } MUTEX_EXIT(&di->dnsi_lock); RWLOCK_EXIT(&softd->ipf_p_dns_rwlock); return APR_ERR(rc); } /* ARGSUSED */ int -ipf_p_dns_match(fin, aps, nat) - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_dns_match(fr_info_t *fin, ap_session_t *aps, nat_t *nat) { dnsinfo_t *di = aps->aps_data; ipf_dns_hdr_t *dnh; if ((fin->fin_dlen < sizeof(u_short)) || (fin->fin_flx & FI_FRAG)) return -1; dnh = (ipf_dns_hdr_t *)((char *)fin->fin_dp + sizeof(udphdr_t)); if (((dnh->dns_id[0] << 8) | dnh->dns_id[1]) != di->dnsi_id) return -1; return 0; } diff --git a/sys/netpfil/ipfilter/netinet/ip_dstlist.c b/sys/netpfil/ipfilter/netinet/ip_dstlist.c index af584d8e4d72..f0b6444f9b27 100644 --- a/sys/netpfil/ipfilter/netinet/ip_dstlist.c +++ b/sys/netpfil/ipfilter/netinet/ip_dstlist.c @@ -1,1344 +1,1282 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #if !defined(_KERNEL) && !defined(__KERNEL__) # include # include # include # define _KERNEL # include # undef _KERNEL #else # include # if defined(NetBSD) && (__NetBSD_Version__ >= 104000000) # include # endif #endif #include #include #include #if defined(_KERNEL) && !defined(__SVR4) # include #endif #if defined(__SVR4) # include # include # ifdef _KERNEL # include # endif # include # include #endif #if defined(__FreeBSD__) # include #endif #include #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_lookup.h" #include "netinet/ip_dstlist.h" /* END OF INCLUDES */ #ifdef HAS_SYS_MD5_H # include #else # include "md5.h" #endif #if !defined(lint) static const char rcsid[] = "@(#)$Id: ip_dstlist.c,v 1.13.2.12 2012/07/20 08:40:19 darren_r Exp $"; #endif typedef struct ipf_dstl_softc_s { ippool_dst_t *dstlist[LOOKUP_POOL_SZ]; ippool_dst_t **tails[LOOKUP_POOL_SZ]; ipf_dstl_stat_t stats; } ipf_dstl_softc_t; static void *ipf_dstlist_soft_create(ipf_main_softc_t *); static void ipf_dstlist_soft_destroy(ipf_main_softc_t *, void *); static int ipf_dstlist_soft_init(ipf_main_softc_t *, void *); static void ipf_dstlist_soft_fini(ipf_main_softc_t *, void *); static int ipf_dstlist_addr_find(ipf_main_softc_t *, void *, int, void *, u_int); static size_t ipf_dstlist_flush(ipf_main_softc_t *, void *, iplookupflush_t *); static int ipf_dstlist_iter_deref(ipf_main_softc_t *, void *, int, int, void *); static int ipf_dstlist_iter_next(ipf_main_softc_t *, void *, ipftoken_t *, ipflookupiter_t *); static int ipf_dstlist_node_add(ipf_main_softc_t *, void *, iplookupop_t *, int); static int ipf_dstlist_node_del(ipf_main_softc_t *, void *, iplookupop_t *, int); static int ipf_dstlist_stats_get(ipf_main_softc_t *, void *, iplookupop_t *); static int ipf_dstlist_table_add(ipf_main_softc_t *, void *, iplookupop_t *); static int ipf_dstlist_table_del(ipf_main_softc_t *, void *, iplookupop_t *); static int ipf_dstlist_table_deref(ipf_main_softc_t *, void *, void *); static void *ipf_dstlist_table_find(void *, int, char *); static void ipf_dstlist_table_free(ipf_dstl_softc_t *, ippool_dst_t *); static void ipf_dstlist_table_remove(ipf_main_softc_t *, ipf_dstl_softc_t *, ippool_dst_t *); static void ipf_dstlist_table_clearnodes(ipf_dstl_softc_t *, ippool_dst_t *); static ipf_dstnode_t *ipf_dstlist_select(fr_info_t *, ippool_dst_t *); static void *ipf_dstlist_select_ref(void *, int, char *); static void ipf_dstlist_node_free(ipf_dstl_softc_t *, ippool_dst_t *, ipf_dstnode_t *); static int ipf_dstlist_node_deref(void *, ipf_dstnode_t *); static void ipf_dstlist_expire(ipf_main_softc_t *, void *); static void ipf_dstlist_sync(ipf_main_softc_t *, void *); ipf_lookup_t ipf_dstlist_backend = { IPLT_DSTLIST, ipf_dstlist_soft_create, ipf_dstlist_soft_destroy, ipf_dstlist_soft_init, ipf_dstlist_soft_fini, ipf_dstlist_addr_find, ipf_dstlist_flush, ipf_dstlist_iter_deref, ipf_dstlist_iter_next, ipf_dstlist_node_add, ipf_dstlist_node_del, ipf_dstlist_stats_get, ipf_dstlist_table_add, ipf_dstlist_table_del, ipf_dstlist_table_deref, ipf_dstlist_table_find, ipf_dstlist_select_ref, ipf_dstlist_select_node, ipf_dstlist_expire, ipf_dstlist_sync }; /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_soft_create */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Allocating a chunk of memory filled with 0's is enough for the current */ /* soft context used with destination lists. */ /* ------------------------------------------------------------------------ */ static void * -ipf_dstlist_soft_create(softc) - ipf_main_softc_t *softc; +ipf_dstlist_soft_create(ipf_main_softc_t *softc) { ipf_dstl_softc_t *softd; int i; KMALLOC(softd, ipf_dstl_softc_t *); if (softd == NULL) { IPFERROR(120028); return NULL; } bzero((char *)softd, sizeof(*softd)); for (i = 0; i <= IPL_LOGMAX; i++) softd->tails[i] = &softd->dstlist[i]; return softd; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* For destination lists, the only thing we have to do when destroying the */ /* soft context is free it! */ /* ------------------------------------------------------------------------ */ static void -ipf_dstlist_soft_destroy(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_dstlist_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_dstl_softc_t *softd = arg; KFREE(softd); } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_soft_init */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* There is currently no soft context for destination list management. */ /* ------------------------------------------------------------------------ */ static int -ipf_dstlist_soft_init(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_dstlist_soft_init(ipf_main_softc_t *softc, void *arg) { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_soft_fini */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* There is currently no soft context for destination list management. */ /* ------------------------------------------------------------------------ */ static void -ipf_dstlist_soft_fini(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_dstlist_soft_fini(ipf_main_softc_t *softc, void *arg) { ipf_dstl_softc_t *softd = arg; int i; for (i = -1; i <= IPL_LOGMAX; i++) { while (softd->dstlist[i + 1] != NULL) { ipf_dstlist_table_remove(softc, softd, softd->dstlist[i + 1]); } } ASSERT(softd->stats.ipls_numderefnodes == 0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_addr_find */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg1(I) - pointer to local context to use */ /* arg2(I) - pointer to local context to use */ /* arg3(I) - pointer to local context to use */ /* arg4(I) - pointer to local context to use */ /* */ /* There is currently no such thing as searching a destination list for an */ /* address so this function becomes a no-op. Its presence is required as */ /* ipf_lookup_res_name() stores the "addr_find" function pointer in the */ /* pointer passed in to it as funcptr, although it could be a generic null- */ /* op function rather than a specific one. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ static int -ipf_dstlist_addr_find(softc, arg1, arg2, arg3, arg4) - ipf_main_softc_t *softc; - void *arg1, *arg3; - int arg2; - u_int arg4; +ipf_dstlist_addr_find(ipf_main_softc_t *softc, void *arg1, int arg2, + void *arg3, u_int arg4) { return -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_flush */ /* Returns: int - number of objects deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* fop(I) - pointer to lookup flush operation data */ /* */ /* Flush all of the destination tables that match the data passed in with */ /* the iplookupflush_t. There are two ways to match objects: the device for */ /* which they are to be used with and their name. */ /* ------------------------------------------------------------------------ */ static size_t -ipf_dstlist_flush(softc, arg, fop) - ipf_main_softc_t *softc; - void *arg; - iplookupflush_t *fop; +ipf_dstlist_flush(ipf_main_softc_t *softc, void *arg, iplookupflush_t *fop) { ipf_dstl_softc_t *softd = arg; ippool_dst_t *node, *next; int n, i; for (n = 0, i = -1; i <= IPL_LOGMAX; i++) { if (fop->iplf_unit != IPLT_ALL && fop->iplf_unit != i) continue; for (node = softd->dstlist[i + 1]; node != NULL; node = next) { next = node->ipld_next; if ((*fop->iplf_name != '\0') && strncmp(fop->iplf_name, node->ipld_name, FR_GROUPLEN)) continue; ipf_dstlist_table_remove(softc, softd, node); n++; } } return n; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_iter_deref */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* otype(I) - type of data structure to iterate through */ /* unit(I) - device we are working with */ /* data(I) - address of object in kernel space */ /* */ /* This function is called when the iteration token is being free'd and is */ /* responsible for dropping the reference count of the structure it points */ /* to. */ /* ------------------------------------------------------------------------ */ static int -ipf_dstlist_iter_deref(softc, arg, otype, unit, data) - ipf_main_softc_t *softc; - void *arg; - int otype, unit; - void *data; +ipf_dstlist_iter_deref(ipf_main_softc_t *softc, void *arg, int otype, + int unit, void *data) { if (data == NULL) { IPFERROR(120001); return EINVAL; } if (unit < -1 || unit > IPL_LOGMAX) { IPFERROR(120002); return EINVAL; } switch (otype) { case IPFLOOKUPITER_LIST : ipf_dstlist_table_deref(softc, arg, (ippool_dst_t *)data); break; case IPFLOOKUPITER_NODE : ipf_dstlist_node_deref(arg, (ipf_dstnode_t *)data); break; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_iter_next */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* uid(I) - uid of process doing the ioctl */ /* */ /* This function is responsible for either selecting the next destination */ /* list or node on a destination list to be returned as a user process */ /* iterates through the list of destination lists or nodes. */ /* ------------------------------------------------------------------------ */ static int -ipf_dstlist_iter_next(softc, arg, token, iter) - ipf_main_softc_t *softc; - void *arg; - ipftoken_t *token; - ipflookupiter_t *iter; +ipf_dstlist_iter_next(ipf_main_softc_t *softc, void *arg, + ipftoken_t *token, ipflookupiter_t *iter) { ipf_dstnode_t zn, *nextnode = NULL, *node = NULL; ippool_dst_t zero, *next = NULL, *dsttab = NULL; ipf_dstl_softc_t *softd = arg; int err = 0; void *hint; switch (iter->ili_otype) { case IPFLOOKUPITER_LIST : dsttab = token->ipt_data; if (dsttab == NULL) { next = softd->dstlist[(int)iter->ili_unit + 1]; } else { next = dsttab->ipld_next; } if (next != NULL) { ATOMIC_INC32(next->ipld_ref); token->ipt_data = next; hint = next->ipld_next; } else { bzero((char *)&zero, sizeof(zero)); next = &zero; token->ipt_data = NULL; hint = NULL; } break; case IPFLOOKUPITER_NODE : node = token->ipt_data; if (node == NULL) { dsttab = ipf_dstlist_table_find(arg, iter->ili_unit, iter->ili_name); if (dsttab == NULL) { IPFERROR(120004); err = ESRCH; nextnode = NULL; } else { if (dsttab->ipld_dests == NULL) nextnode = NULL; else nextnode = *dsttab->ipld_dests; dsttab = NULL; } } else { nextnode = node->ipfd_next; } if (nextnode != NULL) { MUTEX_ENTER(&nextnode->ipfd_lock); nextnode->ipfd_ref++; MUTEX_EXIT(&nextnode->ipfd_lock); token->ipt_data = nextnode; hint = nextnode->ipfd_next; } else { bzero((char *)&zn, sizeof(zn)); nextnode = &zn; token->ipt_data = NULL; hint = NULL; } break; default : IPFERROR(120003); err = EINVAL; break; } if (err != 0) return err; switch (iter->ili_otype) { case IPFLOOKUPITER_LIST : if (dsttab != NULL) ipf_dstlist_table_deref(softc, arg, dsttab); err = COPYOUT(next, iter->ili_data, sizeof(*next)); if (err != 0) { IPFERROR(120005); err = EFAULT; } break; case IPFLOOKUPITER_NODE : if (node != NULL) ipf_dstlist_node_deref(arg, node); err = COPYOUT(nextnode, iter->ili_data, sizeof(*nextnode)); if (err != 0) { IPFERROR(120006); err = EFAULT; } break; } if (hint == NULL) ipf_token_mark_complete(token); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_node_add */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* uid(I) - uid of process doing the ioctl */ /* Locks: WRITE(ipf_poolrw) */ /* */ /* Add a new node to a destination list. To do this, we only copy in the */ /* frdest_t structure because that contains the only data required from the */ /* application to create a new node. The frdest_t doesn't contain the name */ /* itself. When loading filter rules, fd_name is a 'pointer' to the name. */ /* In this case, the 'pointer' does not work, instead it is the length of */ /* the name and the name is immediately following the frdest_t structure. */ /* fd_name must include the trailing \0, so it should be strlen(str) + 1. */ /* For simple sanity checking, an upper bound on the size of fd_name is */ /* imposed - 128. */ /* ------------------------------------------------------------------------ */ static int -ipf_dstlist_node_add(softc, arg, op, uid) - ipf_main_softc_t *softc; - void *arg; - iplookupop_t *op; - int uid; +ipf_dstlist_node_add(ipf_main_softc_t *softc, void *arg, + iplookupop_t *op, int uid) { ipf_dstl_softc_t *softd = arg; ipf_dstnode_t *node, **nodes; ippool_dst_t *d; frdest_t dest; int err; if (op->iplo_size < sizeof(frdest_t)) { IPFERROR(120007); return EINVAL; } err = COPYIN(op->iplo_struct, &dest, sizeof(dest)); if (err != 0) { IPFERROR(120009); return EFAULT; } d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); if (d == NULL) { IPFERROR(120010); return ESRCH; } switch (dest.fd_addr.adf_family) { case AF_INET : case AF_INET6 : break; default : IPFERROR(120019); return EINVAL; } if (dest.fd_name < -1 || dest.fd_name > 128) { IPFERROR(120018); return EINVAL; } KMALLOCS(node, ipf_dstnode_t *, sizeof(*node) + dest.fd_name); if (node == NULL) { softd->stats.ipls_nomem++; IPFERROR(120008); return ENOMEM; } bzero((char *)node, sizeof(*node) + dest.fd_name); bcopy(&dest, &node->ipfd_dest, sizeof(dest)); node->ipfd_size = sizeof(*node) + dest.fd_name; if (dest.fd_name > 0) { /* * fd_name starts out as the length of the string to copy * in (including \0) and ends up being the offset from * fd_names (0). */ err = COPYIN((char *)op->iplo_struct + sizeof(dest), node->ipfd_names, dest.fd_name); if (err != 0) { IPFERROR(120017); KFREES(node, node->ipfd_size); return EFAULT; } node->ipfd_dest.fd_name = 0; } else { node->ipfd_dest.fd_name = -1; } if (d->ipld_nodes == d->ipld_maxnodes) { KMALLOCS(nodes, ipf_dstnode_t **, sizeof(*nodes) * (d->ipld_maxnodes + 1)); if (nodes == NULL) { softd->stats.ipls_nomem++; IPFERROR(120022); KFREES(node, node->ipfd_size); return ENOMEM; } if (d->ipld_dests != NULL) { bcopy(d->ipld_dests, nodes, sizeof(*nodes) * d->ipld_maxnodes); KFREES(d->ipld_dests, sizeof(*nodes) * d->ipld_nodes); nodes[0]->ipfd_pnext = nodes; } d->ipld_dests = nodes; d->ipld_maxnodes++; } d->ipld_dests[d->ipld_nodes] = node; d->ipld_nodes++; if (d->ipld_nodes == 1) { node->ipfd_pnext = d->ipld_dests; } else if (d->ipld_nodes > 1) { node->ipfd_pnext = &d->ipld_dests[d->ipld_nodes - 2]->ipfd_next; } *node->ipfd_pnext = node; MUTEX_INIT(&node->ipfd_lock, "ipf dst node lock"); node->ipfd_uid = uid; node->ipfd_ref = 1; if (node->ipfd_dest.fd_name == 0) (void) ipf_resolvedest(softc, node->ipfd_names, &node->ipfd_dest, AF_INET); #ifdef USE_INET6 if (node->ipfd_dest.fd_name == 0 && node->ipfd_dest.fd_ptr == (void *)-1) (void) ipf_resolvedest(softc, node->ipfd_names, &node->ipfd_dest, AF_INET6); #endif softd->stats.ipls_numnodes++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_node_deref */ /* Returns: int - 0 = success, else error */ /* Parameters: arg(I) - pointer to local context to use */ /* node(I) - pointer to destionation node to free */ /* */ /* Dereference the use count by one. If it drops to zero then we can assume */ /* that it has been removed from any lists/tables and is ripe for freeing. */ /* The pointer to context is required for the purpose of maintaining */ /* statistics. */ /* ------------------------------------------------------------------------ */ static int -ipf_dstlist_node_deref(arg, node) - void *arg; - ipf_dstnode_t *node; +ipf_dstlist_node_deref(void *arg, ipf_dstnode_t *node) { ipf_dstl_softc_t *softd = arg; int ref; MUTEX_ENTER(&node->ipfd_lock); ref = --node->ipfd_ref; MUTEX_EXIT(&node->ipfd_lock); if (ref > 0) return 0; if ((node->ipfd_flags & IPDST_DELETE) != 0) softd->stats.ipls_numderefnodes--; MUTEX_DESTROY(&node->ipfd_lock); KFREES(node, node->ipfd_size); softd->stats.ipls_numnodes--; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_node_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* uid(I) - uid of process doing the ioctl */ /* */ /* Look for a matching destination node on the named table and free it if */ /* found. Because the name embedded in the frdest_t is variable in length, */ /* it is necessary to allocate some memory locally, to complete this op. */ /* ------------------------------------------------------------------------ */ static int -ipf_dstlist_node_del(softc, arg, op, uid) - ipf_main_softc_t *softc; - void *arg; - iplookupop_t *op; - int uid; +ipf_dstlist_node_del(ipf_main_softc_t *softc, void *arg, iplookupop_t *op, + int uid) { ipf_dstl_softc_t *softd = arg; ipf_dstnode_t *node; frdest_t frd, *temp; ippool_dst_t *d; size_t size; int err; d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); if (d == NULL) { IPFERROR(120012); return ESRCH; } err = COPYIN(op->iplo_struct, &frd, sizeof(frd)); if (err != 0) { IPFERROR(120011); return EFAULT; } size = sizeof(*temp) + frd.fd_name; KMALLOCS(temp, frdest_t *, size); if (temp == NULL) { softd->stats.ipls_nomem++; IPFERROR(120026); return ENOMEM; } err = COPYIN(op->iplo_struct, temp, size); if (err != 0) { IPFERROR(120027); KFREES(temp, size); return EFAULT; } MUTEX_ENTER(&d->ipld_lock); for (node = *d->ipld_dests; node != NULL; node = node->ipfd_next) { if ((uid != 0) && (node->ipfd_uid != uid)) continue; if (node->ipfd_size != size) continue; if (!bcmp(&node->ipfd_dest.fd_ip6, &frd.fd_ip6, size - offsetof(frdest_t, fd_ip6))) { ipf_dstlist_node_free(softd, d, node); MUTEX_EXIT(&d->ipld_lock); KFREES(temp, size); return 0; } } MUTEX_EXIT(&d->ipld_lock); KFREES(temp, size); return ESRCH; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_node_free */ /* Returns: Nil */ /* Parameters: softd(I) - pointer to the destination list context */ /* d(I) - pointer to destination list */ /* node(I) - pointer to node to free */ /* Locks: MUTEX(ipld_lock) or WRITE(ipf_poolrw) */ /* */ /* Free the destination node by first removing it from any lists and then */ /* checking if this was the last reference held to the object. While the */ /* array of pointers to nodes is compacted, its size isn't reduced (by way */ /* of allocating a new smaller one and copying) because the belief is that */ /* it is likely the array will again reach that size. */ /* ------------------------------------------------------------------------ */ static void -ipf_dstlist_node_free(softd, d, node) - ipf_dstl_softc_t *softd; - ippool_dst_t *d; - ipf_dstnode_t *node; +ipf_dstlist_node_free(ipf_dstl_softc_t *softd, ippool_dst_t *d, + ipf_dstnode_t *node) { int i; /* * Compact the array of pointers to nodes. */ for (i = 0; i < d->ipld_nodes; i++) if (d->ipld_dests[i] == node) break; if (d->ipld_nodes - i > 1) { bcopy(&d->ipld_dests[i + 1], &d->ipld_dests[i], sizeof(*d->ipld_dests) * (d->ipld_nodes - i - 1)); } d->ipld_nodes--; if (node->ipfd_pnext != NULL) *node->ipfd_pnext = node->ipfd_next; if (node->ipfd_next != NULL) node->ipfd_next->ipfd_pnext = node->ipfd_pnext; node->ipfd_pnext = NULL; node->ipfd_next = NULL; if ((node->ipfd_flags & IPDST_DELETE) == 0) { softd->stats.ipls_numderefnodes++; node->ipfd_flags |= IPDST_DELETE; } ipf_dstlist_node_deref(softd, node); } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_stats_get */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Return the current statistics for destination lists. This may be for all */ /* of them or just information pertaining to a particular table. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ static int -ipf_dstlist_stats_get(softc, arg, op) - ipf_main_softc_t *softc; - void *arg; - iplookupop_t *op; +ipf_dstlist_stats_get(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) { ipf_dstl_softc_t *softd = arg; ipf_dstl_stat_t stats; int unit, i, err = 0; if (op->iplo_size != sizeof(ipf_dstl_stat_t)) { IPFERROR(120023); return EINVAL; } stats = softd->stats; unit = op->iplo_unit; if (unit == IPL_LOGALL) { for (i = 0; i <= IPL_LOGMAX; i++) stats.ipls_list[i] = softd->dstlist[i]; } else if (unit >= 0 && unit <= IPL_LOGMAX) { void *ptr; if (op->iplo_name[0] != '\0') ptr = ipf_dstlist_table_find(softd, unit, op->iplo_name); else ptr = softd->dstlist[unit + 1]; stats.ipls_list[unit] = ptr; } else { IPFERROR(120024); err = EINVAL; } if (err == 0) { err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); if (err != 0) { IPFERROR(120025); return EFAULT; } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_add */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Add a new destination table to the list of those available for the given */ /* device. Because we seldom operate on these objects (find/add/delete), */ /* they are just kept in a simple linked list. */ /* ------------------------------------------------------------------------ */ static int -ipf_dstlist_table_add(softc, arg, op) - ipf_main_softc_t *softc; - void *arg; - iplookupop_t *op; +ipf_dstlist_table_add(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) { ipf_dstl_softc_t *softd = arg; ippool_dst_t user, *d, *new; int unit, err; d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); if (d != NULL) { IPFERROR(120013); return EEXIST; } err = COPYIN(op->iplo_struct, &user, sizeof(user)); if (err != 0) { IPFERROR(120021); return EFAULT; } KMALLOC(new, ippool_dst_t *); if (new == NULL) { softd->stats.ipls_nomem++; IPFERROR(120014); return ENOMEM; } bzero((char *)new, sizeof(*new)); MUTEX_INIT(&new->ipld_lock, "ipf dst table lock"); strncpy(new->ipld_name, op->iplo_name, FR_GROUPLEN); unit = op->iplo_unit; new->ipld_unit = unit; new->ipld_policy = user.ipld_policy; new->ipld_seed = ipf_random(); new->ipld_ref = 1; new->ipld_pnext = softd->tails[unit + 1]; *softd->tails[unit + 1] = new; softd->tails[unit + 1] = &new->ipld_next; softd->stats.ipls_numlists++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Find a named destinstion list table and delete it. If there are other */ /* references to it, the caller isn't told. */ /* ------------------------------------------------------------------------ */ static int -ipf_dstlist_table_del(softc, arg, op) - ipf_main_softc_t *softc; - void *arg; - iplookupop_t *op; +ipf_dstlist_table_del(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) { ippool_dst_t *d; d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); if (d == NULL) { IPFERROR(120015); return ESRCH; } if (d->ipld_dests != NULL) { IPFERROR(120016); return EBUSY; } ipf_dstlist_table_remove(softc, arg, d); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_remove */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softd(I) - pointer to the destination list context */ /* d(I) - pointer to destination list */ /* */ /* Remove a given destination list from existance. While the IPDST_DELETE */ /* flag is set every time we call this function and the reference count is */ /* non-zero, the "numdereflists" counter is always incremented because the */ /* decision about whether it will be freed or not is not made here. This */ /* means that the only action the code can take here is to treat it as if */ /* it will become a detached. */ /* ------------------------------------------------------------------------ */ static void -ipf_dstlist_table_remove(softc, softd, d) - ipf_main_softc_t *softc; - ipf_dstl_softc_t *softd; - ippool_dst_t *d; +ipf_dstlist_table_remove(ipf_main_softc_t *softc, ipf_dstl_softc_t *softd, + ippool_dst_t *d) { if (softd->tails[d->ipld_unit + 1] == &d->ipld_next) softd->tails[d->ipld_unit + 1] = d->ipld_pnext; if (d->ipld_pnext != NULL) *d->ipld_pnext = d->ipld_next; if (d->ipld_next != NULL) d->ipld_next->ipld_pnext = d->ipld_pnext; d->ipld_pnext = NULL; d->ipld_next = NULL; ipf_dstlist_table_clearnodes(softd, d); softd->stats.ipls_numdereflists++; d->ipld_flags |= IPDST_DELETE; ipf_dstlist_table_deref(softc, softd, d); } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_free */ /* Returns: Nil */ /* Parameters: softd(I) - pointer to the destination list context */ /* d(I) - pointer to destination list */ /* */ /* Free up a destination list data structure and any other memory that was */ /* directly allocated as part of creating it. Individual destination list */ /* nodes are not freed. It is assumed the caller will have already emptied */ /* the destination list. */ /* ------------------------------------------------------------------------ */ static void -ipf_dstlist_table_free(softd, d) - ipf_dstl_softc_t *softd; - ippool_dst_t *d; +ipf_dstlist_table_free(ipf_dstl_softc_t *softd, ippool_dst_t *d) { MUTEX_DESTROY(&d->ipld_lock); if ((d->ipld_flags & IPDST_DELETE) != 0) softd->stats.ipls_numdereflists--; softd->stats.ipls_numlists--; if (d->ipld_dests != NULL) { KFREES(d->ipld_dests, d->ipld_maxnodes * sizeof(*d->ipld_dests)); } KFREE(d); } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_deref */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Drops the reference count on a destination list table object and free's */ /* it if 0 has been reached. */ /* ------------------------------------------------------------------------ */ static int -ipf_dstlist_table_deref(softc, arg, table) - ipf_main_softc_t *softc; - void *arg; - void *table; +ipf_dstlist_table_deref(ipf_main_softc_t *softc, void *arg, void *table) { ippool_dst_t *d = table; d->ipld_ref--; if (d->ipld_ref > 0) return d->ipld_ref; ipf_dstlist_table_free(arg, d); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_clearnodes */ /* Returns: Nil */ /* Parameters: softd(I) - pointer to the destination list context */ /* dst(I) - pointer to destination list */ /* */ /* Free all of the destination nodes attached to the given table. */ /* ------------------------------------------------------------------------ */ static void -ipf_dstlist_table_clearnodes(softd, dst) - ipf_dstl_softc_t *softd; - ippool_dst_t *dst; +ipf_dstlist_table_clearnodes(ipf_dstl_softc_t *softd, ippool_dst_t *dst) { ipf_dstnode_t *node; if (dst->ipld_dests == NULL) return; while ((node = *dst->ipld_dests) != NULL) { ipf_dstlist_node_free(softd, dst, node); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_table_find */ /* Returns: int - 0 = success, else error */ /* Parameters: arg(I) - pointer to local context to use */ /* unit(I) - device we are working with */ /* name(I) - destination table name to find */ /* */ /* Return a pointer to a destination table that matches the unit+name that */ /* is passed in. */ /* ------------------------------------------------------------------------ */ static void * -ipf_dstlist_table_find(arg, unit, name) - void *arg; - int unit; - char *name; +ipf_dstlist_table_find(void *arg, int unit, char *name) { ipf_dstl_softc_t *softd = arg; ippool_dst_t *d; for (d = softd->dstlist[unit + 1]; d != NULL; d = d->ipld_next) { if ((d->ipld_unit == unit) && !strncmp(d->ipld_name, name, FR_GROUPLEN)) { return d; } } return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_select_ref */ /* Returns: void * - NULL = failure, else pointer to table */ /* Parameters: arg(I) - pointer to local context to use */ /* unit(I) - device we are working with */ /* name(I) - destination table name to find */ /* */ /* Attempt to find a destination table that matches the name passed in and */ /* if successful, bump up the reference count on it because we intend to */ /* store the pointer to it somewhere else. */ /* ------------------------------------------------------------------------ */ static void * -ipf_dstlist_select_ref(arg, unit, name) - void *arg; - int unit; - char *name; +ipf_dstlist_select_ref(void *arg, int unit, char *name) { ippool_dst_t *d; d = ipf_dstlist_table_find(arg, unit, name); if (d != NULL) { MUTEX_ENTER(&d->ipld_lock); d->ipld_ref++; MUTEX_EXIT(&d->ipld_lock); } return d; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_select */ /* Returns: void * - NULL = failure, else pointer to table */ /* Parameters: fin(I) - pointer to packet information */ /* d(I) - pointer to destination list */ /* */ /* Find the next node in the destination list to be used according to the */ /* defined policy. Of these, "connection" is the most expensive policy to */ /* implement as it always looks for the node with the least number of */ /* connections associated with it. */ /* */ /* The hashes exclude the port numbers so that all protocols map to the */ /* same destination. Otherwise, someone doing a ping would target a */ /* different server than their TCP connection, etc. MD-5 is used to */ /* transform the addressese into something random that the other end could */ /* not easily guess and use in an attack. ipld_seed introduces an unknown */ /* into the hash calculation to increase the difficult of an attacker */ /* guessing the bucket. */ /* */ /* One final comment: mixing different address families in a single pool */ /* will currently result in failures as the address family of the node is */ /* only matched up with that in the packet as the last step. While this can */ /* be coded around for the weighted connection and round-robin models, it */ /* cannot be supported for the hash/random models as they do not search and */ /* nor is the algorithm conducive to searching. */ /* ------------------------------------------------------------------------ */ static ipf_dstnode_t * -ipf_dstlist_select(fin, d) - fr_info_t *fin; - ippool_dst_t *d; +ipf_dstlist_select(fr_info_t *fin, ippool_dst_t *d) { ipf_dstnode_t *node, *sel; int connects; u_32_t hash[4]; MD5_CTX ctx; int family; int x; if (d == NULL || d->ipld_dests == NULL || *d->ipld_dests == NULL) return NULL; family = fin->fin_family; MUTEX_ENTER(&d->ipld_lock); switch (d->ipld_policy) { case IPLDP_ROUNDROBIN: sel = d->ipld_selected; if (sel == NULL) { sel = *d->ipld_dests; } else { sel = sel->ipfd_next; if (sel == NULL) sel = *d->ipld_dests; } break; case IPLDP_CONNECTION: if (d->ipld_selected == NULL) { sel = *d->ipld_dests; break; } sel = d->ipld_selected; connects = 0x7fffffff; node = sel->ipfd_next; if (node == NULL) node = *d->ipld_dests; while (node != d->ipld_selected) { if (node->ipfd_states == 0) { sel = node; break; } if (node->ipfd_states < connects) { sel = node; connects = node->ipfd_states; } node = node->ipfd_next; if (node == NULL) node = *d->ipld_dests; } break; case IPLDP_RANDOM : x = ipf_random() % d->ipld_nodes; sel = d->ipld_dests[x]; break; case IPLDP_HASHED : MD5Init(&ctx); MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); MD5Update(&ctx, (u_char *)&fin->fin_src6, sizeof(fin->fin_src6)); MD5Update(&ctx, (u_char *)&fin->fin_dst6, sizeof(fin->fin_dst6)); MD5Final((u_char *)hash, &ctx); x = ntohl(hash[0]) % d->ipld_nodes; sel = d->ipld_dests[x]; break; case IPLDP_SRCHASH : MD5Init(&ctx); MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); MD5Update(&ctx, (u_char *)&fin->fin_src6, sizeof(fin->fin_src6)); MD5Final((u_char *)hash, &ctx); x = ntohl(hash[0]) % d->ipld_nodes; sel = d->ipld_dests[x]; break; case IPLDP_DSTHASH : MD5Init(&ctx); MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); MD5Update(&ctx, (u_char *)&fin->fin_dst6, sizeof(fin->fin_dst6)); MD5Final((u_char *)hash, &ctx); x = ntohl(hash[0]) % d->ipld_nodes; sel = d->ipld_dests[x]; break; default : sel = NULL; break; } if (sel && sel->ipfd_dest.fd_addr.adf_family != family) sel = NULL; d->ipld_selected = sel; MUTEX_EXIT(&d->ipld_lock); return sel; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_select_node */ /* Returns: int - -1 == failure, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* group(I) - destination pool to search */ /* addr(I) - pointer to store selected address */ /* pfdp(O) - pointer to storage for selected destination node */ /* */ /* This function is only responsible for obtaining the next IP address for */ /* use and storing it in the caller's address space (addr). "addr" is only */ /* used for storage if pfdp is NULL. No permanent reference is currently */ /* kept on the node. */ /* ------------------------------------------------------------------------ */ int -ipf_dstlist_select_node(fin, group, addr, pfdp) - fr_info_t *fin; - void *group; - u_32_t *addr; - frdest_t *pfdp; +ipf_dstlist_select_node(fr_info_t *fin, void *group, u_32_t *addr, + frdest_t *pfdp) { #ifdef USE_MUTEXES ipf_main_softc_t *softc = fin->fin_main_soft; #endif ippool_dst_t *d = group; ipf_dstnode_t *node; frdest_t *fdp; READ_ENTER(&softc->ipf_poolrw); node = ipf_dstlist_select(fin, d); if (node == NULL) { RWLOCK_EXIT(&softc->ipf_poolrw); return -1; } if (pfdp != NULL) { bcopy(&node->ipfd_dest, pfdp, sizeof(*pfdp)); } else { if (fin->fin_family == AF_INET) { addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0]; } else if (fin->fin_family == AF_INET6) { addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0]; addr[1] = node->ipfd_dest.fd_addr.adf_addr.i6[1]; addr[2] = node->ipfd_dest.fd_addr.adf_addr.i6[2]; addr[3] = node->ipfd_dest.fd_addr.adf_addr.i6[3]; } } fdp = &node->ipfd_dest; if (fdp->fd_ptr == NULL) fdp->fd_ptr = fin->fin_ifp; MUTEX_ENTER(&node->ipfd_lock); node->ipfd_states++; MUTEX_EXIT(&node->ipfd_lock); RWLOCK_EXIT(&softc->ipf_poolrw); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* There are currently no objects to expire in destination lists. */ /* ------------------------------------------------------------------------ */ static void -ipf_dstlist_expire(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_dstlist_expire(ipf_main_softc_t *softc, void *arg) { return; } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstlist_sync */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* When a network interface appears or disappears, we need to revalidate */ /* all of the network interface names that have been configured as a target */ /* in a destination list. */ /* ------------------------------------------------------------------------ */ void -ipf_dstlist_sync(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_dstlist_sync(ipf_main_softc_t *softc, void *arg) { ipf_dstl_softc_t *softd = arg; ipf_dstnode_t *node; ippool_dst_t *list; int i; int j; for (i = 0; i < IPL_LOGMAX; i++) { for (list = softd->dstlist[i]; list != NULL; list = list->ipld_next) { for (j = 0; j < list->ipld_maxnodes; j++) { node = list->ipld_dests[j]; if (node == NULL) continue; if (node->ipfd_dest.fd_name == -1) continue; (void) ipf_resolvedest(softc, node->ipfd_names, &node->ipfd_dest, AF_INET); } } } } diff --git a/sys/netpfil/ipfilter/netinet/ip_fil_freebsd.c b/sys/netpfil/ipfilter/netinet/ip_fil_freebsd.c index 6f48a9c4ce97..a6d346e2ef0d 100644 --- a/sys/netpfil/ipfilter/netinet/ip_fil_freebsd.c +++ b/sys/netpfil/ipfilter/netinet/ip_fil_freebsd.c @@ -1,1489 +1,1452 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if !defined(lint) static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; static const char rcsid[] = "@(#)$Id$"; #endif #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #if defined(__FreeBSD__) && \ !defined(KLD_MODULE) && !defined(IPFILTER_LKM) # include "opt_inet6.h" #endif #include #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netinet/ip_compat.h" #ifdef USE_INET6 # include #endif #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #include "netinet/ip_auth.h" #include "netinet/ip_sync.h" #include "netinet/ip_lookup.h" #include "netinet/ip_dstlist.h" #ifdef IPFILTER_SCAN # include "netinet/ip_scan.h" #endif #include "netinet/ip_pool.h" #include #include #ifdef CSUM_DATA_VALID # include #endif extern int ip_optcopy(struct ip *, struct ip *); #ifdef IPFILTER_M_IPFILTER MALLOC_DEFINE(M_IPFILTER, "ipfilter", "IP Filter packet filter data structures"); #endif static int ipf_send_ip(fr_info_t *, mb_t *); static void ipf_timer_func(void *arg); VNET_DEFINE(ipf_main_softc_t, ipfmain) = { .ipf_running = -2, }; #define V_ipfmain VNET(ipfmain) #include #include VNET_DEFINE_STATIC(eventhandler_tag, ipf_arrivetag); VNET_DEFINE_STATIC(eventhandler_tag, ipf_departtag); #define V_ipf_arrivetag VNET(ipf_arrivetag) #define V_ipf_departtag VNET(ipf_departtag) #if 0 /* * Disable the "cloner" event handler; we are getting interface * events before the firewall is fully initiallized and also no vnet * information thus leading to uninitialised memory accesses. * In addition it is unclear why we need it in first place. * If it turns out to be needed, well need a dedicated event handler * for it to deal with the ifc and the correct vnet. */ VNET_DEFINE_STATIC(eventhandler_tag, ipf_clonetag); #define V_ipf_clonetag VNET(ipf_clonetag) #endif static void ipf_ifevent(void *arg, struct ifnet *ifp); -static void ipf_ifevent(arg, ifp) - void *arg; - struct ifnet *ifp; +static void ipf_ifevent(void *arg, struct ifnet *ifp) { CURVNET_SET(ifp->if_vnet); if (V_ipfmain.ipf_running > 0) ipf_sync(&V_ipfmain, NULL); CURVNET_RESTORE(); } static int ipf_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) { struct ip *ip = mtod(*mp, struct ip *); int rv; /* * IPFilter expects evreything in network byte order */ #if (__FreeBSD_version < 1000019) ip->ip_len = htons(ip->ip_len); ip->ip_off = htons(ip->ip_off); #endif CURVNET_SET(ifp->if_vnet); rv = ipf_check(&V_ipfmain, ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT), mp); CURVNET_RESTORE(); #if (__FreeBSD_version < 1000019) if ((rv == 0) && (*mp != NULL)) { ip = mtod(*mp, struct ip *); ip->ip_len = ntohs(ip->ip_len); ip->ip_off = ntohs(ip->ip_off); } #endif return rv; } # ifdef USE_INET6 # include static int ipf_check_wrapper6(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) { int error; CURVNET_SET(ifp->if_vnet); error = ipf_check(&V_ipfmain, mtod(*mp, struct ip *), sizeof(struct ip6_hdr), ifp, (dir == PFIL_OUT), mp); CURVNET_RESTORE(); return (error); } # endif #if defined(IPFILTER_LKM) -int ipf_identify(s) - char *s; +int ipf_identify(char *s) { if (strcmp(s, "ipl") == 0) return 1; return 0; } #endif /* IPFILTER_LKM */ static void -ipf_timer_func(arg) - void *arg; +ipf_timer_func(void *arg) { ipf_main_softc_t *softc = arg; SPL_INT(s); SPL_NET(s); READ_ENTER(&softc->ipf_global); if (softc->ipf_running > 0) ipf_slowtimer(softc); if (softc->ipf_running == -1 || softc->ipf_running == 1) { #if 0 softc->ipf_slow_ch = timeout(ipf_timer_func, softc, hz/2); #endif callout_init(&softc->ipf_slow_ch, 1); callout_reset(&softc->ipf_slow_ch, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT, ipf_timer_func, softc); } RWLOCK_EXIT(&softc->ipf_global); SPL_X(s); } int -ipfattach(softc) - ipf_main_softc_t *softc; +ipfattach(ipf_main_softc_t *softc) { #ifdef USE_SPL int s; #endif SPL_NET(s); if (softc->ipf_running > 0) { SPL_X(s); return EBUSY; } if (ipf_init_all(softc) < 0) { SPL_X(s); return EIO; } bzero((char *)V_ipfmain.ipf_selwait, sizeof(V_ipfmain.ipf_selwait)); softc->ipf_running = 1; if (softc->ipf_control_forwarding & 1) V_ipforwarding = 1; SPL_X(s); #if 0 softc->ipf_slow_ch = timeout(ipf_timer_func, softc, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT); #endif callout_init(&softc->ipf_slow_ch, 1); callout_reset(&softc->ipf_slow_ch, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT, ipf_timer_func, softc); return 0; } /* * Disable the filter by removing the hooks from the IP input/output * stream. */ int -ipfdetach(softc) - ipf_main_softc_t *softc; +ipfdetach(ipf_main_softc_t *softc) { #ifdef USE_SPL int s; #endif if (softc->ipf_control_forwarding & 2) V_ipforwarding = 0; SPL_NET(s); #if 0 if (softc->ipf_slow_ch.callout != NULL) untimeout(ipf_timer_func, softc, softc->ipf_slow_ch); bzero(&softc->ipf_slow, sizeof(softc->ipf_slow)); #endif callout_drain(&softc->ipf_slow_ch); ipf_fini_all(softc); softc->ipf_running = -2; SPL_X(s); return 0; } /* * Filter ioctl interface. */ int -ipfioctl(dev, cmd, data, mode, p) - struct thread *p; +ipfioctl(struct cdev *dev, ioctlcmd_t cmd, caddr_t data, + int mode, struct thread *p) #define p_cred td_ucred #define p_uid td_ucred->cr_ruid - struct cdev *dev; - ioctlcmd_t cmd; - caddr_t data; - int mode; { int error = 0, unit = 0; SPL_INT(s); CURVNET_SET(TD_TO_VNET(p)); if (securelevel_ge(p->p_cred, 3) && (mode & FWRITE)) { V_ipfmain.ipf_interror = 130001; CURVNET_RESTORE(); return EPERM; } unit = GET_MINOR(dev); if ((IPL_LOGMAX < unit) || (unit < 0)) { V_ipfmain.ipf_interror = 130002; CURVNET_RESTORE(); return ENXIO; } if (V_ipfmain.ipf_running <= 0) { if (unit != IPL_LOGIPF && cmd != SIOCIPFINTERROR) { V_ipfmain.ipf_interror = 130003; CURVNET_RESTORE(); return EIO; } if (cmd != SIOCIPFGETNEXT && cmd != SIOCIPFGET && cmd != SIOCIPFSET && cmd != SIOCFRENB && cmd != SIOCGETFS && cmd != SIOCGETFF && cmd != SIOCIPFINTERROR) { V_ipfmain.ipf_interror = 130004; CURVNET_RESTORE(); return EIO; } } SPL_NET(s); error = ipf_ioctlswitch(&V_ipfmain, unit, data, cmd, mode, p->p_uid, p); CURVNET_RESTORE(); if (error != -1) { SPL_X(s); return error; } SPL_X(s); return error; } /* * ipf_send_reset - this could conceivably be a call to tcp_respond(), but that * requires a large amount of setting up and isn't any more efficient. */ int -ipf_send_reset(fin) - fr_info_t *fin; +ipf_send_reset(fr_info_t *fin) { struct tcphdr *tcp, *tcp2; int tlen = 0, hlen; struct mbuf *m; #ifdef USE_INET6 ip6_t *ip6; #endif ip_t *ip; tcp = fin->fin_dp; if (tcp->th_flags & TH_RST) return -1; /* feedback loop */ if (ipf_checkl4sum(fin) == -1) return -1; tlen = fin->fin_dlen - (TCP_OFF(tcp) << 2) + ((tcp->th_flags & TH_SYN) ? 1 : 0) + ((tcp->th_flags & TH_FIN) ? 1 : 0); #ifdef USE_INET6 hlen = (fin->fin_v == 6) ? sizeof(ip6_t) : sizeof(ip_t); #else hlen = sizeof(ip_t); #endif #ifdef MGETHDR MGETHDR(m, M_NOWAIT, MT_HEADER); #else MGET(m, M_NOWAIT, MT_HEADER); #endif if (m == NULL) return -1; if (sizeof(*tcp2) + hlen > MLEN) { if (!(MCLGET(m, M_NOWAIT))) { FREE_MB_T(m); return -1; } } m->m_len = sizeof(*tcp2) + hlen; m->m_data += max_linkhdr; m->m_pkthdr.len = m->m_len; m->m_pkthdr.rcvif = (struct ifnet *)0; ip = mtod(m, struct ip *); bzero((char *)ip, hlen); #ifdef USE_INET6 ip6 = (ip6_t *)ip; #endif tcp2 = (struct tcphdr *)((char *)ip + hlen); tcp2->th_sport = tcp->th_dport; tcp2->th_dport = tcp->th_sport; if (tcp->th_flags & TH_ACK) { tcp2->th_seq = tcp->th_ack; tcp2->th_flags = TH_RST; tcp2->th_ack = 0; } else { tcp2->th_seq = 0; tcp2->th_ack = ntohl(tcp->th_seq); tcp2->th_ack += tlen; tcp2->th_ack = htonl(tcp2->th_ack); tcp2->th_flags = TH_RST|TH_ACK; } TCP_X2_A(tcp2, 0); TCP_OFF_A(tcp2, sizeof(*tcp2) >> 2); tcp2->th_win = tcp->th_win; tcp2->th_sum = 0; tcp2->th_urp = 0; #ifdef USE_INET6 if (fin->fin_v == 6) { ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow; ip6->ip6_plen = htons(sizeof(struct tcphdr)); ip6->ip6_nxt = IPPROTO_TCP; ip6->ip6_hlim = 0; ip6->ip6_src = fin->fin_dst6.in6; ip6->ip6_dst = fin->fin_src6.in6; tcp2->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(*ip6), sizeof(*tcp2)); return ipf_send_ip(fin, m); } #endif ip->ip_p = IPPROTO_TCP; ip->ip_len = htons(sizeof(struct tcphdr)); ip->ip_src.s_addr = fin->fin_daddr; ip->ip_dst.s_addr = fin->fin_saddr; tcp2->th_sum = in_cksum(m, hlen + sizeof(*tcp2)); ip->ip_len = htons(hlen + sizeof(*tcp2)); return ipf_send_ip(fin, m); } /* * ip_len must be in network byte order when called. */ static int -ipf_send_ip(fin, m) - fr_info_t *fin; - mb_t *m; +ipf_send_ip(fr_info_t *fin, mb_t *m) { fr_info_t fnew; ip_t *ip, *oip; int hlen; ip = mtod(m, ip_t *); bzero((char *)&fnew, sizeof(fnew)); fnew.fin_main_soft = fin->fin_main_soft; IP_V_A(ip, fin->fin_v); switch (fin->fin_v) { case 4 : oip = fin->fin_ip; hlen = sizeof(*oip); fnew.fin_v = 4; fnew.fin_p = ip->ip_p; fnew.fin_plen = ntohs(ip->ip_len); IP_HL_A(ip, sizeof(*oip) >> 2); ip->ip_tos = oip->ip_tos; ip->ip_id = fin->fin_ip->ip_id; ip->ip_off = htons(V_path_mtu_discovery ? IP_DF : 0); ip->ip_ttl = V_ip_defttl; ip->ip_sum = 0; break; #ifdef USE_INET6 case 6 : { ip6_t *ip6 = (ip6_t *)ip; ip6->ip6_vfc = 0x60; ip6->ip6_hlim = IPDEFTTL; hlen = sizeof(*ip6); fnew.fin_p = ip6->ip6_nxt; fnew.fin_v = 6; fnew.fin_plen = ntohs(ip6->ip6_plen) + hlen; break; } #endif default : return EINVAL; } #ifdef IPSEC m->m_pkthdr.rcvif = NULL; #endif fnew.fin_ifp = fin->fin_ifp; fnew.fin_flx = FI_NOCKSUM; fnew.fin_m = m; fnew.fin_ip = ip; fnew.fin_mp = &m; fnew.fin_hlen = hlen; fnew.fin_dp = (char *)ip + hlen; (void) ipf_makefrip(hlen, ip, &fnew); return ipf_fastroute(m, &m, &fnew, NULL); } int -ipf_send_icmp_err(type, fin, dst) - int type; - fr_info_t *fin; - int dst; +ipf_send_icmp_err(int type, fr_info_t *fin, int dst) { int err, hlen, xtra, iclen, ohlen, avail, code; struct in_addr dst4; struct icmp *icmp; struct mbuf *m; i6addr_t dst6; void *ifp; #ifdef USE_INET6 ip6_t *ip6; #endif ip_t *ip, *ip2; if ((type < 0) || (type >= ICMP_MAXTYPE)) return -1; code = fin->fin_icode; #ifdef USE_INET6 /* See NetBSD ip_fil_netbsd.c r1.4: */ if ((code < 0) || (code >= sizeof(icmptoicmp6unreach)/sizeof(int))) return -1; #endif if (ipf_checkl4sum(fin) == -1) return -1; #ifdef MGETHDR MGETHDR(m, M_NOWAIT, MT_HEADER); #else MGET(m, M_NOWAIT, MT_HEADER); #endif if (m == NULL) return -1; avail = MHLEN; xtra = 0; hlen = 0; ohlen = 0; dst4.s_addr = 0; ifp = fin->fin_ifp; if (fin->fin_v == 4) { if ((fin->fin_p == IPPROTO_ICMP) && !(fin->fin_flx & FI_SHORT)) switch (ntohs(fin->fin_data[0]) >> 8) { case ICMP_ECHO : case ICMP_TSTAMP : case ICMP_IREQ : case ICMP_MASKREQ : break; default : FREE_MB_T(m); return 0; } if (dst == 0) { if (ipf_ifpaddr(&V_ipfmain, 4, FRI_NORMAL, ifp, &dst6, NULL) == -1) { FREE_MB_T(m); return -1; } dst4 = dst6.in4; } else dst4.s_addr = fin->fin_daddr; hlen = sizeof(ip_t); ohlen = fin->fin_hlen; iclen = hlen + offsetof(struct icmp, icmp_ip) + ohlen; if (fin->fin_hlen < fin->fin_plen) xtra = MIN(fin->fin_dlen, 8); else xtra = 0; } #ifdef USE_INET6 else if (fin->fin_v == 6) { hlen = sizeof(ip6_t); ohlen = sizeof(ip6_t); iclen = hlen + offsetof(struct icmp, icmp_ip) + ohlen; type = icmptoicmp6types[type]; if (type == ICMP6_DST_UNREACH) code = icmptoicmp6unreach[code]; if (iclen + max_linkhdr + fin->fin_plen > avail) { if (!(MCLGET(m, M_NOWAIT))) { FREE_MB_T(m); return -1; } avail = MCLBYTES; } xtra = MIN(fin->fin_plen, avail - iclen - max_linkhdr); xtra = MIN(xtra, IPV6_MMTU - iclen); if (dst == 0) { if (ipf_ifpaddr(&V_ipfmain, 6, FRI_NORMAL, ifp, &dst6, NULL) == -1) { FREE_MB_T(m); return -1; } } else dst6 = fin->fin_dst6; } #endif else { FREE_MB_T(m); return -1; } avail -= (max_linkhdr + iclen); if (avail < 0) { FREE_MB_T(m); return -1; } if (xtra > avail) xtra = avail; iclen += xtra; m->m_data += max_linkhdr; m->m_pkthdr.rcvif = (struct ifnet *)0; m->m_pkthdr.len = iclen; m->m_len = iclen; ip = mtod(m, ip_t *); icmp = (struct icmp *)((char *)ip + hlen); ip2 = (ip_t *)&icmp->icmp_ip; icmp->icmp_type = type; icmp->icmp_code = fin->fin_icode; icmp->icmp_cksum = 0; #ifdef icmp_nextmtu if (type == ICMP_UNREACH && fin->fin_icode == ICMP_UNREACH_NEEDFRAG) { if (fin->fin_mtu != 0) { icmp->icmp_nextmtu = htons(fin->fin_mtu); } else if (ifp != NULL) { icmp->icmp_nextmtu = htons(GETIFMTU_4(ifp)); } else { /* make up a number... */ icmp->icmp_nextmtu = htons(fin->fin_plen - 20); } } #endif bcopy((char *)fin->fin_ip, (char *)ip2, ohlen); #ifdef USE_INET6 ip6 = (ip6_t *)ip; if (fin->fin_v == 6) { ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow; ip6->ip6_plen = htons(iclen - hlen); ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 0; ip6->ip6_src = dst6.in6; ip6->ip6_dst = fin->fin_src6.in6; if (xtra > 0) bcopy((char *)fin->fin_ip + ohlen, (char *)&icmp->icmp_ip + ohlen, xtra); icmp->icmp_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), iclen - hlen); } else #endif { ip->ip_p = IPPROTO_ICMP; ip->ip_src.s_addr = dst4.s_addr; ip->ip_dst.s_addr = fin->fin_saddr; if (xtra > 0) bcopy((char *)fin->fin_ip + ohlen, (char *)&icmp->icmp_ip + ohlen, xtra); icmp->icmp_cksum = ipf_cksum((u_short *)icmp, sizeof(*icmp) + 8); ip->ip_len = htons(iclen); ip->ip_p = IPPROTO_ICMP; } err = ipf_send_ip(fin, m); return err; } /* * m0 - pointer to mbuf where the IP packet starts * mpp - pointer to the mbuf pointer that is the start of the mbuf chain */ int -ipf_fastroute(m0, mpp, fin, fdp) - mb_t *m0, **mpp; - fr_info_t *fin; - frdest_t *fdp; +ipf_fastroute(mb_t *m0, mb_t **mpp, fr_info_t *fin, frdest_t *fdp) { register struct ip *ip, *mhip; register struct mbuf *m = *mpp; int len, off, error = 0, hlen, code; struct ifnet *ifp, *sifp; struct sockaddr_in dst; struct nhop4_extended nh4; u_long fibnum = 0; u_short ip_off; frdest_t node; frentry_t *fr; #ifdef M_WRITABLE /* * HOT FIX/KLUDGE: * * If the mbuf we're about to send is not writable (because of * a cluster reference, for example) we'll need to make a copy * of it since this routine modifies the contents. * * If you have non-crappy network hardware that can transmit data * from the mbuf, rather than making a copy, this is gonna be a * problem. */ if (M_WRITABLE(m) == 0) { m0 = m_dup(m, M_NOWAIT); if (m0 != NULL) { FREE_MB_T(m); m = m0; *mpp = m; } else { error = ENOBUFS; FREE_MB_T(m); goto done; } } #endif #ifdef USE_INET6 if (fin->fin_v == 6) { /* * currently "to " and "to :ip#" are not supported * for IPv6 */ return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); } #endif hlen = fin->fin_hlen; ip = mtod(m0, struct ip *); ifp = NULL; /* * Route packet. */ bzero(&dst, sizeof (dst)); dst.sin_family = AF_INET; dst.sin_addr = ip->ip_dst; dst.sin_len = sizeof(dst); fr = fin->fin_fr; if ((fr != NULL) && !(fr->fr_flags & FR_KEEPSTATE) && (fdp != NULL) && (fdp->fd_type == FRD_DSTLIST)) { if (ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &node) == 0) fdp = &node; } if (fdp != NULL) ifp = fdp->fd_ptr; else ifp = fin->fin_ifp; if ((ifp == NULL) && ((fr == NULL) || !(fr->fr_flags & FR_FASTROUTE))) { error = -2; goto bad; } if ((fdp != NULL) && (fdp->fd_ip.s_addr != 0)) dst.sin_addr = fdp->fd_ip; fibnum = M_GETFIB(m0); if (fib4_lookup_nh_ext(fibnum, dst.sin_addr, NHR_REF, 0, &nh4) != 0) { if (in_localaddr(ip->ip_dst)) error = EHOSTUNREACH; else error = ENETUNREACH; goto bad; } if (ifp == NULL) ifp = nh4.nh_ifp; if (nh4.nh_flags & NHF_GATEWAY) dst.sin_addr = nh4.nh_addr; /* * For input packets which are being "fastrouted", they won't * go back through output filtering and miss their chance to get * NAT'd and counted. Duplicated packets aren't considered to be * part of the normal packet stream, so do not NAT them or pass * them through stateful checking, etc. */ if ((fdp != &fr->fr_dif) && (fin->fin_out == 0)) { sifp = fin->fin_ifp; fin->fin_ifp = ifp; fin->fin_out = 1; (void) ipf_acctpkt(fin, NULL); fin->fin_fr = NULL; if (!fr || !(fr->fr_flags & FR_RETMASK)) { u_32_t pass; (void) ipf_state_check(fin, &pass); } switch (ipf_nat_checkout(fin, NULL)) { case 0 : break; case 1 : ip->ip_sum = 0; break; case -1 : error = -1; goto bad; break; } fin->fin_ifp = sifp; fin->fin_out = 0; } else ip->ip_sum = 0; /* * If small enough for interface, can just send directly. */ if (ntohs(ip->ip_len) <= ifp->if_mtu) { if (!ip->ip_sum) ip->ip_sum = in_cksum(m, hlen); error = (*ifp->if_output)(ifp, m, (struct sockaddr *)&dst, NULL ); goto done; } /* * Too large for interface; fragment if possible. * Must be able to put at least 8 bytes per fragment. */ ip_off = ntohs(ip->ip_off); if (ip_off & IP_DF) { error = EMSGSIZE; goto bad; } len = (ifp->if_mtu - hlen) &~ 7; if (len < 8) { error = EMSGSIZE; goto bad; } { int mhlen, firstlen = len; struct mbuf **mnext = &m->m_act; /* * Loop through length of segment after first fragment, * make new header and copy data of each part and link onto chain. */ m0 = m; mhlen = sizeof (struct ip); for (off = hlen + len; off < ntohs(ip->ip_len); off += len) { #ifdef MGETHDR MGETHDR(m, M_NOWAIT, MT_HEADER); #else MGET(m, M_NOWAIT, MT_HEADER); #endif if (m == NULL) { m = m0; error = ENOBUFS; goto bad; } m->m_data += max_linkhdr; mhip = mtod(m, struct ip *); bcopy((char *)ip, (char *)mhip, sizeof(*ip)); if (hlen > sizeof (struct ip)) { mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); IP_HL_A(mhip, mhlen >> 2); } m->m_len = mhlen; mhip->ip_off = ((off - hlen) >> 3) + ip_off; if (off + len >= ntohs(ip->ip_len)) len = ntohs(ip->ip_len) - off; else mhip->ip_off |= IP_MF; mhip->ip_len = htons((u_short)(len + mhlen)); *mnext = m; m->m_next = m_copym(m0, off, len, M_NOWAIT); if (m->m_next == 0) { error = ENOBUFS; /* ??? */ goto sendorfree; } m->m_pkthdr.len = mhlen + len; m->m_pkthdr.rcvif = NULL; mhip->ip_off = htons((u_short)mhip->ip_off); mhip->ip_sum = 0; mhip->ip_sum = in_cksum(m, mhlen); mnext = &m->m_act; } /* * Update first fragment by trimming what's been copied out * and updating header, then send each fragment (in order). */ m_adj(m0, hlen + firstlen - ip->ip_len); ip->ip_len = htons((u_short)(hlen + firstlen)); ip->ip_off = htons((u_short)IP_MF); ip->ip_sum = 0; ip->ip_sum = in_cksum(m0, hlen); sendorfree: for (m = m0; m; m = m0) { m0 = m->m_act; m->m_act = 0; if (error == 0) error = (*ifp->if_output)(ifp, m, (struct sockaddr *)&dst, NULL ); else FREE_MB_T(m); } } done: if (!error) V_ipfmain.ipf_frouteok[0]++; else V_ipfmain.ipf_frouteok[1]++; return 0; bad: if (error == EMSGSIZE) { sifp = fin->fin_ifp; code = fin->fin_icode; fin->fin_icode = ICMP_UNREACH_NEEDFRAG; fin->fin_ifp = ifp; (void) ipf_send_icmp_err(ICMP_UNREACH, fin, 1); fin->fin_ifp = sifp; fin->fin_icode = code; } FREE_MB_T(m); goto done; } int ipf_verifysrc(fin) fr_info_t *fin; { struct nhop4_basic nh4; if (fib4_lookup_nh_basic(0, fin->fin_src, 0, 0, &nh4) != 0) return (0); return (fin->fin_ifp == nh4.nh_ifp); } /* * return the first IP Address associated with an interface */ int -ipf_ifpaddr(softc, v, atype, ifptr, inp, inpmask) - ipf_main_softc_t *softc; - int v, atype; - void *ifptr; - i6addr_t *inp, *inpmask; +ipf_ifpaddr(ipf_main_softc_t *softc, int v, int atype, void *ifptr, + i6addr_t *inp, i6addr_t *inpmask) { #ifdef USE_INET6 struct in6_addr *ia6 = NULL; #endif struct sockaddr *sock, *mask; struct sockaddr_in *sin; struct ifaddr *ifa; struct ifnet *ifp; if ((ifptr == NULL) || (ifptr == (void *)-1)) return -1; sin = NULL; ifp = ifptr; if (v == 4) inp->in4.s_addr = 0; #ifdef USE_INET6 else if (v == 6) bzero((char *)inp, sizeof(*inp)); #endif ifa = CK_STAILQ_FIRST(&ifp->if_addrhead); sock = ifa->ifa_addr; while (sock != NULL && ifa != NULL) { sin = (struct sockaddr_in *)sock; if ((v == 4) && (sin->sin_family == AF_INET)) break; #ifdef USE_INET6 if ((v == 6) && (sin->sin_family == AF_INET6)) { ia6 = &((struct sockaddr_in6 *)sin)->sin6_addr; if (!IN6_IS_ADDR_LINKLOCAL(ia6) && !IN6_IS_ADDR_LOOPBACK(ia6)) break; } #endif ifa = CK_STAILQ_NEXT(ifa, ifa_link); if (ifa != NULL) sock = ifa->ifa_addr; } if (ifa == NULL || sin == NULL) return -1; mask = ifa->ifa_netmask; if (atype == FRI_BROADCAST) sock = ifa->ifa_broadaddr; else if (atype == FRI_PEERADDR) sock = ifa->ifa_dstaddr; if (sock == NULL) return -1; #ifdef USE_INET6 if (v == 6) { return ipf_ifpfillv6addr(atype, (struct sockaddr_in6 *)sock, (struct sockaddr_in6 *)mask, inp, inpmask); } #endif return ipf_ifpfillv4addr(atype, (struct sockaddr_in *)sock, (struct sockaddr_in *)mask, &inp->in4, &inpmask->in4); } u_32_t ipf_newisn(fin) fr_info_t *fin; { u_32_t newiss; newiss = arc4random(); return newiss; } INLINE int -ipf_checkv4sum(fin) - fr_info_t *fin; +ipf_checkv4sum(fr_info_t *fin) { #ifdef CSUM_DATA_VALID int manual = 0; u_short sum; ip_t *ip; mb_t *m; if ((fin->fin_flx & FI_NOCKSUM) != 0) return 0; if ((fin->fin_flx & FI_SHORT) != 0) return 1; if (fin->fin_cksum != FI_CK_NEEDED) return (fin->fin_cksum > FI_CK_NEEDED) ? 0 : -1; m = fin->fin_m; if (m == NULL) { manual = 1; goto skipauto; } ip = fin->fin_ip; if ((m->m_pkthdr.csum_flags & (CSUM_IP_CHECKED|CSUM_IP_VALID)) == CSUM_IP_CHECKED) { fin->fin_cksum = FI_CK_BAD; fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkv4sum_csum_ip_checked, fr_info_t *, fin, u_int, m->m_pkthdr.csum_flags & (CSUM_IP_CHECKED|CSUM_IP_VALID)); return -1; } if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { /* Depending on the driver, UDP may have zero checksum */ if (fin->fin_p == IPPROTO_UDP && (fin->fin_flx & (FI_FRAG|FI_SHORT|FI_BAD)) == 0) { udphdr_t *udp = fin->fin_dp; if (udp->uh_sum == 0) { /* * we're good no matter what the hardware * checksum flags and csum_data say (handling * of csum_data for zero UDP checksum is not * consistent across all drivers) */ fin->fin_cksum = 1; return 0; } } if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) sum = m->m_pkthdr.csum_data; else sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htonl(m->m_pkthdr.csum_data + fin->fin_dlen + fin->fin_p)); sum ^= 0xffff; if (sum != 0) { fin->fin_cksum = FI_CK_BAD; fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkv4sum_sum, fr_info_t *, fin, u_int, sum); } else { fin->fin_cksum = FI_CK_SUMOK; return 0; } } else { if (m->m_pkthdr.csum_flags == CSUM_DELAY_DATA) { fin->fin_cksum = FI_CK_L4FULL; return 0; } else if (m->m_pkthdr.csum_flags == CSUM_TCP || m->m_pkthdr.csum_flags == CSUM_UDP) { fin->fin_cksum = FI_CK_L4PART; return 0; } else if (m->m_pkthdr.csum_flags == CSUM_IP) { fin->fin_cksum = FI_CK_L4PART; return 0; } else { manual = 1; } } skipauto: if (manual != 0) { if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkv4sum_manual, fr_info_t *, fin, u_int, manual); return -1; } } #else if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkv4sum_checkl4sum, fr_info_t *, fin, u_int, -1); return -1; } #endif return 0; } #ifdef USE_INET6 INLINE int -ipf_checkv6sum(fin) - fr_info_t *fin; +ipf_checkv6sum(fr_info_t *fin) { if ((fin->fin_flx & FI_NOCKSUM) != 0) { DT(ipf_checkv6sum_fi_nocksum); return 0; } if ((fin->fin_flx & FI_SHORT) != 0) { DT(ipf_checkv6sum_fi_short); return 1; } if (fin->fin_cksum != FI_CK_NEEDED) { DT(ipf_checkv6sum_fi_ck_needed); return (fin->fin_cksum > FI_CK_NEEDED) ? 0 : -1; } if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkv6sum_checkl4sum, fr_info_t *, fin, u_int, -1); return -1; } return 0; } #endif /* USE_INET6 */ size_t -mbufchainlen(m0) - struct mbuf *m0; +mbufchainlen(struct mbuf *m0) { size_t len; if ((m0->m_flags & M_PKTHDR) != 0) { len = m0->m_pkthdr.len; } else { struct mbuf *m; for (m = m0, len = 0; m != NULL; m = m->m_next) len += m->m_len; } return len; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pullup */ /* Returns: NULL == pullup failed, else pointer to protocol header */ /* Parameters: xmin(I)- pointer to buffer where data packet starts */ /* fin(I) - pointer to packet information */ /* len(I) - number of bytes to pullup */ /* */ /* Attempt to move at least len bytes (from the start of the buffer) into a */ /* single buffer for ease of access. Operating system native functions are */ /* used to manage buffers - if necessary. If the entire packet ends up in */ /* a single buffer, set the FI_COALESCE flag even though ipf_coalesce() has */ /* not been called. Both fin_ip and fin_dp are updated before exiting _IF_ */ /* and ONLY if the pullup succeeds. */ /* */ /* We assume that 'xmin' is a pointer to a buffer that is part of the chain */ /* of buffers that starts at *fin->fin_mp. */ /* ------------------------------------------------------------------------ */ void * -ipf_pullup(xmin, fin, len) - mb_t *xmin; - fr_info_t *fin; - int len; +ipf_pullup(mb_t *xmin, fr_info_t *fin, int len) { int dpoff, ipoff; mb_t *m = xmin; char *ip; if (m == NULL) return NULL; ip = (char *)fin->fin_ip; if ((fin->fin_flx & FI_COALESCE) != 0) return ip; ipoff = fin->fin_ipoff; if (fin->fin_dp != NULL) dpoff = (char *)fin->fin_dp - (char *)ip; else dpoff = 0; if (M_LEN(m) < len) { mb_t *n = *fin->fin_mp; /* * Assume that M_PKTHDR is set and just work with what is left * rather than check.. * Should not make any real difference, anyway. */ if (m != n) { /* * Record the mbuf that points to the mbuf that we're * about to go to work on so that we can update the * m_next appropriately later. */ for (; n->m_next != m; n = n->m_next) ; } else { n = NULL; } #ifdef MHLEN if (len > MHLEN) #else if (len > MLEN) #endif { #ifdef HAVE_M_PULLDOWN if (m_pulldown(m, 0, len, NULL) == NULL) m = NULL; #else FREE_MB_T(*fin->fin_mp); m = NULL; n = NULL; #endif } else { m = m_pullup(m, len); } if (n != NULL) n->m_next = m; if (m == NULL) { /* * When n is non-NULL, it indicates that m pointed to * a sub-chain (tail) of the mbuf and that the head * of this chain has not yet been free'd. */ if (n != NULL) { FREE_MB_T(*fin->fin_mp); } *fin->fin_mp = NULL; fin->fin_m = NULL; return NULL; } if (n == NULL) *fin->fin_mp = m; while (M_LEN(m) == 0) { m = m->m_next; } fin->fin_m = m; ip = MTOD(m, char *) + ipoff; fin->fin_ip = (ip_t *)ip; if (fin->fin_dp != NULL) fin->fin_dp = (char *)fin->fin_ip + dpoff; if (fin->fin_fraghdr != NULL) fin->fin_fraghdr = (char *)ip + ((char *)fin->fin_fraghdr - (char *)fin->fin_ip); } if (len == fin->fin_plen) fin->fin_flx |= FI_COALESCE; return ip; } int -ipf_inject(fin, m) - fr_info_t *fin; - mb_t *m; +ipf_inject(fr_info_t *fin, mb_t *m) { int error = 0; if (fin->fin_out == 0) { netisr_dispatch(NETISR_IP, m); } else { fin->fin_ip->ip_len = ntohs(fin->fin_ip->ip_len); fin->fin_ip->ip_off = ntohs(fin->fin_ip->ip_off); error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL); } return error; } int ipf_pfil_unhook(void) { struct pfil_head *ph_inet; #ifdef USE_INET6 struct pfil_head *ph_inet6; #endif ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); if (ph_inet != NULL) pfil_remove_hook((void *)ipf_check_wrapper, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet); # ifdef USE_INET6 ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); if (ph_inet6 != NULL) pfil_remove_hook((void *)ipf_check_wrapper6, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6); # endif return (0); } int ipf_pfil_hook(void) { struct pfil_head *ph_inet; #ifdef USE_INET6 struct pfil_head *ph_inet6; #endif ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); # ifdef USE_INET6 ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); # endif if (ph_inet == NULL # ifdef USE_INET6 && ph_inet6 == NULL # endif ) { return ENODEV; } if (ph_inet != NULL) pfil_add_hook((void *)ipf_check_wrapper, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet); # ifdef USE_INET6 if (ph_inet6 != NULL) pfil_add_hook((void *)ipf_check_wrapper6, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6); # endif return (0); } void ipf_event_reg(void) { V_ipf_arrivetag = EVENTHANDLER_REGISTER(ifnet_arrival_event, \ ipf_ifevent, NULL, \ EVENTHANDLER_PRI_ANY); V_ipf_departtag = EVENTHANDLER_REGISTER(ifnet_departure_event, \ ipf_ifevent, NULL, \ EVENTHANDLER_PRI_ANY); #if 0 V_ipf_clonetag = EVENTHANDLER_REGISTER(if_clone_event, ipf_ifevent, \ NULL, EVENTHANDLER_PRI_ANY); #endif } void ipf_event_dereg(void) { if (V_ipf_arrivetag != NULL) { EVENTHANDLER_DEREGISTER(ifnet_arrival_event, V_ipf_arrivetag); } if (V_ipf_departtag != NULL) { EVENTHANDLER_DEREGISTER(ifnet_departure_event, V_ipf_departtag); } #if 0 if (V_ipf_clonetag != NULL) { EVENTHANDLER_DEREGISTER(if_clone_event, V_ipf_clonetag); } #endif } u_32_t -ipf_random() +ipf_random(void) { return arc4random(); } u_int -ipf_pcksum(fin, hlen, sum) - fr_info_t *fin; - int hlen; - u_int sum; +ipf_pcksum(fr_info_t *fin, int hlen, u_int sum) { struct mbuf *m; u_int sum2; int off; m = fin->fin_m; off = (char *)fin->fin_dp - (char *)fin->fin_ip; m->m_data += hlen; m->m_len -= hlen; sum2 = in_cksum(fin->fin_m, fin->fin_plen - off); m->m_len += hlen; m->m_data -= hlen; /* * Both sum and sum2 are partial sums, so combine them together. */ sum += ~sum2 & 0xffff; while (sum > 0xffff) sum = (sum & 0xffff) + (sum >> 16); sum2 = ~sum & 0xffff; return sum2; } #ifdef USE_INET6 u_int -ipf_pcksum6(m, ip6, off, len) - struct mbuf *m; - ip6_t *ip6; - u_int32_t off; - u_int32_t len; +ipf_pcksum6(struct mbuf *m, ip6_t *ip6, u_int32_t off, u_int32_t len) { #ifdef _KERNEL int sum; if (m->m_len < sizeof(struct ip6_hdr)) { return 0xffff; } sum = in6_cksum(m, ip6->ip6_nxt, off, len); return(sum); #else u_short *sp; u_int sum; sp = (u_short *)&ip6->ip6_src; sum = *sp++; /* ip6_src */ sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; /* ip6_dst */ sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; sum += *sp++; return(ipf_pcksum(fin, off, sum)); #endif } #endif void ipf_fbsd_kenv_get(ipf_main_softc_t *softc) { TUNABLE_INT_FETCH("net.inet.ipf.large_nat", &softc->ipf_large_nat); } diff --git a/sys/netpfil/ipfilter/netinet/ip_frag.c b/sys/netpfil/ipfilter/netinet/ip_frag.c index 4a2596e72563..72b552e69709 100644 --- a/sys/netpfil/ipfilter/netinet/ip_frag.c +++ b/sys/netpfil/ipfilter/netinet/ip_frag.c @@ -1,1360 +1,1298 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if !defined(_KERNEL) # include # include # include # define _KERNEL # include # undef _KERNEL #endif #if defined(_KERNEL) && defined(__FreeBSD__) # include # include #else # include #endif # include #include #if defined(_KERNEL) # include # if !defined(__SVR4) # include # endif #endif #if !defined(__SVR4) # if defined(_KERNEL) # include # endif #else # include # ifdef _KERNEL # include # endif # include # include #endif #include #ifdef sun # include #endif #include #include #include # include #include #include #include #include "netinet/ip_compat.h" #include #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_auth.h" #include "netinet/ip_lookup.h" #include "netinet/ip_proxy.h" #include "netinet/ip_sync.h" /* END OF INCLUDES */ #if !defined(lint) static const char sccsid[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-2000 Darren Reed"; static const char rcsid[] = "@(#)$FreeBSD$"; /* static const char rcsid[] = "@(#)$Id: ip_frag.c,v 2.77.2.12 2007/09/20 12:51:51 darrenr Exp $"; */ #endif #ifdef USE_MUTEXES static ipfr_t *ipfr_frag_new(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, u_32_t, ipfr_t **, ipfrwlock_t *); static ipfr_t *ipf_frag_lookup(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **, ipfrwlock_t *); static void ipf_frag_deref(void *, ipfr_t **, ipfrwlock_t *); static int ipf_frag_next(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, ipfr_t **, ipfrwlock_t *); #else static ipfr_t *ipfr_frag_new(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, u_32_t, ipfr_t **); static ipfr_t *ipf_frag_lookup(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **); static void ipf_frag_deref(void *, ipfr_t **); static int ipf_frag_next(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, ipfr_t **); #endif static void ipf_frag_delete(ipf_main_softc_t *, ipfr_t *, ipfr_t ***); static void ipf_frag_free(ipf_frag_softc_t *, ipfr_t *); static frentry_t ipfr_block; static ipftuneable_t ipf_frag_tuneables[] = { { { (void *)offsetof(ipf_frag_softc_t, ipfr_size) }, "frag_size", 1, 0x7fffffff, stsizeof(ipf_frag_softc_t, ipfr_size), IPFT_WRDISABLED, NULL, NULL }, { { (void *)offsetof(ipf_frag_softc_t, ipfr_ttl) }, "frag_ttl", 1, 0x7fffffff, stsizeof(ipf_frag_softc_t, ipfr_ttl), 0, NULL, NULL }, { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL } }; #define FBUMP(x) softf->ipfr_stats.x++ #define FBUMPD(x) do { softf->ipfr_stats.x++; DT(x); } while (0) /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_main_load */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: Nil */ /* */ /* Initialise the filter rule associted with blocked packets - everyone can */ /* use it. */ /* ------------------------------------------------------------------------ */ int -ipf_frag_main_load() +ipf_frag_main_load(void) { bzero((char *)&ipfr_block, sizeof(ipfr_block)); ipfr_block.fr_flags = FR_BLOCK|FR_QUICK; ipfr_block.fr_ref = 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_main_unload */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: Nil */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int -ipf_frag_main_unload() +ipf_frag_main_unload(void) { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_soft_create */ /* Returns: void * - NULL = failure, else pointer to local context */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Allocate a new soft context structure to track fragment related info. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ void * -ipf_frag_soft_create(softc) - ipf_main_softc_t *softc; +ipf_frag_soft_create(ipf_main_softc_t *softc) { ipf_frag_softc_t *softf; KMALLOC(softf, ipf_frag_softc_t *); if (softf == NULL) return NULL; bzero((char *)softf, sizeof(*softf)); RWLOCK_INIT(&softf->ipfr_ipidfrag, "frag ipid lock"); RWLOCK_INIT(&softf->ipfr_frag, "ipf fragment rwlock"); RWLOCK_INIT(&softf->ipfr_natfrag, "ipf NAT fragment rwlock"); softf->ipf_frag_tune = ipf_tune_array_copy(softf, sizeof(ipf_frag_tuneables), ipf_frag_tuneables); if (softf->ipf_frag_tune == NULL) { ipf_frag_soft_destroy(softc, softf); return NULL; } if (ipf_tune_array_link(softc, softf->ipf_frag_tune) == -1) { ipf_frag_soft_destroy(softc, softf); return NULL; } softf->ipfr_size = IPFT_SIZE; softf->ipfr_ttl = IPF_TTLVAL(60); softf->ipfr_lock = 1; softf->ipfr_tail = &softf->ipfr_list; softf->ipfr_nattail = &softf->ipfr_natlist; softf->ipfr_ipidtail = &softf->ipfr_ipidlist; return softf; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Initialise the hash tables for the fragment cache lookups. */ /* ------------------------------------------------------------------------ */ void -ipf_frag_soft_destroy(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_frag_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_frag_softc_t *softf = arg; RW_DESTROY(&softf->ipfr_ipidfrag); RW_DESTROY(&softf->ipfr_frag); RW_DESTROY(&softf->ipfr_natfrag); if (softf->ipf_frag_tune != NULL) { ipf_tune_array_unlink(softc, softf->ipf_frag_tune); KFREES(softf->ipf_frag_tune, sizeof(ipf_frag_tuneables)); softf->ipf_frag_tune = NULL; } KFREE(softf); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_soft_init */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Initialise the hash tables for the fragment cache lookups. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ int -ipf_frag_soft_init(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_frag_soft_init(ipf_main_softc_t *softc, void *arg) { ipf_frag_softc_t *softf = arg; KMALLOCS(softf->ipfr_heads, ipfr_t **, softf->ipfr_size * sizeof(ipfr_t *)); if (softf->ipfr_heads == NULL) return -1; bzero((char *)softf->ipfr_heads, softf->ipfr_size * sizeof(ipfr_t *)); KMALLOCS(softf->ipfr_nattab, ipfr_t **, softf->ipfr_size * sizeof(ipfr_t *)); if (softf->ipfr_nattab == NULL) return -2; bzero((char *)softf->ipfr_nattab, softf->ipfr_size * sizeof(ipfr_t *)); KMALLOCS(softf->ipfr_ipidtab, ipfr_t **, softf->ipfr_size * sizeof(ipfr_t *)); if (softf->ipfr_ipidtab == NULL) return -3; bzero((char *)softf->ipfr_ipidtab, softf->ipfr_size * sizeof(ipfr_t *)); softf->ipfr_lock = 0; softf->ipfr_inited = 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_soft_fini */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Free all memory allocated whilst running and from initialisation. */ /* ------------------------------------------------------------------------ */ int -ipf_frag_soft_fini(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_frag_soft_fini(ipf_main_softc_t *softc, void *arg) { ipf_frag_softc_t *softf = arg; softf->ipfr_lock = 1; if (softf->ipfr_inited == 1) { ipf_frag_clear(softc); softf->ipfr_inited = 0; } if (softf->ipfr_heads != NULL) KFREES(softf->ipfr_heads, softf->ipfr_size * sizeof(ipfr_t *)); softf->ipfr_heads = NULL; if (softf->ipfr_nattab != NULL) KFREES(softf->ipfr_nattab, softf->ipfr_size * sizeof(ipfr_t *)); softf->ipfr_nattab = NULL; if (softf->ipfr_ipidtab != NULL) KFREES(softf->ipfr_ipidtab, softf->ipfr_size * sizeof(ipfr_t *)); softf->ipfr_ipidtab = NULL; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_set_lock */ /* Returns: Nil */ /* Parameters: arg(I) - pointer to local context to use */ /* tmp(I) - new value for lock */ /* */ /* Stub function that allows for external manipulation of ipfr_lock */ /* ------------------------------------------------------------------------ */ void -ipf_frag_setlock(arg, tmp) - void *arg; - int tmp; +ipf_frag_setlock(void *arg, int tmp) { ipf_frag_softc_t *softf = arg; softf->ipfr_lock = tmp; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_stats */ /* Returns: ipfrstat_t* - pointer to struct with current frag stats */ /* Parameters: arg(I) - pointer to local context to use */ /* */ /* Updates ipfr_stats with current information and returns a pointer to it */ /* ------------------------------------------------------------------------ */ ipfrstat_t * -ipf_frag_stats(arg) - void *arg; +ipf_frag_stats(void *arg) { ipf_frag_softc_t *softf = arg; softf->ipfr_stats.ifs_table = softf->ipfr_heads; softf->ipfr_stats.ifs_nattab = softf->ipfr_nattab; return &softf->ipfr_stats; } /* ------------------------------------------------------------------------ */ /* Function: ipfr_frag_new */ /* Returns: ipfr_t * - pointer to fragment cache state info or NULL */ /* Parameters: fin(I) - pointer to packet information */ /* table(I) - pointer to frag table to add to */ /* lock(I) - pointer to lock to get a write hold of */ /* */ /* Add a new entry to the fragment cache, registering it as having come */ /* through this box, with the result of the filter operation. */ /* */ /* If this function succeeds, it returns with a write lock held on "lock". */ /* If it fails, no lock is held on return. */ /* ------------------------------------------------------------------------ */ static ipfr_t * -ipfr_frag_new(softc, softf, fin, pass, table +ipfr_frag_new(ipf_main_softc_t *softc, ipf_frag_softc_t *softf, + fr_info_t *fin, u_32_t pass, ipfr_t *table[] #ifdef USE_MUTEXES -, lock +, ipfrwlock_t *lock #endif ) - ipf_main_softc_t *softc; - ipf_frag_softc_t *softf; - fr_info_t *fin; - u_32_t pass; - ipfr_t *table[]; -#ifdef USE_MUTEXES - ipfrwlock_t *lock; -#endif { ipfr_t *fra, frag, *fran; u_int idx, off; frentry_t *fr; if (softf->ipfr_stats.ifs_inuse >= softf->ipfr_size) { FBUMPD(ifs_maximum); return NULL; } if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) { FBUMPD(ifs_newbad); return NULL; } if (pass & FR_FRSTRICT) { if (fin->fin_off != 0) { FBUMPD(ifs_newrestrictnot0); return NULL; } } memset(&frag, 0, sizeof(frag)); frag.ipfr_v = fin->fin_v; idx = fin->fin_v; frag.ipfr_p = fin->fin_p; idx += fin->fin_p; frag.ipfr_id = fin->fin_id; idx += fin->fin_id; frag.ipfr_source = fin->fin_fi.fi_src; idx += frag.ipfr_src.s_addr; frag.ipfr_dest = fin->fin_fi.fi_dst; idx += frag.ipfr_dst.s_addr; frag.ipfr_ifp = fin->fin_ifp; idx *= 127; idx %= softf->ipfr_size; frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; frag.ipfr_auth = fin->fin_fi.fi_auth; off = fin->fin_off >> 3; if (off == 0) { char *ptr; int end; #ifdef USE_INET6 if (fin->fin_v == 6) { ptr = (char *)fin->fin_fraghdr + sizeof(struct ip6_frag); } else #endif { ptr = fin->fin_dp; } end = fin->fin_plen - (ptr - (char *)fin->fin_ip); frag.ipfr_firstend = end >> 3; } else { frag.ipfr_firstend = 0; } /* * allocate some memory, if possible, if not, just record that we * failed to do so. */ KMALLOC(fran, ipfr_t *); if (fran == NULL) { FBUMPD(ifs_nomem); return NULL; } memset(fran, 0, sizeof(*fran)); WRITE_ENTER(lock); /* * first, make sure it isn't already there... */ for (fra = table[idx]; (fra != NULL); fra = fra->ipfr_hnext) if (!bcmp((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ)) { RWLOCK_EXIT(lock); FBUMPD(ifs_exists); KFREE(fran); return NULL; } fra = fran; fran = NULL; fr = fin->fin_fr; fra->ipfr_rule = fr; if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } /* * Insert the fragment into the fragment table, copy the struct used * in the search using bcopy rather than reassign each field. * Set the ttl to the default. */ if ((fra->ipfr_hnext = table[idx]) != NULL) table[idx]->ipfr_hprev = &fra->ipfr_hnext; fra->ipfr_hprev = table + idx; fra->ipfr_data = NULL; table[idx] = fra; bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ); fra->ipfr_v = fin->fin_v; fra->ipfr_p = fin->fin_p; fra->ipfr_ttl = softc->ipf_ticks + softf->ipfr_ttl; fra->ipfr_firstend = frag.ipfr_firstend; /* * Compute the offset of the expected start of the next packet. */ if (off == 0) fra->ipfr_seen0 = 1; fra->ipfr_off = off + (fin->fin_dlen >> 3); fra->ipfr_pass = pass; fra->ipfr_ref = 1; fra->ipfr_pkts = 1; fra->ipfr_bytes = fin->fin_plen; FBUMP(ifs_inuse); FBUMP(ifs_new); return fra; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_new */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Add a new entry to the fragment cache table based on the current packet */ /* ------------------------------------------------------------------------ */ int -ipf_frag_new(softc, fin, pass) - ipf_main_softc_t *softc; - u_32_t pass; - fr_info_t *fin; +ipf_frag_new(ipf_main_softc_t *softc, fr_info_t *fin, u_32_t pass) { ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fra; if (softf->ipfr_lock != 0) return -1; #ifdef USE_MUTEXES fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads, &softc->ipf_frag); #else fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads); #endif if (fra != NULL) { *softf->ipfr_tail = fra; fra->ipfr_prev = softf->ipfr_tail; softf->ipfr_tail = &fra->ipfr_next; fra->ipfr_next = NULL; RWLOCK_EXIT(&softc->ipf_frag); } return fra ? 0 : -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_natnew */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* */ /* Create a new NAT fragment cache entry based on the current packet and */ /* the NAT structure for this "session". */ /* ------------------------------------------------------------------------ */ int -ipf_frag_natnew(softc, fin, pass, nat) - ipf_main_softc_t *softc; - fr_info_t *fin; - u_32_t pass; - nat_t *nat; +ipf_frag_natnew(ipf_main_softc_t *softc, fr_info_t *fin, u_32_t pass, + nat_t *nat) { ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fra; if (softf->ipfr_lock != 0) return 0; #ifdef USE_MUTEXES fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab, &softf->ipfr_natfrag); #else fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab); #endif if (fra != NULL) { fra->ipfr_data = nat; nat->nat_data = fra; *softf->ipfr_nattail = fra; fra->ipfr_prev = softf->ipfr_nattail; softf->ipfr_nattail = &fra->ipfr_next; fra->ipfr_next = NULL; RWLOCK_EXIT(&softf->ipfr_natfrag); return 0; } return -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_ipidnew */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: fin(I) - pointer to packet information */ /* ipid(I) - new IP ID for this fragmented packet */ /* */ /* Create a new fragment cache entry for this packet and store, as a data */ /* pointer, the new IP ID value. */ /* ------------------------------------------------------------------------ */ int -ipf_frag_ipidnew(fin, ipid) - fr_info_t *fin; - u_32_t ipid; +ipf_frag_ipidnew(fr_info_t *fin, u_32_t ipid) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fra; if (softf->ipfr_lock) return 0; #ifdef USE_MUTEXES fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab, &softf->ipfr_ipidfrag); #else fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab); #endif if (fra != NULL) { fra->ipfr_data = (void *)(intptr_t)ipid; *softf->ipfr_ipidtail = fra; fra->ipfr_prev = softf->ipfr_ipidtail; softf->ipfr_ipidtail = &fra->ipfr_next; fra->ipfr_next = NULL; RWLOCK_EXIT(&softf->ipfr_ipidfrag); } return fra ? 0 : -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_lookup */ /* Returns: ipfr_t * - pointer to ipfr_t structure if there's a */ /* matching entry in the frag table, else NULL */ /* Parameters: fin(I) - pointer to packet information */ /* table(I) - pointer to fragment cache table to search */ /* */ /* Check the fragment cache to see if there is already a record of this */ /* packet with its filter result known. */ /* */ /* If this function succeeds, it returns with a write lock held on "lock". */ /* If it fails, no lock is held on return. */ /* ------------------------------------------------------------------------ */ static ipfr_t * -ipf_frag_lookup(softc, softf, fin, table +ipf_frag_lookup(ipf_main_softc_t *softc, ipf_frag_softc_t *softf, + fr_info_t *fin, ipfr_t *table[] #ifdef USE_MUTEXES -, lock +, ipfrwlock_t *lock #endif ) - ipf_main_softc_t *softc; - ipf_frag_softc_t *softf; - fr_info_t *fin; - ipfr_t *table[]; -#ifdef USE_MUTEXES - ipfrwlock_t *lock; -#endif { ipfr_t *f, frag; u_int idx; /* * We don't want to let short packets match because they could be * compromising the security of other rules that want to match on * layer 4 fields (and can't because they have been fragmented off.) * Why do this check here? The counter acts as an indicator of this * kind of attack, whereas if it was elsewhere, it wouldn't know if * other matching packets had been seen. */ if (fin->fin_flx & FI_SHORT) { FBUMPD(ifs_short); return NULL; } if ((fin->fin_flx & FI_BAD) != 0) { FBUMPD(ifs_bad); return NULL; } /* * For fragments, we record protocol, packet id, TOS and both IP#'s * (these should all be the same for all fragments of a packet). * * build up a hash value to index the table with. */ memset(&frag, 0, sizeof(frag)); frag.ipfr_v = fin->fin_v; idx = fin->fin_v; frag.ipfr_p = fin->fin_p; idx += fin->fin_p; frag.ipfr_id = fin->fin_id; idx += fin->fin_id; frag.ipfr_source = fin->fin_fi.fi_src; idx += frag.ipfr_src.s_addr; frag.ipfr_dest = fin->fin_fi.fi_dst; idx += frag.ipfr_dst.s_addr; frag.ipfr_ifp = fin->fin_ifp; idx *= 127; idx %= softf->ipfr_size; frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; frag.ipfr_auth = fin->fin_fi.fi_auth; READ_ENTER(lock); /* * check the table, careful to only compare the right amount of data */ for (f = table[idx]; f; f = f->ipfr_hnext) { if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp, IPFR_CMPSZ)) { u_short off; /* * XXX - We really need to be guarding against the * retransmission of (src,dst,id,offset-range) here * because a fragmented packet is never resent with * the same IP ID# (or shouldn't). */ off = fin->fin_off >> 3; if (f->ipfr_seen0) { if (off == 0) { FBUMPD(ifs_retrans0); continue; } /* * Case 3. See comment for frpr_fragment6. */ if ((f->ipfr_firstend != 0) && (off < f->ipfr_firstend)) { FBUMP(ifs_overlap); DT2(ifs_overlap, u_short, off, ipfr_t *, f); DT3(ipf_fi_bad_ifs_overlap, fr_info_t *, fin, u_short, off, ipfr_t *, f); fin->fin_flx |= FI_BAD; break; } } else if (off == 0) f->ipfr_seen0 = 1; if (f != table[idx] && MUTEX_TRY_UPGRADE(lock)) { ipfr_t **fp; /* * Move fragment info. to the top of the list * to speed up searches. First, delink... */ fp = f->ipfr_hprev; (*fp) = f->ipfr_hnext; if (f->ipfr_hnext != NULL) f->ipfr_hnext->ipfr_hprev = fp; /* * Then put back at the top of the chain. */ f->ipfr_hnext = table[idx]; table[idx]->ipfr_hprev = &f->ipfr_hnext; f->ipfr_hprev = table + idx; table[idx] = f; MUTEX_DOWNGRADE(lock); } /* * If we've follwed the fragments, and this is the * last (in order), shrink expiration time. */ if (off == f->ipfr_off) { f->ipfr_off = (fin->fin_dlen >> 3) + off; /* * Well, we could shrink the expiration time * but only if every fragment has been seen * in order upto this, the last. ipfr_badorder * is used here to count those out of order * and if it equals 0 when we get to the last * fragment then we can assume all of the * fragments have been seen and in order. */ #if 0 /* * Doing this properly requires moving it to * the head of the list which is infesible. */ if ((more == 0) && (f->ipfr_badorder == 0)) f->ipfr_ttl = softc->ipf_ticks + 1; #endif } else { f->ipfr_badorder++; FBUMPD(ifs_unordered); if (f->ipfr_pass & FR_FRSTRICT) { FBUMPD(ifs_strict); continue; } } f->ipfr_pkts++; f->ipfr_bytes += fin->fin_plen; FBUMP(ifs_hits); return f; } } RWLOCK_EXIT(lock); FBUMP(ifs_miss); return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_natknown */ /* Returns: nat_t* - pointer to 'parent' NAT structure if frag table */ /* match found, else NULL */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Functional interface for NAT lookups of the NAT fragment cache */ /* ------------------------------------------------------------------------ */ nat_t * -ipf_frag_natknown(fin) - fr_info_t *fin; +ipf_frag_natknown(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_frag_softc_t *softf = softc->ipf_frag_soft; nat_t *nat; ipfr_t *ipf; if ((softf->ipfr_lock) || !softf->ipfr_natlist) return NULL; #ifdef USE_MUTEXES ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab, &softf->ipfr_natfrag); #else ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab); #endif if (ipf != NULL) { nat = ipf->ipfr_data; /* * This is the last fragment for this packet. */ if ((ipf->ipfr_ttl == softc->ipf_ticks + 1) && (nat != NULL)) { nat->nat_data = NULL; ipf->ipfr_data = NULL; } RWLOCK_EXIT(&softf->ipfr_natfrag); } else nat = NULL; return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_ipidknown */ /* Returns: u_32_t - IPv4 ID for this packet if match found, else */ /* return 0xfffffff to indicate no match. */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Functional interface for IP ID lookups of the IP ID fragment cache */ /* ------------------------------------------------------------------------ */ u_32_t -ipf_frag_ipidknown(fin) - fr_info_t *fin; +ipf_frag_ipidknown(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *ipf; u_32_t id; if (softf->ipfr_lock || !softf->ipfr_ipidlist) return 0xffffffff; #ifdef USE_MUTEXES ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab, &softf->ipfr_ipidfrag); #else ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab); #endif if (ipf != NULL) { id = (u_32_t)(intptr_t)ipf->ipfr_data; RWLOCK_EXIT(&softf->ipfr_ipidfrag); } else id = 0xffffffff; return id; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_known */ /* Returns: frentry_t* - pointer to filter rule if a match is found in */ /* the frag cache table, else NULL. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(O) - pointer to where to store rule flags resturned */ /* */ /* Functional interface for normal lookups of the fragment cache. If a */ /* match is found, return the rule pointer and flags from the rule, except */ /* that if FR_LOGFIRST is set, reset FR_LOG. */ /* ------------------------------------------------------------------------ */ frentry_t * -ipf_frag_known(fin, passp) - fr_info_t *fin; - u_32_t *passp; +ipf_frag_known(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_frag_softc_t *softf = softc->ipf_frag_soft; frentry_t *fr = NULL; ipfr_t *fra; u_32_t pass; if ((softf->ipfr_lock) || (softf->ipfr_list == NULL)) return NULL; #ifdef USE_MUTEXES fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads, &softc->ipf_frag); #else fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads); #endif if (fra != NULL) { if (fin->fin_flx & FI_BAD) { fr = &ipfr_block; fin->fin_reason = FRB_BADFRAG; DT2(ipf_frb_badfrag, fr_info_t *, fin, uint, fra); } else { fr = fra->ipfr_rule; } fin->fin_fr = fr; if (fr != NULL) { pass = fr->fr_flags; if ((pass & FR_KEEPSTATE) != 0) { fin->fin_flx |= FI_STATE; /* * Reset the keep state flag here so that we * don't try and add a new state entry because * of a match here. That leads to blocking of * the packet later because the add fails. */ pass &= ~FR_KEEPSTATE; } if ((pass & FR_LOGFIRST) != 0) pass &= ~(FR_LOGFIRST|FR_LOG); *passp = pass; } RWLOCK_EXIT(&softc->ipf_frag); } return fr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_natforget */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* ptr(I) - pointer to data structure */ /* */ /* Search through all of the fragment cache entries for NAT and wherever a */ /* pointer is found to match ptr, reset it to NULL. */ /* ------------------------------------------------------------------------ */ void -ipf_frag_natforget(softc, ptr) - ipf_main_softc_t *softc; - void *ptr; +ipf_frag_natforget(ipf_main_softc_t *softc, void *ptr) { ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fr; WRITE_ENTER(&softf->ipfr_natfrag); for (fr = softf->ipfr_natlist; fr; fr = fr->ipfr_next) if (fr->ipfr_data == ptr) fr->ipfr_data = NULL; RWLOCK_EXIT(&softf->ipfr_natfrag); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_delete */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fra(I) - pointer to fragment structure to delete */ /* tail(IO) - pointer to the pointer to the tail of the frag */ /* list */ /* */ /* Remove a fragment cache table entry from the table & list. Also free */ /* the filter rule it is associated with it if it is no longer used as a */ /* result of decreasing the reference count. */ /* ------------------------------------------------------------------------ */ static void -ipf_frag_delete(softc, fra, tail) - ipf_main_softc_t *softc; - ipfr_t *fra, ***tail; +ipf_frag_delete(ipf_main_softc_t *softc, ipfr_t *fra, ipfr_t ***tail) { ipf_frag_softc_t *softf = softc->ipf_frag_soft; if (fra->ipfr_next) fra->ipfr_next->ipfr_prev = fra->ipfr_prev; *fra->ipfr_prev = fra->ipfr_next; if (*tail == &fra->ipfr_next) *tail = fra->ipfr_prev; if (fra->ipfr_hnext) fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev; *fra->ipfr_hprev = fra->ipfr_hnext; if (fra->ipfr_rule != NULL) { (void) ipf_derefrule(softc, &fra->ipfr_rule); } if (fra->ipfr_ref <= 0) ipf_frag_free(softf, fra); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_free */ /* Returns: Nil */ /* Parameters: softf(I) - pointer to fragment context information */ /* fra(I) - pointer to fragment structure to free */ /* */ /* Free up a fragment cache entry and bump relevent statistics. */ /* ------------------------------------------------------------------------ */ static void -ipf_frag_free(softf, fra) - ipf_frag_softc_t *softf; - ipfr_t *fra; +ipf_frag_free(ipf_frag_softc_t *softf, ipfr_t *fra) { KFREE(fra); FBUMP(ifs_expire); softf->ipfr_stats.ifs_inuse--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_clear */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Free memory in use by fragment state information kept. Do the normal */ /* fragment state stuff first and then the NAT-fragment table. */ /* ------------------------------------------------------------------------ */ void -ipf_frag_clear(softc) - ipf_main_softc_t *softc; +ipf_frag_clear(ipf_main_softc_t *softc) { ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fra; nat_t *nat; WRITE_ENTER(&softc->ipf_frag); while ((fra = softf->ipfr_list) != NULL) { fra->ipfr_ref--; ipf_frag_delete(softc, fra, &softf->ipfr_tail); } softf->ipfr_tail = &softf->ipfr_list; RWLOCK_EXIT(&softc->ipf_frag); WRITE_ENTER(&softc->ipf_nat); WRITE_ENTER(&softf->ipfr_natfrag); while ((fra = softf->ipfr_natlist) != NULL) { nat = fra->ipfr_data; if (nat != NULL) { if (nat->nat_data == fra) nat->nat_data = NULL; } fra->ipfr_ref--; ipf_frag_delete(softc, fra, &softf->ipfr_nattail); } softf->ipfr_nattail = &softf->ipfr_natlist; RWLOCK_EXIT(&softf->ipfr_natfrag); RWLOCK_EXIT(&softc->ipf_nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Expire entries in the fragment cache table that have been there too long */ /* ------------------------------------------------------------------------ */ void -ipf_frag_expire(softc) - ipf_main_softc_t *softc; +ipf_frag_expire(ipf_main_softc_t *softc) { ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t **fp, *fra; nat_t *nat; SPL_INT(s); if (softf->ipfr_lock) return; SPL_NET(s); WRITE_ENTER(&softc->ipf_frag); /* * Go through the entire table, looking for entries to expire, * which is indicated by the ttl being less than or equal to ipf_ticks. */ for (fp = &softf->ipfr_list; ((fra = *fp) != NULL); ) { if (fra->ipfr_ttl > softc->ipf_ticks) break; fra->ipfr_ref--; ipf_frag_delete(softc, fra, &softf->ipfr_tail); } RWLOCK_EXIT(&softc->ipf_frag); WRITE_ENTER(&softf->ipfr_ipidfrag); for (fp = &softf->ipfr_ipidlist; ((fra = *fp) != NULL); ) { if (fra->ipfr_ttl > softc->ipf_ticks) break; fra->ipfr_ref--; ipf_frag_delete(softc, fra, &softf->ipfr_ipidtail); } RWLOCK_EXIT(&softf->ipfr_ipidfrag); /* * Same again for the NAT table, except that if the structure also * still points to a NAT structure, and the NAT structure points back * at the one to be free'd, NULL the reference from the NAT struct. * NOTE: We need to grab both mutex's early, and in this order so as * to prevent a deadlock if both try to expire at the same time. * The extra if() statement here is because it locks out all NAT * operations - no need to do that if there are no entries in this * list, right? */ if (softf->ipfr_natlist != NULL) { WRITE_ENTER(&softc->ipf_nat); WRITE_ENTER(&softf->ipfr_natfrag); for (fp = &softf->ipfr_natlist; ((fra = *fp) != NULL); ) { if (fra->ipfr_ttl > softc->ipf_ticks) break; nat = fra->ipfr_data; if (nat != NULL) { if (nat->nat_data == fra) nat->nat_data = NULL; } fra->ipfr_ref--; ipf_frag_delete(softc, fra, &softf->ipfr_nattail); } RWLOCK_EXIT(&softf->ipfr_natfrag); RWLOCK_EXIT(&softc->ipf_nat); } SPL_X(s); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_pkt_next */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to token information for this caller */ /* itp(I) - pointer to generic iterator from caller */ /* */ /* This function is used to step through the fragment cache list used for */ /* filter rules. The hard work is done by the more generic ipf_frag_next. */ /* ------------------------------------------------------------------------ */ int -ipf_frag_pkt_next(softc, token, itp) - ipf_main_softc_t *softc; - ipftoken_t *token; - ipfgeniter_t *itp; +ipf_frag_pkt_next(ipf_main_softc_t *softc, ipftoken_t *token, + ipfgeniter_t *itp) { ipf_frag_softc_t *softf = softc->ipf_frag_soft; #ifdef USE_MUTEXES return ipf_frag_next(softc, token, itp, &softf->ipfr_list, &softf->ipfr_frag); #else return ipf_frag_next(softc, token, itp, &softf->ipfr_list); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_nat_next */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to token information for this caller */ /* itp(I) - pointer to generic iterator from caller */ /* */ /* This function is used to step through the fragment cache list used for */ /* NAT. The hard work is done by the more generic ipf_frag_next. */ /* ------------------------------------------------------------------------ */ int -ipf_frag_nat_next(softc, token, itp) - ipf_main_softc_t *softc; - ipftoken_t *token; - ipfgeniter_t *itp; +ipf_frag_nat_next(ipf_main_softc_t *softc, ipftoken_t *token, + ipfgeniter_t *itp) { ipf_frag_softc_t *softf = softc->ipf_frag_soft; #ifdef USE_MUTEXES return ipf_frag_next(softc, token, itp, &softf->ipfr_natlist, &softf->ipfr_natfrag); #else return ipf_frag_next(softc, token, itp, &softf->ipfr_natlist); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_next */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to token information for this caller */ /* itp(I) - pointer to generic iterator from caller */ /* top(I) - top of the fragment list */ /* lock(I) - fragment cache lock */ /* */ /* This function is used to interate through the list of entries in the */ /* fragment cache. It increases the reference count on the one currently */ /* being returned so that the caller can come back and resume from it later.*/ /* */ /* This function is used for both the NAT fragment cache as well as the ipf */ /* fragment cache - hence the reason for passing in top and lock. */ /* ------------------------------------------------------------------------ */ static int -ipf_frag_next(softc, token, itp, top +ipf_frag_next(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp, + ipfr_t **top #ifdef USE_MUTEXES -, lock +, ipfrwlock_t *lock #endif ) - ipf_main_softc_t *softc; - ipftoken_t *token; - ipfgeniter_t *itp; - ipfr_t **top; -#ifdef USE_MUTEXES - ipfrwlock_t *lock; -#endif { ipfr_t *frag, *next, zero; int error = 0; if (itp->igi_data == NULL) { IPFERROR(20001); return EFAULT; } if (itp->igi_nitems != 1) { IPFERROR(20003); return EFAULT; } frag = token->ipt_data; READ_ENTER(lock); if (frag == NULL) next = *top; else next = frag->ipfr_next; if (next != NULL) { ATOMIC_INC(next->ipfr_ref); token->ipt_data = next; } else { bzero(&zero, sizeof(zero)); next = &zero; token->ipt_data = NULL; } if (next->ipfr_next == NULL) ipf_token_mark_complete(token); RWLOCK_EXIT(lock); error = COPYOUT(next, itp->igi_data, sizeof(*next)); if (error != 0) IPFERROR(20002); if (frag != NULL) { #ifdef USE_MUTEXES ipf_frag_deref(softc, &frag, lock); #else ipf_frag_deref(softc, &frag); #endif } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_pkt_deref */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to frag cache pointer */ /* */ /* This function is the external interface for dropping a reference to a */ /* fragment cache entry used by filter rules. */ /* ------------------------------------------------------------------------ */ void -ipf_frag_pkt_deref(softc, data) - ipf_main_softc_t *softc; - void *data; +ipf_frag_pkt_deref(ipf_main_softc_t *softc, void *data) { ipfr_t **frp = data; #ifdef USE_MUTEXES ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_frag); #else ipf_frag_deref(softc->ipf_frag_soft, frp); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_nat_deref */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to frag cache pointer */ /* */ /* This function is the external interface for dropping a reference to a */ /* fragment cache entry used by NAT table entries. */ /* ------------------------------------------------------------------------ */ void -ipf_frag_nat_deref(softc, data) - ipf_main_softc_t *softc; - void *data; +ipf_frag_nat_deref(ipf_main_softc_t *softc, void *data) { ipfr_t **frp = data; #ifdef USE_MUTEXES ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_natfrag); #else ipf_frag_deref(softc->ipf_frag_soft, frp); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_deref */ /* Returns: Nil */ /* Parameters: frp(IO) - pointer to fragment structure to deference */ /* lock(I) - lock associated with the fragment */ /* */ /* This function dereferences a fragment structure (ipfr_t). The pointer */ /* passed in will always be reset back to NULL, even if the structure is */ /* not freed, to enforce the notion that the caller is no longer entitled */ /* to use the pointer it is dropping the reference to. */ /* ------------------------------------------------------------------------ */ static void -ipf_frag_deref(arg, frp +ipf_frag_deref(void *arg, ipfr_t **frp #ifdef USE_MUTEXES -, lock +, ipfrwlock_t *lock #endif ) - void *arg; - ipfr_t **frp; -#ifdef USE_MUTEXES - ipfrwlock_t *lock; -#endif { ipf_frag_softc_t *softf = arg; ipfr_t *fra; fra = *frp; *frp = NULL; WRITE_ENTER(lock); fra->ipfr_ref--; if (fra->ipfr_ref <= 0) ipf_frag_free(softf, fra); RWLOCK_EXIT(lock); } diff --git a/sys/netpfil/ipfilter/netinet/ip_ftp_pxy.c b/sys/netpfil/ipfilter/netinet/ip_ftp_pxy.c index 9dcfca35de24..0c9f88bf566f 100644 --- a/sys/netpfil/ipfilter/netinet/ip_ftp_pxy.c +++ b/sys/netpfil/ipfilter/netinet/ip_ftp_pxy.c @@ -1,2136 +1,2054 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * Simple FTP transparent proxy for in-kernel use. For use with the NAT * code. * * $FreeBSD$ * Id: ip_ftp_pxy.c,v 2.88.2.19 2006/04/01 10:14:53 darrenr Exp $ */ #define IPF_FTP_PROXY #define IPF_MINPORTLEN 18 #define IPF_MINEPRTLEN 20 #define IPF_MAXPORTLEN 30 #define IPF_MIN227LEN 39 #define IPF_MAX227LEN 51 #define IPF_MIN229LEN 47 #define IPF_MAX229LEN 51 #define FTPXY_GO 0 #define FTPXY_INIT 1 #define FTPXY_USER_1 2 #define FTPXY_USOK_1 3 #define FTPXY_PASS_1 4 #define FTPXY_PAOK_1 5 #define FTPXY_AUTH_1 6 #define FTPXY_AUOK_1 7 #define FTPXY_ADAT_1 8 #define FTPXY_ADOK_1 9 #define FTPXY_ACCT_1 10 #define FTPXY_ACOK_1 11 #define FTPXY_USER_2 12 #define FTPXY_USOK_2 13 #define FTPXY_PASS_2 14 #define FTPXY_PAOK_2 15 #define FTPXY_JUNK_OK 0 #define FTPXY_JUNK_BAD 1 /* Ignore all commands for this connection */ #define FTPXY_JUNK_EOL 2 /* consume the rest of this line only */ #define FTPXY_JUNK_CONT 3 /* Saerching for next numeric */ /* * Values for FTP commands. Numerics cover 0-999 */ #define FTPXY_C_PASV 1000 #define FTPXY_C_PORT 1001 #define FTPXY_C_EPSV 1002 #define FTPXY_C_EPRT 1003 typedef struct ipf_ftp_softc_s { int ipf_p_ftp_pasvonly; /* Do not require logins before transfers */ int ipf_p_ftp_insecure; int ipf_p_ftp_pasvrdr; /* PASV must be last command prior to 227 */ int ipf_p_ftp_forcepasv; int ipf_p_ftp_debug; int ipf_p_ftp_single_xfer; void *ipf_p_ftp_tune; } ipf_ftp_softc_t; void ipf_p_ftp_main_load(void); void ipf_p_ftp_main_unload(void); void *ipf_p_ftp_soft_create(ipf_main_softc_t *); void ipf_p_ftp_soft_destroy(ipf_main_softc_t *, void *); int ipf_p_ftp_client(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int); int ipf_p_ftp_complete(char *, size_t); int ipf_p_ftp_in(void *, fr_info_t *, ap_session_t *, nat_t *); int ipf_p_ftp_new(void *, fr_info_t *, ap_session_t *, nat_t *); void ipf_p_ftp_del(ipf_main_softc_t *, ap_session_t *); int ipf_p_ftp_out(void *, fr_info_t *, ap_session_t *, nat_t *); int ipf_p_ftp_pasv(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int); int ipf_p_ftp_epsv(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int); int ipf_p_ftp_port(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int); int ipf_p_ftp_process(ipf_ftp_softc_t *, fr_info_t *, nat_t *, ftpinfo_t *, int); int ipf_p_ftp_server(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int); int ipf_p_ftp_valid(ipf_ftp_softc_t *, ftpinfo_t *, int, char *, size_t); int ipf_p_ftp_server_valid(ipf_ftp_softc_t *, ftpside_t *, char *, size_t); int ipf_p_ftp_client_valid(ipf_ftp_softc_t *, ftpside_t *, char *, size_t); u_short ipf_p_ftp_atoi(char **); int ipf_p_ftp_pasvreply(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, u_int, char *, char *); int ipf_p_ftp_eprt(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int); int ipf_p_ftp_eprt4(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int); int ipf_p_ftp_eprt6(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int); int ipf_p_ftp_addport(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int, int, int); void ipf_p_ftp_setpending(ipf_main_softc_t *, ftpinfo_t *); /* * Debug levels */ #define DEBUG_SECURITY 0x01 #define DEBUG_ERROR 0x02 #define DEBUG_INFO 0x04 #define DEBUG_PARSE_ERR 0x08 #define DEBUG_PARSE_INFO 0x10 #define DEBUG_PARSE 0x20 static int ipf_p_ftp_proxy_init = 0; static frentry_t ftppxyfr; static ipftuneable_t ipf_ftp_tuneables[] = { { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_debug) }, "ftp_debug", 0, 0x7f, stsizeof(ipf_ftp_softc_t, ipf_p_ftp_debug), 0, NULL, NULL }, { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly) }, "ftp_pasvonly", 0, 1, stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly), 0, NULL, NULL }, { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_insecure) }, "ftp_insecure", 0, 1, stsizeof(ipf_ftp_softc_t, ipf_p_ftp_insecure), 0, NULL, NULL }, { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr) }, "ftp_pasvrdr", 0, 1, stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr), 0, NULL, NULL }, { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv) }, "ftp_forcepasv", 0, 1, stsizeof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv), 0, NULL, NULL }, { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer) }, "ftp_single_xfer", 0, 1, stsizeof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer), 0, NULL, NULL }, { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL } }; void -ipf_p_ftp_main_load() +ipf_p_ftp_main_load(void) { bzero((char *)&ftppxyfr, sizeof(ftppxyfr)); ftppxyfr.fr_ref = 1; ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&ftppxyfr.fr_lock, "FTP Proxy Mutex"); ipf_p_ftp_proxy_init = 1; } void -ipf_p_ftp_main_unload() +ipf_p_ftp_main_unload(void) { if (ipf_p_ftp_proxy_init == 1) { MUTEX_DESTROY(&ftppxyfr.fr_lock); ipf_p_ftp_proxy_init = 0; } } /* * Initialize local structures. */ void * -ipf_p_ftp_soft_create(softc) - ipf_main_softc_t *softc; +ipf_p_ftp_soft_create(ipf_main_softc_t *softc) { ipf_ftp_softc_t *softf; KMALLOC(softf, ipf_ftp_softc_t *); if (softf == NULL) return NULL; bzero((char *)softf, sizeof(*softf)); #if defined(_KERNEL) softf->ipf_p_ftp_debug = 0; #else softf->ipf_p_ftp_debug = DEBUG_PARSE_ERR; #endif softf->ipf_p_ftp_forcepasv = 1; softf->ipf_p_ftp_tune = ipf_tune_array_copy(softf, sizeof(ipf_ftp_tuneables), ipf_ftp_tuneables); if (softf->ipf_p_ftp_tune == NULL) { ipf_p_ftp_soft_destroy(softc, softf); return NULL; } if (ipf_tune_array_link(softc, softf->ipf_p_ftp_tune) == -1) { ipf_p_ftp_soft_destroy(softc, softf); return NULL; } return softf; } void -ipf_p_ftp_soft_destroy(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_p_ftp_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_ftp_softc_t *softf = arg; if (softf->ipf_p_ftp_tune != NULL) { ipf_tune_array_unlink(softc, softf->ipf_p_ftp_tune); KFREES(softf->ipf_p_ftp_tune, sizeof(ipf_ftp_tuneables)); softf->ipf_p_ftp_tune = NULL; } KFREE(softf); } int -ipf_p_ftp_new(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_ftp_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { ftpinfo_t *ftp; ftpside_t *f; KMALLOC(ftp, ftpinfo_t *); if (ftp == NULL) return -1; nat = nat; /* LINT */ aps->aps_data = ftp; aps->aps_psiz = sizeof(ftpinfo_t); aps->aps_sport = htons(fin->fin_sport); aps->aps_dport = htons(fin->fin_dport); bzero((char *)ftp, sizeof(*ftp)); f = &ftp->ftp_side[0]; f->ftps_rptr = f->ftps_buf; f->ftps_wptr = f->ftps_buf; f = &ftp->ftp_side[1]; f->ftps_rptr = f->ftps_buf; f->ftps_wptr = f->ftps_buf; ftp->ftp_passok = FTPXY_INIT; ftp->ftp_incok = 0; return 0; } void ipf_p_ftp_setpending(ipf_main_softc_t *softc, ftpinfo_t *ftp) { if (ftp->ftp_pendnat != NULL) ipf_nat_setpending(softc, ftp->ftp_pendnat); if (ftp->ftp_pendstate != NULL) { READ_ENTER(&softc->ipf_state); ipf_state_setpending(softc, ftp->ftp_pendstate); RWLOCK_EXIT(&softc->ipf_state); } } void ipf_p_ftp_del(softc, aps) ipf_main_softc_t *softc; ap_session_t *aps; { ftpinfo_t *ftp; ftp = aps->aps_data; if (ftp != NULL) ipf_p_ftp_setpending(softc, ftp); } int -ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen) - ipf_ftp_softc_t *softf; - fr_info_t *fin; - ip_t *ip; - nat_t *nat; - ftpinfo_t *ftp; - int dlen; +ipf_p_ftp_port(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat, + ftpinfo_t *ftp, int dlen) { char newbuf[IPF_FTPBUFSZ], *s; u_int a1, a2, a3, a4; u_short a5, a6, sp; size_t nlen, olen; tcphdr_t *tcp; int inc, off; ftpside_t *f; mb_t *m; m = fin->fin_m; f = &ftp->ftp_side[0]; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; /* * Check for client sending out PORT message. */ if (dlen < IPF_MINPORTLEN) { DT3(ftp_PORT_error_dlen, nat_t *, nat, ftpside_t *, f, u_int, dlen); if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("ipf_p_ftp_port:dlen(%d) < IPF_MINPORTLEN\n", dlen); return 0; } /* * Skip the PORT command + space */ s = f->ftps_rptr + 5; /* * Pick out the address components, two at a time. */ a1 = ipf_p_ftp_atoi(&s); if (s == NULL) { DT2(ftp_PORT_error_atoi_1, nat_t *, nat, ftpside_t *, f); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 1); return 0; } a2 = ipf_p_ftp_atoi(&s); if (s == NULL) { DT2(ftp_PORT_error_atoi_2, nat_t *, nat, ftpside_t *, f); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 2); return 0; } /* * Check that IP address in the PORT/PASV reply is the same as the * sender of the command - prevents using PORT for port scanning. */ a1 <<= 16; a1 |= a2; if (((nat->nat_dir == NAT_OUTBOUND) && (a1 != ntohl(nat->nat_osrcaddr))) || ((nat->nat_dir == NAT_INBOUND) && (a1 != ntohl(nat->nat_nsrcaddr)))) { DT3(ftp_PORT_error_address, nat_t *, nat, ftpside_t *, f, u_int, a1); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_port:%s != nat->nat_inip\n", "a1"); return APR_ERR(1); } a5 = ipf_p_ftp_atoi(&s); if (s == NULL) { DT2(ftp_PORT_error_atoi_3, nat_t *, nat, ftpside_t *, f); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 3); return 0; } if (*s == ')') s++; /* * check for CR-LF at the end. */ if (*s == '\n') s--; if ((*s != '\r') || (*(s + 1) != '\n')) { DT2(ftp_PORT_error_no_crlf, nat_t *, nat, ftpside_t *, f); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_port:missing %s\n", "cr-lf"); return 0; } s += 2; a6 = a5 & 0xff; /* * Calculate the source port. Verification of > 1024 is in * ipf_p_ftp_addport. */ a5 >>= 8; a5 &= 0xff; sp = a5 << 8 | a6; /* * Calculate new address parts for PORT command */ if (nat->nat_dir == NAT_INBOUND) a1 = ntohl(nat->nat_ndstaddr); else a1 = ntohl(ip->ip_src.s_addr); a1 = ntohl(ip->ip_src.s_addr); a2 = (a1 >> 16) & 0xff; a3 = (a1 >> 8) & 0xff; a4 = a1 & 0xff; a1 >>= 24; olen = s - f->ftps_rptr; (void) snprintf(newbuf, sizeof(newbuf), "%s %u,%u,%u,%u,%u,%u\r\n", "PORT", a1, a2, a3, a4, a5, a6); nlen = strlen(newbuf); inc = nlen - olen; if ((inc + fin->fin_plen) > 65535) { DT3(ftp_PORT_error_inc, nat_t *, nat, ftpside_t *, f, int, inc); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_port:inc(%d) + ip->ip_len > 65535\n", inc); return 0; } #if !defined(_KERNEL) M_ADJ(m, inc); #else /* * m_adj takes care of pkthdr.len, if required and treats inc<0 to * mean remove -len bytes from the end of the packet. * The mbuf chain will be extended if necessary by m_copyback(). */ if (inc < 0) M_ADJ(m, inc); #endif /* !defined(_KERNEL) */ COPYBACK(m, off, nlen, newbuf); fin->fin_flx |= FI_DOCKSUM; if (inc != 0) { fin->fin_plen += inc; ip->ip_len = htons(fin->fin_plen); fin->fin_dlen += inc; } f->ftps_cmd = FTPXY_C_PORT; return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, sp, inc); } int -ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, nport, inc) - ipf_ftp_softc_t *softf; - fr_info_t *fin; - ip_t *ip; - nat_t *nat; - ftpinfo_t *ftp; - int dlen, nport, inc; +ipf_p_ftp_addport(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat, + ftpinfo_t *ftp, int dlen, int nport, int inc) { tcphdr_t tcph, *tcp2 = &tcph; ipf_main_softc_t *softc; ipf_nat_softc_t *softn; int direction; fr_info_t fi; ipnat_t *ipn; nat_t *nat2; u_short sp; int flags; softc = fin->fin_main_soft; softn = softc->ipf_nat_soft; if ((ftp->ftp_pendnat != NULL) || (ftp->ftp_pendstate != NULL)) { if (softf->ipf_p_ftp_single_xfer != 0) { DT2(ftp_PORT_error_add_active, nat_t *, nat, ftpinfo_t *, ftp); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_addport:xfer active %p/%p\n", ftp->ftp_pendnat, ftp->ftp_pendstate); return 0; } ipf_p_ftp_setpending(softc, ftp); } /* * Add skeleton NAT entry for connection which will come back the * other way. */ sp = nport; /* * Don't allow the PORT command to specify a port < 1024 due to * security risks. */ if (sp < 1024) { DT3(ftp_PORT_error_port, nat_t *, nat, ftpinfo_t *, ftp, u_int, sp); if (softf->ipf_p_ftp_debug & DEBUG_SECURITY) printf("ipf_p_ftp_addport:sp(%d) < 1024\n", sp); return 0; } /* * The server may not make the connection back from port 20, but * it is the most likely so use it here to check for a conflicting * mapping. */ bcopy((char *)fin, (char *)&fi, sizeof(fi)); fi.fin_flx |= FI_IGNORE; fi.fin_data[0] = sp; fi.fin_data[1] = fin->fin_data[1] - 1; fi.fin_src6 = nat->nat_ndst6; fi.fin_dst6 = nat->nat_nsrc6; #ifndef USE_INET6 if (nat->nat_v[0] == 6) return APR_INC(inc); #endif /* * If an existing entry already exists, use it instead. */ #ifdef USE_INET6 if (nat->nat_v[0] == 6) { if (nat->nat_dir == NAT_OUTBOUND) { nat2 = ipf_nat6_outlookup(&fi, IPN_TCP|NAT_SEARCH, nat->nat_pr[1], &nat->nat_osrc6.in6, &nat->nat_odst6.in6); } else { nat2 = ipf_nat6_inlookup(&fi, IPN_TCP|NAT_SEARCH, nat->nat_pr[0], &nat->nat_odst6.in6, &nat->nat_osrc6.in6); } } else #endif { if (nat->nat_dir == NAT_OUTBOUND) { nat2 = ipf_nat_outlookup(&fi, IPN_TCP|NAT_SEARCH, nat->nat_pr[1], nat->nat_osrcip, nat->nat_odstip); } else { nat2 = ipf_nat_inlookup(&fi, IPN_TCP|NAT_SEARCH, nat->nat_pr[0], nat->nat_odstip, nat->nat_osrcip); } } if (nat2 != NULL) return APR_INC(inc); /* * An existing entry doesn't exist. Let's make one. */ ipn = ipf_proxy_rule_rev(nat); if (ipn == NULL) return APR_ERR(1); ipn->in_use = 0; fi.fin_fr = &ftppxyfr; fi.fin_dp = (char *)tcp2; fi.fin_dlen = sizeof(*tcp2); fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; fi.fin_data[1] = sp; fi.fin_data[0] = 0; bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_sport = 0; tcp2->th_dport = htons(sp); tcp2->th_win = htons(8192); TCP_OFF_A(tcp2, 5); tcp2->th_flags = TH_SYN; if (nat->nat_dir == NAT_INBOUND) { fi.fin_out = 1; direction = NAT_OUTBOUND; } else { fi.fin_out = 0; direction = NAT_INBOUND; } flags = SI_W_SPORT|NAT_SLAVE|IPN_TCP; MUTEX_ENTER(&softn->ipf_nat_new); #ifdef USE_INET6 if (nat->nat_v[0] == 6) nat2 = ipf_nat6_add(&fi, ipn, &ftp->ftp_pendnat, flags, direction); else #endif nat2 = ipf_nat_add(&fi, ipn, &ftp->ftp_pendnat, flags, direction); MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 == NULL) { KFREES(ipn, ipn->in_size); return APR_ERR(1); } (void) ipf_nat_proto(&fi, nat2, IPN_TCP); MUTEX_ENTER(&nat2->nat_lock); ipf_nat_update(&fi, nat2); MUTEX_EXIT(&nat2->nat_lock); fi.fin_ifp = NULL; if (nat2->nat_dir == NAT_INBOUND) fi.fin_dst6 = nat->nat_osrc6; if (ipf_state_add(softc, &fi, (ipstate_t **)&ftp->ftp_pendstate, SI_W_SPORT) != 0) ipf_nat_setpending(softc, nat2); return APR_INC(inc); } int -ipf_p_ftp_client(softf, fin, ip, nat, ftp, dlen) - ipf_ftp_softc_t *softf; - fr_info_t *fin; - nat_t *nat; - ftpinfo_t *ftp; - ip_t *ip; - int dlen; +ipf_p_ftp_client(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, + nat_t *nat, ftpinfo_t *ftp, int dlen) { char *rptr, *wptr, cmd[6], c; ftpside_t *f; int inc, i; inc = 0; f = &ftp->ftp_side[0]; rptr = f->ftps_rptr; wptr = f->ftps_wptr; for (i = 0; (i < 5) && (i < dlen); i++) { c = rptr[i]; if (ISALPHA(c)) { cmd[i] = TOUPPER(c); } else { cmd[i] = c; } } cmd[i] = '\0'; ftp->ftp_incok = 0; DT2(ftp_client_command, char [], cmd, int, ftp->ftp_passok); if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) { if (ftp->ftp_passok == FTPXY_ADOK_1 || ftp->ftp_passok == FTPXY_AUOK_1) { ftp->ftp_passok = FTPXY_USER_2; ftp->ftp_incok = 1; } else { ftp->ftp_passok = FTPXY_USER_1; ftp->ftp_incok = 1; } } else if (!strncmp(cmd, "AUTH ", 5)) { ftp->ftp_passok = FTPXY_AUTH_1; ftp->ftp_incok = 1; } else if (!strncmp(cmd, "PASS ", 5)) { if (ftp->ftp_passok == FTPXY_USOK_1) { ftp->ftp_passok = FTPXY_PASS_1; ftp->ftp_incok = 1; } else if (ftp->ftp_passok == FTPXY_USOK_2) { ftp->ftp_passok = FTPXY_PASS_2; ftp->ftp_incok = 1; } } else if ((ftp->ftp_passok == FTPXY_AUOK_1) && !strncmp(cmd, "ADAT ", 5)) { ftp->ftp_passok = FTPXY_ADAT_1; ftp->ftp_incok = 1; } else if ((ftp->ftp_passok == FTPXY_PAOK_1 || ftp->ftp_passok == FTPXY_PAOK_2) && !strncmp(cmd, "ACCT ", 5)) { ftp->ftp_passok = FTPXY_ACCT_1; ftp->ftp_incok = 1; } else if ((ftp->ftp_passok == FTPXY_GO) && !softf->ipf_p_ftp_pasvonly && !strncmp(cmd, "PORT ", 5)) { inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen); } else if ((ftp->ftp_passok == FTPXY_GO) && !softf->ipf_p_ftp_pasvonly && !strncmp(cmd, "EPRT ", 5)) { inc = ipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen); } else if (softf->ipf_p_ftp_insecure && !softf->ipf_p_ftp_pasvonly && !strncmp(cmd, "PORT ", 5)) { inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen); } if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("ipf_p_ftp_client: cmd[%s] passok %d incok %d inc %d\n", cmd, ftp->ftp_passok, ftp->ftp_incok, inc); DT2(ftp_client_passok, char *, cmd, int, ftp->ftp_passok); while ((*rptr++ != '\n') && (rptr < wptr)) ; f->ftps_rptr = rptr; return inc; } int -ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen) - ipf_ftp_softc_t *softf; - fr_info_t *fin; - ip_t *ip; - nat_t *nat; - ftpinfo_t *ftp; - int dlen; +ipf_p_ftp_pasv(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat, + ftpinfo_t *ftp, int dlen) { u_int a1, a2, a3, a4, data_ip; char newbuf[IPF_FTPBUFSZ]; const char *brackets[2]; u_short a5, a6; ftpside_t *f; char *s; if ((softf->ipf_p_ftp_forcepasv != 0) && (ftp->ftp_side[0].ftps_cmd != FTPXY_C_PASV)) { DT2(ftp_PASV_error_state, nat_t *, nat, ftpinfo_t *, ftp); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_pasv:ftps_cmd(%d) != FTPXY_C_PASV\n", ftp->ftp_side[0].ftps_cmd); return 0; } f = &ftp->ftp_side[1]; #define PASV_REPLEN 24 /* * Check for PASV reply message. */ if (dlen < IPF_MIN227LEN) { DT3(ftp_PASV_error_short, nat_t *, nat, ftpinfo_t *, ftp, int, dlen); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n", dlen); return 0; } else if (strncmp(f->ftps_rptr, "227 Entering Passive Mod", PASV_REPLEN)) { DT2(ftp_PASV_error_string, nat_t *, nat, ftpinfo_t *, ftp); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_pasv:%d reply wrong\n", 227); return 0; } brackets[0] = ""; brackets[1] = ""; /* * Skip the PASV reply + space */ s = f->ftps_rptr + PASV_REPLEN; while (*s && !ISDIGIT(*s)) { if (*s == '(') { brackets[0] = "("; brackets[1] = ")"; } s++; } /* * Pick out the address components, two at a time. */ a1 = ipf_p_ftp_atoi(&s); if (s == NULL) { DT2(ftp_PASV_error_atoi_1, nat_t *, nat, ftpside_t *, f); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 1); return 0; } a2 = ipf_p_ftp_atoi(&s); if (s == NULL) { DT2(ftp_PASV_error_atoi_2, nat_t *, nat, ftpside_t *, f); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 2); return 0; } /* * check that IP address in the PASV reply is the same as the * sender of the command - prevents using PASV for port scanning. */ a1 <<= 16; a1 |= a2; if (((nat->nat_dir == NAT_INBOUND) && (a1 != ntohl(nat->nat_ndstaddr))) || ((nat->nat_dir == NAT_OUTBOUND) && (a1 != ntohl(nat->nat_odstaddr)))) { DT3(ftp_PASV_error_address, nat_t *, nat, ftpside_t *, f, u_int, a1); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_pasv:%s != nat->nat_oip\n", "a1"); return 0; } a5 = ipf_p_ftp_atoi(&s); if (s == NULL) { DT2(ftp_PASV_error_atoi_3, nat_t *, nat, ftpside_t *, f); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 3); return 0; } if (*s == ')') s++; if (*s == '.') s++; if (*s == '\n') s--; /* * check for CR-LF at the end. */ if ((*s != '\r') || (*(s + 1) != '\n')) { DT(pasv_missing_crlf); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_pasv:missing %s", "cr-lf\n"); return 0; } s += 2; a6 = a5 & 0xff; a5 >>= 8; /* * Calculate new address parts for 227 reply */ if (nat->nat_dir == NAT_INBOUND) { data_ip = nat->nat_odstaddr; a1 = ntohl(data_ip); } else data_ip = htonl(a1); a2 = (a1 >> 16) & 0xff; a3 = (a1 >> 8) & 0xff; a4 = a1 & 0xff; a1 >>= 24; (void) snprintf(newbuf, sizeof(newbuf), "%s %s%u,%u,%u,%u,%u,%u%s\r\n", "227 Entering Passive Mode", brackets[0], a1, a2, a3, a4, a5, a6, brackets[1]); return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (a5 << 8 | a6), newbuf, s); } int -ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, port, newmsg, s) - ipf_ftp_softc_t *softf; - fr_info_t *fin; - ip_t *ip; - nat_t *nat; - ftpinfo_t *ftp; - u_int port; - char *newmsg; - char *s; +ipf_p_ftp_pasvreply(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, + nat_t *nat, ftpinfo_t *ftp, u_int port, char *newmsg, char *s) { int inc, off, nflags; tcphdr_t *tcp, tcph, *tcp2; ipf_main_softc_t *softc; ipf_nat_softc_t *softn; size_t nlen, olen; #ifdef USE_INET6 ip6_t *ip6; #endif ipnat_t *ipn; fr_info_t fi; ftpside_t *f; nat_t *nat2; mb_t *m; softc = fin->fin_main_soft; softn = softc->ipf_nat_soft; if ((ftp->ftp_pendnat != NULL) || (ftp->ftp_pendstate != NULL)) ipf_p_ftp_setpending(softc, ftp); m = fin->fin_m; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; tcp2 = &tcph; inc = 0; f = &ftp->ftp_side[1]; olen = s - f->ftps_rptr; nlen = strlen(newmsg); inc = nlen - olen; if ((inc + fin->fin_plen) > 65535) { DT3(ftp_PASV_error_inc, nat_t *, nat, ftpside_t *, f, int, inc); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_pasv:inc(%d) + ip->ip_len > 65535\n", inc); return 0; } ipn = ipf_proxy_rule_fwd(nat); if (ipn == NULL) return APR_ERR(1); ipn->in_use = 0; /* * Add skeleton NAT entry for connection which will come back the * other way. */ bzero((char *)tcp2, sizeof(*tcp2)); bcopy((char *)fin, (char *)&fi, sizeof(fi)); fi.fin_flx |= FI_IGNORE; fi.fin_data[0] = 0; fi.fin_data[1] = port; nflags = IPN_TCP|SI_W_SPORT; fi.fin_fr = &ftppxyfr; fi.fin_dp = (char *)tcp2; fi.fin_out = 1 - fin->fin_out; fi.fin_dlen = sizeof(*tcp2); fi.fin_src6 = nat->nat_osrc6; fi.fin_dst6 = nat->nat_odst6; fi.fin_plen = fi.fin_hlen + sizeof(*tcp); fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; TCP_OFF_A(tcp2, 5); tcp2->th_flags = TH_SYN; tcp2->th_win = htons(8192); tcp2->th_dport = htons(port); MUTEX_ENTER(&softn->ipf_nat_new); #ifdef USE_INET6 if (nat->nat_v[0] == 6) nat2 = ipf_nat6_add(&fi, ipn, &ftp->ftp_pendnat, nflags, nat->nat_dir); else #endif nat2 = ipf_nat_add(&fi, ipn, &ftp->ftp_pendnat, nflags, nat->nat_dir); MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 == NULL) { KFREES(ipn, ipn->in_size); return APR_ERR(1); } (void) ipf_nat_proto(&fi, nat2, IPN_TCP); MUTEX_ENTER(&nat2->nat_lock); ipf_nat_update(&fi, nat2); MUTEX_EXIT(&nat2->nat_lock); fi.fin_ifp = NULL; if (nat->nat_dir == NAT_INBOUND) { #ifdef USE_INET6 if (nat->nat_v[0] == 6) fi.fin_dst6 = nat->nat_ndst6; else #endif fi.fin_daddr = nat->nat_ndstaddr; } if (ipf_state_add(softc, &fi, (ipstate_t **)&ftp->ftp_pendstate, SI_W_SPORT) != 0) ipf_nat_setpending(softc, nat2); #if !defined(_KERNEL) M_ADJ(m, inc); #else /* * m_adj takes care of pkthdr.len, if required and treats inc<0 to * mean remove -len bytes from the end of the packet. * The mbuf chain will be extended if necessary by m_copyback(). */ if (inc < 0) M_ADJ(m, inc); #endif /* !defined(_KERNEL) */ COPYBACK(m, off, nlen, newmsg); fin->fin_flx |= FI_DOCKSUM; if (inc != 0) { fin->fin_plen += inc; fin->fin_dlen += inc; #ifdef USE_INET6 if (nat->nat_v[0] == 6) { ip6 = (ip6_t *)fin->fin_ip; u_short len = ntohs(ip6->ip6_plen) + inc; ip6->ip6_plen = htons(len); } else #endif ip->ip_len = htons(fin->fin_plen); } return APR_INC(inc); } int -ipf_p_ftp_server(softf, fin, ip, nat, ftp, dlen) - ipf_ftp_softc_t *softf; - fr_info_t *fin; - ip_t *ip; - nat_t *nat; - ftpinfo_t *ftp; - int dlen; +ipf_p_ftp_server(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat, + ftpinfo_t *ftp, int dlen) { char *rptr, *wptr; ftpside_t *f; int inc; inc = 0; f = &ftp->ftp_side[1]; rptr = f->ftps_rptr; wptr = f->ftps_wptr; DT2(ftp_server_response, char *, rptr, int, ftp->ftp_passok); if (*rptr == ' ') goto server_cmd_ok; if (!ISDIGIT(*rptr) || !ISDIGIT(*(rptr + 1)) || !ISDIGIT(*(rptr + 2))) return 0; if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("ipf_p_ftp_server_1: cmd[%4.4s] passok %d\n", rptr, ftp->ftp_passok); if (ftp->ftp_passok == FTPXY_GO) { if (!strncmp(rptr, "227 ", 4)) inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen); else if (!strncmp(rptr, "229 ", 4)) inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen); else if (strncmp(rptr, "200", 3)) { /* * 200 is returned for a successful command. */ ; } } else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "227 ", 4)) { inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen); } else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "229 ", 4)) { inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen); } else if (*rptr == '5' || *rptr == '4') ftp->ftp_passok = FTPXY_INIT; else if (ftp->ftp_incok) { if (*rptr == '3') { if (ftp->ftp_passok == FTPXY_ACCT_1) ftp->ftp_passok = FTPXY_GO; else ftp->ftp_passok++; } else if (*rptr == '2') { switch (ftp->ftp_passok) { case FTPXY_USER_1 : case FTPXY_USER_2 : case FTPXY_PASS_1 : case FTPXY_PASS_2 : case FTPXY_ACCT_1 : ftp->ftp_passok = FTPXY_GO; break; default : ftp->ftp_passok += 3; break; } } } ftp->ftp_incok = 0; server_cmd_ok: if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("ipf_p_ftp_server_2: cmd[%4.4s] passok %d\n", rptr, ftp->ftp_passok); DT3(ftp_server_passok, char *,rptr, int, ftp->ftp_incok, int, ftp->ftp_passok); while ((*rptr++ != '\n') && (rptr < wptr)) ; f->ftps_rptr = rptr; return inc; } /* * 0 FTPXY_JUNK_OK * 1 FTPXY_JUNK_BAD * 2 FTPXY_JUNK_EOL * 3 FTPXY_JUNK_CONT * * Look to see if the buffer starts with something which we recognise as * being the correct syntax for the FTP protocol. */ int -ipf_p_ftp_client_valid(softf, ftps, buf, len) - ipf_ftp_softc_t *softf; - ftpside_t *ftps; - char *buf; - size_t len; +ipf_p_ftp_client_valid(ipf_ftp_softc_t *softf, ftpside_t *ftps, char *buf, + size_t len) { register char *s, c, pc; register size_t i = len; char cmd[5]; s = buf; if (ftps->ftps_junk == FTPXY_JUNK_BAD) return FTPXY_JUNK_BAD; if (i < 5) { DT1(client_valid, int, i); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_client_valid:i(%d) < 5\n", (int)i); return 2; } i--; c = *s++; if (ISALPHA(c)) { cmd[0] = TOUPPER(c); c = *s++; i--; if (ISALPHA(c)) { cmd[1] = TOUPPER(c); c = *s++; i--; if (ISALPHA(c)) { cmd[2] = TOUPPER(c); c = *s++; i--; if (ISALPHA(c)) { cmd[3] = TOUPPER(c); c = *s++; i--; if ((c != ' ') && (c != '\r')) goto bad_client_command; } else if ((c != ' ') && (c != '\r')) goto bad_client_command; } else goto bad_client_command; } else goto bad_client_command; } else { bad_client_command: DT4(client_junk, int, len, int, i, int, c, char *, buf); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n", "ipf_p_ftp_client_valid", ftps->ftps_junk, (int)len, (int)i, c, (int)len, (int)len, buf); return FTPXY_JUNK_BAD; } for (; i; i--) { pc = c; c = *s++; if ((pc == '\r') && (c == '\n')) { cmd[4] = '\0'; if (!strcmp(cmd, "PASV")) { ftps->ftps_cmd = FTPXY_C_PASV; } else if (!strcmp(cmd, "EPSV")) { ftps->ftps_cmd = FTPXY_C_EPSV; } else { ftps->ftps_cmd = 0; } return 0; } } #if !defined(_KERNEL) printf("ipf_p_ftp_client_valid:junk after cmd[%*.*s]\n", (int)len, (int)len, buf); #endif return FTPXY_JUNK_EOL; } int -ipf_p_ftp_server_valid(softf, ftps, buf, len) - ipf_ftp_softc_t *softf; - ftpside_t *ftps; - char *buf; - size_t len; +ipf_p_ftp_server_valid(ipf_ftp_softc_t *softf, ftpside_t *ftps, char *buf, + size_t len) { register char *s, c, pc; register size_t i = len; int cmd; s = buf; cmd = 0; if (ftps->ftps_junk == FTPXY_JUNK_BAD) return FTPXY_JUNK_BAD; if (i < 5) { DT1(server_valid, int, i); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_servert_valid:i(%d) < 5\n", (int)i); return 2; } c = *s++; i--; if (c == ' ') { cmd = -1; goto search_eol; } if (ISDIGIT(c)) { cmd = (c - '0') * 100; c = *s++; i--; if (ISDIGIT(c)) { cmd += (c - '0') * 10; c = *s++; i--; if (ISDIGIT(c)) { cmd += (c - '0'); c = *s++; i--; if ((c != '-') && (c != ' ')) goto bad_server_command; if (c == '-') return FTPXY_JUNK_CONT; } else goto bad_server_command; } else goto bad_server_command; } else { bad_server_command: DT4(server_junk, int len, buf, int, i, int, c, char *, buf); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n", "ipf_p_ftp_server_valid", ftps->ftps_junk, (int)len, (int)i, c, (int)len, (int)len, buf); if (ftps->ftps_junk == FTPXY_JUNK_CONT) return FTPXY_JUNK_CONT; return FTPXY_JUNK_BAD; } search_eol: for (; i; i--) { pc = c; c = *s++; if ((pc == '\r') && (c == '\n')) { if (cmd == -1) { if (ftps->ftps_junk == FTPXY_JUNK_CONT) return FTPXY_JUNK_CONT; } else { ftps->ftps_cmd = cmd; } return FTPXY_JUNK_OK; } } DT2(junk_eol, int, len, char *, buf); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) printf("ipf_p_ftp_server_valid:junk after cmd[%*.*s]\n", (int)len, (int)len, buf); return FTPXY_JUNK_EOL; } int -ipf_p_ftp_valid(softf, ftp, side, buf, len) - ipf_ftp_softc_t *softf; - ftpinfo_t *ftp; - int side; - char *buf; - size_t len; +ipf_p_ftp_valid(ipf_ftp_softc_t *softf, ftpinfo_t *ftp, int side, char *buf, + size_t len) { ftpside_t *ftps; ftps = &ftp->ftp_side[side]; if (side == 0) return(ipf_p_ftp_client_valid(softf, ftps, buf, len)); else return(ipf_p_ftp_server_valid(softf, ftps, buf, len)); } /* * For map rules, the following applies: * rv == 0 for outbound processing, * rv == 1 for inbound processing. * For rdr rules, the following applies: * rv == 0 for inbound processing, * rv == 1 for outbound processing. */ int -ipf_p_ftp_process(softf, fin, nat, ftp, rv) - ipf_ftp_softc_t *softf; - fr_info_t *fin; - nat_t *nat; - ftpinfo_t *ftp; - int rv; +ipf_p_ftp_process(ipf_ftp_softc_t *softf, fr_info_t *fin, nat_t *nat, + ftpinfo_t *ftp, int rv) { int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff, retry; char *rptr, *wptr, *s; u_32_t thseq, thack; ap_session_t *aps; ftpside_t *f, *t; tcphdr_t *tcp; ip_t *ip; mb_t *m; m = fin->fin_m; ip = fin->fin_ip; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; f = &ftp->ftp_side[rv]; t = &ftp->ftp_side[1 - rv]; thseq = ntohl(tcp->th_seq); thack = ntohl(tcp->th_ack); mlen = MSGDSIZE(m) - off; DT3(process_debug, tcphdr_t *, tcp, int, off, int, mlen); if (softf->ipf_p_ftp_debug & DEBUG_INFO) printf("ipf_p_ftp_process: %d:%d,%d, mlen %d flags %x\n", fin->fin_out, fin->fin_sport, fin->fin_dport, mlen, tcp->th_flags); if ((mlen == 0) && ((tcp->th_flags & TH_OPENING) == TH_OPENING)) { f->ftps_seq[0] = thseq + 1; t->ftps_seq[0] = thack; return 0; } else if (mlen < 0) { return 0; } aps = nat->nat_aps; sel = aps->aps_sel[1 - rv]; sel2 = aps->aps_sel[rv]; if (rv == 1) { seqoff = aps->aps_seqoff[sel]; if (aps->aps_seqmin[sel] > seqoff + thseq) seqoff = aps->aps_seqoff[!sel]; ackoff = aps->aps_ackoff[sel2]; if (aps->aps_ackmin[sel2] > ackoff + thack) ackoff = aps->aps_ackoff[!sel2]; } else { seqoff = aps->aps_ackoff[sel]; if (softf->ipf_p_ftp_debug & DEBUG_INFO) printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq, aps->aps_ackmin[sel]); if (aps->aps_ackmin[sel] > seqoff + thseq) seqoff = aps->aps_ackoff[!sel]; ackoff = aps->aps_seqoff[sel2]; if (softf->ipf_p_ftp_debug & DEBUG_INFO) printf("ackoff %d thack %x seqmin %x\n", ackoff, thack, aps->aps_seqmin[sel2]); if (ackoff > 0) { if (aps->aps_seqmin[sel2] > ackoff + thack) ackoff = aps->aps_seqoff[!sel2]; } else { if (aps->aps_seqmin[sel2] > thack) ackoff = aps->aps_seqoff[!sel2]; } } if (softf->ipf_p_ftp_debug & DEBUG_INFO) { printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n", rv ? "IN" : "OUT", tcp->th_flags, thseq, seqoff, thack, ackoff, mlen, fin->fin_plen, off); printf("sel %d seqmin %x/%x offset %d/%d\n", sel, aps->aps_seqmin[sel], aps->aps_seqmin[sel2], aps->aps_seqoff[sel], aps->aps_seqoff[sel2]); printf("sel %d ackmin %x/%x offset %d/%d\n", sel2, aps->aps_ackmin[sel], aps->aps_ackmin[sel2], aps->aps_ackoff[sel], aps->aps_ackoff[sel2]); } /* * XXX - Ideally, this packet should get dropped because we now know * that it is out of order (and there is no real danger in doing so * apart from causing packets to go through here ordered). */ if (softf->ipf_p_ftp_debug & DEBUG_INFO) { printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n", rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff); } ok = 0; if (t->ftps_seq[0] == 0) { t->ftps_seq[0] = thack; ok = 1; } else { if (ackoff == 0) { if (t->ftps_seq[0] == thack) ok = 1; else if (t->ftps_seq[1] == thack) { t->ftps_seq[0] = thack; ok = 1; } } else { if (t->ftps_seq[0] + ackoff == thack) { t->ftps_seq[0] = thack; ok = 1; } else if (t->ftps_seq[0] == thack + ackoff) { t->ftps_seq[0] = thack + ackoff; ok = 1; } else if (t->ftps_seq[1] + ackoff == thack) { t->ftps_seq[0] = thack; ok = 1; } else if (t->ftps_seq[1] == thack + ackoff) { t->ftps_seq[0] = thack + ackoff; ok = 1; } } } if (softf->ipf_p_ftp_debug & DEBUG_INFO) { if (!ok) printf("%s ok\n", "not"); } if (!mlen) { if (t->ftps_seq[0] + ackoff != thack && t->ftps_seq[1] + ackoff != thack) { DT3(thack, ftpside_t *t, t, int, ackoff, u_32_t, thack); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) { printf("%s:seq[0](%u) + (%d) != (%u)\n", "ipf_p_ftp_process", t->ftps_seq[0], ackoff, thack); printf("%s:seq[1](%u) + (%d) != (%u)\n", "ipf_p_ftp_process", t->ftps_seq[1], ackoff, thack); } return APR_ERR(1); } if (softf->ipf_p_ftp_debug & DEBUG_PARSE) { printf("ipf_p_ftp_process:f:seq[0] %x seq[1] %x\n", f->ftps_seq[0], f->ftps_seq[1]); } if (tcp->th_flags & TH_FIN) { if (thseq == f->ftps_seq[1]) { f->ftps_seq[0] = f->ftps_seq[1] - seqoff; f->ftps_seq[1] = thseq + 1 - seqoff; } else { DT2(thseq, ftpside_t *t, t, u_32_t, thseq); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) { printf("FIN: thseq %x seqoff %d ftps_seq %x\n", thseq, seqoff, f->ftps_seq[0]); } return APR_ERR(1); } } f->ftps_len = 0; return 0; } ok = 0; if ((thseq == f->ftps_seq[0]) || (thseq == f->ftps_seq[1])) { ok = 1; /* * Retransmitted data packet. */ } else if ((thseq + mlen == f->ftps_seq[0]) || (thseq + mlen == f->ftps_seq[1])) { ok = 1; } if (ok == 0) { DT3(ok_0, ftpside_t *, f, u_32_t, thseq, int, mlen); inc = thseq - f->ftps_seq[0]; if (softf->ipf_p_ftp_debug & DEBUG_ERROR) { printf("inc %d sel %d rv %d\n", inc, sel, rv); printf("th_seq %x ftps_seq %x/%x\n", thseq, f->ftps_seq[0], f->ftps_seq[1]); printf("ackmin %x ackoff %d\n", aps->aps_ackmin[sel], aps->aps_ackoff[sel]); printf("seqmin %x seqoff %d\n", aps->aps_seqmin[sel], aps->aps_seqoff[sel]); } return APR_ERR(1); } inc = 0; rptr = f->ftps_rptr; wptr = f->ftps_wptr; f->ftps_seq[0] = thseq; f->ftps_seq[1] = f->ftps_seq[0] + mlen; f->ftps_len = mlen; while (mlen > 0) { len = MIN(mlen, sizeof(f->ftps_buf) - (wptr - rptr)); if (len == 0) break; COPYDATA(m, off, len, wptr); mlen -= len; off += len; wptr += len; whilemore: if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("%s:len %d/%d off %d wptr %lx junk %d [%*.*s]\n", "ipf_p_ftp_process", len, mlen, off, (u_long)wptr, f->ftps_junk, len, len, rptr); f->ftps_wptr = wptr; if (f->ftps_junk != FTPXY_JUNK_OK) { i = f->ftps_junk; f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, rptr, wptr - rptr); DT2(junk_transit, int, i, int, f->ftps_junk); if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("%s:junk %d -> %d\n", "ipf_p_ftp_process", i, f->ftps_junk); if (f->ftps_junk == FTPXY_JUNK_BAD) { DT(buffer_full); if (wptr - rptr == sizeof(f->ftps_buf)) { if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) printf("%s:full buffer\n", "ipf_p_ftp_process"); f->ftps_rptr = f->ftps_buf; f->ftps_wptr = f->ftps_buf; rptr = f->ftps_rptr; wptr = f->ftps_wptr; continue; } } } while ((f->ftps_junk == FTPXY_JUNK_OK) && (wptr > rptr)) { len = wptr - rptr; f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, rptr, len); DT5(junk_ftp_valid, int, len, int, rv, u_long, rptr, u_long, wptr, int, f->ftps_junk); if (softf->ipf_p_ftp_debug & DEBUG_PARSE) { printf("%s=%d len %d rv %d ptr %lx/%lx ", "ipf_p_ftp_valid", f->ftps_junk, len, rv, (u_long)rptr, (u_long)wptr); printf("buf [%*.*s]\n", len, len, rptr); } if (f->ftps_junk == FTPXY_JUNK_OK) { f->ftps_cmds++; f->ftps_rptr = rptr; if (rv) inc += ipf_p_ftp_server(softf, fin, ip, nat, ftp, len); else inc += ipf_p_ftp_client(softf, fin, ip, nat, ftp, len); rptr = f->ftps_rptr; wptr = f->ftps_wptr; } } /* * Off to a bad start so lets just forget about using the * ftp proxy for this connection. */ if ((f->ftps_cmds == 0) && (f->ftps_junk == FTPXY_JUNK_BAD)) { /* f->ftps_seq[1] += inc; */ DT(ftp_junk_cmd); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("%s:cmds == 0 junk == 1\n", "ipf_p_ftp_process"); return APR_ERR(2); } retry = 0; if ((f->ftps_junk != FTPXY_JUNK_OK) && (rptr < wptr)) { for (s = rptr; s < wptr; s++) { if ((*s == '\r') && (s + 1 < wptr) && (*(s + 1) == '\n')) { rptr = s + 2; retry = 1; if (f->ftps_junk != FTPXY_JUNK_CONT) f->ftps_junk = FTPXY_JUNK_OK; break; } } } if (rptr == wptr) { rptr = wptr = f->ftps_buf; } else { /* * Compact the buffer back to the start. The junk * flag should already be set and because we're not * throwing away any data, it is preserved from its * current state. */ if (rptr > f->ftps_buf) { bcopy(rptr, f->ftps_buf, wptr - rptr); wptr -= rptr - f->ftps_buf; rptr = f->ftps_buf; } } f->ftps_rptr = rptr; f->ftps_wptr = wptr; if (retry) goto whilemore; } /* f->ftps_seq[1] += inc; */ if (tcp->th_flags & TH_FIN) f->ftps_seq[1]++; if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) { mlen = MSGDSIZE(m); mlen -= off; printf("ftps_seq[1] = %x inc %d len %d\n", f->ftps_seq[1], inc, mlen); } f->ftps_rptr = rptr; f->ftps_wptr = wptr; return APR_INC(inc); } int -ipf_p_ftp_out(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_ftp_out(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { ipf_ftp_softc_t *softf = arg; ftpinfo_t *ftp; int rev; ftp = aps->aps_data; if (ftp == NULL) return 0; rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1; if (ftp->ftp_side[1 - rev].ftps_ifp == NULL) ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp; return ipf_p_ftp_process(softf, fin, nat, ftp, rev); } int -ipf_p_ftp_in(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_ftp_in(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { ipf_ftp_softc_t *softf = arg; ftpinfo_t *ftp; int rev; ftp = aps->aps_data; if (ftp == NULL) return 0; rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1; if (ftp->ftp_side[rev].ftps_ifp == NULL) ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp; return ipf_p_ftp_process(softf, fin, nat, ftp, 1 - rev); } /* * ipf_p_ftp_atoi - implement a version of atoi which processes numbers in * pairs separated by commas (which are expected to be in the range 0 - 255), * returning a 16 bit number combining either side of the , as the MSB and * LSB. */ u_short -ipf_p_ftp_atoi(ptr) - char **ptr; +ipf_p_ftp_atoi(char **ptr) { register char *s = *ptr, c; register u_char i = 0, j = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } if (c != ',') { *ptr = NULL; return 0; } while (((c = *s++) != '\0') && ISDIGIT(c)) { j *= 10; j += c - '0'; } *ptr = s; i &= 0xff; j &= 0xff; return (i << 8) | j; } int -ipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen) - ipf_ftp_softc_t *softf; - fr_info_t *fin; - ip_t *ip; - nat_t *nat; - ftpinfo_t *ftp; - int dlen; +ipf_p_ftp_eprt(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat, + ftpinfo_t *ftp, int dlen) { ftpside_t *f; /* * Check for client sending out EPRT message. */ if (dlen < IPF_MINEPRTLEN) { DT1(epert_dlen, int, dlen); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_eprt:dlen(%d) < IPF_MINEPRTLEN\n", dlen); return 0; } /* * Parse the EPRT command. Format is: * "EPRT |1|1.2.3.4|2000|" for IPv4 and * "EPRT |2|ef00::1:2|2000|" for IPv6 */ f = &ftp->ftp_side[0]; if (f->ftps_rptr[5] != '|') return 0; if (f->ftps_rptr[5] == f->ftps_rptr[7]) { if (f->ftps_rptr[6] == '1' && nat->nat_v[0] == 4) return ipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen); #ifdef USE_INET6 if (f->ftps_rptr[6] == '2' && nat->nat_v[0] == 6) return ipf_p_ftp_eprt6(softf, fin, ip, nat, ftp, dlen); #endif } return 0; } int -ipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen) - ipf_ftp_softc_t *softf; - fr_info_t *fin; - ip_t *ip; - nat_t *nat; - ftpinfo_t *ftp; - int dlen; +ipf_p_ftp_eprt4(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat, + ftpinfo_t *ftp, int dlen) { int a1, a2, a3, a4, port, olen, nlen, inc, off; char newbuf[IPF_FTPBUFSZ]; char *s, c, delim; u_32_t addr, i; tcphdr_t *tcp; ftpside_t *f; mb_t *m; m = fin->fin_m; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; f = &ftp->ftp_side[0]; delim = f->ftps_rptr[5]; s = f->ftps_rptr + 8; /* * get the IP address. */ i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } if (i > 255) return 0; if (c != '.') return 0; addr = (i << 24); i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } if (i > 255) return 0; if (c != '.') return 0; addr |= (addr << 16); i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } if (i > 255) return 0; if (c != '.') return 0; addr |= (addr << 8); i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } if (i > 255) return 0; if (c != delim) return 0; addr |= addr; /* * Get the port number */ i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } if (i > 65535) return 0; if (c != delim) return 0; port = i; /* * Check for CR-LF at the end of the command string. */ if ((*s != '\r') || (*(s + 1) != '\n')) { DT(eprt4_no_crlf); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_eprt4:missing %s\n", "cr-lf"); return 0; } s += 2; /* * Calculate new address parts for PORT command */ if (nat->nat_dir == NAT_INBOUND) a1 = ntohl(nat->nat_odstaddr); else a1 = ntohl(ip->ip_src.s_addr); a2 = (a1 >> 16) & 0xff; a3 = (a1 >> 8) & 0xff; a4 = a1 & 0xff; a1 >>= 24; olen = s - f->ftps_rptr; /* DO NOT change this to snprintf! */ /* * While we could force the use of | as a delimiter here, it makes * sense to preserve whatever character is being used by the systems * involved in the communication. */ (void) snprintf(newbuf, sizeof(newbuf), "%s %c1%c%u.%u.%u.%u%c%u%c\r\n", "EPRT", delim, delim, a1, a2, a3, a4, delim, port, delim); nlen = strlen(newbuf); inc = nlen - olen; if ((inc + fin->fin_plen) > 65535) { DT2(eprt4_len, int, inc, int, fin->fin_plen); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_eprt4:inc(%d) + ip->ip_len > 65535\n", inc); return 0; } off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; #if !defined(_KERNEL) M_ADJ(m, inc); #else if (inc < 0) M_ADJ(m, inc); #endif /* the mbuf chain will be extended if necessary by m_copyback() */ COPYBACK(m, off, nlen, newbuf); fin->fin_flx |= FI_DOCKSUM; if (inc != 0) { fin->fin_plen += inc; ip->ip_len = htons(fin->fin_plen); fin->fin_dlen += inc; } f->ftps_cmd = FTPXY_C_EPRT; return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc); } int -ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen) - ipf_ftp_softc_t *softf; - fr_info_t *fin; - ip_t *ip; - nat_t *nat; - ftpinfo_t *ftp; - int dlen; +ipf_p_ftp_epsv(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat, + ftpinfo_t *ftp, int dlen) { char newbuf[IPF_FTPBUFSZ]; u_short ap = 0; ftpside_t *f; char *s; if ((softf->ipf_p_ftp_forcepasv != 0) && (ftp->ftp_side[0].ftps_cmd != FTPXY_C_EPSV)) { DT1(epsv_cmd, int, ftp->ftp_side[0].ftps_cmd); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_epsv:ftps_cmd(%d) != FTPXY_C_EPSV\n", ftp->ftp_side[0].ftps_cmd); return 0; } f = &ftp->ftp_side[1]; #define EPSV_REPLEN 33 /* * Check for EPSV reply message. */ if (dlen < IPF_MIN229LEN) { return (0); } else if (strncmp(f->ftps_rptr, "229 Entering Extended Passive Mode", EPSV_REPLEN)) { return (0); } /* * Skip the EPSV command + space */ s = f->ftps_rptr + 33; while (*s && !ISDIGIT(*s)) s++; /* * As per RFC 2428, there are no addres components in the EPSV * response. So we'll go straight to getting the port. */ while (*s && ISDIGIT(*s)) { ap *= 10; ap += *s++ - '0'; } if (*s == '|') s++; if (*s == ')') s++; if (*s == '\n') s--; /* * check for CR-LF at the end. */ if ((*s != '\r') || (*(s + 1) != '\n')) { return 0; } s += 2; (void) snprintf(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n", "229 Entering Extended Passive Mode", ap); return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (u_int)ap, newbuf, s); } #ifdef USE_INET6 int -ipf_p_ftp_eprt6(softf, fin, ip, nat, ftp, dlen) - ipf_ftp_softc_t *softf; - fr_info_t *fin; - ip_t *ip; - nat_t *nat; - ftpinfo_t *ftp; - int dlen; +ipf_p_ftp_eprt6(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat, + ftpinfo_t *ftp, int dlen) { int port, olen, nlen, inc, off, left, i; char newbuf[IPF_FTPBUFSZ]; char *s, c; i6addr_t addr, *a6; tcphdr_t *tcp; ip6_t *ip6; char delim; u_short whole; u_short part; ftpside_t *f; u_short *t; int fwd; mb_t *m; u_32_t a; m = fin->fin_m; ip6 = (ip6_t *)ip; f = &ftp->ftp_side[0]; s = f->ftps_rptr + 8; f = &ftp->ftp_side[0]; delim = f->ftps_rptr[5]; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; addr.i6[0] = 0; addr.i6[1] = 0; addr.i6[2] = 0; addr.i6[3] = 0; /* * Parse an IPv6 address. * Go forward until either :: or | is found. If :: is found, * reverse direction. Direction change is performed to ease * parsing an unknown number of 0s in the middle. */ whole = 0; t = (u_short *)&addr; fwd = 1; for (part = 0; (c = *s) != '\0'; ) { if (c == delim) { *t = htons((u_short)whole); break; } if (c == ':') { *t = part; if (fwd) { *t = htons((u_short)whole); t++; } else { *t = htons((u_short)(whole >> 16)); t--; } whole = 0; if (fwd == 1 && s[1] == ':') { while (*s && *s != '|') s++; if ((c = *s) != delim) break; t = (u_short *)&addr.i6[3]; t++; fwd = 0; } else if (fwd == 0 && s[-1] == ':') { break; } } else { if (c >= '0' && c <= '9') { c -= '0'; } else if (c >= 'a' && c <= 'f') { c -= 'a' + 10; } else if (c >= 'A' && c <= 'F') { c -= 'A' + 10; } if (fwd) { whole <<= 8; whole |= c; } else { whole >>= 8; whole |= ((u_32_t)c) << 24; } } if (fwd) s++; else s--; } if (c != ':' && c != delim) return 0; while (*s != '|') s++; s++; /* * Get the port number */ i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } if (i > 65535) return 0; if (c != delim) return 0; port = (u_short)(i & 0xffff); /* * Check for CR-LF at the end of the command string. */ if ((*s != '\r') || (*(s + 1) != '\n')) { DT(eprt6_no_crlf); if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("ipf_p_ftp_eprt6:missing %s\n", "cr-lf"); return 0; } s += 2; /* * Calculate new address parts for PORT command */ a6 = (i6addr_t *)&ip6->ip6_src; olen = s - f->ftps_rptr; /* DO NOT change this to snprintf! */ /* * While we could force the use of | as a delimiter here, it makes * sense to preserve whatever character is being used by the systems * involved in the communication. */ s = newbuf; left = sizeof(newbuf); (void) snprintf(s, left, "EPRT %c2%c", delim, delim); s += strlen(s); a = ntohl(a6->i6[0]); snprintf(s, left, "%x:%x:", a >> 16, a & 0xffff); left -= strlen(s); s += strlen(s); a = ntohl(a6->i6[1]); snprintf(s, left, "%x:%x:", a >> 16, a & 0xffff); left -= strlen(s); s += strlen(s); a = ntohl(a6->i6[2]); snprintf(s, left,"%x:%x:", a >> 16, a & 0xffff); left -= strlen(s); s += strlen(s); a = ntohl(a6->i6[3]); snprintf(s, left, "%x:%x", a >> 16, a & 0xffff); left -= strlen(s); s += strlen(s); snprintf(s, left, "|%d|\r\n", port); nlen = strlen(newbuf); inc = nlen - olen; if ((inc + fin->fin_plen) > 65535) { DT2(eprt6_len, int, inc, int, fin->fin_plen); if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("ipf_p_ftp_eprt6:inc(%d) + ip->ip_len > 65535\n", inc); return 0; } off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; #if !defined(_KERNEL) M_ADJ(m, inc); #else if (inc < 0) M_ADJ(m, inc); #endif /* the mbuf chain will be extended if necessary by m_copyback() */ COPYBACK(m, off, nlen, newbuf); fin->fin_flx |= FI_DOCKSUM; if (inc != 0) { fin->fin_plen += inc; ip6->ip6_plen = htons(fin->fin_plen - fin->fin_hlen); fin->fin_dlen += inc; } f->ftps_cmd = FTPXY_C_EPRT; return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc); } #endif diff --git a/sys/netpfil/ipfilter/netinet/ip_htable.c b/sys/netpfil/ipfilter/netinet/ip_htable.c index d1c20448f9ec..82442c9033d8 100644 --- a/sys/netpfil/ipfilter/netinet/ip_htable.c +++ b/sys/netpfil/ipfilter/netinet/ip_htable.c @@ -1,1459 +1,1380 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if !defined(_KERNEL) # include # include # define _KERNEL # include # undef _KERNEL #endif #include #if defined(__FreeBSD__) # include #endif #if defined(__FreeBSD__) # include # include #endif #if !defined(__SVR4) # include #endif #if defined(_KERNEL) # include #else # include "ipf.h" #endif #include #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_lookup.h" #include "netinet/ip_htable.h" /* END OF INCLUDES */ #if !defined(lint) static const char rcsid[] = "@(#)$Id$"; #endif # ifdef USE_INET6 static iphtent_t *ipf_iphmfind6(iphtable_t *, i6addr_t *); # endif static iphtent_t *ipf_iphmfind(iphtable_t *, struct in_addr *); static int ipf_iphmfindip(ipf_main_softc_t *, void *, int, void *, u_int); static int ipf_htable_clear(ipf_main_softc_t *, void *, iphtable_t *); static int ipf_htable_create(ipf_main_softc_t *, void *, iplookupop_t *); static int ipf_htable_deref(ipf_main_softc_t *, void *, void *); static int ipf_htable_destroy(ipf_main_softc_t *, void *, int, char *); static void *ipf_htable_exists(void *, int, char *); static size_t ipf_htable_flush(ipf_main_softc_t *, void *, iplookupflush_t *); static void ipf_htable_free(void *, iphtable_t *); static int ipf_htable_iter_deref(ipf_main_softc_t *, void *, int, int, void *); static int ipf_htable_iter_next(ipf_main_softc_t *, void *, ipftoken_t *, ipflookupiter_t *); static int ipf_htable_node_add(ipf_main_softc_t *, void *, iplookupop_t *, int); static int ipf_htable_node_del(ipf_main_softc_t *, void *, iplookupop_t *, int); static int ipf_htable_remove(ipf_main_softc_t *, void *, iphtable_t *); static void *ipf_htable_soft_create(ipf_main_softc_t *); static void ipf_htable_soft_destroy(ipf_main_softc_t *, void *); static int ipf_htable_soft_init(ipf_main_softc_t *, void *); static void ipf_htable_soft_fini(ipf_main_softc_t *, void *); static int ipf_htable_stats_get(ipf_main_softc_t *, void *, iplookupop_t *); static int ipf_htable_table_add(ipf_main_softc_t *, void *, iplookupop_t *); static int ipf_htable_table_del(ipf_main_softc_t *, void *, iplookupop_t *); static int ipf_htent_deref(void *, iphtent_t *); static iphtent_t *ipf_htent_find(iphtable_t *, iphtent_t *); static int ipf_htent_insert(ipf_main_softc_t *, void *, iphtable_t *, iphtent_t *); static int ipf_htent_remove(ipf_main_softc_t *, void *, iphtable_t *, iphtent_t *); static void *ipf_htable_select_add_ref(void *, int, char *); static void ipf_htable_expire(ipf_main_softc_t *, void *); typedef struct ipf_htable_softc_s { u_long ipht_nomem[LOOKUP_POOL_SZ]; u_long ipf_nhtables[LOOKUP_POOL_SZ]; u_long ipf_nhtnodes[LOOKUP_POOL_SZ]; iphtable_t *ipf_htables[LOOKUP_POOL_SZ]; iphtent_t *ipf_node_explist; } ipf_htable_softc_t; ipf_lookup_t ipf_htable_backend = { IPLT_HASH, ipf_htable_soft_create, ipf_htable_soft_destroy, ipf_htable_soft_init, ipf_htable_soft_fini, ipf_iphmfindip, ipf_htable_flush, ipf_htable_iter_deref, ipf_htable_iter_next, ipf_htable_node_add, ipf_htable_node_del, ipf_htable_stats_get, ipf_htable_table_add, ipf_htable_table_del, ipf_htable_deref, ipf_htable_exists, ipf_htable_select_add_ref, NULL, ipf_htable_expire, NULL }; /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_soft_create */ /* Returns: void * - NULL = failure, else pointer to local context */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise the routing table data structures where required. */ /* ------------------------------------------------------------------------ */ static void * -ipf_htable_soft_create(softc) - ipf_main_softc_t *softc; +ipf_htable_soft_create(ipf_main_softc_t *softc) { ipf_htable_softc_t *softh; KMALLOC(softh, ipf_htable_softc_t *); if (softh == NULL) { IPFERROR(30026); return NULL; } bzero((char *)softh, sizeof(*softh)); return softh; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Clean up the pool by free'ing the radix tree associated with it and free */ /* up the pool context too. */ /* ------------------------------------------------------------------------ */ static void -ipf_htable_soft_destroy(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_htable_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_htable_softc_t *softh = arg; KFREE(softh); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_soft_init */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Initialise the hash table ready for use. */ /* ------------------------------------------------------------------------ */ static int ipf_htable_soft_init(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_htable_softc_t *softh = arg; bzero((char *)softh, sizeof(*softh)); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_soft_fini */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* Locks: WRITE(ipf_global) */ /* */ /* Clean up all the pool data structures allocated and call the cleanup */ /* function for the radix tree that supports the pools. ipf_pool_destroy is */ /* used to delete the pools one by one to ensure they're properly freed up. */ /* ------------------------------------------------------------------------ */ static void -ipf_htable_soft_fini(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_htable_soft_fini(ipf_main_softc_t *softc, void *arg) { iplookupflush_t fop; fop.iplf_type = IPLT_HASH; fop.iplf_unit = IPL_LOGALL; fop.iplf_arg = 0; fop.iplf_count = 0; *fop.iplf_name = '\0'; ipf_htable_flush(softc, arg, &fop); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_stats_get */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Copy the relevant statistics out of internal structures and into the */ /* structure used to export statistics. */ /* ------------------------------------------------------------------------ */ static int -ipf_htable_stats_get(softc, arg, op) - ipf_main_softc_t *softc; - void *arg; - iplookupop_t *op; +ipf_htable_stats_get(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) { ipf_htable_softc_t *softh = arg; iphtstat_t stats; int err; if (op->iplo_size != sizeof(stats)) { IPFERROR(30001); return EINVAL; } stats.iphs_tables = softh->ipf_htables[op->iplo_unit + 1]; stats.iphs_numtables = softh->ipf_nhtables[op->iplo_unit + 1]; stats.iphs_numnodes = softh->ipf_nhtnodes[op->iplo_unit + 1]; stats.iphs_nomem = softh->ipht_nomem[op->iplo_unit + 1]; err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); if (err != 0) { IPFERROR(30013); return EFAULT; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_create */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Create a new hash table using the template passed. */ /* ------------------------------------------------------------------------ */ static int -ipf_htable_create(softc, arg, op) - ipf_main_softc_t *softc; - void *arg; - iplookupop_t *op; +ipf_htable_create(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) { ipf_htable_softc_t *softh = arg; iphtable_t htab, *iph, *oiph; char name[FR_GROUPLEN]; int err, i, unit; if (op->iplo_size != sizeof(htab)) { IPFERROR(30024); return EINVAL; } err = COPYIN(op->iplo_struct, &htab, sizeof(htab)); if (err != 0) { IPFERROR(30003); return EFAULT; } unit = op->iplo_unit; if (htab.iph_unit != unit) { IPFERROR(30005); return EINVAL; } if (htab.iph_size < 1) { IPFERROR(30025); return EINVAL; } if ((op->iplo_arg & IPHASH_ANON) == 0) { iph = ipf_htable_exists(softh, unit, op->iplo_name); if (iph != NULL) { if ((iph->iph_flags & IPHASH_DELETE) == 0) { IPFERROR(30004); return EEXIST; } iph->iph_flags &= ~IPHASH_DELETE; iph->iph_ref++; return 0; } } KMALLOC(iph, iphtable_t *); if (iph == NULL) { softh->ipht_nomem[op->iplo_unit + 1]++; IPFERROR(30002); return ENOMEM; } *iph = htab; if ((op->iplo_arg & IPHASH_ANON) != 0) { i = IPHASH_ANON; do { i++; (void)snprintf(name, sizeof(name), "%u", i); for (oiph = softh->ipf_htables[unit + 1]; oiph != NULL; oiph = oiph->iph_next) if (strncmp(oiph->iph_name, name, sizeof(oiph->iph_name)) == 0) break; } while (oiph != NULL); (void)strncpy(iph->iph_name, name, sizeof(iph->iph_name)); (void)strncpy(op->iplo_name, name, sizeof(op->iplo_name)); iph->iph_type |= IPHASH_ANON; } else { (void)strncpy(iph->iph_name, op->iplo_name, sizeof(iph->iph_name)); iph->iph_name[sizeof(iph->iph_name) - 1] = '\0'; } KMALLOCS(iph->iph_table, iphtent_t **, iph->iph_size * sizeof(*iph->iph_table)); if (iph->iph_table == NULL) { KFREE(iph); softh->ipht_nomem[unit + 1]++; IPFERROR(30006); return ENOMEM; } bzero((char *)iph->iph_table, iph->iph_size * sizeof(*iph->iph_table)); iph->iph_maskset[0] = 0; iph->iph_maskset[1] = 0; iph->iph_maskset[2] = 0; iph->iph_maskset[3] = 0; iph->iph_ref = 1; iph->iph_list = NULL; iph->iph_tail = &iph->iph_list; iph->iph_next = softh->ipf_htables[unit + 1]; iph->iph_pnext = &softh->ipf_htables[unit + 1]; if (softh->ipf_htables[unit + 1] != NULL) softh->ipf_htables[unit + 1]->iph_pnext = &iph->iph_next; softh->ipf_htables[unit + 1] = iph; softh->ipf_nhtables[unit + 1]++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_table_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_htable_table_del(softc, arg, op) - ipf_main_softc_t *softc; - void *arg; - iplookupop_t *op; +ipf_htable_table_del(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) { return ipf_htable_destroy(softc, arg, op->iplo_unit, op->iplo_name); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_destroy */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* Find the hash table that belongs to the relevant part of ipfilter with a */ /* matching name and attempt to destroy it. If it is in use, empty it out */ /* and mark it for deletion so that when all the references disappear, it */ /* can be removed. */ /* ------------------------------------------------------------------------ */ static int -ipf_htable_destroy(softc, arg, unit, name) - ipf_main_softc_t *softc; - void *arg; - int unit; - char *name; +ipf_htable_destroy(ipf_main_softc_t *softc, void *arg, int unit, char *name) { iphtable_t *iph; iph = ipf_htable_find(arg, unit, name); if (iph == NULL) { IPFERROR(30007); return ESRCH; } if (iph->iph_unit != unit) { IPFERROR(30008); return EINVAL; } if (iph->iph_ref != 0) { ipf_htable_clear(softc, arg, iph); iph->iph_flags |= IPHASH_DELETE; return 0; } ipf_htable_remove(softc, arg, iph); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_clear */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* iph(I) - pointer to hash table to destroy */ /* */ /* Clean out the hash table by walking the list of entries and removing */ /* each one, one by one. */ /* ------------------------------------------------------------------------ */ static int -ipf_htable_clear(softc, arg, iph) - ipf_main_softc_t *softc; - void *arg; - iphtable_t *iph; +ipf_htable_clear(ipf_main_softc_t *softc, void *arg, iphtable_t *iph) { iphtent_t *ipe; while ((ipe = iph->iph_list) != NULL) if (ipf_htent_remove(softc, arg, iph, ipe) != 0) return 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_free */ /* Returns: Nil */ /* Parameters: arg(I) - pointer to local context to use */ /* iph(I) - pointer to hash table to destroy */ /* */ /* ------------------------------------------------------------------------ */ static void -ipf_htable_free(arg, iph) - void *arg; - iphtable_t *iph; +ipf_htable_free(void *arg, iphtable_t *iph) { ipf_htable_softc_t *softh = arg; if (iph->iph_next != NULL) iph->iph_next->iph_pnext = iph->iph_pnext; if (iph->iph_pnext != NULL) *iph->iph_pnext = iph->iph_next; iph->iph_pnext = NULL; iph->iph_next = NULL; softh->ipf_nhtables[iph->iph_unit + 1]--; KFREES(iph->iph_table, iph->iph_size * sizeof(*iph->iph_table)); KFREE(iph); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_remove */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* iph(I) - pointer to hash table to destroy */ /* */ /* It is necessary to unlink here as well as free (called by deref) so that */ /* the while loop in ipf_htable_flush() functions properly. */ /* ------------------------------------------------------------------------ */ static int -ipf_htable_remove(softc, arg, iph) - ipf_main_softc_t *softc; - void *arg; - iphtable_t *iph; +ipf_htable_remove(ipf_main_softc_t *softc, void *arg, iphtable_t *iph) { if (ipf_htable_clear(softc, arg, iph) != 0) return 1; if (iph->iph_pnext != NULL) *iph->iph_pnext = iph->iph_next; if (iph->iph_next != NULL) iph->iph_next->iph_pnext = iph->iph_pnext; iph->iph_pnext = NULL; iph->iph_next = NULL; return ipf_htable_deref(softc, arg, iph); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_node_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* uid(I) - real uid of process doing operation */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_htable_node_del(softc, arg, op, uid) - ipf_main_softc_t *softc; - void *arg; - iplookupop_t *op; - int uid; +ipf_htable_node_del(ipf_main_softc_t *softc, void *arg, iplookupop_t *op, + int uid) { iphtable_t *iph; iphtent_t hte, *ent; int err; if (op->iplo_size != sizeof(hte)) { IPFERROR(30014); return EINVAL; } err = COPYIN(op->iplo_struct, &hte, sizeof(hte)); if (err != 0) { IPFERROR(30015); return EFAULT; } iph = ipf_htable_find(arg, op->iplo_unit, op->iplo_name); if (iph == NULL) { IPFERROR(30016); return ESRCH; } ent = ipf_htent_find(iph, &hte); if (ent == NULL) { IPFERROR(30022); return ESRCH; } if ((uid != 0) && (ent->ipe_uid != uid)) { IPFERROR(30023); return EACCES; } err = ipf_htent_remove(softc, arg, iph, ent); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_node_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_htable_table_add(softc, arg, op) - ipf_main_softc_t *softc; - void *arg; - iplookupop_t *op; +ipf_htable_table_add(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) { int err; if (ipf_htable_find(arg, op->iplo_unit, op->iplo_name) != NULL) { IPFERROR(30017); err = EEXIST; } else { err = ipf_htable_create(softc, arg, op); } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htent_remove */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* iph(I) - pointer to hash table */ /* ipe(I) - pointer to hash table entry to remove */ /* */ /* Delete an entry from a hash table. */ /* ------------------------------------------------------------------------ */ static int -ipf_htent_remove(softc, arg, iph, ipe) - ipf_main_softc_t *softc; - void *arg; - iphtable_t *iph; - iphtent_t *ipe; +ipf_htent_remove(ipf_main_softc_t *softc, void *arg, iphtable_t *iph, + iphtent_t *ipe) { if (iph->iph_tail == &ipe->ipe_next) iph->iph_tail = ipe->ipe_pnext; if (ipe->ipe_hnext != NULL) ipe->ipe_hnext->ipe_phnext = ipe->ipe_phnext; if (ipe->ipe_phnext != NULL) *ipe->ipe_phnext = ipe->ipe_hnext; ipe->ipe_phnext = NULL; ipe->ipe_hnext = NULL; if (ipe->ipe_dnext != NULL) ipe->ipe_dnext->ipe_pdnext = ipe->ipe_pdnext; if (ipe->ipe_pdnext != NULL) *ipe->ipe_pdnext = ipe->ipe_dnext; ipe->ipe_pdnext = NULL; ipe->ipe_dnext = NULL; if (ipe->ipe_next != NULL) ipe->ipe_next->ipe_pnext = ipe->ipe_pnext; if (ipe->ipe_pnext != NULL) *ipe->ipe_pnext = ipe->ipe_next; ipe->ipe_pnext = NULL; ipe->ipe_next = NULL; switch (iph->iph_type & ~IPHASH_ANON) { case IPHASH_GROUPMAP : if (ipe->ipe_group != NULL) ipf_group_del(softc, ipe->ipe_ptr, NULL); break; default : ipe->ipe_ptr = NULL; ipe->ipe_value = 0; break; } return ipf_htent_deref(arg, ipe); } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_deref */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* object(I) - pointer to hash table */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_htable_deref(softc, arg, object) - ipf_main_softc_t *softc; - void *arg, *object; +ipf_htable_deref(ipf_main_softc_t *softc, void *arg, void *object) { ipf_htable_softc_t *softh = arg; iphtable_t *iph = object; int refs; iph->iph_ref--; refs = iph->iph_ref; if (iph->iph_ref == 0) { ipf_htable_free(softh, iph); } return refs; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htent_deref */ /* Parameters: arg(I) - pointer to local context to use */ /* ipe(I) - */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_htent_deref(arg, ipe) - void *arg; - iphtent_t *ipe; +ipf_htent_deref(void *arg, iphtent_t *ipe) { ipf_htable_softc_t *softh = arg; ipe->ipe_ref--; if (ipe->ipe_ref == 0) { softh->ipf_nhtnodes[ipe->ipe_unit + 1]--; KFREE(ipe); return 0; } return ipe->ipe_ref; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_exists */ /* Parameters: arg(I) - pointer to local context to use */ /* */ /* ------------------------------------------------------------------------ */ static void * -ipf_htable_exists(arg, unit, name) - void *arg; - int unit; - char *name; +ipf_htable_exists(void *arg, int unit, char *name) { ipf_htable_softc_t *softh = arg; iphtable_t *iph; if (unit == IPL_LOGALL) { int i; for (i = 0; i <= LOOKUP_POOL_MAX; i++) { for (iph = softh->ipf_htables[i]; iph != NULL; iph = iph->iph_next) { if (strncmp(iph->iph_name, name, sizeof(iph->iph_name)) == 0) break; } if (iph != NULL) break; } } else { for (iph = softh->ipf_htables[unit + 1]; iph != NULL; iph = iph->iph_next) { if (strncmp(iph->iph_name, name, sizeof(iph->iph_name)) == 0) break; } } return iph; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_select_add_ref */ /* Returns: void * - NULL = failure, else pointer to the hash table */ /* Parameters: arg(I) - pointer to local context to use */ /* unit(I) - ipfilter device to which we are working on */ /* name(I) - name of the hash table */ /* */ /* ------------------------------------------------------------------------ */ static void * -ipf_htable_select_add_ref(arg, unit, name) - void *arg; - int unit; - char *name; +ipf_htable_select_add_ref(void *arg, int unit, char *name) { iphtable_t *iph; iph = ipf_htable_exists(arg, unit, name); if (iph != NULL) { ATOMIC_INC32(iph->iph_ref); } return iph; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_find */ /* Returns: void * - NULL = failure, else pointer to the hash table */ /* Parameters: arg(I) - pointer to local context to use */ /* unit(I) - ipfilter device to which we are working on */ /* name(I) - name of the hash table */ /* */ /* This function is exposed becaues it is used in the group-map feature. */ /* ------------------------------------------------------------------------ */ iphtable_t * -ipf_htable_find(arg, unit, name) - void *arg; - int unit; - char *name; +ipf_htable_find(void *arg, int unit, char *name) { iphtable_t *iph; iph = ipf_htable_exists(arg, unit, name); if ((iph != NULL) && (iph->iph_flags & IPHASH_DELETE) == 0) return iph; return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_flush */ /* Returns: size_t - number of entries flushed */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* */ /* ------------------------------------------------------------------------ */ static size_t -ipf_htable_flush(softc, arg, op) - ipf_main_softc_t *softc; - void *arg; - iplookupflush_t *op; +ipf_htable_flush(ipf_main_softc_t *softc, void *arg, iplookupflush_t *op) { ipf_htable_softc_t *softh = arg; iphtable_t *iph; size_t freed; int i; freed = 0; for (i = -1; i <= IPL_LOGMAX; i++) { if (op->iplf_unit == i || op->iplf_unit == IPL_LOGALL) { while ((iph = softh->ipf_htables[i + 1]) != NULL) { if (ipf_htable_remove(softc, arg, iph) == 0) { freed++; } else { iph->iph_flags |= IPHASH_DELETE; } } } } return freed; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_node_add */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* uid(I) - real uid of process doing operation */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_htable_node_add(softc, arg, op, uid) - ipf_main_softc_t *softc; - void *arg; - iplookupop_t *op; - int uid; +ipf_htable_node_add(ipf_main_softc_t *softc, void *arg, iplookupop_t *op, + int uid) { iphtable_t *iph; iphtent_t hte; int err; if (op->iplo_size != sizeof(hte)) { IPFERROR(30018); return EINVAL; } err = COPYIN(op->iplo_struct, &hte, sizeof(hte)); if (err != 0) { IPFERROR(30019); return EFAULT; } hte.ipe_uid = uid; iph = ipf_htable_find(arg, op->iplo_unit, op->iplo_name); if (iph == NULL) { IPFERROR(30020); return ESRCH; } if (ipf_htent_find(iph, &hte) != NULL) { IPFERROR(30021); return EEXIST; } err = ipf_htent_insert(softc, arg, iph, &hte); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htent_insert */ /* Returns: int - 0 = success, -1 = error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operation data */ /* ipeo(I) - */ /* */ /* Add an entry to a hash table. */ /* ------------------------------------------------------------------------ */ static int -ipf_htent_insert(softc, arg, iph, ipeo) - ipf_main_softc_t *softc; - void *arg; - iphtable_t *iph; - iphtent_t *ipeo; +ipf_htent_insert(ipf_main_softc_t *softc, void *arg, iphtable_t *iph, + iphtent_t *ipeo) { ipf_htable_softc_t *softh = arg; iphtent_t *ipe; u_int hv; int bits; KMALLOC(ipe, iphtent_t *); if (ipe == NULL) return -1; bcopy((char *)ipeo, (char *)ipe, sizeof(*ipe)); ipe->ipe_addr.i6[0] &= ipe->ipe_mask.i6[0]; if (ipe->ipe_family == AF_INET) { bits = count4bits(ipe->ipe_mask.in4_addr); ipe->ipe_addr.i6[1] = 0; ipe->ipe_addr.i6[2] = 0; ipe->ipe_addr.i6[3] = 0; ipe->ipe_mask.i6[1] = 0; ipe->ipe_mask.i6[2] = 0; ipe->ipe_mask.i6[3] = 0; hv = IPE_V4_HASH_FN(ipe->ipe_addr.in4_addr, ipe->ipe_mask.in4_addr, iph->iph_size); } else #ifdef USE_INET6 if (ipe->ipe_family == AF_INET6) { ipe->ipe_addr.i6[1] &= ipe->ipe_mask.i6[1]; ipe->ipe_addr.i6[2] &= ipe->ipe_mask.i6[2]; ipe->ipe_addr.i6[3] &= ipe->ipe_mask.i6[3]; bits = count6bits(ipe->ipe_mask.i6); hv = IPE_V6_HASH_FN(ipe->ipe_addr.i6, ipe->ipe_mask.i6, iph->iph_size); } else #endif { KFREE(ipe); return -1; } ipe->ipe_owner = iph; ipe->ipe_ref = 1; ipe->ipe_hnext = iph->iph_table[hv]; ipe->ipe_phnext = iph->iph_table + hv; if (iph->iph_table[hv] != NULL) iph->iph_table[hv]->ipe_phnext = &ipe->ipe_hnext; iph->iph_table[hv] = ipe; ipe->ipe_pnext = iph->iph_tail; *iph->iph_tail = ipe; iph->iph_tail = &ipe->ipe_next; ipe->ipe_next = NULL; if (ipe->ipe_die != 0) { /* * If the new node has a given expiration time, insert it * into the list of expiring nodes with the ones to be * removed first added to the front of the list. The * insertion is O(n) but it is kept sorted for quick scans * at expiration interval checks. */ iphtent_t *n; ipe->ipe_die = softc->ipf_ticks + IPF_TTLVAL(ipe->ipe_die); for (n = softh->ipf_node_explist; n != NULL; n = n->ipe_dnext) { if (ipe->ipe_die < n->ipe_die) break; if (n->ipe_dnext == NULL) { /* * We've got to the last node and everything * wanted to be expired before this new node, * so we have to tack it on the end... */ n->ipe_dnext = ipe; ipe->ipe_pdnext = &n->ipe_dnext; n = NULL; break; } } if (softh->ipf_node_explist == NULL) { softh->ipf_node_explist = ipe; ipe->ipe_pdnext = &softh->ipf_node_explist; } else if (n != NULL) { ipe->ipe_dnext = n; ipe->ipe_pdnext = n->ipe_pdnext; n->ipe_pdnext = &ipe->ipe_dnext; } } if (ipe->ipe_family == AF_INET) { ipf_inet_mask_add(bits, &iph->iph_v4_masks); } #ifdef USE_INET6 else if (ipe->ipe_family == AF_INET6) { ipf_inet6_mask_add(bits, &ipe->ipe_mask, &iph->iph_v6_masks); } #endif switch (iph->iph_type & ~IPHASH_ANON) { case IPHASH_GROUPMAP : ipe->ipe_ptr = ipf_group_add(softc, ipe->ipe_group, NULL, iph->iph_flags, IPL_LOGIPF, softc->ipf_active); break; default : ipe->ipe_ptr = NULL; ipe->ipe_value = 0; break; } ipe->ipe_unit = iph->iph_unit; softh->ipf_nhtnodes[ipe->ipe_unit + 1]++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htent_find */ /* Returns: int - 0 = success, else error */ /* Parameters: iph(I) - pointer to table to search */ /* ipeo(I) - pointer to entry to find */ /* */ /* While it isn't absolutely necessary to for the address and mask to be */ /* passed in through an iphtent_t structure, one is always present when it */ /* is time to call this function, so it is just more convenient. */ /* ------------------------------------------------------------------------ */ static iphtent_t * -ipf_htent_find(iph, ipeo) - iphtable_t *iph; - iphtent_t *ipeo; +ipf_htent_find(iphtable_t *iph, iphtent_t *ipeo) { iphtent_t ipe, *ent; u_int hv; int bits; bcopy((char *)ipeo, (char *)&ipe, sizeof(ipe)); ipe.ipe_addr.i6[0] &= ipe.ipe_mask.i6[0]; ipe.ipe_addr.i6[1] &= ipe.ipe_mask.i6[1]; ipe.ipe_addr.i6[2] &= ipe.ipe_mask.i6[2]; ipe.ipe_addr.i6[3] &= ipe.ipe_mask.i6[3]; if (ipe.ipe_family == AF_INET) { bits = count4bits(ipe.ipe_mask.in4_addr); ipe.ipe_addr.i6[1] = 0; ipe.ipe_addr.i6[2] = 0; ipe.ipe_addr.i6[3] = 0; ipe.ipe_mask.i6[1] = 0; ipe.ipe_mask.i6[2] = 0; ipe.ipe_mask.i6[3] = 0; hv = IPE_V4_HASH_FN(ipe.ipe_addr.in4_addr, ipe.ipe_mask.in4_addr, iph->iph_size); } else #ifdef USE_INET6 if (ipe.ipe_family == AF_INET6) { bits = count6bits(ipe.ipe_mask.i6); hv = IPE_V6_HASH_FN(ipe.ipe_addr.i6, ipe.ipe_mask.i6, iph->iph_size); } else #endif return NULL; for (ent = iph->iph_table[hv]; ent != NULL; ent = ent->ipe_hnext) { if (ent->ipe_family != ipe.ipe_family) continue; if (IP6_NEQ(&ipe.ipe_addr, &ent->ipe_addr)) continue; if (IP6_NEQ(&ipe.ipe_mask, &ent->ipe_mask)) continue; break; } return ent; } /* ------------------------------------------------------------------------ */ /* Function: ipf_iphmfindgroup */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* tptr(I) - */ /* aptr(I) - */ /* */ /* Search a hash table for a matching entry and return the pointer stored */ /* in it for use as the next group of rules to search. */ /* */ /* This function is exposed becaues it is used in the group-map feature. */ /* ------------------------------------------------------------------------ */ void * -ipf_iphmfindgroup(softc, tptr, aptr) - ipf_main_softc_t *softc; - void *tptr, *aptr; +ipf_iphmfindgroup(ipf_main_softc_t *softc, void *tptr, void *aptr) { struct in_addr *addr; iphtable_t *iph; iphtent_t *ipe; void *rval; READ_ENTER(&softc->ipf_poolrw); iph = tptr; addr = aptr; ipe = ipf_iphmfind(iph, addr); if (ipe != NULL) rval = ipe->ipe_ptr; else rval = NULL; RWLOCK_EXIT(&softc->ipf_poolrw); return rval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_iphmfindip */ /* Returns: int - 0 == +ve match, -1 == error, 1 == -ve/no match */ /* Parameters: softc(I) - pointer to soft context main structure */ /* tptr(I) - pointer to the pool to search */ /* ipversion(I) - IP protocol version (4 or 6) */ /* aptr(I) - pointer to address information */ /* bytes(I) - packet length */ /* */ /* Search the hash table for a given address and return a search result. */ /* ------------------------------------------------------------------------ */ static int -ipf_iphmfindip(softc, tptr, ipversion, aptr, bytes) - ipf_main_softc_t *softc; - void *tptr, *aptr; - int ipversion; - u_int bytes; +ipf_iphmfindip(ipf_main_softc_t *softc, void *tptr, int ipversion, void *aptr, + u_int bytes) { struct in_addr *addr; iphtable_t *iph; iphtent_t *ipe; int rval; if (tptr == NULL || aptr == NULL) return -1; iph = tptr; addr = aptr; READ_ENTER(&softc->ipf_poolrw); if (ipversion == 4) { ipe = ipf_iphmfind(iph, addr); #ifdef USE_INET6 } else if (ipversion == 6) { ipe = ipf_iphmfind6(iph, (i6addr_t *)addr); #endif } else { ipe = NULL; } if (ipe != NULL) { rval = 0; ipe->ipe_hits++; ipe->ipe_bytes += bytes; } else { rval = 1; } RWLOCK_EXIT(&softc->ipf_poolrw); return rval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_iphmfindip */ /* Parameters: iph(I) - pointer to hash table */ /* addr(I) - pointer to IPv4 address */ /* Locks: ipf_poolrw */ /* */ /* ------------------------------------------------------------------------ */ static iphtent_t * -ipf_iphmfind(iph, addr) - iphtable_t *iph; - struct in_addr *addr; +ipf_iphmfind(iphtable_t *iph, struct in_addr *addr) { u_32_t msk, ips; iphtent_t *ipe; u_int hv; int i; i = 0; maskloop: msk = iph->iph_v4_masks.imt4_active[i]; ips = addr->s_addr & msk; hv = IPE_V4_HASH_FN(ips, msk, iph->iph_size); for (ipe = iph->iph_table[hv]; (ipe != NULL); ipe = ipe->ipe_hnext) { if ((ipe->ipe_family != AF_INET) || (ipe->ipe_mask.in4_addr != msk) || (ipe->ipe_addr.in4_addr != ips)) { continue; } break; } if (ipe == NULL) { i++; if (i < iph->iph_v4_masks.imt4_max) goto maskloop; } return ipe; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_iter_next */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* token(I) - */ /* ilp(I) - */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_htable_iter_next(softc, arg, token, ilp) - ipf_main_softc_t *softc; - void *arg; - ipftoken_t *token; - ipflookupiter_t *ilp; +ipf_htable_iter_next(ipf_main_softc_t *softc, void *arg, ipftoken_t *token, + ipflookupiter_t *ilp) { ipf_htable_softc_t *softh = arg; iphtent_t *node, zn, *nextnode; iphtable_t *iph, zp, *nextiph; void *hnext; int err; err = 0; iph = NULL; node = NULL; nextiph = NULL; nextnode = NULL; READ_ENTER(&softc->ipf_poolrw); switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : iph = token->ipt_data; if (iph == NULL) { nextiph = softh->ipf_htables[(int)ilp->ili_unit + 1]; } else { nextiph = iph->iph_next; } if (nextiph != NULL) { ATOMIC_INC(nextiph->iph_ref); token->ipt_data = nextiph; } else { bzero((char *)&zp, sizeof(zp)); nextiph = &zp; token->ipt_data = NULL; } hnext = nextiph->iph_next; break; case IPFLOOKUPITER_NODE : node = token->ipt_data; if (node == NULL) { iph = ipf_htable_find(arg, ilp->ili_unit, ilp->ili_name); if (iph == NULL) { IPFERROR(30009); err = ESRCH; } else { nextnode = iph->iph_list; } } else { nextnode = node->ipe_next; } if (nextnode != NULL) { ATOMIC_INC(nextnode->ipe_ref); token->ipt_data = nextnode; } else { bzero((char *)&zn, sizeof(zn)); nextnode = &zn; token->ipt_data = NULL; } hnext = nextnode->ipe_next; break; default : IPFERROR(30010); err = EINVAL; hnext = NULL; break; } RWLOCK_EXIT(&softc->ipf_poolrw); if (err != 0) return err; switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : err = COPYOUT(nextiph, ilp->ili_data, sizeof(*nextiph)); if (err != 0) { IPFERROR(30011); err = EFAULT; } if (iph != NULL) { WRITE_ENTER(&softc->ipf_poolrw); ipf_htable_deref(softc, softh, iph); RWLOCK_EXIT(&softc->ipf_poolrw); } break; case IPFLOOKUPITER_NODE : err = COPYOUT(nextnode, ilp->ili_data, sizeof(*nextnode)); if (err != 0) { IPFERROR(30012); err = EFAULT; } if (node != NULL) { WRITE_ENTER(&softc->ipf_poolrw); ipf_htent_deref(softc, node); RWLOCK_EXIT(&softc->ipf_poolrw); } break; } if (hnext == NULL) ipf_token_mark_complete(token); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_iter_deref */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* otype(I) - which data structure type is being walked */ /* unit(I) - ipfilter device to which we are working on */ /* data(I) - pointer to old data structure */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_htable_iter_deref(softc, arg, otype, unit, data) - ipf_main_softc_t *softc; - void *arg; - int otype; - int unit; - void *data; +ipf_htable_iter_deref(ipf_main_softc_t *softc, void *arg, int otype, int unit, + void *data) { if (data == NULL) return EFAULT; if (unit < -1 || unit > IPL_LOGMAX) return EINVAL; switch (otype) { case IPFLOOKUPITER_LIST : ipf_htable_deref(softc, arg, (iphtable_t *)data); break; case IPFLOOKUPITER_NODE : ipf_htent_deref(arg, (iphtent_t *)data); break; default : break; } return 0; } #ifdef USE_INET6 /* ------------------------------------------------------------------------ */ /* Function: ipf_iphmfind6 */ /* Parameters: iph(I) - pointer to hash table */ /* addr(I) - pointer to IPv6 address */ /* Locks: ipf_poolrw */ /* */ /* ------------------------------------------------------------------------ */ static iphtent_t * -ipf_iphmfind6(iph, addr) - iphtable_t *iph; - i6addr_t *addr; +ipf_iphmfind6(iphtable_t *iph, i6addr_t *addr) { i6addr_t *msk, ips; iphtent_t *ipe; u_int hv; int i; i = 0; maskloop: msk = iph->iph_v6_masks.imt6_active + i; ips.i6[0] = addr->i6[0] & msk->i6[0]; ips.i6[1] = addr->i6[1] & msk->i6[1]; ips.i6[2] = addr->i6[2] & msk->i6[2]; ips.i6[3] = addr->i6[3] & msk->i6[3]; hv = IPE_V6_HASH_FN(ips.i6, msk->i6, iph->iph_size); for (ipe = iph->iph_table[hv]; (ipe != NULL); ipe = ipe->ipe_next) { if ((ipe->ipe_family != AF_INET6) || IP6_NEQ(&ipe->ipe_mask, msk) || IP6_NEQ(&ipe->ipe_addr, &ips)) { continue; } break; } if (ipe == NULL) { i++; if (i < iph->iph_v6_masks.imt6_max) goto maskloop; } return ipe; } #endif static void -ipf_htable_expire(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_htable_expire(ipf_main_softc_t *softc, void *arg) { ipf_htable_softc_t *softh = arg; iphtent_t *n; while ((n = softh->ipf_node_explist) != NULL) { if (n->ipe_die > softc->ipf_ticks) break; ipf_htent_remove(softc, softh, n->ipe_owner, n); } } #ifndef _KERNEL /* ------------------------------------------------------------------------ */ /* */ /* ------------------------------------------------------------------------ */ void -ipf_htable_dump(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_htable_dump(ipf_main_softc_t *softc, void *arg) { ipf_htable_softc_t *softh = arg; iphtable_t *iph; int i; printf("List of configured hash tables\n"); for (i = 0; i < IPL_LOGSIZE; i++) for (iph = softh->ipf_htables[i]; iph != NULL; iph = iph->iph_next) printhash(iph, bcopywrap, NULL, opts, NULL); } #endif diff --git a/sys/netpfil/ipfilter/netinet/ip_ipsec_pxy.c b/sys/netpfil/ipfilter/netinet/ip_ipsec_pxy.c index ca0927d00dbf..707a107c3a41 100644 --- a/sys/netpfil/ipfilter/netinet/ip_ipsec_pxy.c +++ b/sys/netpfil/ipfilter/netinet/ip_ipsec_pxy.c @@ -1,431 +1,411 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * Simple ISAKMP transparent proxy for in-kernel use. For use with the NAT * code. * * $Id$ * */ #define IPF_IPSEC_PROXY /* * IPSec proxy */ typedef struct ipf_ipsec_softc_s { frentry_t ipsec_fr; int ipsec_proxy_init; int ipsec_proxy_ttl; ipftq_t *ipsec_nat_tqe; ipftq_t *ipsec_state_tqe; char ipsec_buffer[1500]; } ipf_ipsec_softc_t; void *ipf_p_ipsec_soft_create(ipf_main_softc_t *); void ipf_p_ipsec_soft_destroy(ipf_main_softc_t *, void *); int ipf_p_ipsec_soft_init(ipf_main_softc_t *, void *); void ipf_p_ipsec_soft_fini(ipf_main_softc_t *, void *); int ipf_p_ipsec_init(void); void ipf_p_ipsec_fini(void); int ipf_p_ipsec_new(void *, fr_info_t *, ap_session_t *, nat_t *); void ipf_p_ipsec_del(ipf_main_softc_t *, ap_session_t *); int ipf_p_ipsec_inout(void *, fr_info_t *, ap_session_t *, nat_t *); int ipf_p_ipsec_match(fr_info_t *, ap_session_t *, nat_t *); /* * IPSec application proxy initialization. */ void * -ipf_p_ipsec_soft_create(softc) - ipf_main_softc_t *softc; +ipf_p_ipsec_soft_create(ipf_main_softc_t *softc) { ipf_ipsec_softc_t *softi; KMALLOC(softi, ipf_ipsec_softc_t *); if (softi == NULL) return NULL; bzero((char *)softi, sizeof(*softi)); softi->ipsec_fr.fr_ref = 1; softi->ipsec_fr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&softi->ipsec_fr.fr_lock, "IPsec proxy rule lock"); softi->ipsec_proxy_init = 1; softi->ipsec_proxy_ttl = 60; return softi; } int -ipf_p_ipsec_soft_init(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_p_ipsec_soft_init(ipf_main_softc_t *softc, void *arg) { ipf_ipsec_softc_t *softi = arg; softi->ipsec_nat_tqe = ipf_state_add_tq(softc, softi->ipsec_proxy_ttl); if (softi->ipsec_nat_tqe == NULL) return -1; softi->ipsec_state_tqe = ipf_nat_add_tq(softc, softi->ipsec_proxy_ttl); if (softi->ipsec_state_tqe == NULL) { if (ipf_deletetimeoutqueue(softi->ipsec_nat_tqe) == 0) ipf_freetimeoutqueue(softc, softi->ipsec_nat_tqe); softi->ipsec_nat_tqe = NULL; return -1; } softi->ipsec_nat_tqe->ifq_flags |= IFQF_PROXY; softi->ipsec_state_tqe->ifq_flags |= IFQF_PROXY; softi->ipsec_fr.fr_age[0] = softi->ipsec_proxy_ttl; softi->ipsec_fr.fr_age[1] = softi->ipsec_proxy_ttl; return 0; } void -ipf_p_ipsec_soft_fini(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_p_ipsec_soft_fini(ipf_main_softc_t *softc, void *arg) { ipf_ipsec_softc_t *softi = arg; if (arg == NULL) return; if (softi->ipsec_nat_tqe != NULL) { if (ipf_deletetimeoutqueue(softi->ipsec_nat_tqe) == 0) ipf_freetimeoutqueue(softc, softi->ipsec_nat_tqe); } softi->ipsec_nat_tqe = NULL; if (softi->ipsec_state_tqe != NULL) { if (ipf_deletetimeoutqueue(softi->ipsec_state_tqe) == 0) ipf_freetimeoutqueue(softc, softi->ipsec_state_tqe); } softi->ipsec_state_tqe = NULL; } void -ipf_p_ipsec_soft_destroy(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_p_ipsec_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_ipsec_softc_t *softi = arg; if (softi->ipsec_proxy_init == 1) { MUTEX_DESTROY(&softi->ipsec_fr.fr_lock); softi->ipsec_proxy_init = 0; } KFREE(softi); } /* * Setup for a new IPSEC proxy. */ int -ipf_p_ipsec_new(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_ipsec_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { ipf_ipsec_softc_t *softi = arg; ipf_main_softc_t *softc = fin->fin_main_soft; #ifdef USE_MUTEXES ipf_nat_softc_t *softn = softc->ipf_nat_soft; #endif int p, off, dlen, ttl; ipsec_pxy_t *ipsec; ipnat_t *ipn, *np; fr_info_t fi; char *ptr; int size; ip_t *ip; mb_t *m; if (fin->fin_v != 4) return -1; off = fin->fin_plen - fin->fin_dlen + fin->fin_ipoff; bzero(softi->ipsec_buffer, sizeof(softi->ipsec_buffer)); ip = fin->fin_ip; m = fin->fin_m; dlen = M_LEN(m) - off; if (dlen < 16) return -1; COPYDATA(m, off, MIN(sizeof(softi->ipsec_buffer), dlen), softi->ipsec_buffer); if (ipf_nat_outlookup(fin, 0, IPPROTO_ESP, nat->nat_nsrcip, ip->ip_dst) != NULL) return -1; np = nat->nat_ptr; size = np->in_size; KMALLOC(ipsec, ipsec_pxy_t *); if (ipsec == NULL) return -1; KMALLOCS(ipn, ipnat_t *, size); if (ipn == NULL) { KFREE(ipsec); return -1; } aps->aps_data = ipsec; aps->aps_psiz = sizeof(*ipsec); bzero((char *)ipsec, sizeof(*ipsec)); bzero((char *)ipn, size); ipsec->ipsc_rule = ipn; /* * Create NAT rule against which the tunnel/transport mapping is * created. This is required because the current NAT rule does not * describe ESP but UDP instead. */ ipn->in_size = size; ttl = IPF_TTLVAL(softi->ipsec_nat_tqe->ifq_ttl); ipn->in_tqehead[0] = ipf_nat_add_tq(softc, ttl); ipn->in_tqehead[1] = ipf_nat_add_tq(softc, ttl); ipn->in_ifps[0] = fin->fin_ifp; ipn->in_apr = NULL; ipn->in_use = 1; ipn->in_hits = 1; ipn->in_snip = ntohl(nat->nat_nsrcaddr); ipn->in_ippip = 1; ipn->in_osrcip = nat->nat_osrcip; ipn->in_osrcmsk = 0xffffffff; ipn->in_nsrcip = nat->nat_nsrcip; ipn->in_nsrcmsk = 0xffffffff; ipn->in_odstip = nat->nat_odstip; ipn->in_odstmsk = 0xffffffff; ipn->in_ndstip = nat->nat_ndstip; ipn->in_ndstmsk = 0xffffffff; ipn->in_redir = NAT_MAP; ipn->in_pr[0] = IPPROTO_ESP; ipn->in_pr[1] = IPPROTO_ESP; ipn->in_flags = (np->in_flags | IPN_PROXYRULE); MUTEX_INIT(&ipn->in_lock, "IPSec proxy NAT rule"); ipn->in_namelen = np->in_namelen; bcopy(np->in_names, ipn->in_ifnames, ipn->in_namelen); ipn->in_ifnames[0] = np->in_ifnames[0]; ipn->in_ifnames[1] = np->in_ifnames[1]; bcopy((char *)fin, (char *)&fi, sizeof(fi)); fi.fin_fi.fi_p = IPPROTO_ESP; fi.fin_fr = &softi->ipsec_fr; fi.fin_data[0] = 0; fi.fin_data[1] = 0; p = ip->ip_p; ip->ip_p = IPPROTO_ESP; fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); fi.fin_flx |= FI_IGNORE; ptr = softi->ipsec_buffer; bcopy(ptr, (char *)ipsec->ipsc_icookie, sizeof(ipsec_cookie_t)); ptr += sizeof(ipsec_cookie_t); bcopy(ptr, (char *)ipsec->ipsc_rcookie, sizeof(ipsec_cookie_t)); /* * The responder cookie should only be non-zero if the initiator * cookie is non-zero. Therefore, it is safe to assume(!) that the * cookies are both set after copying if the responder is non-zero. */ if ((ipsec->ipsc_rcookie[0]|ipsec->ipsc_rcookie[1]) != 0) ipsec->ipsc_rckset = 1; MUTEX_ENTER(&softn->ipf_nat_new); ipsec->ipsc_nat = ipf_nat_add(&fi, ipn, &ipsec->ipsc_nat, NAT_SLAVE|SI_WILDP, NAT_OUTBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (ipsec->ipsc_nat != NULL) { (void) ipf_nat_proto(&fi, ipsec->ipsc_nat, 0); MUTEX_ENTER(&ipsec->ipsc_nat->nat_lock); ipf_nat_update(&fi, ipsec->ipsc_nat); MUTEX_EXIT(&ipsec->ipsc_nat->nat_lock); fi.fin_data[0] = 0; fi.fin_data[1] = 0; (void) ipf_state_add(softc, &fi, &ipsec->ipsc_state, SI_WILDP); } ip->ip_p = p & 0xff; return 0; } /* * For outgoing IKE packets. refresh timeouts for NAT & state entries, if * we can. If they have disappeared, recreate them. */ int -ipf_p_ipsec_inout(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_ipsec_inout(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { ipf_ipsec_softc_t *softi = arg; ipf_main_softc_t *softc = fin->fin_main_soft; ipsec_pxy_t *ipsec; fr_info_t fi; ip_t *ip; int p; if ((fin->fin_out == 1) && (nat->nat_dir == NAT_INBOUND)) return 0; if ((fin->fin_out == 0) && (nat->nat_dir == NAT_OUTBOUND)) return 0; ipsec = aps->aps_data; if (ipsec != NULL) { ip = fin->fin_ip; p = ip->ip_p; if ((ipsec->ipsc_nat == NULL) || (ipsec->ipsc_state == NULL)) { bcopy((char *)fin, (char *)&fi, sizeof(fi)); fi.fin_fi.fi_p = IPPROTO_ESP; fi.fin_fr = &softi->ipsec_fr; fi.fin_data[0] = 0; fi.fin_data[1] = 0; ip->ip_p = IPPROTO_ESP; fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); fi.fin_flx |= FI_IGNORE; } /* * Update NAT timeout/create NAT if missing. */ if (ipsec->ipsc_nat != NULL) ipf_queueback(softc->ipf_ticks, &ipsec->ipsc_nat->nat_tqe); else { #ifdef USE_MUTEXES ipf_nat_softc_t *softn = softc->ipf_nat_soft; #endif MUTEX_ENTER(&softn->ipf_nat_new); ipsec->ipsc_nat = ipf_nat_add(&fi, ipsec->ipsc_rule, &ipsec->ipsc_nat, NAT_SLAVE|SI_WILDP, nat->nat_dir); MUTEX_EXIT(&softn->ipf_nat_new); if (ipsec->ipsc_nat != NULL) { (void) ipf_nat_proto(&fi, ipsec->ipsc_nat, 0); MUTEX_ENTER(&ipsec->ipsc_nat->nat_lock); ipf_nat_update(&fi, ipsec->ipsc_nat); MUTEX_EXIT(&ipsec->ipsc_nat->nat_lock); } } /* * Update state timeout/create state if missing. */ READ_ENTER(&softc->ipf_state); if (ipsec->ipsc_state != NULL) { ipf_queueback(softc->ipf_ticks, &ipsec->ipsc_state->is_sti); ipsec->ipsc_state->is_die = nat->nat_age; RWLOCK_EXIT(&softc->ipf_state); } else { RWLOCK_EXIT(&softc->ipf_state); fi.fin_data[0] = 0; fi.fin_data[1] = 0; (void) ipf_state_add(softc, &fi, &ipsec->ipsc_state, SI_WILDP); } ip->ip_p = p; } return 0; } /* * This extends the NAT matching to be based on the cookies associated with * a session and found at the front of IKE packets. The cookies are always * in the same order (not reversed depending on packet flow direction as with * UDP/TCP port numbers). */ int -ipf_p_ipsec_match(fin, aps, nat) - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_ipsec_match(fr_info_t *fin, ap_session_t *aps, nat_t *nat) { ipsec_pxy_t *ipsec; u_32_t cookies[4]; mb_t *m; int off; nat = nat; /* LINT */ if ((fin->fin_dlen < sizeof(cookies)) || (fin->fin_flx & FI_FRAG)) return -1; off = fin->fin_plen - fin->fin_dlen + fin->fin_ipoff; ipsec = aps->aps_data; m = fin->fin_m; COPYDATA(m, off, sizeof(cookies), (char *)cookies); if ((cookies[0] != ipsec->ipsc_icookie[0]) || (cookies[1] != ipsec->ipsc_icookie[1])) return -1; if (ipsec->ipsc_rckset == 0) { if ((cookies[2]|cookies[3]) == 0) { return 0; } ipsec->ipsc_rckset = 1; ipsec->ipsc_rcookie[0] = cookies[2]; ipsec->ipsc_rcookie[1] = cookies[3]; return 0; } if ((cookies[2] != ipsec->ipsc_rcookie[0]) || (cookies[3] != ipsec->ipsc_rcookie[1])) return -1; return 0; } /* * clean up after ourselves. */ void -ipf_p_ipsec_del(softc, aps) - ipf_main_softc_t *softc; - ap_session_t *aps; +ipf_p_ipsec_del(ipf_main_softc_t *softc, ap_session_t *aps) { ipsec_pxy_t *ipsec; ipsec = aps->aps_data; if (ipsec != NULL) { /* * Don't bother changing any of the NAT structure details, * *_del() is on a callback from aps_free(), from nat_delete() */ READ_ENTER(&softc->ipf_state); if (ipsec->ipsc_state != NULL) { ipsec->ipsc_state->is_die = softc->ipf_ticks + 1; ipsec->ipsc_state->is_me = NULL; ipf_queuefront(&ipsec->ipsc_state->is_sti); } RWLOCK_EXIT(&softc->ipf_state); ipsec->ipsc_state = NULL; ipsec->ipsc_nat = NULL; ipsec->ipsc_rule->in_flags |= IPN_DELETE; ipf_nat_rule_deref(softc, &ipsec->ipsc_rule); } } diff --git a/sys/netpfil/ipfilter/netinet/ip_irc_pxy.c b/sys/netpfil/ipfilter/netinet/ip_irc_pxy.c index cedc55c081d1..37f2638d0776 100644 --- a/sys/netpfil/ipfilter/netinet/ip_irc_pxy.c +++ b/sys/netpfil/ipfilter/netinet/ip_irc_pxy.c @@ -1,442 +1,429 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id$ */ #define IPF_IRC_PROXY #define IPF_IRCBUFSZ 96 /* This *MUST* be >= 64! */ void ipf_p_irc_main_load(void); void ipf_p_irc_main_unload(void); int ipf_p_irc_new(void *, fr_info_t *, ap_session_t *, nat_t *); int ipf_p_irc_out(void *, fr_info_t *, ap_session_t *, nat_t *); int ipf_p_irc_send(fr_info_t *, nat_t *); int ipf_p_irc_complete(ircinfo_t *, char *, size_t); u_short ipf_irc_atoi(char **); static frentry_t ircnatfr; int irc_proxy_init = 0; /* * Initialize local structures. */ void -ipf_p_irc_main_load() +ipf_p_irc_main_load(void) { bzero((char *)&ircnatfr, sizeof(ircnatfr)); ircnatfr.fr_ref = 1; ircnatfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&ircnatfr.fr_lock, "IRC proxy rule lock"); irc_proxy_init = 1; } void -ipf_p_irc_main_unload() +ipf_p_irc_main_unload(void) { if (irc_proxy_init == 1) { MUTEX_DESTROY(&ircnatfr.fr_lock); irc_proxy_init = 0; } } const char *ipf_p_irc_dcctypes[] = { "CHAT ", /* CHAT chat ipnumber portnumber */ "SEND ", /* SEND filename ipnumber portnumber */ "MOVE ", "TSEND ", "SCHAT ", NULL, }; /* * :A PRIVMSG B :^ADCC CHAT chat 0 0^A\r\n * PRIVMSG B ^ADCC CHAT chat 0 0^A\r\n */ int -ipf_p_irc_complete(ircp, buf, len) - ircinfo_t *ircp; - char *buf; - size_t len; +ipf_p_irc_complete(ircinfo_t *ircp, char *buf, size_t len) { register char *s, c; register size_t i; u_32_t l; int j, k; ircp->irc_ipnum = 0; ircp->irc_port = 0; if (len < 31) return 0; s = buf; c = *s++; i = len - 1; if ((c != ':') && (c != 'P')) return 0; if (c == ':') { /* * Loosely check that the source is a nickname of some sort */ s++; c = *s; ircp->irc_snick = s; if (!ISALPHA(c)) return 0; i--; for (c = *s; !ISSPACE(c) && (i > 0); i--) c = *s++; if (i < 31) return 0; if (c != 'P') return 0; } else ircp->irc_snick = NULL; /* * Check command string */ if (strncmp(s, "PRIVMSG ", 8)) return 0; i -= 8; s += 8; c = *s; ircp->irc_dnick = s; /* * Loosely check that the destination is a nickname of some sort */ if (!ISALPHA(c)) return 0; for (; !ISSPACE(c) && (i > 0); i--) c = *s++; if (i < 20) return 0; s++, i--; /* * Look for a ^A to start the DCC */ c = *s; if (c == ':') { s++; c = *s; } if (strncmp(s, "\001DCC ", 4)) return 0; i -= 4; s += 4; /* * Check for a recognised DCC command */ for (j = 0, k = 0; ipf_p_irc_dcctypes[j]; j++) { k = MIN(strlen(ipf_p_irc_dcctypes[j]), i); if (!strncmp(ipf_p_irc_dcctypes[j], s, k)) break; } if (!ipf_p_irc_dcctypes[j]) return 0; ircp->irc_type = s; i -= k; s += k; if (i < 11) return 0; /* * Check for the arg */ c = *s; if (ISSPACE(c)) return 0; ircp->irc_arg = s; for (; (c != ' ') && (c != '\001') && (i > 0); i--) c = *s++; if (c == '\001') /* In reality a ^A can quote another ^A...*/ return 0; if (i < 5) return 0; s++; i--; c = *s; if (!ISDIGIT(c)) return 0; ircp->irc_addr = s; /* * Get the IP# */ for (l = 0; ISDIGIT(c) && (i > 0); i--) { l *= 10; l += c - '0'; c = *s++; } if (i < 4) return 0; if (c != ' ') return 0; ircp->irc_ipnum = l; s++; i--; c = *s; if (!ISDIGIT(c)) return 0; /* * Get the port# */ for (l = 0; ISDIGIT(c) && (i > 0); i--) { l *= 10; l += c - '0'; c = *s++; } if (i < 3) return 0; if (strncmp(s, "\001\r\n", 3)) return 0; s += 3; ircp->irc_len = s - buf; ircp->irc_port = l; return 1; } int -ipf_p_irc_new(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_irc_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { ircinfo_t *irc; if (fin->fin_v != 4) return -1; KMALLOC(irc, ircinfo_t *); if (irc == NULL) return -1; nat = nat; /* LINT */ aps->aps_data = irc; aps->aps_psiz = sizeof(ircinfo_t); bzero((char *)irc, sizeof(*irc)); return 0; } int -ipf_p_irc_send(fin, nat) - fr_info_t *fin; - nat_t *nat; +ipf_p_irc_send(fr_info_t *fin, nat_t *nat) { char ctcpbuf[IPF_IRCBUFSZ], newbuf[IPF_IRCBUFSZ]; tcphdr_t *tcp, tcph, *tcp2 = &tcph; int off, inc = 0, i, dlen; ipf_main_softc_t *softc; size_t nlen = 0, olen; struct in_addr swip; u_short a5, sp; ircinfo_t *irc; fr_info_t fi; nat_t *nat2; u_int a1; ip_t *ip; mb_t *m; #if SOLARIS mb_t *m1; #endif softc = fin->fin_main_soft; m = fin->fin_m; ip = fin->fin_ip; tcp = (tcphdr_t *)fin->fin_dp; bzero(ctcpbuf, sizeof(ctcpbuf)); off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; dlen = MSGDSIZE(m) - off; if (dlen <= 0) return 0; COPYDATA(m, off, MIN(sizeof(ctcpbuf), dlen), ctcpbuf); if (dlen <= 0) return 0; ctcpbuf[sizeof(ctcpbuf) - 1] = '\0'; *newbuf = '\0'; irc = nat->nat_aps->aps_data; if (ipf_p_irc_complete(irc, ctcpbuf, dlen) == 0) return 0; /* * check that IP address in the DCC reply is the same as the * sender of the command - prevents use for port scanning. */ if (irc->irc_ipnum != ntohl(nat->nat_osrcaddr)) return 0; a5 = irc->irc_port; /* * Calculate new address parts for the DCC command */ a1 = ntohl(ip->ip_src.s_addr); olen = irc->irc_len; i = irc->irc_addr - ctcpbuf; i++; (void) strncpy(newbuf, ctcpbuf, i); /* DO NOT change these! */ (void) snprintf(newbuf, sizeof(newbuf), "%u %u\001\r\n", a1, a5); nlen = strlen(newbuf); inc = nlen - olen; if ((inc + fin->fin_plen) > 65535) return 0; #if SOLARIS for (m1 = m; m1->b_cont; m1 = m1->b_cont) ; if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { mblk_t *nm; /* alloc enough to keep same trailer space for lower driver */ nm = allocb(nlen, BPRI_MED); PANIC((!nm),("ipf_p_irc_out: allocb failed")); nm->b_band = m1->b_band; nm->b_wptr += nlen; m1->b_wptr -= olen; PANIC((m1->b_wptr < m1->b_rptr), ("ipf_p_irc_out: cannot handle fragmented data block")); linkb(m1, nm); } else { # if SOLARIS && defined(ICK_VALID) if (m1->b_datap->db_struiolim == m1->b_wptr) m1->b_datap->db_struiolim += inc; m1->b_datap->db_struioflag &= ~STRUIO_IP; # endif m1->b_wptr += inc; } #else if (inc < 0) m_adj(m, inc); /* the mbuf chain will be extended if necessary by m_copyback() */ #endif COPYBACK(m, off, nlen, newbuf); fin->fin_flx |= FI_DOCKSUM; if (inc != 0) { #if SOLARIS register u_32_t sum1, sum2; sum1 = fin->fin_plen; sum2 = fin->fin_plen + inc; /* Because ~1 == -2, We really need ~1 == -1 */ if (sum1 > sum2) sum2--; sum2 -= sum1; sum2 = (sum2 & 0xffff) + (sum2 >> 16); ipf_fix_outcksum(0, &ip->ip_sum, sum2, 0); #endif fin->fin_plen += inc; ip->ip_len = htons(fin->fin_plen); fin->fin_dlen += inc; } /* * Add skeleton NAT entry for connection which will come back the * other way. */ sp = htons(a5); /* * Don't allow the PORT command to specify a port < 1024 due to * security crap. */ if (ntohs(sp) < 1024) return 0; /* * The server may not make the connection back from port 20, but * it is the most likely so use it here to check for a conflicting * mapping. */ bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); fi.fin_data[0] = sp; fi.fin_data[1] = fin->fin_data[1]; nat2 = ipf_nat_outlookup(fin, IPN_TCP, nat->nat_pr[1], nat->nat_nsrcip, ip->ip_dst); if (nat2 == NULL) { #ifdef USE_MUTEXES ipf_nat_softc_t *softn = softc->ipf_nat_soft; #endif bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_win = htons(8192); tcp2->th_sport = sp; tcp2->th_dport = 0; /* XXX - don't specify remote port */ fi.fin_data[0] = ntohs(sp); fi.fin_data[1] = 0; fi.fin_dp = (char *)tcp2; fi.fin_fr = &ircnatfr; fi.fin_dlen = sizeof(*tcp2); fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); swip = ip->ip_src; ip->ip_src = nat->nat_nsrcip; MUTEX_ENTER(&softn->ipf_nat_new); nat2 = ipf_nat_add(&fi, nat->nat_ptr, NULL, NAT_SLAVE|IPN_TCP|SI_W_DPORT, NAT_OUTBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 != NULL) { (void) ipf_nat_proto(&fi, nat2, 0); MUTEX_ENTER(&nat2->nat_lock); ipf_nat_update(&fi, nat2); MUTEX_EXIT(&nat2->nat_lock); (void) ipf_state_add(softc, &fi, NULL, SI_W_DPORT); } ip->ip_src = swip; } return inc; } int -ipf_p_irc_out(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_irc_out(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { aps = aps; /* LINT */ return ipf_p_irc_send(fin, nat); } diff --git a/sys/netpfil/ipfilter/netinet/ip_log.c b/sys/netpfil/ipfilter/netinet/ip_log.c index 0f31b9983551..03550682b5e4 100644 --- a/sys/netpfil/ipfilter/netinet/ip_log.c +++ b/sys/netpfil/ipfilter/netinet/ip_log.c @@ -1,901 +1,874 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $FreeBSD$ * Id: ip_log.c,v 2.75.2.19 2007/09/09 11:32:06 darrenr Exp $ */ #include #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #if defined(__FreeBSD__) && !defined(_KERNEL) # include #endif #ifndef SOLARIS # if defined(sun) && defined(__SVR4) # define SOLARIS 1 # else # define SOLARIS 0 # endif #endif #include #include #include #ifndef _KERNEL # include # include # include # include # define _KERNEL # define KERNEL # include # undef _KERNEL # undef KERNEL #endif #if defined(__FreeBSD__) && defined(_KERNEL) # include # include #else # include #endif #include #if defined(_KERNEL) # include # if (defined(NetBSD) && (__NetBSD_Version__ >= 104000000)) # include # endif #endif /* _KERNEL */ # if defined(NetBSD) || defined(__FreeBSD__) # include # include # include # endif # if defined(__FreeBSD__) # include # endif #if SOLARIS && defined(_KERNEL) # include # include # include # include # include # include # include # include # include #endif /* SOLARIS && _KERNEL */ # include #include #include #ifdef sun # include #endif #if defined(__FreeBSD__) # include #endif #include # include #include #include #include #include #include #ifdef USE_INET6 # include #endif # include #ifndef _KERNEL # include #endif #include "netinet/ip_compat.h" #include #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_auth.h" #if defined(__FreeBSD__) || defined(__NetBSD__) # include #endif /* END OF INCLUDES */ #ifdef IPFILTER_LOG # if defined(IPL_SELECT) # include # include # define READ_COLLISION 0x001 extern int selwait; # endif /* IPL_SELECT */ typedef struct ipf_log_softc_s { ipfmutex_t ipl_mutex[IPL_LOGSIZE]; # if SOLARIS && defined(_KERNEL) kcondvar_t ipl_wait[IPL_LOGSIZE]; # endif iplog_t **iplh[IPL_LOGSIZE]; iplog_t *iplt[IPL_LOGSIZE]; iplog_t *ipll[IPL_LOGSIZE]; u_long ipl_logfail[IPL_LOGSIZE]; u_long ipl_logok[IPL_LOGSIZE]; fr_info_t ipl_crc[IPL_LOGSIZE]; u_32_t ipl_counter[IPL_LOGSIZE]; int ipl_suppress; int ipl_logall; int ipl_log_init; int ipl_logsize; int ipl_used[IPL_LOGSIZE]; int ipl_magic[IPL_LOGSIZE]; ipftuneable_t *ipf_log_tune; int ipl_readers[IPL_LOGSIZE]; } ipf_log_softc_t; static int magic[IPL_LOGSIZE] = { IPL_MAGIC, IPL_MAGIC_NAT, IPL_MAGIC_STATE, IPL_MAGIC, IPL_MAGIC, IPL_MAGIC, IPL_MAGIC, IPL_MAGIC }; static ipftuneable_t ipf_log_tuneables[] = { /* log */ { { (void *)offsetof(ipf_log_softc_t, ipl_suppress) }, "log_suppress", 0, 1, stsizeof(ipf_log_softc_t, ipl_suppress), 0, NULL, NULL }, { { (void *)offsetof(ipf_log_softc_t, ipl_logall) }, "log_all", 0, 1, stsizeof(ipf_log_softc_t, ipl_logall), 0, NULL, NULL }, { { (void *)offsetof(ipf_log_softc_t, ipl_logsize) }, "log_size", 0, 0x80000, stsizeof(ipf_log_softc_t, ipl_logsize), 0, NULL, NULL }, { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL } }; int -ipf_log_main_load() +ipf_log_main_load(void) { return 0; } int -ipf_log_main_unload() +ipf_log_main_unload(void) { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_log_soft_create */ /* Returns: void * - NULL = failure, else pointer to log context data */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise log buffers & pointers. Also iniialised the CRC to a local */ /* secret for use in calculating the "last log checksum". */ /* ------------------------------------------------------------------------ */ void * -ipf_log_soft_create(softc) - ipf_main_softc_t *softc; +ipf_log_soft_create(ipf_main_softc_t *softc) { ipf_log_softc_t *softl; int i; KMALLOC(softl, ipf_log_softc_t *); if (softl == NULL) return NULL; bzero((char *)softl, sizeof(*softl)); bcopy((char *)magic, (char *)softl->ipl_magic, sizeof(magic)); softl->ipf_log_tune = ipf_tune_array_copy(softl, sizeof(ipf_log_tuneables), ipf_log_tuneables); if (softl->ipf_log_tune == NULL) { ipf_log_soft_destroy(softc, softl); return NULL; } if (ipf_tune_array_link(softc, softl->ipf_log_tune) == -1) { ipf_log_soft_destroy(softc, softl); return NULL; } for (i = IPL_LOGMAX; i >= 0; i--) { MUTEX_INIT(&softl->ipl_mutex[i], "ipf log mutex"); } softl->ipl_suppress = 1; softl->ipl_logall = 0; softl->ipl_log_init = 0; softl->ipl_logsize = IPFILTER_LOGSIZE; return softl; } /* ------------------------------------------------------------------------ */ /* Function: ipf_log_soft_init */ /* Returns: int - 0 == success (always returned) */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise log buffers & pointers. Also iniialised the CRC to a local */ /* secret for use in calculating the "last log checksum". */ /* ------------------------------------------------------------------------ */ int -ipf_log_soft_init(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_log_soft_init(ipf_main_softc_t *softc, void *arg) { ipf_log_softc_t *softl = arg; int i; for (i = IPL_LOGMAX; i >= 0; i--) { softl->iplt[i] = NULL; softl->ipll[i] = NULL; softl->iplh[i] = &softl->iplt[i]; bzero((char *)&softl->ipl_crc[i], sizeof(softl->ipl_crc[i])); # ifdef IPL_SELECT softl->iplog_ss[i].read_waiter = 0; softl->iplog_ss[i].state = 0; # endif } softl->ipl_log_init = 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_log_soft_fini */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to log context structure */ /* */ /* Clean up any log data that has accumulated without being read. */ /* ------------------------------------------------------------------------ */ int -ipf_log_soft_fini(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_log_soft_fini(ipf_main_softc_t *softc, void *arg) { ipf_log_softc_t *softl = arg; int i; if (softl->ipl_log_init == 0) return 0; softl->ipl_log_init = 0; for (i = IPL_LOGMAX; i >= 0; i--) { (void) ipf_log_clear(softc, i); /* * This is a busy-wait loop so as to avoid yet another lock * to wait on. */ MUTEX_ENTER(&softl->ipl_mutex[i]); while (softl->ipl_readers[i] > 0) { # if SOLARIS && defined(_KERNEL) cv_broadcast(&softl->ipl_wait[i]); MUTEX_EXIT(&softl->ipl_mutex[i]); delay(100); pollwakeup(&softc->ipf_poll_head[i], POLLRDNORM); # else MUTEX_EXIT(&softl->ipl_mutex[i]); WAKEUP(softl->iplh, i); POLLWAKEUP(i); # endif MUTEX_ENTER(&softl->ipl_mutex[i]); } MUTEX_EXIT(&softl->ipl_mutex[i]); } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_log_soft_destroy */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to log context structure */ /* */ /* When this function is called, it is expected that there are no longer */ /* any threads active in the reading code path or the logging code path. */ /* ------------------------------------------------------------------------ */ void -ipf_log_soft_destroy(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_log_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_log_softc_t *softl = arg; int i; for (i = IPL_LOGMAX; i >= 0; i--) { # if SOLARIS && defined(_KERNEL) cv_destroy(&softl->ipl_wait[i]); # endif MUTEX_DESTROY(&softl->ipl_mutex[i]); } if (softl->ipf_log_tune != NULL) { ipf_tune_array_unlink(softc, softl->ipf_log_tune); KFREES(softl->ipf_log_tune, sizeof(ipf_log_tuneables)); softl->ipf_log_tune = NULL; } KFREE(softl); } /* ------------------------------------------------------------------------ */ /* Function: ipf_log_pkt */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: fin(I) - pointer to packet information */ /* flags(I) - flags from filter rules */ /* */ /* Create a log record for a packet given that it has been triggered by a */ /* rule (or the default setting). Calculate the transport protocol header */ /* size using predetermined size of a couple of popular protocols and thus */ /* how much data to copy into the log, including part of the data body if */ /* requested. */ /* ------------------------------------------------------------------------ */ int -ipf_log_pkt(fin, flags) - fr_info_t *fin; - u_int flags; +ipf_log_pkt(fr_info_t *fin, u_int flags) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_log_softc_t *softl = softc->ipf_log_soft; register size_t hlen; int types[2], mlen; size_t sizes[2]; void *ptrs[2]; ipflog_t ipfl; u_char p; mb_t *m; # if SOLARIS && defined(_KERNEL) && !defined(FW_HOOKS) qif_t *ifp; # else struct ifnet *ifp; # endif /* SOLARIS */ m = fin->fin_m; if (m == NULL) return -1; ipfl.fl_nattag.ipt_num[0] = 0; ifp = fin->fin_ifp; hlen = (char *)fin->fin_dp - (char *)fin->fin_ip; /* * calculate header size. */ if (fin->fin_off == 0) { p = fin->fin_fi.fi_p; if (p == IPPROTO_TCP) hlen += MIN(sizeof(tcphdr_t), fin->fin_dlen); else if (p == IPPROTO_UDP) hlen += MIN(sizeof(udphdr_t), fin->fin_dlen); else if (p == IPPROTO_ICMP) { struct icmp *icmp; icmp = (struct icmp *)fin->fin_dp; /* * For ICMP, if the packet is an error packet, also * include the information about the packet which * caused the error. */ switch (icmp->icmp_type) { case ICMP_UNREACH : case ICMP_SOURCEQUENCH : case ICMP_REDIRECT : case ICMP_TIMXCEED : case ICMP_PARAMPROB : hlen += MIN(sizeof(struct icmp) + 8, fin->fin_dlen); break; default : hlen += MIN(sizeof(struct icmp), fin->fin_dlen); break; } } # ifdef USE_INET6 else if (p == IPPROTO_ICMPV6) { struct icmp6_hdr *icmp; icmp = (struct icmp6_hdr *)fin->fin_dp; /* * For ICMPV6, if the packet is an error packet, also * include the information about the packet which * caused the error. */ if (icmp->icmp6_type < 128) { hlen += MIN(sizeof(struct icmp6_hdr) + 8, fin->fin_dlen); } else { hlen += MIN(sizeof(struct icmp6_hdr), fin->fin_dlen); } } # endif } /* * Get the interface number and name to which this packet is * currently associated. */ # if SOLARIS && defined(_KERNEL) # if !defined(FW_HOOKS) ipfl.fl_unit = (u_int)ifp->qf_ppa; # endif COPYIFNAME(fin->fin_v, ifp, ipfl.fl_ifname); # else # if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603)) || \ defined(__FreeBSD__) COPYIFNAME(fin->fin_v, ifp, ipfl.fl_ifname); # else ipfl.fl_unit = (u_int)ifp->if_unit; # if defined(_KERNEL) if ((ipfl.fl_ifname[0] = ifp->if_name[0])) if ((ipfl.fl_ifname[1] = ifp->if_name[1])) if ((ipfl.fl_ifname[2] = ifp->if_name[2])) ipfl.fl_ifname[3] = ifp->if_name[3]; # else (void) strncpy(ipfl.fl_ifname, IFNAME(ifp), sizeof(ipfl.fl_ifname)); ipfl.fl_ifname[sizeof(ipfl.fl_ifname) - 1] = '\0'; # endif # endif # endif /* __hpux || SOLARIS */ mlen = fin->fin_plen - hlen; if (!softl->ipl_logall) { mlen = (flags & FR_LOGBODY) ? MIN(mlen, 128) : 0; } else if ((flags & FR_LOGBODY) == 0) { mlen = 0; } if (mlen < 0) mlen = 0; ipfl.fl_plen = (u_char)mlen; ipfl.fl_hlen = (u_char)hlen; ipfl.fl_rule = fin->fin_rule; (void) strncpy(ipfl.fl_group, fin->fin_group, FR_GROUPLEN); if (fin->fin_fr != NULL) { ipfl.fl_loglevel = fin->fin_fr->fr_loglevel; ipfl.fl_logtag = fin->fin_fr->fr_logtag; } else { ipfl.fl_loglevel = 0xffff; ipfl.fl_logtag = FR_NOLOGTAG; } if (fin->fin_nattag != NULL) bcopy(fin->fin_nattag, (void *)&ipfl.fl_nattag, sizeof(ipfl.fl_nattag)); ipfl.fl_flags = flags; ipfl.fl_breason = (fin->fin_reason & 0xff); ipfl.fl_dir = fin->fin_out; ipfl.fl_lflags = fin->fin_flx; ipfl.fl_family = fin->fin_family; ptrs[0] = (void *)&ipfl; sizes[0] = sizeof(ipfl); types[0] = 0; # if SOLARIS && defined(_KERNEL) /* * Are we copied from the mblk or an aligned array ? */ if (fin->fin_ip == (ip_t *)m->b_rptr) { ptrs[1] = m; sizes[1] = hlen + mlen; types[1] = 1; } else { ptrs[1] = fin->fin_ip; sizes[1] = hlen + mlen; types[1] = 0; } # else ptrs[1] = m; sizes[1] = hlen + mlen; types[1] = 1; # endif /* SOLARIS */ return ipf_log_items(softc, IPL_LOGIPF, fin, ptrs, sizes, types, 2); } /* ------------------------------------------------------------------------ */ /* Function: ipf_log_items */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: softc(I) - pointer to main soft context */ /* unit(I) - device we are reading from */ /* fin(I) - pointer to packet information */ /* items(I) - array of pointers to log data */ /* itemsz(I) - array of size of valid memory pointed to */ /* types(I) - type of data pointed to by items pointers */ /* cnt(I) - number of elements in arrays items/itemsz/types */ /* */ /* Takes an array of parameters and constructs one record to include the */ /* miscellaneous packet information, as well as packet data, for reading */ /* from the log device. */ /* ------------------------------------------------------------------------ */ int -ipf_log_items(softc, unit, fin, items, itemsz, types, cnt) - ipf_main_softc_t *softc; - int unit; - fr_info_t *fin; - void **items; - size_t *itemsz; - int *types, cnt; +ipf_log_items(ipf_main_softc_t *softc, int unit, fr_info_t *fin, void **items, + size_t *itemsz, int *types, int cnt) { ipf_log_softc_t *softl = softc->ipf_log_soft; caddr_t buf, ptr; iplog_t *ipl; size_t len; int i; SPL_INT(s); /* * Get the total amount of data to be logged. */ for (i = 0, len = sizeof(iplog_t); i < cnt; i++) len += itemsz[i]; SPL_NET(s); MUTEX_ENTER(&softl->ipl_mutex[unit]); softl->ipl_counter[unit]++; /* * check that we have space to record this information and can * allocate that much. */ if ((softl->ipl_used[unit] + len) > softl->ipl_logsize) { softl->ipl_logfail[unit]++; MUTEX_EXIT(&softl->ipl_mutex[unit]); return -1; } KMALLOCS(buf, caddr_t, len); if (buf == NULL) { softl->ipl_logfail[unit]++; MUTEX_EXIT(&softl->ipl_mutex[unit]); return -1; } ipl = (iplog_t *)buf; ipl->ipl_magic = softl->ipl_magic[unit]; ipl->ipl_count = 1; ipl->ipl_seqnum = softl->ipl_counter[unit]; ipl->ipl_next = NULL; ipl->ipl_dsize = len; #ifdef _KERNEL GETKTIME(&ipl->ipl_sec); #else ipl->ipl_sec = 0; ipl->ipl_usec = 0; #endif /* * Loop through all the items to be logged, copying each one to the * buffer. Use bcopy for normal data or the mb_t copyout routine. */ for (i = 0, ptr = buf + sizeof(*ipl); i < cnt; i++) { if (types[i] == 0) { bcopy(items[i], ptr, itemsz[i]); } else if (types[i] == 1) { COPYDATA(items[i], 0, itemsz[i], ptr); } ptr += itemsz[i]; } /* * Check to see if this log record has a CRC which matches the last * record logged. If it does, just up the count on the previous one * rather than create a new one. */ if (softl->ipl_suppress) { if ((fin != NULL) && (fin->fin_off == 0)) { if ((softl->ipll[unit] != NULL) && (fin->fin_crc == softl->ipl_crc[unit].fin_crc) && bcmp((char *)fin, (char *)&softl->ipl_crc[unit], FI_LCSIZE) == 0) { softl->ipll[unit]->ipl_count++; MUTEX_EXIT(&softl->ipl_mutex[unit]); SPL_X(s); KFREES(buf, len); return 0; } bcopy((char *)fin, (char *)&softl->ipl_crc[unit], FI_LCSIZE); softl->ipl_crc[unit].fin_crc = fin->fin_crc; } else bzero((char *)&softl->ipl_crc[unit], FI_CSIZE); } /* * advance the log pointer to the next empty record and deduct the * amount of space we're going to use. */ softl->ipl_logok[unit]++; softl->ipll[unit] = ipl; *softl->iplh[unit] = ipl; softl->iplh[unit] = &ipl->ipl_next; softl->ipl_used[unit] += len; /* * Now that the log record has been completed and added to the queue, * wake up any listeners who may want to read it. */ # if SOLARIS && defined(_KERNEL) cv_signal(&softl->ipl_wait[unit]); MUTEX_EXIT(&softl->ipl_mutex[unit]); pollwakeup(&softc->ipf_poll_head[unit], POLLRDNORM); # else MUTEX_EXIT(&softl->ipl_mutex[unit]); WAKEUP(softl->iplh, unit); POLLWAKEUP(unit); # endif SPL_X(s); # ifdef IPL_SELECT iplog_input_ready(unit); # endif return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_log_read */ /* Returns: int - 0 == success, else error value. */ /* Parameters: softc(I) - pointer to main soft context */ /* unit(I) - device we are reading from */ /* uio(O) - pointer to information about where to store data */ /* */ /* Called to handle a read on an IPFilter device. Returns only complete */ /* log messages - will not partially copy a log record out to userland. */ /* */ /* NOTE: This function will block and wait for a signal to return data if */ /* there is none present. Asynchronous I/O is not implemented. */ /* ------------------------------------------------------------------------ */ int -ipf_log_read(softc, unit, uio) - ipf_main_softc_t *softc; - minor_t unit; - struct uio *uio; +ipf_log_read(ipf_main_softc_t *softc, minor_t unit, struct uio *uio) { ipf_log_softc_t *softl = softc->ipf_log_soft; size_t dlen; int error = 0; iplog_t *ipl; SPL_INT(s); if (softl->ipl_log_init == 0) { IPFERROR(40007); return 0; } /* * Sanity checks. Make sure the minor # is valid and we're copying * a valid chunk of data. */ if (IPL_LOGMAX < unit) { IPFERROR(40001); return ENXIO; } if (uio->uio_resid == 0) return 0; if (uio->uio_resid < sizeof(iplog_t)) { IPFERROR(40002); return EINVAL; } if (uio->uio_resid > softl->ipl_logsize) { IPFERROR(40005); return EINVAL; } /* * Lock the log so we can snapshot the variables. Wait for a signal * if the log is empty. */ SPL_NET(s); MUTEX_ENTER(&softl->ipl_mutex[unit]); softl->ipl_readers[unit]++; while (softl->ipl_log_init == 1 && softl->iplt[unit] == NULL) { # if SOLARIS && defined(_KERNEL) if (!cv_wait_sig(&softl->ipl_wait[unit], &softl->ipl_mutex[unit].ipf_lk)) { softl->ipl_readers[unit]--; MUTEX_EXIT(&softl->ipl_mutex[unit]); IPFERROR(40003); return EINTR; } # else MUTEX_EXIT(&softl->ipl_mutex[unit]); SPL_X(s); error = SLEEP(unit + softl->iplh, "ipl sleep"); SPL_NET(s); MUTEX_ENTER(&softl->ipl_mutex[unit]); if (error) { softl->ipl_readers[unit]--; MUTEX_EXIT(&softl->ipl_mutex[unit]); IPFERROR(40004); return error; } # endif /* SOLARIS */ } if (softl->ipl_log_init != 1) { softl->ipl_readers[unit]--; MUTEX_EXIT(&softl->ipl_mutex[unit]); IPFERROR(40008); return EIO; } # if defined(BSD) uio->uio_rw = UIO_READ; # endif for (; (ipl = softl->iplt[unit]) != NULL;) { dlen = ipl->ipl_dsize; if (dlen > uio->uio_resid) break; /* * Don't hold the mutex over the uiomove call. */ softl->iplt[unit] = ipl->ipl_next; softl->ipl_used[unit] -= dlen; MUTEX_EXIT(&softl->ipl_mutex[unit]); SPL_X(s); error = UIOMOVE(ipl, dlen, UIO_READ, uio); if (error) { SPL_NET(s); MUTEX_ENTER(&softl->ipl_mutex[unit]); IPFERROR(40006); ipl->ipl_next = softl->iplt[unit]; softl->iplt[unit] = ipl; softl->ipl_used[unit] += dlen; break; } MUTEX_ENTER(&softl->ipl_mutex[unit]); KFREES((caddr_t)ipl, dlen); SPL_NET(s); } if (!softl->iplt[unit]) { softl->ipl_used[unit] = 0; softl->iplh[unit] = &softl->iplt[unit]; softl->ipll[unit] = NULL; } softl->ipl_readers[unit]--; MUTEX_EXIT(&softl->ipl_mutex[unit]); SPL_X(s); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_log_clear */ /* Returns: int - number of log bytes cleared. */ /* Parameters: softc(I) - pointer to main soft context */ /* unit(I) - device we are reading from */ /* */ /* Deletes all queued up log records for a given output device. */ /* ------------------------------------------------------------------------ */ int -ipf_log_clear(softc, unit) - ipf_main_softc_t *softc; - minor_t unit; +ipf_log_clear(ipf_main_softc_t *softc, minor_t unit) { ipf_log_softc_t *softl = softc->ipf_log_soft; iplog_t *ipl; int used; SPL_INT(s); SPL_NET(s); MUTEX_ENTER(&softl->ipl_mutex[unit]); while ((ipl = softl->iplt[unit]) != NULL) { softl->iplt[unit] = ipl->ipl_next; KFREES((caddr_t)ipl, ipl->ipl_dsize); } softl->iplh[unit] = &softl->iplt[unit]; softl->ipll[unit] = NULL; used = softl->ipl_used[unit]; softl->ipl_used[unit] = 0; bzero((char *)&softl->ipl_crc[unit], FI_CSIZE); MUTEX_EXIT(&softl->ipl_mutex[unit]); SPL_X(s); return used; } /* ------------------------------------------------------------------------ */ /* Function: ipf_log_canread */ /* Returns: int - 0 == no data to read, 1 = data present */ /* Parameters: softc(I) - pointer to main soft context */ /* unit(I) - device we are reading from */ /* */ /* Returns an indication of whether or not there is data present in the */ /* current buffer for the selected ipf device. */ /* ------------------------------------------------------------------------ */ int -ipf_log_canread(softc, unit) - ipf_main_softc_t *softc; - int unit; +ipf_log_canread(ipf_main_softc_t *softc, int unit) { ipf_log_softc_t *softl = softc->ipf_log_soft; return softl->iplt[unit] != NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_log_canread */ /* Returns: int - 0 == no data to read, 1 = data present */ /* Parameters: softc(I) - pointer to main soft context */ /* unit(I) - device we are reading from */ /* */ /* Returns how many bytes are currently held in log buffers for the */ /* selected ipf device. */ /* ------------------------------------------------------------------------ */ int -ipf_log_bytesused(softc, unit) - ipf_main_softc_t *softc; - int unit; +ipf_log_bytesused(ipf_main_softc_t *softc, int unit) { ipf_log_softc_t *softl = softc->ipf_log_soft; if (softl == NULL) return 0; return softl->ipl_used[unit]; } /* ------------------------------------------------------------------------ */ /* Function: ipf_log_failures */ /* Returns: U_QUAD_T - number of log failures */ /* Parameters: softc(I) - pointer to main soft context */ /* unit(I) - device we are reading from */ /* */ /* Returns how many times we've tried to log a packet but failed to do so */ /* for the selected ipf device. */ /* ------------------------------------------------------------------------ */ u_long -ipf_log_failures(softc, unit) - ipf_main_softc_t *softc; - int unit; +ipf_log_failures(ipf_main_softc_t *softc, int unit) { ipf_log_softc_t *softl = softc->ipf_log_soft; if (softl == NULL) return 0; return softl->ipl_logfail[unit]; } /* ------------------------------------------------------------------------ */ /* Function: ipf_log_logok */ /* Returns: U_QUAD_T - number of packets logged */ /* Parameters: softc(I) - pointer to main soft context */ /* unit(I) - device we are reading from */ /* */ /* Returns how many times we've successfully logged a packet for the */ /* selected ipf device. */ /* ------------------------------------------------------------------------ */ u_long -ipf_log_logok(softc, unit) - ipf_main_softc_t *softc; - int unit; +ipf_log_logok(ipf_main_softc_t *softc, int unit) { ipf_log_softc_t *softl = softc->ipf_log_soft; if (softl == NULL) return 0; return softl->ipl_logok[unit]; } #endif /* IPFILTER_LOG */ diff --git a/sys/netpfil/ipfilter/netinet/ip_lookup.c b/sys/netpfil/ipfilter/netinet/ip_lookup.c index 1a92093080b1..a6b641b07b6d 100644 --- a/sys/netpfil/ipfilter/netinet/ip_lookup.c +++ b/sys/netpfil/ipfilter/netinet/ip_lookup.c @@ -1,994 +1,939 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if defined(__FreeBSD__) && defined(_KERNEL) # include # include #else # include #endif #if !defined(_KERNEL) # include # include # include # define _KERNEL # include # undef _KERNEL #endif #include #include #if defined(__FreeBSD__) # include # include #endif #if defined(_KERNEL) # include # if !defined(__SVR4) # include # endif #else # include "ipf.h" #endif #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_lookup.h" #include "netinet/ip_pool.h" #include "netinet/ip_htable.h" #include "netinet/ip_dstlist.h" /* END OF INCLUDES */ #if !defined(lint) static const char rcsid[] = "@(#)$Id$"; #endif /* * In this file, ip_pool.c, ip_htable.c and ip_dstlist.c, you will find the * range for unit is [-1,IPL_LOGMAX]. The -1 is considered to be a valid number * and represents a "wildcard" or "all" units (IPL_LOGALL). The reason for not * starting the numbering at 0 is because the numbers [0,IPL_LOGMAX] correspond * to the minor device number for their respective device. Thus where there is * array indexing on the unit, +1 is used to map [-1.IPL_LOGMAX] to * [0.POOL_LOOKUP_MAX]. */ static int ipf_lookup_addnode(ipf_main_softc_t *, caddr_t, int); static int ipf_lookup_delnode(ipf_main_softc_t *, caddr_t, int); static int ipf_lookup_addtable(ipf_main_softc_t *, caddr_t); static int ipf_lookup_deltable(ipf_main_softc_t *, caddr_t); static int ipf_lookup_stats(ipf_main_softc_t *, caddr_t); static int ipf_lookup_flush(ipf_main_softc_t *, caddr_t); static int ipf_lookup_iterate(ipf_main_softc_t *, void *, int, void *); static int ipf_lookup_deltok(ipf_main_softc_t *, void *, int, void *); #define MAX_BACKENDS 3 static ipf_lookup_t *backends[MAX_BACKENDS] = { &ipf_pool_backend, &ipf_htable_backend, &ipf_dstlist_backend }; typedef struct ipf_lookup_softc_s { void *ipf_back[MAX_BACKENDS]; } ipf_lookup_softc_t; /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_init */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise all of the subcomponents of the lookup infrstructure. */ /* ------------------------------------------------------------------------ */ void * -ipf_lookup_soft_create(softc) - ipf_main_softc_t *softc; +ipf_lookup_soft_create(ipf_main_softc_t *softc) { ipf_lookup_softc_t *softl; ipf_lookup_t **l; int i; KMALLOC(softl, ipf_lookup_softc_t *); if (softl == NULL) return NULL; bzero((char *)softl, sizeof(*softl)); for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { softl->ipf_back[i] = (*(*l)->ipfl_create)(softc); if (softl->ipf_back[i] == NULL) { ipf_lookup_soft_destroy(softc, softl); return NULL; } } return softl; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_soft_init */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Initialise all of the subcomponents of the lookup infrstructure. */ /* ------------------------------------------------------------------------ */ int -ipf_lookup_soft_init(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_lookup_soft_init(ipf_main_softc_t *softc, void *arg) { ipf_lookup_softc_t *softl = (ipf_lookup_softc_t *)arg; int err = 0; int i; for (i = 0; i < MAX_BACKENDS; i++) { err = (*backends[i]->ipfl_init)(softc, softl->ipf_back[i]); if (err != 0) break; } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_soft_fini */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Call the fini function in each backend to cleanup all allocated data. */ /* ------------------------------------------------------------------------ */ int -ipf_lookup_soft_fini(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_lookup_soft_fini(ipf_main_softc_t *softc, void *arg) { ipf_lookup_softc_t *softl = (ipf_lookup_softc_t *)arg; int i; for (i = 0; i < MAX_BACKENDS; i++) { if (softl->ipf_back[i] != NULL) (*backends[i]->ipfl_fini)(softc, softl->ipf_back[i]); } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Step through each of the backends and call their expire functions, */ /* allowing them to delete any lifetime limited data. */ /* ------------------------------------------------------------------------ */ void -ipf_lookup_expire(softc) - ipf_main_softc_t *softc; +ipf_lookup_expire(ipf_main_softc_t *softc) { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; int i; WRITE_ENTER(&softc->ipf_poolrw); for (i = 0; i < MAX_BACKENDS; i++) (*backends[i]->ipfl_expire)(softc, softl->ipf_back[i]); RWLOCK_EXIT(&softc->ipf_poolrw); } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_softc_destroy */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Free up all pool related memory that has been allocated whilst IPFilter */ /* has been running. Also, do any other deinitialisation required such */ /* ipf_lookup_init() can be called again, safely. */ /* ------------------------------------------------------------------------ */ void -ipf_lookup_soft_destroy(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_lookup_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_lookup_softc_t *softl = (ipf_lookup_softc_t *)arg; int i; for (i = 0; i < MAX_BACKENDS; i++) { if (softl->ipf_back[i] != NULL) (*backends[i]->ipfl_destroy)(softc, softl->ipf_back[i]); } KFREE(softl); } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_ioctl */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* data(IO) - pointer to ioctl data to be copied to/from user */ /* space. */ /* cmd(I) - ioctl command number */ /* mode(I) - file mode bits used with open */ /* uid(I) - uid of process doing ioctl */ /* ctx(I) - pointer that represents context for uid */ /* */ /* Handle ioctl commands sent to the ioctl device. For the most part, this */ /* involves just calling another function to handle the specifics of each */ /* command. */ /* ------------------------------------------------------------------------ */ int -ipf_lookup_ioctl(softc, data, cmd, mode, uid, ctx) - ipf_main_softc_t *softc; - caddr_t data; - ioctlcmd_t cmd; - int mode, uid; - void *ctx; +ipf_lookup_ioctl(ipf_main_softc_t *softc, caddr_t data, ioctlcmd_t cmd, + int mode, int uid, void *ctx) { int err; SPL_INT(s); mode = mode; /* LINT */ SPL_NET(s); switch (cmd) { case SIOCLOOKUPADDNODE : case SIOCLOOKUPADDNODEW : WRITE_ENTER(&softc->ipf_poolrw); err = ipf_lookup_addnode(softc, data, uid); RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPDELNODE : case SIOCLOOKUPDELNODEW : WRITE_ENTER(&softc->ipf_poolrw); err = ipf_lookup_delnode(softc, data, uid); RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPADDTABLE : WRITE_ENTER(&softc->ipf_poolrw); err = ipf_lookup_addtable(softc, data); RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPDELTABLE : WRITE_ENTER(&softc->ipf_poolrw); err = ipf_lookup_deltable(softc, data); RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPSTAT : case SIOCLOOKUPSTATW : WRITE_ENTER(&softc->ipf_poolrw); err = ipf_lookup_stats(softc, data); RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPFLUSH : WRITE_ENTER(&softc->ipf_poolrw); err = ipf_lookup_flush(softc, data); RWLOCK_EXIT(&softc->ipf_poolrw); break; case SIOCLOOKUPITER : err = ipf_lookup_iterate(softc, data, uid, ctx); break; case SIOCIPFDELTOK : err = ipf_lookup_deltok(softc, data, uid, ctx); break; default : IPFERROR(50001); err = EINVAL; break; } SPL_X(s); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_addnode */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to data from ioctl call */ /* */ /* Add a new data node to a lookup structure. First, check to see if the */ /* parent structure refered to by name exists and if it does, then go on to */ /* add a node to it. */ /* ------------------------------------------------------------------------ */ static int -ipf_lookup_addnode(softc, data, uid) - ipf_main_softc_t *softc; - caddr_t data; - int uid; +ipf_lookup_addnode(ipf_main_softc_t *softc, caddr_t data, int uid) { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; ipf_lookup_t **l; int err; int i; err = BCOPYIN(data, &op, sizeof(op)); if (err != 0) { IPFERROR(50002); return EFAULT; } if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) && (op.iplo_unit != IPLT_ALL)) { IPFERROR(50003); return EINVAL; } op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { if (op.iplo_type == (*l)->ipfl_type) { err = (*(*l)->ipfl_node_add)(softc, softl->ipf_back[i], &op, uid); break; } } if (i == MAX_BACKENDS) { IPFERROR(50012); err = EINVAL; } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_delnode */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to data from ioctl call */ /* */ /* Delete a node from a lookup table by first looking for the table it is */ /* in and then deleting the entry that gets found. */ /* ------------------------------------------------------------------------ */ static int -ipf_lookup_delnode(softc, data, uid) - ipf_main_softc_t *softc; - caddr_t data; - int uid; +ipf_lookup_delnode(ipf_main_softc_t *softc, caddr_t data, int uid) { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; ipf_lookup_t **l; int err; int i; err = BCOPYIN(data, &op, sizeof(op)); if (err != 0) { IPFERROR(50042); return EFAULT; } if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) && (op.iplo_unit != IPLT_ALL)) { IPFERROR(50013); return EINVAL; } op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { if (op.iplo_type == (*l)->ipfl_type) { err = (*(*l)->ipfl_node_del)(softc, softl->ipf_back[i], &op, uid); break; } } if (i == MAX_BACKENDS) { IPFERROR(50021); err = EINVAL; } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_addtable */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to data from ioctl call */ /* */ /* Create a new lookup table, if one doesn't already exist using the name */ /* for this one. */ /* ------------------------------------------------------------------------ */ static int -ipf_lookup_addtable(softc, data) - ipf_main_softc_t *softc; - caddr_t data; +ipf_lookup_addtable(ipf_main_softc_t *softc, caddr_t data) { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; ipf_lookup_t **l; int err, i; err = BCOPYIN(data, &op, sizeof(op)); if (err != 0) { IPFERROR(50022); return EFAULT; } if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) && (op.iplo_unit != IPLT_ALL)) { IPFERROR(50023); return EINVAL; } op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { if (op.iplo_type == (*l)->ipfl_type) { err = (*(*l)->ipfl_table_add)(softc, softl->ipf_back[i], &op); break; } } if (i == MAX_BACKENDS) { IPFERROR(50026); err = EINVAL; } /* * For anonymous pools, copy back the operation struct because in the * case of success it will contain the new table's name. */ if ((err == 0) && ((op.iplo_arg & LOOKUP_ANON) != 0)) { err = BCOPYOUT(&op, data, sizeof(op)); if (err != 0) { IPFERROR(50027); err = EFAULT; } } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_deltable */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to data from ioctl call */ /* */ /* Decodes ioctl request to remove a particular hash table or pool and */ /* calls the relevant function to do the cleanup. */ /* ------------------------------------------------------------------------ */ static int -ipf_lookup_deltable(softc, data) - ipf_main_softc_t *softc; - caddr_t data; +ipf_lookup_deltable(ipf_main_softc_t *softc, caddr_t data) { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; ipf_lookup_t **l; int err, i; err = BCOPYIN(data, &op, sizeof(op)); if (err != 0) { IPFERROR(50028); return EFAULT; } if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) && (op.iplo_unit != IPLT_ALL)) { IPFERROR(50029); return EINVAL; } op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { if (op.iplo_type == (*l)->ipfl_type) { err = (*(*l)->ipfl_table_del)(softc, softl->ipf_back[i], &op); break; } } if (i == MAX_BACKENDS) { IPFERROR(50030); err = EINVAL; } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_stats */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to data from ioctl call */ /* */ /* Copy statistical information from inside the kernel back to user space. */ /* ------------------------------------------------------------------------ */ static int -ipf_lookup_stats(softc, data) - ipf_main_softc_t *softc; - caddr_t data; +ipf_lookup_stats(ipf_main_softc_t *softc, caddr_t data) { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; iplookupop_t op; ipf_lookup_t **l; int err; int i; err = BCOPYIN(data, &op, sizeof(op)); if (err != 0) { IPFERROR(50031); return EFAULT; } if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) && (op.iplo_unit != IPLT_ALL)) { IPFERROR(50032); return EINVAL; } for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { if (op.iplo_type == (*l)->ipfl_type) { err = (*(*l)->ipfl_stats_get)(softc, softl->ipf_back[i], &op); break; } } if (i == MAX_BACKENDS) { IPFERROR(50033); err = EINVAL; } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_flush */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to data from ioctl call */ /* */ /* A flush is called when we want to flush all the nodes from a particular */ /* entry in the hash table/pool or want to remove all groups from those. */ /* ------------------------------------------------------------------------ */ static int -ipf_lookup_flush(softc, data) - ipf_main_softc_t *softc; - caddr_t data; +ipf_lookup_flush(ipf_main_softc_t *softc, caddr_t data) { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; int err, unit, num, type, i; iplookupflush_t flush; ipf_lookup_t **l; err = BCOPYIN(data, &flush, sizeof(flush)); if (err != 0) { IPFERROR(50034); return EFAULT; } unit = flush.iplf_unit; if ((unit < 0 || unit > IPL_LOGMAX) && (unit != IPLT_ALL)) { IPFERROR(50035); return EINVAL; } flush.iplf_name[sizeof(flush.iplf_name) - 1] = '\0'; type = flush.iplf_type; IPFERROR(50036); err = EINVAL; num = 0; for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { if (type == (*l)->ipfl_type || type == IPLT_ALL) { err = 0; num += (*(*l)->ipfl_flush)(softc, softl->ipf_back[i], &flush); } } if (err == 0) { flush.iplf_count = num; err = BCOPYOUT(&flush, data, sizeof(flush)); if (err != 0) { IPFERROR(50037); err = EFAULT; } } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_delref */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* type(I) - table type to operate on */ /* ptr(I) - pointer to object to remove reference for */ /* */ /* This function organises calling the correct deref function for a given */ /* type of object being passed into it. */ /* ------------------------------------------------------------------------ */ void -ipf_lookup_deref(softc, type, ptr) - ipf_main_softc_t *softc; - int type; - void *ptr; +ipf_lookup_deref(ipf_main_softc_t *softc, int type, void *ptr) { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; int i; if (ptr == NULL) return; for (i = 0; i < MAX_BACKENDS; i++) { if (type == backends[i]->ipfl_type) { WRITE_ENTER(&softc->ipf_poolrw); (*backends[i]->ipfl_table_deref)(softc, softl->ipf_back[i], ptr); RWLOCK_EXIT(&softc->ipf_poolrw); break; } } } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_iterate */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to data from ioctl call */ /* uid(I) - uid of caller */ /* ctx(I) - pointer to give the uid context */ /* */ /* Decodes ioctl request to step through either hash tables or pools. */ /* ------------------------------------------------------------------------ */ static int -ipf_lookup_iterate(softc, data, uid, ctx) - ipf_main_softc_t *softc; - void *data; - int uid; - void *ctx; +ipf_lookup_iterate(ipf_main_softc_t *softc, void *data, int uid, void *ctx) { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; ipflookupiter_t iter; ipftoken_t *token; int err, i; SPL_INT(s); err = ipf_inobj(softc, data, NULL, &iter, IPFOBJ_LOOKUPITER); if (err != 0) return err; if (iter.ili_unit < IPL_LOGALL && iter.ili_unit > IPL_LOGMAX) { IPFERROR(50038); return EINVAL; } if (iter.ili_ival != IPFGENITER_LOOKUP) { IPFERROR(50039); return EINVAL; } SPL_SCHED(s); token = ipf_token_find(softc, iter.ili_key, uid, ctx); if (token == NULL) { SPL_X(s); IPFERROR(50040); return ESRCH; } for (i = 0; i < MAX_BACKENDS; i++) { if (iter.ili_type == backends[i]->ipfl_type) { err = (*backends[i]->ipfl_iter_next)(softc, softl->ipf_back[i], token, &iter); break; } } SPL_X(s); if (i == MAX_BACKENDS) { IPFERROR(50041); err = EINVAL; } WRITE_ENTER(&softc->ipf_tokens); ipf_token_deref(softc, token); RWLOCK_EXIT(&softc->ipf_tokens); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_iterderef */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* type(I) - backend type to iterate through */ /* data(I) - pointer to data from ioctl call */ /* */ /* Decodes ioctl request to remove a particular hash table or pool and */ /* calls the relevant function to do the cleanup. */ /* Because each of the backend types has a different data structure, */ /* iteration is limited to one type at a time (i.e. it is not permitted to */ /* go on from pool types to hash types as part of the "get next".) */ /* ------------------------------------------------------------------------ */ void -ipf_lookup_iterderef(softc, type, data) - ipf_main_softc_t *softc; - u_32_t type; - void *data; +ipf_lookup_iterderef(ipf_main_softc_t *softc, u_32_t type, void *data) { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; struct iplookupiterkey *lkey; iplookupiterkey_t key; int i; key.ilik_key = type; lkey = &key.ilik_unstr; if (lkey->ilik_ival != IPFGENITER_LOOKUP) return; WRITE_ENTER(&softc->ipf_poolrw); for (i = 0; i < MAX_BACKENDS; i++) { if (lkey->ilik_type == backends[i]->ipfl_type) { (*backends[i]->ipfl_iter_deref)(softc, softl->ipf_back[i], lkey->ilik_otype, lkey->ilik_unit, data); break; } } RWLOCK_EXIT(&softc->ipf_poolrw); } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_deltok */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to data from ioctl call */ /* uid(I) - uid of caller */ /* ctx(I) - pointer to give the uid context */ /* */ /* Deletes the token identified by the combination of (type,uid,ctx) */ /* "key" is a combination of the table type, iterator type and the unit for */ /* which the token was being used. */ /* ------------------------------------------------------------------------ */ int -ipf_lookup_deltok(softc, data, uid, ctx) - ipf_main_softc_t *softc; - void *data; - int uid; - void *ctx; +ipf_lookup_deltok(ipf_main_softc_t *softc, void *data, int uid, void *ctx) { int error, key; SPL_INT(s); SPL_SCHED(s); error = BCOPYIN(data, &key, sizeof(key)); if (error == 0) error = ipf_token_del(softc, key, uid, ctx); SPL_X(s); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_res_num */ /* Returns: void * - NULL = failure, else success. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* unit(I) - device for which this is for */ /* type(I) - type of lookup these parameters are for. */ /* number(I) - table number to use when searching */ /* funcptr(IO) - pointer to pointer for storing IP address */ /* searching function. */ /* */ /* Search for the "table" number passed in amongst those configured for */ /* that particular type. If the type is recognised then the function to */ /* call to do the IP address search will be change, regardless of whether */ /* or not the "table" number exists. */ /* ------------------------------------------------------------------------ */ void * -ipf_lookup_res_num(softc, unit, type, number, funcptr) - ipf_main_softc_t *softc; - int unit; - u_int type; - u_int number; - lookupfunc_t *funcptr; +ipf_lookup_res_num(ipf_main_softc_t *softc, int unit, u_int type, u_int number, + lookupfunc_t *funcptr) { char name[FR_GROUPLEN]; (void) snprintf(name, sizeof(name), "%u", number); return ipf_lookup_res_name(softc, unit, type, name, funcptr); } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_res_name */ /* Returns: void * - NULL = failure, else success. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* unit(I) - device for which this is for */ /* type(I) - type of lookup these parameters are for. */ /* name(I) - table name to use when searching */ /* funcptr(IO) - pointer to pointer for storing IP address */ /* searching function. */ /* */ /* Search for the "table" number passed in amongst those configured for */ /* that particular type. If the type is recognised then the function to */ /* call to do the IP address search will be changed, regardless of whether */ /* or not the "table" number exists. */ /* ------------------------------------------------------------------------ */ void * -ipf_lookup_res_name(softc, unit, type, name, funcptr) - ipf_main_softc_t *softc; - int unit; - u_int type; - char *name; - lookupfunc_t *funcptr; +ipf_lookup_res_name(ipf_main_softc_t *softc, int unit, u_int type, char *name, + lookupfunc_t *funcptr) { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; ipf_lookup_t **l; void *ptr = NULL; int i; READ_ENTER(&softc->ipf_poolrw); for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) { if (type == (*l)->ipfl_type) { ptr = (*(*l)->ipfl_select_add_ref)(softl->ipf_back[i], unit, name); if (ptr != NULL && funcptr != NULL) { *funcptr = (*l)->ipfl_addr_find; } break; } } if (i == MAX_BACKENDS) { ptr = NULL; if (funcptr != NULL) *funcptr = NULL; } RWLOCK_EXIT(&softc->ipf_poolrw); return ptr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_find_htable */ /* Returns: void * - NULL = failure, else success. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* unit(I) - device for which this is for */ /* name(I) - table name to use when searching */ /* */ /* To support the group-map feature, where a hash table maps address */ /* networks to rule group numbers, we need to expose a function that uses */ /* only the hash table backend. */ /* ------------------------------------------------------------------------ */ void * -ipf_lookup_find_htable(softc, unit, name) - ipf_main_softc_t *softc; - int unit; - char *name; +ipf_lookup_find_htable(ipf_main_softc_t *softc, int unit, char *name) { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; ipf_lookup_t **l; void *tab = NULL; int i; READ_ENTER(&softc->ipf_poolrw); for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) if (IPLT_HASH == (*l)->ipfl_type) { tab = ipf_htable_find(softl->ipf_back[i], unit, name); break; } RWLOCK_EXIT(&softc->ipf_poolrw); return tab; } /* ------------------------------------------------------------------------ */ /* Function: ipf_lookup_sync */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* This function is the interface that the machine dependent sync functions */ /* call when a network interface name change occurs. It then calls the sync */ /* functions of the lookup implementations - if they have one. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ void -ipf_lookup_sync(softc, ifp) - ipf_main_softc_t *softc; - void *ifp; +ipf_lookup_sync(ipf_main_softc_t *softc, void *ifp) { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; ipf_lookup_t **l; int i; READ_ENTER(&softc->ipf_poolrw); for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) if ((*l)->ipfl_sync != NULL) (*(*l)->ipfl_sync)(softc, softl->ipf_back[i]); RWLOCK_EXIT(&softc->ipf_poolrw); } #ifndef _KERNEL void -ipf_lookup_dump(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_lookup_dump(ipf_main_softc_t *softc, void *arg) { ipf_lookup_softc_t *softl = softc->ipf_lookup_soft; ipf_lookup_t **l; int i; for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) if (IPLT_POOL == (*l)->ipfl_type) { ipf_pool_dump(softc, softl->ipf_back[i]); break; } for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) if (IPLT_HASH == (*l)->ipfl_type) { ipf_htable_dump(softc, softl->ipf_back[i]); break; } } #endif diff --git a/sys/netpfil/ipfilter/netinet/ip_nat.c b/sys/netpfil/ipfilter/netinet/ip_nat.c index e990658551b3..471d6afb15e0 100644 --- a/sys/netpfil/ipfilter/netinet/ip_nat.c +++ b/sys/netpfil/ipfilter/netinet/ip_nat.c @@ -1,8582 +1,8392 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if defined(_KERNEL) && \ (defined(__NetBSD_Version) && (__NetBSD_Version >= 399002000)) # include #endif #if !defined(_KERNEL) # include # include # include # define KERNEL # ifdef _OpenBSD__ struct file; # endif # include # undef KERNEL #endif #if defined(_KERNEL) && defined(__FreeBSD__) # include # include #else # include #endif # include # include #include #if defined(_KERNEL) # include # if !defined(__SVR4) # include # endif #endif #if defined(__SVR4) # include # include # ifdef KERNEL # include # endif # include # include #endif #if defined(__FreeBSD__) # include #endif #include #if defined(__FreeBSD__) # include #endif #ifdef sun # include #endif #include #include #include #ifdef RFC1825 # include # include extern struct ifnet vpnif; #endif # include #include #include #include #include "netinet/ip_compat.h" #include #include "netinet/ipl.h" #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #include "netinet/ip_lookup.h" #include "netinet/ip_dstlist.h" #include "netinet/ip_sync.h" #if defined(__FreeBSD__) # include #endif #ifdef HAS_SYS_MD5_H # include #else # include "md5.h" #endif /* END OF INCLUDES */ #undef SOCKADDR_IN #define SOCKADDR_IN struct sockaddr_in #if !defined(lint) static const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; static const char rcsid[] = "@(#)$FreeBSD$"; /* static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.102 2007/10/16 10:08:10 darrenr Exp $"; */ #endif #define NATFSUM(n,v,f) ((v) == 4 ? (n)->f.in4.s_addr : (n)->f.i6[0] + \ (n)->f.i6[1] + (n)->f.i6[2] + (n)->f.i6[3]) #define NBUMP(x) softn->(x)++ #define NBUMPD(x, y) do { \ softn->x.y++; \ DT(y); \ } while (0) #define NBUMPSIDE(y,x) softn->ipf_nat_stats.ns_side[y].x++ #define NBUMPSIDED(y,x) do { softn->ipf_nat_stats.ns_side[y].x++; \ DT(x); } while (0) #define NBUMPSIDEX(y,x,z) \ do { softn->ipf_nat_stats.ns_side[y].x++; \ DT(z); } while (0) #define NBUMPSIDEDF(y,x)do { softn->ipf_nat_stats.ns_side[y].x++; \ DT1(x, fr_info_t *, fin); } while (0) static ipftuneable_t ipf_nat_tuneables[] = { /* nat */ { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_lock) }, "nat_lock", 0, 1, stsizeof(ipf_nat_softc_t, ipf_nat_lock), IPFT_RDONLY, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_sz) }, "nat_table_size", 1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_table_sz), 0, NULL, ipf_nat_rehash }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_max) }, "nat_table_max", 1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_table_max), 0, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maprules_sz) }, "nat_rules_size", 1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_maprules_sz), 0, NULL, ipf_nat_rehash_rules }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_rdrrules_sz) }, "rdr_rules_size", 1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_rdrrules_sz), 0, NULL, ipf_nat_rehash_rules }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_hostmap_sz) }, "hostmap_size", 1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_hostmap_sz), 0, NULL, ipf_nat_hostmap_rehash }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maxbucket) }, "nat_maxbucket",1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_maxbucket), 0, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_logging) }, "nat_logging", 0, 1, stsizeof(ipf_nat_softc_t, ipf_nat_logging), 0, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_doflush) }, "nat_doflush", 0, 1, stsizeof(ipf_nat_softc_t, ipf_nat_doflush), 0, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_low) }, "nat_table_wm_low", 1, 99, stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_low), 0, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_high) }, "nat_table_wm_high", 2, 100, stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_high), 0, NULL, NULL }, { { 0 }, NULL, 0, 0, 0, 0, NULL, NULL } }; /* ======================================================================== */ /* How the NAT is organised and works. */ /* */ /* Inside (interface y) NAT Outside (interface x) */ /* -------------------- -+- ------------------------------------- */ /* Packet going | out, processsed by ipf_nat_checkout() for x */ /* ------------> | ------------> */ /* src=10.1.1.1 | src=192.1.1.1 */ /* | */ /* | in, processed by ipf_nat_checkin() for x */ /* <------------ | <------------ */ /* dst=10.1.1.1 | dst=192.1.1.1 */ /* -------------------- -+- ------------------------------------- */ /* ipf_nat_checkout() - changes ip_src and if required, sport */ /* - creates a new mapping, if required. */ /* ipf_nat_checkin() - changes ip_dst and if required, dport */ /* */ /* In the NAT table, internal source is recorded as "in" and externally */ /* seen as "out". */ /* ======================================================================== */ #if SOLARIS && !defined(INSTANCES) extern int pfil_delayed_copy; #endif static int ipf_nat_flush_entry(ipf_main_softc_t *, void *); static int ipf_nat_getent(ipf_main_softc_t *, caddr_t, int); static int ipf_nat_getsz(ipf_main_softc_t *, caddr_t, int); static int ipf_nat_putent(ipf_main_softc_t *, caddr_t, int); static void ipf_nat_addmap(ipf_nat_softc_t *, ipnat_t *); static void ipf_nat_addrdr(ipf_nat_softc_t *, ipnat_t *); static int ipf_nat_builddivertmp(ipf_nat_softc_t *, ipnat_t *); static int ipf_nat_clearlist(ipf_main_softc_t *, ipf_nat_softc_t *); static int ipf_nat_cmp_rules(ipnat_t *, ipnat_t *); static int ipf_nat_decap(fr_info_t *, nat_t *); static void ipf_nat_delrule(ipf_main_softc_t *, ipf_nat_softc_t *, ipnat_t *, int); static int ipf_nat_extraflush(ipf_main_softc_t *, ipf_nat_softc_t *, int); static int ipf_nat_finalise(fr_info_t *, nat_t *); static int ipf_nat_flushtable(ipf_main_softc_t *, ipf_nat_softc_t *); static int ipf_nat_getnext(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, ipfobj_t *); static int ipf_nat_gettable(ipf_main_softc_t *, ipf_nat_softc_t *, char *); static hostmap_t *ipf_nat_hostmap(ipf_nat_softc_t *, ipnat_t *, struct in_addr, struct in_addr, struct in_addr, u_32_t); static int ipf_nat_icmpquerytype(int); static int ipf_nat_iterator(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, ipfobj_t *); static int ipf_nat_match(fr_info_t *, ipnat_t *); static int ipf_nat_matcharray(nat_t *, int *, u_long); static int ipf_nat_matchflush(ipf_main_softc_t *, ipf_nat_softc_t *, caddr_t); static void ipf_nat_mssclamp(tcphdr_t *, u_32_t, fr_info_t *, u_short *); static int ipf_nat_newmap(fr_info_t *, nat_t *, natinfo_t *); static int ipf_nat_newdivert(fr_info_t *, nat_t *, natinfo_t *); static int ipf_nat_newrdr(fr_info_t *, nat_t *, natinfo_t *); static int ipf_nat_newrewrite(fr_info_t *, nat_t *, natinfo_t *); static int ipf_nat_nextaddr(fr_info_t *, nat_addr_t *, u_32_t *, u_32_t *); static int ipf_nat_nextaddrinit(ipf_main_softc_t *, char *, nat_addr_t *, int, void *); static int ipf_nat_resolverule(ipf_main_softc_t *, ipnat_t *); static int ipf_nat_ruleaddrinit(ipf_main_softc_t *, ipf_nat_softc_t *, ipnat_t *); static void ipf_nat_rule_fini(ipf_main_softc_t *, ipnat_t *); static int ipf_nat_rule_init(ipf_main_softc_t *, ipf_nat_softc_t *, ipnat_t *); static int ipf_nat_siocaddnat(ipf_main_softc_t *, ipf_nat_softc_t *, ipnat_t *, int); static void ipf_nat_siocdelnat(ipf_main_softc_t *, ipf_nat_softc_t *, ipnat_t *, int); static void ipf_nat_tabmove(ipf_nat_softc_t *, nat_t *); /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_main_load */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ /* The only global NAT structure that needs to be initialised is the filter */ /* rule that is used with blocking packets. */ /* ------------------------------------------------------------------------ */ int -ipf_nat_main_load() +ipf_nat_main_load(void) { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_main_unload */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int -ipf_nat_main_unload() +ipf_nat_main_unload(void) { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_soft_create */ /* Returns: void * - NULL = failure, else pointer to NAT context */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Allocate the initial soft context structure for NAT and populate it with */ /* some default values. Creating the tables is left until we call _init so */ /* that sizes can be changed before we get under way. */ /* ------------------------------------------------------------------------ */ void * -ipf_nat_soft_create(softc) - ipf_main_softc_t *softc; +ipf_nat_soft_create(ipf_main_softc_t *softc) { ipf_nat_softc_t *softn; KMALLOC(softn, ipf_nat_softc_t *); if (softn == NULL) return NULL; bzero((char *)softn, sizeof(*softn)); softn->ipf_nat_tune = ipf_tune_array_copy(softn, sizeof(ipf_nat_tuneables), ipf_nat_tuneables); if (softn->ipf_nat_tune == NULL) { ipf_nat_soft_destroy(softc, softn); return NULL; } if (ipf_tune_array_link(softc, softn->ipf_nat_tune) == -1) { ipf_nat_soft_destroy(softc, softn); return NULL; } softn->ipf_nat_list_tail = &softn->ipf_nat_list; if (softc->ipf_large_nat) { softn->ipf_nat_table_max = NAT_TABLE_MAX_LARGE; softn->ipf_nat_table_sz = NAT_TABLE_SZ_LARGE; softn->ipf_nat_maprules_sz = NAT_SIZE_LARGE; softn->ipf_nat_rdrrules_sz = RDR_SIZE_LARGE; softn->ipf_nat_hostmap_sz = HOSTMAP_SIZE_LARGE; } else { softn->ipf_nat_table_max = NAT_TABLE_MAX_NORMAL; softn->ipf_nat_table_sz = NAT_TABLE_SZ_NORMAL; softn->ipf_nat_maprules_sz = NAT_SIZE_NORMAL; softn->ipf_nat_rdrrules_sz = RDR_SIZE_NORMAL; softn->ipf_nat_hostmap_sz = HOSTMAP_SIZE_NORMAL; } softn->ipf_nat_doflush = 0; #ifdef IPFILTER_LOG softn->ipf_nat_logging = 1; #else softn->ipf_nat_logging = 0; #endif softn->ipf_nat_defage = DEF_NAT_AGE; softn->ipf_nat_defipage = IPF_TTLVAL(60); softn->ipf_nat_deficmpage = IPF_TTLVAL(3); softn->ipf_nat_table_wm_high = 99; softn->ipf_nat_table_wm_low = 90; return softn; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* ------------------------------------------------------------------------ */ void -ipf_nat_soft_destroy(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_nat_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_nat_softc_t *softn = arg; if (softn->ipf_nat_tune != NULL) { ipf_tune_array_unlink(softc, softn->ipf_nat_tune); KFREES(softn->ipf_nat_tune, sizeof(ipf_nat_tuneables)); softn->ipf_nat_tune = NULL; } KFREE(softn); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_init */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise all of the NAT locks, tables and other structures. */ /* ------------------------------------------------------------------------ */ int -ipf_nat_soft_init(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_nat_soft_init(ipf_main_softc_t *softc, void *arg) { ipf_nat_softc_t *softn = arg; ipftq_t *tq; int i; KMALLOCS(softn->ipf_nat_table[0], nat_t **, \ sizeof(nat_t *) * softn->ipf_nat_table_sz); if (softn->ipf_nat_table[0] != NULL) { bzero((char *)softn->ipf_nat_table[0], softn->ipf_nat_table_sz * sizeof(nat_t *)); } else { return -1; } KMALLOCS(softn->ipf_nat_table[1], nat_t **, \ sizeof(nat_t *) * softn->ipf_nat_table_sz); if (softn->ipf_nat_table[1] != NULL) { bzero((char *)softn->ipf_nat_table[1], softn->ipf_nat_table_sz * sizeof(nat_t *)); } else { return -2; } KMALLOCS(softn->ipf_nat_map_rules, ipnat_t **, \ sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz); if (softn->ipf_nat_map_rules != NULL) { bzero((char *)softn->ipf_nat_map_rules, softn->ipf_nat_maprules_sz * sizeof(ipnat_t *)); } else { return -3; } KMALLOCS(softn->ipf_nat_rdr_rules, ipnat_t **, \ sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz); if (softn->ipf_nat_rdr_rules != NULL) { bzero((char *)softn->ipf_nat_rdr_rules, softn->ipf_nat_rdrrules_sz * sizeof(ipnat_t *)); } else { return -4; } KMALLOCS(softn->ipf_hm_maptable, hostmap_t **, \ sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); if (softn->ipf_hm_maptable != NULL) { bzero((char *)softn->ipf_hm_maptable, sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); } else { return -5; } softn->ipf_hm_maplist = NULL; KMALLOCS(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, u_int *, softn->ipf_nat_table_sz * sizeof(u_int)); if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen == NULL) { return -6; } bzero((char *)softn->ipf_nat_stats.ns_side[0].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); KMALLOCS(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, u_int *, softn->ipf_nat_table_sz * sizeof(u_int)); if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen == NULL) { return -7; } bzero((char *)softn->ipf_nat_stats.ns_side[1].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); if (softn->ipf_nat_maxbucket == 0) { for (i = softn->ipf_nat_table_sz; i > 0; i >>= 1) softn->ipf_nat_maxbucket++; softn->ipf_nat_maxbucket *= 2; } ipf_sttab_init(softc, softn->ipf_nat_tcptq); /* * Increase this because we may have "keep state" following this too * and packet storms can occur if this is removed too quickly. */ softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; softn->ipf_nat_tcptq[IPF_TCP_NSTATES - 1].ifq_next = &softn->ipf_nat_udptq; IPFTQ_INIT(&softn->ipf_nat_udptq, softn->ipf_nat_defage, "nat ipftq udp tab"); softn->ipf_nat_udptq.ifq_next = &softn->ipf_nat_udpacktq; IPFTQ_INIT(&softn->ipf_nat_udpacktq, softn->ipf_nat_defage, "nat ipftq udpack tab"); softn->ipf_nat_udpacktq.ifq_next = &softn->ipf_nat_icmptq; IPFTQ_INIT(&softn->ipf_nat_icmptq, softn->ipf_nat_deficmpage, "nat icmp ipftq tab"); softn->ipf_nat_icmptq.ifq_next = &softn->ipf_nat_icmpacktq; IPFTQ_INIT(&softn->ipf_nat_icmpacktq, softn->ipf_nat_defage, "nat icmpack ipftq tab"); softn->ipf_nat_icmpacktq.ifq_next = &softn->ipf_nat_iptq; IPFTQ_INIT(&softn->ipf_nat_iptq, softn->ipf_nat_defipage, "nat ip ipftq tab"); softn->ipf_nat_iptq.ifq_next = &softn->ipf_nat_pending; IPFTQ_INIT(&softn->ipf_nat_pending, 1, "nat pending ipftq tab"); softn->ipf_nat_pending.ifq_next = NULL; for (i = 0, tq = softn->ipf_nat_tcptq; i < IPF_TCP_NSTATES; i++, tq++) { if (tq->ifq_ttl < softn->ipf_nat_deficmpage) tq->ifq_ttl = softn->ipf_nat_deficmpage; else if (tq->ifq_ttl > softn->ipf_nat_defage && softc->ipf_large_nat) tq->ifq_ttl = softn->ipf_nat_defage; } /* * Increase this because we may have "keep state" following * this too and packet storms can occur if this is removed * too quickly. */ softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; MUTEX_INIT(&softn->ipf_nat_new, "ipf nat new mutex"); MUTEX_INIT(&softn->ipf_nat_io, "ipf nat io mutex"); softn->ipf_nat_inited = 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_soft_fini */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Free all memory used by NAT structures allocated at runtime. */ /* ------------------------------------------------------------------------ */ int -ipf_nat_soft_fini(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_nat_soft_fini(ipf_main_softc_t *softc, void *arg) { ipf_nat_softc_t *softn = arg; ipftq_t *ifq, *ifqnext; (void) ipf_nat_clearlist(softc, softn); (void) ipf_nat_flushtable(softc, softn); /* * Proxy timeout queues are not cleaned here because although they * exist on the NAT list, ipf_proxy_unload is called after unload * and the proxies actually are responsible for them being created. * Should the proxy timeouts have their own list? There's no real * justification as this is the only complication. */ for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; if (ipf_deletetimeoutqueue(ifq) == 0) ipf_freetimeoutqueue(softc, ifq); } if (softn->ipf_nat_table[0] != NULL) { KFREES(softn->ipf_nat_table[0], sizeof(nat_t *) * softn->ipf_nat_table_sz); softn->ipf_nat_table[0] = NULL; } if (softn->ipf_nat_table[1] != NULL) { KFREES(softn->ipf_nat_table[1], sizeof(nat_t *) * softn->ipf_nat_table_sz); softn->ipf_nat_table[1] = NULL; } if (softn->ipf_nat_map_rules != NULL) { KFREES(softn->ipf_nat_map_rules, sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz); softn->ipf_nat_map_rules = NULL; } if (softn->ipf_nat_rdr_rules != NULL) { KFREES(softn->ipf_nat_rdr_rules, sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz); softn->ipf_nat_rdr_rules = NULL; } if (softn->ipf_hm_maptable != NULL) { KFREES(softn->ipf_hm_maptable, sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); softn->ipf_hm_maptable = NULL; } if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, sizeof(u_int) * softn->ipf_nat_table_sz); softn->ipf_nat_stats.ns_side[0].ns_bucketlen = NULL; } if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, sizeof(u_int) * softn->ipf_nat_table_sz); softn->ipf_nat_stats.ns_side[1].ns_bucketlen = NULL; } if (softn->ipf_nat_inited == 1) { softn->ipf_nat_inited = 0; ipf_sttab_destroy(softn->ipf_nat_tcptq); MUTEX_DESTROY(&softn->ipf_nat_new); MUTEX_DESTROY(&softn->ipf_nat_io); MUTEX_DESTROY(&softn->ipf_nat_udptq.ifq_lock); MUTEX_DESTROY(&softn->ipf_nat_udpacktq.ifq_lock); MUTEX_DESTROY(&softn->ipf_nat_icmptq.ifq_lock); MUTEX_DESTROY(&softn->ipf_nat_icmpacktq.ifq_lock); MUTEX_DESTROY(&softn->ipf_nat_iptq.ifq_lock); MUTEX_DESTROY(&softn->ipf_nat_pending.ifq_lock); } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_setlock */ /* Returns: Nil */ /* Parameters: arg(I) - pointer to soft state information */ /* tmp(I) - new lock value */ /* */ /* Set the "lock status" of NAT to the value in tmp. */ /* ------------------------------------------------------------------------ */ void -ipf_nat_setlock(arg, tmp) - void *arg; - int tmp; +ipf_nat_setlock(void *arg, int tmp) { ipf_nat_softc_t *softn = arg; softn->ipf_nat_lock = tmp; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_addrdr */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to add */ /* */ /* Adds a redirect rule to the hash table of redirect rules and the list of */ /* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ /* use by redirect rules. */ /* ------------------------------------------------------------------------ */ static void -ipf_nat_addrdr(softn, n) - ipf_nat_softc_t *softn; - ipnat_t *n; +ipf_nat_addrdr(ipf_nat_softc_t *softn, ipnat_t *n) { ipnat_t **np; u_32_t j; u_int hv; u_int rhv; int k; if (n->in_odstatype == FRI_NORMAL) { k = count4bits(n->in_odstmsk); ipf_inet_mask_add(k, &softn->ipf_nat_rdr_mask); j = (n->in_odstaddr & n->in_odstmsk); rhv = NAT_HASH_FN(j, 0, 0xffffffff); } else { ipf_inet_mask_add(0, &softn->ipf_nat_rdr_mask); j = 0; rhv = 0; } hv = rhv % softn->ipf_nat_rdrrules_sz; np = softn->ipf_nat_rdr_rules + hv; while (*np != NULL) np = &(*np)->in_rnext; n->in_rnext = NULL; n->in_prnext = np; n->in_hv[0] = hv; n->in_use++; *np = n; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_addmap */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to add */ /* */ /* Adds a NAT map rule to the hash table of rules and the list of loaded */ /* NAT rules. Updates the bitmask indicating which netmasks are in use by */ /* redirect rules. */ /* ------------------------------------------------------------------------ */ static void -ipf_nat_addmap(softn, n) - ipf_nat_softc_t *softn; - ipnat_t *n; +ipf_nat_addmap(ipf_nat_softc_t *softn, ipnat_t *n) { ipnat_t **np; u_32_t j; u_int hv; u_int rhv; int k; if (n->in_osrcatype == FRI_NORMAL) { k = count4bits(n->in_osrcmsk); ipf_inet_mask_add(k, &softn->ipf_nat_map_mask); j = (n->in_osrcaddr & n->in_osrcmsk); rhv = NAT_HASH_FN(j, 0, 0xffffffff); } else { ipf_inet_mask_add(0, &softn->ipf_nat_map_mask); j = 0; rhv = 0; } hv = rhv % softn->ipf_nat_maprules_sz; np = softn->ipf_nat_map_rules + hv; while (*np != NULL) np = &(*np)->in_mnext; n->in_mnext = NULL; n->in_pmnext = np; n->in_hv[1] = rhv; n->in_use++; *np = n; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_delrdr */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to delete */ /* */ /* Removes a redirect rule from the hash table of redirect rules. */ /* ------------------------------------------------------------------------ */ void -ipf_nat_delrdr(softn, n) - ipf_nat_softc_t *softn; - ipnat_t *n; +ipf_nat_delrdr(ipf_nat_softc_t *softn, ipnat_t *n) { if (n->in_odstatype == FRI_NORMAL) { int k = count4bits(n->in_odstmsk); ipf_inet_mask_del(k, &softn->ipf_nat_rdr_mask); } else { ipf_inet_mask_del(0, &softn->ipf_nat_rdr_mask); } if (n->in_rnext) n->in_rnext->in_prnext = n->in_prnext; *n->in_prnext = n->in_rnext; n->in_use--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_delmap */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to delete */ /* */ /* Removes a NAT map rule from the hash table of NAT map rules. */ /* ------------------------------------------------------------------------ */ void -ipf_nat_delmap(softn, n) - ipf_nat_softc_t *softn; - ipnat_t *n; +ipf_nat_delmap(ipf_nat_softc_t *softn, ipnat_t *n) { if (n->in_osrcatype == FRI_NORMAL) { int k = count4bits(n->in_osrcmsk); ipf_inet_mask_del(k, &softn->ipf_nat_map_mask); } else { ipf_inet_mask_del(0, &softn->ipf_nat_map_mask); } if (n->in_mnext != NULL) n->in_mnext->in_pmnext = n->in_pmnext; *n->in_pmnext = n->in_mnext; n->in_use--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_hostmap */ /* Returns: struct hostmap* - NULL if no hostmap could be created, */ /* else a pointer to the hostmapping to use */ /* Parameters: np(I) - pointer to NAT rule */ /* real(I) - real IP address */ /* map(I) - mapped IP address */ /* port(I) - destination port number */ /* Write Locks: ipf_nat */ /* */ /* Check if an ip address has already been allocated for a given mapping */ /* that is not doing port based translation. If is not yet allocated, then */ /* create a new entry if a non-NULL NAT rule pointer has been supplied. */ /* ------------------------------------------------------------------------ */ static struct hostmap * -ipf_nat_hostmap(softn, np, src, dst, map, port) - ipf_nat_softc_t *softn; - ipnat_t *np; - struct in_addr src; - struct in_addr dst; - struct in_addr map; - u_32_t port; +ipf_nat_hostmap(ipf_nat_softc_t *softn, ipnat_t *np, struct in_addr src, + struct in_addr dst, struct in_addr map, u_32_t port) { hostmap_t *hm; u_int hv, rhv; hv = (src.s_addr ^ dst.s_addr); hv += src.s_addr; hv += dst.s_addr; rhv = hv; hv %= softn->ipf_nat_hostmap_sz; for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_hnext) if ((hm->hm_osrcip.s_addr == src.s_addr) && (hm->hm_odstip.s_addr == dst.s_addr) && ((np == NULL) || (np == hm->hm_ipnat)) && ((port == 0) || (port == hm->hm_port))) { softn->ipf_nat_stats.ns_hm_addref++; hm->hm_ref++; return hm; } if (np == NULL) { softn->ipf_nat_stats.ns_hm_nullnp++; return NULL; } KMALLOC(hm, hostmap_t *); if (hm) { hm->hm_next = softn->ipf_hm_maplist; hm->hm_pnext = &softn->ipf_hm_maplist; if (softn->ipf_hm_maplist != NULL) softn->ipf_hm_maplist->hm_pnext = &hm->hm_next; softn->ipf_hm_maplist = hm; hm->hm_hnext = softn->ipf_hm_maptable[hv]; hm->hm_phnext = softn->ipf_hm_maptable + hv; if (softn->ipf_hm_maptable[hv] != NULL) softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; softn->ipf_hm_maptable[hv] = hm; hm->hm_ipnat = np; np->in_use++; hm->hm_osrcip = src; hm->hm_odstip = dst; hm->hm_nsrcip = map; hm->hm_ndstip.s_addr = 0; hm->hm_ref = 1; hm->hm_port = port; hm->hm_hv = rhv; hm->hm_v = 4; softn->ipf_nat_stats.ns_hm_new++; } else { softn->ipf_nat_stats.ns_hm_newfail++; } return hm; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_hostmapdel */ /* Returns: Nil */ /* Parameters: hmp(I) - pointer to hostmap structure pointer */ /* Write Locks: ipf_nat */ /* */ /* Decrement the references to this hostmap structure by one. If this */ /* reaches zero then remove it and free it. */ /* ------------------------------------------------------------------------ */ void -ipf_nat_hostmapdel(softc, hmp) - ipf_main_softc_t *softc; - struct hostmap **hmp; +ipf_nat_hostmapdel(ipf_main_softc_t *softc, struct hostmap **hmp) { struct hostmap *hm; hm = *hmp; *hmp = NULL; hm->hm_ref--; if (hm->hm_ref == 0) { ipf_nat_rule_deref(softc, &hm->hm_ipnat); if (hm->hm_hnext) hm->hm_hnext->hm_phnext = hm->hm_phnext; *hm->hm_phnext = hm->hm_hnext; if (hm->hm_next) hm->hm_next->hm_pnext = hm->hm_pnext; *hm->hm_pnext = hm->hm_next; KFREE(hm); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_fix_outcksum */ /* Returns: Nil */ /* Parameters: cksum(I) - ipf_cksum_t, value of fin_cksum */ /* sp(I) - location of 16bit checksum to update */ /* n(I) - amount to adjust checksum by */ /* partial(I) - partial checksum */ /* */ /* Adjusts the 16bit checksum by "n" for packets going out. */ /* ------------------------------------------------------------------------ */ void -ipf_fix_outcksum(cksum, sp, n, partial) - int cksum; - u_short *sp; - u_32_t n, partial; +ipf_fix_outcksum(int cksum, u_short *sp, u_32_t n, u_32_t partial) { u_short sumshort; u_32_t sum1; if (n == 0) return; if (cksum == 4) { *sp = 0; return; } if (cksum == 2) { sum1 = partial; sum1 = (sum1 & 0xffff) + (sum1 >> 16); *sp = htons(sum1); return; } sum1 = (~ntohs(*sp)) & 0xffff; sum1 += (n); sum1 = (sum1 >> 16) + (sum1 & 0xffff); /* Again */ sum1 = (sum1 >> 16) + (sum1 & 0xffff); sumshort = ~(u_short)sum1; *(sp) = htons(sumshort); } /* ------------------------------------------------------------------------ */ /* Function: ipf_fix_incksum */ /* Returns: Nil */ /* Parameters: cksum(I) - ipf_cksum_t, value of fin_cksum */ /* sp(I) - location of 16bit checksum to update */ /* n(I) - amount to adjust checksum by */ /* partial(I) - partial checksum */ /* */ /* Adjusts the 16bit checksum by "n" for packets going in. */ /* ------------------------------------------------------------------------ */ void -ipf_fix_incksum(cksum, sp, n, partial) - int cksum; - u_short *sp; - u_32_t n, partial; +ipf_fix_incksum(int cksum, u_short *sp, u_32_t n, u_32_t partial) { u_short sumshort; u_32_t sum1; if (n == 0) return; if (cksum == 4) { *sp = 0; return; } if (cksum == 2) { sum1 = partial; sum1 = (sum1 & 0xffff) + (sum1 >> 16); *sp = htons(sum1); return; } sum1 = (~ntohs(*sp)) & 0xffff; sum1 += ~(n) & 0xffff; sum1 = (sum1 >> 16) + (sum1 & 0xffff); /* Again */ sum1 = (sum1 >> 16) + (sum1 & 0xffff); sumshort = ~(u_short)sum1; *(sp) = htons(sumshort); } /* ------------------------------------------------------------------------ */ /* Function: ipf_fix_datacksum */ /* Returns: Nil */ /* Parameters: sp(I) - location of 16bit checksum to update */ /* n(I) - amount to adjust checksum by */ /* */ /* Fix_datacksum is used *only* for the adjustments of checksums in the */ /* data section of an IP packet. */ /* */ /* The only situation in which you need to do this is when NAT'ing an */ /* ICMP error message. Such a message, contains in its body the IP header */ /* of the original IP packet, that causes the error. */ /* */ /* You can't use fix_incksum or fix_outcksum in that case, because for the */ /* kernel the data section of the ICMP error is just data, and no special */ /* processing like hardware cksum or ntohs processing have been done by the */ /* kernel on the data section. */ /* ------------------------------------------------------------------------ */ void -ipf_fix_datacksum(sp, n) - u_short *sp; - u_32_t n; +ipf_fix_datacksum(u_short *sp, u_32_t n) { u_short sumshort; u_32_t sum1; if (n == 0) return; sum1 = (~ntohs(*sp)) & 0xffff; sum1 += (n); sum1 = (sum1 >> 16) + (sum1 & 0xffff); /* Again */ sum1 = (sum1 >> 16) + (sum1 & 0xffff); sumshort = ~(u_short)sum1; *(sp) = htons(sumshort); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_ioctl */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to ioctl data */ /* cmd(I) - ioctl command integer */ /* mode(I) - file mode bits used with open */ /* uid(I) - uid of calling process */ /* ctx(I) - pointer used as key for finding context */ /* */ /* Processes an ioctl call made to operate on the IP Filter NAT device. */ /* ------------------------------------------------------------------------ */ int -ipf_nat_ioctl(softc, data, cmd, mode, uid, ctx) - ipf_main_softc_t *softc; - ioctlcmd_t cmd; - caddr_t data; - int mode, uid; - void *ctx; +ipf_nat_ioctl(ipf_main_softc_t *softc, caddr_t data, ioctlcmd_t cmd, + int mode, int uid, void *ctx) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; int error = 0, ret, arg, getlock; ipnat_t *nat, *nt, *n; ipnat_t natd; SPL_INT(s); #if !SOLARIS && defined(_KERNEL) # if NETBSD_GE_REV(399002000) if ((mode & FWRITE) && kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_FIREWALL, KAUTH_REQ_NETWORK_FIREWALL_FW, NULL, NULL, NULL)) # else # if defined(__FreeBSD__) if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE)) # else if ((securelevel >= 3) && (mode & FWRITE)) # endif # endif { IPFERROR(60001); return EPERM; } #endif getlock = (mode & NAT_LOCKHELD) ? 0 : 1; n = NULL; nt = NULL; nat = NULL; if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT) || (cmd == (ioctlcmd_t)SIOCPURGENAT)) { if (mode & NAT_SYSSPACE) { bcopy(data, (char *)&natd, sizeof(natd)); nat = &natd; error = 0; } else { bzero(&natd, sizeof(natd)); error = ipf_inobj(softc, data, NULL, &natd, IPFOBJ_IPNAT); if (error != 0) goto done; if (natd.in_size < sizeof(ipnat_t)) { error = EINVAL; goto done; } KMALLOCS(nt, ipnat_t *, natd.in_size); if (nt == NULL) { IPFERROR(60070); error = ENOMEM; goto done; } bzero(nt, natd.in_size); error = ipf_inobjsz(softc, data, nt, IPFOBJ_IPNAT, natd.in_size); if (error) goto done; nat = nt; } /* * For add/delete, look to see if the NAT entry is * already present */ nat->in_flags &= IPN_USERFLAGS; if ((nat->in_redir & NAT_MAPBLK) == 0) { if (nat->in_osrcatype == FRI_NORMAL || nat->in_osrcatype == FRI_NONE) nat->in_osrcaddr &= nat->in_osrcmsk; if (nat->in_odstatype == FRI_NORMAL || nat->in_odstatype == FRI_NONE) nat->in_odstaddr &= nat->in_odstmsk; if ((nat->in_flags & (IPN_SPLIT|IPN_SIPRANGE)) == 0) { if (nat->in_nsrcatype == FRI_NORMAL) nat->in_nsrcaddr &= nat->in_nsrcmsk; if (nat->in_ndstatype == FRI_NORMAL) nat->in_ndstaddr &= nat->in_ndstmsk; } } error = ipf_nat_rule_init(softc, softn, nat); if (error != 0) goto done; MUTEX_ENTER(&softn->ipf_nat_io); for (n = softn->ipf_nat_list; n != NULL; n = n->in_next) if (ipf_nat_cmp_rules(nat, n) == 0) break; } switch (cmd) { #ifdef IPFILTER_LOG case SIOCIPFFB : { int tmp; if (!(mode & FWRITE)) { IPFERROR(60002); error = EPERM; } else { tmp = ipf_log_clear(softc, IPL_LOGNAT); error = BCOPYOUT(&tmp, data, sizeof(tmp)); if (error != 0) { IPFERROR(60057); error = EFAULT; } } break; } case SIOCSETLG : if (!(mode & FWRITE)) { IPFERROR(60003); error = EPERM; } else { error = BCOPYIN(data, &softn->ipf_nat_logging, sizeof(softn->ipf_nat_logging)); if (error != 0) error = EFAULT; } break; case SIOCGETLG : error = BCOPYOUT(&softn->ipf_nat_logging, data, sizeof(softn->ipf_nat_logging)); if (error != 0) { IPFERROR(60004); error = EFAULT; } break; case FIONREAD : arg = ipf_log_bytesused(softc, IPL_LOGNAT); error = BCOPYOUT(&arg, data, sizeof(arg)); if (error != 0) { IPFERROR(60005); error = EFAULT; } break; #endif case SIOCADNAT : if (!(mode & FWRITE)) { IPFERROR(60006); error = EPERM; } else if (n != NULL) { natd.in_flineno = n->in_flineno; (void) ipf_outobj(softc, data, &natd, IPFOBJ_IPNAT); IPFERROR(60007); error = EEXIST; } else if (nt == NULL) { IPFERROR(60008); error = ENOMEM; } if (error != 0) { MUTEX_EXIT(&softn->ipf_nat_io); break; } if (nat != nt) bcopy((char *)nat, (char *)nt, sizeof(*n)); error = ipf_nat_siocaddnat(softc, softn, nt, getlock); MUTEX_EXIT(&softn->ipf_nat_io); if (error == 0) { nat = NULL; nt = NULL; } break; case SIOCRMNAT : case SIOCPURGENAT : if (!(mode & FWRITE)) { IPFERROR(60009); error = EPERM; n = NULL; } else if (n == NULL) { IPFERROR(60010); error = ESRCH; } if (error != 0) { MUTEX_EXIT(&softn->ipf_nat_io); break; } if (cmd == (ioctlcmd_t)SIOCPURGENAT) { error = ipf_outobjsz(softc, data, n, IPFOBJ_IPNAT, n->in_size); if (error) { MUTEX_EXIT(&softn->ipf_nat_io); goto done; } n->in_flags |= IPN_PURGE; } ipf_nat_siocdelnat(softc, softn, n, getlock); MUTEX_EXIT(&softn->ipf_nat_io); n = NULL; break; case SIOCGNATS : { natstat_t *nsp = &softn->ipf_nat_stats; nsp->ns_side[0].ns_table = softn->ipf_nat_table[0]; nsp->ns_side[1].ns_table = softn->ipf_nat_table[1]; nsp->ns_list = softn->ipf_nat_list; nsp->ns_maptable = softn->ipf_hm_maptable; nsp->ns_maplist = softn->ipf_hm_maplist; nsp->ns_nattab_sz = softn->ipf_nat_table_sz; nsp->ns_nattab_max = softn->ipf_nat_table_max; nsp->ns_rultab_sz = softn->ipf_nat_maprules_sz; nsp->ns_rdrtab_sz = softn->ipf_nat_rdrrules_sz; nsp->ns_hostmap_sz = softn->ipf_nat_hostmap_sz; nsp->ns_instances = softn->ipf_nat_instances; nsp->ns_ticks = softc->ipf_ticks; #ifdef IPFILTER_LOGGING nsp->ns_log_ok = ipf_log_logok(softc, IPF_LOGNAT); nsp->ns_log_fail = ipf_log_failures(softc, IPF_LOGNAT); #else nsp->ns_log_ok = 0; nsp->ns_log_fail = 0; #endif error = ipf_outobj(softc, data, nsp, IPFOBJ_NATSTAT); break; } case SIOCGNATL : { natlookup_t nl; error = ipf_inobj(softc, data, NULL, &nl, IPFOBJ_NATLOOKUP); if (error == 0) { void *ptr; if (getlock) { READ_ENTER(&softc->ipf_nat); } switch (nl.nl_v) { case 4 : ptr = ipf_nat_lookupredir(&nl); break; #ifdef USE_INET6 case 6 : ptr = ipf_nat6_lookupredir(&nl); break; #endif default: ptr = NULL; break; } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (ptr != NULL) { error = ipf_outobj(softc, data, &nl, IPFOBJ_NATLOOKUP); } else { IPFERROR(60011); error = ESRCH; } } break; } case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ if (!(mode & FWRITE)) { IPFERROR(60012); error = EPERM; break; } if (getlock) { WRITE_ENTER(&softc->ipf_nat); } error = BCOPYIN(data, &arg, sizeof(arg)); if (error != 0) { IPFERROR(60013); error = EFAULT; } else { if (arg == 0) ret = ipf_nat_flushtable(softc, softn); else if (arg == 1) ret = ipf_nat_clearlist(softc, softn); else ret = ipf_nat_extraflush(softc, softn, arg); ipf_proxy_flush(softc->ipf_proxy_soft, arg); } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (error == 0) { error = BCOPYOUT(&ret, data, sizeof(ret)); } break; case SIOCMATCHFLUSH : if (!(mode & FWRITE)) { IPFERROR(60014); error = EPERM; break; } if (getlock) { WRITE_ENTER(&softc->ipf_nat); } error = ipf_nat_matchflush(softc, softn, data); if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } break; case SIOCPROXY : error = ipf_proxy_ioctl(softc, data, cmd, mode, ctx); break; case SIOCSTLCK : if (!(mode & FWRITE)) { IPFERROR(60015); error = EPERM; } else { error = ipf_lock(data, &softn->ipf_nat_lock); } break; case SIOCSTPUT : if ((mode & FWRITE) != 0) { error = ipf_nat_putent(softc, data, getlock); } else { IPFERROR(60016); error = EACCES; } break; case SIOCSTGSZ : if (softn->ipf_nat_lock) { error = ipf_nat_getsz(softc, data, getlock); } else { IPFERROR(60017); error = EACCES; } break; case SIOCSTGET : if (softn->ipf_nat_lock) { error = ipf_nat_getent(softc, data, getlock); } else { IPFERROR(60018); error = EACCES; } break; case SIOCGENITER : { ipfgeniter_t iter; ipftoken_t *token; ipfobj_t obj; error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); if (error != 0) break; SPL_SCHED(s); token = ipf_token_find(softc, iter.igi_type, uid, ctx); if (token != NULL) { error = ipf_nat_iterator(softc, token, &iter, &obj); WRITE_ENTER(&softc->ipf_tokens); ipf_token_deref(softc, token); RWLOCK_EXIT(&softc->ipf_tokens); } SPL_X(s); break; } case SIOCIPFDELTOK : error = BCOPYIN(data, &arg, sizeof(arg)); if (error == 0) { SPL_SCHED(s); error = ipf_token_del(softc, arg, uid, ctx); SPL_X(s); } else { IPFERROR(60019); error = EFAULT; } break; case SIOCGTQTAB : error = ipf_outobj(softc, data, softn->ipf_nat_tcptq, IPFOBJ_STATETQTAB); break; case SIOCGTABL : error = ipf_nat_gettable(softc, softn, data); break; default : IPFERROR(60020); error = EINVAL; break; } done: if (nat != NULL) ipf_nat_rule_fini(softc, nat); if (nt != NULL) KFREES(nt, nt->in_size); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_siocaddnat */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* n(I) - pointer to new NAT rule */ /* np(I) - pointer to where to insert new NAT rule */ /* getlock(I) - flag indicating if lock on is held */ /* Mutex Locks: ipf_nat_io */ /* */ /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ /* from information passed to the kernel, then add it to the appropriate */ /* NAT rule table(s). */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_siocaddnat(softc, softn, n, getlock) - ipf_main_softc_t *softc; - ipf_nat_softc_t *softn; - ipnat_t *n; - int getlock; +ipf_nat_siocaddnat(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, ipnat_t *n, + int getlock) { int error = 0; if (ipf_nat_resolverule(softc, n) != 0) { IPFERROR(60022); return ENOENT; } if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) { IPFERROR(60023); return EINVAL; } if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { /* * Prerecord whether or not the destination of the divert * is local or not to the interface the packet is going * to be sent out. */ n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], n->in_ifps[1], &n->in_ndstip6); } if (getlock) { WRITE_ENTER(&softc->ipf_nat); } n->in_next = NULL; n->in_pnext = softn->ipf_nat_list_tail; *n->in_pnext = n; softn->ipf_nat_list_tail = &n->in_next; n->in_use++; if (n->in_redir & NAT_REDIRECT) { n->in_flags &= ~IPN_NOTDST; switch (n->in_v[0]) { case 4 : ipf_nat_addrdr(softn, n); break; #ifdef USE_INET6 case 6 : ipf_nat6_addrdr(softn, n); break; #endif default : break; } ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_rdr); } if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { n->in_flags &= ~IPN_NOTSRC; switch (n->in_v[0]) { case 4 : ipf_nat_addmap(softn, n); break; #ifdef USE_INET6 case 6 : ipf_nat6_addmap(softn, n); break; #endif default : break; } ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_map); } if (n->in_age[0] != 0) n->in_tqehead[0] = ipf_addtimeoutqueue(softc, &softn->ipf_nat_utqe, n->in_age[0]); if (n->in_age[1] != 0) n->in_tqehead[1] = ipf_addtimeoutqueue(softc, &softn->ipf_nat_utqe, n->in_age[1]); MUTEX_INIT(&n->in_lock, "ipnat rule lock"); n = NULL; ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); #if SOLARIS && !defined(INSTANCES) pfil_delayed_copy = 0; #endif if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); /* WRITE */ } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_ruleaddrinit */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* n(I) - pointer to NAT rule */ /* */ /* Initialise all of the NAT address structures in a NAT rule. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_ruleaddrinit(softc, softn, n) - ipf_main_softc_t *softc; - ipf_nat_softc_t *softn; - ipnat_t *n; +ipf_nat_ruleaddrinit(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, + ipnat_t *n) { int idx, error; if ((n->in_ndst.na_atype == FRI_LOOKUP) && (n->in_ndst.na_type != IPLT_DSTLIST)) { IPFERROR(60071); return EINVAL; } if ((n->in_nsrc.na_atype == FRI_LOOKUP) && (n->in_nsrc.na_type != IPLT_DSTLIST)) { IPFERROR(60069); return EINVAL; } if (n->in_redir == NAT_BIMAP) { n->in_ndstaddr = n->in_osrcaddr; n->in_ndstmsk = n->in_osrcmsk; n->in_odstaddr = n->in_nsrcaddr; n->in_odstmsk = n->in_nsrcmsk; } if (n->in_redir & NAT_REDIRECT) idx = 1; else idx = 0; /* * Initialise all of the address fields. */ error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 1, n->in_ifps[idx]); if (error != 0) return error; error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 1, n->in_ifps[idx]); if (error != 0) return error; error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1, n->in_ifps[idx]); if (error != 0) return error; error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 1, n->in_ifps[idx]); if (error != 0) return error; if (n->in_redir & NAT_DIVERTUDP) ipf_nat_builddivertmp(softn, n); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_resolvrule */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* n(I) - pointer to NAT rule */ /* */ /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ /* from information passed to the kernel, then add it to the appropriate */ /* NAT rule table(s). */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_resolverule(softc, n) - ipf_main_softc_t *softc; - ipnat_t *n; +ipf_nat_resolverule(ipf_main_softc_t *softc, ipnat_t *n) { char *base; base = n->in_names; n->in_ifps[0] = ipf_resolvenic(softc, base + n->in_ifnames[0], n->in_v[0]); if (n->in_ifnames[1] == -1) { n->in_ifnames[1] = n->in_ifnames[0]; n->in_ifps[1] = n->in_ifps[0]; } else { n->in_ifps[1] = ipf_resolvenic(softc, base + n->in_ifnames[1], n->in_v[1]); } if (n->in_plabel != -1) { if (n->in_redir & NAT_REDIRECT) n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft, n->in_pr[0], base + n->in_plabel); else n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft, n->in_pr[1], base + n->in_plabel); if (n->in_apr == NULL) return -1; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_siocdelnat */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* n(I) - pointer to new NAT rule */ /* getlock(I) - flag indicating if lock on is held */ /* Mutex Locks: ipf_nat_io */ /* */ /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ /* from information passed to the kernel, then add it to the appropriate */ /* NAT rule table(s). */ /* ------------------------------------------------------------------------ */ static void -ipf_nat_siocdelnat(softc, softn, n, getlock) - ipf_main_softc_t *softc; - ipf_nat_softc_t *softn; - ipnat_t *n; - int getlock; +ipf_nat_siocdelnat(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, ipnat_t *n, + int getlock) { if (getlock) { WRITE_ENTER(&softc->ipf_nat); } ipf_nat_delrule(softc, softn, n, 1); if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); /* READ/WRITE */ } } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_getsz */ /* Returns: int - 0 == success, != 0 is the error value. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to natget structure with kernel */ /* pointer get the size of. */ /* getlock(I) - flag indicating whether or not the caller */ /* holds a lock on ipf_nat */ /* */ /* Handle SIOCSTGSZ. */ /* Return the size of the nat list entry to be copied back to user space. */ /* The size of the entry is stored in the ng_sz field and the enture natget */ /* structure is copied back to the user. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_getsz(softc, data, getlock) - ipf_main_softc_t *softc; - caddr_t data; - int getlock; +ipf_nat_getsz(ipf_main_softc_t *softc, caddr_t data, int getlock) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; ap_session_t *aps; nat_t *nat, *n; natget_t ng; int error; error = BCOPYIN(data, &ng, sizeof(ng)); if (error != 0) { IPFERROR(60024); return EFAULT; } if (getlock) { READ_ENTER(&softc->ipf_nat); } nat = ng.ng_ptr; if (!nat) { nat = softn->ipf_nat_instances; ng.ng_sz = 0; /* * Empty list so the size returned is 0. Simple. */ if (nat == NULL) { if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } error = BCOPYOUT(&ng, data, sizeof(ng)); if (error != 0) { IPFERROR(60025); return EFAULT; } return 0; } } else { /* * Make sure the pointer we're copying from exists in the * current list of entries. Security precaution to prevent * copying of random kernel data. */ for (n = softn->ipf_nat_instances; n; n = n->nat_next) if (n == nat) break; if (n == NULL) { if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } IPFERROR(60026); return ESRCH; } } /* * Incluse any space required for proxy data structures. */ ng.ng_sz = sizeof(nat_save_t); aps = nat->nat_aps; if (aps != NULL) { ng.ng_sz += sizeof(ap_session_t) - 4; if (aps->aps_data != 0) ng.ng_sz += aps->aps_psiz; } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } error = BCOPYOUT(&ng, data, sizeof(ng)); if (error != 0) { IPFERROR(60027); return EFAULT; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_getent */ /* Returns: int - 0 == success, != 0 is the error value. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to natget structure with kernel pointer*/ /* to NAT structure to copy out. */ /* getlock(I) - flag indicating whether or not the caller */ /* holds a lock on ipf_nat */ /* */ /* Handle SIOCSTGET. */ /* Copies out NAT entry to user space. Any additional data held for a */ /* proxy is also copied, as to is the NAT rule which was responsible for it */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_getent(softc, data, getlock) - ipf_main_softc_t *softc; - caddr_t data; - int getlock; +ipf_nat_getent(ipf_main_softc_t *softc, caddr_t data, int getlock) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; int error, outsize; ap_session_t *aps; nat_save_t *ipn, ipns; nat_t *n, *nat; error = ipf_inobj(softc, data, NULL, &ipns, IPFOBJ_NATSAVE); if (error != 0) return error; if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) { IPFERROR(60028); return EINVAL; } KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize); if (ipn == NULL) { IPFERROR(60029); return ENOMEM; } if (getlock) { READ_ENTER(&softc->ipf_nat); } ipn->ipn_dsize = ipns.ipn_dsize; nat = ipns.ipn_next; if (nat == NULL) { nat = softn->ipf_nat_instances; if (nat == NULL) { if (softn->ipf_nat_instances == NULL) { IPFERROR(60030); error = ENOENT; } goto finished; } } else { /* * Make sure the pointer we're copying from exists in the * current list of entries. Security precaution to prevent * copying of random kernel data. */ for (n = softn->ipf_nat_instances; n; n = n->nat_next) if (n == nat) break; if (n == NULL) { IPFERROR(60031); error = ESRCH; goto finished; } } ipn->ipn_next = nat->nat_next; /* * Copy the NAT structure. */ bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat)); /* * If we have a pointer to the NAT rule it belongs to, save that too. */ if (nat->nat_ptr != NULL) bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat, ipn->ipn_ipnat.in_size); /* * If we also know the NAT entry has an associated filter rule, * save that too. */ if (nat->nat_fr != NULL) bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr, sizeof(ipn->ipn_fr)); /* * Last but not least, if there is an application proxy session set * up for this NAT entry, then copy that out too, including any * private data saved along side it by the proxy. */ aps = nat->nat_aps; outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data); if (aps != NULL) { char *s; if (outsize < sizeof(*aps)) { IPFERROR(60032); error = ENOBUFS; goto finished; } s = ipn->ipn_data; bcopy((char *)aps, s, sizeof(*aps)); s += sizeof(*aps); outsize -= sizeof(*aps); if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz)) bcopy(aps->aps_data, s, aps->aps_psiz); else { IPFERROR(60033); error = ENOBUFS; } } if (error == 0) { error = ipf_outobjsz(softc, data, ipn, IPFOBJ_NATSAVE, ipns.ipn_dsize); } finished: if (ipn != NULL) { KFREES(ipn, ipns.ipn_dsize); } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_putent */ /* Returns: int - 0 == success, != 0 is the error value. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to natget structure with NAT */ /* structure information to load into the kernel */ /* getlock(I) - flag indicating whether or not a write lock */ /* on is already held. */ /* */ /* Handle SIOCSTPUT. */ /* Loads a NAT table entry from user space, including a NAT rule, proxy and */ /* firewall rule data structures, if pointers to them indicate so. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_putent(softc, data, getlock) - ipf_main_softc_t *softc; - caddr_t data; - int getlock; +ipf_nat_putent(ipf_main_softc_t *softc, caddr_t data, int getlock) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; nat_save_t ipn, *ipnn; ap_session_t *aps; nat_t *n, *nat; frentry_t *fr; fr_info_t fin; ipnat_t *in; int error; error = ipf_inobj(softc, data, NULL, &ipn, IPFOBJ_NATSAVE); if (error != 0) return error; /* * Initialise early because of code at junkput label. */ n = NULL; in = NULL; aps = NULL; nat = NULL; ipnn = NULL; fr = NULL; /* * New entry, copy in the rest of the NAT entry if it's size is more * than just the nat_t structure. */ if (ipn.ipn_dsize > sizeof(ipn)) { if (ipn.ipn_dsize > 81920) { IPFERROR(60034); error = ENOMEM; goto junkput; } KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize); if (ipnn == NULL) { IPFERROR(60035); return ENOMEM; } bzero(ipnn, ipn.ipn_dsize); error = ipf_inobjsz(softc, data, ipnn, IPFOBJ_NATSAVE, ipn.ipn_dsize); if (error != 0) { goto junkput; } } else ipnn = &ipn; KMALLOC(nat, nat_t *); if (nat == NULL) { IPFERROR(60037); error = ENOMEM; goto junkput; } bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat)); switch (nat->nat_v[0]) { case 4: #ifdef USE_INET6 case 6 : #endif break; default : IPFERROR(60061); error = EPROTONOSUPPORT; goto junkput; /*NOTREACHED*/ } /* * Initialize all these so that ipf_nat_delete() doesn't cause a crash. */ bzero((char *)nat, offsetof(struct nat, nat_tqe)); nat->nat_tqe.tqe_pnext = NULL; nat->nat_tqe.tqe_next = NULL; nat->nat_tqe.tqe_ifq = NULL; nat->nat_tqe.tqe_parent = nat; /* * Restore the rule associated with this nat session */ in = ipnn->ipn_nat.nat_ptr; if (in != NULL) { KMALLOCS(in, ipnat_t *, ipnn->ipn_ipnat.in_size); nat->nat_ptr = in; if (in == NULL) { IPFERROR(60038); error = ENOMEM; goto junkput; } bcopy((char *)&ipnn->ipn_ipnat, (char *)in, ipnn->ipn_ipnat.in_size); in->in_use = 1; in->in_flags |= IPN_DELETE; ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); if (ipf_nat_resolverule(softc, in) != 0) { IPFERROR(60039); error = ESRCH; goto junkput; } } /* * Check that the NAT entry doesn't already exist in the kernel. * * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry. To do * this, we check to see if the inbound combination of addresses and * ports is already known. Similar logic is applied for NAT_INBOUND. * */ bzero((char *)&fin, sizeof(fin)); fin.fin_v = nat->nat_v[0]; fin.fin_p = nat->nat_pr[0]; fin.fin_rev = nat->nat_rev; fin.fin_ifp = nat->nat_ifps[0]; fin.fin_data[0] = ntohs(nat->nat_ndport); fin.fin_data[1] = ntohs(nat->nat_nsport); switch (nat->nat_dir) { case NAT_OUTBOUND : case NAT_DIVERTOUT : if (getlock) { READ_ENTER(&softc->ipf_nat); } fin.fin_v = nat->nat_v[1]; if (nat->nat_v[1] == 4) { n = ipf_nat_inlookup(&fin, nat->nat_flags, fin.fin_p, nat->nat_ndstip, nat->nat_nsrcip); #ifdef USE_INET6 } else if (nat->nat_v[1] == 6) { n = ipf_nat6_inlookup(&fin, nat->nat_flags, fin.fin_p, &nat->nat_ndst6.in6, &nat->nat_nsrc6.in6); #endif } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (n != NULL) { IPFERROR(60040); error = EEXIST; goto junkput; } break; case NAT_INBOUND : case NAT_DIVERTIN : if (getlock) { READ_ENTER(&softc->ipf_nat); } if (fin.fin_v == 4) { n = ipf_nat_outlookup(&fin, nat->nat_flags, fin.fin_p, nat->nat_ndstip, nat->nat_nsrcip); #ifdef USE_INET6 } else if (fin.fin_v == 6) { n = ipf_nat6_outlookup(&fin, nat->nat_flags, fin.fin_p, &nat->nat_ndst6.in6, &nat->nat_nsrc6.in6); #endif } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (n != NULL) { IPFERROR(60041); error = EEXIST; goto junkput; } break; default : IPFERROR(60042); error = EINVAL; goto junkput; } /* * Restore ap_session_t structure. Include the private data allocated * if it was there. */ aps = nat->nat_aps; if (aps != NULL) { KMALLOC(aps, ap_session_t *); nat->nat_aps = aps; if (aps == NULL) { IPFERROR(60043); error = ENOMEM; goto junkput; } bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); if (in != NULL) aps->aps_apr = in->in_apr; else aps->aps_apr = NULL; if (aps->aps_psiz != 0) { if (aps->aps_psiz > 81920) { IPFERROR(60044); error = ENOMEM; goto junkput; } KMALLOCS(aps->aps_data, void *, aps->aps_psiz); if (aps->aps_data == NULL) { IPFERROR(60045); error = ENOMEM; goto junkput; } bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, aps->aps_psiz); } else { aps->aps_psiz = 0; aps->aps_data = NULL; } } /* * If there was a filtering rule associated with this entry then * build up a new one. */ fr = nat->nat_fr; if (fr != NULL) { if ((nat->nat_flags & SI_NEWFR) != 0) { KMALLOC(fr, frentry_t *); nat->nat_fr = fr; if (fr == NULL) { IPFERROR(60046); error = ENOMEM; goto junkput; } ipnn->ipn_nat.nat_fr = fr; fr->fr_ref = 1; (void) ipf_outobj(softc, data, ipnn, IPFOBJ_NATSAVE); bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr)); fr->fr_ref = 1; fr->fr_dsize = 0; fr->fr_data = NULL; fr->fr_type = FR_T_NONE; MUTEX_NUKE(&fr->fr_lock); MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock"); } else { if (getlock) { READ_ENTER(&softc->ipf_nat); } for (n = softn->ipf_nat_instances; n; n = n->nat_next) if (n->nat_fr == fr) break; if (n != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (n == NULL) { IPFERROR(60047); error = ESRCH; goto junkput; } } } if (ipnn != &ipn) { KFREES(ipnn, ipn.ipn_dsize); ipnn = NULL; } if (getlock) { WRITE_ENTER(&softc->ipf_nat); } if (fin.fin_v == 4) error = ipf_nat_finalise(&fin, nat); #ifdef USE_INET6 else error = ipf_nat6_finalise(&fin, nat); #endif if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (error == 0) return 0; IPFERROR(60048); error = ENOMEM; junkput: if (fr != NULL) { (void) ipf_derefrule(softc, &fr); } if ((ipnn != NULL) && (ipnn != &ipn)) { KFREES(ipnn, ipn.ipn_dsize); } if (nat != NULL) { if (aps != NULL) { if (aps->aps_data != NULL) { KFREES(aps->aps_data, aps->aps_psiz); } KFREE(aps); } if (in != NULL) { if (in->in_apr) ipf_proxy_deref(in->in_apr); KFREES(in, in->in_size); } KFREE(nat); } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_delete */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* nat(I) - pointer to NAT structure to delete */ /* logtype(I) - type of LOG record to create before deleting */ /* Write Lock: ipf_nat */ /* */ /* Delete a nat entry from the various lists and table. If NAT logging is */ /* enabled then generate a NAT log record for this event. */ /* ------------------------------------------------------------------------ */ void -ipf_nat_delete(softc, nat, logtype) - ipf_main_softc_t *softc; - struct nat *nat; - int logtype; +ipf_nat_delete(ipf_main_softc_t *softc, struct nat *nat, int logtype) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; int madeorphan = 0, bkt, removed = 0; nat_stat_side_t *nss; struct ipnat *ipn; if (logtype != 0 && softn->ipf_nat_logging != 0) ipf_nat_log(softc, softn, nat, logtype); /* * Take it as a general indication that all the pointers are set if * nat_pnext is set. */ if (nat->nat_pnext != NULL) { removed = 1; bkt = nat->nat_hv[0] % softn->ipf_nat_table_sz; nss = &softn->ipf_nat_stats.ns_side[0]; if (nss->ns_bucketlen[bkt] > 0) nss->ns_bucketlen[bkt]--; if (nss->ns_bucketlen[bkt] == 0) { nss->ns_inuse--; } bkt = nat->nat_hv[1] % softn->ipf_nat_table_sz; nss = &softn->ipf_nat_stats.ns_side[1]; if (nss->ns_bucketlen[bkt] > 0) nss->ns_bucketlen[bkt]--; if (nss->ns_bucketlen[bkt] == 0) { nss->ns_inuse--; } *nat->nat_pnext = nat->nat_next; if (nat->nat_next != NULL) { nat->nat_next->nat_pnext = nat->nat_pnext; nat->nat_next = NULL; } nat->nat_pnext = NULL; *nat->nat_phnext[0] = nat->nat_hnext[0]; if (nat->nat_hnext[0] != NULL) { nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; nat->nat_hnext[0] = NULL; } nat->nat_phnext[0] = NULL; *nat->nat_phnext[1] = nat->nat_hnext[1]; if (nat->nat_hnext[1] != NULL) { nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; nat->nat_hnext[1] = NULL; } nat->nat_phnext[1] = NULL; if ((nat->nat_flags & SI_WILDP) != 0) { ATOMIC_DEC32(softn->ipf_nat_stats.ns_wilds); } madeorphan = 1; } if (nat->nat_me != NULL) { *nat->nat_me = NULL; nat->nat_me = NULL; nat->nat_ref--; ASSERT(nat->nat_ref >= 0); } if (nat->nat_tqe.tqe_ifq != NULL) { /* * No call to ipf_freetimeoutqueue() is made here, they are * garbage collected in ipf_nat_expire(). */ (void) ipf_deletequeueentry(&nat->nat_tqe); } if (nat->nat_sync) { ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); nat->nat_sync = NULL; } if (logtype == NL_EXPIRE) softn->ipf_nat_stats.ns_expire++; MUTEX_ENTER(&nat->nat_lock); /* * NL_DESTROY should only be passed in when we've got nat_ref >= 2. * This happens when a nat'd packet is blocked and we want to throw * away the NAT session. */ if (logtype == NL_DESTROY) { if (nat->nat_ref > 2) { nat->nat_ref -= 2; MUTEX_EXIT(&nat->nat_lock); if (removed) softn->ipf_nat_stats.ns_orphans++; return; } } else if (nat->nat_ref > 1) { nat->nat_ref--; MUTEX_EXIT(&nat->nat_lock); if (madeorphan == 1) softn->ipf_nat_stats.ns_orphans++; return; } ASSERT(nat->nat_ref >= 0); MUTEX_EXIT(&nat->nat_lock); nat->nat_ref = 0; if (madeorphan == 0) softn->ipf_nat_stats.ns_orphans--; /* * At this point, nat_ref can be either 0 or -1 */ softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]--; if (nat->nat_fr != NULL) { (void) ipf_derefrule(softc, &nat->nat_fr); } if (nat->nat_hm != NULL) { ipf_nat_hostmapdel(softc, &nat->nat_hm); } /* * If there is an active reference from the nat entry to its parent * rule, decrement the rule's reference count and free it too if no * longer being used. */ ipn = nat->nat_ptr; nat->nat_ptr = NULL; if (ipn != NULL) { ipn->in_space++; ipf_nat_rule_deref(softc, &ipn); } if (nat->nat_aps != NULL) { ipf_proxy_free(softc, nat->nat_aps); nat->nat_aps = NULL; } MUTEX_DESTROY(&nat->nat_lock); softn->ipf_nat_stats.ns_active--; /* * If there's a fragment table entry too for this nat entry, then * dereference that as well. This is after nat_lock is released * because of Tru64. */ ipf_frag_natforget(softc, (void *)nat); KFREE(nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_flushtable */ /* Returns: int - number of NAT rules deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* Write Lock: ipf_nat */ /* */ /* Deletes all currently active NAT sessions. In deleting each NAT entry a */ /* log record should be emitted in ipf_nat_delete() if NAT logging is */ /* enabled. */ /* ------------------------------------------------------------------------ */ /* * nat_flushtable - clear the NAT table of all mapping entries. */ static int -ipf_nat_flushtable(softc, softn) - ipf_main_softc_t *softc; - ipf_nat_softc_t *softn; +ipf_nat_flushtable(ipf_main_softc_t *softc, ipf_nat_softc_t *softn) { nat_t *nat; int j = 0; /* * ALL NAT mappings deleted, so lets just make the deletions * quicker. */ if (softn->ipf_nat_table[0] != NULL) bzero((char *)softn->ipf_nat_table[0], sizeof(softn->ipf_nat_table[0]) * softn->ipf_nat_table_sz); if (softn->ipf_nat_table[1] != NULL) bzero((char *)softn->ipf_nat_table[1], sizeof(softn->ipf_nat_table[1]) * softn->ipf_nat_table_sz); while ((nat = softn->ipf_nat_instances) != NULL) { ipf_nat_delete(softc, nat, NL_FLUSH); j++; } return j; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_clearlist */ /* Returns: int - number of NAT/RDR rules deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* */ /* Delete all rules in the current list of rules. There is nothing elegant */ /* about this cleanup: simply free all entries on the list of rules and */ /* clear out the tables used for hashed NAT rule lookups. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_clearlist(softc, softn) - ipf_main_softc_t *softc; - ipf_nat_softc_t *softn; +ipf_nat_clearlist(ipf_main_softc_t *softc, ipf_nat_softc_t *softn) { ipnat_t *n; int i = 0; if (softn->ipf_nat_map_rules != NULL) { bzero((char *)softn->ipf_nat_map_rules, sizeof(*softn->ipf_nat_map_rules) * softn->ipf_nat_maprules_sz); } if (softn->ipf_nat_rdr_rules != NULL) { bzero((char *)softn->ipf_nat_rdr_rules, sizeof(*softn->ipf_nat_rdr_rules) * softn->ipf_nat_rdrrules_sz); } while ((n = softn->ipf_nat_list) != NULL) { ipf_nat_delrule(softc, softn, n, 0); i++; } #if SOLARIS && !defined(INSTANCES) pfil_delayed_copy = 1; #endif return i; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_delrule */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* np(I) - pointer to NAT rule to delete */ /* purge(I) - 1 == allow purge, 0 == prevent purge */ /* Locks: WRITE(ipf_nat) */ /* */ /* Preventing "purge" from occuring is allowed because when all of the NAT */ /* rules are being removed, allowing the "purge" to walk through the list */ /* of NAT sessions, possibly multiple times, would be a large performance */ /* hit, on the order of O(N^2). */ /* ------------------------------------------------------------------------ */ static void -ipf_nat_delrule(softc, softn, np, purge) - ipf_main_softc_t *softc; - ipf_nat_softc_t *softn; - ipnat_t *np; - int purge; +ipf_nat_delrule(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, ipnat_t *np, + int purge) { if (np->in_pnext != NULL) { *np->in_pnext = np->in_next; if (np->in_next != NULL) np->in_next->in_pnext = np->in_pnext; if (softn->ipf_nat_list_tail == &np->in_next) softn->ipf_nat_list_tail = np->in_pnext; } if ((purge == 1) && ((np->in_flags & IPN_PURGE) != 0)) { nat_t *next; nat_t *nat; for (next = softn->ipf_nat_instances; (nat = next) != NULL;) { next = nat->nat_next; if (nat->nat_ptr == np) ipf_nat_delete(softc, nat, NL_PURGE); } } if ((np->in_flags & IPN_DELETE) == 0) { if (np->in_redir & NAT_REDIRECT) { switch (np->in_v[0]) { case 4 : ipf_nat_delrdr(softn, np); break; #ifdef USE_INET6 case 6 : ipf_nat6_delrdr(softn, np); break; #endif } } if (np->in_redir & (NAT_MAPBLK|NAT_MAP)) { switch (np->in_v[0]) { case 4 : ipf_nat_delmap(softn, np); break; #ifdef USE_INET6 case 6 : ipf_nat6_delmap(softn, np); break; #endif } } } np->in_flags |= IPN_DELETE; ipf_nat_rule_deref(softc, &np); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_newmap */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* */ /* Given an empty NAT structure, populate it with new information about a */ /* new NAT session, as defined by the matching NAT rule. */ /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ /* to the new IP address for the translation. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_newmap(fin, nat, ni) - fr_info_t *fin; - nat_t *nat; - natinfo_t *ni; +ipf_nat_newmap(fr_info_t *fin, nat_t *nat, natinfo_t *ni) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short st_port, dport, sport, port, sp, dp; struct in_addr in, inb; hostmap_t *hm; u_32_t flags; u_32_t st_ip; ipnat_t *np; nat_t *natl; int l; /* * If it's an outbound packet which doesn't match any existing * record, then create a new port */ l = 0; hm = NULL; np = ni->nai_np; st_ip = np->in_snip; st_port = np->in_spnext; flags = nat->nat_flags; if (flags & IPN_ICMPQUERY) { sport = fin->fin_data[1]; dport = 0; } else { sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); } /* * Do a loop until we either run out of entries to try or we find * a NAT mapping that isn't currently being used. This is done * because the change to the source is not (usually) being fixed. */ do { port = 0; in.s_addr = htonl(np->in_snip); if (l == 0) { /* * Check to see if there is an existing NAT * setup for this IP address pair. */ hm = ipf_nat_hostmap(softn, np, fin->fin_src, fin->fin_dst, in, 0); if (hm != NULL) in.s_addr = hm->hm_nsrcip.s_addr; } else if ((l == 1) && (hm != NULL)) { ipf_nat_hostmapdel(softc, &hm); } in.s_addr = ntohl(in.s_addr); nat->nat_hm = hm; if ((np->in_nsrcmsk == 0xffffffff) && (np->in_spnext == 0)) { if (l > 0) { NBUMPSIDEX(1, ns_exhausted, ns_exhausted_1); DT4(ns_exhausted_1, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); return -1; } } if (np->in_redir == NAT_BIMAP && np->in_osrcmsk == np->in_nsrcmsk) { /* * map the address block in a 1:1 fashion */ in.s_addr = np->in_nsrcaddr; in.s_addr |= fin->fin_saddr & ~np->in_osrcmsk; in.s_addr = ntohl(in.s_addr); } else if (np->in_redir & NAT_MAPBLK) { if ((l >= np->in_ppip) || ((l > 0) && !(flags & IPN_TCPUDP))) { NBUMPSIDEX(1, ns_exhausted, ns_exhausted_2); DT4(ns_exhausted_2, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); return -1; } /* * map-block - Calculate destination address. */ in.s_addr = ntohl(fin->fin_saddr); in.s_addr &= ntohl(~np->in_osrcmsk); inb.s_addr = in.s_addr; in.s_addr /= np->in_ippip; in.s_addr &= ntohl(~np->in_nsrcmsk); in.s_addr += ntohl(np->in_nsrcaddr); /* * Calculate destination port. */ if ((flags & IPN_TCPUDP) && (np->in_ppip != 0)) { port = ntohs(sport) + l; port %= np->in_ppip; port += np->in_ppip * (inb.s_addr % np->in_ippip); port += MAPBLK_MINPORT; port = htons(port); } } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) { i6addr_t in6; /* * 0/32 - use the interface's IP address. */ if ((l > 0) || ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, &in6, NULL) == -1) { NBUMPSIDEX(1, ns_new_ifpaddr, ns_new_ifpaddr_1); DT4(ns_new_ifpaddr_1, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); return -1; } in.s_addr = ntohl(in6.in4.s_addr); } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { /* * 0/0 - use the original source address/port. */ if (l > 0) { NBUMPSIDEX(1, ns_exhausted, ns_exhausted_3); DT4(ns_exhausted_3, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); return -1; } in.s_addr = ntohl(fin->fin_saddr); } else if ((np->in_nsrcmsk != 0xffffffff) && (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) np->in_snip++; natl = NULL; if ((flags & IPN_TCPUDP) && ((np->in_redir & NAT_MAPBLK) == 0) && (np->in_flags & IPN_AUTOPORTMAP)) { /* * "ports auto" (without map-block) */ if ((l > 0) && (l % np->in_ppip == 0)) { if ((l > np->in_ppip) && np->in_nsrcmsk != 0xffffffff) np->in_snip++; } if (np->in_ppip != 0) { port = ntohs(sport); port += (l % np->in_ppip); port %= np->in_ppip; port += np->in_ppip * (ntohl(fin->fin_saddr) % np->in_ippip); port += MAPBLK_MINPORT; port = htons(port); } } else if (((np->in_redir & NAT_MAPBLK) == 0) && (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) { /* * Standard port translation. Select next port. */ if (np->in_flags & IPN_SEQUENTIAL) { port = np->in_spnext; } else { port = ipf_random() % (np->in_spmax - np->in_spmin + 1); port += np->in_spmin; } port = htons(port); np->in_spnext++; if (np->in_spnext > np->in_spmax) { np->in_spnext = np->in_spmin; if (np->in_nsrcmsk != 0xffffffff) np->in_snip++; } } if (np->in_flags & IPN_SIPRANGE) { if (np->in_snip > ntohl(np->in_nsrcmsk)) np->in_snip = ntohl(np->in_nsrcaddr); } else { if ((np->in_nsrcmsk != 0xffffffff) && ((np->in_snip + 1) & ntohl(np->in_nsrcmsk)) > ntohl(np->in_nsrcaddr)) np->in_snip = ntohl(np->in_nsrcaddr) + 1; } if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) port = sport; /* * Here we do a lookup of the connection as seen from * the outside. If an IP# pair already exists, try * again. So if you have A->B becomes C->B, you can * also have D->E become C->E but not D->B causing * another C->B. Also take protocol and ports into * account when determining whether a pre-existing * NAT setup will cause an external conflict where * this is appropriate. */ inb.s_addr = htonl(in.s_addr); sp = fin->fin_data[0]; dp = fin->fin_data[1]; fin->fin_data[0] = fin->fin_data[1]; fin->fin_data[1] = ntohs(port); natl = ipf_nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)fin->fin_p, fin->fin_dst, inb); fin->fin_data[0] = sp; fin->fin_data[1] = dp; /* * Has the search wrapped around and come back to the * start ? */ if ((natl != NULL) && (np->in_spnext != 0) && (st_port == np->in_spnext) && (np->in_snip != 0) && (st_ip == np->in_snip)) { NBUMPSIDED(1, ns_wrap); DT4(ns_wrap, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); return -1; } l++; } while (natl != NULL); /* Setup the NAT table */ nat->nat_osrcip = fin->fin_src; nat->nat_nsrcaddr = htonl(in.s_addr); nat->nat_odstip = fin->fin_dst; nat->nat_ndstip = fin->fin_dst; if (nat->nat_hm == NULL) nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, fin->fin_dst, nat->nat_nsrcip, 0); if (flags & IPN_TCPUDP) { nat->nat_osport = sport; nat->nat_nsport = port; /* sport */ nat->nat_odport = dport; nat->nat_ndport = dport; ((tcphdr_t *)fin->fin_dp)->th_sport = port; } else if (flags & IPN_ICMPQUERY) { nat->nat_oicmpid = fin->fin_data[1]; ((icmphdr_t *)fin->fin_dp)->icmp_id = port; nat->nat_nicmpid = port; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_newrdr */ /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ /* allow rule to be moved if IPN_ROUNDR is set. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* */ /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ /* to the new IP address for the translation. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_newrdr(fin, nat, ni) - fr_info_t *fin; - nat_t *nat; - natinfo_t *ni; +ipf_nat_newrdr(fr_info_t *fin, nat_t *nat, natinfo_t *ni) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short nport, dport, sport; struct in_addr in, inb; u_short sp, dp; hostmap_t *hm; u_32_t flags; ipnat_t *np; nat_t *natl; int move; move = 1; hm = NULL; in.s_addr = 0; np = ni->nai_np; flags = nat->nat_flags; if (flags & IPN_ICMPQUERY) { dport = fin->fin_data[1]; sport = 0; } else { sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); } /* TRACE sport, dport */ /* * If the matching rule has IPN_STICKY set, then we want to have the * same rule kick in as before. Why would this happen? If you have * a collection of rdr rules with "round-robin sticky", the current * packet might match a different one to the previous connection but * we want the same destination to be used. */ if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && ((np->in_flags & IPN_STICKY) != 0)) { hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, fin->fin_dst, in, (u_32_t)dport); if (hm != NULL) { in.s_addr = ntohl(hm->hm_ndstip.s_addr); np = hm->hm_ipnat; ni->nai_np = np; move = 0; ipf_nat_hostmapdel(softc, &hm); } } /* * Otherwise, it's an inbound packet. Most likely, we don't * want to rewrite source ports and source addresses. Instead, * we want to rewrite to a fixed internal address and fixed * internal port. */ if (np->in_flags & IPN_SPLIT) { in.s_addr = np->in_dnip; inb.s_addr = htonl(in.s_addr); if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, fin->fin_dst, inb, (u_32_t)dport); if (hm != NULL) { in.s_addr = hm->hm_ndstip.s_addr; move = 0; } } if (hm == NULL || hm->hm_ref == 1) { if (np->in_ndstaddr == htonl(in.s_addr)) { np->in_dnip = ntohl(np->in_ndstmsk); move = 0; } else { np->in_dnip = ntohl(np->in_ndstaddr); } } if (hm != NULL) ipf_nat_hostmapdel(softc, &hm); } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { i6addr_t in6; /* * 0/32 - use the interface's IP address. */ if (ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, &in6, NULL) == -1) { NBUMPSIDEX(0, ns_new_ifpaddr, ns_new_ifpaddr_2); DT3(ns_new_ifpaddr_2, fr_info_t *, fin, nat_t *, nat, natinfo_t, ni); return -1; } in.s_addr = ntohl(in6.in4.s_addr); } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk== 0)) { /* * 0/0 - use the original destination address/port. */ in.s_addr = ntohl(fin->fin_daddr); } else if (np->in_redir == NAT_BIMAP && np->in_ndstmsk == np->in_odstmsk) { /* * map the address block in a 1:1 fashion */ in.s_addr = np->in_ndstaddr; in.s_addr |= fin->fin_daddr & ~np->in_ndstmsk; in.s_addr = ntohl(in.s_addr); } else { in.s_addr = ntohl(np->in_ndstaddr); } if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) nport = dport; else { /* * Whilst not optimized for the case where * pmin == pmax, the gain is not significant. */ if (((np->in_flags & IPN_FIXEDDPORT) == 0) && (np->in_odport != np->in_dtop)) { nport = ntohs(dport) - np->in_odport + np->in_dpmax; nport = htons(nport); } else { nport = htons(np->in_dpnext); np->in_dpnext++; if (np->in_dpnext > np->in_dpmax) np->in_dpnext = np->in_dpmin; } } /* * When the redirect-to address is set to 0.0.0.0, just * assume a blank `forwarding' of the packet. We don't * setup any translation for this either. */ if (in.s_addr == 0) { if (nport == dport) { NBUMPSIDED(0, ns_xlate_null); return -1; } in.s_addr = ntohl(fin->fin_daddr); } /* * Check to see if this redirect mapping already exists and if * it does, return "failure" (allowing it to be created will just * cause one or both of these "connections" to stop working.) */ inb.s_addr = htonl(in.s_addr); sp = fin->fin_data[0]; dp = fin->fin_data[1]; fin->fin_data[1] = fin->fin_data[0]; fin->fin_data[0] = ntohs(nport); natl = ipf_nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)fin->fin_p, inb, fin->fin_src); fin->fin_data[0] = sp; fin->fin_data[1] = dp; if (natl != NULL) { DT2(ns_new_xlate_exists, fr_info_t *, fin, nat_t *, natl); NBUMPSIDE(0, ns_xlate_exists); return -1; } inb.s_addr = htonl(in.s_addr); nat->nat_ndstaddr = htonl(in.s_addr); nat->nat_odstip = fin->fin_dst; nat->nat_nsrcip = fin->fin_src; nat->nat_osrcip = fin->fin_src; if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, fin->fin_dst, inb, (u_32_t)dport); if (flags & IPN_TCPUDP) { nat->nat_odport = dport; nat->nat_ndport = nport; nat->nat_osport = sport; nat->nat_nsport = sport; ((tcphdr_t *)fin->fin_dp)->th_dport = nport; } else if (flags & IPN_ICMPQUERY) { nat->nat_oicmpid = fin->fin_data[1]; ((icmphdr_t *)fin->fin_dp)->icmp_id = nport; nat->nat_nicmpid = nport; } return move; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_add */ /* Returns: nat_t* - NULL == failure to create new NAT structure, */ /* else pointer to new NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* np(I) - pointer to NAT rule */ /* natsave(I) - pointer to where to store NAT struct pointer */ /* flags(I) - flags describing the current packet */ /* direction(I) - direction of packet (in/out) */ /* Write Lock: ipf_nat */ /* */ /* Attempts to create a new NAT entry. Does not actually change the packet */ /* in any way. */ /* */ /* This function is in three main parts: (1) deal with creating a new NAT */ /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ /* and (3) building that structure and putting it into the NAT table(s). */ /* */ /* NOTE: natsave should NOT be used to point back to an ipstate_t struct */ /* as it can result in memory being corrupted. */ /* ------------------------------------------------------------------------ */ nat_t * -ipf_nat_add(fin, np, natsave, flags, direction) - fr_info_t *fin; - ipnat_t *np; - nat_t **natsave; - u_int flags; - int direction; +ipf_nat_add(fr_info_t *fin, ipnat_t *np, nat_t **natsave, u_int flags, + int direction) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; hostmap_t *hm = NULL; nat_t *nat, *natl; natstat_t *nsp; u_int nflags; natinfo_t ni; int move; nsp = &softn->ipf_nat_stats; if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) > softn->ipf_nat_table_wm_high) { softn->ipf_nat_doflush = 1; } if (nsp->ns_active >= softn->ipf_nat_table_max) { NBUMPSIDED(fin->fin_out, ns_table_max); DT2(ns_table_max, nat_stat_t *, nsp, ipf_nat_softc_t *, softn); return NULL; } move = 1; nflags = np->in_flags & flags; nflags &= NAT_FROMRULE; ni.nai_np = np; ni.nai_dport = 0; ni.nai_sport = 0; /* Give me a new nat */ KMALLOC(nat, nat_t *); if (nat == NULL) { DT(ns_memfail); NBUMPSIDED(fin->fin_out, ns_memfail); /* * Try to automatically tune the max # of entries in the * table allowed to be less than what will cause kmem_alloc() * to fail and try to eliminate panics due to out of memory * conditions arising. */ if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) && (nsp->ns_active > 100)) { softn->ipf_nat_table_max = nsp->ns_active - 100; printf("table_max reduced to %d\n", softn->ipf_nat_table_max); } return NULL; } if (flags & IPN_ICMPQUERY) { /* * In the ICMP query NAT code, we translate the ICMP id fields * to make them unique. This is indepedent of the ICMP type * (e.g. in the unlikely event that a host sends an echo and * an tstamp request with the same id, both packets will have * their ip address/id field changed in the same way). */ /* The icmp_id field is used by the sender to identify the * process making the icmp request. (the receiver justs * copies it back in its response). So, it closely matches * the concept of source port. We overlay sport, so we can * maximally reuse the existing code. */ ni.nai_sport = fin->fin_data[1]; ni.nai_dport = 0; } bzero((char *)nat, sizeof(*nat)); nat->nat_flags = flags; nat->nat_redir = np->in_redir; nat->nat_dir = direction; nat->nat_pr[0] = fin->fin_p; nat->nat_pr[1] = fin->fin_p; /* * Search the current table for a match and create a new mapping * if there is none found. */ if (np->in_redir & NAT_DIVERTUDP) { move = ipf_nat_newdivert(fin, nat, &ni); } else if (np->in_redir & NAT_REWRITE) { move = ipf_nat_newrewrite(fin, nat, &ni); } else if (direction == NAT_OUTBOUND) { /* * We can now arrange to call this for the same connection * because ipf_nat_new doesn't protect the code path into * this function. */ natl = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, fin->fin_src, fin->fin_dst); if (natl != NULL) { KFREE(nat); nat = natl; goto done; } move = ipf_nat_newmap(fin, nat, &ni); } else { /* * NAT_INBOUND is used for redirects rules */ natl = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, fin->fin_src, fin->fin_dst); if (natl != NULL) { KFREE(nat); nat = natl; goto done; } move = ipf_nat_newrdr(fin, nat, &ni); } if (move == -1) goto badnat; np = ni.nai_np; nat->nat_mssclamp = np->in_mssclamp; nat->nat_me = natsave; nat->nat_fr = fin->fin_fr; nat->nat_rev = fin->fin_rev; nat->nat_ptr = np; nat->nat_dlocal = np->in_dlocal; if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) { if (ipf_proxy_new(fin, nat) == -1) { NBUMPSIDED(fin->fin_out, ns_appr_fail); DT3(ns_appr_fail, fr_info_t *, fin, nat_t *, nat, ipnat_t *, np); goto badnat; } } nat->nat_ifps[0] = np->in_ifps[0]; if (np->in_ifps[0] != NULL) { COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]); } nat->nat_ifps[1] = np->in_ifps[1]; if (np->in_ifps[1] != NULL) { COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]); } if (ipf_nat_finalise(fin, nat) == -1) { goto badnat; } np->in_use++; if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) { ipf_nat_delrdr(softn, np); ipf_nat_addrdr(softn, np); } else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) { ipf_nat_delmap(softn, np); ipf_nat_addmap(softn, np); } } if (flags & SI_WILDP) nsp->ns_wilds++; nsp->ns_proto[nat->nat_pr[0]]++; goto done; badnat: DT3(ns_badnatnew, fr_info_t *, fin, nat_t *, nat, ipnat_t *, np); NBUMPSIDE(fin->fin_out, ns_badnatnew); if ((hm = nat->nat_hm) != NULL) ipf_nat_hostmapdel(softc, &hm); KFREE(nat); nat = NULL; done: if (nat != NULL && np != NULL) np->in_hits++; if (natsave != NULL) *natsave = nat; return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_finalise */ /* Returns: int - 0 == sucess, -1 == failure */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* Write Lock: ipf_nat */ /* */ /* This is the tail end of constructing a new NAT entry and is the same */ /* for both IPv4 and IPv6. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ static int -ipf_nat_finalise(fin, nat) - fr_info_t *fin; - nat_t *nat; +ipf_nat_finalise(fr_info_t *fin, nat_t *nat) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sum1, sum2, sumd; frentry_t *fr; u_32_t flags; #if SOLARIS && defined(_KERNEL) && defined(ICK_M_CTL_MAGIC) qpktinfo_t *qpi = fin->fin_qpi; #endif flags = nat->nat_flags; switch (nat->nat_pr[0]) { case IPPROTO_ICMP : sum1 = LONG_SUM(ntohs(nat->nat_oicmpid)); sum2 = LONG_SUM(ntohs(nat->nat_nicmpid)); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); break; default : sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr) + \ ntohs(nat->nat_osport)); sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr) + \ ntohs(nat->nat_nsport)); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); sum1 = LONG_SUM(ntohl(nat->nat_odstaddr) + \ ntohs(nat->nat_odport)); sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr) + \ ntohs(nat->nat_ndport)); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); break; } /* * Compute the partial checksum, just in case. * This is only ever placed into outbound packets so care needs * to be taken over which pair of addresses are used. */ if (nat->nat_dir == NAT_OUTBOUND) { sum1 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); sum1 += LONG_SUM(ntohl(nat->nat_ndstaddr)); } else { sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); sum1 += LONG_SUM(ntohl(nat->nat_odstaddr)); } sum1 += nat->nat_pr[1]; nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16); sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); CALC_SUMD(sum1, sum2, sumd); nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); sum1 = LONG_SUM(ntohl(nat->nat_odstaddr)); sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); CALC_SUMD(sum1, sum2, sumd); nat->nat_ipsumd += (sumd & 0xffff) + (sumd >> 16); nat->nat_v[0] = 4; nat->nat_v[1] = 4; if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); } if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); } if ((nat->nat_flags & SI_CLONE) == 0) nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat); if (ipf_nat_insert(softc, softn, nat) == 0) { if (softn->ipf_nat_logging) ipf_nat_log(softc, softn, nat, NL_NEW); fr = nat->nat_fr; if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } return 0; } NBUMPSIDED(fin->fin_out, ns_unfinalised); DT2(ns_unfinalised, fr_info_t *, fin, nat_t *, nat); /* * nat_insert failed, so cleanup time... */ if (nat->nat_sync != NULL) ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); return -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_insert */ /* Returns: int - 0 == sucess, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to NAT structure */ /* Write Lock: ipf_nat */ /* */ /* Insert a NAT entry into the hash tables for searching and add it to the */ /* list of active NAT entries. Adjust global counters when complete. */ /* ------------------------------------------------------------------------ */ int -ipf_nat_insert(softc, softn, nat) - ipf_main_softc_t *softc; - ipf_nat_softc_t *softn; - nat_t *nat; +ipf_nat_insert(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, nat_t *nat) { u_int hv0, hv1; u_int sp, dp; ipnat_t *in; int ret; /* * Try and return an error as early as possible, so calculate the hash * entry numbers first and then proceed. */ if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { if ((nat->nat_flags & IPN_TCPUDP) != 0) { sp = nat->nat_osport; dp = nat->nat_odport; } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { sp = 0; dp = nat->nat_oicmpid; } else { sp = 0; dp = 0; } hv0 = NAT_HASH_FN(nat->nat_osrcaddr, sp, 0xffffffff); hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0 + dp, 0xffffffff); /* * TRACE nat_osrcaddr, nat_osport, nat_odstaddr, * nat_odport, hv0 */ if ((nat->nat_flags & IPN_TCPUDP) != 0) { sp = nat->nat_nsport; dp = nat->nat_ndport; } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { sp = 0; dp = nat->nat_nicmpid; } else { sp = 0; dp = 0; } hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, sp, 0xffffffff); hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1 + dp, 0xffffffff); /* * TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, * nat_ndport, hv1 */ } else { hv0 = NAT_HASH_FN(nat->nat_osrcaddr, 0, 0xffffffff); hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0, 0xffffffff); /* TRACE nat_osrcaddr, nat_odstaddr, hv0 */ hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, 0, 0xffffffff); hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1, 0xffffffff); /* TRACE nat_nsrcaddr, nat_ndstaddr, hv1 */ } nat->nat_hv[0] = hv0; nat->nat_hv[1] = hv1; MUTEX_INIT(&nat->nat_lock, "nat entry lock"); in = nat->nat_ptr; nat->nat_ref = nat->nat_me ? 2 : 1; nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], 4); if (nat->nat_ifnames[1][0] != '\0') { nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; nat->nat_ifps[1] = ipf_resolvenic(softc, nat->nat_ifnames[1], 4); } else if (in->in_ifnames[1] != -1) { char *name; name = in->in_names + in->in_ifnames[1]; if (name[1] != '\0' && name[0] != '-' && name[0] != '*') { (void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0], LIFNAMSIZ); nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; nat->nat_ifps[1] = nat->nat_ifps[0]; } } if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); } if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); } ret = ipf_nat_hashtab_add(softc, softn, nat); if (ret == -1) MUTEX_DESTROY(&nat->nat_lock); return ret; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_hashtab_add */ /* Returns: int - 0 == sucess, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to NAT structure */ /* */ /* Handle the insertion of a NAT entry into the table/list. */ /* ------------------------------------------------------------------------ */ int -ipf_nat_hashtab_add(softc, softn, nat) - ipf_main_softc_t *softc; - ipf_nat_softc_t *softn; - nat_t *nat; +ipf_nat_hashtab_add(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, + nat_t *nat) { nat_t **natp; u_int hv0; u_int hv1; hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz; hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz; if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { u_int swap; swap = hv0; hv0 = hv1; hv1 = swap; } if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0] >= softn->ipf_nat_maxbucket) { DT1(ns_bucket_max_0, int, softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]); NBUMPSIDE(0, ns_bucket_max); return -1; } if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1] >= softn->ipf_nat_maxbucket) { DT1(ns_bucket_max_1, int, softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]); NBUMPSIDE(1, ns_bucket_max); return -1; } /* * The ordering of operations in the list and hash table insertion * is very important. The last operation for each task should be * to update the top of the list, after all the "nexts" have been * done so that walking the list while it is being done does not * find strange pointers. * * Global list of NAT instances */ nat->nat_next = softn->ipf_nat_instances; nat->nat_pnext = &softn->ipf_nat_instances; if (softn->ipf_nat_instances) softn->ipf_nat_instances->nat_pnext = &nat->nat_next; softn->ipf_nat_instances = nat; /* * Inbound hash table. */ natp = &softn->ipf_nat_table[0][hv0]; nat->nat_phnext[0] = natp; nat->nat_hnext[0] = *natp; if (*natp) { (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; } else { NBUMPSIDE(0, ns_inuse); } *natp = nat; NBUMPSIDE(0, ns_bucketlen[hv0]); /* * Outbound hash table. */ natp = &softn->ipf_nat_table[1][hv1]; nat->nat_phnext[1] = natp; nat->nat_hnext[1] = *natp; if (*natp) (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; else { NBUMPSIDE(1, ns_inuse); } *natp = nat; NBUMPSIDE(1, ns_bucketlen[hv1]); ipf_nat_setqueue(softc, softn, nat); if (nat->nat_dir & NAT_OUTBOUND) { NBUMPSIDE(1, ns_added); } else { NBUMPSIDE(0, ns_added); } softn->ipf_nat_stats.ns_active++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_icmperrorlookup */ /* Returns: nat_t* - point to matching NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* dir(I) - direction of packet (in/out) */ /* */ /* Check if the ICMP error message is related to an existing TCP, UDP or */ /* ICMP query nat entry. It is assumed that the packet is already of the */ /* the required length. */ /* ------------------------------------------------------------------------ */ nat_t * -ipf_nat_icmperrorlookup(fin, dir) - fr_info_t *fin; - int dir; +ipf_nat_icmperrorlookup(fr_info_t *fin, int dir) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; int flags = 0, type, minlen; icmphdr_t *icmp, *orgicmp; nat_stat_side_t *nside; tcphdr_t *tcp = NULL; u_short data[2]; nat_t *nat; ip_t *oip; u_int p; icmp = fin->fin_dp; type = icmp->icmp_type; nside = &softn->ipf_nat_stats.ns_side[fin->fin_out]; /* * Does it at least have the return (basic) IP header ? * Only a basic IP header (no options) should be with an ICMP error * header. Also, if it's not an error type, then return. */ if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) { ATOMIC_INCL(nside->ns_icmp_basic); return NULL; } /* * Check packet size */ oip = (ip_t *)((char *)fin->fin_dp + 8); minlen = IP_HL(oip) << 2; if ((minlen < sizeof(ip_t)) || (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) { ATOMIC_INCL(nside->ns_icmp_size); return NULL; } /* * Is the buffer big enough for all of it ? It's the size of the IP * header claimed in the encapsulated part which is of concern. It * may be too big to be in this buffer but not so big that it's * outside the ICMP packet, leading to TCP deref's causing problems. * This is possible because we don't know how big oip_hl is when we * do the pullup early in ipf_check() and thus can't gaurantee it is * all here now. */ #ifdef ipf_nat_KERNEL { mb_t *m; m = fin->fin_m; # if SOLARIS if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) { ATOMIC_INCL(nside->ns_icmp_mbuf); return NULL; } # else if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)fin->fin_ip + M_LEN(m)) { ATOMIC_INCL(nside->ns_icmp_mbuf); return NULL; } # endif } #endif if (fin->fin_daddr != oip->ip_src.s_addr) { ATOMIC_INCL(nside->ns_icmp_address); return NULL; } p = oip->ip_p; if (p == IPPROTO_TCP) flags = IPN_TCP; else if (p == IPPROTO_UDP) flags = IPN_UDP; else if (p == IPPROTO_ICMP) { orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); /* see if this is related to an ICMP query */ if (ipf_nat_icmpquerytype(orgicmp->icmp_type)) { data[0] = fin->fin_data[0]; data[1] = fin->fin_data[1]; fin->fin_data[0] = 0; fin->fin_data[1] = orgicmp->icmp_id; flags = IPN_ICMPERR|IPN_ICMPQUERY; /* * NOTE : dir refers to the direction of the original * ip packet. By definition the icmp error * message flows in the opposite direction. */ if (dir == NAT_INBOUND) nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst, oip->ip_src); else nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst, oip->ip_src); fin->fin_data[0] = data[0]; fin->fin_data[1] = data[1]; return nat; } } if (flags & IPN_TCPUDP) { minlen += 8; /* + 64bits of data to get ports */ /* TRACE (fin,minlen) */ if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) { ATOMIC_INCL(nside->ns_icmp_short); return NULL; } data[0] = fin->fin_data[0]; data[1] = fin->fin_data[1]; tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); fin->fin_data[0] = ntohs(tcp->th_dport); fin->fin_data[1] = ntohs(tcp->th_sport); if (dir == NAT_INBOUND) { nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst, oip->ip_src); } else { nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst, oip->ip_src); } fin->fin_data[0] = data[0]; fin->fin_data[1] = data[1]; return nat; } if (dir == NAT_INBOUND) nat = ipf_nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); else nat = ipf_nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_icmperror */ /* Returns: nat_t* - point to matching NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* nflags(I) - NAT flags for this packet */ /* dir(I) - direction of packet (in/out) */ /* */ /* Fix up an ICMP packet which is an error message for an existing NAT */ /* session. This will correct both packet header data and checksums. */ /* */ /* This should *ONLY* be used for incoming ICMP error packets to make sure */ /* a NAT'd ICMP packet gets correctly recognised. */ /* ------------------------------------------------------------------------ */ nat_t * -ipf_nat_icmperror(fin, nflags, dir) - fr_info_t *fin; - u_int *nflags; - int dir; +ipf_nat_icmperror(fr_info_t *fin, u_int *nflags, int dir) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sum1, sum2, sumd, sumd2; struct in_addr a1, a2, a3, a4; int flags, dlen, odst; icmphdr_t *icmp; u_short *csump; tcphdr_t *tcp; nat_t *nat; ip_t *oip; void *dp; if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { NBUMPSIDED(fin->fin_out, ns_icmp_short); return NULL; } /* * ipf_nat_icmperrorlookup() will return NULL for `defective' packets. */ if ((fin->fin_v != 4) || !(nat = ipf_nat_icmperrorlookup(fin, dir))) { NBUMPSIDED(fin->fin_out, ns_icmp_notfound); return NULL; } tcp = NULL; csump = NULL; flags = 0; sumd2 = 0; *nflags = IPN_ICMPERR; icmp = fin->fin_dp; oip = (ip_t *)&icmp->icmp_ip; dp = (((char *)oip) + (IP_HL(oip) << 2)); if (oip->ip_p == IPPROTO_TCP) { tcp = (tcphdr_t *)dp; csump = (u_short *)&tcp->th_sum; flags = IPN_TCP; } else if (oip->ip_p == IPPROTO_UDP) { udphdr_t *udp; udp = (udphdr_t *)dp; tcp = (tcphdr_t *)dp; csump = (u_short *)&udp->uh_sum; flags = IPN_UDP; } else if (oip->ip_p == IPPROTO_ICMP) flags = IPN_ICMPQUERY; dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip); /* * Need to adjust ICMP header to include the real IP#'s and * port #'s. Only apply a checksum change relative to the * IP address change as it will be modified again in ipf_nat_checkout * for both address and port. Two checksum changes are * necessary for the two header address changes. Be careful * to only modify the checksum once for the port # and twice * for the IP#. */ /* * Step 1 * Fix the IP addresses in the offending IP packet. You also need * to adjust the IP header checksum of that offending IP packet. * * Normally, you would expect that the ICMP checksum of the * ICMP error message needs to be adjusted as well for the * IP address change in oip. * However, this is a NOP, because the ICMP checksum is * calculated over the complete ICMP packet, which includes the * changed oip IP addresses and oip->ip_sum. However, these * two changes cancel each other out (if the delta for * the IP address is x, then the delta for ip_sum is minus x), * so no change in the icmp_cksum is necessary. * * Inbound ICMP * ------------ * MAP rule, SRC=a,DST=b -> SRC=c,DST=b * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b) * - OIP_SRC(c)=nat_newsrcip, OIP_DST(b)=nat_newdstip *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(b)=nat_olddstip * * RDR rule, SRC=a,DST=b -> SRC=a,DST=c * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip * * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d) * - OIP_SRC(c)=nat_newsrcip, OIP_DST(d)=nat_newdstip *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(d)=nat_olddstip * * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip * * Outbound ICMP * ------------- * MAP rule, SRC=a,DST=b -> SRC=c,DST=b * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip * * RDR rule, SRC=a,DST=b -> SRC=a,DST=c * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c) * - OIP_SRC(a)=nat_newsrcip, OIP_DST(c)=nat_newdstip *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip * * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d) * - OIP_SRC(c)=nat_olddstip, OIP_DST(d)=nat_oldsrcip *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip * * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat_newsrcip, OIP_DST(a)=nat_newdstip *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip */ if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) || ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) { a1.s_addr = ntohl(nat->nat_osrcaddr); a4.s_addr = ntohl(oip->ip_src.s_addr); a3.s_addr = ntohl(nat->nat_odstaddr); a2.s_addr = ntohl(oip->ip_dst.s_addr); oip->ip_src.s_addr = htonl(a1.s_addr); oip->ip_dst.s_addr = htonl(a3.s_addr); odst = 1; } else { a1.s_addr = ntohl(nat->nat_ndstaddr); a2.s_addr = ntohl(oip->ip_dst.s_addr); a3.s_addr = ntohl(nat->nat_nsrcaddr); a4.s_addr = ntohl(oip->ip_src.s_addr); oip->ip_dst.s_addr = htonl(a3.s_addr); oip->ip_src.s_addr = htonl(a1.s_addr); odst = 0; } sum1 = 0; sum2 = 0; sumd = 0; CALC_SUMD(a2.s_addr, a3.s_addr, sum1); CALC_SUMD(a4.s_addr, a1.s_addr, sum2); sumd = sum2 + sum1; if (sumd != 0) ipf_fix_datacksum(&oip->ip_sum, sumd); sumd2 = sumd; sum1 = 0; sum2 = 0; /* * Fix UDP pseudo header checksum to compensate for the * IP address change. */ if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { u_32_t sum3, sum4, sumt; /* * Step 2 : * For offending TCP/UDP IP packets, translate the ports as * well, based on the NAT specification. Of course such * a change may be reflected in the ICMP checksum as well. * * Since the port fields are part of the TCP/UDP checksum * of the offending IP packet, you need to adjust that checksum * as well... except that the change in the port numbers should * be offset by the checksum change. However, the TCP/UDP * checksum will also need to change if there has been an * IP address change. */ if (odst == 1) { sum1 = ntohs(nat->nat_osport); sum4 = ntohs(tcp->th_sport); sum3 = ntohs(nat->nat_odport); sum2 = ntohs(tcp->th_dport); tcp->th_sport = htons(sum1); tcp->th_dport = htons(sum3); } else { sum1 = ntohs(nat->nat_ndport); sum2 = ntohs(tcp->th_dport); sum3 = ntohs(nat->nat_nsport); sum4 = ntohs(tcp->th_sport); tcp->th_dport = htons(sum3); tcp->th_sport = htons(sum1); } CALC_SUMD(sum4, sum1, sumt); sumd += sumt; CALC_SUMD(sum2, sum3, sumt); sumd += sumt; if (sumd != 0 || sumd2 != 0) { /* * At this point, sumd is the delta to apply to the * TCP/UDP header, given the changes in both the IP * address and the ports and sumd2 is the delta to * apply to the ICMP header, given the IP address * change delta that may need to be applied to the * TCP/UDP checksum instead. * * If we will both the IP and TCP/UDP checksums * then the ICMP checksum changes by the address * delta applied to the TCP/UDP checksum. If we * do not change the TCP/UDP checksum them we * apply the delta in ports to the ICMP checksum. */ if (oip->ip_p == IPPROTO_UDP) { if ((dlen >= 8) && (*csump != 0)) { ipf_fix_datacksum(csump, sumd); } else { CALC_SUMD(sum1, sum4, sumd2); CALC_SUMD(sum3, sum2, sumt); sumd2 += sumt; } } else if (oip->ip_p == IPPROTO_TCP) { if (dlen >= 18) { ipf_fix_datacksum(csump, sumd); } else { CALC_SUMD(sum1, sum4, sumd2); CALC_SUMD(sum3, sum2, sumt); sumd2 += sumt; } } if (sumd2 != 0) { sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); ipf_fix_incksum(0, &icmp->icmp_cksum, sumd2, 0); } } } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { icmphdr_t *orgicmp; /* * XXX - what if this is bogus hl and we go off the end ? * In this case, ipf_nat_icmperrorlookup() will have * returned NULL. */ orgicmp = (icmphdr_t *)dp; if (odst == 1) { if (orgicmp->icmp_id != nat->nat_osport) { /* * Fix ICMP checksum (of the offening ICMP * query packet) to compensate the change * in the ICMP id of the offending ICMP * packet. * * Since you modify orgicmp->icmp_id with * a delta (say x) and you compensate that * in origicmp->icmp_cksum with a delta * minus x, you don't have to adjust the * overall icmp->icmp_cksum */ sum1 = ntohs(orgicmp->icmp_id); sum2 = ntohs(nat->nat_oicmpid); CALC_SUMD(sum1, sum2, sumd); orgicmp->icmp_id = nat->nat_oicmpid; ipf_fix_datacksum(&orgicmp->icmp_cksum, sumd); } } /* nat_dir == NAT_INBOUND is impossible for icmp queries */ } return nat; } /* * MAP-IN MAP-OUT RDR-IN RDR-OUT * osrc X == src == src X * odst X == dst == dst X * nsrc == dst X X == dst * ndst == src X X == src * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND */ /* * NB: these lookups don't lock access to the list, it assumed that it has * already been done! */ /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_inlookup */ /* Returns: nat_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: fin(I) - pointer to packet information */ /* flags(I) - NAT flags for this packet */ /* p(I) - protocol for this packet */ /* src(I) - source IP address */ /* mapdst(I) - destination IP address */ /* */ /* Lookup a nat entry based on the mapped destination ip address/port and */ /* real source address/port. We use this lookup when receiving a packet, */ /* we're looking for a table entry, based on the destination address. */ /* */ /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ /* */ /* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ /* */ /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ /* the packet is of said protocol */ /* ------------------------------------------------------------------------ */ nat_t * -ipf_nat_inlookup(fin, flags, p, src, mapdst) - fr_info_t *fin; - u_int flags, p; - struct in_addr src , mapdst; +ipf_nat_inlookup(fr_info_t *fin, u_int flags, u_int p, + struct in_addr src , struct in_addr mapdst) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short sport, dport; grehdr_t *gre; ipnat_t *ipn; u_int sflags; nat_t *nat; int nflags; u_32_t dst; void *ifp; u_int hv, rhv; ifp = fin->fin_ifp; gre = NULL; dst = mapdst.s_addr; sflags = flags & NAT_TCPUDPICMP; switch (p) { case IPPROTO_TCP : case IPPROTO_UDP : sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); break; case IPPROTO_ICMP : sport = 0; dport = fin->fin_data[1]; break; default : sport = 0; dport = 0; break; } if ((flags & SI_WILDP) != 0) goto find_in_wild_ports; rhv = NAT_HASH_FN(dst, dport, 0xffffffff); rhv = NAT_HASH_FN(src.s_addr, rhv + sport, 0xffffffff); hv = rhv % softn->ipf_nat_table_sz; nat = softn->ipf_nat_table[1][hv]; /* TRACE dst, dport, src, sport, hv, nat */ for (; nat; nat = nat->nat_hnext[1]) { if (nat->nat_ifps[0] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) continue; } if (nat->nat_pr[0] != p) continue; switch (nat->nat_dir) { case NAT_INBOUND : case NAT_DIVERTIN : if (nat->nat_v[0] != 4) continue; if (nat->nat_osrcaddr != src.s_addr || nat->nat_odstaddr != dst) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_osport != sport) continue; if (nat->nat_odport != dport) continue; } else if (p == IPPROTO_ICMP) { if (nat->nat_osport != dport) { continue; } } break; case NAT_DIVERTOUT : if (nat->nat_dlocal) continue; case NAT_OUTBOUND : if (nat->nat_v[1] != 4) continue; if (nat->nat_dlocal) continue; if (nat->nat_dlocal) continue; if (nat->nat_ndstaddr != src.s_addr || nat->nat_nsrcaddr != dst) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_ndport != sport) continue; if (nat->nat_nsport != dport) continue; } else if (p == IPPROTO_ICMP) { if (nat->nat_osport != dport) { continue; } } break; } if ((nat->nat_flags & IPN_TCPUDP) != 0) { ipn = nat->nat_ptr; if ((ipn != NULL) && (nat->nat_aps != NULL)) if (ipf_proxy_match(fin, nat) != 0) continue; } if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { nat->nat_ifps[0] = ifp; nat->nat_mtu[0] = GETIFMTU_4(ifp); } return nat; } /* * So if we didn't find it but there are wildcard members in the hash * table, go back and look for them. We do this search and update here * because it is modifying the NAT table and we want to do this only * for the first packet that matches. The exception, of course, is * for "dummy" (FI_IGNORE) lookups. */ find_in_wild_ports: if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { NBUMPSIDEX(0, ns_lookup_miss, ns_lookup_miss_0); return NULL; } if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { NBUMPSIDEX(0, ns_lookup_nowild, ns_lookup_nowild_0); return NULL; } RWLOCK_EXIT(&softc->ipf_nat); hv = NAT_HASH_FN(dst, 0, 0xffffffff); hv = NAT_HASH_FN(src.s_addr, hv, softn->ipf_nat_table_sz); WRITE_ENTER(&softc->ipf_nat); nat = softn->ipf_nat_table[1][hv]; /* TRACE dst, src, hv, nat */ for (; nat; nat = nat->nat_hnext[1]) { if (nat->nat_ifps[0] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) continue; } if (nat->nat_pr[0] != fin->fin_p) continue; switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) { case NAT_INBOUND : if (nat->nat_v[0] != 4) continue; if (nat->nat_osrcaddr != src.s_addr || nat->nat_odstaddr != dst) continue; break; case NAT_OUTBOUND : if (nat->nat_v[1] != 4) continue; if (nat->nat_ndstaddr != src.s_addr || nat->nat_nsrcaddr != dst) continue; break; } nflags = nat->nat_flags; if (!(nflags & (NAT_TCPUDP|SI_WILDP))) continue; if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags, NAT_INBOUND) == 1) { if ((fin->fin_flx & FI_IGNORE) != 0) break; if ((nflags & SI_CLONE) != 0) { nat = ipf_nat_clone(fin, nat); if (nat == NULL) break; } else { MUTEX_ENTER(&softn->ipf_nat_new); softn->ipf_nat_stats.ns_wilds--; MUTEX_EXIT(&softn->ipf_nat_new); } if (nat->nat_dir == NAT_INBOUND) { if (nat->nat_osport == 0) { nat->nat_osport = sport; nat->nat_nsport = sport; } if (nat->nat_odport == 0) { nat->nat_odport = dport; nat->nat_ndport = dport; } } else if (nat->nat_dir == NAT_OUTBOUND) { if (nat->nat_osport == 0) { nat->nat_osport = dport; nat->nat_nsport = dport; } if (nat->nat_odport == 0) { nat->nat_odport = sport; nat->nat_ndport = sport; } } if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { nat->nat_ifps[0] = ifp; nat->nat_mtu[0] = GETIFMTU_4(ifp); } nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); ipf_nat_tabmove(softn, nat); break; } } MUTEX_DOWNGRADE(&softc->ipf_nat); if (nat == NULL) { NBUMPSIDE(0, ns_lookup_miss); } return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_tabmove */ /* Returns: Nil */ /* Parameters: softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to NAT structure */ /* Write Lock: ipf_nat */ /* */ /* This function is only called for TCP/UDP NAT table entries where the */ /* original was placed in the table without hashing on the ports and we now */ /* want to include hashing on port numbers. */ /* ------------------------------------------------------------------------ */ static void -ipf_nat_tabmove(softn, nat) - ipf_nat_softc_t *softn; - nat_t *nat; +ipf_nat_tabmove(ipf_nat_softc_t *softn, nat_t *nat) { u_int hv0, hv1, rhv0, rhv1; natstat_t *nsp; nat_t **natp; if (nat->nat_flags & SI_CLONE) return; nsp = &softn->ipf_nat_stats; /* * Remove the NAT entry from the old location */ if (nat->nat_hnext[0]) nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; *nat->nat_phnext[0] = nat->nat_hnext[0]; nsp->ns_side[0].ns_bucketlen[nat->nat_hv[0] % softn->ipf_nat_table_sz]--; if (nat->nat_hnext[1]) nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; *nat->nat_phnext[1] = nat->nat_hnext[1]; nsp->ns_side[1].ns_bucketlen[nat->nat_hv[1] % softn->ipf_nat_table_sz]--; /* * Add into the NAT table in the new position */ rhv0 = NAT_HASH_FN(nat->nat_osrcaddr, nat->nat_osport, 0xffffffff); rhv0 = NAT_HASH_FN(nat->nat_odstaddr, rhv0 + nat->nat_odport, 0xffffffff); rhv1 = NAT_HASH_FN(nat->nat_nsrcaddr, nat->nat_nsport, 0xffffffff); rhv1 = NAT_HASH_FN(nat->nat_ndstaddr, rhv1 + nat->nat_ndport, 0xffffffff); hv0 = rhv0 % softn->ipf_nat_table_sz; hv1 = rhv1 % softn->ipf_nat_table_sz; if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { u_int swap; swap = hv0; hv0 = hv1; hv1 = swap; } /* TRACE nat_osrcaddr, nat_osport, nat_odstaddr, nat_odport, hv0 */ /* TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, nat_ndport, hv1 */ nat->nat_hv[0] = rhv0; natp = &softn->ipf_nat_table[0][hv0]; if (*natp) (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; nat->nat_phnext[0] = natp; nat->nat_hnext[0] = *natp; *natp = nat; nsp->ns_side[0].ns_bucketlen[hv0]++; nat->nat_hv[1] = rhv1; natp = &softn->ipf_nat_table[1][hv1]; if (*natp) (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; nat->nat_phnext[1] = natp; nat->nat_hnext[1] = *natp; *natp = nat; nsp->ns_side[1].ns_bucketlen[hv1]++; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_outlookup */ /* Returns: nat_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: fin(I) - pointer to packet information */ /* flags(I) - NAT flags for this packet */ /* p(I) - protocol for this packet */ /* src(I) - source IP address */ /* dst(I) - destination IP address */ /* rw(I) - 1 == write lock on held, 0 == read lock. */ /* */ /* Lookup a nat entry based on the source 'real' ip address/port and */ /* destination address/port. We use this lookup when sending a packet out, */ /* we're looking for a table entry, based on the source address. */ /* */ /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ /* */ /* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ /* */ /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ /* the packet is of said protocol */ /* ------------------------------------------------------------------------ */ nat_t * -ipf_nat_outlookup(fin, flags, p, src, dst) - fr_info_t *fin; - u_int flags, p; - struct in_addr src , dst; +ipf_nat_outlookup(fr_info_t *fin, u_int flags, u_int p, + struct in_addr src , struct in_addr dst) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short sport, dport; u_int sflags; ipnat_t *ipn; nat_t *nat; void *ifp; u_int hv; ifp = fin->fin_ifp; sflags = flags & IPN_TCPUDPICMP; switch (p) { case IPPROTO_TCP : case IPPROTO_UDP : sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); break; case IPPROTO_ICMP : sport = 0; dport = fin->fin_data[1]; break; default : sport = 0; dport = 0; break; } if ((flags & SI_WILDP) != 0) goto find_out_wild_ports; hv = NAT_HASH_FN(src.s_addr, sport, 0xffffffff); hv = NAT_HASH_FN(dst.s_addr, hv + dport, softn->ipf_nat_table_sz); nat = softn->ipf_nat_table[0][hv]; /* TRACE src, sport, dst, dport, hv, nat */ for (; nat; nat = nat->nat_hnext[0]) { if (nat->nat_ifps[1] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) continue; } if (nat->nat_pr[1] != p) continue; switch (nat->nat_dir) { case NAT_INBOUND : case NAT_DIVERTIN : if (nat->nat_v[1] != 4) continue; if (nat->nat_ndstaddr != src.s_addr || nat->nat_nsrcaddr != dst.s_addr) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_ndport != sport) continue; if (nat->nat_nsport != dport) continue; } else if (p == IPPROTO_ICMP) { if (nat->nat_osport != dport) { continue; } } break; case NAT_OUTBOUND : case NAT_DIVERTOUT : if (nat->nat_v[0] != 4) continue; if (nat->nat_osrcaddr != src.s_addr || nat->nat_odstaddr != dst.s_addr) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_odport != dport) continue; if (nat->nat_osport != sport) continue; } else if (p == IPPROTO_ICMP) { if (nat->nat_osport != dport) { continue; } } break; } ipn = nat->nat_ptr; if ((ipn != NULL) && (nat->nat_aps != NULL)) if (ipf_proxy_match(fin, nat) != 0) continue; if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { nat->nat_ifps[1] = ifp; nat->nat_mtu[1] = GETIFMTU_4(ifp); } return nat; } /* * So if we didn't find it but there are wildcard members in the hash * table, go back and look for them. We do this search and update here * because it is modifying the NAT table and we want to do this only * for the first packet that matches. The exception, of course, is * for "dummy" (FI_IGNORE) lookups. */ find_out_wild_ports: if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { NBUMPSIDEX(1, ns_lookup_miss, ns_lookup_miss_1); return NULL; } if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { NBUMPSIDEX(1, ns_lookup_nowild, ns_lookup_nowild_1); return NULL; } RWLOCK_EXIT(&softc->ipf_nat); hv = NAT_HASH_FN(src.s_addr, 0, 0xffffffff); hv = NAT_HASH_FN(dst.s_addr, hv, softn->ipf_nat_table_sz); WRITE_ENTER(&softc->ipf_nat); nat = softn->ipf_nat_table[0][hv]; for (; nat; nat = nat->nat_hnext[0]) { if (nat->nat_ifps[1] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) continue; } if (nat->nat_pr[1] != fin->fin_p) continue; switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) { case NAT_INBOUND : if (nat->nat_v[1] != 4) continue; if (nat->nat_ndstaddr != src.s_addr || nat->nat_nsrcaddr != dst.s_addr) continue; break; case NAT_OUTBOUND : if (nat->nat_v[0] != 4) continue; if (nat->nat_osrcaddr != src.s_addr || nat->nat_odstaddr != dst.s_addr) continue; break; } if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP))) continue; if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags, NAT_OUTBOUND) == 1) { if ((fin->fin_flx & FI_IGNORE) != 0) break; if ((nat->nat_flags & SI_CLONE) != 0) { nat = ipf_nat_clone(fin, nat); if (nat == NULL) break; } else { MUTEX_ENTER(&softn->ipf_nat_new); softn->ipf_nat_stats.ns_wilds--; MUTEX_EXIT(&softn->ipf_nat_new); } if (nat->nat_dir == NAT_OUTBOUND) { if (nat->nat_osport == 0) { nat->nat_osport = sport; nat->nat_nsport = sport; } if (nat->nat_odport == 0) { nat->nat_odport = dport; nat->nat_ndport = dport; } } else if (nat->nat_dir == NAT_INBOUND) { if (nat->nat_osport == 0) { nat->nat_osport = dport; nat->nat_nsport = dport; } if (nat->nat_odport == 0) { nat->nat_odport = sport; nat->nat_ndport = sport; } } if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { nat->nat_ifps[1] = ifp; nat->nat_mtu[1] = GETIFMTU_4(ifp); } nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); ipf_nat_tabmove(softn, nat); break; } } MUTEX_DOWNGRADE(&softc->ipf_nat); if (nat == NULL) { NBUMPSIDE(1, ns_lookup_miss); } return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_lookupredir */ /* Returns: nat_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: np(I) - pointer to description of packet to find NAT table */ /* entry for. */ /* */ /* Lookup the NAT tables to search for a matching redirect */ /* The contents of natlookup_t should imitate those found in a packet that */ /* would be translated - ie a packet coming in for RDR or going out for MAP.*/ /* We can do the lookup in one of two ways, imitating an inbound or */ /* outbound packet. By default we assume outbound, unless IPN_IN is set. */ /* For IN, the fields are set as follows: */ /* nl_real* = source information */ /* nl_out* = destination information (translated) */ /* For an out packet, the fields are set like this: */ /* nl_in* = source information (untranslated) */ /* nl_out* = destination information (translated) */ /* ------------------------------------------------------------------------ */ nat_t * -ipf_nat_lookupredir(np) - natlookup_t *np; +ipf_nat_lookupredir(natlookup_t *np) { fr_info_t fi; nat_t *nat; bzero((char *)&fi, sizeof(fi)); if (np->nl_flags & IPN_IN) { fi.fin_data[0] = ntohs(np->nl_realport); fi.fin_data[1] = ntohs(np->nl_outport); } else { fi.fin_data[0] = ntohs(np->nl_inport); fi.fin_data[1] = ntohs(np->nl_outport); } if (np->nl_flags & IPN_TCP) fi.fin_p = IPPROTO_TCP; else if (np->nl_flags & IPN_UDP) fi.fin_p = IPPROTO_UDP; else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) fi.fin_p = IPPROTO_ICMP; /* * We can do two sorts of lookups: * - IPN_IN: we have the `real' and `out' address, look for `in'. * - default: we have the `in' and `out' address, look for `real'. */ if (np->nl_flags & IPN_IN) { if ((nat = ipf_nat_inlookup(&fi, np->nl_flags, fi.fin_p, np->nl_realip, np->nl_outip))) { np->nl_inip = nat->nat_odstip; np->nl_inport = nat->nat_odport; } } else { /* * If nl_inip is non null, this is a lookup based on the real * ip address. Else, we use the fake. */ if ((nat = ipf_nat_outlookup(&fi, np->nl_flags, fi.fin_p, np->nl_inip, np->nl_outip))) { if ((np->nl_flags & IPN_FINDFORWARD) != 0) { fr_info_t fin; bzero((char *)&fin, sizeof(fin)); fin.fin_p = nat->nat_pr[0]; fin.fin_data[0] = ntohs(nat->nat_ndport); fin.fin_data[1] = ntohs(nat->nat_nsport); if (ipf_nat_inlookup(&fin, np->nl_flags, fin.fin_p, nat->nat_ndstip, nat->nat_nsrcip) != NULL) { np->nl_flags &= ~IPN_FINDFORWARD; } } np->nl_realip = nat->nat_odstip; np->nl_realport = nat->nat_odport; } } return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_match */ /* Returns: int - 0 == no match, 1 == match */ /* Parameters: fin(I) - pointer to packet information */ /* np(I) - pointer to NAT rule */ /* */ /* Pull the matching of a packet against a NAT rule out of that complex */ /* loop inside ipf_nat_checkin() and lay it out properly in its own function. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_match(fin, np) - fr_info_t *fin; - ipnat_t *np; +ipf_nat_match(fr_info_t *fin, ipnat_t *np) { ipf_main_softc_t *softc = fin->fin_main_soft; frtuc_t *ft; int match; match = 0; switch (np->in_osrcatype) { case FRI_NORMAL : match = ((fin->fin_saddr & np->in_osrcmsk) != np->in_osrcaddr); break; case FRI_LOOKUP : match = (*np->in_osrcfunc)(softc, np->in_osrcptr, 4, &fin->fin_saddr, fin->fin_plen); break; } match ^= ((np->in_flags & IPN_NOTSRC) != 0); if (match) return 0; match = 0; switch (np->in_odstatype) { case FRI_NORMAL : match = ((fin->fin_daddr & np->in_odstmsk) != np->in_odstaddr); break; case FRI_LOOKUP : match = (*np->in_odstfunc)(softc, np->in_odstptr, 4, &fin->fin_daddr, fin->fin_plen); break; } match ^= ((np->in_flags & IPN_NOTDST) != 0); if (match) return 0; ft = &np->in_tuc; if (!(fin->fin_flx & FI_TCPUDP) || (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { if (ft->ftu_scmp || ft->ftu_dcmp) return 0; return 1; } return ipf_tcpudpchk(&fin->fin_fi, ft); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_update */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* */ /* Updates the lifetime of a NAT table entry for non-TCP packets. Must be */ /* called with fin_rev updated - i.e. after calling ipf_nat_proto(). */ /* */ /* This *MUST* be called after ipf_nat_proto() as it expects fin_rev to */ /* already be set. */ /* ------------------------------------------------------------------------ */ void -ipf_nat_update(fin, nat) - fr_info_t *fin; - nat_t *nat; +ipf_nat_update(fr_info_t *fin, nat_t *nat) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipftq_t *ifq, *ifq2; ipftqent_t *tqe; ipnat_t *np = nat->nat_ptr; tqe = &nat->nat_tqe; ifq = tqe->tqe_ifq; /* * We allow over-riding of NAT timeouts from NAT rules, even for * TCP, however, if it is TCP and there is no rule timeout set, * then do not update the timeout here. */ if (np != NULL) { np->in_bytes[fin->fin_rev] += fin->fin_plen; ifq2 = np->in_tqehead[fin->fin_rev]; } else { ifq2 = NULL; } if (nat->nat_pr[0] == IPPROTO_TCP && ifq2 == NULL) { (void) ipf_tcp_age(&nat->nat_tqe, fin, softn->ipf_nat_tcptq, 0, 2); } else { if (ifq2 == NULL) { if (nat->nat_pr[0] == IPPROTO_UDP) ifq2 = fin->fin_rev ? &softn->ipf_nat_udpacktq : &softn->ipf_nat_udptq; else if (nat->nat_pr[0] == IPPROTO_ICMP || nat->nat_pr[0] == IPPROTO_ICMPV6) ifq2 = fin->fin_rev ? &softn->ipf_nat_icmpacktq: &softn->ipf_nat_icmptq; else ifq2 = &softn->ipf_nat_iptq; } ipf_movequeue(softc->ipf_ticks, tqe, ifq, ifq2); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_checkout */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 0 == no packet translation occurred, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(I) - pointer to filtering result flags */ /* */ /* Check to see if an outcoming packet should be changed. ICMP packets are */ /* first checked to see if they match an existing entry (if an error), */ /* otherwise a search of the current NAT table is made. If neither results */ /* in a match then a search for a matching NAT rule is made. Create a new */ /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ /* packet header(s) as required. */ /* ------------------------------------------------------------------------ */ int -ipf_nat_checkout(fin, passp) - fr_info_t *fin; - u_32_t *passp; +ipf_nat_checkout(fr_info_t *fin, u_32_t *passp) { ipnat_t *np = NULL, *npnext; struct ifnet *ifp, *sifp; ipf_main_softc_t *softc; ipf_nat_softc_t *softn; icmphdr_t *icmp = NULL; tcphdr_t *tcp = NULL; int rval, natfailed; u_int nflags = 0; u_32_t ipa, iph; int natadd = 1; frentry_t *fr; nat_t *nat; if (fin->fin_v == 6) { #ifdef USE_INET6 return ipf_nat6_checkout(fin, passp); #else return 0; #endif } softc = fin->fin_main_soft; softn = softc->ipf_nat_soft; if (softn->ipf_nat_lock != 0) return 0; if (softn->ipf_nat_stats.ns_rules == 0 && softn->ipf_nat_instances == NULL) return 0; natfailed = 0; fr = fin->fin_fr; sifp = fin->fin_ifp; if (fr != NULL) { ifp = fr->fr_tifs[fin->fin_rev].fd_ptr; if ((ifp != NULL) && (ifp != (void *)-1)) fin->fin_ifp = ifp; } ifp = fin->fin_ifp; if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { switch (fin->fin_p) { case IPPROTO_TCP : nflags = IPN_TCP; break; case IPPROTO_UDP : nflags = IPN_UDP; break; case IPPROTO_ICMP : icmp = fin->fin_dp; /* * This is an incoming packet, so the destination is * the icmp_id and the source port equals 0 */ if ((fin->fin_flx & FI_ICMPQUERY) != 0) nflags = IPN_ICMPQUERY; break; default : break; } if ((nflags & IPN_TCPUDP)) tcp = fin->fin_dp; } ipa = fin->fin_saddr; READ_ENTER(&softc->ipf_nat); if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && (nat = ipf_nat_icmperror(fin, &nflags, NAT_OUTBOUND))) /*EMPTY*/; else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) natadd = 0; else if ((nat = ipf_nat_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, fin->fin_src, fin->fin_dst))) { nflags = nat->nat_flags; } else if (fin->fin_off == 0) { u_32_t hv, msk, nmsk = 0; /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ maskloop: msk = softn->ipf_nat_map_active_masks[nmsk]; iph = ipa & msk; hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_maprules_sz); retry_roundrobin: for (np = softn->ipf_nat_map_rules[hv]; np; np = npnext) { npnext = np->in_mnext; if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) continue; if (np->in_v[0] != 4) continue; if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p)) continue; if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) continue; if (np->in_flags & IPN_FILTER) { switch (ipf_nat_match(fin, np)) { case 0 : continue; case -1 : rval = -3; goto outmatchfail; case 1 : default : break; } } else if ((ipa & np->in_osrcmsk) != np->in_osrcaddr) continue; if ((fr != NULL) && !ipf_matchtag(&np->in_tag, &fr->fr_nattag)) continue; if (np->in_plabel != -1) { if (((np->in_flags & IPN_FILTER) == 0) && (np->in_odport != fin->fin_data[1])) continue; if (ipf_proxy_ok(fin, tcp, np) == 0) continue; } if (np->in_flags & IPN_NO) { np->in_hits++; break; } MUTEX_ENTER(&softn->ipf_nat_new); /* * If we've matched a round-robin rule but it has * moved in the list since we got it, start over as * this is now no longer correct. */ if (npnext != np->in_mnext) { if ((np->in_flags & IPN_ROUNDR) != 0) { MUTEX_EXIT(&softn->ipf_nat_new); goto retry_roundrobin; } npnext = np->in_mnext; } nat = ipf_nat_add(fin, np, NULL, nflags, NAT_OUTBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (nat != NULL) { natfailed = 0; break; } natfailed = -2; } if ((np == NULL) && (nmsk < softn->ipf_nat_map_max)) { nmsk++; goto maskloop; } } if (nat != NULL) { rval = ipf_nat_out(fin, nat, natadd, nflags); if (rval == 1) { MUTEX_ENTER(&nat->nat_lock); ipf_nat_update(fin, nat); nat->nat_bytes[1] += fin->fin_plen; nat->nat_pkts[1]++; fin->fin_pktnum = nat->nat_pkts[1]; MUTEX_EXIT(&nat->nat_lock); } } else rval = natfailed; outmatchfail: RWLOCK_EXIT(&softc->ipf_nat); switch (rval) { case -3 : /* ipf_nat_match() failure */ /* FALLTHROUGH */ case -2 : /* retry_roundrobin loop failure */ /* FALLTHROUGH */ case -1 : /* proxy failure detected by ipf_nat_out() */ if (passp != NULL) { DT2(frb_natv4out, fr_info_t *, fin, int, rval); NBUMPSIDED(1, ns_drop); *passp = FR_BLOCK; fin->fin_reason = FRB_NATV4; } fin->fin_flx |= FI_BADNAT; NBUMPSIDED(1, ns_badnat); rval = -1; /* We only return -1 on error. */ break; case 0 : NBUMPSIDE(1, ns_ignored); break; case 1 : NBUMPSIDE(1, ns_translated); break; } fin->fin_ifp = sifp; return rval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_out */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* natadd(I) - flag indicating if it is safe to add frag cache */ /* nflags(I) - NAT flags set for this packet */ /* */ /* Translate a packet coming "out" on an interface. */ /* ------------------------------------------------------------------------ */ int -ipf_nat_out(fin, nat, natadd, nflags) - fr_info_t *fin; - nat_t *nat; - int natadd; - u_32_t nflags; +ipf_nat_out(fr_info_t *fin, nat_t *nat, int natadd, u_32_t nflags) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; icmphdr_t *icmp; tcphdr_t *tcp; ipnat_t *np; int skip; int i; tcp = NULL; icmp = NULL; np = nat->nat_ptr; if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) (void) ipf_frag_natnew(softc, fin, 0, nat); /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. * This is only done for STREAMS based IP implementations where the * checksum has already been calculated by IP. In all other cases, * IPFilter is called before the checksum needs calculating so there * is no call to modify whatever is in the header now. */ if (nflags == IPN_ICMPERR) { u_32_t s1, s2, sumd, msumd; s1 = LONG_SUM(ntohl(fin->fin_saddr)); if (nat->nat_dir == NAT_OUTBOUND) { s2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); } else { s2 = LONG_SUM(ntohl(nat->nat_odstaddr)); } CALC_SUMD(s1, s2, sumd); msumd = sumd; s1 = LONG_SUM(ntohl(fin->fin_daddr)); if (nat->nat_dir == NAT_OUTBOUND) { s2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); } else { s2 = LONG_SUM(ntohl(nat->nat_osrcaddr)); } CALC_SUMD(s1, s2, sumd); msumd += sumd; ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, msumd, 0); } #if !defined(_KERNEL) || SOLARIS || \ defined(BRIDGE_IPF) || defined(__FreeBSD__) else { /* * We always do this on FreeBSD because this code doesn't * exist in fastforward. */ switch (nat->nat_dir) { case NAT_OUTBOUND : ipf_fix_outcksum(fin->fin_cksum & FI_CK_L4PART, &fin->fin_ip->ip_sum, nat->nat_ipsumd, 0); break; case NAT_INBOUND : ipf_fix_incksum(fin->fin_cksum & FI_CK_L4PART, &fin->fin_ip->ip_sum, nat->nat_ipsumd, 0); break; default : break; } } #endif /* * Address assignment is after the checksum modification because * we are using the address in the packet for determining the * correct checksum offset (the ICMP error could be coming from * anyone...) */ switch (nat->nat_dir) { case NAT_OUTBOUND : fin->fin_ip->ip_src = nat->nat_nsrcip; fin->fin_saddr = nat->nat_nsrcaddr; fin->fin_ip->ip_dst = nat->nat_ndstip; fin->fin_daddr = nat->nat_ndstaddr; break; case NAT_INBOUND : fin->fin_ip->ip_src = nat->nat_odstip; fin->fin_saddr = nat->nat_ndstaddr; fin->fin_ip->ip_dst = nat->nat_osrcip; fin->fin_daddr = nat->nat_nsrcaddr; break; case NAT_DIVERTIN : { mb_t *m; skip = ipf_nat_decap(fin, nat); if (skip <= 0) { NBUMPSIDED(1, ns_decap_fail); return -1; } m = fin->fin_m; #if SOLARIS && defined(_KERNEL) m->b_rptr += skip; #else m->m_data += skip; m->m_len -= skip; # ifdef M_PKTHDR if (m->m_flags & M_PKTHDR) m->m_pkthdr.len -= skip; # endif #endif MUTEX_ENTER(&nat->nat_lock); ipf_nat_update(fin, nat); MUTEX_EXIT(&nat->nat_lock); fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; return 1; /* NOTREACHED */ } case NAT_DIVERTOUT : { u_32_t s1, s2, sumd; udphdr_t *uh; ip_t *ip; mb_t *m; m = M_DUP(np->in_divmp); if (m == NULL) { NBUMPSIDED(1, ns_divert_dup); return -1; } ip = MTOD(m, ip_t *); ip_fillid(ip); s2 = ntohs(ip->ip_id); s1 = ip->ip_len; ip->ip_len = ntohs(ip->ip_len); ip->ip_len += fin->fin_plen; ip->ip_len = htons(ip->ip_len); s2 += ntohs(ip->ip_len); CALC_SUMD(s1, s2, sumd); uh = (udphdr_t *)(ip + 1); uh->uh_ulen += fin->fin_plen; uh->uh_ulen = htons(uh->uh_ulen); #if !defined(_KERNEL) || SOLARIS || \ defined(BRIDGE_IPF) || defined(__FreeBSD__) ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); #endif PREP_MB_T(fin, m); fin->fin_src = ip->ip_src; fin->fin_dst = ip->ip_dst; fin->fin_ip = ip; fin->fin_plen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ nflags &= ~IPN_TCPUDPICMP; break; } default : break; } if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { u_short *csump; if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) { tcp = fin->fin_dp; switch (nat->nat_dir) { case NAT_OUTBOUND : tcp->th_sport = nat->nat_nsport; fin->fin_data[0] = ntohs(nat->nat_nsport); tcp->th_dport = nat->nat_ndport; fin->fin_data[1] = ntohs(nat->nat_ndport); break; case NAT_INBOUND : tcp->th_sport = nat->nat_odport; fin->fin_data[0] = ntohs(nat->nat_odport); tcp->th_dport = nat->nat_osport; fin->fin_data[1] = ntohs(nat->nat_osport); break; } } if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) { icmp = fin->fin_dp; icmp->icmp_id = nat->nat_nicmpid; } csump = ipf_nat_proto(fin, nat, nflags); /* * The above comments do not hold for layer 4 (or higher) * checksums... */ if (csump != NULL) { if (nat->nat_dir == NAT_OUTBOUND) ipf_fix_outcksum(fin->fin_cksum, csump, nat->nat_sumd[0], nat->nat_sumd[1] + fin->fin_dlen); else ipf_fix_incksum(fin->fin_cksum, csump, nat->nat_sumd[0], nat->nat_sumd[1] + fin->fin_dlen); } } ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); /* ------------------------------------------------------------- */ /* A few quick notes: */ /* Following are test conditions prior to calling the */ /* ipf_proxy_check routine. */ /* */ /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ /* with a redirect rule, we attempt to match the packet's */ /* source port against in_dport, otherwise we'd compare the */ /* packet's destination. */ /* ------------------------------------------------------------- */ if ((np != NULL) && (np->in_apr != NULL)) { i = ipf_proxy_check(fin, nat); if (i == -1) { NBUMPSIDED(1, ns_ipf_proxy_fail); } } else { i = 1; } fin->fin_flx |= FI_NATED; return i; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_checkin */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 0 == no packet translation occurred, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(I) - pointer to filtering result flags */ /* */ /* Check to see if an incoming packet should be changed. ICMP packets are */ /* first checked to see if they match an existing entry (if an error), */ /* otherwise a search of the current NAT table is made. If neither results */ /* in a match then a search for a matching NAT rule is made. Create a new */ /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ /* packet header(s) as required. */ /* ------------------------------------------------------------------------ */ int -ipf_nat_checkin(fin, passp) - fr_info_t *fin; - u_32_t *passp; +ipf_nat_checkin(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc; ipf_nat_softc_t *softn; u_int nflags, natadd; ipnat_t *np, *npnext; int rval, natfailed; struct ifnet *ifp; struct in_addr in; icmphdr_t *icmp; tcphdr_t *tcp; u_short dport; nat_t *nat; u_32_t iph; softc = fin->fin_main_soft; softn = softc->ipf_nat_soft; if (softn->ipf_nat_lock != 0) return 0; if (softn->ipf_nat_stats.ns_rules == 0 && softn->ipf_nat_instances == NULL) return 0; tcp = NULL; icmp = NULL; dport = 0; natadd = 1; nflags = 0; natfailed = 0; ifp = fin->fin_ifp; if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { switch (fin->fin_p) { case IPPROTO_TCP : nflags = IPN_TCP; break; case IPPROTO_UDP : nflags = IPN_UDP; break; case IPPROTO_ICMP : icmp = fin->fin_dp; /* * This is an incoming packet, so the destination is * the icmp_id and the source port equals 0 */ if ((fin->fin_flx & FI_ICMPQUERY) != 0) { nflags = IPN_ICMPQUERY; dport = icmp->icmp_id; } break; default : break; } if ((nflags & IPN_TCPUDP)) { tcp = fin->fin_dp; dport = fin->fin_data[1]; } } in = fin->fin_dst; READ_ENTER(&softc->ipf_nat); if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && (nat = ipf_nat_icmperror(fin, &nflags, NAT_INBOUND))) /*EMPTY*/; else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) natadd = 0; else if ((nat = ipf_nat_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, fin->fin_src, in))) { nflags = nat->nat_flags; } else if (fin->fin_off == 0) { u_32_t hv, msk, rmsk = 0; /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ maskloop: msk = softn->ipf_nat_rdr_active_masks[rmsk]; iph = in.s_addr & msk; hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_rdrrules_sz); retry_roundrobin: /* TRACE (iph,msk,rmsk,hv,softn->ipf_nat_rdrrules_sz) */ for (np = softn->ipf_nat_rdr_rules[hv]; np; np = npnext) { npnext = np->in_rnext; if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) continue; if (np->in_v[0] != 4) continue; if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p)) continue; if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) continue; if (np->in_flags & IPN_FILTER) { switch (ipf_nat_match(fin, np)) { case 0 : continue; case -1 : rval = -3; goto inmatchfail; case 1 : default : break; } } else { if ((in.s_addr & np->in_odstmsk) != np->in_odstaddr) continue; if (np->in_odport && ((np->in_dtop < dport) || (dport < np->in_odport))) continue; } if (np->in_plabel != -1) { if (!ipf_proxy_ok(fin, tcp, np)) { continue; } } if (np->in_flags & IPN_NO) { np->in_hits++; break; } MUTEX_ENTER(&softn->ipf_nat_new); /* * If we've matched a round-robin rule but it has * moved in the list since we got it, start over as * this is now no longer correct. */ if (npnext != np->in_rnext) { if ((np->in_flags & IPN_ROUNDR) != 0) { MUTEX_EXIT(&softn->ipf_nat_new); goto retry_roundrobin; } npnext = np->in_rnext; } nat = ipf_nat_add(fin, np, NULL, nflags, NAT_INBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (nat != NULL) { natfailed = 0; break; } natfailed = -2; } if ((np == NULL) && (rmsk < softn->ipf_nat_rdr_max)) { rmsk++; goto maskloop; } } if (nat != NULL) { rval = ipf_nat_in(fin, nat, natadd, nflags); if (rval == 1) { MUTEX_ENTER(&nat->nat_lock); ipf_nat_update(fin, nat); nat->nat_bytes[0] += fin->fin_plen; nat->nat_pkts[0]++; fin->fin_pktnum = nat->nat_pkts[0]; MUTEX_EXIT(&nat->nat_lock); } } else rval = natfailed; inmatchfail: RWLOCK_EXIT(&softc->ipf_nat); DT2(frb_natv4in, fr_info_t *, fin, int, rval); switch (rval) { case -3 : /* ipf_nat_match() failure */ /* FALLTHROUGH */ case -2 : /* retry_roundrobin loop failure */ /* FALLTHROUGH */ case -1 : /* proxy failure detected by ipf_nat_in() */ if (passp != NULL) { NBUMPSIDED(0, ns_drop); *passp = FR_BLOCK; fin->fin_reason = FRB_NATV4; } fin->fin_flx |= FI_BADNAT; NBUMPSIDED(0, ns_badnat); rval = -1; /* We only return -1 on error. */ break; case 0 : NBUMPSIDE(0, ns_ignored); break; case 1 : NBUMPSIDE(0, ns_translated); break; } return rval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_in */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* natadd(I) - flag indicating if it is safe to add frag cache */ /* nflags(I) - NAT flags set for this packet */ /* Locks Held: ipf_nat(READ) */ /* */ /* Translate a packet coming "in" on an interface. */ /* ------------------------------------------------------------------------ */ int -ipf_nat_in(fin, nat, natadd, nflags) - fr_info_t *fin; - nat_t *nat; - int natadd; - u_32_t nflags; +ipf_nat_in(fr_info_t *fin, nat_t *nat, int natadd, u_32_t nflags) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sumd, ipsumd, sum1, sum2; icmphdr_t *icmp; tcphdr_t *tcp; ipnat_t *np; int skip; int i; tcp = NULL; np = nat->nat_ptr; fin->fin_fr = nat->nat_fr; if (np != NULL) { if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) (void) ipf_frag_natnew(softc, fin, 0, nat); /* ------------------------------------------------------------- */ /* A few quick notes: */ /* Following are test conditions prior to calling the */ /* ipf_proxy_check routine. */ /* */ /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ /* with a map rule, we attempt to match the packet's */ /* source port against in_dport, otherwise we'd compare the */ /* packet's destination. */ /* ------------------------------------------------------------- */ if (np->in_apr != NULL) { i = ipf_proxy_check(fin, nat); if (i == -1) { NBUMPSIDED(0, ns_ipf_proxy_fail); return -1; } } } ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); ipsumd = nat->nat_ipsumd; /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. * Why only do this for some platforms on inbound packets ? * Because for those that it is done, IP processing is yet to happen * and so the IPv4 header checksum has not yet been evaluated. * Perhaps it should always be done for the benefit of things like * fast forwarding (so that it doesn't need to be recomputed) but with * header checksum offloading, perhaps it is a moot point. */ switch (nat->nat_dir) { case NAT_INBOUND : if ((fin->fin_flx & FI_ICMPERR) == 0) { fin->fin_ip->ip_src = nat->nat_nsrcip; fin->fin_saddr = nat->nat_nsrcaddr; } else { sum1 = nat->nat_osrcaddr; sum2 = nat->nat_nsrcaddr; CALC_SUMD(sum1, sum2, sumd); ipsumd -= sumd; } fin->fin_ip->ip_dst = nat->nat_ndstip; fin->fin_daddr = nat->nat_ndstaddr; #if !defined(_KERNEL) || SOLARIS ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); #endif break; case NAT_OUTBOUND : if ((fin->fin_flx & FI_ICMPERR) == 0) { fin->fin_ip->ip_src = nat->nat_odstip; fin->fin_saddr = nat->nat_odstaddr; } else { sum1 = nat->nat_odstaddr; sum2 = nat->nat_ndstaddr; CALC_SUMD(sum1, sum2, sumd); ipsumd -= sumd; } fin->fin_ip->ip_dst = nat->nat_osrcip; fin->fin_daddr = nat->nat_osrcaddr; #if !defined(_KERNEL) || SOLARIS ipf_fix_incksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); #endif break; case NAT_DIVERTIN : { udphdr_t *uh; ip_t *ip; mb_t *m; m = M_DUP(np->in_divmp); if (m == NULL) { NBUMPSIDED(0, ns_divert_dup); return -1; } ip = MTOD(m, ip_t *); ip_fillid(ip); sum1 = ntohs(ip->ip_len); ip->ip_len = ntohs(ip->ip_len); ip->ip_len += fin->fin_plen; ip->ip_len = htons(ip->ip_len); uh = (udphdr_t *)(ip + 1); uh->uh_ulen += fin->fin_plen; uh->uh_ulen = htons(uh->uh_ulen); sum2 = ntohs(ip->ip_id) + ntohs(ip->ip_len); sum2 += ntohs(ip->ip_off) & IP_DF; CALC_SUMD(sum1, sum2, sumd); #if !defined(_KERNEL) || SOLARIS ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); #endif PREP_MB_T(fin, m); fin->fin_ip = ip; fin->fin_plen += sizeof(ip_t) + 8; /* UDP + new IPv4 hdr */ fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + old IPv4 hdr */ nflags &= ~IPN_TCPUDPICMP; break; } case NAT_DIVERTOUT : { mb_t *m; skip = ipf_nat_decap(fin, nat); if (skip <= 0) { NBUMPSIDED(0, ns_decap_fail); return -1; } m = fin->fin_m; #if SOLARIS && defined(_KERNEL) m->b_rptr += skip; #else m->m_data += skip; m->m_len -= skip; # ifdef M_PKTHDR if (m->m_flags & M_PKTHDR) m->m_pkthdr.len -= skip; # endif #endif ipf_nat_update(fin, nat); nflags &= ~IPN_TCPUDPICMP; fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; return 1; /* NOTREACHED */ } } if (nflags & IPN_TCPUDP) tcp = fin->fin_dp; if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { u_short *csump; if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) { switch (nat->nat_dir) { case NAT_INBOUND : tcp->th_sport = nat->nat_nsport; fin->fin_data[0] = ntohs(nat->nat_nsport); tcp->th_dport = nat->nat_ndport; fin->fin_data[1] = ntohs(nat->nat_ndport); break; case NAT_OUTBOUND : tcp->th_sport = nat->nat_odport; fin->fin_data[0] = ntohs(nat->nat_odport); tcp->th_dport = nat->nat_osport; fin->fin_data[1] = ntohs(nat->nat_osport); break; } } if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) { icmp = fin->fin_dp; icmp->icmp_id = nat->nat_nicmpid; } csump = ipf_nat_proto(fin, nat, nflags); /* * The above comments do not hold for layer 4 (or higher) * checksums... */ if (csump != NULL) { if (nat->nat_dir == NAT_OUTBOUND) ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0); else ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0); } } fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; return 1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_proto */ /* Returns: u_short* - pointer to transport header checksum to update, */ /* NULL if the transport protocol is not recognised */ /* as needing a checksum update. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* nflags(I) - NAT flags set for this packet */ /* */ /* Return the pointer to the checksum field for each protocol so understood.*/ /* If support for making other changes to a protocol header is required, */ /* that is not strictly 'address' translation, such as clamping the MSS in */ /* TCP down to a specific value, then do it from here. */ /* ------------------------------------------------------------------------ */ u_short * -ipf_nat_proto(fin, nat, nflags) - fr_info_t *fin; - nat_t *nat; - u_int nflags; +ipf_nat_proto(fr_info_t *fin, nat_t *nat, u_int nflags) { icmphdr_t *icmp; u_short *csump; tcphdr_t *tcp; udphdr_t *udp; csump = NULL; if (fin->fin_out == 0) { fin->fin_rev = (nat->nat_dir & NAT_OUTBOUND); } else { fin->fin_rev = ((nat->nat_dir & NAT_OUTBOUND) == 0); } switch (fin->fin_p) { case IPPROTO_TCP : tcp = fin->fin_dp; if ((nflags & IPN_TCP) != 0) csump = &tcp->th_sum; /* * Do a MSS CLAMPING on a SYN packet, * only deal IPv4 for now. */ if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0) ipf_nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump); break; case IPPROTO_UDP : udp = fin->fin_dp; if ((nflags & IPN_UDP) != 0) { if (udp->uh_sum != 0) csump = &udp->uh_sum; } break; case IPPROTO_ICMP : icmp = fin->fin_dp; if ((nflags & IPN_ICMPQUERY) != 0) { if (icmp->icmp_cksum != 0) csump = &icmp->icmp_cksum; } break; #ifdef USE_INET6 case IPPROTO_ICMPV6 : { struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)fin->fin_dp; icmp6 = fin->fin_dp; if ((nflags & IPN_ICMPQUERY) != 0) { if (icmp6->icmp6_cksum != 0) csump = &icmp6->icmp6_cksum; } break; } #endif } return csump; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Check all of the timeout queues for entries at the top which need to be */ /* expired. */ /* ------------------------------------------------------------------------ */ void -ipf_nat_expire(softc) - ipf_main_softc_t *softc; +ipf_nat_expire(ipf_main_softc_t *softc) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipftq_t *ifq, *ifqnext; ipftqent_t *tqe, *tqn; int i; SPL_INT(s); SPL_NET(s); WRITE_ENTER(&softc->ipf_nat); for (ifq = softn->ipf_nat_tcptq, i = 0; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); } } for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); } } for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; if (((ifq->ifq_flags & IFQF_DELETE) != 0) && (ifq->ifq_ref == 0)) { ipf_freetimeoutqueue(softc, ifq); } } if (softn->ipf_nat_doflush != 0) { ipf_nat_extraflush(softc, softn, 2); softn->ipf_nat_doflush = 0; } RWLOCK_EXIT(&softc->ipf_nat); SPL_X(s); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_sync */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* ifp(I) - pointer to network interface */ /* */ /* Walk through all of the currently active NAT sessions, looking for those */ /* which need to have their translated address updated. */ /* ------------------------------------------------------------------------ */ void -ipf_nat_sync(softc, ifp) - ipf_main_softc_t *softc; - void *ifp; +ipf_nat_sync(ipf_main_softc_t *softc, void *ifp) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sum1, sum2, sumd; i6addr_t in; ipnat_t *n; nat_t *nat; void *ifp2; int idx; SPL_INT(s); if (softc->ipf_running <= 0) return; /* * Change IP addresses for NAT sessions for any protocol except TCP * since it will break the TCP connection anyway. The only rules * which will get changed are those which are "map ... -> 0/32", * where the rule specifies the address is taken from the interface. */ SPL_NET(s); WRITE_ENTER(&softc->ipf_nat); if (softc->ipf_running <= 0) { RWLOCK_EXIT(&softc->ipf_nat); return; } for (nat = softn->ipf_nat_instances; nat; nat = nat->nat_next) { if ((nat->nat_flags & IPN_TCP) != 0) continue; n = nat->nat_ptr; if (n != NULL) { if (n->in_v[1] == 4) { if (n->in_redir & NAT_MAP) { if ((n->in_nsrcaddr != 0) || (n->in_nsrcmsk != 0xffffffff)) continue; } else if (n->in_redir & NAT_REDIRECT) { if ((n->in_ndstaddr != 0) || (n->in_ndstmsk != 0xffffffff)) continue; } } #ifdef USE_INET6 if (n->in_v[1] == 4) { if (n->in_redir & NAT_MAP) { if (!IP6_ISZERO(&n->in_nsrcaddr) || !IP6_ISONES(&n->in_nsrcmsk)) continue; } else if (n->in_redir & NAT_REDIRECT) { if (!IP6_ISZERO(&n->in_ndstaddr) || !IP6_ISONES(&n->in_ndstmsk)) continue; } } #endif } if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) || (ifp == nat->nat_ifps[1]))) { nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0], nat->nat_v[0]); if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); } if (nat->nat_ifnames[1][0] != '\0') { nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1], nat->nat_v[1]); } else { nat->nat_ifps[1] = nat->nat_ifps[0]; } if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); } ifp2 = nat->nat_ifps[0]; if (ifp2 == NULL) continue; /* * Change the map-to address to be the same as the * new one. */ sum1 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); if (ipf_ifpaddr(softc, nat->nat_v[0], FRI_NORMAL, ifp2, &in, NULL) != -1) { if (nat->nat_v[0] == 4) nat->nat_nsrcip = in.in4; } sum2 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); if (sum1 == sum2) continue; /* * Readjust the checksum adjustment to take into * account the new IP#. */ CALC_SUMD(sum1, sum2, sumd); /* XXX - dont change for TCP when solaris does * hardware checksumming. */ sumd += nat->nat_sumd[0]; nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); nat->nat_sumd[1] = nat->nat_sumd[0]; } } for (n = softn->ipf_nat_list; (n != NULL); n = n->in_next) { char *base = n->in_names; if ((ifp == NULL) || (n->in_ifps[0] == ifp)) n->in_ifps[0] = ipf_resolvenic(softc, base + n->in_ifnames[0], n->in_v[0]); if ((ifp == NULL) || (n->in_ifps[1] == ifp)) n->in_ifps[1] = ipf_resolvenic(softc, base + n->in_ifnames[1], n->in_v[1]); if (n->in_redir & NAT_REDIRECT) idx = 1; else idx = 0; if (((ifp == NULL) || (n->in_ifps[idx] == ifp)) && (n->in_ifps[idx] != NULL && n->in_ifps[idx] != (void *)-1)) { ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 0, n->in_ifps[idx]); ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 0, n->in_ifps[idx]); ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 0, n->in_ifps[idx]); ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 0, n->in_ifps[idx]); } } RWLOCK_EXIT(&softc->ipf_nat); SPL_X(s); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_icmpquerytype */ /* Returns: int - 1 == success, 0 == failure */ /* Parameters: icmptype(I) - ICMP type number */ /* */ /* Tests to see if the ICMP type number passed is a query/response type or */ /* not. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_icmpquerytype(icmptype) - int icmptype; +ipf_nat_icmpquerytype(int icmptype) { /* * For the ICMP query NAT code, it is essential that both the query * and the reply match on the NAT rule. Because the NAT structure * does not keep track of the icmptype, and a single NAT structure * is used for all icmp types with the same src, dest and id, we * simply define the replies as queries as well. The funny thing is, * altough it seems silly to call a reply a query, this is exactly * as it is defined in the IPv4 specification */ switch (icmptype) { case ICMP_ECHOREPLY: case ICMP_ECHO: /* route advertisement/solicitation is currently unsupported: */ /* it would require rewriting the ICMP data section */ case ICMP_TSTAMP: case ICMP_TSTAMPREPLY: case ICMP_IREQ: case ICMP_IREQREPLY: case ICMP_MASKREQ: case ICMP_MASKREPLY: return 1; default: return 0; } } /* ------------------------------------------------------------------------ */ /* Function: nat_log */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to NAT structure */ /* action(I) - action related to NAT structure being performed */ /* */ /* Creates a NAT log entry. */ /* ------------------------------------------------------------------------ */ void -ipf_nat_log(softc, softn, nat, action) - ipf_main_softc_t *softc; - ipf_nat_softc_t *softn; - struct nat *nat; - u_int action; +ipf_nat_log(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, struct nat *nat, + u_int action) { #ifdef IPFILTER_LOG struct ipnat *np; int rulen; struct natlog natl; void *items[1]; size_t sizes[1]; int types[1]; bcopy((char *)&nat->nat_osrc6, (char *)&natl.nl_osrcip, sizeof(natl.nl_osrcip)); bcopy((char *)&nat->nat_nsrc6, (char *)&natl.nl_nsrcip, sizeof(natl.nl_nsrcip)); bcopy((char *)&nat->nat_odst6, (char *)&natl.nl_odstip, sizeof(natl.nl_odstip)); bcopy((char *)&nat->nat_ndst6, (char *)&natl.nl_ndstip, sizeof(natl.nl_ndstip)); natl.nl_bytes[0] = nat->nat_bytes[0]; natl.nl_bytes[1] = nat->nat_bytes[1]; natl.nl_pkts[0] = nat->nat_pkts[0]; natl.nl_pkts[1] = nat->nat_pkts[1]; natl.nl_odstport = nat->nat_odport; natl.nl_osrcport = nat->nat_osport; natl.nl_nsrcport = nat->nat_nsport; natl.nl_ndstport = nat->nat_ndport; natl.nl_p[0] = nat->nat_pr[0]; natl.nl_p[1] = nat->nat_pr[1]; natl.nl_v[0] = nat->nat_v[0]; natl.nl_v[1] = nat->nat_v[1]; natl.nl_type = nat->nat_redir; natl.nl_action = action; natl.nl_rule = -1; bcopy(nat->nat_ifnames[0], natl.nl_ifnames[0], sizeof(nat->nat_ifnames[0])); bcopy(nat->nat_ifnames[1], natl.nl_ifnames[1], sizeof(nat->nat_ifnames[1])); if (softc->ipf_large_nat && nat->nat_ptr != NULL) { for (rulen = 0, np = softn->ipf_nat_list; np != NULL; np = np->in_next, rulen++) if (np == nat->nat_ptr) { natl.nl_rule = rulen; break; } } items[0] = &natl; sizes[0] = sizeof(natl); types[0] = 0; (void) ipf_log_items(softc, IPL_LOGNAT, NULL, items, sizes, types, 1); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_rule_deref */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* inp(I) - pointer to pointer to NAT rule */ /* Write Locks: ipf_nat */ /* */ /* Dropping the refernce count for a rule means that whatever held the */ /* pointer to this rule (*inp) is no longer interested in it and when the */ /* reference count drops to zero, any resources allocated for the rule can */ /* be released and the rule itself free'd. */ /* ------------------------------------------------------------------------ */ void -ipf_nat_rule_deref(softc, inp) - ipf_main_softc_t *softc; - ipnat_t **inp; +ipf_nat_rule_deref(ipf_main_softc_t *softc, ipnat_t **inp) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipnat_t *n; n = *inp; *inp = NULL; n->in_use--; if (n->in_use > 0) return; if (n->in_apr != NULL) ipf_proxy_deref(n->in_apr); ipf_nat_rule_fini(softc, n); if (n->in_redir & NAT_REDIRECT) { if ((n->in_flags & IPN_PROXYRULE) == 0) { ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_rdr); } } if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { if ((n->in_flags & IPN_PROXYRULE) == 0) { ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_map); } } if (n->in_tqehead[0] != NULL) { if (ipf_deletetimeoutqueue(n->in_tqehead[0]) == 0) { ipf_freetimeoutqueue(softc, n->in_tqehead[0]); } } if (n->in_tqehead[1] != NULL) { if (ipf_deletetimeoutqueue(n->in_tqehead[1]) == 0) { ipf_freetimeoutqueue(softc, n->in_tqehead[1]); } } if ((n->in_flags & IPN_PROXYRULE) == 0) { ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules); } MUTEX_DESTROY(&n->in_lock); KFREES(n, n->in_size); #if SOLARIS && !defined(INSTANCES) if (softn->ipf_nat_stats.ns_rules == 0) pfil_delayed_copy = 1; #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_deref */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* natp(I) - pointer to pointer to NAT table entry */ /* */ /* Decrement the reference counter for this NAT table entry and free it if */ /* there are no more things using it. */ /* */ /* IF nat_ref == 1 when this function is called, then we have an orphan nat */ /* structure *because* it only gets called on paths _after_ nat_ref has been*/ /* incremented. If nat_ref == 1 then we shouldn't decrement it here */ /* because nat_delete() will do that and send nat_ref to -1. */ /* */ /* Holding the lock on nat_lock is required to serialise nat_delete() being */ /* called from a NAT flush ioctl with a deref happening because of a packet.*/ /* ------------------------------------------------------------------------ */ void -ipf_nat_deref(softc, natp) - ipf_main_softc_t *softc; - nat_t **natp; +ipf_nat_deref(ipf_main_softc_t *softc, nat_t **natp) { nat_t *nat; nat = *natp; *natp = NULL; MUTEX_ENTER(&nat->nat_lock); if (nat->nat_ref > 1) { nat->nat_ref--; ASSERT(nat->nat_ref >= 0); MUTEX_EXIT(&nat->nat_lock); return; } MUTEX_EXIT(&nat->nat_lock); WRITE_ENTER(&softc->ipf_nat); ipf_nat_delete(softc, nat, NL_EXPIRE); RWLOCK_EXIT(&softc->ipf_nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_clone */ /* Returns: ipstate_t* - NULL == cloning failed, */ /* else pointer to new state structure */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to master state structure */ /* Write Lock: ipf_nat */ /* */ /* Create a "duplcate" state table entry from the master. */ /* ------------------------------------------------------------------------ */ nat_t * -ipf_nat_clone(fin, nat) - fr_info_t *fin; - nat_t *nat; +ipf_nat_clone(fr_info_t *fin, nat_t *nat) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; frentry_t *fr; nat_t *clone; ipnat_t *np; KMALLOC(clone, nat_t *); if (clone == NULL) { NBUMPSIDED(fin->fin_out, ns_clone_nomem); return NULL; } bcopy((char *)nat, (char *)clone, sizeof(*clone)); MUTEX_NUKE(&clone->nat_lock); clone->nat_rev = fin->fin_rev; clone->nat_aps = NULL; /* * Initialize all these so that ipf_nat_delete() doesn't cause a crash. */ clone->nat_tqe.tqe_pnext = NULL; clone->nat_tqe.tqe_next = NULL; clone->nat_tqe.tqe_ifq = NULL; clone->nat_tqe.tqe_parent = clone; clone->nat_flags &= ~SI_CLONE; clone->nat_flags |= SI_CLONED; if (clone->nat_hm) clone->nat_hm->hm_ref++; if (ipf_nat_insert(softc, softn, clone) == -1) { KFREE(clone); NBUMPSIDED(fin->fin_out, ns_insert_fail); return NULL; } np = clone->nat_ptr; if (np != NULL) { if (softn->ipf_nat_logging) ipf_nat_log(softc, softn, clone, NL_CLONE); np->in_use++; } fr = clone->nat_fr; if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } /* * Because the clone is created outside the normal loop of things and * TCP has special needs in terms of state, initialise the timeout * state of the new NAT from here. */ if (clone->nat_pr[0] == IPPROTO_TCP) { (void) ipf_tcp_age(&clone->nat_tqe, fin, softn->ipf_nat_tcptq, clone->nat_flags, 2); } clone->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, clone); if (softn->ipf_nat_logging) ipf_nat_log(softc, softn, clone, NL_CLONE); return clone; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_wildok */ /* Returns: int - 1 == packet's ports match wildcards */ /* 0 == packet's ports don't match wildcards */ /* Parameters: nat(I) - NAT entry */ /* sport(I) - source port */ /* dport(I) - destination port */ /* flags(I) - wildcard flags */ /* dir(I) - packet direction */ /* */ /* Use NAT entry and packet direction to determine which combination of */ /* wildcard flags should be used. */ /* ------------------------------------------------------------------------ */ int -ipf_nat_wildok(nat, sport, dport, flags, dir) - nat_t *nat; - int sport, dport, flags, dir; +ipf_nat_wildok(nat_t *nat, int sport, int dport, int flags, int dir) { /* * When called by dir is set to * nat_inlookup NAT_INBOUND (0) * nat_outlookup NAT_OUTBOUND (1) * * We simply combine the packet's direction in dir with the original * "intended" direction of that NAT entry in nat->nat_dir to decide * which combination of wildcard flags to allow. */ switch ((dir << 1) | (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))) { case 3: /* outbound packet / outbound entry */ if (((nat->nat_osport == sport) || (flags & SI_W_SPORT)) && ((nat->nat_odport == dport) || (flags & SI_W_DPORT))) return 1; break; case 2: /* outbound packet / inbound entry */ if (((nat->nat_osport == dport) || (flags & SI_W_SPORT)) && ((nat->nat_odport == sport) || (flags & SI_W_DPORT))) return 1; break; case 1: /* inbound packet / outbound entry */ if (((nat->nat_osport == dport) || (flags & SI_W_SPORT)) && ((nat->nat_odport == sport) || (flags & SI_W_DPORT))) return 1; break; case 0: /* inbound packet / inbound entry */ if (((nat->nat_osport == sport) || (flags & SI_W_SPORT)) && ((nat->nat_odport == dport) || (flags & SI_W_DPORT))) return 1; break; default: break; } return(0); } /* ------------------------------------------------------------------------ */ /* Function: nat_mssclamp */ /* Returns: Nil */ /* Parameters: tcp(I) - pointer to TCP header */ /* maxmss(I) - value to clamp the TCP MSS to */ /* fin(I) - pointer to packet information */ /* csump(I) - pointer to TCP checksum */ /* */ /* Check for MSS option and clamp it if necessary. If found and changed, */ /* then the TCP header checksum will be updated to reflect the change in */ /* the MSS. */ /* ------------------------------------------------------------------------ */ static void -ipf_nat_mssclamp(tcp, maxmss, fin, csump) - tcphdr_t *tcp; - u_32_t maxmss; - fr_info_t *fin; - u_short *csump; +ipf_nat_mssclamp(tcphdr_t *tcp, u_32_t maxmss, fr_info_t *fin, u_short *csump) { u_char *cp, *ep, opt; int hlen, advance; u_32_t mss, sumd; hlen = TCP_OFF(tcp) << 2; if (hlen > sizeof(*tcp)) { cp = (u_char *)tcp + sizeof(*tcp); ep = (u_char *)tcp + hlen; while (cp < ep) { opt = cp[0]; if (opt == TCPOPT_EOL) break; else if (opt == TCPOPT_NOP) { cp++; continue; } if (cp + 1 >= ep) break; advance = cp[1]; if ((cp + advance > ep) || (advance <= 0)) break; switch (opt) { case TCPOPT_MAXSEG: if (advance != 4) break; mss = cp[2] * 256 + cp[3]; if (mss > maxmss) { cp[2] = maxmss / 256; cp[3] = maxmss & 0xff; CALC_SUMD(mss, maxmss, sumd); ipf_fix_outcksum(0, csump, sumd, 0); } break; default: /* ignore unknown options */ break; } cp += advance; } } } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_setqueue */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I)- pointer to NAT structure */ /* Locks: ipf_nat (read or write) */ /* */ /* Put the NAT entry on its default queue entry, using rev as a helped in */ /* determining which queue it should be placed on. */ /* ------------------------------------------------------------------------ */ void -ipf_nat_setqueue(softc, softn, nat) - ipf_main_softc_t *softc; - ipf_nat_softc_t *softn; - nat_t *nat; +ipf_nat_setqueue(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, nat_t *nat) { ipftq_t *oifq, *nifq; int rev = nat->nat_rev; if (nat->nat_ptr != NULL) nifq = nat->nat_ptr->in_tqehead[rev]; else nifq = NULL; if (nifq == NULL) { switch (nat->nat_pr[0]) { case IPPROTO_UDP : nifq = &softn->ipf_nat_udptq; break; case IPPROTO_ICMP : nifq = &softn->ipf_nat_icmptq; break; case IPPROTO_TCP : nifq = softn->ipf_nat_tcptq + nat->nat_tqe.tqe_state[rev]; break; default : nifq = &softn->ipf_nat_iptq; break; } } oifq = nat->nat_tqe.tqe_ifq; /* * If it's currently on a timeout queue, move it from one queue to * another, else put it on the end of the newly determined queue. */ if (oifq != NULL) ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, nifq); else ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, nifq, nat); return; } /* ------------------------------------------------------------------------ */ /* Function: nat_getnext */ /* Returns: int - 0 == ok, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to ipftoken structure */ /* itp(I) - pointer to ipfgeniter_t structure */ /* */ /* Fetch the next nat/ipnat structure pointer from the linked list and */ /* copy it out to the storage space pointed to by itp_data. The next item */ /* in the list to look at is put back in the ipftoken struture. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_getnext(softc, t, itp, objp) - ipf_main_softc_t *softc; - ipftoken_t *t; - ipfgeniter_t *itp; - ipfobj_t *objp; +ipf_nat_getnext(ipf_main_softc_t *softc, ipftoken_t *t, ipfgeniter_t *itp, + ipfobj_t *objp) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; hostmap_t *hm, *nexthm = NULL, zerohm; ipnat_t *ipn, *nextipnat = NULL, zeroipn; nat_t *nat, *nextnat = NULL, zeronat; int error = 0; void *nnext; if (itp->igi_nitems != 1) { IPFERROR(60075); return ENOSPC; } READ_ENTER(&softc->ipf_nat); switch (itp->igi_type) { case IPFGENITER_HOSTMAP : hm = t->ipt_data; if (hm == NULL) { nexthm = softn->ipf_hm_maplist; } else { nexthm = hm->hm_next; } if (nexthm != NULL) { ATOMIC_INC32(nexthm->hm_ref); t->ipt_data = nexthm; } else { bzero(&zerohm, sizeof(zerohm)); nexthm = &zerohm; t->ipt_data = NULL; } nnext = nexthm->hm_next; break; case IPFGENITER_IPNAT : ipn = t->ipt_data; if (ipn == NULL) { nextipnat = softn->ipf_nat_list; } else { nextipnat = ipn->in_next; } if (nextipnat != NULL) { ATOMIC_INC32(nextipnat->in_use); t->ipt_data = nextipnat; } else { bzero(&zeroipn, sizeof(zeroipn)); nextipnat = &zeroipn; t->ipt_data = NULL; } nnext = nextipnat->in_next; break; case IPFGENITER_NAT : nat = t->ipt_data; if (nat == NULL) { nextnat = softn->ipf_nat_instances; } else { nextnat = nat->nat_next; } if (nextnat != NULL) { MUTEX_ENTER(&nextnat->nat_lock); nextnat->nat_ref++; MUTEX_EXIT(&nextnat->nat_lock); t->ipt_data = nextnat; } else { bzero(&zeronat, sizeof(zeronat)); nextnat = &zeronat; t->ipt_data = NULL; } nnext = nextnat->nat_next; break; default : RWLOCK_EXIT(&softc->ipf_nat); IPFERROR(60055); return EINVAL; } RWLOCK_EXIT(&softc->ipf_nat); objp->ipfo_ptr = itp->igi_data; switch (itp->igi_type) { case IPFGENITER_HOSTMAP : error = COPYOUT(nexthm, objp->ipfo_ptr, sizeof(*nexthm)); if (error != 0) { IPFERROR(60049); error = EFAULT; } if (hm != NULL) { WRITE_ENTER(&softc->ipf_nat); ipf_nat_hostmapdel(softc, &hm); RWLOCK_EXIT(&softc->ipf_nat); } break; case IPFGENITER_IPNAT : objp->ipfo_size = nextipnat->in_size; objp->ipfo_type = IPFOBJ_IPNAT; error = ipf_outobjk(softc, objp, nextipnat); if (ipn != NULL) { WRITE_ENTER(&softc->ipf_nat); ipf_nat_rule_deref(softc, &ipn); RWLOCK_EXIT(&softc->ipf_nat); } break; case IPFGENITER_NAT : objp->ipfo_size = sizeof(nat_t); objp->ipfo_type = IPFOBJ_NAT; error = ipf_outobjk(softc, objp, nextnat); if (nat != NULL) ipf_nat_deref(softc, &nat); break; } if (nnext == NULL) ipf_token_mark_complete(t); return error; } /* ------------------------------------------------------------------------ */ /* Function: nat_extraflush */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* which(I) - how to flush the active NAT table */ /* Write Locks: ipf_nat */ /* */ /* Flush nat tables. Three actions currently defined: */ /* which == 0 : flush all nat table entries */ /* which == 1 : flush TCP connections which have started to close but are */ /* stuck for some reason. */ /* which == 2 : flush TCP connections which have been idle for a long time, */ /* starting at > 4 days idle and working back in successive half-*/ /* days to at most 12 hours old. If this fails to free enough */ /* slots then work backwards in half hour slots to 30 minutes. */ /* If that too fails, then work backwards in 30 second intervals */ /* for the last 30 minutes to at worst 30 seconds idle. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_extraflush(softc, softn, which) - ipf_main_softc_t *softc; - ipf_nat_softc_t *softn; - int which; +ipf_nat_extraflush(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, int which) { nat_t *nat, **natp; ipftqent_t *tqn; ipftq_t *ifq; int removed; SPL_INT(s); removed = 0; SPL_NET(s); switch (which) { case 0 : softn->ipf_nat_stats.ns_flush_all++; /* * Style 0 flush removes everything... */ for (natp = &softn->ipf_nat_instances; ((nat = *natp) != NULL); ) { ipf_nat_delete(softc, nat, NL_FLUSH); removed++; } break; case 1 : softn->ipf_nat_stats.ns_flush_closing++; /* * Since we're only interested in things that are closing, * we can start with the appropriate timeout queue. */ for (ifq = softn->ipf_nat_tcptq + IPF_TCPS_CLOSE_WAIT; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; tqn != NULL; ) { nat = tqn->tqe_parent; tqn = tqn->tqe_next; if (nat->nat_pr[0] != IPPROTO_TCP || nat->nat_pr[1] != IPPROTO_TCP) break; ipf_nat_delete(softc, nat, NL_EXPIRE); removed++; } } /* * Also need to look through the user defined queues. */ for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; tqn != NULL; ) { nat = tqn->tqe_parent; tqn = tqn->tqe_next; if (nat->nat_pr[0] != IPPROTO_TCP || nat->nat_pr[1] != IPPROTO_TCP) continue; if ((nat->nat_tcpstate[0] > IPF_TCPS_ESTABLISHED) && (nat->nat_tcpstate[1] > IPF_TCPS_ESTABLISHED)) { ipf_nat_delete(softc, nat, NL_EXPIRE); removed++; } } } break; /* * Args 5-11 correspond to flushing those particular states * for TCP connections. */ case IPF_TCPS_CLOSE_WAIT : case IPF_TCPS_FIN_WAIT_1 : case IPF_TCPS_CLOSING : case IPF_TCPS_LAST_ACK : case IPF_TCPS_FIN_WAIT_2 : case IPF_TCPS_TIME_WAIT : case IPF_TCPS_CLOSED : softn->ipf_nat_stats.ns_flush_state++; tqn = softn->ipf_nat_tcptq[which].ifq_head; while (tqn != NULL) { nat = tqn->tqe_parent; tqn = tqn->tqe_next; ipf_nat_delete(softc, nat, NL_FLUSH); removed++; } break; default : if (which < 30) break; softn->ipf_nat_stats.ns_flush_timeout++; /* * Take a large arbitrary number to mean the number of seconds * for which which consider to be the maximum value we'll allow * the expiration to be. */ which = IPF_TTLVAL(which); for (natp = &softn->ipf_nat_instances; ((nat = *natp) != NULL); ) { if (softc->ipf_ticks - nat->nat_touched > which) { ipf_nat_delete(softc, nat, NL_FLUSH); removed++; } else natp = &nat->nat_next; } break; } if (which != 2) { SPL_X(s); return removed; } softn->ipf_nat_stats.ns_flush_queue++; /* * Asked to remove inactive entries because the table is full, try * again, 3 times, if first attempt failed with a different criteria * each time. The order tried in must be in decreasing age. * Another alternative is to implement random drop and drop N entries * at random until N have been freed up. */ if (softc->ipf_ticks - softn->ipf_nat_last_force_flush > IPF_TTLVAL(5)) { softn->ipf_nat_last_force_flush = softc->ipf_ticks; removed = ipf_queueflush(softc, ipf_nat_flush_entry, softn->ipf_nat_tcptq, softn->ipf_nat_utqe, &softn->ipf_nat_stats.ns_active, softn->ipf_nat_table_sz, softn->ipf_nat_table_wm_low); } SPL_X(s); return removed; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_flush_entry */ /* Returns: 0 - always succeeds */ /* Parameters: softc(I) - pointer to soft context main structure */ /* entry(I) - pointer to NAT entry */ /* Write Locks: ipf_nat */ /* */ /* This function is a stepping stone between ipf_queueflush() and */ /* nat_dlete(). It is used so we can provide a uniform interface via the */ /* ipf_queueflush() function. Since the nat_delete() function returns void */ /* we translate that to mean it always succeeds in deleting something. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_flush_entry(softc, entry) - ipf_main_softc_t *softc; - void *entry; +ipf_nat_flush_entry(ipf_main_softc_t *softc, void *entry) { ipf_nat_delete(softc, entry, NL_FLUSH); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_iterator */ /* Returns: int - 0 == ok, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to ipftoken structure */ /* itp(I) - pointer to ipfgeniter_t structure */ /* obj(I) - pointer to data description structure */ /* */ /* This function acts as a handler for the SIOCGENITER ioctls that use a */ /* generic structure to iterate through a list. There are three different */ /* linked lists of NAT related information to go through: NAT rules, active */ /* NAT mappings and the NAT fragment cache. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_iterator(softc, token, itp, obj) - ipf_main_softc_t *softc; - ipftoken_t *token; - ipfgeniter_t *itp; - ipfobj_t *obj; +ipf_nat_iterator(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp, + ipfobj_t *obj) { int error; if (itp->igi_data == NULL) { IPFERROR(60052); return EFAULT; } switch (itp->igi_type) { case IPFGENITER_HOSTMAP : case IPFGENITER_IPNAT : case IPFGENITER_NAT : error = ipf_nat_getnext(softc, token, itp, obj); break; case IPFGENITER_NATFRAG : error = ipf_frag_nat_next(softc, token, itp); break; default : IPFERROR(60053); error = EINVAL; break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_setpending */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* nat(I) - pointer to NAT structure */ /* Locks: ipf_nat (read or write) */ /* */ /* Put the NAT entry on to the pending queue - this queue has a very short */ /* lifetime where items are put that can't be deleted straight away because */ /* of locking issues but we want to delete them ASAP, anyway. In calling */ /* this function, it is assumed that the owner (if there is one, as shown */ /* by nat_me) is no longer interested in it. */ /* ------------------------------------------------------------------------ */ void -ipf_nat_setpending(softc, nat) - ipf_main_softc_t *softc; - nat_t *nat; +ipf_nat_setpending(ipf_main_softc_t *softc, nat_t *nat) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipftq_t *oifq; oifq = nat->nat_tqe.tqe_ifq; if (oifq != NULL) ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, &softn->ipf_nat_pending); else ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, &softn->ipf_nat_pending, nat); if (nat->nat_me != NULL) { *nat->nat_me = NULL; nat->nat_me = NULL; nat->nat_ref--; ASSERT(nat->nat_ref >= 0); } } /* ------------------------------------------------------------------------ */ /* Function: nat_newrewrite */ /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ /* allow rule to be moved if IPN_ROUNDR is set. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* Write Lock: ipf_nat */ /* */ /* This function is responsible for setting up an active NAT session where */ /* we are changing both the source and destination parameters at the same */ /* time. The loop in here works differently to elsewhere - each iteration */ /* is responsible for changing a single parameter that can be incremented. */ /* So one pass may increase the source IP#, next source port, next dest. IP#*/ /* and the last destination port for a total of 4 iterations to try each. */ /* This is done to try and exhaustively use the translation space available.*/ /* ------------------------------------------------------------------------ */ static int -ipf_nat_newrewrite(fin, nat, nai) - fr_info_t *fin; - nat_t *nat; - natinfo_t *nai; +ipf_nat_newrewrite(fr_info_t *fin, nat_t *nat, natinfo_t *nai) { int src_search = 1; int dst_search = 1; fr_info_t frnat; u_32_t flags; u_short swap; ipnat_t *np; nat_t *natl; int l = 0; int changed; natl = NULL; changed = -1; np = nai->nai_np; flags = nat->nat_flags; bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); nat->nat_hm = NULL; do { changed = -1; /* TRACE (l, src_search, dst_search, np) */ DT4(ipf_nat_rewrite_1, int, l, int, src_search, int, dst_search, ipnat_t *, np); if ((src_search == 0) && (np->in_spnext == 0) && (dst_search == 0) && (np->in_dpnext == 0)) { if (l > 0) return -1; } /* * Find a new source address */ if (ipf_nat_nextaddr(fin, &np->in_nsrc, &frnat.fin_saddr, &frnat.fin_saddr) == -1) { return -1; } if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) { src_search = 0; if (np->in_stepnext == 0) np->in_stepnext = 1; } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { src_search = 0; if (np->in_stepnext == 0) np->in_stepnext = 1; } else if (np->in_nsrcmsk == 0xffffffff) { src_search = 0; if (np->in_stepnext == 0) np->in_stepnext = 1; } else if (np->in_nsrcmsk != 0xffffffff) { if (np->in_stepnext == 0 && changed == -1) { np->in_snip++; np->in_stepnext++; changed = 0; } } if ((flags & IPN_TCPUDPICMP) != 0) { if (np->in_spnext != 0) frnat.fin_data[0] = np->in_spnext; /* * Standard port translation. Select next port. */ if ((flags & IPN_FIXEDSPORT) != 0) { np->in_stepnext = 2; } else if ((np->in_stepnext == 1) && (changed == -1) && (natl != NULL)) { np->in_spnext++; np->in_stepnext++; changed = 1; if (np->in_spnext > np->in_spmax) np->in_spnext = np->in_spmin; } } else { np->in_stepnext = 2; } np->in_stepnext &= 0x3; /* * Find a new destination address */ /* TRACE (fin, np, l, frnat) */ DT4(ipf_nat_rewrite_2, frinfo_t *, fin, ipnat_t *, np, int, l, frinfo_t *, &frnat); if (ipf_nat_nextaddr(fin, &np->in_ndst, &frnat.fin_daddr, &frnat.fin_daddr) == -1) return -1; if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { dst_search = 0; if (np->in_stepnext == 2) np->in_stepnext = 3; } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0)) { dst_search = 0; if (np->in_stepnext == 2) np->in_stepnext = 3; } else if (np->in_ndstmsk == 0xffffffff) { dst_search = 0; if (np->in_stepnext == 2) np->in_stepnext = 3; } else if (np->in_ndstmsk != 0xffffffff) { if ((np->in_stepnext == 2) && (changed == -1) && (natl != NULL)) { changed = 2; np->in_stepnext++; np->in_dnip++; } } if ((flags & IPN_TCPUDPICMP) != 0) { if (np->in_dpnext != 0) frnat.fin_data[1] = np->in_dpnext; /* * Standard port translation. Select next port. */ if ((flags & IPN_FIXEDDPORT) != 0) { np->in_stepnext = 0; } else if (np->in_stepnext == 3 && changed == -1) { np->in_dpnext++; np->in_stepnext++; changed = 3; if (np->in_dpnext > np->in_dpmax) np->in_dpnext = np->in_dpmin; } } else { if (np->in_stepnext == 3) np->in_stepnext = 0; } /* TRACE (frnat) */ DT1(ipf_nat_rewrite_3, frinfo_t *, &frnat); /* * Here we do a lookup of the connection as seen from * the outside. If an IP# pair already exists, try * again. So if you have A->B becomes C->B, you can * also have D->E become C->E but not D->B causing * another C->B. Also take protocol and ports into * account when determining whether a pre-existing * NAT setup will cause an external conflict where * this is appropriate. * * fin_data[] is swapped around because we are doing a * lookup of the packet is if it were moving in the opposite * direction of the one we are working with now. */ if (flags & IPN_TCPUDP) { swap = frnat.fin_data[0]; frnat.fin_data[0] = frnat.fin_data[1]; frnat.fin_data[1] = swap; } if (fin->fin_out == 1) { natl = ipf_nat_inlookup(&frnat, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)frnat.fin_p, frnat.fin_dst, frnat.fin_src); } else { natl = ipf_nat_outlookup(&frnat, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)frnat.fin_p, frnat.fin_dst, frnat.fin_src); } if (flags & IPN_TCPUDP) { swap = frnat.fin_data[0]; frnat.fin_data[0] = frnat.fin_data[1]; frnat.fin_data[1] = swap; } /* TRACE natl, in_stepnext, l */ DT3(ipf_nat_rewrite_2, nat_t *, natl, ipnat_t *, np , int, l); if ((natl != NULL) && (l > 8)) /* XXX 8 is arbitrary */ return -1; np->in_stepnext &= 0x3; l++; changed = -1; } while (natl != NULL); nat->nat_osrcip = fin->fin_src; nat->nat_odstip = fin->fin_dst; nat->nat_nsrcip = frnat.fin_src; nat->nat_ndstip = frnat.fin_dst; if ((flags & IPN_TCPUDP) != 0) { nat->nat_osport = htons(fin->fin_data[0]); nat->nat_odport = htons(fin->fin_data[1]); nat->nat_nsport = htons(frnat.fin_data[0]); nat->nat_ndport = htons(frnat.fin_data[1]); } else if ((flags & IPN_ICMPQUERY) != 0) { nat->nat_oicmpid = fin->fin_data[1]; nat->nat_nicmpid = frnat.fin_data[1]; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: nat_newdivert */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* Write Lock: ipf_nat */ /* */ /* Create a new NAT divert session as defined by the NAT rule. This is */ /* somewhat different to other NAT session creation routines because we */ /* do not iterate through either port numbers or IP addresses, searching */ /* for a unique mapping, however, a complimentary duplicate check is made. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_newdivert(fin, nat, nai) - fr_info_t *fin; - nat_t *nat; - natinfo_t *nai; +ipf_nat_newdivert(fr_info_t *fin, nat_t *nat, natinfo_t *nai) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; fr_info_t frnat; ipnat_t *np; nat_t *natl; int p; np = nai->nai_np; bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); nat->nat_pr[0] = 0; nat->nat_osrcaddr = fin->fin_saddr; nat->nat_odstaddr = fin->fin_daddr; frnat.fin_saddr = htonl(np->in_snip); frnat.fin_daddr = htonl(np->in_dnip); if ((nat->nat_flags & IPN_TCPUDP) != 0) { nat->nat_osport = htons(fin->fin_data[0]); nat->nat_odport = htons(fin->fin_data[1]); } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { nat->nat_oicmpid = fin->fin_data[1]; } if (np->in_redir & NAT_DIVERTUDP) { frnat.fin_data[0] = np->in_spnext; frnat.fin_data[1] = np->in_dpnext; frnat.fin_flx |= FI_TCPUDP; p = IPPROTO_UDP; } else { frnat.fin_flx &= ~FI_TCPUDP; p = IPPROTO_IPIP; } if (fin->fin_out == 1) { natl = ipf_nat_inlookup(&frnat, 0, p, frnat.fin_dst, frnat.fin_src); } else { natl = ipf_nat_outlookup(&frnat, 0, p, frnat.fin_dst, frnat.fin_src); } if (natl != NULL) { NBUMPSIDED(fin->fin_out, ns_divert_exist); DT3(ns_divert_exist, fr_info_t *, fin, nat_t *, nat, natinfo_t, nai); return -1; } nat->nat_nsrcaddr = frnat.fin_saddr; nat->nat_ndstaddr = frnat.fin_daddr; if ((nat->nat_flags & IPN_TCPUDP) != 0) { nat->nat_nsport = htons(frnat.fin_data[0]); nat->nat_ndport = htons(frnat.fin_data[1]); } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { nat->nat_nicmpid = frnat.fin_data[1]; } nat->nat_pr[fin->fin_out] = fin->fin_p; nat->nat_pr[1 - fin->fin_out] = p; if (np->in_redir & NAT_REDIRECT) nat->nat_dir = NAT_DIVERTIN; else nat->nat_dir = NAT_DIVERTOUT; return 0; } /* ------------------------------------------------------------------------ */ /* Function: nat_builddivertmp */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: softn(I) - pointer to NAT context structure */ /* np(I) - pointer to a NAT rule */ /* */ /* For divert rules, a skeleton packet representing what will be prepended */ /* to the real packet is created. Even though we don't have the full */ /* packet here, a checksum is calculated that we update later when we */ /* fill in the final details. At present a 0 checksum for UDP is being set */ /* here because it is expected that divert will be used for localhost. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_builddivertmp(softn, np) - ipf_nat_softc_t *softn; - ipnat_t *np; +ipf_nat_builddivertmp(ipf_nat_softc_t *softn, ipnat_t *np) { udphdr_t *uh; size_t len; ip_t *ip; if ((np->in_redir & NAT_DIVERTUDP) != 0) len = sizeof(ip_t) + sizeof(udphdr_t); else len = sizeof(ip_t); ALLOC_MB_T(np->in_divmp, len); if (np->in_divmp == NULL) { NBUMPD(ipf_nat_stats, ns_divert_build); return -1; } /* * First, the header to get the packet diverted to the new destination */ ip = MTOD(np->in_divmp, ip_t *); IP_V_A(ip, 4); IP_HL_A(ip, 5); ip->ip_tos = 0; if ((np->in_redir & NAT_DIVERTUDP) != 0) ip->ip_p = IPPROTO_UDP; else ip->ip_p = IPPROTO_IPIP; ip->ip_ttl = 255; ip->ip_off = 0; ip->ip_sum = 0; ip->ip_len = htons(len); ip->ip_id = 0; ip->ip_src.s_addr = htonl(np->in_snip); ip->ip_dst.s_addr = htonl(np->in_dnip); ip->ip_sum = ipf_cksum((u_short *)ip, sizeof(*ip)); if (np->in_redir & NAT_DIVERTUDP) { uh = (udphdr_t *)(ip + 1); uh->uh_sum = 0; uh->uh_ulen = 8; uh->uh_sport = htons(np->in_spnext); uh->uh_dport = htons(np->in_dpnext); } return 0; } #define MINDECAP (sizeof(ip_t) + sizeof(udphdr_t) + sizeof(ip_t)) /* ------------------------------------------------------------------------ */ /* Function: nat_decap */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* This function is responsible for undoing a packet's encapsulation in the */ /* reverse of an encap/divert rule. After removing the outer encapsulation */ /* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/ /* match the "new" packet as it may still be used by IPFilter elsewhere. */ /* We use "dir" here as the basis for some of the expectations about the */ /* outer header. If we return an error, the goal is to leave the original */ /* packet information undisturbed - this falls short at the end where we'd */ /* need to back a backup copy of "fin" - expensive. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_decap(fin, nat) - fr_info_t *fin; - nat_t *nat; +ipf_nat_decap(fr_info_t *fin, nat_t *nat) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; char *hdr; int hlen; int skip; mb_t *m; if ((fin->fin_flx & FI_ICMPERR) != 0) { /* * ICMP packets don't get decapsulated, instead what we need * to do is change the ICMP reply from including (in the data * portion for errors) the encapsulated packet that we sent * out to something that resembles the original packet prior * to encapsulation. This isn't done here - all we're doing * here is changing the outer address to ensure that it gets * targetted back to the correct system. */ if (nat->nat_dir & NAT_OUTBOUND) { u_32_t sum1, sum2, sumd; sum1 = ntohl(fin->fin_daddr); sum2 = ntohl(nat->nat_osrcaddr); CALC_SUMD(sum1, sum2, sumd); fin->fin_ip->ip_dst = nat->nat_osrcip; fin->fin_daddr = nat->nat_osrcaddr; #if !defined(_KERNEL) || SOLARIS ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, sumd, 0); #endif } return 0; } m = fin->fin_m; skip = fin->fin_hlen; switch (nat->nat_dir) { case NAT_DIVERTIN : case NAT_DIVERTOUT : if (fin->fin_plen < MINDECAP) return -1; skip += sizeof(udphdr_t); break; case NAT_ENCAPIN : case NAT_ENCAPOUT : if (fin->fin_plen < (skip + sizeof(ip_t))) return -1; break; default : return -1; /* NOTREACHED */ } /* * The aim here is to keep the original packet details in "fin" for * as long as possible so that returning with an error is for the * original packet and there is little undoing work to do. */ if (M_LEN(m) < skip + sizeof(ip_t)) { if (ipf_pr_pullup(fin, skip + sizeof(ip_t)) == -1) return -1; } hdr = MTOD(fin->fin_m, char *); fin->fin_ip = (ip_t *)(hdr + skip); hlen = IP_HL(fin->fin_ip) << 2; if (ipf_pr_pullup(fin, skip + hlen) == -1) { NBUMPSIDED(fin->fin_out, ns_decap_pullup); return -1; } fin->fin_hlen = hlen; fin->fin_dlen -= skip; fin->fin_plen -= skip; fin->fin_ipoff += skip; if (ipf_makefrip(hlen, (ip_t *)hdr, fin) == -1) { NBUMPSIDED(fin->fin_out, ns_decap_bad); return -1; } return skip; } /* ------------------------------------------------------------------------ */ /* Function: nat_nextaddr */ /* Returns: int - -1 == bad input (no new address), */ /* 0 == success and dst has new address */ /* Parameters: fin(I) - pointer to packet information */ /* na(I) - how to generate new address */ /* old(I) - original address being replaced */ /* dst(O) - where to put the new address */ /* Write Lock: ipf_nat */ /* */ /* This function uses the contents of the "na" structure, in combination */ /* with "old" to produce a new address to store in "dst". Not all of the */ /* possible uses of "na" will result in a new address. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_nextaddr(fin, na, old, dst) - fr_info_t *fin; - nat_addr_t *na; - u_32_t *old, *dst; +ipf_nat_nextaddr(fr_info_t *fin, nat_addr_t *na, u_32_t *old, u_32_t *dst) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t amin, amax, new; i6addr_t newip; int error; new = 0; amin = na->na_addr[0].in4.s_addr; switch (na->na_atype) { case FRI_RANGE : amax = na->na_addr[1].in4.s_addr; break; case FRI_NETMASKED : case FRI_DYNAMIC : case FRI_NORMAL : /* * Compute the maximum address by adding the inverse of the * netmask to the minimum address. */ amax = ~na->na_addr[1].in4.s_addr; amax |= amin; break; case FRI_LOOKUP : break; case FRI_BROADCAST : case FRI_PEERADDR : case FRI_NETWORK : default : DT4(ns_na_atype, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); return -1; } error = -1; if (na->na_atype == FRI_LOOKUP) { if (na->na_type == IPLT_DSTLIST) { error = ipf_dstlist_select_node(fin, na->na_ptr, dst, NULL); } else { NBUMPSIDE(fin->fin_out, ns_badnextaddr); DT4(ns_badnextaddr_1, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); } } else if (na->na_atype == IPLT_NONE) { /* * 0/0 as the new address means leave it alone. */ if (na->na_addr[0].in4.s_addr == 0 && na->na_addr[1].in4.s_addr == 0) { new = *old; /* * 0/32 means get the interface's address */ } else if (na->na_addr[0].in4.s_addr == 0 && na->na_addr[1].in4.s_addr == 0xffffffff) { if (ipf_ifpaddr(softc, 4, na->na_atype, fin->fin_ifp, &newip, NULL) == -1) { NBUMPSIDED(fin->fin_out, ns_ifpaddrfail); DT4(ns_ifpaddrfail, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); return -1; } new = newip.in4.s_addr; } else { new = htonl(na->na_nextip); } *dst = new; error = 0; } else { NBUMPSIDE(fin->fin_out, ns_badnextaddr); DT4(ns_badnextaddr_2, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); } return error; } /* ------------------------------------------------------------------------ */ /* Function: nat_nextaddrinit */ /* Returns: int - 0 == success, else error number */ /* Parameters: softc(I) - pointer to soft context main structure */ /* na(I) - NAT address information for generating new addr*/ /* initial(I) - flag indicating if it is the first call for */ /* this "na" structure. */ /* ifp(I) - network interface to derive address */ /* information from. */ /* */ /* This function is expected to be called in two scenarious: when a new NAT */ /* rule is loaded into the kernel and when the list of NAT rules is sync'd */ /* up with the valid network interfaces (possibly due to them changing.) */ /* To distinguish between these, the "initial" parameter is used. If it is */ /* 1 then this indicates the rule has just been reloaded and 0 for when we */ /* are updating information. This difference is important because in */ /* instances where we are not updating address information associated with */ /* a network interface, we don't want to disturb what the "next" address to */ /* come out of ipf_nat_nextaddr() will be. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_nextaddrinit(softc, base, na, initial, ifp) - ipf_main_softc_t *softc; - char *base; - nat_addr_t *na; - int initial; - void *ifp; +ipf_nat_nextaddrinit(ipf_main_softc_t *softc, char *base, nat_addr_t *na, + int initial, void *ifp) { switch (na->na_atype) { case FRI_LOOKUP : if (na->na_subtype == 0) { na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT, na->na_type, na->na_num, &na->na_func); } else if (na->na_subtype == 1) { na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT, na->na_type, base + na->na_num, &na->na_func); } if (na->na_func == NULL) { IPFERROR(60060); return ESRCH; } if (na->na_ptr == NULL) { IPFERROR(60056); return ESRCH; } break; case FRI_DYNAMIC : case FRI_BROADCAST : case FRI_NETWORK : case FRI_NETMASKED : case FRI_PEERADDR : if (ifp != NULL) (void )ipf_ifpaddr(softc, 4, na->na_atype, ifp, &na->na_addr[0], &na->na_addr[1]); break; case FRI_SPLIT : case FRI_RANGE : if (initial) na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); break; case FRI_NONE : na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; return 0; case FRI_NORMAL : na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; break; default : IPFERROR(60054); return EINVAL; } if (initial && (na->na_atype == FRI_NORMAL)) { if (na->na_addr[0].in4.s_addr == 0) { if ((na->na_addr[1].in4.s_addr == 0xffffffff) || (na->na_addr[1].in4.s_addr == 0)) { return 0; } } if (na->na_addr[1].in4.s_addr == 0xffffffff) { na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); } else { na->na_nextip = ntohl(na->na_addr[0].in4.s_addr) + 1; } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_matchflush */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to current NAT session */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_matchflush(softc, softn, data) - ipf_main_softc_t *softc; - ipf_nat_softc_t *softn; - caddr_t data; +ipf_nat_matchflush(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, + caddr_t data) { int *array, flushed, error; nat_t *nat, *natnext; ipfobj_t obj; error = ipf_matcharray_load(softc, data, &obj, &array); if (error != 0) return error; flushed = 0; for (nat = softn->ipf_nat_instances; nat != NULL; nat = natnext) { natnext = nat->nat_next; if (ipf_nat_matcharray(nat, array, softc->ipf_ticks) == 0) { ipf_nat_delete(softc, nat, NL_FLUSH); flushed++; } } obj.ipfo_retval = flushed; error = BCOPYOUT(&obj, data, sizeof(obj)); KFREES(array, array[0] * sizeof(*array)); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_matcharray */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_matcharray(nat, array, ticks) - nat_t *nat; - int *array; - u_long ticks; +ipf_nat_matcharray(nat_t *nat, int *array, u_long ticks) { int i, n, *x, e, p; e = 0; n = array[0]; x = array + 1; for (; n > 0; x += 3 + x[2]) { if (x[0] == IPF_EXP_END) break; e = 0; n -= x[2] + 3; if (n < 0) break; p = x[0] >> 16; if (p != 0 && p != nat->nat_pr[1]) break; switch (x[0]) { case IPF_EXP_IP_PR : for (i = 0; !e && i < x[2]; i++) { e |= (nat->nat_pr[1] == x[i + 3]); } break; case IPF_EXP_IP_SRCADDR : if (nat->nat_v[0] == 4) { for (i = 0; !e && i < x[2]; i++) { e |= ((nat->nat_osrcaddr & x[i + 4]) == x[i + 3]); } } if (nat->nat_v[1] == 4) { for (i = 0; !e && i < x[2]; i++) { e |= ((nat->nat_nsrcaddr & x[i + 4]) == x[i + 3]); } } break; case IPF_EXP_IP_DSTADDR : if (nat->nat_v[0] == 4) { for (i = 0; !e && i < x[2]; i++) { e |= ((nat->nat_odstaddr & x[i + 4]) == x[i + 3]); } } if (nat->nat_v[1] == 4) { for (i = 0; !e && i < x[2]; i++) { e |= ((nat->nat_ndstaddr & x[i + 4]) == x[i + 3]); } } break; case IPF_EXP_IP_ADDR : for (i = 0; !e && i < x[2]; i++) { if (nat->nat_v[0] == 4) { e |= ((nat->nat_osrcaddr & x[i + 4]) == x[i + 3]); } if (nat->nat_v[1] == 4) { e |= ((nat->nat_nsrcaddr & x[i + 4]) == x[i + 3]); } if (nat->nat_v[0] == 4) { e |= ((nat->nat_odstaddr & x[i + 4]) == x[i + 3]); } if (nat->nat_v[1] == 4) { e |= ((nat->nat_ndstaddr & x[i + 4]) == x[i + 3]); } } break; #ifdef USE_INET6 case IPF_EXP_IP6_SRCADDR : if (nat->nat_v[0] == 6) { for (i = 0; !e && i < x[3]; i++) { e |= IP6_MASKEQ(&nat->nat_osrc6, x + i + 7, x + i + 3); } } if (nat->nat_v[1] == 6) { for (i = 0; !e && i < x[3]; i++) { e |= IP6_MASKEQ(&nat->nat_nsrc6, x + i + 7, x + i + 3); } } break; case IPF_EXP_IP6_DSTADDR : if (nat->nat_v[0] == 6) { for (i = 0; !e && i < x[3]; i++) { e |= IP6_MASKEQ(&nat->nat_odst6, x + i + 7, x + i + 3); } } if (nat->nat_v[1] == 6) { for (i = 0; !e && i < x[3]; i++) { e |= IP6_MASKEQ(&nat->nat_ndst6, x + i + 7, x + i + 3); } } break; case IPF_EXP_IP6_ADDR : for (i = 0; !e && i < x[3]; i++) { if (nat->nat_v[0] == 6) { e |= IP6_MASKEQ(&nat->nat_osrc6, x + i + 7, x + i + 3); } if (nat->nat_v[0] == 6) { e |= IP6_MASKEQ(&nat->nat_odst6, x + i + 7, x + i + 3); } if (nat->nat_v[1] == 6) { e |= IP6_MASKEQ(&nat->nat_nsrc6, x + i + 7, x + i + 3); } if (nat->nat_v[1] == 6) { e |= IP6_MASKEQ(&nat->nat_ndst6, x + i + 7, x + i + 3); } } break; #endif case IPF_EXP_UDP_PORT : case IPF_EXP_TCP_PORT : for (i = 0; !e && i < x[2]; i++) { e |= (nat->nat_nsport == x[i + 3]) || (nat->nat_ndport == x[i + 3]); } break; case IPF_EXP_UDP_SPORT : case IPF_EXP_TCP_SPORT : for (i = 0; !e && i < x[2]; i++) { e |= (nat->nat_nsport == x[i + 3]); } break; case IPF_EXP_UDP_DPORT : case IPF_EXP_TCP_DPORT : for (i = 0; !e && i < x[2]; i++) { e |= (nat->nat_ndport == x[i + 3]); } break; case IPF_EXP_TCP_STATE : for (i = 0; !e && i < x[2]; i++) { e |= (nat->nat_tcpstate[0] == x[i + 3]) || (nat->nat_tcpstate[1] == x[i + 3]); } break; case IPF_EXP_IDLE_GT : e |= (ticks - nat->nat_touched > x[3]); break; } e ^= x[1]; if (!e) break; } return e; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_gettable */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* data(I) - pointer to ioctl data */ /* */ /* This function handles ioctl requests for tables of nat information. */ /* At present the only table it deals with is the hash bucket statistics. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_gettable(softc, softn, data) - ipf_main_softc_t *softc; - ipf_nat_softc_t *softn; - char *data; +ipf_nat_gettable(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, char *data) { ipftable_t table; int error; error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE); if (error != 0) return error; switch (table.ita_type) { case IPFTABLE_BUCKETS_NATIN : error = COPYOUT(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, table.ita_table, softn->ipf_nat_table_sz * sizeof(u_int)); break; case IPFTABLE_BUCKETS_NATOUT : error = COPYOUT(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, table.ita_table, softn->ipf_nat_table_sz * sizeof(u_int)); break; default : IPFERROR(60058); return EINVAL; } if (error != 0) { IPFERROR(60059); error = EFAULT; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_settimeout */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to tunable */ /* p(I) - pointer to new tuning data */ /* */ /* Apply the timeout change to the NAT timeout queues. */ /* ------------------------------------------------------------------------ */ int -ipf_nat_settimeout(softc, t, p) - struct ipf_main_softc_s *softc; - ipftuneable_t *t; - ipftuneval_t *p; +ipf_nat_settimeout(struct ipf_main_softc_s *softc, ipftuneable_t *t, + ipftuneval_t *p) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; if (!strncmp(t->ipft_name, "tcp_", 4)) return ipf_settimeout_tcp(t, p, softn->ipf_nat_tcptq); if (!strcmp(t->ipft_name, "udp_timeout")) { ipf_apply_timeout(&softn->ipf_nat_udptq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "udp_ack_timeout")) { ipf_apply_timeout(&softn->ipf_nat_udpacktq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "icmp_timeout")) { ipf_apply_timeout(&softn->ipf_nat_icmptq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "icmp_ack_timeout")) { ipf_apply_timeout(&softn->ipf_nat_icmpacktq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "ip_timeout")) { ipf_apply_timeout(&softn->ipf_nat_iptq, p->ipftu_int); } else { IPFERROR(60062); return ESRCH; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_rehash */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to tunable */ /* p(I) - pointer to new tuning data */ /* */ /* To change the size of the basic NAT table, we need to first allocate the */ /* new tables (lest it fails and we've got nowhere to store all of the NAT */ /* sessions currently active) and then walk through the entire list and */ /* insert them into the table. There are two tables here: an inbound one */ /* and an outbound one. Each NAT entry goes into each table once. */ /* ------------------------------------------------------------------------ */ int -ipf_nat_rehash(softc, t, p) - ipf_main_softc_t *softc; - ipftuneable_t *t; - ipftuneval_t *p; +ipf_nat_rehash(ipf_main_softc_t *softc, ipftuneable_t *t, ipftuneval_t *p) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; nat_t **newtab[2], *nat, **natp; u_int *bucketlens[2]; u_int maxbucket; u_int newsize; int error; u_int hv; int i; newsize = p->ipftu_int; /* * In case there is nothing to do... */ if (newsize == softn->ipf_nat_table_sz) return 0; newtab[0] = NULL; newtab[1] = NULL; bucketlens[0] = NULL; bucketlens[1] = NULL; /* * 4 tables depend on the NAT table size: the inbound looking table, * the outbound lookup table and the hash chain length for each. */ KMALLOCS(newtab[0], nat_t **, newsize * sizeof(nat_t *)); if (newtab[0] == NULL) { error = 60063; goto badrehash; } KMALLOCS(newtab[1], nat_t **, newsize * sizeof(nat_t *)); if (newtab[1] == NULL) { error = 60064; goto badrehash; } KMALLOCS(bucketlens[0], u_int *, newsize * sizeof(u_int)); if (bucketlens[0] == NULL) { error = 60065; goto badrehash; } KMALLOCS(bucketlens[1], u_int *, newsize * sizeof(u_int)); if (bucketlens[1] == NULL) { error = 60066; goto badrehash; } /* * Recalculate the maximum length based on the new size. */ for (maxbucket = 0, i = newsize; i > 0; i >>= 1) maxbucket++; maxbucket *= 2; bzero((char *)newtab[0], newsize * sizeof(nat_t *)); bzero((char *)newtab[1], newsize * sizeof(nat_t *)); bzero((char *)bucketlens[0], newsize * sizeof(u_int)); bzero((char *)bucketlens[1], newsize * sizeof(u_int)); WRITE_ENTER(&softc->ipf_nat); if (softn->ipf_nat_table[0] != NULL) { KFREES(softn->ipf_nat_table[0], softn->ipf_nat_table_sz * sizeof(*softn->ipf_nat_table[0])); } softn->ipf_nat_table[0] = newtab[0]; if (softn->ipf_nat_table[1] != NULL) { KFREES(softn->ipf_nat_table[1], softn->ipf_nat_table_sz * sizeof(*softn->ipf_nat_table[1])); } softn->ipf_nat_table[1] = newtab[1]; if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); } softn->ipf_nat_stats.ns_side[0].ns_bucketlen = bucketlens[0]; if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); } softn->ipf_nat_stats.ns_side[1].ns_bucketlen = bucketlens[1]; #ifdef USE_INET6 if (softn->ipf_nat_stats.ns_side6[0].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side6[0].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); } softn->ipf_nat_stats.ns_side6[0].ns_bucketlen = bucketlens[0]; if (softn->ipf_nat_stats.ns_side6[1].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side6[1].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); } softn->ipf_nat_stats.ns_side6[1].ns_bucketlen = bucketlens[1]; #endif softn->ipf_nat_maxbucket = maxbucket; softn->ipf_nat_table_sz = newsize; /* * Walk through the entire list of NAT table entries and put them * in the new NAT table, somewhere. Because we have a new table, * we need to restart the counter of how many chains are in use. */ softn->ipf_nat_stats.ns_side[0].ns_inuse = 0; softn->ipf_nat_stats.ns_side[1].ns_inuse = 0; #ifdef USE_INET6 softn->ipf_nat_stats.ns_side6[0].ns_inuse = 0; softn->ipf_nat_stats.ns_side6[1].ns_inuse = 0; #endif for (nat = softn->ipf_nat_instances; nat != NULL; nat = nat->nat_next) { nat->nat_hnext[0] = NULL; nat->nat_phnext[0] = NULL; hv = nat->nat_hv[0] % softn->ipf_nat_table_sz; natp = &softn->ipf_nat_table[0][hv]; if (*natp) { (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; } else { NBUMPSIDE(0, ns_inuse); } nat->nat_phnext[0] = natp; nat->nat_hnext[0] = *natp; *natp = nat; NBUMPSIDE(0, ns_bucketlen[hv]); nat->nat_hnext[1] = NULL; nat->nat_phnext[1] = NULL; hv = nat->nat_hv[1] % softn->ipf_nat_table_sz; natp = &softn->ipf_nat_table[1][hv]; if (*natp) { (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; } else { NBUMPSIDE(1, ns_inuse); } nat->nat_phnext[1] = natp; nat->nat_hnext[1] = *natp; *natp = nat; NBUMPSIDE(1, ns_bucketlen[hv]); } RWLOCK_EXIT(&softc->ipf_nat); return 0; badrehash: if (bucketlens[1] != NULL) { KFREES(bucketlens[0], newsize * sizeof(u_int)); } if (bucketlens[0] != NULL) { KFREES(bucketlens[0], newsize * sizeof(u_int)); } if (newtab[0] != NULL) { KFREES(newtab[0], newsize * sizeof(nat_t *)); } if (newtab[1] != NULL) { KFREES(newtab[1], newsize * sizeof(nat_t *)); } IPFERROR(error); return ENOMEM; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_rehash_rules */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to tunable */ /* p(I) - pointer to new tuning data */ /* */ /* All of the NAT rules hang off of a hash table that is searched with a */ /* hash on address after the netmask is applied. There is a different table*/ /* for both inbound rules (rdr) and outbound (map.) The resizing will only */ /* affect one of these two tables. */ /* ------------------------------------------------------------------------ */ int -ipf_nat_rehash_rules(softc, t, p) - ipf_main_softc_t *softc; - ipftuneable_t *t; - ipftuneval_t *p; +ipf_nat_rehash_rules(ipf_main_softc_t *softc, ipftuneable_t *t, + ipftuneval_t *p) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipnat_t **newtab, *np, ***old, **npp; u_int newsize; u_int mask; u_int hv; newsize = p->ipftu_int; /* * In case there is nothing to do... */ if (newsize == *t->ipft_pint) return 0; /* * All inbound rules have the NAT_REDIRECT bit set in in_redir and * all outbound rules have either NAT_MAP or MAT_MAPBLK set. * This if statement allows for some more generic code to be below, * rather than two huge gobs of code that almost do the same thing. */ if (t->ipft_pint == &softn->ipf_nat_rdrrules_sz) { old = &softn->ipf_nat_rdr_rules; mask = NAT_REDIRECT; } else { old = &softn->ipf_nat_map_rules; mask = NAT_MAP|NAT_MAPBLK; } KMALLOCS(newtab, ipnat_t **, newsize * sizeof(ipnat_t *)); if (newtab == NULL) { IPFERROR(60067); return ENOMEM; } bzero((char *)newtab, newsize * sizeof(ipnat_t *)); WRITE_ENTER(&softc->ipf_nat); if (*old != NULL) { KFREES(*old, *t->ipft_pint * sizeof(ipnat_t **)); } *old = newtab; *t->ipft_pint = newsize; for (np = softn->ipf_nat_list; np != NULL; np = np->in_next) { if ((np->in_redir & mask) == 0) continue; if (np->in_redir & NAT_REDIRECT) { np->in_rnext = NULL; hv = np->in_hv[0] % newsize; for (npp = newtab + hv; *npp != NULL; ) npp = &(*npp)->in_rnext; np->in_prnext = npp; *npp = np; } if (np->in_redir & NAT_MAP) { np->in_mnext = NULL; hv = np->in_hv[1] % newsize; for (npp = newtab + hv; *npp != NULL; ) npp = &(*npp)->in_mnext; np->in_pmnext = npp; *npp = np; } } RWLOCK_EXIT(&softc->ipf_nat); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_hostmap_rehash */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to tunable */ /* p(I) - pointer to new tuning data */ /* */ /* Allocate and populate a new hash table that will contain a reference to */ /* all of the active IP# translations currently in place. */ /* ------------------------------------------------------------------------ */ int -ipf_nat_hostmap_rehash(softc, t, p) - ipf_main_softc_t *softc; - ipftuneable_t *t; - ipftuneval_t *p; +ipf_nat_hostmap_rehash(ipf_main_softc_t *softc, ipftuneable_t *t, + ipftuneval_t *p) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; hostmap_t *hm, **newtab; u_int newsize; u_int hv; newsize = p->ipftu_int; /* * In case there is nothing to do... */ if (newsize == *t->ipft_pint) return 0; KMALLOCS(newtab, hostmap_t **, newsize * sizeof(hostmap_t *)); if (newtab == NULL) { IPFERROR(60068); return ENOMEM; } bzero((char *)newtab, newsize * sizeof(hostmap_t *)); WRITE_ENTER(&softc->ipf_nat); if (softn->ipf_hm_maptable != NULL) { KFREES(softn->ipf_hm_maptable, softn->ipf_nat_hostmap_sz * sizeof(hostmap_t *)); } softn->ipf_hm_maptable = newtab; softn->ipf_nat_hostmap_sz = newsize; for (hm = softn->ipf_hm_maplist; hm != NULL; hm = hm->hm_next) { hv = hm->hm_hv % softn->ipf_nat_hostmap_sz; hm->hm_hnext = softn->ipf_hm_maptable[hv]; hm->hm_phnext = softn->ipf_hm_maptable + hv; if (softn->ipf_hm_maptable[hv] != NULL) softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; softn->ipf_hm_maptable[hv] = hm; } RWLOCK_EXIT(&softc->ipf_nat); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_add_tq */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* ------------------------------------------------------------------------ */ ipftq_t * -ipf_nat_add_tq(softc, ttl) - ipf_main_softc_t *softc; - int ttl; +ipf_nat_add_tq(ipf_main_softc_t *softc, int ttl) { ipf_nat_softc_t *softs = softc->ipf_nat_soft; return ipf_addtimeoutqueue(softc, &softs->ipf_nat_utqe, ttl); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_uncreate */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* This function is used to remove a NAT entry from the NAT table when we */ /* decide that the create was actually in error. It is thus assumed that */ /* fin_flx will have both FI_NATED and FI_NATNEW set. Because we're dealing */ /* with the translated packet (not the original), we have to reverse the */ /* lookup. Although doing the lookup is expensive (relatively speaking), it */ /* is not anticipated that this will be a frequent occurance for normal */ /* traffic patterns. */ /* ------------------------------------------------------------------------ */ void -ipf_nat_uncreate(fin) - fr_info_t *fin; +ipf_nat_uncreate(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; int nflags; nat_t *nat; switch (fin->fin_p) { case IPPROTO_TCP : nflags = IPN_TCP; break; case IPPROTO_UDP : nflags = IPN_UDP; break; default : nflags = 0; break; } WRITE_ENTER(&softc->ipf_nat); if (fin->fin_out == 0) { nat = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, fin->fin_dst, fin->fin_src); } else { nat = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, fin->fin_src, fin->fin_dst); } if (nat != NULL) { NBUMPSIDE(fin->fin_out, ns_uncreate[0]); ipf_nat_delete(softc, nat, NL_DESTROY); } else { NBUMPSIDE(fin->fin_out, ns_uncreate[1]); } RWLOCK_EXIT(&softc->ipf_nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_cmp_rules */ /* Returns: int - 0 == success, else rules do not match. */ /* Parameters: n1(I) - first rule to compare */ /* n2(I) - first rule to compare */ /* */ /* Compare two rules using pointers to each rule. A straight bcmp will not */ /* work as some fields (such as in_dst, in_pkts) actually do change once */ /* the rule has been loaded into the kernel. Whilst this function returns */ /* various non-zero returns, they're strictly to aid in debugging. Use of */ /* this function should simply care if the result is zero or not. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_cmp_rules(n1, n2) - ipnat_t *n1, *n2; +ipf_nat_cmp_rules(ipnat_t *n1, ipnat_t *n2) { if (n1->in_size != n2->in_size) return 1; if (bcmp((char *)&n1->in_v, (char *)&n2->in_v, offsetof(ipnat_t, in_ndst) - offsetof(ipnat_t, in_v)) != 0) return 2; if (bcmp((char *)&n1->in_tuc, (char *)&n2->in_tuc, n1->in_size - offsetof(ipnat_t, in_tuc)) != 0) return 3; if (n1->in_ndst.na_atype != n2->in_ndst.na_atype) return 5; if (n1->in_ndst.na_function != n2->in_ndst.na_function) return 6; if (bcmp((char *)&n1->in_ndst.na_addr, (char *)&n2->in_ndst.na_addr, sizeof(n1->in_ndst.na_addr))) return 7; if (n1->in_nsrc.na_atype != n2->in_nsrc.na_atype) return 8; if (n1->in_nsrc.na_function != n2->in_nsrc.na_function) return 9; if (bcmp((char *)&n1->in_nsrc.na_addr, (char *)&n2->in_nsrc.na_addr, sizeof(n1->in_nsrc.na_addr))) return 10; if (n1->in_odst.na_atype != n2->in_odst.na_atype) return 11; if (n1->in_odst.na_function != n2->in_odst.na_function) return 12; if (bcmp((char *)&n1->in_odst.na_addr, (char *)&n2->in_odst.na_addr, sizeof(n1->in_odst.na_addr))) return 13; if (n1->in_osrc.na_atype != n2->in_osrc.na_atype) return 14; if (n1->in_osrc.na_function != n2->in_osrc.na_function) return 15; if (bcmp((char *)&n1->in_osrc.na_addr, (char *)&n2->in_osrc.na_addr, sizeof(n1->in_osrc.na_addr))) return 16; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_rule_init */ /* Returns: int - 0 == success, else rules do not match. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* n(I) - first rule to compare */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_nat_rule_init(softc, softn, n) - ipf_main_softc_t *softc; - ipf_nat_softc_t *softn; - ipnat_t *n; +ipf_nat_rule_init(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, + ipnat_t *n) { int error = 0; if ((n->in_flags & IPN_SIPRANGE) != 0) n->in_nsrcatype = FRI_RANGE; if ((n->in_flags & IPN_DIPRANGE) != 0) n->in_ndstatype = FRI_RANGE; if ((n->in_flags & IPN_SPLIT) != 0) n->in_ndstatype = FRI_SPLIT; if ((n->in_redir & (NAT_MAP|NAT_REWRITE|NAT_DIVERTUDP)) != 0) n->in_spnext = n->in_spmin; if ((n->in_redir & (NAT_REWRITE|NAT_DIVERTUDP)) != 0) { n->in_dpnext = n->in_dpmin; } else if (n->in_redir == NAT_REDIRECT) { n->in_dpnext = n->in_dpmin; } n->in_stepnext = 0; switch (n->in_v[0]) { case 4 : error = ipf_nat_ruleaddrinit(softc, softn, n); if (error != 0) return error; break; #ifdef USE_INET6 case 6 : error = ipf_nat6_ruleaddrinit(softc, softn, n); if (error != 0) return error; break; #endif default : break; } if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { /* * Prerecord whether or not the destination of the divert * is local or not to the interface the packet is going * to be sent out. */ n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], n->in_ifps[1], &n->in_ndstip6); } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_rule_fini */ /* Returns: int - 0 == success, else rules do not match. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* n(I) - rule to work on */ /* */ /* This function is used to release any objects that were referenced during */ /* the rule initialisation. This is useful both when free'ing the rule and */ /* when handling ioctls that need to initialise these fields but not */ /* actually use them after the ioctl processing has finished. */ /* ------------------------------------------------------------------------ */ static void -ipf_nat_rule_fini(softc, n) - ipf_main_softc_t *softc; - ipnat_t *n; +ipf_nat_rule_fini(ipf_main_softc_t *softc, ipnat_t *n) { if (n->in_odst.na_atype == FRI_LOOKUP && n->in_odst.na_ptr != NULL) ipf_lookup_deref(softc, n->in_odst.na_type, n->in_odst.na_ptr); if (n->in_osrc.na_atype == FRI_LOOKUP && n->in_osrc.na_ptr != NULL) ipf_lookup_deref(softc, n->in_osrc.na_type, n->in_osrc.na_ptr); if (n->in_ndst.na_atype == FRI_LOOKUP && n->in_ndst.na_ptr != NULL) ipf_lookup_deref(softc, n->in_ndst.na_type, n->in_ndst.na_ptr); if (n->in_nsrc.na_atype == FRI_LOOKUP && n->in_nsrc.na_ptr != NULL) ipf_lookup_deref(softc, n->in_nsrc.na_type, n->in_nsrc.na_ptr); if (n->in_divmp != NULL) FREE_MB_T(n->in_divmp); } diff --git a/sys/netpfil/ipfilter/netinet/ip_nat6.c b/sys/netpfil/ipfilter/netinet/ip_nat6.c index b12eaeef674c..75be4a403c3d 100644 --- a/sys/netpfil/ipfilter/netinet/ip_nat6.c +++ b/sys/netpfil/ipfilter/netinet/ip_nat6.c @@ -1,4091 +1,4018 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if defined(_KERNEL) && defined(__NetBSD_Version__) && \ (__NetBSD_Version__ >= 399002000) # include #endif #if !defined(_KERNEL) # include # include # include # define _KERNEL # ifdef ipf_nat6__OpenBSD__ struct file; # endif # include # undef _KERNEL #endif #if defined(_KERNEL) && defined(__FreeBSD__) # include # include #else # include #endif # include # include #include #if defined(_KERNEL) # include # if !defined(__SVR4) # include # endif #endif #if defined(__SVR4) # include # include # ifdef _KERNEL # include # endif # include # include #endif #if defined(__FreeBSD__) # include #endif #include #if defined(__FreeBSD__) # include #endif #ifdef sun # include #endif #include #include #include #include #ifdef RFC1825 # include # include extern struct ifnet vpnif; #endif # include #include #include #include #include "netinet/ip_compat.h" #include #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #include "netinet/ip_lookup.h" #include "netinet/ip_dstlist.h" #include "netinet/ip_sync.h" #if defined(__FreeBSD__) # include #endif #ifdef HAS_SYS_MD5_H # include #else # include "md5.h" #endif /* END OF INCLUDES */ #undef SOCKADDR_IN #define SOCKADDR_IN struct sockaddr_in #if !defined(lint) static const char rcsid[] = "@(#)$Id: ip_nat6.c,v 1.22.2.20 2012/07/22 08:04:23 darren_r Exp $"; #endif #ifdef USE_INET6 static struct hostmap *ipf_nat6_hostmap(ipf_nat_softc_t *, ipnat_t *, i6addr_t *, i6addr_t *, i6addr_t *, u_32_t); static int ipf_nat6_match(fr_info_t *, ipnat_t *); static void ipf_nat6_tabmove(ipf_nat_softc_t *, nat_t *); static int ipf_nat6_decap(fr_info_t *, nat_t *); static int ipf_nat6_nextaddr(fr_info_t *, nat_addr_t *, i6addr_t *, i6addr_t *); static int ipf_nat6_icmpquerytype(int); static int ipf_nat6_out(fr_info_t *, nat_t *, int, u_32_t); static int ipf_nat6_in(fr_info_t *, nat_t *, int, u_32_t); static int ipf_nat6_builddivertmp(ipf_nat_softc_t *, ipnat_t *); static int ipf_nat6_nextaddrinit(ipf_main_softc_t *, char *, nat_addr_t *, int, void *); static int ipf_nat6_insert(ipf_main_softc_t *, ipf_nat_softc_t *, nat_t *); #define NINCLSIDE6(y,x) ATOMIC_INCL(softn->ipf_nat_stats.ns_side6[y].x) #define NBUMPSIDE(y,x) softn->ipf_nat_stats.ns_side[y].x++ #define NBUMPSIDE6(y,x) softn->ipf_nat_stats.ns_side6[y].x++ #define NBUMPSIDE6D(y,x) \ do { \ softn->ipf_nat_stats.ns_side6[y].x++; \ DT(x); \ } while (0) #define NBUMPSIDE6DX(y,x,z) \ do { \ softn->ipf_nat_stats.ns_side6[y].x++; \ DT(z); \ } while (0) /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_ruleaddrinit */ /* Returns: int - 0 == success, else failure */ /* Parameters: in(I) - NAT rule that requires address fields to be init'd */ /* */ /* For each of the source/destination address fields in a NAT rule, call */ /* ipf_nat6_nextaddrinit() to prepare the structure for active duty. Other */ /* IPv6 specific actions can also be taken care of here. */ /* ------------------------------------------------------------------------ */ int -ipf_nat6_ruleaddrinit(softc, softn, n) - ipf_main_softc_t *softc; - ipf_nat_softc_t *softn; - ipnat_t *n; +ipf_nat6_ruleaddrinit(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, + ipnat_t *n) { int idx, error; if (n->in_redir == NAT_BIMAP) { n->in_ndstip6 = n->in_osrcip6; n->in_ndstmsk6 = n->in_osrcmsk6; n->in_odstip6 = n->in_nsrcip6; n->in_odstmsk6 = n->in_nsrcmsk6; } if (n->in_redir & NAT_REDIRECT) idx = 1; else idx = 0; /* * Initialise all of the address fields. */ error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_osrc, 1, n->in_ifps[idx]); if (error != 0) return error; error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_odst, 1, n->in_ifps[idx]); if (error != 0) return error; error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1, n->in_ifps[idx]); if (error != 0) return error; error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_ndst, 1, n->in_ifps[idx]); if (error != 0) return error; if (n->in_redir & NAT_DIVERTUDP) ipf_nat6_builddivertmp(softn, n); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_addrdr */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to add */ /* */ /* Adds a redirect rule to the hash table of redirect rules and the list of */ /* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ /* use by redirect rules. */ /* ------------------------------------------------------------------------ */ void -ipf_nat6_addrdr(softn, n) - ipf_nat_softc_t *softn; - ipnat_t *n; +ipf_nat6_addrdr(ipf_nat_softc_t *softn, ipnat_t *n) { i6addr_t *mask; ipnat_t **np; i6addr_t j; u_int hv; int k; if ((n->in_redir & NAT_BIMAP) == NAT_BIMAP) { k = count6bits(n->in_nsrcmsk6.i6); mask = &n->in_nsrcmsk6; IP6_AND(&n->in_odstip6, &n->in_odstmsk6, &j); hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_rdrrules_sz); } else if (n->in_odstatype == FRI_NORMAL) { k = count6bits(n->in_odstmsk6.i6); mask = &n->in_odstmsk6; IP6_AND(&n->in_odstip6, &n->in_odstmsk6, &j); hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_rdrrules_sz); } else { k = 0; hv = 0; mask = NULL; } ipf_inet6_mask_add(k, mask, &softn->ipf_nat6_rdr_mask); np = softn->ipf_nat_rdr_rules + hv; while (*np != NULL) np = &(*np)->in_rnext; n->in_rnext = NULL; n->in_prnext = np; n->in_hv[0] = hv; n->in_use++; *np = n; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_addmap */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to add */ /* */ /* Adds a NAT map rule to the hash table of rules and the list of loaded */ /* NAT rules. Updates the bitmask indicating which netmasks are in use by */ /* redirect rules. */ /* ------------------------------------------------------------------------ */ void -ipf_nat6_addmap(softn, n) - ipf_nat_softc_t *softn; - ipnat_t *n; +ipf_nat6_addmap(ipf_nat_softc_t *softn, ipnat_t *n) { i6addr_t *mask; ipnat_t **np; i6addr_t j; u_int hv; int k; if (n->in_osrcatype == FRI_NORMAL) { k = count6bits(n->in_osrcmsk6.i6); mask = &n->in_osrcmsk6; IP6_AND(&n->in_osrcip6, &n->in_osrcmsk6, &j); hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_maprules_sz); } else { k = 0; hv = 0; mask = NULL; } ipf_inet6_mask_add(k, mask, &softn->ipf_nat6_map_mask); np = softn->ipf_nat_map_rules + hv; while (*np != NULL) np = &(*np)->in_mnext; n->in_mnext = NULL; n->in_pmnext = np; n->in_hv[1] = hv; n->in_use++; *np = n; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_del_rdr */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to delete */ /* */ /* Removes a NAT rdr rule from the hash table of NAT rdr rules. */ /* ------------------------------------------------------------------------ */ void -ipf_nat6_delrdr(softn, n) - ipf_nat_softc_t *softn; - ipnat_t *n; +ipf_nat6_delrdr(ipf_nat_softc_t *softn, ipnat_t *n) { i6addr_t *mask; int k; if ((n->in_redir & NAT_BIMAP) == NAT_BIMAP) { k = count6bits(n->in_nsrcmsk6.i6); mask = &n->in_nsrcmsk6; } else if (n->in_odstatype == FRI_NORMAL) { k = count6bits(n->in_odstmsk6.i6); mask = &n->in_odstmsk6; } else { k = 0; mask = NULL; } ipf_inet6_mask_del(k, mask, &softn->ipf_nat6_rdr_mask); if (n->in_rnext != NULL) n->in_rnext->in_prnext = n->in_prnext; *n->in_prnext = n->in_rnext; n->in_use--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_delmap */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to delete */ /* */ /* Removes a NAT map rule from the hash table of NAT map rules. */ /* ------------------------------------------------------------------------ */ void -ipf_nat6_delmap(softn, n) - ipf_nat_softc_t *softn; - ipnat_t *n; +ipf_nat6_delmap(ipf_nat_softc_t *softn, ipnat_t *n) { i6addr_t *mask; int k; if (n->in_osrcatype == FRI_NORMAL) { k = count6bits(n->in_osrcmsk6.i6); mask = &n->in_osrcmsk6; } else { k = 0; mask = NULL; } ipf_inet6_mask_del(k, mask, &softn->ipf_nat6_map_mask); if (n->in_mnext != NULL) n->in_mnext->in_pmnext = n->in_pmnext; *n->in_pmnext = n->in_mnext; n->in_use--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_hostmap */ /* Returns: struct hostmap* - NULL if no hostmap could be created, */ /* else a pointer to the hostmapping to use */ /* Parameters: np(I) - pointer to NAT rule */ /* real(I) - real IP address */ /* map(I) - mapped IP address */ /* port(I) - destination port number */ /* Write Locks: ipf_nat */ /* */ /* Check if an ip address has already been allocated for a given mapping */ /* that is not doing port based translation. If is not yet allocated, then */ /* create a new entry if a non-NULL NAT rule pointer has been supplied. */ /* ------------------------------------------------------------------------ */ static struct hostmap * -ipf_nat6_hostmap(softn, np, src, dst, map, port) - ipf_nat_softc_t *softn; - ipnat_t *np; - i6addr_t *src, *dst, *map; - u_32_t port; +ipf_nat6_hostmap(ipf_nat_softc_t *softn, ipnat_t *np, + i6addr_t *src, i6addr_t *dst, i6addr_t *map, u_32_t port) { hostmap_t *hm; u_int hv; hv = (src->i6[3] ^ dst->i6[3]); hv += (src->i6[2] ^ dst->i6[2]); hv += (src->i6[1] ^ dst->i6[1]); hv += (src->i6[0] ^ dst->i6[0]); hv += src->i6[3]; hv += src->i6[2]; hv += src->i6[1]; hv += src->i6[0]; hv += dst->i6[3]; hv += dst->i6[2]; hv += dst->i6[1]; hv += dst->i6[0]; hv %= softn->ipf_nat_hostmap_sz; for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_next) if (IP6_EQ(&hm->hm_osrc6, src) && IP6_EQ(&hm->hm_odst6, dst) && ((np == NULL) || (np == hm->hm_ipnat)) && ((port == 0) || (port == hm->hm_port))) { softn->ipf_nat_stats.ns_hm_addref++; hm->hm_ref++; return hm; } if (np == NULL) { softn->ipf_nat_stats.ns_hm_nullnp++; return NULL; } KMALLOC(hm, hostmap_t *); if (hm) { hm->hm_next = softn->ipf_hm_maplist; hm->hm_pnext = &softn->ipf_hm_maplist; if (softn->ipf_hm_maplist != NULL) softn->ipf_hm_maplist->hm_pnext = &hm->hm_next; softn->ipf_hm_maplist = hm; hm->hm_hnext = softn->ipf_hm_maptable[hv]; hm->hm_phnext = softn->ipf_hm_maptable + hv; if (softn->ipf_hm_maptable[hv] != NULL) softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; softn->ipf_hm_maptable[hv] = hm; hm->hm_ipnat = np; np->in_use++; hm->hm_osrcip6 = *src; hm->hm_odstip6 = *dst; hm->hm_nsrcip6 = *map; hm->hm_ndstip6.i6[0] = 0; hm->hm_ndstip6.i6[1] = 0; hm->hm_ndstip6.i6[2] = 0; hm->hm_ndstip6.i6[3] = 0; hm->hm_ref = 1; hm->hm_port = port; hm->hm_hv = hv; hm->hm_v = 6; softn->ipf_nat_stats.ns_hm_new++; } else { softn->ipf_nat_stats.ns_hm_newfail++; } return hm; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_newmap */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* */ /* Given an empty NAT structure, populate it with new information about a */ /* new NAT session, as defined by the matching NAT rule. */ /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ /* to the new IP address for the translation. */ /* ------------------------------------------------------------------------ */ int -ipf_nat6_newmap(fin, nat, ni) - fr_info_t *fin; - nat_t *nat; - natinfo_t *ni; +ipf_nat6_newmap(fr_info_t *fin, nat_t *nat, natinfo_t *ni) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short st_port, dport, sport, port, sp, dp; i6addr_t in, st_ip; hostmap_t *hm; u_32_t flags; ipnat_t *np; nat_t *natl; int l; /* * If it's an outbound packet which doesn't match any existing * record, then create a new port */ l = 0; hm = NULL; np = ni->nai_np; st_ip = np->in_snip6; st_port = np->in_spnext; flags = nat->nat_flags; if (flags & IPN_ICMPQUERY) { sport = fin->fin_data[1]; dport = 0; } else { sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); } /* * Do a loop until we either run out of entries to try or we find * a NAT mapping that isn't currently being used. This is done * because the change to the source is not (usually) being fixed. */ do { port = 0; in = np->in_nsrc.na_nextaddr; if (l == 0) { /* * Check to see if there is an existing NAT * setup for this IP address pair. */ hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6, &fin->fin_dst6, &in, 0); if (hm != NULL) in = hm->hm_nsrcip6; } else if ((l == 1) && (hm != NULL)) { ipf_nat_hostmapdel(softc, &hm); } nat->nat_hm = hm; if (IP6_ISONES(&np->in_nsrcmsk6) && (np->in_spnext == 0)) { if (l > 0) { NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_1); return -1; } } if ((np->in_redir == NAT_BIMAP) && IP6_EQ(&np->in_osrcmsk6, &np->in_nsrcmsk6)) { i6addr_t temp; /* * map the address block in a 1:1 fashion */ temp.i6[0] = fin->fin_src6.i6[0] & ~np->in_osrcmsk6.i6[0]; temp.i6[1] = fin->fin_src6.i6[1] & ~np->in_osrcmsk6.i6[1]; temp.i6[2] = fin->fin_src6.i6[2] & ~np->in_osrcmsk6.i6[0]; temp.i6[3] = fin->fin_src6.i6[3] & ~np->in_osrcmsk6.i6[3]; in = np->in_nsrcip6; IP6_MERGE(&in, &temp, &np->in_osrc); #ifdef NEED_128BIT_MATH } else if (np->in_redir & NAT_MAPBLK) { if ((l >= np->in_ppip) || ((l > 0) && !(flags & IPN_TCPUDP))) { NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_2); return -1; } /* * map-block - Calculate destination address. */ IP6_MASK(&in, &fin->fin_src6, &np->in_osrcmsk6); in = ntohl(in); inb = in; in.s_addr /= np->in_ippip; in.s_addr &= ntohl(~np->in_nsrcmsk6); in.s_addr += ntohl(np->in_nsrcaddr6); /* * Calculate destination port. */ if ((flags & IPN_TCPUDP) && (np->in_ppip != 0)) { port = ntohs(sport) + l; port %= np->in_ppip; port += np->in_ppip * (inb.s_addr % np->in_ippip); port += MAPBLK_MINPORT; port = htons(port); } #endif } else if (IP6_ISZERO(&np->in_nsrcaddr) && IP6_ISONES(&np->in_nsrcmsk)) { /* * 0/32 - use the interface's IP address. */ if ((l > 0) || ipf_ifpaddr(softc, 6, FRI_NORMAL, fin->fin_ifp, &in, NULL) == -1) { NBUMPSIDE6DX(1, ns_new_ifpaddr, ns_new_ifpaddr_1); return -1; } } else if (IP6_ISZERO(&np->in_nsrcip6) && IP6_ISZERO(&np->in_nsrcmsk6)) { /* * 0/0 - use the original source address/port. */ if (l > 0) { NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_3); return -1; } in = fin->fin_src6; } else if (!IP6_ISONES(&np->in_nsrcmsk6) && (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) { IP6_INC(&np->in_snip6); } natl = NULL; if ((flags & IPN_TCPUDP) && ((np->in_redir & NAT_MAPBLK) == 0) && (np->in_flags & IPN_AUTOPORTMAP)) { #ifdef NEED_128BIT_MATH /* * "ports auto" (without map-block) */ if ((l > 0) && (l % np->in_ppip == 0)) { if ((l > np->in_ppip) && !IP6_ISONES(&np->in_nsrcmsk)) { IP6_INC(&np->in_snip6) } } if (np->in_ppip != 0) { port = ntohs(sport); port += (l % np->in_ppip); port %= np->in_ppip; port += np->in_ppip * (ntohl(fin->fin_src6) % np->in_ippip); port += MAPBLK_MINPORT; port = htons(port); } #endif } else if (((np->in_redir & NAT_MAPBLK) == 0) && (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) { /* * Standard port translation. Select next port. */ if (np->in_flags & IPN_SEQUENTIAL) { port = np->in_spnext; } else { port = ipf_random() % (np->in_spmax - np->in_spmin + 1); port += np->in_spmin; } port = htons(port); np->in_spnext++; if (np->in_spnext > np->in_spmax) { np->in_spnext = np->in_spmin; if (!IP6_ISONES(&np->in_nsrcmsk6)) { IP6_INC(&np->in_snip6); } } } if (np->in_flags & IPN_SIPRANGE) { if (IP6_GT(&np->in_snip, &np->in_nsrcmsk)) np->in_snip6 = np->in_nsrcip6; } else { i6addr_t a1, a2; a1 = np->in_snip6; IP6_INC(&a1); IP6_AND(&a1, &np->in_nsrcmsk6, &a2); if (!IP6_ISONES(&np->in_nsrcmsk6) && IP6_GT(&a2, &np->in_nsrcip6)) { IP6_ADD(&np->in_nsrcip6, 1, &np->in_snip6); } } if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) port = sport; /* * Here we do a lookup of the connection as seen from * the outside. If an IP# pair already exists, try * again. So if you have A->B becomes C->B, you can * also have D->E become C->E but not D->B causing * another C->B. Also take protocol and ports into * account when determining whether a pre-existing * NAT setup will cause an external conflict where * this is appropriate. */ sp = fin->fin_data[0]; dp = fin->fin_data[1]; fin->fin_data[0] = fin->fin_data[1]; fin->fin_data[1] = ntohs(port); natl = ipf_nat6_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)fin->fin_p, &fin->fin_dst6.in6, &in.in6); fin->fin_data[0] = sp; fin->fin_data[1] = dp; /* * Has the search wrapped around and come back to the * start ? */ if ((natl != NULL) && (np->in_spnext != 0) && (st_port == np->in_spnext) && (!IP6_ISZERO(&np->in_snip6) && IP6_EQ(&st_ip, &np->in_snip6))) { NBUMPSIDE6D(1, ns_wrap); return -1; } l++; } while (natl != NULL); /* Setup the NAT table */ nat->nat_osrc6 = fin->fin_src6; nat->nat_nsrc6 = in; nat->nat_odst6 = fin->fin_dst6; nat->nat_ndst6 = fin->fin_dst6; if (nat->nat_hm == NULL) nat->nat_hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6, &fin->fin_dst6, &nat->nat_nsrc6, 0); if (flags & IPN_TCPUDP) { nat->nat_osport = sport; nat->nat_nsport = port; /* sport */ nat->nat_odport = dport; nat->nat_ndport = dport; ((tcphdr_t *)fin->fin_dp)->th_sport = port; } else if (flags & IPN_ICMPQUERY) { nat->nat_oicmpid = fin->fin_data[1]; ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = port; nat->nat_nicmpid = port; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_newrdr */ /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ /* allow rule to be moved if IPN_ROUNDR is set. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* */ /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ /* to the new IP address for the translation. */ /* ------------------------------------------------------------------------ */ int -ipf_nat6_newrdr(fin, nat, ni) - fr_info_t *fin; - nat_t *nat; - natinfo_t *ni; +ipf_nat6_newrdr(fr_info_t *fin, nat_t *nat, natinfo_t *ni) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short nport, dport, sport; u_short sp, dp; hostmap_t *hm; u_32_t flags; i6addr_t in; ipnat_t *np; nat_t *natl; int move; move = 1; hm = NULL; in.i6[0] = 0; in.i6[1] = 0; in.i6[2] = 0; in.i6[3] = 0; np = ni->nai_np; flags = nat->nat_flags; if (flags & IPN_ICMPQUERY) { dport = fin->fin_data[1]; sport = 0; } else { sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); } /* TRACE sport, dport */ /* * If the matching rule has IPN_STICKY set, then we want to have the * same rule kick in as before. Why would this happen? If you have * a collection of rdr rules with "round-robin sticky", the current * packet might match a different one to the previous connection but * we want the same destination to be used. */ if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && ((np->in_flags & IPN_STICKY) != 0)) { hm = ipf_nat6_hostmap(softn, NULL, &fin->fin_src6, &fin->fin_dst6, &in, (u_32_t)dport); if (hm != NULL) { in = hm->hm_ndstip6; np = hm->hm_ipnat; ni->nai_np = np; move = 0; } } /* * Otherwise, it's an inbound packet. Most likely, we don't * want to rewrite source ports and source addresses. Instead, * we want to rewrite to a fixed internal address and fixed * internal port. */ if (np->in_flags & IPN_SPLIT) { in = np->in_dnip6; if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { hm = ipf_nat6_hostmap(softn, NULL, &fin->fin_src6, &fin->fin_dst6, &in, (u_32_t)dport); if (hm != NULL) { in = hm->hm_ndstip6; move = 0; } } if (hm == NULL || hm->hm_ref == 1) { if (IP6_EQ(&np->in_ndstip6, &in)) { np->in_dnip6 = np->in_ndstmsk6; move = 0; } else { np->in_dnip6 = np->in_ndstip6; } } } else if (IP6_ISZERO(&np->in_ndstaddr) && IP6_ISONES(&np->in_ndstmsk)) { /* * 0/32 - use the interface's IP address. */ if (ipf_ifpaddr(softc, 6, FRI_NORMAL, fin->fin_ifp, &in, NULL) == -1) { NBUMPSIDE6DX(0, ns_new_ifpaddr, ns_new_ifpaddr_2); return -1; } } else if (IP6_ISZERO(&np->in_ndstip6) && IP6_ISZERO(&np->in_ndstmsk6)) { /* * 0/0 - use the original destination address/port. */ in = fin->fin_dst6; } else if (np->in_redir == NAT_BIMAP && IP6_EQ(&np->in_ndstmsk6, &np->in_odstmsk6)) { i6addr_t temp; /* * map the address block in a 1:1 fashion */ temp.i6[0] = fin->fin_dst6.i6[0] & ~np->in_osrcmsk6.i6[0]; temp.i6[1] = fin->fin_dst6.i6[1] & ~np->in_osrcmsk6.i6[1]; temp.i6[2] = fin->fin_dst6.i6[2] & ~np->in_osrcmsk6.i6[0]; temp.i6[3] = fin->fin_dst6.i6[3] & ~np->in_osrcmsk6.i6[3]; in = np->in_ndstip6; IP6_MERGE(&in, &temp, &np->in_ndstmsk6); } else { in = np->in_ndstip6; } if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) nport = dport; else { /* * Whilst not optimized for the case where * pmin == pmax, the gain is not significant. */ if (((np->in_flags & IPN_FIXEDDPORT) == 0) && (np->in_odport != np->in_dtop)) { nport = ntohs(dport) - np->in_odport + np->in_dpmax; nport = htons(nport); } else { nport = htons(np->in_dpnext); np->in_dpnext++; if (np->in_dpnext > np->in_dpmax) np->in_dpnext = np->in_dpmin; } } /* * When the redirect-to address is set to 0.0.0.0, just * assume a blank `forwarding' of the packet. We don't * setup any translation for this either. */ if (IP6_ISZERO(&in)) { if (nport == dport) { NBUMPSIDE6D(0, ns_xlate_null); return -1; } in = fin->fin_dst6; } /* * Check to see if this redirect mapping already exists and if * it does, return "failure" (allowing it to be created will just * cause one or both of these "connections" to stop working.) */ sp = fin->fin_data[0]; dp = fin->fin_data[1]; fin->fin_data[1] = fin->fin_data[0]; fin->fin_data[0] = ntohs(nport); natl = ipf_nat6_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)fin->fin_p, &in.in6, &fin->fin_src6.in6); fin->fin_data[0] = sp; fin->fin_data[1] = dp; if (natl != NULL) { NBUMPSIDE6D(0, ns_xlate_exists); return -1; } nat->nat_ndst6 = in; nat->nat_odst6 = fin->fin_dst6; nat->nat_nsrc6 = fin->fin_src6; nat->nat_osrc6 = fin->fin_src6; if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) nat->nat_hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6, &fin->fin_dst6, &in, (u_32_t)dport); if (flags & IPN_TCPUDP) { nat->nat_odport = dport; nat->nat_ndport = nport; nat->nat_osport = sport; nat->nat_nsport = sport; ((tcphdr_t *)fin->fin_dp)->th_dport = nport; } else if (flags & IPN_ICMPQUERY) { nat->nat_oicmpid = fin->fin_data[1]; ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = nport; nat->nat_nicmpid = nport; } return move; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_add */ /* Returns: nat6_t* - NULL == failure to create new NAT structure, */ /* else pointer to new NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* np(I) - pointer to NAT rule */ /* natsave(I) - pointer to where to store NAT struct pointer */ /* flags(I) - flags describing the current packet */ /* direction(I) - direction of packet (in/out) */ /* Write Lock: ipf_nat */ /* */ /* Attempts to create a new NAT entry. Does not actually change the packet */ /* in any way. */ /* */ /* This fucntion is in three main parts: (1) deal with creating a new NAT */ /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ /* and (3) building that structure and putting it into the NAT table(s). */ /* */ /* NOTE: natsave should NOT be used top point back to an ipstate_t struct */ /* as it can result in memory being corrupted. */ /* ------------------------------------------------------------------------ */ nat_t * -ipf_nat6_add(fin, np, natsave, flags, direction) - fr_info_t *fin; - ipnat_t *np; - nat_t **natsave; - u_int flags; - int direction; +ipf_nat6_add(fr_info_t *fin, ipnat_t *np, nat_t **natsave, u_int flags, + int direction) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; hostmap_t *hm = NULL; nat_t *nat, *natl; natstat_t *nsp; u_int nflags; natinfo_t ni; int move; #if SOLARIS && defined(_KERNEL) && defined(ICK_M_CTL_MAGIC) qpktinfo_t *qpi = fin->fin_qpi; #endif nsp = &softn->ipf_nat_stats; if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) > softn->ipf_nat_table_wm_high) { softn->ipf_nat_doflush = 1; } if (nsp->ns_active >= softn->ipf_nat_table_max) { NBUMPSIDE6(fin->fin_out, ns_table_max); return NULL; } move = 1; nflags = np->in_flags & flags; nflags &= NAT_FROMRULE; ni.nai_np = np; ni.nai_dport = 0; ni.nai_sport = 0; /* Give me a new nat */ KMALLOC(nat, nat_t *); if (nat == NULL) { NBUMPSIDE6(fin->fin_out, ns_memfail); /* * Try to automatically tune the max # of entries in the * table allowed to be less than what will cause kmem_alloc() * to fail and try to eliminate panics due to out of memory * conditions arising. */ if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) && (nsp->ns_active > 100)) { softn->ipf_nat_table_max = nsp->ns_active - 100; printf("table_max reduced to %d\n", softn->ipf_nat_table_max); } return NULL; } if (flags & IPN_ICMPQUERY) { /* * In the ICMP query NAT code, we translate the ICMP id fields * to make them unique. This is indepedent of the ICMP type * (e.g. in the unlikely event that a host sends an echo and * an tstamp request with the same id, both packets will have * their ip address/id field changed in the same way). */ /* The icmp6_id field is used by the sender to identify the * process making the icmp request. (the receiver justs * copies it back in its response). So, it closely matches * the concept of source port. We overlay sport, so we can * maximally reuse the existing code. */ ni.nai_sport = fin->fin_data[1]; ni.nai_dport = 0; } bzero((char *)nat, sizeof(*nat)); nat->nat_flags = flags; nat->nat_redir = np->in_redir; nat->nat_dir = direction; nat->nat_pr[0] = fin->fin_p; nat->nat_pr[1] = fin->fin_p; /* * Search the current table for a match and create a new mapping * if there is none found. */ if (np->in_redir & NAT_DIVERTUDP) { move = ipf_nat6_newdivert(fin, nat, &ni); } else if (np->in_redir & NAT_REWRITE) { move = ipf_nat6_newrewrite(fin, nat, &ni); } else if (direction == NAT_OUTBOUND) { /* * We can now arrange to call this for the same connection * because ipf_nat6_new doesn't protect the code path into * this function. */ natl = ipf_nat6_outlookup(fin, nflags, (u_int)fin->fin_p, &fin->fin_src6.in6, &fin->fin_dst6.in6); if (natl != NULL) { KFREE(nat); nat = natl; goto done; } move = ipf_nat6_newmap(fin, nat, &ni); } else { /* * NAT_INBOUND is used for redirects rules */ natl = ipf_nat6_inlookup(fin, nflags, (u_int)fin->fin_p, &fin->fin_src6.in6, &fin->fin_dst6.in6); if (natl != NULL) { KFREE(nat); nat = natl; goto done; } move = ipf_nat6_newrdr(fin, nat, &ni); } if (move == -1) goto badnat; np = ni.nai_np; nat->nat_mssclamp = np->in_mssclamp; nat->nat_me = natsave; nat->nat_fr = fin->fin_fr; nat->nat_rev = fin->fin_rev; nat->nat_ptr = np; nat->nat_dlocal = np->in_dlocal; if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) { if (ipf_proxy_new(fin, nat) == -1) { NBUMPSIDE6D(fin->fin_out, ns_appr_fail); goto badnat; } } nat->nat_ifps[0] = np->in_ifps[0]; if (np->in_ifps[0] != NULL) { COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]); } nat->nat_ifps[1] = np->in_ifps[1]; if (np->in_ifps[1] != NULL) { COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]); } if (ipf_nat6_finalise(fin, nat) == -1) { goto badnat; } np->in_use++; if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) { ipf_nat6_delrdr(softn, np); ipf_nat6_addrdr(softn, np); } else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) { ipf_nat6_delmap(softn, np); ipf_nat6_addmap(softn, np); } } if (flags & SI_WILDP) nsp->ns_wilds++; softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]++; goto done; badnat: NBUMPSIDE6(fin->fin_out, ns_badnatnew); if ((hm = nat->nat_hm) != NULL) ipf_nat_hostmapdel(softc, &hm); KFREE(nat); nat = NULL; done: if (nat != NULL && np != NULL) np->in_hits++; if (natsave != NULL) *natsave = nat; return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_finalise */ /* Returns: int - 0 == sucess, -1 == failure */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* Write Lock: ipf_nat */ /* */ /* This is the tail end of constructing a new NAT entry and is the same */ /* for both IPv4 and IPv6. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ int -ipf_nat6_finalise(fin, nat) - fr_info_t *fin; - nat_t *nat; +ipf_nat6_finalise(fr_info_t *fin, nat_t *nat) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sum1, sum2, sumd; frentry_t *fr; u_32_t flags; flags = nat->nat_flags; switch (fin->fin_p) { case IPPROTO_ICMPV6 : sum1 = LONG_SUM6(&nat->nat_osrc6); sum1 += ntohs(nat->nat_oicmpid); sum2 = LONG_SUM6(&nat->nat_nsrc6); sum2 += ntohs(nat->nat_nicmpid); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); sum1 = LONG_SUM6(&nat->nat_odst6); sum2 = LONG_SUM6(&nat->nat_ndst6); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); break; case IPPROTO_TCP : case IPPROTO_UDP : sum1 = LONG_SUM6(&nat->nat_osrc6); sum1 += ntohs(nat->nat_osport); sum2 = LONG_SUM6(&nat->nat_nsrc6); sum2 += ntohs(nat->nat_nsport); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); sum1 = LONG_SUM6(&nat->nat_odst6); sum1 += ntohs(nat->nat_odport); sum2 = LONG_SUM6(&nat->nat_ndst6); sum2 += ntohs(nat->nat_ndport); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); break; default : sum1 = LONG_SUM6(&nat->nat_osrc6); sum2 = LONG_SUM6(&nat->nat_nsrc6); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); sum1 = LONG_SUM6(&nat->nat_odst6); sum2 = LONG_SUM6(&nat->nat_ndst6); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); break; } /* * Compute the partial checksum, just in case. * This is only ever placed into outbound packets so care needs * to be taken over which pair of addresses are used. */ if (nat->nat_dir == NAT_OUTBOUND) { sum1 = LONG_SUM6(&nat->nat_nsrc6); sum1 += LONG_SUM6(&nat->nat_ndst6); } else { sum1 = LONG_SUM6(&nat->nat_osrc6); sum1 += LONG_SUM6(&nat->nat_odst6); } sum1 += nat->nat_pr[1]; nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16); if ((nat->nat_flags & SI_CLONE) == 0) nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat); if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { nat->nat_mtu[0] = GETIFMTU_6(nat->nat_ifps[0]); } if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { nat->nat_mtu[1] = GETIFMTU_6(nat->nat_ifps[1]); } nat->nat_v[0] = 6; nat->nat_v[1] = 6; if (ipf_nat6_insert(softc, softn, nat) == 0) { if (softn->ipf_nat_logging) ipf_nat_log(softc, softn, nat, NL_NEW); fr = nat->nat_fr; if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } return 0; } NBUMPSIDE6D(fin->fin_out, ns_unfinalised); /* * nat6_insert failed, so cleanup time... */ if (nat->nat_sync != NULL) ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); return -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_insert */ /* Returns: int - 0 == sucess, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to NAT structure */ /* Write Lock: ipf_nat */ /* */ /* Insert a NAT entry into the hash tables for searching and add it to the */ /* list of active NAT entries. Adjust global counters when complete. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat6_insert(softc, softn, nat) - ipf_main_softc_t *softc; - ipf_nat_softc_t *softn; - nat_t *nat; +ipf_nat6_insert(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, nat_t *nat) { u_int hv1, hv2; u_32_t sp, dp; ipnat_t *in; /* * Try and return an error as early as possible, so calculate the hash * entry numbers first and then proceed. */ if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { if ((nat->nat_flags & IPN_TCPUDP) != 0) { sp = nat->nat_osport; dp = nat->nat_odport; } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { sp = 0; dp = nat->nat_oicmpid; } else { sp = 0; dp = 0; } hv1 = NAT_HASH_FN6(&nat->nat_osrc6, sp, 0xffffffff); hv1 = NAT_HASH_FN6(&nat->nat_odst6, hv1 + dp, softn->ipf_nat_table_sz); /* * TRACE nat6_osrc6, nat6_osport, nat6_odst6, * nat6_odport, hv1 */ if ((nat->nat_flags & IPN_TCPUDP) != 0) { sp = nat->nat_nsport; dp = nat->nat_ndport; } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { sp = 0; dp = nat->nat_nicmpid; } else { sp = 0; dp = 0; } hv2 = NAT_HASH_FN6(&nat->nat_nsrc6, sp, 0xffffffff); hv2 = NAT_HASH_FN6(&nat->nat_ndst6, hv2 + dp, softn->ipf_nat_table_sz); /* * TRACE nat6_nsrcaddr, nat6_nsport, nat6_ndstaddr, * nat6_ndport, hv1 */ } else { hv1 = NAT_HASH_FN6(&nat->nat_osrc6, 0, 0xffffffff); hv1 = NAT_HASH_FN6(&nat->nat_odst6, hv1, softn->ipf_nat_table_sz); /* TRACE nat6_osrcip6, nat6_odstip6, hv1 */ hv2 = NAT_HASH_FN6(&nat->nat_nsrc6, 0, 0xffffffff); hv2 = NAT_HASH_FN6(&nat->nat_ndst6, hv2, softn->ipf_nat_table_sz); /* TRACE nat6_nsrcip6, nat6_ndstip6, hv2 */ } nat->nat_hv[0] = hv1; nat->nat_hv[1] = hv2; MUTEX_INIT(&nat->nat_lock, "nat entry lock"); in = nat->nat_ptr; nat->nat_ref = nat->nat_me ? 2 : 1; nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], nat->nat_v[0]); if (nat->nat_ifnames[1][0] != '\0') { nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; nat->nat_ifps[1] = ipf_resolvenic(softc, nat->nat_ifnames[1], nat->nat_v[1]); } else if (in->in_ifnames[1] != -1) { char *name; name = in->in_names + in->in_ifnames[1]; if (name[1] != '\0' && name[0] != '-' && name[0] != '*') { (void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0], LIFNAMSIZ); nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; nat->nat_ifps[1] = nat->nat_ifps[0]; } } if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { nat->nat_mtu[0] = GETIFMTU_6(nat->nat_ifps[0]); } if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { nat->nat_mtu[1] = GETIFMTU_6(nat->nat_ifps[1]); } return ipf_nat_hashtab_add(softc, softn, nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_icmperrorlookup */ /* Returns: nat6_t* - point to matching NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* dir(I) - direction of packet (in/out) */ /* */ /* Check if the ICMP error message is related to an existing TCP, UDP or */ /* ICMP query nat entry. It is assumed that the packet is already of the */ /* the required length. */ /* ------------------------------------------------------------------------ */ nat_t * -ipf_nat6_icmperrorlookup(fin, dir) - fr_info_t *fin; - int dir; +ipf_nat6_icmperrorlookup(fr_info_t *fin, int dir) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; struct icmp6_hdr *icmp6, *orgicmp; int flags = 0, type, minlen; nat_stat_side_t *nside; tcphdr_t *tcp = NULL; u_short data[2]; ip6_t *oip6; nat_t *nat; u_int p; minlen = 40; icmp6 = fin->fin_dp; type = icmp6->icmp6_type; nside = &softn->ipf_nat_stats.ns_side6[fin->fin_out]; /* * Does it at least have the return (basic) IP header ? * Only a basic IP header (no options) should be with an ICMP error * header. Also, if it's not an error type, then return. */ if (!(fin->fin_flx & FI_ICMPERR)) { ATOMIC_INCL(nside->ns_icmp_basic); return NULL; } /* * Check packet size */ if (fin->fin_plen < ICMP6ERR_IPICMPHLEN) { ATOMIC_INCL(nside->ns_icmp_size); return NULL; } oip6 = (ip6_t *)((char *)fin->fin_dp + 8); /* * Is the buffer big enough for all of it ? It's the size of the IP * header claimed in the encapsulated part which is of concern. It * may be too big to be in this buffer but not so big that it's * outside the ICMP packet, leading to TCP deref's causing problems. * This is possible because we don't know how big oip_hl is when we * do the pullup early in ipf_check() and thus can't gaurantee it is * all here now. */ #ifdef _KERNEL { mb_t *m; m = fin->fin_m; # if SOLARIS if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) { ATOMIC_INCL(nside->ns_icmp_mbuf); return NULL; } # else if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)fin->fin_ip + M_LEN(m)) { ATOMIC_INCL(nside->ns_icmp_mbuf); return NULL; } # endif } #endif if (IP6_NEQ(&fin->fin_dst6, &oip6->ip6_src)) { ATOMIC_INCL(nside->ns_icmp_address); return NULL; } p = oip6->ip6_nxt; if (p == IPPROTO_TCP) flags = IPN_TCP; else if (p == IPPROTO_UDP) flags = IPN_UDP; else if (p == IPPROTO_ICMPV6) { orgicmp = (struct icmp6_hdr *)(oip6 + 1); /* see if this is related to an ICMP query */ if (ipf_nat6_icmpquerytype(orgicmp->icmp6_type)) { data[0] = fin->fin_data[0]; data[1] = fin->fin_data[1]; fin->fin_data[0] = 0; fin->fin_data[1] = orgicmp->icmp6_id; flags = IPN_ICMPERR|IPN_ICMPQUERY; /* * NOTE : dir refers to the direction of the original * ip packet. By definition the icmp error * message flows in the opposite direction. */ if (dir == NAT_INBOUND) nat = ipf_nat6_inlookup(fin, flags, p, &oip6->ip6_dst, &oip6->ip6_src); else nat = ipf_nat6_outlookup(fin, flags, p, &oip6->ip6_dst, &oip6->ip6_src); fin->fin_data[0] = data[0]; fin->fin_data[1] = data[1]; return nat; } } if (flags & IPN_TCPUDP) { minlen += 8; /* + 64bits of data to get ports */ /* TRACE (fin,minlen) */ if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) { ATOMIC_INCL(nside->ns_icmp_short); return NULL; } data[0] = fin->fin_data[0]; data[1] = fin->fin_data[1]; tcp = (tcphdr_t *)(oip6 + 1); fin->fin_data[0] = ntohs(tcp->th_dport); fin->fin_data[1] = ntohs(tcp->th_sport); if (dir == NAT_INBOUND) { nat = ipf_nat6_inlookup(fin, flags, p, &oip6->ip6_dst, &oip6->ip6_src); } else { nat = ipf_nat6_outlookup(fin, flags, p, &oip6->ip6_dst, &oip6->ip6_src); } fin->fin_data[0] = data[0]; fin->fin_data[1] = data[1]; return nat; } if (dir == NAT_INBOUND) nat = ipf_nat6_inlookup(fin, 0, p, &oip6->ip6_dst, &oip6->ip6_src); else nat = ipf_nat6_outlookup(fin, 0, p, &oip6->ip6_dst, &oip6->ip6_src); return nat; } /* result = ip1 - ip2 */ u_32_t -ipf_nat6_ip6subtract(ip1, ip2) - i6addr_t *ip1, *ip2; +ipf_nat6_ip6subtract(i6addr_t *ip1, i6addr_t *ip2) { i6addr_t l1, l2, d; u_short *s1, *s2, *ds; u_32_t r; int i, neg; neg = 0; l1 = *ip1; l2 = *ip2; s1 = (u_short *)&l1; s2 = (u_short *)&l2; ds = (u_short *)&d; for (i = 7; i > 0; i--) { if (s1[i] > s2[i]) { ds[i] = s2[i] + 0x10000 - s1[i]; s2[i - 1] += 0x10000; } else { ds[i] = s2[i] - s1[i]; } } if (s2[0] > s1[0]) { ds[0] = s2[0] + 0x10000 - s1[0]; neg = 1; } else { ds[0] = s2[0] - s1[0]; } for (i = 0, r = 0; i < 8; i++) { r += ds[i]; } return r; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_icmperror */ /* Returns: nat6_t* - point to matching NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* nflags(I) - NAT flags for this packet */ /* dir(I) - direction of packet (in/out) */ /* */ /* Fix up an ICMP packet which is an error message for an existing NAT */ /* session. This will correct both packet header data and checksums. */ /* */ /* This should *ONLY* be used for incoming ICMP error packets to make sure */ /* a NAT'd ICMP packet gets correctly recognised. */ /* ------------------------------------------------------------------------ */ nat_t * -ipf_nat6_icmperror(fin, nflags, dir) - fr_info_t *fin; - u_int *nflags; - int dir; +ipf_nat6_icmperror(fr_info_t *fin, u_int *nflags, int dir) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sum1, sum2, sumd, sumd2; i6addr_t a1, a2, a3, a4; struct icmp6_hdr *icmp6; int flags, dlen, odst; u_short *csump; tcphdr_t *tcp; ip6_t *oip6; nat_t *nat; void *dp; if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { NBUMPSIDE6D(fin->fin_out, ns_icmp_short); return NULL; } /* * ipf_nat6_icmperrorlookup() will return NULL for `defective' packets. */ if ((fin->fin_v != 6) || !(nat = ipf_nat6_icmperrorlookup(fin, dir))) { NBUMPSIDE6D(fin->fin_out, ns_icmp_notfound); return NULL; } tcp = NULL; csump = NULL; flags = 0; sumd2 = 0; *nflags = IPN_ICMPERR; icmp6 = fin->fin_dp; oip6 = (ip6_t *)((u_char *)icmp6 + sizeof(*icmp6)); dp = (u_char *)oip6 + sizeof(*oip6); if (oip6->ip6_nxt == IPPROTO_TCP) { tcp = (tcphdr_t *)dp; csump = (u_short *)&tcp->th_sum; flags = IPN_TCP; } else if (oip6->ip6_nxt == IPPROTO_UDP) { udphdr_t *udp; udp = (udphdr_t *)dp; tcp = (tcphdr_t *)dp; csump = (u_short *)&udp->uh_sum; flags = IPN_UDP; } else if (oip6->ip6_nxt == IPPROTO_ICMPV6) flags = IPN_ICMPQUERY; dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip); /* * Need to adjust ICMP header to include the real IP#'s and * port #'s. Only apply a checksum change relative to the * IP address change as it will be modified again in ipf_nat6_checkout * for both address and port. Two checksum changes are * necessary for the two header address changes. Be careful * to only modify the checksum once for the port # and twice * for the IP#. */ /* * Step 1 * Fix the IP addresses in the offending IP packet. You also need * to adjust the IP header checksum of that offending IP packet. * * Normally, you would expect that the ICMP checksum of the * ICMP error message needs to be adjusted as well for the * IP address change in oip. * However, this is a NOP, because the ICMP checksum is * calculated over the complete ICMP packet, which includes the * changed oip IP addresses and oip6->ip6_sum. However, these * two changes cancel each other out (if the delta for * the IP address is x, then the delta for ip_sum is minus x), * so no change in the icmp_cksum is necessary. * * Inbound ICMP * ------------ * MAP rule, SRC=a,DST=b -> SRC=c,DST=b * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b) * - OIP_SRC(c)=nat6_newsrcip, OIP_DST(b)=nat6_newdstip *=> OIP_SRC(c)=nat6_oldsrcip, OIP_DST(b)=nat6_olddstip * * RDR rule, SRC=a,DST=b -> SRC=a,DST=c * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat6_olddstip, OIP_DST(a)=nat6_oldsrcip *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip * * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d) * - OIP_SRC(c)=nat6_newsrcip, OIP_DST(d)=nat6_newdstip *=> OIP_SRC(c)=nat6_oldsrcip, OIP_DST(d)=nat6_olddstip * * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat6_olddstip, OIP_DST(a)=nat6_oldsrcip *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip * * Outbound ICMP * ------------- * MAP rule, SRC=a,DST=b -> SRC=c,DST=b * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat6_olddstip, OIP_DST(a)=nat6_oldsrcip *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip * * RDR rule, SRC=a,DST=b -> SRC=a,DST=c * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c) * - OIP_SRC(a)=nat6_newsrcip, OIP_DST(c)=nat6_newdstip *=> OIP_SRC(a)=nat6_oldsrcip, OIP_DST(c)=nat6_olddstip * * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d) * - OIP_SRC(c)=nat6_olddstip, OIP_DST(d)=nat6_oldsrcip *=> OIP_SRC(b)=nat6_newdstip, OIP_DST(a)=nat6_newsrcip * * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat6_newsrcip, OIP_DST(a)=nat6_newdstip *=> OIP_SRC(a)=nat6_oldsrcip, OIP_DST(c)=nat6_olddstip */ if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) || ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) { a1 = nat->nat_osrc6; a4.in6 = oip6->ip6_src; a3 = nat->nat_odst6; a2.in6 = oip6->ip6_dst; oip6->ip6_src = a1.in6; oip6->ip6_dst = a3.in6; odst = 1; } else { a1 = nat->nat_ndst6; a2.in6 = oip6->ip6_dst; a3 = nat->nat_nsrc6; a4.in6 = oip6->ip6_src; oip6->ip6_dst = a3.in6; oip6->ip6_src = a1.in6; odst = 0; } sumd = 0; if (IP6_NEQ(&a3, &a2) || IP6_NEQ(&a1, &a4)) { if (IP6_GT(&a3, &a2)) { sumd = ipf_nat6_ip6subtract(&a2, &a3); sumd--; } else { sumd = ipf_nat6_ip6subtract(&a2, &a3); } if (IP6_GT(&a1, &a4)) { sumd += ipf_nat6_ip6subtract(&a4, &a1); sumd--; } else { sumd += ipf_nat6_ip6subtract(&a4, &a1); } sumd = ~sumd; } sumd2 = sumd; sum1 = 0; sum2 = 0; /* * Fix UDP pseudo header checksum to compensate for the * IP address change. */ if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { u_32_t sum3, sum4; /* * Step 2 : * For offending TCP/UDP IP packets, translate the ports as * well, based on the NAT specification. Of course such * a change may be reflected in the ICMP checksum as well. * * Since the port fields are part of the TCP/UDP checksum * of the offending IP packet, you need to adjust that checksum * as well... except that the change in the port numbers should * be offset by the checksum change. However, the TCP/UDP * checksum will also need to change if there has been an * IP address change. */ if (odst == 1) { sum1 = ntohs(nat->nat_osport); sum4 = ntohs(tcp->th_sport); sum3 = ntohs(nat->nat_odport); sum2 = ntohs(tcp->th_dport); tcp->th_sport = htons(sum1); tcp->th_dport = htons(sum3); } else { sum1 = ntohs(nat->nat_ndport); sum2 = ntohs(tcp->th_dport); sum3 = ntohs(nat->nat_nsport); sum4 = ntohs(tcp->th_sport); tcp->th_dport = htons(sum3); tcp->th_sport = htons(sum1); } sumd += sum1 - sum4; sumd += sum3 - sum2; if (sumd != 0 || sumd2 != 0) { /* * At this point, sumd is the delta to apply to the * TCP/UDP header, given the changes in both the IP * address and the ports and sumd2 is the delta to * apply to the ICMP header, given the IP address * change delta that may need to be applied to the * TCP/UDP checksum instead. * * If we will both the IP and TCP/UDP checksums * then the ICMP checksum changes by the address * delta applied to the TCP/UDP checksum. If we * do not change the TCP/UDP checksum them we * apply the delta in ports to the ICMP checksum. */ if (oip6->ip6_nxt == IPPROTO_UDP) { if ((dlen >= 8) && (*csump != 0)) { ipf_fix_datacksum(csump, sumd); } else { sumd2 = sum4 - sum1; if (sum1 > sum4) sumd2--; sumd2 += sum2 - sum3; if (sum3 > sum2) sumd2--; } } else if (oip6->ip6_nxt == IPPROTO_TCP) { if (dlen >= 18) { ipf_fix_datacksum(csump, sumd); } else { sumd2 = sum4 - sum1; if (sum1 > sum4) sumd2--; sumd2 += sum2 - sum3; if (sum3 > sum2) sumd2--; } } if (sumd2 != 0) { sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); ipf_fix_incksum(0, &icmp6->icmp6_cksum, sumd2, 0); } } } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { struct icmp6_hdr *orgicmp; /* * XXX - what if this is bogus hl and we go off the end ? * In this case, ipf_nat6_icmperrorlookup() will have * returned NULL. */ orgicmp = (struct icmp6_hdr *)dp; if (odst == 1) { if (orgicmp->icmp6_id != nat->nat_osport) { /* * Fix ICMP checksum (of the offening ICMP * query packet) to compensate the change * in the ICMP id of the offending ICMP * packet. * * Since you modify orgicmp->icmp6_id with * a delta (say x) and you compensate that * in origicmp->icmp6_cksum with a delta * minus x, you don't have to adjust the * overall icmp->icmp6_cksum */ sum1 = ntohs(orgicmp->icmp6_id); sum2 = ntohs(nat->nat_osport); CALC_SUMD(sum1, sum2, sumd); orgicmp->icmp6_id = nat->nat_oicmpid; ipf_fix_datacksum(&orgicmp->icmp6_cksum, sumd); } } /* nat6_dir == NAT_INBOUND is impossible for icmp queries */ } return nat; } /* * MAP-IN MAP-OUT RDR-IN RDR-OUT * osrc X == src == src X * odst X == dst == dst X * nsrc == dst X X == dst * ndst == src X X == src * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND */ /* * NB: these lookups don't lock access to the list, it assumed that it has * already been done! */ /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_inlookup */ /* Returns: nat6_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: fin(I) - pointer to packet information */ /* flags(I) - NAT flags for this packet */ /* p(I) - protocol for this packet */ /* src(I) - source IP address */ /* mapdst(I) - destination IP address */ /* */ /* Lookup a nat entry based on the mapped destination ip address/port and */ /* real source address/port. We use this lookup when receiving a packet, */ /* we're looking for a table entry, based on the destination address. */ /* */ /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ /* */ /* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ /* */ /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ /* the packet is of said protocol */ /* ------------------------------------------------------------------------ */ nat_t * -ipf_nat6_inlookup(fin, flags, p, src, mapdst) - fr_info_t *fin; - u_int flags, p; - struct in6_addr *src , *mapdst; +ipf_nat6_inlookup(fr_info_t *fin, u_int flags, u_int p, + struct in6_addr *src , struct in6_addr *mapdst) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short sport, dport; grehdr_t *gre; ipnat_t *ipn; u_int sflags; nat_t *nat; int nflags; i6addr_t dst; void *ifp; u_int hv; ifp = fin->fin_ifp; sport = 0; dport = 0; gre = NULL; dst.in6 = *mapdst; sflags = flags & NAT_TCPUDPICMP; switch (p) { case IPPROTO_TCP : case IPPROTO_UDP : sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); break; case IPPROTO_ICMPV6 : if (flags & IPN_ICMPERR) sport = fin->fin_data[1]; else dport = fin->fin_data[1]; break; default : break; } if ((flags & SI_WILDP) != 0) goto find_in_wild_ports; hv = NAT_HASH_FN6(&dst, dport, 0xffffffff); hv = NAT_HASH_FN6(src, hv + sport, softn->ipf_nat_table_sz); nat = softn->ipf_nat_table[1][hv]; /* TRACE dst, dport, src, sport, hv, nat */ for (; nat; nat = nat->nat_hnext[1]) { if (nat->nat_ifps[0] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) continue; } if (nat->nat_pr[0] != p) continue; switch (nat->nat_dir) { case NAT_INBOUND : if (nat->nat_v[0] != 6) continue; if (IP6_NEQ(&nat->nat_osrc6, src) || IP6_NEQ(&nat->nat_odst6, &dst)) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_osport != sport) continue; if (nat->nat_odport != dport) continue; } else if (p == IPPROTO_ICMPV6) { if (nat->nat_osport != dport) { continue; } } break; case NAT_OUTBOUND : if (nat->nat_v[1] != 6) continue; if (IP6_NEQ(&nat->nat_ndst6, src) || IP6_NEQ(&nat->nat_nsrc6, &dst)) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_ndport != sport) continue; if (nat->nat_nsport != dport) continue; } else if (p == IPPROTO_ICMPV6) { if (nat->nat_osport != dport) { continue; } } break; } if ((nat->nat_flags & IPN_TCPUDP) != 0) { ipn = nat->nat_ptr; #ifdef IPF_V6_PROXIES if ((ipn != NULL) && (nat->nat_aps != NULL)) if (appr_match(fin, nat) != 0) continue; #endif } if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { nat->nat_ifps[0] = ifp; nat->nat_mtu[0] = GETIFMTU_6(ifp); } return nat; } /* * So if we didn't find it but there are wildcard members in the hash * table, go back and look for them. We do this search and update here * because it is modifying the NAT table and we want to do this only * for the first packet that matches. The exception, of course, is * for "dummy" (FI_IGNORE) lookups. */ find_in_wild_ports: if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { NBUMPSIDE6DX(0, ns_lookup_miss, ns_lookup_miss_1); return NULL; } if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { NBUMPSIDE6D(0, ns_lookup_nowild); return NULL; } RWLOCK_EXIT(&softc->ipf_nat); hv = NAT_HASH_FN6(&dst, 0, 0xffffffff); hv = NAT_HASH_FN6(src, hv, softn->ipf_nat_table_sz); WRITE_ENTER(&softc->ipf_nat); nat = softn->ipf_nat_table[1][hv]; /* TRACE dst, src, hv, nat */ for (; nat; nat = nat->nat_hnext[1]) { if (nat->nat_ifps[0] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) continue; } if (nat->nat_pr[0] != fin->fin_p) continue; switch (nat->nat_dir) { case NAT_INBOUND : if (nat->nat_v[0] != 6) continue; if (IP6_NEQ(&nat->nat_osrc6, src) || IP6_NEQ(&nat->nat_odst6, &dst)) continue; break; case NAT_OUTBOUND : if (nat->nat_v[1] != 6) continue; if (IP6_NEQ(&nat->nat_ndst6, src) || IP6_NEQ(&nat->nat_nsrc6, &dst)) continue; break; } nflags = nat->nat_flags; if (!(nflags & (NAT_TCPUDP|SI_WILDP))) continue; if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags, NAT_INBOUND) == 1) { if ((fin->fin_flx & FI_IGNORE) != 0) break; if ((nflags & SI_CLONE) != 0) { nat = ipf_nat_clone(fin, nat); if (nat == NULL) break; } else { MUTEX_ENTER(&softn->ipf_nat_new); softn->ipf_nat_stats.ns_wilds--; MUTEX_EXIT(&softn->ipf_nat_new); } if (nat->nat_dir == NAT_INBOUND) { if (nat->nat_osport == 0) { nat->nat_osport = sport; nat->nat_nsport = sport; } if (nat->nat_odport == 0) { nat->nat_odport = dport; nat->nat_ndport = dport; } } else { if (nat->nat_osport == 0) { nat->nat_osport = dport; nat->nat_nsport = dport; } if (nat->nat_odport == 0) { nat->nat_odport = sport; nat->nat_ndport = sport; } } if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { nat->nat_ifps[0] = ifp; nat->nat_mtu[0] = GETIFMTU_6(ifp); } nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); ipf_nat6_tabmove(softn, nat); break; } } MUTEX_DOWNGRADE(&softc->ipf_nat); if (nat == NULL) { NBUMPSIDE6DX(0, ns_lookup_miss, ns_lookup_miss_2); } return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_tabmove */ /* Returns: Nil */ /* Parameters: nat(I) - pointer to NAT structure */ /* Write Lock: ipf_nat */ /* */ /* This function is only called for TCP/UDP NAT table entries where the */ /* original was placed in the table without hashing on the ports and we now */ /* want to include hashing on port numbers. */ /* ------------------------------------------------------------------------ */ static void -ipf_nat6_tabmove(softn, nat) - ipf_nat_softc_t *softn; - nat_t *nat; +ipf_nat6_tabmove(ipf_nat_softc_t *softn, nat_t *nat) { nat_t **natp; u_int hv0, hv1; if (nat->nat_flags & SI_CLONE) return; /* * Remove the NAT entry from the old location */ if (nat->nat_hnext[0]) nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; *nat->nat_phnext[0] = nat->nat_hnext[0]; softn->ipf_nat_stats.ns_side[0].ns_bucketlen[nat->nat_hv[0]]--; if (nat->nat_hnext[1]) nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; *nat->nat_phnext[1] = nat->nat_hnext[1]; softn->ipf_nat_stats.ns_side[1].ns_bucketlen[nat->nat_hv[1]]--; /* * Add into the NAT table in the new position */ hv0 = NAT_HASH_FN6(&nat->nat_osrc6, nat->nat_osport, 0xffffffff); hv0 = NAT_HASH_FN6(&nat->nat_odst6, hv0 + nat->nat_odport, softn->ipf_nat_table_sz); hv1 = NAT_HASH_FN6(&nat->nat_nsrc6, nat->nat_nsport, 0xffffffff); hv1 = NAT_HASH_FN6(&nat->nat_ndst6, hv1 + nat->nat_ndport, softn->ipf_nat_table_sz); if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { u_int swap; swap = hv0; hv0 = hv1; hv1 = swap; } /* TRACE nat_osrc6, nat_osport, nat_odst6, nat_odport, hv0 */ /* TRACE nat_nsrc6, nat_nsport, nat_ndst6, nat_ndport, hv1 */ nat->nat_hv[0] = hv0; natp = &softn->ipf_nat_table[0][hv0]; if (*natp) (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; nat->nat_phnext[0] = natp; nat->nat_hnext[0] = *natp; *natp = nat; softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]++; nat->nat_hv[1] = hv1; natp = &softn->ipf_nat_table[1][hv1]; if (*natp) (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; nat->nat_phnext[1] = natp; nat->nat_hnext[1] = *natp; *natp = nat; softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]++; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_outlookup */ /* Returns: nat6_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: fin(I) - pointer to packet information */ /* flags(I) - NAT flags for this packet */ /* p(I) - protocol for this packet */ /* src(I) - source IP address */ /* dst(I) - destination IP address */ /* rw(I) - 1 == write lock on held, 0 == read lock. */ /* */ /* Lookup a nat entry based on the source 'real' ip address/port and */ /* destination address/port. We use this lookup when sending a packet out, */ /* we're looking for a table entry, based on the source address. */ /* */ /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ /* */ /* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ /* */ /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ /* the packet is of said protocol */ /* ------------------------------------------------------------------------ */ nat_t * -ipf_nat6_outlookup(fin, flags, p, src, dst) - fr_info_t *fin; - u_int flags, p; - struct in6_addr *src , *dst; +ipf_nat6_outlookup(fr_info_t *fin, u_int flags, u_int p, + struct in6_addr *src, struct in6_addr *dst) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short sport, dport; u_int sflags; ipnat_t *ipn; nat_t *nat; void *ifp; u_int hv; ifp = fin->fin_ifp; sflags = flags & IPN_TCPUDPICMP; sport = 0; dport = 0; switch (p) { case IPPROTO_TCP : case IPPROTO_UDP : sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); break; case IPPROTO_ICMPV6 : if (flags & IPN_ICMPERR) sport = fin->fin_data[1]; else dport = fin->fin_data[1]; break; default : break; } if ((flags & SI_WILDP) != 0) goto find_out_wild_ports; hv = NAT_HASH_FN6(src, sport, 0xffffffff); hv = NAT_HASH_FN6(dst, hv + dport, softn->ipf_nat_table_sz); nat = softn->ipf_nat_table[0][hv]; /* TRACE src, sport, dst, dport, hv, nat */ for (; nat; nat = nat->nat_hnext[0]) { if (nat->nat_ifps[1] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) continue; } if (nat->nat_pr[1] != p) continue; switch (nat->nat_dir) { case NAT_INBOUND : if (nat->nat_v[1] != 6) continue; if (IP6_NEQ(&nat->nat_ndst6, src) || IP6_NEQ(&nat->nat_nsrc6, dst)) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_ndport != sport) continue; if (nat->nat_nsport != dport) continue; } else if (p == IPPROTO_ICMPV6) { if (nat->nat_osport != dport) { continue; } } break; case NAT_OUTBOUND : if (nat->nat_v[0] != 6) continue; if (IP6_NEQ(&nat->nat_osrc6, src) || IP6_NEQ(&nat->nat_odst6, dst)) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_odport != dport) continue; if (nat->nat_osport != sport) continue; } else if (p == IPPROTO_ICMPV6) { if (nat->nat_osport != dport) { continue; } } break; } ipn = nat->nat_ptr; #ifdef IPF_V6_PROXIES if ((ipn != NULL) && (nat->nat_aps != NULL)) if (appr_match(fin, nat) != 0) continue; #endif if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { nat->nat_ifps[1] = ifp; nat->nat_mtu[1] = GETIFMTU_6(ifp); } return nat; } /* * So if we didn't find it but there are wildcard members in the hash * table, go back and look for them. We do this search and update here * because it is modifying the NAT table and we want to do this only * for the first packet that matches. The exception, of course, is * for "dummy" (FI_IGNORE) lookups. */ find_out_wild_ports: if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { NBUMPSIDE6DX(1, ns_lookup_miss, ns_lookup_miss_3); return NULL; } if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { NBUMPSIDE6D(1, ns_lookup_nowild); return NULL; } RWLOCK_EXIT(&softc->ipf_nat); hv = NAT_HASH_FN6(src, 0, 0xffffffff); hv = NAT_HASH_FN6(dst, hv, softn->ipf_nat_table_sz); WRITE_ENTER(&softc->ipf_nat); nat = softn->ipf_nat_table[0][hv]; for (; nat; nat = nat->nat_hnext[0]) { if (nat->nat_ifps[1] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) continue; } if (nat->nat_pr[1] != fin->fin_p) continue; switch (nat->nat_dir) { case NAT_INBOUND : if (nat->nat_v[1] != 6) continue; if (IP6_NEQ(&nat->nat_ndst6, src) || IP6_NEQ(&nat->nat_nsrc6, dst)) continue; break; case NAT_OUTBOUND : if (nat->nat_v[0] != 6) continue; if (IP6_NEQ(&nat->nat_osrc6, src) || IP6_NEQ(&nat->nat_odst6, dst)) continue; break; } if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP))) continue; if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags, NAT_OUTBOUND) == 1) { if ((fin->fin_flx & FI_IGNORE) != 0) break; if ((nat->nat_flags & SI_CLONE) != 0) { nat = ipf_nat_clone(fin, nat); if (nat == NULL) break; } else { MUTEX_ENTER(&softn->ipf_nat_new); softn->ipf_nat_stats.ns_wilds--; MUTEX_EXIT(&softn->ipf_nat_new); } if (nat->nat_dir == NAT_OUTBOUND) { if (nat->nat_osport == 0) { nat->nat_osport = sport; nat->nat_nsport = sport; } if (nat->nat_odport == 0) { nat->nat_odport = dport; nat->nat_ndport = dport; } } else { if (nat->nat_osport == 0) { nat->nat_osport = dport; nat->nat_nsport = dport; } if (nat->nat_odport == 0) { nat->nat_odport = sport; nat->nat_ndport = sport; } } if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { nat->nat_ifps[1] = ifp; nat->nat_mtu[1] = GETIFMTU_6(ifp); } nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); ipf_nat6_tabmove(softn, nat); break; } } MUTEX_DOWNGRADE(&softc->ipf_nat); if (nat == NULL) { NBUMPSIDE6DX(1, ns_lookup_miss, ns_lookup_miss_4); } return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_lookupredir */ /* Returns: nat6_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: np(I) - pointer to description of packet to find NAT table */ /* entry for. */ /* */ /* Lookup the NAT tables to search for a matching redirect */ /* The contents of natlookup_t should imitate those found in a packet that */ /* would be translated - ie a packet coming in for RDR or going out for MAP.*/ /* We can do the lookup in one of two ways, imitating an inbound or */ /* outbound packet. By default we assume outbound, unless IPN_IN is set. */ /* For IN, the fields are set as follows: */ /* nl_real* = source information */ /* nl_out* = destination information (translated) */ /* For an out packet, the fields are set like this: */ /* nl_in* = source information (untranslated) */ /* nl_out* = destination information (translated) */ /* ------------------------------------------------------------------------ */ nat_t * -ipf_nat6_lookupredir(np) - natlookup_t *np; +ipf_nat6_lookupredir(natlookup_t *np) { fr_info_t fi; nat_t *nat; bzero((char *)&fi, sizeof(fi)); if (np->nl_flags & IPN_IN) { fi.fin_data[0] = ntohs(np->nl_realport); fi.fin_data[1] = ntohs(np->nl_outport); } else { fi.fin_data[0] = ntohs(np->nl_inport); fi.fin_data[1] = ntohs(np->nl_outport); } if (np->nl_flags & IPN_TCP) fi.fin_p = IPPROTO_TCP; else if (np->nl_flags & IPN_UDP) fi.fin_p = IPPROTO_UDP; else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) fi.fin_p = IPPROTO_ICMPV6; /* * We can do two sorts of lookups: * - IPN_IN: we have the `real' and `out' address, look for `in'. * - default: we have the `in' and `out' address, look for `real'. */ if (np->nl_flags & IPN_IN) { if ((nat = ipf_nat6_inlookup(&fi, np->nl_flags, fi.fin_p, &np->nl_realip6, &np->nl_outip6))) { np->nl_inip6 = nat->nat_odst6.in6; np->nl_inport = nat->nat_odport; } } else { /* * If nl_inip is non null, this is a lookup based on the real * ip address. Else, we use the fake. */ if ((nat = ipf_nat6_outlookup(&fi, np->nl_flags, fi.fin_p, &np->nl_inip6, &np->nl_outip6))) { if ((np->nl_flags & IPN_FINDFORWARD) != 0) { fr_info_t fin; bzero((char *)&fin, sizeof(fin)); fin.fin_p = nat->nat_pr[0]; fin.fin_data[0] = ntohs(nat->nat_ndport); fin.fin_data[1] = ntohs(nat->nat_nsport); if (ipf_nat6_inlookup(&fin, np->nl_flags, fin.fin_p, &nat->nat_ndst6.in6, &nat->nat_nsrc6.in6) != NULL) { np->nl_flags &= ~IPN_FINDFORWARD; } } np->nl_realip6 = nat->nat_odst6.in6; np->nl_realport = nat->nat_odport; } } return nat; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_match */ /* Returns: int - 0 == no match, 1 == match */ /* Parameters: fin(I) - pointer to packet information */ /* np(I) - pointer to NAT rule */ /* */ /* Pull the matching of a packet against a NAT rule out of that complex */ /* loop inside ipf_nat6_checkin() and lay it out properly in its own */ /* function. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat6_match(fin, np) - fr_info_t *fin; - ipnat_t *np; +ipf_nat6_match(fr_info_t *fin, ipnat_t *np) { frtuc_t *ft; int match; match = 0; switch (np->in_osrcatype) { case FRI_NORMAL : match = IP6_MASKNEQ(&fin->fin_src6, &np->in_osrcmsk6, &np->in_osrcip6); break; case FRI_LOOKUP : match = (*np->in_osrcfunc)(fin->fin_main_soft, np->in_osrcptr, 6, &fin->fin_src6, fin->fin_plen); break; } match ^= ((np->in_flags & IPN_NOTSRC) != 0); if (match) return 0; match = 0; switch (np->in_odstatype) { case FRI_NORMAL : match = IP6_MASKNEQ(&fin->fin_dst6, &np->in_odstmsk6, &np->in_odstip6); break; case FRI_LOOKUP : match = (*np->in_odstfunc)(fin->fin_main_soft, np->in_odstptr, 6, &fin->fin_dst6, fin->fin_plen); break; } match ^= ((np->in_flags & IPN_NOTDST) != 0); if (match) return 0; ft = &np->in_tuc; if (!(fin->fin_flx & FI_TCPUDP) || (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { if (ft->ftu_scmp || ft->ftu_dcmp) return 0; return 1; } return ipf_tcpudpchk(&fin->fin_fi, ft); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_checkout */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 0 == no packet translation occurred, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(I) - pointer to filtering result flags */ /* */ /* Check to see if an outcoming packet should be changed. ICMP packets are */ /* first checked to see if they match an existing entry (if an error), */ /* otherwise a search of the current NAT table is made. If neither results */ /* in a match then a search for a matching NAT rule is made. Create a new */ /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ /* packet header(s) as required. */ /* ------------------------------------------------------------------------ */ int -ipf_nat6_checkout(fin, passp) - fr_info_t *fin; - u_32_t *passp; +ipf_nat6_checkout(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; struct icmp6_hdr *icmp6 = NULL; struct ifnet *ifp, *sifp; tcphdr_t *tcp = NULL; int rval, natfailed; ipnat_t *np = NULL; u_int nflags = 0; i6addr_t ipa, iph; int natadd = 1; frentry_t *fr; nat_t *nat; if (softn->ipf_nat_stats.ns_rules == 0 || softn->ipf_nat_lock != 0) return 0; icmp6 = NULL; natfailed = 0; fr = fin->fin_fr; sifp = fin->fin_ifp; if (fr != NULL) { ifp = fr->fr_tifs[fin->fin_rev].fd_ptr; if ((ifp != NULL) && (ifp != (void *)-1)) fin->fin_ifp = ifp; } ifp = fin->fin_ifp; if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { switch (fin->fin_p) { case IPPROTO_TCP : nflags = IPN_TCP; break; case IPPROTO_UDP : nflags = IPN_UDP; break; case IPPROTO_ICMPV6 : icmp6 = fin->fin_dp; /* * Apart from ECHO request and reply, all other * informational messages should not be translated * so as to keep IPv6 working. */ if (icmp6->icmp6_type > ICMP6_ECHO_REPLY) return 0; /* * This is an incoming packet, so the destination is * the icmp6_id and the source port equals 0 */ if ((fin->fin_flx & FI_ICMPQUERY) != 0) nflags = IPN_ICMPQUERY; break; default : break; } if ((nflags & IPN_TCPUDP)) tcp = fin->fin_dp; } ipa = fin->fin_src6; READ_ENTER(&softc->ipf_nat); if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) && (nat = ipf_nat6_icmperror(fin, &nflags, NAT_OUTBOUND))) /*EMPTY*/; else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) natadd = 0; else if ((nat = ipf_nat6_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, &fin->fin_src6.in6, &fin->fin_dst6.in6))) { nflags = nat->nat_flags; } else if (fin->fin_off == 0) { u_32_t hv, nmsk = 0; i6addr_t *msk; /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ maskloop: msk = &softn->ipf_nat6_map_active_masks[nmsk]; IP6_AND(&ipa, msk, &iph); hv = NAT_HASH_FN6(&iph, 0, softn->ipf_nat_maprules_sz); for (np = softn->ipf_nat_map_rules[hv]; np; np = np->in_mnext) { if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) continue; if (np->in_v[0] != 6) continue; if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p)) continue; if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) continue; if (np->in_flags & IPN_FILTER) { switch (ipf_nat6_match(fin, np)) { case 0 : continue; case -1 : rval = -1; goto outmatchfail; case 1 : default : break; } } else if (!IP6_MASKEQ(&ipa, &np->in_osrcmsk, &np->in_osrcip6)) continue; if ((fr != NULL) && !ipf_matchtag(&np->in_tag, &fr->fr_nattag)) continue; #ifdef IPF_V6_PROXIES if (np->in_plabel != -1) { if (((np->in_flags & IPN_FILTER) == 0) && (np->in_odport != fin->fin_data[1])) continue; if (appr_ok(fin, tcp, np) == 0) continue; } #endif if (np->in_flags & IPN_NO) { np->in_hits++; break; } MUTEX_ENTER(&softn->ipf_nat_new); nat = ipf_nat6_add(fin, np, NULL, nflags, NAT_OUTBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (nat != NULL) { np->in_hits++; break; } natfailed = -1; } if ((np == NULL) && (nmsk < softn->ipf_nat6_map_max)) { nmsk++; goto maskloop; } } if (nat != NULL) { rval = ipf_nat6_out(fin, nat, natadd, nflags); if (rval == 1) { MUTEX_ENTER(&nat->nat_lock); ipf_nat_update(fin, nat); nat->nat_bytes[1] += fin->fin_plen; nat->nat_pkts[1]++; MUTEX_EXIT(&nat->nat_lock); } } else rval = natfailed; outmatchfail: RWLOCK_EXIT(&softc->ipf_nat); switch (rval) { case -1 : if (passp != NULL) { NBUMPSIDE6D(1, ns_drop); *passp = FR_BLOCK; fin->fin_reason = FRB_NATV6; } fin->fin_flx |= FI_BADNAT; NBUMPSIDE6D(1, ns_badnat); break; case 0 : NBUMPSIDE6D(1, ns_ignored); break; case 1 : NBUMPSIDE6D(1, ns_translated); break; } fin->fin_ifp = sifp; return rval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_out */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* natadd(I) - flag indicating if it is safe to add frag cache */ /* nflags(I) - NAT flags set for this packet */ /* */ /* Translate a packet coming "out" on an interface. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat6_out(fin, nat, natadd, nflags) - fr_info_t *fin; - nat_t *nat; - int natadd; - u_32_t nflags; +ipf_nat6_out(fr_info_t *fin, nat_t *nat, int natadd, u_32_t nflags) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; struct icmp6_hdr *icmp6; tcphdr_t *tcp; ipnat_t *np; int skip; int i; tcp = NULL; icmp6 = NULL; np = nat->nat_ptr; if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) (void) ipf_frag_natnew(softc, fin, 0, nat); /* * Address assignment is after the checksum modification because * we are using the address in the packet for determining the * correct checksum offset (the ICMP error could be coming from * anyone...) */ switch (nat->nat_dir) { case NAT_OUTBOUND : fin->fin_ip6->ip6_src = nat->nat_nsrc6.in6; fin->fin_src6 = nat->nat_nsrc6; fin->fin_ip6->ip6_dst = nat->nat_ndst6.in6; fin->fin_dst6 = nat->nat_ndst6; break; case NAT_INBOUND : fin->fin_ip6->ip6_src = nat->nat_odst6.in6; fin->fin_src6 = nat->nat_ndst6; fin->fin_ip6->ip6_dst = nat->nat_osrc6.in6; fin->fin_dst6 = nat->nat_nsrc6; break; case NAT_DIVERTIN : { mb_t *m; skip = ipf_nat6_decap(fin, nat); if (skip <= 0) { NBUMPSIDE6D(1, ns_decap_fail); return -1; } m = fin->fin_m; #if SOLARIS && defined(_KERNEL) m->b_rptr += skip; #else m->m_data += skip; m->m_len -= skip; # ifdef M_PKTHDR if (m->m_flags & M_PKTHDR) m->m_pkthdr.len -= skip; # endif #endif MUTEX_ENTER(&nat->nat_lock); ipf_nat_update(fin, nat); MUTEX_EXIT(&nat->nat_lock); fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; return 1; /* NOTREACHED */ } case NAT_DIVERTOUT : { udphdr_t *uh; ip6_t *ip6; mb_t *m; m = M_DUP(np->in_divmp); if (m == NULL) { NBUMPSIDE6D(1, ns_divert_dup); return -1; } ip6 = MTOD(m, ip6_t *); ip6->ip6_plen = htons(fin->fin_plen + 8); uh = (udphdr_t *)(ip6 + 1); uh->uh_ulen = htons(fin->fin_plen); PREP_MB_T(fin, m); fin->fin_ip6 = ip6; fin->fin_plen += sizeof(ip6_t) + 8; /* UDP + new IPv4 hdr */ fin->fin_dlen += sizeof(ip6_t) + 8; /* UDP + old IPv4 hdr */ nflags &= ~IPN_TCPUDPICMP; break; } default : break; } if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { u_short *csump; if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) { tcp = fin->fin_dp; switch (nat->nat_dir) { case NAT_OUTBOUND : tcp->th_sport = nat->nat_nsport; fin->fin_data[0] = ntohs(nat->nat_nsport); tcp->th_dport = nat->nat_ndport; fin->fin_data[1] = ntohs(nat->nat_ndport); break; case NAT_INBOUND : tcp->th_sport = nat->nat_odport; fin->fin_data[0] = ntohs(nat->nat_odport); tcp->th_dport = nat->nat_osport; fin->fin_data[1] = ntohs(nat->nat_osport); break; } } if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) { icmp6 = fin->fin_dp; icmp6->icmp6_id = nat->nat_nicmpid; } csump = ipf_nat_proto(fin, nat, nflags); /* * The above comments do not hold for layer 4 (or higher) * checksums... */ if (csump != NULL) { if (nat->nat_dir == NAT_OUTBOUND) ipf_fix_outcksum(fin->fin_cksum, csump, nat->nat_sumd[0], nat->nat_sumd[1] + fin->fin_dlen); else ipf_fix_incksum(fin->fin_cksum, csump, nat->nat_sumd[0], nat->nat_sumd[1] + fin->fin_dlen); } } ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); /* ------------------------------------------------------------- */ /* A few quick notes: */ /* Following are test conditions prior to calling the */ /* ipf_proxy_check routine. */ /* */ /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ /* with a redirect rule, we attempt to match the packet's */ /* source port against in_dport, otherwise we'd compare the */ /* packet's destination. */ /* ------------------------------------------------------------- */ if ((np != NULL) && (np->in_apr != NULL)) { i = ipf_proxy_check(fin, nat); if (i == -1) { NBUMPSIDE6D(1, ns_ipf_proxy_fail); } } else { i = 1; } fin->fin_flx |= FI_NATED; return i; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_checkin */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 0 == no packet translation occurred, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(I) - pointer to filtering result flags */ /* */ /* Check to see if an incoming packet should be changed. ICMP packets are */ /* first checked to see if they match an existing entry (if an error), */ /* otherwise a search of the current NAT table is made. If neither results */ /* in a match then a search for a matching NAT rule is made. Create a new */ /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ /* packet header(s) as required. */ /* ------------------------------------------------------------------------ */ int -ipf_nat6_checkin(fin, passp) - fr_info_t *fin; - u_32_t *passp; +ipf_nat6_checkin(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; struct icmp6_hdr *icmp6; u_int nflags, natadd; int rval, natfailed; struct ifnet *ifp; i6addr_t ipa, iph; tcphdr_t *tcp; u_short dport; ipnat_t *np; nat_t *nat; if (softn->ipf_nat_stats.ns_rules == 0 || softn->ipf_nat_lock != 0) return 0; tcp = NULL; icmp6 = NULL; dport = 0; natadd = 1; nflags = 0; natfailed = 0; ifp = fin->fin_ifp; if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { switch (fin->fin_p) { case IPPROTO_TCP : nflags = IPN_TCP; break; case IPPROTO_UDP : nflags = IPN_UDP; break; case IPPROTO_ICMPV6 : icmp6 = fin->fin_dp; /* * Apart from ECHO request and reply, all other * informational messages should not be translated * so as to keep IPv6 working. */ if (icmp6->icmp6_type > ICMP6_ECHO_REPLY) return 0; /* * This is an incoming packet, so the destination is * the icmp6_id and the source port equals 0 */ if ((fin->fin_flx & FI_ICMPQUERY) != 0) { nflags = IPN_ICMPQUERY; dport = icmp6->icmp6_id; } break; default : break; } if ((nflags & IPN_TCPUDP)) { tcp = fin->fin_dp; dport = fin->fin_data[1]; } } ipa = fin->fin_dst6; READ_ENTER(&softc->ipf_nat); if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) && (nat = ipf_nat6_icmperror(fin, &nflags, NAT_INBOUND))) /*EMPTY*/; else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) natadd = 0; else if ((nat = ipf_nat6_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, &fin->fin_src6.in6, &ipa.in6))) { nflags = nat->nat_flags; } else if (fin->fin_off == 0) { u_32_t hv, rmsk = 0; i6addr_t *msk; /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ maskloop: msk = &softn->ipf_nat6_rdr_active_masks[rmsk]; IP6_AND(&ipa, msk, &iph); hv = NAT_HASH_FN6(&iph, 0, softn->ipf_nat_rdrrules_sz); for (np = softn->ipf_nat_rdr_rules[hv]; np; np = np->in_rnext) { if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) continue; if (np->in_v[0] != 6) continue; if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p)) continue; if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) continue; if (np->in_flags & IPN_FILTER) { switch (ipf_nat6_match(fin, np)) { case 0 : continue; case -1 : rval = -1; goto inmatchfail; case 1 : default : break; } } else { if (!IP6_MASKEQ(&ipa, &np->in_odstmsk6, &np->in_odstip6)) { continue; } if (np->in_odport && ((np->in_dtop < dport) || (dport < np->in_odport))) continue; } #ifdef IPF_V6_PROXIES if (np->in_plabel != -1) { if (!appr_ok(fin, tcp, np)) { continue; } } #endif if (np->in_flags & IPN_NO) { np->in_hits++; break; } MUTEX_ENTER(&softn->ipf_nat_new); nat = ipf_nat6_add(fin, np, NULL, nflags, NAT_INBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (nat != NULL) { np->in_hits++; break; } natfailed = -1; } if ((np == NULL) && (rmsk < softn->ipf_nat6_rdr_max)) { rmsk++; goto maskloop; } } if (nat != NULL) { rval = ipf_nat6_in(fin, nat, natadd, nflags); if (rval == 1) { MUTEX_ENTER(&nat->nat_lock); ipf_nat_update(fin, nat); nat->nat_bytes[0] += fin->fin_plen; nat->nat_pkts[0]++; MUTEX_EXIT(&nat->nat_lock); } } else rval = natfailed; inmatchfail: RWLOCK_EXIT(&softc->ipf_nat); DT2(frb_natv6in, fr_info_t *, fin, int, rval); switch (rval) { case -1 : if (passp != NULL) { NBUMPSIDE6D(0, ns_drop); *passp = FR_BLOCK; fin->fin_reason = FRB_NATV6; } fin->fin_flx |= FI_BADNAT; NBUMPSIDE6D(0, ns_badnat); break; case 0 : NBUMPSIDE6D(0, ns_ignored); break; case 1 : NBUMPSIDE6D(0, ns_translated); break; } return rval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_in */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* natadd(I) - flag indicating if it is safe to add frag cache */ /* nflags(I) - NAT flags set for this packet */ /* Locks Held: (READ) */ /* */ /* Translate a packet coming "in" on an interface. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat6_in(fin, nat, natadd, nflags) - fr_info_t *fin; - nat_t *nat; - int natadd; - u_32_t nflags; +ipf_nat6_in(fr_info_t *fin, nat_t *nat, int natadd, u_32_t nflags) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; struct icmp6_hdr *icmp6; u_short *csump; tcphdr_t *tcp; ipnat_t *np; int skip; int i; tcp = NULL; csump = NULL; np = nat->nat_ptr; fin->fin_fr = nat->nat_fr; if (np != NULL) { if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) (void) ipf_frag_natnew(softc, fin, 0, nat); /* ------------------------------------------------------------- */ /* A few quick notes: */ /* Following are test conditions prior to calling the */ /* ipf_proxy_check routine. */ /* */ /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ /* with a map rule, we attempt to match the packet's */ /* source port against in_dport, otherwise we'd compare the */ /* packet's destination. */ /* ------------------------------------------------------------- */ if (np->in_apr != NULL) { i = ipf_proxy_check(fin, nat); if (i == -1) { NBUMPSIDE6D(0, ns_ipf_proxy_fail); return -1; } } } ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. * Why only do this for some platforms on inbound packets ? * Because for those that it is done, IP processing is yet to happen * and so the IPv4 header checksum has not yet been evaluated. * Perhaps it should always be done for the benefit of things like * fast forwarding (so that it doesn't need to be recomputed) but with * header checksum offloading, perhaps it is a moot point. */ switch (nat->nat_dir) { case NAT_INBOUND : if ((fin->fin_flx & FI_ICMPERR) == 0) { fin->fin_ip6->ip6_src = nat->nat_nsrc6.in6; fin->fin_src6 = nat->nat_nsrc6; } fin->fin_ip6->ip6_dst = nat->nat_ndst6.in6; fin->fin_dst6 = nat->nat_ndst6; break; case NAT_OUTBOUND : if ((fin->fin_flx & FI_ICMPERR) == 0) { fin->fin_ip6->ip6_src = nat->nat_odst6.in6; fin->fin_src6 = nat->nat_odst6; } fin->fin_ip6->ip6_dst = nat->nat_osrc6.in6; fin->fin_dst6 = nat->nat_osrc6; break; case NAT_DIVERTIN : { udphdr_t *uh; ip6_t *ip6; mb_t *m; m = M_DUP(np->in_divmp); if (m == NULL) { NBUMPSIDE6D(0, ns_divert_dup); return -1; } ip6 = MTOD(m, ip6_t *); ip6->ip6_plen = htons(fin->fin_plen + sizeof(udphdr_t)); uh = (udphdr_t *)(ip6 + 1); uh->uh_ulen = ntohs(fin->fin_plen); PREP_MB_T(fin, m); fin->fin_ip6 = ip6; fin->fin_plen += sizeof(ip6_t) + 8; /* UDP + new IPv6 hdr */ fin->fin_dlen += sizeof(ip6_t) + 8; /* UDP + old IPv6 hdr */ nflags &= ~IPN_TCPUDPICMP; break; } case NAT_DIVERTOUT : { mb_t *m; skip = ipf_nat6_decap(fin, nat); if (skip <= 0) { NBUMPSIDE6D(0, ns_decap_fail); return -1; } m = fin->fin_m; #if SOLARIS && defined(_KERNEL) m->b_rptr += skip; #else m->m_data += skip; m->m_len -= skip; # ifdef M_PKTHDR if (m->m_flags & M_PKTHDR) m->m_pkthdr.len -= skip; # endif #endif ipf_nat_update(fin, nat); fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; return 1; /* NOTREACHED */ } } if (nflags & IPN_TCPUDP) tcp = fin->fin_dp; if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) { switch (nat->nat_dir) { case NAT_INBOUND : tcp->th_sport = nat->nat_nsport; fin->fin_data[0] = ntohs(nat->nat_nsport); tcp->th_dport = nat->nat_ndport; fin->fin_data[1] = ntohs(nat->nat_ndport); break; case NAT_OUTBOUND : tcp->th_sport = nat->nat_odport; fin->fin_data[0] = ntohs(nat->nat_odport); tcp->th_dport = nat->nat_osport; fin->fin_data[1] = ntohs(nat->nat_osport); break; } } if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) { icmp6 = fin->fin_dp; icmp6->icmp6_id = nat->nat_nicmpid; } csump = ipf_nat_proto(fin, nat, nflags); } /* * The above comments do not hold for layer 4 (or higher) checksums... */ if (csump != NULL) { if (nat->nat_dir == NAT_OUTBOUND) ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0); else ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0); } fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; return 1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_newrewrite */ /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ /* allow rule to be moved if IPN_ROUNDR is set. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* Write Lock: ipf_nat */ /* */ /* This function is responsible for setting up an active NAT session where */ /* we are changing both the source and destination parameters at the same */ /* time. The loop in here works differently to elsewhere - each iteration */ /* is responsible for changing a single parameter that can be incremented. */ /* So one pass may increase the source IP#, next source port, next dest. IP#*/ /* and the last destination port for a total of 4 iterations to try each. */ /* This is done to try and exhaustively use the translation space available.*/ /* ------------------------------------------------------------------------ */ int -ipf_nat6_newrewrite(fin, nat, nai) - fr_info_t *fin; - nat_t *nat; - natinfo_t *nai; +ipf_nat6_newrewrite(fr_info_t *fin, nat_t *nat, natinfo_t *nai) { int src_search = 1; int dst_search = 1; fr_info_t frnat; u_32_t flags; u_short swap; ipnat_t *np; nat_t *natl; int l = 0; int changed; natl = NULL; changed = -1; np = nai->nai_np; flags = nat->nat_flags; bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); nat->nat_hm = NULL; do { changed = -1; /* TRACE (l, src_search, dst_search, np) */ if ((src_search == 0) && (np->in_spnext == 0) && (dst_search == 0) && (np->in_dpnext == 0)) { if (l > 0) return -1; } /* * Find a new source address */ if (ipf_nat6_nextaddr(fin, &np->in_nsrc, &frnat.fin_src6, &frnat.fin_src6) == -1) { return -1; } if (IP6_ISZERO(&np->in_nsrcip6) && IP6_ISONES(&np->in_nsrcmsk6)) { src_search = 0; if (np->in_stepnext == 0) np->in_stepnext = 1; } else if (IP6_ISZERO(&np->in_nsrcip6) && IP6_ISZERO(&np->in_nsrcmsk6)) { src_search = 0; if (np->in_stepnext == 0) np->in_stepnext = 1; } else if (IP6_ISONES(&np->in_nsrcmsk)) { src_search = 0; if (np->in_stepnext == 0) np->in_stepnext = 1; } else if (!IP6_ISONES(&np->in_nsrcmsk6)) { if (np->in_stepnext == 0 && changed == -1) { IP6_INC(&np->in_snip); np->in_stepnext++; changed = 0; } } if ((flags & IPN_TCPUDPICMP) != 0) { if (np->in_spnext != 0) frnat.fin_data[0] = np->in_spnext; /* * Standard port translation. Select next port. */ if ((flags & IPN_FIXEDSPORT) != 0) { np->in_stepnext = 2; } else if ((np->in_stepnext == 1) && (changed == -1) && (natl != NULL)) { np->in_spnext++; np->in_stepnext++; changed = 1; if (np->in_spnext > np->in_spmax) np->in_spnext = np->in_spmin; } } else { np->in_stepnext = 2; } np->in_stepnext &= 0x3; /* * Find a new destination address */ /* TRACE (fin, np, l, frnat) */ if (ipf_nat6_nextaddr(fin, &np->in_ndst, &frnat.fin_dst6, &frnat.fin_dst6) == -1) return -1; if (IP6_ISZERO(&np->in_ndstip6) && IP6_ISONES(&np->in_ndstmsk6)) { dst_search = 0; if (np->in_stepnext == 2) np->in_stepnext = 3; } else if (IP6_ISZERO(&np->in_ndstip6) && IP6_ISZERO(&np->in_ndstmsk6)) { dst_search = 0; if (np->in_stepnext == 2) np->in_stepnext = 3; } else if (IP6_ISONES(&np->in_ndstmsk6)) { dst_search = 0; if (np->in_stepnext == 2) np->in_stepnext = 3; } else if (!IP6_ISONES(&np->in_ndstmsk6)) { if ((np->in_stepnext == 2) && (changed == -1) && (natl != NULL)) { changed = 2; np->in_stepnext++; IP6_INC(&np->in_dnip6); } } if ((flags & IPN_TCPUDPICMP) != 0) { if (np->in_dpnext != 0) frnat.fin_data[1] = np->in_dpnext; /* * Standard port translation. Select next port. */ if ((flags & IPN_FIXEDDPORT) != 0) { np->in_stepnext = 0; } else if (np->in_stepnext == 3 && changed == -1) { np->in_dpnext++; np->in_stepnext++; changed = 3; if (np->in_dpnext > np->in_dpmax) np->in_dpnext = np->in_dpmin; } } else { if (np->in_stepnext == 3) np->in_stepnext = 0; } /* TRACE (frnat) */ /* * Here we do a lookup of the connection as seen from * the outside. If an IP# pair already exists, try * again. So if you have A->B becomes C->B, you can * also have D->E become C->E but not D->B causing * another C->B. Also take protocol and ports into * account when determining whether a pre-existing * NAT setup will cause an external conflict where * this is appropriate. * * fin_data[] is swapped around because we are doing a * lookup of the packet is if it were moving in the opposite * direction of the one we are working with now. */ if (flags & IPN_TCPUDP) { swap = frnat.fin_data[0]; frnat.fin_data[0] = frnat.fin_data[1]; frnat.fin_data[1] = swap; } if (fin->fin_out == 1) { natl = ipf_nat6_inlookup(&frnat, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)frnat.fin_p, &frnat.fin_dst6.in6, &frnat.fin_src6.in6); } else { natl = ipf_nat6_outlookup(&frnat, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)frnat.fin_p, &frnat.fin_dst6.in6, &frnat.fin_src6.in6); } if (flags & IPN_TCPUDP) { swap = frnat.fin_data[0]; frnat.fin_data[0] = frnat.fin_data[1]; frnat.fin_data[1] = swap; } /* TRACE natl, in_stepnext, l */ if ((natl != NULL) && (l > 8)) /* XXX 8 is arbitrary */ return -1; np->in_stepnext &= 0x3; l++; changed = -1; } while (natl != NULL); nat->nat_osrc6 = fin->fin_src6; nat->nat_odst6 = fin->fin_dst6; nat->nat_nsrc6 = frnat.fin_src6; nat->nat_ndst6 = frnat.fin_dst6; if ((flags & IPN_TCPUDP) != 0) { nat->nat_osport = htons(fin->fin_data[0]); nat->nat_odport = htons(fin->fin_data[1]); nat->nat_nsport = htons(frnat.fin_data[0]); nat->nat_ndport = htons(frnat.fin_data[1]); } else if ((flags & IPN_ICMPQUERY) != 0) { nat->nat_oicmpid = fin->fin_data[1]; nat->nat_nicmpid = frnat.fin_data[1]; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_newdivert */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* Write Lock: ipf_nat */ /* */ /* Create a new NAT divert session as defined by the NAT rule. This is */ /* somewhat different to other NAT session creation routines because we */ /* do not iterate through either port numbers or IP addresses, searching */ /* for a unique mapping, however, a complimentary duplicate check is made. */ /* ------------------------------------------------------------------------ */ int -ipf_nat6_newdivert(fin, nat, nai) - fr_info_t *fin; - nat_t *nat; - natinfo_t *nai; +ipf_nat6_newdivert(fr_info_t *fin, nat_t *nat, natinfo_t *nai) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; fr_info_t frnat; ipnat_t *np; nat_t *natl; int p; np = nai->nai_np; bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); nat->nat_pr[0] = 0; nat->nat_osrc6 = fin->fin_src6; nat->nat_odst6 = fin->fin_dst6; nat->nat_osport = htons(fin->fin_data[0]); nat->nat_odport = htons(fin->fin_data[1]); frnat.fin_src6 = np->in_snip6; frnat.fin_dst6 = np->in_dnip6; if (np->in_redir & NAT_DIVERTUDP) { frnat.fin_data[0] = np->in_spnext; frnat.fin_data[1] = np->in_dpnext; frnat.fin_flx |= FI_TCPUDP; p = IPPROTO_UDP; } else { frnat.fin_flx &= ~FI_TCPUDP; p = IPPROTO_IPIP; } if (fin->fin_out == 1) { natl = ipf_nat6_inlookup(&frnat, 0, p, &frnat.fin_dst6.in6, &frnat.fin_src6.in6); } else { natl = ipf_nat6_outlookup(&frnat, 0, p, &frnat.fin_dst6.in6, &frnat.fin_src6.in6); } if (natl != NULL) { NBUMPSIDE6D(fin->fin_out, ns_divert_exist); return -1; } nat->nat_nsrc6 = frnat.fin_src6; nat->nat_ndst6 = frnat.fin_dst6; if (np->in_redir & NAT_DIVERTUDP) { nat->nat_nsport = htons(frnat.fin_data[0]); nat->nat_ndport = htons(frnat.fin_data[1]); } nat->nat_pr[fin->fin_out] = fin->fin_p; nat->nat_pr[1 - fin->fin_out] = p; if (np->in_redir & NAT_REDIRECT) nat->nat_dir = NAT_DIVERTIN; else nat->nat_dir = NAT_DIVERTOUT; return 0; } /* ------------------------------------------------------------------------ */ /* Function: nat6_builddivertmp */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: np(I) - pointer to a NAT rule */ /* */ /* For divert rules, a skeleton packet representing what will be prepended */ /* to the real packet is created. Even though we don't have the full */ /* packet here, a checksum is calculated that we update later when we */ /* fill in the final details. At present a 0 checksum for UDP is being set */ /* here because it is expected that divert will be used for localhost. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat6_builddivertmp(softn, np) - ipf_nat_softc_t *softn; - ipnat_t *np; +ipf_nat6_builddivertmp(ipf_nat_softc_t *softn, ipnat_t *np) { udphdr_t *uh; size_t len; ip6_t *ip6; if ((np->in_redir & NAT_DIVERTUDP) != 0) len = sizeof(ip6_t) + sizeof(udphdr_t); else len = sizeof(ip6_t); ALLOC_MB_T(np->in_divmp, len); if (np->in_divmp == NULL) { ATOMIC_INCL(softn->ipf_nat_stats.ns_divert_build); return -1; } /* * First, the header to get the packet diverted to the new destination */ ip6 = MTOD(np->in_divmp, ip6_t *); ip6->ip6_vfc = 0x60; if ((np->in_redir & NAT_DIVERTUDP) != 0) ip6->ip6_nxt = IPPROTO_UDP; else ip6->ip6_nxt = IPPROTO_IPIP; ip6->ip6_hlim = 255; ip6->ip6_plen = 0; ip6->ip6_src = np->in_snip6.in6; ip6->ip6_dst = np->in_dnip6.in6; if (np->in_redir & NAT_DIVERTUDP) { uh = (udphdr_t *)((u_char *)ip6 + sizeof(*ip6)); uh->uh_sum = 0; uh->uh_ulen = 8; uh->uh_sport = htons(np->in_spnext); uh->uh_dport = htons(np->in_dpnext); } return 0; } #define MINDECAP (sizeof(ip6_t) + sizeof(udphdr_t) + sizeof(ip6_t)) /* ------------------------------------------------------------------------ */ /* Function: nat6_decap */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* This function is responsible for undoing a packet's encapsulation in the */ /* reverse of an encap/divert rule. After removing the outer encapsulation */ /* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/ /* match the "new" packet as it may still be used by IPFilter elsewhere. */ /* We use "dir" here as the basis for some of the expectations about the */ /* outer header. If we return an error, the goal is to leave the original */ /* packet information undisturbed - this falls short at the end where we'd */ /* need to back a backup copy of "fin" - expensive. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat6_decap(fin, nat) - fr_info_t *fin; - nat_t *nat; +ipf_nat6_decap(fr_info_t *fin, nat_t *nat) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; char *hdr; int skip; mb_t *m; if ((fin->fin_flx & FI_ICMPERR) != 0) { return 0; } m = fin->fin_m; skip = fin->fin_hlen; switch (nat->nat_dir) { case NAT_DIVERTIN : case NAT_DIVERTOUT : if (fin->fin_plen < MINDECAP) return -1; skip += sizeof(udphdr_t); break; case NAT_ENCAPIN : case NAT_ENCAPOUT : if (fin->fin_plen < (skip + sizeof(ip6_t))) return -1; break; default : return -1; /* NOTREACHED */ } /* * The aim here is to keep the original packet details in "fin" for * as long as possible so that returning with an error is for the * original packet and there is little undoing work to do. */ if (M_LEN(m) < skip + sizeof(ip6_t)) { if (ipf_pr_pullup(fin, skip + sizeof(ip6_t)) == -1) return -1; } hdr = MTOD(fin->fin_m, char *); fin->fin_ip6 = (ip6_t *)(hdr + skip); if (ipf_pr_pullup(fin, skip + sizeof(ip6_t)) == -1) { NBUMPSIDE6D(fin->fin_out, ns_decap_pullup); return -1; } fin->fin_hlen = sizeof(ip6_t); fin->fin_dlen -= skip; fin->fin_plen -= skip; fin->fin_ipoff += skip; if (ipf_makefrip(sizeof(ip6_t), (ip_t *)hdr, fin) == -1) { NBUMPSIDE6D(fin->fin_out, ns_decap_bad); return -1; } return skip; } /* ------------------------------------------------------------------------ */ /* Function: nat6_nextaddr */ /* Returns: int - -1 == bad input (no new address), */ /* 0 == success and dst has new address */ /* Parameters: fin(I) - pointer to packet information */ /* na(I) - how to generate new address */ /* old(I) - original address being replaced */ /* dst(O) - where to put the new address */ /* Write Lock: ipf_nat */ /* */ /* This function uses the contents of the "na" structure, in combination */ /* with "old" to produce a new address to store in "dst". Not all of the */ /* possible uses of "na" will result in a new address. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat6_nextaddr(fin, na, old, dst) - fr_info_t *fin; - nat_addr_t *na; - i6addr_t *old, *dst; +ipf_nat6_nextaddr(fr_info_t *fin, nat_addr_t *na, i6addr_t *old, i6addr_t *dst) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; i6addr_t newip, new; u_32_t amin, amax; int error; new.i6[0] = 0; new.i6[1] = 0; new.i6[2] = 0; new.i6[3] = 0; amin = na->na_addr[0].in4.s_addr; switch (na->na_atype) { case FRI_RANGE : amax = na->na_addr[1].in4.s_addr; break; case FRI_NETMASKED : case FRI_DYNAMIC : case FRI_NORMAL : /* * Compute the maximum address by adding the inverse of the * netmask to the minimum address. */ amax = ~na->na_addr[1].in4.s_addr; amax |= amin; break; case FRI_LOOKUP : break; case FRI_BROADCAST : case FRI_PEERADDR : case FRI_NETWORK : default : return -1; } error = -1; switch (na->na_function) { case IPLT_DSTLIST : error = ipf_dstlist_select_node(fin, na->na_ptr, dst->i6, NULL); break; case IPLT_NONE : /* * 0/0 as the new address means leave it alone. */ if (na->na_addr[0].in4.s_addr == 0 && na->na_addr[1].in4.s_addr == 0) { new = *old; /* * 0/32 means get the interface's address */ } else if (IP6_ISZERO(&na->na_addr[0].in6) && IP6_ISONES(&na->na_addr[1].in6)) { if (ipf_ifpaddr(softc, 6, na->na_atype, fin->fin_ifp, &newip, NULL) == -1) { NBUMPSIDE6(fin->fin_out, ns_ifpaddrfail); return -1; } new = newip; } else { new.in6 = na->na_nextip6; } *dst = new; error = 0; break; default : NBUMPSIDE6(fin->fin_out, ns_badnextaddr); break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_nextaddrinit */ /* Returns: int - 0 == success, else error number */ /* Parameters: na(I) - NAT address information for generating new addr*/ /* base(I) - start of where to find strings */ /* initial(I) - flag indicating if it is the first call for */ /* this "na" structure. */ /* ifp(I) - network interface to derive address */ /* information from. */ /* */ /* This function is expected to be called in two scenarious: when a new NAT */ /* rule is loaded into the kernel and when the list of NAT rules is sync'd */ /* up with the valid network interfaces (possibly due to them changing.) */ /* To distinguish between these, the "initial" parameter is used. If it is */ /* 1 then this indicates the rule has just been reloaded and 0 for when we */ /* are updating information. This difference is important because in */ /* instances where we are not updating address information associated with */ /* a network interface, we don't want to disturb what the "next" address to */ /* come out of ipf_nat6_nextaddr() will be. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat6_nextaddrinit(softc, base, na, initial, ifp) - ipf_main_softc_t *softc; - char *base; - nat_addr_t *na; - int initial; - void *ifp; +ipf_nat6_nextaddrinit(ipf_main_softc_t *softc, char *base, nat_addr_t *na, + int initial, void *ifp) { switch (na->na_atype) { case FRI_LOOKUP : if (na->na_subtype == 0) { na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT, na->na_type, na->na_num, &na->na_func); } else if (na->na_subtype == 1) { na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT, na->na_type, base + na->na_num, &na->na_func); } if (na->na_func == NULL) { IPFERROR(60072); return ESRCH; } if (na->na_ptr == NULL) { IPFERROR(60073); return ESRCH; } break; case FRI_DYNAMIC : case FRI_BROADCAST : case FRI_NETWORK : case FRI_NETMASKED : case FRI_PEERADDR : if (ifp != NULL) (void )ipf_ifpaddr(softc, 6, na->na_atype, ifp, &na->na_addr[0], &na->na_addr[1]); break; case FRI_SPLIT : case FRI_RANGE : if (initial) na->na_nextip6 = na->na_addr[0].in6; break; case FRI_NONE : IP6_ANDASSIGN(&na->na_addr[0].in6, &na->na_addr[1].in6); return 0; case FRI_NORMAL : IP6_ANDASSIGN(&na->na_addr[0].in6, &na->na_addr[1].in6); break; default : IPFERROR(60074); return EINVAL; } if (initial && (na->na_atype == FRI_NORMAL)) { if (IP6_ISZERO(&na->na_addr[0].in6)) { if (IP6_ISONES(&na->na_addr[1].in6) || IP6_ISZERO(&na->na_addr[1].in6)) { return 0; } } na->na_nextip6 = na->na_addr[0].in6; if (!IP6_ISONES(&na->na_addr[1].in6)) { IP6_INC(&na->na_nextip6); } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat6_icmpquerytype */ /* Returns: int - 1 == success, 0 == failure */ /* Parameters: icmptype(I) - ICMP type number */ /* */ /* Tests to see if the ICMP type number passed is a query/response type or */ /* not. */ /* ------------------------------------------------------------------------ */ static int -ipf_nat6_icmpquerytype(icmptype) - int icmptype; +ipf_nat6_icmpquerytype(int icmptype) { /* * For the ICMP query NAT code, it is essential that both the query * and the reply match on the NAT rule. Because the NAT structure * does not keep track of the icmptype, and a single NAT structure * is used for all icmp types with the same src, dest and id, we * simply define the replies as queries as well. The funny thing is, * altough it seems silly to call a reply a query, this is exactly * as it is defined in the IPv4 specification */ switch (icmptype) { case ICMP6_ECHO_REPLY: case ICMP6_ECHO_REQUEST: /* route aedvertisement/solliciation is currently unsupported: */ /* it would require rewriting the ICMP data section */ case ICMP6_MEMBERSHIP_QUERY: case ICMP6_MEMBERSHIP_REPORT: case ICMP6_MEMBERSHIP_REDUCTION: case ICMP6_WRUREQUEST: case ICMP6_WRUREPLY: case MLD6_MTRACE_RESP: case MLD6_MTRACE: return 1; default: return 0; } } #endif /* USE_INET6 */ diff --git a/sys/netpfil/ipfilter/netinet/ip_netbios_pxy.c b/sys/netpfil/ipfilter/netinet/ip_netbios_pxy.c index d3f226325f42..72cb7c70e24b 100644 --- a/sys/netpfil/ipfilter/netinet/ip_netbios_pxy.c +++ b/sys/netpfil/ipfilter/netinet/ip_netbios_pxy.c @@ -1,120 +1,116 @@ /* * Simple netbios-dgm transparent proxy for in-kernel use. * For use with the NAT code. * $Id$ */ /*- * Copyright (c) 2002-2003 Paul J. Ledbetter III * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id$ */ #define IPF_NETBIOS_PROXY void ipf_p_netbios_main_load(void); void ipf_p_netbios_main_unload(void); int ipf_p_netbios_out(void *, fr_info_t *, ap_session_t *, nat_t *); static frentry_t netbiosfr; int netbios_proxy_init = 0; /* * Initialize local structures. */ void -ipf_p_netbios_main_load() +ipf_p_netbios_main_load(void) { bzero((char *)&netbiosfr, sizeof(netbiosfr)); netbiosfr.fr_ref = 1; netbiosfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&netbiosfr.fr_lock, "NETBIOS proxy rule lock"); netbios_proxy_init = 1; } void -ipf_p_netbios_main_unload() +ipf_p_netbios_main_unload(void) { if (netbios_proxy_init == 1) { MUTEX_DESTROY(&netbiosfr.fr_lock); netbios_proxy_init = 0; } } int -ipf_p_netbios_out(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_netbios_out(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { char dgmbuf[6]; int off, dlen; udphdr_t *udp; ip_t *ip; mb_t *m; aps = aps; /* LINT */ nat = nat; /* LINT */ m = fin->fin_m; dlen = fin->fin_dlen - sizeof(*udp); /* * no net bios datagram could possibly be shorter than this */ if (dlen < 11) return 0; ip = fin->fin_ip; udp = (udphdr_t *)fin->fin_dp; off = (char *)udp - (char *)ip + sizeof(*udp) + fin->fin_ipoff; /* * move past the * ip header; * udp header; * 4 bytes into the net bios dgm header. * According to rfc1002, this should be the exact location of * the source address/port */ off += 4; /* Copy NATed source Address/port*/ dgmbuf[0] = (char)((ip->ip_src.s_addr ) &0xFF); dgmbuf[1] = (char)((ip->ip_src.s_addr >> 8) &0xFF); dgmbuf[2] = (char)((ip->ip_src.s_addr >> 16)&0xFF); dgmbuf[3] = (char)((ip->ip_src.s_addr >> 24)&0xFF); dgmbuf[4] = (char)((udp->uh_sport )&0xFF); dgmbuf[5] = (char)((udp->uh_sport >> 8)&0xFF); /* replace data in packet */ COPYBACK(m, off, sizeof(dgmbuf), dgmbuf); return 0; } diff --git a/sys/netpfil/ipfilter/netinet/ip_pool.c b/sys/netpfil/ipfilter/netinet/ip_pool.c index bb026fb40b3e..3a7f4a73be53 100644 --- a/sys/netpfil/ipfilter/netinet/ip_pool.c +++ b/sys/netpfil/ipfilter/netinet/ip_pool.c @@ -1,1475 +1,1403 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #if !defined(_KERNEL) && !defined(__KERNEL__) # include # include # include # define _KERNEL # include # undef _KERNEL #else # include # if defined(NetBSD) && (__NetBSD_Version__ >= 104000000) # include # endif #endif #include #if defined(_KERNEL) && !defined(SOLARIS2) # include #endif #if defined(__SVR4) # include # ifdef _KERNEL # include # endif # include # include #endif #if defined(__FreeBSD__) # include #endif #include #include #include #if !defined(_KERNEL) # include "ipf.h" #endif #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_pool.h" #include "netinet/radix_ipf.h" /* END OF INCLUDES */ #if !defined(lint) static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; static const char rcsid[] = "@(#)$Id$"; #endif typedef struct ipf_pool_softc_s { void *ipf_radix; ip_pool_t *ipf_pool_list[LOOKUP_POOL_SZ]; ipf_pool_stat_t ipf_pool_stats; ip_pool_node_t *ipf_node_explist; } ipf_pool_softc_t; static void ipf_pool_clearnodes(ipf_main_softc_t *, ipf_pool_softc_t *, ip_pool_t *); static int ipf_pool_create(ipf_main_softc_t *, ipf_pool_softc_t *, iplookupop_t *); static int ipf_pool_deref(ipf_main_softc_t *, void *, void *); static int ipf_pool_destroy(ipf_main_softc_t *, ipf_pool_softc_t *, int, char *); static void *ipf_pool_exists(ipf_pool_softc_t *, int, char *); static void *ipf_pool_find(void *, int, char *); static ip_pool_node_t *ipf_pool_findeq(ipf_pool_softc_t *, ip_pool_t *, addrfamily_t *, addrfamily_t *); static void ipf_pool_free(ipf_main_softc_t *, ipf_pool_softc_t *, ip_pool_t *); static int ipf_pool_insert_node(ipf_main_softc_t *, ipf_pool_softc_t *, ip_pool_t *, struct ip_pool_node *); static int ipf_pool_iter_deref(ipf_main_softc_t *, void *, int, int, void *); static int ipf_pool_iter_next(ipf_main_softc_t *, void *, ipftoken_t *, ipflookupiter_t *); static size_t ipf_pool_flush(ipf_main_softc_t *, void *, iplookupflush_t *); static int ipf_pool_node_add(ipf_main_softc_t *, void *, iplookupop_t *, int); static int ipf_pool_node_del(ipf_main_softc_t *, void *, iplookupop_t *, int); static void ipf_pool_node_deref(ipf_pool_softc_t *, ip_pool_node_t *); static int ipf_pool_remove_node(ipf_main_softc_t *, ipf_pool_softc_t *, ip_pool_t *, ip_pool_node_t *); static int ipf_pool_search(ipf_main_softc_t *, void *, int, void *, u_int); static void *ipf_pool_soft_create(ipf_main_softc_t *); static void ipf_pool_soft_destroy(ipf_main_softc_t *, void *); static void ipf_pool_soft_fini(ipf_main_softc_t *, void *); static int ipf_pool_soft_init(ipf_main_softc_t *, void *); static int ipf_pool_stats_get(ipf_main_softc_t *, void *, iplookupop_t *); static int ipf_pool_table_add(ipf_main_softc_t *, void *, iplookupop_t *); static int ipf_pool_table_del(ipf_main_softc_t *, void *, iplookupop_t *); static void *ipf_pool_select_add_ref(void *, int, char *); static void ipf_pool_expire(ipf_main_softc_t *, void *); ipf_lookup_t ipf_pool_backend = { IPLT_POOL, ipf_pool_soft_create, ipf_pool_soft_destroy, ipf_pool_soft_init, ipf_pool_soft_fini, ipf_pool_search, ipf_pool_flush, ipf_pool_iter_deref, ipf_pool_iter_next, ipf_pool_node_add, ipf_pool_node_del, ipf_pool_stats_get, ipf_pool_table_add, ipf_pool_table_del, ipf_pool_deref, ipf_pool_find, ipf_pool_select_add_ref, NULL, ipf_pool_expire, NULL }; #ifdef TEST_POOL void treeprint(ip_pool_t *); int -main(argc, argv) - int argc; - char *argv[]; +main(int argc, char *argv[]) { ip_pool_node_t node; addrfamily_t a, b; iplookupop_t op; ip_pool_t *ipo; i6addr_t ip; RWLOCK_INIT(softc->ipf_poolrw, "poolrw"); ipf_pool_init(); bzero((char *)&ip, sizeof(ip)); bzero((char *)&op, sizeof(op)); bzero((char *)&node, sizeof(node)); strcpy(op.iplo_name, "0"); if (ipf_pool_create(&op) == 0) ipo = ipf_pool_exists(0, "0"); node.ipn_addr.adf_family = AF_INET; node.ipn_addr.adf_addr.in4.s_addr = 0x0a010203; node.ipn_mask.adf_addr.in4.s_addr = 0xffffffff; node.ipn_info = 1; ipf_pool_insert_node(ipo, &node); node.ipn_addr.adf_addr.in4.s_addr = 0x0a000000; node.ipn_mask.adf_addr.in4.s_addr = 0xff000000; node.ipn_info = 0; ipf_pool_insert_node(ipo, &node); node.ipn_addr.adf_addr.in4.s_addr = 0x0a010100; node.ipn_mask.adf_addr.in4.s_addr = 0xffffff00; node.ipn_info = 1; ipf_pool_insert_node(ipo, &node); node.ipn_addr.adf_addr.in4.s_addr = 0x0a010200; node.ipn_mask.adf_addr.in4.s_addr = 0xffffff00; node.ipn_info = 0; ipf_pool_insert_node(ipo, &node); node.ipn_addr.adf_addr.in4.s_addr = 0x0a010000; node.ipn_mask.adf_addr.in4.s_addr = 0xffff0000; node.ipn_info = 1; ipf_pool_insert_node(ipo, &node); node.ipn_addr.adf_addr.in4.s_addr = 0x0a01020f; node.ipn_mask.adf_addr.in4.s_addr = 0xffffffff; node.ipn_info = 1; ipf_pool_insert_node(ipo, &node); #ifdef DEBUG_POOL treeprint(ipo); #endif ip.in4.s_addr = 0x0a00aabb; printf("search(%#x) = %d (0)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a000001; printf("search(%#x) = %d (0)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a000101; printf("search(%#x) = %d (0)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a010001; printf("search(%#x) = %d (1)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a010101; printf("search(%#x) = %d (1)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a010201; printf("search(%#x) = %d (0)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a010203; printf("search(%#x) = %d (1)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0a01020f; printf("search(%#x) = %d (1)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); ip.in4.s_addr = 0x0b00aabb; printf("search(%#x) = %d (-1)\n", ip.in4.s_addr, ipf_pool_search(ipo, 4, &ip, 1)); #ifdef DEBUG_POOL treeprint(ipo); #endif ipf_pool_fini(); return 0; } void -treeprint(ipo) - ip_pool_t *ipo; +treeprint(ip_pool_t *ipo) { ip_pool_node_t *c; for (c = ipo->ipo_list; c != NULL; c = c->ipn_next) printf("Node %p(%s) (%#x/%#x) = %d hits %lu\n", c, c->ipn_name, c->ipn_addr.adf_addr.in4.s_addr, c->ipn_mask.adf_addr.in4.s_addr, c->ipn_info, c->ipn_hits); } #endif /* TEST_POOL */ /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_soft_create */ /* Returns: void * - NULL = failure, else pointer to local context */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise the routing table data structures where required. */ /* ------------------------------------------------------------------------ */ static void * -ipf_pool_soft_create(softc) - ipf_main_softc_t *softc; +ipf_pool_soft_create(ipf_main_softc_t *softc) { ipf_pool_softc_t *softp; KMALLOC(softp, ipf_pool_softc_t *); if (softp == NULL) { IPFERROR(70032); return NULL; } bzero((char *)softp, sizeof(*softp)); softp->ipf_radix = ipf_rx_create(); if (softp->ipf_radix == NULL) { IPFERROR(70033); KFREE(softp); return NULL; } return softp; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_soft_init */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Initialise the routing table data structures where required. */ /* ------------------------------------------------------------------------ */ static int -ipf_pool_soft_init(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_pool_soft_init(ipf_main_softc_t *softc, void *arg) { ipf_pool_softc_t *softp = arg; ipf_rx_init(softp->ipf_radix); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_soft_fini */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* Locks: WRITE(ipf_global) */ /* */ /* Clean up all the pool data structures allocated and call the cleanup */ /* function for the radix tree that supports the pools. ipf_pool_destroy is */ /* used to delete the pools one by one to ensure they're properly freed up. */ /* ------------------------------------------------------------------------ */ static void -ipf_pool_soft_fini(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_pool_soft_fini(ipf_main_softc_t *softc, void *arg) { ipf_pool_softc_t *softp = arg; ip_pool_t *p, *q; int i; softc = arg; for (i = -1; i <= IPL_LOGMAX; i++) { for (q = softp->ipf_pool_list[i + 1]; (p = q) != NULL; ) { q = p->ipo_next; (void) ipf_pool_destroy(softc, arg, i, p->ipo_name); } } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Clean up the pool by free'ing the radix tree associated with it and free */ /* up the pool context too. */ /* ------------------------------------------------------------------------ */ static void -ipf_pool_soft_destroy(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_pool_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_pool_softc_t *softp = arg; ipf_rx_destroy(softp->ipf_radix); KFREE(softp); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_node_add */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operatin data */ /* */ /* When adding a new node, a check is made to ensure that the address/mask */ /* pair supplied has been appropriately prepared by applying the mask to */ /* the address prior to calling for the pair to be added. */ /* ------------------------------------------------------------------------ */ static int -ipf_pool_node_add(softc, arg, op, uid) - ipf_main_softc_t *softc; - void *arg; - iplookupop_t *op; - int uid; +ipf_pool_node_add(ipf_main_softc_t *softc, void *arg, iplookupop_t *op, + int uid) { ip_pool_node_t node, *m; ip_pool_t *p; int err; if (op->iplo_size != sizeof(node)) { IPFERROR(70014); return EINVAL; } err = COPYIN(op->iplo_struct, &node, sizeof(node)); if (err != 0) { IPFERROR(70015); return EFAULT; } p = ipf_pool_find(arg, op->iplo_unit, op->iplo_name); if (p == NULL) { IPFERROR(70017); return ESRCH; } if (node.ipn_addr.adf_family == AF_INET) { if (node.ipn_addr.adf_len != offsetof(addrfamily_t, adf_addr) + sizeof(struct in_addr)) { IPFERROR(70028); return EINVAL; } } #ifdef USE_INET6 else if (node.ipn_addr.adf_family == AF_INET6) { if (node.ipn_addr.adf_len != offsetof(addrfamily_t, adf_addr) + sizeof(struct in6_addr)) { IPFERROR(70034); return EINVAL; } } #endif if (node.ipn_mask.adf_len != node.ipn_addr.adf_len) { IPFERROR(70029); return EINVAL; } /* * Check that the address/mask pair works. */ if (node.ipn_addr.adf_family == AF_INET) { if ((node.ipn_addr.adf_addr.in4.s_addr & node.ipn_mask.adf_addr.in4.s_addr) != node.ipn_addr.adf_addr.in4.s_addr) { IPFERROR(70035); return EINVAL; } } #ifdef USE_INET6 else if (node.ipn_addr.adf_family == AF_INET6) { if (IP6_MASKNEQ(&node.ipn_addr.adf_addr.in6, &node.ipn_mask.adf_addr.in6, &node.ipn_addr.adf_addr.in6)) { IPFERROR(70036); return EINVAL; } } #endif /* * add an entry to a pool - return an error if it already * exists remove an entry from a pool - if it exists * - in both cases, the pool *must* exist! */ m = ipf_pool_findeq(arg, p, &node.ipn_addr, &node.ipn_mask); if (m != NULL) { IPFERROR(70018); return EEXIST; } err = ipf_pool_insert_node(softc, arg, p, &node); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_node_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operatin data */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_pool_node_del(softc, arg, op, uid) - ipf_main_softc_t *softc; - void *arg; - iplookupop_t *op; - int uid; +ipf_pool_node_del(ipf_main_softc_t *softc, void *arg, iplookupop_t *op, + int uid) { ip_pool_node_t node, *m; ip_pool_t *p; int err; if (op->iplo_size != sizeof(node)) { IPFERROR(70019); return EINVAL; } node.ipn_uid = uid; err = COPYIN(op->iplo_struct, &node, sizeof(node)); if (err != 0) { IPFERROR(70020); return EFAULT; } if (node.ipn_addr.adf_family == AF_INET) { if (node.ipn_addr.adf_len != offsetof(addrfamily_t, adf_addr) + sizeof(struct in_addr)) { IPFERROR(70030); return EINVAL; } } #ifdef USE_INET6 else if (node.ipn_addr.adf_family == AF_INET6) { if (node.ipn_addr.adf_len != offsetof(addrfamily_t, adf_addr) + sizeof(struct in6_addr)) { IPFERROR(70037); return EINVAL; } } #endif if (node.ipn_mask.adf_len != node.ipn_addr.adf_len) { IPFERROR(70031); return EINVAL; } p = ipf_pool_find(arg, op->iplo_unit, op->iplo_name); if (p == NULL) { IPFERROR(70021); return ESRCH; } m = ipf_pool_findeq(arg, p, &node.ipn_addr, &node.ipn_mask); if (m == NULL) { IPFERROR(70022); return ENOENT; } if ((uid != 0) && (uid != m->ipn_uid)) { IPFERROR(70024); return EACCES; } err = ipf_pool_remove_node(softc, arg, p, m); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_table_add */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operatin data */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_pool_table_add(softc, arg, op) - ipf_main_softc_t *softc; - void *arg; - iplookupop_t *op; +ipf_pool_table_add(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) { int err; if (((op->iplo_arg & LOOKUP_ANON) == 0) && (ipf_pool_find(arg, op->iplo_unit, op->iplo_name) != NULL)) { IPFERROR(70023); err = EEXIST; } else { err = ipf_pool_create(softc, arg, op); } return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_table_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operatin data */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_pool_table_del(softc, arg, op) - ipf_main_softc_t *softc; - void *arg; - iplookupop_t *op; +ipf_pool_table_del(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) { return ipf_pool_destroy(softc, arg, op->iplo_unit, op->iplo_name); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_statistics */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* op(I) - pointer to lookup operatin data */ /* */ /* Copy the current statistics out into user space, collecting pool list */ /* pointers as appropriate for later use. */ /* ------------------------------------------------------------------------ */ static int -ipf_pool_stats_get(softc, arg, op) - ipf_main_softc_t *softc; - void *arg; - iplookupop_t *op; +ipf_pool_stats_get(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) { ipf_pool_softc_t *softp = arg; ipf_pool_stat_t stats; int unit, i, err = 0; if (op->iplo_size != sizeof(ipf_pool_stat_t)) { IPFERROR(70001); return EINVAL; } bcopy((char *)&softp->ipf_pool_stats, (char *)&stats, sizeof(stats)); unit = op->iplo_unit; if (unit == IPL_LOGALL) { for (i = 0; i <= LOOKUP_POOL_MAX; i++) stats.ipls_list[i] = softp->ipf_pool_list[i]; } else if (unit >= 0 && unit <= IPL_LOGMAX) { unit++; /* -1 => 0 */ if (op->iplo_name[0] != '\0') stats.ipls_list[unit] = ipf_pool_exists(softp, unit - 1, op->iplo_name); else stats.ipls_list[unit] = softp->ipf_pool_list[unit]; } else { IPFERROR(70025); err = EINVAL; } if (err == 0) { err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); if (err != 0) { IPFERROR(70026); return EFAULT; } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_exists */ /* Returns: int - 0 = success, else error */ /* Parameters: softp(I) - pointer to soft context pool information */ /* unit(I) - ipfilter device to which we are working on */ /* name(I) - name of the pool */ /* */ /* Find a matching pool inside the collection of pools for a particular */ /* device, indicated by the unit number. */ /* ------------------------------------------------------------------------ */ static void * -ipf_pool_exists(softp, unit, name) - ipf_pool_softc_t *softp; - int unit; - char *name; +ipf_pool_exists(ipf_pool_softc_t *softp, int unit, char *name) { ip_pool_t *p; int i; if (unit == IPL_LOGALL) { for (i = 0; i <= LOOKUP_POOL_MAX; i++) { for (p = softp->ipf_pool_list[i]; p != NULL; p = p->ipo_next) { if (strncmp(p->ipo_name, name, sizeof(p->ipo_name)) == 0) break; } if (p != NULL) break; } } else { for (p = softp->ipf_pool_list[unit + 1]; p != NULL; p = p->ipo_next) if (strncmp(p->ipo_name, name, sizeof(p->ipo_name)) == 0) break; } return p; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_find */ /* Returns: int - 0 = success, else error */ /* Parameters: arg(I) - pointer to local context to use */ /* unit(I) - ipfilter device to which we are working on */ /* name(I) - name of the pool */ /* */ /* Find a matching pool inside the collection of pools for a particular */ /* device, indicated by the unit number. If it is marked for deletion then */ /* pretend it does not exist. */ /* ------------------------------------------------------------------------ */ static void * -ipf_pool_find(arg, unit, name) - void *arg; - int unit; - char *name; +ipf_pool_find(void *arg, int unit, char *name) { ipf_pool_softc_t *softp = arg; ip_pool_t *p; p = ipf_pool_exists(softp, unit, name); if ((p != NULL) && (p->ipo_flags & IPOOL_DELETE)) return NULL; return p; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_select_add_ref */ /* Returns: int - 0 = success, else error */ /* Parameters: arg(I) - pointer to local context to use */ /* unit(I) - ipfilter device to which we are working on */ /* name(I) - name of the pool */ /* */ /* ------------------------------------------------------------------------ */ static void * -ipf_pool_select_add_ref(arg, unit, name) - void *arg; - int unit; - char *name; +ipf_pool_select_add_ref(void *arg, int unit, char *name) { ip_pool_t *p; p = ipf_pool_find(arg, -1, name); if (p == NULL) p = ipf_pool_find(arg, unit, name); if (p != NULL) { ATOMIC_INC32(p->ipo_ref); } return p; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_findeq */ /* Returns: int - 0 = success, else error */ /* Parameters: softp(I) - pointer to soft context pool information */ /* ipo(I) - pointer to the pool getting the new node. */ /* addr(I) - pointer to address information to match on */ /* mask(I) - pointer to the address mask to match */ /* */ /* Searches for an exact match of an entry in the pool. */ /* ------------------------------------------------------------------------ */ extern void printhostmask(int, u_32_t *, u_32_t *); static ip_pool_node_t * -ipf_pool_findeq(softp, ipo, addr, mask) - ipf_pool_softc_t *softp; - ip_pool_t *ipo; - addrfamily_t *addr, *mask; +ipf_pool_findeq(ipf_pool_softc_t *softp, ip_pool_t *ipo, addrfamily_t *addr, + addrfamily_t *mask) { ipf_rdx_node_t *n; n = ipo->ipo_head->lookup(ipo->ipo_head, addr, mask); return (ip_pool_node_t *)n; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_search */ /* Returns: int - 0 == +ve match, -1 == error, 1 == -ve/no match */ /* Parameters: softc(I) - pointer to soft context main structure */ /* tptr(I) - pointer to the pool to search */ /* version(I) - IP protocol version (4 or 6) */ /* dptr(I) - pointer to address information */ /* bytes(I) - length of packet */ /* */ /* Search the pool for a given address and return a search result. */ /* ------------------------------------------------------------------------ */ static int -ipf_pool_search(softc, tptr, ipversion, dptr, bytes) - ipf_main_softc_t *softc; - void *tptr; - int ipversion; - void *dptr; - u_int bytes; +ipf_pool_search(ipf_main_softc_t *softc, void *tptr, int ipversion, void *dptr, + u_int bytes) { ipf_rdx_node_t *rn; ip_pool_node_t *m; i6addr_t *addr; addrfamily_t v; ip_pool_t *ipo; int rv; ipo = tptr; if (ipo == NULL) return -1; rv = 1; m = NULL; addr = (i6addr_t *)dptr; bzero(&v, sizeof(v)); if (ipversion == 4) { v.adf_family = AF_INET; v.adf_len = offsetof(addrfamily_t, adf_addr) + sizeof(struct in_addr); v.adf_addr.in4 = addr->in4; #ifdef USE_INET6 } else if (ipversion == 6) { v.adf_family = AF_INET6; v.adf_len = offsetof(addrfamily_t, adf_addr) + sizeof(struct in6_addr); v.adf_addr.in6 = addr->in6; #endif } else return -1; READ_ENTER(&softc->ipf_poolrw); rn = ipo->ipo_head->matchaddr(ipo->ipo_head, &v); if ((rn != NULL) && (rn->root == 0)) { m = (ip_pool_node_t *)rn; ipo->ipo_hits++; m->ipn_bytes += bytes; m->ipn_hits++; rv = m->ipn_info; } RWLOCK_EXIT(&softc->ipf_poolrw); return rv; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_insert_node */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softp(I) - pointer to soft context pool information */ /* ipo(I) - pointer to the pool getting the new node. */ /* node(I) - structure with address/mask to add */ /* Locks: WRITE(ipf_poolrw) */ /* */ /* Add another node to the pool given by ipo. The three parameters passed */ /* in (addr, mask, info) shold all be stored in the node. */ /* ------------------------------------------------------------------------ */ static int -ipf_pool_insert_node(softc, softp, ipo, node) - ipf_main_softc_t *softc; - ipf_pool_softc_t *softp; - ip_pool_t *ipo; - struct ip_pool_node *node; +ipf_pool_insert_node(ipf_main_softc_t *softc, ipf_pool_softc_t *softp, + ip_pool_t *ipo, struct ip_pool_node *node) { ipf_rdx_node_t *rn; ip_pool_node_t *x; if ((node->ipn_addr.adf_len > sizeof(*rn)) || (node->ipn_addr.adf_len < 4)) { IPFERROR(70003); return EINVAL; } if ((node->ipn_mask.adf_len > sizeof(*rn)) || (node->ipn_mask.adf_len < 4)) { IPFERROR(70004); return EINVAL; } KMALLOC(x, ip_pool_node_t *); if (x == NULL) { IPFERROR(70002); return ENOMEM; } *x = *node; bzero((char *)x->ipn_nodes, sizeof(x->ipn_nodes)); x->ipn_owner = ipo; x->ipn_hits = 0; x->ipn_next = NULL; x->ipn_pnext = NULL; x->ipn_dnext = NULL; x->ipn_pdnext = NULL; if (x->ipn_die != 0) { /* * If the new node has a given expiration time, insert it * into the list of expiring nodes with the ones to be * removed first added to the front of the list. The * insertion is O(n) but it is kept sorted for quick scans * at expiration interval checks. */ ip_pool_node_t *n; x->ipn_die = softc->ipf_ticks + IPF_TTLVAL(x->ipn_die); for (n = softp->ipf_node_explist; n != NULL; n = n->ipn_dnext) { if (x->ipn_die < n->ipn_die) break; if (n->ipn_dnext == NULL) { /* * We've got to the last node and everything * wanted to be expired before this new node, * so we have to tack it on the end... */ n->ipn_dnext = x; x->ipn_pdnext = &n->ipn_dnext; n = NULL; break; } } if (softp->ipf_node_explist == NULL) { softp->ipf_node_explist = x; x->ipn_pdnext = &softp->ipf_node_explist; } else if (n != NULL) { x->ipn_dnext = n; x->ipn_pdnext = n->ipn_pdnext; n->ipn_pdnext = &x->ipn_dnext; } } rn = ipo->ipo_head->addaddr(ipo->ipo_head, &x->ipn_addr, &x->ipn_mask, x->ipn_nodes); #ifdef DEBUG_POOL printf("Added %p at %p\n", x, rn); #endif if (rn == NULL) { KFREE(x); IPFERROR(70005); return ENOMEM; } x->ipn_ref = 1; x->ipn_pnext = ipo->ipo_tail; *ipo->ipo_tail = x; ipo->ipo_tail = &x->ipn_next; softp->ipf_pool_stats.ipls_nodes++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_create */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softp(I) - pointer to soft context pool information */ /* op(I) - pointer to iplookup struct with call details */ /* Locks: WRITE(ipf_poolrw) */ /* */ /* Creates a new group according to the paramters passed in via the */ /* iplookupop structure. Does not check to see if the group already exists */ /* when being inserted - assume this has already been done. If the pool is */ /* marked as being anonymous, give it a new, unique, identifier. Call any */ /* other functions required to initialise the structure. */ /* */ /* If the structure is flagged for deletion then reset the flag and return, */ /* as this likely means we've tried to free a pool that is in use (flush) */ /* and now want to repopulate it with "new" data. */ /* ------------------------------------------------------------------------ */ static int -ipf_pool_create(softc, softp, op) - ipf_main_softc_t *softc; - ipf_pool_softc_t *softp; - iplookupop_t *op; +ipf_pool_create(ipf_main_softc_t *softc, ipf_pool_softc_t *softp, + iplookupop_t *op) { char name[FR_GROUPLEN]; int poolnum, unit; ip_pool_t *h; unit = op->iplo_unit; if ((op->iplo_arg & LOOKUP_ANON) == 0) { h = ipf_pool_exists(softp, unit, op->iplo_name); if (h != NULL) { if ((h->ipo_flags & IPOOL_DELETE) == 0) { IPFERROR(70006); return EEXIST; } h->ipo_flags &= ~IPOOL_DELETE; return 0; } } KMALLOC(h, ip_pool_t *); if (h == NULL) { IPFERROR(70007); return ENOMEM; } bzero(h, sizeof(*h)); if (ipf_rx_inithead(softp->ipf_radix, &h->ipo_head) != 0) { KFREE(h); IPFERROR(70008); return ENOMEM; } if ((op->iplo_arg & LOOKUP_ANON) != 0) { ip_pool_t *p; h->ipo_flags |= IPOOL_ANON; poolnum = LOOKUP_ANON; (void)snprintf(name, sizeof(name), "%x", poolnum); for (p = softp->ipf_pool_list[unit + 1]; p != NULL; ) { if (strncmp(name, p->ipo_name, sizeof(p->ipo_name)) == 0) { poolnum++; (void)snprintf(name, sizeof(name), "%x", poolnum); p = softp->ipf_pool_list[unit + 1]; } else p = p->ipo_next; } (void)strncpy(h->ipo_name, name, sizeof(h->ipo_name)); (void)strncpy(op->iplo_name, name, sizeof(op->iplo_name)); } else { (void)strncpy(h->ipo_name, op->iplo_name, sizeof(h->ipo_name)); } h->ipo_radix = softp->ipf_radix; h->ipo_ref = 1; h->ipo_list = NULL; h->ipo_tail = &h->ipo_list; h->ipo_unit = unit; h->ipo_next = softp->ipf_pool_list[unit + 1]; if (softp->ipf_pool_list[unit + 1] != NULL) softp->ipf_pool_list[unit + 1]->ipo_pnext = &h->ipo_next; h->ipo_pnext = &softp->ipf_pool_list[unit + 1]; softp->ipf_pool_list[unit + 1] = h; softp->ipf_pool_stats.ipls_pools++; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_remove_node */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* ipo(I) - pointer to the pool to remove the node from. */ /* ipe(I) - address being deleted as a node */ /* Locks: WRITE(ipf_poolrw) */ /* */ /* Remove a node from the pool given by ipo. */ /* ------------------------------------------------------------------------ */ static int -ipf_pool_remove_node(softc, softp, ipo, ipe) - ipf_main_softc_t *softc; - ipf_pool_softc_t *softp; - ip_pool_t *ipo; - ip_pool_node_t *ipe; +ipf_pool_remove_node(ipf_main_softc_t *softc, ipf_pool_softc_t *softp, + ip_pool_t *ipo, ip_pool_node_t *ipe) { void *ptr; if (ipo->ipo_tail == &ipe->ipn_next) ipo->ipo_tail = ipe->ipn_pnext; if (ipe->ipn_pnext != NULL) *ipe->ipn_pnext = ipe->ipn_next; if (ipe->ipn_next != NULL) ipe->ipn_next->ipn_pnext = ipe->ipn_pnext; if (ipe->ipn_pdnext != NULL) *ipe->ipn_pdnext = ipe->ipn_dnext; if (ipe->ipn_dnext != NULL) ipe->ipn_dnext->ipn_pdnext = ipe->ipn_pdnext; ptr = ipo->ipo_head->deladdr(ipo->ipo_head, &ipe->ipn_addr, &ipe->ipn_mask); if (ptr != NULL) { ipf_pool_node_deref(softp, ipe); return 0; } IPFERROR(70027); return ESRCH; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_destroy */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softp(I) - pointer to soft context pool information */ /* unit(I) - ipfilter device to which we are working on */ /* name(I) - name of the pool */ /* Locks: WRITE(ipf_poolrw) or WRITE(ipf_global) */ /* */ /* Search for a pool using paramters passed in and if it's not otherwise */ /* busy, free it. If it is busy, clear all of its nodes, mark it for being */ /* deleted and return an error saying it is busy. */ /* */ /* NOTE: Because this function is called out of ipfdetach() where ipf_poolrw*/ /* may not be initialised, we can't use an ASSERT to enforce the locking */ /* assertion that one of the two (ipf_poolrw,ipf_global) is held. */ /* ------------------------------------------------------------------------ */ static int -ipf_pool_destroy(softc, softp, unit, name) - ipf_main_softc_t *softc; - ipf_pool_softc_t *softp; - int unit; - char *name; +ipf_pool_destroy(ipf_main_softc_t *softc, ipf_pool_softc_t *softp, + int unit, char *name) { ip_pool_t *ipo; ipo = ipf_pool_exists(softp, unit, name); if (ipo == NULL) { IPFERROR(70009); return ESRCH; } if (ipo->ipo_ref != 1) { ipf_pool_clearnodes(softc, softp, ipo); ipo->ipo_flags |= IPOOL_DELETE; return 0; } ipf_pool_free(softc, softp, ipo); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_flush */ /* Returns: int - number of pools deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* fp(I) - which pool(s) to flush */ /* Locks: WRITE(ipf_poolrw) or WRITE(ipf_global) */ /* */ /* Free all pools associated with the device that matches the unit number */ /* passed in with operation. */ /* */ /* NOTE: Because this function is called out of ipfdetach() where ipf_poolrw*/ /* may not be initialised, we can't use an ASSERT to enforce the locking */ /* assertion that one of the two (ipf_poolrw,ipf_global) is held. */ /* ------------------------------------------------------------------------ */ static size_t -ipf_pool_flush(softc, arg, fp) - ipf_main_softc_t *softc; - void *arg; - iplookupflush_t *fp; +ipf_pool_flush(ipf_main_softc_t *softc, void *arg, iplookupflush_t *fp) { ipf_pool_softc_t *softp = arg; int i, num = 0, unit, err; ip_pool_t *p, *q; unit = fp->iplf_unit; for (i = -1; i <= IPL_LOGMAX; i++) { if (unit != IPLT_ALL && i != unit) continue; for (q = softp->ipf_pool_list[i + 1]; (p = q) != NULL; ) { q = p->ipo_next; err = ipf_pool_destroy(softc, softp, i, p->ipo_name); if (err == 0) num++; } } return num; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_free */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softp(I) - pointer to soft context pool information */ /* ipo(I) - pointer to pool structure */ /* Locks: WRITE(ipf_poolrw) or WRITE(ipf_global) */ /* */ /* Deletes the pool strucutre passed in from the list of pools and deletes */ /* all of the address information stored in it, including any tree data */ /* structures also allocated. */ /* */ /* NOTE: Because this function is called out of ipfdetach() where ipf_poolrw*/ /* may not be initialised, we can't use an ASSERT to enforce the locking */ /* assertion that one of the two (ipf_poolrw,ipf_global) is held. */ /* ------------------------------------------------------------------------ */ static void -ipf_pool_free(softc, softp, ipo) - ipf_main_softc_t *softc; - ipf_pool_softc_t *softp; - ip_pool_t *ipo; +ipf_pool_free(ipf_main_softc_t *softc, ipf_pool_softc_t *softp, ip_pool_t *ipo) { ipf_pool_clearnodes(softc, softp, ipo); if (ipo->ipo_next != NULL) ipo->ipo_next->ipo_pnext = ipo->ipo_pnext; *ipo->ipo_pnext = ipo->ipo_next; ipf_rx_freehead(ipo->ipo_head); KFREE(ipo); softp->ipf_pool_stats.ipls_pools--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_clearnodes */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softp(I) - pointer to soft context pool information */ /* ipo(I) - pointer to pool structure */ /* Locks: WRITE(ipf_poolrw) or WRITE(ipf_global) */ /* */ /* Deletes all nodes stored in a pool structure. */ /* ------------------------------------------------------------------------ */ static void -ipf_pool_clearnodes(softc, softp, ipo) - ipf_main_softc_t *softc; - ipf_pool_softc_t *softp; - ip_pool_t *ipo; +ipf_pool_clearnodes(ipf_main_softc_t *softc, ipf_pool_softc_t *softp, + ip_pool_t *ipo) { ip_pool_node_t *n, **next; for (next = &ipo->ipo_list; (n = *next) != NULL; ) ipf_pool_remove_node(softc, softp, ipo, n); ipo->ipo_list = NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_deref */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* pool(I) - pointer to pool structure */ /* Locks: WRITE(ipf_poolrw) */ /* */ /* Drop the number of known references to this pool structure by one and if */ /* we arrive at zero known references, free it. */ /* ------------------------------------------------------------------------ */ static int -ipf_pool_deref(softc, arg, pool) - ipf_main_softc_t *softc; - void *arg, *pool; +ipf_pool_deref(ipf_main_softc_t *softc, void *arg, void *pool) { ip_pool_t *ipo = pool; ipo->ipo_ref--; if (ipo->ipo_ref == 0) ipf_pool_free(softc, arg, ipo); else if ((ipo->ipo_ref == 1) && (ipo->ipo_flags & IPOOL_DELETE)) ipf_pool_destroy(softc, arg, ipo->ipo_unit, ipo->ipo_name); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_node_deref */ /* Returns: void */ /* Parameters: softp(I) - pointer to soft context pool information */ /* ipn(I) - pointer to pool structure */ /* Locks: WRITE(ipf_poolrw) */ /* */ /* Drop a reference to the pool node passed in and if we're the last, free */ /* it all up and adjust the stats accordingly. */ /* ------------------------------------------------------------------------ */ static void -ipf_pool_node_deref(softp, ipn) - ipf_pool_softc_t *softp; - ip_pool_node_t *ipn; +ipf_pool_node_deref(ipf_pool_softc_t *softp, ip_pool_node_t *ipn) { ipn->ipn_ref--; if (ipn->ipn_ref == 0) { KFREE(ipn); softp->ipf_pool_stats.ipls_nodes--; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_iter_next */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* token(I) - pointer to pool structure */ /* ilp(IO) - pointer to pool iterating structure */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_pool_iter_next(softc, arg, token, ilp) - ipf_main_softc_t *softc; - void *arg; - ipftoken_t *token; - ipflookupiter_t *ilp; +ipf_pool_iter_next(ipf_main_softc_t *softc, void *arg, ipftoken_t *token, + ipflookupiter_t *ilp) { ipf_pool_softc_t *softp = arg; ip_pool_node_t *node, zn, *nextnode; ip_pool_t *ipo, zp, *nextipo; void *pnext; int err; err = 0; node = NULL; nextnode = NULL; ipo = NULL; nextipo = NULL; READ_ENTER(&softc->ipf_poolrw); switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : ipo = token->ipt_data; if (ipo == NULL) { nextipo = softp->ipf_pool_list[(int)ilp->ili_unit + 1]; } else { nextipo = ipo->ipo_next; } if (nextipo != NULL) { ATOMIC_INC32(nextipo->ipo_ref); token->ipt_data = nextipo; } else { bzero((char *)&zp, sizeof(zp)); nextipo = &zp; token->ipt_data = NULL; } pnext = nextipo->ipo_next; break; case IPFLOOKUPITER_NODE : node = token->ipt_data; if (node == NULL) { ipo = ipf_pool_exists(arg, ilp->ili_unit, ilp->ili_name); if (ipo == NULL) { IPFERROR(70010); err = ESRCH; } else { nextnode = ipo->ipo_list; ipo = NULL; } } else { nextnode = node->ipn_next; } if (nextnode != NULL) { ATOMIC_INC32(nextnode->ipn_ref); token->ipt_data = nextnode; } else { bzero((char *)&zn, sizeof(zn)); nextnode = &zn; token->ipt_data = NULL; } pnext = nextnode->ipn_next; break; default : IPFERROR(70011); pnext = NULL; err = EINVAL; break; } RWLOCK_EXIT(&softc->ipf_poolrw); if (err != 0) return err; switch (ilp->ili_otype) { case IPFLOOKUPITER_LIST : err = COPYOUT(nextipo, ilp->ili_data, sizeof(*nextipo)); if (err != 0) { IPFERROR(70012); err = EFAULT; } if (ipo != NULL) { WRITE_ENTER(&softc->ipf_poolrw); ipf_pool_deref(softc, softp, ipo); RWLOCK_EXIT(&softc->ipf_poolrw); } break; case IPFLOOKUPITER_NODE : err = COPYOUT(nextnode, ilp->ili_data, sizeof(*nextnode)); if (err != 0) { IPFERROR(70013); err = EFAULT; } if (node != NULL) { WRITE_ENTER(&softc->ipf_poolrw); ipf_pool_node_deref(softp, node); RWLOCK_EXIT(&softc->ipf_poolrw); } break; } if (pnext == NULL) ipf_token_mark_complete(token); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_iterderef */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* unit(I) - ipfilter device to which we are working on */ /* Locks: WRITE(ipf_poolrw) */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_pool_iter_deref(softc, arg, otype, unit, data) - ipf_main_softc_t *softc; - void *arg; - int otype; - int unit; - void *data; +ipf_pool_iter_deref(ipf_main_softc_t *softc, void *arg, int otype, int unit, + void *data) { ipf_pool_softc_t *softp = arg; if (data == NULL) return EINVAL; if (unit < 0 || unit > IPL_LOGMAX) return EINVAL; switch (otype) { case IPFLOOKUPITER_LIST : ipf_pool_deref(softc, softp, (ip_pool_t *)data); break; case IPFLOOKUPITER_NODE : ipf_pool_node_deref(softp, (ip_pool_node_t *)data); break; default : break; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pool_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* At present this function exists just to support temporary addition of */ /* nodes to the address pool. */ /* ------------------------------------------------------------------------ */ static void -ipf_pool_expire(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_pool_expire(ipf_main_softc_t *softc, void *arg) { ipf_pool_softc_t *softp = arg; ip_pool_node_t *n; while ((n = softp->ipf_node_explist) != NULL) { /* * Because the list is kept sorted on insertion, the fist * one that dies in the future means no more work to do. */ if (n->ipn_die > softc->ipf_ticks) break; ipf_pool_remove_node(softc, softp, n->ipn_owner, n); } } #ifndef _KERNEL void ipf_pool_dump(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_pool_softc_t *softp = arg; ip_pool_t *ipl; int i; printf("List of configured pools\n"); for (i = 0; i <= LOOKUP_POOL_MAX; i++) for (ipl = softp->ipf_pool_list[i]; ipl != NULL; ipl = ipl->ipo_next) printpool(ipl, bcopywrap, NULL, opts, NULL); } #endif diff --git a/sys/netpfil/ipfilter/netinet/ip_pptp_pxy.c b/sys/netpfil/ipfilter/netinet/ip_pptp_pxy.c index e18748a05fae..f265dd6e2562 100644 --- a/sys/netpfil/ipfilter/netinet/ip_pptp_pxy.c +++ b/sys/netpfil/ipfilter/netinet/ip_pptp_pxy.c @@ -1,580 +1,557 @@ /* * Copyright (C) 2012 by Darren Reed. * * Simple PPTP transparent proxy for in-kernel use. For use with the NAT * code. * * $Id$ * */ #define IPF_PPTP_PROXY /* * PPTP proxy */ typedef struct pptp_side { u_32_t pptps_nexthdr; u_32_t pptps_next; int pptps_state; int pptps_gothdr; int pptps_len; int pptps_bytes; char *pptps_wptr; char pptps_buffer[512]; } pptp_side_t; typedef struct pptp_pxy { nat_t *pptp_nat; struct ipstate *pptp_state; u_short pptp_call[2]; pptp_side_t pptp_side[2]; ipnat_t *pptp_rule; } pptp_pxy_t; typedef struct pptp_hdr { u_short pptph_len; u_short pptph_type; u_32_t pptph_cookie; } pptp_hdr_t; #define PPTP_MSGTYPE_CTL 1 #define PPTP_MTCTL_STARTREQ 1 #define PPTP_MTCTL_STARTREP 2 #define PPTP_MTCTL_STOPREQ 3 #define PPTP_MTCTL_STOPREP 4 #define PPTP_MTCTL_ECHOREQ 5 #define PPTP_MTCTL_ECHOREP 6 #define PPTP_MTCTL_OUTREQ 7 #define PPTP_MTCTL_OUTREP 8 #define PPTP_MTCTL_INREQ 9 #define PPTP_MTCTL_INREP 10 #define PPTP_MTCTL_INCONNECT 11 #define PPTP_MTCTL_CLEAR 12 #define PPTP_MTCTL_DISCONNECT 13 #define PPTP_MTCTL_WANERROR 14 #define PPTP_MTCTL_LINKINFO 15 void ipf_p_pptp_main_load(void); void ipf_p_pptp_main_unload(void); int ipf_p_pptp_new(void *, fr_info_t *, ap_session_t *, nat_t *); void ipf_p_pptp_del(ipf_main_softc_t *, ap_session_t *); int ipf_p_pptp_inout(void *, fr_info_t *, ap_session_t *, nat_t *); void ipf_p_pptp_donatstate(fr_info_t *, nat_t *, pptp_pxy_t *); int ipf_p_pptp_message(fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *); int ipf_p_pptp_nextmessage(fr_info_t *, nat_t *, pptp_pxy_t *, int); int ipf_p_pptp_mctl(fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *); static frentry_t pptpfr; static int pptp_proxy_init = 0; static int ipf_p_pptp_debug = 0; static int ipf_p_pptp_gretimeout = IPF_TTLVAL(120); /* 2 minutes */ /* * PPTP application proxy initialization. */ void -ipf_p_pptp_main_load() +ipf_p_pptp_main_load(void) { bzero((char *)&pptpfr, sizeof(pptpfr)); pptpfr.fr_ref = 1; pptpfr.fr_age[0] = ipf_p_pptp_gretimeout; pptpfr.fr_age[1] = ipf_p_pptp_gretimeout; pptpfr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&pptpfr.fr_lock, "PPTP proxy rule lock"); pptp_proxy_init = 1; } void -ipf_p_pptp_main_unload() +ipf_p_pptp_main_unload(void) { if (pptp_proxy_init == 1) { MUTEX_DESTROY(&pptpfr.fr_lock); pptp_proxy_init = 0; } } /* * Setup for a new PPTP proxy. * * NOTE: The printf's are broken up with %s in them to prevent them being * optimised into puts statements on FreeBSD (this doesn't exist in the kernel) */ int -ipf_p_pptp_new(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_pptp_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { pptp_pxy_t *pptp; ipnat_t *ipn; ipnat_t *np; int size; ip_t *ip; if (fin->fin_v != 4) return -1; ip = fin->fin_ip; np = nat->nat_ptr; size = np->in_size; if (ipf_nat_outlookup(fin, 0, IPPROTO_GRE, nat->nat_osrcip, ip->ip_dst) != NULL) { if (ipf_p_pptp_debug > 0) printf("ipf_p_pptp_new: GRE session already exists\n"); return -1; } KMALLOC(pptp, pptp_pxy_t *); if (pptp == NULL) { if (ipf_p_pptp_debug > 0) printf("ipf_p_pptp_new: malloc for aps_data failed\n"); return -1; } KMALLOCS(ipn, ipnat_t *, size); if (ipn == NULL) { KFREE(pptp); return -1; } aps->aps_data = pptp; aps->aps_psiz = sizeof(*pptp); bzero((char *)pptp, sizeof(*pptp)); bzero((char *)ipn, size); pptp->pptp_rule = ipn; /* * Create NAT rule against which the tunnel/transport mapping is * created. This is required because the current NAT rule does not * describe GRE but TCP instead. */ ipn->in_size = size; ipn->in_ifps[0] = fin->fin_ifp; ipn->in_apr = NULL; ipn->in_use = 1; ipn->in_hits = 1; ipn->in_ippip = 1; ipn->in_snip = ntohl(nat->nat_nsrcaddr); ipn->in_nsrcaddr = fin->fin_saddr; ipn->in_dnip = ntohl(nat->nat_ndstaddr); ipn->in_ndstaddr = nat->nat_ndstaddr; ipn->in_redir = np->in_redir; ipn->in_osrcaddr = nat->nat_osrcaddr; ipn->in_odstaddr = nat->nat_odstaddr; ipn->in_osrcmsk = 0xffffffff; ipn->in_nsrcmsk = 0xffffffff; ipn->in_odstmsk = 0xffffffff; ipn->in_ndstmsk = 0xffffffff; ipn->in_flags = (np->in_flags | IPN_PROXYRULE); MUTEX_INIT(&ipn->in_lock, "pptp proxy NAT rule"); ipn->in_namelen = np->in_namelen; bcopy(np->in_names, ipn->in_ifnames, ipn->in_namelen); ipn->in_ifnames[0] = np->in_ifnames[0]; ipn->in_ifnames[1] = np->in_ifnames[1]; ipn->in_pr[0] = IPPROTO_GRE; ipn->in_pr[1] = IPPROTO_GRE; pptp->pptp_side[0].pptps_wptr = pptp->pptp_side[0].pptps_buffer; pptp->pptp_side[1].pptps_wptr = pptp->pptp_side[1].pptps_buffer; return 0; } void -ipf_p_pptp_donatstate(fin, nat, pptp) - fr_info_t *fin; - nat_t *nat; - pptp_pxy_t *pptp; +ipf_p_pptp_donatstate(fr_info_t *fin, nat_t *nat, pptp_pxy_t *pptp) { ipf_main_softc_t *softc = fin->fin_main_soft; fr_info_t fi; grehdr_t gre; nat_t *nat2; u_char p; ip_t *ip; ip = fin->fin_ip; p = ip->ip_p; nat2 = pptp->pptp_nat; if ((nat2 == NULL) || (pptp->pptp_state == NULL)) { bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)&gre, sizeof(gre)); fi.fin_fi.fi_p = IPPROTO_GRE; fi.fin_fr = &pptpfr; if ((nat->nat_dir == NAT_OUTBOUND && fin->fin_out) || (nat->nat_dir == NAT_INBOUND && !fin->fin_out)) { fi.fin_data[0] = pptp->pptp_call[0]; fi.fin_data[1] = pptp->pptp_call[1]; } else { fi.fin_data[0] = pptp->pptp_call[1]; fi.fin_data[1] = pptp->pptp_call[0]; } ip = fin->fin_ip; ip->ip_p = IPPROTO_GRE; fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); fi.fin_flx |= FI_IGNORE; fi.fin_dp = &gre; gre.gr_flags = htons(1 << 13); fi.fin_fi.fi_saddr = nat->nat_osrcaddr; fi.fin_fi.fi_daddr = nat->nat_odstaddr; } /* * Update NAT timeout/create NAT if missing. */ if (nat2 != NULL) ipf_queueback(softc->ipf_ticks, &nat2->nat_tqe); else { #ifdef USE_MUTEXES ipf_nat_softc_t *softn = softc->ipf_nat_soft; #endif MUTEX_ENTER(&softn->ipf_nat_new); nat2 = ipf_nat_add(&fi, pptp->pptp_rule, &pptp->pptp_nat, NAT_SLAVE, nat->nat_dir); MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 != NULL) { (void) ipf_nat_proto(&fi, nat2, 0); MUTEX_ENTER(&nat2->nat_lock); ipf_nat_update(&fi, nat2); MUTEX_EXIT(&nat2->nat_lock); } } READ_ENTER(&softc->ipf_state); if (pptp->pptp_state != NULL) { ipf_queueback(softc->ipf_ticks, &pptp->pptp_state->is_sti); RWLOCK_EXIT(&softc->ipf_state); } else { RWLOCK_EXIT(&softc->ipf_state); if (nat2 != NULL) { if (nat->nat_dir == NAT_INBOUND) fi.fin_fi.fi_daddr = nat2->nat_ndstaddr; else fi.fin_fi.fi_saddr = nat2->nat_osrcaddr; } fi.fin_ifp = NULL; (void) ipf_state_add(softc, &fi, &pptp->pptp_state, 0); } ip->ip_p = p; return; } /* * Try and build up the next PPTP message in the TCP stream and if we can * build it up completely (fits in our buffer) then pass it off to the message * parsing function. */ int -ipf_p_pptp_nextmessage(fin, nat, pptp, rev) - fr_info_t *fin; - nat_t *nat; - pptp_pxy_t *pptp; - int rev; +ipf_p_pptp_nextmessage(fr_info_t *fin, nat_t *nat, pptp_pxy_t *pptp, int rev) { static const char *funcname = "ipf_p_pptp_nextmessage"; pptp_side_t *pptps; u_32_t start, end; pptp_hdr_t *hdr; tcphdr_t *tcp; int dlen, off; u_short len; char *msg; tcp = fin->fin_dp; dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2); start = ntohl(tcp->th_seq); pptps = &pptp->pptp_side[rev]; off = (char *)tcp - (char *)fin->fin_ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; if (dlen <= 0) return 0; /* * If the complete data packet is before what we expect to see * "next", just ignore it as the chances are we've already seen it. * The next if statement following this one really just causes packets * ahead of what we've seen to be dropped, implying that something in * the middle went missing and we want to see that first. */ end = start + dlen; if (pptps->pptps_next > end && pptps->pptps_next > start) return 0; if (pptps->pptps_next != start) { if (ipf_p_pptp_debug > 5) printf("%s: next (%x) != start (%x)\n", funcname, pptps->pptps_next, start); return -1; } msg = (char *)fin->fin_dp + (TCP_OFF(tcp) << 2); while (dlen > 0) { off += pptps->pptps_bytes; if (pptps->pptps_gothdr == 0) { /* * PPTP has an 8 byte header that inclues the cookie. * The start of every message should include one and * it should match 1a2b3c4d. Byte order is ignored, * deliberately, when printing out the error. */ len = MIN(8 - pptps->pptps_bytes, dlen); COPYDATA(fin->fin_m, off, len, pptps->pptps_wptr); pptps->pptps_bytes += len; pptps->pptps_wptr += len; hdr = (pptp_hdr_t *)pptps->pptps_buffer; if (pptps->pptps_bytes == 8) { pptps->pptps_next += 8; if (ntohl(hdr->pptph_cookie) != 0x1a2b3c4d) { if (ipf_p_pptp_debug > 1) printf("%s: bad cookie (%x)\n", funcname, hdr->pptph_cookie); return -1; } } dlen -= len; msg += len; off += len; pptps->pptps_gothdr = 1; len = ntohs(hdr->pptph_len); pptps->pptps_len = len; pptps->pptps_nexthdr += len; /* * If a message is too big for the buffer, just set * the fields for the next message to come along. * The messages defined in RFC 2637 will not exceed * 512 bytes (in total length) so this is likely a * bad data packet, anyway. */ if (len > sizeof(pptps->pptps_buffer)) { if (ipf_p_pptp_debug > 3) printf("%s: message too big (%d)\n", funcname, len); pptps->pptps_next = pptps->pptps_nexthdr; pptps->pptps_wptr = pptps->pptps_buffer; pptps->pptps_gothdr = 0; pptps->pptps_bytes = 0; pptps->pptps_len = 0; break; } } len = MIN(pptps->pptps_len - pptps->pptps_bytes, dlen); COPYDATA(fin->fin_m, off, len, pptps->pptps_wptr); pptps->pptps_bytes += len; pptps->pptps_wptr += len; pptps->pptps_next += len; if (pptps->pptps_len > pptps->pptps_bytes) break; ipf_p_pptp_message(fin, nat, pptp, pptps); pptps->pptps_wptr = pptps->pptps_buffer; pptps->pptps_gothdr = 0; pptps->pptps_bytes = 0; pptps->pptps_len = 0; start += len; msg += len; dlen -= len; } return 0; } /* * handle a complete PPTP message */ int -ipf_p_pptp_message(fin, nat, pptp, pptps) - fr_info_t *fin; - nat_t *nat; - pptp_pxy_t *pptp; - pptp_side_t *pptps; +ipf_p_pptp_message(fr_info_t *fin, nat_t *nat, pptp_pxy_t *pptp, + pptp_side_t *pptps) { pptp_hdr_t *hdr = (pptp_hdr_t *)pptps->pptps_buffer; switch (ntohs(hdr->pptph_type)) { case PPTP_MSGTYPE_CTL : ipf_p_pptp_mctl(fin, nat, pptp, pptps); break; default : break; } return 0; } /* * handle a complete PPTP control message */ int -ipf_p_pptp_mctl(fin, nat, pptp, pptps) - fr_info_t *fin; - nat_t *nat; - pptp_pxy_t *pptp; - pptp_side_t *pptps; +ipf_p_pptp_mctl(fr_info_t *fin, nat_t *nat, pptp_pxy_t *pptp, + pptp_side_t *pptps) { u_short *buffer = (u_short *)(pptps->pptps_buffer); pptp_side_t *pptpo; if (pptps == &pptp->pptp_side[0]) pptpo = &pptp->pptp_side[1]; else pptpo = &pptp->pptp_side[0]; /* * Breakout to handle all the various messages. Most are just state * transition. */ switch (ntohs(buffer[4])) { case PPTP_MTCTL_STARTREQ : pptps->pptps_state = PPTP_MTCTL_STARTREQ; break; case PPTP_MTCTL_STARTREP : if (pptpo->pptps_state == PPTP_MTCTL_STARTREQ) pptps->pptps_state = PPTP_MTCTL_STARTREP; break; case PPTP_MTCTL_STOPREQ : pptps->pptps_state = PPTP_MTCTL_STOPREQ; break; case PPTP_MTCTL_STOPREP : if (pptpo->pptps_state == PPTP_MTCTL_STOPREQ) pptps->pptps_state = PPTP_MTCTL_STOPREP; break; case PPTP_MTCTL_ECHOREQ : pptps->pptps_state = PPTP_MTCTL_ECHOREQ; break; case PPTP_MTCTL_ECHOREP : if (pptpo->pptps_state == PPTP_MTCTL_ECHOREQ) pptps->pptps_state = PPTP_MTCTL_ECHOREP; break; case PPTP_MTCTL_OUTREQ : pptps->pptps_state = PPTP_MTCTL_OUTREQ; break; case PPTP_MTCTL_OUTREP : if (pptpo->pptps_state == PPTP_MTCTL_OUTREQ) { pptps->pptps_state = PPTP_MTCTL_OUTREP; pptp->pptp_call[0] = buffer[7]; pptp->pptp_call[1] = buffer[6]; ipf_p_pptp_donatstate(fin, nat, pptp); } break; case PPTP_MTCTL_INREQ : pptps->pptps_state = PPTP_MTCTL_INREQ; break; case PPTP_MTCTL_INREP : if (pptpo->pptps_state == PPTP_MTCTL_INREQ) { pptps->pptps_state = PPTP_MTCTL_INREP; pptp->pptp_call[0] = buffer[7]; pptp->pptp_call[1] = buffer[6]; ipf_p_pptp_donatstate(fin, nat, pptp); } break; case PPTP_MTCTL_INCONNECT : pptps->pptps_state = PPTP_MTCTL_INCONNECT; break; case PPTP_MTCTL_CLEAR : pptps->pptps_state = PPTP_MTCTL_CLEAR; break; case PPTP_MTCTL_DISCONNECT : pptps->pptps_state = PPTP_MTCTL_DISCONNECT; break; case PPTP_MTCTL_WANERROR : pptps->pptps_state = PPTP_MTCTL_WANERROR; break; case PPTP_MTCTL_LINKINFO : pptps->pptps_state = PPTP_MTCTL_LINKINFO; break; } return 0; } /* * For outgoing PPTP packets. refresh timeouts for NAT & state entries, if * we can. If they have disappeared, recreate them. */ int -ipf_p_pptp_inout(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_pptp_inout(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { pptp_pxy_t *pptp; tcphdr_t *tcp; int rev; if ((fin->fin_out == 1) && (nat->nat_dir == NAT_INBOUND)) rev = 1; else if ((fin->fin_out == 0) && (nat->nat_dir == NAT_OUTBOUND)) rev = 1; else rev = 0; tcp = (tcphdr_t *)fin->fin_dp; if ((tcp->th_flags & TH_OPENING) == TH_OPENING) { pptp = (pptp_pxy_t *)aps->aps_data; pptp->pptp_side[1 - rev].pptps_next = ntohl(tcp->th_ack); pptp->pptp_side[1 - rev].pptps_nexthdr = ntohl(tcp->th_ack); pptp->pptp_side[rev].pptps_next = ntohl(tcp->th_seq) + 1; pptp->pptp_side[rev].pptps_nexthdr = ntohl(tcp->th_seq) + 1; } return ipf_p_pptp_nextmessage(fin, nat, (pptp_pxy_t *)aps->aps_data, rev); } /* * clean up after ourselves. */ void -ipf_p_pptp_del(softc, aps) - ipf_main_softc_t *softc; - ap_session_t *aps; +ipf_p_pptp_del(ipf_main_softc_t *softc, ap_session_t *aps) { pptp_pxy_t *pptp; pptp = aps->aps_data; if (pptp != NULL) { /* * Don't bother changing any of the NAT structure details, * *_del() is on a callback from aps_free(), from nat_delete() */ READ_ENTER(&softc->ipf_state); if (pptp->pptp_state != NULL) { ipf_state_setpending(softc, pptp->pptp_state); } RWLOCK_EXIT(&softc->ipf_state); if (pptp->pptp_nat != NULL) ipf_nat_setpending(softc, pptp->pptp_nat); pptp->pptp_rule->in_flags |= IPN_DELETE; ipf_nat_rule_deref(softc, &pptp->pptp_rule); } } diff --git a/sys/netpfil/ipfilter/netinet/ip_proxy.c b/sys/netpfil/ipfilter/netinet/ip_proxy.c index 52991723b25b..3620652b8011 100644 --- a/sys/netpfil/ipfilter/netinet/ip_proxy.c +++ b/sys/netpfil/ipfilter/netinet/ip_proxy.c @@ -1,1465 +1,1423 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include # include #if !defined(_KERNEL) && !defined(__KERNEL__) # include # include # include # include # define _KERNEL # include # undef _KERNEL #endif # include #include #if defined(_KERNEL) #ifdef __FreeBSD__ # include # endif # include # if !defined(__SVR4) # include # endif #endif #if defined(_KERNEL) && defined(__FreeBSD__) # include # include #else # include #endif #if defined(__SVR4) # include # ifdef _KERNEL # include # endif # include # include #endif #ifdef __FreeBSD__ # include #endif #include #if defined(__FreeBSD__) && defined(_KERNEL) #include #else #define CURVNET_SET(arg) #define CURVNET_RESTORE() #define VNET_DEFINE(_t, _v) _t _v #define VNET_DECLARE(_t, _v) extern _t _v #define VNET(arg) arg #endif #ifdef sun # include #endif #include #include #include # include #include #include #include #include "netinet/ip_compat.h" #include #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #if defined(__FreeBSD__) # include #endif /* END OF INCLUDES */ #include "netinet/ip_ftp_pxy.c" #include "netinet/ip_tftp_pxy.c" #include "netinet/ip_rcmd_pxy.c" #include "netinet/ip_pptp_pxy.c" #if defined(_KERNEL) # include "netinet/ip_irc_pxy.c" # include "netinet/ip_raudio_pxy.c" # include "netinet/ip_netbios_pxy.c" #endif #include "netinet/ip_ipsec_pxy.c" #include "netinet/ip_rpcb_pxy.c" #if !defined(lint) static const char rcsid[] = "@(#)$Id$"; #endif #define AP_SESS_SIZE 53 static int ipf_proxy_fixseqack(fr_info_t *, ip_t *, ap_session_t *, int ); static aproxy_t *ipf_proxy_create_clone(ipf_main_softc_t *, aproxy_t *); typedef struct ipf_proxy_softc_s { int ips_proxy_debug; int ips_proxy_session_size; ap_session_t **ips_sess_tab; ap_session_t *ips_sess_list; aproxy_t *ips_proxies; int ips_init_run; ipftuneable_t *ipf_proxy_tune; } ipf_proxy_softc_t; static ipftuneable_t ipf_proxy_tuneables[] = { { { (void *)offsetof(ipf_proxy_softc_t, ips_proxy_debug) }, "proxy_debug", 0, 0x1f, stsizeof(ipf_proxy_softc_t, ips_proxy_debug), 0, NULL, NULL }, { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL} }; static aproxy_t *ap_proxylist = NULL; static aproxy_t ips_proxies[] = { #ifdef IPF_FTP_PROXY { NULL, NULL, "ftp", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_ftp_main_load, ipf_p_ftp_main_unload, ipf_p_ftp_soft_create, ipf_p_ftp_soft_destroy, NULL, NULL, ipf_p_ftp_new, ipf_p_ftp_del, ipf_p_ftp_in, ipf_p_ftp_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_TFTP_PROXY { NULL, NULL, "tftp", (char)IPPROTO_UDP, 0, 0, 0, ipf_p_tftp_main_load, ipf_p_tftp_main_unload, ipf_p_tftp_soft_create, ipf_p_tftp_soft_destroy, NULL, NULL, ipf_p_tftp_new, ipf_p_tftp_del, ipf_p_tftp_in, ipf_p_tftp_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_IRC_PROXY { NULL, NULL, "irc", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_irc_main_load, ipf_p_irc_main_unload, NULL, NULL, NULL, NULL, ipf_p_irc_new, NULL, NULL, ipf_p_irc_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_RCMD_PROXY { NULL, NULL, "rcmd", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_rcmd_main_load, ipf_p_rcmd_main_unload, NULL, NULL, NULL, NULL, ipf_p_rcmd_new, ipf_p_rcmd_del, ipf_p_rcmd_in, ipf_p_rcmd_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_RAUDIO_PROXY { NULL, NULL, "raudio", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_raudio_main_load, ipf_p_raudio_main_unload, NULL, NULL, NULL, NULL, ipf_p_raudio_new, NULL, ipf_p_raudio_in, ipf_p_raudio_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_MSNRPC_PROXY { NULL, NULL, "msnrpc", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_msnrpc_init, ipf_p_msnrpc_fini, NULL, NULL, NULL, NULL, ipf_p_msnrpc_new, NULL, ipf_p_msnrpc_in, ipf_p_msnrpc_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_NETBIOS_PROXY { NULL, NULL, "netbios", (char)IPPROTO_UDP, 0, 0, 0, ipf_p_netbios_main_load, ipf_p_netbios_main_unload, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ipf_p_netbios_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_IPSEC_PROXY { NULL, NULL, "ipsec", (char)IPPROTO_UDP, 0, 0, 0, NULL, NULL, ipf_p_ipsec_soft_create, ipf_p_ipsec_soft_destroy, ipf_p_ipsec_soft_init, ipf_p_ipsec_soft_fini, ipf_p_ipsec_new, ipf_p_ipsec_del, ipf_p_ipsec_inout, ipf_p_ipsec_inout, ipf_p_ipsec_match, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_DNS_PROXY { NULL, NULL, "dns", (char)IPPROTO_UDP, 0, 0, 0, NULL, NULL, ipf_p_dns_soft_create, ipf_p_dns_soft_destroy, NULL, NULL, ipf_p_dns_new, ipf_p_ipsec_del, ipf_p_dns_inout, ipf_p_dns_inout, ipf_p_dns_match, ipf_p_dns_ctl, NULL, NULL, NULL }, #endif #ifdef IPF_PPTP_PROXY { NULL, NULL, "pptp", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_pptp_main_load, ipf_p_pptp_main_unload, NULL, NULL, NULL, NULL, ipf_p_pptp_new, ipf_p_pptp_del, ipf_p_pptp_inout, ipf_p_pptp_inout, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_RPCB_PROXY # ifndef _KERNEL { NULL, NULL, "rpcbt", (char)IPPROTO_TCP, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, ipf_p_rpcb_new, ipf_p_rpcb_del, ipf_p_rpcb_in, ipf_p_rpcb_out, NULL, NULL, NULL, NULL, NULL }, # endif { NULL, NULL, "rpcbu", (char)IPPROTO_UDP, 0, 0, 0, ipf_p_rpcb_main_load, ipf_p_rpcb_main_unload, NULL, NULL, NULL, NULL, ipf_p_rpcb_new, ipf_p_rpcb_del, ipf_p_rpcb_in, ipf_p_rpcb_out, NULL, NULL, NULL, NULL, NULL }, #endif { NULL, NULL, "", '\0', 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_main_load */ /* Returns: int - 0 == success, else failure. */ /* Parameters: Nil */ /* */ /* Initialise hook for kernel application proxies. */ /* Call the initialise routine for all the compiled in kernel proxies. */ /* ------------------------------------------------------------------------ */ int -ipf_proxy_main_load() +ipf_proxy_main_load(void) { aproxy_t *ap; for (ap = ips_proxies; ap->apr_p; ap++) { if (ap->apr_load != NULL) (*ap->apr_load)(); } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_main_unload */ /* Returns: int - 0 == success, else failure. */ /* Parameters: Nil */ /* */ /* Unload hook for kernel application proxies. */ /* Call the finialise routine for all the compiled in kernel proxies. */ /* ------------------------------------------------------------------------ */ int -ipf_proxy_main_unload() +ipf_proxy_main_unload(void) { aproxy_t *ap; for (ap = ips_proxies; ap->apr_p; ap++) if (ap->apr_unload != NULL) (*ap->apr_unload)(); for (ap = ap_proxylist; ap; ap = ap->apr_next) if (ap->apr_unload != NULL) (*ap->apr_unload)(); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_soft_create */ /* Returns: void * - */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Build the structure to hold all of the run time data to support proxies. */ /* ------------------------------------------------------------------------ */ void * -ipf_proxy_soft_create(softc) - ipf_main_softc_t *softc; +ipf_proxy_soft_create(ipf_main_softc_t *softc) { ipf_proxy_softc_t *softp; aproxy_t *last; aproxy_t *apn; aproxy_t *ap; KMALLOC(softp, ipf_proxy_softc_t *); if (softp == NULL) return softp; bzero((char *)softp, sizeof(*softp)); #if defined(_KERNEL) softp->ips_proxy_debug = 0; #else softp->ips_proxy_debug = 2; #endif softp->ips_proxy_session_size = AP_SESS_SIZE; softp->ipf_proxy_tune = ipf_tune_array_copy(softp, sizeof(ipf_proxy_tuneables), ipf_proxy_tuneables); if (softp->ipf_proxy_tune == NULL) { ipf_proxy_soft_destroy(softc, softp); return NULL; } if (ipf_tune_array_link(softc, softp->ipf_proxy_tune) == -1) { ipf_proxy_soft_destroy(softc, softp); return NULL; } last = NULL; for (ap = ips_proxies; ap->apr_p; ap++) { apn = ipf_proxy_create_clone(softc, ap); if (apn == NULL) goto failed; if (last != NULL) last->apr_next = apn; else softp->ips_proxies = apn; last = apn; } for (ap = ips_proxies; ap != NULL; ap = ap->apr_next) { apn = ipf_proxy_create_clone(softc, ap); if (apn == NULL) goto failed; if (last != NULL) last->apr_next = apn; else softp->ips_proxies = apn; last = apn; } return softp; failed: ipf_proxy_soft_destroy(softc, softp); return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_soft_create */ /* Returns: void * - */ /* Parameters: softc(I) - pointer to soft context main structure */ /* orig(I) - pointer to proxy definition to copy */ /* */ /* This function clones a proxy definition given by orig and returns a */ /* a pointer to that copy. */ /* ------------------------------------------------------------------------ */ static aproxy_t * -ipf_proxy_create_clone(softc, orig) - ipf_main_softc_t *softc; - aproxy_t *orig; +ipf_proxy_create_clone(ipf_main_softc_t *softc, aproxy_t *orig) { aproxy_t *apn; KMALLOC(apn, aproxy_t *); if (apn == NULL) return NULL; bcopy((char *)orig, (char *)apn, sizeof(*apn)); apn->apr_next = NULL; apn->apr_soft = NULL; if (apn->apr_create != NULL) { apn->apr_soft = (*apn->apr_create)(softc); if (apn->apr_soft == NULL) { KFREE(apn); return NULL; } } apn->apr_parent = orig; orig->apr_clones++; return apn; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_soft_create */ /* Returns: int - 0 == success, else failure. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to proxy contect data */ /* */ /* Initialise the proxy context and walk through each of the proxies and */ /* call its initialisation function. This allows for proxies to do any */ /* local setup prior to actual use. */ /* ------------------------------------------------------------------------ */ int -ipf_proxy_soft_init(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_proxy_soft_init(ipf_main_softc_t *softc, void *arg) { ipf_proxy_softc_t *softp; aproxy_t *ap; u_int size; int err; softp = arg; size = softp->ips_proxy_session_size * sizeof(ap_session_t *); KMALLOCS(softp->ips_sess_tab, ap_session_t **, size); if (softp->ips_sess_tab == NULL) return -1; bzero(softp->ips_sess_tab, size); for (ap = softp->ips_proxies; ap != NULL; ap = ap->apr_next) { if (ap->apr_init != NULL) { err = (*ap->apr_init)(softc, ap->apr_soft); if (err != 0) return -2; } } softp->ips_init_run = 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_soft_create */ /* Returns: int - 0 == success, else failure. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to proxy contect data */ /* */ /* This function should always succeed. It is responsible for ensuring that */ /* the proxy context can be safely called when ipf_proxy_soft_destroy is */ /* called and suring all of the proxies have similarly been instructed. */ /* ------------------------------------------------------------------------ */ int -ipf_proxy_soft_fini(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_proxy_soft_fini(ipf_main_softc_t *softc, void *arg) { ipf_proxy_softc_t *softp = arg; aproxy_t *ap; for (ap = softp->ips_proxies; ap != NULL; ap = ap->apr_next) { if (ap->apr_fini != NULL) { (*ap->apr_fini)(softc, ap->apr_soft); } } if (softp->ips_sess_tab != NULL) { KFREES(softp->ips_sess_tab, softp->ips_proxy_session_size * sizeof(ap_session_t *)); softp->ips_sess_tab = NULL; } softp->ips_init_run = 0; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to proxy contect data */ /* */ /* Free up all of the local data structures allocated during creation. */ /* ------------------------------------------------------------------------ */ void -ipf_proxy_soft_destroy(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_proxy_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_proxy_softc_t *softp = arg; aproxy_t *ap; while ((ap = softp->ips_proxies) != NULL) { softp->ips_proxies = ap->apr_next; if (ap->apr_destroy != NULL) (*ap->apr_destroy)(softc, ap->apr_soft); ap->apr_parent->apr_clones--; KFREE(ap); } if (softp->ipf_proxy_tune != NULL) { ipf_tune_array_unlink(softc, softp->ipf_proxy_tune); KFREES(softp->ipf_proxy_tune, sizeof(ipf_proxy_tuneables)); softp->ipf_proxy_tune = NULL; } KFREE(softp); } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_flush */ /* Returns: Nil */ /* Parameters: arg(I) - pointer to proxy contect data */ /* how(I) - indicates the type of flush operation */ /* */ /* Walk through all of the proxies and pass on the flush command as either */ /* a flush or a clear. */ /* ------------------------------------------------------------------------ */ void -ipf_proxy_flush(arg, how) - void *arg; - int how; +ipf_proxy_flush(void *arg, int how) { ipf_proxy_softc_t *softp = arg; aproxy_t *ap; switch (how) { case 0 : for (ap = softp->ips_proxies; ap; ap = ap->apr_next) if (ap->apr_flush != NULL) (*ap->apr_flush)(ap, how); break; case 1 : for (ap = softp->ips_proxies; ap; ap = ap->apr_next) if (ap->apr_clear != NULL) (*ap->apr_clear)(ap); break; default : break; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_add */ /* Returns: int - 0 == success, else failure. */ /* Parameters: ap(I) - pointer to proxy structure */ /* */ /* Dynamically add a new kernel proxy. Ensure that it is unique in the */ /* collection compiled in and dynamically added. */ /* ------------------------------------------------------------------------ */ int -ipf_proxy_add(arg, ap) - void *arg; - aproxy_t *ap; +ipf_proxy_add(void *arg, aproxy_t *ap) { ipf_proxy_softc_t *softp = arg; aproxy_t *a; for (a = ips_proxies; a->apr_p; a++) if ((a->apr_p == ap->apr_p) && !strncmp(a->apr_label, ap->apr_label, sizeof(ap->apr_label))) { if (softp->ips_proxy_debug & 0x01) printf("ipf_proxy_add: %s/%d present (B)\n", a->apr_label, a->apr_p); return -1; } for (a = ap_proxylist; (a != NULL); a = a->apr_next) if ((a->apr_p == ap->apr_p) && !strncmp(a->apr_label, ap->apr_label, sizeof(ap->apr_label))) { if (softp->ips_proxy_debug & 0x01) printf("ipf_proxy_add: %s/%d present (D)\n", a->apr_label, a->apr_p); return -1; } ap->apr_next = ap_proxylist; ap_proxylist = ap; if (ap->apr_load != NULL) (*ap->apr_load)(); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_ctl */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to proxy context */ /* ctl(I) - pointer to proxy control structure */ /* */ /* Check to see if the proxy this control request has come through for */ /* exists, and if it does and it has a control function then invoke that */ /* control function. */ /* ------------------------------------------------------------------------ */ int -ipf_proxy_ctl(softc, arg, ctl) - ipf_main_softc_t *softc; - void *arg; - ap_ctl_t *ctl; +ipf_proxy_ctl(ipf_main_softc_t *softc, void *arg, ap_ctl_t *ctl) { ipf_proxy_softc_t *softp = arg; aproxy_t *a; int error; a = ipf_proxy_lookup(arg, ctl->apc_p, ctl->apc_label); if (a == NULL) { if (softp->ips_proxy_debug & 0x01) printf("ipf_proxy_ctl: can't find %s/%d\n", ctl->apc_label, ctl->apc_p); IPFERROR(80001); error = ESRCH; } else if (a->apr_ctl == NULL) { if (softp->ips_proxy_debug & 0x01) printf("ipf_proxy_ctl: no ctl function for %s/%d\n", ctl->apc_label, ctl->apc_p); IPFERROR(80002); error = ENXIO; } else { error = (*a->apr_ctl)(softc, a->apr_soft, ctl); if ((error != 0) && (softp->ips_proxy_debug & 0x02)) printf("ipf_proxy_ctl: %s/%d ctl error %d\n", a->apr_label, a->apr_p, error); } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_del */ /* Returns: int - 0 == success, else failure. */ /* Parameters: ap(I) - pointer to proxy structure */ /* */ /* Delete a proxy that has been added dynamically from those available. */ /* If it is in use, return 1 (do not destroy NOW), not in use 0 or -1 */ /* if it cannot be matched. */ /* ------------------------------------------------------------------------ */ int -ipf_proxy_del(ap) - aproxy_t *ap; +ipf_proxy_del(aproxy_t *ap) { aproxy_t *a, **app; for (app = &ap_proxylist; ((a = *app) != NULL); app = &a->apr_next) { if (a == ap) { a->apr_flags |= APR_DELETE; if (ap->apr_ref == 0 && ap->apr_clones == 0) { *app = a->apr_next; return 0; } return 1; } } return -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_ok */ /* Returns: int - 1 == good match else not. */ /* Parameters: fin(I) - pointer to packet information */ /* tcp(I) - pointer to TCP/UDP header */ /* nat(I) - pointer to current NAT session */ /* */ /* This function extends the NAT matching to ensure that a packet that has */ /* arrived matches the proxy information attached to the NAT rule. Notably, */ /* if the proxy is scheduled to be deleted then packets will not match the */ /* rule even if the rule is still active. */ /* ------------------------------------------------------------------------ */ int -ipf_proxy_ok(fin, tcp, np) - fr_info_t *fin; - tcphdr_t *tcp; - ipnat_t *np; +ipf_proxy_ok(fr_info_t *fin, tcphdr_t *tcp, ipnat_t *np) { aproxy_t *apr = np->in_apr; u_short dport = np->in_odport; if ((apr == NULL) || (apr->apr_flags & APR_DELETE) || (fin->fin_p != apr->apr_p)) return 0; if ((tcp == NULL) && dport) return 0; return 1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_ioctl */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to ioctl data */ /* cmd(I) - ioctl command */ /* mode(I) - mode bits for device */ /* ctx(I) - pointer to context information */ /* */ /* ------------------------------------------------------------------------ */ int -ipf_proxy_ioctl(softc, data, cmd, mode, ctx) - ipf_main_softc_t *softc; - caddr_t data; - ioctlcmd_t cmd; - int mode; - void *ctx; +ipf_proxy_ioctl(ipf_main_softc_t *softc, caddr_t data, ioctlcmd_t cmd, + int mode, void *ctx) { ap_ctl_t ctl; caddr_t ptr; int error; mode = mode; /* LINT */ switch (cmd) { case SIOCPROXY : error = ipf_inobj(softc, data, NULL, &ctl, IPFOBJ_PROXYCTL); if (error != 0) { return error; } ptr = NULL; if (ctl.apc_dsize > 0) { KMALLOCS(ptr, caddr_t, ctl.apc_dsize); if (ptr == NULL) { IPFERROR(80003); error = ENOMEM; } else { error = copyinptr(softc, ctl.apc_data, ptr, ctl.apc_dsize); if (error == 0) ctl.apc_data = ptr; } } else { ctl.apc_data = NULL; error = 0; } if (error == 0) error = ipf_proxy_ctl(softc, softc->ipf_proxy_soft, &ctl); if ((error != 0) && (ptr != NULL)) { KFREES(ptr, ctl.apc_dsize); } break; default : IPFERROR(80004); error = EINVAL; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_match */ /* Returns: int - 0 == success, else error */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* If a proxy has a match function, call that to do extended packet */ /* matching. Whilst other parts of the NAT code are rather lenient when it */ /* comes to the quality of the packet that it will transform, the proxy */ /* matching is not because they need to work with data, not just headers. */ /* ------------------------------------------------------------------------ */ int -ipf_proxy_match(fin, nat) - fr_info_t *fin; - nat_t *nat; +ipf_proxy_match(fr_info_t *fin, nat_t *nat) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; aproxy_t *apr; ipnat_t *ipn; int result; ipn = nat->nat_ptr; if (softp->ips_proxy_debug & 0x04) printf("ipf_proxy_match(%lx,%lx) aps %lx ptr %lx\n", (u_long)fin, (u_long)nat, (u_long)nat->nat_aps, (u_long)ipn); if ((fin->fin_flx & (FI_SHORT|FI_BAD)) != 0) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_match: flx 0x%x (BAD|SHORT)\n", fin->fin_flx); return -1; } apr = ipn->in_apr; if ((apr == NULL) || (apr->apr_flags & APR_DELETE)) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_match:apr %lx apr_flags 0x%x\n", (u_long)apr, apr ? apr->apr_flags : 0); return -1; } if (apr->apr_match != NULL) { result = (*apr->apr_match)(fin, nat->nat_aps, nat); if (result != 0) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_match: result %d\n", result); return -1; } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_new */ /* Returns: int - 0 == success, else error */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* Allocate a new application proxy structure and fill it in with the */ /* relevant details. call the init function once complete, prior to */ /* returning. */ /* ------------------------------------------------------------------------ */ int -ipf_proxy_new(fin, nat) - fr_info_t *fin; - nat_t *nat; +ipf_proxy_new(fr_info_t *fin, nat_t *nat) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; register ap_session_t *aps; aproxy_t *apr; if (softp->ips_proxy_debug & 0x04) printf("ipf_proxy_new(%lx,%lx) \n", (u_long)fin, (u_long)nat); if ((nat->nat_ptr == NULL) || (nat->nat_aps != NULL)) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_new: nat_ptr %lx nat_aps %lx\n", (u_long)nat->nat_ptr, (u_long)nat->nat_aps); return -1; } apr = nat->nat_ptr->in_apr; if ((apr->apr_flags & APR_DELETE) || (fin->fin_p != apr->apr_p)) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_new: apr_flags 0x%x p %d/%d\n", apr->apr_flags, fin->fin_p, apr->apr_p); return -1; } KMALLOC(aps, ap_session_t *); if (!aps) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_new: malloc failed (%lu)\n", (u_long)sizeof(ap_session_t)); return -1; } bzero((char *)aps, sizeof(*aps)); aps->aps_data = NULL; aps->aps_apr = apr; aps->aps_psiz = 0; if (apr->apr_new != NULL) if ((*apr->apr_new)(apr->apr_soft, fin, aps, nat) == -1) { if ((aps->aps_data != NULL) && (aps->aps_psiz != 0)) { KFREES(aps->aps_data, aps->aps_psiz); } KFREE(aps); if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_new: new(%lx) failed\n", (u_long)apr->apr_new); return -1; } aps->aps_nat = nat; aps->aps_next = softp->ips_sess_list; softp->ips_sess_list = aps; nat->nat_aps = aps; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_check */ /* Returns: int - -1 == error, 1 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* Check to see if a packet should be passed through an active proxy */ /* routine if one has been setup for it. We don't need to check the */ /* checksum here if IPFILTER_CKSUM is defined because if it is, a failed */ /* check causes FI_BAD to be set. */ /* ------------------------------------------------------------------------ */ int -ipf_proxy_check(fin, nat) - fr_info_t *fin; - nat_t *nat; +ipf_proxy_check(fr_info_t *fin, nat_t *nat) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; #if SOLARIS && defined(_KERNEL) && defined(ICK_VALID) mb_t *m; #endif tcphdr_t *tcp = NULL; udphdr_t *udp = NULL; ap_session_t *aps; aproxy_t *apr; short adjlen; int dosum; ip_t *ip; short rv; int err; #if !defined(_KERNEL) || SOLARIS || defined(__FreeBSD__) u_32_t s1, s2, sd; #endif if (fin->fin_flx & FI_BAD) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_check: flx 0x%x (BAD)\n", fin->fin_flx); return -1; } #ifndef IPFILTER_CKSUM if ((fin->fin_out == 0) && (ipf_checkl4sum(fin) == -1)) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_check: l4 checksum failure %d\n", fin->fin_p); if (fin->fin_p == IPPROTO_TCP) softc->ipf_stats[fin->fin_out].fr_tcpbad++; return -1; } #endif aps = nat->nat_aps; if (aps != NULL) { /* * If there is data in this packet to be proxied then try and * get it all into the one buffer, else drop it. */ #if SOLARIS || defined(HAVE_M_PULLDOWN) if ((fin->fin_dlen > 0) && !(fin->fin_flx & FI_COALESCE)) if (ipf_coalesce(fin) == -1) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_check: %s %x\n", "coalesce failed", fin->fin_flx); return -1; } #endif ip = fin->fin_ip; if (fin->fin_cksum > FI_CK_SUMOK) dosum = 0; else dosum = 1; switch (fin->fin_p) { case IPPROTO_TCP : tcp = (tcphdr_t *)fin->fin_dp; #if SOLARIS && defined(_KERNEL) && defined(ICK_VALID) m = fin->fin_qfm; if (dohwcksum && (m->b_ick_flag == ICK_VALID)) dosum = 0; #endif break; case IPPROTO_UDP : udp = (udphdr_t *)fin->fin_dp; break; default : break; } apr = aps->aps_apr; err = 0; if (fin->fin_out != 0) { if (apr->apr_outpkt != NULL) err = (*apr->apr_outpkt)(apr->apr_soft, fin, aps, nat); } else { if (apr->apr_inpkt != NULL) err = (*apr->apr_inpkt)(apr->apr_soft, fin, aps, nat); } rv = APR_EXIT(err); if (((softp->ips_proxy_debug & 0x08) && (rv != 0)) || (softp->ips_proxy_debug & 0x04)) printf("ipf_proxy_check: out %d err %x rv %d\n", fin->fin_out, err, rv); if (rv == 1) return -1; if (rv == 2) { ipf_proxy_deref(apr); nat->nat_aps = NULL; return -1; } /* * If err != 0 then the data size of the packet has changed * so we need to recalculate the header checksums for the * packet. */ adjlen = APR_INC(err); #if !defined(_KERNEL) || SOLARIS || defined(__FreeBSD__) s1 = LONG_SUM(fin->fin_plen - adjlen); s2 = LONG_SUM(fin->fin_plen); CALC_SUMD(s1, s2, sd); if ((err != 0) && (fin->fin_cksum < FI_CK_L4PART) && fin->fin_v == 4) ipf_fix_outcksum(0, &ip->ip_sum, sd, 0); #endif if (fin->fin_flx & FI_DOCKSUM) dosum = 1; /* * For TCP packets, we may need to adjust the sequence and * acknowledgement numbers to reflect changes in size of the * data stream. * * For both TCP and UDP, recalculate the layer 4 checksum, * regardless, as we can't tell (here) if data has been * changed or not. */ if (tcp != NULL) { err = ipf_proxy_fixseqack(fin, ip, aps, adjlen); if (fin->fin_cksum == FI_CK_L4PART) { u_short sum = ntohs(tcp->th_sum); sum += adjlen; tcp->th_sum = htons(sum); } else if (fin->fin_cksum < FI_CK_L4PART) { tcp->th_sum = fr_cksum(fin, ip, IPPROTO_TCP, tcp); } } else if ((udp != NULL) && (udp->uh_sum != 0)) { if (fin->fin_cksum == FI_CK_L4PART) { u_short sum = ntohs(udp->uh_sum); sum += adjlen; udp->uh_sum = htons(sum); } else if (dosum) { udp->uh_sum = fr_cksum(fin, ip, IPPROTO_UDP, udp); } } aps->aps_bytes += fin->fin_plen; aps->aps_pkts++; } return 1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_lookup */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: arg(I) - pointer to proxy context information */ /* pr(I) - protocol number for proxy */ /* name(I) - proxy name */ /* */ /* Search for a proxy by the protocol being used and by its name. */ /* ------------------------------------------------------------------------ */ aproxy_t * -ipf_proxy_lookup(arg, pr, name) - void *arg; - u_int pr; - char *name; +ipf_proxy_lookup(void *arg, u_int pr, char *name) { ipf_proxy_softc_t *softp = arg; aproxy_t *ap; if (softp->ips_proxy_debug & 0x04) printf("ipf_proxy_lookup(%d,%s)\n", pr, name); for (ap = softp->ips_proxies; ap != NULL; ap = ap->apr_next) if ((ap->apr_p == pr) && !strncmp(name, ap->apr_label, sizeof(ap->apr_label))) { ap->apr_ref++; return ap; } if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_lookup: failed for %d/%s\n", pr, name); return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_deref */ /* Returns: Nil */ /* Parameters: ap(I) - pointer to proxy structure */ /* */ /* Drop the reference counter associated with the proxy. */ /* ------------------------------------------------------------------------ */ void -ipf_proxy_deref(ap) - aproxy_t *ap; +ipf_proxy_deref(aproxy_t *ap) { ap->apr_ref--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_free */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* aps(I) - pointer to current proxy session */ /* Locks Held: ipf_nat_new, ipf_nat(W) */ /* */ /* Free up proxy session information allocated to be used with a NAT */ /* session. */ /* ------------------------------------------------------------------------ */ void -ipf_proxy_free(softc, aps) - ipf_main_softc_t *softc; - ap_session_t *aps; +ipf_proxy_free(ipf_main_softc_t *softc, ap_session_t *aps) { ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; ap_session_t *a, **ap; aproxy_t *apr; if (!aps) return; for (ap = &softp->ips_sess_list; ((a = *ap) != NULL); ap = &a->aps_next) if (a == aps) { *ap = a->aps_next; break; } apr = aps->aps_apr; if ((apr != NULL) && (apr->apr_del != NULL)) (*apr->apr_del)(softc, aps); if ((aps->aps_data != NULL) && (aps->aps_psiz != 0)) KFREES(aps->aps_data, aps->aps_psiz); KFREE(aps); } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_fixseqack */ /* Returns: int - 2 if TCP ack/seq is changed, else 0 */ /* Parameters: fin(I) - pointer to packet information */ /* ip(I) - pointer to IP header */ /* nat(I) - pointer to current NAT session */ /* inc(I) - delta to apply to TCP sequence numbering */ /* */ /* Adjust the TCP sequence/acknowledge numbers in the TCP header based on */ /* whether or not the new header is past the point at which an adjustment */ /* occurred. This might happen because of (say) an FTP string being changed */ /* and the new string being a different length to the old. */ /* ------------------------------------------------------------------------ */ static int -ipf_proxy_fixseqack(fin, ip, aps, inc) - fr_info_t *fin; - ip_t *ip; - ap_session_t *aps; - int inc; +ipf_proxy_fixseqack(fr_info_t *fin, ip_t *ip, ap_session_t *aps, int inc) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; int sel, ch = 0, out, nlen; u_32_t seq1, seq2; tcphdr_t *tcp; short inc2; tcp = (tcphdr_t *)fin->fin_dp; out = fin->fin_out; /* * ip_len has already been adjusted by 'inc'. */ nlen = fin->fin_dlen; nlen -= (TCP_OFF(tcp) << 2); inc2 = inc; inc = (int)inc2; if (out != 0) { seq1 = (u_32_t)ntohl(tcp->th_seq); sel = aps->aps_sel[out]; /* switch to other set ? */ if ((aps->aps_seqmin[!sel] > aps->aps_seqmin[sel]) && (seq1 > aps->aps_seqmin[!sel])) { if (softp->ips_proxy_debug & 0x10) printf("proxy out switch set seq %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_seqmin[!sel]); sel = aps->aps_sel[out] = !sel; } if (aps->aps_seqoff[sel]) { seq2 = aps->aps_seqmin[sel] - aps->aps_seqoff[sel]; if (seq1 > seq2) { seq2 = aps->aps_seqoff[sel]; seq1 += seq2; tcp->th_seq = htonl(seq1); ch = 1; } } if (inc && (seq1 > aps->aps_seqmin[!sel])) { aps->aps_seqmin[sel] = seq1 + nlen - 1; aps->aps_seqoff[sel] = aps->aps_seqoff[sel] + inc; if (softp->ips_proxy_debug & 0x10) printf("proxy seq set %d at %x to %d + %d\n", sel, aps->aps_seqmin[sel], aps->aps_seqoff[sel], inc); } /***/ seq1 = ntohl(tcp->th_ack); sel = aps->aps_sel[1 - out]; /* switch to other set ? */ if ((aps->aps_ackmin[!sel] > aps->aps_ackmin[sel]) && (seq1 > aps->aps_ackmin[!sel])) { if (softp->ips_proxy_debug & 0x10) printf("proxy out switch set ack %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_ackmin[!sel]); sel = aps->aps_sel[1 - out] = !sel; } if (aps->aps_ackoff[sel] && (seq1 > aps->aps_ackmin[sel])) { seq2 = aps->aps_ackoff[sel]; tcp->th_ack = htonl(seq1 - seq2); ch = 1; } } else { seq1 = ntohl(tcp->th_seq); sel = aps->aps_sel[out]; /* switch to other set ? */ if ((aps->aps_ackmin[!sel] > aps->aps_ackmin[sel]) && (seq1 > aps->aps_ackmin[!sel])) { if (softp->ips_proxy_debug & 0x10) printf("proxy in switch set ack %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_ackmin[!sel]); sel = aps->aps_sel[out] = !sel; } if (aps->aps_ackoff[sel]) { seq2 = aps->aps_ackmin[sel] - aps->aps_ackoff[sel]; if (seq1 > seq2) { seq2 = aps->aps_ackoff[sel]; seq1 += seq2; tcp->th_seq = htonl(seq1); ch = 1; } } if (inc && (seq1 > aps->aps_ackmin[!sel])) { aps->aps_ackmin[!sel] = seq1 + nlen - 1; aps->aps_ackoff[!sel] = aps->aps_ackoff[sel] + inc; if (softp->ips_proxy_debug & 0x10) printf("proxy ack set %d at %x to %d + %d\n", !sel, aps->aps_seqmin[!sel], aps->aps_seqoff[sel], inc); } /***/ seq1 = ntohl(tcp->th_ack); sel = aps->aps_sel[1 - out]; /* switch to other set ? */ if ((aps->aps_seqmin[!sel] > aps->aps_seqmin[sel]) && (seq1 > aps->aps_seqmin[!sel])) { if (softp->ips_proxy_debug & 0x10) printf("proxy in switch set seq %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_seqmin[!sel]); sel = aps->aps_sel[1 - out] = !sel; } if (aps->aps_seqoff[sel] != 0) { if (softp->ips_proxy_debug & 0x10) printf("sel %d seqoff %d seq1 %x seqmin %x\n", sel, aps->aps_seqoff[sel], seq1, aps->aps_seqmin[sel]); if (seq1 > aps->aps_seqmin[sel]) { seq2 = aps->aps_seqoff[sel]; tcp->th_ack = htonl(seq1 - seq2); ch = 1; } } } if (softp->ips_proxy_debug & 0x10) printf("ipf_proxy_fixseqack: seq %u ack %u\n", (u_32_t)ntohl(tcp->th_seq), (u_32_t)ntohl(tcp->th_ack)); return ch ? 2 : 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_rule_rev */ /* Returns: ipnat_t * - NULL = failure, else pointer to new rule */ /* Parameters: nat(I) - pointer to NAT session to create rule from */ /* */ /* This function creates a NAT rule that is based upon the reverse packet */ /* flow associated with this NAT session. Thus if this NAT session was */ /* created with a map rule then this function will create a rdr rule. */ /* Only address fields and network interfaces are assigned in this function */ /* and the address fields are formed such that an exact is required. If the */ /* original rule had a netmask, that is not replicated here not is it */ /* desired. The ultimate goal here is to create a NAT rule to support a NAT */ /* session being created that does not have a user configured rule. The */ /* classic example is supporting the FTP proxy, where a data channel needs */ /* to be setup, based on the addresses used for the control connection. In */ /* that case, this function is used to handle creating NAT rules to support */ /* data connections with the PORT and EPRT commands. */ /* ------------------------------------------------------------------------ */ ipnat_t * -ipf_proxy_rule_rev(nat) - nat_t *nat; +ipf_proxy_rule_rev(nat_t *nat) { ipnat_t *old; ipnat_t *ipn; int size; old = nat->nat_ptr; size = old->in_size; KMALLOCS(ipn, ipnat_t *, size); if (ipn == NULL) return NULL; bzero((char *)ipn, size); ipn->in_use = 1; ipn->in_hits = 1; ipn->in_ippip = 1; ipn->in_apr = NULL; ipn->in_size = size; ipn->in_pr[0] = old->in_pr[1]; ipn->in_pr[1] = old->in_pr[0]; ipn->in_v[0] = old->in_v[1]; ipn->in_v[1] = old->in_v[0]; ipn->in_ifps[0] = old->in_ifps[1]; ipn->in_ifps[1] = old->in_ifps[0]; ipn->in_flags = (old->in_flags | IPN_PROXYRULE); ipn->in_nsrcip6 = nat->nat_odst6; ipn->in_osrcip6 = nat->nat_ndst6; if ((old->in_redir & NAT_REDIRECT) != 0) { ipn->in_redir = NAT_MAP; if (ipn->in_v[0] == 4) { ipn->in_snip = ntohl(nat->nat_odstaddr); ipn->in_dnip = ntohl(nat->nat_nsrcaddr); #ifdef USE_INET6 } else { ipn->in_snip6 = nat->nat_odst6; ipn->in_dnip6 = nat->nat_nsrc6; #endif } ipn->in_ndstip6 = nat->nat_nsrc6; ipn->in_odstip6 = nat->nat_osrc6; } else { ipn->in_redir = NAT_REDIRECT; if (ipn->in_v[0] == 4) { ipn->in_snip = ntohl(nat->nat_odstaddr); ipn->in_dnip = ntohl(nat->nat_osrcaddr); #ifdef USE_INET6 } else { ipn->in_snip6 = nat->nat_odst6; ipn->in_dnip6 = nat->nat_osrc6; #endif } ipn->in_ndstip6 = nat->nat_osrc6; ipn->in_odstip6 = nat->nat_nsrc6; } IP6_SETONES(&ipn->in_osrcmsk6); IP6_SETONES(&ipn->in_nsrcmsk6); IP6_SETONES(&ipn->in_odstmsk6); IP6_SETONES(&ipn->in_ndstmsk6); ipn->in_namelen = old->in_namelen; ipn->in_ifnames[0] = old->in_ifnames[1]; ipn->in_ifnames[1] = old->in_ifnames[0]; bcopy(old->in_names, ipn->in_names, ipn->in_namelen); MUTEX_INIT(&ipn->in_lock, "ipnat rev rule lock"); return ipn; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_rule_fwd */ /* Returns: ipnat_t * - NULL = failure, else pointer to new rule */ /* Parameters: nat(I) - pointer to NAT session to create rule from */ /* */ /* The purpose and rationale of this function is much the same as the above */ /* function, ipf_proxy_rule_rev, except that a rule is created that matches */ /* the same direction as that of the existing NAT session. Thus if this NAT */ /* session was created with a map rule then this function will also create */ /* a data structure to represent a map rule. Whereas ipf_proxy_rule_rev is */ /* used to support PORT/EPRT, this function supports PASV/EPSV. */ /* ------------------------------------------------------------------------ */ ipnat_t * -ipf_proxy_rule_fwd(nat) - nat_t *nat; +ipf_proxy_rule_fwd(nat_t *nat) { ipnat_t *old; ipnat_t *ipn; int size; old = nat->nat_ptr; size = old->in_size; KMALLOCS(ipn, ipnat_t *, size); if (ipn == NULL) return NULL; bzero((char *)ipn, size); ipn->in_use = 1; ipn->in_hits = 1; ipn->in_ippip = 1; ipn->in_apr = NULL; ipn->in_size = size; ipn->in_pr[0] = old->in_pr[0]; ipn->in_pr[1] = old->in_pr[1]; ipn->in_v[0] = old->in_v[0]; ipn->in_v[1] = old->in_v[1]; ipn->in_ifps[0] = nat->nat_ifps[0]; ipn->in_ifps[1] = nat->nat_ifps[1]; ipn->in_flags = (old->in_flags | IPN_PROXYRULE); ipn->in_nsrcip6 = nat->nat_nsrc6; ipn->in_osrcip6 = nat->nat_osrc6; ipn->in_ndstip6 = nat->nat_ndst6; ipn->in_odstip6 = nat->nat_odst6; ipn->in_redir = old->in_redir; if (ipn->in_v[0] == 4) { ipn->in_snip = ntohl(nat->nat_nsrcaddr); ipn->in_dnip = ntohl(nat->nat_ndstaddr); #ifdef USE_INET6 } else { ipn->in_snip6 = nat->nat_nsrc6; ipn->in_dnip6 = nat->nat_ndst6; #endif } IP6_SETONES(&ipn->in_osrcmsk6); IP6_SETONES(&ipn->in_nsrcmsk6); IP6_SETONES(&ipn->in_odstmsk6); IP6_SETONES(&ipn->in_ndstmsk6); ipn->in_namelen = old->in_namelen; ipn->in_ifnames[0] = old->in_ifnames[0]; ipn->in_ifnames[1] = old->in_ifnames[1]; bcopy(old->in_names, ipn->in_names, ipn->in_namelen); MUTEX_INIT(&ipn->in_lock, "ipnat fwd rule lock"); return ipn; } diff --git a/sys/netpfil/ipfilter/netinet/ip_raudio_pxy.c b/sys/netpfil/ipfilter/netinet/ip_raudio_pxy.c index 0853b90d31e0..754c3d7262df 100644 --- a/sys/netpfil/ipfilter/netinet/ip_raudio_pxy.c +++ b/sys/netpfil/ipfilter/netinet/ip_raudio_pxy.c @@ -1,345 +1,333 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id: ip_raudio_pxy.c,v 1.40.2.4 2006/07/14 06:12:17 darrenr Exp $ */ #define IPF_RAUDIO_PROXY void ipf_p_raudio_main_load(void); void ipf_p_raudio_main_unload(void); int ipf_p_raudio_new(void *, fr_info_t *, ap_session_t *, nat_t *); int ipf_p_raudio_in(void *, fr_info_t *, ap_session_t *, nat_t *); int ipf_p_raudio_out(void *, fr_info_t *, ap_session_t *, nat_t *); static frentry_t raudiofr; int raudio_proxy_init = 0; /* * Real Audio application proxy initialization. */ void -ipf_p_raudio_main_load() +ipf_p_raudio_main_load(void) { bzero((char *)&raudiofr, sizeof(raudiofr)); raudiofr.fr_ref = 1; raudiofr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&raudiofr.fr_lock, "Real Audio proxy rule lock"); raudio_proxy_init = 1; } void -ipf_p_raudio_main_unload() +ipf_p_raudio_main_unload(void) { if (raudio_proxy_init == 1) { MUTEX_DESTROY(&raudiofr.fr_lock); raudio_proxy_init = 0; } } /* * Setup for a new proxy to handle Real Audio. */ int -ipf_p_raudio_new(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_raudio_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { raudio_t *rap; nat = nat; /* LINT */ if (fin->fin_v != 4) return -1; KMALLOCS(aps->aps_data, void *, sizeof(raudio_t)); if (aps->aps_data == NULL) return -1; bzero(aps->aps_data, sizeof(raudio_t)); rap = aps->aps_data; aps->aps_psiz = sizeof(raudio_t); rap->rap_mode = RAP_M_TCP; /* default is for TCP */ return 0; } int -ipf_p_raudio_out(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_raudio_out(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { raudio_t *rap = aps->aps_data; unsigned char membuf[512 + 1], *s; u_short id = 0; tcphdr_t *tcp; int off, dlen; int len = 0; mb_t *m; nat = nat; /* LINT */ /* * If we've already processed the start messages, then nothing left * for the proxy to do. */ if (rap->rap_eos == 1) return 0; m = fin->fin_m; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)fin->fin_ip; off += (TCP_OFF(tcp) << 2) + fin->fin_ipoff; dlen = MSGDSIZE(m) - off; if (dlen <= 0) return 0; if (dlen > sizeof(membuf)) dlen = sizeof(membuf); bzero((char *)membuf, sizeof(membuf)); COPYDATA(m, off, dlen, (char *)membuf); /* * In all the startup parsing, ensure that we don't go outside * the packet buffer boundary. */ /* * Look for the start of connection "PNA" string if not seen yet. */ if (rap->rap_seenpna == 0) { s = (u_char *)memstr("PNA", (char *)membuf, 3, dlen); if (s == NULL) return 0; s += 3; rap->rap_seenpna = 1; } else s = membuf; /* * Directly after the PNA will be the version number of this * connection. */ if (rap->rap_seenpna == 1 && rap->rap_seenver == 0) { if ((s + 1) - membuf < dlen) { rap->rap_version = (*s << 8) | *(s + 1); s += 2; rap->rap_seenver = 1; } else return 0; } /* * Now that we've been past the PNA and version number, we're into the * startup messages block. This ends when a message with an ID of 0. */ while ((rap->rap_eos == 0) && ((s + 1) - membuf < dlen)) { if (rap->rap_gotid == 0) { id = (*s << 8) | *(s + 1); s += 2; rap->rap_gotid = 1; if (id == RA_ID_END) { rap->rap_eos = 1; break; } } else if (rap->rap_gotlen == 0) { len = (*s << 8) | *(s + 1); s += 2; rap->rap_gotlen = 1; } if (rap->rap_gotid == 1 && rap->rap_gotlen == 1) { if (id == RA_ID_UDP) { rap->rap_mode &= ~RAP_M_TCP; rap->rap_mode |= RAP_M_UDP; rap->rap_plport = (*s << 8) | *(s + 1); } else if (id == RA_ID_ROBUST) { rap->rap_mode |= RAP_M_ROBUST; rap->rap_prport = (*s << 8) | *(s + 1); } s += len; rap->rap_gotlen = 0; rap->rap_gotid = 0; } } return 0; } int -ipf_p_raudio_in(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_raudio_in(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { unsigned char membuf[IPF_MAXPORTLEN + 1], *s; tcphdr_t *tcp, tcph, *tcp2 = &tcph; raudio_t *rap = aps->aps_data; ipf_main_softc_t *softc; ipf_nat_softc_t *softn; struct in_addr swa, swb; int off, dlen, slen; int a1, a2, a3, a4; u_short sp, dp; fr_info_t fi; tcp_seq seq; nat_t *nat2; u_char swp; ip_t *ip; mb_t *m; softc = fin->fin_main_soft; softn = softc->ipf_nat_soft; /* * Wait until we've seen the end of the start messages and even then * only proceed further if we're using UDP. If they want to use TCP * then data is sent back on the same channel that is already open. */ if (rap->rap_sdone != 0) return 0; m = fin->fin_m; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)fin->fin_ip; off += (TCP_OFF(tcp) << 2) + fin->fin_ipoff; dlen = MSGDSIZE(m) - off; if (dlen <= 0) return 0; if (dlen > sizeof(membuf)) dlen = sizeof(membuf); bzero((char *)membuf, sizeof(membuf)); COPYDATA(m, off, dlen, (char *)membuf); seq = ntohl(tcp->th_seq); /* * Check to see if the data in this packet is of interest to us. * We only care for the first 19 bytes coming back from the server. */ if (rap->rap_sseq == 0) { s = (u_char *)memstr("PNA", (char *)membuf, 3, dlen); if (s == NULL) return 0; a1 = s - membuf; dlen -= a1; a1 = 0; rap->rap_sseq = seq; a2 = MIN(dlen, sizeof(rap->rap_svr)); } else if (seq <= rap->rap_sseq + sizeof(rap->rap_svr)) { /* * seq # which is the start of data and from that the offset * into the buffer array. */ a1 = seq - rap->rap_sseq; a2 = MIN(dlen, sizeof(rap->rap_svr)); a2 -= a1; s = membuf; } else return 0; for (a3 = a1, a4 = a2; (a4 > 0) && (a3 < 19) && (a3 >= 0); a4--,a3++) { rap->rap_sbf |= (1 << a3); rap->rap_svr[a3] = *s++; } if ((rap->rap_sbf != 0x7ffff) || (!rap->rap_eos)) /* 19 bits */ return 0; rap->rap_sdone = 1; s = (u_char *)rap->rap_svr + 11; if (((*s << 8) | *(s + 1)) == RA_ID_ROBUST) { s += 2; rap->rap_srport = (*s << 8) | *(s + 1); } ip = fin->fin_ip; swp = ip->ip_p; swa = ip->ip_src; swb = ip->ip_dst; ip->ip_p = IPPROTO_UDP; ip->ip_src = nat->nat_ndstip; ip->ip_dst = nat->nat_odstip; bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); TCP_OFF_A(tcp2, 5); fi.fin_flx |= FI_IGNORE; fi.fin_dp = (char *)tcp2; fi.fin_fr = &raudiofr; fi.fin_dlen = sizeof(*tcp2); fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); tcp2->th_win = htons(8192); slen = ip->ip_len; ip->ip_len = htons(fin->fin_hlen + sizeof(*tcp)); if (((rap->rap_mode & RAP_M_UDP_ROBUST) == RAP_M_UDP_ROBUST) && (rap->rap_srport != 0)) { dp = rap->rap_srport; sp = rap->rap_prport; tcp2->th_sport = htons(sp); tcp2->th_dport = htons(dp); fi.fin_data[0] = dp; fi.fin_data[1] = sp; fi.fin_out = 0; MUTEX_ENTER(&softn->ipf_nat_new); nat2 = ipf_nat_add(&fi, nat->nat_ptr, NULL, NAT_SLAVE|IPN_UDP | (sp ? 0 : SI_W_SPORT), NAT_OUTBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 != NULL) { (void) ipf_nat_proto(&fi, nat2, IPN_UDP); MUTEX_ENTER(&nat2->nat_lock); ipf_nat_update(&fi, nat2); MUTEX_EXIT(&nat2->nat_lock); (void) ipf_state_add(softc, &fi, NULL, (sp ? 0 : SI_W_SPORT)); } } if ((rap->rap_mode & RAP_M_UDP) == RAP_M_UDP) { sp = rap->rap_plport; tcp2->th_sport = htons(sp); tcp2->th_dport = 0; /* XXX - don't specify remote port */ fi.fin_data[0] = sp; fi.fin_data[1] = 0; fi.fin_out = 1; MUTEX_ENTER(&softn->ipf_nat_new); nat2 = ipf_nat_add(&fi, nat->nat_ptr, NULL, NAT_SLAVE|IPN_UDP|SI_W_DPORT, NAT_OUTBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 != NULL) { (void) ipf_nat_proto(&fi, nat2, IPN_UDP); MUTEX_ENTER(&nat2->nat_lock); ipf_nat_update(&fi, nat2); MUTEX_EXIT(&nat2->nat_lock); (void) ipf_state_add(softc, &fi, NULL, SI_W_DPORT); } } ip->ip_p = swp; ip->ip_len = slen; ip->ip_src = swa; ip->ip_dst = swb; return 0; } diff --git a/sys/netpfil/ipfilter/netinet/ip_rcmd_pxy.c b/sys/netpfil/ipfilter/netinet/ip_rcmd_pxy.c index 5dfdc040de4c..4e3834e49307 100644 --- a/sys/netpfil/ipfilter/netinet/ip_rcmd_pxy.c +++ b/sys/netpfil/ipfilter/netinet/ip_rcmd_pxy.c @@ -1,352 +1,334 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id$ * * Simple RCMD transparent proxy for in-kernel use. For use with the NAT * code. * $FreeBSD$ */ #define IPF_RCMD_PROXY typedef struct rcmdinfo { u_32_t rcmd_port; /* Port number seen */ u_32_t rcmd_portseq; /* Sequence number where port is first seen */ ipnat_t *rcmd_rule; /* Template rule for back connection */ } rcmdinfo_t; void ipf_p_rcmd_main_load(void); void ipf_p_rcmd_main_unload(void); int ipf_p_rcmd_init(void); void ipf_p_rcmd_fini(void); void ipf_p_rcmd_del(ipf_main_softc_t *, ap_session_t *); int ipf_p_rcmd_new(void *, fr_info_t *, ap_session_t *, nat_t *); int ipf_p_rcmd_out(void *, fr_info_t *, ap_session_t *, nat_t *); int ipf_p_rcmd_in(void *, fr_info_t *, ap_session_t *, nat_t *); u_short ipf_rcmd_atoi(char *); int ipf_p_rcmd_portmsg(fr_info_t *, ap_session_t *, nat_t *); static frentry_t rcmdfr; static int rcmd_proxy_init = 0; /* * RCMD application proxy initialization. */ void -ipf_p_rcmd_main_load() +ipf_p_rcmd_main_load(void) { bzero((char *)&rcmdfr, sizeof(rcmdfr)); rcmdfr.fr_ref = 1; rcmdfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&rcmdfr.fr_lock, "RCMD proxy rule lock"); rcmd_proxy_init = 1; } void -ipf_p_rcmd_main_unload() +ipf_p_rcmd_main_unload(void) { if (rcmd_proxy_init == 1) { MUTEX_DESTROY(&rcmdfr.fr_lock); rcmd_proxy_init = 0; } } /* * Setup for a new RCMD proxy. */ int -ipf_p_rcmd_new(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_rcmd_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; rcmdinfo_t *rc; ipnat_t *ipn; ipnat_t *np; int size; fin = fin; /* LINT */ np = nat->nat_ptr; size = np->in_size; KMALLOC(rc, rcmdinfo_t *); if (rc == NULL) { #ifdef IP_RCMD_PROXY_DEBUG printf("ipf_p_rcmd_new:KMALLOCS(%d) failed\n", sizeof(*rc)); #endif return -1; } aps->aps_sport = tcp->th_sport; aps->aps_dport = tcp->th_dport; ipn = ipf_proxy_rule_rev(nat); if (ipn == NULL) { KFREE(rc); return -1; } aps->aps_data = rc; aps->aps_psiz = sizeof(*rc); bzero((char *)rc, sizeof(*rc)); rc->rcmd_rule = ipn; return 0; } void -ipf_p_rcmd_del(softc, aps) - ipf_main_softc_t *softc; - ap_session_t *aps; +ipf_p_rcmd_del(ipf_main_softc_t *softc, ap_session_t *aps) { rcmdinfo_t *rci; rci = aps->aps_data; if (rci != NULL) { rci->rcmd_rule->in_flags |= IPN_DELETE; ipf_nat_rule_deref(softc, &rci->rcmd_rule); } } /* * ipf_rcmd_atoi - implement a simple version of atoi */ u_short -ipf_rcmd_atoi(ptr) - char *ptr; +ipf_rcmd_atoi(char *ptr) { register char *s = ptr, c; register u_short i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } return i; } int -ipf_p_rcmd_portmsg(fin, aps, nat) - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_rcmd_portmsg(fr_info_t *fin, ap_session_t *aps, nat_t *nat) { tcphdr_t *tcp, tcph, *tcp2 = &tcph; int off, dlen, nflags, direction; ipf_main_softc_t *softc; ipf_nat_softc_t *softn; char portbuf[8], *s; rcmdinfo_t *rc; fr_info_t fi; u_short sp; nat_t *nat2; #ifdef USE_INET6 ip6_t *ip6; #endif int tcpsz; int slen = 0; /* silence gcc */ ip_t *ip; mb_t *m; tcp = (tcphdr_t *)fin->fin_dp; m = fin->fin_m; ip = fin->fin_ip; tcpsz = TCP_OFF(tcp) << 2; #ifdef USE_INET6 ip6 = (ip6_t *)fin->fin_ip; #endif softc = fin->fin_main_soft; softn = softc->ipf_nat_soft; off = (char *)tcp - (char *)ip + tcpsz + fin->fin_ipoff; dlen = fin->fin_dlen - tcpsz; if (dlen <= 0) return 0; rc = (rcmdinfo_t *)aps->aps_data; if ((rc->rcmd_portseq != 0) && (tcp->th_seq != rc->rcmd_portseq)) return 0; bzero(portbuf, sizeof(portbuf)); COPYDATA(m, off, MIN(sizeof(portbuf), dlen), portbuf); portbuf[sizeof(portbuf) - 1] = '\0'; s = portbuf; sp = ipf_rcmd_atoi(s); if (sp == 0) { #ifdef IP_RCMD_PROXY_DEBUG printf("ipf_p_rcmd_portmsg:sp == 0 dlen %d [%s]\n", dlen, portbuf); #endif return 0; } if (rc->rcmd_port != 0 && sp != rc->rcmd_port) { #ifdef IP_RCMD_PROXY_DEBUG printf("ipf_p_rcmd_portmsg:sp(%d) != rcmd_port(%d)\n", sp, rc->rcmd_port); #endif return 0; } rc->rcmd_port = sp; rc->rcmd_portseq = tcp->th_seq; /* * Initialise the packet info structure so we can search the NAT * table to see if there already is soemthing present that matches * up with what we want to add. */ bcopy((char *)fin, (char *)&fi, sizeof(fi)); fi.fin_flx |= FI_IGNORE; fi.fin_data[0] = 0; fi.fin_data[1] = sp; fi.fin_src6 = nat->nat_ndst6; fi.fin_dst6 = nat->nat_nsrc6; if (nat->nat_v[0] == 6) { #ifdef USE_INET6 if (nat->nat_dir == NAT_OUTBOUND) { nat2 = ipf_nat6_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_pr[1], &nat->nat_osrc6.in6, &nat->nat_odst6.in6); } else { nat2 = ipf_nat6_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_pr[0], &nat->nat_osrc6.in6, &nat->nat_odst6.in6); } #else nat2 = (void *)-1; #endif } else { if (nat->nat_dir == NAT_OUTBOUND) { nat2 = ipf_nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_pr[1], nat->nat_osrcip, nat->nat_odstip); } else { nat2 = ipf_nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_pr[0], nat->nat_osrcip, nat->nat_odstip); } } if (nat2 != NULL) return APR_ERR(1); /* * Add skeleton NAT entry for connection which will come * back the other way. */ if (nat->nat_v[0] == 6) { #ifdef USE_INET6 slen = ip6->ip6_plen; ip6->ip6_plen = htons(sizeof(*tcp)); #endif } else { slen = ip->ip_len; ip->ip_len = htons(fin->fin_hlen + sizeof(*tcp)); } /* * Fill out the fake TCP header with a few fields that ipfilter * considers to be important. */ bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_win = htons(8192); TCP_OFF_A(tcp2, 5); tcp2->th_flags = TH_SYN; fi.fin_dp = (char *)tcp2; fi.fin_fr = &rcmdfr; fi.fin_dlen = sizeof(*tcp2); fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; if (nat->nat_dir == NAT_OUTBOUND) { fi.fin_out = 0; direction = NAT_INBOUND; } else { fi.fin_out = 1; direction = NAT_OUTBOUND; } nflags = SI_W_SPORT|NAT_SLAVE|IPN_TCP; MUTEX_ENTER(&softn->ipf_nat_new); if (fin->fin_v == 4) nat2 = ipf_nat_add(&fi, rc->rcmd_rule, NULL, nflags, direction); #ifdef USE_INET6 else nat2 = ipf_nat6_add(&fi, rc->rcmd_rule, NULL, nflags, direction); #endif MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 != NULL) { (void) ipf_nat_proto(&fi, nat2, IPN_TCP); MUTEX_ENTER(&nat2->nat_lock); ipf_nat_update(&fi, nat2); MUTEX_EXIT(&nat2->nat_lock); fi.fin_ifp = NULL; if (nat2->nat_dir == NAT_INBOUND) fi.fin_dst6 = nat->nat_osrc6; (void) ipf_state_add(softc, &fi, NULL, SI_W_SPORT); } if (nat->nat_v[0] == 6) { #ifdef USE_INET6 ip6->ip6_plen = slen; #endif } else { ip->ip_len = slen; } if (nat2 == NULL) return APR_ERR(1); return 0; } int -ipf_p_rcmd_out(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_rcmd_out(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { if (nat->nat_dir == NAT_OUTBOUND) return ipf_p_rcmd_portmsg(fin, aps, nat); return 0; } int -ipf_p_rcmd_in(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_rcmd_in(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { if (nat->nat_dir == NAT_INBOUND) return ipf_p_rcmd_portmsg(fin, aps, nat); return 0; } diff --git a/sys/netpfil/ipfilter/netinet/ip_rpcb_pxy.c b/sys/netpfil/ipfilter/netinet/ip_rpcb_pxy.c index 1b283ab76b97..94c89241a841 100644 --- a/sys/netpfil/ipfilter/netinet/ip_rpcb_pxy.c +++ b/sys/netpfil/ipfilter/netinet/ip_rpcb_pxy.c @@ -1,1463 +1,1404 @@ /* * Copyright (C) 2002-2012 by Ryan Beasley * * See the IPFILTER.LICENCE file for details on licencing. */ /* * Overview: * This is an in-kernel application proxy for Sun's RPCBIND (nee portmap) * protocol as defined in RFC1833. It is far from complete, mostly * lacking in less-likely corner cases, but it's definitely functional. * * Invocation: * rdr /32 port -> port udp proxy rpcbu * * If the host running IP Filter is the same as the RPC server, it's * perfectly legal for both the internal and external addresses and ports * to match. * * When triggered by appropriate IP NAT rules, this proxy works by * examining data contained in received packets. Requests and replies are * modified, NAT and state table entries created, etc., as necessary. */ /* * TODO / NOTES * * o Must implement locking to protect proxy session data. * o Fragmentation isn't supported. * o Only supports UDP. * o Doesn't support multiple RPC records in a single request. * o Errors should be more fine-grained. (e.g., malloc failure vs. * illegal RPCB request / reply) * o Even with the limit on the total amount of recorded transactions, * should there be a timeout on transaction removal? * o There is a potential collision between cloning, wildcard NAT and * state entries. There should be an appr_getport routine for * to avoid this. * o The enclosed hack of STREAMS support is pretty sick and most likely * broken. * * $Id$ */ #define IPF_RPCB_PROXY /* * Function prototypes */ void ipf_p_rpcb_main_load(void); void ipf_p_rpcb_main_unload(void); int ipf_p_rpcb_new(void *, fr_info_t *, ap_session_t *, nat_t *); void ipf_p_rpcb_del(ipf_main_softc_t *, ap_session_t *); int ipf_p_rpcb_in(void *, fr_info_t *, ap_session_t *, nat_t *); int ipf_p_rpcb_out(void *, fr_info_t *, ap_session_t *, nat_t *); static void ipf_p_rpcb_flush(rpcb_session_t *); static int ipf_p_rpcb_decodereq(fr_info_t *, nat_t *, rpcb_session_t *, rpc_msg_t *); static int ipf_p_rpcb_skipauth(rpc_msg_t *, xdr_auth_t *, u_32_t **); static int ipf_p_rpcb_insert(rpcb_session_t *, rpcb_xact_t *); static int ipf_p_rpcb_xdrrpcb(rpc_msg_t *, u_32_t *, rpcb_args_t *); static int ipf_p_rpcb_getuaddr(rpc_msg_t *, xdr_uaddr_t *, u_32_t **); static u_int ipf_p_rpcb_atoi(char *); static int ipf_p_rpcb_modreq(fr_info_t *, nat_t *, rpc_msg_t *, mb_t *, u_int); static int ipf_p_rpcb_decoderep(fr_info_t *, nat_t *, rpcb_session_t *, rpc_msg_t *, rpcb_xact_t **); static rpcb_xact_t * ipf_p_rpcb_lookup(rpcb_session_t *, u_32_t); static void ipf_p_rpcb_deref(rpcb_session_t *, rpcb_xact_t *); static int ipf_p_rpcb_getproto(rpc_msg_t *, xdr_proto_t *, u_32_t **); static int ipf_p_rpcb_getnat(fr_info_t *, nat_t *, u_int, u_int); static int ipf_p_rpcb_modv3(fr_info_t *, nat_t *, rpc_msg_t *, mb_t *, u_int); static int ipf_p_rpcb_modv4(fr_info_t *, nat_t *, rpc_msg_t *, mb_t *, u_int); static void ipf_p_rpcb_fixlen(fr_info_t *, int); /* * Global variables */ static frentry_t rpcbfr; /* Skeleton rule for reference by entities this proxy creates. */ VNET_DEFINE_STATIC(int, rpcbcnt); #define V_rpcbcnt VNET(rpcbcnt) /* Upper bound of allocated RPCB sessions. */ /* XXX rpcbcnt still requires locking. */ static int rpcb_proxy_init = 0; /* * Since rpc_msg contains only pointers, one should use this macro as a * handy way to get to the goods. (In case you're wondering about the name, * this started as BYTEREF -> BREF -> B.) */ #define B(r) (u_32_t)ntohl(*(r)) /* * Public subroutines */ /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_main_load */ /* Returns: void */ /* Parameters: (void) */ /* */ /* Initialize the filter rule entry and session limiter. */ /* -------------------------------------------------------------------- */ void -ipf_p_rpcb_main_load() +ipf_p_rpcb_main_load(void) { V_rpcbcnt = 0; bzero((char *)&rpcbfr, sizeof(rpcbfr)); rpcbfr.fr_ref = 1; rpcbfr.fr_flags = FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&rpcbfr.fr_lock, "ipf Sun RPCB proxy rule lock"); rpcb_proxy_init = 1; } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_main_unload */ /* Returns: void */ /* Parameters: (void) */ /* */ /* Destroy rpcbfr's mutex to avoid a lock leak. */ /* -------------------------------------------------------------------- */ void -ipf_p_rpcb_main_unload() +ipf_p_rpcb_main_unload(void) { if (rpcb_proxy_init == 1) { MUTEX_DESTROY(&rpcbfr.fr_lock); rpcb_proxy_init = 0; } } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_new */ /* Returns: int - -1 == failure, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* aps(I) - pointer to proxy session structure */ /* nat(I) - pointer to NAT session structure */ /* */ /* Allocate resources for per-session proxy structures. */ /* -------------------------------------------------------------------- */ int -ipf_p_rpcb_new(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_rpcb_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { rpcb_session_t *rs; nat = nat; /* LINT */ if (fin->fin_v != 4) return -1; KMALLOC(rs, rpcb_session_t *); if (rs == NULL) return(-1); bzero((char *)rs, sizeof(*rs)); MUTEX_INIT(&rs->rs_rxlock, "ipf Sun RPCB proxy session lock"); aps->aps_data = rs; return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_del */ /* Returns: void */ /* Parameters: aps(I) - pointer to proxy session structure */ /* */ /* Free up a session's list of RPCB requests. */ /* -------------------------------------------------------------------- */ void -ipf_p_rpcb_del(softc, aps) - ipf_main_softc_t *softc; - ap_session_t *aps; +ipf_p_rpcb_del(ipf_main_softc_t *softc, ap_session_t *aps) { rpcb_session_t *rs; rs = (rpcb_session_t *)aps->aps_data; MUTEX_ENTER(&rs->rs_rxlock); ipf_p_rpcb_flush(rs); MUTEX_EXIT(&rs->rs_rxlock); MUTEX_DESTROY(&rs->rs_rxlock); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_in */ /* Returns: int - APR_ERR(1) == drop the packet, */ /* APR_ERR(2) == kill the proxy session, */ /* else change in packet length (in bytes) */ /* Parameters: fin(I) - pointer to packet information */ /* ip(I) - pointer to packet header */ /* aps(I) - pointer to proxy session structure */ /* nat(I) - pointer to NAT session structure */ /* */ /* Given a presumed RPCB request, perform some minor tests and pass off */ /* for decoding. Also pass packet off for a rewrite if necessary. */ /* -------------------------------------------------------------------- */ int -ipf_p_rpcb_in(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_rpcb_in(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { rpc_msg_t rpcmsg, *rm; rpcb_session_t *rs; u_int off, dlen; mb_t *m; int rv; /* Disallow fragmented or illegally short packets. */ if ((fin->fin_flx & (FI_FRAG|FI_SHORT)) != 0) return(APR_ERR(1)); /* Perform basic variable initialization. */ rs = (rpcb_session_t *)aps->aps_data; m = fin->fin_m; off = (char *)fin->fin_dp - (char *)fin->fin_ip; off += sizeof(udphdr_t) + fin->fin_ipoff; dlen = fin->fin_dlen - sizeof(udphdr_t); /* Disallow packets outside legal range for supported requests. */ if ((dlen < RPCB_REQMIN) || (dlen > RPCB_REQMAX)) return(APR_ERR(1)); /* Copy packet over to convenience buffer. */ rm = &rpcmsg; bzero((char *)rm, sizeof(*rm)); COPYDATA(m, off, dlen, (caddr_t)&rm->rm_msgbuf); rm->rm_buflen = dlen; /* Send off to decode request. */ rv = ipf_p_rpcb_decodereq(fin, nat, rs, rm); switch(rv) { case -1: return(APR_ERR(1)); /*NOTREACHED*/ break; case 0: break; case 1: rv = ipf_p_rpcb_modreq(fin, nat, rm, m, off); break; default: /*CONSTANTCONDITION*/ IPF_PANIC(1, ("illegal rv %d (ipf_p_rpcb_req)", rv)); } return(rv); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_out */ /* Returns: int - APR_ERR(1) == drop the packet, */ /* APR_ERR(2) == kill the proxy session, */ /* else change in packet length (in bytes) */ /* Parameters: fin(I) - pointer to packet information */ /* ip(I) - pointer to packet header */ /* aps(I) - pointer to proxy session structure */ /* nat(I) - pointer to NAT session structure */ /* */ /* Given a presumed RPCB reply, perform some minor tests and pass off */ /* for decoding. If the message indicates a successful request with */ /* valid addressing information, create NAT and state structures to */ /* allow direct communication between RPC client and server. */ /* -------------------------------------------------------------------- */ int -ipf_p_rpcb_out(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_rpcb_out(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { rpc_msg_t rpcmsg, *rm; rpcb_session_t *rs; rpcb_xact_t *rx; u_int off, dlen; int rv, diff; mb_t *m; /* Disallow fragmented or illegally short packets. */ if ((fin->fin_flx & (FI_FRAG|FI_SHORT)) != 0) return(APR_ERR(1)); /* Perform basic variable initialization. */ rs = (rpcb_session_t *)aps->aps_data; rx = NULL; m = fin->fin_m; off = (char *)fin->fin_dp - (char *)fin->fin_ip; off += sizeof(udphdr_t) + fin->fin_ipoff; dlen = fin->fin_dlen - sizeof(udphdr_t); diff = 0; /* Disallow packets outside legal range for supported requests. */ if ((dlen < RPCB_REPMIN) || (dlen > RPCB_REPMAX)) return(APR_ERR(1)); /* Copy packet over to convenience buffer. */ rm = &rpcmsg; bzero((char *)rm, sizeof(*rm)); COPYDATA(m, off, dlen, (caddr_t)&rm->rm_msgbuf); rm->rm_buflen = dlen; rx = NULL; /* XXX gcc */ /* Send off to decode reply. */ rv = ipf_p_rpcb_decoderep(fin, nat, rs, rm, &rx); switch(rv) { case -1: /* Bad packet */ if (rx != NULL) { MUTEX_ENTER(&rs->rs_rxlock); ipf_p_rpcb_deref(rs, rx); MUTEX_EXIT(&rs->rs_rxlock); } return(APR_ERR(1)); /*NOTREACHED*/ break; case 0: /* Negative reply / request rejected */ break; case 1: /* Positive reply */ /* * With the IP address embedded in a GETADDR(LIST) reply, * we'll need to rewrite the packet in the very possible * event that the internal & external addresses aren't the * same. (i.e., this box is either a router or rpcbind * only listens on loopback.) */ if (nat->nat_odstaddr != nat->nat_ndstaddr) { if (rx->rx_type == RPCB_RES_STRING) diff = ipf_p_rpcb_modv3(fin, nat, rm, m, off); else if (rx->rx_type == RPCB_RES_LIST) diff = ipf_p_rpcb_modv4(fin, nat, rm, m, off); } break; default: /*CONSTANTCONDITION*/ IPF_PANIC(1, ("illegal rv %d (ipf_p_rpcb_decoderep)", rv)); } if (rx != NULL) { MUTEX_ENTER(&rs->rs_rxlock); /* XXX Gross hack - I'm overloading the reference * counter to deal with both threads and retransmitted * requests. One deref signals that this thread is * finished with rx, and the other signals that we've * processed its reply. */ ipf_p_rpcb_deref(rs, rx); ipf_p_rpcb_deref(rs, rx); MUTEX_EXIT(&rs->rs_rxlock); } return(diff); } /* * Private support subroutines */ /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_flush */ /* Returns: void */ /* Parameters: rs(I) - pointer to RPCB session structure */ /* */ /* Simply flushes the list of outstanding transactions, if any. */ /* -------------------------------------------------------------------- */ static void -ipf_p_rpcb_flush(rs) - rpcb_session_t *rs; +ipf_p_rpcb_flush(rpcb_session_t *rs) { rpcb_xact_t *r1, *r2; r1 = rs->rs_rxlist; if (r1 == NULL) return; while (r1 != NULL) { r2 = r1; r1 = r1->rx_next; KFREE(r2); } } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_decodereq */ /* Returns: int - -1 == bad request or critical failure, */ /* 0 == request successfully decoded, */ /* 1 == request successfully decoded; requires */ /* address rewrite/modification */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session structure */ /* rs(I) - pointer to RPCB session structure */ /* rm(I) - pointer to RPC message structure */ /* */ /* Take a presumed RPCB request, decode it, and store the results in */ /* the transaction list. If the internal target address needs to be */ /* modified, store its location in ptr. */ /* WARNING: It's the responsibility of the caller to make sure there */ /* is enough room in rs_buf for the basic RPC message "preamble". */ /* -------------------------------------------------------------------- */ static int -ipf_p_rpcb_decodereq(fin, nat, rs, rm) - fr_info_t *fin; - nat_t *nat; - rpcb_session_t *rs; - rpc_msg_t *rm; +ipf_p_rpcb_decodereq(fr_info_t *fin, nat_t *nat, rpcb_session_t *rs, + rpc_msg_t *rm) { rpcb_args_t *ra; u_32_t xdr, *p; rpc_call_t *rc; rpcb_xact_t rx; int mod; p = (u_32_t *)rm->rm_msgbuf; mod = 0; bzero((char *)&rx, sizeof(rx)); rc = &rm->rm_call; rm->rm_xid = p; rx.rx_xid = B(p++); /* Record this message's XID. */ /* Parse out and test the RPC header. */ if ((B(p++) != RPCB_CALL) || (B(p++) != RPCB_MSG_VERSION) || (B(p++) != RPCB_PROG)) return(-1); /* Record the RPCB version and procedure. */ rc->rc_vers = p++; rc->rc_proc = p++; /* Bypass RPC authentication stuff. */ if (ipf_p_rpcb_skipauth(rm, &rc->rc_authcred, &p) != 0) return(-1); if (ipf_p_rpcb_skipauth(rm, &rc->rc_authverf, &p) != 0) return(-1); /* Compare RPCB version and procedure numbers. */ switch(B(rc->rc_vers)) { case 2: /* This proxy only supports PMAP_GETPORT. */ if (B(rc->rc_proc) != RPCB_GETPORT) return(-1); /* Portmap requests contain four 4 byte parameters. */ if (RPCB_BUF_EQ(rm, p, 16) == 0) return(-1); p += 2; /* Skip requested program and version numbers. */ /* Sanity check the requested protocol. */ xdr = B(p); if (!(xdr == IPPROTO_UDP || xdr == IPPROTO_TCP)) return(-1); rx.rx_type = RPCB_RES_PMAP; rx.rx_proto = xdr; break; case 3: case 4: /* GETADDRLIST is exclusive to v4; GETADDR for v3 & v4 */ switch(B(rc->rc_proc)) { case RPCB_GETADDR: rx.rx_type = RPCB_RES_STRING; rx.rx_proto = (u_int)fin->fin_p; break; case RPCB_GETADDRLIST: if (B(rc->rc_vers) != 4) return(-1); rx.rx_type = RPCB_RES_LIST; break; default: return(-1); } ra = &rc->rc_rpcbargs; /* Decode the 'struct rpcb' request. */ if (ipf_p_rpcb_xdrrpcb(rm, p, ra) != 0) return(-1); /* Are the target address & port valid? */ if ((ra->ra_maddr.xu_ip != nat->nat_ndstaddr) || (ra->ra_maddr.xu_port != nat->nat_ndport)) return(-1); /* Do we need to rewrite this packet? */ if ((nat->nat_ndstaddr != nat->nat_odstaddr) || (nat->nat_ndport != nat->nat_odport)) mod = 1; break; default: return(-1); } MUTEX_ENTER(&rs->rs_rxlock); if (ipf_p_rpcb_insert(rs, &rx) != 0) { MUTEX_EXIT(&rs->rs_rxlock); return(-1); } MUTEX_EXIT(&rs->rs_rxlock); return(mod); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_skipauth */ /* Returns: int -- -1 == illegal auth parameters (lengths) */ /* 0 == valid parameters, pointer advanced */ /* Parameters: rm(I) - pointer to RPC message structure */ /* auth(I) - pointer to RPC auth structure */ /* buf(IO) - pointer to location within convenience buffer */ /* */ /* Record auth data length & location of auth data, then advance past */ /* it. */ /* -------------------------------------------------------------------- */ static int -ipf_p_rpcb_skipauth(rm, auth, buf) - rpc_msg_t *rm; - xdr_auth_t *auth; - u_32_t **buf; +ipf_p_rpcb_skipauth(rpc_msg_t *rm, xdr_auth_t *auth, u_32_t **buf) { u_32_t *p, xdr; p = *buf; /* Make sure we have enough space for expected fixed auth parms. */ if (RPCB_BUF_GEQ(rm, p, 8) == 0) return(-1); p++; /* We don't care about auth_flavor. */ auth->xa_string.xs_len = p; xdr = B(p++); /* Length of auth_data */ /* Test for absurdity / illegality of auth_data length. */ if ((XDRALIGN(xdr) < xdr) || (RPCB_BUF_GEQ(rm, p, XDRALIGN(xdr)) == 0)) return(-1); auth->xa_string.xs_str = (char *)p; p += XDRALIGN(xdr); /* Advance our location. */ *buf = (u_32_t *)p; return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_insert */ /* Returns: int -- -1 == list insertion failed, */ /* 0 == item successfully added */ /* Parameters: rs(I) - pointer to RPCB session structure */ /* rx(I) - pointer to RPCB transaction structure */ /* -------------------------------------------------------------------- */ static int -ipf_p_rpcb_insert(rs, rx) - rpcb_session_t *rs; - rpcb_xact_t *rx; +ipf_p_rpcb_insert(rpcb_session_t *rs, rpcb_xact_t *rx) { rpcb_xact_t *rxp; rxp = ipf_p_rpcb_lookup(rs, rx->rx_xid); if (rxp != NULL) { ++rxp->rx_ref; return(0); } if (V_rpcbcnt == RPCB_MAXREQS) return(-1); KMALLOC(rxp, rpcb_xact_t *); if (rxp == NULL) return(-1); bcopy((char *)rx, (char *)rxp, sizeof(*rx)); if (rs->rs_rxlist != NULL) rs->rs_rxlist->rx_pnext = &rxp->rx_next; rxp->rx_pnext = &rs->rs_rxlist; rxp->rx_next = rs->rs_rxlist; rs->rs_rxlist = rxp; rxp->rx_ref = 1; ++V_rpcbcnt; return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_xdrrpcb */ /* Returns: int -- -1 == failure to properly decode the request */ /* 0 == rpcb successfully decoded */ /* Parameters: rs(I) - pointer to RPCB session structure */ /* p(I) - pointer to location within session buffer */ /* rpcb(O) - pointer to rpcb (xdr type) structure */ /* */ /* Decode a XDR encoded rpcb structure and record its contents in rpcb */ /* within only the context of TCP/UDP over IP networks. */ /* -------------------------------------------------------------------- */ static int -ipf_p_rpcb_xdrrpcb(rm, p, ra) - rpc_msg_t *rm; - u_32_t *p; - rpcb_args_t *ra; +ipf_p_rpcb_xdrrpcb(rpc_msg_t *rm, u_32_t *p, rpcb_args_t *ra) { if (!RPCB_BUF_GEQ(rm, p, 20)) return(-1); /* Bypass target program & version. */ p += 2; /* Decode r_netid. Must be "tcp" or "udp". */ if (ipf_p_rpcb_getproto(rm, &ra->ra_netid, &p) != 0) return(-1); /* Decode r_maddr. */ if (ipf_p_rpcb_getuaddr(rm, &ra->ra_maddr, &p) != 0) return(-1); /* Advance to r_owner and make sure it's empty. */ if (!RPCB_BUF_EQ(rm, p, 4) || (B(p) != 0)) return(-1); return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_getuaddr */ /* Returns: int -- -1 == illegal string, */ /* 0 == string parsed; contents recorded */ /* Parameters: rm(I) - pointer to RPC message structure */ /* xu(I) - pointer to universal address structure */ /* p(IO) - pointer to location within message buffer */ /* */ /* Decode the IP address / port at p and record them in xu. */ /* -------------------------------------------------------------------- */ static int -ipf_p_rpcb_getuaddr(rm, xu, p) - rpc_msg_t *rm; - xdr_uaddr_t *xu; - u_32_t **p; +ipf_p_rpcb_getuaddr(rpc_msg_t *rm, xdr_uaddr_t *xu, u_32_t **p) { char *c, *i, *b, *pp; u_int d, dd, l, t; char uastr[24]; /* Test for string length. */ if (!RPCB_BUF_GEQ(rm, *p, 4)) return(-1); xu->xu_xslen = (*p)++; xu->xu_xsstr = (char *)*p; /* Length check */ l = B(xu->xu_xslen); if (l < 11 || l > 23 || !RPCB_BUF_GEQ(rm, *p, XDRALIGN(l))) return(-1); /* Advance p */ *(char **)p += XDRALIGN(l); /* Copy string to local buffer & terminate C style */ bcopy(xu->xu_xsstr, uastr, l); uastr[l] = '\0'; i = (char *)&xu->xu_ip; pp = (char *)&xu->xu_port; /* * Expected format: a.b.c.d.e.f where [a-d] correspond to bytes of * an IP address and [ef] are the bytes of a L4 port. */ if (!(ISDIGIT(uastr[0]) && ISDIGIT(uastr[l-1]))) return(-1); b = uastr; for (c = &uastr[1], d = 0, dd = 0; c < &uastr[l-1]; c++) { if (ISDIGIT(*c)) { dd = 0; continue; } if (*c == '.') { if (dd != 0) return(-1); /* Check for ASCII byte. */ *c = '\0'; t = ipf_p_rpcb_atoi(b); if (t > 255) return(-1); /* Aim b at beginning of the next byte. */ b = c + 1; /* Switch off IP addr vs port parsing. */ if (d < 4) i[d++] = t & 0xff; else pp[d++ - 4] = t & 0xff; dd = 1; continue; } return(-1); } if (d != 5) /* String must contain exactly 5 periods. */ return(-1); /* Handle the last byte (port low byte) */ t = ipf_p_rpcb_atoi(b); if (t > 255) return(-1); pp[d - 4] = t & 0xff; return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_atoi (XXX should be generic for all proxies) */ /* Returns: int -- integer representation of supplied string */ /* Parameters: ptr(I) - input string */ /* */ /* Simple version of atoi(3) ripped from ip_rcmd_pxy.c. */ /* -------------------------------------------------------------------- */ static u_int -ipf_p_rpcb_atoi(ptr) - char *ptr; +ipf_p_rpcb_atoi(char *ptr) { register char *s = ptr, c; register u_int i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } return i; } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_modreq */ /* Returns: int -- change in datagram length */ /* APR_ERR(2) - critical failure */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session */ /* rm(I) - pointer to RPC message structure */ /* m(I) - pointer to mbuf chain */ /* off(I) - current offset within mbuf chain */ /* */ /* When external and internal addresses differ, we rewrite the former */ /* with the latter. (This is exclusive to protocol versions 3 & 4). */ /* -------------------------------------------------------------------- */ static int -ipf_p_rpcb_modreq(fin, nat, rm, m, off) - fr_info_t *fin; - nat_t *nat; - rpc_msg_t *rm; - mb_t *m; - u_int off; +ipf_p_rpcb_modreq(fr_info_t *fin, nat_t *nat, rpc_msg_t *rm, mb_t *m, + u_int off) { u_int len, xlen, pos, bogo; rpcb_args_t *ra; char uaddr[24]; udphdr_t *udp; char *i, *p; int diff; ra = &rm->rm_call.rc_rpcbargs; i = (char *)&nat->nat_odstaddr; p = (char *)&nat->nat_odport; /* Form new string. */ bzero(uaddr, sizeof(uaddr)); /* Just in case we need padding. */ (void) snprintf(uaddr, sizeof(uaddr), "%u.%u.%u.%u.%u.%u", i[0] & 0xff, i[1] & 0xff, i[2] & 0xff, i[3] & 0xff, p[0] & 0xff, p[1] & 0xff); len = strlen(uaddr); xlen = XDRALIGN(len); /* Determine mbuf offset to start writing to. */ pos = (char *)ra->ra_maddr.xu_xslen - rm->rm_msgbuf; off += pos; /* Write new string length. */ bogo = htonl(len); COPYBACK(m, off, 4, (caddr_t)&bogo); off += 4; /* Write new string. */ COPYBACK(m, off, xlen, uaddr); off += xlen; /* Write in zero r_owner. */ bogo = 0; COPYBACK(m, off, 4, (caddr_t)&bogo); /* Determine difference in data lengths. */ diff = xlen - XDRALIGN(B(ra->ra_maddr.xu_xslen)); /* * If our new string has a different length, make necessary * adjustments. */ if (diff != 0) { udp = fin->fin_dp; udp->uh_ulen = htons(ntohs(udp->uh_ulen) + diff); fin->fin_plen += diff; fin->fin_ip->ip_len = htons(fin->fin_plen); fin->fin_dlen += diff; /* XXX Storage lengths. */ } return(diff); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_decoderep */ /* Returns: int - -1 == bad request or critical failure, */ /* 0 == valid, negative reply */ /* 1 == vaddlid, positive reply; needs no changes */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session structure */ /* rs(I) - pointer to RPCB session structure */ /* rm(I) - pointer to RPC message structure */ /* rxp(O) - pointer to RPCB transaction structure */ /* */ /* Take a presumed RPCB reply, extract the XID, search for the original */ /* request information, and determine whether the request was accepted */ /* or rejected. With a valid accepted reply, go ahead and create NAT */ /* and state entries, and finish up by rewriting the packet as */ /* required. */ /* */ /* WARNING: It's the responsibility of the caller to make sure there */ /* is enough room in rs_buf for the basic RPC message "preamble". */ /* -------------------------------------------------------------------- */ static int -ipf_p_rpcb_decoderep(fin, nat, rs, rm, rxp) - fr_info_t *fin; - nat_t *nat; - rpcb_session_t *rs; - rpc_msg_t *rm; - rpcb_xact_t **rxp; +ipf_p_rpcb_decoderep(fr_info_t *fin, nat_t *nat, rpcb_session_t *rs, + rpc_msg_t *rm, rpcb_xact_t **rxp) { rpcb_listp_t *rl; rpcb_entry_t *re; rpcb_xact_t *rx; u_32_t xdr, *p; rpc_resp_t *rr; int rv, cnt; p = (u_32_t *)rm->rm_msgbuf; bzero((char *)&rx, sizeof(rx)); rr = &rm->rm_resp; rm->rm_xid = p; xdr = B(p++); /* Record this message's XID. */ /* Lookup XID */ MUTEX_ENTER(&rs->rs_rxlock); if ((rx = ipf_p_rpcb_lookup(rs, xdr)) == NULL) { MUTEX_EXIT(&rs->rs_rxlock); return(-1); } ++rx->rx_ref; /* per thread reference */ MUTEX_EXIT(&rs->rs_rxlock); *rxp = rx; /* Test call vs reply */ if (B(p++) != RPCB_REPLY) return(-1); /* Test reply_stat */ switch(B(p++)) { case RPCB_MSG_DENIED: return(0); case RPCB_MSG_ACCEPTED: break; default: return(-1); } /* Bypass RPC authentication stuff. */ if (ipf_p_rpcb_skipauth(rm, &rr->rr_authverf, &p) != 0) return(-1); /* Test accept status */ if (!RPCB_BUF_GEQ(rm, p, 4)) return(-1); if (B(p++) != 0) return(0); /* Parse out the expected reply */ switch(rx->rx_type) { case RPCB_RES_PMAP: /* There must be only one 4 byte argument. */ if (!RPCB_BUF_EQ(rm, p, 4)) return(-1); rr->rr_v2 = p; xdr = B(rr->rr_v2); /* Reply w/ a 0 port indicates service isn't registered */ if (xdr == 0) return(0); /* Is the value sane? */ if (xdr > 65535) return(-1); /* Create NAT & state table entries. */ if (ipf_p_rpcb_getnat(fin, nat, rx->rx_proto, (u_int)xdr) != 0) return(-1); break; case RPCB_RES_STRING: /* Expecting a XDR string; need 4 bytes for length */ if (!RPCB_BUF_GEQ(rm, p, 4)) return(-1); rr->rr_v3.xu_str.xs_len = p++; rr->rr_v3.xu_str.xs_str = (char *)p; xdr = B(rr->rr_v3.xu_xslen); /* A null string indicates an unregistered service */ if ((xdr == 0) && RPCB_BUF_EQ(rm, p, 0)) return(0); /* Decode the target IP address / port. */ if (ipf_p_rpcb_getuaddr(rm, &rr->rr_v3, &p) != 0) return(-1); /* Validate the IP address and port contained. */ if (nat->nat_odstaddr != rr->rr_v3.xu_ip) return(-1); /* Create NAT & state table entries. */ if (ipf_p_rpcb_getnat(fin, nat, rx->rx_proto, (u_int)rr->rr_v3.xu_port) != 0) return(-1); break; case RPCB_RES_LIST: if (!RPCB_BUF_GEQ(rm, p, 4)) return(-1); /* rpcb_entry_list_ptr */ switch(B(p)) { case 0: return(0); /*NOTREACHED*/ break; case 1: break; default: return(-1); } rl = &rr->rr_v4; rl->rl_list = p++; cnt = 0; for(;;) { re = &rl->rl_entries[rl->rl_cnt]; if (ipf_p_rpcb_getuaddr(rm, &re->re_maddr, &p) != 0) return(-1); if (ipf_p_rpcb_getproto(rm, &re->re_netid, &p) != 0) return(-1); /* re_semantics & re_pfamily length */ if (!RPCB_BUF_GEQ(rm, p, 12)) return(-1); p++; /* Skipping re_semantics. */ xdr = B(p++); if ((xdr != 4) || strncmp((char *)p, "inet", 4)) return(-1); p++; if (ipf_p_rpcb_getproto(rm, &re->re_proto, &p) != 0) return(-1); if (!RPCB_BUF_GEQ(rm, p, 4)) return(-1); re->re_more = p; if (B(re->re_more) > 1) /* 0,1 only legal values */ return(-1); ++rl->rl_cnt; ++cnt; if (B(re->re_more) == 0) break; /* Replies in max out at 2; TCP and/or UDP */ if (cnt > 2) return(-1); p++; } for(rl->rl_cnt = 0; rl->rl_cnt < cnt; rl->rl_cnt++) { re = &rl->rl_entries[rl->rl_cnt]; rv = ipf_p_rpcb_getnat(fin, nat, re->re_proto.xp_proto, (u_int)re->re_maddr.xu_port); if (rv != 0) return(-1); } break; default: /*CONSTANTCONDITION*/ IPF_PANIC(1, ("illegal rx_type %d", rx->rx_type)); } return(1); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_lookup */ /* Returns: rpcb_xact_t * - NULL == no matching record, */ /* else pointer to relevant entry */ /* Parameters: rs(I) - pointer to RPCB session */ /* xid(I) - XID to look for */ /* -------------------------------------------------------------------- */ static rpcb_xact_t * -ipf_p_rpcb_lookup(rs, xid) - rpcb_session_t *rs; - u_32_t xid; +ipf_p_rpcb_lookup(rpcb_session_t *rs, u_32_t xid) { rpcb_xact_t *rx; if (rs->rs_rxlist == NULL) return(NULL); for (rx = rs->rs_rxlist; rx != NULL; rx = rx->rx_next) if (rx->rx_xid == xid) break; return(rx); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_deref */ /* Returns: (void) */ /* Parameters: rs(I) - pointer to RPCB session */ /* rx(I) - pointer to RPC transaction struct to remove */ /* force(I) - indicates to delete entry regardless of */ /* reference count */ /* Locking: rs->rs_rxlock must be held write only */ /* */ /* Free the RPCB transaction record rx from the chain of entries. */ /* -------------------------------------------------------------------- */ static void -ipf_p_rpcb_deref(rs, rx) - rpcb_session_t *rs; - rpcb_xact_t *rx; +ipf_p_rpcb_deref(rpcb_session_t *rs, rpcb_xact_t *rx) { rs = rs; /* LINT */ if (rx == NULL) return; if (--rx->rx_ref != 0) return; if (rx->rx_next != NULL) rx->rx_next->rx_pnext = rx->rx_pnext; *rx->rx_pnext = rx->rx_next; KFREE(rx); --V_rpcbcnt; } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_getproto */ /* Returns: int - -1 == illegal protocol/netid, */ /* 0 == legal protocol/netid */ /* Parameters: rm(I) - pointer to RPC message structure */ /* xp(I) - pointer to netid structure */ /* p(IO) - pointer to location within packet buffer */ /* */ /* Decode netid/proto stored at p and record its numeric value. */ /* -------------------------------------------------------------------- */ static int -ipf_p_rpcb_getproto(rm, xp, p) - rpc_msg_t *rm; - xdr_proto_t *xp; - u_32_t **p; +ipf_p_rpcb_getproto(rpc_msg_t *rm, xdr_proto_t *xp, u_32_t **p) { u_int len; /* Must have 4 bytes for length & 4 bytes for "tcp" or "udp". */ if (!RPCB_BUF_GEQ(rm, p, 8)) return(-1); xp->xp_xslen = (*p)++; xp->xp_xsstr = (char *)*p; /* Test the string length. */ len = B(xp->xp_xslen); if (len != 3) return(-1); /* Test the actual string & record the protocol accordingly. */ if (!strncmp((char *)xp->xp_xsstr, "tcp\0", 4)) xp->xp_proto = IPPROTO_TCP; else if (!strncmp((char *)xp->xp_xsstr, "udp\0", 4)) xp->xp_proto = IPPROTO_UDP; else { return(-1); } /* Advance past the string. */ (*p)++; return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_getnat */ /* Returns: int -- -1 == failed to create table entries, */ /* 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT table entry */ /* proto(I) - transport protocol for new entries */ /* port(I) - new port to use w/ wildcard table entries */ /* */ /* Create state and NAT entries to handle an anticipated connection */ /* attempt between RPC client and server. */ /* -------------------------------------------------------------------- */ static int -ipf_p_rpcb_getnat(fin, nat, proto, port) - fr_info_t *fin; - nat_t *nat; - u_int proto; - u_int port; +ipf_p_rpcb_getnat(fr_info_t *fin, nat_t *nat, u_int proto, u_int port) { ipf_main_softc_t *softc = fin->fin_main_soft; ipnat_t *ipn, ipnat; tcphdr_t tcp; ipstate_t *is; fr_info_t fi; nat_t *natl; int nflags; ipn = nat->nat_ptr; /* Generate dummy fr_info */ bcopy((char *)fin, (char *)&fi, sizeof(fi)); fi.fin_out = 0; fi.fin_p = proto; fi.fin_sport = 0; fi.fin_dport = port & 0xffff; fi.fin_flx |= FI_IGNORE; fi.fin_saddr = nat->nat_osrcaddr; fi.fin_daddr = nat->nat_odstaddr; bzero((char *)&tcp, sizeof(tcp)); tcp.th_dport = htons(port); if (proto == IPPROTO_TCP) { tcp.th_win = htons(8192); TCP_OFF_A(&tcp, sizeof(tcphdr_t) >> 2); fi.fin_dlen = sizeof(tcphdr_t); tcp.th_flags = TH_SYN; nflags = NAT_TCP; } else { fi.fin_dlen = sizeof(udphdr_t); nflags = NAT_UDP; } nflags |= SI_W_SPORT|NAT_SEARCH; fi.fin_dp = &tcp; fi.fin_plen = fi.fin_hlen + fi.fin_dlen; /* * Search for existing NAT & state entries. Pay close attention to * mutexes / locks grabbed from lookup routines, as not doing so could * lead to bad things. * * If successful, fr_stlookup returns with ipf_state locked. We have * no use for this lock, so simply unlock it if necessary. */ is = ipf_state_lookup(&fi, &tcp, NULL); if (is != NULL) { RWLOCK_EXIT(&softc->ipf_state); } RWLOCK_EXIT(&softc->ipf_nat); WRITE_ENTER(&softc->ipf_nat); natl = ipf_nat_inlookup(&fi, nflags, proto, fi.fin_src, fi.fin_dst); if ((natl != NULL) && (is != NULL)) { MUTEX_DOWNGRADE(&softc->ipf_nat); return(0); } /* Slightly modify the following structures for actual use in creating * NAT and/or state entries. We're primarily concerned with stripping * flags that may be detrimental to the creation process or simply * shouldn't be associated with a table entry. */ fi.fin_fr = &rpcbfr; fi.fin_flx &= ~FI_IGNORE; nflags &= ~NAT_SEARCH; if (natl == NULL) { #ifdef USE_MUTEXES ipf_nat_softc_t *softn = softc->ipf_nat_soft; #endif /* XXX Since we're just copying the original ipn contents * back, would we be better off just sending a pointer to * the 'temp' copy off to nat_new instead? */ /* Generate template/bogus NAT rule. */ bcopy((char *)ipn, (char *)&ipnat, sizeof(ipnat)); ipn->in_flags = nflags & IPN_TCPUDP; ipn->in_apr = NULL; ipn->in_pr[0] = proto; ipn->in_pr[1] = proto; ipn->in_dpmin = fi.fin_dport; ipn->in_dpmax = fi.fin_dport; ipn->in_dpnext = fi.fin_dport; ipn->in_space = 1; ipn->in_ippip = 1; if (ipn->in_flags & IPN_FILTER) { ipn->in_scmp = 0; ipn->in_dcmp = 0; } ipn->in_plabel = -1; /* Create NAT entry. return NULL if this fails. */ MUTEX_ENTER(&softn->ipf_nat_new); natl = ipf_nat_add(&fi, ipn, NULL, nflags|SI_CLONE|NAT_SLAVE, NAT_INBOUND); MUTEX_EXIT(&softn->ipf_nat_new); bcopy((char *)&ipnat, (char *)ipn, sizeof(ipnat)); if (natl == NULL) { MUTEX_DOWNGRADE(&softc->ipf_nat); return(-1); } natl->nat_ptr = ipn; fi.fin_saddr = natl->nat_nsrcaddr; fi.fin_daddr = natl->nat_ndstaddr; ipn->in_use++; (void) ipf_nat_proto(&fi, natl, nflags); MUTEX_ENTER(&natl->nat_lock); ipf_nat_update(&fi, natl); MUTEX_EXIT(&natl->nat_lock); } MUTEX_DOWNGRADE(&softc->ipf_nat); if (is == NULL) { /* Create state entry. Return NULL if this fails. */ fi.fin_flx |= FI_NATED; fi.fin_flx &= ~FI_STATE; nflags &= NAT_TCPUDP; nflags |= SI_W_SPORT|SI_CLONE; if (ipf_state_add(softc, &fi, NULL, nflags) != 0) { /* * XXX nat_delete is private to ip_nat.c. Should * check w/ Darren about this one. * * nat_delete(natl, NL_EXPIRE); */ return(-1); } } return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_modv3 */ /* Returns: int -- change in packet length */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session */ /* rm(I) - pointer to RPC message structure */ /* m(I) - pointer to mbuf chain */ /* off(I) - offset within mbuf chain */ /* */ /* Write a new universal address string to this packet, adjusting */ /* lengths as necessary. */ /* -------------------------------------------------------------------- */ static int -ipf_p_rpcb_modv3(fin, nat, rm, m, off) - fr_info_t *fin; - nat_t *nat; - rpc_msg_t *rm; - mb_t *m; - u_int off; +ipf_p_rpcb_modv3(fr_info_t *fin, nat_t *nat, rpc_msg_t *rm, mb_t *m, + u_int off) { u_int len, xlen, pos, bogo; rpc_resp_t *rr; char uaddr[24]; char *i, *p; int diff; rr = &rm->rm_resp; i = (char *)&nat->nat_ndstaddr; p = (char *)&rr->rr_v3.xu_port; /* Form new string. */ bzero(uaddr, sizeof(uaddr)); /* Just in case we need padding. */ (void) snprintf(uaddr, sizeof(uaddr), "%u.%u.%u.%u.%u.%u", i[0] & 0xff, i[1] & 0xff, i[2] & 0xff, i[3] & 0xff, p[0] & 0xff, p[1] & 0xff); len = strlen(uaddr); xlen = XDRALIGN(len); /* Determine mbuf offset to write to. */ pos = (char *)rr->rr_v3.xu_xslen - rm->rm_msgbuf; off += pos; /* Write new string length. */ bogo = htonl(len); COPYBACK(m, off, 4, (caddr_t)&bogo); off += 4; /* Write new string. */ COPYBACK(m, off, xlen, uaddr); /* Determine difference in data lengths. */ diff = xlen - XDRALIGN(B(rr->rr_v3.xu_xslen)); /* * If our new string has a different length, make necessary * adjustments. */ if (diff != 0) ipf_p_rpcb_fixlen(fin, diff); return(diff); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_modv4 */ /* Returns: int -- change in packet length */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session */ /* rm(I) - pointer to RPC message structure */ /* m(I) - pointer to mbuf chain */ /* off(I) - offset within mbuf chain */ /* */ /* Write new rpcb_entry list, adjusting lengths as necessary. */ /* -------------------------------------------------------------------- */ static int -ipf_p_rpcb_modv4(fin, nat, rm, m, off) - fr_info_t *fin; - nat_t *nat; - rpc_msg_t *rm; - mb_t *m; - u_int off; +ipf_p_rpcb_modv4(fr_info_t *fin, nat_t *nat, rpc_msg_t *rm, mb_t *m, + u_int off) { u_int len, xlen, pos, bogo; rpcb_listp_t *rl; rpcb_entry_t *re; rpc_resp_t *rr; char uaddr[24]; int diff, cnt; char *i, *p; diff = 0; rr = &rm->rm_resp; rl = &rr->rr_v4; i = (char *)&nat->nat_ndstaddr; /* Determine mbuf offset to write to. */ re = &rl->rl_entries[0]; pos = (char *)re->re_maddr.xu_xslen - rm->rm_msgbuf; off += pos; for (cnt = 0; cnt < rl->rl_cnt; cnt++) { re = &rl->rl_entries[cnt]; p = (char *)&re->re_maddr.xu_port; /* Form new string. */ bzero(uaddr, sizeof(uaddr)); /* Just in case we need padding. */ (void) snprintf(uaddr, sizeof(uaddr), "%u.%u.%u.%u.%u.%u", i[0] & 0xff, i[1] & 0xff, i[2] & 0xff, i[3] & 0xff, p[0] & 0xff, p[1] & 0xff); len = strlen(uaddr); xlen = XDRALIGN(len); /* Write new string length. */ bogo = htonl(len); COPYBACK(m, off, 4, (caddr_t)&bogo); off += 4; /* Write new string. */ COPYBACK(m, off, xlen, uaddr); off += xlen; /* Record any change in length. */ diff += xlen - XDRALIGN(B(re->re_maddr.xu_xslen)); /* If the length changed, copy back the rest of this entry. */ len = ((char *)re->re_more + 4) - (char *)re->re_netid.xp_xslen; if (diff != 0) { COPYBACK(m, off, len, (caddr_t)re->re_netid.xp_xslen); } off += len; } /* * If our new string has a different length, make necessary * adjustments. */ if (diff != 0) ipf_p_rpcb_fixlen(fin, diff); return(diff); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_fixlen */ /* Returns: (void) */ /* Parameters: fin(I) - pointer to packet information */ /* len(I) - change in packet length */ /* */ /* Adjust various packet related lengths held in structure and packet */ /* header fields. */ /* -------------------------------------------------------------------- */ static void -ipf_p_rpcb_fixlen(fin, len) - fr_info_t *fin; - int len; +ipf_p_rpcb_fixlen(fr_info_t *fin, int len) { udphdr_t *udp; udp = fin->fin_dp; udp->uh_ulen = htons(ntohs(udp->uh_ulen) + len); fin->fin_plen += len; fin->fin_ip->ip_len = htons(fin->fin_plen); fin->fin_dlen += len; } #undef B diff --git a/sys/netpfil/ipfilter/netinet/ip_rules.c b/sys/netpfil/ipfilter/netinet/ip_rules.c index fcf6923adeb3..29355567648f 100644 --- a/sys/netpfil/ipfilter/netinet/ip_rules.c +++ b/sys/netpfil/ipfilter/netinet/ip_rules.c @@ -1,270 +1,270 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and due credit is given * to the original author and the contributors. */ #include #include #include #include #if defined(__FreeBSD__) # if defined(_KERNEL) # include # else # include # endif #else # include #endif #include #include #if !defined(__SVR4) # include #endif #if defined(__FreeBSD__) # include #if defined(_KERNEL) #include #else #define CURVNET_SET(arg) #define CURVNET_RESTORE() #define VNET_DEFINE(_t, _v) _t _v #define VNET_DECLARE(_t, _v) extern _t _v #define VNET(arg) arg #endif #else # include #endif /* FreeBSD */ #include #include #include #include #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_rules.h" #ifndef _KERNEL # include #endif /* _KERNEL */ #ifdef IPFILTER_COMPILED VNET_DECLARE(ipf_main_softc_t, ipfmain); #define V_ipfmain VNET(ipfmain) static u_long in_rule__0[] = { 0, 0, 0, 0, 0, 0, 0, 0x8070d88, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0x1b0, 0x1, 0, 0, 0, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40000000, 0x8002, 0, 0, 0, 0xffff, 0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0 }; static u_long out_rule__0[] = { 0, 0, 0, 0, 0, 0, 0, 0x8070d88, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0x1b0, 0x1, 0, 0, 0, 0x3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40000000, 0x4002, 0, 0, 0, 0xffff, 0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0 }; frentry_t *ipf_rules_in_[1] = { (frentry_t *)&in_rule__0 }; /* XXX This file (ip_rules.c) is not part of the ipfilter tarball, it is XXX generated by the ipfilter build process. Unfortunately the build XXX process did not generate the following lines so they are added XXX by hand here. This is a bit of a hack but it works for now. Future XXX imports/merges of ipfilter may generate this so the following will XXX need to be removed following some future merge. XXX */ frentry_t *ipf_rules_out_[1] = { (frentry_t *)&out_rule__0 }; frentry_t *ipfrule_match_in_(fin, passp) fr_info_t *fin; u_32_t *passp; { frentry_t *fr = NULL; fr = (frentry_t *)&in_rule__0; return fr; } frentry_t *ipfrule_match_out_(fin, passp) fr_info_t *fin; u_32_t *passp; { frentry_t *fr = NULL; fr = (frentry_t *)&out_rule__0; return fr; } static frentry_t ipfrule_out_; -int ipfrule_add_out_() +int ipfrule_add_out_(void) { int i, j, err = 0, max; frentry_t *fp; max = sizeof(ipf_rules_out_)/sizeof(frentry_t *); for (i = 0; i < max; i++) { fp = ipf_rules_out_[i]; fp->fr_next = NULL; for (j = i + 1; j < max; j++) if (strncmp(fp->fr_names + fp->fr_group, ipf_rules_out_[j]->fr_names + ipf_rules_out_[j]->fr_group, FR_GROUPLEN) == 0) { if (ipf_rules_out_[j] != NULL) ipf_rules_out_[j]->fr_pnext = &fp->fr_next; fp->fr_pnext = &ipf_rules_out_[j]; fp->fr_next = ipf_rules_out_[j]; break; } } fp = &ipfrule_out_; bzero((char *)fp, sizeof(*fp)); fp->fr_type = FR_T_CALLFUNC_BUILTIN; fp->fr_flags = FR_OUTQUE|FR_NOMATCH; fp->fr_data = (void *)ipf_rules_out_[0]; fp->fr_dsize = sizeof(ipf_rules_out_[0]); fp->fr_family = AF_INET; fp->fr_func = (ipfunc_t)ipfrule_match_out_; err = frrequest(&V_ipfmain, IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, V_ipfmain.ipf_active, 0); return err; } -int ipfrule_remove_out_() +int ipfrule_remove_out_(void) { int err = 0, i; frentry_t *fp; /* * Try to remove the outbound rule. */ if (ipfrule_out_.fr_ref > 0) { err = EBUSY; } else { i = sizeof(ipf_rules_out_)/sizeof(frentry_t *) - 1; for (; i >= 0; i--) { fp = ipf_rules_out_[i]; if (fp->fr_ref > 1) { err = EBUSY; break; } } } if (err == 0) err = frrequest(&V_ipfmain, IPL_LOGIPF, SIOCDELFR, (caddr_t)&ipfrule_out_, V_ipfmain.ipf_active, 0); if (err) return err; return err; } static frentry_t ipfrule_in_; -int ipfrule_add_in_() +int ipfrule_add_in_(void) { int i, j, err = 0, max; frentry_t *fp; max = sizeof(ipf_rules_in_)/sizeof(frentry_t *); for (i = 0; i < max; i++) { fp = ipf_rules_in_[i]; fp->fr_next = NULL; for (j = i + 1; j < max; j++) if (strncmp(fp->fr_names + fp->fr_group, ipf_rules_in_[j]->fr_names + ipf_rules_in_[j]->fr_group, FR_GROUPLEN) == 0) { if (ipf_rules_in_[j] != NULL) ipf_rules_in_[j]->fr_pnext = &fp->fr_next; fp->fr_pnext = &ipf_rules_in_[j]; fp->fr_next = ipf_rules_in_[j]; break; } } fp = &ipfrule_in_; bzero((char *)fp, sizeof(*fp)); fp->fr_type = FR_T_CALLFUNC_BUILTIN; fp->fr_flags = FR_INQUE|FR_NOMATCH; fp->fr_data = (void *)ipf_rules_in_[0]; fp->fr_dsize = sizeof(ipf_rules_in_[0]); fp->fr_family = AF_INET; fp->fr_func = (ipfunc_t)ipfrule_match_in_; err = frrequest(&V_ipfmain, IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, V_ipfmain.ipf_active, 0); return err; } -int ipfrule_remove_in_() +int ipfrule_remove_in_(void) { int err = 0, i; frentry_t *fp; /* * Try to remove the inbound rule. */ if (ipfrule_in_.fr_ref > 0) { err = EBUSY; } else { i = sizeof(ipf_rules_in_)/sizeof(frentry_t *) - 1; for (; i >= 0; i--) { fp = ipf_rules_in_[i]; if (fp->fr_ref > 1) { err = EBUSY; break; } } } if (err == 0) err = frrequest(&V_ipfmain, IPL_LOGIPF, SIOCDELFR, (caddr_t)&ipfrule_in_, V_ipfmain.ipf_active, 0); if (err) return err; return err; } -int ipfrule_add() +int ipfrule_add(void) { int err; err = ipfrule_add_out_(); if (err != 0) return err; err = ipfrule_add_in_(); if (err != 0) return err; return 0; } -int ipfrule_remove() +int ipfrule_remove(void) { int err; err = ipfrule_remove_out_(); if (err != 0) return err; err = ipfrule_remove_in_(); if (err != 0) return err; return 0; } #endif /* IPFILTER_COMPILED */ diff --git a/sys/netpfil/ipfilter/netinet/ip_scan.c b/sys/netpfil/ipfilter/netinet/ip_scan.c index fd3ad538915d..d6a443c5b77d 100644 --- a/sys/netpfil/ipfilter/netinet/ip_scan.c +++ b/sys/netpfil/ipfilter/netinet/ip_scan.c @@ -1,617 +1,598 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #if !defined(_KERNEL) # include # include # define _KERNEL # include # undef _KERNEL #else # include # if !defined(__SVR4) # include # endif #endif #include # include #ifdef __FreeBSD__ # include # include #else # include #endif #include #include #include #include #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_state.h" #include "netinet/ip_scan.h" /* END OF INCLUDES */ #if !defined(lint) static const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-2000 Darren Reed"; static const char rcsid[] = "@(#)$Id$"; #endif #ifdef IPFILTER_SCAN /* endif at bottom of file */ ipscan_t *ipf_scan_list = NULL, *ipf_scan_tail = NULL; ipscanstat_t ipf_scan_stat; # ifdef USE_MUTEXES ipfrwlock_t ipf_scan_rwlock; # endif # ifndef isalpha # define isalpha(x) (((x) >= 'A' && 'Z' >= (x)) || \ ((x) >= 'a' && 'z' >= (x))) # endif int ipf_scan_add(caddr_t); int ipf_scan_remove(caddr_t); struct ipscan *ipf_scan_lookup(char *); int ipf_scan_matchstr(sinfo_t *, char *, int); int ipf_scan_matchisc(ipscan_t *, ipstate_t *, int, int, int *); int ipf_scan_match(ipstate_t *); static int ipf_scan_inited = 0; int -ipf_scan_init() +ipf_scan_init(void) { RWLOCK_INIT(&ipf_scan_rwlock, "ip scan rwlock"); ipf_scan_inited = 1; return 0; } void ipf_scan_unload(ipf_main_softc_t *arg) { if (ipf_scan_inited == 1) { RW_DESTROY(&ipf_scan_rwlock); ipf_scan_inited = 0; } } int -ipf_scan_add(data) - caddr_t data; +ipf_scan_add(caddr_t data) { ipscan_t *i, *isc; int err; KMALLOC(isc, ipscan_t *); if (!isc) { ipf_interror = 90001; return ENOMEM; } err = copyinptr(data, isc, sizeof(*isc)); if (err) { KFREE(isc); return err; } WRITE_ENTER(&ipf_scan_rwlock); i = ipf_scan_lookup(isc->ipsc_tag); if (i != NULL) { RWLOCK_EXIT(&ipf_scan_rwlock); KFREE(isc); ipf_interror = 90002; return EEXIST; } if (ipf_scan_tail) { ipf_scan_tail->ipsc_next = isc; isc->ipsc_pnext = &ipf_scan_tail->ipsc_next; ipf_scan_tail = isc; } else { ipf_scan_list = isc; ipf_scan_tail = isc; isc->ipsc_pnext = &ipf_scan_list; } isc->ipsc_next = NULL; isc->ipsc_hits = 0; isc->ipsc_fref = 0; isc->ipsc_sref = 0; isc->ipsc_active = 0; ipf_scan_stat.iscs_entries++; RWLOCK_EXIT(&ipf_scan_rwlock); return 0; } int -ipf_scan_remove(data) - caddr_t data; +ipf_scan_remove(caddr_t data) { ipscan_t isc, *i; int err; err = copyinptr(data, &isc, sizeof(isc)); if (err) return err; WRITE_ENTER(&ipf_scan_rwlock); i = ipf_scan_lookup(isc.ipsc_tag); if (i == NULL) err = ENOENT; else { if (i->ipsc_fref) { RWLOCK_EXIT(&ipf_scan_rwlock); ipf_interror = 90003; return EBUSY; } *i->ipsc_pnext = i->ipsc_next; if (i->ipsc_next) i->ipsc_next->ipsc_pnext = i->ipsc_pnext; else { if (i->ipsc_pnext == &ipf_scan_list) ipf_scan_tail = NULL; else ipf_scan_tail = *(*i->ipsc_pnext)->ipsc_pnext; } ipf_scan_stat.iscs_entries--; KFREE(i); } RWLOCK_EXIT(&ipf_scan_rwlock); return err; } struct ipscan * -ipf_scan_lookup(tag) - char *tag; +ipf_scan_lookup(char *tag) { ipscan_t *i; for (i = ipf_scan_list; i; i = i->ipsc_next) if (!strcmp(i->ipsc_tag, tag)) return i; return NULL; } int -ipf_scan_attachfr(fr) - struct frentry *fr; +ipf_scan_attachfr(struct frentry *fr) { ipscan_t *i; if (fr->fr_isctag != -1) { READ_ENTER(&ipf_scan_rwlock); i = ipf_scan_lookup(fr->fr_isctag + fr->fr_names); if (i != NULL) { ATOMIC_INC32(i->ipsc_fref); } RWLOCK_EXIT(&ipf_scan_rwlock); if (i == NULL) { ipf_interror = 90004; return ENOENT; } fr->fr_isc = i; } return 0; } int -ipf_scan_attachis(is) - struct ipstate *is; +ipf_scan_attachis(struct ipstate *is) { frentry_t *fr; ipscan_t *i; READ_ENTER(&ipf_scan_rwlock); fr = is->is_rule; if (fr != NULL) { i = fr->fr_isc; if ((i != NULL) && (i != (ipscan_t *)-1)) { is->is_isc = i; ATOMIC_INC32(i->ipsc_sref); if (i->ipsc_clen) is->is_flags |= IS_SC_CLIENT; else is->is_flags |= IS_SC_MATCHC; if (i->ipsc_slen) is->is_flags |= IS_SC_SERVER; else is->is_flags |= IS_SC_MATCHS; } } RWLOCK_EXIT(&ipf_scan_rwlock); return 0; } int -ipf_scan_detachfr(fr) - struct frentry *fr; +ipf_scan_detachfr(struct frentry *fr) { ipscan_t *i; i = fr->fr_isc; if (i != NULL) { ATOMIC_DEC32(i->ipsc_fref); } return 0; } int ipf_scan_detachis(is) struct ipstate *is; { ipscan_t *i; READ_ENTER(&ipf_scan_rwlock); if ((i = is->is_isc) && (i != (ipscan_t *)-1)) { ATOMIC_DEC32(i->ipsc_sref); is->is_isc = NULL; is->is_flags &= ~(IS_SC_CLIENT|IS_SC_SERVER); } RWLOCK_EXIT(&ipf_scan_rwlock); return 0; } /* * 'string' compare for scanning */ int -ipf_scan_matchstr(sp, str, n) - sinfo_t *sp; - char *str; - int n; +ipf_scan_matchstr(sinfo_t *sp, char *str, int n) { char *s, *t, *up; int i = n; if (i > sp->s_len) i = sp->s_len; up = str; for (s = sp->s_txt, t = sp->s_msk; i; i--, s++, t++, up++) switch ((int)*t) { case '.' : if (*s != *up) return 1; break; case '?' : if (!ISALPHA(*up) || ((*s & 0x5f) != (*up & 0x5f))) return 1; break; case '*' : break; } return 0; } /* * Returns 3 if both server and client match, 2 if just server, * 1 if just client */ int -ipf_scan_matchisc(isc, is, cl, sl, maxm) - ipscan_t *isc; - ipstate_t *is; - int cl, sl, maxm[2]; +ipf_scan_matchisc(ipscan_t *isc, ipstate_t *is, int cl, int sl, int maxm[2]) { int i, j, k, n, ret = 0, flags; flags = is->is_flags; /* * If we've already matched more than what is on offer, then * assume we have a better match already and forget this one. */ if (maxm != NULL) { if (isc->ipsc_clen < maxm[0]) return 0; if (isc->ipsc_slen < maxm[1]) return 0; j = maxm[0]; k = maxm[1]; } else { j = 0; k = 0; } if (!isc->ipsc_clen) ret = 1; else if (((flags & (IS_SC_MATCHC|IS_SC_CLIENT)) == IS_SC_CLIENT) && cl && isc->ipsc_clen) { i = 0; n = MIN(cl, isc->ipsc_clen); if ((n > 0) && (!maxm || (n >= maxm[1]))) { if (!ipf_scan_matchstr(&isc->ipsc_cl, is->is_sbuf[0], n)) { i++; ret |= 1; if (n > j) j = n; } } } if (!isc->ipsc_slen) ret |= 2; else if (((flags & (IS_SC_MATCHS|IS_SC_SERVER)) == IS_SC_SERVER) && sl && isc->ipsc_slen) { i = 0; n = MIN(cl, isc->ipsc_slen); if ((n > 0) && (!maxm || (n >= maxm[1]))) { if (!ipf_scan_matchstr(&isc->ipsc_sl, is->is_sbuf[1], n)) { i++; ret |= 2; if (n > k) k = n; } } } if (maxm && (ret == 3)) { maxm[0] = j; maxm[1] = k; } return ret; } int -ipf_scan_match(is) - ipstate_t *is; +ipf_scan_match(ipstate_t *is) { int i, j, k, n, cl, sl, maxm[2]; ipscan_t *isc, *lm; tcpdata_t *t; for (cl = 0, n = is->is_smsk[0]; n & 1; n >>= 1) cl++; for (sl = 0, n = is->is_smsk[1]; n & 1; n >>= 1) sl++; j = 0; isc = is->is_isc; if (isc != NULL) { /* * Known object to scan for. */ i = ipf_scan_matchisc(isc, is, cl, sl, NULL); if (i & 1) { is->is_flags |= IS_SC_MATCHC; is->is_flags &= ~IS_SC_CLIENT; } else if (cl >= isc->ipsc_clen) is->is_flags &= ~IS_SC_CLIENT; if (i & 2) { is->is_flags |= IS_SC_MATCHS; is->is_flags &= ~IS_SC_SERVER; } else if (sl >= isc->ipsc_slen) is->is_flags &= ~IS_SC_SERVER; } else { i = 0; lm = NULL; maxm[0] = 0; maxm[1] = 0; for (k = 0, isc = ipf_scan_list; isc; isc = isc->ipsc_next) { i = ipf_scan_matchisc(isc, is, cl, sl, maxm); if (i) { /* * We only want to remember the best match * and the number of times we get a best * match. */ if ((j == 3) && (i < 3)) continue; if ((i == 3) && (j != 3)) k = 1; else k++; j = i; lm = isc; } } if (k == 1) isc = lm; if (isc == NULL) return 0; /* * No matches or partial matches, so reset the respective * search flag. */ if (!(j & 1)) is->is_flags &= ~IS_SC_CLIENT; if (!(j & 2)) is->is_flags &= ~IS_SC_SERVER; /* * If we found the best match, then set flags appropriately. */ if ((j == 3) && (k == 1)) { is->is_flags &= ~(IS_SC_SERVER|IS_SC_CLIENT); is->is_flags |= (IS_SC_MATCHS|IS_SC_MATCHC); } } /* * If the acknowledged side of a connection has moved past the data in * which we are interested, then reset respective flag. */ t = &is->is_tcp.ts_data[0]; if (t->td_end > is->is_s0[0] + 15) is->is_flags &= ~IS_SC_CLIENT; t = &is->is_tcp.ts_data[1]; if (t->td_end > is->is_s0[1] + 15) is->is_flags &= ~IS_SC_SERVER; /* * Matching complete ? */ j = ISC_A_NONE; if ((is->is_flags & IS_SC_MATCHALL) == IS_SC_MATCHALL) { j = isc->ipsc_action; ipf_scan_stat.iscs_acted++; } else if ((is->is_isc != NULL) && ((is->is_flags & IS_SC_MATCHALL) != IS_SC_MATCHALL) && !(is->is_flags & (IS_SC_CLIENT|IS_SC_SERVER))) { /* * Matching failed... */ j = isc->ipsc_else; ipf_scan_stat.iscs_else++; } switch (j) { case ISC_A_CLOSE : /* * If as a result of a successful match we are to * close a connection, change the "keep state" info. * to block packets and generate TCP RST's. */ is->is_pass &= ~FR_RETICMP; is->is_pass |= FR_RETRST; break; default : break; } return i; } /* * check if a packet matches what we're scanning for */ int -ipf_scan_packet(fin, is) - fr_info_t *fin; - ipstate_t *is; +ipf_scan_packet(fr_info_t *fin, ipstate_t *is) { int i, j, rv, dlen, off, thoff; u_32_t seq, s0; tcphdr_t *tcp; rv = !IP6_EQ(&fin->fin_fi.fi_src, &is->is_src); tcp = fin->fin_dp; seq = ntohl(tcp->th_seq); if (!is->is_s0[rv]) return 1; /* * check if this packet has more data that falls within the first * 16 bytes sent in either direction. */ s0 = is->is_s0[rv]; off = seq - s0; if ((off > 15) || (off < 0)) return 1; thoff = TCP_OFF(tcp) << 2; dlen = fin->fin_dlen - thoff; if (dlen <= 0) return 1; if (dlen > 16) dlen = 16; if (off + dlen > 16) dlen = 16 - off; j = 0xffff >> (16 - dlen); i = (0xffff & j) << off; #ifdef _KERNEL COPYDATA(*(mb_t **)fin->fin_mp, fin->fin_plen - fin->fin_dlen + thoff, dlen, (caddr_t)is->is_sbuf[rv] + off); #endif is->is_smsk[rv] |= i; for (j = 0, i = is->is_smsk[rv]; i & 1; i >>= 1) j++; if (j == 0) return 1; (void) ipf_scan_match(is); #if 0 /* * There is the potential here for plain text passwords to get * buffered and stored for some time... */ if (!(is->is_flags & IS_SC_CLIENT)) bzero(is->is_sbuf[0], sizeof(is->is_sbuf[0])); if (!(is->is_flags & IS_SC_SERVER)) bzero(is->is_sbuf[1], sizeof(is->is_sbuf[1])); #endif return 0; } int -ipf_scan_ioctl(data, cmd, mode, uid, ctx) - caddr_t data; - ioctlcmd_t cmd; - int mode, uid; - void *ctx; +ipf_scan_ioctl(caddr_t data, ioctlcmd_t cmd, int mode, int uid, void *ctx) { ipscanstat_t ipscs; int err = 0; switch (cmd) { case SIOCADSCA : err = ipf_scan_add(data); break; case SIOCRMSCA : err = ipf_scan_remove(data); break; case SIOCGSCST : bcopy((char *)&ipf_scan_stat, (char *)&ipscs, sizeof(ipscs)); ipscs.iscs_list = ipf_scan_list; err = BCOPYOUT(&ipscs, data, sizeof(ipscs)); if (err != 0) { ipf_interror = 90005; err = EFAULT; } break; default : err = EINVAL; break; } return err; } #endif /* IPFILTER_SCAN */ diff --git a/sys/netpfil/ipfilter/netinet/ip_state.c b/sys/netpfil/ipfilter/netinet/ip_state.c index 7918f34fe685..e342f5f83211 100644 --- a/sys/netpfil/ipfilter/netinet/ip_state.c +++ b/sys/netpfil/ipfilter/netinet/ip_state.c @@ -1,5394 +1,5282 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * Copyright 2008 Sun Microsystems. * * $Id$ */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #if defined(_KERNEL) && defined(__FreeBSD__) && \ !defined(KLD_MODULE) #include "opt_inet6.h" #endif #if !defined(_KERNEL) && !defined(__KERNEL__) # include # include # include # define _KERNEL # include # undef _KERNEL #endif #if defined(_KERNEL) && defined(__FreeBSD__) # include # include #else # include #endif #include # include #include #if defined(_KERNEL) # include # if !defined(__SVR4) # include # endif #endif #if defined(__SVR4) # include # include # ifdef _KERNEL # include # endif # include # include #endif #include #ifdef sun # include #endif #include #include #include #include # include #include #include #if !defined(_KERNEL) # include "ipf.h" #endif #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #include "netinet/ip_lookup.h" #include "netinet/ip_dstlist.h" #include "netinet/ip_sync.h" #ifdef USE_INET6 #include #endif #ifdef __FreeBSD__ # include # if defined(_KERNEL) && !defined(IPFILTER_LKM) # include # include # endif #endif /* END OF INCLUDES */ #if !defined(lint) static const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-2000 Darren Reed"; static const char rcsid[] = "@(#)$Id$"; #endif static ipftuneable_t ipf_state_tuneables[] = { { { (void *)offsetof(ipf_state_softc_t, ipf_state_max) }, "state_max", 1, 0x7fffffff, stsizeof(ipf_state_softc_t, ipf_state_max), 0, NULL, NULL }, { { (void *)offsetof(ipf_state_softc_t, ipf_state_size) }, "state_size", 1, 0x7fffffff, stsizeof(ipf_state_softc_t, ipf_state_size), 0, NULL, ipf_state_rehash }, { { (void *)offsetof(ipf_state_softc_t, ipf_state_lock) }, "state_lock", 0, 1, stsizeof(ipf_state_softc_t, ipf_state_lock), IPFT_RDONLY, NULL, NULL }, { { (void *)offsetof(ipf_state_softc_t, ipf_state_maxbucket) }, "state_maxbucket", 1, 0x7fffffff, stsizeof(ipf_state_softc_t, ipf_state_maxbucket), 0, NULL, NULL }, { { (void *)offsetof(ipf_state_softc_t, ipf_state_logging) }, "state_logging",0, 1, stsizeof(ipf_state_softc_t, ipf_state_logging), 0, NULL, NULL }, { { (void *)offsetof(ipf_state_softc_t, ipf_state_wm_high) }, "state_wm_high",2, 100, stsizeof(ipf_state_softc_t, ipf_state_wm_high), 0, NULL, NULL }, { { (void *)offsetof(ipf_state_softc_t, ipf_state_wm_low) }, "state_wm_low", 1, 99, stsizeof(ipf_state_softc_t, ipf_state_wm_low), 0, NULL, NULL }, { { (void *)offsetof(ipf_state_softc_t, ipf_state_wm_freq) }, "state_wm_freq",2, 999999, stsizeof(ipf_state_softc_t, ipf_state_wm_freq), 0, NULL, NULL }, { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL } }; #define SINCL(x) ATOMIC_INCL(softs->x) #define SBUMP(x) (softs->x)++ #define SBUMPD(x, y) do { (softs->x.y)++; DT(y); } while (0) #define SBUMPDX(x, y, z)do { (softs->x.y)++; DT(z); } while (0) #ifdef USE_INET6 static ipstate_t *ipf_checkicmp6matchingstate(fr_info_t *); #endif static int ipf_allowstateicmp(fr_info_t *, ipstate_t *, i6addr_t *); static ipstate_t *ipf_matchsrcdst(fr_info_t *, ipstate_t *, i6addr_t *, i6addr_t *, tcphdr_t *, u_32_t); static ipstate_t *ipf_checkicmpmatchingstate(fr_info_t *); static int ipf_state_flush_entry(ipf_main_softc_t *, void *); static ips_stat_t *ipf_state_stats(ipf_main_softc_t *); static int ipf_state_del(ipf_main_softc_t *, ipstate_t *, int); static int ipf_state_remove(ipf_main_softc_t *, caddr_t); static int ipf_state_match(ipstate_t *is1, ipstate_t *is2); static int ipf_state_matchaddresses(ipstate_t *is1, ipstate_t *is2); static int ipf_state_matchipv4addrs(ipstate_t *is1, ipstate_t *is2); static int ipf_state_matchipv6addrs(ipstate_t *is1, ipstate_t *is2); static int ipf_state_matchisps(ipstate_t *is1, ipstate_t *is2); static int ipf_state_matchports(udpinfo_t *is1, udpinfo_t *is2); static int ipf_state_matcharray(ipstate_t *, int *, u_long); static void ipf_ipsmove(ipf_state_softc_t *, ipstate_t *, u_int); static int ipf_state_tcp(ipf_main_softc_t *, ipf_state_softc_t *, fr_info_t *, tcphdr_t *, ipstate_t *); static int ipf_tcpoptions(ipf_state_softc_t *, fr_info_t *, tcphdr_t *, tcpdata_t *); static ipstate_t *ipf_state_clone(fr_info_t *, tcphdr_t *, ipstate_t *); static void ipf_fixinisn(fr_info_t *, ipstate_t *); static void ipf_fixoutisn(fr_info_t *, ipstate_t *); static void ipf_checknewisn(fr_info_t *, ipstate_t *); static int ipf_state_iter(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, ipfobj_t *); static int ipf_state_gettable(ipf_main_softc_t *, ipf_state_softc_t *, char *); static int ipf_state_tcpinwindow(struct fr_info *, struct tcpdata *, struct tcpdata *, tcphdr_t *, int); static int ipf_state_getent(ipf_main_softc_t *, ipf_state_softc_t *, caddr_t); static int ipf_state_putent(ipf_main_softc_t *, ipf_state_softc_t *, caddr_t); #define ONE_DAY IPF_TTLVAL(1 * 86400) /* 1 day */ #define FIVE_DAYS (5 * ONE_DAY) #define DOUBLE_HASH(x) (((x) + softs->ipf_state_seed[(x) % \ softs->ipf_state_size]) % softs->ipf_state_size) /* ------------------------------------------------------------------------ */ /* Function: ipf_state_main_load */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int -ipf_state_main_load() +ipf_state_main_load(void) { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_main_unload */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int -ipf_state_main_unload() +ipf_state_main_unload(void) { return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_soft_create */ /* Returns: void * - NULL = failure, else pointer to soft context */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Create a new state soft context structure and populate it with the list */ /* of tunables and other default settings. */ /* ------------------------------------------------------------------------ */ void * -ipf_state_soft_create(softc) - ipf_main_softc_t *softc; +ipf_state_soft_create(ipf_main_softc_t *softc) { ipf_state_softc_t *softs; KMALLOC(softs, ipf_state_softc_t *); if (softs == NULL) return NULL; bzero((char *)softs, sizeof(*softs)); softs->ipf_state_tune = ipf_tune_array_copy(softs, sizeof(ipf_state_tuneables), ipf_state_tuneables); if (softs->ipf_state_tune == NULL) { ipf_state_soft_destroy(softc, softs); return NULL; } if (ipf_tune_array_link(softc, softs->ipf_state_tune) == -1) { ipf_state_soft_destroy(softc, softs); return NULL; } #ifdef IPFILTER_LOG softs->ipf_state_logging = 1; #else softs->ipf_state_logging = 0; #endif softs->ipf_state_size = IPSTATE_SIZE, softs->ipf_state_maxbucket = 0; softs->ipf_state_wm_freq = IPF_TTLVAL(10); softs->ipf_state_max = IPSTATE_MAX; softs->ipf_state_wm_last = 0; softs->ipf_state_wm_high = 99; softs->ipf_state_wm_low = 90; softs->ipf_state_inited = 0; softs->ipf_state_lock = 0; softs->ipf_state_doflush = 0; return softs; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Undo only what we did in soft create: unlink and free the tunables and */ /* free the soft context structure itself. */ /* ------------------------------------------------------------------------ */ void -ipf_state_soft_destroy(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_state_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_state_softc_t *softs = arg; if (softs->ipf_state_tune != NULL) { ipf_tune_array_unlink(softc, softs->ipf_state_tune); KFREES(softs->ipf_state_tune, sizeof(ipf_state_tuneables)); softs->ipf_state_tune = NULL; } KFREE(softs); } static void * ipf_state_seed_alloc(u_int state_size, u_int state_max) { u_int i; u_long *state_seed; KMALLOCS(state_seed, u_long *, state_size * sizeof(*state_seed)); if (state_seed == NULL) return NULL; for (i = 0; i < state_size; i++) { /* * XXX - ipf_state_seed[X] should be a random number of sorts. */ #ifdef __FreeBSD__ state_seed[i] = arc4random(); #else state_seed[i] = ((u_long)state_seed + i) * state_size; state_seed[i] ^= 0xa5a55a5a; state_seed[i] *= (u_long)state_seed; state_seed[i] ^= 0x5a5aa5a5; state_seed[i] *= state_max; #endif } return state_seed; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_soft_init */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Initialise the state soft context structure so it is ready for use. */ /* This involves: */ /* - allocating a hash table and zero'ing it out */ /* - building a secondary table of seeds for double hashing to make it more */ /* difficult to attempt to attack the hash table itself (for DoS) */ /* - initialise all of the timeout queues, including a table for TCP, some */ /* pairs of query/response for UDP and other IP protocols (typically the */ /* reply queue has a shorter timeout than the query) */ /* ------------------------------------------------------------------------ */ int -ipf_state_soft_init(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_state_soft_init(ipf_main_softc_t *softc, void *arg) { ipf_state_softc_t *softs = arg; int i; KMALLOCS(softs->ipf_state_table, ipstate_t **, softs->ipf_state_size * sizeof(ipstate_t *)); if (softs->ipf_state_table == NULL) return -1; bzero((char *)softs->ipf_state_table, softs->ipf_state_size * sizeof(ipstate_t *)); softs->ipf_state_seed = ipf_state_seed_alloc(softs->ipf_state_size, softs->ipf_state_max); if (softs->ipf_state_seed == NULL) return -2; KMALLOCS(softs->ipf_state_stats.iss_bucketlen, u_int *, softs->ipf_state_size * sizeof(u_int)); if (softs->ipf_state_stats.iss_bucketlen == NULL) return -3; bzero((char *)softs->ipf_state_stats.iss_bucketlen, softs->ipf_state_size * sizeof(u_int)); if (softs->ipf_state_maxbucket == 0) { for (i = softs->ipf_state_size; i > 0; i >>= 1) softs->ipf_state_maxbucket++; softs->ipf_state_maxbucket *= 2; } ipf_sttab_init(softc, softs->ipf_state_tcptq); softs->ipf_state_stats.iss_tcptab = softs->ipf_state_tcptq; softs->ipf_state_tcptq[IPF_TCP_NSTATES - 1].ifq_next = &softs->ipf_state_udptq; IPFTQ_INIT(&softs->ipf_state_udptq, softc->ipf_udptimeout, "ipftq udp tab"); softs->ipf_state_udptq.ifq_next = &softs->ipf_state_udpacktq; IPFTQ_INIT(&softs->ipf_state_udpacktq, softc->ipf_udpacktimeout, "ipftq udpack tab"); softs->ipf_state_udpacktq.ifq_next = &softs->ipf_state_icmptq; IPFTQ_INIT(&softs->ipf_state_icmptq, softc->ipf_icmptimeout, "ipftq icmp tab"); softs->ipf_state_icmptq.ifq_next = &softs->ipf_state_icmpacktq; IPFTQ_INIT(&softs->ipf_state_icmpacktq, softc->ipf_icmpacktimeout, "ipftq icmpack tab"); softs->ipf_state_icmpacktq.ifq_next = &softs->ipf_state_iptq; IPFTQ_INIT(&softs->ipf_state_iptq, softc->ipf_iptimeout, "ipftq iptimeout tab"); softs->ipf_state_iptq.ifq_next = &softs->ipf_state_pending; IPFTQ_INIT(&softs->ipf_state_pending, IPF_HZ_DIVIDE, "ipftq pending"); softs->ipf_state_pending.ifq_next = &softs->ipf_state_deletetq; IPFTQ_INIT(&softs->ipf_state_deletetq, 1, "ipftq delete"); softs->ipf_state_deletetq.ifq_next = NULL; MUTEX_INIT(&softs->ipf_stinsert, "ipf state insert mutex"); softs->ipf_state_wm_last = softc->ipf_ticks; softs->ipf_state_inited = 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_soft_fini */ /* Returns: int - 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Release and destroy any resources acquired or initialised so that */ /* IPFilter can be unloaded or re-initialised. */ /* ------------------------------------------------------------------------ */ int -ipf_state_soft_fini(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_state_soft_fini(ipf_main_softc_t *softc, void *arg) { ipf_state_softc_t *softs = arg; ipftq_t *ifq, *ifqnext; ipstate_t *is; while ((is = softs->ipf_state_list) != NULL) ipf_state_del(softc, is, ISL_UNLOAD); /* * Proxy timeout queues are not cleaned here because although they * exist on the state list, appr_unload is called after * ipf_state_unload and the proxies actually are responsible for them * being created. Should the proxy timeouts have their own list? * There's no real justification as this is the only complication. */ for (ifq = softs->ipf_state_usertq; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; if (ipf_deletetimeoutqueue(ifq) == 0) ipf_freetimeoutqueue(softc, ifq); } softs->ipf_state_stats.iss_inuse = 0; softs->ipf_state_stats.iss_active = 0; if (softs->ipf_state_inited == 1) { softs->ipf_state_inited = 0; ipf_sttab_destroy(softs->ipf_state_tcptq); MUTEX_DESTROY(&softs->ipf_state_udptq.ifq_lock); MUTEX_DESTROY(&softs->ipf_state_icmptq.ifq_lock); MUTEX_DESTROY(&softs->ipf_state_udpacktq.ifq_lock); MUTEX_DESTROY(&softs->ipf_state_icmpacktq.ifq_lock); MUTEX_DESTROY(&softs->ipf_state_iptq.ifq_lock); MUTEX_DESTROY(&softs->ipf_state_deletetq.ifq_lock); MUTEX_DESTROY(&softs->ipf_state_pending.ifq_lock); MUTEX_DESTROY(&softs->ipf_stinsert); } if (softs->ipf_state_table != NULL) { KFREES(softs->ipf_state_table, softs->ipf_state_size * sizeof(*softs->ipf_state_table)); softs->ipf_state_table = NULL; } if (softs->ipf_state_seed != NULL) { KFREES(softs->ipf_state_seed, softs->ipf_state_size * sizeof(*softs->ipf_state_seed)); softs->ipf_state_seed = NULL; } if (softs->ipf_state_stats.iss_bucketlen != NULL) { KFREES(softs->ipf_state_stats.iss_bucketlen, softs->ipf_state_size * sizeof(u_int)); softs->ipf_state_stats.iss_bucketlen = NULL; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_setlock */ /* Returns: Nil */ /* Parameters: arg(I) - pointer to local context to use */ /* tmp(I) - new value for lock */ /* */ /* Stub function that allows for external manipulation of ipf_state_lock */ /* ------------------------------------------------------------------------ */ void -ipf_state_setlock(arg, tmp) - void *arg; - int tmp; +ipf_state_setlock(void *arg, int tmp) { ipf_state_softc_t *softs = arg; softs->ipf_state_lock = tmp; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_stats */ /* Returns: ips_state_t* - pointer to state stats structure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Put all the current numbers and pointers into a single struct and return */ /* a pointer to it. */ /* ------------------------------------------------------------------------ */ static ips_stat_t * -ipf_state_stats(softc) - ipf_main_softc_t *softc; +ipf_state_stats(ipf_main_softc_t *softc) { ipf_state_softc_t *softs = softc->ipf_state_soft; ips_stat_t *issp = &softs->ipf_state_stats; issp->iss_state_size = softs->ipf_state_size; issp->iss_state_max = softs->ipf_state_max; issp->iss_table = softs->ipf_state_table; issp->iss_list = softs->ipf_state_list; issp->iss_ticks = softc->ipf_ticks; #ifdef IPFILTER_LOGGING issp->iss_log_ok = ipf_log_logok(softc, IPF_LOGSTATE); issp->iss_log_fail = ipf_log_failures(softc, IPF_LOGSTATE); #else issp->iss_log_ok = 0; issp->iss_log_fail = 0; #endif return issp; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_remove */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to state structure to delete from table */ /* */ /* Search for a state structure that matches the one passed, according to */ /* the IP addresses and other protocol specific information. */ /* ------------------------------------------------------------------------ */ static int -ipf_state_remove(softc, data) - ipf_main_softc_t *softc; - caddr_t data; +ipf_state_remove(ipf_main_softc_t *softc, caddr_t data) { ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *sp, st; int error; sp = &st; error = ipf_inobj(softc, data, NULL, &st, IPFOBJ_IPSTATE); if (error) return EFAULT; WRITE_ENTER(&softc->ipf_state); for (sp = softs->ipf_state_list; sp; sp = sp->is_next) if ((sp->is_p == st.is_p) && (sp->is_v == st.is_v) && !bcmp((caddr_t)&sp->is_src, (caddr_t)&st.is_src, sizeof(st.is_src)) && !bcmp((caddr_t)&sp->is_dst, (caddr_t)&st.is_dst, sizeof(st.is_dst)) && !bcmp((caddr_t)&sp->is_ps, (caddr_t)&st.is_ps, sizeof(st.is_ps))) { ipf_state_del(softc, sp, ISL_REMOVE); RWLOCK_EXIT(&softc->ipf_state); return 0; } RWLOCK_EXIT(&softc->ipf_state); IPFERROR(100001); return ESRCH; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_ioctl */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to ioctl data */ /* cmd(I) - ioctl command integer */ /* mode(I) - file mode bits used with open */ /* uid(I) - uid of process making the ioctl call */ /* ctx(I) - pointer specific to context of the call */ /* */ /* Processes an ioctl call made to operate on the IP Filter state device. */ /* ------------------------------------------------------------------------ */ int -ipf_state_ioctl(softc, data, cmd, mode, uid, ctx) - ipf_main_softc_t *softc; - caddr_t data; - ioctlcmd_t cmd; - int mode, uid; - void *ctx; +ipf_state_ioctl(ipf_main_softc_t *softc, caddr_t data, ioctlcmd_t cmd, + int mode, int uid, void *ctx) { ipf_state_softc_t *softs = softc->ipf_state_soft; int arg, ret, error = 0; SPL_INT(s); switch (cmd) { /* * Delete an entry from the state table. */ case SIOCDELST : error = ipf_state_remove(softc, data); break; /* * Flush the state table */ case SIOCIPFFL : error = BCOPYIN(data, &arg, sizeof(arg)); if (error != 0) { IPFERROR(100002); error = EFAULT; } else { WRITE_ENTER(&softc->ipf_state); ret = ipf_state_flush(softc, arg, 4); RWLOCK_EXIT(&softc->ipf_state); error = BCOPYOUT(&ret, data, sizeof(ret)); if (error != 0) { IPFERROR(100003); error = EFAULT; } } break; #ifdef USE_INET6 case SIOCIPFL6 : error = BCOPYIN(data, &arg, sizeof(arg)); if (error != 0) { IPFERROR(100004); error = EFAULT; } else { WRITE_ENTER(&softc->ipf_state); ret = ipf_state_flush(softc, arg, 6); RWLOCK_EXIT(&softc->ipf_state); error = BCOPYOUT(&ret, data, sizeof(ret)); if (error != 0) { IPFERROR(100005); error = EFAULT; } } break; #endif case SIOCMATCHFLUSH : WRITE_ENTER(&softc->ipf_state); error = ipf_state_matchflush(softc, data); RWLOCK_EXIT(&softc->ipf_state); break; #ifdef IPFILTER_LOG /* * Flush the state log. */ case SIOCIPFFB : if (!(mode & FWRITE)) { IPFERROR(100008); error = EPERM; } else { int tmp; tmp = ipf_log_clear(softc, IPL_LOGSTATE); error = BCOPYOUT(&tmp, data, sizeof(tmp)); if (error != 0) { IPFERROR(100009); error = EFAULT; } } break; /* * Turn logging of state information on/off. */ case SIOCSETLG : if (!(mode & FWRITE)) { IPFERROR(100010); error = EPERM; } else { error = BCOPYIN(data, &softs->ipf_state_logging, sizeof(softs->ipf_state_logging)); if (error != 0) { IPFERROR(100011); error = EFAULT; } } break; /* * Return the current state of logging. */ case SIOCGETLG : error = BCOPYOUT(&softs->ipf_state_logging, data, sizeof(softs->ipf_state_logging)); if (error != 0) { IPFERROR(100012); error = EFAULT; } break; /* * Return the number of bytes currently waiting to be read. */ case FIONREAD : arg = ipf_log_bytesused(softc, IPL_LOGSTATE); error = BCOPYOUT(&arg, data, sizeof(arg)); if (error != 0) { IPFERROR(100013); error = EFAULT; } break; #endif /* * Get the current state statistics. */ case SIOCGETFS : error = ipf_outobj(softc, data, ipf_state_stats(softc), IPFOBJ_STATESTAT); break; /* * Lock/Unlock the state table. (Locking prevents any changes, which * means no packets match). */ case SIOCSTLCK : if (!(mode & FWRITE)) { IPFERROR(100014); error = EPERM; } else { error = ipf_lock(data, &softs->ipf_state_lock); } break; /* * Add an entry to the current state table. */ case SIOCSTPUT : if (!softs->ipf_state_lock || !(mode &FWRITE)) { IPFERROR(100015); error = EACCES; break; } error = ipf_state_putent(softc, softs, data); break; /* * Get a state table entry. */ case SIOCSTGET : if (!softs->ipf_state_lock) { IPFERROR(100016); error = EACCES; break; } error = ipf_state_getent(softc, softs, data); break; /* * Return a copy of the hash table bucket lengths */ case SIOCSTAT1 : error = BCOPYOUT(softs->ipf_state_stats.iss_bucketlen, data, softs->ipf_state_size * sizeof(u_int)); if (error != 0) { IPFERROR(100017); error = EFAULT; } break; case SIOCGENITER : { ipftoken_t *token; ipfgeniter_t iter; ipfobj_t obj; error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); if (error != 0) break; SPL_SCHED(s); token = ipf_token_find(softc, IPFGENITER_STATE, uid, ctx); if (token != NULL) { error = ipf_state_iter(softc, token, &iter, &obj); WRITE_ENTER(&softc->ipf_tokens); ipf_token_deref(softc, token); RWLOCK_EXIT(&softc->ipf_tokens); } else { IPFERROR(100018); error = ESRCH; } SPL_X(s); break; } case SIOCGTABL : error = ipf_state_gettable(softc, softs, data); break; case SIOCIPFDELTOK : error = BCOPYIN(data, &arg, sizeof(arg)); if (error != 0) { IPFERROR(100019); error = EFAULT; } else { SPL_SCHED(s); error = ipf_token_del(softc, arg, uid, ctx); SPL_X(s); } break; case SIOCGTQTAB : error = ipf_outobj(softc, data, softs->ipf_state_tcptq, IPFOBJ_STATETQTAB); break; default : IPFERROR(100020); error = EINVAL; break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_getent */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softs(I) - pointer to state context structure */ /* data(I) - pointer to state structure to retrieve from table*/ /* */ /* Copy out state information from the kernel to a user space process. If */ /* there is a filter rule associated with the state entry, copy that out */ /* as well. The entry to copy out is taken from the value of "ips_next" in */ /* the struct passed in and if not null and not found in the list of current*/ /* state entries, the retrieval fails. */ /* ------------------------------------------------------------------------ */ static int -ipf_state_getent(softc, softs, data) - ipf_main_softc_t *softc; - ipf_state_softc_t *softs; - caddr_t data; +ipf_state_getent(ipf_main_softc_t *softc, ipf_state_softc_t *softs, + caddr_t data) { ipstate_t *is, *isn; ipstate_save_t ips; int error; error = ipf_inobj(softc, data, NULL, &ips, IPFOBJ_STATESAVE); if (error) return EFAULT; READ_ENTER(&softc->ipf_state); isn = ips.ips_next; if (isn == NULL) { isn = softs->ipf_state_list; if (isn == NULL) { if (ips.ips_next == NULL) { RWLOCK_EXIT(&softc->ipf_state); IPFERROR(100021); return ENOENT; } return 0; } } else { /* * Make sure the pointer we're copying from exists in the * current list of entries. Security precaution to prevent * copying of random kernel data. */ for (is = softs->ipf_state_list; is; is = is->is_next) if (is == isn) break; if (!is) { RWLOCK_EXIT(&softc->ipf_state); IPFERROR(100022); return ESRCH; } } ips.ips_next = isn->is_next; bcopy((char *)isn, (char *)&ips.ips_is, sizeof(ips.ips_is)); ips.ips_rule = isn->is_rule; if (isn->is_rule != NULL) bcopy((char *)isn->is_rule, (char *)&ips.ips_fr, sizeof(ips.ips_fr)); RWLOCK_EXIT(&softc->ipf_state); error = ipf_outobj(softc, data, &ips, IPFOBJ_STATESAVE); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_putent */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softs(I) - pointer to state context structure */ /* data(I) - pointer to state information struct */ /* */ /* This function implements the SIOCSTPUT ioctl: insert a state entry into */ /* the state table. If the state info. includes a pointer to a filter rule */ /* then also add in an orphaned rule (will not show up in any "ipfstat -io" */ /* output. */ /* ------------------------------------------------------------------------ */ int -ipf_state_putent(softc, softs, data) - ipf_main_softc_t *softc; - ipf_state_softc_t *softs; - caddr_t data; +ipf_state_putent(ipf_main_softc_t *softc, ipf_state_softc_t *softs, + caddr_t data) { ipstate_t *is, *isn; ipstate_save_t ips; int error, out, i; frentry_t *fr; char *name; error = ipf_inobj(softc, data, NULL, &ips, IPFOBJ_STATESAVE); if (error != 0) return error; KMALLOC(isn, ipstate_t *); if (isn == NULL) { IPFERROR(100023); return ENOMEM; } bcopy((char *)&ips.ips_is, (char *)isn, sizeof(*isn)); bzero((char *)isn, offsetof(struct ipstate, is_pkts)); isn->is_sti.tqe_pnext = NULL; isn->is_sti.tqe_next = NULL; isn->is_sti.tqe_ifq = NULL; isn->is_sti.tqe_parent = isn; isn->is_ifp[0] = NULL; isn->is_ifp[1] = NULL; isn->is_ifp[2] = NULL; isn->is_ifp[3] = NULL; isn->is_sync = NULL; fr = ips.ips_rule; if (fr == NULL) { int inserr; READ_ENTER(&softc->ipf_state); inserr = ipf_state_insert(softc, isn, 0); MUTEX_EXIT(&isn->is_lock); RWLOCK_EXIT(&softc->ipf_state); return inserr; } if (isn->is_flags & SI_NEWFR) { KMALLOC(fr, frentry_t *); if (fr == NULL) { KFREE(isn); IPFERROR(100024); return ENOMEM; } bcopy((char *)&ips.ips_fr, (char *)fr, sizeof(*fr)); out = fr->fr_flags & FR_OUTQUE ? 1 : 0; isn->is_rule = fr; ips.ips_is.is_rule = fr; MUTEX_NUKE(&fr->fr_lock); MUTEX_INIT(&fr->fr_lock, "state filter rule lock"); /* * Look up all the interface names in the rule. */ for (i = 0; i < FR_NUM(fr->fr_ifnames); i++) { if (fr->fr_ifnames[i] == -1) { fr->fr_ifas[i] = NULL; continue; } name = FR_NAME(fr, fr_ifnames[i]); fr->fr_ifas[i] = ipf_resolvenic(softc, name, fr->fr_family); } for (i = 0; i < FR_NUM(isn->is_ifname); i++) { name = isn->is_ifname[i]; isn->is_ifp[i] = ipf_resolvenic(softc, name, isn->is_v); } fr->fr_ref = 0; fr->fr_dsize = 0; fr->fr_data = NULL; fr->fr_type = FR_T_NONE; (void) ipf_resolvedest(softc, fr->fr_names, &fr->fr_tifs[0], fr->fr_family); (void) ipf_resolvedest(softc, fr->fr_names, &fr->fr_tifs[1], fr->fr_family); (void) ipf_resolvedest(softc, fr->fr_names, &fr->fr_dif, fr->fr_family); /* * send a copy back to userland of what we ended up * to allow for verification. */ error = ipf_outobj(softc, data, &ips, IPFOBJ_STATESAVE); if (error != 0) { KFREE(isn); MUTEX_DESTROY(&fr->fr_lock); KFREE(fr); IPFERROR(100025); return EFAULT; } READ_ENTER(&softc->ipf_state); error = ipf_state_insert(softc, isn, 0); MUTEX_EXIT(&isn->is_lock); RWLOCK_EXIT(&softc->ipf_state); } else { READ_ENTER(&softc->ipf_state); for (is = softs->ipf_state_list; is; is = is->is_next) if (is->is_rule == fr) { error = ipf_state_insert(softc, isn, 0); MUTEX_EXIT(&isn->is_lock); break; } if (is == NULL) { KFREE(isn); isn = NULL; } RWLOCK_EXIT(&softc->ipf_state); if (isn == NULL) { IPFERROR(100033); error = ESRCH; } } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_insert */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* Parameters: is(I) - pointer to state structure */ /* rev(I) - flag indicating direction of packet */ /* */ /* Inserts a state structure into the hash table (for lookups) and the list */ /* of state entries (for enumeration). Resolves all of the interface names */ /* to pointers and adjusts running stats for the hash table as appropriate. */ /* */ /* This function can fail if the filter rule has had a population policy of */ /* IP addresses used with stateful filtering assigned to it. */ /* */ /* Locking: it is assumed that some kind of lock on ipf_state is held. */ /* Exits with is_lock initialised and held - *EVEN IF ERROR*. */ /* ------------------------------------------------------------------------ */ int -ipf_state_insert(softc, is, rev) - ipf_main_softc_t *softc; - ipstate_t *is; - int rev; +ipf_state_insert(ipf_main_softc_t *softc, ipstate_t *is, int rev) { ipf_state_softc_t *softs = softc->ipf_state_soft; frentry_t *fr; u_int hv; int i; /* * Look up all the interface names in the state entry. */ for (i = 0; i < FR_NUM(is->is_ifp); i++) { if (is->is_ifp[i] != NULL) continue; is->is_ifp[i] = ipf_resolvenic(softc, is->is_ifname[i], is->is_v); } /* * If we could trust is_hv, then the modulus would not be needed, * but when running with IPFILTER_SYNC, this stops bad values. */ hv = is->is_hv % softs->ipf_state_size; /* TRACE is, hv */ is->is_hv = hv; /* * We need to get both of these locks...the first because it is * possible that once the insert is complete another packet might * come along, match the entry and want to update it. */ MUTEX_INIT(&is->is_lock, "ipf state entry"); MUTEX_ENTER(&is->is_lock); MUTEX_ENTER(&softs->ipf_stinsert); fr = is->is_rule; if (fr != NULL) { if ((fr->fr_srctrack.ht_max_nodes != 0) && (ipf_ht_node_add(softc, &fr->fr_srctrack, is->is_family, &is->is_src) == -1)) { SBUMPD(ipf_state_stats, iss_max_track); MUTEX_EXIT(&softs->ipf_stinsert); return -1; } MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); fr->fr_statecnt++; } if (is->is_flags & (SI_WILDP|SI_WILDA)) { DT(iss_wild_plus_one); SINCL(ipf_state_stats.iss_wild); } SBUMP(ipf_state_stats.iss_proto[is->is_p]); SBUMP(ipf_state_stats.iss_active_proto[is->is_p]); /* * add into list table. */ if (softs->ipf_state_list != NULL) softs->ipf_state_list->is_pnext = &is->is_next; is->is_pnext = &softs->ipf_state_list; is->is_next = softs->ipf_state_list; softs->ipf_state_list = is; if (softs->ipf_state_table[hv] != NULL) softs->ipf_state_table[hv]->is_phnext = &is->is_hnext; else softs->ipf_state_stats.iss_inuse++; is->is_phnext = softs->ipf_state_table + hv; is->is_hnext = softs->ipf_state_table[hv]; softs->ipf_state_table[hv] = is; softs->ipf_state_stats.iss_bucketlen[hv]++; softs->ipf_state_stats.iss_active++; MUTEX_EXIT(&softs->ipf_stinsert); ipf_state_setqueue(softc, is, rev); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_matchipv4addrs */ /* Returns: int - 2 addresses match (strong match), 1 reverse match, */ /* 0 no match */ /* Parameters: is1, is2 pointers to states we are checking */ /* */ /* Function matches IPv4 addresses it returns strong match for ICMP proto */ /* even there is only reverse match */ /* ------------------------------------------------------------------------ */ static int -ipf_state_matchipv4addrs(is1, is2) - ipstate_t *is1, *is2; +ipf_state_matchipv4addrs(ipstate_t *is1, ipstate_t *is2) { int rv; if (is1->is_saddr == is2->is_saddr && is1->is_daddr == is2->is_daddr) rv = 2; else if (is1->is_saddr == is2->is_daddr && is1->is_daddr == is2->is_saddr) { /* force strong match for ICMP protocol */ rv = (is1->is_p == IPPROTO_ICMP) ? 2 : 1; } else rv = 0; return (rv); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_matchipv6addrs */ /* Returns: int - 2 addresses match (strong match), 1 reverse match, */ /* 0 no match */ /* Parameters: is1, is2 pointers to states we are checking */ /* */ /* Function matches IPv6 addresses it returns strong match for ICMP proto */ /* even there is only reverse match */ /* ------------------------------------------------------------------------ */ static int -ipf_state_matchipv6addrs(is1, is2) - ipstate_t *is1, *is2; +ipf_state_matchipv6addrs(ipstate_t *is1, ipstate_t *is2) { int rv; if (IP6_EQ(&is1->is_src, &is2->is_src) && IP6_EQ(&is1->is_dst, &is2->is_dst)) rv = 2; else if (IP6_EQ(&is1->is_src, &is2->is_dst) && IP6_EQ(&is1->is_dst, &is2->is_src)) { /* force strong match for ICMPv6 protocol */ rv = (is1->is_p == IPPROTO_ICMPV6) ? 2 : 1; } else rv = 0; return (rv); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_matchaddresses */ /* Returns: int - 2 addresses match, 1 reverse match, zero no match */ /* Parameters: is1, is2 pointers to states we are checking */ /* */ /* function retruns true if two pairs of addresses belong to single */ /* connection. suppose there are two endpoints: */ /* endpoint1 1.1.1.1 */ /* endpoint2 1.1.1.2 */ /* */ /* the state is established by packet flying from .1 to .2 so we see: */ /* is1->src = 1.1.1.1 */ /* is1->dst = 1.1.1.2 */ /* now endpoint 1.1.1.2 sends answer */ /* retreives is1 record created by first packat and compares it with is2 */ /* temporal record, is2 is initialized as follows: */ /* is2->src = 1.1.1.2 */ /* is2->dst = 1.1.1.1 */ /* in this case 1 will be returned */ /* */ /* the ipf_matchaddresses() assumes those two records to be same. of course */ /* the ipf_matchaddresses() also assume records are same in case you pass */ /* identical arguments (i.e. ipf_matchaddress(is1, is1) would return 2 */ /* ------------------------------------------------------------------------ */ static int -ipf_state_matchaddresses(is1, is2) - ipstate_t *is1, *is2; +ipf_state_matchaddresses(ipstate_t *is1, ipstate_t *is2) { int rv; if (is1->is_v == 4) { rv = ipf_state_matchipv4addrs(is1, is2); } else { rv = ipf_state_matchipv6addrs(is1, is2); } return (rv); } /* ------------------------------------------------------------------------ */ /* Function: ipf_matchports */ /* Returns: int - 2 match, 1 rverse match, 0 no match */ /* Parameters: ppairs1, ppairs - src, dst ports we want to match */ /* */ /* performs the same match for isps members as for addresses */ /* ------------------------------------------------------------------------ */ static int -ipf_state_matchports(ppairs1, ppairs2) - udpinfo_t *ppairs1, *ppairs2; +ipf_state_matchports(udpinfo_t *ppairs1, udpinfo_t *ppairs2) { int rv; if (ppairs1->us_sport == ppairs2->us_sport && ppairs1->us_dport == ppairs2->us_dport) rv = 2; else if (ppairs1->us_sport == ppairs2->us_dport && ppairs1->us_dport == ppairs2->us_sport) rv = 1; else rv = 0; return (rv); } /* ------------------------------------------------------------------------ */ /* Function: ipf_matchisps */ /* Returns: int - nonzero if isps members match, 0 nomatch */ /* Parameters: is1, is2 - states we want to match */ /* */ /* performs the same match for isps members as for addresses */ /* ------------------------------------------------------------------------ */ static int -ipf_state_matchisps(is1, is2) - ipstate_t *is1, *is2; +ipf_state_matchisps(ipstate_t *is1, ipstate_t *is2) { int rv; if (is1->is_p == is2->is_p) { switch (is1->is_p) { case IPPROTO_TCP : case IPPROTO_UDP : case IPPROTO_GRE : /* greinfo_t can be also interprted as port pair */ rv = ipf_state_matchports(&is1->is_ps.is_us, &is2->is_ps.is_us); break; case IPPROTO_ICMP : case IPPROTO_ICMPV6 : /* force strong match for ICMP datagram. */ if (bcmp(&is1->is_ps, &is2->is_ps, sizeof(icmpinfo_t)) == 0) { rv = 2; } else { rv = 0; } break; default: rv = 0; } } else { rv = 0; } return (rv); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_match */ /* Returns: int - nonzero match, zero no match */ /* Parameters: is1, is2 - states we want to match */ /* */ /* ------------------------------------------------------------------------ */ static int -ipf_state_match(is1, is2) - ipstate_t *is1, *is2; +ipf_state_match(ipstate_t *is1, ipstate_t *is2) { int rv; int amatch; int pomatch; if (bcmp(&is1->is_pass, &is2->is_pass, offsetof(struct ipstate, is_authmsk) - offsetof(struct ipstate, is_pass)) == 0) { pomatch = ipf_state_matchisps(is1, is2); amatch = ipf_state_matchaddresses(is1, is2); rv = (amatch != 0) && (amatch == pomatch); } else { rv = 0; } return (rv); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_add */ /* Returns: ipstate_t - 0 = success */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fin(I) - pointer to packet information */ /* stsave(O) - pointer to place to save pointer to created */ /* state structure. */ /* flags(I) - flags to use when creating the structure */ /* */ /* Creates a new IP state structure from the packet information collected. */ /* Inserts it into the state table and appends to the bottom of the active */ /* list. If the capacity of the table has reached the maximum allowed then */ /* the call will fail and a flush is scheduled for the next timeout call. */ /* */ /* NOTE: The use of stsave to point to nat_state will result in memory */ /* corruption. It should only be used to point to objects that will */ /* either outlive this (not expired) or will deref the ip_state_t */ /* when they are deleted. */ /* ------------------------------------------------------------------------ */ int -ipf_state_add(softc, fin, stsave, flags) - ipf_main_softc_t *softc; - fr_info_t *fin; - ipstate_t **stsave; - u_int flags; +ipf_state_add(ipf_main_softc_t *softc, fr_info_t *fin, ipstate_t **stsave, + u_int flags) { ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *is, ips; struct icmp *ic; u_int pass, hv; frentry_t *fr; tcphdr_t *tcp; frdest_t *fdp; int out; /* * If a locally created packet is trying to egress but it * does not match because of this lock, it is likely that * the policy will block it and return network unreachable further * up the stack. To mitigate this error, EAGAIN is returned instead, * telling the IP stack to try sending this packet again later. */ if (softs->ipf_state_lock) { SBUMPD(ipf_state_stats, iss_add_locked); fin->fin_error = EAGAIN; return -1; } if (fin->fin_flx & (FI_SHORT|FI_STATE|FI_FRAGBODY|FI_BAD)) { SBUMPD(ipf_state_stats, iss_add_bad); return -1; } if ((fin->fin_flx & FI_OOW) && !(fin->fin_tcpf & TH_SYN)) { SBUMPD(ipf_state_stats, iss_add_oow); return -1; } if ((softs->ipf_state_stats.iss_active * 100 / softs->ipf_state_max) > softs->ipf_state_wm_high) { softs->ipf_state_doflush = 1; } /* * If a "keep state" rule has reached the maximum number of references * to it, then schedule an automatic flush in case we can clear out * some "dead old wood". Note that because the lock isn't held on * fr it is possible that we could overflow. The cost of overflowing * is being ignored here as the number by which it can overflow is * a product of the number of simultaneous threads that could be * executing in here, so a limit of 100 won't result in 200, but could * result in 101 or 102. */ fr = fin->fin_fr; if (fr != NULL) { if ((softs->ipf_state_stats.iss_active >= softs->ipf_state_max) && (fr->fr_statemax == 0)) { SBUMPD(ipf_state_stats, iss_max); return 1; } if ((fr->fr_statemax != 0) && (fr->fr_statecnt >= fr->fr_statemax)) { SBUMPD(ipf_state_stats, iss_max_ref); return 2; } } is = &ips; if (fr == NULL) { pass = softc->ipf_flags; is->is_tag = FR_NOLOGTAG; } else { pass = fr->fr_flags; } ic = NULL; tcp = NULL; out = fin->fin_out; bzero((char *)is, sizeof(*is)); is->is_die = 1 + softc->ipf_ticks; /* * We want to check everything that is a property of this packet, * but we don't (automatically) care about its fragment status as * this may change. */ is->is_pass = pass; is->is_v = fin->fin_v; is->is_sec = fin->fin_secmsk; is->is_secmsk = 0xffff; is->is_auth = fin->fin_auth; is->is_authmsk = 0xffff; is->is_family = fin->fin_family; is->is_opt[0] = fin->fin_optmsk; is->is_optmsk[0] = 0xffffffff; if (is->is_v == 6) { is->is_opt[0] &= ~0x8; is->is_optmsk[0] &= ~0x8; } /* * Copy and calculate... */ hv = (is->is_p = fin->fin_fi.fi_p); is->is_src = fin->fin_fi.fi_src; hv += is->is_saddr; is->is_dst = fin->fin_fi.fi_dst; hv += is->is_daddr; #ifdef USE_INET6 if (fin->fin_v == 6) { /* * For ICMPv6, we check to see if the destination address is * a multicast address. If it is, do not include it in the * calculation of the hash because the correct reply will come * back from a real address, not a multicast address. */ if ((is->is_p == IPPROTO_ICMPV6) && IN6_IS_ADDR_MULTICAST(&is->is_dst.in6)) { /* * So you can do keep state with neighbour discovery. * * Here we could use the address from the neighbour * solicit message to put in the state structure and * we could use that without a wildcard flag too... */ flags |= SI_W_DADDR; hv -= is->is_daddr; } else { hv += is->is_dst.i6[1]; hv += is->is_dst.i6[2]; hv += is->is_dst.i6[3]; } hv += is->is_src.i6[1]; hv += is->is_src.i6[2]; hv += is->is_src.i6[3]; } #endif if ((fin->fin_v == 4) && (fin->fin_flx & (FI_MULTICAST|FI_BROADCAST|FI_MBCAST))) { flags |= SI_W_DADDR; hv -= is->is_daddr; } switch (is->is_p) { #ifdef USE_INET6 case IPPROTO_ICMPV6 : ic = fin->fin_dp; switch (ic->icmp_type) { case ICMP6_ECHO_REQUEST : hv += (is->is_icmp.ici_id = ic->icmp_id); /*FALLTHROUGH*/ case ICMP6_MEMBERSHIP_QUERY : case ND_ROUTER_SOLICIT : case ND_NEIGHBOR_SOLICIT : case ICMP6_NI_QUERY : is->is_icmp.ici_type = ic->icmp_type; break; default : SBUMPD(ipf_state_stats, iss_icmp6_notquery); return -2; } break; #endif case IPPROTO_ICMP : ic = fin->fin_dp; switch (ic->icmp_type) { case ICMP_ECHO : case ICMP_TSTAMP : case ICMP_IREQ : case ICMP_MASKREQ : is->is_icmp.ici_type = ic->icmp_type; hv += (is->is_icmp.ici_id = ic->icmp_id); break; default : SBUMPD(ipf_state_stats, iss_icmp_notquery); return -3; } break; #if 0 case IPPROTO_GRE : gre = fin->fin_dp; is->is_gre.gs_flags = gre->gr_flags; is->is_gre.gs_ptype = gre->gr_ptype; if (GRE_REV(is->is_gre.gs_flags) == 1) { is->is_call[0] = fin->fin_data[0]; is->is_call[1] = fin->fin_data[1]; } break; #endif case IPPROTO_TCP : tcp = fin->fin_dp; if (tcp->th_flags & TH_RST) { SBUMPD(ipf_state_stats, iss_tcp_rstadd); return -4; } /* TRACE is, flags, hv */ /* * The endian of the ports doesn't matter, but the ack and * sequence numbers do as we do mathematics on them later. */ is->is_sport = htons(fin->fin_data[0]); is->is_dport = htons(fin->fin_data[1]); if ((flags & (SI_W_DPORT|SI_W_SPORT)) == 0) { hv += is->is_sport; hv += is->is_dport; } /* TRACE is, flags, hv */ /* * If this is a real packet then initialise fields in the * state information structure from the TCP header information. */ is->is_maxdwin = 1; is->is_maxswin = ntohs(tcp->th_win); if (is->is_maxswin == 0) is->is_maxswin = 1; if ((fin->fin_flx & FI_IGNORE) == 0) { is->is_send = ntohl(tcp->th_seq) + fin->fin_dlen - (TCP_OFF(tcp) << 2) + ((tcp->th_flags & TH_SYN) ? 1 : 0) + ((tcp->th_flags & TH_FIN) ? 1 : 0); is->is_maxsend = is->is_send; /* * Window scale option is only present in * SYN/SYN-ACK packet. */ if ((tcp->th_flags & ~(TH_FIN|TH_ACK|TH_ECNALL)) == TH_SYN && (TCP_OFF(tcp) > (sizeof(tcphdr_t) >> 2))) { if (ipf_tcpoptions(softs, fin, tcp, &is->is_tcp.ts_data[0]) == -1) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_tcpoptions_th_fin_ack_ecnall, fr_info_t *, fin); } } if ((fin->fin_out != 0) && (pass & FR_NEWISN) != 0) { ipf_checknewisn(fin, is); ipf_fixoutisn(fin, is); } if ((tcp->th_flags & TH_OPENING) == TH_SYN) flags |= IS_TCPFSM; else { is->is_maxdwin = is->is_maxswin * 2; is->is_dend = ntohl(tcp->th_ack); is->is_maxdend = ntohl(tcp->th_ack); is->is_maxdwin *= 2; } } /* * If we're creating state for a starting connection, start * the timer on it as we'll never see an error if it fails * to connect. */ break; case IPPROTO_UDP : tcp = fin->fin_dp; is->is_sport = htons(fin->fin_data[0]); is->is_dport = htons(fin->fin_data[1]); if ((flags & (SI_W_DPORT|SI_W_SPORT)) == 0) { hv += tcp->th_dport; hv += tcp->th_sport; } break; default : break; } hv = DOUBLE_HASH(hv); is->is_hv = hv; /* * Look for identical state. */ for (is = softs->ipf_state_table[hv % softs->ipf_state_size]; is != NULL; is = is->is_hnext) { if (ipf_state_match(&ips, is) == 1) break; } if (is != NULL) { SBUMPD(ipf_state_stats, iss_add_dup); return 3; } if (softs->ipf_state_stats.iss_bucketlen[hv] >= softs->ipf_state_maxbucket) { SBUMPD(ipf_state_stats, iss_bucket_full); return 4; } /* * No existing state; create new */ KMALLOC(is, ipstate_t *); if (is == NULL) { SBUMPD(ipf_state_stats, iss_nomem); return 5; } bcopy((char *)&ips, (char *)is, sizeof(*is)); is->is_flags = flags & IS_INHERITED; is->is_rulen = fin->fin_rule; is->is_rule = fr; /* * Do not do the modulus here, it is done in ipf_state_insert(). */ if (fr != NULL) { ipftq_t *tq; (void) strncpy(is->is_group, FR_NAME(fr, fr_group), FR_GROUPLEN); if (fr->fr_age[0] != 0) { tq = ipf_addtimeoutqueue(softc, &softs->ipf_state_usertq, fr->fr_age[0]); is->is_tqehead[0] = tq; is->is_sti.tqe_flags |= TQE_RULEBASED; } if (fr->fr_age[1] != 0) { tq = ipf_addtimeoutqueue(softc, &softs->ipf_state_usertq, fr->fr_age[1]); is->is_tqehead[1] = tq; is->is_sti.tqe_flags |= TQE_RULEBASED; } is->is_tag = fr->fr_logtag; } /* * It may seem strange to set is_ref to 2, but if stsave is not NULL * then a copy of the pointer is being stored somewhere else and in * the end, it will expect to be able to do something with it. */ is->is_me = stsave; if (stsave != NULL) { *stsave = is; is->is_ref = 2; } else { is->is_ref = 1; } is->is_pkts[0] = 0, is->is_bytes[0] = 0; is->is_pkts[1] = 0, is->is_bytes[1] = 0; is->is_pkts[2] = 0, is->is_bytes[2] = 0; is->is_pkts[3] = 0, is->is_bytes[3] = 0; if ((fin->fin_flx & FI_IGNORE) == 0) { is->is_pkts[out] = 1; fin->fin_pktnum = 1; is->is_bytes[out] = fin->fin_plen; is->is_flx[out][0] = fin->fin_flx & FI_CMP; is->is_flx[out][0] &= ~FI_OOW; } if (pass & FR_STLOOSE) is->is_flags |= IS_LOOSE; if (pass & FR_STSTRICT) is->is_flags |= IS_STRICT; if (pass & FR_STATESYNC) is->is_flags |= IS_STATESYNC; if (pass & FR_LOGFIRST) is->is_pass &= ~(FR_LOGFIRST|FR_LOG); READ_ENTER(&softc->ipf_state); if (ipf_state_insert(softc, is, fin->fin_rev) == -1) { RWLOCK_EXIT(&softc->ipf_state); /* * This is a bit more manual than it should be but * ipf_state_del cannot be called. */ MUTEX_EXIT(&is->is_lock); MUTEX_DESTROY(&is->is_lock); if (is->is_tqehead[0] != NULL) { if (ipf_deletetimeoutqueue(is->is_tqehead[0]) == 0) ipf_freetimeoutqueue(softc, is->is_tqehead[0]); is->is_tqehead[0] = NULL; } if (is->is_tqehead[1] != NULL) { if (ipf_deletetimeoutqueue(is->is_tqehead[1]) == 0) ipf_freetimeoutqueue(softc, is->is_tqehead[1]); is->is_tqehead[1] = NULL; } KFREE(is); return -1; } /* * Filling in the interface name is after the insert so that an * event (such as add/delete) of an interface that is referenced * by this rule will see this state entry. */ if (fr != NULL) { /* * The name '-' is special for network interfaces and causes * a NULL name to be present, always, allowing packets to * match it, regardless of their interface. */ if ((fin->fin_ifp == NULL) || (fr->fr_ifnames[out << 1] != -1 && fr->fr_names[fr->fr_ifnames[out << 1] + 0] == '-' && fr->fr_names[fr->fr_ifnames[out << 1] + 1] == '\0')) { is->is_ifp[out << 1] = fr->fr_ifas[0]; strncpy(is->is_ifname[out << 1], FR_NAME(fr, fr_ifnames[0]), sizeof(fr->fr_ifnames[0])); } else { is->is_ifp[out << 1] = fin->fin_ifp; COPYIFNAME(fin->fin_v, fin->fin_ifp, is->is_ifname[out << 1]); } is->is_ifp[(out << 1) + 1] = fr->fr_ifas[1]; if (fr->fr_ifnames[1] != -1) { strncpy(is->is_ifname[(out << 1) + 1], FR_NAME(fr, fr_ifnames[1]), sizeof(fr->fr_ifnames[1])); } is->is_ifp[(1 - out) << 1] = fr->fr_ifas[2]; if (fr->fr_ifnames[2] != -1) { strncpy(is->is_ifname[((1 - out) << 1)], FR_NAME(fr, fr_ifnames[2]), sizeof(fr->fr_ifnames[2])); } is->is_ifp[((1 - out) << 1) + 1] = fr->fr_ifas[3]; if (fr->fr_ifnames[3] != -1) { strncpy(is->is_ifname[((1 - out) << 1) + 1], FR_NAME(fr, fr_ifnames[3]), sizeof(fr->fr_ifnames[3])); } } else { if (fin->fin_ifp != NULL) { is->is_ifp[out << 1] = fin->fin_ifp; COPYIFNAME(fin->fin_v, fin->fin_ifp, is->is_ifname[out << 1]); } } if (fin->fin_p == IPPROTO_TCP) { /* * If we're creating state for a starting connection, start the * timer on it as we'll never see an error if it fails to * connect. */ (void) ipf_tcp_age(&is->is_sti, fin, softs->ipf_state_tcptq, is->is_flags, 2); } MUTEX_EXIT(&is->is_lock); if ((is->is_flags & IS_STATESYNC) && ((is->is_flags & SI_CLONE) == 0)) is->is_sync = ipf_sync_new(softc, SMC_STATE, fin, is); if (softs->ipf_state_logging) ipf_state_log(softc, is, ISL_NEW); RWLOCK_EXIT(&softc->ipf_state); fin->fin_flx |= FI_STATE; if (fin->fin_flx & FI_FRAG) (void) ipf_frag_new(softc, fin, pass); fdp = &fr->fr_tifs[0]; if (fdp->fd_type == FRD_DSTLIST) { ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &is->is_tifs[0]); } else { bcopy(fdp, &is->is_tifs[0], sizeof(*fdp)); } fdp = &fr->fr_tifs[1]; if (fdp->fd_type == FRD_DSTLIST) { ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &is->is_tifs[1]); } else { bcopy(fdp, &is->is_tifs[1], sizeof(*fdp)); } fin->fin_tif = &is->is_tifs[fin->fin_rev]; fdp = &fr->fr_dif; if (fdp->fd_type == FRD_DSTLIST) { ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &is->is_dif); } else { bcopy(fdp, &is->is_dif, sizeof(*fdp)); } fin->fin_dif = &is->is_dif; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_tcpoptions */ /* Returns: int - 1 == packet matches state entry, 0 == it does not, */ /* -1 == packet has bad TCP options data */ /* Parameters: softs(I) - pointer to state context structure */ /* fin(I) - pointer to packet information */ /* tcp(I) - pointer to TCP packet header */ /* td(I) - pointer to TCP data held as part of the state */ /* */ /* Look after the TCP header for any options and deal with those that are */ /* present. Record details about those that we recogise. */ /* ------------------------------------------------------------------------ */ static int -ipf_tcpoptions(softs, fin, tcp, td) - ipf_state_softc_t *softs; - fr_info_t *fin; - tcphdr_t *tcp; - tcpdata_t *td; +ipf_tcpoptions(ipf_state_softc_t *softs, fr_info_t *fin, tcphdr_t *tcp, + tcpdata_t *td) { int off, mlen, ol, i, len, retval; char buf[64], *s, opt; mb_t *m = NULL; len = (TCP_OFF(tcp) << 2); if (fin->fin_dlen < len) { SBUMPD(ipf_state_stats, iss_tcp_toosmall); return 0; } len -= sizeof(*tcp); off = fin->fin_plen - fin->fin_dlen + sizeof(*tcp) + fin->fin_ipoff; m = fin->fin_m; mlen = MSGDSIZE(m) - off; if (len > mlen) { len = mlen; retval = 0; } else { retval = 1; } COPYDATA(m, off, len, buf); for (s = buf; len > 0; ) { opt = *s; if (opt == TCPOPT_EOL) break; else if (opt == TCPOPT_NOP) ol = 1; else { if (len < 2) break; ol = (int)*(s + 1); if (ol < 2 || ol > len) break; /* * Extract the TCP options we are interested in out of * the header and store them in the the tcpdata struct. */ switch (opt) { case TCPOPT_WINDOW : if (ol == TCPOLEN_WINDOW) { i = (int)*(s + 2); if (i > TCP_WSCALE_MAX) i = TCP_WSCALE_MAX; else if (i < 0) i = 0; td->td_winscale = i; td->td_winflags |= TCP_WSCALE_SEEN| TCP_WSCALE_FIRST; } else retval = -1; break; case TCPOPT_MAXSEG : /* * So, if we wanted to set the TCP MAXSEG, * it should be done here... */ if (ol == TCPOLEN_MAXSEG) { i = (int)*(s + 2); i <<= 8; i += (int)*(s + 3); td->td_maxseg = i; } else retval = -1; break; case TCPOPT_SACK_PERMITTED : if (ol == TCPOLEN_SACK_PERMITTED) td->td_winflags |= TCP_SACK_PERMIT; else retval = -1; break; } } len -= ol; s += ol; } if (retval == -1) { SBUMPD(ipf_state_stats, iss_tcp_badopt); } return retval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_tcp */ /* Returns: int - 1 == packet matches state entry, 0 == it does not */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softs(I) - pointer to state context structure */ /* fin(I) - pointer to packet information */ /* tcp(I) - pointer to TCP packet header */ /* is(I) - pointer to master state structure */ /* */ /* Check to see if a packet with TCP headers fits within the TCP window. */ /* Change timeout depending on whether new packet is a SYN-ACK returning */ /* for a SYN or a RST or FIN which indicate time to close up shop. */ /* ------------------------------------------------------------------------ */ static int -ipf_state_tcp(softc, softs, fin, tcp, is) - ipf_main_softc_t *softc; - ipf_state_softc_t *softs; - fr_info_t *fin; - tcphdr_t *tcp; - ipstate_t *is; +ipf_state_tcp(ipf_main_softc_t *softc, ipf_state_softc_t *softs, + fr_info_t *fin, tcphdr_t *tcp, ipstate_t *is) { tcpdata_t *fdata, *tdata; int source, ret, flags; source = !fin->fin_rev; if (((is->is_flags & IS_TCPFSM) != 0) && (source == 1) && (ntohs(is->is_sport) != fin->fin_data[0])) source = 0; fdata = &is->is_tcp.ts_data[!source]; tdata = &is->is_tcp.ts_data[source]; MUTEX_ENTER(&is->is_lock); /* * If a SYN packet is received for a connection that is on the way out * but hasn't yet departed then advance this session along the way. */ if ((tcp->th_flags & TH_OPENING) == TH_SYN) { if ((is->is_state[0] > IPF_TCPS_ESTABLISHED) && (is->is_state[1] > IPF_TCPS_ESTABLISHED)) { is->is_state[!source] = IPF_TCPS_CLOSED; ipf_movequeue(softc->ipf_ticks, &is->is_sti, is->is_sti.tqe_ifq, &softs->ipf_state_deletetq); MUTEX_EXIT(&is->is_lock); DT1(iss_tcp_closing, ipstate_t *, is); SBUMP(ipf_state_stats.iss_tcp_closing); return 0; } } if (is->is_flags & IS_LOOSE) ret = 1; else ret = ipf_state_tcpinwindow(fin, fdata, tdata, tcp, is->is_flags); if (ret > 0) { /* * Nearing end of connection, start timeout. */ ret = ipf_tcp_age(&is->is_sti, fin, softs->ipf_state_tcptq, is->is_flags, ret); if (ret == 0) { MUTEX_EXIT(&is->is_lock); DT2(iss_tcp_fsm, fr_info_t *, fin, ipstate_t *, is); SBUMP(ipf_state_stats.iss_tcp_fsm); return 0; } if (softs->ipf_state_logging > 4) ipf_state_log(softc, is, ISL_STATECHANGE); /* * set s0's as appropriate. Use syn-ack packet as it * contains both pieces of required information. */ /* * Window scale option is only present in SYN/SYN-ACK packet. * Compare with ~TH_FIN to mask out T/TCP setups. */ flags = tcp->th_flags & ~(TH_FIN|TH_ECNALL); if (flags == (TH_SYN|TH_ACK)) { is->is_s0[source] = ntohl(tcp->th_ack); is->is_s0[!source] = ntohl(tcp->th_seq) + 1; if ((TCP_OFF(tcp) > (sizeof(tcphdr_t) >> 2))) { if (ipf_tcpoptions(softs, fin, tcp, fdata) == -1) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_winscale_syn_ack, fr_info_t *, fin); } } if ((fin->fin_out != 0) && (is->is_pass & FR_NEWISN)) ipf_checknewisn(fin, is); } else if (flags == TH_SYN) { is->is_s0[source] = ntohl(tcp->th_seq) + 1; if ((TCP_OFF(tcp) > (sizeof(tcphdr_t) >> 2))) { if (ipf_tcpoptions(softs, fin, tcp, fdata) == -1) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_winscale_syn, fr_info_t *, fin); } } if ((fin->fin_out != 0) && (is->is_pass & FR_NEWISN)) ipf_checknewisn(fin, is); } ret = 1; } else { DT2(iss_tcp_oow, fr_info_t *, fin, ipstate_t *, is); SBUMP(ipf_state_stats.iss_tcp_oow); ret = 0; } MUTEX_EXIT(&is->is_lock); return ret; } /* ------------------------------------------------------------------------ */ /* Function: ipf_checknewisn */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to master state structure */ /* */ /* Check to see if this TCP connection is expecting and needs a new */ /* sequence number for a particular direction of the connection. */ /* */ /* NOTE: This does not actually change the sequence numbers, only gets new */ /* one ready. */ /* ------------------------------------------------------------------------ */ static void -ipf_checknewisn(fin, is) - fr_info_t *fin; - ipstate_t *is; +ipf_checknewisn(fr_info_t *fin, ipstate_t *is) { u_32_t sumd, old, new; tcphdr_t *tcp; int i; i = fin->fin_rev; tcp = fin->fin_dp; if (((i == 0) && !(is->is_flags & IS_ISNSYN)) || ((i == 1) && !(is->is_flags & IS_ISNACK))) { old = ntohl(tcp->th_seq); new = ipf_newisn(fin); is->is_isninc[i] = new - old; CALC_SUMD(old, new, sumd); is->is_sumd[i] = (sumd & 0xffff) + (sumd >> 16); is->is_flags |= ((i == 0) ? IS_ISNSYN : IS_ISNACK); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_tcpinwindow */ /* Returns: int - 1 == packet inside TCP "window", 0 == not inside. */ /* Parameters: fin(I) - pointer to packet information */ /* fdata(I) - pointer to tcp state informatio (forward) */ /* tdata(I) - pointer to tcp state informatio (reverse) */ /* tcp(I) - pointer to TCP packet header */ /* */ /* Given a packet has matched addresses and ports, check to see if it is */ /* within the TCP data window. In a show of generosity, allow packets that */ /* are within the window space behind the current sequence # as well. */ /* ------------------------------------------------------------------------ */ static int -ipf_state_tcpinwindow(fin, fdata, tdata, tcp, flags) - fr_info_t *fin; - tcpdata_t *fdata, *tdata; - tcphdr_t *tcp; - int flags; +ipf_state_tcpinwindow(fr_info_t *fin, tcpdata_t *fdata, tcpdata_t *tdata, + tcphdr_t *tcp, int flags) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_state_softc_t *softs = softc->ipf_state_soft; tcp_seq seq, ack, end; int ackskew, tcpflags; u_32_t win, maxwin; int dsize, inseq; /* * Find difference between last checked packet and this packet. */ tcpflags = tcp->th_flags; seq = ntohl(tcp->th_seq); ack = ntohl(tcp->th_ack); if (tcpflags & TH_SYN) win = ntohs(tcp->th_win); else win = ntohs(tcp->th_win) << fdata->td_winscale; /* * A window of 0 produces undesirable behaviour from this function. */ if (win == 0) win = 1; dsize = fin->fin_dlen - (TCP_OFF(tcp) << 2) + ((tcpflags & TH_SYN) ? 1 : 0) + ((tcpflags & TH_FIN) ? 1 : 0); /* * if window scaling is present, the scaling is only allowed * for windows not in the first SYN packet. In that packet the * window is 65535 to specify the largest window possible * for receivers not implementing the window scale option. * Currently, we do not assume TTCP here. That means that * if we see a second packet from a host (after the initial * SYN), we can assume that the receiver of the SYN did * already send back the SYN/ACK (and thus that we know if * the receiver also does window scaling) */ if (!(tcpflags & TH_SYN) && (fdata->td_winflags & TCP_WSCALE_FIRST)) { fdata->td_winflags &= ~TCP_WSCALE_FIRST; fdata->td_maxwin = win; } end = seq + dsize; if ((fdata->td_end == 0) && (!(flags & IS_TCPFSM) || ((tcpflags & TH_OPENING) == TH_OPENING))) { /* * Must be a (outgoing) SYN-ACK in reply to a SYN. */ fdata->td_end = end - 1; fdata->td_maxwin = 1; fdata->td_maxend = end + win; } if (!(tcpflags & TH_ACK)) { /* Pretend an ack was sent */ ack = tdata->td_end; } else if (((tcpflags & (TH_ACK|TH_RST)) == (TH_ACK|TH_RST)) && (ack == 0)) { /* gross hack to get around certain broken tcp stacks */ ack = tdata->td_end; } maxwin = tdata->td_maxwin; ackskew = tdata->td_end - ack; /* * Strict sequencing only allows in-order delivery. */ if ((flags & IS_STRICT) != 0) { if (seq != fdata->td_end) { DT2(iss_tcp_struct, tcpdata_t *, fdata, int, seq); SBUMP(ipf_state_stats.iss_tcp_strict); fin->fin_flx |= FI_OOW; return 0; } } #define SEQ_GE(a,b) ((int)((a) - (b)) >= 0) #define SEQ_GT(a,b) ((int)((a) - (b)) > 0) inseq = 0; if ((SEQ_GE(fdata->td_maxend, end)) && (SEQ_GE(seq, fdata->td_end - maxwin)) && /* XXX what about big packets */ #define MAXACKWINDOW 66000 (-ackskew <= (MAXACKWINDOW)) && ( ackskew <= (MAXACKWINDOW << fdata->td_winscale))) { inseq = 1; /* * Microsoft Windows will send the next packet to the right of the * window if SACK is in use. */ } else if ((seq == fdata->td_maxend) && (ackskew == 0) && (fdata->td_winflags & TCP_SACK_PERMIT) && (tdata->td_winflags & TCP_SACK_PERMIT)) { DT2(iss_sinsack, tcpdata_t *, fdata, int, seq); SBUMP(ipf_state_stats.iss_winsack); inseq = 1; /* * Sometimes a TCP RST will be generated with only the ACK field * set to non-zero. */ } else if ((seq == 0) && (tcpflags == (TH_RST|TH_ACK)) && (ackskew >= -1) && (ackskew <= 1)) { inseq = 1; } else if (!(flags & IS_TCPFSM)) { int i; i = (fin->fin_rev << 1) + fin->fin_out; #if 0 if (is_pkts[i]0 == 0) { /* * Picking up a connection in the middle, the "next" * packet seen from a direction that is new should be * accepted, even if it appears out of sequence. */ inseq = 1; } else #endif if (!(fdata->td_winflags & (TCP_WSCALE_SEEN|TCP_WSCALE_FIRST))) { /* * No TCPFSM and no window scaling, so make some * extra guesses. */ if ((seq == fdata->td_maxend) && (ackskew == 0)) inseq = 1; else if (SEQ_GE(seq + maxwin, fdata->td_end - maxwin)) inseq = 1; } } /* TRACE(inseq, fdata, tdata, seq, end, ack, ackskew, win, maxwin) */ if (inseq) { /* if ackskew < 0 then this should be due to fragmented * packets. There is no way to know the length of the * total packet in advance. * We do know the total length from the fragment cache though. * Note however that there might be more sessions with * exactly the same source and destination parameters in the * state cache (and source and destination is the only stuff * that is saved in the fragment cache). Note further that * some TCP connections in the state cache are hashed with * sport and dport as well which makes it not worthwhile to * look for them. * Thus, when ackskew is negative but still seems to belong * to this session, we bump up the destinations end value. */ if (ackskew < 0) tdata->td_end = ack; /* update max window seen */ if (fdata->td_maxwin < win) fdata->td_maxwin = win; if (SEQ_GT(end, fdata->td_end)) fdata->td_end = end; if (SEQ_GE(ack + win, tdata->td_maxend)) tdata->td_maxend = ack + win; return 1; } SBUMP(ipf_state_stats.iss_oow); fin->fin_flx |= FI_OOW; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_clone */ /* Returns: ipstate_t* - NULL == cloning failed, */ /* else pointer to new state structure */ /* Parameters: fin(I) - pointer to packet information */ /* tcp(I) - pointer to TCP/UDP header */ /* is(I) - pointer to master state structure */ /* */ /* Create a "duplcate" state table entry from the master. */ /* ------------------------------------------------------------------------ */ static ipstate_t * -ipf_state_clone(fin, tcp, is) - fr_info_t *fin; - tcphdr_t *tcp; - ipstate_t *is; +ipf_state_clone(fr_info_t *fin, tcphdr_t *tcp, ipstate_t *is) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *clone; u_32_t send; if (softs->ipf_state_stats.iss_active == softs->ipf_state_max) { SBUMPD(ipf_state_stats, iss_max); softs->ipf_state_doflush = 1; return NULL; } KMALLOC(clone, ipstate_t *); if (clone == NULL) { SBUMPD(ipf_state_stats, iss_clone_nomem); return NULL; } bcopy((char *)is, (char *)clone, sizeof(*clone)); MUTEX_NUKE(&clone->is_lock); /* * It has not yet been placed on any timeout queue, so make sure * all of that data is zero'd out. */ clone->is_sti.tqe_pnext = NULL; clone->is_sti.tqe_next = NULL; clone->is_sti.tqe_ifq = NULL; clone->is_sti.tqe_parent = clone; clone->is_die = ONE_DAY + softc->ipf_ticks; clone->is_state[0] = 0; clone->is_state[1] = 0; send = ntohl(tcp->th_seq) + fin->fin_dlen - (TCP_OFF(tcp) << 2) + ((tcp->th_flags & TH_SYN) ? 1 : 0) + ((tcp->th_flags & TH_FIN) ? 1 : 0); if (fin->fin_rev == 1) { clone->is_dend = send; clone->is_maxdend = send; clone->is_send = 0; clone->is_maxswin = 1; clone->is_maxdwin = ntohs(tcp->th_win); if (clone->is_maxdwin == 0) clone->is_maxdwin = 1; } else { clone->is_send = send; clone->is_maxsend = send; clone->is_dend = 0; clone->is_maxdwin = 1; clone->is_maxswin = ntohs(tcp->th_win); if (clone->is_maxswin == 0) clone->is_maxswin = 1; } clone->is_flags &= ~SI_CLONE; clone->is_flags |= SI_CLONED; if (ipf_state_insert(softc, clone, fin->fin_rev) == -1) { KFREE(clone); return NULL; } clone->is_ref = 1; if (clone->is_p == IPPROTO_TCP) { (void) ipf_tcp_age(&clone->is_sti, fin, softs->ipf_state_tcptq, clone->is_flags, 2); } MUTEX_EXIT(&clone->is_lock); if (is->is_flags & IS_STATESYNC) clone->is_sync = ipf_sync_new(softc, SMC_STATE, fin, clone); DT2(iss_clone, ipstate_t *, is, ipstate_t *, clone); SBUMP(ipf_state_stats.iss_cloned); return clone; } /* ------------------------------------------------------------------------ */ /* Function: ipf_matchsrcdst */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to state structure */ /* src(I) - pointer to source address */ /* dst(I) - pointer to destination address */ /* tcp(I) - pointer to TCP/UDP header */ /* cmask(I) - mask of FI_* bits to check */ /* */ /* Match a state table entry against an IP packet. The logic below is that */ /* ret gets set to one if the match succeeds, else remains 0. If it is */ /* still 0 after the test. no match. */ /* ------------------------------------------------------------------------ */ static ipstate_t * -ipf_matchsrcdst(fin, is, src, dst, tcp, cmask) - fr_info_t *fin; - ipstate_t *is; - i6addr_t *src, *dst; - tcphdr_t *tcp; - u_32_t cmask; +ipf_matchsrcdst(fr_info_t *fin, ipstate_t *is, i6addr_t *src, i6addr_t *dst, + tcphdr_t *tcp, u_32_t cmask) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_state_softc_t *softs = softc->ipf_state_soft; int ret = 0, rev, out, flags, flx = 0, idx; u_short sp, dp; u_32_t cflx; void *ifp; /* * If a connection is about to be deleted, no packets * are allowed to match it. */ if (is->is_sti.tqe_ifq == &softs->ipf_state_deletetq) return NULL; rev = IP6_NEQ(&is->is_dst, dst); ifp = fin->fin_ifp; out = fin->fin_out; flags = is->is_flags; sp = 0; dp = 0; if (tcp != NULL) { sp = htons(fin->fin_sport); dp = ntohs(fin->fin_dport); } if (!rev) { if (tcp != NULL) { if (!(flags & SI_W_SPORT) && (sp != is->is_sport)) rev = 1; else if (!(flags & SI_W_DPORT) && (dp != is->is_dport)) rev = 1; } } idx = (out << 1) + rev; /* * If the interface for this 'direction' is set, make sure it matches. * An interface name that is not set matches any, as does a name of *. */ if ((is->is_ifp[idx] == ifp) || (is->is_ifp[idx] == NULL && (*is->is_ifname[idx] == '\0' || *is->is_ifname[idx] == '-' || *is->is_ifname[idx] == '*'))) ret = 1; if (ret == 0) { DT2(iss_lookup_badifp, fr_info_t *, fin, ipstate_t *, is); SBUMP(ipf_state_stats.iss_lookup_badifp); /* TRACE is, out, rev, idx */ return NULL; } ret = 0; /* * Match addresses and ports. */ if (rev == 0) { if ((IP6_EQ(&is->is_dst, dst) || (flags & SI_W_DADDR)) && (IP6_EQ(&is->is_src, src) || (flags & SI_W_SADDR))) { if (tcp) { if ((sp == is->is_sport || flags & SI_W_SPORT) && (dp == is->is_dport || flags & SI_W_DPORT)) ret = 1; } else { ret = 1; } } } else { if ((IP6_EQ(&is->is_dst, src) || (flags & SI_W_DADDR)) && (IP6_EQ(&is->is_src, dst) || (flags & SI_W_SADDR))) { if (tcp) { if ((dp == is->is_sport || flags & SI_W_SPORT) && (sp == is->is_dport || flags & SI_W_DPORT)) ret = 1; } else { ret = 1; } } } if (ret == 0) { SBUMP(ipf_state_stats.iss_lookup_badport); DT2(iss_lookup_badport, fr_info_t *, fin, ipstate_t *, is); /* TRACE rev, is, sp, dp, src, dst */ return NULL; } /* * Whether or not this should be here, is questionable, but the aim * is to get this out of the main line. */ if (tcp == NULL) flags = is->is_flags & ~(SI_WILDP|SI_NEWFR|SI_CLONE|SI_CLONED); /* * Only one of the source or destination address can be flaged as a * wildcard. Fill in the missing address, if set. * For IPv6, if the address being copied in is multicast, then * don't reset the wild flag - multicast causes it to be set in the * first place! */ if ((flags & (SI_W_SADDR|SI_W_DADDR))) { fr_ip_t *fi = &fin->fin_fi; if ((flags & SI_W_SADDR) != 0) { if (rev == 0) { is->is_src = fi->fi_src; is->is_flags &= ~SI_W_SADDR; } else { if (!(fin->fin_flx & (FI_MULTICAST|FI_MBCAST))){ is->is_src = fi->fi_dst; is->is_flags &= ~SI_W_SADDR; } } } else if ((flags & SI_W_DADDR) != 0) { if (rev == 0) { if (!(fin->fin_flx & (FI_MULTICAST|FI_MBCAST))){ is->is_dst = fi->fi_dst; is->is_flags &= ~SI_W_DADDR; } } else { is->is_dst = fi->fi_src; is->is_flags &= ~SI_W_DADDR; } } if ((is->is_flags & (SI_WILDA|SI_WILDP)) == 0) { ATOMIC_DECL(softs->ipf_state_stats.iss_wild); } } flx = fin->fin_flx & cmask; cflx = is->is_flx[out][rev]; /* * Match up any flags set from IP options. */ if ((cflx && (flx != (cflx & cmask))) || ((fin->fin_optmsk & is->is_optmsk[rev]) != is->is_opt[rev]) || ((fin->fin_secmsk & is->is_secmsk) != is->is_sec) || ((fin->fin_auth & is->is_authmsk) != is->is_auth)) { SBUMPD(ipf_state_stats, iss_miss_mask); return NULL; } if ((fin->fin_flx & FI_IGNORE) != 0) { fin->fin_rev = rev; return is; } /* * Only one of the source or destination port can be flagged as a * wildcard. When filling it in, fill in a copy of the matched entry * if it has the cloning flag set. */ if ((flags & (SI_W_SPORT|SI_W_DPORT))) { if ((flags & SI_CLONE) != 0) { ipstate_t *clone; clone = ipf_state_clone(fin, tcp, is); if (clone == NULL) return NULL; is = clone; } else { ATOMIC_DECL(softs->ipf_state_stats.iss_wild); } if ((flags & SI_W_SPORT) != 0) { if (rev == 0) { is->is_sport = sp; is->is_send = ntohl(tcp->th_seq); } else { is->is_sport = dp; is->is_send = ntohl(tcp->th_ack); } is->is_maxsend = is->is_send + 1; } else if ((flags & SI_W_DPORT) != 0) { if (rev == 0) { is->is_dport = dp; is->is_dend = ntohl(tcp->th_ack); } else { is->is_dport = sp; is->is_dend = ntohl(tcp->th_seq); } is->is_maxdend = is->is_dend + 1; } is->is_flags &= ~(SI_W_SPORT|SI_W_DPORT); if ((flags & SI_CLONED) && softs->ipf_state_logging) ipf_state_log(softc, is, ISL_CLONE); } ret = -1; if (is->is_flx[out][rev] == 0) { is->is_flx[out][rev] = flx; if (rev == 1 && is->is_optmsk[1] == 0) { is->is_opt[1] = fin->fin_optmsk; is->is_optmsk[1] = 0xffffffff; if (is->is_v == 6) { is->is_opt[1] &= ~0x8; is->is_optmsk[1] &= ~0x8; } } } /* * Check if the interface name for this "direction" is set and if not, * fill it in. */ if (is->is_ifp[idx] == NULL && (*is->is_ifname[idx] == '\0' || *is->is_ifname[idx] == '*')) { is->is_ifp[idx] = ifp; COPYIFNAME(fin->fin_v, ifp, is->is_ifname[idx]); } fin->fin_rev = rev; return is; } /* ------------------------------------------------------------------------ */ /* Function: ipf_checkicmpmatchingstate */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* If we've got an ICMP error message, using the information stored in the */ /* ICMP packet, look for a matching state table entry. */ /* */ /* If we return NULL then no lock on ipf_state is held. */ /* If we return non-null then a read-lock on ipf_state is held. */ /* ------------------------------------------------------------------------ */ static ipstate_t * -ipf_checkicmpmatchingstate(fin) - fr_info_t *fin; +ipf_checkicmpmatchingstate(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *is, **isp; i6addr_t dst, src; struct icmp *ic; u_short savelen; icmphdr_t *icmp; fr_info_t ofin; tcphdr_t *tcp; int type, len; u_char pr; ip_t *oip; u_int hv; /* * Does it at least have the return (basic) IP header ? * Is it an actual recognised ICMP error type? * Only a basic IP header (no options) should be with * an ICMP error header. */ if ((fin->fin_v != 4) || (fin->fin_hlen != sizeof(ip_t)) || (fin->fin_plen < ICMPERR_MINPKTLEN) || !(fin->fin_flx & FI_ICMPERR)) { SBUMPD(ipf_state_stats, iss_icmp_bad); return NULL; } ic = fin->fin_dp; type = ic->icmp_type; oip = (ip_t *)((char *)ic + ICMPERR_ICMPHLEN); /* * Check if the at least the old IP header (with options) and * 8 bytes of payload is present. */ if (fin->fin_plen < ICMPERR_MAXPKTLEN + ((IP_HL(oip) - 5) << 2)) { SBUMPDX(ipf_state_stats, iss_icmp_short, iss_icmp_short_1); return NULL; } /* * Sanity Checks. */ len = fin->fin_dlen - ICMPERR_ICMPHLEN; if ((len <= 0) || ((IP_HL(oip) << 2) > len)) { DT2(iss_icmp_len, fr_info_t *, fin, struct ip*, oip); SBUMPDX(ipf_state_stats, iss_icmp_short, iss_icmp_short_1); return NULL; } /* * Is the buffer big enough for all of it ? It's the size of the IP * header claimed in the encapsulated part which is of concern. It * may be too big to be in this buffer but not so big that it's * outside the ICMP packet, leading to TCP deref's causing problems. * This is possible because we don't know how big oip_hl is when we * do the pullup early in ipf_check() and thus can't guarantee it is * all here now. */ #ifdef _KERNEL { mb_t *m; m = fin->fin_m; # if SOLARIS if ((char *)oip + len > (char *)m->b_wptr) { SBUMPDX(ipf_state_stats, iss_icmp_short, iss_icmp_short_2); return NULL; } # else if ((char *)oip + len > (char *)fin->fin_ip + m->m_len) { SBUMPDX(ipf_state_stats, iss_icmp_short, iss_icmp_short_3); return NULL; } # endif } #endif bcopy((char *)fin, (char *)&ofin, sizeof(*fin)); /* * in the IPv4 case we must zero the i6addr union otherwise * the IP6_EQ and IP6_NEQ macros produce the wrong results because * of the 'junk' in the unused part of the union */ bzero((char *)&src, sizeof(src)); bzero((char *)&dst, sizeof(dst)); /* * we make an fin entry to be able to feed it to * matchsrcdst note that not all fields are encessary * but this is the cleanest way. Note further we fill * in fin_mp such that if someone uses it we'll get * a kernel panic. ipf_matchsrcdst does not use this. * * watch out here, as ip is in host order and oip in network * order. Any change we make must be undone afterwards, like * oip->ip_len. */ savelen = oip->ip_len; oip->ip_len = htons(len); ofin.fin_flx = FI_NOCKSUM; ofin.fin_v = 4; ofin.fin_ip = oip; ofin.fin_m = NULL; /* if dereferenced, panic XXX */ ofin.fin_mp = NULL; /* if dereferenced, panic XXX */ (void) ipf_makefrip(IP_HL(oip) << 2, oip, &ofin); ofin.fin_ifp = fin->fin_ifp; ofin.fin_out = !fin->fin_out; hv = (pr = oip->ip_p); src.in4 = oip->ip_src; hv += src.in4.s_addr; dst.in4 = oip->ip_dst; hv += dst.in4.s_addr; /* * Reset the short and bad flag here because in ipf_matchsrcdst() * the flags for the current packet (fin_flx) are compared against * those for the existing session. */ ofin.fin_flx &= ~(FI_BAD|FI_SHORT); /* * Put old values of ip_len back as we don't know * if we have to forward the packet or process it again. */ oip->ip_len = savelen; switch (oip->ip_p) { case IPPROTO_ICMP : /* * an ICMP error can only be generated as a result of an * ICMP query, not as the response on an ICMP error * * XXX theoretically ICMP_ECHOREP and the other reply's are * ICMP query's as well, but adding them here seems strange XXX */ if ((ofin.fin_flx & FI_ICMPERR) != 0) { DT1(iss_icmp_icmperr, fr_info_t *, &ofin); SBUMP(ipf_state_stats.iss_icmp_icmperr); return NULL; } /* * perform a lookup of the ICMP packet in the state table */ icmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); hv += icmp->icmp_id; hv = DOUBLE_HASH(hv); READ_ENTER(&softc->ipf_state); for (isp = &softs->ipf_state_table[hv]; ((is = *isp) != NULL); ) { isp = &is->is_hnext; if ((is->is_p != pr) || (is->is_v != 4)) continue; if (is->is_pass & FR_NOICMPERR) continue; is = ipf_matchsrcdst(&ofin, is, &src, &dst, NULL, FI_ICMPCMP); if ((is != NULL) && !ipf_allowstateicmp(fin, is, &src)) return is; } RWLOCK_EXIT(&softc->ipf_state); SBUMPDX(ipf_state_stats, iss_icmp_miss, iss_icmp_miss_1); return NULL; case IPPROTO_TCP : case IPPROTO_UDP : break; default : SBUMPDX(ipf_state_stats, iss_icmp_miss, iss_icmp_miss_2); return NULL; } tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); hv += tcp->th_dport; hv += tcp->th_sport; hv = DOUBLE_HASH(hv); READ_ENTER(&softc->ipf_state); for (isp = &softs->ipf_state_table[hv]; ((is = *isp) != NULL); ) { isp = &is->is_hnext; /* * Only allow this icmp though if the * encapsulated packet was allowed through the * other way around. Note that the minimal amount * of info present does not allow for checking against * tcp internals such as seq and ack numbers. Only the * ports are known to be present and can be even if the * short flag is set. */ if ((is->is_p == pr) && (is->is_v == 4) && (is = ipf_matchsrcdst(&ofin, is, &src, &dst, tcp, FI_ICMPCMP))) { if (ipf_allowstateicmp(fin, is, &src) == 0) return is; } } RWLOCK_EXIT(&softc->ipf_state); SBUMPDX(ipf_state_stats, iss_icmp_miss, iss_icmp_miss_3); return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_allowstateicmp */ /* Returns: int - 1 = packet denied, 0 = packet allowed */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to state table entry */ /* src(I) - source address to check permission for */ /* */ /* For an ICMP packet that has so far matched a state table entry, check if */ /* there are any further refinements that might mean we want to block this */ /* packet. This code isn't specific to either IPv4 or IPv6. */ /* ------------------------------------------------------------------------ */ static int -ipf_allowstateicmp(fin, is, src) - fr_info_t *fin; - ipstate_t *is; - i6addr_t *src; +ipf_allowstateicmp(fr_info_t *fin, ipstate_t *is, i6addr_t *src) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_state_softc_t *softs = softc->ipf_state_soft; frentry_t *savefr; frentry_t *fr; u_32_t ipass; int backward; int oi; int i; fr = is->is_rule; if (fr != NULL && fr->fr_icmpgrp != NULL) { savefr = fin->fin_fr; fin->fin_fr = fr->fr_icmpgrp->fg_start; ipass = ipf_scanlist(fin, softc->ipf_pass); fin->fin_fr = savefr; if (FR_ISBLOCK(ipass)) { SBUMPD(ipf_state_stats, iss_icmp_headblock); return 1; } } /* * i : the index of this packet (the icmp unreachable) * oi : the index of the original packet found in the * icmp header (i.e. the packet causing this icmp) * backward : original packet was backward compared to * the state */ backward = IP6_NEQ(&is->is_src, src); fin->fin_rev = !backward; i = (!backward << 1) + fin->fin_out; oi = (backward << 1) + !fin->fin_out; if (is->is_pass & FR_NOICMPERR) { SBUMPD(ipf_state_stats, iss_icmp_banned); return 1; } if (is->is_icmppkts[i] > is->is_pkts[oi]) { SBUMPD(ipf_state_stats, iss_icmp_toomany); return 1; } DT2(iss_icmp_hits, fr_info_t *, fin, ipstate_t *, is); SBUMP(ipf_state_stats.iss_icmp_hits); is->is_icmppkts[i]++; /* * we deliberately do not touch the timeouts * for the accompanying state table entry. * It remains to be seen if that is correct. XXX */ return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_ipsmove */ /* Returns: Nil */ /* Parameters: is(I) - pointer to state table entry */ /* hv(I) - new hash value for state table entry */ /* Write Locks: ipf_state */ /* */ /* Move a state entry from one position in the hash table to another. */ /* ------------------------------------------------------------------------ */ static void -ipf_ipsmove(softs, is, hv) - ipf_state_softc_t *softs; - ipstate_t *is; - u_int hv; +ipf_ipsmove(ipf_state_softc_t *softs, ipstate_t *is, u_int hv) { ipstate_t **isp; u_int hvm; hvm = is->is_hv; /* TRACE is, is_hv, hvm */ /* * Remove the hash from the old location... */ isp = is->is_phnext; if (is->is_hnext) is->is_hnext->is_phnext = isp; *isp = is->is_hnext; if (softs->ipf_state_table[hvm] == NULL) softs->ipf_state_stats.iss_inuse--; softs->ipf_state_stats.iss_bucketlen[hvm]--; /* * ...and put the hash in the new one. */ hvm = DOUBLE_HASH(hv); is->is_hv = hvm; /* TRACE is, hv, is_hv, hvm */ isp = &softs->ipf_state_table[hvm]; if (*isp) (*isp)->is_phnext = &is->is_hnext; else softs->ipf_state_stats.iss_inuse++; softs->ipf_state_stats.iss_bucketlen[hvm]++; is->is_phnext = isp; is->is_hnext = *isp; *isp = is; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_lookup */ /* Returns: ipstate_t* - NULL == no matching state found, */ /* else pointer to state information is returned */ /* Parameters: fin(I) - pointer to packet information */ /* tcp(I) - pointer to TCP/UDP header. */ /* ifqp(O) - pointer for storing tailq timeout */ /* */ /* Search the state table for a matching entry to the packet described by */ /* the contents of *fin. For certain protocols, when a match is found the */ /* timeout queue is also selected and stored in ifpq if it is non-NULL. */ /* */ /* If we return NULL then no lock on ipf_state is held. */ /* If we return non-null then a read-lock on ipf_state is held. */ /* ------------------------------------------------------------------------ */ ipstate_t * -ipf_state_lookup(fin, tcp, ifqp) - fr_info_t *fin; - tcphdr_t *tcp; - ipftq_t **ifqp; +ipf_state_lookup(fr_info_t *fin, tcphdr_t *tcp, ipftq_t **ifqp) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_state_softc_t *softs = softc->ipf_state_soft; u_int hv, hvm, pr, v, tryagain; ipstate_t *is, **isp; u_short dport, sport; i6addr_t src, dst; struct icmp *ic; ipftq_t *ifq; int oow; is = NULL; ifq = NULL; tcp = fin->fin_dp; ic = (struct icmp *)tcp; hv = (pr = fin->fin_fi.fi_p); src = fin->fin_fi.fi_src; dst = fin->fin_fi.fi_dst; hv += src.in4.s_addr; hv += dst.in4.s_addr; v = fin->fin_fi.fi_v; #ifdef USE_INET6 if (v == 6) { hv += fin->fin_fi.fi_src.i6[1]; hv += fin->fin_fi.fi_src.i6[2]; hv += fin->fin_fi.fi_src.i6[3]; if ((fin->fin_p == IPPROTO_ICMPV6) && IN6_IS_ADDR_MULTICAST(&fin->fin_fi.fi_dst.in6)) { hv -= dst.in4.s_addr; } else { hv += fin->fin_fi.fi_dst.i6[1]; hv += fin->fin_fi.fi_dst.i6[2]; hv += fin->fin_fi.fi_dst.i6[3]; } } #endif if ((v == 4) && (fin->fin_flx & (FI_MULTICAST|FI_BROADCAST|FI_MBCAST))) { if (fin->fin_out == 0) { hv -= src.in4.s_addr; } else { hv -= dst.in4.s_addr; } } /* TRACE fin_saddr, fin_daddr, hv */ /* * Search the hash table for matching packet header info. */ switch (pr) { #ifdef USE_INET6 case IPPROTO_ICMPV6 : tryagain = 0; if (v == 6) { if ((ic->icmp_type == ICMP6_ECHO_REQUEST) || (ic->icmp_type == ICMP6_ECHO_REPLY)) { hv += ic->icmp_id; } } READ_ENTER(&softc->ipf_state); icmp6again: hvm = DOUBLE_HASH(hv); for (isp = &softs->ipf_state_table[hvm]; ((is = *isp) != NULL); ) { isp = &is->is_hnext; if ((is->is_p != pr) || (is->is_v != v)) continue; is = ipf_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); if (is != NULL && ipf_matchicmpqueryreply(v, &is->is_icmp, ic, fin->fin_rev)) { if (fin->fin_rev) ifq = &softs->ipf_state_icmpacktq; else ifq = &softs->ipf_state_icmptq; break; } } if (is != NULL) { if ((tryagain != 0) && !(is->is_flags & SI_W_DADDR)) { hv += fin->fin_fi.fi_src.i6[0]; hv += fin->fin_fi.fi_src.i6[1]; hv += fin->fin_fi.fi_src.i6[2]; hv += fin->fin_fi.fi_src.i6[3]; ipf_ipsmove(softs, is, hv); MUTEX_DOWNGRADE(&softc->ipf_state); } break; } RWLOCK_EXIT(&softc->ipf_state); /* * No matching icmp state entry. Perhaps this is a * response to another state entry. * * XXX With some ICMP6 packets, the "other" address is already * in the packet, after the ICMP6 header, and this could be * used in place of the multicast address. However, taking * advantage of this requires some significant code changes * to handle the specific types where that is the case. */ if ((softs->ipf_state_stats.iss_wild != 0) && ((fin->fin_flx & FI_NOWILD) == 0) && (v == 6) && (tryagain == 0)) { hv -= fin->fin_fi.fi_src.i6[0]; hv -= fin->fin_fi.fi_src.i6[1]; hv -= fin->fin_fi.fi_src.i6[2]; hv -= fin->fin_fi.fi_src.i6[3]; tryagain = 1; WRITE_ENTER(&softc->ipf_state); goto icmp6again; } is = ipf_checkicmp6matchingstate(fin); if (is != NULL) return is; break; #endif case IPPROTO_ICMP : if (v == 4) { hv += ic->icmp_id; } hv = DOUBLE_HASH(hv); READ_ENTER(&softc->ipf_state); for (isp = &softs->ipf_state_table[hv]; ((is = *isp) != NULL); ) { isp = &is->is_hnext; if ((is->is_p != pr) || (is->is_v != v)) continue; is = ipf_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); if ((is != NULL) && (ic->icmp_id == is->is_icmp.ici_id) && ipf_matchicmpqueryreply(v, &is->is_icmp, ic, fin->fin_rev)) { if (fin->fin_rev) ifq = &softs->ipf_state_icmpacktq; else ifq = &softs->ipf_state_icmptq; break; } } if (is == NULL) { RWLOCK_EXIT(&softc->ipf_state); } break; case IPPROTO_TCP : case IPPROTO_UDP : ifqp = NULL; sport = htons(fin->fin_data[0]); hv += sport; dport = htons(fin->fin_data[1]); hv += dport; oow = 0; tryagain = 0; READ_ENTER(&softc->ipf_state); retry_tcpudp: hvm = DOUBLE_HASH(hv); /* TRACE hv, hvm */ for (isp = &softs->ipf_state_table[hvm]; ((is = *isp) != NULL); ) { isp = &is->is_hnext; if ((is->is_p != pr) || (is->is_v != v)) continue; fin->fin_flx &= ~FI_OOW; is = ipf_matchsrcdst(fin, is, &src, &dst, tcp, FI_CMP); if (is != NULL) { if (pr == IPPROTO_TCP) { if (!ipf_state_tcp(softc, softs, fin, tcp, is)) { oow |= fin->fin_flx & FI_OOW; continue; } } break; } } if (is != NULL) { if (tryagain && !(is->is_flags & (SI_CLONE|SI_WILDP|SI_WILDA))) { hv += dport; hv += sport; ipf_ipsmove(softs, is, hv); MUTEX_DOWNGRADE(&softc->ipf_state); } break; } RWLOCK_EXIT(&softc->ipf_state); if ((softs->ipf_state_stats.iss_wild != 0) && ((fin->fin_flx & FI_NOWILD) == 0)) { if (tryagain == 0) { hv -= dport; hv -= sport; } else if (tryagain == 1) { hv = fin->fin_fi.fi_p; /* * If we try to pretend this is a reply to a * multicast/broadcast packet then we need to * exclude part of the address from the hash * calculation. */ if (fin->fin_out == 0) { hv += src.in4.s_addr; } else { hv += dst.in4.s_addr; } hv += dport; hv += sport; } tryagain++; if (tryagain <= 2) { WRITE_ENTER(&softc->ipf_state); goto retry_tcpudp; } } fin->fin_flx |= oow; break; #if 0 case IPPROTO_GRE : gre = fin->fin_dp; if (GRE_REV(gre->gr_flags) == 1) { hv += gre->gr_call; } /* FALLTHROUGH */ #endif default : ifqp = NULL; hvm = DOUBLE_HASH(hv); READ_ENTER(&softc->ipf_state); for (isp = &softs->ipf_state_table[hvm]; ((is = *isp) != NULL); ) { isp = &is->is_hnext; if ((is->is_p != pr) || (is->is_v != v)) continue; is = ipf_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); if (is != NULL) { ifq = &softs->ipf_state_iptq; break; } } if (is == NULL) { RWLOCK_EXIT(&softc->ipf_state); } break; } if (is != NULL) { if (((is->is_sti.tqe_flags & TQE_RULEBASED) != 0) && (is->is_tqehead[fin->fin_rev] != NULL)) ifq = is->is_tqehead[fin->fin_rev]; if (ifq != NULL && ifqp != NULL) *ifqp = ifq; } else { SBUMP(ipf_state_stats.iss_lookup_miss); } return is; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_check */ /* Returns: frentry_t* - NULL == search failed, */ /* else pointer to rule for matching state */ /* Parameters: fin(I) - pointer to packet information */ /* passp(I) - pointer to filtering result flags */ /* */ /* Check if a packet is associated with an entry in the state table. */ /* ------------------------------------------------------------------------ */ frentry_t * -ipf_state_check(fin, passp) - fr_info_t *fin; - u_32_t *passp; +ipf_state_check(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_state_softc_t *softs = softc->ipf_state_soft; ipftqent_t *tqe; ipstate_t *is; frentry_t *fr; tcphdr_t *tcp; ipftq_t *ifq; u_int pass; int inout; if (softs->ipf_state_lock || (softs->ipf_state_list == NULL)) return NULL; if (fin->fin_flx & (FI_SHORT|FI_FRAGBODY|FI_BAD)) { SBUMPD(ipf_state_stats, iss_check_bad); return NULL; } if ((fin->fin_flx & FI_TCPUDP) || (fin->fin_fi.fi_p == IPPROTO_ICMP) #ifdef USE_INET6 || (fin->fin_fi.fi_p == IPPROTO_ICMPV6) #endif ) tcp = fin->fin_dp; else tcp = NULL; ifq = NULL; /* * Search the hash table for matching packet header info. */ is = ipf_state_lookup(fin, tcp, &ifq); switch (fin->fin_p) { #ifdef USE_INET6 case IPPROTO_ICMPV6 : if (is != NULL) break; if (fin->fin_v == 6) { is = ipf_checkicmp6matchingstate(fin); } break; #endif case IPPROTO_ICMP : if (is != NULL) break; /* * No matching icmp state entry. Perhaps this is a * response to another state entry. */ is = ipf_checkicmpmatchingstate(fin); break; case IPPROTO_TCP : if (is == NULL) break; if (is->is_pass & FR_NEWISN) { if (fin->fin_out == 0) ipf_fixinisn(fin, is); else if (fin->fin_out == 1) ipf_fixoutisn(fin, is); } break; default : if (fin->fin_rev) ifq = &softs->ipf_state_udpacktq; else ifq = &softs->ipf_state_udptq; break; } if (is == NULL) { SBUMP(ipf_state_stats.iss_check_miss); return NULL; } fr = is->is_rule; if (fr != NULL) { if ((fin->fin_out == 0) && (fr->fr_nattag.ipt_num[0] != 0)) { if (fin->fin_nattag == NULL) { RWLOCK_EXIT(&softc->ipf_state); SBUMPD(ipf_state_stats, iss_check_notag); return NULL; } if (ipf_matchtag(&fr->fr_nattag, fin->fin_nattag)!=0) { RWLOCK_EXIT(&softc->ipf_state); SBUMPD(ipf_state_stats, iss_check_nattag); return NULL; } } (void) strncpy(fin->fin_group, FR_NAME(fr, fr_group), FR_GROUPLEN); fin->fin_icode = fr->fr_icode; } fin->fin_rule = is->is_rulen; fin->fin_fr = fr; /* * If this packet is a fragment and the rule says to track fragments, * then create a new fragment cache entry. */ if (fin->fin_flx & FI_FRAG && FR_ISPASS(is->is_pass) && is->is_pass & FR_KEEPFRAG) (void) ipf_frag_new(softc, fin, is->is_pass); /* * For TCP packets, ifq == NULL. For all others, check if this new * queue is different to the last one it was on and move it if so. */ tqe = &is->is_sti; if ((tqe->tqe_flags & TQE_RULEBASED) != 0) ifq = is->is_tqehead[fin->fin_rev]; MUTEX_ENTER(&is->is_lock); if (ifq != NULL) ipf_movequeue(softc->ipf_ticks, tqe, tqe->tqe_ifq, ifq); inout = (fin->fin_rev << 1) + fin->fin_out; is->is_pkts[inout]++; is->is_bytes[inout] += fin->fin_plen; fin->fin_pktnum = is->is_pkts[inout] + is->is_icmppkts[inout]; MUTEX_EXIT(&is->is_lock); pass = is->is_pass; if (is->is_flags & IS_STATESYNC) ipf_sync_update(softc, SMC_STATE, fin, is->is_sync); RWLOCK_EXIT(&softc->ipf_state); SBUMP(ipf_state_stats.iss_hits); fin->fin_dif = &is->is_dif; fin->fin_tif = &is->is_tifs[fin->fin_rev]; fin->fin_flx |= FI_STATE; if ((pass & FR_LOGFIRST) != 0) pass &= ~(FR_LOGFIRST|FR_LOG); *passp = pass; return fr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_fixoutisn */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to master state structure */ /* */ /* Called only for outbound packets, adjusts the sequence number and the */ /* TCP checksum to match that change. */ /* ------------------------------------------------------------------------ */ static void -ipf_fixoutisn(fin, is) - fr_info_t *fin; - ipstate_t *is; +ipf_fixoutisn(fr_info_t *fin, ipstate_t *is) { tcphdr_t *tcp; int rev; u_32_t seq; tcp = fin->fin_dp; rev = fin->fin_rev; if ((is->is_flags & IS_ISNSYN) != 0) { if ((rev == 0) && (fin->fin_cksum < FI_CK_L4PART)) { seq = ntohl(tcp->th_seq); seq += is->is_isninc[0]; tcp->th_seq = htonl(seq); ipf_fix_outcksum(0, &tcp->th_sum, is->is_sumd[0], 0); } } if ((is->is_flags & IS_ISNACK) != 0) { if ((rev == 1) && (fin->fin_cksum < FI_CK_L4PART)) { seq = ntohl(tcp->th_seq); seq += is->is_isninc[1]; tcp->th_seq = htonl(seq); ipf_fix_outcksum(0, &tcp->th_sum, is->is_sumd[1], 0); } } } /* ------------------------------------------------------------------------ */ /* Function: ipf_fixinisn */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to master state structure */ /* */ /* Called only for inbound packets, adjusts the acknowledge number and the */ /* TCP checksum to match that change. */ /* ------------------------------------------------------------------------ */ static void -ipf_fixinisn(fin, is) - fr_info_t *fin; - ipstate_t *is; +ipf_fixinisn(fr_info_t *fin, ipstate_t *is) { tcphdr_t *tcp; int rev; u_32_t ack; tcp = fin->fin_dp; rev = fin->fin_rev; if ((is->is_flags & IS_ISNSYN) != 0) { if ((rev == 1) && (fin->fin_cksum < FI_CK_L4PART)) { ack = ntohl(tcp->th_ack); ack -= is->is_isninc[0]; tcp->th_ack = htonl(ack); ipf_fix_incksum(0, &tcp->th_sum, is->is_sumd[0], 0); } } if ((is->is_flags & IS_ISNACK) != 0) { if ((rev == 0) && (fin->fin_cksum < FI_CK_L4PART)) { ack = ntohl(tcp->th_ack); ack -= is->is_isninc[1]; tcp->th_ack = htonl(ack); ipf_fix_incksum(0, &tcp->th_sum, is->is_sumd[1], 0); } } } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_sync */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* ifp(I) - pointer to interface */ /* */ /* Walk through all state entries and if an interface pointer match is */ /* found then look it up again, based on its name in case the pointer has */ /* changed since last time. */ /* */ /* If ifp is passed in as being non-null then we are only doing updates for */ /* existing, matching, uses of it. */ /* ------------------------------------------------------------------------ */ void -ipf_state_sync(softc, ifp) - ipf_main_softc_t *softc; - void *ifp; +ipf_state_sync(ipf_main_softc_t *softc, void *ifp) { ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *is; int i; if (softc->ipf_running <= 0) return; WRITE_ENTER(&softc->ipf_state); if (softc->ipf_running <= 0) { RWLOCK_EXIT(&softc->ipf_state); return; } for (is = softs->ipf_state_list; is; is = is->is_next) { /* * Look up all the interface names in the state entry. */ for (i = 0; i < FR_NUM(is->is_ifp); i++) { if (ifp == NULL || ifp == is->is_ifp[i]) is->is_ifp[i] = ipf_resolvenic(softc, is->is_ifname[i], is->is_v); } } RWLOCK_EXIT(&softc->ipf_state); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_del */ /* Returns: int - 0 = deleted, else refernce count on active struct */ /* Parameters: softc(I) - pointer to soft context main structure */ /* is(I) - pointer to state structure to delete */ /* why(I) - if not 0, log reason why it was deleted */ /* Write Locks: ipf_state */ /* */ /* Deletes a state entry from the enumerated list as well as the hash table */ /* and timeout queue lists. Make adjustments to hash table statistics and */ /* global counters as required. */ /* ------------------------------------------------------------------------ */ static int -ipf_state_del(softc, is, why) - ipf_main_softc_t *softc; - ipstate_t *is; - int why; +ipf_state_del(ipf_main_softc_t *softc, ipstate_t *is, int why) { ipf_state_softc_t *softs = softc->ipf_state_soft; int orphan = 1; frentry_t *fr; /* * Since we want to delete this, remove it from the state table, * where it can be found & used, first. */ if (is->is_phnext != NULL) { *is->is_phnext = is->is_hnext; if (is->is_hnext != NULL) is->is_hnext->is_phnext = is->is_phnext; if (softs->ipf_state_table[is->is_hv] == NULL) softs->ipf_state_stats.iss_inuse--; softs->ipf_state_stats.iss_bucketlen[is->is_hv]--; is->is_phnext = NULL; is->is_hnext = NULL; orphan = 0; } /* * Because ipf_state_stats.iss_wild is a count of entries in the state * table that have wildcard flags set, only decerement it once * and do it here. */ if (is->is_flags & (SI_WILDP|SI_WILDA)) { if (!(is->is_flags & SI_CLONED)) { ATOMIC_DECL(softs->ipf_state_stats.iss_wild); } is->is_flags &= ~(SI_WILDP|SI_WILDA); } /* * Next, remove it from the timeout queue it is in. */ if (is->is_sti.tqe_ifq != NULL) ipf_deletequeueentry(&is->is_sti); /* * If it is still in use by something else, do not go any further, * but note that at this point it is now an orphan. How can this * be? ipf_state_flush() calls ipf_delete() directly because it wants * to empty the table out and if something has a hold on a state * entry (such as ipfstat), it'll do the deref path that'll bring * us back here to do the real delete & free. */ MUTEX_ENTER(&is->is_lock); if (is->is_me != NULL) { *is->is_me = NULL; is->is_me = NULL; is->is_ref--; } is->is_ref--; if (is->is_ref > 0) { int refs; refs = is->is_ref; MUTEX_EXIT(&is->is_lock); if (!orphan) softs->ipf_state_stats.iss_orphan++; return refs; } fr = is->is_rule; is->is_rule = NULL; if (fr != NULL) { if (fr->fr_srctrack.ht_max_nodes != 0) { (void) ipf_ht_node_del(&fr->fr_srctrack, is->is_family, &is->is_src); } } ASSERT(is->is_ref == 0); MUTEX_EXIT(&is->is_lock); if (is->is_tqehead[0] != NULL) { if (ipf_deletetimeoutqueue(is->is_tqehead[0]) == 0) ipf_freetimeoutqueue(softc, is->is_tqehead[0]); } if (is->is_tqehead[1] != NULL) { if (ipf_deletetimeoutqueue(is->is_tqehead[1]) == 0) ipf_freetimeoutqueue(softc, is->is_tqehead[1]); } if (is->is_sync) ipf_sync_del_state(softc->ipf_sync_soft, is->is_sync); /* * Now remove it from the linked list of known states */ if (is->is_pnext != NULL) { *is->is_pnext = is->is_next; if (is->is_next != NULL) is->is_next->is_pnext = is->is_pnext; is->is_pnext = NULL; is->is_next = NULL; } if (softs->ipf_state_logging != 0 && why != 0) ipf_state_log(softc, is, why); if (is->is_p == IPPROTO_TCP) softs->ipf_state_stats.iss_fin++; else softs->ipf_state_stats.iss_expire++; if (orphan) softs->ipf_state_stats.iss_orphan--; if (fr != NULL) { fr->fr_statecnt--; (void) ipf_derefrule(softc, &fr); } softs->ipf_state_stats.iss_active_proto[is->is_p]--; MUTEX_DESTROY(&is->is_lock); KFREE(is); softs->ipf_state_stats.iss_active--; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Slowly expire held state for thingslike UDP and ICMP. The algorithm */ /* used here is to keep the queue sorted with the oldest things at the top */ /* and the youngest at the bottom. So if the top one doesn't need to be */ /* expired then neither will any under it. */ /* ------------------------------------------------------------------------ */ void -ipf_state_expire(softc) - ipf_main_softc_t *softc; +ipf_state_expire(ipf_main_softc_t *softc) { ipf_state_softc_t *softs = softc->ipf_state_soft; ipftq_t *ifq, *ifqnext; ipftqent_t *tqe, *tqn; ipstate_t *is; SPL_INT(s); SPL_NET(s); WRITE_ENTER(&softc->ipf_state); for (ifq = softs->ipf_state_tcptq; ifq != NULL; ifq = ifq->ifq_next) for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; is = tqe->tqe_parent; ipf_state_del(softc, is, ISL_EXPIRE); } for (ifq = softs->ipf_state_usertq; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; is = tqe->tqe_parent; ipf_state_del(softc, is, ISL_EXPIRE); } } for (ifq = softs->ipf_state_usertq; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; if (((ifq->ifq_flags & IFQF_DELETE) != 0) && (ifq->ifq_ref == 0)) { ipf_freetimeoutqueue(softc, ifq); } } if (softs->ipf_state_doflush) { (void) ipf_state_flush(softc, 2, 0); softs->ipf_state_doflush = 0; softs->ipf_state_wm_last = softc->ipf_ticks; } RWLOCK_EXIT(&softc->ipf_state); SPL_X(s); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_flush */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* which(I) - which flush action to perform */ /* proto(I) - which protocol to flush (0 == ALL) */ /* Write Locks: ipf_state */ /* */ /* Flush state tables. Three actions currently defined: */ /* which == 0 : flush all state table entries */ /* which == 1 : flush TCP connections which have started to close but are */ /* stuck for some reason. */ /* which == 2 : flush TCP connections which have been idle for a long time, */ /* starting at > 4 days idle and working back in successive half-*/ /* days to at most 12 hours old. If this fails to free enough */ /* slots then work backwards in half hour slots to 30 minutes. */ /* If that too fails, then work backwards in 30 second intervals */ /* for the last 30 minutes to at worst 30 seconds idle. */ /* ------------------------------------------------------------------------ */ int -ipf_state_flush(softc, which, proto) - ipf_main_softc_t *softc; - int which, proto; +ipf_state_flush(ipf_main_softc_t *softc, int which, int proto) { ipf_state_softc_t *softs = softc->ipf_state_soft; ipftqent_t *tqe, *tqn; ipstate_t *is, **isp; ipftq_t *ifq; int removed; SPL_INT(s); removed = 0; SPL_NET(s); switch (which) { case 0 : SBUMP(ipf_state_stats.iss_flush_all); /* * Style 0 flush removes everything... */ for (isp = &softs->ipf_state_list; ((is = *isp) != NULL); ) { if ((proto != 0) && (is->is_v != proto)) { isp = &is->is_next; continue; } if (ipf_state_del(softc, is, ISL_FLUSH) == 0) removed++; else isp = &is->is_next; } break; case 1 : SBUMP(ipf_state_stats.iss_flush_closing); /* * Since we're only interested in things that are closing, * we can start with the appropriate timeout queue. */ for (ifq = softs->ipf_state_tcptq + IPF_TCPS_CLOSE_WAIT; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { tqn = tqe->tqe_next; is = tqe->tqe_parent; if (is->is_p != IPPROTO_TCP) break; if (ipf_state_del(softc, is, ISL_FLUSH) == 0) removed++; } } /* * Also need to look through the user defined queues. */ for (ifq = softs->ipf_state_usertq; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { tqn = tqe->tqe_next; is = tqe->tqe_parent; if (is->is_p != IPPROTO_TCP) continue; if ((is->is_state[0] > IPF_TCPS_ESTABLISHED) && (is->is_state[1] > IPF_TCPS_ESTABLISHED)) { if (ipf_state_del(softc, is, ISL_FLUSH) == 0) removed++; } } } break; case 2 : break; /* * Args 5-11 correspond to flushing those particular states * for TCP connections. */ case IPF_TCPS_CLOSE_WAIT : case IPF_TCPS_FIN_WAIT_1 : case IPF_TCPS_CLOSING : case IPF_TCPS_LAST_ACK : case IPF_TCPS_FIN_WAIT_2 : case IPF_TCPS_TIME_WAIT : case IPF_TCPS_CLOSED : SBUMP(ipf_state_stats.iss_flush_queue); tqn = softs->ipf_state_tcptq[which].ifq_head; while (tqn != NULL) { tqe = tqn; tqn = tqe->tqe_next; is = tqe->tqe_parent; if (ipf_state_del(softc, is, ISL_FLUSH) == 0) removed++; } break; default : if (which < 30) break; SBUMP(ipf_state_stats.iss_flush_state); /* * Take a large arbitrary number to mean the number of seconds * for which which consider to be the maximum value we'll allow * the expiration to be. */ which = IPF_TTLVAL(which); for (isp = &softs->ipf_state_list; ((is = *isp) != NULL); ) { if ((proto == 0) || (is->is_v == proto)) { if (softc->ipf_ticks - is->is_touched > which) { if (ipf_state_del(softc, is, ISL_FLUSH) == 0) { removed++; continue; } } } isp = &is->is_next; } break; } if (which != 2) { SPL_X(s); return removed; } SBUMP(ipf_state_stats.iss_flush_timeout); /* * Asked to remove inactive entries because the table is full, try * again, 3 times, if first attempt failed with a different criteria * each time. The order tried in must be in decreasing age. * Another alternative is to implement random drop and drop N entries * at random until N have been freed up. */ if (softc->ipf_ticks - softs->ipf_state_wm_last > softs->ipf_state_wm_freq) { removed = ipf_queueflush(softc, ipf_state_flush_entry, softs->ipf_state_tcptq, softs->ipf_state_usertq, &softs->ipf_state_stats.iss_active, softs->ipf_state_size, softs->ipf_state_wm_low); softs->ipf_state_wm_last = softc->ipf_ticks; } SPL_X(s); return removed; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_flush_entry */ /* Returns: int - 0 = entry deleted, else not deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* entry(I) - pointer to state structure to delete */ /* Write Locks: ipf_state */ /* */ /* This function is a stepping stone between ipf_queueflush() and */ /* ipf_state_del(). It is used so we can provide a uniform interface via */ /* the ipf_queueflush() function. */ /* ------------------------------------------------------------------------ */ static int -ipf_state_flush_entry(softc, entry) - ipf_main_softc_t *softc; - void *entry; +ipf_state_flush_entry(ipf_main_softc_t *softc, void *entry) { return ipf_state_del(softc, entry, ISL_FLUSH); } /* ------------------------------------------------------------------------ */ /* Function: ipf_tcp_age */ /* Returns: int - 1 == state transition made, 0 == no change (rejected) */ /* Parameters: tqe(I) - pointer to timeout queue information */ /* fin(I) - pointer to packet information */ /* tqtab(I) - TCP timeout queue table this is in */ /* flags(I) - flags from state/NAT entry */ /* ok(I) - can we advance state */ /* */ /* Rewritten by Arjan de Vet , 2000-07-29: */ /* */ /* - (try to) base state transitions on real evidence only, */ /* i.e. packets that are sent and have been received by ipfilter; */ /* diagram 18.12 of TCP/IP volume 1 by W. Richard Stevens was used. */ /* */ /* - deal with half-closed connections correctly; */ /* */ /* - store the state of the source in state[0] such that ipfstat */ /* displays the state as source/dest instead of dest/source; the calls */ /* to ipf_tcp_age have been changed accordingly. */ /* */ /* Internal Parameters: */ /* */ /* state[0] = state of source (host that initiated connection) */ /* state[1] = state of dest (host that accepted the connection) */ /* */ /* dir == 0 : a packet from source to dest */ /* dir == 1 : a packet from dest to source */ /* */ /* A typical procession for a connection is as follows: */ /* */ /* +--------------+-------------------+ */ /* | Side '0' | Side '1' | */ /* +--------------+-------------------+ */ /* | 0 -> 1 (SYN) | | */ /* | | 0 -> 2 (SYN-ACK) | */ /* | 1 -> 3 (ACK) | | */ /* | | 2 -> 4 (ACK-PUSH) | */ /* | 3 -> 4 (ACK) | | */ /* | ... | ... | */ /* | | 4 -> 6 (FIN-ACK) | */ /* | 4 -> 5 (ACK) | | */ /* | | 6 -> 6 (ACK-PUSH) | */ /* | 5 -> 5 (ACK) | | */ /* | 5 -> 8 (FIN) | | */ /* | | 6 -> 10 (ACK) | */ /* +--------------+-------------------+ */ /* */ /* Locking: it is assumed that the parent of the tqe structure is locked. */ /* ------------------------------------------------------------------------ */ int -ipf_tcp_age(tqe, fin, tqtab, flags, ok) - ipftqent_t *tqe; - fr_info_t *fin; - ipftq_t *tqtab; - int flags, ok; +ipf_tcp_age(ipftqent_t *tqe, fr_info_t *fin, ipftq_t *tqtab, int flags, int ok) { ipf_main_softc_t *softc = fin->fin_main_soft; int dlen, ostate, nstate, rval, dir; u_char tcpflags; tcphdr_t *tcp; tcp = fin->fin_dp; rval = 0; dir = fin->fin_rev; tcpflags = tcp->th_flags; dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2); ostate = tqe->tqe_state[1 - dir]; nstate = tqe->tqe_state[dir]; if (tcpflags & TH_RST) { if (!(tcpflags & TH_PUSH) && !dlen) nstate = IPF_TCPS_CLOSED; else nstate = IPF_TCPS_CLOSE_WAIT; if (ostate <= IPF_TCPS_ESTABLISHED) { tqe->tqe_state[1 - dir] = IPF_TCPS_CLOSE_WAIT; } rval = 1; } else { switch (nstate) { case IPF_TCPS_LISTEN: /* 0 */ if ((tcpflags & TH_OPENING) == TH_OPENING) { /* * 'dir' received an S and sends SA in * response, LISTEN -> SYN_RECEIVED */ nstate = IPF_TCPS_SYN_RECEIVED; rval = 1; } else if ((tcpflags & TH_OPENING) == TH_SYN) { /* 'dir' sent S, LISTEN -> SYN_SENT */ nstate = IPF_TCPS_SYN_SENT; rval = 1; } /* * the next piece of code makes it possible to get * already established connections into the state table * after a restart or reload of the filter rules; this * does not work when a strict 'flags S keep state' is * used for tcp connections of course */ if (((flags & IS_TCPFSM) == 0) && ((tcpflags & TH_ACKMASK) == TH_ACK)) { /* * we saw an A, guess 'dir' is in ESTABLISHED * mode */ switch (ostate) { case IPF_TCPS_LISTEN : case IPF_TCPS_SYN_RECEIVED : nstate = IPF_TCPS_HALF_ESTAB; rval = 1; break; case IPF_TCPS_HALF_ESTAB : case IPF_TCPS_ESTABLISHED : nstate = IPF_TCPS_ESTABLISHED; rval = 1; break; default : break; } } /* * TODO: besides regular ACK packets we can have other * packets as well; it is yet to be determined how we * should initialize the states in those cases */ break; case IPF_TCPS_SYN_SENT: /* 1 */ if ((tcpflags & ~(TH_ECN|TH_CWR)) == TH_SYN) { /* * A retransmitted SYN packet. We do not reset * the timeout here to ipf_tcptimeout because a * connection connect timeout does not renew * after every packet that is sent. We need to * set rval so as to indicate the packet has * passed the check for its flags being valid * in the TCP FSM. Setting rval to 2 has the * result of not resetting the timeout. */ rval = 2; } else if ((tcpflags & (TH_SYN|TH_FIN|TH_ACK)) == TH_ACK) { /* * we see an A from 'dir' which is in SYN_SENT * state: 'dir' sent an A in response to an SA * which it received, SYN_SENT -> ESTABLISHED */ nstate = IPF_TCPS_ESTABLISHED; rval = 1; } else if (tcpflags & TH_FIN) { /* * we see an F from 'dir' which is in SYN_SENT * state and wants to close its side of the * connection; SYN_SENT -> FIN_WAIT_1 */ nstate = IPF_TCPS_FIN_WAIT_1; rval = 1; } else if ((tcpflags & TH_OPENING) == TH_OPENING) { /* * we see an SA from 'dir' which is already in * SYN_SENT state, this means we have a * simultaneous open; SYN_SENT -> SYN_RECEIVED */ nstate = IPF_TCPS_SYN_RECEIVED; rval = 1; } break; case IPF_TCPS_SYN_RECEIVED: /* 2 */ if ((tcpflags & (TH_SYN|TH_FIN|TH_ACK)) == TH_ACK) { /* * we see an A from 'dir' which was in * SYN_RECEIVED state so it must now be in * established state, SYN_RECEIVED -> * ESTABLISHED */ nstate = IPF_TCPS_ESTABLISHED; rval = 1; } else if ((tcpflags & ~(TH_ECN|TH_CWR)) == TH_OPENING) { /* * We see an SA from 'dir' which is already in * SYN_RECEIVED state. */ rval = 2; } else if (tcpflags & TH_FIN) { /* * we see an F from 'dir' which is in * SYN_RECEIVED state and wants to close its * side of the connection; SYN_RECEIVED -> * FIN_WAIT_1 */ nstate = IPF_TCPS_FIN_WAIT_1; rval = 1; } break; case IPF_TCPS_HALF_ESTAB: /* 3 */ if (tcpflags & TH_FIN) { nstate = IPF_TCPS_FIN_WAIT_1; rval = 1; } else if ((tcpflags & TH_ACKMASK) == TH_ACK) { /* * If we've picked up a connection in mid * flight, we could be looking at a follow on * packet from the same direction as the one * that created this state. Recognise it but * do not advance the entire connection's * state. */ switch (ostate) { case IPF_TCPS_LISTEN : case IPF_TCPS_SYN_SENT : case IPF_TCPS_SYN_RECEIVED : rval = 1; break; case IPF_TCPS_HALF_ESTAB : case IPF_TCPS_ESTABLISHED : nstate = IPF_TCPS_ESTABLISHED; rval = 1; break; default : break; } } break; case IPF_TCPS_ESTABLISHED: /* 4 */ rval = 1; if (tcpflags & TH_FIN) { /* * 'dir' closed its side of the connection; * this gives us a half-closed connection; * ESTABLISHED -> FIN_WAIT_1 */ if (ostate == IPF_TCPS_FIN_WAIT_1) { nstate = IPF_TCPS_CLOSING; } else { nstate = IPF_TCPS_FIN_WAIT_1; } } else if (tcpflags & TH_ACK) { /* * an ACK, should we exclude other flags here? */ if (ostate == IPF_TCPS_FIN_WAIT_1) { /* * We know the other side did an active * close, so we are ACKing the recvd * FIN packet (does the window matching * code guarantee this?) and go into * CLOSE_WAIT state; this gives us a * half-closed connection */ nstate = IPF_TCPS_CLOSE_WAIT; } else if (ostate < IPF_TCPS_CLOSE_WAIT) { /* * still a fully established * connection reset timeout */ nstate = IPF_TCPS_ESTABLISHED; } } break; case IPF_TCPS_CLOSE_WAIT: /* 5 */ rval = 1; if (tcpflags & TH_FIN) { /* * application closed and 'dir' sent a FIN, * we're now going into LAST_ACK state */ nstate = IPF_TCPS_LAST_ACK; } else { /* * we remain in CLOSE_WAIT because the other * side has closed already and we did not * close our side yet; reset timeout */ nstate = IPF_TCPS_CLOSE_WAIT; } break; case IPF_TCPS_FIN_WAIT_1: /* 6 */ rval = 1; if ((tcpflags & TH_ACK) && ostate > IPF_TCPS_CLOSE_WAIT) { /* * if the other side is not active anymore * it has sent us a FIN packet that we are * ack'ing now with an ACK; this means both * sides have now closed the connection and * we go into TIME_WAIT */ /* * XXX: how do we know we really are ACKing * the FIN packet here? does the window code * guarantee that? */ nstate = IPF_TCPS_LAST_ACK; } else { /* * we closed our side of the connection * already but the other side is still active * (ESTABLISHED/CLOSE_WAIT); continue with * this half-closed connection */ nstate = IPF_TCPS_FIN_WAIT_1; } break; case IPF_TCPS_CLOSING: /* 7 */ if ((tcpflags & (TH_FIN|TH_ACK)) == TH_ACK) { nstate = IPF_TCPS_TIME_WAIT; } rval = 1; break; case IPF_TCPS_LAST_ACK: /* 8 */ if (tcpflags & TH_ACK) { rval = 1; } /* * we cannot detect when we go out of LAST_ACK state * to CLOSED because that is based on the reception * of ACK packets; ipfilter can only detect that a * packet has been sent by a host */ break; case IPF_TCPS_FIN_WAIT_2: /* 9 */ /* NOT USED */ break; case IPF_TCPS_TIME_WAIT: /* 10 */ /* we're in 2MSL timeout now */ if (ostate == IPF_TCPS_LAST_ACK) { nstate = IPF_TCPS_CLOSED; rval = 1; } else { rval = 2; } break; case IPF_TCPS_CLOSED: /* 11 */ rval = 2; break; default : #if !defined(_KERNEL) abort(); #endif break; } } /* * If rval == 2 then do not update the queue position, but treat the * packet as being ok. */ if (rval == 2) rval = 1; else if (rval == 1) { if (ok) tqe->tqe_state[dir] = nstate; if ((tqe->tqe_flags & TQE_RULEBASED) == 0) ipf_movequeue(softc->ipf_ticks, tqe, tqe->tqe_ifq, tqtab + nstate); } return rval; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_log */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* is(I) - pointer to state structure */ /* type(I) - type of log entry to create */ /* */ /* Creates a state table log entry using the state structure and type info. */ /* passed in. Log packet/byte counts, source/destination address and other */ /* protocol specific information. */ /* ------------------------------------------------------------------------ */ void -ipf_state_log(softc, is, type) - ipf_main_softc_t *softc; - struct ipstate *is; - u_int type; +ipf_state_log(ipf_main_softc_t *softc, struct ipstate *is, u_int type) { #ifdef IPFILTER_LOG struct ipslog ipsl; size_t sizes[1]; void *items[1]; int types[1]; /* * Copy information out of the ipstate_t structure and into the * structure used for logging. */ ipsl.isl_type = type; ipsl.isl_pkts[0] = is->is_pkts[0] + is->is_icmppkts[0]; ipsl.isl_bytes[0] = is->is_bytes[0]; ipsl.isl_pkts[1] = is->is_pkts[1] + is->is_icmppkts[1]; ipsl.isl_bytes[1] = is->is_bytes[1]; ipsl.isl_pkts[2] = is->is_pkts[2] + is->is_icmppkts[2]; ipsl.isl_bytes[2] = is->is_bytes[2]; ipsl.isl_pkts[3] = is->is_pkts[3] + is->is_icmppkts[3]; ipsl.isl_bytes[3] = is->is_bytes[3]; ipsl.isl_src = is->is_src; ipsl.isl_dst = is->is_dst; ipsl.isl_p = is->is_p; ipsl.isl_v = is->is_v; ipsl.isl_flags = is->is_flags; ipsl.isl_tag = is->is_tag; ipsl.isl_rulen = is->is_rulen; (void) strncpy(ipsl.isl_group, is->is_group, FR_GROUPLEN); if (ipsl.isl_p == IPPROTO_TCP || ipsl.isl_p == IPPROTO_UDP) { ipsl.isl_sport = is->is_sport; ipsl.isl_dport = is->is_dport; if (ipsl.isl_p == IPPROTO_TCP) { ipsl.isl_state[0] = is->is_state[0]; ipsl.isl_state[1] = is->is_state[1]; } } else if (ipsl.isl_p == IPPROTO_ICMP) { ipsl.isl_itype = is->is_icmp.ici_type; } else if (ipsl.isl_p == IPPROTO_ICMPV6) { ipsl.isl_itype = is->is_icmp.ici_type; } else { ipsl.isl_ps.isl_filler[0] = 0; ipsl.isl_ps.isl_filler[1] = 0; } items[0] = &ipsl; sizes[0] = sizeof(ipsl); types[0] = 0; (void) ipf_log_items(softc, IPL_LOGSTATE, NULL, items, sizes, types, 1); #endif } #ifdef USE_INET6 /* ------------------------------------------------------------------------ */ /* Function: ipf_checkicmp6matchingstate */ /* Returns: ipstate_t* - NULL == no match found, */ /* else pointer to matching state entry */ /* Parameters: fin(I) - pointer to packet information */ /* Locks: NULL == no locks, else Read Lock on ipf_state */ /* */ /* If we've got an ICMPv6 error message, using the information stored in */ /* the ICMPv6 packet, look for a matching state table entry. */ /* ------------------------------------------------------------------------ */ static ipstate_t * -ipf_checkicmp6matchingstate(fin) - fr_info_t *fin; +ipf_checkicmp6matchingstate(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_state_softc_t *softs = softc->ipf_state_soft; struct icmp6_hdr *ic6, *oic; ipstate_t *is, **isp; u_short sport, dport; i6addr_t dst, src; u_short savelen; icmpinfo_t *ic; fr_info_t ofin; tcphdr_t *tcp; ip6_t *oip6; u_char pr; u_int hv; int type; /* * Does it at least have the return (basic) IP header ? * Is it an actual recognised ICMP error type? * Only a basic IP header (no options) should be with * an ICMP error header. */ if ((fin->fin_v != 6) || (fin->fin_plen < ICMP6ERR_MINPKTLEN) || !(fin->fin_flx & FI_ICMPERR)) { SBUMPD(ipf_state_stats, iss_icmp_bad); return NULL; } ic6 = fin->fin_dp; type = ic6->icmp6_type; oip6 = (ip6_t *)((char *)ic6 + ICMPERR_ICMPHLEN); if (fin->fin_plen < sizeof(*oip6)) { SBUMPD(ipf_state_stats, iss_icmp_short); return NULL; } bcopy((char *)fin, (char *)&ofin, sizeof(*fin)); ofin.fin_v = 6; ofin.fin_ifp = fin->fin_ifp; ofin.fin_out = !fin->fin_out; ofin.fin_m = NULL; /* if dereferenced, panic XXX */ ofin.fin_mp = NULL; /* if dereferenced, panic XXX */ /* * We make a fin entry to be able to feed it to * matchsrcdst. Note that not all fields are necessary * but this is the cleanest way. Note further we fill * in fin_mp such that if someone uses it we'll get * a kernel panic. ipf_matchsrcdst does not use this. * * watch out here, as ip is in host order and oip6 in network * order. Any change we make must be undone afterwards. */ savelen = oip6->ip6_plen; oip6->ip6_plen = htons(fin->fin_dlen - ICMPERR_ICMPHLEN); ofin.fin_flx = FI_NOCKSUM; ofin.fin_ip = (ip_t *)oip6; (void) ipf_makefrip(sizeof(*oip6), (ip_t *)oip6, &ofin); ofin.fin_flx &= ~(FI_BAD|FI_SHORT); oip6->ip6_plen = savelen; pr = ofin.fin_p; /* * an ICMP error can never generate an ICMP error in response. */ if (ofin.fin_flx & FI_ICMPERR) { DT1(iss_icmp6_icmperr, fr_info_t *, &ofin); SBUMP(ipf_state_stats.iss_icmp6_icmperr); return NULL; } if (oip6->ip6_nxt == IPPROTO_ICMPV6) { oic = ofin.fin_dp; /* * an ICMP error can only be generated as a result of an * ICMP query, not as the response on an ICMP error * * XXX theoretically ICMP_ECHOREP and the other reply's are * ICMP query's as well, but adding them here seems strange XXX */ if (!(oic->icmp6_type & ICMP6_INFOMSG_MASK)) { DT1(iss_icmp6_notinfo, fr_info_t *, &ofin); SBUMP(ipf_state_stats.iss_icmp6_notinfo); return NULL; } /* * perform a lookup of the ICMP packet in the state table */ hv = (pr = oip6->ip6_nxt); src.in6 = oip6->ip6_src; hv += src.in4.s_addr; dst.in6 = oip6->ip6_dst; hv += dst.in4.s_addr; hv += oic->icmp6_id; hv += oic->icmp6_seq; hv = DOUBLE_HASH(hv); READ_ENTER(&softc->ipf_state); for (isp = &softs->ipf_state_table[hv]; ((is = *isp) != NULL); ) { ic = &is->is_icmp; isp = &is->is_hnext; if ((is->is_p == pr) && !(is->is_pass & FR_NOICMPERR) && (oic->icmp6_id == ic->ici_id) && (oic->icmp6_seq == ic->ici_seq) && (is = ipf_matchsrcdst(&ofin, is, &src, &dst, NULL, FI_ICMPCMP))) { /* * in the state table ICMP query's are stored * with the type of the corresponding ICMP * response. Correct here */ if (((ic->ici_type == ICMP6_ECHO_REPLY) && (oic->icmp6_type == ICMP6_ECHO_REQUEST)) || (ic->ici_type - 1 == oic->icmp6_type )) { if (!ipf_allowstateicmp(fin, is, &src)) return is; } } } RWLOCK_EXIT(&softc->ipf_state); SBUMPD(ipf_state_stats, iss_icmp6_miss); return NULL; } hv = (pr = oip6->ip6_nxt); src.in6 = oip6->ip6_src; hv += src.i6[0]; hv += src.i6[1]; hv += src.i6[2]; hv += src.i6[3]; dst.in6 = oip6->ip6_dst; hv += dst.i6[0]; hv += dst.i6[1]; hv += dst.i6[2]; hv += dst.i6[3]; tcp = NULL; switch (oip6->ip6_nxt) { case IPPROTO_TCP : case IPPROTO_UDP : tcp = (tcphdr_t *)(oip6 + 1); dport = tcp->th_dport; sport = tcp->th_sport; hv += dport; hv += sport; break; case IPPROTO_ICMPV6 : oic = (struct icmp6_hdr *)(oip6 + 1); hv += oic->icmp6_id; hv += oic->icmp6_seq; break; default : break; } hv = DOUBLE_HASH(hv); READ_ENTER(&softc->ipf_state); for (isp = &softs->ipf_state_table[hv]; ((is = *isp) != NULL); ) { isp = &is->is_hnext; /* * Only allow this icmp though if the * encapsulated packet was allowed through the * other way around. Note that the minimal amount * of info present does not allow for checking against * tcp internals such as seq and ack numbers. */ if ((is->is_p != pr) || (is->is_v != 6) || (is->is_pass & FR_NOICMPERR)) continue; is = ipf_matchsrcdst(&ofin, is, &src, &dst, tcp, FI_ICMPCMP); if ((is != NULL) && (ipf_allowstateicmp(fin, is, &src) == 0)) return is; } RWLOCK_EXIT(&softc->ipf_state); SBUMPD(ipf_state_stats, iss_icmp_miss); return NULL; } #endif /* ------------------------------------------------------------------------ */ /* Function: ipf_sttab_init */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* tqp(I) - pointer to an array of timeout queues for TCP */ /* */ /* Initialise the array of timeout queues for TCP. */ /* ------------------------------------------------------------------------ */ void ipf_sttab_init(softc, tqp) ipf_main_softc_t *softc; ipftq_t *tqp; { int i; for (i = IPF_TCP_NSTATES - 1; i >= 0; i--) { IPFTQ_INIT(&tqp[i], 0, "ipftq tcp tab"); tqp[i].ifq_next = tqp + i + 1; } tqp[IPF_TCP_NSTATES - 1].ifq_next = NULL; tqp[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcpclosed; tqp[IPF_TCPS_LISTEN].ifq_ttl = softc->ipf_tcptimeout; tqp[IPF_TCPS_SYN_SENT].ifq_ttl = softc->ipf_tcpsynsent; tqp[IPF_TCPS_SYN_RECEIVED].ifq_ttl = softc->ipf_tcpsynrecv; tqp[IPF_TCPS_ESTABLISHED].ifq_ttl = softc->ipf_tcpidletimeout; tqp[IPF_TCPS_CLOSE_WAIT].ifq_ttl = softc->ipf_tcphalfclosed; tqp[IPF_TCPS_FIN_WAIT_1].ifq_ttl = softc->ipf_tcphalfclosed; tqp[IPF_TCPS_CLOSING].ifq_ttl = softc->ipf_tcptimeout; tqp[IPF_TCPS_LAST_ACK].ifq_ttl = softc->ipf_tcplastack; tqp[IPF_TCPS_FIN_WAIT_2].ifq_ttl = softc->ipf_tcpclosewait; tqp[IPF_TCPS_TIME_WAIT].ifq_ttl = softc->ipf_tcptimewait; tqp[IPF_TCPS_HALF_ESTAB].ifq_ttl = softc->ipf_tcptimeout; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sttab_destroy */ /* Returns: Nil */ /* Parameters: tqp(I) - pointer to an array of timeout queues for TCP */ /* */ /* Do whatever is necessary to "destroy" each of the entries in the array */ /* of timeout queues for TCP. */ /* ------------------------------------------------------------------------ */ void -ipf_sttab_destroy(tqp) - ipftq_t *tqp; +ipf_sttab_destroy(ipftq_t *tqp) { int i; for (i = IPF_TCP_NSTATES - 1; i >= 0; i--) MUTEX_DESTROY(&tqp[i].ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_deref */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* isp(I) - pointer to pointer to state table entry */ /* */ /* Decrement the reference counter for this state table entry and free it */ /* if there are no more things using it. */ /* */ /* This function is only called when cleaning up after increasing is_ref by */ /* one earlier in the 'code path' so if is_ref is 1 when entering, we do */ /* have an orphan, otherwise not. However there is a possible race between */ /* the entry being deleted via flushing with an ioctl call (that calls the */ /* delete function directly) and the tail end of packet processing so we */ /* need to grab is_lock before doing the check to synchronise the two code */ /* paths. */ /* */ /* When operating in userland (ipftest), we have no timers to clear a state */ /* entry. Therefore, we make a few simple tests before deleting an entry */ /* outright. We compare states on each side looking for a combination of */ /* TIME_WAIT (should really be FIN_WAIT_2?) and LAST_ACK. Then we factor */ /* in packet direction with the interface list to make sure we don't */ /* prematurely delete an entry on a final inbound packet that's we're also */ /* supposed to route elsewhere. */ /* */ /* Internal parameters: */ /* state[0] = state of source (host that initiated connection) */ /* state[1] = state of dest (host that accepted the connection) */ /* */ /* dir == 0 : a packet from source to dest */ /* dir == 1 : a packet from dest to source */ /* ------------------------------------------------------------------------ */ void -ipf_state_deref(softc, isp) - ipf_main_softc_t *softc; - ipstate_t **isp; +ipf_state_deref(ipf_main_softc_t *softc, ipstate_t **isp) { ipstate_t *is = *isp; is = *isp; *isp = NULL; MUTEX_ENTER(&is->is_lock); if (is->is_ref > 1) { is->is_ref--; MUTEX_EXIT(&is->is_lock); #ifndef _KERNEL if ((is->is_sti.tqe_state[0] > IPF_TCPS_ESTABLISHED) || (is->is_sti.tqe_state[1] > IPF_TCPS_ESTABLISHED)) { ipf_state_del(softc, is, ISL_EXPIRE); } #endif return; } MUTEX_EXIT(&is->is_lock); WRITE_ENTER(&softc->ipf_state); ipf_state_del(softc, is, ISL_ORPHAN); RWLOCK_EXIT(&softc->ipf_state); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_setqueue */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* is(I) - pointer to state structure */ /* rev(I) - forward(0) or reverse(1) direction */ /* Locks: ipf_state (read or write) */ /* */ /* Put the state entry on its default queue entry, using rev as a helped in */ /* determining which queue it should be placed on. */ /* ------------------------------------------------------------------------ */ void -ipf_state_setqueue(softc, is, rev) - ipf_main_softc_t *softc; - ipstate_t *is; - int rev; +ipf_state_setqueue(ipf_main_softc_t *softc, ipstate_t *is, int rev) { ipf_state_softc_t *softs = softc->ipf_state_soft; ipftq_t *oifq, *nifq; if ((is->is_sti.tqe_flags & TQE_RULEBASED) != 0) nifq = is->is_tqehead[rev]; else nifq = NULL; if (nifq == NULL) { switch (is->is_p) { #ifdef USE_INET6 case IPPROTO_ICMPV6 : if (rev == 1) nifq = &softs->ipf_state_icmpacktq; else nifq = &softs->ipf_state_icmptq; break; #endif case IPPROTO_ICMP : if (rev == 1) nifq = &softs->ipf_state_icmpacktq; else nifq = &softs->ipf_state_icmptq; break; case IPPROTO_TCP : nifq = softs->ipf_state_tcptq + is->is_state[rev]; break; case IPPROTO_UDP : if (rev == 1) nifq = &softs->ipf_state_udpacktq; else nifq = &softs->ipf_state_udptq; break; default : nifq = &softs->ipf_state_iptq; break; } } oifq = is->is_sti.tqe_ifq; /* * If it's currently on a timeout queue, move it from one queue to * another, else put it on the end of the newly determined queue. */ if (oifq != NULL) ipf_movequeue(softc->ipf_ticks, &is->is_sti, oifq, nifq); else ipf_queueappend(softc->ipf_ticks, &is->is_sti, nifq, is); return; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_iter */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to main soft context */ /* token(I) - pointer to ipftoken structure */ /* itp(I) - pointer to ipfgeniter structure */ /* obj(I) - pointer to data description structure */ /* */ /* This function handles the SIOCGENITER ioctl for the state tables and */ /* walks through the list of entries in the state table list (softs->ipf_state_list.) */ /* ------------------------------------------------------------------------ */ static int -ipf_state_iter(softc, token, itp, obj) - ipf_main_softc_t *softc; - ipftoken_t *token; - ipfgeniter_t *itp; - ipfobj_t *obj; +ipf_state_iter(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp, + ipfobj_t *obj) { ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t *is, *next, zero; int error; if (itp->igi_data == NULL) { IPFERROR(100026); return EFAULT; } if (itp->igi_nitems < 1) { IPFERROR(100027); return ENOSPC; } if (itp->igi_type != IPFGENITER_STATE) { IPFERROR(100028); return EINVAL; } is = token->ipt_data; if (is == (void *)-1) { IPFERROR(100029); return ESRCH; } error = 0; obj->ipfo_type = IPFOBJ_IPSTATE; obj->ipfo_size = sizeof(ipstate_t); READ_ENTER(&softc->ipf_state); is = token->ipt_data; if (is == NULL) { next = softs->ipf_state_list; } else { next = is->is_next; } /* * If we find a state entry to use, bump its reference count so that * it can be used for is_next when we come back. */ if (next != NULL) { MUTEX_ENTER(&next->is_lock); next->is_ref++; MUTEX_EXIT(&next->is_lock); token->ipt_data = next; } else { bzero(&zero, sizeof(zero)); next = &zero; token->ipt_data = NULL; } if (next->is_next == NULL) ipf_token_mark_complete(token); RWLOCK_EXIT(&softc->ipf_state); obj->ipfo_ptr = itp->igi_data; error = ipf_outobjk(softc, obj, next); if (is != NULL) ipf_state_deref(softc, &is); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_gettable */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to main soft context */ /* softs(I) - pointer to state context structure */ /* data(I) - pointer to ioctl data */ /* */ /* This function handles ioctl requests for tables of state information. */ /* At present the only table it deals with is the hash bucket statistics. */ /* ------------------------------------------------------------------------ */ static int -ipf_state_gettable(softc, softs, data) - ipf_main_softc_t *softc; - ipf_state_softc_t *softs; - char *data; +ipf_state_gettable(ipf_main_softc_t *softc, ipf_state_softc_t *softs, + char *data) { ipftable_t table; int error; error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE); if (error != 0) return error; if (table.ita_type != IPFTABLE_BUCKETS) { IPFERROR(100031); return EINVAL; } error = COPYOUT(softs->ipf_state_stats.iss_bucketlen, table.ita_table, softs->ipf_state_size * sizeof(u_int)); if (error != 0) { IPFERROR(100032); error = EFAULT; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_setpending */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to main soft context */ /* is(I) - pointer to state structure */ /* Locks: ipf_state (read or write) */ /* */ /* Put the state entry on to the pending queue - this queue has a very */ /* short lifetime where items are put that can't be deleted straight away */ /* because of locking issues but we want to delete them ASAP, anyway. */ /* ------------------------------------------------------------------------ */ void -ipf_state_setpending(softc, is) - ipf_main_softc_t *softc; - ipstate_t *is; +ipf_state_setpending(ipf_main_softc_t *softc, ipstate_t *is) { ipf_state_softc_t *softs = softc->ipf_state_soft; ipftq_t *oifq; oifq = is->is_sti.tqe_ifq; if (oifq != NULL) ipf_movequeue(softc->ipf_ticks, &is->is_sti, oifq, &softs->ipf_state_pending); else ipf_queueappend(softc->ipf_ticks, &is->is_sti, &softs->ipf_state_pending, is); MUTEX_ENTER(&is->is_lock); if (is->is_me != NULL) { *is->is_me = NULL; is->is_me = NULL; is->is_ref--; } MUTEX_EXIT(&is->is_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_matchflush */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to main soft context */ /* data(I) - pointer to state structure */ /* Locks: ipf_state (read or write) */ /* */ /* Flush all entries from the list of state entries that match the */ /* properties in the array loaded. */ /* ------------------------------------------------------------------------ */ int -ipf_state_matchflush(softc, data) - ipf_main_softc_t *softc; - caddr_t data; +ipf_state_matchflush(ipf_main_softc_t *softc, caddr_t data) { ipf_state_softc_t *softs = softc->ipf_state_soft; int *array, flushed, error; ipstate_t *state, *statenext; ipfobj_t obj; error = ipf_matcharray_load(softc, data, &obj, &array); if (error != 0) return error; flushed = 0; for (state = softs->ipf_state_list; state != NULL; state = statenext) { statenext = state->is_next; if (ipf_state_matcharray(state, array, softc->ipf_ticks) == 0) { ipf_state_del(softc, state, ISL_FLUSH); flushed++; } } obj.ipfo_retval = flushed; error = BCOPYOUT(&obj, data, sizeof(obj)); KFREES(array, array[0] * sizeof(*array)); return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_matcharray */ /* Returns: int - 0 = no match, 1 = match */ /* Parameters: state(I) - pointer to state structure */ /* array(I) - pointer to ipf matching expression */ /* ticks(I) - current value of ipfilter tick timer */ /* Locks: ipf_state (read or write) */ /* */ /* Compare a state entry with the match array passed in and return a value */ /* to indicate whether or not the matching was successful. */ /* ------------------------------------------------------------------------ */ static int -ipf_state_matcharray(state, array, ticks) - ipstate_t *state; - int *array; - u_long ticks; +ipf_state_matcharray(ipstate_t *state, int *array, u_long ticks) { int i, n, *x, rv, p; ipfexp_t *e; rv = 0; n = array[0]; x = array + 1; for (; n > 0; x += 3 + x[3], rv = 0) { e = (ipfexp_t *)x; n -= e->ipfe_size; if (x[0] == IPF_EXP_END) break; /* * If we need to match the protocol and that doesn't match, * don't even both with the instruction array. */ p = e->ipfe_cmd >> 16; if ((p != 0) && (p != state->is_p)) break; switch (e->ipfe_cmd) { case IPF_EXP_IP_PR : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (state->is_p == e->ipfe_arg0[i]); } break; case IPF_EXP_IP_SRCADDR : if (state->is_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((state->is_saddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; case IPF_EXP_IP_DSTADDR : if (state->is_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((state->is_daddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; case IPF_EXP_IP_ADDR : if (state->is_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((state->is_saddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]) || ((state->is_daddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; #ifdef USE_INET6 case IPF_EXP_IP6_SRCADDR : if (state->is_v != 6) break; for (i = 0; !rv && i < x[3]; i++) { rv |= IP6_MASKEQ(&state->is_src.in6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; case IPF_EXP_IP6_DSTADDR : if (state->is_v != 6) break; for (i = 0; !rv && i < x[3]; i++) { rv |= IP6_MASKEQ(&state->is_dst.in6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; case IPF_EXP_IP6_ADDR : if (state->is_v != 6) break; for (i = 0; !rv && i < x[3]; i++) { rv |= IP6_MASKEQ(&state->is_src.in6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]) || IP6_MASKEQ(&state->is_dst.in6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; #endif case IPF_EXP_UDP_PORT : case IPF_EXP_TCP_PORT : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (state->is_sport == e->ipfe_arg0[i]) || (state->is_dport == e->ipfe_arg0[i]); } break; case IPF_EXP_UDP_SPORT : case IPF_EXP_TCP_SPORT : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (state->is_sport == e->ipfe_arg0[i]); } break; case IPF_EXP_UDP_DPORT : case IPF_EXP_TCP_DPORT : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (state->is_dport == e->ipfe_arg0[i]); } break; case IPF_EXP_TCP_STATE : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (state->is_state[0] == e->ipfe_arg0[i]) || (state->is_state[1] == e->ipfe_arg0[i]); } break; case IPF_EXP_IDLE_GT : rv |= (ticks - state->is_touched > e->ipfe_arg0[0]); break; } /* * Factor in doing a negative match. */ rv ^= e->ipfe_not; if (rv == 0) break; } return rv; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_settimeout */ /* Returns: int 0 = success, else failure */ /* Parameters: softc(I) - pointer to main soft context */ /* t(I) - pointer to tuneable being changed */ /* p(I) - pointer to the new value */ /* */ /* Sets a timeout value for one of the many timeout queues. We find the */ /* correct queue using a somewhat manual process of comparing the timeout */ /* names for each specific value available and calling ipf_apply_timeout on */ /* that queue so that all of the items on it are updated accordingly. */ /* ------------------------------------------------------------------------ */ int -ipf_state_settimeout(softc, t, p) - struct ipf_main_softc_s *softc; - ipftuneable_t *t; - ipftuneval_t *p; +ipf_state_settimeout(struct ipf_main_softc_s *softc, ipftuneable_t *t, + ipftuneval_t *p) { ipf_state_softc_t *softs = softc->ipf_state_soft; /* * In case there is nothing to do... */ if (*t->ipft_pint == p->ipftu_int) return 0; if (!strncmp(t->ipft_name, "tcp_", 4)) return ipf_settimeout_tcp(t, p, softs->ipf_state_tcptq); if (!strcmp(t->ipft_name, "udp_timeout")) { ipf_apply_timeout(&softs->ipf_state_udptq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "udp_ack_timeout")) { ipf_apply_timeout(&softs->ipf_state_udpacktq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "icmp_timeout")) { ipf_apply_timeout(&softs->ipf_state_icmptq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "icmp_ack_timeout")) { ipf_apply_timeout(&softs->ipf_state_icmpacktq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "ip_timeout")) { ipf_apply_timeout(&softs->ipf_state_iptq, p->ipftu_int); } else { IPFERROR(100034); return ESRCH; } /* * Update the tuneable being set. */ *t->ipft_pint = p->ipftu_int; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_rehash */ /* Returns: int 0 = success, else failure */ /* Parameters: softc(I) - pointer to main soft context */ /* t(I) - pointer to tuneable being changed */ /* p(I) - pointer to the new value */ /* */ /* To change the size of the state hash table at runtime, a new table has */ /* to be allocated and then all of the existing entries put in it, bumping */ /* up the bucketlength for it as we go along. */ /* ------------------------------------------------------------------------ */ int -ipf_state_rehash(softc, t, p) - ipf_main_softc_t *softc; - ipftuneable_t *t; - ipftuneval_t *p; +ipf_state_rehash(ipf_main_softc_t *softc, ipftuneable_t *t, ipftuneval_t *p) { ipf_state_softc_t *softs = softc->ipf_state_soft; ipstate_t **newtab, *is; u_long *newseed; u_int *bucketlens; u_int maxbucket; u_int newsize; u_int hv; int i; newsize = p->ipftu_int; /* * In case there is nothing to do... */ if (newsize == softs->ipf_state_size) return 0; KMALLOCS(newtab, ipstate_t **, newsize * sizeof(ipstate_t *)); if (newtab == NULL) { IPFERROR(100035); return ENOMEM; } KMALLOCS(bucketlens, u_int *, newsize * sizeof(u_int)); if (bucketlens == NULL) { KFREES(newtab, newsize * sizeof(*softs->ipf_state_table)); IPFERROR(100036); return ENOMEM; } newseed = ipf_state_seed_alloc(newsize, softs->ipf_state_max); if (newseed == NULL) { KFREES(bucketlens, newsize * sizeof(*bucketlens)); KFREES(newtab, newsize * sizeof(*newtab)); IPFERROR(100037); return ENOMEM; } for (maxbucket = 0, i = newsize; i > 0; i >>= 1) maxbucket++; maxbucket *= 2; bzero((char *)newtab, newsize * sizeof(ipstate_t *)); bzero((char *)bucketlens, newsize * sizeof(u_int)); WRITE_ENTER(&softc->ipf_state); if (softs->ipf_state_table != NULL) { KFREES(softs->ipf_state_table, softs->ipf_state_size * sizeof(*softs->ipf_state_table)); } softs->ipf_state_table = newtab; if (softs->ipf_state_seed != NULL) { KFREES(softs->ipf_state_seed, softs->ipf_state_size * sizeof(*softs->ipf_state_seed)); } softs->ipf_state_seed = newseed; if (softs->ipf_state_stats.iss_bucketlen != NULL) { KFREES(softs->ipf_state_stats.iss_bucketlen, softs->ipf_state_size * sizeof(u_int)); } softs->ipf_state_stats.iss_bucketlen = bucketlens; softs->ipf_state_maxbucket = maxbucket; softs->ipf_state_size = newsize; /* * Walk through the entire list of state table entries and put them * in the new state table, somewhere. Because we have a new table, * we need to restart the counter of how many chains are in use. */ softs->ipf_state_stats.iss_inuse = 0; for (is = softs->ipf_state_list; is != NULL; is = is->is_next) { is->is_hnext = NULL; is->is_phnext = NULL; hv = is->is_hv % softs->ipf_state_size; if (softs->ipf_state_table[hv] != NULL) softs->ipf_state_table[hv]->is_phnext = &is->is_hnext; else softs->ipf_state_stats.iss_inuse++; is->is_phnext = softs->ipf_state_table + hv; is->is_hnext = softs->ipf_state_table[hv]; softs->ipf_state_table[hv] = is; softs->ipf_state_stats.iss_bucketlen[hv]++; } RWLOCK_EXIT(&softc->ipf_state); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_state_add_tq */ /* Returns: ipftq_t * - NULL = failure, else pointer to new timeout */ /* queue */ /* Parameters: softc(I) - pointer to main soft context */ /* ttl(I) - pointer to the ttl for the new queue */ /* */ /* Request a pointer to a timeout queue that has a ttl as given by the */ /* value being passed in. The timeout queue is added tot the list of those */ /* used internally for stateful filtering. */ /* ------------------------------------------------------------------------ */ ipftq_t * -ipf_state_add_tq(softc, ttl) - ipf_main_softc_t *softc; - int ttl; +ipf_state_add_tq(ipf_main_softc_t *softc, int ttl) { ipf_state_softc_t *softs = softc->ipf_state_soft; return ipf_addtimeoutqueue(softc, &softs->ipf_state_usertq, ttl); } #ifndef _KERNEL /* * Display the built up state table rules and mapping entries. */ void -ipf_state_dump(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_state_dump(ipf_main_softc_t *softc, void *arg) { ipf_state_softc_t *softs = arg; ipstate_t *ips; printf("List of active state sessions:\n"); for (ips = softs->ipf_state_list; ips != NULL; ) ips = printstate(ips, opts & (OPT_DEBUG|OPT_VERBOSE), softc->ipf_ticks); } #endif diff --git a/sys/netpfil/ipfilter/netinet/ip_sync.c b/sys/netpfil/ipfilter/netinet/ip_sync.c index 66bf88e877f0..3baddc5bc58a 100644 --- a/sys/netpfil/ipfilter/netinet/ip_sync.c +++ b/sys/netpfil/ipfilter/netinet/ip_sync.c @@ -1,1461 +1,1413 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #if !defined(_KERNEL) && !defined(__KERNEL__) # include # include # include # define _KERNEL # define KERNEL # include # undef _KERNEL # undef KERNEL #else # include # if !defined(__SVR4) # include # endif # include # ifdef __FreeBSD__ # include # endif #endif #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000) # include #endif #if defined(_KERNEL) && defined(__FreeBSD__) # include # include #else # include #endif #include # include #include #if defined(__SVR4) # include # include # ifdef _KERNEL # include # endif # include # include #endif #include #ifdef sun # include #endif #include #include #include #include # include # include #include #include #include "netinet/ip_compat.h" #include #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #include "netinet/ip_sync.h" #ifdef USE_INET6 #include #endif #if defined(__FreeBSD__) # include # if defined(_KERNEL) && !defined(IPFILTER_LKM) # include # include # endif #endif /* END OF INCLUDES */ #if !defined(lint) static const char rcsid[] = "@(#)$Id$"; #endif #define SYNC_STATETABSZ 256 #define SYNC_NATTABSZ 256 typedef struct ipf_sync_softc_s { ipfmutex_t ipf_syncadd; ipfmutex_t ipsl_mutex; ipfrwlock_t ipf_syncstate; ipfrwlock_t ipf_syncnat; #if SOLARIS && defined(_KERNEL) kcondvar_t ipslwait; #endif synclist_t **syncstatetab; synclist_t **syncnattab; synclogent_t *synclog; syncupdent_t *syncupd; u_int ipf_sync_num; u_int ipf_sync_wrap; u_int sl_idx; /* next available sync log entry */ u_int su_idx; /* next available sync update entry */ u_int sl_tail; /* next sync log entry to read */ u_int su_tail; /* next sync update entry to read */ int ipf_sync_log_sz; int ipf_sync_nat_tab_sz; int ipf_sync_state_tab_sz; int ipf_sync_debug; int ipf_sync_events; u_32_t ipf_sync_lastwakeup; int ipf_sync_wake_interval; int ipf_sync_event_high_wm; int ipf_sync_queue_high_wm; int ipf_sync_inited; } ipf_sync_softc_t; static int ipf_sync_flush_table(ipf_sync_softc_t *, int, synclist_t **); static void ipf_sync_wakeup(ipf_main_softc_t *); static void ipf_sync_del(ipf_sync_softc_t *, synclist_t *); static void ipf_sync_poll_wakeup(ipf_main_softc_t *); static int ipf_sync_nat(ipf_main_softc_t *, synchdr_t *, void *); static int ipf_sync_state(ipf_main_softc_t *, synchdr_t *, void *); # if !defined(sparc) && !defined(__hppa) void ipf_sync_tcporder(int, struct tcpdata *); void ipf_sync_natorder(int, struct nat *); void ipf_sync_storder(int, struct ipstate *); # endif void * -ipf_sync_soft_create(softc) - ipf_main_softc_t *softc; +ipf_sync_soft_create(ipf_main_softc_t *softc) { ipf_sync_softc_t *softs; KMALLOC(softs, ipf_sync_softc_t *); if (softs == NULL) { IPFERROR(110024); return NULL; } bzero((char *)softs, sizeof(*softs)); softs->ipf_sync_log_sz = SYNCLOG_SZ; softs->ipf_sync_nat_tab_sz = SYNC_STATETABSZ; softs->ipf_sync_state_tab_sz = SYNC_STATETABSZ; softs->ipf_sync_event_high_wm = SYNCLOG_SZ * 100 / 90; /* 90% */ softs->ipf_sync_queue_high_wm = SYNCLOG_SZ * 100 / 90; /* 90% */ return softs; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_init */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ /* Initialise all of the locks required for the sync code and initialise */ /* any data structures, as required. */ /* ------------------------------------------------------------------------ */ int -ipf_sync_soft_init(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_sync_soft_init(ipf_main_softc_t *softc, void *arg) { ipf_sync_softc_t *softs = arg; KMALLOCS(softs->synclog, synclogent_t *, softs->ipf_sync_log_sz * sizeof(*softs->synclog)); if (softs->synclog == NULL) return -1; bzero((char *)softs->synclog, softs->ipf_sync_log_sz * sizeof(*softs->synclog)); KMALLOCS(softs->syncupd, syncupdent_t *, softs->ipf_sync_log_sz * sizeof(*softs->syncupd)); if (softs->syncupd == NULL) return -2; bzero((char *)softs->syncupd, softs->ipf_sync_log_sz * sizeof(*softs->syncupd)); KMALLOCS(softs->syncstatetab, synclist_t **, softs->ipf_sync_state_tab_sz * sizeof(*softs->syncstatetab)); if (softs->syncstatetab == NULL) return -3; bzero((char *)softs->syncstatetab, softs->ipf_sync_state_tab_sz * sizeof(*softs->syncstatetab)); KMALLOCS(softs->syncnattab, synclist_t **, softs->ipf_sync_nat_tab_sz * sizeof(*softs->syncnattab)); if (softs->syncnattab == NULL) return -3; bzero((char *)softs->syncnattab, softs->ipf_sync_nat_tab_sz * sizeof(*softs->syncnattab)); softs->ipf_sync_num = 1; softs->ipf_sync_wrap = 0; softs->sl_idx = 0; softs->su_idx = 0; softs->sl_tail = 0; softs->su_tail = 0; softs->ipf_sync_events = 0; softs->ipf_sync_lastwakeup = 0; # if SOLARIS && defined(_KERNEL) cv_init(&softs->ipslwait, "ipsl condvar", CV_DRIVER, NULL); # endif RWLOCK_INIT(&softs->ipf_syncstate, "add things to state sync table"); RWLOCK_INIT(&softs->ipf_syncnat, "add things to nat sync table"); MUTEX_INIT(&softs->ipf_syncadd, "add things to sync table"); MUTEX_INIT(&softs->ipsl_mutex, "read ring lock"); softs->ipf_sync_inited = 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_unload */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ /* Destroy the locks created when initialising and free any memory in use */ /* with the synchronisation tables. */ /* ------------------------------------------------------------------------ */ int -ipf_sync_soft_fini(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_sync_soft_fini(ipf_main_softc_t *softc, void *arg) { ipf_sync_softc_t *softs = arg; if (softs->syncnattab != NULL) { ipf_sync_flush_table(softs, softs->ipf_sync_nat_tab_sz, softs->syncnattab); KFREES(softs->syncnattab, softs->ipf_sync_nat_tab_sz * sizeof(*softs->syncnattab)); softs->syncnattab = NULL; } if (softs->syncstatetab != NULL) { ipf_sync_flush_table(softs, softs->ipf_sync_state_tab_sz, softs->syncstatetab); KFREES(softs->syncstatetab, softs->ipf_sync_state_tab_sz * sizeof(*softs->syncstatetab)); softs->syncstatetab = NULL; } if (softs->syncupd != NULL) { KFREES(softs->syncupd, softs->ipf_sync_log_sz * sizeof(*softs->syncupd)); softs->syncupd = NULL; } if (softs->synclog != NULL) { KFREES(softs->synclog, softs->ipf_sync_log_sz * sizeof(*softs->synclog)); softs->synclog = NULL; } if (softs->ipf_sync_inited == 1) { MUTEX_DESTROY(&softs->ipsl_mutex); MUTEX_DESTROY(&softs->ipf_syncadd); RW_DESTROY(&softs->ipf_syncnat); RW_DESTROY(&softs->ipf_syncstate); softs->ipf_sync_inited = 0; } return 0; } void -ipf_sync_soft_destroy(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_sync_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_sync_softc_t *softs = arg; KFREE(softs); } # if !defined(sparc) /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_tcporder */ /* Returns: Nil */ /* Parameters: way(I) - direction of byte order conversion. */ /* td(IO) - pointer to data to be converted. */ /* */ /* Do byte swapping on values in the TCP state information structure that */ /* need to be used at both ends by the host in their native byte order. */ /* ------------------------------------------------------------------------ */ void -ipf_sync_tcporder(way, td) - int way; - tcpdata_t *td; +ipf_sync_tcporder(int way, tcpdata_t *td) { if (way) { td->td_maxwin = htons(td->td_maxwin); td->td_end = htonl(td->td_end); td->td_maxend = htonl(td->td_maxend); } else { td->td_maxwin = ntohs(td->td_maxwin); td->td_end = ntohl(td->td_end); td->td_maxend = ntohl(td->td_maxend); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_natorder */ /* Returns: Nil */ /* Parameters: way(I) - direction of byte order conversion. */ /* nat(IO) - pointer to data to be converted. */ /* */ /* Do byte swapping on values in the NAT data structure that need to be */ /* used at both ends by the host in their native byte order. */ /* ------------------------------------------------------------------------ */ void -ipf_sync_natorder(way, n) - int way; - nat_t *n; +ipf_sync_natorder(int way, nat_t *n) { if (way) { n->nat_age = htonl(n->nat_age); n->nat_flags = htonl(n->nat_flags); n->nat_ipsumd = htonl(n->nat_ipsumd); n->nat_use = htonl(n->nat_use); n->nat_dir = htonl(n->nat_dir); } else { n->nat_age = ntohl(n->nat_age); n->nat_flags = ntohl(n->nat_flags); n->nat_ipsumd = ntohl(n->nat_ipsumd); n->nat_use = ntohl(n->nat_use); n->nat_dir = ntohl(n->nat_dir); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_storder */ /* Returns: Nil */ /* Parameters: way(I) - direction of byte order conversion. */ /* ips(IO) - pointer to data to be converted. */ /* */ /* Do byte swapping on values in the IP state data structure that need to */ /* be used at both ends by the host in their native byte order. */ /* ------------------------------------------------------------------------ */ void -ipf_sync_storder(way, ips) - int way; - ipstate_t *ips; +ipf_sync_storder(int way, ipstate_t *ips) { ipf_sync_tcporder(way, &ips->is_tcp.ts_data[0]); ipf_sync_tcporder(way, &ips->is_tcp.ts_data[1]); if (way) { ips->is_hv = htonl(ips->is_hv); ips->is_die = htonl(ips->is_die); ips->is_pass = htonl(ips->is_pass); ips->is_flags = htonl(ips->is_flags); ips->is_opt[0] = htonl(ips->is_opt[0]); ips->is_opt[1] = htonl(ips->is_opt[1]); ips->is_optmsk[0] = htonl(ips->is_optmsk[0]); ips->is_optmsk[1] = htonl(ips->is_optmsk[1]); ips->is_sec = htons(ips->is_sec); ips->is_secmsk = htons(ips->is_secmsk); ips->is_auth = htons(ips->is_auth); ips->is_authmsk = htons(ips->is_authmsk); ips->is_s0[0] = htonl(ips->is_s0[0]); ips->is_s0[1] = htonl(ips->is_s0[1]); ips->is_smsk[0] = htons(ips->is_smsk[0]); ips->is_smsk[1] = htons(ips->is_smsk[1]); } else { ips->is_hv = ntohl(ips->is_hv); ips->is_die = ntohl(ips->is_die); ips->is_pass = ntohl(ips->is_pass); ips->is_flags = ntohl(ips->is_flags); ips->is_opt[0] = ntohl(ips->is_opt[0]); ips->is_opt[1] = ntohl(ips->is_opt[1]); ips->is_optmsk[0] = ntohl(ips->is_optmsk[0]); ips->is_optmsk[1] = ntohl(ips->is_optmsk[1]); ips->is_sec = ntohs(ips->is_sec); ips->is_secmsk = ntohs(ips->is_secmsk); ips->is_auth = ntohs(ips->is_auth); ips->is_authmsk = ntohs(ips->is_authmsk); ips->is_s0[0] = ntohl(ips->is_s0[0]); ips->is_s0[1] = ntohl(ips->is_s0[1]); ips->is_smsk[0] = ntohl(ips->is_smsk[0]); ips->is_smsk[1] = ntohl(ips->is_smsk[1]); } } # else /* !defined(sparc) */ # define ipf_sync_tcporder(x,y) # define ipf_sync_natorder(x,y) # define ipf_sync_storder(x,y) # endif /* !defined(sparc) */ /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_write */ /* Returns: int - 0 == success, else error value. */ /* Parameters: uio(I) - pointer to information about data to write */ /* */ /* Moves data from user space into the kernel and uses it for updating data */ /* structures in the state/NAT tables. */ /* ------------------------------------------------------------------------ */ int -ipf_sync_write(softc, uio) - ipf_main_softc_t *softc; - struct uio *uio; +ipf_sync_write(ipf_main_softc_t *softc, struct uio *uio) { ipf_sync_softc_t *softs = softc->ipf_sync_soft; synchdr_t sh; /* * THIS MUST BE SUFFICIENT LARGE TO STORE * ANY POSSIBLE DATA TYPE */ char data[2048]; int err = 0; # if defined(__NetBSD__) || defined(__FreeBSD__) uio->uio_rw = UIO_WRITE; # endif /* Try to get bytes */ while (uio->uio_resid > 0) { if (uio->uio_resid >= sizeof(sh)) { err = UIOMOVE(&sh, sizeof(sh), UIO_WRITE, uio); if (err) { if (softs->ipf_sync_debug > 2) printf("uiomove(header) failed: %d\n", err); return err; } /* convert to host order */ sh.sm_magic = ntohl(sh.sm_magic); sh.sm_len = ntohl(sh.sm_len); sh.sm_num = ntohl(sh.sm_num); if (softs->ipf_sync_debug > 8) printf("[%d] Read v:%d p:%d cmd:%d table:%d rev:%d len:%d magic:%x\n", sh.sm_num, sh.sm_v, sh.sm_p, sh.sm_cmd, sh.sm_table, sh.sm_rev, sh.sm_len, sh.sm_magic); if (sh.sm_magic != SYNHDRMAGIC) { if (softs->ipf_sync_debug > 2) printf("uiomove(header) invalid %s\n", "magic"); IPFERROR(110001); return EINVAL; } if (sh.sm_v != 4 && sh.sm_v != 6) { if (softs->ipf_sync_debug > 2) printf("uiomove(header) invalid %s\n", "protocol"); IPFERROR(110002); return EINVAL; } if (sh.sm_cmd > SMC_MAXCMD) { if (softs->ipf_sync_debug > 2) printf("uiomove(header) invalid %s\n", "command"); IPFERROR(110003); return EINVAL; } if (sh.sm_table > SMC_MAXTBL) { if (softs->ipf_sync_debug > 2) printf("uiomove(header) invalid %s\n", "table"); IPFERROR(110004); return EINVAL; } } else { /* unsufficient data, wait until next call */ if (softs->ipf_sync_debug > 2) printf("uiomove(header) insufficient data"); IPFERROR(110005); return EAGAIN; } /* * We have a header, so try to read the amount of data * needed for the request */ /* not supported */ if (sh.sm_len == 0) { if (softs->ipf_sync_debug > 2) printf("uiomove(data zero length %s\n", "not supported"); IPFERROR(110006); return EINVAL; } if (uio->uio_resid >= sh.sm_len) { err = UIOMOVE(data, sh.sm_len, UIO_WRITE, uio); if (err) { if (softs->ipf_sync_debug > 2) printf("uiomove(data) failed: %d\n", err); return err; } if (softs->ipf_sync_debug > 7) printf("uiomove(data) %d bytes read\n", sh.sm_len); if (sh.sm_table == SMC_STATE) err = ipf_sync_state(softc, &sh, data); else if (sh.sm_table == SMC_NAT) err = ipf_sync_nat(softc, &sh, data); if (softs->ipf_sync_debug > 7) printf("[%d] Finished with error %d\n", sh.sm_num, err); } else { /* insufficient data, wait until next call */ if (softs->ipf_sync_debug > 2) printf("uiomove(data) %s %d bytes, got %d\n", "insufficient data, need", sh.sm_len, (int)uio->uio_resid); IPFERROR(110007); return EAGAIN; } } /* no more data */ return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_read */ /* Returns: int - 0 == success, else error value. */ /* Parameters: uio(O) - pointer to information about where to store data */ /* */ /* This function is called when a user program wants to read some data */ /* for pending state/NAT updates. If no data is available, the caller is */ /* put to sleep, pending a wakeup from the "lower half" of this code. */ /* ------------------------------------------------------------------------ */ int -ipf_sync_read(softc, uio) - ipf_main_softc_t *softc; - struct uio *uio; +ipf_sync_read(ipf_main_softc_t *softc, struct uio *uio) { ipf_sync_softc_t *softs = softc->ipf_sync_soft; syncupdent_t *su; synclogent_t *sl; int err = 0; if ((uio->uio_resid & 3) || (uio->uio_resid < 8)) { IPFERROR(110008); return EINVAL; } # if defined(__NetBSD__) || defined(__FreeBSD__) uio->uio_rw = UIO_READ; # endif MUTEX_ENTER(&softs->ipsl_mutex); while ((softs->sl_tail == softs->sl_idx) && (softs->su_tail == softs->su_idx)) { # if defined(_KERNEL) # if SOLARIS if (!cv_wait_sig(&softs->ipslwait, &softs->ipsl_mutex.ipf_lk)) { MUTEX_EXIT(&softs->ipsl_mutex); IPFERROR(110009); return EINTR; } # else MUTEX_EXIT(&softs->ipsl_mutex); err = SLEEP(&softs->sl_tail, "ipl sleep"); if (err) { IPFERROR(110012); return EINTR; } MUTEX_ENTER(&softs->ipsl_mutex); # endif /* SOLARIS */ # endif /* _KERNEL */ } while ((softs->sl_tail < softs->sl_idx) && (uio->uio_resid > sizeof(*sl))) { sl = softs->synclog + softs->sl_tail++; MUTEX_EXIT(&softs->ipsl_mutex); err = UIOMOVE(sl, sizeof(*sl), UIO_READ, uio); if (err != 0) goto goterror; MUTEX_ENTER(&softs->ipsl_mutex); } while ((softs->su_tail < softs->su_idx) && (uio->uio_resid > sizeof(*su))) { su = softs->syncupd + softs->su_tail; softs->su_tail++; MUTEX_EXIT(&softs->ipsl_mutex); err = UIOMOVE(su, sizeof(*su), UIO_READ, uio); if (err != 0) goto goterror; MUTEX_ENTER(&softs->ipsl_mutex); if (su->sup_hdr.sm_sl != NULL) su->sup_hdr.sm_sl->sl_idx = -1; } if (softs->sl_tail == softs->sl_idx) softs->sl_tail = softs->sl_idx = 0; if (softs->su_tail == softs->su_idx) softs->su_tail = softs->su_idx = 0; MUTEX_EXIT(&softs->ipsl_mutex); goterror: return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_state */ /* Returns: int - 0 == success, else error value. */ /* Parameters: sp(I) - pointer to sync packet data header */ /* uio(I) - pointer to user data for further information */ /* */ /* Updates the state table according to information passed in the sync */ /* header. As required, more data is fetched from the uio structure but */ /* varies depending on the contents of the sync header. This function can */ /* create a new state entry or update one. Deletion is left to the state */ /* structures being timed out correctly. */ /* ------------------------------------------------------------------------ */ static int -ipf_sync_state(softc, sp, data) - ipf_main_softc_t *softc; - synchdr_t *sp; - void *data; +ipf_sync_state(ipf_main_softc_t *softc, synchdr_t *sp, void *data) { ipf_sync_softc_t *softs = softc->ipf_sync_soft; synctcp_update_t su; ipstate_t *is, sn; synclist_t *sl; frentry_t *fr; u_int hv; int err = 0; hv = sp->sm_num & (softs->ipf_sync_state_tab_sz - 1); switch (sp->sm_cmd) { case SMC_CREATE : bcopy(data, &sn, sizeof(sn)); KMALLOC(is, ipstate_t *); if (is == NULL) { IPFERROR(110013); err = ENOMEM; break; } KMALLOC(sl, synclist_t *); if (sl == NULL) { IPFERROR(110014); err = ENOMEM; KFREE(is); break; } bzero((char *)is, offsetof(ipstate_t, is_die)); bcopy((char *)&sn.is_die, (char *)&is->is_die, sizeof(*is) - offsetof(ipstate_t, is_die)); ipf_sync_storder(0, is); /* * We need to find the same rule on the slave as was used on * the master to create this state entry. */ READ_ENTER(&softc->ipf_mutex); fr = ipf_getrulen(softc, IPL_LOGIPF, sn.is_group, sn.is_rulen); if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; fr->fr_statecnt++; MUTEX_EXIT(&fr->fr_lock); } RWLOCK_EXIT(&softc->ipf_mutex); if (softs->ipf_sync_debug > 4) printf("[%d] Filter rules = %p\n", sp->sm_num, fr); is->is_rule = fr; is->is_sync = sl; sl->sl_idx = -1; sl->sl_ips = is; bcopy(sp, &sl->sl_hdr, sizeof(struct synchdr)); WRITE_ENTER(&softs->ipf_syncstate); WRITE_ENTER(&softc->ipf_state); sl->sl_pnext = softs->syncstatetab + hv; sl->sl_next = softs->syncstatetab[hv]; if (softs->syncstatetab[hv] != NULL) softs->syncstatetab[hv]->sl_pnext = &sl->sl_next; softs->syncstatetab[hv] = sl; MUTEX_DOWNGRADE(&softs->ipf_syncstate); ipf_state_insert(softc, is, sp->sm_rev); /* * Do not initialise the interface pointers for the state * entry as the full complement of interface names may not * be present. * * Put this state entry on its timeout queue. */ /*fr_setstatequeue(is, sp->sm_rev);*/ break; case SMC_UPDATE : bcopy(data, &su, sizeof(su)); if (softs->ipf_sync_debug > 4) printf("[%d] Update age %lu state %d/%d \n", sp->sm_num, su.stu_age, su.stu_state[0], su.stu_state[1]); READ_ENTER(&softs->ipf_syncstate); for (sl = softs->syncstatetab[hv]; (sl != NULL); sl = sl->sl_next) if (sl->sl_hdr.sm_num == sp->sm_num) break; if (sl == NULL) { if (softs->ipf_sync_debug > 1) printf("[%d] State not found - can't update\n", sp->sm_num); RWLOCK_EXIT(&softs->ipf_syncstate); IPFERROR(110015); err = ENOENT; break; } READ_ENTER(&softc->ipf_state); if (softs->ipf_sync_debug > 6) printf("[%d] Data from state v:%d p:%d cmd:%d table:%d rev:%d\n", sp->sm_num, sl->sl_hdr.sm_v, sl->sl_hdr.sm_p, sl->sl_hdr.sm_cmd, sl->sl_hdr.sm_table, sl->sl_hdr.sm_rev); is = sl->sl_ips; MUTEX_ENTER(&is->is_lock); switch (sp->sm_p) { case IPPROTO_TCP : /* XXX FV --- shouldn't we do ntohl/htonl???? XXX */ is->is_send = su.stu_data[0].td_end; is->is_maxsend = su.stu_data[0].td_maxend; is->is_maxswin = su.stu_data[0].td_maxwin; is->is_state[0] = su.stu_state[0]; is->is_dend = su.stu_data[1].td_end; is->is_maxdend = su.stu_data[1].td_maxend; is->is_maxdwin = su.stu_data[1].td_maxwin; is->is_state[1] = su.stu_state[1]; break; default : break; } if (softs->ipf_sync_debug > 6) printf("[%d] Setting timers for state\n", sp->sm_num); ipf_state_setqueue(softc, is, sp->sm_rev); MUTEX_EXIT(&is->is_lock); break; default : IPFERROR(110016); err = EINVAL; break; } if (err == 0) { RWLOCK_EXIT(&softc->ipf_state); RWLOCK_EXIT(&softs->ipf_syncstate); } if (softs->ipf_sync_debug > 6) printf("[%d] Update completed with error %d\n", sp->sm_num, err); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_del */ /* Returns: Nil */ /* Parameters: sl(I) - pointer to synclist object to delete */ /* */ /* Deletes an object from the synclist. */ /* ------------------------------------------------------------------------ */ static void -ipf_sync_del(softs, sl) - ipf_sync_softc_t *softs; - synclist_t *sl; +ipf_sync_del(ipf_sync_softc_t *softs, synclist_t *sl) { *sl->sl_pnext = sl->sl_next; if (sl->sl_next != NULL) sl->sl_next->sl_pnext = sl->sl_pnext; if (sl->sl_idx != -1) softs->syncupd[sl->sl_idx].sup_hdr.sm_sl = NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_del_state */ /* Returns: Nil */ /* Parameters: sl(I) - pointer to synclist object to delete */ /* */ /* Deletes an object from the synclist state table and free's its memory. */ /* ------------------------------------------------------------------------ */ void -ipf_sync_del_state(arg, sl) - void *arg; - synclist_t *sl; +ipf_sync_del_state(void *arg, synclist_t *sl) { ipf_sync_softc_t *softs = arg; WRITE_ENTER(&softs->ipf_syncstate); ipf_sync_del(softs, sl); RWLOCK_EXIT(&softs->ipf_syncstate); KFREE(sl); } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_del_nat */ /* Returns: Nil */ /* Parameters: sl(I) - pointer to synclist object to delete */ /* */ /* Deletes an object from the synclist nat table and free's its memory. */ /* ------------------------------------------------------------------------ */ void -ipf_sync_del_nat(arg, sl) - void *arg; - synclist_t *sl; +ipf_sync_del_nat(void *arg, synclist_t *sl) { ipf_sync_softc_t *softs = arg; WRITE_ENTER(&softs->ipf_syncnat); ipf_sync_del(softs, sl); RWLOCK_EXIT(&softs->ipf_syncnat); KFREE(sl); } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_nat */ /* Returns: int - 0 == success, else error value. */ /* Parameters: sp(I) - pointer to sync packet data header */ /* uio(I) - pointer to user data for further information */ /* */ /* Updates the NAT table according to information passed in the sync */ /* header. As required, more data is fetched from the uio structure but */ /* varies depending on the contents of the sync header. This function can */ /* create a new NAT entry or update one. Deletion is left to the NAT */ /* structures being timed out correctly. */ /* ------------------------------------------------------------------------ */ static int -ipf_sync_nat(softc, sp, data) - ipf_main_softc_t *softc; - synchdr_t *sp; - void *data; +ipf_sync_nat(ipf_main_softc_t *softc, synchdr_t *sp, void *data) { ipf_sync_softc_t *softs = softc->ipf_sync_soft; syncupdent_t su; nat_t *n, *nat; synclist_t *sl; u_int hv = 0; int err = 0; READ_ENTER(&softs->ipf_syncnat); switch (sp->sm_cmd) { case SMC_CREATE : KMALLOC(n, nat_t *); if (n == NULL) { IPFERROR(110017); err = ENOMEM; break; } KMALLOC(sl, synclist_t *); if (sl == NULL) { IPFERROR(110018); err = ENOMEM; KFREE(n); break; } nat = (nat_t *)data; bzero((char *)n, offsetof(nat_t, nat_age)); bcopy((char *)&nat->nat_age, (char *)&n->nat_age, sizeof(*n) - offsetof(nat_t, nat_age)); ipf_sync_natorder(0, n); n->nat_sync = sl; n->nat_rev = sl->sl_rev; sl->sl_idx = -1; sl->sl_ipn = n; sl->sl_num = ntohl(sp->sm_num); WRITE_ENTER(&softc->ipf_nat); sl->sl_pnext = softs->syncnattab + hv; sl->sl_next = softs->syncnattab[hv]; if (softs->syncnattab[hv] != NULL) softs->syncnattab[hv]->sl_pnext = &sl->sl_next; softs->syncnattab[hv] = sl; (void) ipf_nat_insert(softc, softc->ipf_nat_soft, n); RWLOCK_EXIT(&softc->ipf_nat); break; case SMC_UPDATE : bcopy(data, &su, sizeof(su)); for (sl = softs->syncnattab[hv]; (sl != NULL); sl = sl->sl_next) if (sl->sl_hdr.sm_num == sp->sm_num) break; if (sl == NULL) { IPFERROR(110019); err = ENOENT; break; } READ_ENTER(&softc->ipf_nat); nat = sl->sl_ipn; nat->nat_rev = sl->sl_rev; MUTEX_ENTER(&nat->nat_lock); ipf_nat_setqueue(softc, softc->ipf_nat_soft, nat); MUTEX_EXIT(&nat->nat_lock); RWLOCK_EXIT(&softc->ipf_nat); break; default : IPFERROR(110020); err = EINVAL; break; } RWLOCK_EXIT(&softs->ipf_syncnat); return err; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_new */ /* Returns: synclist_t* - NULL == failure, else pointer to new synclist */ /* data structure. */ /* Parameters: tab(I) - type of synclist_t to create */ /* fin(I) - pointer to packet information */ /* ptr(I) - pointer to owning object */ /* */ /* Creates a new sync table entry and notifies any sleepers that it's there */ /* waiting to be processed. */ /* ------------------------------------------------------------------------ */ synclist_t * -ipf_sync_new(softc, tab, fin, ptr) - ipf_main_softc_t *softc; - int tab; - fr_info_t *fin; - void *ptr; +ipf_sync_new(ipf_main_softc_t *softc, int tab, fr_info_t *fin, void *ptr) { ipf_sync_softc_t *softs = softc->ipf_sync_soft; synclist_t *sl, *ss; synclogent_t *sle; u_int hv, sz; if (softs->sl_idx == softs->ipf_sync_log_sz) return NULL; KMALLOC(sl, synclist_t *); if (sl == NULL) return NULL; MUTEX_ENTER(&softs->ipf_syncadd); /* * Get a unique number for this synclist_t. The number is only meant * to be unique for the lifetime of the structure and may be reused * later. */ softs->ipf_sync_num++; if (softs->ipf_sync_num == 0) { softs->ipf_sync_num = 1; softs->ipf_sync_wrap++; } /* * Use the synch number of the object as the hash key. Should end up * with relatively even distribution over time. * XXX - an attacker could lunch an DoS attack, of sorts, if they are * the only one causing new table entries by only keeping open every * nth connection they make, where n is a value in the interval * [0, SYNC_STATETABSZ-1]. */ switch (tab) { case SMC_STATE : hv = softs->ipf_sync_num & (softs->ipf_sync_state_tab_sz - 1); while (softs->ipf_sync_wrap != 0) { for (ss = softs->syncstatetab[hv]; ss; ss = ss->sl_next) if (ss->sl_hdr.sm_num == softs->ipf_sync_num) break; if (ss == NULL) break; softs->ipf_sync_num++; hv = softs->ipf_sync_num & (softs->ipf_sync_state_tab_sz - 1); } sl->sl_pnext = softs->syncstatetab + hv; sl->sl_next = softs->syncstatetab[hv]; softs->syncstatetab[hv] = sl; break; case SMC_NAT : hv = softs->ipf_sync_num & (softs->ipf_sync_nat_tab_sz - 1); while (softs->ipf_sync_wrap != 0) { for (ss = softs->syncnattab[hv]; ss; ss = ss->sl_next) if (ss->sl_hdr.sm_num == softs->ipf_sync_num) break; if (ss == NULL) break; softs->ipf_sync_num++; hv = softs->ipf_sync_num & (softs->ipf_sync_nat_tab_sz - 1); } sl->sl_pnext = softs->syncnattab + hv; sl->sl_next = softs->syncnattab[hv]; softs->syncnattab[hv] = sl; break; default : break; } sl->sl_num = softs->ipf_sync_num; MUTEX_EXIT(&softs->ipf_syncadd); sl->sl_magic = htonl(SYNHDRMAGIC); sl->sl_v = fin->fin_v; sl->sl_p = fin->fin_p; sl->sl_cmd = SMC_CREATE; sl->sl_idx = -1; sl->sl_table = tab; sl->sl_rev = fin->fin_rev; if (tab == SMC_STATE) { sl->sl_ips = ptr; sz = sizeof(*sl->sl_ips); } else if (tab == SMC_NAT) { sl->sl_ipn = ptr; sz = sizeof(*sl->sl_ipn); } else { ptr = NULL; sz = 0; } sl->sl_len = sz; /* * Create the log entry to be read by a user daemon. When it has been * finished and put on the queue, send a signal to wakeup any waiters. */ MUTEX_ENTER(&softs->ipf_syncadd); sle = softs->synclog + softs->sl_idx++; bcopy((char *)&sl->sl_hdr, (char *)&sle->sle_hdr, sizeof(sle->sle_hdr)); sle->sle_hdr.sm_num = htonl(sle->sle_hdr.sm_num); sle->sle_hdr.sm_len = htonl(sle->sle_hdr.sm_len); if (ptr != NULL) { bcopy((char *)ptr, (char *)&sle->sle_un, sz); if (tab == SMC_STATE) { ipf_sync_storder(1, &sle->sle_un.sleu_ips); } else if (tab == SMC_NAT) { ipf_sync_natorder(1, &sle->sle_un.sleu_ipn); } } MUTEX_EXIT(&softs->ipf_syncadd); ipf_sync_wakeup(softc); return sl; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_update */ /* Returns: Nil */ /* Parameters: tab(I) - type of synclist_t to create */ /* fin(I) - pointer to packet information */ /* sl(I) - pointer to synchronisation object */ /* */ /* For outbound packets, only, create an sync update record for the user */ /* process to read. */ /* ------------------------------------------------------------------------ */ void -ipf_sync_update(softc, tab, fin, sl) - ipf_main_softc_t *softc; - int tab; - fr_info_t *fin; - synclist_t *sl; +ipf_sync_update(ipf_main_softc_t *softc, int tab, fr_info_t *fin, + synclist_t *sl) { ipf_sync_softc_t *softs = softc->ipf_sync_soft; synctcp_update_t *st; syncupdent_t *slu; ipstate_t *ips; nat_t *nat; ipfrwlock_t *lock; if (fin->fin_out == 0 || sl == NULL) return; if (tab == SMC_STATE) { lock = &softs->ipf_syncstate; } else { lock = &softs->ipf_syncnat; } READ_ENTER(lock); if (sl->sl_idx == -1) { MUTEX_ENTER(&softs->ipf_syncadd); slu = softs->syncupd + softs->su_idx; sl->sl_idx = softs->su_idx++; MUTEX_EXIT(&softs->ipf_syncadd); bcopy((char *)&sl->sl_hdr, (char *)&slu->sup_hdr, sizeof(slu->sup_hdr)); slu->sup_hdr.sm_magic = htonl(SYNHDRMAGIC); slu->sup_hdr.sm_sl = sl; slu->sup_hdr.sm_cmd = SMC_UPDATE; slu->sup_hdr.sm_table = tab; slu->sup_hdr.sm_num = htonl(sl->sl_num); slu->sup_hdr.sm_len = htonl(sizeof(struct synctcp_update)); slu->sup_hdr.sm_rev = fin->fin_rev; # if 0 if (fin->fin_p == IPPROTO_TCP) { st->stu_len[0] = 0; st->stu_len[1] = 0; } # endif } else slu = softs->syncupd + sl->sl_idx; /* * Only TCP has complex timeouts, others just use default timeouts. * For TCP, we only need to track the connection state and window. */ if (fin->fin_p == IPPROTO_TCP) { st = &slu->sup_tcp; if (tab == SMC_STATE) { ips = sl->sl_ips; st->stu_age = htonl(ips->is_die); st->stu_data[0].td_end = ips->is_send; st->stu_data[0].td_maxend = ips->is_maxsend; st->stu_data[0].td_maxwin = ips->is_maxswin; st->stu_state[0] = ips->is_state[0]; st->stu_data[1].td_end = ips->is_dend; st->stu_data[1].td_maxend = ips->is_maxdend; st->stu_data[1].td_maxwin = ips->is_maxdwin; st->stu_state[1] = ips->is_state[1]; } else if (tab == SMC_NAT) { nat = sl->sl_ipn; st->stu_age = htonl(nat->nat_age); } } RWLOCK_EXIT(lock); ipf_sync_wakeup(softc); } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_flush_table */ /* Returns: int - number of entries freed by flushing table */ /* Parameters: tabsize(I) - size of the array pointed to by table */ /* table(I) - pointer to sync table to empty */ /* */ /* Walk through a table of sync entries and free each one. It is assumed */ /* that some lock is held so that nobody else tries to access the table */ /* during this cleanup. */ /* ------------------------------------------------------------------------ */ static int -ipf_sync_flush_table(softs, tabsize, table) - ipf_sync_softc_t *softs; - int tabsize; - synclist_t **table; +ipf_sync_flush_table(ipf_sync_softc_t *softs, int tabsize, synclist_t **table) { synclist_t *sl; int i, items; items = 0; for (i = 0; i < tabsize; i++) { while ((sl = table[i]) != NULL) { switch (sl->sl_table) { case SMC_STATE : if (sl->sl_ips != NULL) sl->sl_ips->is_sync = NULL; break; case SMC_NAT : if (sl->sl_ipn != NULL) sl->sl_ipn->nat_sync = NULL; break; } if (sl->sl_next != NULL) sl->sl_next->sl_pnext = sl->sl_pnext; table[i] = sl->sl_next; if (sl->sl_idx != -1) softs->syncupd[sl->sl_idx].sup_hdr.sm_sl = NULL; KFREE(sl); items++; } } return items; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_ioctl */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: data(I) - pointer to ioctl data */ /* cmd(I) - ioctl command integer */ /* mode(I) - file mode bits used with open */ /* */ /* This function currently does not handle any ioctls and so just returns */ /* EINVAL on all occasions. */ /* ------------------------------------------------------------------------ */ int -ipf_sync_ioctl(softc, data, cmd, mode, uid, ctx) - ipf_main_softc_t *softc; - caddr_t data; - ioctlcmd_t cmd; - int mode, uid; - void *ctx; +ipf_sync_ioctl(ipf_main_softc_t *softc, caddr_t data, ioctlcmd_t cmd, + int mode, int uid, void *ctx) { ipf_sync_softc_t *softs = softc->ipf_sync_soft; int error, i; SPL_INT(s); switch (cmd) { case SIOCIPFFL: error = BCOPYIN(data, &i, sizeof(i)); if (error != 0) { IPFERROR(110023); error = EFAULT; break; } switch (i) { case SMC_RLOG : SPL_NET(s); MUTEX_ENTER(&softs->ipsl_mutex); i = (softs->sl_tail - softs->sl_idx) + (softs->su_tail - softs->su_idx); softs->sl_idx = 0; softs->su_idx = 0; softs->sl_tail = 0; softs->su_tail = 0; MUTEX_EXIT(&softs->ipsl_mutex); SPL_X(s); break; case SMC_NAT : SPL_NET(s); WRITE_ENTER(&softs->ipf_syncnat); i = ipf_sync_flush_table(softs, SYNC_NATTABSZ, softs->syncnattab); RWLOCK_EXIT(&softs->ipf_syncnat); SPL_X(s); break; case SMC_STATE : SPL_NET(s); WRITE_ENTER(&softs->ipf_syncstate); i = ipf_sync_flush_table(softs, SYNC_STATETABSZ, softs->syncstatetab); RWLOCK_EXIT(&softs->ipf_syncstate); SPL_X(s); break; } error = BCOPYOUT(&i, data, sizeof(i)); if (error != 0) { IPFERROR(110022); error = EFAULT; } break; default : IPFERROR(110021); error = EINVAL; break; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_canread */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: Nil */ /* */ /* This function provides input to the poll handler about whether or not */ /* there is data waiting to be read from the /dev/ipsync device. */ /* ------------------------------------------------------------------------ */ int -ipf_sync_canread(arg) - void *arg; +ipf_sync_canread(void *arg) { ipf_sync_softc_t *softs = arg; return !((softs->sl_tail == softs->sl_idx) && (softs->su_tail == softs->su_idx)); } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_canwrite */ /* Returns: int - 1 == can always write */ /* Parameters: Nil */ /* */ /* This function lets the poll handler know that it is always ready willing */ /* to accept write events. */ /* XXX Maybe this should return false if the sync table is full? */ /* ------------------------------------------------------------------------ */ int -ipf_sync_canwrite(arg) - void *arg; +ipf_sync_canwrite(void *arg) { return 1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_wakeup */ /* Parameters: Nil */ /* Returns: Nil */ /* */ /* This function implements the heuristics that decide how often to */ /* generate a poll wakeup for programs that are waiting for information */ /* about when they can do a read on /dev/ipsync. */ /* */ /* There are three different considerations here: */ /* - do not keep a program waiting too long: ipf_sync_wake_interval is the */ /* maximum number of ipf ticks to let pass by; */ /* - do not let the queue of ouststanding things to generate notifies for */ /* get too full (ipf_sync_queue_high_wm is the high water mark); */ /* - do not let too many events get collapsed in before deciding that the */ /* other host(s) need an update (ipf_sync_event_high_wm is the high water */ /* mark for this counter.) */ /* ------------------------------------------------------------------------ */ static void -ipf_sync_wakeup(softc) - ipf_main_softc_t *softc; +ipf_sync_wakeup(ipf_main_softc_t *softc) { ipf_sync_softc_t *softs = softc->ipf_sync_soft; softs->ipf_sync_events++; if ((softc->ipf_ticks > softs->ipf_sync_lastwakeup + softs->ipf_sync_wake_interval) || (softs->ipf_sync_events > softs->ipf_sync_event_high_wm) || ((softs->sl_tail - softs->sl_idx) > softs->ipf_sync_queue_high_wm) || ((softs->su_tail - softs->su_idx) > softs->ipf_sync_queue_high_wm)) { ipf_sync_poll_wakeup(softc); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_poll_wakeup */ /* Parameters: Nil */ /* Returns: Nil */ /* */ /* Deliver a poll wakeup and reset counters for two of the three heuristics */ /* ------------------------------------------------------------------------ */ static void -ipf_sync_poll_wakeup(softc) - ipf_main_softc_t *softc; +ipf_sync_poll_wakeup(ipf_main_softc_t *softc) { ipf_sync_softc_t *softs = softc->ipf_sync_soft; softs->ipf_sync_events = 0; softs->ipf_sync_lastwakeup = softc->ipf_ticks; # ifdef _KERNEL # if SOLARIS MUTEX_ENTER(&softs->ipsl_mutex); cv_signal(&softs->ipslwait); MUTEX_EXIT(&softs->ipsl_mutex); pollwakeup(&softc->ipf_poll_head[IPL_LOGSYNC], POLLIN|POLLRDNORM); # else WAKEUP(&softs->sl_tail, 0); POLLWAKEUP(IPL_LOGSYNC); # endif # endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync_expire */ /* Parameters: Nil */ /* Returns: Nil */ /* */ /* This is the function called even ipf_tick. It implements one of the */ /* three heuristics above *IF* there are events waiting. */ /* ------------------------------------------------------------------------ */ void -ipf_sync_expire(softc) - ipf_main_softc_t *softc; +ipf_sync_expire(ipf_main_softc_t *softc) { ipf_sync_softc_t *softs = softc->ipf_sync_soft; if ((softs->ipf_sync_events > 0) && (softc->ipf_ticks > softs->ipf_sync_lastwakeup + softs->ipf_sync_wake_interval)) { ipf_sync_poll_wakeup(softc); } } diff --git a/sys/netpfil/ipfilter/netinet/ip_tftp_pxy.c b/sys/netpfil/ipfilter/netinet/ip_tftp_pxy.c index 6f6648899a44..a6c502a6337e 100644 --- a/sys/netpfil/ipfilter/netinet/ip_tftp_pxy.c +++ b/sys/netpfil/ipfilter/netinet/ip_tftp_pxy.c @@ -1,508 +1,482 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id: ip_tftp_pxy.c,v 1.1.2.9 2012/07/22 08:04:23 darren_r Exp $ */ #define IPF_TFTP_PROXY typedef struct ipf_tftp_softc_s { int ipf_p_tftp_readonly; ipftuneable_t *ipf_p_tftp_tune; } ipf_tftp_softc_t; int ipf_p_tftp_backchannel(fr_info_t *, ap_session_t *, nat_t *); int ipf_p_tftp_client(ipf_tftp_softc_t *, fr_info_t *, ap_session_t *, nat_t *); int ipf_p_tftp_in(void *, fr_info_t *, ap_session_t *, nat_t *); void ipf_p_tftp_main_load(void); void ipf_p_tftp_main_unload(void); int ipf_p_tftp_new(void *, fr_info_t *, ap_session_t *, nat_t *); void ipf_p_tftp_del(ipf_main_softc_t *, ap_session_t *); int ipf_p_tftp_out(void *, fr_info_t *, ap_session_t *, nat_t *); int ipf_p_tftp_server(ipf_tftp_softc_t *, fr_info_t *, ap_session_t *, nat_t *); void *ipf_p_tftp_soft_create(ipf_main_softc_t *); void ipf_p_tftp_soft_destroy(ipf_main_softc_t *, void *); static frentry_t tftpfr; static int tftp_proxy_init = 0; typedef enum tftp_cmd_e { TFTP_CMD_READ = 1, TFTP_CMD_WRITE = 2, TFTP_CMD_DATA = 3, TFTP_CMD_ACK = 4, TFTP_CMD_ERROR = 5 } tftp_cmd_t; typedef struct tftpinfo { tftp_cmd_t ti_lastcmd; int ti_nextblk; int ti_lastblk; int ti_lasterror; char ti_filename[80]; ipnat_t *ti_rule; } tftpinfo_t; static ipftuneable_t ipf_tftp_tuneables[] = { { { (void *)offsetof(ipf_tftp_softc_t, ipf_p_tftp_readonly) }, "tftp_read_only", 0, 1, stsizeof(ipf_tftp_softc_t, ipf_p_tftp_readonly), 0, NULL, NULL }, { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL } }; /* * TFTP application proxy initialization. */ void -ipf_p_tftp_main_load() +ipf_p_tftp_main_load(void) { bzero((char *)&tftpfr, sizeof(tftpfr)); tftpfr.fr_ref = 1; tftpfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&tftpfr.fr_lock, "TFTP proxy rule lock"); tftp_proxy_init = 1; } void -ipf_p_tftp_main_unload() +ipf_p_tftp_main_unload(void) { if (tftp_proxy_init == 1) { MUTEX_DESTROY(&tftpfr.fr_lock); tftp_proxy_init = 0; } } void * -ipf_p_tftp_soft_create(softc) - ipf_main_softc_t *softc; +ipf_p_tftp_soft_create(ipf_main_softc_t *softc) { ipf_tftp_softc_t *softt; KMALLOC(softt, ipf_tftp_softc_t *); if (softt == NULL) return NULL; bzero((char *)softt, sizeof(*softt)); softt->ipf_p_tftp_tune = ipf_tune_array_copy(softt, sizeof(ipf_tftp_tuneables), ipf_tftp_tuneables); if (softt->ipf_p_tftp_tune == NULL) { ipf_p_tftp_soft_destroy(softc, softt); return NULL; } if (ipf_tune_array_link(softc, softt->ipf_p_tftp_tune) == -1) { ipf_p_tftp_soft_destroy(softc, softt); return NULL; } softt->ipf_p_tftp_readonly = 1; return softt; } void -ipf_p_tftp_soft_destroy(softc, arg) - ipf_main_softc_t *softc; - void *arg; +ipf_p_tftp_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_tftp_softc_t *softt = arg; if (softt->ipf_p_tftp_tune != NULL) { ipf_tune_array_unlink(softc, softt->ipf_p_tftp_tune); KFREES(softt->ipf_p_tftp_tune, sizeof(ipf_tftp_tuneables)); softt->ipf_p_tftp_tune = NULL; } KFREE(softt); } int -ipf_p_tftp_out(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_tftp_out(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { ipf_tftp_softc_t *softt = arg; fin->fin_flx |= FI_NOWILD; if (nat->nat_dir == NAT_OUTBOUND) return ipf_p_tftp_client(softt, fin, aps, nat); return ipf_p_tftp_server(softt, fin, aps, nat); } int -ipf_p_tftp_in(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_tftp_in(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { ipf_tftp_softc_t *softt = arg; fin->fin_flx |= FI_NOWILD; if (nat->nat_dir == NAT_INBOUND) return ipf_p_tftp_client(softt, fin, aps, nat); return ipf_p_tftp_server(softt, fin, aps, nat); } int -ipf_p_tftp_new(arg, fin, aps, nat) - void *arg; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_tftp_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) { udphdr_t *udp; tftpinfo_t *ti; ipnat_t *ipn; ipnat_t *np; int size; fin = fin; /* LINT */ np = nat->nat_ptr; size = np->in_size; KMALLOC(ti, tftpinfo_t *); if (ti == NULL) return -1; KMALLOCS(ipn, ipnat_t *, size); if (ipn == NULL) { KFREE(ti); return -1; } aps->aps_data = ti; aps->aps_psiz = sizeof(*ti); bzero((char *)ti, sizeof(*ti)); bzero((char *)ipn, size); ti->ti_rule = ipn; udp = (udphdr_t *)fin->fin_dp; aps->aps_sport = udp->uh_sport; aps->aps_dport = udp->uh_dport; ipn->in_size = size; ipn->in_apr = NULL; ipn->in_use = 1; ipn->in_hits = 1; ipn->in_ippip = 1; ipn->in_pr[0] = IPPROTO_UDP; ipn->in_pr[1] = IPPROTO_UDP; ipn->in_ifps[0] = nat->nat_ifps[0]; ipn->in_ifps[1] = nat->nat_ifps[1]; ipn->in_v[0] = nat->nat_ptr->in_v[1]; ipn->in_v[1] = nat->nat_ptr->in_v[0]; ipn->in_flags = IPN_UDP|IPN_FIXEDDPORT|IPN_PROXYRULE; ipn->in_nsrcip6 = nat->nat_odst6; ipn->in_osrcip6 = nat->nat_ndst6; if ((np->in_redir & NAT_REDIRECT) != 0) { ipn->in_redir = NAT_MAP; if (ipn->in_v[0] == 4) { ipn->in_snip = ntohl(nat->nat_odstaddr); ipn->in_dnip = ntohl(nat->nat_nsrcaddr); } else { #ifdef USE_INET6 ipn->in_snip6 = nat->nat_odst6; ipn->in_dnip6 = nat->nat_nsrc6; #endif } ipn->in_ndstip6 = nat->nat_nsrc6; ipn->in_odstip6 = nat->nat_osrc6; } else { ipn->in_redir = NAT_REDIRECT; if (ipn->in_v[0] == 4) { ipn->in_snip = ntohl(nat->nat_odstaddr); ipn->in_dnip = ntohl(nat->nat_osrcaddr); } else { #ifdef USE_INET6 ipn->in_snip6 = nat->nat_odst6; ipn->in_dnip6 = nat->nat_osrc6; #endif } ipn->in_ndstip6 = nat->nat_osrc6; ipn->in_odstip6 = nat->nat_nsrc6; } ipn->in_odport = htons(fin->fin_sport); ipn->in_ndport = htons(fin->fin_sport); IP6_SETONES(&ipn->in_osrcmsk6); IP6_SETONES(&ipn->in_nsrcmsk6); IP6_SETONES(&ipn->in_odstmsk6); IP6_SETONES(&ipn->in_ndstmsk6); MUTEX_INIT(&ipn->in_lock, "tftp proxy NAT rule"); ipn->in_namelen = np->in_namelen; bcopy(np->in_names, ipn->in_ifnames, ipn->in_namelen); ipn->in_ifnames[0] = np->in_ifnames[0]; ipn->in_ifnames[1] = np->in_ifnames[1]; ti->ti_lastcmd = 0; return 0; } void -ipf_p_tftp_del(softc, aps) - ipf_main_softc_t *softc; - ap_session_t *aps; +ipf_p_tftp_del(ipf_main_softc_t *softc, ap_session_t *aps) { tftpinfo_t *tftp; tftp = aps->aps_data; if (tftp != NULL) { tftp->ti_rule->in_flags |= IPN_DELETE; ipf_nat_rule_deref(softc, &tftp->ti_rule); } } /* * Setup for a new TFTP proxy. */ int -ipf_p_tftp_backchannel(fin, aps, nat) - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_tftp_backchannel(fr_info_t *fin, ap_session_t *aps, nat_t *nat) { ipf_main_softc_t *softc = fin->fin_main_soft; #ifdef USE_MUTEXES ipf_nat_softc_t *softn = softc->ipf_nat_soft; #endif #ifdef USE_INET6 i6addr_t swip6, sw2ip6; ip6_t *ip6; #endif struct in_addr swip, sw2ip; tftpinfo_t *ti; udphdr_t udp; fr_info_t fi; u_short slen = 0; /* silence gcc */ nat_t *nat2; int nflags; ip_t *ip; int dir; ti = aps->aps_data; /* * Add skeleton NAT entry for connection which will come back the * other way. */ bcopy((char *)fin, (char *)&fi, sizeof(fi)); fi.fin_flx |= FI_IGNORE; fi.fin_data[1] = 0; bzero((char *)&udp, sizeof(udp)); udp.uh_sport = 0; /* XXX - don't specify remote port */ udp.uh_dport = ti->ti_rule->in_ndport; udp.uh_ulen = htons(sizeof(udp)); udp.uh_sum = 0; fi.fin_fr = &tftpfr; fi.fin_dp = (char *)&udp; fi.fin_sport = 0; fi.fin_dport = ntohs(ti->ti_rule->in_ndport); fi.fin_dlen = sizeof(udp); fi.fin_plen = fi.fin_hlen + sizeof(udp); fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; nflags = NAT_SLAVE|IPN_UDP|SI_W_SPORT; #ifdef USE_INET6 ip6 = (ip6_t *)fin->fin_ip; #endif ip = fin->fin_ip; sw2ip.s_addr = 0; swip.s_addr = 0; fi.fin_src6 = nat->nat_ndst6; fi.fin_dst6 = nat->nat_nsrc6; if (nat->nat_v[0] == 4) { slen = ip->ip_len; ip->ip_len = htons(fin->fin_hlen + sizeof(udp)); swip = ip->ip_src; sw2ip = ip->ip_dst; ip->ip_src = nat->nat_ndstip; ip->ip_dst = nat->nat_nsrcip; } else { #ifdef USE_INET6 slen = ip6->ip6_plen; ip6->ip6_plen = htons(sizeof(udp)); swip6.in6 = ip6->ip6_src; sw2ip6.in6 = ip6->ip6_dst; ip6->ip6_src = nat->nat_ndst6.in6; ip6->ip6_dst = nat->nat_nsrc6.in6; #endif } if (nat->nat_dir == NAT_INBOUND) { dir = NAT_OUTBOUND; fi.fin_out = 1; } else { dir = NAT_INBOUND; fi.fin_out = 0; } nflags |= NAT_NOTRULEPORT; MUTEX_ENTER(&softn->ipf_nat_new); #ifdef USE_INET6 if (nat->nat_v[0] == 6) nat2 = ipf_nat6_add(&fi, ti->ti_rule, NULL, nflags, dir); else #endif nat2 = ipf_nat_add(&fi, ti->ti_rule, NULL, nflags, dir); MUTEX_EXIT(&softn->ipf_nat_new); if (nat2 != NULL) { (void) ipf_nat_proto(&fi, nat2, IPN_UDP); ipf_nat_update(&fi, nat2); fi.fin_ifp = NULL; if (ti->ti_rule->in_redir == NAT_MAP) { fi.fin_src6 = nat->nat_ndst6; fi.fin_dst6 = nat->nat_nsrc6; if (nat->nat_v[0] == 4) { ip->ip_src = nat->nat_ndstip; ip->ip_dst = nat->nat_nsrcip; } else { #ifdef USE_INET6 ip6->ip6_src = nat->nat_ndst6.in6; ip6->ip6_dst = nat->nat_nsrc6.in6; #endif } } else { fi.fin_src6 = nat->nat_odst6; fi.fin_dst6 = nat->nat_osrc6; if (fin->fin_v == 4) { ip->ip_src = nat->nat_odstip; ip->ip_dst = nat->nat_osrcip; } else { #ifdef USE_INET6 ip6->ip6_src = nat->nat_odst6.in6; ip6->ip6_dst = nat->nat_osrc6.in6; #endif } } if (ipf_state_add(softc, &fi, NULL, SI_W_SPORT) != 0) { ipf_nat_setpending(softc, nat2); } } if (nat->nat_v[0] == 4) { ip->ip_len = slen; ip->ip_src = swip; ip->ip_dst = sw2ip; } else { #ifdef USE_INET6 ip6->ip6_plen = slen; ip6->ip6_src = swip6.in6; ip6->ip6_dst = sw2ip6.in6; #endif } return 0; } int -ipf_p_tftp_client(softt, fin, aps, nat) - ipf_tftp_softc_t *softt; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_tftp_client(ipf_tftp_softc_t *softt, fr_info_t *fin, ap_session_t *aps, + nat_t *nat) { u_char *msg, *s, *t; tftpinfo_t *ti; u_short opcode; udphdr_t *udp; int len; if (fin->fin_dlen < 4) return 0; ti = aps->aps_data; msg = fin->fin_dp; msg += sizeof(udphdr_t); opcode = (msg[0] << 8) | msg[1]; DT3(tftp_cmd, fr_info_t *, fin, int, opcode, nat_t *, nat); switch (opcode) { case TFTP_CMD_WRITE : if (softt->ipf_p_tftp_readonly != 0) break; /* FALLTHROUGH */ case TFTP_CMD_READ : len = fin->fin_dlen - sizeof(*udp) - 2; if (len > sizeof(ti->ti_filename) - 1) len = sizeof(ti->ti_filename) - 1; s = msg + 2; for (t = (u_char *)ti->ti_filename; (len > 0); len--, s++) { *t++ = *s; if (*s == '\0') break; } ipf_p_tftp_backchannel(fin, aps, nat); break; default : return -1; } ti = aps->aps_data; ti->ti_lastcmd = opcode; return 0; } int -ipf_p_tftp_server(softt, fin, aps, nat) - ipf_tftp_softc_t *softt; - fr_info_t *fin; - ap_session_t *aps; - nat_t *nat; +ipf_p_tftp_server(ipf_tftp_softc_t *softt, fr_info_t *fin, ap_session_t *aps, + nat_t *nat) { tftpinfo_t *ti; u_short opcode; u_short arg; u_char *msg; if (fin->fin_dlen < 4) return 0; ti = aps->aps_data; msg = fin->fin_dp; msg += sizeof(udphdr_t); arg = (msg[2] << 8) | msg[3]; opcode = (msg[0] << 8) | msg[1]; switch (opcode) { case TFTP_CMD_ACK : ti->ti_lastblk = arg; break; case TFTP_CMD_ERROR : ti->ti_lasterror = arg; break; default : return -1; } ti->ti_lastcmd = opcode; return 0; } diff --git a/sys/netpfil/ipfilter/netinet/mlfk_ipl.c b/sys/netpfil/ipfilter/netinet/mlfk_ipl.c index 9f827d17fd70..ff15a530c6db 100644 --- a/sys/netpfil/ipfilter/netinet/mlfk_ipl.c +++ b/sys/netpfil/ipfilter/netinet/mlfk_ipl.c @@ -1,670 +1,660 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * $FreeBSD$ * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ # include # include # ifdef _KERNEL # include # else # define CURVNET_SET(arg) # define CURVNET_RESTORE() # define VNET_DEFINE(_t, _v) _t _v # define VNET_DECLARE(_t, _v) extern _t _v # define VNET(arg) arg # endif #endif #include #include #include #include "netinet/ipl.h" #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_state.h" #include "netinet/ip_nat.h" #include "netinet/ip_auth.h" #include "netinet/ip_frag.h" #include "netinet/ip_sync.h" VNET_DECLARE(ipf_main_softc_t, ipfmain); #define V_ipfmain VNET(ipfmain) #ifdef __FreeBSD__ static struct cdev *ipf_devs[IPL_LOGSIZE]; #else static dev_t ipf_devs[IPL_LOGSIZE]; #endif static int sysctl_ipf_int ( SYSCTL_HANDLER_ARGS ); static int sysctl_ipf_int_nat ( SYSCTL_HANDLER_ARGS ); static int sysctl_ipf_int_state ( SYSCTL_HANDLER_ARGS ); static int sysctl_ipf_int_auth ( SYSCTL_HANDLER_ARGS ); static int sysctl_ipf_int_frag ( SYSCTL_HANDLER_ARGS ); static int ipf_modload(void); static int ipf_modunload(void); static int ipf_fbsd_sysctl_create(void); static int ipf_fbsd_sysctl_destroy(void); #ifdef __FreeBSD__ static int ipfopen(struct cdev*, int, int, struct thread *); static int ipfclose(struct cdev*, int, int, struct thread *); static int ipfread(struct cdev*, struct uio *, int); static int ipfwrite(struct cdev*, struct uio *, int); #else static int ipfopen(dev_t, int, int, struct proc *); static int ipfclose(dev_t, int, int, struct proc *); static int ipfread(dev_t, struct uio *, int); static int ipfwrite(dev_t, struct uio *, int); #endif #ifdef LARGE_NAT #define IPF_LARGE_NAT 1 #else #define IPF_LARGE_NAT 0 #endif SYSCTL_DECL(_net_inet); #define SYSCTL_IPF(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_INT|CTLFLAG_VNET|access, \ ptr, val, sysctl_ipf_int, "I", descr) #define SYSCTL_DYN_IPF_NAT(parent, nbr, name, access,ptr, val, descr) \ SYSCTL_ADD_OID(&ipf_clist, SYSCTL_STATIC_CHILDREN(parent), nbr, name, \ CTLTYPE_INT|CTLFLAG_VNET|access, ptr, val, sysctl_ipf_int_nat, "I", descr) #define SYSCTL_DYN_IPF_STATE(parent, nbr, name, access,ptr, val, descr) \ SYSCTL_ADD_OID(&ipf_clist, SYSCTL_STATIC_CHILDREN(parent), nbr, name, \ CTLTYPE_INT|CTLFLAG_VNET|access, ptr, val, sysctl_ipf_int_state, "I", descr) #define SYSCTL_DYN_IPF_FRAG(parent, nbr, name, access,ptr, val, descr) \ SYSCTL_ADD_OID(&ipf_clist, SYSCTL_STATIC_CHILDREN(parent), nbr, name, \ CTLTYPE_INT|CTLFLAG_VNET|access, ptr, val, sysctl_ipf_int_frag, "I", descr) #define SYSCTL_DYN_IPF_AUTH(parent, nbr, name, access,ptr, val, descr) \ SYSCTL_ADD_OID(&ipf_clist, SYSCTL_STATIC_CHILDREN(parent), nbr, name, \ CTLTYPE_INT|CTLFLAG_VNET|access, ptr, val, sysctl_ipf_int_auth, "I", descr) static struct sysctl_ctx_list ipf_clist; #define CTLFLAG_OFF 0x00800000 /* IPFilter must be disabled */ #define CTLFLAG_RWO (CTLFLAG_RW|CTLFLAG_OFF) SYSCTL_NODE(_net_inet, OID_AUTO, ipf, CTLFLAG_RW, 0, "IPF"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_flags, CTLFLAG_RW, &VNET_NAME(ipfmain.ipf_flags), 0, "IPF flags"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, ipf_pass, CTLFLAG_RW, &VNET_NAME(ipfmain.ipf_pass), 0, "default pass/block"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_active, CTLFLAG_RD, &VNET_NAME(ipfmain.ipf_active), 0, "IPF is active"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpidletimeout, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcpidletimeout), 0, "TCP idle timeout in seconds"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcphalfclosed, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcphalfclosed), 0, "timeout for half closed TCP sessions"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpclosewait, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcpclosewait), 0, "timeout for TCP sessions in closewait status"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcplastack, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcplastack), 0, "timeout for TCP sessions in last ack status"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcptimeout, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcptimeout), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpclosed, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcpclosed), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_udptimeout, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_udptimeout), 0, "UDP timeout"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_udpacktimeout, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_udpacktimeout), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_icmptimeout, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_icmptimeout), 0, "ICMP timeout"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_running, CTLFLAG_RD, &VNET_NAME(ipfmain.ipf_running), 0, "IPF is running"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_chksrc, CTLFLAG_RW, &VNET_NAME(ipfmain.ipf_chksrc), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_minttl, CTLFLAG_RW, &VNET_NAME(ipfmain.ipf_minttl), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, large_nat, CTLFLAG_RD, &VNET_NAME(ipfmain.ipf_large_nat), 0, "large_nat"); #define CDEV_MAJOR 79 #include #ifdef __FreeBSD__ # include static int ipfpoll(struct cdev *dev, int events, struct thread *td); static struct cdevsw ipf_cdevsw = { .d_version = D_VERSION, .d_flags = 0, /* D_NEEDGIANT - Should be SMP safe */ .d_open = ipfopen, .d_close = ipfclose, .d_read = ipfread, .d_write = ipfwrite, .d_ioctl = ipfioctl, .d_poll = ipfpoll, .d_name = "ipf", }; #else static int ipfpoll(dev_t dev, int events, struct proc *td); static struct cdevsw ipf_cdevsw = { /* open */ ipfopen, /* close */ ipfclose, /* read */ ipfread, /* write */ ipfwrite, /* ioctl */ ipfioctl, /* poll */ ipfpoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "ipf", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, }; #endif static char *ipf_devfiles[] = { IPL_NAME, IPNAT_NAME, IPSTATE_NAME, IPAUTH_NAME, IPSYNC_NAME, IPSCAN_NAME, IPLOOKUP_NAME, NULL }; static int ipfilter_modevent(module_t mod, int type, void *unused) { int error = 0; switch (type) { case MOD_LOAD : error = ipf_modload(); break; case MOD_UNLOAD : error = ipf_modunload(); break; default: error = EINVAL; break; } return error; } static void vnet_ipf_init(void) { char *defpass; int error; if (ipf_create_all(&V_ipfmain) == NULL) return; error = ipfattach(&V_ipfmain); if (error) { ipf_destroy_all(&V_ipfmain); return; } if (FR_ISPASS(V_ipfmain.ipf_pass)) defpass = "pass"; else if (FR_ISBLOCK(V_ipfmain.ipf_pass)) defpass = "block"; else defpass = "no-match -> block"; if (IS_DEFAULT_VNET(curvnet)) { printf("%s initialized. Default = %s all, Logging = %s%s\n", ipfilter_version, defpass, #ifdef IPFILTER_LOG "enabled", #else "disabled", #endif #ifdef IPFILTER_COMPILED " (COMPILED)" #else "" #endif ); } else { (void)ipf_pfil_hook(); ipf_event_reg(); } } VNET_SYSINIT(vnet_ipf_init, SI_SUB_PROTO_FIREWALL, SI_ORDER_THIRD, vnet_ipf_init, NULL); static int -ipf_modload() +ipf_modload(void) { char *c, *str; int i, j, error; if (ipf_load_all() != 0) return EIO; if (ipf_fbsd_sysctl_create() != 0) { return EIO; } for (i = 0; i < IPL_LOGSIZE; i++) ipf_devs[i] = NULL; for (i = 0; (str = ipf_devfiles[i]); i++) { c = NULL; for(j = strlen(str); j > 0; j--) if (str[j] == '/') { c = str + j + 1; break; } if (!c) c = str; ipf_devs[i] = make_dev(&ipf_cdevsw, i, 0, 0, 0600, "%s", c); } error = ipf_pfil_hook(); if (error != 0) return error; ipf_event_reg(); return 0; } static void vnet_ipf_uninit(void) { if (V_ipfmain.ipf_refcnt) return; if (V_ipfmain.ipf_running >= 0) { if (ipfdetach(&V_ipfmain) != 0) return; V_ipfmain.ipf_running = -2; ipf_destroy_all(&V_ipfmain); if (!IS_DEFAULT_VNET(curvnet)) { ipf_event_dereg(); (void)ipf_pfil_unhook(); } } } VNET_SYSUNINIT(vnet_ipf_uninit, SI_SUB_PROTO_FIREWALL, SI_ORDER_THIRD, vnet_ipf_uninit, NULL); static int -ipf_modunload() +ipf_modunload(void) { int error, i; ipf_event_dereg(); ipf_fbsd_sysctl_destroy(); error = ipf_pfil_unhook(); if (error != 0) return error; for (i = 0; ipf_devfiles[i]; i++) { if (ipf_devs[i] != NULL) destroy_dev(ipf_devs[i]); } ipf_unload_all(); printf("%s unloaded\n", ipfilter_version); return (0); } static moduledata_t ipfiltermod = { "ipfilter", ipfilter_modevent, 0 }; DECLARE_MODULE(ipfilter, ipfiltermod, SI_SUB_PROTO_FIREWALL, SI_ORDER_SECOND); #ifdef MODULE_VERSION MODULE_VERSION(ipfilter, 1); #endif #ifdef SYSCTL_IPF int sysctl_ipf_int ( SYSCTL_HANDLER_ARGS ) { int error = 0; if (arg1) error = SYSCTL_OUT(req, arg1, sizeof(int)); else error = SYSCTL_OUT(req, &arg2, sizeof(int)); if (error || !req->newptr) return (error); if (!arg1) error = EPERM; else { if ((oidp->oid_kind & CTLFLAG_OFF) && (V_ipfmain.ipf_running > 0)) error = EBUSY; else error = SYSCTL_IN(req, arg1, sizeof(int)); } return (error); } /* * In the VIMAGE case kern_sysctl.c already adds the vnet base address given * we set CTLFLAG_VNET to get proper access checks. Have to undo this. * Then we add the given offset to the specific malloced struct hanging off * virtualized ipmain struct. */ static int sysctl_ipf_int_nat ( SYSCTL_HANDLER_ARGS ) { if (arg1) { ipf_nat_softc_t *nat_softc; nat_softc = V_ipfmain.ipf_nat_soft; #ifdef VIMAGE arg1 = (void *)((uintptr_t)arg1 - curvnet->vnet_data_base); #endif arg1 = (void *)((uintptr_t)nat_softc + (uintptr_t)arg1); } return (sysctl_ipf_int(oidp, arg1, arg2, req)); } static int sysctl_ipf_int_state ( SYSCTL_HANDLER_ARGS ) { if (arg1) { ipf_state_softc_t *state_softc; state_softc = V_ipfmain.ipf_state_soft; #ifdef VIMAGE arg1 = (void *)((uintptr_t)arg1 - curvnet->vnet_data_base); #endif arg1 = (void *)((uintptr_t)state_softc + (uintptr_t)arg1); } return (sysctl_ipf_int(oidp, arg1, arg2, req)); } static int sysctl_ipf_int_auth ( SYSCTL_HANDLER_ARGS ) { if (arg1) { ipf_auth_softc_t *auth_softc; auth_softc = V_ipfmain.ipf_auth_soft; #ifdef VIMAGE arg1 = (void *)((uintptr_t)arg1 - curvnet->vnet_data_base); #endif arg1 = (void *)((uintptr_t)auth_softc + (uintptr_t)arg1); } return (sysctl_ipf_int(oidp, arg1, arg2, req)); } static int sysctl_ipf_int_frag ( SYSCTL_HANDLER_ARGS ) { if (arg1) { ipf_frag_softc_t *frag_softc; frag_softc = V_ipfmain.ipf_frag_soft; #ifdef VIMAGE arg1 = (void *)((uintptr_t)arg1 - curvnet->vnet_data_base); #endif arg1 = (void *)((uintptr_t)frag_softc + (uintptr_t)arg1); } return (sysctl_ipf_int(oidp, arg1, arg2, req)); } #endif static int #ifdef __FreeBSD__ ipfpoll(struct cdev *dev, int events, struct thread *td) #else ipfpoll(dev_t dev, int events, struct proc *td) #endif { int unit = GET_MINOR(dev); int revents; if (unit < 0 || unit > IPL_LOGMAX) return 0; revents = 0; CURVNET_SET(TD_TO_VNET(td)); switch (unit) { case IPL_LOGIPF : case IPL_LOGNAT : case IPL_LOGSTATE : #ifdef IPFILTER_LOG if ((events & (POLLIN | POLLRDNORM)) && ipf_log_canread(&V_ipfmain, unit)) revents |= events & (POLLIN | POLLRDNORM); #endif break; case IPL_LOGAUTH : if ((events & (POLLIN | POLLRDNORM)) && ipf_auth_waiting(&V_ipfmain)) revents |= events & (POLLIN | POLLRDNORM); break; case IPL_LOGSYNC : if ((events & (POLLIN | POLLRDNORM)) && ipf_sync_canread(&V_ipfmain)) revents |= events & (POLLIN | POLLRDNORM); if ((events & (POLLOUT | POLLWRNORM)) && ipf_sync_canwrite(&V_ipfmain)) revents |= events & (POLLOUT | POLLWRNORM); break; case IPL_LOGSCAN : case IPL_LOGLOOKUP : default : break; } if ((revents == 0) && ((events & (POLLIN|POLLRDNORM)) != 0)) selrecord(td, &V_ipfmain.ipf_selwait[unit]); CURVNET_RESTORE(); return revents; } /* * routines below for saving IP headers to buffer */ -static int ipfopen(dev, flags +static int #ifdef __FreeBSD__ -, devtype, p) - int devtype; - struct thread *p; - struct cdev *dev; +ipfopen(struct cdev *dev, int flags, int devtype, struct thread *p) #else -) - dev_t dev; +ipfopen(dev_t dev, int flags) #endif - int flags; { int unit = GET_MINOR(dev); int error; if (IPL_LOGMAX < unit) error = ENXIO; else { switch (unit) { case IPL_LOGIPF : case IPL_LOGNAT : case IPL_LOGSTATE : case IPL_LOGAUTH : case IPL_LOGLOOKUP : case IPL_LOGSYNC : #ifdef IPFILTER_SCAN case IPL_LOGSCAN : #endif error = 0; break; default : error = ENXIO; break; } } return error; } -static int ipfclose(dev, flags +static int #ifdef __FreeBSD__ -, devtype, p) - int devtype; - struct thread *p; - struct cdev *dev; +ipfclose(struct cdev *dev, int flags, int devtype, struct thread *p) #else -) - dev_t dev; +ipfclose(dev_t dev, int flags) #endif - int flags; { int unit = GET_MINOR(dev); if (IPL_LOGMAX < unit) unit = ENXIO; else unit = 0; return unit; } /* * ipfread/ipflog * both of these must operate with at least splnet() lest they be * called during packet processing and cause an inconsistancy to appear in * the filter lists. */ static int ipfread(dev, uio, ioflag) int ioflag; #ifdef __FreeBSD__ struct cdev *dev; #else dev_t dev; #endif struct uio *uio; { int error; int unit = GET_MINOR(dev); if (unit < 0) return ENXIO; CURVNET_SET(TD_TO_VNET(curthread)); if (V_ipfmain.ipf_running < 1) { CURVNET_RESTORE(); return EIO; } if (unit == IPL_LOGSYNC) { error = ipf_sync_read(&V_ipfmain, uio); CURVNET_RESTORE(); return error; } #ifdef IPFILTER_LOG error = ipf_log_read(&V_ipfmain, unit, uio); #else error = ENXIO; #endif CURVNET_RESTORE(); return error; } /* * ipfwrite * both of these must operate with at least splnet() lest they be * called during packet processing and cause an inconsistancy to appear in * the filter lists. */ static int ipfwrite(dev, uio, ioflag) int ioflag; #ifdef __FreeBSD__ struct cdev *dev; #else dev_t dev; #endif struct uio *uio; { int error; CURVNET_SET(TD_TO_VNET(curthread)); if (V_ipfmain.ipf_running < 1) { CURVNET_RESTORE(); return EIO; } if (GET_MINOR(dev) == IPL_LOGSYNC) { error = ipf_sync_write(&V_ipfmain, uio); CURVNET_RESTORE(); return error; } return ENXIO; } static int ipf_fbsd_sysctl_create(void) { sysctl_ctx_init(&ipf_clist); SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "fr_defnatage", CTLFLAG_RWO, (void *)offsetof(ipf_nat_softc_t, ipf_nat_defage), 0, ""); SYSCTL_DYN_IPF_STATE(_net_inet_ipf, OID_AUTO, "fr_statesize", CTLFLAG_RWO, (void *)offsetof(ipf_state_softc_t, ipf_state_size), 0, ""); SYSCTL_DYN_IPF_STATE(_net_inet_ipf, OID_AUTO, "fr_statemax", CTLFLAG_RWO, (void *)offsetof(ipf_state_softc_t, ipf_state_max), 0, ""); SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "ipf_nattable_max", CTLFLAG_RWO, (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_max), 0, ""); SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "ipf_nattable_sz", CTLFLAG_RWO, (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_sz), 0, ""); SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "ipf_natrules_sz", CTLFLAG_RWO, (void *)offsetof(ipf_nat_softc_t, ipf_nat_maprules_sz), 0, ""); SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "ipf_rdrrules_sz", CTLFLAG_RWO, (void *)offsetof(ipf_nat_softc_t, ipf_nat_rdrrules_sz), 0, ""); SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "ipf_hostmap_sz", CTLFLAG_RWO, (void *)offsetof(ipf_nat_softc_t, ipf_nat_hostmap_sz), 0, ""); SYSCTL_DYN_IPF_AUTH(_net_inet_ipf, OID_AUTO, "fr_authsize", CTLFLAG_RWO, (void *)offsetof(ipf_auth_softc_t, ipf_auth_size), 0, ""); SYSCTL_DYN_IPF_AUTH(_net_inet_ipf, OID_AUTO, "fr_authused", CTLFLAG_RD, (void *)offsetof(ipf_auth_softc_t, ipf_auth_used), 0, ""); SYSCTL_DYN_IPF_AUTH(_net_inet_ipf, OID_AUTO, "fr_defaultauthage", CTLFLAG_RW, (void *)offsetof(ipf_auth_softc_t, ipf_auth_defaultage), 0, ""); SYSCTL_DYN_IPF_FRAG(_net_inet_ipf, OID_AUTO, "fr_ipfrttl", CTLFLAG_RW, (void *)offsetof(ipf_frag_softc_t, ipfr_ttl), 0, ""); return 0; } static int ipf_fbsd_sysctl_destroy(void) { if (sysctl_ctx_free(&ipf_clist)) { printf("sysctl_ctx_free failed"); return(ENOTEMPTY); } return 0; } diff --git a/sys/netpfil/ipfilter/netinet/radix_ipf.c b/sys/netpfil/ipfilter/netinet/radix_ipf.c index 02e73de54c6a..33f6fbf378dd 100644 --- a/sys/netpfil/ipfilter/netinet/radix_ipf.c +++ b/sys/netpfil/ipfilter/netinet/radix_ipf.c @@ -1,1534 +1,1484 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #include #include #include #include #include #include #ifdef _KERNEL #include #else # include # include # include # include #endif /* !_KERNEL */ #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #ifdef RDX_DEBUG # include # include # include #endif #include "netinet/radix_ipf.h" #define ADF_OFF offsetof(addrfamily_t, adf_addr) #define ADF_OFF_BITS (ADF_OFF << 3) static ipf_rdx_node_t *ipf_rx_insert(ipf_rdx_head_t *, ipf_rdx_node_t nodes[2], int *); static void ipf_rx_attach_mask(ipf_rdx_node_t *, ipf_rdx_mask_t *); static int count_mask_bits(addrfamily_t *, u_32_t **); static void buildnodes(addrfamily_t *, addrfamily_t *, ipf_rdx_node_t n[2]); static ipf_rdx_node_t *ipf_rx_find_addr(ipf_rdx_node_t *, u_32_t *); static ipf_rdx_node_t *ipf_rx_lookup(ipf_rdx_head_t *, addrfamily_t *, addrfamily_t *); static ipf_rdx_node_t *ipf_rx_match(ipf_rdx_head_t *, addrfamily_t *); /* * Foreword. * --------- * The code in this file has been written to target using the addrfamily_t * data structure to house the address information and no other. Thus there * are certain aspects of thise code (such as offsets to the address itself) * that are hard coded here whilst they might be more variable elsewhere. * Similarly, this code enforces no maximum key length as that's implied by * all keys needing to be stored in addrfamily_t. */ /* ------------------------------------------------------------------------ */ /* Function: count_mask_bits */ /* Returns: number of consecutive bits starting at "mask". */ /* */ /* Count the number of bits set in the address section of addrfamily_t and */ /* return both that number and a pointer to the last word with a bit set if */ /* lastp is not NULL. The bit count is performed using network byte order */ /* as the guide for which bit is the most significant bit. */ /* ------------------------------------------------------------------------ */ static int -count_mask_bits(mask, lastp) - addrfamily_t *mask; - u_32_t **lastp; +count_mask_bits(addrfamily_t *mask, u_32_t **lastp) { u_32_t *mp = (u_32_t *)&mask->adf_addr; u_32_t m; int count = 0; int mlen; mlen = mask->adf_len - offsetof(addrfamily_t, adf_addr); for (; mlen > 0; mlen -= 4, mp++) { if ((m = ntohl(*mp)) == 0) break; if (lastp != NULL) *lastp = mp; for (; m & 0x80000000; m <<= 1) count++; } return count; } /* ------------------------------------------------------------------------ */ /* Function: buildnodes */ /* Returns: Nil */ /* Parameters: addr(I) - network address for this radix node */ /* mask(I) - netmask associated with the above address */ /* nodes(O) - pair of ipf_rdx_node_t's to initialise with data */ /* associated with addr and mask. */ /* */ /* Initialise the fields in a pair of radix tree nodes according to the */ /* data supplied in the paramters "addr" and "mask". It is expected that */ /* "mask" will contain a consecutive string of bits set. Masks with gaps in */ /* the middle are not handled by this implementation. */ /* ------------------------------------------------------------------------ */ static void -buildnodes(addr, mask, nodes) - addrfamily_t *addr, *mask; - ipf_rdx_node_t nodes[2]; +buildnodes(addrfamily_t *addr, addrfamily_t *mask, ipf_rdx_node_t nodes[2]) { u_32_t maskbits; u_32_t lastmask; u_32_t *last; int masklen; last = NULL; maskbits = count_mask_bits(mask, &last); if (last == NULL) { masklen = 0; lastmask = 0; } else { masklen = last - (u_32_t *)mask; lastmask = *last; } bzero(&nodes[0], sizeof(ipf_rdx_node_t) * 2); nodes[0].maskbitcount = maskbits; nodes[0].index = -1 - (ADF_OFF_BITS + maskbits); nodes[0].addrkey = (u_32_t *)addr; nodes[0].maskkey = (u_32_t *)mask; nodes[0].addroff = nodes[0].addrkey + masklen; nodes[0].maskoff = nodes[0].maskkey + masklen; nodes[0].parent = &nodes[1]; nodes[0].offset = masklen; nodes[0].lastmask = lastmask; nodes[1].offset = masklen; nodes[1].left = &nodes[0]; nodes[1].maskbitcount = maskbits; #ifdef RDX_DEBUG (void) strcpy(nodes[0].name, "_BUILD.0"); (void) strcpy(nodes[1].name, "_BUILD.1"); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_find_addr */ /* Returns: ipf_rdx_node_t * - pointer to a node in the radix tree. */ /* Parameters: tree(I) - pointer to first right node in tree to search */ /* addr(I) - pointer to address to match */ /* */ /* Walk the radix tree given by "tree", looking for a leaf node that is a */ /* match for the address given by "addr". */ /* ------------------------------------------------------------------------ */ static ipf_rdx_node_t * -ipf_rx_find_addr(tree, addr) - ipf_rdx_node_t *tree; - u_32_t *addr; +ipf_rx_find_addr(ipf_rdx_node_t *tree, u_32_t *addr) { ipf_rdx_node_t *cur; for (cur = tree; cur->index >= 0;) { if (cur->bitmask & addr[cur->offset]) { cur = cur->right; } else { cur = cur->left; } } return (cur); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_match */ /* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ /* added to the tree. */ /* Paramters: head(I) - pointer to tree head to search */ /* addr(I) - pointer to address to find */ /* */ /* Search the radix tree for the best match to the address pointed to by */ /* "addr" and return a pointer to that node. This search will not match the */ /* address information stored in either of the root leaves as neither of */ /* them are considered to be part of the tree of data being stored. */ /* ------------------------------------------------------------------------ */ static ipf_rdx_node_t * -ipf_rx_match(head, addr) - ipf_rdx_head_t *head; - addrfamily_t *addr; +ipf_rx_match(ipf_rdx_head_t *head, addrfamily_t *addr) { ipf_rdx_mask_t *masknode; ipf_rdx_node_t *prev; ipf_rdx_node_t *node; ipf_rdx_node_t *cur; u_32_t *data; u_32_t *mask; u_32_t *key; u_32_t *end; int len; int i; len = addr->adf_len; end = (u_32_t *)((u_char *)addr + len); node = ipf_rx_find_addr(head->root, (u_32_t *)addr); /* * Search the dupkey list for a potential match. */ for (cur = node; (cur != NULL) && (cur->root == 0); cur = cur->dupkey) { i = cur[0].addroff - cur[0].addrkey; data = cur[0].addrkey + i; mask = cur[0].maskkey + i; key = (u_32_t *)addr + i; for (; key < end; data++, key++, mask++) if ((*key & *mask) != *data) break; if ((end == key) && (cur->root == 0)) return (cur); /* Equal keys */ } prev = node->parent; key = (u_32_t *)addr; for (node = prev; node->root == 0; node = node->parent) { /* * We know that the node hasn't matched so therefore only * the entries in the mask list are searched, not the top * node nor the dupkey list. */ masknode = node->masks; for (; masknode != NULL; masknode = masknode->next) { if (masknode->maskbitcount > node->maskbitcount) continue; cur = masknode->node; for (i = ADF_OFF >> 2; i <= node->offset; i++) { if ((key[i] & masknode->mask[i]) == cur->addrkey[i]) return (cur); } } } return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_lookup */ /* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ /* added to the tree. */ /* Paramters: head(I) - pointer to tree head to search */ /* addr(I) - address part of the key to match */ /* mask(I) - netmask part of the key to match */ /* */ /* ipf_rx_lookup searches for an exact match on (addr,mask). The intention */ /* is to see if a given key is in the tree, not to see if a route exists. */ /* ------------------------------------------------------------------------ */ ipf_rdx_node_t * -ipf_rx_lookup(head, addr, mask) - ipf_rdx_head_t *head; - addrfamily_t *addr, *mask; +ipf_rx_lookup(ipf_rdx_head_t *head, addrfamily_t *addr, addrfamily_t *mask) { ipf_rdx_node_t *found; ipf_rdx_node_t *node; u_32_t *akey; int count; found = ipf_rx_find_addr(head->root, (u_32_t *)addr); if (found->root == 1) return NULL; /* * It is possible to find a matching address in the tree but for the * netmask to not match. If the netmask does not match and there is * no list of alternatives present at dupkey, return a failure. */ count = count_mask_bits(mask, NULL); if (count != found->maskbitcount && found->dupkey == NULL) return (NULL); akey = (u_32_t *)addr; if ((found->addrkey[found->offset] & found->maskkey[found->offset]) != akey[found->offset]) return NULL; if (found->dupkey != NULL) { node = found; while (node != NULL && node->maskbitcount != count) node = node->dupkey; if (node == NULL) return (NULL); found = node; } return found; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_attach_mask */ /* Returns: Nil */ /* Parameters: node(I) - pointer to a radix tree node */ /* mask(I) - pointer to mask structure to add */ /* */ /* Add the netmask to the given node in an ordering where the most specific */ /* netmask is at the top of the list. */ /* ------------------------------------------------------------------------ */ static void -ipf_rx_attach_mask(node, mask) - ipf_rdx_node_t *node; - ipf_rdx_mask_t *mask; +ipf_rx_attach_mask(ipf_rdx_node_t *node, ipf_rdx_mask_t *mask) { ipf_rdx_mask_t **pm; ipf_rdx_mask_t *m; for (pm = &node->masks; (m = *pm) != NULL; pm = &m->next) if (m->maskbitcount < mask->maskbitcount) break; mask->next = *pm; *pm = mask; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_insert */ /* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ /* added to the tree. */ /* Paramters: head(I) - pointer to tree head to add nodes to */ /* nodes(I) - pointer to radix nodes to be added */ /* dup(O) - set to 1 if node is a duplicate, else 0. */ /* */ /* Add the new radix tree entry that owns nodes[] to the tree given by head.*/ /* If there is already a matching key in the table, "dup" will be set to 1 */ /* and the existing node pointer returned if there is a complete key match. */ /* A complete key match is a matching of all key data that is presented by */ /* by the netmask. */ /* ------------------------------------------------------------------------ */ static ipf_rdx_node_t * -ipf_rx_insert(head, nodes, dup) - ipf_rdx_head_t *head; - ipf_rdx_node_t nodes[2]; - int *dup; +ipf_rx_insert(ipf_rdx_head_t *head, ipf_rdx_node_t *nodes, int *dup) { ipf_rdx_mask_t **pmask; ipf_rdx_node_t *node; ipf_rdx_node_t *prev; ipf_rdx_mask_t *mask; ipf_rdx_node_t *cur; u_32_t nodemask; u_32_t *addr; u_32_t *data; int nodebits; u_32_t *key; u_32_t *end; u_32_t bits; int nodekey; int nodeoff; int nlen; int len; addr = nodes[0].addrkey; node = ipf_rx_find_addr(head->root, addr); len = ((addrfamily_t *)addr)->adf_len; key = (u_32_t *)&((addrfamily_t *)addr)->adf_addr; data= (u_32_t *)&((addrfamily_t *)node->addrkey)->adf_addr; end = (u_32_t *)((u_char *)addr + len); for (nlen = 0; key < end; data++, key++, nlen += 32) if (*key != *data) break; if (end == data) { *dup = 1; return (node); /* Equal keys */ } *dup = 0; bits = (ntohl(*data) ^ ntohl(*key)); for (; bits != 0; nlen++) { if ((bits & 0x80000000) != 0) break; bits <<= 1; } nlen += ADF_OFF_BITS; nodes[1].index = nlen; nodes[1].bitmask = htonl(0x80000000 >> (nlen & 0x1f)); nodes[0].offset = nlen / 32; nodes[1].offset = nlen / 32; /* * Walk through the tree and look for the correct place to attach * this node. ipf_rx_fin_addr is not used here because the place * to attach this node may be an internal node (same key, different * netmask.) Additionally, the depth of the search is forcibly limited * here to not exceed the netmask, so that a short netmask will be * added higher up the tree even if there are lower branches. */ cur = head->root; key = nodes[0].addrkey; do { prev = cur; if (key[cur->offset] & cur->bitmask) { cur = cur->right; } else { cur = cur->left; } } while (nlen > (unsigned)cur->index); if ((key[prev->offset] & prev->bitmask) == 0) { prev->left = &nodes[1]; } else { prev->right = &nodes[1]; } cur->parent = &nodes[1]; nodes[1].parent = prev; if ((key[nodes[1].offset] & nodes[1].bitmask) == 0) { nodes[1].right = cur; } else { nodes[1].right = &nodes[0]; nodes[1].left = cur; } nodeoff = nodes[0].offset; nodekey = nodes[0].addrkey[nodeoff]; nodemask = nodes[0].lastmask; nodebits = nodes[0].maskbitcount; prev = NULL; /* * Find the node up the tree with the largest pattern that still * matches the node being inserted to see if this mask can be * moved there. */ for (cur = nodes[1].parent; cur->root == 0; cur = cur->parent) { if (cur->maskbitcount <= nodebits) break; if (((cur - 1)->addrkey[nodeoff] & nodemask) != nodekey) break; prev = cur; } KMALLOC(mask, ipf_rdx_mask_t *); if (mask == NULL) return NULL; bzero(mask, sizeof(*mask)); mask->next = NULL; mask->node = &nodes[0]; mask->maskbitcount = nodebits; mask->mask = nodes[0].maskkey; nodes[0].mymask = mask; if (prev != NULL) { ipf_rdx_mask_t *m; for (pmask = &prev->masks; (m = *pmask) != NULL; pmask = &m->next) { if (m->maskbitcount < nodebits) break; } } else { /* * No higher up nodes qualify, so attach mask locally. */ pmask = &nodes[0].masks; } mask->next = *pmask; *pmask = mask; /* * Search the mask list on each child to see if there are any masks * there that can be moved up to this newly inserted node. */ cur = nodes[1].right; if (cur->root == 0) { for (pmask = &cur->masks; (mask = *pmask) != NULL; ) { if (mask->maskbitcount < nodebits) { *pmask = mask->next; ipf_rx_attach_mask(&nodes[0], mask); } else { pmask = &mask->next; } } } cur = nodes[1].left; if (cur->root == 0 && cur != &nodes[0]) { for (pmask = &cur->masks; (mask = *pmask) != NULL; ) { if (mask->maskbitcount < nodebits) { *pmask = mask->next; ipf_rx_attach_mask(&nodes[0], mask); } else { pmask = &mask->next; } } } return (&nodes[0]); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_addroute */ /* Returns: ipf_rdx_node_t * - NULL on error, else pointer to the node */ /* added to the tree. */ /* Paramters: head(I) - pointer to tree head to search */ /* addr(I) - address portion of "route" to add */ /* mask(I) - netmask portion of "route" to add */ /* nodes(I) - radix tree data nodes inside allocate structure */ /* */ /* Attempt to add a node to the radix tree. The key for the node is the */ /* (addr,mask). No memory allocation for the radix nodes themselves is */ /* performed here, the data structure that this radix node is being used to */ /* find is expected to house the node data itself however the call to */ /* ipf_rx_insert() will attempt to allocate memory in order for netmask to */ /* be promoted further up the tree. */ /* In this case, the ip_pool_node_t structure from ip_pool.h contains both */ /* the key material (addr,mask) and the radix tree nodes[]. */ /* */ /* The mechanics of inserting the node into the tree is handled by the */ /* function ipf_rx_insert() above. Here, the code deals with the case */ /* where the data to be inserted is a duplicate. */ /* ------------------------------------------------------------------------ */ ipf_rdx_node_t * -ipf_rx_addroute(head, addr, mask, nodes) - ipf_rdx_head_t *head; - addrfamily_t *addr, *mask; - ipf_rdx_node_t *nodes; +ipf_rx_addroute(ipf_rdx_head_t *head, addrfamily_t *addr, addrfamily_t *mask, + ipf_rdx_node_t *nodes) { ipf_rdx_node_t *node; ipf_rdx_node_t *prev; ipf_rdx_node_t *x; int dup; buildnodes(addr, mask, nodes); x = ipf_rx_insert(head, nodes, &dup); if (x == NULL) return NULL; if (dup == 1) { node = &nodes[0]; prev = NULL; /* * The duplicate list is kept sorted with the longest * mask at the top, meaning that the most specific entry * in the listis found first. This list thus allows for * duplicates such as 128.128.0.0/32 and 128.128.0.0/16. */ while ((x != NULL) && (x->maskbitcount > node->maskbitcount)) { prev = x; x = x->dupkey; } /* * Is it a complete duplicate? If so, return NULL and * fail the insert. Otherwise, insert it into the list * of netmasks active for this key. */ if ((x != NULL) && (x->maskbitcount == node->maskbitcount)) return (NULL); if (prev != NULL) { nodes[0].dupkey = x; prev->dupkey = &nodes[0]; nodes[0].parent = prev; if (x != NULL) x->parent = &nodes[0]; } else { nodes[0].dupkey = x->dupkey; prev = x->parent; nodes[0].parent = prev; x->parent = &nodes[0]; if (prev->left == x) prev->left = &nodes[0]; else prev->right = &nodes[0]; } } return &nodes[0]; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_delete */ /* Returns: ipf_rdx_node_t * - NULL on error, else node removed from */ /* the tree. */ /* Paramters: head(I) - pointer to tree head to search */ /* addr(I) - pointer to the address part of the key */ /* mask(I) - pointer to the netmask part of the key */ /* */ /* Search for an entry in the radix tree that is an exact match for (addr, */ /* mask) and remove it if it exists. In the case where (addr,mask) is a not */ /* a unique key, the tree structure itself is not changed - only the list */ /* of duplicate keys. */ /* ------------------------------------------------------------------------ */ ipf_rdx_node_t * -ipf_rx_delete(head, addr, mask) - ipf_rdx_head_t *head; - addrfamily_t *addr, *mask; +ipf_rx_delete(ipf_rdx_head_t *head, addrfamily_t *addr, addrfamily_t *mask) { ipf_rdx_mask_t **pmask; ipf_rdx_node_t *parent; ipf_rdx_node_t *found; ipf_rdx_node_t *prev; ipf_rdx_node_t *node; ipf_rdx_node_t *cur; ipf_rdx_mask_t *m; int count; found = ipf_rx_find_addr(head->root, (u_32_t *)addr); if (found == NULL) return NULL; if (found->root == 1) return NULL; count = count_mask_bits(mask, NULL); parent = found->parent; if (found->dupkey != NULL) { node = found; while (node != NULL && node->maskbitcount != count) node = node->dupkey; if (node == NULL) return (NULL); if (node != found) { /* * Remove from the dupkey list. Here, "parent" is * the previous node on the list (rather than tree) * and "dupkey" is the next node on the list. */ parent = node->parent; parent->dupkey = node->dupkey; node->dupkey->parent = parent; } else { /* * * When removing the top node of the dupkey list, * the pointers at the top of the list that point * to other tree nodes need to be preserved and * any children must have their parent updated. */ node = node->dupkey; node->parent = found->parent; node->right = found->right; node->left = found->left; found->right->parent = node; found->left->parent = node; if (parent->left == found) parent->left = node; else parent->right= node; } } else { if (count != found->maskbitcount) return (NULL); /* * Remove the node from the tree and reconnect the subtree * below. */ /* * If there is a tree to the left, look for something to * attach in place of "found". */ prev = found + 1; cur = parent->parent; if (parent != found + 1) { if ((found + 1)->parent->right == found + 1) (found + 1)->parent->right = parent; else (found + 1)->parent->left = parent; if (cur->right == parent) { if (parent->left == found) { cur->right = parent->right; } else if (parent->left != parent - 1) { cur->right = parent->left; } else { cur->right = parent - 1; } cur->right->parent = cur; } else { if (parent->right == found) { cur->left = parent->left; } else if (parent->right != parent - 1) { cur->left = parent->right; } else { cur->left = parent - 1; } cur->left->parent = cur; } parent->left = (found + 1)->left; if ((found + 1)->right != parent) parent->right = (found + 1)->right; parent->left->parent = parent; parent->right->parent = parent; parent->parent = (found + 1)->parent; parent->bitmask = prev->bitmask; parent->offset = prev->offset; parent->index = prev->index; } else { /* * We found an edge node. */ cur = parent->parent; if (cur->left == parent) { if (parent->left == found) { cur->left = parent->right; parent->right->parent = cur; } else { cur->left = parent->left; parent->left->parent = cur; } } else { if (parent->right != found) { cur->right = parent->right; parent->right->parent = cur; } else { cur->right = parent->left; prev->left->parent = cur; } } } } /* * Remove mask associated with this node. */ for (cur = parent; cur->root == 0; cur = cur->parent) { ipf_rdx_mask_t **pm; if (cur->maskbitcount <= found->maskbitcount) break; if (((cur - 1)->addrkey[found->offset] & found->bitmask) != found->addrkey[found->offset]) break; for (pm = &cur->masks; (m = *pm) != NULL; ) if (m->node == cur) { *pm = m->next; break; } else { pm = &m->next; } } KFREE(found->mymask); /* * Masks that have been brought up to this node from below need to * be sent back down. */ for (pmask = &parent->masks; (m = *pmask) != NULL; ) { *pmask = m->next; cur = m->node; if (cur == found) continue; if (found->addrkey[cur->offset] & cur->lastmask) { ipf_rx_attach_mask(parent->right, m); } else if (parent->left != found) { ipf_rx_attach_mask(parent->left, m); } } return (found); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_walktree */ /* Returns: Nil */ /* Paramters: head(I) - pointer to tree head to search */ /* walker(I) - function to call for each node in the tree */ /* arg(I) - parameter to pass to walker, in addition to the */ /* node pointer */ /* */ /* A standard tree walking function except that it is iterative, rather */ /* than recursive and tracks the next node in case the "walker" function */ /* should happen to delete and free the current node. It thus goes without */ /* saying that the "walker" function is not permitted to cause any change */ /* in the validity of the data found at either the left or right child. */ /* ------------------------------------------------------------------------ */ void -ipf_rx_walktree(head, walker, arg) - ipf_rdx_head_t *head; - radix_walk_func_t walker; - void *arg; +ipf_rx_walktree(ipf_rdx_head_t *head, radix_walk_func_t walker, void *arg) { ipf_rdx_node_t *next; ipf_rdx_node_t *node = head->root; ipf_rdx_node_t *base; while (node->index >= 0) node = node->left; for (;;) { base = node; while ((node->parent->right == node) && (node->root == 0)) node = node->parent; for (node = node->parent->right; node->index >= 0; ) node = node->left; next = node; for (node = base; node != NULL; node = base) { base = node->dupkey; if (node->root == 0) walker(node, arg); } node = next; if (node->root) return; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_inithead */ /* Returns: int - 0 = success, else failure */ /* Paramters: softr(I) - pointer to radix context */ /* headp(O) - location for where to store allocated tree head */ /* */ /* This function allocates and initialises a radix tree head structure. */ /* As a traditional radix tree, node 0 is used as the "0" sentinel and node */ /* "2" is used as the all ones sentinel, leaving node "1" as the root from */ /* which the tree is hung with node "0" on its left and node "2" to the */ /* right. The context, "softr", is used here to provide a common source of */ /* the zeroes and ones data rather than have one per head. */ /* ------------------------------------------------------------------------ */ int -ipf_rx_inithead(softr, headp) - radix_softc_t *softr; - ipf_rdx_head_t **headp; +ipf_rx_inithead(radix_softc_t *softr, ipf_rdx_head_t **headp) { ipf_rdx_head_t *ptr; ipf_rdx_node_t *node; KMALLOC(ptr, ipf_rdx_head_t *); *headp = ptr; if (ptr == NULL) return -1; bzero(ptr, sizeof(*ptr)); node = ptr->nodes; ptr->root = node + 1; node[0].index = ADF_OFF_BITS; node[0].index = -1 - node[0].index; node[1].index = ADF_OFF_BITS; node[2].index = node[0].index; node[0].parent = node + 1; node[1].parent = node + 1; node[2].parent = node + 1; node[1].bitmask = htonl(0x80000000); node[0].root = 1; node[1].root = 1; node[2].root = 1; node[0].offset = ADF_OFF_BITS >> 5; node[1].offset = ADF_OFF_BITS >> 5; node[2].offset = ADF_OFF_BITS >> 5; node[1].left = &node[0]; node[1].right = &node[2]; node[0].addrkey = (u_32_t *)softr->zeros; node[2].addrkey = (u_32_t *)softr->ones; #ifdef RDX_DEBUG (void) strcpy(node[0].name, "0_ROOT"); (void) strcpy(node[1].name, "1_ROOT"); (void) strcpy(node[2].name, "2_ROOT"); #endif ptr->addaddr = ipf_rx_addroute; ptr->deladdr = ipf_rx_delete; ptr->lookup = ipf_rx_lookup; ptr->matchaddr = ipf_rx_match; ptr->walktree = ipf_rx_walktree; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_freehead */ /* Returns: Nil */ /* Paramters: head(I) - pointer to tree head to free */ /* */ /* This function simply free's up the radix tree head. Prior to calling */ /* this function, it is expected that the tree will have been emptied. */ /* ------------------------------------------------------------------------ */ void -ipf_rx_freehead(head) - ipf_rdx_head_t *head; +ipf_rx_freehead(ipf_rdx_head_t *head) { KFREE(head); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_create */ /* Parameters: Nil */ /* */ /* ------------------------------------------------------------------------ */ void * -ipf_rx_create() +ipf_rx_create(void) { radix_softc_t *softr; KMALLOC(softr, radix_softc_t *); if (softr == NULL) return NULL; bzero((char *)softr, sizeof(*softr)); KMALLOCS(softr->zeros, u_char *, 3 * sizeof(addrfamily_t)); if (softr->zeros == NULL) { KFREE(softr); return (NULL); } softr->ones = softr->zeros + sizeof(addrfamily_t); return softr; } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_init */ /* Returns: int - 0 = success (always) */ /* */ /* ------------------------------------------------------------------------ */ int -ipf_rx_init(ctx) - void *ctx; +ipf_rx_init(void *ctx) { radix_softc_t *softr = ctx; memset(softr->zeros, 0, 3 * sizeof(addrfamily_t)); memset(softr->ones, 0xff, sizeof(addrfamily_t)); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rx_destroy */ /* Returns: Nil */ /* */ /* ------------------------------------------------------------------------ */ void -ipf_rx_destroy(ctx) - void *ctx; +ipf_rx_destroy(void *ctx) { radix_softc_t *softr = ctx; if (softr->zeros != NULL) KFREES(softr->zeros, 3 * sizeof(addrfamily_t)); KFREE(softr); } /* ====================================================================== */ #ifdef RDX_DEBUG /* * To compile this file as a standalone test unit, use -DRDX_DEBUG=1 */ #define NAME(x) ((x)->index < 0 ? (x)->name : (x)->name) #define GNAME(y) ((y) == NULL ? "NULL" : NAME(y)) typedef struct myst { struct ipf_rdx_node nodes[2]; addrfamily_t dst; addrfamily_t mask; struct myst *next; int printed; } myst_t; typedef struct tabe_s { char *host; char *mask; char *what; } tabe_t; tabe_t builtin[] = { #if 1 { "192:168:100::0", "48", "d" }, { "192:168:100::2", "128", "d" }, #else { "127.192.0.0", "255.255.255.0", "d" }, { "127.128.0.0", "255.255.255.0", "d" }, { "127.96.0.0", "255.255.255.0", "d" }, { "127.80.0.0", "255.255.255.0", "d" }, { "127.72.0.0", "255.255.255.0", "d" }, { "127.64.0.0", "255.255.255.0", "d" }, { "127.56.0.0", "255.255.255.0", "d" }, { "127.48.0.0", "255.255.255.0", "d" }, { "127.40.0.0", "255.255.255.0", "d" }, { "127.32.0.0", "255.255.255.0", "d" }, { "127.24.0.0", "255.255.255.0", "d" }, { "127.16.0.0", "255.255.255.0", "d" }, { "127.8.0.0", "255.255.255.0", "d" }, { "124.0.0.0", "255.0.0.0", "d" }, { "125.0.0.0", "255.0.0.0", "d" }, { "126.0.0.0", "255.0.0.0", "d" }, { "127.0.0.0", "255.0.0.0", "d" }, { "10.0.0.0", "255.0.0.0", "d" }, { "128.250.0.0", "255.255.0.0", "d" }, { "192.168.0.0", "255.255.0.0", "d" }, { "192.168.1.0", "255.255.255.0", "d" }, #endif { NULL, NULL, NULL } }; char *mtable[][1] = { #if 1 { "192:168:100::2" }, { "192:168:101::2" }, #else { "9.0.0.0" }, { "9.0.0.1" }, { "11.0.0.0" }, { "11.0.0.1" }, { "127.0.0.1" }, { "127.0.1.0" }, { "255.255.255.0" }, { "126.0.0.1" }, { "128.251.0.0" }, { "128.251.0.1" }, { "128.251.255.255" }, { "129.250.0.0" }, { "129.250.0.1" }, { "192.168.255.255" }, #endif { NULL } }; int forder[22] = { 14, 13, 12, 5, 10, 3, 19, 7, 4, 20, 8, 2, 17, 9, 16, 11, 15, 1, 6, 18, 0, 21 }; static int nodecount = 0; myst_t *myst_top = NULL; tabe_t *ttable = NULL; void add_addr(ipf_rdx_head_t *, int , int); void checktree(ipf_rdx_head_t *); void delete_addr(ipf_rdx_head_t *rnh, int item); void dumptree(ipf_rdx_head_t *rnh); void nodeprinter(ipf_rdx_node_t *, void *); void printroots(ipf_rdx_head_t *); void random_add(ipf_rdx_head_t *); void random_delete(ipf_rdx_head_t *); void test_addr(ipf_rdx_head_t *rnh, int pref, addrfamily_t *, int); static void -ipf_rx_freenode(node, arg) - ipf_rdx_node_t *node; - void *arg; +ipf_rx_freenode(ipf_rdx_node_t *node, void *arg) { ipf_rdx_head_t *head = arg; ipf_rdx_node_t *rv; myst_t *stp; stp = (myst_t *)node; rv = ipf_rx_delete(head, &stp->dst, &stp->mask); if (rv != NULL) { free(rv); } } const char * -addrname(ap) - addrfamily_t *ap; +addrname(addrfamily_t *ap) { static char name[80]; const char *txt; bzero((char *)name, sizeof(name)); txt = inet_ntop(ap->adf_family, &ap->adf_addr, name, sizeof(name)); return txt; } void -fill6bits(bits, msk) - int bits; - u_int *msk; +fill6bits(int bits, u_int *msk) { if (bits == 0) { msk[0] = 0; msk[1] = 0; msk[2] = 0; msk[3] = 0; return; } msk[0] = 0xffffffff; msk[1] = 0xffffffff; msk[2] = 0xffffffff; msk[3] = 0xffffffff; if (bits == 128) return; if (bits > 96) { msk[3] = htonl(msk[3] << (128 - bits)); } else if (bits > 64) { msk[3] = 0; msk[2] = htonl(msk[2] << (96 - bits)); } else if (bits > 32) { msk[3] = 0; msk[2] = 0; msk[1] = htonl(msk[1] << (64 - bits)); } else { msk[3] = 0; msk[2] = 0; msk[1] = 0; msk[0] = htonl(msk[0] << (32 - bits)); } } void -setaddr(afp, str) - addrfamily_t *afp; - char *str; +setaddr(addrfamily_t *afp, char *str) { bzero((char *)afp, sizeof(*afp)); if (strchr(str, ':') == NULL) { afp->adf_family = AF_INET; afp->adf_len = offsetof(addrfamily_t, adf_addr) + 4; } else { afp->adf_family = AF_INET6; afp->adf_len = offsetof(addrfamily_t, adf_addr) + 16; } inet_pton(afp->adf_family, str, &afp->adf_addr); } void -setmask(afp, str) - addrfamily_t *afp; - char *str; +setmask(addrfamily_t *afp, char *str) { if (strchr(str, '.') != NULL) { afp->adf_addr.in4.s_addr = inet_addr(str); afp->adf_len = offsetof(addrfamily_t, adf_addr) + 4; } else if (afp->adf_family == AF_INET) { afp->adf_addr.i6[0] = htonl(0xffffffff << (32 - atoi(str))); afp->adf_len = offsetof(addrfamily_t, adf_addr) + 4; } else if (afp->adf_family == AF_INET6) { fill6bits(atoi(str), afp->adf_addr.i6); afp->adf_len = offsetof(addrfamily_t, adf_addr) + 16; } } void -nodeprinter(node, arg) - ipf_rdx_node_t *node; - void *arg; +nodeprinter(ipf_rdx_node_t *node, void *arg) { myst_t *stp = (myst_t *)node; printf("Node %-9.9s L %-9.9s R %-9.9s P %9.9s/%-9.9s %s/%d\n", node[0].name, GNAME(node[1].left), GNAME(node[1].right), GNAME(node[0].parent), GNAME(node[1].parent), addrname(&stp->dst), node[0].maskbitcount); if (stp->printed == -1) printf("!!! %d\n", stp->printed); else stp->printed = 1; } void -printnode(stp) - myst_t *stp; +printnode(myst_t *stp) { ipf_rdx_node_t *node = &stp->nodes[0]; if (stp->nodes[0].index > 0) stp = (myst_t *)&stp->nodes[-1]; printf("Node %-9.9s ", node[0].name); printf("L %-9.9s ", GNAME(node[1].left)); printf("R %-9.9s ", GNAME(node[1].right)); printf("P %9.9s", GNAME(node[0].parent)); printf("/%-9.9s ", GNAME(node[1].parent)); printf("%s P%d\n", addrname(&stp->dst), stp->printed); } void buildtab(void) { char line[80], *s; tabe_t *tab; int lines; FILE *fp; lines = 0; fp = fopen("hosts", "r"); while (fgets(line, sizeof(line), fp) != NULL) { s = strchr(line, '\n'); if (s != NULL) *s = '\0'; lines++; if (lines == 1) tab = malloc(sizeof(*tab) * 2); else tab = realloc(tab, (lines + 1) * sizeof(*tab)); tab[lines - 1].host = strdup(line); s = strchr(tab[lines - 1].host, '/'); *s++ = '\0'; tab[lines - 1].mask = s; tab[lines - 1].what = "d"; } fclose(fp); tab[lines].host = NULL; tab[lines].mask = NULL; tab[lines].what = NULL; ttable = tab; } void -printroots(rnh) - ipf_rdx_head_t *rnh; +printroots(ipf_rdx_head_t *rnh) { printf("Root.0.%s b %3d p %-9.9s l %-9.9s r %-9.9s\n", GNAME(&rnh->nodes[0]), rnh->nodes[0].index, GNAME(rnh->nodes[0].parent), GNAME(rnh->nodes[0].left), GNAME(rnh->nodes[0].right)); printf("Root.1.%s b %3d p %-9.9s l %-9.9s r %-9.9s\n", GNAME(&rnh->nodes[1]), rnh->nodes[1].index, GNAME(rnh->nodes[1].parent), GNAME(rnh->nodes[1].left), GNAME(rnh->nodes[1].right)); printf("Root.2.%s b %3d p %-9.9s l %-9.9s r %-9.9s\n", GNAME(&rnh->nodes[2]), rnh->nodes[2].index, GNAME(rnh->nodes[2].parent), GNAME(rnh->nodes[2].left), GNAME(rnh->nodes[2].right)); } int main(int argc, char *argv[]) { addrfamily_t af; ipf_rdx_head_t *rnh; radix_softc_t *ctx; int j; int i; rnh = NULL; buildtab(); ctx = ipf_rx_create(); ipf_rx_init(ctx); ipf_rx_inithead(ctx, &rnh); printf("=== ADD-0 ===\n"); for (i = 0; ttable[i].host != NULL; i++) { add_addr(rnh, i, i); checktree(rnh); } printroots(rnh); ipf_rx_walktree(rnh, nodeprinter, NULL); printf("=== DELETE-0 ===\n"); for (i = 0; ttable[i].host != NULL; i++) { delete_addr(rnh, i); printroots(rnh); ipf_rx_walktree(rnh, nodeprinter, NULL); } printf("=== ADD-1 ===\n"); for (i = 0; ttable[i].host != NULL; i++) { setaddr(&af, ttable[i].host); add_addr(rnh, i, i); /*forder[i]); */ checktree(rnh); } dumptree(rnh); ipf_rx_walktree(rnh, nodeprinter, NULL); printf("=== TEST-1 ===\n"); for (i = 0; ttable[i].host != NULL; i++) { setaddr(&af, ttable[i].host); test_addr(rnh, i, &af, -1); } printf("=== TEST-2 ===\n"); for (i = 0; mtable[i][0] != NULL; i++) { setaddr(&af, mtable[i][0]); test_addr(rnh, i, &af, -1); } printf("=== DELETE-1 ===\n"); for (i = 0; ttable[i].host != NULL; i++) { if (ttable[i].what[0] != 'd') continue; delete_addr(rnh, i); for (j = 0; ttable[j].host != NULL; j++) { setaddr(&af, ttable[j].host); test_addr(rnh, i, &af, 3); } printroots(rnh); ipf_rx_walktree(rnh, nodeprinter, NULL); } dumptree(rnh); printf("=== ADD-2 ===\n"); random_add(rnh); checktree(rnh); dumptree(rnh); ipf_rx_walktree(rnh, nodeprinter, NULL); printf("=== DELETE-2 ===\n"); random_delete(rnh); checktree(rnh); dumptree(rnh); ipf_rx_walktree(rnh, ipf_rx_freenode, rnh); return 0; } void -dumptree(rnh) - ipf_rdx_head_t *rnh; +dumptree(ipf_rdx_head_t *rnh) { myst_t *stp; printf("VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV\n"); printroots(rnh); for (stp = myst_top; stp; stp = stp->next) printnode(stp); printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); } void -test_addr(rnh, pref, addr, limit) - ipf_rdx_head_t *rnh; - int pref, limit; - addrfamily_t *addr; +test_addr(ipf_rdx_head_t *rnh, int pref, addrfamily_t *addr, int limit) { static int extras[14] = { 0, -1, 1, 3, 5, 8, 9, 15, 16, 19, 255, 256, 65535, 65536 }; ipf_rdx_node_t *rn; addrfamily_t af; char name[80]; myst_t *stp; int i; memset(&af, 0, sizeof(af)); #if 0 if (limit < 0 || limit > 14) limit = 14; for (i = 0; i < limit; i++) { if (ttable[i].host == NULL) break; setaddr(&af, ttable[i].host); printf("%d.%d.LOOKUP(%s)", pref, i, addrname(&af)); rn = ipf_rx_match(rnh, &af); stp = (myst_t *)rn; printf(" = %s (%s/%d)\n", GNAME(rn), rn ? addrname(&stp->dst) : "NULL", rn ? rn->maskbitcount : 0); } #else printf("%d.%d.LOOKUP(%s)", pref, -1, addrname(addr)); rn = ipf_rx_match(rnh, addr); stp = (myst_t *)rn; printf(" = %s (%s/%d)\n", GNAME(rn), rn ? addrname(&stp->dst) : "NULL", rn ? rn->maskbitcount : 0); #endif } void -delete_addr(rnh, item) - ipf_rdx_head_t *rnh; - int item; +delete_addr(ipf_rdx_head_t *rnh, int item) { ipf_rdx_node_t *rn; addrfamily_t mask; addrfamily_t af; myst_t **pstp; myst_t *stp; memset(&af, 0, sizeof(af)); memset(&mask, 0, sizeof(mask)); setaddr(&af, ttable[item].host); mask.adf_family = af.adf_family; setmask(&mask, ttable[item].mask); printf("DELETE(%s)\n", addrname(&af)); rn = ipf_rx_delete(rnh, &af, &mask); if (rn == NULL) { printf("FAIL LOOKUP DELETE\n"); checktree(rnh); for (stp = myst_top; stp != NULL; stp = stp->next) if (stp->printed != -1) stp->printed = -2; ipf_rx_walktree(rnh, nodeprinter, NULL); dumptree(rnh); abort(); } printf("%d.delete(%s) = %s\n", item, addrname(&af), GNAME(rn)); for (pstp = &myst_top; (stp = *pstp) != NULL; pstp = &stp->next) if (stp == (myst_t *)rn) break; stp->printed = -1; stp->nodes[0].parent = &stp->nodes[0]; stp->nodes[1].parent = &stp->nodes[1]; *pstp = stp->next; free(stp); nodecount--; checktree(rnh); } void -add_addr(rnh, n, item) - ipf_rdx_head_t *rnh; - int n, item; +add_addr(ipf_rdx_head_t *rnh, int n, int item) { ipf_rdx_node_t *rn; myst_t *stp; stp = calloc(1, sizeof(*stp)); rn = (ipf_rdx_node_t *)stp; setaddr(&stp->dst, ttable[item].host); stp->mask.adf_family = stp->dst.adf_family; setmask(&stp->mask, ttable[item].mask); stp->next = myst_top; myst_top = stp; #ifdef RDX_DEBUG (void) snprintf(rn[0].name, sizeof(ipf_rdx_node.name), "_BORN.0"); (void) snprintf(rn[1].name, sizeof(ipf_rdx_node.name), "_BORN.1"); rn = ipf_rx_addroute(rnh, &stp->dst, &stp->mask, stp->nodes); (void) snprintf(rn[0].name, sizeof(ipf_rdx_node.name), "%d_NODE.0", item); (void) snprintf(rn[1].name, sizeof(ipf_rdx_node.name), "%d_NODE.1", item); printf("ADD %d/%d %s/%s\n", n, item, rn[0].name, rn[1].name); #endif nodecount++; checktree(rnh); } void checktree(ipf_rdx_head_t *head) { myst_t *s1; ipf_rdx_node_t *rn; if (nodecount <= 1) return; for (s1 = myst_top; s1 != NULL; s1 = s1->next) { int fault = 0; if (s1->printed == -1) continue; rn = &s1->nodes[1]; if (rn->right->parent != rn) fault |= 1; if (rn->left->parent != rn) fault |= 2; if (rn->parent->left != rn && rn->parent->right != rn) fault |= 4; if (fault != 0) { printf("FAULT %#x %s\n", fault, rn->name); dumptree(head); ipf_rx_walktree(head, nodeprinter, NULL); fflush(stdout); fflush(stderr); printf("--\n"); abort(); } } } int * randomize(int *pnitems) { int *order; int nitems; int choice; int j; int i; nitems = sizeof(ttable) / sizeof(ttable[0]); *pnitems = nitems; order = calloc(nitems, sizeof(*order)); srandom(getpid() * time(NULL)); memset(order, 0xff, nitems * sizeof(*order)); order[21] = 21; for (i = 0; i < nitems - 1; i++) { do { choice = rand() % (nitems - 1); for (j = 0; j < nitems; j++) if (order[j] == choice) break; } while (j != nitems); order[i] = choice; } return order; } void -random_add(rnh) - ipf_rdx_head_t *rnh; +random_add(ipf_rdx_head_t *rnh) { int *order; int nitems; int i; order = randomize(&nitems); for (i = 0; i < nitems - 1; i++) { add_addr(rnh, i, order[i]); checktree(rnh); } free(order); } void -random_delete(rnh) - ipf_rdx_head_t *rnh; +random_delete(ipf_rdx_head_t *rnh) { int *order; int nitems; int i; order = randomize(&nitems); for (i = 0; i < nitems - 1; i++) { delete_addr(rnh, i); checktree(rnh); } free(order); } #endif /* RDX_DEBUG */