mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-18 10:35:55 +00:00
4739b8076f
Now most big problem of IPv6 is getting IPv6 address assignment. 6to4 solve the problem. 6to4 addr is defined like below, 2002: 4byte v4 addr : 2byte SLA ID : 8byte interface ID The most important point of the address format is that an IPv4 addr is embeded in it. So any user who has IPv4 addr can get IPv6 address block with 2byte subnet space. Also, the IPv4 addr is used for semi-automatic IPv6 over IPv4 tunneling. With 6to4, getting IPv6 addr become dramatically easy. The attached patch enable 6to4 extension, and confirmed to work, between "Richard Seaman, Jr." <dick@tar.com> and me. Approved by: jkh Reviewed by: itojun
351 lines
8.4 KiB
C
351 lines
8.4 KiB
C
/*
|
|
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the project nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
/*
|
|
* in_gif.c
|
|
*/
|
|
|
|
#include "opt_mrouting.h"
|
|
#include "opt_inet6.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/protosw.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/route.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/ip.h>
|
|
#ifdef INET6
|
|
#include <netinet/ip6.h>
|
|
#endif
|
|
#include <netinet/ip_var.h>
|
|
#include <netinet/in_gif.h>
|
|
#include <netinet/ip_ecn.h>
|
|
#ifdef INET6
|
|
#include <netinet6/ip6_ecn.h>
|
|
#endif
|
|
|
|
#ifdef MROUTING
|
|
#include <netinet/ip_mroute.h>
|
|
#endif /* MROUTING */
|
|
|
|
#include <net/if_gif.h>
|
|
|
|
#include "gif.h"
|
|
|
|
#include <machine/stdarg.h>
|
|
|
|
#include <net/net_osdep.h>
|
|
|
|
#if NGIF > 0
|
|
int ip_gif_ttl = GIF_TTL;
|
|
#else
|
|
int ip_gif_ttl = 0;
|
|
#endif
|
|
|
|
SYSCTL_DECL(_net_inet_ip);
|
|
SYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW,
|
|
&ip_gif_ttl, 0, "");
|
|
|
|
#define IN6_IS_ADDR_6TO4(x) (ntohs((x)->s6_addr16[0]) == 0x2002)
|
|
#define GET_V4(x) ((struct in_addr *)(&(x)->s6_addr16[1]))
|
|
|
|
int
|
|
in_gif_output(ifp, family, m, rt)
|
|
struct ifnet *ifp;
|
|
int family;
|
|
struct mbuf *m;
|
|
struct rtentry *rt;
|
|
{
|
|
register struct gif_softc *sc = (struct gif_softc*)ifp;
|
|
struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst;
|
|
struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc;
|
|
struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst;
|
|
struct ip iphdr; /* capsule IP header, host byte ordered */
|
|
int proto, error;
|
|
u_int8_t tos;
|
|
#ifdef INET6
|
|
struct ip6_hdr *ip6 = NULL;
|
|
#endif
|
|
|
|
if (sin_src == NULL || sin_dst == NULL ||
|
|
sin_src->sin_family != AF_INET ||
|
|
sin_dst->sin_family != AF_INET) {
|
|
m_freem(m);
|
|
return EAFNOSUPPORT;
|
|
}
|
|
|
|
switch (family) {
|
|
case AF_INET:
|
|
{
|
|
struct ip *ip;
|
|
|
|
proto = IPPROTO_IPV4;
|
|
if (m->m_len < sizeof(*ip)) {
|
|
m = m_pullup(m, sizeof(*ip));
|
|
if (!m)
|
|
return ENOBUFS;
|
|
}
|
|
ip = mtod(m, struct ip *);
|
|
tos = ip->ip_tos;
|
|
break;
|
|
}
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
{
|
|
proto = IPPROTO_IPV6;
|
|
if (m->m_len < sizeof(*ip6)) {
|
|
m = m_pullup(m, sizeof(*ip6));
|
|
if (!m)
|
|
return ENOBUFS;
|
|
}
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
|
|
break;
|
|
}
|
|
#endif /*INET6*/
|
|
default:
|
|
#ifdef DIAGNOSTIC
|
|
printf("in_gif_output: warning: unknown family %d passed\n",
|
|
family);
|
|
#endif
|
|
m_freem(m);
|
|
return EAFNOSUPPORT;
|
|
}
|
|
|
|
bzero(&iphdr, sizeof(iphdr));
|
|
iphdr.ip_src = sin_src->sin_addr;
|
|
#ifdef INET6
|
|
/* XXX: temporal stf support hack */
|
|
if (bcmp(ifp->if_name, "stf", 3) == 0 && ip6 != NULL) {
|
|
if (IN6_IS_ADDR_6TO4(&ip6->ip6_dst))
|
|
iphdr.ip_dst = *GET_V4(&ip6->ip6_dst);
|
|
else if (rt && rt->rt_gateway->sa_family == AF_INET6) {
|
|
struct in6_addr *dst6;
|
|
|
|
dst6 = &((struct sockaddr_in6 *)
|
|
(rt->rt_gateway))->sin6_addr;
|
|
if (IN6_IS_ADDR_6TO4(dst6))
|
|
iphdr.ip_dst = *GET_V4(dst6);
|
|
else {
|
|
m_freem(m);
|
|
return ENETUNREACH;
|
|
}
|
|
} else {
|
|
m_freem(m);
|
|
return ENETUNREACH;
|
|
}
|
|
} else
|
|
#endif
|
|
if (ifp->if_flags & IFF_LINK0) {
|
|
/* multi-destination mode */
|
|
if (sin_dst->sin_addr.s_addr != INADDR_ANY)
|
|
iphdr.ip_dst = sin_dst->sin_addr;
|
|
else if (rt) {
|
|
iphdr.ip_dst = ((struct sockaddr_in *)
|
|
(rt->rt_gateway))->sin_addr;
|
|
} else {
|
|
m_freem(m);
|
|
return ENETUNREACH;
|
|
}
|
|
} else {
|
|
/* bidirectional configured tunnel mode */
|
|
if (sin_dst->sin_addr.s_addr != INADDR_ANY)
|
|
iphdr.ip_dst = sin_dst->sin_addr;
|
|
else {
|
|
m_freem(m);
|
|
return ENETUNREACH;
|
|
}
|
|
}
|
|
iphdr.ip_p = proto;
|
|
/* version will be set in ip_output() */
|
|
iphdr.ip_ttl = ip_gif_ttl;
|
|
iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip);
|
|
if (ifp->if_flags & IFF_LINK1)
|
|
ip_ecn_ingress(ECN_ALLOWED, &iphdr.ip_tos, &tos);
|
|
|
|
/* prepend new IP header */
|
|
M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
|
|
if (m && m->m_len < sizeof(struct ip))
|
|
m = m_pullup(m, sizeof(struct ip));
|
|
if (m == NULL) {
|
|
printf("ENOBUFS in in_gif_output %d\n", __LINE__);
|
|
return ENOBUFS;
|
|
}
|
|
|
|
*(mtod(m, struct ip *)) = iphdr;
|
|
|
|
if (dst->sin_family != sin_dst->sin_family ||
|
|
dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) {
|
|
/* cache route doesn't match */
|
|
dst->sin_family = sin_dst->sin_family;
|
|
dst->sin_len = sizeof(struct sockaddr_in);
|
|
dst->sin_addr = sin_dst->sin_addr;
|
|
if (sc->gif_ro.ro_rt) {
|
|
RTFREE(sc->gif_ro.ro_rt);
|
|
sc->gif_ro.ro_rt = NULL;
|
|
}
|
|
}
|
|
|
|
if (sc->gif_ro.ro_rt == NULL) {
|
|
rtalloc(&sc->gif_ro);
|
|
if (sc->gif_ro.ro_rt == NULL) {
|
|
m_freem(m);
|
|
return ENETUNREACH;
|
|
}
|
|
}
|
|
|
|
error = ip_output(m, 0, &sc->gif_ro, 0, 0);
|
|
return(error);
|
|
}
|
|
|
|
void
|
|
in_gif_input(struct mbuf *m, int off, int proto)
|
|
{
|
|
struct gif_softc *sc;
|
|
struct ifnet *gifp = NULL;
|
|
struct ip *ip;
|
|
int i, af;
|
|
u_int8_t otos;
|
|
|
|
ip = mtod(m, struct ip *);
|
|
|
|
/* this code will be soon improved. */
|
|
#define satosin(sa) ((struct sockaddr_in *)(sa))
|
|
for (i = 0, sc = gif; i < ngif; i++, sc++) {
|
|
if (sc->gif_psrc == NULL
|
|
|| sc->gif_pdst == NULL
|
|
|| sc->gif_psrc->sa_family != AF_INET
|
|
|| sc->gif_pdst->sa_family != AF_INET) {
|
|
continue;
|
|
}
|
|
|
|
if ((sc->gif_if.if_flags & IFF_UP) == 0)
|
|
continue;
|
|
|
|
#ifdef INET6
|
|
/* XXX: temporal stf support hack */
|
|
if (proto == IPPROTO_IPV6 &&
|
|
bcmp(sc->gif_if.if_name, "stf", 3) == 0 &&
|
|
(satosin(sc->gif_psrc)->sin_addr.s_addr ==
|
|
ip->ip_dst.s_addr ||
|
|
IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) &&
|
|
satosin(sc->gif_pdst)->sin_addr.s_addr ==
|
|
INADDR_BROADCAST) {
|
|
gifp = &sc->gif_if;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if ((sc->gif_if.if_flags & IFF_LINK0)
|
|
&& satosin(sc->gif_psrc)->sin_addr.s_addr == ip->ip_dst.s_addr
|
|
&& satosin(sc->gif_pdst)->sin_addr.s_addr == INADDR_ANY) {
|
|
gifp = &sc->gif_if;
|
|
continue;
|
|
}
|
|
|
|
if (satosin(sc->gif_psrc)->sin_addr.s_addr == ip->ip_dst.s_addr
|
|
&& satosin(sc->gif_pdst)->sin_addr.s_addr == ip->ip_src.s_addr)
|
|
{
|
|
gifp = &sc->gif_if;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (gifp == NULL) {
|
|
#ifdef MROUTING
|
|
/* for backward compatibility */
|
|
if (proto == IPPROTO_IPV4) {
|
|
ipip_input(m, off, proto);
|
|
return;
|
|
}
|
|
#endif /*MROUTING*/
|
|
m_freem(m);
|
|
ipstat.ips_nogif++;
|
|
return;
|
|
}
|
|
|
|
otos = ip->ip_tos;
|
|
m_adj(m, off);
|
|
|
|
switch (proto) {
|
|
case IPPROTO_IPV4:
|
|
{
|
|
struct ip *ip;
|
|
af = AF_INET;
|
|
if (m->m_len < sizeof(*ip)) {
|
|
m = m_pullup(m, sizeof(*ip));
|
|
if (!m)
|
|
return;
|
|
}
|
|
ip = mtod(m, struct ip *);
|
|
if (gifp->if_flags & IFF_LINK1)
|
|
ip_ecn_egress(ECN_ALLOWED, &otos, &ip->ip_tos);
|
|
break;
|
|
}
|
|
#ifdef INET6
|
|
case IPPROTO_IPV6:
|
|
{
|
|
struct ip6_hdr *ip6;
|
|
u_int8_t itos;
|
|
af = AF_INET6;
|
|
if (m->m_len < sizeof(*ip6)) {
|
|
m = m_pullup(m, sizeof(*ip6));
|
|
if (!m)
|
|
return;
|
|
}
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
|
|
if (gifp->if_flags & IFF_LINK1)
|
|
ip_ecn_egress(ECN_ALLOWED, &otos, &itos);
|
|
ip6->ip6_flow &= ~htonl(0xff << 20);
|
|
ip6->ip6_flow |= htonl((u_int32_t)itos << 20);
|
|
break;
|
|
}
|
|
#endif /* INET6 */
|
|
default:
|
|
ipstat.ips_nogif++;
|
|
m_freem(m);
|
|
return;
|
|
}
|
|
gif_input(m, af, gifp);
|
|
return;
|
|
}
|