in6_gif.c revision 1.20
1/* $NetBSD: in6_gif.c,v 1.20 2001/05/14 13:35:21 itojun Exp $ */ 2/* $KAME: in6_gif.c,v 1.48 2001/05/03 14:51:48 itojun Exp $ */ 3 4/* 5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#include "opt_inet.h" 34#include "opt_iso.h" 35 36#include <sys/param.h> 37#include <sys/systm.h> 38#include <sys/socket.h> 39#include <sys/sockio.h> 40#include <sys/mbuf.h> 41#include <sys/errno.h> 42#include <sys/ioctl.h> 43#include <sys/queue.h> 44#include <sys/syslog.h> 45 46#include <net/if.h> 47#include <net/route.h> 48 49#include <netinet/in.h> 50#include <netinet/in_systm.h> 51#ifdef INET 52#include <netinet/ip.h> 53#endif 54#include <netinet/ip_encap.h> 55#ifdef INET6 56#include <netinet/ip6.h> 57#include <netinet6/ip6_var.h> 58#include <netinet6/in6_gif.h> 59#include <netinet6/in6_var.h> 60#endif 61#include <netinet/ip_ecn.h> 62 63#include <net/if_gif.h> 64 65#include <net/net_osdep.h> 66 67int 68in6_gif_output(ifp, family, m, rt) 69 struct ifnet *ifp; 70 int family; /* family of the packet to be encapsulate. */ 71 struct mbuf *m; 72 struct rtentry *rt; 73{ 74 struct gif_softc *sc = (struct gif_softc*)ifp; 75 struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst; 76 struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc; 77 struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst; 78 struct ip6_hdr *ip6; 79 int proto; 80 u_int8_t itos, otos; 81 82 if (sin6_src == NULL || sin6_dst == NULL || 83 sin6_src->sin6_family != AF_INET6 || 84 sin6_dst->sin6_family != AF_INET6) { 85 m_freem(m); 86 return EAFNOSUPPORT; 87 } 88 89 switch (family) { 90#ifdef INET 91 case AF_INET: 92 { 93 struct ip *ip; 94 95 proto = IPPROTO_IPV4; 96 if (m->m_len < sizeof(*ip)) { 97 m = m_pullup(m, sizeof(*ip)); 98 if (!m) 99 return ENOBUFS; 100 } 101 ip = mtod(m, struct ip *); 102 itos = ip->ip_tos; 103 break; 104 } 105#endif 106#ifdef INET6 107 case AF_INET6: 108 { 109 struct ip6_hdr *ip6; 110 proto = IPPROTO_IPV6; 111 if (m->m_len < sizeof(*ip6)) { 112 m = m_pullup(m, sizeof(*ip6)); 113 if (!m) 114 return ENOBUFS; 115 } 116 ip6 = mtod(m, struct ip6_hdr *); 117 itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 118 break; 119 } 120#endif 121#ifdef ISO 122 case AF_ISO: 123 proto = IPPROTO_EON; 124 itos = 0; 125 break; 126#endif 127 default: 128#ifdef DEBUG 129 printf("in6_gif_output: warning: unknown family %d passed\n", 130 family); 131#endif 132 m_freem(m); 133 return EAFNOSUPPORT; 134 } 135 136 /* prepend new IP header */ 137 M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT); 138 if (m && m->m_len < sizeof(struct ip6_hdr)) 139 m = m_pullup(m, sizeof(struct ip6_hdr)); 140 if (m == NULL) { 141 printf("ENOBUFS in in6_gif_output %d\n", __LINE__); 142 return ENOBUFS; 143 } 144 145 ip6 = mtod(m, struct ip6_hdr *); 146 ip6->ip6_flow = 0; 147 ip6->ip6_vfc &= ~IPV6_VERSION_MASK; 148 ip6->ip6_vfc |= IPV6_VERSION; 149 ip6->ip6_plen = htons((u_short)m->m_pkthdr.len); 150 ip6->ip6_nxt = proto; 151 ip6->ip6_hlim = ip6_gif_hlim; 152 ip6->ip6_src = sin6_src->sin6_addr; 153 /* bidirectional configured tunnel mode */ 154 if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) 155 ip6->ip6_dst = sin6_dst->sin6_addr; 156 else { 157 m_freem(m); 158 return ENETUNREACH; 159 } 160 if (ifp->if_flags & IFF_LINK1) 161 ip_ecn_ingress(ECN_ALLOWED, &otos, &itos); 162 else 163 ip_ecn_ingress(ECN_NOCARE, &otos, &itos); 164 ip6->ip6_flow &= ~ntohl(0xff00000); 165 ip6->ip6_flow |= htonl((u_int32_t)otos << 20); 166 167 if (dst->sin6_family != sin6_dst->sin6_family || 168 !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) { 169 /* cache route doesn't match */ 170 bzero(dst, sizeof(*dst)); 171 dst->sin6_family = sin6_dst->sin6_family; 172 dst->sin6_len = sizeof(struct sockaddr_in6); 173 dst->sin6_addr = sin6_dst->sin6_addr; 174 if (sc->gif_ro6.ro_rt) { 175 RTFREE(sc->gif_ro6.ro_rt); 176 sc->gif_ro6.ro_rt = NULL; 177 } 178#if 0 179 sc->gif_if.if_mtu = GIF_MTU; 180#endif 181 } 182 183 if (sc->gif_ro6.ro_rt == NULL) { 184 rtalloc((struct route *)&sc->gif_ro6); 185 if (sc->gif_ro6.ro_rt == NULL) { 186 m_freem(m); 187 return ENETUNREACH; 188 } 189 190 /* if it constitutes infinite encapsulation, punt. */ 191 if (sc->gif_ro.ro_rt->rt_ifp == ifp) { 192 m_freem(m); 193 return ENETUNREACH; /*XXX*/ 194 } 195#if 0 196 ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu 197 - sizeof(struct ip6_hdr); 198#endif 199 } 200 201#ifdef IPV6_MINMTU 202 /* 203 * force fragmentation to minimum MTU, to avoid path MTU discovery. 204 * it is too painful to ask for resend of inner packet, to achieve 205 * path MTU discovery for encapsulated packets. 206 */ 207 return(ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL)); 208#else 209 return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL)); 210#endif 211} 212 213int in6_gif_input(mp, offp, proto) 214 struct mbuf **mp; 215 int *offp, proto; 216{ 217 struct mbuf *m = *mp; 218 struct ifnet *gifp = NULL; 219 struct ip6_hdr *ip6; 220 int af = 0; 221 u_int32_t otos; 222 223 ip6 = mtod(m, struct ip6_hdr *); 224 225 gifp = (struct ifnet *)encap_getarg(m); 226 227 if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { 228 m_freem(m); 229 ip6stat.ip6s_nogif++; 230 return IPPROTO_DONE; 231 } 232 233 otos = ip6->ip6_flow; 234 m_adj(m, *offp); 235 236 switch (proto) { 237#ifdef INET 238 case IPPROTO_IPV4: 239 { 240 struct ip *ip; 241 u_int8_t otos8; 242 af = AF_INET; 243 otos8 = (ntohl(otos) >> 20) & 0xff; 244 if (m->m_len < sizeof(*ip)) { 245 m = m_pullup(m, sizeof(*ip)); 246 if (!m) 247 return IPPROTO_DONE; 248 } 249 ip = mtod(m, struct ip *); 250 if (gifp->if_flags & IFF_LINK1) 251 ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos); 252 else 253 ip_ecn_egress(ECN_NOCARE, &otos8, &ip->ip_tos); 254 break; 255 } 256#endif /* INET */ 257#ifdef INET6 258 case IPPROTO_IPV6: 259 { 260 struct ip6_hdr *ip6; 261 af = AF_INET6; 262 if (m->m_len < sizeof(*ip6)) { 263 m = m_pullup(m, sizeof(*ip6)); 264 if (!m) 265 return IPPROTO_DONE; 266 } 267 ip6 = mtod(m, struct ip6_hdr *); 268 if (gifp->if_flags & IFF_LINK1) 269 ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow); 270 else 271 ip6_ecn_egress(ECN_NOCARE, &otos, &ip6->ip6_flow); 272 break; 273 } 274#endif 275#ifdef ISO 276 case IPPROTO_EON: 277 af = AF_ISO; 278 break; 279#endif 280 default: 281 ip6stat.ip6s_nogif++; 282 m_freem(m); 283 return IPPROTO_DONE; 284 } 285 286 gif_input(m, af, gifp); 287 return IPPROTO_DONE; 288} 289 290/* 291 * we know that we are in IFF_UP, outer address available, and outer family 292 * matched the physical addr family. see gif_encapcheck(). 293 */ 294int 295gif_encapcheck6(m, off, proto, arg) 296 const struct mbuf *m; 297 int off; 298 int proto; 299 void *arg; 300{ 301 struct ip6_hdr ip6; 302 struct gif_softc *sc; 303 struct sockaddr_in6 *src, *dst; 304 int addrmatch; 305 306 /* sanity check done in caller */ 307 sc = (struct gif_softc *)arg; 308 src = (struct sockaddr_in6 *)sc->gif_psrc; 309 dst = (struct sockaddr_in6 *)sc->gif_pdst; 310 311 /* LINTED const cast */ 312 m_copydata((struct mbuf *)m, 0, sizeof(ip6), (caddr_t)&ip6); 313 314 /* check for address match */ 315 addrmatch = 0; 316 if (IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6.ip6_dst)) 317 addrmatch |= 1; 318 if (IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6.ip6_src)) 319 addrmatch |= 2; 320 if (addrmatch != 3) 321 return 0; 322 323 /* martian filters on outer source - done in ip6_input */ 324 325 /* ingress filters on outer source */ 326 if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && 327 (m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) { 328 struct sockaddr_in6 sin6; 329 struct rtentry *rt; 330 331 bzero(&sin6, sizeof(sin6)); 332 sin6.sin6_family = AF_INET6; 333 sin6.sin6_len = sizeof(struct sockaddr_in6); 334 sin6.sin6_addr = ip6.ip6_src; 335 /* XXX scopeid */ 336 rt = rtalloc1((struct sockaddr *)&sin6, 0); 337 if (!rt || rt->rt_ifp != m->m_pkthdr.rcvif) { 338#if 0 339 log(LOG_WARNING, "%s: packet from %s dropped " 340 "due to ingress filter\n", if_name(&sc->gif_if), 341 ip6_sprintf(&sin6.sin6_addr)); 342#endif 343 if (rt) 344 rtfree(rt); 345 return 0; 346 } 347 rtfree(rt); 348 } 349 350 return 128 * 2; 351} 352