in6_gif.c revision 78064
1185377Ssam/* $FreeBSD: head/sys/netinet6/in6_gif.c 78064 2001-06-11 12:39:29Z ume $ */ 2185377Ssam/* $KAME: in6_gif.c,v 1.49 2001/05/14 14:02:17 itojun Exp $ */ 3185377Ssam 4185377Ssam/* 5185377Ssam * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6185377Ssam * All rights reserved. 7185377Ssam * 8185377Ssam * Redistribution and use in source and binary forms, with or without 9185377Ssam * modification, are permitted provided that the following conditions 10185377Ssam * are met: 11185377Ssam * 1. Redistributions of source code must retain the above copyright 12185377Ssam * notice, this list of conditions and the following disclaimer. 13185377Ssam * 2. Redistributions in binary form must reproduce the above copyright 14185377Ssam * notice, this list of conditions and the following disclaimer in the 15185377Ssam * documentation and/or other materials provided with the distribution. 16185377Ssam * 3. Neither the name of the project nor the names of its contributors 17203158Srpaulo * may be used to endorse or promote products derived from this software 18185377Ssam * without specific prior written permission. 19185377Ssam * 20185377Ssam * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21185377Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22185377Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23185377Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24185377Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25185377Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26185377Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27185377Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28185377Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29185377Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30224512Sadrian * SUCH DAMAGE. 31224512Sadrian */ 32224512Sadrian 33224512Sadrian#include "opt_inet.h" 34224512Sadrian#include "opt_inet6.h" 35224512Sadrian 36224512Sadrian#include <sys/param.h> 37224512Sadrian#include <sys/systm.h> 38224512Sadrian#include <sys/socket.h> 39224512Sadrian#include <sys/sockio.h> 40224512Sadrian#include <sys/mbuf.h> 41224512Sadrian#include <sys/errno.h> 42224512Sadrian#include <sys/queue.h> 43224512Sadrian#include <sys/syslog.h> 44224512Sadrian 45224512Sadrian#include <sys/malloc.h> 46224512Sadrian 47224512Sadrian#include <net/if.h> 48224512Sadrian#include <net/route.h> 49224512Sadrian 50224512Sadrian#include <netinet/in.h> 51224512Sadrian#include <netinet/in_systm.h> 52224512Sadrian#ifdef INET 53224512Sadrian#include <netinet/ip.h> 54224512Sadrian#endif 55224512Sadrian#include <netinet/ip_encap.h> 56224512Sadrian#ifdef INET6 57224512Sadrian#include <netinet/ip6.h> 58224512Sadrian#include <netinet6/ip6_var.h> 59224512Sadrian#include <netinet6/in6_gif.h> 60224512Sadrian#include <netinet6/in6_var.h> 61224512Sadrian#endif 62224512Sadrian#include <netinet/ip_ecn.h> 63224512Sadrian#ifdef INET6 64224512Sadrian#include <netinet6/ip6_ecn.h> 65224512Sadrian#endif 66224512Sadrian 67224512Sadrian#include <net/if_gif.h> 68224512Sadrian 69224512Sadrian#include <net/net_osdep.h> 70234747Sadrian 71234747Sadrianint 72234747Sadrianin6_gif_output(ifp, family, m, rt) 73234747Sadrian struct ifnet *ifp; 74234747Sadrian int family; /* family of the packet to be encapsulate. */ 75234747Sadrian struct mbuf *m; 76234747Sadrian struct rtentry *rt; 77234747Sadrian{ 78234747Sadrian struct gif_softc *sc = (struct gif_softc*)ifp; 79234747Sadrian struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst; 80234747Sadrian struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc; 81234747Sadrian struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst; 82234747Sadrian struct ip6_hdr *ip6; 83234747Sadrian int proto; 84234747Sadrian u_int8_t itos, otos; 85234747Sadrian 86234747Sadrian if (sin6_src == NULL || sin6_dst == NULL || 87234747Sadrian sin6_src->sin6_family != AF_INET6 || 88234747Sadrian sin6_dst->sin6_family != AF_INET6) { 89234747Sadrian m_freem(m); 90234747Sadrian return EAFNOSUPPORT; 91234747Sadrian } 92234747Sadrian 93234747Sadrian switch (family) { 94234747Sadrian#ifdef INET 95234747Sadrian case AF_INET: 96234747Sadrian { 97234747Sadrian struct ip *ip; 98234747Sadrian 99234747Sadrian proto = IPPROTO_IPV4; 100234747Sadrian if (m->m_len < sizeof(*ip)) { 101234747Sadrian m = m_pullup(m, sizeof(*ip)); 102234747Sadrian if (!m) 103234747Sadrian return ENOBUFS; 104185377Ssam } 105185377Ssam ip = mtod(m, struct ip *); 106185377Ssam itos = ip->ip_tos; 107185377Ssam break; 108185377Ssam } 109185377Ssam#endif 110185377Ssam#ifdef INET6 111185377Ssam case AF_INET6: 112185377Ssam { 113185377Ssam struct ip6_hdr *ip6; 114185380Ssam proto = IPPROTO_IPV6; 115185380Ssam if (m->m_len < sizeof(*ip6)) { 116185380Ssam m = m_pullup(m, sizeof(*ip6)); 117185380Ssam if (!m) 118185380Ssam return ENOBUFS; 119185380Ssam } 120185377Ssam ip6 = mtod(m, struct ip6_hdr *); 121185377Ssam itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 122185377Ssam break; 123185377Ssam } 124185377Ssam#endif 125185377Ssam default: 126185377Ssam#ifdef DEBUG 127185377Ssam printf("in6_gif_output: warning: unknown family %d passed\n", 128185377Ssam family); 129185377Ssam#endif 130185377Ssam m_freem(m); 131185377Ssam return EAFNOSUPPORT; 132185377Ssam } 133185377Ssam 134185377Ssam /* prepend new IP header */ 135185377Ssam M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT); 136185377Ssam if (m && m->m_len < sizeof(struct ip6_hdr)) 137185377Ssam m = m_pullup(m, sizeof(struct ip6_hdr)); 138185377Ssam if (m == NULL) { 139185377Ssam printf("ENOBUFS in in6_gif_output %d\n", __LINE__); 140185377Ssam return ENOBUFS; 141185377Ssam } 142185377Ssam 143185377Ssam ip6 = mtod(m, struct ip6_hdr *); 144185377Ssam ip6->ip6_flow = 0; 145185377Ssam ip6->ip6_vfc &= ~IPV6_VERSION_MASK; 146185377Ssam ip6->ip6_vfc |= IPV6_VERSION; 147185377Ssam ip6->ip6_plen = htons((u_short)m->m_pkthdr.len); 148185377Ssam ip6->ip6_nxt = proto; 149185377Ssam ip6->ip6_hlim = ip6_gif_hlim; 150185377Ssam ip6->ip6_src = sin6_src->sin6_addr; 151185377Ssam /* bidirectional configured tunnel mode */ 152185377Ssam if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) 153185377Ssam ip6->ip6_dst = sin6_dst->sin6_addr; 154220259Sadrian else { 155225922Sadrian m_freem(m); 156220259Sadrian return ENETUNREACH; 157185377Ssam } 158185377Ssam if (ifp->if_flags & IFF_LINK1) 159185377Ssam ip_ecn_ingress(ECN_ALLOWED, &otos, &itos); 160185377Ssam else 161185377Ssam ip_ecn_ingress(ECN_NOCARE, &otos, &itos); 162185377Ssam ip6->ip6_flow &= ~ntohl(0xff00000); 163185377Ssam ip6->ip6_flow |= htonl((u_int32_t)otos << 20); 164185377Ssam 165185377Ssam if (dst->sin6_family != sin6_dst->sin6_family || 166185377Ssam !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) { 167185377Ssam /* cache route doesn't match */ 168185377Ssam bzero(dst, sizeof(*dst)); 169185377Ssam dst->sin6_family = sin6_dst->sin6_family; 170185377Ssam dst->sin6_len = sizeof(struct sockaddr_in6); 171185377Ssam dst->sin6_addr = sin6_dst->sin6_addr; 172185377Ssam if (sc->gif_ro6.ro_rt) { 173185377Ssam RTFREE(sc->gif_ro6.ro_rt); 174185377Ssam sc->gif_ro6.ro_rt = NULL; 175185377Ssam } 176185377Ssam#if 0 177185377Ssam sc->gif_if.if_mtu = GIF_MTU; 178185377Ssam#endif 179185377Ssam } 180185377Ssam 181185377Ssam if (sc->gif_ro6.ro_rt == NULL) { 182185377Ssam rtalloc((struct route *)&sc->gif_ro6); 183185377Ssam if (sc->gif_ro6.ro_rt == NULL) { 184185377Ssam m_freem(m); 185185377Ssam return ENETUNREACH; 186185377Ssam } 187185377Ssam 188185377Ssam /* if it constitutes infinite encapsulation, punt. */ 189185377Ssam if (sc->gif_ro.ro_rt->rt_ifp == ifp) { 190185377Ssam m_freem(m); 191185377Ssam return ENETUNREACH; /*XXX*/ 192185377Ssam } 193185377Ssam#if 0 194185377Ssam ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu 195185377Ssam - sizeof(struct ip6_hdr); 196185377Ssam#endif 197185377Ssam } 198185377Ssam 199185377Ssam#ifdef IPV6_MINMTU 200185377Ssam /* 201185377Ssam * force fragmentation to minimum MTU, to avoid path MTU discovery. 202185377Ssam * it is too painful to ask for resend of inner packet, to achieve 203185377Ssam * path MTU discovery for encapsulated packets. 204185377Ssam */ 205185377Ssam return(ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL)); 206185377Ssam#else 207185377Ssam return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL)); 208185377Ssam#endif 209185377Ssam} 210185377Ssam 211185377Ssamint in6_gif_input(mp, offp, proto) 212250346Sadrian struct mbuf **mp; 213250346Sadrian int *offp, proto; 214250346Sadrian{ 215250346Sadrian struct mbuf *m = *mp; 216250346Sadrian struct ifnet *gifp = NULL; 217250346Sadrian struct ip6_hdr *ip6; 218250346Sadrian int af = 0; 219250346Sadrian u_int32_t otos; 220185377Ssam 221185377Ssam ip6 = mtod(m, struct ip6_hdr *); 222185377Ssam 223185377Ssam gifp = (struct ifnet *)encap_getarg(m); 224185377Ssam 225185377Ssam if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { 226185377Ssam m_freem(m); 227185377Ssam ip6stat.ip6s_nogif++; 228185377Ssam return IPPROTO_DONE; 229185377Ssam } 230185377Ssam 231185377Ssam otos = ip6->ip6_flow; 232185377Ssam m_adj(m, *offp); 233185377Ssam 234185377Ssam switch (proto) { 235185377Ssam#ifdef INET 236185377Ssam case IPPROTO_IPV4: 237185377Ssam { 238185377Ssam struct ip *ip; 239237519Sadrian u_int8_t otos8; 240237519Sadrian af = AF_INET; 241237519Sadrian otos8 = (ntohl(otos) >> 20) & 0xff; 242237521Sadrian if (m->m_len < sizeof(*ip)) { 243237521Sadrian m = m_pullup(m, sizeof(*ip)); 244237521Sadrian if (!m) 245237519Sadrian return IPPROTO_DONE; 246237519Sadrian } 247237519Sadrian ip = mtod(m, struct ip *); 248237519Sadrian if (gifp->if_flags & IFF_LINK1) 249185377Ssam ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos); 250185377Ssam else 251237626Sadrian ip_ecn_egress(ECN_NOCARE, &otos8, &ip->ip_tos); 252237626Sadrian break; 253237626Sadrian } 254237626Sadrian#endif /* INET */ 255237626Sadrian#ifdef INET6 256237626Sadrian case IPPROTO_IPV6: 257185377Ssam { 258237626Sadrian struct ip6_hdr *ip6; 259237626Sadrian af = AF_INET6; 260237626Sadrian if (m->m_len < sizeof(*ip6)) { 261237626Sadrian m = m_pullup(m, sizeof(*ip6)); 262237626Sadrian if (!m) 263237626Sadrian return IPPROTO_DONE; 264237626Sadrian } 265237626Sadrian ip6 = mtod(m, struct ip6_hdr *); 266237626Sadrian if (gifp->if_flags & IFF_LINK1) 267237626Sadrian ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow); 268237626Sadrian else 269237521Sadrian ip6_ecn_egress(ECN_NOCARE, &otos, &ip6->ip6_flow); 270237521Sadrian break; 271237519Sadrian } 272237519Sadrian#endif 273185377Ssam default: 274185377Ssam ip6stat.ip6s_nogif++; 275185377Ssam m_freem(m); 276185377Ssam return IPPROTO_DONE; 277185377Ssam } 278185377Ssam 279185377Ssam gif_input(m, af, gifp); 280 return IPPROTO_DONE; 281} 282 283/* 284 * we know that we are in IFF_UP, outer address available, and outer family 285 * matched the physical addr family. see gif_encapcheck(). 286 */ 287int 288gif_encapcheck6(m, off, proto, arg) 289 const struct mbuf *m; 290 int off; 291 int proto; 292 void *arg; 293{ 294 struct ip6_hdr ip6; 295 struct gif_softc *sc; 296 struct sockaddr_in6 *src, *dst; 297 int addrmatch; 298 299 /* sanity check done in caller */ 300 sc = (struct gif_softc *)arg; 301 src = (struct sockaddr_in6 *)sc->gif_psrc; 302 dst = (struct sockaddr_in6 *)sc->gif_pdst; 303 304 /* LINTED const cast */ 305 m_copydata((struct mbuf *)m, 0, sizeof(ip6), (caddr_t)&ip6); 306 307 /* check for address match */ 308 addrmatch = 0; 309 if (IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6.ip6_dst)) 310 addrmatch |= 1; 311 if (IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6.ip6_src)) 312 addrmatch |= 2; 313 if (addrmatch != 3) 314 return 0; 315 316 /* martian filters on outer source - done in ip6_input */ 317 318 /* ingress filters on outer source */ 319 if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && 320 (m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) { 321 struct sockaddr_in6 sin6; 322 struct rtentry *rt; 323 324 bzero(&sin6, sizeof(sin6)); 325 sin6.sin6_family = AF_INET6; 326 sin6.sin6_len = sizeof(struct sockaddr_in6); 327 sin6.sin6_addr = ip6.ip6_src; 328 /* XXX scopeid */ 329 rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL); 330 if (!rt || rt->rt_ifp != m->m_pkthdr.rcvif) { 331#if 0 332 log(LOG_WARNING, "%s: packet from %s dropped " 333 "due to ingress filter\n", if_name(&sc->gif_if), 334 ip6_sprintf(&sin6.sin6_addr)); 335#endif 336 if (rt) 337 rtfree(rt); 338 return 0; 339 } 340 rtfree(rt); 341 } 342 343 return 128 * 2; 344} 345