Index: share/man/man4/stf.4 diff -u share/man/man4/stf.4.orig share/man/man4/stf.4 --- share/man/man4/stf.4.orig Wed Sep 18 01:53:04 2002 +++ share/man/man4/stf.4 Sun Sep 22 01:53:31 2002 @@ -175,6 +175,20 @@ Note, however, there are other security risks exist. If you wish to use the configuration, you must not advertise your 6to4 address to others. +.Pp +You can configure to use 6to4 over NAT by specifying +.Dq tunnel +option of +.Xr ifconfig 8 . +In this case, you must specify your actuall IPv4 address with the +source address of the peer. Though the destination address of the peer +has no meaning in 6to4, you still need to specify it as 0.0.0.0. It's +just a dummy. +If you are directly connected to the Internet, you shouldn't specify +.Dq tunnel +option of +.Xr ifconfig 8 . +This is only hack to use 6to4 from within a NAT. .\" .Sh EXAMPLES Note that Index: sys/net/if_stf.c diff -u sys/net/if_stf.c.orig sys/net/if_stf.c --- sys/net/if_stf.c.orig Sun Apr 28 14:40:25 2002 +++ sys/net/if_stf.c Sat Sep 21 21:15:16 2002 @@ -115,11 +115,14 @@ #include +#define STFNAME "stf" + #define IN6_IS_ADDR_6TO4(x) (ntohs((x)->s6_addr16[0]) == 0x2002) #define GET_V4(x) ((struct in_addr *)(&(x)->s6_addr16[1])) struct stf_softc { struct ifnet sc_if; /* common area */ + struct in_addr stf_psrc; /* Physical src addr */ union { struct route __sc_ro4; struct route_in6 __sc_ro6; /* just for safety */ @@ -144,9 +147,10 @@ static int stfmodevent __P((module_t, int, void *)); static int stf_encapcheck __P((const struct mbuf *, int, int, void *)); -static struct in6_ifaddr *stf_getsrcifa6 __P((struct ifnet *)); +static struct in6_ifaddr *stf_getsrcifa6 __P((struct ifnet *, int)); static int stf_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *)); +static int isrfc1918addr __P((struct in_addr *)); static int stf_checkaddr4 __P((struct stf_softc *, struct in_addr *, struct ifnet *)); static int stf_checkaddr6 __P((struct stf_softc *, struct in6_addr *, @@ -252,7 +256,7 @@ if (ip.ip_v != 4) return 0; - ia6 = stf_getsrcifa6(&sc->sc_if); + ia6 = stf_getsrcifa6(&sc->sc_if, sc->stf_psrc.s_addr != INADDR_ANY); if (ia6 == NULL) return 0; @@ -261,9 +265,14 @@ * local 6to4 address. * success on: dst = 10.1.1.1, ia6->ia_addr = 2002:0a01:0101:... */ - if (bcmp(GET_V4(&ia6->ia_addr.sin6_addr), &ip.ip_dst, - sizeof(ip.ip_dst)) != 0) - return 0; + if (sc->stf_psrc.s_addr != INADDR_ANY) { + if (sc->stf_psrc.s_addr != ip.ip_dst.s_addr) + return 0; + } else { + if (bcmp(GET_V4(&ia6->ia_addr.sin6_addr), &ip.ip_dst, + sizeof(ip.ip_dst)) != 0) + return 0; + } /* * check if IPv4 src matches the IPv4 address derived from the @@ -284,8 +293,9 @@ } static struct in6_ifaddr * -stf_getsrcifa6(ifp) +stf_getsrcifa6(ifp, no_addr4check) struct ifnet *ifp; + int no_addr4check; { struct ifaddr *ia; struct in_ifaddr *ia4; @@ -304,12 +314,14 @@ if (!IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) continue; - bcopy(GET_V4(&sin6->sin6_addr), &in, sizeof(in)); - LIST_FOREACH(ia4, INADDR_HASH(in.s_addr), ia_hash) - if (ia4->ia_addr.sin_addr.s_addr == in.s_addr) - break; - if (ia4 == NULL) - continue; + if (!no_addr4check) { + bcopy(GET_V4(&sin6->sin6_addr), &in, sizeof(in)); + LIST_FOREACH(ia4, INADDR_HASH(in.s_addr), ia_hash) + if (ia4->ia_addr.sin_addr.s_addr == in.s_addr) + break; + if (ia4 == NULL) + continue; + } return (struct in6_ifaddr *)ia; } @@ -347,7 +359,7 @@ * we shouldn't generate output. Without this check, we'll end up * using wrong IPv4 source. */ - ia6 = stf_getsrcifa6(ifp); + ia6 = stf_getsrcifa6(ifp, sc->stf_psrc.s_addr != INADDR_ANY); if (ia6 == NULL) { m_freem(m); return ENETDOWN; @@ -407,8 +419,12 @@ bzero(ip, sizeof(*ip)); - bcopy(GET_V4(&((struct sockaddr_in6 *)&ia6->ia_addr)->sin6_addr), - &ip->ip_src, sizeof(ip->ip_src)); + if (sc->stf_psrc.s_addr != INADDR_ANY) { + ip->ip_src.s_addr = sc->stf_psrc.s_addr; + } else { + bcopy(GET_V4(&((struct sockaddr_in6 *)&ia6->ia_addr)->sin6_addr), + &ip->ip_src, sizeof(ip->ip_src)); + } bcopy(in4, &ip->ip_dst, sizeof(ip->ip_dst)); ip->ip_p = IPPROTO_IPV6; ip->ip_ttl = ip_stf_ttl; @@ -443,6 +459,22 @@ } static int +isrfc1918addr(in) + struct in_addr *in; +{ + /* + * returns 1 if private address range: + * 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 + */ + if ((ntohl(in->s_addr) & 0xff000000) >> 24 == 10 || + (ntohl(in->s_addr) & 0xfff00000) >> 16 == 172 * 256 + 16 || + (ntohl(in->s_addr) & 0xffff0000) >> 16 == 192 * 256 + 168) + return 1; + + return 0; +} + +static int stf_checkaddr4(sc, in, inifp) struct stf_softc *sc; struct in_addr *in; @@ -511,8 +543,16 @@ /* * check 6to4 addresses */ - if (IN6_IS_ADDR_6TO4(in6)) + if (IN6_IS_ADDR_6TO4(in6)) { + /* + * reject packets with private address range. + * (requirement from RFC3056 section 2 1st paragraph) + */ + if (isrfc1918addr(GET_V4(in6))) + return -1; + return stf_checkaddr4(sc, GET_V4(in6), inifp); + } /* * reject anything that look suspicious. the test is implemented @@ -575,6 +615,21 @@ return; } + /* + * XXX: Skip following check to allow incoming packets with + * private address for dest. Though it may breasks the + * requirement from RFC3056 section 2 1st paragraph, it helps + * for 6to4 over NAT. + */ + if (sc->stf_psrc.s_addr == INADDR_ANY && isrfc1918addr(&ip->ip_dst)) { + m_freem(m); + return; + } + if (isrfc1918addr(&ip->ip_src)) { + m_freem(m); + return; + } + otos = ip->ip_tos; m_adj(m, off); @@ -668,10 +723,15 @@ u_long cmd; caddr_t data; { + struct stf_softc *sc = (struct stf_softc*)ifp; struct ifaddr *ifa; struct ifreq *ifr; struct sockaddr_in6 *sin6; - int error; + struct sockaddr_in si; + int error, size; + struct sockaddr *dst, *src; + struct ifnet *ifp2; + struct stf_softc *sc2; error = 0; switch (cmd) { @@ -682,7 +742,8 @@ break; } sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; - if (IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) { + if (IN6_IS_ADDR_6TO4(&sin6->sin6_addr) && + !isrfc1918addr(GET_V4(&sin6->sin6_addr))) { ifa->ifa_rtrequest = stf_rtrequest; ifp->if_flags |= IFF_UP; } else @@ -698,10 +759,101 @@ error = EAFNOSUPPORT; break; + case SIOCSIFPHYADDR: + src = (struct sockaddr *) + &(((struct in_aliasreq *)data)->ifra_addr); + + if (src->sa_len != sizeof(struct sockaddr_in)) + return EINVAL; + + if (src->sa_family != AF_INET) + return EINVAL; + + TAILQ_FOREACH(ifp2, &ifnet, if_link) { + if (strcmp(ifp2->if_name, STFNAME) != 0) + continue; + sc2 = ifp2->if_softc; + if (sc2 == sc) + continue; + if (sc->stf_psrc.s_addr == INADDR_ANY) + continue; + + /* Disallow parallel tunnels. */ + if (sc2->stf_psrc.s_addr == satosin(src)->sin_addr.s_addr) { + error = EADDRNOTAVAIL; + goto bad; + } + } + + sc->stf_psrc.s_addr = satosin(src)->sin_addr.s_addr; + break; + + case SIOCDIFPHYADDR: + sc->stf_psrc.s_addr = INADDR_ANY; + /* change the IFF_{UP, RUNNING} flag as well? */ + break; + + case SIOCGIFPSRCADDR: + case SIOCGIFPSRCADDR_IN6: + if (sc->stf_psrc.s_addr == INADDR_ANY) { + error = EADDRNOTAVAIL; + goto bad; + } + switch (cmd) { + case SIOCGIFPSRCADDR: + ifr = (struct ifreq *)data; + dst = &ifr->ifr_addr; + size = sizeof(ifr->ifr_addr); + break; + case SIOCGIFPSRCADDR_IN6: + dst = (struct sockaddr *) + &(((struct in6_ifreq *)data)->ifr_addr); + size = sizeof(((struct in6_ifreq *)data)->ifr_addr); + break; + default: + error = EADDRNOTAVAIL; + goto bad; + } + bzero(&si, sizeof(si)); + si.sin_family = AF_INET; + si.sin_len = sizeof(struct sockaddr_in); + si.sin_addr.s_addr = sc->stf_psrc.s_addr; + bcopy(&si, (caddr_t)dst, size); + break; + + case SIOCGIFPDSTADDR: + case SIOCGIFPDSTADDR_IN6: + if (sc->stf_psrc.s_addr == INADDR_ANY) { + error = EADDRNOTAVAIL; + goto bad; + } + switch (cmd) { + case SIOCGIFPDSTADDR: + ifr = (struct ifreq *)data; + dst = &ifr->ifr_addr; + size = sizeof(ifr->ifr_addr); + break; + case SIOCGIFPDSTADDR_IN6: + dst = (struct sockaddr *) + &(((struct in6_ifreq *)data)->ifr_addr); + size = sizeof(((struct in6_ifreq *)data)->ifr_addr); + break; + default: + error = EADDRNOTAVAIL; + goto bad; + } + bzero(&si, sizeof(si)); + si.sin_family = AF_INET; + si.sin_len = sizeof(struct sockaddr_in); + si.sin_addr.s_addr = INADDR_ANY; + bcopy(&si, (caddr_t)dst, size); + break; + default: error = EINVAL; break; } + bad: return error; }