diff options
Diffstat (limited to 'sys/contrib/ipfilter/netinet/ip_state.c')
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_state.c | 607 |
1 files changed, 408 insertions, 199 deletions
diff --git a/sys/contrib/ipfilter/netinet/ip_state.c b/sys/contrib/ipfilter/netinet/ip_state.c index 585929f..322e59f 100644 --- a/sys/contrib/ipfilter/netinet/ip_state.c +++ b/sys/contrib/ipfilter/netinet/ip_state.c @@ -1,9 +1,12 @@ /* - * Copyright (C) 1995-2001 by Darren Reed. + * Copyright (C) 1995-2002 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ +#ifdef __sgi +# include <sys/ptimers.h> +#endif #include <sys/errno.h> #include <sys/types.h> #include <sys/param.h> @@ -36,7 +39,6 @@ # include <sys/ioctl.h> #endif #include <sys/time.h> -#include <sys/uio.h> #ifndef linux # include <sys/protosw.h> #endif @@ -78,7 +80,6 @@ #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" -#include "netinet/ip_proxy.h" #include "netinet/ip_state.h" #ifdef USE_INET6 #include <netinet/icmp6.h> @@ -104,7 +105,6 @@ static const char rcsid[] = "@(#)$FreeBSD$"; #define TCP_CLOSE (TH_FIN|TH_RST) static ipstate_t **ips_table = NULL; -static ipstate_t *ips_list = NULL; static int ips_num = 0; static int ips_wild = 0; static ips_stat_t ips_stats; @@ -147,8 +147,12 @@ int fr_statemax = IPSTATE_MAX, fr_statesize = IPSTATE_SIZE; int fr_state_doflush = 0, fr_state_lock = 0; +ipstate_t *ips_list = NULL; static int icmpreplytype4[ICMP_MAXTYPE + 1]; +#ifdef USE_INET6 +static int icmpreplytype6[ICMP6_MAXTYPE + 1]; +#endif int fr_stateinit() { @@ -167,6 +171,16 @@ int fr_stateinit() 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; } @@ -186,15 +200,18 @@ static ips_stat_t *fr_statetstats() * 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. */ static int fr_state_flush(which) int which; { - register ipstate_t *is, **isp; + ipstate_t *is, **isp; #if defined(_KERNEL) && !SOLARIS int s; #endif - int delete, removed = 0; + int delete, removed = 0, try; SPL_NET(s); for (isp = &ips_list; (is = *isp); ) { @@ -206,6 +223,7 @@ int which; delete = 1; break; case 1 : + case 2 : if (is->is_p != IPPROTO_TCP) break; if ((is->is_state[0] != TCPS_ESTABLISHED) || @@ -227,6 +245,40 @@ int which; } else isp = &is->is_next; } + + /* + * Asked to remove inactive entries, try again if first attempt + * failed. In this case, 86400 is half a day because the counter is + * activated every half second. + */ + if ((which == 2) && (removed == 0)) { + try = 86400; /* half a day */ + for (; (try < FIVE_DAYS) && (removed == 0); try += 86400) { + for (isp = &ips_list; (is = *isp); ) { + delete = 0; + if ((is->is_p == IPPROTO_TCP) && + ((is->is_state[0] == TCPS_ESTABLISHED) || + (is->is_state[1] == TCPS_ESTABLISHED)) && + (is->is_age < try)) { + ips_stats.iss_fin++; + delete = 1; + } else if ((is->is_p != IPPROTO_TCP) && + (is->is_pkts > 1)) { + ips_stats.iss_expire++; + delete = 1; + } + if (delete) { +#ifdef IPFILTER_LOG + ipstate_log(is, ISL_FLUSH); +#endif + fr_delstate(is); + removed++; + } else + isp = &is->is_next; + } + } + } + SPL_X(s); return removed; } @@ -339,19 +391,19 @@ int mode; } +/* + * Copy out state information from the kernel to a user space process. + */ int fr_stgetent(data) caddr_t data; { register ipstate_t *is, *isn; - ipstate_save_t ips, *ipsp; + ipstate_save_t ips; int error; - error = IRCOPY(data, (caddr_t)&ipsp, sizeof(ipsp)); - if (error) - return EFAULT; - error = IRCOPY((caddr_t)ipsp, (caddr_t)&ips, sizeof(ips)); + error = IRCOPYPTR(data, (caddr_t)&ips, sizeof(ips)); if (error) - return EFAULT; + return error; isn = ips.ips_next; if (!isn) { @@ -378,7 +430,7 @@ caddr_t data; if (isn->is_rule) bcopy((char *)isn->is_rule, (char *)&ips.ips_fr, sizeof(ips.ips_fr)); - error = IWCOPY((caddr_t)&ips, ipsp, sizeof(ips)); + error = IWCOPYPTR((caddr_t)&ips, data, sizeof(ips)); if (error) error = EFAULT; return error; @@ -389,16 +441,14 @@ int fr_stputent(data) caddr_t data; { register ipstate_t *is, *isn; - ipstate_save_t ips, *ipsp; - int error, out; + ipstate_save_t ips; + int error, out, i; frentry_t *fr; + char *name; - error = IRCOPY(data, (caddr_t)&ipsp, sizeof(ipsp)); - if (error) - return EFAULT; - error = IRCOPY((caddr_t)ipsp, (caddr_t)&ips, sizeof(ips)); + error = IRCOPYPTR(data, (caddr_t)&ips, sizeof(ips)); if (error) - return EFAULT; + return error; KMALLOC(isn, ipstate_t *); if (isn == NULL) @@ -417,24 +467,34 @@ caddr_t data; out = fr->fr_flags & FR_OUTQUE ? 1 : 0; isn->is_rule = fr; ips.ips_is.is_rule = fr; - if (*fr->fr_ifname) { - fr->fr_ifa = GETUNIT(fr->fr_ifname, fr->fr_v); - if (fr->fr_ifa == NULL) - fr->fr_ifa = (void *)-1; -#ifdef _KERNEL - else { - strncpy(isn->is_ifname[out], - IFNAME(fr->fr_ifa), IFNAMSIZ); - isn->is_ifp[out] = fr->fr_ifa; + + /* + * Look up all the interface names in the rule. + */ + for (i = 0; i < 4; i++) { + name = fr->fr_ifnames[i]; + if ((name[1] == '\0') && + ((name[0] == '-') || (name[0] == '*'))) { + fr->fr_ifas[i] = NULL; + } else if (*name != '\0') { + fr->fr_ifas[i] = GETUNIT(name, + fr->fr_v); + if (fr->fr_ifas[i] == NULL) + fr->fr_ifas[i] = (void *)-1; + else { + strncpy(isn->is_ifname[i], + IFNAME(fr->fr_ifas[i]), + IFNAMSIZ); + } } -#endif - } else - fr->fr_ifa = NULL; + isn->is_ifp[out] = fr->fr_ifas[i]; + } + /* * send a copy back to userland of what we ended up * to allow for verification. */ - error = IWCOPY((caddr_t)&ips, ipsp, sizeof(ips)); + error = IWCOPYPTR((caddr_t)&ips, data, sizeof(ips)); if (error) { KFREE(isn); KFREE(fr); @@ -455,22 +515,34 @@ caddr_t data; } +/* + * Insert a state table entry manually. + */ void fr_stinsert(is) register ipstate_t *is; { register u_int hv = is->is_hv; + char *name; + int i; MUTEX_INIT(&is->is_lock, "ipf state entry", NULL); - is->is_ifname[0][sizeof(is->is_ifname[0]) - 1] = '\0'; - if (is->is_ifname[0][0] != '\0') { - is->is_ifp[0] = GETUNIT(is->is_ifname[0], is->is_v); - } - is->is_ifname[1][sizeof(is->is_ifname[0]) - 1] = '\0'; - if (is->is_ifname[1][0] != '\0') { - is->is_ifp[1] = GETUNIT(is->is_ifname[1], is->is_v); + /* + * Look up all the interface names in the state entry. + */ + for (i = 0; i < 4; i++) { + name = is->is_ifname[i]; + if ((name[1] == '\0') && + ((name[0] == '-') || (name[0] == '*'))) { + is->is_ifp[0] = NULL; + } else if (*name != '\0') { + is->is_ifp[i] = GETUNIT(name, is->is_v); + if (is->is_ifp[i] == NULL) + is->is_ifp[i] = (void *)-1; + } } + /* * add into list table. */ @@ -493,16 +565,19 @@ register ipstate_t *is; /* * Create a new ipstate structure and hang it off the hash table. */ -ipstate_t *fr_addstate(ip, fin, flags) +ipstate_t *fr_addstate(ip, fin, stsave, flags) ip_t *ip; fr_info_t *fin; +ipstate_t **stsave; u_int flags; { register tcphdr_t *tcp = NULL; register ipstate_t *is; register u_int hv; + struct icmp *ic; ipstate_t ips; u_int pass; + void *ifp; int out; if (fr_state_lock || (fin->fin_off != 0) || (fin->fin_fl & FI_SHORT)) @@ -516,8 +591,6 @@ u_int flags; is = &ips; bzero((char *)is, sizeof(*is)); ips.is_age = 1; - ips.is_state[0] = 0; - ips.is_state[1] = 0; /* * Copy and calculate... */ @@ -528,14 +601,21 @@ u_int flags; hv += is->is_daddr; #ifdef USE_INET6 if (fin->fin_v == 6) { - if (is->is_p == IPPROTO_ICMPV6) { - if (IN6_IS_ADDR_MULTICAST(&is->is_dst.in6)) - flags |= FI_W_DADDR; - if (out) - hv -= is->is_daddr; - else - hv -= is->is_saddr; + if ((is->is_p == IPPROTO_ICMPV6) && + IN6_IS_ADDR_MULTICAST(&is->is_dst.in6)) { + /* + * So you can do keep state with neighbour discovery. + */ + flags |= FI_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 @@ -543,30 +623,35 @@ u_int flags; { #ifdef USE_INET6 case IPPROTO_ICMPV6 : -#endif - case IPPROTO_ICMP : - { - struct icmp *ic = (struct icmp *)fin->fin_dp; - -#ifdef USE_INET6 - if ((is->is_p == IPPROTO_ICMPV6) && - ((ic->icmp_type & ICMP6_INFOMSG_MASK) == 0)) + ic = (struct icmp *)fin->fin_dp; + if ((ic->icmp_type & ICMP6_INFOMSG_MASK) == 0) return NULL; -#endif + switch (ic->icmp_type) { -#ifdef USE_INET6 case ICMP6_ECHO_REQUEST : - is->is_icmp.ics_type = ICMP6_ECHO_REPLY; + is->is_icmp.ics_type = ic->icmp_type; hv += (is->is_icmp.ics_id = ic->icmp_id); hv += (is->is_icmp.ics_seq = ic->icmp_seq); break; case ICMP6_MEMBERSHIP_QUERY : case ND_ROUTER_SOLICIT : case ND_NEIGHBOR_SOLICIT : - is->is_icmp.ics_type = ic->icmp_type + 1; + case ICMP6_NI_QUERY : + is->is_icmp.ics_type = ic->icmp_type; break; + default : + return NULL; + } + ATOMIC_INCL(ips_stats.iss_icmp); + is->is_age = fr_icmptimeout; + break; #endif + case IPPROTO_ICMP : + ic = (struct icmp *)fin->fin_dp; + + switch (ic->icmp_type) + { case ICMP_ECHO : case ICMP_TSTAMP : case ICMP_IREQ : @@ -581,9 +666,7 @@ u_int flags; ATOMIC_INCL(ips_stats.iss_icmp); is->is_age = fr_icmptimeout; break; - } case IPPROTO_TCP : - { tcp = (tcphdr_t *)fin->fin_dp; if (tcp->th_flags & TH_RST) @@ -592,11 +675,11 @@ u_int flags; * The endian of the ports doesn't matter, but the ack and * sequence numbers do as we do mathematics on them later. */ - is->is_dport = tcp->th_dport; - is->is_sport = tcp->th_sport; + is->is_sport = htons(fin->fin_data[0]); + is->is_dport = htons(fin->fin_data[1]); if ((flags & (FI_W_DPORT|FI_W_SPORT)) == 0) { - hv += tcp->th_dport; - hv += tcp->th_sport; + hv += is->is_sport; + hv += is->is_dport; } is->is_send = ntohl(tcp->th_seq) + fin->fin_dlen - (tcp->th_off << 2) + @@ -615,23 +698,22 @@ u_int flags; */ ATOMIC_INCL(ips_stats.iss_tcp); break; - } + case IPPROTO_UDP : - { tcp = (tcphdr_t *)fin->fin_dp; - is->is_dport = tcp->th_dport; - is->is_sport = tcp->th_sport; + is->is_sport = htons(fin->fin_data[0]); + is->is_dport = htons(fin->fin_data[1]); if ((flags & (FI_W_DPORT|FI_W_SPORT)) == 0) { - hv += tcp->th_dport; - hv += tcp->th_sport; + hv += is->is_sport; + hv += is->is_dport; } ATOMIC_INCL(ips_stats.iss_udp); is->is_age = fr_udptimeout; break; - } default : - return NULL; + is->is_age = fr_udptimeout; + break; } KMALLOC(is, ipstate_t *); @@ -646,19 +728,47 @@ u_int flags; if (is->is_rule != NULL) { ATOMIC_INC32(is->is_rule->fr_ref); pass = is->is_rule->fr_flags; + is->is_frage[0] = is->is_rule->fr_age[0]; + is->is_frage[1] = is->is_rule->fr_age[1]; + if (is->is_frage[0] != 0) + is->is_age = is->is_frage[0]; + + is->is_ifp[(out << 1) + 1] = is->is_rule->fr_ifas[1]; + is->is_ifp[(1 - out) << 1] = is->is_rule->fr_ifas[2]; + is->is_ifp[((1 - out) << 1) + 1] = is->is_rule->fr_ifas[3]; + + if (((ifp = is->is_rule->fr_ifas[1]) != NULL) && + (ifp != (void *)-1)) + strncpy(is->is_ifname[(out << 1) + 1], + IFNAME(ifp), IFNAMSIZ); + if (((ifp = is->is_rule->fr_ifas[2]) != NULL) && + (ifp != (void *)-1)) + strncpy(is->is_ifname[(1 - out) << 1], + IFNAME(ifp), IFNAMSIZ); + if (((ifp = is->is_rule->fr_ifas[3]) != NULL) && + (ifp != (void *)-1)) + strncpy(is->is_ifname[((1 - out) << 1) + 1], + IFNAME(ifp), IFNAMSIZ); } else pass = fr_flags; + + is->is_ifp[out << 1] = fin->fin_ifp; + strncpy(is->is_ifname[out << 1], IFNAME(fin->fin_ifp), IFNAMSIZ); + WRITE_ENTER(&ipf_state); is->is_pass = pass; - is->is_pkts = 1; - is->is_bytes = fin->fin_dlen + fin->fin_hlen; + if ((flags & FI_IGNOREPKT) == 0) { + is->is_pkts = 1; + is->is_bytes = fin->fin_dlen + fin->fin_hlen; + } /* * We want to check everything that is a property of this packet, * but we don't (automatically) care about it's fragment status as * this may change. */ - is->is_v = fin->fin_fi.fi_v; + is->is_v = fin->fin_v; + is->is_rulen = fin->fin_rule; is->is_opt = fin->fin_fi.fi_optmsk; is->is_optmsk = 0xffffffff; is->is_sec = fin->fin_fi.fi_secmsk; @@ -670,20 +780,14 @@ u_int flags; is->is_flags |= flags & (FI_WILDP|FI_WILDA); if (flags & (FI_WILDP|FI_WILDA)) ips_wild++; - is->is_ifp[1 - out] = NULL; - is->is_ifp[out] = fin->fin_ifp; -#ifdef _KERNEL - strncpy(is->is_ifname[out], IFNAME(fin->fin_ifp), IFNAMSIZ); -#endif - is->is_ifname[1 - out][0] = '\0'; + if (pass & FR_LOGFIRST) is->is_pass &= ~(FR_LOGFIRST|FR_LOG); fr_stinsert(is); + is->is_me = stsave; if (is->is_p == IPPROTO_TCP) { - MUTEX_ENTER(&is->is_lock); fr_tcp_age(&is->is_age, is->is_state, fin, 0); /* 0 = packet from the source */ - MUTEX_EXIT(&is->is_lock); } #ifdef IPFILTER_LOG ipstate_log(is, ISL_NEW); @@ -803,19 +907,25 @@ tcphdr_t *tcp; } +/* + * Match a state table entry against an IP packet. + */ static int fr_matchsrcdst(is, src, dst, fin, tcp) ipstate_t *is; union i6addr src, dst; fr_info_t *fin; tcphdr_t *tcp; { - int ret = 0, rev, out, flags; + int ret = 0, rev, out, flags, idx; u_short sp, dp; void *ifp; rev = IP6NEQ(is->is_dst, dst); ifp = fin->fin_ifp; out = fin->fin_out; + flags = is->is_flags & (FI_WILDA|FI_WILDP); + sp = 0; + dp = 0; if (tcp != NULL) { flags = is->is_flags; @@ -827,44 +937,28 @@ tcphdr_t *tcp; else if (!(flags & FI_W_DPORT) && (dp != is->is_dport)) rev = 1; } - } else { - flags = is->is_flags & FI_WILDA; - sp = 0; - dp = 0; } - if (rev == 0) { - if (!out) { - if (is->is_ifpin == NULL || is->is_ifpin == ifp) - ret = 1; - } else { - if (is->is_ifpout == NULL || is->is_ifpout == ifp) - ret = 1; - } - } else { - if (out) { - if (is->is_ifpin == NULL || is->is_ifpin == ifp) - ret = 1; - } else { - if (is->is_ifpout == NULL || is->is_ifpout == ifp) - ret = 1; - } - } + idx = (out << 1) + rev; + + if ((is->is_ifp[idx] == NULL && + (*is->is_ifname[idx] == '\0' || *is->is_ifname[idx] == '*')) || + is->is_ifp[idx] == ifp) + ret = 1; + if (ret == 0) return 0; ret = 0; if (rev == 0) { - if ( - (IP6EQ(is->is_dst, dst) || (flags & FI_W_DADDR)) && + if ((IP6EQ(is->is_dst, dst) || (flags & FI_W_DADDR)) && (IP6EQ(is->is_src, src) || (flags & FI_W_SADDR)) && (!tcp || ((sp == is->is_sport || flags & FI_W_SPORT) && (dp == is->is_dport || flags & FI_W_DPORT)))) { ret = 1; } } else { - if ( - (IP6EQ(is->is_dst, src) || (flags & FI_W_DADDR)) && + if ((IP6EQ(is->is_dst, src) || (flags & FI_W_DADDR)) && (IP6EQ(is->is_src, dst) || (flags & FI_W_SADDR)) && (!tcp || ((sp == is->is_dport || flags & FI_W_DPORT) && (dp == is->is_sport || flags & FI_W_SPORT)))) { @@ -887,6 +981,26 @@ tcphdr_t *tcp; (fin->fin_fi.fi_auth != is->is_auth)) return 0; + flags = is->is_flags & (FI_WILDA|FI_WILDP); + if ((flags & (FI_W_SADDR|FI_W_DADDR))) { + if ((flags & FI_W_SADDR) != 0) { + if (rev == 0) { + is->is_src = fin->fin_fi.fi_src; + } else { + is->is_src = fin->fin_fi.fi_dst; + } + } else if ((flags & FI_W_DPORT) != 0) { + if (rev == 0) { + is->is_dst = fin->fin_fi.fi_dst; + } else { + is->is_dst = fin->fin_fi.fi_src; + } + } + is->is_flags &= ~(FI_W_SADDR|FI_W_DADDR); + if ((is->is_flags & (FI_WILDA|FI_WILDP)) == 0) + ips_wild--; + } + if ((flags & (FI_W_SPORT|FI_W_DPORT))) { if ((flags & FI_W_SPORT) != 0) { if (rev == 0) { @@ -913,30 +1027,14 @@ tcphdr_t *tcp; ret = -1; - if (!rev) { - if (out) { - if (!is->is_ifpout) - ret = 1; - } else { - if (!is->is_ifpin) - ret = 0; - } - } else { - if (out) { - if (!is->is_ifpin) - ret = 0; - } else { - if (!is->is_ifpout) - ret = 1; - } - } + if (is->is_ifp[idx] == NULL && + (*is->is_ifname[idx] == '\0' || *is->is_ifname[idx] == '*')) + ret = idx; if (ret >= 0) { is->is_ifp[ret] = ifp; -#ifdef _KERNEL - strncpy(is->is_ifname[ret], IFNAME(fin->fin_ifp), + strncpy(is->is_ifname[ret], IFNAME(ifp), sizeof(is->is_ifname[ret])); -#endif } fin->fin_rev = rev; return 1; @@ -953,20 +1051,24 @@ icmphdr_t *icmp; * it will still be the same type. */ if (((icmp->icmp_type == is->is_type) || - (icmpreplytype4[is->is_type] == icmp->icmp_type)) && - (icmp->icmp_id == is->is_icmp.ics_id) && - (icmp->icmp_seq == is->is_icmp.ics_seq)) { - return 1; - }; + (icmpreplytype4[is->is_type] == icmp->icmp_type))) { + if (icmp->icmp_type != ICMP_ECHOREPLY) + return 1; + if ((icmp->icmp_id == is->is_icmp.ics_id) && + (icmp->icmp_seq == is->is_icmp.ics_seq)) + return 1; + } } #ifdef USE_INET6 else if (is->is_v == 6) { - if ((is->is_type == ICMP6_ECHO_REPLY) && - (icmp->icmp_type == ICMP6_ECHO_REQUEST) && - (icmp->icmp_id == is->is_icmp.ics_id) && - (icmp->icmp_seq == is->is_icmp.ics_seq)) { - return 1; - }; + if (((icmp->icmp_type == is->is_type) || + (icmpreplytype6[is->is_type] == icmp->icmp_type))) { + if (icmp->icmp_type != ICMP6_ECHO_REPLY) + return 1; + if ((icmp->icmp_id == is->is_icmp.ics_id) && + (icmp->icmp_seq == is->is_icmp.ics_seq)) + return 1; + } } #endif return 0; @@ -998,6 +1100,7 @@ fr_info_t *fin; if (((ip->ip_v != 4) || (ip->ip_hl != 5)) || (fin->fin_plen < ICMPERR_MINPKTLEN)) return NULL; + ic = (struct icmp *)fin->fin_dp; type = ic->icmp_type; /* @@ -1051,8 +1154,11 @@ fr_info_t *fin; */ bzero((char *)&src, sizeof(src)); bzero((char *)&dst, sizeof(dst)); + fr = NULL; - if (oip->ip_p == IPPROTO_ICMP) { + switch (oip->ip_p) + { + case IPPROTO_ICMP : icmp = (icmphdr_t *)((char *)oip + (oip->ip_hl << 2)); /* @@ -1098,15 +1204,17 @@ fr_info_t *fin; is->is_pkts++; is->is_bytes += ip->ip_len; fr = is->is_rule; - RWLOCK_EXIT(&ipf_state); - return fr; + break; } RWLOCK_EXIT(&ipf_state); + return fr; + + case IPPROTO_TCP : + case IPPROTO_UDP : + break; + default : return NULL; - }; - - if ((oip->ip_p != IPPROTO_TCP) && (oip->ip_p != IPPROTO_UDP)) - return NULL; + } tcp = (tcphdr_t *)((char *)oip + (oip->ip_hl << 2)); dport = tcp->th_dport; @@ -1158,15 +1266,18 @@ fr_info_t *fin; * for the accompanying state table entry. * It remains to be seen if that is correct. XXX */ - RWLOCK_EXIT(&ipf_state); - return fr; + break; } } RWLOCK_EXIT(&ipf_state); - return NULL; + return fr; } +/* + * Move a state hash table entry from its old location at is->is_hv to + * its new location, indexed by hv % fr_statesize. + */ static void fr_ipsmove(isp, is, hv) ipstate_t **isp, *is; u_int hv; @@ -1213,6 +1324,7 @@ fr_info_t *fin; struct icmp *ic; frentry_t *fr; tcphdr_t *tcp; + int rev; if (fr_state_lock || (fin->fin_off != 0) || (fin->fin_fl & FI_SHORT)) return NULL; @@ -1229,42 +1341,113 @@ fr_info_t *fin; /* * Search the hash table for matching packet header info. + * At the bottom of this switch statement, the following is expected: + * is == NULL, no lock on ipf_state is held. + * is != NULL, a lock on ipf_state is held. */ v = fin->fin_fi.fi_v; - switch (fin->fin_fi.fi_p) +#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 + + switch (fin->fin_p) { #ifdef USE_INET6 case IPPROTO_ICMPV6 : + tcp = NULL; + tryagain = 0; if (v == 6) { - if (fin->fin_out) - hv -= dst.in4.s_addr; - else - hv -= src.in4.s_addr; if ((ic->icmp_type == ICMP6_ECHO_REQUEST) || (ic->icmp_type == ICMP6_ECHO_REPLY)) { hv += ic->icmp_id; hv += ic->icmp_seq; } } + READ_ENTER(&ipf_state); +icmp6again: + hvm = hv % fr_statesize; + for (isp = &ips_table[hvm]; (is = *isp); isp = &is->is_hnext) + if ((is->is_p == pr) && (is->is_v == v) && + fr_matchsrcdst(is, src, dst, fin, NULL) && + fr_matchicmpqueryreply(v, is, ic)) { + rev = fin->fin_rev; + if (is->is_frage[rev] != 0) + is->is_age = is->is_frage[rev]; + else if (fin->fin_rev) + is->is_age = fr_icmpacktimeout; + else + is->is_age = fr_icmptimeout; + break; + } + + if (is != NULL) { + if (tryagain && !(is->is_flags & FI_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]; + fr_ipsmove(isp, is, hv); + MUTEX_DOWNGRADE(&ipf_state); + } + break; + } + RWLOCK_EXIT(&ipf_state); + + /* + * No matching icmp state entry. Perhaps this is a + * response to another state entry. + */ + if ((ips_wild != 0) && (v == 6) && (tryagain == 0) && + !IN6_IS_ADDR_MULTICAST(&fin->fin_fi.fi_src.in6)) { + 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(&ipf_state); + goto icmp6again; + } + + fr = fr_checkicmp6matchingstate((ip6_t *)ip, fin); + if (fr) + return fr; + break; #endif case IPPROTO_ICMP : + tcp = NULL; if (v == 4) { hv += ic->icmp_id; hv += ic->icmp_seq; } - hv %= fr_statesize; + hvm = hv % fr_statesize; READ_ENTER(&ipf_state); - for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_hnext) { + for (isp = &ips_table[hvm]; (is = *isp); isp = &is->is_hnext) if ((is->is_p == pr) && (is->is_v == v) && fr_matchsrcdst(is, src, dst, fin, NULL) && fr_matchicmpqueryreply(v, is, ic)) { - if (fin->fin_rev) + rev = fin->fin_rev; + if (is->is_frage[rev] != 0) + is->is_age = is->is_frage[rev]; + else if (fin->fin_rev) is->is_age = fr_icmpacktimeout; else is->is_age = fr_icmptimeout; break; } - } + if (is != NULL) break; RWLOCK_EXIT(&ipf_state); @@ -1272,28 +1455,21 @@ fr_info_t *fin; * No matching icmp state entry. Perhaps this is a * response to another state entry. */ -#ifdef USE_INET6 - if (v == 6) - fr = fr_checkicmp6matchingstate((ip6_t *)ip, fin); - else -#endif - fr = fr_checkicmpmatchingstate(ip, fin); + fr = fr_checkicmpmatchingstate(ip, fin); if (fr) return fr; break; case IPPROTO_TCP : - { - register u_short dport, sport; - register int i; - - i = tcp->th_flags; /* * Just plain ignore RST flag set with either FIN or SYN. */ - if ((i & TH_RST) && - ((i & (TH_FIN|TH_SYN|TH_RST)) != TH_RST)) + if ((tcp->th_flags & TH_RST) && + ((tcp->th_flags & (TH_FIN|TH_SYN|TH_RST)) != TH_RST)) break; case IPPROTO_UDP : + { + register u_short dport, sport; + dport = tcp->th_dport; sport = tcp->th_sport; tryagain = 0; @@ -1305,12 +1481,15 @@ retry_tcpudp: for (isp = &ips_table[hvm]; (is = *isp); isp = &is->is_hnext) if ((is->is_p == pr) && (is->is_v == v) && fr_matchsrcdst(is, src, dst, fin, tcp)) { + rev = fin->fin_rev; if ((pr == IPPROTO_TCP)) { if (!fr_tcpstate(is, fin, ip, tcp)) { continue; } } else if ((pr == IPPROTO_UDP)) { - if (fin->fin_rev) + if (is->is_frage[rev] != 0) + is->is_age = is->is_frage[rev]; + else if (fin->fin_rev) is->is_age = fr_udpacktimeout; else is->is_age = fr_udptimeout; @@ -1338,47 +1517,74 @@ retry_tcpudp: break; } default : + tcp = NULL; + hv %= fr_statesize; + READ_ENTER(&ipf_state); + for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_hnext) { + if ((is->is_p == pr) && (is->is_v == v) && + fr_matchsrcdst(is, src, dst, fin, NULL)) { + rev = fin->fin_rev; + if (is->is_frage[rev] != 0) + is->is_age = is->is_frage[rev]; + else + is->is_age = fr_udptimeout; + break; + } + } + if (is == NULL) { + RWLOCK_EXIT(&ipf_state); + } break; } + if (is == NULL) { ATOMIC_INCL(ips_stats.iss_miss); return NULL; } + MUTEX_ENTER(&is->is_lock); is->is_bytes += fin->fin_plen; ips_stats.iss_hits++; is->is_pkts++; MUTEX_EXIT(&is->is_lock); fr = is->is_rule; + fin->fin_rule = is->is_rulen; + if (fr != NULL) { + fin->fin_group = fr->fr_group; + fin->fin_icode = fr->fr_icode; + } fin->fin_fr = fr; pass = is->is_pass; + RWLOCK_EXIT(&ipf_state); + if ((fin->fin_fl & FI_FRAG) && (pass & FR_KEEPFRAG)) + ipfr_newfrag(ip, fin, pass ^ FR_KEEPSTATE); #ifndef _KERNEL - if (tcp->th_flags & TCP_CLOSE) + if ((tcp != NULL) && (tcp->th_flags & TCP_CLOSE)) fr_delstate(is); #endif - RWLOCK_EXIT(&ipf_state); - if ((fin->fin_fi.fi_fl & FI_FRAG) && (pass & FR_KEEPFRAG)) - ipfr_newfrag(ip, fin, pass ^ FR_KEEPSTATE); return fr; } +/* + * Sync. state entries. If interfaces come or go or just change position, + * this is needed. + */ void ip_statesync(ifp) void *ifp; { register ipstate_t *is; + int i; WRITE_ENTER(&ipf_state); for (is = ips_list; is; is = is->is_next) { - if (is->is_ifpin == ifp) { - is->is_ifpin = GETUNIT(is->is_ifname[0], is->is_v); - if (!is->is_ifpin) - is->is_ifpin = (void *)-1; - } - if (is->is_ifpout == ifp) { - is->is_ifpout = GETUNIT(is->is_ifname[1], is->is_v); - if (!is->is_ifpout) - is->is_ifpout = (void *)-1; + for (i = 0; i < 4; i++) { + if (is->is_ifp[i] == ifp) { + is->is_ifpin = GETUNIT(is->is_ifname[i], + is->is_v); + if (!is->is_ifp[i]) + is->is_ifp[i] = (void *)-1; + } } } RWLOCK_EXIT(&ipf_state); @@ -1403,6 +1609,8 @@ ipstate_t *is; *is->is_phnext = is->is_hnext; if (ips_table[is->is_hv] == NULL) ips_stats.iss_inuse--; + if (is->is_me) + *is->is_me = NULL; fr = is->is_rule; if (fr != NULL) { @@ -1464,7 +1672,7 @@ void fr_timeoutstate() } else isp = &is->is_next; if (fr_state_doflush) { - (void) fr_state_flush(1); + (void) fr_state_flush(2); fr_state_doflush = 0; } RWLOCK_EXIT(&ipf_state); @@ -1548,8 +1756,11 @@ int dir; */ if ((flags & (TH_FIN|TH_SYN|TH_RST|TH_ACK)) == TH_ACK) { /* we saw an A, guess 'dir' is in ESTABLISHED mode */ - state[dir] = TCPS_ESTABLISHED; - *age = fr_tcpidletimeout; + if (state[1 - dir] == TCPS_CLOSED || + state[1 - dir] == TCPS_ESTABLISHED) { + state[dir] = TCPS_ESTABLISHED; + *age = fr_tcpidletimeout; + } } /* * TODO: besides regular ACK packets we can have other @@ -1738,9 +1949,11 @@ u_int type; ipsl.isl_state[0] = is->is_state[0]; ipsl.isl_state[1] = is->is_state[1]; } - } else if (ipsl.isl_p == IPPROTO_ICMP) + } else if (ipsl.isl_p == IPPROTO_ICMP) { + ipsl.isl_itype = is->is_icmp.ics_type; + } else if (ipsl.isl_p == IPPROTO_ICMPV6) { ipsl.isl_itype = is->is_icmp.ics_type; - else { + } else { ipsl.isl_ps.isl_filler[0] = 0; ipsl.isl_ps.isl_filler[1] = 0; } @@ -1893,10 +2106,6 @@ fr_info_t *fin; fr_matchsrcdst(is, src, dst, &ofin, tcp)) { fr = is->is_rule; ips_stats.iss_hits++; - /* - * we must swap src and dst here because the icmp - * comes the other way around - */ is->is_pkts++; is->is_bytes += fin->fin_plen; /* |