in_gif.c revision 172307
1207753Smm/* $FreeBSD: head/sys/netinet/in_gif.c 172307 2007-09-23 17:50:17Z csjp $ */ 2207753Smm/* $KAME: in_gif.c,v 1.54 2001/05/14 14:02:16 itojun Exp $ */ 3207753Smm 4207753Smm/*- 5207753Smm * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6207753Smm * All rights reserved. 7207753Smm * 8207753Smm * Redistribution and use in source and binary forms, with or without 9207753Smm * modification, are permitted provided that the following conditions 10207753Smm * are met: 11207753Smm * 1. Redistributions of source code must retain the above copyright 12207753Smm * notice, this list of conditions and the following disclaimer. 13207753Smm * 2. Redistributions in binary form must reproduce the above copyright 14207753Smm * notice, this list of conditions and the following disclaimer in the 15207753Smm * documentation and/or other materials provided with the distribution. 16207753Smm * 3. Neither the name of the project nor the names of its contributors 17207753Smm * may be used to endorse or promote products derived from this software 18207753Smm * without specific prior written permission. 19207753Smm * 20207753Smm * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21207753Smm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22207753Smm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23207753Smm * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24207753Smm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25207753Smm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26207753Smm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27207753Smm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28207753Smm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29207753Smm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30207753Smm * SUCH DAMAGE. 31207753Smm */ 32207753Smm 33207753Smm#include "opt_mrouting.h" 34207753Smm#include "opt_inet.h" 35207753Smm#include "opt_inet6.h" 36207753Smm 37207753Smm#include <sys/param.h> 38207753Smm#include <sys/systm.h> 39207753Smm#include <sys/socket.h> 40207753Smm#include <sys/sockio.h> 41207753Smm#include <sys/mbuf.h> 42207753Smm#include <sys/errno.h> 43207753Smm#include <sys/kernel.h> 44207753Smm#include <sys/sysctl.h> 45207753Smm#include <sys/protosw.h> 46207753Smm 47207753Smm#include <sys/malloc.h> 48207753Smm 49207753Smm#include <net/if.h> 50207753Smm#include <net/route.h> 51207753Smm 52207753Smm#include <netinet/in.h> 53207753Smm#include <netinet/in_systm.h> 54207753Smm#include <netinet/ip.h> 55207753Smm#include <netinet/ip_var.h> 56207753Smm#include <netinet/in_gif.h> 57207753Smm#include <netinet/in_var.h> 58207753Smm#include <netinet/ip_encap.h> 59207753Smm#include <netinet/ip_ecn.h> 60207753Smm 61207753Smm#ifdef INET6 62207753Smm#include <netinet/ip6.h> 63207753Smm#endif 64207753Smm 65207753Smm#ifdef MROUTING 66207753Smm#include <netinet/ip_mroute.h> 67207753Smm#endif /* MROUTING */ 68207753Smm 69207753Smm#include <net/if_gif.h> 70207753Smm 71207753Smmstatic int gif_validate4(const struct ip *, struct gif_softc *, 72207753Smm struct ifnet *); 73215187Smm 74215187Smmextern struct domain inetdomain; 75215187Smmstruct protosw in_gif_protosw = { 76215187Smm .pr_type = SOCK_RAW, 77215187Smm .pr_domain = &inetdomain, 78215187Smm .pr_protocol = 0/* IPPROTO_IPV[46] */, 79215187Smm .pr_flags = PR_ATOMIC|PR_ADDR, 80215187Smm .pr_input = in_gif_input, 81215187Smm .pr_output = (pr_output_t*)rip_output, 82215187Smm .pr_ctloutput = rip_ctloutput, 83215187Smm .pr_usrreqs = &rip_usrreqs 84207753Smm}; 85207753Smm 86207753Smmstatic int ip_gif_ttl = GIF_TTL; 87207753SmmSYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW, 88207753Smm &ip_gif_ttl, 0, ""); 89207753Smm 90223935Smmint 91207753Smmin_gif_output(struct ifnet *ifp, int family, struct mbuf *m) 92207753Smm{ 93207753Smm struct gif_softc *sc = ifp->if_softc; 94207753Smm struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst; 95207753Smm struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc; 96207753Smm struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst; 97207753Smm struct ip iphdr; /* capsule IP header, host byte ordered */ 98207753Smm struct etherip_header eiphdr; 99207753Smm int proto, error; 100207753Smm u_int8_t tos; 101207753Smm 102207753Smm GIF_LOCK_ASSERT(sc); 103207753Smm 104207753Smm if (sin_src == NULL || sin_dst == NULL || 105207753Smm sin_src->sin_family != AF_INET || 106207753Smm sin_dst->sin_family != AF_INET) { 107207753Smm m_freem(m); 108207753Smm return EAFNOSUPPORT; 109207753Smm } 110207753Smm 111207753Smm switch (family) { 112207753Smm#ifdef INET 113207753Smm case AF_INET: 114207753Smm { 115207753Smm struct ip *ip; 116207753Smm 117207753Smm proto = IPPROTO_IPV4; 118207753Smm if (m->m_len < sizeof(*ip)) { 119207753Smm m = m_pullup(m, sizeof(*ip)); 120207753Smm if (!m) 121207753Smm return ENOBUFS; 122207753Smm } 123207753Smm ip = mtod(m, struct ip *); 124207753Smm tos = ip->ip_tos; 125207753Smm break; 126207753Smm } 127207753Smm#endif /* INET */ 128207753Smm#ifdef INET6 129207753Smm case AF_INET6: 130207753Smm { 131207753Smm struct ip6_hdr *ip6; 132207753Smm proto = IPPROTO_IPV6; 133207753Smm if (m->m_len < sizeof(*ip6)) { 134207753Smm m = m_pullup(m, sizeof(*ip6)); 135207753Smm if (!m) 136207753Smm return ENOBUFS; 137207753Smm } 138207753Smm ip6 = mtod(m, struct ip6_hdr *); 139207753Smm tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 140215187Smm break; 141207753Smm } 142207753Smm#endif /* INET6 */ 143215187Smm case AF_LINK: 144207753Smm proto = IPPROTO_ETHERIP; 145207753Smm eiphdr.eip_ver = ETHERIP_VERSION & ETHERIP_VER_VERS_MASK; 146215187Smm eiphdr.eip_pad = 0; 147215187Smm /* prepend Ethernet-in-IP header */ 148207753Smm M_PREPEND(m, sizeof(struct etherip_header), M_DONTWAIT); 149207753Smm if (m && m->m_len < sizeof(struct etherip_header)) 150207753Smm m = m_pullup(m, sizeof(struct etherip_header)); 151207753Smm if (m == NULL) 152207753Smm return ENOBUFS; 153207753Smm bcopy(&eiphdr, mtod(m, struct etherip_header *), 154207753Smm sizeof(struct etherip_header)); 155207753Smm break; 156207753Smm 157207753Smm default: 158207753Smm#ifdef DEBUG 159207753Smm printf("in_gif_output: warning: unknown family %d passed\n", 160207753Smm family); 161207753Smm#endif 162207753Smm m_freem(m); 163207753Smm return EAFNOSUPPORT; 164207753Smm } 165207753Smm 166207753Smm bzero(&iphdr, sizeof(iphdr)); 167207753Smm iphdr.ip_src = sin_src->sin_addr; 168207753Smm /* bidirectional configured tunnel mode */ 169207753Smm if (sin_dst->sin_addr.s_addr != INADDR_ANY) 170207753Smm iphdr.ip_dst = sin_dst->sin_addr; 171207753Smm else { 172207753Smm m_freem(m); 173207753Smm return ENETUNREACH; 174207753Smm } 175207753Smm iphdr.ip_p = proto; 176207753Smm /* version will be set in ip_output() */ 177207753Smm iphdr.ip_ttl = ip_gif_ttl; 178207753Smm iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip); 179207753Smm ip_ecn_ingress((ifp->if_flags & IFF_LINK1) ? ECN_ALLOWED : ECN_NOCARE, 180207753Smm &iphdr.ip_tos, &tos); 181207753Smm 182207753Smm /* prepend new IP header */ 183207753Smm M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); 184207753Smm if (m && m->m_len < sizeof(struct ip)) 185207753Smm m = m_pullup(m, sizeof(struct ip)); 186207753Smm if (m == NULL) { 187207753Smm printf("ENOBUFS in in_gif_output %d\n", __LINE__); 188207753Smm return ENOBUFS; 189207753Smm } 190207753Smm bcopy(&iphdr, mtod(m, struct ip *), sizeof(struct ip)); 191207753Smm 192207753Smm if (dst->sin_family != sin_dst->sin_family || 193207753Smm dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) { 194207753Smm /* cache route doesn't match */ 195207753Smm bzero(dst, sizeof(*dst)); 196207753Smm dst->sin_family = sin_dst->sin_family; 197244601Smm dst->sin_len = sizeof(struct sockaddr_in); 198207753Smm dst->sin_addr = sin_dst->sin_addr; 199207753Smm if (sc->gif_ro.ro_rt) { 200207753Smm RTFREE(sc->gif_ro.ro_rt); 201207753Smm sc->gif_ro.ro_rt = NULL; 202207753Smm } 203207753Smm#if 0 204207753Smm GIF2IFP(sc)->if_mtu = GIF_MTU; 205207753Smm#endif 206207753Smm } 207207753Smm 208207753Smm if (sc->gif_ro.ro_rt == NULL) { 209207753Smm rtalloc_ign(&sc->gif_ro, 0); 210207753Smm if (sc->gif_ro.ro_rt == NULL) { 211207753Smm m_freem(m); 212207753Smm return ENETUNREACH; 213207753Smm } 214207753Smm 215207753Smm /* if it constitutes infinite encapsulation, punt. */ 216207753Smm if (sc->gif_ro.ro_rt->rt_ifp == ifp) { 217207753Smm m_freem(m); 218207753Smm return ENETUNREACH; /* XXX */ 219207753Smm } 220207753Smm#if 0 221207753Smm ifp->if_mtu = sc->gif_ro.ro_rt->rt_ifp->if_mtu 222207753Smm - sizeof(struct ip); 223207753Smm#endif 224207753Smm } 225207753Smm 226207753Smm error = ip_output(m, NULL, &sc->gif_ro, 0, NULL, NULL); 227207753Smm 228207753Smm if (!(GIF2IFP(sc)->if_flags & IFF_LINK0) && 229207753Smm sc->gif_ro.ro_rt != NULL) { 230207753Smm RTFREE(sc->gif_ro.ro_rt); 231207753Smm sc->gif_ro.ro_rt = NULL; 232207753Smm } 233207753Smm 234207753Smm return (error); 235207753Smm} 236207753Smm 237207753Smmvoid 238207753Smmin_gif_input(struct mbuf *m, int off) 239207753Smm{ 240207753Smm struct ifnet *gifp = NULL; 241207753Smm struct gif_softc *sc; 242207753Smm struct ip *ip; 243207753Smm int af; 244207753Smm u_int8_t otos; 245207753Smm int proto; 246207753Smm 247207753Smm ip = mtod(m, struct ip *); 248207753Smm proto = ip->ip_p; 249207753Smm 250292588Sdelphij sc = (struct gif_softc *)encap_getarg(m); 251292588Sdelphij if (sc == NULL) { 252292588Sdelphij m_freem(m); 253292588Sdelphij ipstat.ips_nogif++; 254207753Smm return; 255207753Smm } 256207753Smm 257207753Smm gifp = GIF2IFP(sc); 258207753Smm if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { 259207753Smm m_freem(m); 260207753Smm ipstat.ips_nogif++; 261207753Smm return; 262207753Smm } 263207753Smm 264207753Smm otos = ip->ip_tos; 265207753Smm m_adj(m, off); 266207753Smm 267207753Smm switch (proto) { 268207753Smm#ifdef INET 269207753Smm case IPPROTO_IPV4: 270207753Smm { 271207753Smm struct ip *ip; 272207753Smm af = AF_INET; 273207753Smm if (m->m_len < sizeof(*ip)) { 274207753Smm m = m_pullup(m, sizeof(*ip)); 275292588Sdelphij if (!m) 276207753Smm return; 277207753Smm } 278207753Smm ip = mtod(m, struct ip *); 279207753Smm if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ? 280207753Smm ECN_ALLOWED : ECN_NOCARE, 281207753Smm &otos, &ip->ip_tos) == 0) { 282207753Smm m_freem(m); 283207753Smm return; 284207753Smm } 285207753Smm break; 286207753Smm } 287207753Smm#endif 288207753Smm#ifdef INET6 289207753Smm case IPPROTO_IPV6: 290207753Smm { 291207753Smm struct ip6_hdr *ip6; 292207753Smm u_int8_t itos, oitos; 293207753Smm 294207753Smm af = AF_INET6; 295207753Smm if (m->m_len < sizeof(*ip6)) { 296207753Smm m = m_pullup(m, sizeof(*ip6)); 297207753Smm if (!m) 298213700Smm return; 299213700Smm } 300207753Smm ip6 = mtod(m, struct ip6_hdr *); 301207753Smm itos = oitos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 302207753Smm if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ? 303207753Smm ECN_ALLOWED : ECN_NOCARE, 304207753Smm &otos, &itos) == 0) { 305207753Smm m_freem(m); 306207753Smm return; 307207753Smm } 308207753Smm if (itos != oitos) { 309207753Smm ip6->ip6_flow &= ~htonl(0xff << 20); 310207753Smm ip6->ip6_flow |= htonl((u_int32_t)itos << 20); 311213700Smm } 312213700Smm break; 313207753Smm } 314207753Smm#endif /* INET6 */ 315207753Smm case IPPROTO_ETHERIP: 316207753Smm af = AF_LINK; 317207753Smm break; 318207753Smm 319207753Smm default: 320207753Smm ipstat.ips_nogif++; 321207753Smm m_freem(m); 322207753Smm return; 323207753Smm } 324207753Smm gif_input(m, af, gifp); 325207753Smm return; 326207753Smm} 327207753Smm 328207753Smm/* 329207753Smm * validate outer address. 330207753Smm */ 331207753Smmstatic int 332207753Smmgif_validate4(const struct ip *ip, struct gif_softc *sc, struct ifnet *ifp) 333207753Smm{ 334207753Smm struct sockaddr_in *src, *dst; 335207753Smm struct in_ifaddr *ia4; 336207753Smm 337207753Smm src = (struct sockaddr_in *)sc->gif_psrc; 338207753Smm dst = (struct sockaddr_in *)sc->gif_pdst; 339207753Smm 340207753Smm /* check for address match */ 341207753Smm if (src->sin_addr.s_addr != ip->ip_dst.s_addr || 342207753Smm dst->sin_addr.s_addr != ip->ip_src.s_addr) 343207753Smm return 0; 344207753Smm 345207753Smm /* martian filters on outer source - NOT done in ip_input! */ 346207753Smm if (IN_MULTICAST(ntohl(ip->ip_src.s_addr))) 347207753Smm return 0; 348207753Smm switch ((ntohl(ip->ip_src.s_addr) & 0xff000000) >> 24) { 349292588Sdelphij case 0: case 127: case 255: 350207753Smm return 0; 351207753Smm } 352207753Smm /* reject packets with broadcast on source */ 353207753Smm TAILQ_FOREACH(ia4, &in_ifaddrhead, ia_link) { 354207753Smm if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) 355207753Smm continue; 356207753Smm if (ip->ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr) 357207753Smm return 0; 358207753Smm } 359207753Smm 360207753Smm /* ingress filters on outer source */ 361207753Smm if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0 && ifp) { 362292588Sdelphij struct sockaddr_in sin; 363207753Smm struct rtentry *rt; 364207753Smm 365207753Smm bzero(&sin, sizeof(sin)); 366207753Smm sin.sin_family = AF_INET; 367207753Smm sin.sin_len = sizeof(struct sockaddr_in); 368207753Smm sin.sin_addr = ip->ip_src; 369207753Smm rt = rtalloc1((struct sockaddr *)&sin, 0, 0UL); 370207753Smm if (!rt || rt->rt_ifp != ifp) { 371207753Smm#if 0 372207753Smm log(LOG_WARNING, "%s: packet from 0x%x dropped " 373207753Smm "due to ingress filter\n", if_name(GIF2IFP(sc)), 374207753Smm (u_int32_t)ntohl(sin.sin_addr.s_addr)); 375207753Smm#endif 376213700Smm if (rt) 377213700Smm RTFREE_LOCKED(rt); 378207753Smm return 0; 379207753Smm } 380207753Smm RTFREE_LOCKED(rt); 381207753Smm } 382207753Smm 383207753Smm return 32 * 2; 384292588Sdelphij} 385207753Smm 386207753Smm/* 387292588Sdelphij * we know that we are in IFF_UP, outer address available, and outer family 388207753Smm * matched the physical addr family. see gif_encapcheck(). 389207753Smm */ 390207753Smmint 391207753Smmgif_encapcheck4(const struct mbuf *m, int off, int proto, void *arg) 392292588Sdelphij{ 393292588Sdelphij struct ip ip; 394207753Smm struct gif_softc *sc; 395207753Smm struct ifnet *ifp; 396207753Smm 397207753Smm /* sanity check done in caller */ 398207753Smm sc = (struct gif_softc *)arg; 399207753Smm 400207753Smm /* LINTED const cast */ 401207753Smm m_copydata(m, 0, sizeof(ip), (caddr_t)&ip); 402207753Smm ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL; 403207753Smm 404207753Smm return gif_validate4(&ip, sc, ifp); 405207753Smm} 406207753Smm 407207753Smmint 408207753Smmin_gif_attach(struct gif_softc *sc) 409207753Smm{ 410207753Smm sc->encap_cookie4 = encap_attach_func(AF_INET, -1, gif_encapcheck, 411207753Smm &in_gif_protosw, sc); 412207753Smm if (sc->encap_cookie4 == NULL) 413207753Smm return EEXIST; 414207753Smm return 0; 415207753Smm} 416207753Smm 417207753Smmint 418207753Smmin_gif_detach(struct gif_softc *sc) 419207753Smm{ 420207753Smm int error; 421207753Smm 422207753Smm error = encap_detach(sc->encap_cookie4); 423207753Smm if (error == 0) 424207753Smm sc->encap_cookie4 = NULL; 425207753Smm return error; 426207753Smm} 427207753Smm