diff options
Diffstat (limited to 'sys/contrib/ipfilter/netinet/ip_nat.c')
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_nat.c | 321 |
1 files changed, 205 insertions, 116 deletions
diff --git a/sys/contrib/ipfilter/netinet/ip_nat.c b/sys/contrib/ipfilter/netinet/ip_nat.c index 146b490..3c9476f 100644 --- a/sys/contrib/ipfilter/netinet/ip_nat.c +++ b/sys/contrib/ipfilter/netinet/ip_nat.c @@ -9,10 +9,10 @@ */ #if !defined(lint) && defined(LIBC_SCCS) static char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; -static char rcsid[] = "$Id: ip_nat.c,v 1.1.1.3 1997/04/03 10:11:11 darrenr Exp $"; +static char rcsid[] = "$Id: ip_nat.c,v 2.0.2.18 1997/05/24 07:34:44 darrenr Exp $"; #endif -#if defined(__FreeBSD__) && defined(KERNEL) +#if defined(__FreeBSD__) && defined(KERNEL) && !defined(_KERNEL) #define _KERNEL #endif @@ -26,7 +26,13 @@ static char rcsid[] = "$Id: ip_nat.c,v 1.1.1.3 1997/04/03 10:11:11 darrenr Exp $ #include <sys/param.h> #include <sys/time.h> #include <sys/file.h> -#include <sys/ioctl.h> +#if defined(KERNEL) && (__FreeBSD_version >= 220000) +# include <sys/filio.h> +# include <sys/fnctl.h> +#else +# include <sys/ioctl.h> +#endif +#include <sys/fcntl.h> #include <sys/uio.h> #include <sys/protosw.h> #include <sys/socket.h> @@ -36,13 +42,19 @@ static char rcsid[] = "$Id: ip_nat.c,v 1.1.1.3 1997/04/03 10:11:11 darrenr Exp $ #if !defined(__SVR4) && !defined(__svr4__) # include <sys/mbuf.h> #else +# include <sys/filio.h> # include <sys/byteorder.h> # include <sys/dditypes.h> # include <sys/stream.h> # include <sys/kmem.h> #endif - +#if __FreeBSD_version >= 300000 +# include <sys/queue.h> +#endif #include <net/if.h> +#if __FreeBSD_version >= 300000 +# include <net/if_var.h> +#endif #ifdef sun #include <net/af.h> #endif @@ -62,36 +74,30 @@ extern struct ifnet vpnif; #include <netinet/udp.h> #include <netinet/tcpip.h> #include <netinet/ip_icmp.h> -#include "ip_compat.h" -#include "ip_fil.h" -#include "ip_nat.h" -#include "ip_state.h" +#include "netinet/ip_compat.h" +#include "netinet/ip_fil.h" +#include "netinet/ip_proxy.h" +#include "netinet/ip_nat.h" +#include "netinet/ip_frag.h" +#include "netinet/ip_state.h" #ifndef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) #endif +#undef SOCKADDR_IN +#define SOCKADDR_IN struct sockaddr_in nat_t *nat_table[2][NAT_SIZE], *nat_instances = NULL; ipnat_t *nat_list = NULL; -u_long nat_inuse = 0, - fr_defnatage = 1200; +u_long fr_defnatage = 1200; natstat_t nat_stats; -#if SOLARIS -# ifndef _KERNEL -#define bzero(a,b) memset(a,0,b) -#define bcmp(a,b,c) memcpy(a,b,c) -#define bcopy(a,b,c) memmove(b,a,c) -# else +#if SOLARIS && defined(_KERNEL) extern kmutex_t ipf_nat; -# endif +extern kmutex_t ipf_natfrag; #endif static int flush_nattable __P((void)), clear_natlist __P((void)); -static void nattable_sync __P((void)), nat_delete __P((struct nat *)); -static nat_t *nat_new __P((ipnat_t *, ip_t *, fr_info_t *, u_short, int)); -static void fix_outcksum __P((u_short *, u_long)); -static void fix_incksum __P((u_short *, u_long)); -static void fix_outcksum(sp, n) +void fix_outcksum(sp, n) u_short *sp; u_long n; { @@ -112,7 +118,7 @@ u_long n; } -static void fix_incksum(sp, n) +void fix_incksum(sp, n) u_short *sp; u_long n; { @@ -197,6 +203,7 @@ int cmd, mode; } IRCOPY((char *)data, (char *)n, sizeof(*n)); n->in_ifp = (void *)GETUNIT(n->in_ifname); + n->in_apr = ap_match(n->in_p, n->in_plabel); n->in_next = *np; n->in_use = 0; n->in_space = ~(0xffffffff & ntohl(n->in_outmsk)); @@ -208,7 +215,7 @@ int cmd, mode; n->in_nip = ntohl(n->in_outip) + 1; else n->in_nip = ntohl(n->in_outip); - if (n->in_redir == NAT_MAP) { + if (n->in_redir & NAT_MAP) { n->in_pnext = ntohs(n->in_pmin); /* * Multiply by the number of ports made available. @@ -219,6 +226,7 @@ int cmd, mode; } /* Otherwise, these fields are preset */ *np = n; + nat_stats.ns_rules++; break; case SIOCRMNAT : if (!(mode & FWRITE)) { @@ -230,15 +238,20 @@ int cmd, mode; break; } *np = n->in_next; - - KFREE(n); - nattable_sync(); + if (!n->in_use) { + if (n->in_apr) + ap_free(n->in_apr); + KFREE(n); + nat_stats.ns_rules--; + } else { + n->in_flags |= IPN_DELETE; + n->in_next = NULL; + } break; case SIOCGNATS : nat_stats.ns_table[0] = nat_table[0]; nat_stats.ns_table[1] = nat_table[1]; nat_stats.ns_list = nat_list; - nat_stats.ns_inuse = nat_inuse; IWCOPY((char *)&nat_stats, (char *)data, sizeof(nat_stats)); break; case SIOCGNATL : @@ -269,6 +282,11 @@ int cmd, mode; ret = clear_natlist(); IWCOPY((caddr_t)&ret, data, sizeof(ret)); break; + case FIONREAD : +#ifdef IPFILTER_LOG + *(int *)data = iplused[IPL_LOGNAT]; +#endif + break; } SPLX(s); MUTEX_EXIT(&ipf_nat); @@ -280,6 +298,7 @@ static void nat_delete(natd) struct nat *natd; { register struct nat **natp, *nat; + struct ipnat *ipn; for (natp = natd->nat_hstart[0]; (nat = *natp); natp = &nat->nat_hnext[0]) @@ -295,12 +314,21 @@ struct nat *natd; break; } - if (natd->nat_ptr) { - natd->nat_ptr->in_space++; - natd->nat_ptr->in_use--; + if ((ipn = natd->nat_ptr)) { + ipn->in_space++; + ipn->in_use--; + if (!ipn->in_use && (ipn->in_flags & IPN_DELETE)) { + if (ipn->in_apr) + ap_free(ipn->in_apr); + KFREE(ipn); + nat_stats.ns_rules--; + } } + MUTEX_ENTER(&ipf_natfrag); + if (nat->nat_frag && nat->nat_frag->ipfr_data == nat) + nat->nat_frag->ipfr_data = NULL; + MUTEX_EXIT(&ipf_natfrag); KFREE(natd); - nat_inuse--; } @@ -330,43 +358,27 @@ static int flush_nattable() /* - * I know this is O(N*M), but it can't be avoided. - */ -static void nattable_sync() -{ - register nat_t *nat; - register ipnat_t *np; - int i; - - for (i = NAT_SIZE - 1; i >= 0; i--) - for (nat = nat_instances; nat; nat = nat->nat_next) { - for (np = nat_list; np; np = np->in_next) - if (nat->nat_ptr == np) - break; - /* - * XXX - is it better to remove this if ? works the - * same if it is just "nat->nat_ptr = np". - */ - if (!np) - nat->nat_ptr = NULL; - } -} - - -/* * clear_natlist - delete all entries in the active NAT mapping list. */ static int clear_natlist() { - register ipnat_t *n, **np; + register ipnat_t *n, **np = &nat_list; int i = 0; - for (np = &nat_list; (n = *np); i++) { + while ((n = *np)) { *np = n->in_next; - KFREE(n); + if (!n->in_use) { + if (n->in_apr) + ap_free(n->in_apr); + KFREE(n); + nat_stats.ns_rules--; + i++; + } else { + n->in_flags |= IPN_DELETE; + n->in_next = NULL; + } } - - nattable_sync(); + nat_stats.ns_inuse = 0; return i; } @@ -374,7 +386,7 @@ static int clear_natlist() /* * Create a new NAT table entry. */ -static nat_t *nat_new(np, ip, fin, flags, direction) +nat_t *nat_new(np, ip, fin, flags, direction) ipnat_t *np; ip_t *ip; fr_info_t *fin; @@ -426,15 +438,31 @@ int direction; struct ifaddr *ifa; struct sockaddr_in *sin; +# if (__FreeBSD_version >= 300000) + ifa = TAILQ_FIRST(&ifp->if_addrhead); +# else +# ifdef __NetBSD__ + ifa = ifp->if_addrlist.tqh_first; +# else ifa = ifp->if_addrlist; +# endif +# endif # if BSD < 199306 - sin = (struct sockaddr_in *)&ifa->ifa_addr; + sin = (SOCKADDR_IN *)&ifa->ifa_addr; # else - sin = (struct sockaddr_in *)ifa->ifa_addr; + sin = (SOCKADDR_IN *)ifa->ifa_addr; while (sin && ifa && sin->sin_family != AF_INET) { +# if (__FreeBSD_version >= 300000) + ifa = TAILQ_NEXT(ifa, ifa_link); +# else +# ifdef __NetBSD__ + ifa = ifa->ifa_list.tqe_next; +# else ifa = ifa->ifa_next; - sin = (struct sockaddr_in *)ifa->ifa_addr; +# endif +# endif + sin = (SOCKADDR_IN *)ifa->ifa_addr; } if (!ifa) sin = NULL; @@ -465,7 +493,8 @@ int direction; if ((np->in_nip & ntohl(np->in_outmsk)) > ntohl(np->in_outip)) np->in_nip = ntohl(np->in_outip) + 1; - } while (nat_inlookup(flags, ip->ip_dst, dport, in, port)); + } while (nat_inlookup(fin->fin_ifp, flags, ip->ip_dst, + dport, in, port)); /* Setup the NAT table */ nat->nat_inip = ip->ip_src; @@ -562,7 +591,10 @@ int direction; nat->nat_hnext[1] = *natp; *natp = nat; nat->nat_ptr = np; - np->in_use++; + nat->nat_bytes = 0; + nat->nat_pkts = 0; + nat->nat_ifp = fin->fin_ifp; + nat->nat_dir = direction; if (direction == NAT_OUTBOUND) { if (flags & IPN_TCPUDP) tcp->th_sport = htons(port); @@ -571,7 +603,8 @@ int direction; tcp->th_dport = htons(nport); } nat_stats.ns_added++; - nat_inuse++; + nat_stats.ns_inuse++; + np->in_use++; return nat; } @@ -586,7 +619,8 @@ int direction; * 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(flags, src, sport, mapdst, mapdport) +nat_t *nat_inlookup(ifp, flags, src, sport, mapdst, mapdport) +void *ifp; register int flags; struct in_addr src , mapdst; u_short sport, mapdport; @@ -597,7 +631,8 @@ u_short sport, mapdport; nat = nat_table[1][mapdst.s_addr % NAT_SIZE]; for (; nat; nat = nat->nat_hnext[1]) - if (nat->nat_oip.s_addr == src.s_addr && + if ((!ifp || ifp == nat->nat_ifp) && + nat->nat_oip.s_addr == src.s_addr && nat->nat_outip.s_addr == mapdst.s_addr && flags == nat->nat_flags && (!flags || (nat->nat_oport == sport && @@ -613,7 +648,8 @@ u_short sport, mapdport; * 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(flags, src, sport, dst, dport) +nat_t *nat_outlookup(ifp, flags, src, sport, dst, dport) +void *ifp; register int flags; struct in_addr src , dst; u_short sport, dport; @@ -624,7 +660,8 @@ u_short sport, dport; nat = nat_table[0][src.s_addr % NAT_SIZE]; for (; nat; nat = nat->nat_hnext[0]) - if (nat->nat_inip.s_addr == src.s_addr && + if ((!ifp || ifp == nat->nat_ifp) && + nat->nat_inip.s_addr == src.s_addr && nat->nat_oip.s_addr == dst.s_addr && flags == nat->nat_flags && (!flags || (nat->nat_inport == sport && nat->nat_oport == dport))) @@ -638,7 +675,8 @@ u_short sport, dport; * real destination address/port. We use this lookup when sending a packet * out, we're looking for a table entry, based on the source address. */ -nat_t *nat_lookupmapip(flags, mapsrc, mapsport, dst, dport) +nat_t *nat_lookupmapip(ifp, flags, mapsrc, mapsport, dst, dport) +void *ifp; register int flags; struct in_addr mapsrc , dst; u_short mapsport, dport; @@ -649,8 +687,9 @@ u_short mapsport, dport; nat = nat_table[1][mapsrc.s_addr % NAT_SIZE]; for (; nat; nat = nat->nat_hnext[0]) - if (nat->nat_outip.s_addr == mapsrc.s_addr && + if ((!ifp || ifp == nat->nat_ifp) && nat->nat_oip.s_addr == dst.s_addr && + nat->nat_outip.s_addr == mapsrc.s_addr && flags == nat->nat_flags && (!flags || (nat->nat_outport == mapsport && nat->nat_oport == dport))) @@ -671,7 +710,7 @@ register natlookup_t *np; * 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(IPN_TCPUDP, np->nl_inip, np->nl_inport, + if ((nat = nat_outlookup(NULL, IPN_TCPUDP, np->nl_inip, np->nl_inport, np->nl_outip, np->nl_outport))) { np->nl_inip = nat->nat_outip; np->nl_inport = nat->nat_outport; @@ -718,43 +757,56 @@ fr_info_t *fin; ipa = ip->ip_src.s_addr; MUTEX_ENTER(&ipf_nat); - for (np = nat_list; np; np = np->in_next) - if ((np->in_ifp == ifp) && np->in_space && - (!np->in_flags || (np->in_flags & nflags)) && - ((ipa & np->in_inmsk) == np->in_inip) && - ((np->in_redir == NAT_MAP) || - (np->in_pnext == sport))) { - /* - * If there is no current entry in the nat table for - * this IP#, create one for it. - */ - if (!(nat = nat_outlookup(nflags, ip->ip_src, sport, - ip->ip_dst, dport))) { + if ((nat = ipfr_nat_knownfrag(ip, fin))) + ; + else if ((nat = nat_outlookup(fin->fin_ifp, nflags, ip->ip_src, sport, + ip->ip_dst, dport))) + np = nat->nat_ptr; + else + /* + * If there is no current entry in the nat table for this IP#, + * create one for it (if there is a matching rule). + */ + for (np = nat_list; np; np = np->in_next) + if ((np->in_ifp == ifp) && np->in_space && + (!np->in_flags || (np->in_flags & nflags)) && + ((ipa & np->in_inmsk) == np->in_inip) && + ((np->in_redir & NAT_MAP) || + (np->in_pnext == sport))) { + if (*np->in_plabel && !ap_ok(ip, tcp, np)) + continue; /* - * If it's a redirection, then we don't want - * to create new outgoing port stuff. + * 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_REDIRECT) + if (!(np->in_redir & NAT_MAP)) continue; - if (!(nat = nat_new(np, ip, fin, nflags, + if ((nat = nat_new(np, ip, fin, nflags, NAT_OUTBOUND))) - break; #ifdef IPFILTER_LOG - nat_log(nat, (u_short)np->in_redir); + nat_log(nat, (u_short)np->in_redir); +#else + ; #endif + break; } - ip->ip_src = nat->nat_outip; - nat->nat_age = fr_defnatage; /* 5 mins */ + if (nat) { + if (!nat->nat_frag && fin->fin_fi.fi_fl & FI_FRAG) + ipfr_nat_newfrag(ip, fin, 0, nat); + nat->nat_age = fr_defnatage; + ip->ip_src = nat->nat_outip; + nat->nat_bytes += ip->ip_len; + nat->nat_pkts++; /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. */ #if SOLARIS - if (np->in_redir == NAT_MAP) + if (nat->nat_dir == NAT_OUTBOUND) fix_outcksum(&ip->ip_sum, nat->nat_ipsumd); else fix_incksum(&ip->ip_sum, nat->nat_ipsumd); @@ -770,6 +822,14 @@ fr_info_t *fin; csump = &tcp->th_sum; fr_tcp_age(&nat->nat_age, nat->nat_state, ip, fin,1); + /* + * 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; } else if (ip->ip_p == IPPROTO_UDP) { udphdr_t *udp = (udphdr_t *)tcp; @@ -781,7 +841,7 @@ fr_info_t *fin; csump = &ic->icmp_cksum; } if (csump) { - if (np->in_redir == NAT_MAP) + if (nat->nat_dir == NAT_OUTBOUND) fix_outcksum(csump, nat->nat_sumd); else @@ -789,6 +849,7 @@ fr_info_t *fin; nat->nat_sumd); } } + (void) ap_check(ip, tcp, fin, nat); nat_stats.ns_mapped[1]++; MUTEX_EXIT(&ipf_nat); return 1; @@ -829,38 +890,55 @@ fr_info_t *fin; in = ip->ip_dst; MUTEX_ENTER(&ipf_nat); - for (np = nat_list; np; np = np->in_next) - if ((np->in_ifp == ifp) && - (!np->in_flags || (nflags & np->in_flags)) && - ((in.s_addr & np->in_outmsk) == np->in_outip) && - (np->in_redir == NAT_MAP || np->in_pmin == dport)) { - if (!(nat = nat_inlookup(nflags, ip->ip_src, sport, - ip->ip_dst, dport))) { + + if ((nat = ipfr_nat_knownfrag(ip, fin))) + ; + else if ((nat = nat_inlookup(fin->fin_ifp, nflags, ip->ip_src, sport, + ip->ip_dst, dport))) + np = nat->nat_ptr; + else + /* + * If there is no current entry in the nat table for this IP#, + * create one for it (if there is a matching rule). + */ + for (np = nat_list; np; np = np->in_next) + if ((np->in_ifp == ifp) && + (!np->in_flags || (nflags & np->in_flags)) && + ((in.s_addr & np->in_outmsk) == np->in_outip) && + (np->in_redir & NAT_REDIRECT || + np->in_pmin == dport)) { /* * If this rule (np) is a redirection, rather * than a mapping, then do a nat_new. * Otherwise, if it's just a mapping, do a * continue; */ - if (np->in_redir == NAT_MAP) + if (!(np->in_redir & NAT_REDIRECT)) continue; - if (!(nat = nat_new(np, ip, fin, nflags, + if ((nat = nat_new(np, ip, fin, nflags, NAT_INBOUND))) - break; #ifdef IPFILTER_LOG - nat_log(nat, (u_short)np->in_redir); + nat_log(nat, (u_short)np->in_redir); +#else + ; #endif + break; } - ip->ip_dst = nat->nat_inip; - + if (nat) { + if (!nat->nat_frag && fin->fin_fi.fi_fl & FI_FRAG) + ipfr_nat_newfrag(ip, fin, 0, nat); + (void) ap_check(ip, tcp, fin, nat); nat->nat_age = fr_defnatage; + ip->ip_dst = nat->nat_inip; + nat->nat_bytes += ip->ip_len; + nat->nat_pkts++; /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. */ #if SOLARIS - if (np->in_redir == NAT_MAP) + if (nat->nat_dir == NAT_OUTBOUND) fix_incksum(&ip->ip_sum, nat->nat_ipsumd); else fix_outcksum(&ip->ip_sum, nat->nat_ipsumd); @@ -875,6 +953,14 @@ fr_info_t *fin; csump = &tcp->th_sum; fr_tcp_age(&nat->nat_age, nat->nat_state, ip, fin,0); + /* + * 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; } else if (ip->ip_p == IPPROTO_UDP) { udphdr_t *udp = (udphdr_t *)tcp; @@ -886,7 +972,7 @@ fr_info_t *fin; csump = &ic->icmp_cksum; } if (csump) { - if (np->in_redir == NAT_MAP) + if (nat->nat_dir == NAT_OUTBOUND) fix_incksum(csump, nat->nat_sumd); else @@ -914,6 +1000,7 @@ void ip_natunload() SPLNET(s); (void) clear_natlist(); (void) flush_nattable(); + (void) ap_unload(); SPLX(s) MUTEX_EXIT(&ipf_nat); } @@ -970,12 +1057,14 @@ u_short type; # if BSD >= 199306 || defined(__FreeBSD__) microtime((struct timeval *)&natl); # endif + natl.nl_inip = nat->nat_inip; + natl.nl_outip = nat->nat_outip; + natl.nl_origip = nat->nat_oip; + natl.nl_bytes = nat->nat_bytes; + natl.nl_pkts = nat->nat_pkts; natl.nl_origport = nat->nat_oport; - natl.nl_outport = nat->nat_outport; natl.nl_inport = nat->nat_inport; - natl.nl_origip = nat->nat_oip; - natl.nl_outip = nat->nat_outip; - natl.nl_inip = nat->nat_inip; + natl.nl_outport = nat->nat_outport; natl.nl_type = type; natl.nl_rule = -1; if (nat->nat_ptr) { |