tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

6rd support for stf(4)



Here's a port of Hiroki Sato <hrs%freebsd.org@localhost>'s FreeBSD patch that
adds support for 6rd IPv6 tunneling (RFC 5569) to stf(4). His original
patch is at
http://people.allbsd.org/~hrs/FreeBSD/stf_6rd_20100923-1.diff
and his description of it is at
http://lists.freebsd.org/pipermail/freebsd-net/2010-September/026497.html

I'm unfamiliar with NetBSD's networking code (and FreeBSD's too, for
that matter), so I left out the part that only allows one stf
interface to be UP at a time.

Hopefully I got everything else ported over correctly though; it seems
to be working well for me with AT&T's 6rd gateway. AT&T's 6rd prefix
and prefix length are 2602:300::/28, their 6rd gateway's IPv4 address
is 12.83.49.81, and IPv4 mask length is 0. (Note: as far as I can
tell, this patch does not support mask lengths other than 0). My IPv4
address is 75.27.143.66. I created an /etc/ifconfig.stf0 that
contains:

create
inet6 2602:304:b1b8:f420:: prefixlen 28
!route add -inet6 default 2602:300:c533:1510::

Where 2602:304:b1b8:f420:: is the 6rd prefix 2602:030x (28 bits)
followed by 4:b1b8:f42x (my 32-bit IPv4 address in hex), and
2602:300:c533:1510:: is 2602:030x followed by 0:c533:151x (the 6rd
gateway's IPv4 address in hex).
Index: share/man/man4/stf.4
===================================================================
RCS file: /cvsroot/src/share/man/man4/stf.4,v
retrieving revision 1.24
diff -u -p -r1.24 stf.4
--- share/man/man4/stf.4        2 Jan 2011 12:48:21 -0000       1.24
+++ share/man/man4/stf.4        9 Jun 2012 04:57:23 -0000
@@ -2,6 +2,7 @@
 .\"     $KAME: stf.4,v 1.39 2002/11/17 19:34:02 itojun Exp $
 .\"
 .\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+.\" Copyright (c) 2010 Hiroki Sato <hrs%FreeBSD.org@localhost>
 .\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
@@ -39,11 +40,11 @@
 .Sh DESCRIPTION
 The
 .Nm
-interface supports
-.Dq 6to4
-IPv6 in IPv4 encapsulation.
-It can tunnel IPv6 traffic over IPv4, as specified in
-.Li RFC3056 .
+interface supports IPv6 in IPv4 encapsulation by
+tunneling IPv6 traffic over IPv4, as specified in
+.Li RFC3056 Pq 6to4
+and
+.Li RFC5569 Pq 6rd .
 .Nm
 interfaces are dynamically created and destroyed with the
 .Xr ifconfig 8
@@ -55,26 +56,31 @@ Only one
 .Nm
 interface may be created.
 .Pp
-For ordinary nodes in 6to4 sites, you do not need a
+Due to the way the 6to4 protocol is specified,
 .Nm
-interface.
-The
+interface requires certain configuration to work properly.  Two
+different protocols defined in RFC3056 and RFC5569 are basically the
+same as each other except for address handling, so
 .Nm
-interface is only necessary on the site border router
-.Po
-called the
-.Dq 6to4 router
-in the specification
-.Pc .
-.Pp
-Due to the way the 6to4 protocol is specified,
+decides its behavior based on the configured IPv6 addresses as
+explained in the following.
+The
 .Nm
-interfaces require certain configuration to work properly.
+interface can be configured with multiple IPv6 addresses including
+both 6to4 and 6rd.
+.Sh RFC3056 (a.k.a. 6to4)
 A single
-.Pq no more than one
-valid 6to4 address needs to be configured on the interface.
+.Pq no more than 1
+valid 6to4 address needs to be configured to the interface.
 .Dq A valid 6to4 address
-is an address which has the following properties.
+is an address which has the following properties.  For ordinary nodes
+in 6to4 site, you do not need
+.Nm
+interface; it is necessary only for site border router
+(called
+.Dq 6to4 router
+in the specification).
+.Pp
 If any of the following properties are not satisfied,
 .Nm stf
 raises a runtime error on packet transmission.
@@ -108,6 +114,78 @@ The
 .Nm
 interface will check the IPv4 source address on packets
 if the IPv6 prefix length is larger than 16.
+.Sh RFC5569 (a.k.a. 6rd)
+The
+.Nm
+interface works in the 6rd mode when one or more IPv6 addresses that
+consists of an IPv6 prefix and 32-bit IPv4 part with a prefix length
+equal to or shorter than 64.  In 6rd protocol, an IPv6 address
+.Li 2001:db8:c000:205::1/32
+means the following, for example:
+.Bl -bullet
+.It
+The 6rd relay prefix is
+.Li 2001:db8::/32 .
+.It
+The 6rd router's IPv4 address is
+.Li 192.0.2.5 .
+.El
+.Pp
+As you can see the IPv4 address is embedded in the IPv6 address just
+after the prefix.  While you can choose an IPv6 prefix length other
+than 32, it must be from 0 to 32.
+.Pp
+Assuming this address is configured on the
+.Nm
+interface, it does the following:
+.Bl -bullet
+.It
+An incoming IPv6 packet on
+.Nm
+will be encapsuled in an IPv4 packet with the source address
+.Li 192.0.2.5
+and then the IPv4 packet is delivered based on the IPv4 routing table.
+The IPv4 destination address is calculated from the destination
+address of the original IPv6 packet in the same way as the source.
+.It
+An incoming IPv4 packet which encapsules an IPv6 packet whose
+destination address matches a 6rd prefix with embedded IPv4 address
+configured on the
+.Nm
+interface, the IPv6 packet will be decapsulated and delivered based on
+the IPv6 routing table.  Note that
+.Nm
+interface normally has a route which covers whole range of a 6rd relay
+prefix, the delivered IPv6 packet can return to
+.Nm
+if there is no more specific route.  In that case, the returned packet
+will be discarded silently.
+.El
+.\" XXX: example configuration will be added
+.\" .Pp
+.\" By using this interface, you can configure a 6rd domain.  For simplicity,
+.\" we assume the following here:
+.\" .Bl -bullet
+.\" .It
+.\" A 6rd Customer, who has an IPv6/IPv4 LAN and an IPv4-only access
+.\" toward network of his Internet Service Provider.  The Customer has
+.\" a router called
+.\" .Dq CE Pq Customer Edge
+.\" Router, which can communicate between his LAN and the ISP over IPv4
+.\" and encapsulate  
+.\" his networks.
+.\" .It
+.\" A 6rd Provider, who provides IPv6 Internet reachability by using 6rd
+.\" protocol.  The Provider offers access to a router called
+.\" .Dq PE Pq Provider Edge
+.\" Router, which can communicate with 
+.\" .El
+.\" .Pp
+.\" A 6rd customer
+.\" needs to configure
+.\" .Nm
+.\" on his CE (Customer Edge) router.
+.Sh Other Functionality of the Interface
 .Pp
 .Nm
 can be configured to be ECN (Explicit Congestion Notification) friendly.
@@ -145,9 +223,6 @@ Packets with IPv4 multicast addresses as
 Packets with limited broadcast addresses as outer IPv4 source/destination
 .Pq Li 255.0.0.0/8
 .It
-Packets with private addresses as outer IPv4 source/destination
-.Pq Li 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
-.It
 Packets with IPv4 link-local addresses as outer IPv4 source/destination
 .Pq Li 169.254.0.0/16
 .It
@@ -171,6 +246,11 @@ Packets with node-local or link-local mu
 inner IPv6 source/destination
 .El
 .Pp
+In addition to them, packets with private address as outer IPv4
+source/destination
+.Pq Li 10.0.0.0/8 , 172.16.0.0/12 , 192.168.0.0/16
+are filtered out only in the 6to4 mode.
+.Pp
 It is recommended to filter/audit
 incoming IPv4 packets with IP protocol number 41, as necessary.
 It is also recommended to filter/audit encapsulated IPv6 packets as well.
Index: sys/net/if_stf.c
===================================================================
RCS file: /cvsroot/src/sys/net/if_stf.c,v
retrieving revision 1.77
diff -u -p -r1.77 if_stf.c
--- sys/net/if_stf.c    28 Oct 2011 20:13:32 -0000      1.77
+++ sys/net/if_stf.c    9 Jun 2012 04:58:04 -0000
@@ -3,6 +3,7 @@
 
 /*
  * Copyright (C) 2000 WIDE Project.
+ * Copyright (c) 2010 Hiroki Sato <hrs%FreeBSD.org@localhost>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,7 +32,7 @@
  */
 
 /*
- * 6to4 interface, based on RFC3056.
+ * 6to4 interface, based on RFC3056 + 6rd (RFC5569) support.
  *
  * 6to4 interface is NOT capable of link-layer (I mean, IPv4) multicasting.
  * There is no address mapping defined from IPv6 multicast address to IPv4
@@ -72,6 +73,12 @@
  * http://playground.iijlab.net/i-d/draft-itojun-ipv6-transition-abuse-00.txt
  * for details.  The code tries to filter out some of malicious packets.
  * Note that there is no way to be 100% secure.
+ *
+ * 6rd (RFC5569) extension is enabled when an IPv6 GUA other than
+ * 2002::/16 is assigned.  The stf(4) recognizes a 32-bit just after
+ * prefixlen as the IPv4 address of the 6rd customer site.  The
+ * prefixlen must be shorter than 32.
+ *
  */
 
 #include <sys/cdefs.h>
@@ -124,6 +131,24 @@ __KERNEL_RCSID(0, "$NetBSD: if_stf.c,v 1
 #include <net/if_gif.h>
 #endif
 
+#define        STF_DEBUG 0
+#define ip_sprintf(buf, a)                                             \
+       sprintf(buf, "%d.%d.%d.%d",                                     \
+               (ntohl((a)->s_addr)>>24)&0xFF,                          \
+               (ntohl((a)->s_addr)>>16)&0xFF,                          \
+               (ntohl((a)->s_addr)>>8)&0xFF,                           \
+               (ntohl((a)->s_addr))&0xFF);
+#if STF_DEBUG
+int stf_debug = 0;
+#define DEBUG_PRINTF(a, ...)                                           \
+       do {                                                            \
+               if (stf_debug >= a)                                     \
+                       printf(__VA_ARGS__);                            \
+       } while (0)
+#else
+#define DEBUG_PRINTF(a, ...)
+#endif
+
 #define IN6_IS_ADDR_6TO4(x)    (ntohs((x)->s6_addr16[0]) == 0x2002)
 #define GET_V4(x)      ((const struct in_addr *)(&(x)->s6_addr16[1]))
 
@@ -169,6 +194,16 @@ static int stf_checkaddr6(struct stf_sof
        struct ifnet *);
 static void stf_rtrequest(int, struct rtentry *, const struct rt_addrinfo *);
 static int stf_ioctl(struct ifnet *, u_long, void *);
+#define STF_GETIN4_USE_CACHE   1
+static struct sockaddr_in *stf_getin4addr(struct sockaddr_in *,
+                                         struct ifaddr *,
+                                         int);
+static struct sockaddr_in *stf_getin4addr_in6(struct sockaddr_in *,
+                                             struct ifaddr *,
+                                             const struct in6_addr *);
+static struct sockaddr_in *stf_getin4addr_sin6(struct sockaddr_in *,
+                                              struct ifaddr *,
+                                              struct sockaddr_in6 *);
 
 /* ARGSUSED */
 void
@@ -234,9 +269,13 @@ stf_encapcheck(struct mbuf *m, int off, 
 {
        struct ip ip;
        struct in6_ifaddr *ia6;
+       struct sockaddr_in ia6_in4addr;
+       struct sockaddr_in ia6_in4mask;
+       struct sockaddr_in *sin;
        struct stf_softc *sc;
-       struct in_addr a, b;
+       int ret = 0;
 
+       DEBUG_PRINTF(1, "%s: enter\n", __func__);
        sc = (struct stf_softc *)arg;
        if (sc == NULL)
                return 0;
@@ -256,35 +295,107 @@ stf_encapcheck(struct mbuf *m, int off, 
        if (ip.ip_v != 4)
                return 0;
 
+       /* Lookup an ia6 whose IPv4 addr encoded in the IPv6 addr is valid. */
        ia6 = stf_getsrcifa6(&sc->sc_if);
        if (ia6 == NULL)
                return 0;
+       sin = stf_getin4addr(&ia6_in4addr, &ia6->ia_ifa, STF_GETIN4_USE_CACHE);
+       if (sin == NULL)
+               return 0;
 
+#if STF_DEBUG
+       {
+               char buf[INET6_ADDRSTRLEN + 1];
+               memset(&buf, 0, sizeof(buf));
+
+               DEBUG_PRINTF(1, "%s: ia6->ia_ifa.ifa_addr = %s\n", __func__,
+                            
ip6_sprintf(&satosin6(ia6->ia_ifa.ifa_addr)->sin6_addr));
+               DEBUG_PRINTF(1, "%s: ia6->ia_addr = %s\n", __func__,
+                            ip6_sprintf(&ia6->ia_addr.sin6_addr));
+               DEBUG_PRINTF(1, "%s: ia6->ia_ifa.ifa_netmask = %s\n", __func__,
+                            
ip6_sprintf(&satosin6(ia6->ia_ifa.ifa_netmask)->sin6_addr));
+               DEBUG_PRINTF(1, "%s: ia6->ia_prefixmask = %s\n", __func__,
+                            ip6_sprintf(&ia6->ia_prefixmask.sin6_addr));
+
+               ip_sprintf(buf, &ia6_in4addr.sin_addr);
+               DEBUG_PRINTF(1, "%s: ia6_in4addr.sin_addr = %s\n", __func__, 
buf);
+               ip_sprintf(buf, &ip.ip_src);
+               DEBUG_PRINTF(1, "%s: ip.ip_src = %s\n", __func__, buf);
+               ip_sprintf(buf, &ip.ip_dst);
+               DEBUG_PRINTF(1, "%s: ip.ip_dst = %s\n", __func__, buf);
+       }
+#endif
        /*
         * check if IPv4 dst matches the IPv4 address derived from the
         * local 6to4 address.
         * success on: dst = 10.1.1.1, ia6->ia_addr = 2002:0a01:0101:...
         */
-       if (memcmp(GET_V4(&ia6->ia_addr.sin6_addr), &ip.ip_dst,
-           sizeof(ip.ip_dst)) != 0)
-               return 0;
+       DEBUG_PRINTF(1, "%s: check1: ia6_in4addr.sin_addr == ip.ip_dst?\n", 
__func__);
+       if (ia6_in4addr.sin_addr.s_addr != ip.ip_dst.s_addr) {
+               DEBUG_PRINTF(1, "%s: check1: false.  Ignore this packet.\n", 
__func__);
+               goto freeit;
+       }
+
+       DEBUG_PRINTF(1, "%s: check2: ia6->ia_addr is 2002::/16?\n", __func__);
+       if (IN6_IS_ADDR_6TO4(&ia6->ia_addr.sin6_addr)) {
+               /* 6to4 (RFC 3056) */
+               /*
+                * check if IPv4 src matches the IPv4 address derived
+                * from the local 6to4 address masked by prefixmask.
+                * success on: src = 10.1.1.1, ia6->ia_addr = 2002:0a00:.../24
+                * fail on: src = 10.1.1.1, ia6->ia_addr = 2002:0b00:.../24
+                */
+               DEBUG_PRINTF(1, "%s: check2: true.\n", __func__);
+
+               memcpy(&ia6_in4mask.sin_addr,
+                      GET_V4(&ia6->ia_prefixmask.sin6_addr),
+                      sizeof(ia6_in4mask));
+#if STF_DEBUG
+               {
+                       char buf[INET6_ADDRSTRLEN + 1];
+                       memset(&buf, 0, sizeof(buf));
+
+                       ip_sprintf(buf, &ia6_in4addr.sin_addr);
+                       DEBUG_PRINTF(1, "%s: ia6->ia_addr = %s\n",
+                                    __func__, buf);
+                       ip_sprintf(buf, &ip.ip_src);
+                       DEBUG_PRINTF(1, "%s: ip.ip_src = %s\n",
+                                    __func__, buf);
+                       ip_sprintf(buf, &ia6_in4mask.sin_addr);
+                       DEBUG_PRINTF(1, "%s: ia6->ia_prefixmask = %s\n",
+                                    __func__, buf);
 
-       /*
-        * check if IPv4 src matches the IPv4 address derived from the
-        * local 6to4 address masked by prefixmask.
-        * success on: src = 10.1.1.1, ia6->ia_addr = 2002:0a00:.../24
-        * fail on: src = 10.1.1.1, ia6->ia_addr = 2002:0b00:.../24
-        */
-       memset(&a, 0, sizeof(a));
-       a.s_addr = GET_V4(&ia6->ia_addr.sin6_addr)->s_addr;
-       a.s_addr &= GET_V4(&ia6->ia_prefixmask.sin6_addr)->s_addr;
-       b = ip.ip_src;
-       b.s_addr &= GET_V4(&ia6->ia_prefixmask.sin6_addr)->s_addr;
-       if (a.s_addr != b.s_addr)
-               return 0;
+                       DEBUG_PRINTF(1, "%s: check3: ia6_in4addr.sin_addr & 
mask == ip.ip_src & mask\n",
+                                    __func__);
+               }
+#endif
 
+               if ((ia6_in4addr.sin_addr.s_addr & ia6_in4mask.sin_addr.s_addr) 
!=
+                   (ip.ip_src.s_addr & ia6_in4mask.sin_addr.s_addr)) {
+                       DEBUG_PRINTF(1, "%s: check3: false.  Ignore this 
packet.\n",
+                                    __func__);
+                       goto freeit;
+               }
+       } else {
+               /* 6rd (RFC 5569) */
+               DEBUG_PRINTF(1, "%s: check2: false.  6rd.\n", __func__);
+               /*
+                * No restriction on the src address in the case of
+                * 6rd because the stf(4) interface always has a
+                * prefix which covers whole of IPv4 src address
+                * range.  So, stf_output() will catch all of
+                * 6rd-capsuled IPv4 traffic with suspicious inner dst
+                * IPv4 address (i.e. the IPv6 destination address is
+                * one the admin does not like to route to outside),
+                * and then it discard them silently.
+                */
+       }
+       DEBUG_PRINTF(1, "%s: all clear!\n", __func__);
        /* stf interface makes single side match only */
-       return 32;
+       ret = 32;
+freeit:
+
+       return (ret);
 }
 
 static struct in6_ifaddr *
@@ -292,8 +403,8 @@ stf_getsrcifa6(struct ifnet *ifp)
 {
        struct ifaddr *ifa;
        struct in_ifaddr *ia4;
-       struct sockaddr_in6 *sin6;
-       struct in_addr in;
+       struct sockaddr_in *sin;
+       struct sockaddr_in in4;
 
        IFADDR_FOREACH(ifa, ifp)
        {
@@ -301,15 +412,27 @@ stf_getsrcifa6(struct ifnet *ifp)
                        continue;
                if (ifa->ifa_addr->sa_family != AF_INET6)
                        continue;
-               sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
-               if (!IN6_IS_ADDR_6TO4(&sin6->sin6_addr))
+               if ((sin = stf_getin4addr(&in4, ifa,
+                                         STF_GETIN4_USE_CACHE)) == NULL)
                        continue;
 
-               memcpy(&in, GET_V4(&sin6->sin6_addr), sizeof(in));
-               INADDR_TO_IA(in, ia4);
-               if (ia4 == NULL)
+               INADDR_TO_IA(sin->sin_addr, ia4);
+               if (ia4 == NULL)
                        continue;
 
+#if STF_DEBUG
+               {
+                       char buf[INET6_ADDRSTRLEN + 1];
+                       memset(&buf, 0, sizeof(buf));
+
+                       DEBUG_PRINTF(1, "%s: ifa->ifa_addr->sin6_addr = %s\n",
+                                    __func__,
+                                    ip6_sprintf(&((struct sockaddr_in6 
*)ifa->ifa_addr)->sin6_addr));
+                       ip_sprintf(buf, &ia4->ia_addr.sin_addr);
+                       DEBUG_PRINTF(1, "%s: ia4->ia_addr.sin_addr = %s\n",
+                                    __func__, buf);
+               }
+#endif
                return (struct in6_ifaddr *)ifa;
        }
 
@@ -323,7 +446,8 @@ stf_output(struct ifnet *ifp, struct mbu
        struct rtentry *rt;
        struct stf_softc *sc;
        const struct sockaddr_in6 *dst6;
-       const struct in_addr *in4;
+       struct sockaddr_in *sin;
+       struct sockaddr_in in4;
        uint8_t tos;
        struct ip *ip;
        struct ip6_hdr *ip6;
@@ -367,17 +491,28 @@ stf_output(struct ifnet *ifp, struct mbu
        /*
         * Pickup the right outer dst addr from the list of candidates.
         * ip6_dst has priority as it may be able to give us shorter IPv4 hops.
+        *   ip6_dst: destination addr in the packet header.
+        *   dst6: destination addr specified in function argument.
         */
-       if (IN6_IS_ADDR_6TO4(&ip6->ip6_dst))
-               in4 = GET_V4(&ip6->ip6_dst);
-       else if (IN6_IS_ADDR_6TO4(&dst6->sin6_addr))
-               in4 = GET_V4(&dst6->sin6_addr);
-       else {
+       DEBUG_PRINTF(1, "%s: dst addr selection\n", __func__);
+       sin = stf_getin4addr_in6(&in4, &ia6->ia_ifa, &ip6->ip6_dst);
+       if (sin == NULL)
+               sin = stf_getin4addr_in6(&in4, &ia6->ia_ifa, &dst6->sin6_addr);
+       if (sin == NULL) {
                m_freem(m);
                ifp->if_oerrors++;
                return ENETUNREACH;
        }
 
+#if STF_DEBUG
+       {
+               char buf[INET6_ADDRSTRLEN + 1];
+               memset(&buf, 0, sizeof(buf));
+ 
+               ip_sprintf(buf, &sin->sin_addr);
+               DEBUG_PRINTF(1, "%s: ip_dst = %s\n", __func__, buf);
+       }
+#endif
        bpf_mtap_af(ifp, AF_INET6, m);
 
        M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
@@ -390,10 +525,25 @@ stf_output(struct ifnet *ifp, struct mbu
        ip = mtod(m, struct ip *);
 
        memset(ip, 0, sizeof(*ip));
+       bcopy(&in4.sin_addr, &ip->ip_dst, sizeof(ip->ip_dst));
+
+       sin = stf_getin4addr_sin6(&in4, &ia6->ia_ifa, &ia6->ia_addr);
+       if (sin == NULL) {
+               /* IFAFREE(&ia6->ia_ifa); */
+               m_freem(m);
+               ifp->if_oerrors++;
+               return ENETUNREACH;
+       }
+       bcopy(&in4.sin_addr, &ip->ip_src, sizeof(ip->ip_src));
+#if STF_DEBUG
+       {
+               char buf[INET6_ADDRSTRLEN + 1];
+               memset(&buf, 0, sizeof(buf));
 
-       bcopy(GET_V4(&((struct sockaddr_in6 *)&ia6->ia_addr)->sin6_addr),
-           &ip->ip_src, sizeof(ip->ip_src));
-       memcpy(&ip->ip_dst, in4, sizeof(ip->ip_dst));
+               ip_sprintf(buf, &ip->ip_src);
+               DEBUG_PRINTF(1, "%s: ip_src = %s\n", __func__, buf);
+       }
+#endif
        ip->ip_p = IPPROTO_IPV6;
        ip->ip_ttl = ip_gif_ttl;        /*XXX*/
        ip->ip_len = htons(m->m_pkthdr.len);
@@ -419,6 +569,7 @@ stf_output(struct ifnet *ifp, struct mbu
 
        ifp->if_opackets++;
        ifp->if_obytes += m->m_pkthdr.len - sizeof(struct ip);
+       DEBUG_PRINTF(1, "%s: ip_output dispatch.\n", __func__);
        return ip_output(m, NULL, &sc->sc_ro, 0, NULL, NULL);
 }
 
@@ -455,13 +606,6 @@ stf_checkaddr4(struct stf_softc *sc, con
        }
 
        /*
-        * reject packets with private address range.
-        * (requirement from RFC3056 section 2 1st paragraph)
-        */
-       if (isrfc1918addr(in))
-               return -1;
-
-       /*
         * reject packet with IPv4 link-local (169.254.0.0/16),
         * as suggested in draft-savola-v6ops-6to4-security-00.txt
         */
@@ -489,7 +633,7 @@ stf_checkaddr4(struct stf_softc *sc, con
 
                memset(&sin, 0, sizeof(sin));
                sin.sin_family = AF_INET;
-               sin.sin_len = sizeof(struct sockaddr_in);
+               sin.sin_len = sizeof(sin);
                sin.sin_addr = *in;
                rt = rtalloc1((struct sockaddr *)&sin, 0);
                if (!rt || rt->rt_ifp != inifp) {
@@ -557,6 +701,8 @@ in_stf_input(struct mbuf *m, ...)
        struct ifqueue *ifq = NULL;
        struct ifnet *ifp;
        va_list ap;
+       struct sockaddr_in6 sin6;
+       struct rtentry *rt;
 
        va_start(ap, m);
        off = va_arg(ap, int);
@@ -609,6 +755,40 @@ in_stf_input(struct mbuf *m, ...)
                return;
        }
 
+       /*
+        * reject packets with private address range.
+        * (requirement from RFC3056 section 2 1st paragraph)
+        */
+       if ((IN6_IS_ADDR_6TO4(&ip6->ip6_src) && isrfc1918addr(&ip->ip_src)) ||
+           (IN6_IS_ADDR_6TO4(&ip6->ip6_dst) && isrfc1918addr(&ip->ip_dst))) {
+               m_freem(m);
+               return;
+       }
+
+       /*
+        * Ignore if the destination is the same stf interface because
+        * all of valid IPv6 outgoing traffic should go interfaces
+        * except for it.
+        */
+       memset(&sin6, 0, sizeof(sin6));
+       sin6.sin6_family = AF_INET6;
+       sin6.sin6_len = sizeof(sin6);
+       memcpy(&sin6.sin6_addr, &ip6->ip6_dst, sizeof(sin6.sin6_addr));
+       rt = rtalloc1((struct sockaddr *)&sin6, 0);
+       if (rt == NULL) {
+               DEBUG_PRINTF(1, "%s: no IPv6 dst.  Ignored.\n", __func__);
+               m_free(m);
+               return;
+       }
+       if ((rt->rt_ifp == ifp) &&
+           (!IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &sin6.sin6_addr))) {
+               DEBUG_PRINTF(1, "%s: IPv6 dst is the same stf.  Ignored.\n", 
__func__);
+               rtfree(rt);
+               m_free(m);
+               return;
+       }
+       rtfree(rt);
+
        itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
        if ((ifp->if_flags & IFF_LINK1) != 0)
                ip_ecn_egress(ECN_ALLOWED, &otos, &itos);
@@ -657,12 +837,209 @@ stf_rtrequest(int cmd, struct rtentry *r
        }
 }
 
+static struct sockaddr_in *
+stf_getin4addr_in6(struct sockaddr_in *sin,
+                  struct ifaddr *ifa,
+                  const struct in6_addr *in6)
+{
+       struct sockaddr_in6 sin6;
+
+       DEBUG_PRINTF(1, "%s: enter.\n", __func__);
+       if (ifa == NULL || in6 == NULL)
+               return NULL;
+
+       memset(&sin6, 0, sizeof(sin6));
+       memcpy(&sin6.sin6_addr, in6, sizeof(sin6.sin6_addr));
+       sin6.sin6_len = sizeof(sin6);
+       sin6.sin6_family = AF_INET6;
+
+       return(stf_getin4addr_sin6(sin, ifa, &sin6));
+}
+
+static struct sockaddr_in *
+stf_getin4addr_sin6(struct sockaddr_in *sin,
+                   struct ifaddr *ifa,
+                   struct sockaddr_in6 *sin6)
+{
+       struct in6_ifaddr ia6;
+       int i;
+
+       DEBUG_PRINTF(1, "%s: enter.\n", __func__);
+       if (ifa == NULL || sin6 == NULL)
+               return NULL;
+
+       memset(&ia6, 0, sizeof(ia6));
+       memcpy(&ia6, ifatoia6(ifa), sizeof(ia6));
+
+       /*
+        * Use prefixmask information from ifa, and
+        * address information from sin6.
+        */
+       ia6.ia_addr.sin6_family = AF_INET6;
+       ia6.ia_ifa.ifa_addr = (struct sockaddr *)&ia6.ia_addr;
+       ia6.ia_ifa.ifa_dstaddr = NULL;
+       ia6.ia_ifa.ifa_netmask = (struct sockaddr *)&ia6.ia_prefixmask;
+
+#if STF_DEBUG
+       {
+               DEBUG_PRINTF(1, "%s: sin6->sin6_addr = %s\n", __func__,
+                            ip6_sprintf(&sin6->sin6_addr));
+               DEBUG_PRINTF(1, "%s: ia6.ia_addr.sin6_addr = %s\n", __func__,
+                            ip6_sprintf(&ia6.ia_addr.sin6_addr));
+               DEBUG_PRINTF(1, "%s: ia6.ia_prefixmask.sin6_addr = %s\n", 
__func__,
+                            ip6_sprintf(&ia6.ia_prefixmask.sin6_addr));
+       }
+#endif
+
+       /*
+        * When (src addr & src mask) != (dst (sin6) addr & src mask),
+        * the dst is not in the 6rd domain.  The IPv4 address must
+        * not be used.
+        */
+       for (i = 0; i < sizeof(ia6.ia_addr.sin6_addr); i++) {
+               if ((((u_char *)&ia6.ia_addr.sin6_addr)[i] &
+                    ((u_char *)&ia6.ia_prefixmask.sin6_addr)[i])
+                   !=
+                   (((u_char *)&sin6->sin6_addr)[i] &
+                    ((u_char *)&ia6.ia_prefixmask.sin6_addr)[i]))
+                       return NULL;
+       }
+
+       /* After the mask check, overwrite ia6.ia_addr with sin6. */
+       memcpy(&ia6.ia_addr, sin6, sizeof(ia6.ia_addr));
+       return(stf_getin4addr(sin, (struct ifaddr *)&ia6, 0));
+}
+
+static struct sockaddr_in *
+stf_getin4addr(struct sockaddr_in *sin,
+              struct ifaddr *ifa,
+              int flags)
+{
+       struct in_addr *in;
+       struct sockaddr_in6 *sin6;
+       struct in6_ifaddr *ia6;
+
+       DEBUG_PRINTF(1, "%s: enter.\n", __func__);
+       if (ifa == NULL ||
+           ifa->ifa_addr == NULL ||
+           ifa->ifa_addr->sa_family != AF_INET6)
+               return NULL;
+
+       sin6 = satosin6(ifa->ifa_addr);
+       ia6 = ifatoia6(ifa);
+
+       if ((flags & STF_GETIN4_USE_CACHE) &&
+           (ifa->ifa_dstaddr != NULL) &&
+           (ifa->ifa_dstaddr->sa_family == AF_INET)) {
+               /*
+                * XXX: ifa_dstaddr is used as a cache of the
+                * extracted IPv4 address.
+                */
+               memcpy(sin, satosin(ifa->ifa_dstaddr), sizeof(*sin));
+#if STF_DEBUG
+               {
+                       char buf[INET6_ADDRSTRLEN + 1];
+                       memset(&buf, 0, sizeof(buf));
+
+                       ip_sprintf(buf, &sin->sin_addr);
+                       DEBUG_PRINTF(1, "%s: cached address was used = %s\n", 
__func__, buf);
+               }
+#endif
+               return (sin);
+       }
+       memset(sin, 0, sizeof(*sin));
+       in = &sin->sin_addr;
+
+#if STF_DEBUG
+       {
+               DEBUG_PRINTF(1, "%s: sin6->sin6_addr = %s\n", __func__,
+                   ip6_sprintf(&sin6->sin6_addr));
+       }
+#endif
+
+       if (IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) {
+               /* 6to4 (RFC 3056) */
+               bcopy(GET_V4(&sin6->sin6_addr), in, sizeof(*in));
+               if (isrfc1918addr(in))
+                       return NULL;
+       } else {
+               /* 6rd (RFC 5569) */
+               struct in6_addr buf;
+               u_char *p = (u_char *)&buf;
+               u_char *q = (u_char *)in;
+               u_int residue = 0;
+               u_char mask;
+               int i;
+               u_int plen;
+
+               /*
+                * 6rd-relays IPv6 prefix is located at a 32-bit just
+                * after the prefix edge.
+                */
+               plen = in6_mask2len(&satosin6(ifa->ifa_netmask)->sin6_addr, 
NULL);
+               if (32 < plen)
+                       return NULL;
+
+               memcpy(&buf, &sin6->sin6_addr, sizeof(buf));
+               p += plen / 8;
+               residue = plen % 8;
+               mask = ~((u_char)(-1) >> residue);
+
+               /*
+                * The p points head of the IPv4 address part in
+                * bytes.  The residue is a bit-shift factor when
+                * prefixlen is not a multiple of 8.
+                */
+               for (i = 0; i < 4; i++) {
+                       DEBUG_PRINTF(2, "p[%d] = %d\n", i, p[i]);
+                       DEBUG_PRINTF(2, "residue = %d\n", residue);
+                       if (residue) {
+                               p[i] <<= residue;
+                               DEBUG_PRINTF(2, "p[%d] << residue = %d\n",
+                                            i, p[i]);
+                               DEBUG_PRINTF(2, "mask = %x\n",
+                                            mask);
+                               DEBUG_PRINTF(2, "p[%d + 1] & mask = %d\n",
+                                            i, p[i + 1] & mask);
+                               DEBUG_PRINTF(2, "p[%d + 1] & mask >> (8 - 
residue) = %d\n",
+                                            i, (p[i + 1] & mask) >> 
(8-residue));
+                               p[i] |= ((p[i+1] & mask) >> (8 - residue));
+                       }
+                       q[i] = p[i];
+               }
+       }
+#if STF_DEBUG
+       {
+               char buf[INET6_ADDRSTRLEN + 1];
+               memset(&buf, 0, sizeof(buf));
+
+               ip_sprintf(buf, in);
+               DEBUG_PRINTF(1, "%s: in->in_addr = %s\n", __func__, buf);
+               DEBUG_PRINTF(1, "%s: leave\n", __func__);
+       }
+#endif
+       if (flags & STF_GETIN4_USE_CACHE) {
+               DEBUG_PRINTF(1, "%s: try to access ifa->ifa_dstaddr.\n", 
__func__);
+               ifa->ifa_dstaddr = (struct sockaddr *)&ia6->ia_dstaddr;
+               DEBUG_PRINTF(1, "%s: try to memset 0 to ia_dstaddr.\n", 
__func__);
+               memset(&ia6->ia_dstaddr, 0, sizeof(ia6->ia_dstaddr));
+               DEBUG_PRINTF(1, "%s: try to memcpy ifa->ifa_dstaddr.\n", 
__func__);
+               memcpy((struct sockaddr_in *)ifa->ifa_dstaddr,
+                      sin, sizeof(struct sockaddr_in));
+               DEBUG_PRINTF(1, "%s: try to set sa_family.\n", __func__);
+               ifa->ifa_dstaddr->sa_family = AF_INET;
+               DEBUG_PRINTF(1, "%s: in->in_addr is stored in ifa_dstaddr.\n",
+                            __func__);
+       }
+       return (sin);
+}
+
 static int
 stf_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
        struct ifaddr           *ifa;
        struct ifreq            *ifr = data;
-       struct sockaddr_in6     *sin6;
+       struct sockaddr_in      in4;
        int                     error;
 
        error = 0;
@@ -673,9 +1050,7 @@ stf_ioctl(struct ifnet *ifp, u_long cmd,
                        error = EAFNOSUPPORT;
                        break;
                }
-               sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
-               if (IN6_IS_ADDR_6TO4(&sin6->sin6_addr) &&
-                   !isrfc1918addr(GET_V4(&sin6->sin6_addr))) {
+               if (stf_getin4addr(&in4, ifa, 0) != NULL) {
                        ifa->ifa_rtrequest = stf_rtrequest;
                        ifp->if_flags |= IFF_UP;
                } else


Home | Main Index | Thread Index | Old Index