📄 ip_nat.c
字号:
(fin->fin_fi.fi_fl & FI_SHORT) || (ip->ip_off & IP_OFFMASK)) { if (ft->ftu_scmp || ft->ftu_dcmp) return 0; return 1; } return fr_tcpudpchk(ft, fin);}/* * Packets going out on the external interface go through this. * Here, the source address requires alteration, if anything. */int ip_natout(ip, fin)ip_t *ip;fr_info_t *fin;{ register ipnat_t *np = NULL; register u_32_t ipa; tcphdr_t *tcp = NULL; u_short sport = 0, dport = 0, *csump = NULL; struct ifnet *ifp; int natadd = 1; frentry_t *fr; u_int nflags = 0, hv, msk; u_32_t iph; nat_t *nat; int i; if (nat_list == NULL || (fr_nat_lock)) return 0; if ((fr = fin->fin_fr) && !(fr->fr_flags & FR_DUP) && fr->fr_tif.fd_ifp && fr->fr_tif.fd_ifp != (void *)-1) ifp = fr->fr_tif.fd_ifp; else ifp = fin->fin_ifp; if (!(ip->ip_off & IP_OFFMASK) && !(fin->fin_fi.fi_fl & FI_SHORT)) { if (ip->ip_p == IPPROTO_TCP) nflags = IPN_TCP; else if (ip->ip_p == IPPROTO_UDP) nflags = IPN_UDP; if ((nflags & IPN_TCPUDP)) { tcp = (tcphdr_t *)fin->fin_dp; sport = tcp->th_sport; dport = tcp->th_dport; } } ipa = ip->ip_src.s_addr; READ_ENTER(&ipf_nat); if ((ip->ip_p == IPPROTO_ICMP) && (nat = nat_icmp(ip, fin, &nflags, NAT_OUTBOUND))) ; else if ((ip->ip_off & (IP_OFFMASK|IP_MF)) && (nat = ipfr_nat_knownfrag(ip, fin))) natadd = 0; else if ((nat = nat_outlookup(ifp, nflags, (u_int)ip->ip_p, ip->ip_src, ip->ip_dst, (dport << 16) | sport, 0))) { nflags = nat->nat_flags; if ((nflags & (FI_W_SPORT|FI_W_DPORT)) != 0) { if ((nflags & FI_W_SPORT) && (nat->nat_inport != sport)) nat->nat_inport = sport; else if ((nflags & FI_W_DPORT) && (nat->nat_oport != dport)) nat->nat_oport = dport; if (nat->nat_outport == 0) nat->nat_outport = sport; nat->nat_flags &= ~(FI_W_DPORT|FI_W_SPORT); nflags = nat->nat_flags; nat_stats.ns_wilds--; } } else { RWLOCK_EXIT(&ipf_nat); WRITE_ENTER(&ipf_nat); /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ msk = 0xffffffff; i = 32;maskloop: iph = ipa & htonl(msk); hv = NAT_HASH_FN(iph, 0, ipf_natrules_sz); for (np = nat_rules[hv]; np; np = np->in_mnext) { if ((np->in_ifp && (np->in_ifp != ifp)) || !np->in_space) continue; if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) continue; if (np->in_flags & IPN_FILTER) { if (!nat_match(fin, np, ip)) continue; } else if ((ipa & np->in_inmsk) != np->in_inip) continue; if (np->in_redir & (NAT_MAP|NAT_MAPBLK)) { if (*np->in_plabel && !appr_ok(ip, tcp, np)) continue; /* * If it's a redirection, then we don't want to * create new outgoing port stuff. * Redirections are only for incoming * connections. */ if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) continue; if ((nat = nat_new(np, ip, fin, (u_int)nflags, NAT_OUTBOUND))) { np->in_hits++; break; } } } if ((np == NULL) && (i > 0)) { do { i--; msk <<= 1; } while ((i >= 0) && ((nat_masks & (1 << i)) == 0)); if (i >= 0) goto maskloop; } MUTEX_DOWNGRADE(&ipf_nat); } /* * NOTE: ipf_nat must now only be held as a read lock */ if (nat) { np = nat->nat_ptr; if (natadd && fin->fin_fi.fi_fl & FI_FRAG) ipfr_nat_newfrag(ip, fin, 0, nat); MUTEX_ENTER(&nat->nat_lock); nat->nat_age = fr_defnatage; nat->nat_bytes += ip->ip_len; nat->nat_pkts++; MUTEX_EXIT(&nat->nat_lock); /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. */ if (nflags == IPN_ICMPERR) { u_32_t s1, s2, sumd; s1 = LONG_SUM(ntohl(ip->ip_src.s_addr)); s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)); CALC_SUMD(s1, s2, sumd); if (nat->nat_dir == NAT_OUTBOUND) fix_incksum(&ip->ip_sum, sumd); else fix_outcksum(&ip->ip_sum, sumd); }#if SOLARIS || defined(__sgi) else { if (nat->nat_dir == NAT_OUTBOUND) fix_outcksum(&ip->ip_sum, nat->nat_ipsumd); else fix_incksum(&ip->ip_sum, nat->nat_ipsumd); }#endif ip->ip_src = nat->nat_outip; if (!(ip->ip_off & IP_OFFMASK) && !(fin->fin_fi.fi_fl & FI_SHORT)) { if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) { tcp->th_sport = nat->nat_outport; fin->fin_data[0] = ntohs(tcp->th_sport); } if (ip->ip_p == IPPROTO_TCP) { csump = &tcp->th_sum; MUTEX_ENTER(&nat->nat_lock); fr_tcp_age(&nat->nat_age, nat->nat_tcpstate, fin, 1); if (nat->nat_age < fr_defnaticmpage) nat->nat_age = fr_defnaticmpage;#ifdef LARGE_NAT else if (nat->nat_age > fr_defnatage) nat->nat_age = fr_defnatage;#endif /* * Increase this because we may have * "keep state" following this too and * packet storms can occur if this is * removed too quickly. */ if (nat->nat_age == fr_tcpclosed) nat->nat_age = fr_tcplastack; MUTEX_EXIT(&nat->nat_lock); } else if (ip->ip_p == IPPROTO_UDP) { udphdr_t *udp = (udphdr_t *)tcp; if (udp->uh_sum) csump = &udp->uh_sum; } else if (ip->ip_p == IPPROTO_ICMP) { nat->nat_age = fr_defnaticmpage; } if (csump) { if (nat->nat_dir == NAT_OUTBOUND) fix_outcksum(csump, nat->nat_sumd[1]); else fix_incksum(csump, nat->nat_sumd[1]); } } if ((np->in_apr != NULL) && (np->in_dport == 0 || (tcp != NULL && dport == np->in_dport))) { i = appr_check(ip, fin, nat); if (i == 0) i = 1; } else i = 1; ATOMIC_INCL(nat_stats.ns_mapped[1]); RWLOCK_EXIT(&ipf_nat); /* READ */ return i; } RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ return 0;}/* * Packets coming in from the external interface go through this. * Here, the destination address requires alteration, if anything. */int ip_natin(ip, fin)ip_t *ip;fr_info_t *fin;{ register struct in_addr src; register struct in_addr in; register ipnat_t *np; u_int nflags = 0, natadd = 1, hv, msk; struct ifnet *ifp = fin->fin_ifp; tcphdr_t *tcp = NULL; u_short sport = 0, dport = 0, *csump = NULL; nat_t *nat; u_32_t iph; int i; if ((nat_list == NULL) || (ip->ip_v != 4) || (fr_nat_lock)) return 0; if (!(ip->ip_off & IP_OFFMASK) && !(fin->fin_fi.fi_fl & FI_SHORT)) { if (ip->ip_p == IPPROTO_TCP) nflags = IPN_TCP; else if (ip->ip_p == IPPROTO_UDP) nflags = IPN_UDP; if ((nflags & IPN_TCPUDP)) { tcp = (tcphdr_t *)fin->fin_dp; dport = tcp->th_dport; sport = tcp->th_sport; } } in = ip->ip_dst; /* make sure the source address is to be redirected */ src = ip->ip_src; READ_ENTER(&ipf_nat); if ((ip->ip_p == IPPROTO_ICMP) && (nat = nat_icmp(ip, fin, &nflags, NAT_INBOUND))) ; else if ((ip->ip_off & (IP_OFFMASK|IP_MF)) && (nat = ipfr_nat_knownfrag(ip, fin))) natadd = 0; else if ((nat = nat_inlookup(fin->fin_ifp, nflags, (u_int)ip->ip_p, ip->ip_src, in, (dport << 16) | sport, 0))) { nflags = nat->nat_flags; if ((nflags & (FI_W_SPORT|FI_W_DPORT)) != 0) { if ((nat->nat_oport != sport) && (nflags & FI_W_DPORT)) nat->nat_oport = sport; else if ((nat->nat_outport != dport) && (nflags & FI_W_SPORT)) nat->nat_outport = dport; nat->nat_flags &= ~(FI_W_SPORT|FI_W_DPORT); nflags = nat->nat_flags; nat_stats.ns_wilds--; } } else { RWLOCK_EXIT(&ipf_nat); WRITE_ENTER(&ipf_nat); /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ msk = 0xffffffff; i = 32;maskloop: iph = in.s_addr & htonl(msk); hv = NAT_HASH_FN(iph, 0, ipf_rdrrules_sz); for (np = rdr_rules[hv]; np; np = np->in_rnext) { if ((np->in_ifp && (np->in_ifp != ifp)) || (np->in_p && (np->in_p != ip->ip_p)) || (np->in_flags && !(nflags & np->in_flags))) continue; if (np->in_flags & IPN_FILTER) { if (!nat_match(fin, np, ip)) continue; } else if ((in.s_addr & np->in_outmsk) != np->in_outip) continue; if ((np->in_redir & NAT_REDIRECT) && (!np->in_pmin || (np->in_flags & IPN_FILTER) || ((ntohs(np->in_pmax) >= ntohs(dport)) && (ntohs(dport) >= ntohs(np->in_pmin))))) if ((nat = nat_new(np, ip, fin, nflags, NAT_INBOUND))) { np->in_hits++; break; } } if ((np == NULL) && (i > 0)) { do { i--; msk <<= 1; } while ((i >= 0) && ((rdr_masks & (1 << i)) == 0)); if (i >= 0) goto maskloop; } MUTEX_DOWNGRADE(&ipf_nat); } /* * NOTE: ipf_nat must now only be held as a read lock */ if (nat) { np = nat->nat_ptr; fin->fin_fr = nat->nat_fr; if (natadd && fin->fin_fi.fi_fl & FI_FRAG) ipfr_nat_newfrag(ip, fin, 0, nat); if ((np->in_apr != NULL) && (np->in_dport == 0 || (tcp != NULL && sport == np->in_dport))) { i = appr_check(ip, fin, nat); if (i == -1) { RWLOCK_EXIT(&ipf_nat); return i; } } MUTEX_ENTER(&nat->nat_lock); if (nflags != IPN_ICMPERR) nat->nat_age = fr_defnatage; nat->nat_bytes += ip->ip_len; nat->nat_pkts++; MUTEX_EXIT(&nat->nat_lock); ip->ip_dst = nat->nat_inip; fin->fin_fi.fi_daddr = nat->nat_inip.s_addr; /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. */#if SOLARIS || defined(__sgi) if (nat->nat_dir == NAT_OUTBOUND) fix_incksum(&ip->ip_sum, nat->nat_ipsumd); else fix_outcksum(&ip->ip_sum, nat->nat_ipsumd);#endif if (!(ip->ip_off & IP_OFFMASK) && !(fin->fin_fi.fi_fl & FI_SHORT)) { if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) { tcp->th_dport = nat->nat_inport; fin->fin_data[1] = ntohs(tcp->th_dport); } if (ip->ip_p == IPPROTO_TCP) { csump = &tcp->th_sum; MUTEX_ENTER(&nat->nat_lock); fr_tcp_age(&nat->nat_age, nat->nat_tcpstate, fin, 0); if (nat->nat_age < fr_defnaticmpage) nat->nat_age = fr_defnaticmpage;#ifdef LARGE_NAT else if (nat->nat_age > fr_defnatage) nat->nat_age = fr_defnatage;#endif /* * Increase this because we may have * "keep state" following this too and * packet storms can occur if this is * removed too quickly. */ if (nat->nat_age == fr_tcpclosed) nat->nat_age = fr_tcplastack; MUTEX_EXIT(&nat->nat_lock); } else if (ip->ip_p == IPPROTO_UDP) { udphdr_t *udp = (udphdr_t *)tcp; if (udp->uh_sum) csump = &udp->uh_sum; } else if (ip->ip_p == IPPROTO_ICMP) { nat->nat_age = fr_defnaticmpage; } if (csump) { if (nat->nat_dir == NAT_OUTBOUND) fix_incksum(csump, nat->nat_sumd[0]); else fix_outcksum(csump, nat->nat_sumd[0]); } } ATOMIC_INCL(nat_stats.ns_mapped[0]); RWLOCK_EXIT(&ipf_nat); /* READ */ return 1; } RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ return 0;}/* * Free all memory used by NAT structures allocated at runtime. */void ip_natunload(){ WRITE_ENTER(&ipf_nat); (void) nat_clearlist(); (void) nat_flushtable(); RWLOCK_EXIT(&ipf_nat); if (nat_table[0] != NULL) { KFREES(nat_table[0], sizeof(nat_t *) * ipf_nattable_sz); nat_table[0] = NULL; } if (nat_table[1] != NULL) { KFREES(nat_table[1], sizeof(nat_t *) * ipf_nattable_sz); nat_table[1] = NULL; } if (nat_rules != NULL) { KFREES(nat_rules, sizeof(ipnat_t *) * ipf_natrules_sz); nat_rules = NULL; } if (rdr_rules != NULL) { KFREES(rdr_rules, sizeof(ipnat_t *) * ipf_rdrrules_sz); rdr_rules = NULL; } if (maptable != NULL) { KFREES(maptable, sizeof(hostmap_t *) * ipf_hostmap_sz); maptable = NULL; }}/* * Slowly expire held state for NAT entries. Timeouts are set in * expectation of this being called twice per second. */void ip_natexpire(){ register struct nat *nat, **natp;#if defined(_KERNEL) && !SOLARIS int s;#endif SPL_NET(s); WRITE_ENTER(&ipf_nat); for (natp = &nat_instances; (nat = *natp); ) { nat->nat_age--; if (nat->nat_age) { natp = &nat->nat_next; continue; } *natp = nat->nat_next;#ifdef IPFILTER_LOG nat_log(nat, NL_EXPIRE);#endif nat_delete(nat); nat_stats.ns_expire++; } RWLOCK_EXIT(&ipf_nat); SPL_X(s);}/* */void ip_natsync(ifp)void *ifp;{ register ipnat_t *n; register nat_t *nat; register u_32_t sum1, sum2, sumd; struct in_addr in; ipnat_t *np; void *ifp2;#if defined(_KERNEL) && !SOLARIS int s;#endif /* * Change IP addresses for NAT sessions for any protocol except TCP * since it will break the TCP connection anyway. */ SPL_NET(s); WRITE_ENTER(&ipf_nat); for (nat = nat_instances; nat; nat = nat->nat_next) if (((ifp == NULL) || (ifp == nat->nat_ifp)) && !(nat->nat_flags & IPN_TCP) && (np = nat->nat_ptr) && (np->in_outmsk == 0xffffffff) && !np->in_nip) { ifp2 = nat->nat_ifp; /* * Change the map-to address to be the same as the * new one. */ sum1 = nat->nat_outip.s_addr; if (fr_ifpaddr(4, ifp2, &in) != -1) nat->nat_outip = in; sum2 = nat->nat_outip.s_addr; 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 & 0
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -