in6_gif.c revision 62587
162587Sitojun/* $FreeBSD: head/sys/netinet6/in6_gif.c 62587 2000-07-04 16:35:15Z itojun $ */ 262587Sitojun/* $KAME: in6_gif.c,v 1.37 2000/06/17 20:34:25 itojun Exp $ */ 362587Sitojun 454263Sshin/* 554263Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 654263Sshin * All rights reserved. 754263Sshin * 854263Sshin * Redistribution and use in source and binary forms, with or without 954263Sshin * modification, are permitted provided that the following conditions 1054263Sshin * are met: 1154263Sshin * 1. Redistributions of source code must retain the above copyright 1254263Sshin * notice, this list of conditions and the following disclaimer. 1354263Sshin * 2. Redistributions in binary form must reproduce the above copyright 1454263Sshin * notice, this list of conditions and the following disclaimer in the 1554263Sshin * documentation and/or other materials provided with the distribution. 1654263Sshin * 3. Neither the name of the project nor the names of its contributors 1754263Sshin * may be used to endorse or promote products derived from this software 1854263Sshin * without specific prior written permission. 1954263Sshin * 2054263Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2154263Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2254263Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2354263Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2454263Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2554263Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2654263Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2754263Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2854263Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2954263Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3054263Sshin * SUCH DAMAGE. 3154263Sshin */ 3254263Sshin 3354263Sshin/* 3454263Sshin * in6_gif.c 3554263Sshin */ 3654263Sshin 3754263Sshin#include "opt_inet.h" 3862587Sitojun#include "opt_inet6.h" 3954263Sshin 4054263Sshin#include <sys/param.h> 4154263Sshin#include <sys/systm.h> 4254263Sshin#include <sys/socket.h> 4354263Sshin#include <sys/sockio.h> 4454263Sshin#include <sys/mbuf.h> 4554263Sshin#include <sys/errno.h> 4654263Sshin 4754263Sshin#include <net/if.h> 4854263Sshin#include <net/route.h> 4954263Sshin 5054263Sshin#include <netinet/in.h> 5154263Sshin#include <netinet/in_systm.h> 5254263Sshin#ifdef INET 5354263Sshin#include <netinet/ip.h> 5454263Sshin#endif 5562587Sitojun#include <netinet/ip_encap.h> 5662587Sitojun#ifdef INET6 5762587Sitojun#include <netinet/ip6.h> 5854263Sshin#include <netinet6/ip6_var.h> 5954263Sshin#include <netinet6/in6_gif.h> 6062587Sitojun#include <netinet6/in6_var.h> 6162587Sitojun#endif 6255009Sshin#include <netinet/ip_ecn.h> 6362587Sitojun#ifdef INET6 6455009Sshin#include <netinet6/ip6_ecn.h> 6562587Sitojun#endif 6654263Sshin 6754263Sshin#include <net/if_gif.h> 6854263Sshin 6954263Sshin#include <net/net_osdep.h> 7054263Sshin 7162587Sitojun#ifndef offsetof 7262587Sitojun#define offsetof(s, e) ((int)&((s *)0)->e) 7362587Sitojun#endif 7462587Sitojun 7554263Sshinint 7654263Sshinin6_gif_output(ifp, family, m, rt) 7754263Sshin struct ifnet *ifp; 7854263Sshin int family; /* family of the packet to be encapsulate. */ 7954263Sshin struct mbuf *m; 8054263Sshin struct rtentry *rt; 8154263Sshin{ 8254263Sshin struct gif_softc *sc = (struct gif_softc*)ifp; 8354263Sshin struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst; 8454263Sshin struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc; 8554263Sshin struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst; 8654263Sshin struct ip6_hdr *ip6; 8754263Sshin int proto; 8855009Sshin u_int8_t itos, otos; 8954263Sshin 9054263Sshin if (sin6_src == NULL || sin6_dst == NULL || 9154263Sshin sin6_src->sin6_family != AF_INET6 || 9254263Sshin sin6_dst->sin6_family != AF_INET6) { 9354263Sshin m_freem(m); 9454263Sshin return EAFNOSUPPORT; 9554263Sshin } 9654263Sshin 9754263Sshin switch (family) { 9854263Sshin#ifdef INET 9954263Sshin case AF_INET: 10054263Sshin { 10154263Sshin struct ip *ip; 10254263Sshin 10354263Sshin proto = IPPROTO_IPV4; 10454263Sshin if (m->m_len < sizeof(*ip)) { 10554263Sshin m = m_pullup(m, sizeof(*ip)); 10654263Sshin if (!m) 10754263Sshin return ENOBUFS; 10854263Sshin } 10954263Sshin ip = mtod(m, struct ip *); 11055009Sshin itos = ip->ip_tos; 11154263Sshin break; 11254263Sshin } 11354263Sshin#endif 11462587Sitojun#ifdef INET6 11554263Sshin case AF_INET6: 11654263Sshin { 11754263Sshin struct ip6_hdr *ip6; 11854263Sshin proto = IPPROTO_IPV6; 11954263Sshin if (m->m_len < sizeof(*ip6)) { 12054263Sshin m = m_pullup(m, sizeof(*ip6)); 12154263Sshin if (!m) 12254263Sshin return ENOBUFS; 12354263Sshin } 12454263Sshin ip6 = mtod(m, struct ip6_hdr *); 12555009Sshin itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 12654263Sshin break; 12754263Sshin } 12862587Sitojun#endif 12954263Sshin default: 13062587Sitojun#ifdef DEBUG 13154263Sshin printf("in6_gif_output: warning: unknown family %d passed\n", 13254263Sshin family); 13354263Sshin#endif 13454263Sshin m_freem(m); 13554263Sshin return EAFNOSUPPORT; 13654263Sshin } 13762587Sitojun 13854263Sshin /* prepend new IP header */ 13954263Sshin M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT); 14054263Sshin if (m && m->m_len < sizeof(struct ip6_hdr)) 14154263Sshin m = m_pullup(m, sizeof(struct ip6_hdr)); 14254263Sshin if (m == NULL) { 14354263Sshin printf("ENOBUFS in in6_gif_output %d\n", __LINE__); 14454263Sshin return ENOBUFS; 14554263Sshin } 14654263Sshin 14754263Sshin ip6 = mtod(m, struct ip6_hdr *); 14854263Sshin ip6->ip6_flow = 0; 14962587Sitojun ip6->ip6_vfc &= ~IPV6_VERSION_MASK; 15062587Sitojun ip6->ip6_vfc |= IPV6_VERSION; 15154263Sshin ip6->ip6_plen = htons((u_short)m->m_pkthdr.len); 15254263Sshin ip6->ip6_nxt = proto; 15354263Sshin ip6->ip6_hlim = ip6_gif_hlim; 15454263Sshin ip6->ip6_src = sin6_src->sin6_addr; 15554263Sshin if (ifp->if_flags & IFF_LINK0) { 15654263Sshin /* multi-destination mode */ 15754263Sshin if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) 15854263Sshin ip6->ip6_dst = sin6_dst->sin6_addr; 15954263Sshin else if (rt) { 16062587Sitojun if (family != AF_INET6) { 16162587Sitojun m_freem(m); 16262587Sitojun return EINVAL; /*XXX*/ 16362587Sitojun } 16454263Sshin ip6->ip6_dst = ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr; 16554263Sshin } else { 16654263Sshin m_freem(m); 16754263Sshin return ENETUNREACH; 16854263Sshin } 16954263Sshin } else { 17054263Sshin /* bidirectional configured tunnel mode */ 17154263Sshin if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) 17254263Sshin ip6->ip6_dst = sin6_dst->sin6_addr; 17354263Sshin else { 17454263Sshin m_freem(m); 17554263Sshin return ENETUNREACH; 17654263Sshin } 17754263Sshin } 17855009Sshin if (ifp->if_flags & IFF_LINK1) { 17955009Sshin otos = 0; 18055009Sshin ip_ecn_ingress(ECN_ALLOWED, &otos, &itos); 18155009Sshin ip6->ip6_flow |= htonl((u_int32_t)otos << 20); 18255009Sshin } 18354263Sshin 18454263Sshin if (dst->sin6_family != sin6_dst->sin6_family || 18554263Sshin !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) { 18654263Sshin /* cache route doesn't match */ 18754263Sshin bzero(dst, sizeof(*dst)); 18854263Sshin dst->sin6_family = sin6_dst->sin6_family; 18954263Sshin dst->sin6_len = sizeof(struct sockaddr_in6); 19054263Sshin dst->sin6_addr = sin6_dst->sin6_addr; 19154263Sshin if (sc->gif_ro6.ro_rt) { 19254263Sshin RTFREE(sc->gif_ro6.ro_rt); 19354263Sshin sc->gif_ro6.ro_rt = NULL; 19454263Sshin } 19562587Sitojun#if 0 19662587Sitojun sc->gif_if.if_mtu = GIF_MTU; 19762587Sitojun#endif 19854263Sshin } 19954263Sshin 20054263Sshin if (sc->gif_ro6.ro_rt == NULL) { 20154263Sshin rtalloc((struct route *)&sc->gif_ro6); 20254263Sshin if (sc->gif_ro6.ro_rt == NULL) { 20354263Sshin m_freem(m); 20454263Sshin return ENETUNREACH; 20554263Sshin } 20662587Sitojun 20762587Sitojun /* if it constitutes infinite encapsulation, punt. */ 20862587Sitojun if (sc->gif_ro.ro_rt->rt_ifp == ifp) { 20962587Sitojun m_freem(m); 21062587Sitojun return ENETUNREACH; /*XXX*/ 21162587Sitojun } 21262587Sitojun#if 0 21362587Sitojun ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu 21462587Sitojun - sizeof(struct ip6_hdr); 21562587Sitojun#endif 21654263Sshin } 21762587Sitojun 21862587Sitojun#ifdef IPV6_MINMTU 21962587Sitojun /* 22062587Sitojun * force fragmentation to minimum MTU, to avoid path MTU discovery. 22162587Sitojun * it is too painful to ask for resend of inner packet, to achieve 22262587Sitojun * path MTU discovery for encapsulated packets. 22362587Sitojun */ 22462587Sitojun return(ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL)); 22562587Sitojun#else 22654263Sshin return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL)); 22762587Sitojun#endif 22854263Sshin} 22954263Sshin 23054263Sshinint in6_gif_input(mp, offp, proto) 23154263Sshin struct mbuf **mp; 23254263Sshin int *offp, proto; 23354263Sshin{ 23454263Sshin struct mbuf *m = *mp; 23554263Sshin struct ifnet *gifp = NULL; 23654263Sshin struct ip6_hdr *ip6; 23754263Sshin int af = 0; 23855009Sshin u_int32_t otos; 23954263Sshin 24054263Sshin ip6 = mtod(m, struct ip6_hdr *); 24154263Sshin 24262587Sitojun gifp = (struct ifnet *)encap_getarg(m); 24354263Sshin 24462587Sitojun if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { 24554263Sshin m_freem(m); 24654263Sshin ip6stat.ip6s_nogif++; 24754263Sshin return IPPROTO_DONE; 24854263Sshin } 24962587Sitojun 25055009Sshin otos = ip6->ip6_flow; 25154263Sshin m_adj(m, *offp); 25254263Sshin 25354263Sshin switch (proto) { 25454263Sshin#ifdef INET 25554263Sshin case IPPROTO_IPV4: 25654263Sshin { 25754263Sshin struct ip *ip; 25855009Sshin u_int8_t otos8; 25954263Sshin af = AF_INET; 26055009Sshin otos8 = (ntohl(otos) >> 20) & 0xff; 26154263Sshin if (m->m_len < sizeof(*ip)) { 26254263Sshin m = m_pullup(m, sizeof(*ip)); 26354263Sshin if (!m) 26454263Sshin return IPPROTO_DONE; 26554263Sshin } 26654263Sshin ip = mtod(m, struct ip *); 26755009Sshin if (gifp->if_flags & IFF_LINK1) 26855009Sshin ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos); 26954263Sshin break; 27054263Sshin } 27154263Sshin#endif /* INET */ 27262587Sitojun#ifdef INET6 27354263Sshin case IPPROTO_IPV6: 27454263Sshin { 27554263Sshin struct ip6_hdr *ip6; 27654263Sshin af = AF_INET6; 27754263Sshin if (m->m_len < sizeof(*ip6)) { 27854263Sshin m = m_pullup(m, sizeof(*ip6)); 27954263Sshin if (!m) 28054263Sshin return IPPROTO_DONE; 28154263Sshin } 28254263Sshin ip6 = mtod(m, struct ip6_hdr *); 28355009Sshin if (gifp->if_flags & IFF_LINK1) 28455009Sshin ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow); 28554263Sshin break; 28654263Sshin } 28762587Sitojun#endif 28854263Sshin default: 28954263Sshin ip6stat.ip6s_nogif++; 29054263Sshin m_freem(m); 29154263Sshin return IPPROTO_DONE; 29254263Sshin } 29362587Sitojun 29454263Sshin gif_input(m, af, gifp); 29554263Sshin return IPPROTO_DONE; 29654263Sshin} 29762587Sitojun 29862587Sitojun/* 29962587Sitojun * we know that we are in IFF_UP, outer address available, and outer family 30062587Sitojun * matched the physical addr family. see gif_encapcheck(). 30162587Sitojun */ 30262587Sitojunint 30362587Sitojungif_encapcheck6(m, off, proto, arg) 30462587Sitojun const struct mbuf *m; 30562587Sitojun int off; 30662587Sitojun int proto; 30762587Sitojun void *arg; 30862587Sitojun{ 30962587Sitojun struct ip6_hdr ip6; 31062587Sitojun struct gif_softc *sc; 31162587Sitojun struct sockaddr_in6 *src, *dst; 31262587Sitojun int addrmatch; 31362587Sitojun 31462587Sitojun /* sanity check done in caller */ 31562587Sitojun sc = (struct gif_softc *)arg; 31662587Sitojun src = (struct sockaddr_in6 *)sc->gif_psrc; 31762587Sitojun dst = (struct sockaddr_in6 *)sc->gif_pdst; 31862587Sitojun 31962587Sitojun /* LINTED const cast */ 32062587Sitojun m_copydata((struct mbuf *)m, 0, sizeof(ip6), (caddr_t)&ip6); 32162587Sitojun 32262587Sitojun /* check for address match */ 32362587Sitojun addrmatch = 0; 32462587Sitojun if (IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6.ip6_dst)) 32562587Sitojun addrmatch |= 1; 32662587Sitojun if (IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6.ip6_src)) 32762587Sitojun addrmatch |= 2; 32862587Sitojun else if ((sc->gif_if.if_flags & IFF_LINK0) != 0 && 32962587Sitojun IN6_IS_ADDR_UNSPECIFIED(&dst->sin6_addr)) { 33062587Sitojun addrmatch |= 2; /* we accept any source */ 33162587Sitojun } 33262587Sitojun if (addrmatch != 3) 33362587Sitojun return 0; 33462587Sitojun 33562587Sitojun /* martian filters on outer source - done in ip6_input */ 33662587Sitojun 33762587Sitojun /* ingress filters on outer source */ 33862587Sitojun if ((m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) { 33962587Sitojun struct sockaddr_in6 sin6; 34062587Sitojun struct rtentry *rt; 34162587Sitojun 34262587Sitojun bzero(&sin6, sizeof(sin6)); 34362587Sitojun sin6.sin6_family = AF_INET6; 34462587Sitojun sin6.sin6_len = sizeof(struct sockaddr_in6); 34562587Sitojun sin6.sin6_addr = ip6.ip6_src; 34662587Sitojun /* XXX scopeid */ 34762587Sitojun rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL); 34862587Sitojun if (!rt) 34962587Sitojun return 0; 35062587Sitojun if (rt->rt_ifp != m->m_pkthdr.rcvif) { 35162587Sitojun rtfree(rt); 35262587Sitojun return 0; 35362587Sitojun } 35462587Sitojun rtfree(rt); 35562587Sitojun } 35662587Sitojun 35762587Sitojun /* prioritize: IFF_LINK0 mode is less preferred */ 35862587Sitojun return (sc->gif_if.if_flags & IFF_LINK0) ? 128 : 128 * 2; 35962587Sitojun} 360