📄 ip_nat.c
字号:
j++; } nat_stats.ns_inuse = 0; return j;}/* * nat_clearlist - delete all rules in the active NAT mapping list. */static int nat_clearlist(){ register ipnat_t *n, **np = &nat_list; int i = 0; if (nat_rules != NULL) bzero((char *)nat_rules, sizeof(*nat_rules) * ipf_natrules_sz); if (rdr_rules != NULL) bzero((char *)rdr_rules, sizeof(*rdr_rules) * ipf_rdrrules_sz); while ((n = *np)) { *np = n->in_next; if (!n->in_use) { if (n->in_apr) appr_free(n->in_apr); KFREE(n); nat_stats.ns_rules--; } else { n->in_flags |= IPN_DELETE; n->in_next = NULL; } i++; } nat_masks = 0; rdr_masks = 0; return i;}/* * Create a new NAT table entry. * NOTE: assumes write lock on ipf_nat has been obtained already. */nat_t *nat_new(np, ip, fin, flags, direction)ipnat_t *np;ip_t *ip;fr_info_t *fin;u_int flags;int direction;{ register u_32_t sum1, sum2, sumd, l; u_short port = 0, sport = 0, dport = 0, nport = 0; struct in_addr in, inb; tcphdr_t *tcp = NULL; hostmap_t *hm = NULL; nat_t *nat, *natl; u_short nflags;#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) qif_t *qf = fin->fin_qif;#endif nflags = flags & np->in_flags; if (flags & IPN_TCPUDP) { tcp = (tcphdr_t *)fin->fin_dp; sport = tcp->th_sport; dport = tcp->th_dport; } /* Give me a new nat */ KMALLOC(nat, nat_t *); if (nat == NULL) { nat_stats.ns_memfail++; return NULL; } bzero((char *)nat, sizeof(*nat)); nat->nat_flags = flags; if (flags & FI_WILDP) nat_stats.ns_wilds++; /* * Search the current table for a match. */ if (direction == NAT_OUTBOUND) { /* * Values at which the search for a free resouce starts. */ u_32_t st_ip; u_short st_port; /* * If it's an outbound packet which doesn't match any existing * record, then create a new port */ l = 0; st_ip = np->in_nip; st_port = np->in_pnext; do { port = 0; in.s_addr = htonl(np->in_nip); if (l == 0) { /* * Check to see if there is an existing NAT * setup for this IP address pair. */ hm = nat_hostmap(np, ip->ip_src, in); if (hm != NULL) in.s_addr = hm->hm_mapip.s_addr; } else if ((l == 1) && (hm != NULL)) { nat_hostmapdel(hm); hm = NULL; } in.s_addr = ntohl(in.s_addr); nat->nat_hm = hm; if ((np->in_outmsk == 0xffffffff) && (np->in_pnext == 0)) { if (l > 0) goto badnat; } if (np->in_redir & NAT_MAPBLK) { if ((l >= np->in_ppip) || ((l > 0) && !(flags & IPN_TCPUDP))) goto badnat; /* * map-block - Calculate destination address. */ in.s_addr = ntohl(ip->ip_src.s_addr); in.s_addr &= ntohl(~np->in_inmsk); inb.s_addr = in.s_addr; in.s_addr /= np->in_ippip; in.s_addr &= ntohl(~np->in_outmsk); in.s_addr += ntohl(np->in_outip); /* * 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_outip && (np->in_outmsk == 0xffffffff)) { /* * 0/32 - use the interface's IP address. */ if ((l > 0) || fr_ifpaddr(4, fin->fin_ifp, &in) == -1) goto badnat; in.s_addr = ntohl(in.s_addr); } else if (!np->in_outip && !np->in_outmsk) { /* * 0/0 - use the original source address/port. */ if (l > 0) goto badnat; in.s_addr = ntohl(ip->ip_src.s_addr); } else if ((np->in_outmsk != 0xffffffff) && (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) np->in_nip++; natl = NULL; if ((nflags & IPN_TCPUDP) && ((np->in_redir & NAT_MAPBLK) == 0) && (np->in_flags & IPN_AUTOPORTMAP)) { if ((l > 0) && (l % np->in_ppip == 0)) { if (l > np->in_space) { goto badnat; } else if ((l > np->in_ppip) && np->in_outmsk != 0xffffffff) np->in_nip++; } if (np->in_ppip != 0) { port = ntohs(sport); port += (l % np->in_ppip); port %= np->in_ppip; port += np->in_ppip * (ntohl(ip->ip_src.s_addr) % np->in_ippip); port += MAPBLK_MINPORT; port = htons(port); } } else if (((np->in_redir & NAT_MAPBLK) == 0) && (nflags & IPN_TCPUDP) && (np->in_pnext != 0)) { port = htons(np->in_pnext++); if (np->in_pnext > ntohs(np->in_pmax)) { np->in_pnext = ntohs(np->in_pmin); if (np->in_outmsk != 0xffffffff) np->in_nip++; } } if (np->in_flags & IPN_IPRANGE) { if (np->in_nip > ntohl(np->in_outmsk)) np->in_nip = ntohl(np->in_outip); } else { if ((np->in_outmsk != 0xffffffff) && ((np->in_nip + 1) & ntohl(np->in_outmsk)) > ntohl(np->in_outip)) np->in_nip = ntohl(np->in_outip) + 1; } if (!port && (flags & IPN_TCPUDP)) 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); natl = nat_inlookup(fin->fin_ifp, flags & ~FI_WILDP, (u_int)ip->ip_p, ip->ip_dst, inb, (port << 16) | dport, 1); /* * Has the search wrapped around and come back to the * start ? */ if ((natl != NULL) && (np->in_pnext != 0) && (st_port == np->in_pnext) && (np->in_nip != 0) && (st_ip == np->in_nip)) goto badnat; l++; } while (natl != NULL); if (np->in_space > 0) np->in_space--; /* Setup the NAT table */ nat->nat_inip = ip->ip_src; nat->nat_outip.s_addr = htonl(in.s_addr); nat->nat_oip = ip->ip_dst; if (nat->nat_hm == NULL) nat->nat_hm = nat_hostmap(np, ip->ip_src, nat->nat_outip); sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr)) + ntohs(sport); sum2 = LONG_SUM(in.s_addr) + ntohs(port); if (flags & IPN_TCPUDP) { nat->nat_inport = sport; nat->nat_outport = port; /* sport */ nat->nat_oport = dport; } } else { /* * 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_nip; if (np->in_inip == htonl(in.s_addr)) np->in_nip = ntohl(np->in_inmsk); else { np->in_nip = ntohl(np->in_inip); if (np->in_flags & IPN_ROUNDR) { nat_delrdr(np); nat_addrdr(np); } } } else { in.s_addr = ntohl(np->in_inip); if (np->in_flags & IPN_ROUNDR) { nat_delrdr(np); nat_addrdr(np); } } if (!np->in_pnext) nport = dport; else { /* * Whilst not optimized for the case where * pmin == pmax, the gain is not significant. */ nport = ntohs(dport) - ntohs(np->in_pmin) + ntohs(np->in_pnext); nport = htons(nport); } /* * 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) goto badnat; in.s_addr = ntohl(ip->ip_dst.s_addr); } nat->nat_inip.s_addr = htonl(in.s_addr); nat->nat_outip = ip->ip_dst; nat->nat_oip = ip->ip_src; sum1 = LONG_SUM(ntohl(ip->ip_dst.s_addr)) + ntohs(dport); sum2 = LONG_SUM(in.s_addr) + ntohs(nport); if (flags & IPN_TCPUDP) { nat->nat_inport = nport; nat->nat_outport = dport; nat->nat_oport = sport; } } CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) if ((flags == IPN_TCP) && dohwcksum && (qf->qf_ill->ill_ick.ick_magic == ICK_M_CTL_MAGIC)) { if (direction == NAT_OUTBOUND) sum1 = LONG_SUM(ntohl(in.s_addr)); else sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr)); sum1 += LONG_SUM(ntohl(ip->ip_dst.s_addr)); sum1 += 30; sum1 = (sum1 & 0xffff) + (sum1 >> 16); nat->nat_sumd[1] = NAT_HW_CKSUM|(sum1 & 0xffff); } else#endif nat->nat_sumd[1] = nat->nat_sumd[0]; if ((flags & IPN_TCPUDP) && ((sport != port) || (dport != nport))) { if (direction == NAT_OUTBOUND) sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr)); else sum1 = LONG_SUM(ntohl(ip->ip_dst.s_addr)); sum2 = LONG_SUM(in.s_addr); CALC_SUMD(sum1, sum2, sumd); nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); } else nat->nat_ipsumd = nat->nat_sumd[0]; in.s_addr = htonl(in.s_addr);#ifdef _KERNEL strncpy(nat->nat_ifname, IFNAME(fin->fin_ifp), IFNAMSIZ);#endif nat_insert(nat); nat->nat_dir = direction; nat->nat_ifp = fin->fin_ifp; nat->nat_ptr = np; nat->nat_p = ip->ip_p; nat->nat_bytes = 0; nat->nat_pkts = 0; nat->nat_fr = fin->fin_fr; if (nat->nat_fr != NULL) { ATOMIC_INC32(nat->nat_fr->fr_ref); } if (direction == NAT_OUTBOUND) { if (flags & IPN_TCPUDP) tcp->th_sport = port; } else { if (flags & IPN_TCPUDP) tcp->th_dport = nport; } np->in_use++;#ifdef IPFILTER_LOG nat_log(nat, (u_int)np->in_redir);#endif return nat;badnat: nat_stats.ns_badnat++; if ((hm = nat->nat_hm) != NULL) nat_hostmapdel(hm); KFREE(nat); return NULL;}void nat_insert(nat)nat_t *nat;{ nat_t **natp; u_int hv; MUTEX_INIT(&nat->nat_lock, "nat entry lock", NULL); nat->nat_age = fr_defnatage; nat->nat_ifname[sizeof(nat->nat_ifname) - 1] = '\0'; if (nat->nat_ifname[0] !='\0') { nat->nat_ifp = GETUNIT(nat->nat_ifname, 4); } nat->nat_next = nat_instances; nat_instances = nat; hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 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, nat->nat_outport, 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; nat_stats.ns_added++; nat_stats.ns_inuse++;}nat_t *nat_icmplookup(ip, fin, dir)ip_t *ip;fr_info_t *fin;int dir;{ icmphdr_t *icmp; tcphdr_t *tcp = NULL; ip_t *oip; int flags = 0, type, minlen; icmp = (icmphdr_t *)fin->fin_dp; /* * Does it at least have the return (basic) IP header ? * Only a basic IP header (no options) should be with an ICMP error * header. */ if ((ip->ip_hl != 5) || (ip->ip_len < ICMPERR_MINPKTLEN)) return NULL; type = icmp->icmp_type; /* * If it's not an error type, then return. */ if ((type != ICMP_UNREACH) && (type != ICMP_SOURCEQUENCH) && (type != ICMP_REDIRECT) && (type != ICMP_TIMXCEED) && (type != ICMP_PARAMPROB)) return NULL; oip = (ip_t *)((char *)fin->fin_dp + 8); minlen = (oip->ip_hl << 2); if (minlen < sizeof(ip_t)) return NULL; if (ip->ip_len < ICMPERR_IPICMPHLEN + minlen) 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 fr_check() and thus can't gaurantee it is * all here now. */#ifdef _KERNEL { mb_t *m;# if SOLARIS m = fin->fin_qfm; if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) return NULL;# else m = *(mb_t **)fin->fin_mp; if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)ip + m->m_len) return NULL;# endif }#endif if (oip->ip_p == IPPROTO_TCP) flags = IPN_TCP; else if (oip->ip_p == IPPROTO_UDP) flags = IPN_UDP; if (flags & IPN_TCPUDP) { minlen += 8; /* + 64bits of data to get ports */ if (ip->ip_len < ICMPERR_IPICMPHLEN + minlen) return NULL; tcp = (tcphdr_t *)((char *)oip + (oip->ip_hl << 2)); if (dir == NAT_INBOUND) return nat_inlookup(fin->fin_ifp, flags, (u_int)oip->ip_p, oip->ip_dst, oip->ip_src, (tcp->th_sport << 16) | tcp->th_dport, 0); else return nat_outlookup(fin->fin_ifp, flags, (u_int)oip->ip_p, oip->ip_dst, oip->ip_src, (tcp->th_sport << 16) | tcp->th_dport, 0); } if (dir == NAT_INBOUND) return nat_inlookup(fin->fin_ifp, 0, (u_int)oip->ip_p, oip->ip_dst, oip->ip_src, 0, 0); else return nat_outlookup(fin->fin_ifp, 0, (u_int)oip->ip_p, oip->ip_dst, oip->ip_src, 0, 0);}/* * This should *ONLY* be used for incoming packets to make sure a NAT'd ICMP * packet gets correctly recognised. */nat_t *nat_icmp(ip, fin, nflags, dir)ip_t *ip;fr_info_t *fin;u_int *nflags;int dir;{ u_32_t sum1, sum2, sumd, sumd2 = 0; struct in_addr in; icmphdr_t *icmp; udphdr_t *udp; nat_t *nat; ip_t *oip; int flags = 0; if ((fin->fin_fi.fi_fl & FI_SHORT) || (ip->ip_off & IP_OFFMASK)) return NULL; /* * nat_icmplookup() will return NULL for `defective' packets. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -