📄 ip_nat.c
字号:
if ((ip->ip_v != 4) || !(nat = nat_icmplookup(ip, fin, dir))) return NULL; *nflags = IPN_ICMPERR; icmp = (icmphdr_t *)fin->fin_dp; oip = (ip_t *)&icmp->icmp_ip; if (oip->ip_p == IPPROTO_TCP) flags = IPN_TCP; else if (oip->ip_p == IPPROTO_UDP) flags = IPN_UDP; udp = (udphdr_t *)((((char *)oip) + (oip->ip_hl << 2))); /* * 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 ip_natout * 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 * and the ICMP checksum of the ICMP error message itself. * * Unfortunately, for UDP and TCP, the IP addresses are also contained * in the pseudo header that is used to compute the UDP resp. TCP * checksum. So, we must compensate that as well. Even worse, the * change in the UDP and TCP checksums require yet another * adjustment of the ICMP checksum of the ICMP error message. * * For the moment we forget about TCP, because that checksum is not * in the first 8 bytes, so it will not be available in most cases. */ if (oip->ip_dst.s_addr == nat->nat_oip.s_addr) { sum1 = LONG_SUM(ntohl(oip->ip_src.s_addr)); in = nat->nat_inip; oip->ip_src = in; } else { sum1 = LONG_SUM(ntohl(oip->ip_dst.s_addr)); in = nat->nat_outip; oip->ip_dst = in; } sum2 = LONG_SUM(ntohl(in.s_addr)); CALC_SUMD(sum1, sum2, sumd); if (nat->nat_dir == NAT_OUTBOUND) { /* * Fix IP checksum of the offending IP packet to adjust for * the change in the IP address. * * 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. * * Be careful that nat_dir refers to the direction of the * offending IP packet (oip), not to its ICMP response (icmp) */ fix_datacksum(&oip->ip_sum, sumd); /* * Fix UDP pseudo header checksum to compensate for the * IP address change. */ if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { /* * The UDP checksum is optional, only adjust it * if it has been set. */ sum1 = ntohs(udp->uh_sum); fix_datacksum(&udp->uh_sum, sumd); sum2 = ntohs(udp->uh_sum); /* * Fix ICMP checksum to compensate the UDP * checksum adjustment. */ CALC_SUMD(sum1, sum2, sumd); sumd2 = sumd; }#if 0 /* * Fix TCP pseudo header checksum to compensate for the * IP address change. Before we can do the change, we * must make sure that oip is sufficient large to hold * the TCP checksum (normally it does not!). */ if (oip->ip_p == IPPROTO_TCP) { }#endif } else { /* * Fix IP checksum of the offending IP packet to adjust for * the change in the IP address. * * 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. * * Be careful that nat_dir refers to the direction of the * offending IP packet (oip), not to its ICMP response (icmp) */ fix_datacksum(&oip->ip_sum, sumd);/* XXX FV : without having looked at Solaris source code, it seems unlikely * that SOLARIS would compensate this in the kernel (a body of an IP packet * in the data section of an ICMP packet). I have the feeling that this should * be unconditional, but I'm not in a position to check. */#if !SOLARIS && !defined(__sgi) /* * Fix UDP pseudo header checksum to compensate for the * IP address change. */ if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { /* * The UDP checksum is optional, only adjust it * if it has been set */ sum1 = ntohs(udp->uh_sum); fix_datacksum(&udp->uh_sum, sumd); sum2 = ntohs(udp->uh_sum); /* * Fix ICMP checksum to compensate the UDP * checksum adjustment. */ CALC_SUMD(sum1, sum2, sumd); sumd2 = sumd; } #if 0 /* * Fix TCP pseudo header checksum to compensate for the * IP address change. Before we can do the change, we * must make sure that oip is sufficient large to hold * the TCP checksum (normally it does not!). */ if (oip->ip_p == IPPROTO_TCP) { };#endif #endif } if ((flags & IPN_TCPUDP) != 0) { tcphdr_t *tcp; /* * XXX - what if this is bogus hl and we go off the end ? * In this case, nat_icmpinlookup() will have returned NULL. */ tcp = (tcphdr_t *)udp; /* * Step 2 : * For offending TCP/UDP IP packets, translate the ports as * well, based on the NAT specification. Of course such * a change must be reflected in the ICMP checksum as well. * * Advance notice : Now it becomes complicated :-) * * Since the port fields are part of the TCP/UDP checksum * of the offending IP packet, you need to adjust that checksum * as well... but, if you change, you must change the icmp * checksum *again*, to reflect that change. * * To further complicate: the TCP checksum is not in the first * 8 bytes of the offending ip packet, so it most likely is not * available (we might have to fix that if the encounter a * device that returns more than 8 data bytes on icmp error) */ if (nat->nat_oport == tcp->th_dport) { if (tcp->th_sport != nat->nat_inport) { /* * Fix ICMP checksum to compensate port * adjustment. */ sum1 = ntohs(tcp->th_sport); sum2 = ntohs(nat->nat_inport); CALC_SUMD(sum1, sum2, sumd); sumd2 += sumd; tcp->th_sport = nat->nat_inport; /* * Fix udp checksum to compensate port * adjustment. NOTE : the offending IP packet * flows the other direction compared to the * ICMP message. * * The UDP checksum is optional, only adjust * it if it has been set. */ if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { sum1 = ntohs(udp->uh_sum); fix_datacksum(&udp->uh_sum, sumd); sum2 = ntohs(udp->uh_sum); /* * Fix ICMP checksum to * compensate UDP checksum * adjustment. */ CALC_SUMD(sum1, sum2, sumd); sumd2 += sumd; } } } else { if (tcp->th_dport != nat->nat_outport) { /* * Fix ICMP checksum to compensate port * adjustment. */ sum1 = ntohs(tcp->th_dport); sum2 = ntohs(nat->nat_outport); CALC_SUMD(sum1, sum2, sumd); sumd2 += sumd; tcp->th_dport = nat->nat_outport; /* * Fix udp checksum to compensate port * adjustment. NOTE : the offending IP * packet flows the other direction compared * to the ICMP message. * * The UDP checksum is optional, only adjust * it if it has been set. */ if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { sum1 = ntohs(udp->uh_sum); fix_datacksum(&udp->uh_sum, sumd); sum2 = ntohs(udp->uh_sum); /* * Fix ICMP checksum to compensate * UDP checksum adjustment. */ CALC_SUMD(sum1, sum2, sumd); sumd2 += sumd; } } } if (sumd2) { sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); if (nat->nat_dir == NAT_OUTBOUND) { fix_outcksum(&icmp->icmp_cksum, sumd2); } else { fix_incksum(&icmp->icmp_cksum, sumd2); } } } nat->nat_age = fr_defnaticmpage; return nat;}/* * NB: these lookups don't lock access to the list, it assume it has already * been done! *//* * 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. */nat_t *nat_inlookup(ifp, flags, p, src, mapdst, ports, rw)void *ifp;register u_int flags, p;struct in_addr src , mapdst;u_32_t ports;int rw;{ register u_short sport, dport; register nat_t *nat; register int nflags; register u_32_t dst; u_int hv; dst = mapdst.s_addr; dport = ports >> 16; sport = ports & 0xffff; flags &= IPN_TCPUDP; hv = NAT_HASH_FN(dst, dport, ipf_nattable_sz); nat = nat_table[1][hv]; for (; nat; nat = nat->nat_hnext[1]) { nflags = nat->nat_flags; if ((!ifp || ifp == nat->nat_ifp) && nat->nat_oip.s_addr == src.s_addr && nat->nat_outip.s_addr == dst && (((p == 0) && (flags == (nat->nat_flags & IPN_TCPUDP))) || (p == nat->nat_p)) && (!flags || (((nat->nat_oport == sport) || (nflags & FI_W_DPORT)) && ((nat->nat_outport == dport) || (nflags & FI_W_SPORT))))) return nat; } if (!nat_stats.ns_wilds || !(flags & IPN_TCPUDP)) return NULL; if (!rw) { RWLOCK_EXIT(&ipf_nat); } hv = NAT_HASH_FN(dst, 0, ipf_nattable_sz); if (!rw) { WRITE_ENTER(&ipf_nat); } nat = nat_table[1][hv]; for (; nat; nat = nat->nat_hnext[1]) { nflags = nat->nat_flags; if (ifp && ifp != nat->nat_ifp) continue; if (!(nflags & IPN_TCPUDP)) continue; if (!(nflags & FI_WILDP)) continue; if (nat->nat_oip.s_addr != src.s_addr || nat->nat_outip.s_addr != dst) continue; if (((nat->nat_oport == sport) || (nflags & FI_W_DPORT)) && ((nat->nat_outport == dport) || (nflags & FI_W_SPORT))) { nat_tabmove(nat, ports); break; } } if (!rw) { MUTEX_DOWNGRADE(&ipf_nat); } return 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 nat_tabmove(nat, ports)nat_t *nat;u_32_t ports;{ register u_short sport, dport; nat_t **natp; u_int hv; dport = ports >> 16; sport = ports & 0xffff; if (nat->nat_oport == dport) { nat->nat_inport = sport; nat->nat_outport = sport; } /* * 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]; if (nat->nat_hnext[1]) nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; *nat->nat_phnext[1] = nat->nat_hnext[1]; /* * Add into the NAT table in the new position */ hv = NAT_HASH_FN(nat->nat_inip.s_addr, sport, ipf_nattable_sz); natp = &nat_table[0][hv]; if (*natp) (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; nat->nat_phnext[0] = natp; nat->nat_hnext[0] = *natp; *natp = nat; hv = NAT_HASH_FN(nat->nat_outip.s_addr, sport, ipf_nattable_sz); natp = &nat_table[1][hv]; if (*natp) (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; nat->nat_phnext[1] = natp; nat->nat_hnext[1] = *natp; *natp = nat;}/* * 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. */nat_t *nat_outlookup(ifp, flags, p, src, dst, ports, rw)void *ifp;register u_int flags, p;struct in_addr src , dst;u_32_t ports;int rw;{ register u_short sport, dport; register nat_t *nat; register int nflags; u_32_t srcip; u_int hv; sport = ports & 0xffff; dport = ports >> 16; flags &= IPN_TCPUDP; srcip = src.s_addr; hv = NAT_HASH_FN(srcip, sport, ipf_nattable_sz); nat = nat_table[0][hv]; for (; nat; nat = nat->nat_hnext[0]) { nflags = nat->nat_flags; if ((!ifp || ifp == nat->nat_ifp) && nat->nat_inip.s_addr == srcip && nat->nat_oip.s_addr == dst.s_addr && (((p == 0) && (flags == (nflags & IPN_TCPUDP))) || (p == nat->nat_p)) && (!flags || ((nat->nat_inport == sport || nflags & FI_W_SPORT) && (nat->nat_oport == dport || nflags & FI_W_DPORT)))) return nat; } if (!nat_stats.ns_wilds || !(flags & IPN_TCPUDP)) return NULL; if (!rw) { RWLOCK_EXIT(&ipf_nat); } hv = NAT_HASH_FN(srcip, 0, ipf_nattable_sz); if (!rw) { WRITE_ENTER(&ipf_nat); } nat = nat_table[0][hv]; for (; nat; nat = nat->nat_hnext[0]) { nflags = nat->nat_flags; if (ifp && ifp != nat->nat_ifp) continue; if (!(nflags & IPN_TCPUDP)) continue; if (!(nflags & FI_WILDP)) continue; if ((nat->nat_inip.s_addr != srcip) || (nat->nat_oip.s_addr != dst.s_addr)) continue; if (((nat->nat_inport == sport) || (nflags & FI_W_SPORT)) && ((nat->nat_oport == dport) || (nflags & FI_W_DPORT))) { nat_tabmove(nat, ports); break; } } if (!rw) { MUTEX_DOWNGRADE(&ipf_nat); } return nat;}/* * Lookup the NAT tables to search for a matching redirect */nat_t *nat_lookupredir(np)register natlookup_t *np;{ u_32_t ports; nat_t *nat; ports = (np->nl_outport << 16) | np->nl_inport; /* * If nl_inip is non null, this is a lookup based on the real * ip address. Else, we use the fake. */ if ((nat = nat_outlookup(NULL, np->nl_flags, 0, np->nl_inip, np->nl_outip, ports, 0))) { np->nl_realip = nat->nat_outip; np->nl_realport = nat->nat_outport; } return nat;}static int nat_match(fin, np, ip)fr_info_t *fin;ipnat_t *np;ip_t *ip;{ frtuc_t *ft; if (ip->ip_v != 4) return 0; if (np->in_p && ip->ip_p != np->in_p) return 0; if (fin->fin_out) { if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) return 0; if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip) ^ ((np->in_flags & IPN_NOTSRC) != 0)) return 0; if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip) ^ ((np->in_flags & IPN_NOTDST) != 0)) return 0; } else { if (!(np->in_redir & NAT_REDIRECT)) return 0; if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip) ^ ((np->in_flags & IPN_NOTSRC) != 0)) return 0; if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip) ^ ((np->in_flags & IPN_NOTDST) != 0)) return 0; } ft = &np->in_tuc; if (!(fin->fin_fi.fi_fl & FI_TCPUDP) ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -