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