diff options
24 files changed, 4065 insertions, 508 deletions
diff --git a/usr/src/cmd/ipf/lib/Makefile.com b/usr/src/cmd/ipf/lib/Makefile.com index 8019bb8e9c..2d582dc3b4 100644 --- a/usr/src/cmd/ipf/lib/Makefile.com +++ b/usr/src/cmd/ipf/lib/Makefile.com @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -49,7 +49,7 @@ OBJECTS= addicmp.o addipopt.o bcopywrap.o \ printhashnode.o printip.o printpool.o \ printpoolnode.o printfr.o printfraginfo.o \ printhostmap.o printifname.o printhostmask.o \ - printlog.o printmask.o printnat.o printpacket.o \ + printlog.o printlookup.o printmask.o printnat.o printpacket.o \ printpacket6.o printportcmp.o printproto.o \ printsbuf.o printstate.o printtunable.o ratoi.o \ remove_pool.o remove_poolnode.o remove_hash.o \ @@ -57,7 +57,7 @@ OBJECTS= addicmp.o addipopt.o bcopywrap.o \ tcpflags.o var.o verbose.o \ v6ionames.o v6optvalue.o printpool_live.o \ printpooldata.o printhash_live.o printhashdata.o \ - printactivenat.o + printactiveaddr.o printactivenat.o printaddr.o include $(SRC)/lib/Makefile.lib include ../../Makefile.ipf diff --git a/usr/src/cmd/ipf/lib/common/printactiveaddr.c b/usr/src/cmd/ipf/lib/common/printactiveaddr.c new file mode 100644 index 0000000000..f3ec165d7f --- /dev/null +++ b/usr/src/cmd/ipf/lib/common/printactiveaddr.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2002-2004 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * Added redirect stuff and a variety of bug fixes. (mcn@EnGarde.com) + */ + +#include "ipf.h" + +#pragma ident "%Z%%M% %I% %E% SMI" + +#if !defined(lint) +static const char rcsid[] = "@(#)$Id: printactiveaddr.c,v 1.1 2008/02/12 16:11:49 darren_r Exp $"; +#endif + +void +printactiveaddress(v, fmt, addr, ifname) + int v; + char *fmt; + i6addr_t *addr; + char *ifname; +{ + switch (v) + { + case 4 : + printf(fmt, inet_ntoa(addr->in4)); + break; +#ifdef USE_INET6 + case 6 : + printaddr(v, FRI_NORMAL, ifname, (u_32_t *)&addr->in6, NULL); + break; +#endif + default : + break; + } +} diff --git a/usr/src/cmd/ipf/lib/common/printactivenat.c b/usr/src/cmd/ipf/lib/common/printactivenat.c index ace6b6cfe3..54d86f96f4 100644 --- a/usr/src/cmd/ipf/lib/common/printactivenat.c +++ b/usr/src/cmd/ipf/lib/common/printactivenat.c @@ -1,12 +1,7 @@ /* - * Copyright (C) 1993-2001 by Darren Reed. + * Copyright (C) 2002-2004 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. - * - * Added redirect stuff and a variety of bug fixes. (mcn@EnGarde.com) - * - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" @@ -29,17 +24,23 @@ int opts, alive; if (nat->nat_flags & SI_CLONE) printf(" CLONE"); - printf(" %-15s", inet_ntoa(nat->nat_inip)); + printactiveaddress(nat->nat_v, " %-15s", &nat->nat_inip6, + nat->nat_ifnames[0]); if ((nat->nat_flags & IPN_TCPUDP) != 0) printf(" %-5hu", ntohs(nat->nat_inport)); - printf(" <- -> %-15s",inet_ntoa(nat->nat_outip)); + printf(" <- -> "); + printactiveaddress(nat->nat_v, "%-15s", &nat->nat_outip6, + nat->nat_ifnames[0]); if ((nat->nat_flags & IPN_TCPUDP) != 0) printf(" %-5hu", ntohs(nat->nat_outport)); - printf(" [%s", inet_ntoa(nat->nat_oip)); + printf(" ["); + printactiveaddress(nat->nat_v, "%s", &nat->nat_oip6, + nat->nat_ifnames[0]); + if ((nat->nat_flags & IPN_TCPUDP) != 0) printf(" %hu", ntohs(nat->nat_oport)); printf("]"); diff --git a/usr/src/cmd/ipf/lib/common/printaddr.c b/usr/src/cmd/ipf/lib/common/printaddr.c new file mode 100644 index 0000000000..914ec3c663 --- /dev/null +++ b/usr/src/cmd/ipf/lib/common/printaddr.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2005 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ipf.h" + + +void printaddr(v, type, ifname, addr, mask) +int v, type; +char *ifname; +u_32_t *addr, *mask; +{ + char *suffix; + + switch (type) + { + case FRI_BROADCAST : + suffix = "/bcast"; + break; + + case FRI_DYNAMIC : + printf("%s", ifname); + printmask(v, mask); + suffix = NULL; + break; + + case FRI_NETWORK : + suffix = "/net"; + break; + + case FRI_NETMASKED : + suffix = "/netmasked"; + break; + + case FRI_PEERADDR : + suffix = "/peer"; + break; + + case FRI_LOOKUP : + suffix = NULL; + printlookup((i6addr_t *)addr, (i6addr_t *)mask); + break; + + case FRI_NORMAL : + printhostmask(v, addr, mask); + suffix = NULL; + break; + default : + printf("<%d>", type); + printmask(v, mask); + suffix = NULL; + break; + } + + if (suffix != NULL) { + printf("%s/%s", ifname, suffix); + } +} diff --git a/usr/src/cmd/ipf/lib/common/printfr.c b/usr/src/cmd/ipf/lib/common/printfr.c index 5a77998948..063eb87c07 100644 --- a/usr/src/cmd/ipf/lib/common/printfr.c +++ b/usr/src/cmd/ipf/lib/common/printfr.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1993-2001 by Darren Reed. + * Copyright (C) 2000-2005 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -13,86 +13,6 @@ #include "ipf.h" -static void printaddr(int, int, char *, u_32_t *, u_32_t *); - -static void printaddr(v, type, ifname, addr, mask) -int v, type; -char *ifname; -u_32_t *addr, *mask; -{ - char *suffix; - - switch (type) - { - case FRI_BROADCAST : - suffix = "/bcast"; - break; - - case FRI_DYNAMIC : - printf("%s", ifname); - printmask(v, mask); - suffix = NULL; - break; - - case FRI_NETWORK : - suffix = "/net"; - break; - - case FRI_NETMASKED : - suffix = "/netmasked"; - break; - - case FRI_PEERADDR : - suffix = "/peer"; - break; - - case FRI_LOOKUP : - suffix = NULL; - printlookup((i6addr_t *)addr, (i6addr_t *)mask); - break; - - case FRI_NORMAL : - printhostmask(v, addr, mask); - suffix = NULL; - break; - default : - printf("<%d>", type); - printmask(v, mask); - suffix = NULL; - break; - } - - if (suffix != NULL) { - printf("%s/%s", ifname, suffix); - } -} - - -void printlookup(addr, mask) -i6addr_t *addr, *mask; -{ - switch (addr->iplookuptype) - { - case IPLT_POOL : - printf("pool/"); - break; - case IPLT_HASH : - printf("hash/"); - break; - default : - printf("lookup(%x)=", addr->iplookuptype); - break; - } - - printf("%u", addr->iplookupnum); - if (opts & OPT_UNDEF) { - if (mask->iplookupptr == NULL) { - printf("(!)"); - } - } -} - - /* * print the filter structure in a useful way */ diff --git a/usr/src/cmd/ipf/lib/common/printhostmask.c b/usr/src/cmd/ipf/lib/common/printhostmask.c index 2afe5c0400..bd8a798b9f 100644 --- a/usr/src/cmd/ipf/lib/common/printhostmask.c +++ b/usr/src/cmd/ipf/lib/common/printhostmask.c @@ -1,11 +1,11 @@ /* - * Copyright (C) 1993-2001 by Darren Reed. + * Copyright (C) 2000-2005 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id: printhostmask.c,v 1.8 2002/04/11 15:01:19 darrenr Exp $ * - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -24,26 +24,25 @@ u_32_t *addr, *mask; struct in_addr ipa; #endif - if (!*addr && !*mask) + if ((v == 4) && (!*addr) && (!*mask)) printf("any"); else { #ifdef USE_INET6 void *ptr = addr; int af; - if (v == 4) { - ptr = addr; + if (v == 4) af = AF_INET; - } else if (v == 6) { - ptr = addr; + else if (v == 6) af = AF_INET6; - } else + else af = 0; printf("%s", inet_ntop(af, ptr, ipbuf, sizeof(ipbuf))); #else ipa.s_addr = *addr; printf("%s", inet_ntoa(ipa)); #endif - printmask(v, mask); + if (mask != NULL) + printmask(v, mask); } } diff --git a/usr/src/cmd/ipf/lib/common/printlookup.c b/usr/src/cmd/ipf/lib/common/printlookup.c new file mode 100644 index 0000000000..ffc822e595 --- /dev/null +++ b/usr/src/cmd/ipf/lib/common/printlookup.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2005 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ipf.h" + + +void printlookup(addr, mask) + i6addr_t *addr, *mask; +{ + switch (addr->iplookuptype) + { + case IPLT_POOL : + printf("pool/"); + break; + case IPLT_HASH : + printf("hash/"); + break; + default : + printf("lookup(%x)=", addr->iplookuptype); + break; + } + + printf("%u", addr->iplookupnum); + if (opts & OPT_UNDEF) { + if (mask->iplookupptr == NULL) + printf("(!)"); + } +} diff --git a/usr/src/cmd/ipf/lib/common/printnat.c b/usr/src/cmd/ipf/lib/common/printnat.c index 521d057f1e..ea8bd72fb0 100644 --- a/usr/src/cmd/ipf/lib/common/printnat.c +++ b/usr/src/cmd/ipf/lib/common/printnat.c @@ -1,11 +1,11 @@ /* - * Copyright (C) 1993-2001 by Darren Reed. + * Copyright (C) 2002-2005 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * Added redirect stuff and a variety of bug fixes. (mcn@EnGarde.com) * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -27,7 +27,9 @@ ipnat_t *np; int opts; { struct protoent *pr; - int bits; + int bits, af; + char ipbuf[INET6_ADDRSTRLEN]; + void *ptr; pr = getprotobynumber(np->in_p); @@ -63,11 +65,11 @@ int opts; printf("! "); printf("from "); if (np->in_redir == NAT_REDIRECT) { - printhostmask(4, (u_32_t *)&np->in_srcip, - (u_32_t *)&np->in_srcmsk); + printhostmask(np->in_v, (u_32_t *)&np->in_src[0], + (u_32_t *)&np->in_src[1]); } else { - printhostmask(4, (u_32_t *)&np->in_inip, - (u_32_t *)&np->in_inmsk); + printhostmask(np->in_v, (u_32_t *)&np->in_in[0], + (u_32_t *)&np->in_in[1]); } if (np->in_scmp) printportcmp(np->in_p, &np->in_tuc.ftu_src); @@ -76,37 +78,46 @@ int opts; printf(" !"); printf(" to "); if (np->in_redir == NAT_REDIRECT) { - printhostmask(4, (u_32_t *)&np->in_outip, - (u_32_t *)&np->in_outmsk); + printhostmask(np->in_v, (u_32_t *)&np->in_out[0], + (u_32_t *)&np->in_out[1]); } else { - printhostmask(4, (u_32_t *)&np->in_srcip, - (u_32_t *)&np->in_srcmsk); + printhostmask(np->in_v, (u_32_t *)&np->in_src[0], + (u_32_t *)&np->in_src[1]); } if (np->in_dcmp) printportcmp(np->in_p, &np->in_tuc.ftu_dst); } + if (np->in_v == 4) + af = AF_INET; + else if (np->in_v == 6) + af = AF_INET6; + else + af = 0; + if (np->in_redir == NAT_REDIRECT) { if (!(np->in_flags & IPN_FILTER)) { - printf("%s", inet_ntoa(np->in_out[0].in4)); - bits = count4bits(np->in_outmsk); - if (bits != -1) - printf("/%d", bits); - else - printf("/%s", inet_ntoa(np->in_out[1].in4)); + ptr = (void *)(u_32_t *)&np->in_out[0]; + printf("%s", inet_ntop(af, ptr, ipbuf, sizeof (ipbuf))); + printmask(np->in_v, (u_32_t *)&np->in_out[1]); if (np->in_flags & IPN_TCPUDP) { printf(" port %d", ntohs(np->in_pmin)); if (np->in_pmax != np->in_pmin) printf("-%d", ntohs(np->in_pmax)); } } - printf(" -> %s", inet_ntoa(np->in_in[0].in4)); - if (np->in_flags & IPN_SPLIT) - printf(",%s", inet_ntoa(np->in_in[1].in4)); - if (np->in_inip == 0) { - bits = count4bits(np->in_inmsk); - printf("/%d", bits); + printf(" -> "); + ptr = (void *)(u_32_t *)&np->in_in[0]; + printf("%s", inet_ntop(af, ptr, ipbuf, sizeof (ipbuf))); + if (np->in_flags & IPN_SPLIT) { + printf(","); + ptr = (void *)(u_32_t *)&np->in_in[1]; + printf("%s", inet_ntop(af, ptr, ipbuf, sizeof (ipbuf))); } + if (((np->in_v == 4) && (np->in_inip == 0)) || + ((np->in_v == 6) && IP6_ISZERO(&np->in_in[0]))) + printmask(np->in_v, (u_32_t *)&np->in_in[1]); + if (np->in_flags & IPN_TCPUDP) { if ((np->in_flags & IPN_FIXEDDPORT) != 0) printf(" port = %d", ntohs(np->in_pnext)); @@ -127,7 +138,7 @@ int opts; if (np->in_mssclamp != 0) printf(" mssclamp %d", np->in_mssclamp); if (*np->in_plabel != '\0') - printf(" proxy %.*s", (int)sizeof(np->in_plabel), + printf(" proxy %.*s", (int)sizeof (np->in_plabel), np->in_plabel); if (np->in_tag.ipt_tag[0] != '\0') printf(" tag %-.*s", IPFTAG_LEN, np->in_tag.ipt_tag); @@ -136,24 +147,22 @@ int opts; printf("\tpmax %u\n", np->in_pmax); } else { if (!(np->in_flags & IPN_FILTER)) { - printf("%s/", inet_ntoa(np->in_in[0].in4)); - bits = count4bits(np->in_inmsk); - if (bits != -1) - printf("%d", bits); - else - printf("%s", inet_ntoa(np->in_in[1].in4)); + ptr = (void *)(u_32_t *)&np->in_in[0]; + printf("%s", inet_ntop(af, ptr, ipbuf, sizeof (ipbuf))); + printmask(np->in_v, (u_32_t *)&np->in_in[1]); } printf(" -> "); if (np->in_flags & IPN_IPRANGE) { - printf("range %s-", inet_ntoa(np->in_out[0].in4)); - printf("%s", inet_ntoa(np->in_out[1].in4)); + printf("range "); + ptr = (void *)(u_32_t *)&np->in_out[0]; + printf("%s", inet_ntop(af, ptr, ipbuf, sizeof (ipbuf))); + printf("-"); + ptr = (void *)(u_32_t *)&np->in_out[1]; + printf("%s", inet_ntop(af, ptr, ipbuf, sizeof (ipbuf))); } else { - printf("%s/", inet_ntoa(np->in_out[0].in4)); - bits = count4bits(np->in_outmsk); - if (bits != -1) - printf("%d", bits); - else - printf("%s", inet_ntoa(np->in_out[1].in4)); + ptr = (void *)(u_32_t *)&np->in_out[0]; + printf("%s", inet_ntop(af, ptr, ipbuf, sizeof (ipbuf))); + printmask(np->in_v, (u_32_t *)&np->in_out[1]); } if (*np->in_plabel != '\0') { printf(" proxy port "); @@ -168,7 +177,7 @@ int opts; else fputs("???", stdout); } - printf(" %.*s/", (int)sizeof(np->in_plabel), + printf(" %.*s/", (int)sizeof (np->in_plabel), np->in_plabel); printproto(pr, np->in_p, NULL); } else if (np->in_redir == NAT_MAPBLK) { diff --git a/usr/src/cmd/ipf/tools/Makefile.tools b/usr/src/cmd/ipf/tools/Makefile.tools index 65d3eb211b..4b171b1b6a 100644 --- a/usr/src/cmd/ipf/tools/Makefile.tools +++ b/usr/src/cmd/ipf/tools/Makefile.tools @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # @@ -36,8 +36,8 @@ IPMON_OBJS= ipmon.o ipmon_y.o ipmon_l.o IPNAT_OBJS= ipnat.o ipnat_y.o ipnat_l.o IPPOOL_OBJS= ippool.o ippool_y.o ippool_l.o IPFTEST_OBJS= ipftest.o \ - ip_fil.o ip_state.o \ - ip_frag.o ip_nat.o fil.o \ + ip_fil.o ip_state.o ip_compat.o \ + ip_frag.o ip_nat.o ip_nat6.o fil.o \ ip_htable.o ip_lookup.o \ ip_proxy.o ip_auth.o ip_log.o \ ipf_y.o ipf_l.o \ diff --git a/usr/src/cmd/ipf/tools/ip_fil.c b/usr/src/cmd/ipf/tools/ip_fil.c index 52fa867504..bbdb57802a 100644 --- a/usr/src/cmd/ipf/tools/ip_fil.c +++ b/usr/src/cmd/ipf/tools/ip_fil.c @@ -3,7 +3,7 @@ * * See the IPFILTER.LICENCE file for details on licencing. * - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -479,7 +479,8 @@ ipf_stack_t *ifs; f->fr_ifa = (void *)-1; #endif RWLOCK_EXIT(&ifs->ifs_ipf_mutex); - fr_natifpsync(IPFSYNC_OLDIFP, ifp, NULL, ifs); + fr_natifpsync(IPFSYNC_OLDIFP, 4, ifp, NULL, ifs); + fr_natifpsync(IPFSYNC_OLDIFP, 6, ifp, NULL, ifs); } diff --git a/usr/src/cmd/ipf/tools/ipmon.c b/usr/src/cmd/ipf/tools/ipmon.c index 0cacc6ce54..e9d37720ef 100644 --- a/usr/src/cmd/ipf/tools/ipmon.c +++ b/usr/src/cmd/ipf/tools/ipmon.c @@ -1,9 +1,9 @@ /* - * Copyright (C) 1993-2001, 2003 by Darren Reed. + * Copyright (C) 2001-2008 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -737,49 +737,52 @@ int blen; } (void) strftime(t, len, "%T", tm); t += strlen(t); - (void) sprintf(t, ".%-.6ld @%hd ", ipl->ipl_usec, nl->nl_rule + 1); + (void) sprintf(t, ".%-.6ld @%hd ", ipl->ipl_usec, nl->nlg_rule + 1); t += strlen(t); - if (nl->nl_type == NL_NEWMAP) + if (nl->nlg_type == NL_NEWMAP) strcpy(t, "NAT:MAP "); - else if (nl->nl_type == NL_NEWRDR) + else if (nl->nlg_type == NL_NEWRDR) strcpy(t, "NAT:RDR "); - else if (nl->nl_type == NL_FLUSH) + else if (nl->nlg_type == NL_FLUSH) strcpy(t, "NAT:FLUSH "); - else if (nl->nl_type == NL_EXPIRE) + else if (nl->nlg_type == NL_EXPIRE) strcpy(t, "NAT:EXPIRE "); - else if (nl->nl_type == NL_NEWBIMAP) + else if (nl->nlg_type == NL_NEWBIMAP) strcpy(t, "NAT:BIMAP "); - else if (nl->nl_type == NL_NEWBLOCK) + else if (nl->nlg_type == NL_NEWBLOCK) strcpy(t, "NAT:MAPBLOCK "); - else if (nl->nl_type == NL_CLONE) + else if (nl->nlg_type == NL_CLONE) strcpy(t, "NAT:CLONE "); else - sprintf(t, "Type: %d ", nl->nl_type); + sprintf(t, "Type: %d ", nl->nlg_type); t += strlen(t); - proto = getproto(nl->nl_p); + proto = getproto(nl->nlg_p); - (void) sprintf(t, "%s,%s <- -> ", HOSTNAME_V4(res, nl->nl_inip), - portname(res, proto, (u_int)nl->nl_inport)); + (void) sprintf(t, "%s,%s <- -> ", hostname(res, nl->nlg_v, + (u_32_t *)&nl->nlg_inip), + portname(res, proto, (u_int)nl->nlg_inport)); t += strlen(t); - (void) sprintf(t, "%s,%s ", HOSTNAME_V4(res, nl->nl_outip), - portname(res, proto, (u_int)nl->nl_outport)); + (void) sprintf(t, "%s,%s ", hostname(res, nl->nlg_v, + (u_32_t *)&nl->nlg_outip), + portname(res, proto, (u_int)nl->nlg_outport)); t += strlen(t); - (void) sprintf(t, "[%s,%s]", HOSTNAME_V4(res, nl->nl_origip), - portname(res, proto, (u_int)nl->nl_origport)); + (void) sprintf(t, "[%s,%s]", hostname(res, nl->nlg_v, + (u_32_t *)&nl->nlg_origip), + portname(res, proto, (u_int)nl->nlg_origport)); t += strlen(t); - if (nl->nl_type == NL_EXPIRE) { + if (nl->nlg_type == NL_EXPIRE) { #ifdef USE_QUAD_T (void) sprintf(t, " Pkts %qd/%qd Bytes %qd/%qd", - (long long)nl->nl_pkts[0], - (long long)nl->nl_pkts[1], - (long long)nl->nl_bytes[0], - (long long)nl->nl_bytes[1]); + (long long)nl->nlg_pkts[0], + (long long)nl->nlg_pkts[1], + (long long)nl->nlg_bytes[0], + (long long)nl->nlg_bytes[1]); #else (void) sprintf(t, " Pkts %ld/%ld Bytes %ld/%ld", - nl->nl_pkts[0], nl->nl_pkts[1], - nl->nl_bytes[0], nl->nl_bytes[1]); + nl->nlg_pkts[0], nl->nlg_pkts[1], + nl->nlg_bytes[0], nl->nlg_bytes[1]); #endif t += strlen(t); } diff --git a/usr/src/cmd/ipf/tools/ipnat_y.y b/usr/src/cmd/ipf/tools/ipnat_y.y index 48ca425c4f..cab9d6d3c5 100644 --- a/usr/src/cmd/ipf/tools/ipnat_y.y +++ b/usr/src/cmd/ipf/tools/ipnat_y.y @@ -1,10 +1,10 @@ %{ /* - * Copyright (C) 2003 by Darren Reed. + * Copyright (C) 2001-2008 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -66,13 +66,15 @@ static addfunc_t nataddfunc = NULL; static void newnatrule __P((void)); static void setnatproto __P((int)); -static u_32_t lookuphost __P((char *)); %} %union { char *str; u_32_t num; - struct in_addr ipa; + struct { + i6addr_t a; + int v; + } ipa; frentry_t fr; frtuc_t *frt; u_short port; @@ -82,8 +84,9 @@ static u_32_t lookuphost __P((char *)); int pc; } pc; struct { - struct in_addr a; - struct in_addr m; + i6addr_t a; + i6addr_t m; + int v; } ipp; union i6addr ip6; }; @@ -102,8 +105,9 @@ static u_32_t lookuphost __P((char *)); %token IPNY_TLATE %type <port> portspec %type <num> hexnumber compare range proto -%type <ipa> hostname ipv4 -%type <ipp> addr nummask rhaddr +%type <num> saddr daddr sobject dobject mapfrom rdrfrom dip +%type <ipa> hostname ipv4 ipaddr +%type <ipp> addr rhaddr %type <pc> portstuff %% file: line @@ -113,6 +117,8 @@ file: line ; line: xx rule { while ((nat = nattop) != NULL) { + if (nat->in_v == 0) + nat->in_v = 4; nattop = nat->in_next; (*nataddfunc)(natfd, natioctlfunc, nat); free(nat); @@ -145,11 +151,12 @@ eol: | ';' ; map: mapit ifnames addr IPNY_TLATE rhaddr proxy mapoptions - { nat->in_v = 4; - nat->in_inip = $3.a.s_addr; - nat->in_inmsk = $3.m.s_addr; - nat->in_outip = $5.a.s_addr; - nat->in_outmsk = $5.m.s_addr; + { if ($3.v != 0 && $3.v != $5.v && $5.v != 0) + yyerror("1.address family mismatch"); + bcopy(&$3.a, &nat->in_in[0], sizeof($3.a)); + bcopy(&$3.m, &nat->in_in[1], sizeof($3.a)); + bcopy(&$5.a, &nat->in_out[0], sizeof($5.a)); + bcopy(&$5.m, &nat->in_out[1], sizeof($5.a)); if (nat->in_ifnames[1][0] == '\0') strncpy(nat->in_ifnames[1], nat->in_ifnames[0], @@ -161,11 +168,12 @@ map: mapit ifnames addr IPNY_TLATE rhaddr proxy mapoptions nat_setgroupmap(nat); } | mapit ifnames addr IPNY_TLATE rhaddr mapport mapoptions - { nat->in_v = 4; - nat->in_inip = $3.a.s_addr; - nat->in_inmsk = $3.m.s_addr; - nat->in_outip = $5.a.s_addr; - nat->in_outmsk = $5.m.s_addr; + { if ($3.v != 0 && $3.v != $5.v && $5.v != 0) + yyerror("2.address family mismatch"); + bcopy(&$3.a, &nat->in_in[0], sizeof($3.a)); + bcopy(&$3.m, &nat->in_in[1], sizeof($3.a)); + bcopy(&$5.a, &nat->in_out[0], sizeof($5.a)); + bcopy(&$5.m, &nat->in_out[1], sizeof($5.a)); if (nat->in_ifnames[1][0] == '\0') strncpy(nat->in_ifnames[1], nat->in_ifnames[0], @@ -177,9 +185,10 @@ map: mapit ifnames addr IPNY_TLATE rhaddr proxy mapoptions nat_setgroupmap(nat); } | mapit ifnames mapfrom IPNY_TLATE rhaddr proxy mapoptions - { nat->in_v = 4; - nat->in_outip = $5.a.s_addr; - nat->in_outmsk = $5.m.s_addr; + { if ($3 != 0 && $3 != $5.v && $5.v != 0) + yyerror("3.address family mismatch"); + bcopy(&$5.a, &nat->in_out[0], sizeof($5.a)); + bcopy(&$5.m, &nat->in_out[1], sizeof($5.a)); if (nat->in_ifnames[1][0] == '\0') strncpy(nat->in_ifnames[1], nat->in_ifnames[0], @@ -191,9 +200,10 @@ map: mapit ifnames addr IPNY_TLATE rhaddr proxy mapoptions nat_setgroupmap(nat); } | mapit ifnames mapfrom IPNY_TLATE rhaddr mapport mapoptions - { nat->in_v = 4; - nat->in_outip = $5.a.s_addr; - nat->in_outmsk = $5.m.s_addr; + { if ($3 != 0 && $3 != $5.v && $5.v != 0) + yyerror("4.address family mismatch"); + bcopy(&$5.a, &nat->in_out[0], sizeof($5.a)); + bcopy(&$5.m, &nat->in_out[1], sizeof($5.a)); if (nat->in_ifnames[1][0] == '\0') strncpy(nat->in_ifnames[1], nat->in_ifnames[0], @@ -208,11 +218,12 @@ map: mapit ifnames addr IPNY_TLATE rhaddr proxy mapoptions mapblock: mapblockit ifnames addr IPNY_TLATE addr ports mapoptions - { nat->in_v = 4; - nat->in_inip = $3.a.s_addr; - nat->in_inmsk = $3.m.s_addr; - nat->in_outip = $5.a.s_addr; - nat->in_outmsk = $5.m.s_addr; + { if ($3.v != 0 && $3.v != $5.v && $5.v != 0) + yyerror("5.address family mismatch"); + bcopy(&$3.a, &nat->in_in[0], sizeof($3.a)); + bcopy(&$3.m, &nat->in_in[1], sizeof($3.a)); + bcopy(&$5.a, &nat->in_out[0], sizeof($5.a)); + bcopy(&$5.m, &nat->in_out[1], sizeof($5.a)); if (nat->in_ifnames[1][0] == '\0') strncpy(nat->in_ifnames[1], nat->in_ifnames[0], @@ -226,9 +237,10 @@ mapblock: ; redir: rdrit ifnames addr dport IPNY_TLATE dip nport setproto rdroptions - { nat->in_v = 4; - nat->in_outip = $3.a.s_addr; - nat->in_outmsk = $3.m.s_addr; + { if ($6 != 0 && $3.v != 0 && $6 != $3.v) + yyerror("6.address family mismatch"); + bcopy(&$3.a, &nat->in_out[0], sizeof($3.a)); + bcopy(&$3.m, &nat->in_out[1], sizeof($3.a)); if (nat->in_ifnames[1][0] == '\0') strncpy(nat->in_ifnames[1], nat->in_ifnames[0], @@ -241,7 +253,8 @@ redir: rdrit ifnames addr dport IPNY_TLATE dip nport setproto rdroptions setnatproto(IPPROTO_TCP); } | rdrit ifnames rdrfrom IPNY_TLATE dip nport setproto rdroptions - { nat->in_v = 4; + { if ($5 != 0 && $3 != 0 && $5 != $3) + yyerror("7.address family mismatch"); if ((nat->in_p == 0) && ((nat->in_flags & IPN_TCPUDP) == 0) && (nat->in_pmin != 0 || @@ -254,9 +267,10 @@ redir: rdrit ifnames addr dport IPNY_TLATE dip nport setproto rdroptions sizeof(nat->in_ifnames[0])); } | rdrit ifnames addr IPNY_TLATE dip setproto rdroptions - { nat->in_v = 4; - nat->in_outip = $3.a.s_addr; - nat->in_outmsk = $3.m.s_addr; + { if ($5 != 0 && $3.v != 0 && $5 != $3.v) + yyerror("8.address family mismatch"); + bcopy(&$3.a, &nat->in_out[0], sizeof($3.a)); + bcopy(&$3.m, &nat->in_out[1], sizeof($3.a)); if (nat->in_ifnames[1][0] == '\0') strncpy(nat->in_ifnames[1], nat->in_ifnames[0], @@ -307,23 +321,77 @@ setproto: } ; -rhaddr: addr { $$.a = $1.a; $$.m = $1.m; } - | IPNY_RANGE ipv4 '-' ipv4 - { $$.a = $2; $$.m = $4; - nat->in_flags |= IPN_IPRANGE; } +rhaddr: addr { $$.a = $1.a; + $$.m = $1.m; + $$.v = $1.v; + if ($$.v == 0) + $$.v = nat->in_v; + yyexpectaddr = 0; } + | IPNY_RANGE hostname '-' hostname + { if ($2.v != 0 && $4.v != 0 && $4.v != $2.v) + yyerror("9.address family " + "mismatch"); + $$.v = $2.v; + $$.a = $2.a; + $$.m = $4.a; + nat->in_flags |= IPN_IPRANGE; + yyexpectaddr = 0; } ; dip: - hostname { nat->in_inip = $1.s_addr; - nat->in_inmsk = 0xffffffff; } - | hostname '/' YY_NUMBER { nat->in_inip = $1.s_addr; - if (nat->in_inip != 0 || - ($3 != 0 && $3 != 32)) + hostname { bcopy(&$1.a, &nat->in_in[0], + sizeof($1.a)); + if ($1.v == 0) + $1.v = nat->in_v; + if ($1.v == 4) { + nat->in_inmsk = 0xffffffff; + } else { + nat->in_in[1].i6[0] = 0xffffffff; + nat->in_in[1].i6[1] = 0xffffffff; + nat->in_in[1].i6[2] = 0xffffffff; + nat->in_in[1].i6[3] = 0xffffffff; + } + $$ = $1.v; + } + | hostname '/' YY_NUMBER { if ($1.v == 0) + $1.v = nat->in_v; + if ($1.v == 4 && + ($1.a.in4.s_addr != 0 || + ($3 != 0 && $3 != 32))) + yyerror("Invalid mask for dip"); + else if ($1.v == 6 && + ($1.a.in4.s_addr != 0 || + ($3 != 0 && $3 != 128))) yyerror("Invalid mask for dip"); - ntomask(4, $3, &nat->in_inmsk); } - | hostname ',' hostname { nat->in_flags |= IPN_SPLIT; - nat->in_inip = $1.s_addr; - nat->in_inmsk = $3.s_addr; } + else if ($1.v == 0 ) { + if ($1.a.in4.s_addr == 0 && + ($3 == 32 || $3 == 0)) + $1.v = 4; + else if ($3 == 128) + $1.v = 6; + } + bcopy(&$1.a, &nat->in_in[0], + sizeof($1.a)); + ntomask($1.v, $3, + (u_32_t *)&nat->in_in[1]); + nat->in_in[0].i6[0] &= nat->in_in[1].i6[0]; + nat->in_in[0].i6[0] &= nat->in_in[1].i6[1]; + nat->in_in[0].i6[0] &= nat->in_in[1].i6[2]; + nat->in_in[0].i6[0] &= nat->in_in[1].i6[3]; + nat->in_v = $1.v; + $$ = $1.v; + } + | hostname ',' { yyexpectaddr = 1; } hostname + { if ($1.v != $4.v) + yyerror("10.address family " + "mismatch"); + $$ = $1.v; + nat->in_flags |= IPN_SPLIT; + bcopy(&$1.a, &nat->in_in[0], + sizeof($1.a)); + bcopy(&$4.a, &nat->in_in[1], + sizeof($4.a)); + yyexpectaddr = 0; } ; portspec: @@ -368,23 +436,42 @@ mapblockit: ; mapfrom: - from sobject IPNY_TO dobject + from sobject IPNY_TO dobject { if ($2 != 0 && $4 != 0 && $2 != $4) + yyerror("11.address family " + "mismatch"); + $$ = $2; + } | from sobject '!' IPNY_TO dobject - { nat->in_flags |= IPN_NOTDST; } + { if ($2 != 0 && $5 != 0 && $2 != $5) + yyerror("12.address family " + "mismatch"); + nat->in_flags |= IPN_NOTDST; + $$ = $2; + } ; rdrfrom: - from sobject IPNY_TO dobject + from sobject IPNY_TO dobject { if ($2 != 0 && $4 != 0 && $2 != $4) + yyerror("13.address family " + "mismatch"); + $$ = $2; + } | '!' from sobject IPNY_TO dobject - { nat->in_flags |= IPN_NOTSRC; } + { if ($3 != 0 && $5 != 0 && $3 != $5) + yyerror("14.address family " + "mismatch"); + nat->in_flags |= IPN_NOTSRC; + $$ = $3; + } ; -from: IPNY_FROM { nat->in_flags |= IPN_FILTER; } +from: IPNY_FROM { nat->in_flags |= IPN_FILTER; + yyexpectaddr = 1; } ; ifnames: - ifname - | ifname ',' otherifname + ifname { yyexpectaddr = 1; } + | ifname ',' otherifname { yyexpectaddr = 1; } ; ifname: YY_STR { strncpy(nat->in_ifnames[0], $1, @@ -428,24 +515,31 @@ mapport: ; sobject: - saddr + saddr { $$ = $1; } | saddr IPNY_PORT portstuff { nat->in_sport = $3.p1; nat->in_stop = $3.p2; - nat->in_scmp = $3.pc; } + nat->in_scmp = $3.pc; + $$ = $1; + } ; saddr: addr { if (nat->in_redir == NAT_REDIRECT) { - nat->in_srcip = $1.a.s_addr; - nat->in_srcmsk = $1.m.s_addr; + bcopy(&$1.a, &nat->in_src[0], + sizeof($1.a)); + bcopy(&$1.m, &nat->in_src[1], + sizeof($1.a)); } else { - nat->in_inip = $1.a.s_addr; - nat->in_inmsk = $1.m.s_addr; + bcopy(&$1.a, &nat->in_in[0], + sizeof($1.a)); + bcopy(&$1.m, &nat->in_in[1], + sizeof($1.a)); } + $$ = $1.v; } ; dobject: - daddr + daddr { $$ = $1; } | daddr IPNY_PORT portstuff { nat->in_dport = $3.p1; nat->in_dtop = $3.p2; nat->in_dcmp = $3.pc; @@ -455,33 +549,93 @@ dobject: ; daddr: addr { if (nat->in_redir == NAT_REDIRECT) { - nat->in_outip = $1.a.s_addr; - nat->in_outmsk = $1.m.s_addr; + bcopy(&$1.a, &nat->in_out[0], + sizeof($1.a)); + bcopy(&$1.m, &nat->in_out[1], + sizeof($1.a)); } else { - nat->in_srcip = $1.a.s_addr; - nat->in_srcmsk = $1.m.s_addr; + bcopy(&$1.a, &nat->in_src[0], + sizeof($1.a)); + bcopy(&$1.m, &nat->in_src[1], + sizeof($1.a)); } + $$ = $1.v; } ; -addr: IPNY_ANY { $$.a.s_addr = 0; $$.m.s_addr = 0; } - | nummask { $$.a = $1.a; $$.m = $1.m; - $$.a.s_addr &= $$.m.s_addr; } - | hostname '/' ipv4 { $$.a = $1; $$.m = $3; - $$.a.s_addr &= $$.m.s_addr; } - | hostname '/' hexnumber { $$.a = $1; $$.m.s_addr = $3; - $$.a.s_addr &= $$.m.s_addr; } - | hostname IPNY_MASK ipv4 { $$.a = $1; $$.m = $3; - $$.a.s_addr &= $$.m.s_addr; } - | hostname IPNY_MASK hexnumber { $$.a = $1; $$.m.s_addr = $3; - $$.a.s_addr &= $$.m.s_addr; } - ; - -nummask: - hostname { $$.a = $1; - $$.m.s_addr = 0xffffffff; } - | hostname '/' YY_NUMBER { $$.a = $1; - ntomask(4, $3, &$$.m.s_addr); } +addr: IPNY_ANY { yyexpectaddr = 0; + bzero(&$$.a, sizeof($$.a)); + bzero(&$$.m, sizeof($$.a)); + $$.v = nat->in_v; + } + | hostname { $$.a = $1.a; + $$.v = $1.v; + if ($$.v == 4) { + $$.m.in4.s_addr = 0xffffffff; + } else { + $$.m.i6[0] = 0xffffffff; + $$.m.i6[1] = 0xffffffff; + $$.m.i6[2] = 0xffffffff; + $$.m.i6[3] = 0xffffffff; + } + yyexpectaddr = 0; + } + | hostname '/' YY_NUMBER { $$.a = $1.a; + if ($1.v == 0) { + if ($1.a.in4.s_addr != 0) + yyerror("invalid addr"); + if ($3 == 0 || $3 == 32) + $1.v = 4; + else if ($3 == 128) + $1.v = 6; + else + yyerror("invalid mask"); + nat->in_v = $1.v; + } + ntomask($1.v, $3, (u_32_t *)&$$.m); + $$.a.i6[0] &= $$.m.i6[0]; + $$.a.i6[1] &= $$.m.i6[1]; + $$.a.i6[2] &= $$.m.i6[2]; + $$.a.i6[3] &= $$.m.i6[3]; + $$.v = $1.v; + yyexpectaddr = 0; + } + | hostname '/' ipaddr { if ($1.v != $3.v) { + yyerror("1.address family " + "mismatch"); + } + $$.a = $1.a; + $$.m = $3.a; + $$.a.i6[0] &= $$.m.i6[0]; + $$.a.i6[1] &= $$.m.i6[1]; + $$.a.i6[2] &= $$.m.i6[2]; + $$.a.i6[3] &= $$.m.i6[3]; + $$.v = $1.v; + yyexpectaddr = 0; + } + | hostname '/' hexnumber { $$.a = $1.a; + $$.m.in4.s_addr = htonl($3); + $$.a.in4.s_addr &= $$.m.in4.s_addr; + $$.v = 4; + } + | hostname IPNY_MASK ipaddr { if ($1.v != $3.v) { + yyerror("2.address family " + "mismatch"); + } + $$.a = $1.a; + $$.m = $3.a; + $$.a.i6[0] &= $$.m.i6[0]; + $$.a.i6[1] &= $$.m.i6[1]; + $$.a.i6[2] &= $$.m.i6[2]; + $$.a.i6[3] &= $$.m.i6[3]; + $$.v = $1.v; + yyexpectaddr = 0; + } + | hostname IPNY_MASK hexnumber { $$.a = $1.a; + $$.m.in4.s_addr = htonl($3); + $$.a.in4.s_addr &= $$.m.in4.s_addr; + $$.v = 4; + } ; portstuff: @@ -562,13 +716,40 @@ hexnumber: ; hostname: - YY_STR { $$.s_addr = lookuphost($1); - free($1); - if ($$.s_addr == 0) + YY_STR { i6addr_t addr; + if (gethost($1, &addr, 0) == 0) { + $$.a = addr; + $$.v = 4; + } else + if (gethost($1, &addr, 1) == 0) { + $$.a = addr; + $$.v = 6; + } else { yyerror("Unknown hostname"); + } + if ($$.v != 0) + nat->in_v = $$.v; + free($1); + } + | YY_NUMBER { bzero(&$$.a, sizeof($$.a)); + $$.a.in4.s_addr = htonl($1); + if ($$.a.in4.s_addr != 0) + $$.v = 4; + else + $$.v = nat->in_v; + if ($$.v != 0) + nat->in_v = $$.v; + } + | ipv4 { $$ = $1; + nat->in_v = 4; + } + | YY_IPV6 { $$.a = $1; + $$.v = 6; + nat->in_v = 6; + } + | YY_NUMBER YY_IPV6 { $$.a = $2; + $$.v = 6; } - | YY_NUMBER { $$.s_addr = htonl($1); } - | ipv4 { $$.s_addr = $1.s_addr; } ; compare: @@ -585,13 +766,20 @@ range: | YY_RANGE_IN { $$ = FR_INRANGE; } ; +ipaddr: ipv4 { $$ = $1; } + | YY_IPV6 { $$.a = $1; + $$.v = 6; + } + ; + ipv4: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER { if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) { yyerror("Invalid octet string for IP address"); return 0; } - $$.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7; - $$.s_addr = htonl($$.s_addr); + $$.a.in4.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7; + $$.a.in4.s_addr = htonl($$.a.in4.s_addr); + $$.v = 4; } ; @@ -844,13 +1032,3 @@ void *ptr; } } -static u_32_t lookuphost(name) -char *name; -{ - i6addr_t addr; - - if (gethost(name, &addr, 0) == -1) { - return 0; - } - return addr.in4_addr; -} diff --git a/usr/src/cmd/ipf/tools/lexer.c b/usr/src/cmd/ipf/tools/lexer.c index 39975a55ce..3b74edee90 100644 --- a/usr/src/cmd/ipf/tools/lexer.c +++ b/usr/src/cmd/ipf/tools/lexer.c @@ -1,9 +1,9 @@ /* - * Copyright (C) 2004 by Darren Reed. + * Copyright (C) 2002-2008 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -398,7 +398,7 @@ nextchar: * 0000:0000:0000:0000:0000:0000:0000:0000 */ #ifdef USE_INET6 - if (yyexpectaddr == 1 && isbuilding == 0 && (ishex(c) || c == ':')) { + if (isbuilding == 0 && (ishex(c) || c == ':')) { char ipv6buf[45 + 1], *s, oc; int start; diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index dabf3c5035..c91ad888ce 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -1469,7 +1469,7 @@ PHX_OBJS += phx.o IPF_OBJS += ip_fil_solaris.o fil.o solaris.o ip_state.o ip_frag.o ip_nat.o \ ip_proxy.o ip_auth.o ip_pool.o ip_htable.o ip_lookup.o \ - ip_log.o misc.o + ip_log.o misc.o ip_compat.o ip_nat6.o IBD_OBJS += ibd.o diff --git a/usr/src/uts/common/inet/ipf/fil.c b/usr/src/uts/common/inet/ipf/fil.c index f38a3a23a2..7cdedaaa4b 100644 --- a/usr/src/uts/common/inet/ipf/fil.c +++ b/usr/src/uts/common/inet/ipf/fil.c @@ -222,7 +222,6 @@ static void *fr_ifsync __P((int, int, char *, char *, void *, void *, ipf_stack_t *)); static ipftuneable_t *fr_findtunebyname __P((const char *, ipf_stack_t *)); static ipftuneable_t *fr_findtunebycookie __P((void *, void **, ipf_stack_t *)); -static void ipf_unlinktoken __P((ipftoken_t *, ipf_stack_t *)); /* @@ -696,10 +695,15 @@ fr_info_t *fin; fin->fin_data[0] = *(u_short *)icmp6; + if ((icmp6->icmp6_type & ICMP6_INFOMSG_MASK) != 0) + fin->fin_flx |= FI_ICMPQUERY; + switch (icmp6->icmp6_type) { case ICMP6_ECHO_REPLY : case ICMP6_ECHO_REQUEST : + if (fin->fin_dlen >= 6) + fin->fin_data[1] = icmp6->icmp6_id; minicmpsz = ICMP6ERR_MINPKTLEN - sizeof(ip6_t); break; case ICMP6_DST_UNREACH : @@ -2465,9 +2469,24 @@ ipf_stack_t *ifs; */ fr = fr_checkauth(fin, &pass); if (!out) { - if (fr_checknatin(fin, &pass) == -1) { - RWLOCK_EXIT(&ifs->ifs_ipf_mutex); - goto finished; + switch (fin->fin_v) + { + case 4 : + if (fr_checknatin(fin, &pass) == -1) { + RWLOCK_EXIT(&ifs->ifs_ipf_mutex); + goto finished; + } + break; +#ifdef USE_INET6 + case 6 : + if (fr_checknat6in(fin, &pass) == -1) { + RWLOCK_EXIT(&ifs->ifs_ipf_mutex); + goto finished; + } + break; +#endif + default : + break; } } if (!out) @@ -2491,10 +2510,27 @@ ipf_stack_t *ifs; if (out && FR_ISPASS(pass)) { (void) fr_acctpkt(fin, NULL); - if (fr_checknatout(fin, &pass) == -1) { - RWLOCK_EXIT(&ifs->ifs_ipf_mutex); - goto finished; - } else if ((ifs->ifs_fr_update_ipid != 0) && (v == 4)) { + switch (fin->fin_v) + { + case 4 : + if (fr_checknatout(fin, &pass) == -1) { + RWLOCK_EXIT(&ifs->ifs_ipf_mutex); + goto finished; + } + break; +#ifdef USE_INET6 + case 6 : + if (fr_checknat6out(fin, &pass) == -1) { + RWLOCK_EXIT(&ifs->ifs_ipf_mutex); + goto finished; + } + break; +#endif + default : + break; + } + + if ((ifs->ifs_fr_update_ipid != 0) && (v == 4)) { if (fr_updateipid(fin) == -1) { IPF_BUMP(ifs->ifs_frstats[1].fr_ipud); pass &= ~FR_CMDMASK; @@ -5405,15 +5441,14 @@ int type; } else if (obj.ipfo_size != fr_objbytes[type][1]) return EINVAL; #else - if (obj.ipfo_rev != IPFILTER_VERSION) - /* XXX compatibility hook here */ - ; - if ((fr_objbytes[type][0] & 1) != 0) { - if (obj.ipfo_size < fr_objbytes[type][1]) - /* XXX compatibility hook here */ - return EINVAL; - } else if (obj.ipfo_size != fr_objbytes[type][1]) - /* XXX compatibility hook here */ + if (obj.ipfo_rev != IPFILTER_VERSION) { + error = fr_incomptrans(&obj, ptr); + return error; + } + + if ((fr_objbytes[type][0] & 1) != 0 && + obj.ipfo_size < fr_objbytes[type][1] || + obj.ipfo_size != fr_objbytes[type][1]) return EINVAL; #endif @@ -5465,8 +5500,8 @@ int type, sz; return EINVAL; #else if (obj.ipfo_rev != IPFILTER_VERSION) - /* XXX compatibility hook here */ - ; + /*XXX compatibility hook here */ + /*EMPTY*/; if (obj.ipfo_size != sz) /* XXX compatibility hook here */ return EINVAL; @@ -5515,7 +5550,7 @@ int type, sz; #else if (obj.ipfo_rev != IPFILTER_VERSION) /* XXX compatibility hook here */ - ; + /*EMPTY*/; if (obj.ipfo_size != sz) /* XXX compatibility hook here */ return EINVAL; @@ -5560,15 +5595,14 @@ int type; } else if (obj.ipfo_size != fr_objbytes[type][1]) return EINVAL; #else - if (obj.ipfo_rev != IPFILTER_VERSION) - /* XXX compatibility hook here */ - ; - if ((fr_objbytes[type][0] & 1) != 0) { - if (obj.ipfo_size < fr_objbytes[type][1]) - /* XXX compatibility hook here */ - return EINVAL; - } else if (obj.ipfo_size != fr_objbytes[type][1]) - /* XXX compatibility hook here */ + if (obj.ipfo_rev != IPFILTER_VERSION) { + error = fr_outcomptrans(&obj, ptr); + return error; + } + + if ((fr_objbytes[type][0] & 1) != 0 && + obj.ipfo_size < fr_objbytes[type][1] || + obj.ipfo_size != fr_objbytes[type][1]) return EINVAL; #endif diff --git a/usr/src/uts/common/inet/ipf/ip_compat.c b/usr/src/uts/common/inet/ipf/ip_compat.c new file mode 100644 index 0000000000..8d75fadda0 --- /dev/null +++ b/usr/src/uts/common/inet/ipf/ip_compat.c @@ -0,0 +1,310 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#if defined(KERNEL) || defined(_KERNEL) +# undef KERNEL +# undef _KERNEL +# define KERNEL 1 +# define _KERNEL 1 +#endif +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#if defined(__NetBSD__) +# if (NetBSD >= 199905) && !defined(IPFILTER_LKM) && defined(_KERNEL) +# include "opt_ipfilter_log.h" +# endif +#endif +#if defined(_KERNEL) && defined(__FreeBSD_version) && \ + (__FreeBSD_version >= 220000) +# if (__FreeBSD_version >= 400000) +# if !defined(IPFILTER_LKM) +# include "opt_inet6.h" +# endif +# if (__FreeBSD_version == 400019) +# define CSUM_DELAY_DATA +# endif +# endif +# include <sys/filio.h> +#else +# include <sys/ioctl.h> +#endif +#if !defined(_AIX51) +# include <sys/fcntl.h> +#endif +#if defined(_KERNEL) +# include <sys/systm.h> +# include <sys/file.h> +#else +# include <stdio.h> +# include <string.h> +# include <stdlib.h> +# include <stddef.h> +# include <sys/file.h> +# define _KERNEL +# ifdef __OpenBSD__ +struct file; +# endif +# include <sys/uio.h> +# undef _KERNEL +#endif +#if !defined(__SVR4) && !defined(__svr4__) && !defined(__hpux) && \ + !defined(linux) +# include <sys/mbuf.h> +#else +# if !defined(linux) +# include <sys/byteorder.h> +# endif +# if (SOLARIS2 < 5) && defined(sun) +# include <sys/dditypes.h> +# endif +#endif +#ifdef __hpux +# define _NET_ROUTE_INCLUDED +#endif +#if !defined(linux) +# include <sys/protosw.h> +#endif +#include <sys/socket.h> +#include <net/if.h> +#ifdef sun +# include <net/af.h> +#endif +#if !defined(_KERNEL) && defined(__FreeBSD__) +# include "radix_ipf.h" +#endif +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#if !defined(linux) +# include <netinet/ip_var.h> +#endif +#if defined(__sgi) && defined(IFF_DRVRLOCK) /* IRIX 6 */ +# include <sys/hashing.h> +# include <netinet/in_var.h> +#endif +#include <netinet/tcp.h> +#if (!defined(__sgi) && !defined(AIX)) || defined(_KERNEL) +# include <netinet/udp.h> +# include <netinet/ip_icmp.h> +#endif +#ifdef __hpux +# undef _NET_ROUTE_INCLUDED +#endif +#include "netinet/ip_compat.h" +#ifdef USE_INET6 +# include <netinet/icmp6.h> +# if !SOLARIS && defined(_KERNEL) && !defined(__osf__) && !defined(__hpux) +# include <netinet6/in6_var.h> +# endif +#endif +#include <netinet/tcpip.h> +#include "netinet/ip_fil.h" +#include "netinet/ip_nat.h" +#include "netinet/ip_frag.h" +#include "netinet/ip_state.h" +#include "netinet/ip_proxy.h" +#include "netinet/ip_auth.h" +#include "netinet/ipf_stack.h" +#ifdef IPFILTER_SCAN +# include "netinet/ip_scan.h" +#endif +#ifdef IPFILTER_SYNC +# include "netinet/ip_sync.h" +#endif +#include "netinet/ip_pool.h" +#include "netinet/ip_htable.h" +#ifdef IPFILTER_COMPILED +# include "netinet/ip_rules.h" +#endif +#if defined(IPFILTER_BPF) && defined(_KERNEL) +# include <net/bpf.h> +#endif +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) +# include <sys/malloc.h> +# if defined(_KERNEL) && !defined(IPFILTER_LKM) +# include "opt_ipfilter.h" +# endif +#endif +#include "netinet/ipl.h" +/* END OF INCLUDES */ + +#ifdef IPFILTER_COMPAT + +# define IPFILTER_VERSION_4010900 4010900 + +struct nat_4010900 { + ipfmutex_t nat_lock; + struct nat *nat_next; + struct nat **nat_pnext; + struct nat *nat_hnext[2]; + struct nat **nat_phnext[2]; + struct hostmap *nat_hm; + void *nat_data; + struct nat **nat_me; + struct ipstate *nat_state; + struct ap_session *nat_aps; /* proxy session */ + frentry_t *nat_fr; /* filter rule ptr if appropriate */ + struct ipnat *nat_ptr; /* pointer back to the rule */ + void *nat_ifps[2]; + void *nat_sync; + ipftqent_t nat_tqe; + u_32_t nat_flags; + u_32_t nat_sumd[2]; /* ip checksum delta for data segment */ + u_32_t nat_ipsumd; /* ip checksum delta for ip header */ + u_32_t nat_mssclamp; /* if != zero clamp MSS to this */ + i6addr_t nat_inip6; + i6addr_t nat_outip6; + i6addr_t nat_oip6; /* other ip */ + U_QUAD_T nat_pkts[2]; + U_QUAD_T nat_bytes[2]; + union { + udpinfo_t nat_unu; + tcpinfo_t nat_unt; + icmpinfo_t nat_uni; + greinfo_t nat_ugre; + } nat_un; + u_short nat_oport; /* other port */ + u_short nat_use; + u_char nat_p; /* protocol for NAT */ + int nat_dir; + int nat_ref; /* reference count */ + int nat_hv[2]; + char nat_ifnames[2][LIFNAMSIZ]; + int nat_rev; /* 0 = forward, 1 = reverse */ + int nat_redir; +}; + +struct nat_save_4010900 { + void *ipn_next; + struct nat_4010900 ipn_nat; + struct ipnat ipn_ipnat; + struct frentry ipn_fr; + int ipn_dsize; + char ipn_data[4]; +}; + +struct natlookup_4010900 { + struct in_addr nlc_inip; + struct in_addr nlc_outip; + struct in_addr nlc_realip; + int nlc_flags; + u_short nlc_inport; + u_short nlc_outport; + u_short nlc_realport; +}; + + +/* ------------------------------------------------------------------------ */ +/* Function: fr_incomptrans */ +/* Returns: int - 0 = success, else failure */ +/* Parameters: obj(I) - pointer to ioctl data */ +/* ptr(I) - pointer to store real data in */ +/* */ +/* Translate the copied in ipfobj_t to new for backward compatibility at */ +/* the ABI for user land. */ +/* ------------------------------------------------------------------------ */ +int fr_incomptrans(obj, ptr) +ipfobj_t *obj; +void *ptr; +{ + int error; + natlookup_t *nlp; + nat_save_t *nsp; + struct nat_save_4010900 nsc; + struct natlookup_4010900 nlc; + + switch (obj->ipfo_type) + { + case IPFOBJ_NATLOOKUP : + if ((obj->ipfo_rev != IPFILTER_VERSION_4010900) || + (obj->ipfo_size != sizeof (nlc))) + return EINVAL; + error = COPYIN((caddr_t)obj->ipfo_ptr, (caddr_t)&nlc, + obj->ipfo_size); + if (!error) { + nlp = (natlookup_t *)ptr; + bzero((char *)nlp, sizeof (*nlp)); + nlp->nl_inip = nlc.nlc_inip; + nlp->nl_outip = nlc.nlc_outip; + nlp->nl_inport = nlc.nlc_inport; + nlp->nl_outport = nlc.nlc_outport; + nlp->nl_flags = nlc.nlc_flags; + nlp->nl_v = 4; + } + break; + case IPFOBJ_NATSAVE : + if ((obj->ipfo_rev != IPFILTER_VERSION_4010900) || + (obj->ipfo_size != sizeof (nsc))) + return EINVAL; + error = COPYIN((caddr_t)obj->ipfo_ptr, (caddr_t)&nsc, + obj->ipfo_size); + if (!error) { + nsp = (nat_save_t *)ptr; + bzero((char *)nsp, sizeof (*nsp)); + nsp->ipn_next = nsc.ipn_next; + nsp->ipn_dsize = nsc.ipn_dsize; + nsp->ipn_nat.nat_inip = nsc.ipn_nat.nat_inip; + nsp->ipn_nat.nat_outip = nsc.ipn_nat.nat_outip; + nsp->ipn_nat.nat_oip = nsc.ipn_nat.nat_oip; + nsp->ipn_nat.nat_inport = nsc.ipn_nat.nat_inport; + nsp->ipn_nat.nat_outport = nsc.ipn_nat.nat_outport; + nsp->ipn_nat.nat_oport = nsc.ipn_nat.nat_oport; + nsp->ipn_nat.nat_flags = nsc.ipn_nat.nat_flags; + nsp->ipn_nat.nat_v = 4; + } + break; + default : + return EINVAL; + } + return error; +} + +/* ------------------------------------------------------------------------ */ +/* Function: fr_outcomptrans */ +/* Returns: int - 0 = success, else failure */ +/* Parameters: obj(I) - pointer to ioctl data */ +/* ptr(I) - pointer to store real data in */ +/* */ +/* Translate the copied out ipfobj_t to new definition for backward */ +/* compatibility at the ABI for user land. */ +/* ------------------------------------------------------------------------ */ +int fr_outcomptrans(obj, ptr) +ipfobj_t *obj; +void *ptr; +{ + int error; + natlookup_t *nlp; + struct natlookup_4010900 nlc; + + switch (obj->ipfo_type) + { + case IPFOBJ_NATLOOKUP : + if ((obj->ipfo_rev != IPFILTER_VERSION_4010900) || + (obj->ipfo_size != sizeof (nlc))) + return EINVAL; + bzero((char *)&nlc, sizeof (nlc)); + nlp = (natlookup_t *)ptr; + nlc.nlc_inip = nlp->nl_inip; + nlc.nlc_outip = nlp->nl_outip; + nlc.nlc_realip = nlp->nl_realip; + nlc.nlc_inport = nlp->nl_inport; + nlc.nlc_outport = nlp->nl_outport; + nlc.nlc_realport = nlp->nl_realport; + nlc.nlc_flags = nlp->nl_flags; + error = COPYOUT((caddr_t)&nlc, (caddr_t)obj->ipfo_ptr, + obj->ipfo_size); + break; + default : + return EINVAL; + } + return error; +} + +#endif /* IPFILTER_COMPAT */ diff --git a/usr/src/uts/common/inet/ipf/ip_fil_solaris.c b/usr/src/uts/common/inet/ipf/ip_fil_solaris.c index 36f374d586..ede4e0d639 100644 --- a/usr/src/uts/common/inet/ipf/ip_fil_solaris.c +++ b/usr/src/uts/common/inet/ipf/ip_fil_solaris.c @@ -733,8 +733,8 @@ int *rp; WRITE_ENTER(&ifs->ifs_ipf_global); frsync(IPFSYNC_RESYNC, 0, NULL, NULL, ifs); - fr_natifpsync(IPFSYNC_RESYNC, NULL, NULL, ifs); - fr_nataddrsync(NULL, NULL, ifs); + fr_natifpsync(IPFSYNC_RESYNC, 0, NULL, NULL, ifs); + fr_nataddrsync(0, NULL, NULL, ifs); fr_statesync(IPFSYNC_RESYNC, 0, NULL, NULL, ifs); error = 0; } @@ -1010,8 +1010,8 @@ fr_info_t *fin; if (fin->fin_v == 6) { ip6 = (ip6_t *)m->b_rptr; ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow; - ip6->ip6_src = fin->fin_dst6; - ip6->ip6_dst = fin->fin_src6; + ip6->ip6_src = fin->fin_dst6.in6; + ip6->ip6_dst = fin->fin_src6.in6; ip6->ip6_plen = htons(sizeof(*tcp)); ip6->ip6_nxt = IPPROTO_TCP; tcp2->th_sum = fr_cksum(m, (ip_t *)ip6, IPPROTO_TCP, tcp2); @@ -1224,7 +1224,7 @@ int dst; return -1; } } else - dst6 = fin->fin_dst6; + dst6 = fin->fin_dst6.in6; csz = sz; sz -= sizeof(ip6_t); @@ -1233,7 +1233,7 @@ int dst; ip6->ip6_plen = htons((u_short)sz); ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_src = dst6; - ip6->ip6_dst = fin->fin_src6; + ip6->ip6_dst = fin->fin_src6.in6; sz -= offsetof(struct icmp, icmp_ip); bcopy((char *)mb->b_rptr, (char *)&icmp->icmp_ip, sz); icmp->icmp_cksum = csz - sizeof(ip6_t); @@ -2034,8 +2034,8 @@ int ipf_nic_event_v4(hook_event_token_t event, hook_data_t info, { case NE_PLUMB : frsync(IPFSYNC_NEWIFP, 4, (void *)hn->hne_nic, hn->hne_data, - ifs); - fr_natifpsync(IPFSYNC_NEWIFP, (void *)hn->hne_nic, + ifs); + fr_natifpsync(IPFSYNC_NEWIFP, 4, (void *)hn->hne_nic, hn->hne_data, ifs); fr_statesync(IPFSYNC_NEWIFP, 4, (void *)hn->hne_nic, hn->hne_data, ifs); @@ -2043,7 +2043,8 @@ int ipf_nic_event_v4(hook_event_token_t event, hook_data_t info, case NE_UNPLUMB : frsync(IPFSYNC_OLDIFP, 4, (void *)hn->hne_nic, NULL, ifs); - fr_natifpsync(IPFSYNC_OLDIFP, (void *)hn->hne_nic, NULL, ifs); + fr_natifpsync(IPFSYNC_OLDIFP, 4, (void *)hn->hne_nic, NULL, + ifs); fr_statesync(IPFSYNC_OLDIFP, 4, (void *)hn->hne_nic, NULL, ifs); break; @@ -2059,7 +2060,7 @@ int ipf_nic_event_v4(hook_event_token_t event, hook_data_t info, frsync(IPFSYNC_RESYNC, 4, (void *)hn->hne_nic, NULL, ifs); sin = hn->hne_data; - fr_nataddrsync((void *)hn->hne_nic, &sin->sin_addr, + fr_nataddrsync(4, (void *)hn->hne_nic, &sin->sin_addr, ifs); } break; @@ -2084,6 +2085,7 @@ int ipf_nic_event_v4(hook_event_token_t event, hook_data_t info, int ipf_nic_event_v6(hook_event_token_t event, hook_data_t info, netstack_t *ns) { + struct sockaddr_in6 *sin6; hook_nic_event_t *hn; ipf_stack_t *ifs = ns->netstack_ipf; @@ -2092,17 +2094,27 @@ int ipf_nic_event_v6(hook_event_token_t event, hook_data_t info, switch (hn->hne_event) { case NE_PLUMB : - frsync(IPFSYNC_NEWIFP, 6, (void *)hn->hne_nic, hn->hne_data, ifs); + frsync(IPFSYNC_NEWIFP, 6, (void *)hn->hne_nic, + hn->hne_data, ifs); + fr_natifpsync(IPFSYNC_NEWIFP, 6, (void *)hn->hne_nic, + hn->hne_data, ifs); fr_statesync(IPFSYNC_NEWIFP, 6, (void *)hn->hne_nic, hn->hne_data, ifs); break; case NE_UNPLUMB : frsync(IPFSYNC_OLDIFP, 6, (void *)hn->hne_nic, NULL, ifs); + fr_natifpsync(IPFSYNC_OLDIFP, 6, (void *)hn->hne_nic, NULL, + ifs); fr_statesync(IPFSYNC_OLDIFP, 6, (void *)hn->hne_nic, NULL, ifs); break; case NE_ADDRESS_CHANGE : + if (hn->hne_lif == 1) { + sin6 = hn->hne_data; + fr_nataddrsync(6, (void *)hn->hne_nic, &sin6->sin6_addr, + ifs); + } break; default : break; diff --git a/usr/src/uts/common/inet/ipf/ip_nat.c b/usr/src/uts/common/inet/ipf/ip_nat.c index 994a9eb034..2327c01d8c 100644 --- a/usr/src/uts/common/inet/ipf/ip_nat.c +++ b/usr/src/uts/common/inet/ipf/ip_nat.c @@ -145,8 +145,6 @@ static int nat_clearlist __P((ipf_stack_t *)); static void nat_addnat __P((struct ipnat *, ipf_stack_t *)); static void nat_addrdr __P((struct ipnat *, ipf_stack_t *)); static void nat_delete __P((struct nat *, int, ipf_stack_t *)); -static void nat_delrdr __P((struct ipnat *)); -static void nat_delnat __P((struct ipnat *)); static int fr_natgetent __P((caddr_t, ipf_stack_t *)); static int fr_natgetsz __P((caddr_t, ipf_stack_t *)); static int fr_natputent __P((caddr_t, int, ipf_stack_t *)); @@ -158,17 +156,14 @@ static hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr, struct in_addr, struct in_addr, u_32_t, ipf_stack_t *)); static INLINE int nat_icmpquerytype4 __P((int)); -static int nat_siocaddnat __P((ipnat_t *, ipnat_t **, int, - ipf_stack_t *)); -static void nat_siocdelnat __P((ipnat_t *, ipnat_t **, int, - ipf_stack_t *)); +static int nat_ruleaddrinit __P((ipnat_t *)); +static int nat_siocaddnat __P((ipnat_t *, ipnat_t **, int, ipf_stack_t *)); +static void nat_siocdelnat __P((ipnat_t *, ipnat_t **, int, ipf_stack_t *)); static INLINE int nat_icmperrortype4 __P((int)); static INLINE int nat_finalise __P((fr_info_t *, nat_t *, natinfo_t *, tcphdr_t *, nat_t **, int)); static INLINE int nat_resolverule __P((ipnat_t *, ipf_stack_t *)); -static nat_t *fr_natclone __P((fr_info_t *, nat_t *)); static void nat_mssclamp __P((tcphdr_t *, u_32_t, u_short *)); -static INLINE int nat_wildok __P((nat_t *, int, int, int, int)); static int nat_getnext __P((ipftoken_t *, ipfgeniter_t *, ipf_stack_t *)); static int nat_iterator __P((ipftoken_t *, ipfgeniter_t *, ipf_stack_t *)); static int nat_extraflush __P((int, ipf_stack_t *)); @@ -394,7 +389,7 @@ ipf_stack_t *ifs; /* */ /* Removes a redirect rule from the hash table of redirect rules. */ /* ------------------------------------------------------------------------ */ -static void nat_delrdr(n) +void nat_delrdr(n) ipnat_t *n; { if (n->in_rnext) @@ -410,7 +405,7 @@ ipnat_t *n; /* */ /* Removes a NAT map rule from the hash table of NAT map rules. */ /* ------------------------------------------------------------------------ */ -static void nat_delnat(n) +void nat_delnat(n) ipnat_t *n; { if (n->in_mnext != NULL) @@ -479,6 +474,7 @@ ipf_stack_t *ifs; hm->hm_mapip = map; hm->hm_ref = 1; hm->hm_port = port; + hm->hm_v = 4; } return hm; } @@ -795,8 +791,27 @@ ipf_stack_t *ifs; READ_ENTER(&ifs->ifs_ipf_nat); } error = fr_inobj(data, &nl, IPFOBJ_NATLOOKUP); + if (nl.nl_v != 6) + nl.nl_v = 4; if (error == 0) { - if (nat_lookupredir(&nl, ifs) != NULL) { + void *ptr; + + switch (nl.nl_v) + { + case 4: + ptr = nat_lookupredir(&nl, ifs); + break; +#ifdef USE_INET6 + case 6: + ptr = nat6_lookupredir(&nl, ifs); + break; +#endif + default: + ptr = NULL; + break; + } + + if (ptr != NULL) { error = fr_outobj(data, &nl, IPFOBJ_NATLOOKUP); } else { error = ESRCH; @@ -943,6 +958,17 @@ ipf_stack_t *ifs; } else n->in_space = 1; +#ifdef USE_INET6 + if (n->in_v == 6 && (n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0 && + !IP6_ISONES(&n->in_out[1]) && !IP6_ISZERO(&n->in_out[1])) + IP6_ADD(&n->in_out[0], 1, &n->in_next6) + else if (n->in_v == 6 && + (n->in_flags & IPN_SPLIT) && (n->in_redir & NAT_REDIRECT)) + n->in_next6 = n->in_in[0]; + else if (n->in_v == 6) + n->in_next6 = n->in_out[0]; + else +#endif if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) && ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0)) n->in_nip = ntohl(n->in_outip) + 1; @@ -951,6 +977,7 @@ ipf_stack_t *ifs; n->in_nip = ntohl(n->in_inip); else n->in_nip = ntohl(n->in_outip); + if (n->in_redir & NAT_MAP) { n->in_pnext = ntohs(n->in_pmin); /* @@ -1006,11 +1033,35 @@ ipf_stack_t *ifs; if (n->in_redir & NAT_REDIRECT) { n->in_flags &= ~IPN_NOTDST; - nat_addrdr(n, ifs); + switch (n->in_v) + { + case 4 : + nat_addrdr(n, ifs); + break; +#ifdef USE_INET6 + case 6 : + nat6_addrdr(n, ifs); + break; +#endif + default : + break; + } } if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { n->in_flags &= ~IPN_NOTSRC; - nat_addnat(n, ifs); + switch (n->in_v) + { + case 4 : + nat_addnat(n, ifs); + break; +#ifdef USE_INET6 + case 6 : + nat6_addnat(n, ifs); + break; +#endif + default : + break; + } } n = NULL; ifs->ifs_nat_stats.ns_rules++; @@ -1038,14 +1089,14 @@ ipnat_t *n; ipf_stack_t *ifs; { n->in_ifnames[0][LIFNAMSIZ - 1] = '\0'; - n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], 4, ifs); + n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], n->in_v, ifs); n->in_ifnames[1][LIFNAMSIZ - 1] = '\0'; if (n->in_ifnames[1][0] == '\0') { (void) strncpy(n->in_ifnames[1], n->in_ifnames[0], LIFNAMSIZ); n->in_ifps[1] = n->in_ifps[0]; } else { - n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], 4, ifs); + n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], n->in_v, ifs); } if (n->in_plabel[0] != '\0') { @@ -1074,6 +1125,8 @@ ipnat_t *n, **np; int getlock; ipf_stack_t *ifs; { + int i; + if (getlock) { WRITE_ENTER(&ifs->ifs_ipf_nat); } @@ -1084,6 +1137,10 @@ ipf_stack_t *ifs; if (ifs->ifs_nat_list == NULL) { ifs->ifs_nat_masks = 0; ifs->ifs_rdr_masks = 0; + for (i = 0; i < 4; i++) { + ifs->ifs_nat6_masks[i] = 0; + ifs->ifs_rdr6_masks[i] = 0; + } } if (n->in_tqehead[0] != NULL) { @@ -1329,7 +1386,7 @@ finished: /* header adjustment. It is by design see (IP_CKSUM_RECV() macro in ip.c). */ /* */ /* ------------------------------------------------------------------------ */ -static void nat_calc_chksum_diffs(nat) +void nat_calc_chksum_diffs(nat) nat_t *nat; { u_32_t sum_orig = 0; @@ -1338,6 +1395,9 @@ nat_t *nat; u_32_t ipsum_orig = 0; u_32_t ipsum_changed = 0; + if (nat->nat_v != 4 && nat->nat_v != 6) + return; + /* * the switch calculates operands for CALC_SUMD(), * which will compute the partial chksum delta. @@ -1349,16 +1409,26 @@ nat_t *nat; * we are dealing with RDR rule (DST address gets * modified on packet from client) */ - sum_changed = LONG_SUM(ntohl(nat->nat_inip.s_addr)); - sum_orig = LONG_SUM(ntohl(nat->nat_outip.s_addr)); + if (nat->nat_v == 4) { + sum_changed = LONG_SUM(ntohl(nat->nat_inip.s_addr)); + sum_orig = LONG_SUM(ntohl(nat->nat_outip.s_addr)); + } else { + sum_changed = LONG_SUM6(&nat->nat_inip6); + sum_orig = LONG_SUM6(&nat->nat_outip6); + } break; case NAT_OUTBOUND: /* * we are dealing with MAP rule (SRC address gets * modified on packet from client) */ - sum_changed = LONG_SUM(ntohl(nat->nat_outip.s_addr)); - sum_orig = LONG_SUM(ntohl(nat->nat_inip.s_addr)); + if (nat->nat_v == 4) { + sum_changed = LONG_SUM(ntohl(nat->nat_outip.s_addr)); + sum_orig = LONG_SUM(ntohl(nat->nat_inip.s_addr)); + } else { + sum_changed = LONG_SUM6(&nat->nat_outip6); + sum_orig = LONG_SUM6(&nat->nat_inip6); + } break; default: ; break; @@ -1386,24 +1456,34 @@ nat_t *nat; switch (nat->nat_dir) { case NAT_INBOUND: - sum_changed = LONG_SUM( - ntohl(nat->nat_inip.s_addr) + - ntohs(nat->nat_inport) - ); - sum_orig = LONG_SUM( - ntohl(nat->nat_outip.s_addr) + - ntohs(nat->nat_outport) - ); + if (nat->nat_v == 4) { + sum_changed = LONG_SUM( + ntohl(nat->nat_inip.s_addr) + + ntohs(nat->nat_inport)); + sum_orig = LONG_SUM( + ntohl(nat->nat_outip.s_addr) + + ntohs(nat->nat_outport)); + } else { + sum_changed = LONG_SUM6(&nat->nat_inip6) + + ntohs(nat->nat_inport); + sum_orig = LONG_SUM6(&nat->nat_outip6) + + ntohs(nat->nat_outport); + } break; case NAT_OUTBOUND: - sum_changed = LONG_SUM( - ntohl(nat->nat_outip.s_addr) + - ntohs(nat->nat_outport) - ); - sum_orig = LONG_SUM( - ntohl(nat->nat_inip.s_addr) + - ntohs(nat->nat_inport) - ); + if (nat->nat_v == 4) { + sum_changed = LONG_SUM( + ntohl(nat->nat_outip.s_addr) + + ntohs(nat->nat_outport)); + sum_orig = LONG_SUM( + ntohl(nat->nat_inip.s_addr) + + ntohs(nat->nat_inport)); + } else { + sum_changed = LONG_SUM6(&nat->nat_outip6) + + ntohs(nat->nat_outport); + sum_orig = LONG_SUM6(&nat->nat_inip6) + + ntohs(nat->nat_inport); + } break; default: ; break; @@ -1427,27 +1507,28 @@ nat_t *nat; * we may reuse the already computed nat_sumd[0] for IP header chksum * adjustment in case the L4 (TCP/UDP header) is not changed by NAT. */ - if (NAT_HAS_L4_CHANGED(nat)) { - /* - * bad luck, NAT changes also the L4 header, use IP addresses - * to compute chksum adjustment for IP header. - */ - CALC_SUMD(ipsum_orig, ipsum_changed, sumd); - nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); - } - else { - /* - * the NAT does not change L4 hdr -> reuse chksum adjustment - * for IP hdr. - */ - nat->nat_ipsumd = nat->nat_sumd[0]; + if (nat->nat_v == 4) { + if (NAT_HAS_L4_CHANGED(nat)) { + /* + * bad luck, NAT changes also the L4 header, use IP + * addresses to compute chksum adjustment for IP header. + */ + CALC_SUMD(ipsum_orig, ipsum_changed, sumd); + nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); + } else { + /* + * the NAT does not change L4 hdr -> reuse chksum + * adjustment for IP hdr. + */ + nat->nat_ipsumd = nat->nat_sumd[0]; - /* - * if L4 header does not use chkusm - zero out deltas - */ - if (!(nat->nat_flags & IPN_TCPUDPICMP)) { - nat->nat_sumd[0] = 0; - nat->nat_sumd[1] = 0; + /* + * if L4 header does not use chksum - zero out deltas + */ + if (!(nat->nat_flags & IPN_TCPUDP)) { + nat->nat_sumd[0] = 0; + nat->nat_sumd[1] = 0; + } } } @@ -1564,6 +1645,8 @@ ipf_stack_t *ifs; /* * Check that the NAT entry doesn't already exist in the kernel. */ + if (nat->nat_v != 6) + nat->nat_v = 4; bzero((char *)&fin, sizeof(fin)); fin.fin_p = nat->nat_p; fin.fin_ifs = ifs; @@ -1574,8 +1657,25 @@ ipf_stack_t *ifs; if (getlock) { READ_ENTER(&ifs->ifs_ipf_nat); } - n = nat_inlookup(&fin, nat->nat_flags, fin.fin_p, - nat->nat_oip, nat->nat_outip); + + switch (nat->nat_v) + { + case 4: + fin.fin_v = nat->nat_v; + n = nat_inlookup(&fin, nat->nat_flags, fin.fin_p, + nat->nat_oip, nat->nat_outip); + break; +#ifdef USE_INET6 + case 6: + n = nat6_inlookup(&fin, nat->nat_flags, fin.fin_p, + &nat->nat_oip6.in6, &nat->nat_outip6.in6); + break; +#endif + default: + n = NULL; + break; + } + if (getlock) { RWLOCK_EXIT(&ifs->ifs_ipf_nat); } @@ -1590,8 +1690,24 @@ ipf_stack_t *ifs; if (getlock) { READ_ENTER(&ifs->ifs_ipf_nat); } - n = nat_outlookup(&fin, nat->nat_flags, fin.fin_p, - nat->nat_inip, nat->nat_oip); + + switch (nat->nat_v) + { + case 4: + n = nat_outlookup(&fin, nat->nat_flags, fin.fin_p, + nat->nat_inip, nat->nat_oip); + break; +#ifdef USE_INET6 + case 6: + n = nat6_outlookup(&fin, nat->nat_flags, fin.fin_p, + &nat->nat_inip6.in6, &nat->nat_oip6.in6); + break; +#endif + default: + n = NULL; + break; + } + if (getlock) { RWLOCK_EXIT(&ifs->ifs_ipf_nat); } @@ -1696,7 +1812,23 @@ ipf_stack_t *ifs; if (getlock) { WRITE_ENTER(&ifs->ifs_ipf_nat); } - error = nat_insert(nat, nat->nat_rev, ifs); + + nat_calc_chksum_diffs(nat); + + switch (nat->nat_v) + { + case 4 : + error = nat_insert(nat, nat->nat_rev, ifs); + break; +#ifdef USE_INET6 + case 6 : + error = nat6_insert(nat, nat->nat_rev, ifs); + break; +#endif + default : + break; + } + if ((error == 0) && (aps != NULL)) { aps->aps_next = ifs->ifs_ap_sess_list; ifs->ifs_ap_sess_list = aps; @@ -1926,6 +2058,10 @@ ipf_stack_t *ifs; } ifs->ifs_nat_masks = 0; ifs->ifs_rdr_masks = 0; + for (i = 0; i < 4; i++) { + ifs->ifs_nat6_masks[i] = 0; + ifs->ifs_rdr6_masks[i] = 0; + } return i; } @@ -2561,6 +2697,7 @@ int direction; nat->nat_ifps[1] = np->in_ifps[1]; nat->nat_ptr = np; nat->nat_p = fin->fin_p; + nat->nat_v = fin->fin_v; nat->nat_mssclamp = np->in_mssclamp; fr = fin->fin_fr; nat->nat_fr = fr; @@ -3152,6 +3289,9 @@ struct in_addr src , mapdst; hv = NAT_HASH_FN(src.s_addr, hv + sport, ifs->ifs_ipf_nattable_sz); nat = ifs->ifs_nat_table[1][hv]; for (; nat; nat = nat->nat_hnext[1]) { + if (nat->nat_v != 4) + continue; + if (nat->nat_ifps[0] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) continue; @@ -3223,6 +3363,9 @@ find_in_wild_ports: nat = ifs->ifs_nat_table[1][hv]; for (; nat; nat = nat->nat_hnext[1]) { + if (nat->nat_v != 4) + continue; + if (nat->nat_ifps[0] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) continue; @@ -3397,6 +3540,9 @@ struct in_addr src , dst; hv = NAT_HASH_FN(dst.s_addr, hv + dport, ifs->ifs_ipf_nattable_sz); nat = ifs->ifs_nat_table[0][hv]; for (; nat; nat = nat->nat_hnext[0]) { + if (nat->nat_v != 4) + continue; + if (nat->nat_ifps[1] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) continue; @@ -3458,6 +3604,9 @@ find_out_wild_ports: nat = ifs->ifs_nat_table[0][hv]; for (; nat; nat = nat->nat_hnext[0]) { + if (nat->nat_v != 4) + continue; + if (nat->nat_ifps[1] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) continue; @@ -3878,12 +4027,16 @@ u_32_t nflags; int i; ipf_stack_t *ifs = fin->fin_ifs; + if (fin->fin_v == 6) { +#ifdef USE_INET6 + return fr_nat6out(fin, nat, natadd, nflags); +#else + return NULL; +#endif + } + #if SOLARIS && defined(_KERNEL) - net_data_t net_data_p; - if (fin->fin_v == 4) - net_data_p = ifs->ifs_ipf_ipv4; - else - net_data_p = ifs->ifs_ipf_ipv6; + net_data_t net_data_p = ifs->ifs_ipf_ipv4; #endif tcp = NULL; @@ -4198,12 +4351,16 @@ u_32_t nflags; int i; ipf_stack_t *ifs = fin->fin_ifs; + if (fin->fin_v == 6) { +#ifdef USE_INET6 + return fr_nat6in(fin, nat, natadd, nflags); +#else + return NULL; +#endif + } + #if SOLARIS && defined(_KERNEL) - net_data_t net_data_p; - if (fin->fin_v == 4) - net_data_p = ifs->ifs_ipf_ipv4; - else - net_data_p = ifs->ifs_ipf_ipv6; + net_data_t net_data_p = ifs->ifs_ipf_ipv4; #endif tcp = NULL; @@ -4342,6 +4499,7 @@ nat_t *nat; u_int nflags; { icmphdr_t *icmp; + struct icmp6_hdr *icmp6; u_short *csump; tcphdr_t *tcp; udphdr_t *udp; @@ -4384,6 +4542,15 @@ u_int nflags; csump = &icmp->icmp_cksum; } break; + + case IPPROTO_ICMPV6 : + icmp6 = fin->fin_dp; + + if ((nflags & IPN_ICMPQUERY) != 0) { + if (icmp6->icmp6_cksum != 0) + csump = &icmp6->icmp6_cksum; + } + break; } return csump; } @@ -4542,9 +4709,10 @@ ipf_stack_t *ifs; /* matches the one passed in) and change it, recalculating the checksum sum */ /* difference too. */ /* ------------------------------------------------------------------------ */ -void fr_nataddrsync(ifp, addr, ifs) +void fr_nataddrsync(v, ifp, addr, ifs) +int v; void *ifp; -struct in_addr *addr; +void *addr; ipf_stack_t *ifs; { u_32_t sum1, sum2, sumd; @@ -4574,32 +4742,59 @@ ipf_stack_t *ifs; if (((ifp != NULL) && ifp != (nat->nat_ifps[0])) || ((nat->nat_flags & IPN_TCP) != 0)) continue; - if (((np = nat->nat_ptr) == NULL) || - (np->in_nip || (np->in_outmsk != 0xffffffff))) + if ((np = nat->nat_ptr) == NULL) + continue; + if (v == 4 && np->in_v == 4) { + if (np->in_nip || np->in_outmsk != 0xffffffff) + continue; + /* + * Change the map-to address to be the same as + * the new one. + */ + sum1 = nat->nat_outip.s_addr; + nat->nat_outip = *(struct in_addr *)addr; + sum2 = nat->nat_outip.s_addr; + } else if (v == 6 && np->in_v == 6) { + if (!IP6_ISZERO(&np->in_next6.in6) || + !IP6_ISONES(&np->in_out[1].in6)) + continue; + /* + * Change the map-to address to be the same as + * the new one. + */ + nat->nat_outip6.in6 = *(struct in6_addr *)addr; + } else continue; - - /* - * Change the map-to address to be the same as the - * new one. - */ - sum1 = nat->nat_outip.s_addr; - nat->nat_outip = *addr; - sum2 = nat->nat_outip.s_addr; } else if (((ifp == NULL) || (ifp == nat->nat_ifps[0])) && - !(nat->nat_flags & IPN_TCP) && (np = nat->nat_ptr) && - (np->in_outmsk == 0xffffffff) && !np->in_nip) { - struct in_addr in; - - /* - * Change the map-to address to be the same as the - * new one. - */ - sum1 = nat->nat_outip.s_addr; - if (fr_ifpaddr(4, FRI_NORMAL, nat->nat_ifps[0], - &in, NULL, ifs) != -1) - nat->nat_outip = in; - sum2 = nat->nat_outip.s_addr; + !(nat->nat_flags & IPN_TCP) && (np = nat->nat_ptr)) { + if (np->in_v == 4 && (v == 4 || v == 0)) { + struct in_addr in; + if (np->in_outmsk != 0xffffffff || np->in_nip) + continue; + /* + * Change the map-to address to be the same as + * the new one. + */ + sum1 = nat->nat_outip.s_addr; + if (fr_ifpaddr(4, FRI_NORMAL, nat->nat_ifps[0], + &in, NULL, ifs) != -1) + nat->nat_outip = in; + sum2 = nat->nat_outip.s_addr; + } else if (np->in_v == 6 && (v == 6 || v == 0)) { + struct in6_addr in6; + if (!IP6_ISZERO(&np->in_next6.in6) || + !IP6_ISONES(&np->in_out[1].in6)) + continue; + /* + * Change the map-to address to be the same as + * the new one. + */ + if (fr_ifpaddr(6, FRI_NORMAL, nat->nat_ifps[0], + (void *)&in6, NULL, ifs) != -1) + nat->nat_outip6.in6 = in6; + } else + continue; } else { continue; } @@ -4638,8 +4833,8 @@ ipf_stack_t *ifs; /* "name" and for "action == IPFSYNC_OLDIFP", ifp is a pointer for which */ /* there is no longer any interface associated with it. */ /* ------------------------------------------------------------------------ */ -void fr_natifpsync(action, ifp, name, ifs) -int action; +void fr_natifpsync(action, v, ifp, name, ifs) +int action, v; void *ifp; char *name; ipf_stack_t *ifs; @@ -4649,6 +4844,7 @@ ipf_stack_t *ifs; #endif nat_t *nat; ipnat_t *n; + int nv; if (ifs->ifs_fr_running <= 0) return; @@ -4665,34 +4861,42 @@ ipf_stack_t *ifs; { case IPFSYNC_RESYNC : for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) { + nv = (v == 0) ? nat->nat_v : v; + if (nat->nat_v != nv) + continue; if ((ifp == nat->nat_ifps[0]) || (nat->nat_ifps[0] == (void *)-1)) { nat->nat_ifps[0] = - fr_resolvenic(nat->nat_ifnames[0], 4, ifs); + fr_resolvenic(nat->nat_ifnames[0], nv, ifs); } if ((ifp == nat->nat_ifps[1]) || (nat->nat_ifps[1] == (void *)-1)) { nat->nat_ifps[1] = - fr_resolvenic(nat->nat_ifnames[1], 4, ifs); + fr_resolvenic(nat->nat_ifnames[1], nv, ifs); } } for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) { + nv = (v == 0) ? (int)n->in_v : v; + if ((int)n->in_v != nv) + continue; if (n->in_ifps[0] == ifp || n->in_ifps[0] == (void *)-1) { n->in_ifps[0] = - fr_resolvenic(n->in_ifnames[0], 4, ifs); + fr_resolvenic(n->in_ifnames[0], nv, ifs); } if (n->in_ifps[1] == ifp || n->in_ifps[1] == (void *)-1) { n->in_ifps[1] = - fr_resolvenic(n->in_ifnames[1], 4, ifs); + fr_resolvenic(n->in_ifnames[1], nv, ifs); } } break; case IPFSYNC_NEWIFP : for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) { + if (nat->nat_v != v) + continue; if (!strncmp(name, nat->nat_ifnames[0], sizeof(nat->nat_ifnames[0]))) nat->nat_ifps[0] = ifp; @@ -4701,6 +4905,8 @@ ipf_stack_t *ifs; nat->nat_ifps[1] = ifp; } for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) { + if ((int)n->in_v != v) + continue; if (!strncmp(name, n->in_ifnames[0], sizeof(n->in_ifnames[0]))) n->in_ifps[0] = ifp; @@ -4711,12 +4917,16 @@ ipf_stack_t *ifs; break; case IPFSYNC_OLDIFP : for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) { + if (nat->nat_v != v) + continue; if (ifp == nat->nat_ifps[0]) nat->nat_ifps[0] = (void *)-1; if (ifp == nat->nat_ifps[1]) nat->nat_ifps[1] = (void *)-1; } for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) { + if ((int)n->in_v != v) + continue; if (n->in_ifps[0] == ifp) n->in_ifps[0] = (void *)-1; if (n->in_ifps[1] == ifp) @@ -4794,25 +5004,26 @@ ipf_stack_t *ifs; size_t sizes[1]; int types[1]; - natl.nl_inip = nat->nat_inip; - natl.nl_outip = nat->nat_outip; - natl.nl_origip = nat->nat_oip; - natl.nl_bytes[0] = nat->nat_bytes[0]; - natl.nl_bytes[1] = nat->nat_bytes[1]; - natl.nl_pkts[0] = nat->nat_pkts[0]; - natl.nl_pkts[1] = nat->nat_pkts[1]; - natl.nl_origport = nat->nat_oport; - natl.nl_inport = nat->nat_inport; - natl.nl_outport = nat->nat_outport; - natl.nl_p = nat->nat_p; - natl.nl_type = type; - natl.nl_rule = -1; + natl.nlg_inip = nat->nat_inip6; + natl.nlg_outip = nat->nat_outip6; + natl.nlg_origip = nat->nat_oip6; + natl.nlg_bytes[0] = nat->nat_bytes[0]; + natl.nlg_bytes[1] = nat->nat_bytes[1]; + natl.nlg_pkts[0] = nat->nat_pkts[0]; + natl.nlg_pkts[1] = nat->nat_pkts[1]; + natl.nlg_origport = nat->nat_oport; + natl.nlg_inport = nat->nat_inport; + natl.nlg_outport = nat->nat_outport; + natl.nlg_p = nat->nat_p; + natl.nlg_type = type; + natl.nlg_rule = -1; + natl.nlg_v = nat->nat_v; # ifndef LARGE_NAT if (nat->nat_ptr != NULL) { for (rulen = 0, np = ifs->ifs_nat_list; np; np = np->in_next, rulen++) if (np == nat->nat_ptr) { - natl.nl_rule = rulen; + natl.nlg_rule = rulen; break; } } @@ -4925,7 +5136,7 @@ ipf_stack_t *ifs; /* */ /* Create a "duplcate" state table entry from the master. */ /* ------------------------------------------------------------------------ */ -static nat_t *fr_natclone(fin, nat) +nat_t *fr_natclone(fin, nat) fr_info_t *fin; nat_t *nat; { @@ -5004,7 +5215,7 @@ nat_t *nat; /* Use NAT entry and packet direction to determine which combination of */ /* wildcard flags should be used. */ /* ------------------------------------------------------------------------ */ -static INLINE int nat_wildok(nat, sport, dport, flags, dir) +int nat_wildok(nat, sport, dport, flags, dir) nat_t *nat; int sport; int dport; diff --git a/usr/src/uts/common/inet/ipf/ip_nat6.c b/usr/src/uts/common/inet/ipf/ip_nat6.c new file mode 100644 index 0000000000..77fd2c139f --- /dev/null +++ b/usr/src/uts/common/inet/ipf/ip_nat6.c @@ -0,0 +1,2648 @@ +/* + * Copyright (C) 1995-2003 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI"$ + +#if defined(KERNEL) || defined(_KERNEL) +# undef KERNEL +# undef _KERNEL +# define KERNEL 1 +# define _KERNEL 1 +#endif +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/file.h> +#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ + defined(_KERNEL) +# include "opt_ipfilter_log.h" +#endif +#if !defined(_KERNEL) +# include <stdio.h> +# include <string.h> +# include <stdlib.h> +# define _KERNEL +# ifdef __OpenBSD__ +struct file; +# endif +# include <sys/uio.h> +# undef _KERNEL +#endif +#if defined(_KERNEL) && (__FreeBSD_version >= 220000) +# include <sys/filio.h> +# include <sys/fcntl.h> +#else +# include <sys/ioctl.h> +#endif +#if !defined(AIX) +# include <sys/fcntl.h> +#endif +#if !defined(linux) +# include <sys/protosw.h> +#endif +#include <sys/socket.h> +#if defined(_KERNEL) +# include <sys/systm.h> +# if !defined(__SVR4) && !defined(__svr4__) +# include <sys/mbuf.h> +# endif +#endif +#if defined(__SVR4) || defined(__svr4__) +# include <sys/filio.h> +# include <sys/byteorder.h> +# ifdef _KERNEL +# include <sys/dditypes.h> +# endif +# 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> +# if defined(_KERNEL) && !defined(IPFILTER_LKM) +# include "opt_ipfilter.h" +# endif +#endif +#ifdef sun +# include <net/af.h> +#endif +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> + +#ifdef RFC1825 +# include <vpn/md5.h> +# include <vpn/ipsec.h> +extern struct ifnet vpnif; +#endif + +#if !defined(linux) +# include <netinet/ip_var.h> +#endif +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <netinet/ip_icmp.h> +#include "netinet/ip_compat.h" +#include <netinet/tcpip.h> +#include "netinet/ip_fil.h" +#include "netinet/ip_nat.h" +#include "netinet/ip_frag.h" +#include "netinet/ip_state.h" +#include "netinet/ip_proxy.h" +#include "netinet/ipf_stack.h" +#ifdef IPFILTER_SYNC +#include "netinet/ip_sync.h" +#endif +#if (__FreeBSD_version >= 300000) +# include <sys/malloc.h> +#endif +/* END OF INCLUDES */ + +#undef SOCKADDR_IN +#define SOCKADDR_IN struct sockaddr_in + +#if !defined(lint) +static const char rcsid[] = "@(#)$Id: ip_nat6.c,v 1.2 2008/02/14 21:05:50 darrenr Exp $"; +#endif + +static hostmap_t *nat6_hostmap __P((ipnat_t *, i6addr_t *, i6addr_t *, + i6addr_t *, u_32_t, ipf_stack_t *)); +static INLINE int nat6_newmap __P((fr_info_t *, nat_t *, natinfo_t *)); +static INLINE int nat6_newrdr __P((fr_info_t *, nat_t *, natinfo_t *)); +static INLINE int nat6_finalise __P((fr_info_t *, nat_t *, natinfo_t *, + tcphdr_t *, nat_t **, int)); +static void nat6_tabmove __P((nat_t *, ipf_stack_t *)); +static int nat6_match __P((fr_info_t *, ipnat_t *)); +static INLINE int nat_icmpquerytype6 __P((int)); + + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_addrdr */ +/* Returns: Nil */ +/* Parameters: n(I) - pointer to NAT rule to add */ +/* */ +/* Adds a redirect rule to the hash table of redirect rules and the list of */ +/* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ +/* use by redirect rules. */ +/* ------------------------------------------------------------------------ */ +void nat6_addrdr(n, ifs) +ipnat_t *n; +ipf_stack_t *ifs; +{ + ipnat_t **np; + i6addr_t j; + u_int hv; + int k; + + k = count6bits(n->in_out[1].i6); + if ((k >= 0) && (k != 128)) + ifs->ifs_rdr6_masks[k >> 5] |= 1 << (k & 31); + IP6_AND(&n->in_out[0], &n->in_out[1], &j); + hv = NAT_HASH_FN6(&j, 0, ifs->ifs_ipf_rdrrules_sz); + np = ifs->ifs_rdr_rules + hv; + while (*np != NULL) + np = &(*np)->in_rnext; + n->in_rnext = NULL; + n->in_prnext = np; + n->in_hv = hv; + *np = n; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_addnat */ +/* Returns: Nil */ +/* Parameters: n(I) - pointer to NAT rule to add */ +/* */ +/* Adds a NAT map rule to the hash table of rules and the list of loaded */ +/* NAT rules. Updates the bitmask indicating which netmasks are in use by */ +/* redirect rules. */ +/* ------------------------------------------------------------------------ */ +void nat6_addnat(n, ifs) +ipnat_t *n; +ipf_stack_t *ifs; +{ + ipnat_t **np; + i6addr_t j; + u_int hv; + int k; + + k = count6bits(n->in_in[1].i6); + if ((k >= 0) && (k != 128)) + ifs->ifs_nat6_masks[k >> 5] |= 1 << (k & 31); + IP6_AND(&n->in_in[0], &n->in_in[1], &j); + hv = NAT_HASH_FN6(&j, 0, ifs->ifs_ipf_natrules_sz); + np = ifs->ifs_nat_rules + hv; + while (*np != NULL) + np = &(*np)->in_mnext; + n->in_mnext = NULL; + n->in_pmnext = np; + n->in_hv = hv; + *np = n; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_hostmap */ +/* Returns: struct hostmap* - NULL if no hostmap could be created, */ +/* else a pointer to the hostmapping to use */ +/* Parameters: np(I) - pointer to NAT rule */ +/* real(I) - real IP address */ +/* map(I) - mapped IP address */ +/* port(I) - destination port number */ +/* Write Locks: ipf_nat */ +/* */ +/* Check if an ip address has already been allocated for a given mapping */ +/* that is not doing port based translation. If is not yet allocated, then */ +/* create a new entry if a non-NULL NAT rule pointer has been supplied. */ +/* ------------------------------------------------------------------------ */ +static struct hostmap *nat6_hostmap(np, src, dst, map, port, ifs) +ipnat_t *np; +i6addr_t *src, *dst, *map; +u_32_t port; +ipf_stack_t *ifs; +{ + hostmap_t *hm; + u_int hv; + + hv = (src->i6[3] ^ dst->i6[3]); + hv += (src->i6[2] ^ dst->i6[2]); + hv += (src->i6[1] ^ dst->i6[1]); + hv += (src->i6[0] ^ dst->i6[0]); + hv += src->i6[3]; + hv += src->i6[2]; + hv += src->i6[1]; + hv += src->i6[0]; + hv += dst->i6[3]; + hv += dst->i6[2]; + hv += dst->i6[1]; + hv += dst->i6[0]; + hv %= HOSTMAP_SIZE; + for (hm = ifs->ifs_maptable[hv]; hm; hm = hm->hm_next) + if (IP6_EQ(&hm->hm_srcip6, src) && + IP6_EQ(&hm->hm_dstip6, dst) && + ((np == NULL) || (np == hm->hm_ipnat)) && + ((port == 0) || (port == hm->hm_port))) { + hm->hm_ref++; + return hm; + } + + if (np == NULL) + return NULL; + + KMALLOC(hm, hostmap_t *); + if (hm) { + hm->hm_hnext = ifs->ifs_ipf_hm_maplist; + hm->hm_phnext = &ifs->ifs_ipf_hm_maplist; + if (ifs->ifs_ipf_hm_maplist != NULL) + ifs->ifs_ipf_hm_maplist->hm_phnext = &hm->hm_hnext; + ifs->ifs_ipf_hm_maplist = hm; + + hm->hm_next = ifs->ifs_maptable[hv]; + hm->hm_pnext = ifs->ifs_maptable + hv; + if (ifs->ifs_maptable[hv] != NULL) + ifs->ifs_maptable[hv]->hm_pnext = &hm->hm_next; + ifs->ifs_maptable[hv] = hm; + hm->hm_ipnat = np; + hm->hm_src = *src; + hm->hm_dst = *dst; + hm->hm_map = *map; + hm->hm_ref = 1; + hm->hm_port = port; + hm->hm_v = 6; + } + return hm; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_newmap */ +/* Returns: int - -1 == error, 0 == success */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to NAT entry */ +/* ni(I) - pointer to structure with misc. information needed */ +/* to create new NAT entry. */ +/* */ +/* Given an empty NAT structure, populate it with new information about a */ +/* new NAT session, as defined by the matching NAT rule. */ +/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ +/* to the new IP address for the translation. */ +/* ------------------------------------------------------------------------ */ +static INLINE int nat6_newmap(fin, nat, ni) +fr_info_t *fin; +nat_t *nat; +natinfo_t *ni; +{ + u_short st_port, dport, sport, port, sp, dp; + i6addr_t in, st_ip; + hostmap_t *hm; + u_32_t flags; + ipnat_t *np; + nat_t *natl; + int l; + ipf_stack_t *ifs = fin->fin_ifs; + + /* + * If it's an outbound packet which doesn't match any existing + * record, then create a new port + */ + l = 0; + hm = NULL; + np = ni->nai_np; + st_ip = np->in_next6; + st_port = np->in_pnext; + flags = ni->nai_flags; + sport = ni->nai_sport; + dport = ni->nai_dport; + + /* + * Do a loop until we either run out of entries to try or we find + * a NAT mapping that isn't currently being used. This is done + * because the change to the source is not (usually) being fixed. + */ + do { + port = 0; + in = np->in_next6; + if (l == 0) { + /* + * Check to see if there is an existing NAT + * setup for this IP address pair. + */ + hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6, + &in, 0, ifs); + if (hm != NULL) + in = hm->hm_map; + } else if ((l == 1) && (hm != NULL)) { + fr_hostmapdel(&hm); + } + + nat->nat_hm = hm; + + if (IP6_ISONES(&np->in_out[1]) && (np->in_pnext == 0)) { + if (l > 0) + return -1; + } + + if (np->in_redir == NAT_BIMAP && + IP6_EQ(&np->in_in[1], &np->in_out[1])) { + i6addr_t temp; + /* + * map the address block in a 1:1 fashion + */ + temp.i6[0] = fin->fin_src6.i6[0] & + ~np->in_in[1].i6[0]; + temp.i6[1] = fin->fin_src6.i6[1] & + ~np->in_in[1].i6[1]; + temp.i6[2] = fin->fin_src6.i6[2] & + ~np->in_in[1].i6[2]; + temp.i6[3] = fin->fin_src6.i6[3] & + ~np->in_in[1].i6[3]; + in = np->in_out[0]; + IP6_MERGE(&in, &temp, &np->in_in[0]); + +#ifdef NEED_128BIT_MATH + } else if (np->in_redir & NAT_MAPBLK) { + if ((l >= np->in_ppip) || ((l > 0) && + !(flags & IPN_TCPUDP))) + return -1; + /* + * map-block - Calculate destination address. + */ + IP6_MASK(&in, &fin->fin_src6, &np->in_in[1]); + in = ntol(in); + inb = in; + in /= np->in_ippip; + in &= ntohl(~np->in_out[1]); + in += ntohl(np->in_out[0]); + /* + * 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); + } +#endif + + } else if (IP6_ISZERO(&np->in_out[0]) && + IP6_ISONES(&np->in_out[1])) { + /* + * 0/128 - use the interface's IP address. + */ + if ((l > 0) || + fr_ifpaddr(6, FRI_NORMAL, fin->fin_ifp, + (void *)&in, NULL, fin->fin_ifs) == -1) + return -1; + + } else if (IP6_ISZERO(&np->in_out[0]) && + IP6_ISZERO(&np->in_out[1])) { + /* + * 0/0 - use the original source address/port. + */ + if (l > 0) + return -1; + in = fin->fin_src6; + + } else if (!IP6_ISONES(&np->in_out[1]) && + (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) { + IP6_INC(&np->in_next6); + } + + natl = NULL; + + if ((flags & IPN_TCPUDP) && + ((np->in_redir & NAT_MAPBLK) == 0) && + (np->in_flags & IPN_AUTOPORTMAP)) { + /*EMPTY*/; +#ifdef NEED_128BIT_MATH + /* + * XXX "ports auto" (without map-block) + */ + if ((l > 0) && (l % np->in_ppip == 0)) { + if (l > np->in_space) { + return -1; + } else if ((l > np->in_ppip) && + !IP6_ISONES(&np->in_out[1])) { + IP6_INC(&np->in_next6); + } + } + if (np->in_ppip != 0) { + port = ntohs(sport); + port += (l % np->in_ppip); + port %= np->in_ppip; + port += np->in_ppip * + (ntohl(fin->fin_src6) % + np->in_ippip); + port += MAPBLK_MINPORT; + port = htons(port); + } +#endif + + } else if (((np->in_redir & NAT_MAPBLK) == 0) && + (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) { + /* + * Standard port translation. Select next port. + */ + port = htons(np->in_pnext++); + + if (np->in_pnext > ntohs(np->in_pmax)) { + np->in_pnext = ntohs(np->in_pmin); + if (!IP6_ISONES(&np->in_out[1])) { + IP6_INC(&np->in_next6); + } + } + } + + if (np->in_flags & IPN_IPRANGE) { + if (IP6_GT(&np->in_next6, &np->in_out[1])) + np->in_next6 = np->in_out[0]; + } else { + i6addr_t a1, a2; + + a1 = np->in_next6; + IP6_INC(&a1); + IP6_AND(&a1, &np->in_out[1], &a2); + if (!IP6_ISONES(&np->in_out[1]) && + IP6_GT(&a2, &np->in_out[0])) { + IP6_ADD(&np->in_out[0], 1, &np->in_next6); + } + } + + if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) + 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. + */ + sp = fin->fin_data[0]; + dp = fin->fin_data[1]; + fin->fin_data[0] = fin->fin_data[1]; + fin->fin_data[1] = htons(port); + natl = nat6_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), + (u_int)fin->fin_p, &fin->fin_dst6.in6, &in.in6); + fin->fin_data[0] = sp; + fin->fin_data[1] = dp; + + /* + * Has the search wrapped around and come back to the + * start ? + */ + if ((natl != NULL) && + (np->in_pnext != 0) && (st_port == np->in_pnext) && + !IP6_ISZERO(&np->in_next6) && + IP6_EQ(&st_ip, &np->in_next6)) + return -1; + l++; + } while (natl != NULL); + + if (np->in_space > 0) + np->in_space--; + + /* Setup the NAT table */ + nat->nat_inip6 = fin->fin_src6; + nat->nat_outip6 = in; + nat->nat_oip6 = fin->fin_dst6; + if (nat->nat_hm == NULL) + nat->nat_hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6, + &nat->nat_outip6, 0, ifs); + + if (flags & IPN_TCPUDP) { + nat->nat_inport = sport; + nat->nat_outport = port; /* sport */ + nat->nat_oport = dport; + ((tcphdr_t *)fin->fin_dp)->th_sport = port; + } else if (flags & IPN_ICMPQUERY) { + ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = port; + nat->nat_inport = port; + nat->nat_outport = port; + } + + ni->nai_port = port; + ni->nai_nport = dport; + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_newrdr */ +/* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ +/* allow rule to be moved if IPN_ROUNDR is set. */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to NAT entry */ +/* ni(I) - pointer to structure with misc. information needed */ +/* to create new NAT entry. */ +/* */ +/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ +/* to the new IP address for the translation. */ +/* ------------------------------------------------------------------------ */ +static INLINE int nat6_newrdr(fin, nat, ni) +fr_info_t *fin; +nat_t *nat; +natinfo_t *ni; +{ + u_short nport, dport, sport; + i6addr_t in; + u_short sp, dp; + hostmap_t *hm; + u_32_t flags; + ipnat_t *np; + nat_t *natl; + int move; + ipf_stack_t *ifs = fin->fin_ifs; + + move = 1; + hm = NULL; + in.i6[0] = 0; + in.i6[1] = 0; + in.i6[2] = 0; + in.i6[3] = 0; + np = ni->nai_np; + flags = ni->nai_flags; + sport = ni->nai_sport; + dport = ni->nai_dport; + + /* + * If the matching rule has IPN_STICKY set, then we want to have the + * same rule kick in as before. Why would this happen? If you have + * a collection of rdr rules with "round-robin sticky", the current + * packet might match a different one to the previous connection but + * we want the same destination to be used. + */ + if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == + (IPN_ROUNDR|IPN_STICKY)) { + hm = nat6_hostmap(NULL, &fin->fin_src6, &fin->fin_dst6, &in, + (u_32_t)dport, ifs); + if (hm != NULL) { + in = hm->hm_map; + np = hm->hm_ipnat; + ni->nai_np = np; + move = 0; + } + } + + /* + * 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 = np->in_next6; + + if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { + hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6, + &in, (u_32_t)dport, ifs); + if (hm != NULL) { + in = hm->hm_map; + move = 0; + } + } + + if (hm == NULL || hm->hm_ref == 1) { + if (IP6_EQ(&np->in_in[0], &in)) { + np->in_next6 = np->in_in[1]; + move = 0; + } else { + np->in_next6 = np->in_in[0]; + } + } + + } else if (IP6_ISZERO(&np->in_in[0]) && + IP6_ISONES(&np->in_in[1])) { + /* + * 0/128 - use the interface's IP address. + */ + if (fr_ifpaddr(6, FRI_NORMAL, fin->fin_ifp, (void *)&in, NULL, + fin->fin_ifs) == -1) + return -1; + + } else if (IP6_ISZERO(&np->in_in[0]) && + IP6_ISZERO(&np->in_in[1])) { + /* + * 0/0 - use the original destination address/port. + */ + in = fin->fin_dst6; + + } else if (np->in_redir == NAT_BIMAP && + IP6_EQ(&np->in_in[1], &np->in_out[1])) { + i6addr_t temp; + /* + * map the address block in a 1:1 fashion + */ + temp.i6[0] = fin->fin_dst6.i6[0] & ~np->in_in[1].i6[0]; + temp.i6[1] = fin->fin_dst6.i6[1] & ~np->in_in[1].i6[1]; + temp.i6[2] = fin->fin_dst6.i6[2] & ~np->in_in[1].i6[2]; + temp.i6[3] = fin->fin_dst6.i6[3] & ~np->in_in[1].i6[3]; + in = np->in_in[0]; + IP6_MERGE(&in, &temp, &np->in_in[1]); + } else { + in = np->in_in[0]; + } + + if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) + nport = dport; + else { + /* + * Whilst not optimized for the case where + * pmin == pmax, the gain is not significant. + */ + if (((np->in_flags & IPN_FIXEDDPORT) == 0) && + (np->in_pmin != np->in_pmax)) { + nport = ntohs(dport) - ntohs(np->in_pmin) + + ntohs(np->in_pnext); + nport = htons(nport); + } else + nport = np->in_pnext; + } + + /* + * 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 (IP6_ISZERO(&in)) { + if (nport == dport) + return -1; + in = fin->fin_dst6; + } + + /* + * Check to see if this redirect mapping already exists and if + * it does, return "failure" (allowing it to be created will just + * cause one or both of these "connections" to stop working.) + */ + sp = fin->fin_data[0]; + dp = fin->fin_data[1]; + fin->fin_data[1] = fin->fin_data[0]; + fin->fin_data[0] = ntohs(nport); + natl = nat6_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), + (u_int)fin->fin_p, &in.in6, &fin->fin_src6.in6); + fin->fin_data[0] = sp; + fin->fin_data[1] = dp; + if (natl != NULL) + return -1; + + nat->nat_inip6 = in; + nat->nat_outip6 = fin->fin_dst6; + nat->nat_oip6 = fin->fin_src6; + if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) + nat->nat_hm = nat6_hostmap(np, &fin->fin_src6, + &fin->fin_dst6, &in, (u_32_t)dport, ifs); + + ni->nai_nport = nport; + ni->nai_port = sport; + + if (flags & IPN_TCPUDP) { + nat->nat_inport = nport; + nat->nat_outport = dport; + nat->nat_oport = sport; + ((tcphdr_t *)fin->fin_dp)->th_dport = nport; + } else if (flags & IPN_ICMPQUERY) { + ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = nport; + nat->nat_inport = nport; + nat->nat_outport = nport; + } + + return move; +} + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_new */ +/* Returns: nat_t* - NULL == failure to create new NAT structure, */ +/* else pointer to new NAT structure */ +/* Parameters: fin(I) - pointer to packet information */ +/* np(I) - pointer to NAT rule */ +/* natsave(I) - pointer to where to store NAT struct pointer */ +/* flags(I) - flags describing the current packet */ +/* direction(I) - direction of packet (in/out) */ +/* Write Lock: ipf_nat */ +/* */ +/* Attempts to create a new NAT entry. Does not actually change the packet */ +/* in any way. */ +/* */ +/* This fucntion is in three main parts: (1) deal with creating a new NAT */ +/* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ +/* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ +/* and (3) building that structure and putting it into the NAT table(s). */ +/* ------------------------------------------------------------------------ */ +nat_t *nat6_new(fin, np, natsave, flags, direction) +fr_info_t *fin; +ipnat_t *np; +nat_t **natsave; +u_int flags; +int direction; +{ + tcphdr_t *tcp = NULL; + hostmap_t *hm = NULL; + nat_t *nat, *natl; + u_int nflags; + natinfo_t ni; + int move; + ipf_stack_t *ifs = fin->fin_ifs; + + /* + * Trigger automatic call to nat_extraflush() if the + * table has reached capcity specified by hi watermark. + */ + if (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_lvl_hi) + ifs->ifs_nat_doflush = 1; + + if (ifs->ifs_nat_stats.ns_inuse >= ifs->ifs_ipf_nattable_max) { + ifs->ifs_nat_stats.ns_memfail++; + return NULL; + } + + move = 1; + nflags = np->in_flags & flags; + nflags &= NAT_FROMRULE; + + ni.nai_np = np; + ni.nai_nflags = nflags; + ni.nai_flags = flags; + + /* Give me a new nat */ + KMALLOC(nat, nat_t *); + if (nat == NULL) { + ifs->ifs_nat_stats.ns_memfail++; + /* + * Try to automatically tune the max # of entries in the + * table allowed to be less than what will cause kmem_alloc() + * to fail and try to eliminate panics due to out of memory + * conditions arising. + */ + if (ifs->ifs_ipf_nattable_max > ifs->ifs_ipf_nattable_sz) { + ifs->ifs_ipf_nattable_max = + ifs->ifs_nat_stats.ns_inuse - 100; + printf("ipf_nattable_max reduced to %d\n", + ifs->ifs_ipf_nattable_max); + } + return NULL; + } + + if (flags & IPN_TCPUDP) { + tcp = fin->fin_dp; + ni.nai_sport = htons(fin->fin_sport); + ni.nai_dport = htons(fin->fin_dport); + } else if (flags & IPN_ICMPQUERY) { + /* + * In the ICMP query NAT code, we translate the ICMP id fields + * to make them unique. This is indepedent of the ICMP type + * (e.g. in the unlikely event that a host sends an echo and + * an tstamp request with the same id, both packets will have + * their ip address/id field changed in the same way). + * + * The icmp_id field is used by the sender to identify the + * process making the icmp request. (the receiver justs + * copies it back in its response). So, it closely matches + * the concept of source port. We overlay sport, so we can + * maximally reuse the existing code. + */ + ni.nai_sport = ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id; + ni.nai_dport = ni.nai_sport; + } + + bzero((char *)nat, sizeof (*nat)); + nat->nat_flags = flags; + nat->nat_redir = np->in_redir; + + if ((flags & NAT_SLAVE) == 0) { + MUTEX_ENTER(&ifs->ifs_ipf_nat_new); + } + + /* + * Search the current table for a match. + */ + if (direction == NAT_OUTBOUND) { + /* + * We can now arrange to call this for the same connection + * because ipf_nat_new doesn't protect the code path into + * this function. + */ + natl = nat6_outlookup(fin, nflags, (u_int)fin->fin_p, + &fin->fin_src6.in6, &fin->fin_dst6.in6); + if (natl != NULL) { + KFREE(nat); + nat = natl; + goto done; + } + + move = nat6_newmap(fin, nat, &ni); + if (move == -1) + goto badnat; + + np = ni.nai_np; + } else { + /* + * NAT_INBOUND is used only for redirects rules + */ + natl = nat6_inlookup(fin, nflags, (u_int)fin->fin_p, + &fin->fin_src6.in6, &fin->fin_dst6.in6); + if (natl != NULL) { + KFREE(nat); + nat = natl; + goto done; + } + + move = nat6_newrdr(fin, nat, &ni); + if (move == -1) + goto badnat; + + np = ni.nai_np; + } + + if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { + if (np->in_redir == NAT_REDIRECT) { + nat_delrdr(np); + nat6_addrdr(np, ifs); + } else if (np->in_redir == NAT_MAP) { + nat_delnat(np); + nat6_addnat(np, ifs); + } + } + + if (nat6_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) { + goto badnat; + } + + nat_calc_chksum_diffs(nat); + + if (flags & SI_WILDP) + ifs->ifs_nat_stats.ns_wilds++; + goto done; +badnat: + ifs->ifs_nat_stats.ns_badnat++; + if ((hm = nat->nat_hm) != NULL) + fr_hostmapdel(&hm); + KFREE(nat); + nat = NULL; +done: + if ((flags & NAT_SLAVE) == 0) { + MUTEX_EXIT(&ifs->ifs_ipf_nat_new); + } + return nat; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_finalise */ +/* Returns: int - 0 == sucess, -1 == failure */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to NAT entry */ +/* ni(I) - pointer to structure with misc. information needed */ +/* to create new NAT entry. */ +/* Write Lock: ipf_nat */ +/* */ +/* This is the tail end of constructing a new NAT entry and is the same */ +/* for both IPv4 and IPv6. */ +/* ------------------------------------------------------------------------ */ +/*ARGSUSED*/ +static INLINE int nat6_finalise(fin, nat, ni, tcp, natsave, direction) +fr_info_t *fin; +nat_t *nat; +natinfo_t *ni; +tcphdr_t *tcp; +nat_t **natsave; +int direction; +{ + frentry_t *fr; + ipnat_t *np; + ipf_stack_t *ifs = fin->fin_ifs; + + np = ni->nai_np; + + COPYIFNAME(fin->fin_ifp, nat->nat_ifnames[0], fin->fin_v); + +#ifdef IPFILTER_SYNC + if ((nat->nat_flags & SI_CLONE) == 0) + nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat); +#endif + + nat->nat_me = natsave; + nat->nat_dir = direction; + nat->nat_ifps[0] = np->in_ifps[0]; + nat->nat_ifps[1] = np->in_ifps[1]; + nat->nat_ptr = np; + nat->nat_p = fin->fin_p; + nat->nat_v = fin->fin_v; + nat->nat_mssclamp = np->in_mssclamp; + fr = fin->fin_fr; + nat->nat_fr = fr; + nat->nat_v = 6; + +#ifdef IPF_V6_PROXIES + if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0)) + if (appr_new(fin, nat) == -1) + return -1; +#endif + + if (nat6_insert(nat, fin->fin_rev, ifs) == 0) { + if (ifs->ifs_nat_logging) + nat_log(nat, (u_int)np->in_redir, ifs); + np->in_use++; + if (fr != NULL) { + MUTEX_ENTER(&fr->fr_lock); + fr->fr_ref++; + MUTEX_EXIT(&fr->fr_lock); + } + return 0; + } + + /* + * nat6_insert failed, so cleanup time... + */ + return -1; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_insert */ +/* Returns: int - 0 == sucess, -1 == failure */ +/* Parameters: nat(I) - pointer to NAT structure */ +/* rev(I) - flag indicating forward/reverse direction of packet */ +/* Write Lock: ipf_nat */ +/* */ +/* Insert a NAT entry into the hash tables for searching and add it to the */ +/* list of active NAT entries. Adjust global counters when complete. */ +/* ------------------------------------------------------------------------ */ +int nat6_insert(nat, rev, ifs) +nat_t *nat; +int rev; +ipf_stack_t *ifs; +{ + u_int hv1, hv2; + nat_t **natp; + + /* + * Try and return an error as early as possible, so calculate the hash + * entry numbers first and then proceed. + */ + if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { + hv1 = NAT_HASH_FN6(&nat->nat_inip6, nat->nat_inport, + 0xffffffff); + hv1 = NAT_HASH_FN6(&nat->nat_oip6, hv1 + nat->nat_oport, + ifs->ifs_ipf_nattable_sz); + hv2 = NAT_HASH_FN6(&nat->nat_outip6, nat->nat_outport, + 0xffffffff); + hv2 = NAT_HASH_FN6(&nat->nat_oip6, hv2 + nat->nat_oport, + ifs->ifs_ipf_nattable_sz); + } else { + hv1 = NAT_HASH_FN6(&nat->nat_inip6, 0, 0xffffffff); + hv1 = NAT_HASH_FN6(&nat->nat_oip6, hv1, + ifs->ifs_ipf_nattable_sz); + hv2 = NAT_HASH_FN6(&nat->nat_outip6, 0, 0xffffffff); + hv2 = NAT_HASH_FN6(&nat->nat_oip6, hv2, + ifs->ifs_ipf_nattable_sz); + } + + if ((ifs->ifs_nat_stats.ns_bucketlen[0][hv1] >= + ifs->ifs_fr_nat_maxbucket) || + (ifs->ifs_nat_stats.ns_bucketlen[1][hv2] >= + ifs->ifs_fr_nat_maxbucket)) { + return -1; + } + + nat->nat_hv[0] = hv1; + nat->nat_hv[1] = hv2; + + MUTEX_INIT(&nat->nat_lock, "nat entry lock"); + + nat->nat_rev = rev; + nat->nat_ref = 1; + nat->nat_bytes[0] = 0; + nat->nat_pkts[0] = 0; + nat->nat_bytes[1] = 0; + nat->nat_pkts[1] = 0; + + nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; + nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 6, ifs); + + if (nat->nat_ifnames[1][0] !='\0') { + nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; + nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 6, ifs); + } else { + (void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0], + LIFNAMSIZ); + nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; + nat->nat_ifps[1] = nat->nat_ifps[0]; + } + + nat->nat_next = ifs->ifs_nat_instances; + nat->nat_pnext = &ifs->ifs_nat_instances; + if (ifs->ifs_nat_instances) + ifs->ifs_nat_instances->nat_pnext = &nat->nat_next; + ifs->ifs_nat_instances = nat; + + natp = &ifs->ifs_nat_table[0][hv1]; + if (*natp) + (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; + nat->nat_phnext[0] = natp; + nat->nat_hnext[0] = *natp; + *natp = nat; + ifs->ifs_nat_stats.ns_bucketlen[0][hv1]++; + + natp = &ifs->ifs_nat_table[1][hv2]; + if (*natp) + (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; + nat->nat_phnext[1] = natp; + nat->nat_hnext[1] = *natp; + *natp = nat; + ifs->ifs_nat_stats.ns_bucketlen[1][hv2]++; + + fr_setnatqueue(nat, rev, ifs); + + ifs->ifs_nat_stats.ns_added++; + ifs->ifs_nat_stats.ns_inuse++; + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_icmperrorlookup */ +/* Returns: nat_t* - point to matching NAT structure */ +/* Parameters: fin(I) - pointer to packet information */ +/* dir(I) - direction of packet (in/out) */ +/* */ +/* Check if the ICMP error message is related to an existing TCP, UDP or */ +/* ICMP query nat entry. It is assumed that the packet is already of the */ +/* the required length. */ +/* ------------------------------------------------------------------------ */ +nat_t *nat6_icmperrorlookup(fin, dir) +fr_info_t *fin; +int dir; +{ + int flags = 0, minlen; + struct icmp6_hdr *orgicmp; + tcphdr_t *tcp = NULL; + u_short data[2]; + nat_t *nat; + ip6_t *oip6; + u_int p; + + minlen = 40; + /* + * Does it at least have the return (basic) IP header ? + * Only a basic IP header (no options) should be with an ICMP error + * header. Also, if it's not an error type, then return. + */ + if (!(fin->fin_flx & FI_ICMPERR)) + return NULL; + + /* + * Check packet size + */ + if (fin->fin_plen < ICMP6ERR_IPICMPHLEN) + return NULL; + oip6 = (ip6_t *)((char *)fin->fin_dp + 8); + + /* + * 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; + + m = fin->fin_m; +# if defined(MENTAT) + if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) + return NULL; +# else + if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > + (char *)fin->fin_ip + M_LEN(m)) + return NULL; +# endif + } +#endif + + if (IP6_NEQ(&fin->fin_dst6, &oip6->ip6_src)) + return NULL; + + p = oip6->ip6_nxt; + if (p == IPPROTO_TCP) + flags = IPN_TCP; + else if (p == IPPROTO_UDP) + flags = IPN_UDP; + else if (p == IPPROTO_ICMPV6) { + orgicmp = (struct icmp6_hdr *)(oip6 + 1); + + /* see if this is related to an ICMP query */ + if (nat_icmpquerytype6(orgicmp->icmp6_type)) { + data[0] = fin->fin_data[0]; + data[1] = fin->fin_data[1]; + fin->fin_data[0] = 0; + fin->fin_data[1] = orgicmp->icmp6_id; + + flags = IPN_ICMPERR|IPN_ICMPQUERY; + /* + * NOTE : dir refers to the direction of the original + * ip packet. By definition the icmp error + * message flows in the opposite direction. + */ + if (dir == NAT_INBOUND) + nat = nat6_inlookup(fin, flags, p, + &oip6->ip6_dst, &oip6->ip6_src); + else + nat = nat6_outlookup(fin, flags, p, + &oip6->ip6_dst, &oip6->ip6_src); + fin->fin_data[0] = data[0]; + fin->fin_data[1] = data[1]; + return nat; + } + } + + if (flags & IPN_TCPUDP) { + minlen += 8; /* + 64bits of data to get ports */ + if (fin->fin_plen < ICMPERR_ICMPHLEN + minlen) + return NULL; + + data[0] = fin->fin_data[0]; + data[1] = fin->fin_data[1]; + tcp = (tcphdr_t *)(oip6 + 1); + fin->fin_data[0] = ntohs(tcp->th_dport); + fin->fin_data[1] = ntohs(tcp->th_sport); + + if (dir == NAT_INBOUND) { + nat = nat6_inlookup(fin, flags, p, + &oip6->ip6_dst, &oip6->ip6_src); + } else { + nat = nat6_outlookup(fin, flags, p, + &oip6->ip6_dst, &oip6->ip6_src); + } + fin->fin_data[0] = data[0]; + fin->fin_data[1] = data[1]; + return nat; + } + if (dir == NAT_INBOUND) + return nat6_inlookup(fin, 0, p, &oip6->ip6_dst, &oip6->ip6_src); + else + return nat6_outlookup(fin, 0, p, &oip6->ip6_dst, + &oip6->ip6_src); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_icmperror */ +/* Returns: nat_t* - point to matching NAT structure */ +/* Parameters: fin(I) - pointer to packet information */ +/* nflags(I) - NAT flags for this packet */ +/* dir(I) - direction of packet (in/out) */ +/* */ +/* Fix up an ICMP packet which is an error message for an existing NAT */ +/* session. This will correct both packet header data and checksums. */ +/* */ +/* This should *ONLY* be used for incoming ICMP error packets to make sure */ +/* a NAT'd ICMP packet gets correctly recognised. */ +/* ------------------------------------------------------------------------ */ +nat_t *nat6_icmperror(fin, nflags, dir) +fr_info_t *fin; +u_int *nflags; +int dir; +{ + u_32_t sum1, sum2, sumd, psum1, psum2, psumd, sumd1; + i6addr_t in; + struct icmp6_hdr *icmp6, *orgicmp; + int dlen; + udphdr_t *udp; + tcphdr_t *tcp; + nat_t *nat; + ip6_t *oip6; + if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) + return NULL; + + /* + * nat6_icmperrorlookup() looks up nat entry associated with the + * offending IP packet and returns pointer to the entry, or NULL + * if packet wasn't natted or for `defective' packets. + */ + + if ((fin->fin_v != 6) || !(nat = nat6_icmperrorlookup(fin, dir))) + return NULL; + + sumd1 = 0; + *nflags = IPN_ICMPERR; + icmp6 = fin->fin_dp; + oip6 = (ip6_t *)((char *)icmp6 + sizeof (*icmp6)); + udp = (udphdr_t *)(((char *)oip6) + sizeof (*oip6)); + tcp = (tcphdr_t *)udp; + dlen = fin->fin_plen - ((char *)udp - (char *)fin->fin_ip); + + /* + * Need to adjust ICMP header to include the real IP#'s and + * port #'s. There are three steps required. + * + * Step 1 + * No update needed for ip6 header checksum. + * + * Unlike IPv4, we need to update icmp_cksum for IPv6 address + * changes because there's no ip_sum change to cancel it. + */ + + if (IP6_EQ((i6addr_t *)&oip6->ip6_dst, &nat->nat_oip6)) { + sum1 = LONG_SUM6((i6addr_t *)&oip6->ip6_src); + in = nat->nat_inip6; + oip6->ip6_src = in.in6; + } else { + sum1 = LONG_SUM6((i6addr_t *)&oip6->ip6_dst); + in = nat->nat_outip6; + oip6->ip6_dst = in.in6; + } + + sum2 = LONG_SUM6(&in); + CALC_SUMD(sum1, sum2, sumd); + + /* + * Step 2 + * Perform other adjustments based on protocol of offending packet. + */ + + switch (oip6->ip6_nxt) { + case IPPROTO_TCP : + case IPPROTO_UDP : + + /* + * For offending TCP/UDP IP packets, translate the ports + * based on the NAT specification. + * + * Advance notice : Now it becomes complicated :-) + * + * Since the port and IP addresse fields are both part + * of the TCP/UDP checksum of the offending IP packet, + * we need to adjust that checksum as well. + * + * To further complicate things, the TCP/UDP checksum + * may not be present. We must check to see if the + * length of the data portion is big enough to hold + * the checksum. In the UDP case, a test to determine + * if the checksum is even set is also required. + * + * Any changes to an IP address, port or checksum within + * the ICMP packet requires a change to icmp_cksum. + * + * Be extremely careful here ... The change is dependent + * upon whether or not the TCP/UPD checksum is present. + * + * If TCP/UPD checksum is present, the icmp_cksum must + * compensate for checksum modification resulting from + * IP address change only. Port change and resulting + * data checksum adjustments cancel each other out. + * + * If TCP/UDP checksum is not present, icmp_cksum must + * compensate for port change only. The IP address + * change does not modify anything else in this case. + */ + + psum1 = 0; + psum2 = 0; + psumd = 0; + + if ((tcp->th_dport == nat->nat_oport) && + (tcp->th_sport != nat->nat_inport)) { + + /* + * Translate the source port. + */ + + psum1 = ntohs(tcp->th_sport); + psum2 = ntohs(nat->nat_inport); + tcp->th_sport = nat->nat_inport; + + } else if ((tcp->th_sport == nat->nat_oport) && + (tcp->th_dport != nat->nat_outport)) { + + /* + * Translate the destination port. + */ + + psum1 = ntohs(tcp->th_dport); + psum2 = ntohs(nat->nat_outport); + tcp->th_dport = nat->nat_outport; + } + + if ((oip6->ip6_nxt == IPPROTO_TCP) && (dlen >= 18)) { + + /* + * TCP checksum present. + * + * Adjust data checksum and icmp checksum to + * compensate for any IP address change. + */ + + sum1 = ntohs(tcp->th_sum); + fix_datacksum(&tcp->th_sum, sumd); + sum2 = ntohs(tcp->th_sum); + CALC_SUMD(sum1, sum2, sumd); + sumd1 += sumd; + + /* + * Also make data checksum adjustment to + * compensate for any port change. + */ + + if (psum1 != psum2) { + CALC_SUMD(psum1, psum2, psumd); + fix_datacksum(&tcp->th_sum, psumd); + } + + } else if ((oip6->ip6_nxt == IPPROTO_UDP) && + (dlen >= 8) && (udp->uh_sum != 0)) { + + /* + * The UDP checksum is present and set. + * + * Adjust data checksum and icmp checksum to + * compensate for any IP address change. + */ + + sum1 = ntohs(udp->uh_sum); + fix_datacksum(&udp->uh_sum, sumd); + sum2 = ntohs(udp->uh_sum); + CALC_SUMD(sum1, sum2, sumd); + sumd1 += sumd; + + /* + * Also make data checksum adjustment to + * compensate for any port change. + */ + + if (psum1 != psum2) { + CALC_SUMD(psum1, psum2, psumd); + fix_datacksum(&udp->uh_sum, psumd); + } + + } else { + + /* + * Data checksum was not present. + * + * Compensate for any port change. + */ + + CALC_SUMD(psum2, psum1, psumd); + sumd1 += psumd; + } + break; + + case IPPROTO_ICMPV6 : + + orgicmp = (struct icmp6_hdr *)udp; + + if ((nat->nat_dir == NAT_OUTBOUND) && + (orgicmp->icmp6_id != nat->nat_inport) && + (dlen >= 8)) { + + /* + * Fix ICMP checksum (of the offening ICMP + * query packet) to compensate the change + * in the ICMP id of the offending ICMP + * packet. + * + * Since you modify orgicmp->icmp_id with + * a delta (say x) and you compensate that + * in origicmp->icmp_cksum with a delta + * minus x, you don't have to adjust the + * overall icmp->icmp_cksum + */ + + sum1 = ntohs(orgicmp->icmp6_id); + sum2 = ntohs(nat->nat_inport); + CALC_SUMD(sum1, sum2, sumd); + orgicmp->icmp6_id = nat->nat_inport; + fix_datacksum(&orgicmp->icmp6_cksum, sumd); + + } /* nat_dir can't be NAT_INBOUND for icmp queries */ + + break; + + default : + + break; + + } /* switch (oip6->ip6_nxt) */ + + /* + * Step 3 + * Make the adjustments to icmp checksum. + */ + + if (sumd1 != 0) { + sumd1 = (sumd1 & 0xffff) + (sumd1 >> 16); + sumd1 = (sumd1 & 0xffff) + (sumd1 >> 16); + fix_incksum(&icmp6->icmp6_cksum, sumd1); + } + return nat; +} + + +/* + * NB: these lookups don't lock access to the list, it assumed that it has + * already been done! + */ + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_inlookup */ +/* Returns: nat_t* - NULL == no match, */ +/* else pointer to matching NAT entry */ +/* Parameters: fin(I) - pointer to packet information */ +/* flags(I) - NAT flags for this packet */ +/* p(I) - protocol for this packet */ +/* src(I) - source IP address */ +/* mapdst(I) - destination IP address */ +/* */ +/* 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. */ +/* */ +/* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ +/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ +/* */ +/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ +/* the packet is of said protocol */ +/* ------------------------------------------------------------------------ */ +nat_t *nat6_inlookup(fin, flags, p, src, mapdst) +fr_info_t *fin; +u_int flags, p; +struct in6_addr *src, *mapdst; +{ + u_short sport, dport; + u_int sflags; + nat_t *nat; + int nflags; + i6addr_t dst; + void *ifp; + u_int hv; + ipf_stack_t *ifs = fin->fin_ifs; + + if (fin != NULL) + ifp = fin->fin_ifp; + else + ifp = NULL; + sport = 0; + dport = 0; + dst.in6 = *mapdst; + sflags = flags & NAT_TCPUDPICMP; + + switch (p) + { + case IPPROTO_TCP : + case IPPROTO_UDP : + sport = htons(fin->fin_data[0]); + dport = htons(fin->fin_data[1]); + break; + case IPPROTO_ICMPV6 : + if (flags & IPN_ICMPERR) + sport = fin->fin_data[1]; + else + dport = fin->fin_data[1]; + break; + default : + break; + } + + + if ((flags & SI_WILDP) != 0) + goto find_in_wild_ports; + + hv = NAT_HASH_FN6(&dst, dport, 0xffffffff); + hv = NAT_HASH_FN6(src, hv + sport, ifs->ifs_ipf_nattable_sz); + nat = ifs->ifs_nat_table[1][hv]; + for (; nat; nat = nat->nat_hnext[1]) { + if (nat->nat_v != 6) + continue; + + if (nat->nat_ifps[0] != NULL) { + if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) + continue; + } else if (ifp != NULL) + nat->nat_ifps[0] = ifp; + + nflags = nat->nat_flags; + + if (IP6_EQ(&nat->nat_oip6, src) && + IP6_EQ(&nat->nat_outip6, &dst) && + (((p == 0) && + (sflags == (nat->nat_flags & IPN_TCPUDPICMP))) || + (p == nat->nat_p))) { + switch (p) + { +#if 0 + case IPPROTO_GRE : + if (nat->nat_call[1] != fin->fin_data[0]) + continue; + break; +#endif + case IPPROTO_ICMPV6 : + if ((flags & IPN_ICMPERR) != 0) { + if (nat->nat_outport != sport) + continue; + } else { + if (nat->nat_outport != dport) + continue; + } + break; + case IPPROTO_TCP : + case IPPROTO_UDP : + if (nat->nat_oport != sport) + continue; + if (nat->nat_outport != dport) + continue; + break; + default : + break; + } + +#ifdef IPF_V6_PROXIES + ipn = nat->nat_ptr; + if ((ipn != NULL) && (nat->nat_aps != NULL)) + if (appr_match(fin, nat) != 0) + continue; +#endif + return nat; + } + } + + /* + * So if we didn't find it but there are wildcard members in the hash + * table, go back and look for them. We do this search and update here + * because it is modifying the NAT table and we want to do this only + * for the first packet that matches. The exception, of course, is + * for "dummy" (FI_IGNORE) lookups. + */ +find_in_wild_ports: + if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) + return NULL; + if (ifs->ifs_nat_stats.ns_wilds == 0) + return NULL; + + RWLOCK_EXIT(&ifs->ifs_ipf_nat); + + hv = NAT_HASH_FN6(&dst, 0, 0xffffffff); + hv = NAT_HASH_FN6(src, hv, ifs->ifs_ipf_nattable_sz); + + WRITE_ENTER(&ifs->ifs_ipf_nat); + + nat = ifs->ifs_nat_table[1][hv]; + for (; nat; nat = nat->nat_hnext[1]) { + if (nat->nat_v != 6) + continue; + + if (nat->nat_ifps[0] != NULL) { + if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) + continue; + } else if (ifp != NULL) + nat->nat_ifps[0] = ifp; + + if (nat->nat_p != fin->fin_p) + continue; + if (IP6_NEQ(&nat->nat_oip6, src) || + IP6_NEQ(&nat->nat_outip6, &dst)) + continue; + + nflags = nat->nat_flags; + if (!(nflags & (NAT_TCPUDP|SI_WILDP))) + continue; + + if (nat_wildok(nat, (int)sport, (int)dport, nflags, + NAT_INBOUND) == 1) { + if ((fin->fin_flx & FI_IGNORE) != 0) + break; + if ((nflags & SI_CLONE) != 0) { + nat = fr_natclone(fin, nat); + if (nat == NULL) + break; + } else { + MUTEX_ENTER(&ifs->ifs_ipf_nat_new); + ifs->ifs_nat_stats.ns_wilds--; + MUTEX_EXIT(&ifs->ifs_ipf_nat_new); + } + nat->nat_oport = sport; + nat->nat_outport = dport; + nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); + nat6_tabmove(nat, ifs); + break; + } + } + + MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); + + return nat; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_tabmove */ +/* Returns: Nil */ +/* Parameters: nat(I) - pointer to NAT structure */ +/* Write Lock: ipf_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 nat6_tabmove(nat, ifs) +nat_t *nat; +ipf_stack_t *ifs; +{ + nat_t **natp; + u_int hv; + + if (nat->nat_flags & SI_CLONE) + return; + + /* + * 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]; + ifs->ifs_nat_stats.ns_bucketlen[0][nat->nat_hv[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]; + ifs->ifs_nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--; + + /* + * Add into the NAT table in the new position + */ + hv = NAT_HASH_FN6(&nat->nat_inip6, nat->nat_inport, 0xffffffff); + hv = NAT_HASH_FN6(&nat->nat_oip6, hv + nat->nat_oport, + ifs->ifs_ipf_nattable_sz); + nat->nat_hv[0] = hv; + natp = &ifs->ifs_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; + ifs->ifs_nat_stats.ns_bucketlen[0][hv]++; + + hv = NAT_HASH_FN6(&nat->nat_outip6, nat->nat_outport, 0xffffffff); + hv = NAT_HASH_FN6(&nat->nat_oip6, hv + nat->nat_oport, + ifs->ifs_ipf_nattable_sz); + nat->nat_hv[1] = hv; + natp = &ifs->ifs_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; + ifs->ifs_nat_stats.ns_bucketlen[1][hv]++; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_outlookup */ +/* Returns: nat_t* - NULL == no match, */ +/* else pointer to matching NAT entry */ +/* Parameters: fin(I) - pointer to packet information */ +/* flags(I) - NAT flags for this packet */ +/* p(I) - protocol for this packet */ +/* src(I) - source IP address */ +/* dst(I) - destination IP address */ +/* rw(I) - 1 == write lock on ipf_nat held, 0 == read lock. */ +/* */ +/* 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. */ +/* */ +/* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN */ +/* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ +/* */ +/* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ +/* the packet is of said protocol */ +/* ------------------------------------------------------------------------ */ +nat_t *nat6_outlookup(fin, flags, p, src, dst) +fr_info_t *fin; +u_int flags, p; +struct in6_addr *src , *dst; +{ + u_short sport, dport; + u_int sflags; + nat_t *nat; + int nflags; + void *ifp; + u_int hv; + ipf_stack_t *ifs = fin->fin_ifs; + + ifp = fin->fin_ifp; + + sflags = flags & IPN_TCPUDPICMP; + sport = 0; + dport = 0; + + switch (p) + { + case IPPROTO_TCP : + case IPPROTO_UDP : + sport = htons(fin->fin_data[0]); + dport = htons(fin->fin_data[1]); + break; + case IPPROTO_ICMPV6 : + if (flags & IPN_ICMPERR) + sport = fin->fin_data[1]; + else + dport = fin->fin_data[1]; + break; + default : + break; + } + + if ((flags & SI_WILDP) != 0) + goto find_out_wild_ports; + + hv = NAT_HASH_FN6(src, sport, 0xffffffff); + hv = NAT_HASH_FN6(dst, hv + dport, ifs->ifs_ipf_nattable_sz); + nat = ifs->ifs_nat_table[0][hv]; + for (; nat; nat = nat->nat_hnext[0]) { + if (nat->nat_v != 6) + continue; + + if (nat->nat_ifps[1] != NULL) { + if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) + continue; + } else if (ifp != NULL) + nat->nat_ifps[1] = ifp; + + nflags = nat->nat_flags; + + if (IP6_EQ(&nat->nat_inip6, src) && + IP6_EQ(&nat->nat_oip6, dst) && + (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP))) || + (p == nat->nat_p))) { + switch (p) + { +#if 0 + case IPPROTO_GRE : + if (nat->nat_call[1] != fin->fin_data[0]) + continue; + break; +#endif + case IPPROTO_TCP : + case IPPROTO_UDP : + if (nat->nat_oport != dport) + continue; + if (nat->nat_inport != sport) + continue; + break; + default : + break; + } + +#ifdef IPF_V6_PROXIES + ipn = nat->nat_ptr; + if ((ipn != NULL) && (nat->nat_aps != NULL)) + if (appr_match(fin, nat) != 0) + continue; +#endif + return nat; + } + } + + /* + * So if we didn't find it but there are wildcard members in the hash + * table, go back and look for them. We do this search and update here + * because it is modifying the NAT table and we want to do this only + * for the first packet that matches. The exception, of course, is + * for "dummy" (FI_IGNORE) lookups. + */ +find_out_wild_ports: + if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) + return NULL; + if (ifs->ifs_nat_stats.ns_wilds == 0) + return NULL; + + RWLOCK_EXIT(&ifs->ifs_ipf_nat); + + hv = NAT_HASH_FN6(src, 0, 0xffffffff); + hv = NAT_HASH_FN6(dst, hv, ifs->ifs_ipf_nattable_sz); + + WRITE_ENTER(&ifs->ifs_ipf_nat); + + nat = ifs->ifs_nat_table[0][hv]; + for (; nat; nat = nat->nat_hnext[0]) { + if (nat->nat_v != 6) + continue; + + if (nat->nat_ifps[1] != NULL) { + if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) + continue; + } else if (ifp != NULL) + nat->nat_ifps[1] = ifp; + + if (nat->nat_p != fin->fin_p) + continue; + if (IP6_NEQ(&nat->nat_inip6, src) || + IP6_NEQ(&nat->nat_oip6, dst)) + continue; + + nflags = nat->nat_flags; + if (!(nflags & (NAT_TCPUDP|SI_WILDP))) + continue; + + if (nat_wildok(nat, (int)sport, (int)dport, nflags, + NAT_OUTBOUND) == 1) { + if ((fin->fin_flx & FI_IGNORE) != 0) + break; + if ((nflags & SI_CLONE) != 0) { + nat = fr_natclone(fin, nat); + if (nat == NULL) + break; + } else { + MUTEX_ENTER(&ifs->ifs_ipf_nat_new); + ifs->ifs_nat_stats.ns_wilds--; + MUTEX_EXIT(&ifs->ifs_ipf_nat_new); + } + nat->nat_inport = sport; + nat->nat_oport = dport; + if (nat->nat_outport == 0) + nat->nat_outport = sport; + nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); + nat6_tabmove(nat, ifs); + break; + } + } + + MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); + + return nat; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_lookupredir */ +/* Returns: nat_t* - NULL == no match, */ +/* else pointer to matching NAT entry */ +/* Parameters: np(I) - pointer to description of packet to find NAT table */ +/* entry for. */ +/* */ +/* Lookup the NAT tables to search for a matching redirect */ +/* ------------------------------------------------------------------------ */ +nat_t *nat6_lookupredir(np, ifs) +natlookup_t *np; +ipf_stack_t *ifs; +{ + fr_info_t fi; + nat_t *nat; + + bzero((char *)&fi, sizeof (fi)); + if (np->nl_flags & IPN_IN) { + fi.fin_data[0] = ntohs(np->nl_realport); + fi.fin_data[1] = ntohs(np->nl_outport); + } else { + fi.fin_data[0] = ntohs(np->nl_inport); + fi.fin_data[1] = ntohs(np->nl_outport); + } + if (np->nl_flags & IPN_TCP) + fi.fin_p = IPPROTO_TCP; + else if (np->nl_flags & IPN_UDP) + fi.fin_p = IPPROTO_UDP; + else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) + fi.fin_p = IPPROTO_ICMPV6; + + fi.fin_ifs = ifs; + /* + * We can do two sorts of lookups: + * - IPN_IN: we have the `real' and `out' address, look for `in'. + * - default: we have the `in' and `out' address, look for `real'. + */ + if (np->nl_flags & IPN_IN) { + if ((nat = nat6_inlookup(&fi, np->nl_flags, fi.fin_p, + &np->nl_realip6, &np->nl_outip6))) { + np->nl_inipaddr = nat->nat_inip6; + np->nl_inport = nat->nat_inport; + } + } else { + /* + * If nl_inip is non null, this is a lookup based on the real + * ip address. Else, we use the fake. + */ + if ((nat = nat6_outlookup(&fi, np->nl_flags, fi.fin_p, + &np->nl_inip6, &np->nl_outip6))) { + if ((np->nl_flags & IPN_FINDFORWARD) != 0) { + fr_info_t fin; + bzero((char *)&fin, sizeof (fin)); + fin.fin_p = nat->nat_p; + fin.fin_data[0] = ntohs(nat->nat_outport); + fin.fin_data[1] = ntohs(nat->nat_oport); + fin.fin_ifs = ifs; + if (nat6_inlookup(&fin, np->nl_flags, fin.fin_p, + &nat->nat_outip6.in6, + &nat->nat_oip6.in6) != NULL) { + np->nl_flags &= ~IPN_FINDFORWARD; + } + } + + np->nl_realip6 = nat->nat_outip6.in6; + np->nl_realport = nat->nat_outport; + } + } + + return nat; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat6_match */ +/* Returns: int - 0 == no match, 1 == match */ +/* Parameters: fin(I) - pointer to packet information */ +/* np(I) - pointer to NAT rule */ +/* */ +/* Pull the matching of a packet against a NAT rule out of that complex */ +/* loop inside fr_checknat6in() and lay it out properly in its own function.*/ +/* ------------------------------------------------------------------------ */ +static int nat6_match(fin, np) +fr_info_t *fin; +ipnat_t *np; +{ + frtuc_t *ft; + + if (fin->fin_v != 6) + return 0; + + if (np->in_p && fin->fin_p != np->in_p) + return 0; + + if (fin->fin_out) { + if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) + return 0; + if (IP6_MASKNEQ(&fin->fin_src6, &np->in_in[1], &np->in_in[0]) + ^ ((np->in_flags & IPN_NOTSRC) != 0)) + return 0; + if (IP6_MASKNEQ(&fin->fin_dst6, &np->in_src[1], &np->in_src[0]) + ^ ((np->in_flags & IPN_NOTDST) != 0)) + return 0; + } else { + if (!(np->in_redir & NAT_REDIRECT)) + return 0; + if (IP6_MASKNEQ(&fin->fin_src6, &np->in_src[1], &np->in_src[0]) + ^ ((np->in_flags & IPN_NOTSRC) != 0)) + return 0; + if (IP6_MASKNEQ(&fin->fin_dst6, &np->in_out[1], &np->in_out[0]) + ^ ((np->in_flags & IPN_NOTDST) != 0)) + return 0; + } + + ft = &np->in_tuc; + if (!(fin->fin_flx & FI_TCPUDP) || + (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { + if (ft->ftu_scmp || ft->ftu_dcmp) + return 0; + return 1; + } + + return fr_tcpudpchk(fin, ft); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: fr_checknat6out */ +/* Returns: int - -1 == packet failed NAT checks so block it, */ +/* 0 == no packet translation occurred, */ +/* 1 == packet was successfully translated. */ +/* Parameters: fin(I) - pointer to packet information */ +/* passp(I) - pointer to filtering result flags */ +/* */ +/* Check to see if an outcoming packet should be changed. ICMP packets are */ +/* first checked to see if they match an existing entry (if an error), */ +/* otherwise a search of the current NAT table is made. If neither results */ +/* in a match then a search for a matching NAT rule is made. Create a new */ +/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ +/* packet header(s) as required. */ +/* ------------------------------------------------------------------------ */ +int fr_checknat6out(fin, passp) +fr_info_t *fin; +u_32_t *passp; +{ + struct ifnet *ifp, *sifp; + int rval, natfailed; + ipnat_t *np = NULL; + u_int nflags = 0; + i6addr_t ipa, iph; + int natadd = 1; + frentry_t *fr; + nat_t *nat; + ipf_stack_t *ifs = fin->fin_ifs; + + if (ifs->ifs_nat_stats.ns_rules == 0 || ifs->ifs_fr_nat_lock != 0) + return 0; + + natfailed = 0; + fr = fin->fin_fr; + sifp = fin->fin_ifp; + if ((fr != NULL) && !(fr->fr_flags & FR_DUP) && + fr->fr_tifs[fin->fin_rev].fd_ifp && + fr->fr_tifs[fin->fin_rev].fd_ifp != (void *)-1) + fin->fin_ifp = fr->fr_tifs[fin->fin_rev].fd_ifp; + ifp = fin->fin_ifp; + + if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { + switch (fin->fin_p) + { + case IPPROTO_TCP : + nflags = IPN_TCP; + break; + case IPPROTO_UDP : + nflags = IPN_UDP; + break; + case IPPROTO_ICMPV6 : + /* + * This is an incoming packet, so the destination is + * the icmp6_id and the source port equals 0 + */ + if ((fin->fin_flx & FI_ICMPQUERY) != 0) + nflags = IPN_ICMPQUERY; + break; + default : + break; + } + +#ifdef IPF_V6_PROXIES + if ((nflags & IPN_TCPUDP)) + tcp = fin->fin_dp; +#endif + } + + ipa = fin->fin_src6; + + READ_ENTER(&ifs->ifs_ipf_nat); + + if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) && + (nat = nat6_icmperror(fin, &nflags, NAT_OUTBOUND))) + /*EMPTY*/; + else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) + natadd = 0; + else if ((nat = nat6_outlookup(fin, nflags|NAT_SEARCH, + (u_int)fin->fin_p, &fin->fin_src6.in6, + &fin->fin_dst6.in6))) { + nflags = nat->nat_flags; + } else { + u_32_t hv, nmsk; + i6addr_t msk; + int i; + + /* + * If there is no current entry in the nat table for this IP#, + * create one for it (if there is a matching rule). + */ + RWLOCK_EXIT(&ifs->ifs_ipf_nat); + i = 3; + msk.i6[0] = 0xffffffff; + msk.i6[1] = 0xffffffff; + msk.i6[2] = 0xffffffff; + msk.i6[3] = 0xffffffff; + nmsk = ifs->ifs_nat6_masks[3]; + WRITE_ENTER(&ifs->ifs_ipf_nat); +maskloop: + IP6_AND(&ipa, &msk, &iph); + hv = NAT_HASH_FN6(&iph, 0, ifs->ifs_ipf_natrules_sz); + for (np = ifs->ifs_nat_rules[hv]; np; np = np->in_mnext) + { + if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) + continue; + if (np->in_v != 6) + continue; + if (np->in_p && (np->in_p != fin->fin_p)) + continue; + if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) + continue; + if (np->in_flags & IPN_FILTER) { + if (!nat6_match(fin, np)) + continue; + } else if (!IP6_MASKEQ(&ipa, &np->in_in[1], + &np->in_in[0])) + continue; + + if ((fr != NULL) && + !fr_matchtag(&np->in_tag, &fr->fr_nattag)) + continue; + +#ifdef IPF_V6_PROXIES + if (*np->in_plabel != '\0') { + if (((np->in_flags & IPN_FILTER) == 0) && + (np->in_dport != tcp->th_dport)) + continue; + if (appr_ok(fin, tcp, np) == 0) + continue; + } +#endif + + if (nat = nat6_new(fin, np, NULL, nflags, + NAT_OUTBOUND)) { + np->in_hits++; + break; + } else + natfailed = -1; + } + if ((np == NULL) && (i >= 0)) { + while (i >= 0) { + while (nmsk) { + msk.i6[i] = htonl(ntohl(msk.i6[i])<<1); + if ((nmsk & 0x80000000) != 0) { + nmsk <<= 1; + goto maskloop; + } + nmsk <<= 1; + } + msk.i6[i--] = 0; + if (i >= 0) { + nmsk = ifs->ifs_nat6_masks[i]; + if (nmsk != 0) + goto maskloop; + } + } + } + MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); + } + + if (nat != NULL) { + rval = fr_nat6out(fin, nat, natadd, nflags); + if (rval == 1) { + MUTEX_ENTER(&nat->nat_lock); + nat->nat_ref++; + MUTEX_EXIT(&nat->nat_lock); + nat->nat_touched = ifs->ifs_fr_ticks; + fin->fin_nat = nat; + } + } else + rval = natfailed; + RWLOCK_EXIT(&ifs->ifs_ipf_nat); + + if (rval == -1) { + if (passp != NULL) + *passp = FR_BLOCK; + fin->fin_flx |= FI_BADNAT; + } + fin->fin_ifp = sifp; + return rval; +} + +/* ------------------------------------------------------------------------ */ +/* Function: fr_nat6out */ +/* Returns: int - -1 == packet failed NAT checks so block it, */ +/* 1 == packet was successfully translated. */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to NAT structure */ +/* natadd(I) - flag indicating if it is safe to add frag cache */ +/* nflags(I) - NAT flags set for this packet */ +/* */ +/* Translate a packet coming "out" on an interface. */ +/* ------------------------------------------------------------------------ */ +int fr_nat6out(fin, nat, natadd, nflags) +fr_info_t *fin; +nat_t *nat; +int natadd; +u_32_t nflags; +{ + struct icmp6_hdr *icmp6; + u_short *csump; + tcphdr_t *tcp; + ipnat_t *np; + int i; + ipf_stack_t *ifs = fin->fin_ifs; + +#if SOLARIS && defined(_KERNEL) + net_data_t net_data_p = ifs->ifs_ipf_ipv6; +#endif + + tcp = NULL; + icmp6 = NULL; + csump = NULL; + np = nat->nat_ptr; + + if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) + (void) fr_nat_newfrag(fin, 0, nat); + + MUTEX_ENTER(&nat->nat_lock); + nat->nat_bytes[1] += fin->fin_plen; + nat->nat_pkts[1]++; + MUTEX_EXIT(&nat->nat_lock); + + if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { + if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) { + tcp = fin->fin_dp; + + tcp->th_sport = nat->nat_outport; + fin->fin_data[0] = ntohs(nat->nat_outport); + } + + if ((nat->nat_outport != 0) && (nflags & IPN_ICMPQUERY)) { + icmp6 = fin->fin_dp; + icmp6->icmp6_id = nat->nat_outport; + } + + csump = nat_proto(fin, nat, nflags); + } + + fin->fin_ip6->ip6_src = nat->nat_outip6.in6; + fin->fin_src6 = nat->nat_outip6; + + nat_update(fin, nat, np); + + /* + * TCP/UDP/ICMPv6 checksum needs to be adjusted. + */ + if (csump != NULL && (!(nflags & IPN_TCPUDP) || + !NET_IS_HCK_L4_FULL(net_data_p, fin->fin_m))) { + if (nflags & IPN_TCPUDP && + NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) { + if (nat->nat_dir == NAT_OUTBOUND) + fix_outcksum(csump, nat->nat_sumd[1]); + else + fix_incksum(csump, nat->nat_sumd[1]); + } else { + if (nat->nat_dir == NAT_OUTBOUND) + fix_outcksum(csump, nat->nat_sumd[0]); + else + fix_incksum(csump, nat->nat_sumd[0]); + } + } +#ifdef IPFILTER_SYNC + ipfsync_update(SMC_NAT, fin, nat->nat_sync); +#endif + /* ------------------------------------------------------------- */ + /* A few quick notes: */ + /* Following are test conditions prior to calling the */ + /* appr_check routine. */ + /* */ + /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ + /* with a redirect rule, we attempt to match the packet's */ + /* source port against in_dport, otherwise we'd compare the */ + /* packet's destination. */ + /* ------------------------------------------------------------- */ + if ((np != NULL) && (np->in_apr != NULL)) { + i = appr_check(fin, nat); + if (i == 0) + i = 1; + } else + i = 1; + ATOMIC_INCL(ifs->ifs_nat_stats.ns_mapped[1]); + fin->fin_flx |= FI_NATED; + return i; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: fr_checknat6in */ +/* Returns: int - -1 == packet failed NAT checks so block it, */ +/* 0 == no packet translation occurred, */ +/* 1 == packet was successfully translated. */ +/* Parameters: fin(I) - pointer to packet information */ +/* passp(I) - pointer to filtering result flags */ +/* */ +/* Check to see if an incoming packet should be changed. ICMP packets are */ +/* first checked to see if they match an existing entry (if an error), */ +/* otherwise a search of the current NAT table is made. If neither results */ +/* in a match then a search for a matching NAT rule is made. Create a new */ +/* NAT entry if a we matched a NAT rule. Lastly, actually change the */ +/* packet header(s) as required. */ +/* ------------------------------------------------------------------------ */ +int fr_checknat6in(fin, passp) +fr_info_t *fin; +u_32_t *passp; +{ + u_int nflags, natadd; + int rval, natfailed; + struct ifnet *ifp; + struct icmp6_hdr *icmp6; + tcphdr_t *tcp; + u_short dport; + ipnat_t *np; + nat_t *nat; + i6addr_t ipa, iph; + ipf_stack_t *ifs = fin->fin_ifs; + + if (ifs->ifs_nat_stats.ns_rules == 0 || ifs->ifs_fr_nat_lock != 0) + return 0; + + tcp = NULL; + icmp6 = NULL; + dport = 0; + natadd = 1; + nflags = 0; + natfailed = 0; + ifp = fin->fin_ifp; + + if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { + switch (fin->fin_p) + { + case IPPROTO_TCP : + nflags = IPN_TCP; + break; + case IPPROTO_UDP : + nflags = IPN_UDP; + break; + case IPPROTO_ICMPV6 : + icmp6 = fin->fin_dp; + + /* + * This is an incoming packet, so the destination is + * the icmp_id and the source port equals 0 + */ + if ((fin->fin_flx & FI_ICMPQUERY) != 0) { + nflags = IPN_ICMPQUERY; + dport = icmp6->icmp6_id; + } break; + default : + break; + } + + if ((nflags & IPN_TCPUDP)) { + tcp = fin->fin_dp; + dport = tcp->th_dport; + } + } + + ipa = fin->fin_dst6; + + READ_ENTER(&ifs->ifs_ipf_nat); + + if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) && + (nat = nat6_icmperror(fin, &nflags, NAT_INBOUND))) + /*EMPTY*/; + else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin))) + natadd = 0; + else if ((nat = nat6_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, + &fin->fin_src6.in6, &ipa.in6))) { + nflags = nat->nat_flags; + } else { + u_32_t hv, rmsk; + i6addr_t msk; + int i; + + RWLOCK_EXIT(&ifs->ifs_ipf_nat); + i = 3; + msk.i6[0] = 0xffffffff; + msk.i6[1] = 0xffffffff; + msk.i6[2] = 0xffffffff; + msk.i6[3] = 0xffffffff; + rmsk = ifs->ifs_rdr6_masks[3]; + WRITE_ENTER(&ifs->ifs_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). + */ +maskloop: + IP6_AND(&ipa, &msk, &iph); + hv = NAT_HASH_FN6(&iph, 0, ifs->ifs_ipf_rdrrules_sz); + for (np = ifs->ifs_rdr_rules[hv]; np; np = np->in_rnext) { + if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) + continue; + if (np->in_v != fin->fin_v) + continue; + if (np->in_p && (np->in_p != fin->fin_p)) + continue; + if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) + continue; + if (np->in_flags & IPN_FILTER) { + if (!nat6_match(fin, np)) + continue; + } else { + if (!IP6_MASKEQ(&ipa, &np->in_out[1], + &np->in_out[0])) + continue; + if (np->in_pmin && + ((ntohs(np->in_pmax) < ntohs(dport)) || + (ntohs(dport) < ntohs(np->in_pmin)))) + continue; + } + +#ifdef IPF_V6_PROXIES + if (*np->in_plabel != '\0') { + if (!appr_ok(fin, tcp, np)) { + continue; + } + } +#endif + + nat = nat6_new(fin, np, NULL, nflags, NAT_INBOUND); + if (nat != NULL) { + np->in_hits++; + break; + } else + natfailed = -1; + } + + if ((np == NULL) && (i >= 0)) { + while (i >= 0) { + while (rmsk) { + msk.i6[i] = htonl(ntohl(msk.i6[i])<<1); + if ((rmsk & 0x80000000) != 0) { + rmsk <<= 1; + goto maskloop; + } + rmsk <<= 1; + } + msk.i6[i--] = 0; + if (i >= 0) { + rmsk = ifs->ifs_rdr6_masks[i]; + if (rmsk != 0) + goto maskloop; + } + } + } + MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat); + } + if (nat != NULL) { + rval = fr_nat6in(fin, nat, natadd, nflags); + if (rval == 1) { + MUTEX_ENTER(&nat->nat_lock); + nat->nat_ref++; + MUTEX_EXIT(&nat->nat_lock); + nat->nat_touched = ifs->ifs_fr_ticks; + fin->fin_nat = nat; + fin->fin_state = nat->nat_state; + } + } else + rval = natfailed; + RWLOCK_EXIT(&ifs->ifs_ipf_nat); + + if (rval == -1) { + if (passp != NULL) + *passp = FR_BLOCK; + fin->fin_flx |= FI_BADNAT; + } + return rval; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: fr_nat6in */ +/* Returns: int - -1 == packet failed NAT checks so block it, */ +/* 1 == packet was successfully translated. */ +/* Parameters: fin(I) - pointer to packet information */ +/* nat(I) - pointer to NAT structure */ +/* natadd(I) - flag indicating if it is safe to add frag cache */ +/* nflags(I) - NAT flags set for this packet */ +/* Locks Held: ipf_nat (READ) */ +/* */ +/* Translate a packet coming "in" on an interface. */ +/* ------------------------------------------------------------------------ */ +int fr_nat6in(fin, nat, natadd, nflags) +fr_info_t *fin; +nat_t *nat; +int natadd; +u_32_t nflags; +{ + struct icmp6_hdr *icmp6; + u_short *csump; + tcphdr_t *tcp; + ipnat_t *np; + ipf_stack_t *ifs = fin->fin_ifs; + +#if SOLARIS && defined(_KERNEL) + net_data_t net_data_p = ifs->ifs_ipf_ipv6; +#endif + + tcp = NULL; + csump = NULL; + np = nat->nat_ptr; + fin->fin_fr = nat->nat_fr; + + if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) + (void) fr_nat_newfrag(fin, 0, nat); + +#ifdef IPF_V6_PROXIES + if (np != NULL) { + + /* ------------------------------------------------------------- */ + /* A few quick notes: */ + /* Following are test conditions prior to calling the */ + /* appr_check routine. */ + /* */ + /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ + /* with a map rule, we attempt to match the packet's */ + /* source port against in_dport, otherwise we'd compare the */ + /* packet's destination. */ + /* ------------------------------------------------------------- */ + if (np->in_apr != NULL) { + i = appr_check(fin, nat); + if (i == -1) { + return -1; + } + } + } +#endif + +#ifdef IPFILTER_SYNC + ipfsync_update(SMC_NAT, fin, nat->nat_sync); +#endif + + MUTEX_ENTER(&nat->nat_lock); + nat->nat_bytes[0] += fin->fin_plen; + nat->nat_pkts[0]++; + MUTEX_EXIT(&nat->nat_lock); + + fin->fin_ip6->ip6_dst = nat->nat_inip6.in6; + fin->fin_dst6 = nat->nat_inip6; + + if (nflags & IPN_TCPUDP) + tcp = fin->fin_dp; + + if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { + if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) { + tcp->th_dport = nat->nat_inport; + fin->fin_data[1] = ntohs(nat->nat_inport); + } + + + if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) { + icmp6 = fin->fin_dp; + + icmp6->icmp6_id = nat->nat_inport; + } + + csump = nat_proto(fin, nat, nflags); + } + + nat_update(fin, nat, np); + + /* + * In case they are being forwarded, inbound packets always need to have + * their checksum adjusted even if hardware checksum validation said OK. + */ + if (csump != NULL) { + if (nat->nat_dir == NAT_OUTBOUND) + fix_incksum(csump, nat->nat_sumd[0]); + else + fix_outcksum(csump, nat->nat_sumd[0]); + } + +#if SOLARIS && defined(_KERNEL) + if (nflags & IPN_TCPUDP && + NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) { + /* + * Need to adjust the partial checksum result stored in + * db_cksum16, which will be used for validation in IP. + * See IP_CKSUM_RECV(). + * Adjustment data should be the inverse of the IP address + * changes, because db_cksum16 is supposed to be the complement + * of the pesudo header. + */ + csump = &fin->fin_m->b_datap->db_cksum16; + if (nat->nat_dir == NAT_OUTBOUND) + fix_outcksum(csump, nat->nat_sumd[1]); + else + fix_incksum(csump, nat->nat_sumd[1]); + } +#endif + + ATOMIC_INCL(ifs->ifs_nat_stats.ns_mapped[0]); + fin->fin_flx |= FI_NATED; + if (np != NULL && np->in_tag.ipt_num[0] != 0) + fin->fin_nattag = &np->in_tag; + return 1; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: nat_icmpquerytype6 */ +/* Returns: int - 1 == success, 0 == failure */ +/* Parameters: icmptype(I) - ICMP type number */ +/* */ +/* Tests to see if the ICMP type number passed is a query/response type or */ +/* not. */ +/* ------------------------------------------------------------------------ */ +static INLINE int nat_icmpquerytype6(icmptype) +int icmptype; +{ + + /* + * For the ICMP query NAT code, it is essential that both the query + * and the reply match on the NAT rule. Because the NAT structure + * does not keep track of the icmptype, and a single NAT structure + * is used for all icmp types with the same src, dest and id, we + * simply define the replies as queries as well. The funny thing is, + * altough it seems silly to call a reply a query, this is exactly + * as it is defined in the IPv4 specification + */ + + switch (icmptype) + { + + case ICMP6_ECHO_REPLY: + case ICMP6_ECHO_REQUEST: + /* route aedvertisement/solliciation is currently unsupported: */ + /* it would require rewriting the ICMP data section */ + case ICMP6_MEMBERSHIP_QUERY: + case ICMP6_MEMBERSHIP_REPORT: + case ICMP6_MEMBERSHIP_REDUCTION: + case ICMP6_WRUREQUEST: + case ICMP6_WRUREPLY: + case MLD6_MTRACE_RESP: + case MLD6_MTRACE: + return 1; + default: + return 0; + } +} diff --git a/usr/src/uts/common/inet/ipf/ipf.h b/usr/src/uts/common/inet/ipf/ipf.h index d60d89b70c..559349e3d9 100644 --- a/usr/src/uts/common/inet/ipf/ipf.h +++ b/usr/src/uts/common/inet/ipf/ipf.h @@ -6,7 +6,7 @@ * @(#)ipf.h 1.12 6/5/96 * $Id: ipf.h,v 2.71.2.7 2005/06/12 07:18:31 darrenr Exp $ * - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -250,6 +250,7 @@ extern int ports __P((char ***, char *, u_short *, int *, u_short *, int)); extern int pri_findname __P((char *)); extern char *pri_toname __P((int)); extern void print_toif __P((char *, struct frdest *)); +extern void printaddr __P((int, int, char *, u_32_t *, u_32_t *)); extern void printaps __P((ap_session_t *, int)); extern void printbuf __P((char *, int, int)); extern void printfr __P((struct frentry *, ioctlfunc_t)); @@ -298,6 +299,7 @@ extern char *hostname __P((int, void *)); extern struct ipstate *printstate __P((struct ipstate *, int, u_long)); extern void printsbuf __P((char *)); extern void printnat __P((struct ipnat *, int)); +extern void printactiveaddress __P((int, char *, i6addr_t *, char *)); extern void printactivenat __P((struct nat *, int, int)); extern void printhostmap __P((struct hostmap *, u_int)); extern void printpacket __P((struct ip *)); diff --git a/usr/src/uts/common/inet/ipf/netinet/ip_fil.h b/usr/src/uts/common/inet/ipf/netinet/ip_fil.h index dbdbaef7e9..0b98cf420a 100644 --- a/usr/src/uts/common/inet/ipf/netinet/ip_fil.h +++ b/usr/src/uts/common/inet/ipf/netinet/ip_fil.h @@ -131,7 +131,7 @@ typedef union i6addr { void *vptr[2]; lookupfunc_t lptr[2]; } i6addr_t; -#define in6_addr8 in6.s6_addr +#define in6_addr8 in6.s6_addr #else typedef union i6addr { u_32_t i6[4]; @@ -141,7 +141,7 @@ typedef union i6addr { } i6addr_t; #endif -#define in4_addr in4.s_addr +#define in4_addr in4.s_addr #define iplookupnum i6[0] #define iplookuptype i6[1] /* @@ -163,26 +163,34 @@ typedef union i6addr { (I61(a) == I61(b)) && (I60(a) == I60(b))) #define IP6_NEQ(a,b) ((I63(a) != I63(b)) || (I62(a) != I62(b)) || \ (I61(a) != I61(b)) || (I60(a) != I60(b))) -#define IP6_ISZERO(a) ((I60(a) | I61(a) | I62(a) | I63(a)) == 0) +#define IP6_ISZERO(a) ((I60(a) | I61(a) | I62(a) | I63(a)) == 0) #define IP6_NOTZERO(a) ((I60(a) | I61(a) | I62(a) | I63(a)) != 0) -#define IP6_GT(a,b) (HI60(a) > HI60(b) || (HI60(a) == HI60(b) && \ - (HI61(a) > HI61(b) || (HI61(a) == HI61(b) && \ - (HI62(a) > HI62(b) || (HI62(a) == HI62(b) && \ - HI63(a) > HI63(b))))))) -#define IP6_LT(a,b) (HI60(a) < HI60(b) || (HI60(a) == HI60(b) && \ - (HI61(a) < HI61(b) || (HI61(a) == HI61(b) && \ - (HI62(a) < HI62(b) || (HI62(a) == HI62(b) && \ - HI63(a) < HI63(b))))))) +#define IP6_ISONES(a) ((I63(a) == 0xffffffff) && (I62(a) == 0xffffffff) && \ + (I61(a) == 0xffffffff) && (I60(a) == 0xffffffff)) +#define IP6_GT(a,b) (ntohl(HI60(a)) > ntohl(HI60(b)) || \ + (HI60(a) == HI60(b) && \ + (ntohl(HI61(a)) > ntohl(HI61(b)) || \ + (HI61(a) == HI61(b) && \ + (ntohl(HI62(a)) > ntohl(HI62(b)) || \ + (HI62(a) == HI62(b) && \ + ntohl(HI63(a)) > ntohl(HI63(b)))))))) +#define IP6_LT(a,b) (ntohl(HI60(a)) < ntohl(HI60(b)) || \ + (HI60(a) == HI60(b) && \ + (ntohl(HI61(a)) < ntohl(HI61(b)) || \ + (HI61(a) == HI61(b) && \ + (ntohl(HI62(a)) < ntohl(HI62(b)) || \ + (HI62(a) == HI62(b) && \ + ntohl(HI63(a)) < ntohl(HI63(b)))))))) #define NLADD(n,x) htonl(ntohl(n) + (x)) #define IP6_INC(a) \ { i6addr_t *_i6 = (i6addr_t *)(a); \ - _i6->i6[0] = NLADD(_i6->i6[0], 1); \ - if (_i6->i6[0] == 0) { \ - _i6->i6[0] = NLADD(_i6->i6[1], 1); \ - if (_i6->i6[1] == 0) { \ - _i6->i6[0] = NLADD(_i6->i6[2], 1); \ - if (_i6->i6[2] == 0) { \ - _i6->i6[0] = NLADD(_i6->i6[3], 1); \ + _i6->i6[3] = NLADD(_i6->i6[3], 1); \ + if (_i6->i6[3] == 0) { \ + _i6->i6[2] = NLADD(_i6->i6[2], 1); \ + if (_i6->i6[2] == 0) { \ + _i6->i6[1] = NLADD(_i6->i6[1], 1); \ + if (_i6->i6[1] == 0) { \ + _i6->i6[0] = NLADD(_i6->i6[0], 1); \ } \ } \ } \ @@ -190,25 +198,35 @@ typedef union i6addr { #define IP6_ADD(a,x,d) \ { i6addr_t *_s = (i6addr_t *)(a); \ i6addr_t *_d = (i6addr_t *)(d); \ - _d->i6[0] = NLADD(_s->i6[0], x); \ - if (ntohl(_d->i6[0]) < ntohl(_s->i6[0])) { \ - _d->i6[1] = NLADD(_d->i6[1], 1); \ - if (ntohl(_d->i6[1]) < ntohl(_s->i6[1])) { \ - _d->i6[2] = NLADD(_d->i6[2], 1); \ - if (ntohl(_d->i6[2]) < ntohl(_s->i6[2])) { \ - _d->i6[3] = NLADD(_d->i6[3], 1); \ + _d->i6[3] = NLADD(_s->i6[3], x); \ + if (ntohl(_d->i6[3]) < ntohl(_s->i6[3])) { \ + _d->i6[2] = NLADD(_d->i6[2], 1); \ + if (ntohl(_d->i6[2]) < ntohl(_s->i6[2])) { \ + _d->i6[1] = NLADD(_d->i6[1], 1); \ + if (ntohl(_d->i6[1]) < ntohl(_s->i6[1])) { \ + _d->i6[0] = NLADD(_d->i6[0], 1); \ } \ } \ } \ } #define IP6_AND(a,b,d) { i6addr_t *_s1 = (i6addr_t *)(a); \ - i6addr_t *_s2 = (i6addr_t *)(d); \ + i6addr_t *_s2 = (i6addr_t *)(b); \ i6addr_t *_d = (i6addr_t *)(d); \ _d->i6[0] = _s1->i6[0] & _s2->i6[0]; \ _d->i6[1] = _s1->i6[1] & _s2->i6[1]; \ _d->i6[2] = _s1->i6[2] & _s2->i6[2]; \ _d->i6[3] = _s1->i6[3] & _s2->i6[3]; \ } +#define IP6_MASKEQ(a,m,b) \ + (((I60(a) & I60(m)) == I60(b)) && \ + ((I61(a) & I61(m)) == I61(b)) && \ + ((I62(a) & I62(m)) == I62(b)) && \ + ((I63(a) & I63(m)) == I63(b))) +#define IP6_MASKNEQ(a,m,b) \ + (((I60(a) & I60(m)) != I60(b)) || \ + ((I61(a) & I61(m)) != I61(b)) || \ + ((I62(a) & I62(m)) != I62(b)) || \ + ((I63(a) & I63(m)) != I63(b))) #define IP6_MERGE(a,b,c) \ { i6addr_t *_d, *_s1, *_s2; \ _d = (i6addr_t *)(a); \ @@ -217,7 +235,7 @@ typedef union i6addr { _d->i6[0] |= _s1->i6[0] & ~_s2->i6[0]; \ _d->i6[1] |= _s1->i6[1] & ~_s2->i6[1]; \ _d->i6[2] |= _s1->i6[2] & ~_s2->i6[2]; \ - _d->i6[2] |= _s1->i6[3] & ~_s2->i6[3]; \ + _d->i6[3] |= _s1->i6[3] & ~_s2->i6[3]; \ } @@ -261,6 +279,7 @@ typedef struct fr_ip { #define FI_WITH 0xeffe /* Not FI_TCPUDP */ #define FI_V6EXTHDR 0x10000 #define FI_COALESCE 0x20000 +#define FI_ICMPQUERY 0x40000 #define FI_NOCKSUM 0x20000000 /* don't do a L4 checksum validation */ #define FI_DONTCACHE 0x40000000 /* don't cache the result */ #define FI_IGNORE 0x80000000 @@ -323,7 +342,12 @@ typedef struct fr_info { void *fin_nat; void *fin_state; void *fin_nattag; - ip_t *fin_ip; + union { + ip_t *fip_ip; +#ifdef USE_INET6 + ip6_t *fip_ip6; +#endif + } fin_ipu; mb_t **fin_mp; /* pointer to pointer to mbuf */ mb_t *fin_m; /* pointer to mbuf */ #ifdef MENTAT @@ -336,6 +360,8 @@ typedef struct fr_info { #endif } fr_info_t; +#define fin_ip fin_ipu.fip_ip +#define fin_ip6 fin_ipu.fip_ip6 #define fin_v fin_fi.fi_v #define fin_p fin_fi.fi_p #define fin_flx fin_fi.fi_flx @@ -343,16 +369,21 @@ typedef struct fr_info { #define fin_secmsk fin_fi.fi_secmsk #define fin_auth fin_fi.fi_auth #define fin_src fin_fi.fi_src.in4 -#define fin_src6 fin_fi.fi_src.in6 #define fin_saddr fin_fi.fi_saddr #define fin_dst fin_fi.fi_dst.in4 -#define fin_dst6 fin_fi.fi_dst.in6 #define fin_daddr fin_fi.fi_daddr #define fin_data fin_dat.fid_16 #define fin_sport fin_dat.fid_16[0] #define fin_dport fin_dat.fid_16[1] #define fin_ports fin_dat.fid_32 +#ifdef USE_INET6 +# define fin_src6 fin_fi.fi_src +# define fin_dst6 fin_fi.fi_dst +# define fin_dstip6 fin_fi.fi_dst.in6 +# define fin_srcip6 fin_fi.fi_src.in6 +#endif + #define IPF_IN 0 #define IPF_OUT 1 @@ -1397,7 +1428,8 @@ extern int copyoutptr __P((void *, void *, size_t)); extern int fr_fastroute __P((mb_t *, mb_t **, fr_info_t *, frdest_t *)); extern int fr_inobj __P((void *, void *, int)); extern int fr_inobjsz __P((void *, void *, int, int)); -extern int fr_ioctlswitch __P((int, void *, ioctlcmd_t, int, int, void *, ipf_stack_t *)); +extern int fr_ioctlswitch __P((int, void *, ioctlcmd_t, int, int, void *, + ipf_stack_t *)); extern int fr_ipftune __P((ioctlcmd_t, void *, ipf_stack_t *)); extern int fr_outobj __P((void *, void *, int)); extern int fr_outobjsz __P((void *, void *, int, int)); @@ -1414,8 +1446,10 @@ extern ipftq_t *fr_addtimeoutqueue __P((ipftq_t **, u_int, ipf_stack_t *)); extern void fr_deletequeueentry __P((ipftqent_t *)); extern int fr_deletetimeoutqueue __P((ipftq_t *)); extern void fr_freetimeoutqueue __P((ipftq_t *, ipf_stack_t *)); -extern void fr_movequeue __P((ipftqent_t *, ipftq_t *, ipftq_t *, ipf_stack_t *)); -extern void fr_queueappend __P((ipftqent_t *, ipftq_t *, void *, ipf_stack_t *)); +extern void fr_movequeue __P((ipftqent_t *, ipftq_t *, ipftq_t *, + ipf_stack_t *)); +extern void fr_queueappend __P((ipftqent_t *, ipftq_t *, void *, + ipf_stack_t *)); extern void fr_queueback __P((ipftqent_t *, ipf_stack_t *)); extern void fr_queuefront __P((ipftqent_t *)); extern void fr_checkv4sum __P((fr_info_t *)); @@ -1431,6 +1465,10 @@ extern int fr_ifpfillv6addr __P((int, struct sockaddr_in6 *, struct in_addr *)); #endif +#define IPFILTER_COMPAT +extern int fr_incomptrans __P((ipfobj_t *, void *)); +extern int fr_outcomptrans __P((ipfobj_t *, void *)); + extern int fr_addipftune __P((ipftuneable_t *, ipf_stack_t *)); extern int fr_delipftune __P((ipftuneable_t *, ipf_stack_t *)); @@ -1470,7 +1508,7 @@ extern void fr_lock __P((caddr_t, int *)); extern int fr_makefrip __P((int, ip_t *, fr_info_t *)); extern int fr_matchtag __P((ipftag_t *, ipftag_t *)); extern int fr_matchicmpqueryreply __P((int, icmpinfo_t *, - struct icmp *, int)); + struct icmp *, int)); extern u_32_t fr_newisn __P((fr_info_t *)); extern u_short fr_nextipid __P((fr_info_t *)); extern int fr_rulen __P((int, frentry_t *, ipf_stack_t *)); @@ -1479,12 +1517,14 @@ extern frentry_t *fr_srcgrpmap __P((fr_info_t *, u_32_t *)); extern int fr_tcpudpchk __P((fr_info_t *, frtuc_t *)); extern int fr_verifysrc __P((fr_info_t *fin)); extern int fr_zerostats __P((char *, ipf_stack_t *)); -extern ipftoken_t *ipf_findtoken __P((int, int, void *, ipf_stack_t *)); -extern int ipf_getnextrule __P((ipftoken_t *, void *, ipf_stack_t *)); +extern ipftoken_t *ipf_findtoken __P((int, int, void *, ipf_stack_t *)); +extern int ipf_getnextrule __P((ipftoken_t *, void *, + ipf_stack_t *)); extern void ipf_expiretokens __P((ipf_stack_t *)); -extern void ipf_freetoken __P((ipftoken_t *, ipf_stack_t *)); -extern int ipf_deltoken __P((int,int, void *, ipf_stack_t *)); -extern int ipf_genericiter __P((void *, int, void *, ipf_stack_t *)); +extern void ipf_freetoken __P((ipftoken_t *, ipf_stack_t *)); +extern int ipf_deltoken __P((int,int, void *, ipf_stack_t *)); +extern int ipf_genericiter __P((void *, int, void *, + ipf_stack_t *)); extern char ipfilter_version[]; #ifdef USE_INET6 diff --git a/usr/src/uts/common/inet/ipf/netinet/ip_nat.h b/usr/src/uts/common/inet/ipf/netinet/ip_nat.h index 605af9b075..d7df16dba6 100644 --- a/usr/src/uts/common/inet/ipf/netinet/ip_nat.h +++ b/usr/src/uts/common/inet/ipf/netinet/ip_nat.h @@ -6,7 +6,7 @@ * @(#)ip_nat.h 1.5 2/4/96 * $Id: ip_nat.h,v 2.90.2.11 2005/06/18 02:41:32 darrenr Exp $ * - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" @@ -30,7 +30,7 @@ #define SIOCGNATL _IOWR(r, 63, struct ipfobj) #endif -#undef LARGE_NAT /* define this if you're setting up a system to NAT +#undef LARGE_NAT /* define this if you're setting up a system to NAT * LARGE numbers of networks/hosts - i.e. in the * hundreds or thousands. In such a case, you should * also change the RDR_SIZE and NAT_SIZE below to more @@ -114,7 +114,7 @@ typedef struct nat { void *nat_sync; ipftqent_t nat_tqe; u_32_t nat_flags; - u_32_t nat_sumd[2]; /* ip checksum delta for data segment*/ + u_32_t nat_sumd[2]; /* ip checksum delta for data segment */ u_32_t nat_ipsumd; /* ip checksum delta for ip header */ u_32_t nat_mssclamp; /* if != zero clamp MSS to this */ i6addr_t nat_inip6; @@ -136,6 +136,7 @@ typedef struct nat { int nat_hv[2]; char nat_ifnames[2][LIFNAMSIZ]; int nat_rev; /* 0 = forward, 1 = reverse */ + int nat_v; int nat_redir; /* copy of in_redir */ } nat_t; @@ -280,18 +281,26 @@ typedef struct ipnat { #define MAPBLK_MINPORT 1024 /* don't use reserved ports for src port */ #define USABLE_PORTS (65536 - MAPBLK_MINPORT) -#define IPN_CMPSIZ (sizeof(ipnat_t) - offsetof(ipnat_t, in_flags)) +#define IPN_CMPSIZ (sizeof (ipnat_t) - offsetof(ipnat_t, in_flags)) typedef struct natlookup { - struct in_addr nl_inip; - struct in_addr nl_outip; - struct in_addr nl_realip; - int nl_flags; - u_short nl_inport; - u_short nl_outport; - u_short nl_realport; + i6addr_t nl_inipaddr; + i6addr_t nl_outipaddr; + i6addr_t nl_realipaddr; + int nl_v; + int nl_flags; + u_short nl_inport; + u_short nl_outport; + u_short nl_realport; } natlookup_t; +#define nl_inip nl_inipaddr.in4 +#define nl_outip nl_outipaddr.in4 +#define nl_realip nl_realipaddr.in4 +#define nl_inip6 nl_inipaddr.in6 +#define nl_outip6 nl_outipaddr.in6 +#define nl_realip6 nl_realipaddr.in6 + typedef struct nat_save { void *ipn_next; @@ -341,21 +350,27 @@ typedef struct hostmap { struct hostmap *hm_hnext; struct hostmap **hm_phnext; struct ipnat *hm_ipnat; - struct in_addr hm_srcip; - struct in_addr hm_dstip; - struct in_addr hm_mapip; + i6addr_t hm_src; + i6addr_t hm_dst; + i6addr_t hm_map; u_32_t hm_port; int hm_ref; + int hm_v; } hostmap_t; +#define hm_srcip hm_src.in4 +#define hm_dstip hm_dst.in4 +#define hm_mapip hm_map.in4 +#define hm_srcip6 hm_src.in6 +#define hm_dstip6 hm_dst.in6 +#define hm_mapip6 hm_map.in6 + /* * Structure used to pass information in to nat_newmap and nat_newrdr. */ typedef struct natinfo { ipnat_t *nai_np; - u_32_t nai_sum1; - u_32_t nai_sum2; u_32_t nai_nflags; u_32_t nai_flags; struct in_addr nai_ip; @@ -395,17 +410,18 @@ typedef struct natstat { } natstat_t; typedef struct natlog { - struct in_addr nl_origip; - struct in_addr nl_outip; - struct in_addr nl_inip; - u_short nl_origport; - u_short nl_outport; - u_short nl_inport; - u_short nl_type; - int nl_rule; - U_QUAD_T nl_pkts[2]; - U_QUAD_T nl_bytes[2]; - u_char nl_p; + i6addr_t nlg_origip; + i6addr_t nlg_outip; + i6addr_t nlg_inip; + u_short nlg_origport; + u_short nlg_outport; + u_short nlg_inport; + u_short nlg_type; + int nlg_rule; + U_QUAD_T nlg_pkts[2]; + U_QUAD_T nlg_bytes[2]; + u_char nlg_p; + int nlg_v; } natlog_t; @@ -417,9 +433,22 @@ typedef struct natlog { #define NL_FLUSH 0xfffe #define NL_EXPIRE 0xffff -#define NAT_HASH_FN(k,l,m) (((k) + ((k) >> 12) + l) % (m)) +#define NAT_HASH_FN(k, l, m) (((k) + ((k) >> 12) + l) % (m)) +#define NAT_HASH_FN6(k, l, m) ((((u_32_t *)(k))[3] \ + + (((u_32_t *)(k))[3] >> 12) \ + + (((u_32_t *)(k))[2]) \ + + (((u_32_t *)(k))[2] >> 12) \ + + (((u_32_t *)(k))[1]) \ + + (((u_32_t *)(k))[1] >> 12) \ + + (((u_32_t *)(k))[0]) \ + + (((u_32_t *)(k))[0] >> 12) \ + + l) % (m)) #define LONG_SUM(in) (((in) & 0xffff) + ((in) >> 16)) +#define LONG_SUM6(in) (LONG_SUM(ntohl(((u_32_t *)(in))[0])) + \ + LONG_SUM(ntohl(((u_32_t *)(in))[1])) + \ + LONG_SUM(ntohl(((u_32_t *)(in))[2])) + \ + LONG_SUM(ntohl(((u_32_t *)(in))[3]))) #define CALC_SUMD(s1, s2, sd) { \ (s1) = ((s1) & 0xffff) + ((s1) >> 16); \ @@ -436,8 +465,8 @@ typedef struct natlog { #define NAT_LOCKHELD 0x40000000 extern void fr_natsync __P((void *, ipf_stack_t *)); -extern void fr_nataddrsync __P((void *, struct in_addr *, ipf_stack_t *)); -extern void fr_natifpsync __P((int, void *, char *, ipf_stack_t *)); +extern void fr_nataddrsync __P((int, void *, void *, ipf_stack_t *)); +extern void fr_natifpsync __P((int, int, void *, char *, ipf_stack_t *)); #if defined(__OpenBSD__) extern void nat_ifdetach __P((void *, ipf_stack_t *)); @@ -474,4 +503,28 @@ extern void nat_update __P((fr_info_t *, nat_t *, ipnat_t *)); extern void fr_setnatqueue __P((nat_t *, int, ipf_stack_t *)); extern void fr_hostmapdel __P((hostmap_t **)); +extern nat_t *fr_natclone __P((fr_info_t *, nat_t *)); +extern void nat_delrdr __P((struct ipnat *)); +extern void nat_delnat __P((struct ipnat *)); +extern int nat_wildok __P((nat_t *, int, int, int, int)); +extern void nat_calc_chksum_diffs __P((nat_t *)); + +#ifdef USE_INET6 +extern void nat6_addnat __P((ipnat_t *, ipf_stack_t *)); +extern void nat6_addrdr __P((ipnat_t *, ipf_stack_t *)); +extern nat_t *nat6_new __P((fr_info_t *, ipnat_t *, nat_t **, u_int, int)); +extern nat_t *nat6_outlookup __P((fr_info_t *, u_int, u_int, + struct in6_addr *, struct in6_addr *)); +extern nat_t *nat6_inlookup __P((fr_info_t *, u_int, u_int, + struct in6_addr *, struct in6_addr *)); +extern nat_t *nat6_lookupredir __P((natlookup_t *, ipf_stack_t *)); +extern nat_t *nat6_icmperrorlookup __P((fr_info_t *, int)); +extern nat_t *nat6_icmperror __P((fr_info_t *, u_int *, int)); +extern int nat6_insert __P((nat_t *, int, ipf_stack_t *)); +extern int fr_checknat6out __P((fr_info_t *, u_32_t *)); +extern int fr_nat6out __P((fr_info_t *, nat_t *, int, u_32_t)); +extern int fr_checknat6in __P((fr_info_t *, u_32_t *)); +extern int fr_nat6in __P((fr_info_t *, nat_t *, int, u_32_t)); +#endif + #endif /* __IP_NAT_H__ */ diff --git a/usr/src/uts/common/inet/ipf/netinet/ipf_stack.h b/usr/src/uts/common/inet/ipf/netinet/ipf_stack.h index 23f291c866..44ef0ace0f 100644 --- a/usr/src/uts/common/inet/ipf/netinet/ipf_stack.h +++ b/usr/src/uts/common/inet/ipf/netinet/ipf_stack.h @@ -3,7 +3,7 @@ * * See the IPFILTER.LICENCE file for details on licencing. * - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -212,6 +212,8 @@ struct ipf_stack { uint_t ifs_fr_nat_maxbucket_reset; uint32_t ifs_nat_masks; uint32_t ifs_rdr_masks; + uint32_t ifs_nat6_masks[4]; + uint32_t ifs_rdr6_masks[4]; ipnat_t **ifs_nat_rules; ipnat_t **ifs_rdr_rules; hostmap_t **ifs_maptable; diff --git a/usr/src/uts/common/inet/ipf/netinet/ipl.h b/usr/src/uts/common/inet/ipf/netinet/ipl.h index df2c0b4f3b..8c8b1337dc 100644 --- a/usr/src/uts/common/inet/ipf/netinet/ipl.h +++ b/usr/src/uts/common/inet/ipf/netinet/ipl.h @@ -6,7 +6,7 @@ * @(#)ipl.h 1.21 6/5/96 * $Id: ipl.h,v 2.52.2.10 2005/08/13 05:42:49 darrenr Exp $ * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -18,6 +18,6 @@ #define IPL_VERSION "IP Filter: v4.1.9" -#define IPFILTER_VERSION 4010900 +#define IPFILTER_VERSION 4010901 #endif |
