in_gif.c revision 105293
1251607Sdim/* $FreeBSD: head/sys/netinet/in_gif.c 105293 2002-10-16 19:49:37Z ume $ */ 2251607Sdim/* $KAME: in_gif.c,v 1.54 2001/05/14 14:02:16 itojun Exp $ */ 3251607Sdim 4251607Sdim/* 5251607Sdim * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6251607Sdim * All rights reserved. 7251607Sdim * 8251607Sdim * Redistribution and use in source and binary forms, with or without 9251607Sdim * modification, are permitted provided that the following conditions 10251607Sdim * are met: 11251607Sdim * 1. Redistributions of source code must retain the above copyright 12251607Sdim * notice, this list of conditions and the following disclaimer. 13251607Sdim * 2. Redistributions in binary form must reproduce the above copyright 14251607Sdim * notice, this list of conditions and the following disclaimer in the 15251607Sdim * documentation and/or other materials provided with the distribution. 16251607Sdim * 3. Neither the name of the project nor the names of its contributors 17251607Sdim * may be used to endorse or promote products derived from this software 18251607Sdim * without specific prior written permission. 19288943Sdim * 20288943Sdim * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21288943Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22288943Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23288943Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24251607Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25251607Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26251607Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27251607Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28251607Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29251607Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30251607Sdim * SUCH DAMAGE. 31251607Sdim */ 32261991Sdim 33251607Sdim#include "opt_mrouting.h" 34251607Sdim#include "opt_inet.h" 35251607Sdim#include "opt_inet6.h" 36261991Sdim 37261991Sdim#include <sys/param.h> 38261991Sdim#include <sys/systm.h> 39261991Sdim#include <sys/socket.h> 40261991Sdim#include <sys/sockio.h> 41261991Sdim#include <sys/mbuf.h> 42288943Sdim#include <sys/errno.h> 43288943Sdim#include <sys/kernel.h> 44288943Sdim#include <sys/sysctl.h> 45288943Sdim#include <sys/protosw.h> 46288943Sdim 47261991Sdim#include <sys/malloc.h> 48261991Sdim 49261991Sdim#include <net/if.h> 50261991Sdim#include <net/route.h> 51261991Sdim 52261991Sdim#include <netinet/in.h> 53261991Sdim#include <netinet/in_systm.h> 54288943Sdim#include <netinet/ip.h> 55288943Sdim#include <netinet/ip_var.h> 56288943Sdim#include <netinet/in_gif.h> 57288943Sdim#include <netinet/in_var.h> 58261991Sdim#include <netinet/ip_encap.h> 59251607Sdim#include <netinet/ip_ecn.h> 60261991Sdim 61261991Sdim#ifdef INET6 62261991Sdim#include <netinet/ip6.h> 63261991Sdim#endif 64261991Sdim 65261991Sdim#ifdef MROUTING 66251607Sdim#include <netinet/ip_mroute.h> 67251607Sdim#endif /* MROUTING */ 68251607Sdim 69251607Sdim#include <net/if_gif.h> 70251607Sdim 71261991Sdim#include <net/net_osdep.h> 72261991Sdim 73261991Sdimstatic int gif_validate4(const struct ip *, struct gif_softc *, 74261991Sdim struct ifnet *); 75251607Sdim 76261991Sdimextern struct domain inetdomain; 77251607Sdimstruct protosw in_gif_protosw = 78251607Sdim{ SOCK_RAW, &inetdomain, 0/*IPPROTO_IPV[46]*/, PR_ATOMIC|PR_ADDR, 79251607Sdim in_gif_input, (pr_output_t*)rip_output, 0, rip_ctloutput, 80251607Sdim 0, 81288943Sdim 0, 0, 0, 0, 82288943Sdim &rip_usrreqs 83288943Sdim}; 84288943Sdim 85288943Sdimstatic int ip_gif_ttl = GIF_TTL; 86288943SdimSYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW, 87288943Sdim &ip_gif_ttl, 0, ""); 88288943Sdim 89288943Sdimint 90288943Sdimin_gif_output(ifp, family, m, rt) 91288943Sdim struct ifnet *ifp; 92288943Sdim int family; 93288943Sdim struct mbuf *m; 94288943Sdim struct rtentry *rt; 95288943Sdim{ 96288943Sdim struct gif_softc *sc = (struct gif_softc*)ifp; 97251607Sdim struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst; 98261991Sdim struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc; 99261991Sdim struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst; 100261991Sdim struct ip iphdr; /* capsule IP header, host byte ordered */ 101261991Sdim int proto, error; 102261991Sdim u_int8_t tos; 103261991Sdim 104261991Sdim if (sin_src == NULL || sin_dst == NULL || 105261991Sdim sin_src->sin_family != AF_INET || 106261991Sdim sin_dst->sin_family != AF_INET) { 107261991Sdim m_freem(m); 108261991Sdim return EAFNOSUPPORT; 109251607Sdim } 110261991Sdim 111251607Sdim switch (family) { 112288943Sdim#ifdef INET 113251607Sdim case AF_INET: 114251607Sdim { 115251607Sdim struct ip *ip; 116261991Sdim 117251607Sdim proto = IPPROTO_IPV4; 118251607Sdim if (m->m_len < sizeof(*ip)) { 119251607Sdim m = m_pullup(m, sizeof(*ip)); 120251607Sdim if (!m) 121251607Sdim return ENOBUFS; 122261991Sdim } 123251607Sdim ip = mtod(m, struct ip *); 124251607Sdim tos = ip->ip_tos; 125251607Sdim break; 126251607Sdim } 127261991Sdim#endif /* INET */ 128261991Sdim#ifdef INET6 129261991Sdim case AF_INET6: 130261991Sdim { 131261991Sdim struct ip6_hdr *ip6; 132261991Sdim proto = IPPROTO_IPV6; 133261991Sdim if (m->m_len < sizeof(*ip6)) { 134261991Sdim m = m_pullup(m, sizeof(*ip6)); 135261991Sdim if (!m) 136288943Sdim return ENOBUFS; 137288943Sdim } 138288943Sdim ip6 = mtod(m, struct ip6_hdr *); 139288943Sdim tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 140288943Sdim break; 141288943Sdim } 142288943Sdim#endif /* INET6 */ 143251607Sdim default: 144251607Sdim#ifdef DEBUG 145251607Sdim printf("in_gif_output: warning: unknown family %d passed\n", 146251607Sdim family); 147251607Sdim#endif 148251607Sdim m_freem(m); 149251607Sdim return EAFNOSUPPORT; 150251607Sdim } 151251607Sdim 152288943Sdim bzero(&iphdr, sizeof(iphdr)); 153251607Sdim iphdr.ip_src = sin_src->sin_addr; 154251607Sdim /* bidirectional configured tunnel mode */ 155251607Sdim if (sin_dst->sin_addr.s_addr != INADDR_ANY) 156251607Sdim iphdr.ip_dst = sin_dst->sin_addr; 157251607Sdim else { 158288943Sdim m_freem(m); 159251607Sdim return ENETUNREACH; 160251607Sdim } 161251607Sdim iphdr.ip_p = proto; 162251607Sdim /* version will be set in ip_output() */ 163251607Sdim iphdr.ip_ttl = ip_gif_ttl; 164288943Sdim iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip); 165251607Sdim if (ifp->if_flags & IFF_LINK1) 166251607Sdim ip_ecn_ingress(ECN_ALLOWED, &iphdr.ip_tos, &tos); 167251607Sdim else 168251607Sdim ip_ecn_ingress(ECN_NOCARE, &iphdr.ip_tos, &tos); 169251607Sdim 170288943Sdim /* prepend new IP header */ 171251607Sdim M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); 172251607Sdim if (m && m->m_len < sizeof(struct ip)) 173251607Sdim m = m_pullup(m, sizeof(struct ip)); 174251607Sdim if (m == NULL) { 175251607Sdim printf("ENOBUFS in in_gif_output %d\n", __LINE__); 176288943Sdim return ENOBUFS; 177251607Sdim } 178251607Sdim bcopy(&iphdr, mtod(m, struct ip *), sizeof(struct ip)); 179251607Sdim 180251607Sdim if (dst->sin_family != sin_dst->sin_family || 181251607Sdim dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) { 182288943Sdim /* cache route doesn't match */ 183251607Sdim dst->sin_family = sin_dst->sin_family; 184251607Sdim dst->sin_len = sizeof(struct sockaddr_in); 185251607Sdim dst->sin_addr = sin_dst->sin_addr; 186251607Sdim if (sc->gif_ro.ro_rt) { 187288943Sdim RTFREE(sc->gif_ro.ro_rt); 188288943Sdim sc->gif_ro.ro_rt = NULL; 189251607Sdim } 190251607Sdim#if 0 191251607Sdim sc->gif_if.if_mtu = GIF_MTU; 192251607Sdim#endif 193288943Sdim } 194288943Sdim 195251607Sdim if (sc->gif_ro.ro_rt == NULL) { 196251607Sdim rtalloc(&sc->gif_ro); 197288943Sdim if (sc->gif_ro.ro_rt == NULL) { 198288943Sdim m_freem(m); 199288943Sdim return ENETUNREACH; 200288943Sdim } 201288943Sdim 202288943Sdim /* if it constitutes infinite encapsulation, punt. */ 203288943Sdim if (sc->gif_ro.ro_rt->rt_ifp == ifp) { 204288943Sdim m_freem(m); 205288943Sdim return ENETUNREACH; /* XXX */ 206288943Sdim } 207288943Sdim#if 0 208288943Sdim ifp->if_mtu = sc->gif_ro.ro_rt->rt_ifp->if_mtu 209251607Sdim - sizeof(struct ip); 210251607Sdim#endif 211288943Sdim } 212288943Sdim 213251607Sdim error = ip_output(m, NULL, &sc->gif_ro, 0, NULL, NULL); 214251607Sdim return(error); 215251607Sdim} 216251607Sdim 217288943Sdimvoid 218288943Sdimin_gif_input(m, off) 219251607Sdim struct mbuf *m; 220251607Sdim int off; 221251607Sdim{ 222251607Sdim struct ifnet *gifp = NULL; 223288943Sdim struct ip *ip; 224288943Sdim int af; 225251607Sdim u_int8_t otos; 226251607Sdim int proto; 227251607Sdim 228251607Sdim ip = mtod(m, struct ip *); 229288943Sdim proto = ip->ip_p; 230288943Sdim 231251607Sdim gifp = (struct ifnet *)encap_getarg(m); 232251607Sdim 233251607Sdim if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { 234251607Sdim m_freem(m); 235288943Sdim ipstat.ips_nogif++; 236288943Sdim return; 237251607Sdim } 238251607Sdim 239251607Sdim otos = ip->ip_tos; 240251607Sdim m_adj(m, off); 241251607Sdim 242251607Sdim switch (proto) { 243288943Sdim#ifdef INET 244288943Sdim case IPPROTO_IPV4: 245288943Sdim { 246251607Sdim struct ip *ip; 247251607Sdim af = AF_INET; 248251607Sdim if (m->m_len < sizeof(*ip)) { 249251607Sdim m = m_pullup(m, sizeof(*ip)); 250288943Sdim if (!m) 251251607Sdim return; 252251607Sdim } 253251607Sdim ip = mtod(m, struct ip *); 254251607Sdim if (gifp->if_flags & IFF_LINK1) 255251607Sdim ip_ecn_egress(ECN_ALLOWED, &otos, &ip->ip_tos); 256251607Sdim else 257251607Sdim ip_ecn_egress(ECN_NOCARE, &otos, &ip->ip_tos); 258251607Sdim break; 259251607Sdim } 260251607Sdim#endif 261251607Sdim#ifdef INET6 262251607Sdim case IPPROTO_IPV6: 263251607Sdim { 264251607Sdim struct ip6_hdr *ip6; 265251607Sdim u_int8_t itos; 266251607Sdim af = AF_INET6; 267251607Sdim if (m->m_len < sizeof(*ip6)) { 268251607Sdim m = m_pullup(m, sizeof(*ip6)); 269251607Sdim if (!m) 270251607Sdim return; 271251607Sdim } 272251607Sdim ip6 = mtod(m, struct ip6_hdr *); 273251607Sdim itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 274251607Sdim if (gifp->if_flags & IFF_LINK1) 275251607Sdim ip_ecn_egress(ECN_ALLOWED, &otos, &itos); 276251607Sdim else 277251607Sdim ip_ecn_egress(ECN_NOCARE, &otos, &itos); 278251607Sdim ip6->ip6_flow &= ~htonl(0xff << 20); 279251607Sdim ip6->ip6_flow |= htonl((u_int32_t)itos << 20); 280251607Sdim break; 281288943Sdim } 282288943Sdim#endif /* INET6 */ 283288943Sdim default: 284288943Sdim ipstat.ips_nogif++; 285288943Sdim m_freem(m); 286288943Sdim return; 287288943Sdim } 288288943Sdim gif_input(m, af, gifp); 289288943Sdim return; 290288943Sdim} 291288943Sdim 292288943Sdim/* 293276479Sdim * validate outer address. 294276479Sdim */ 295276479Sdimstatic int 296276479Sdimgif_validate4(ip, sc, ifp) 297288943Sdim const struct ip *ip; 298288943Sdim struct gif_softc *sc; 299288943Sdim struct ifnet *ifp; 300288943Sdim{ 301288943Sdim struct sockaddr_in *src, *dst; 302288943Sdim struct in_ifaddr *ia4; 303276479Sdim 304276479Sdim src = (struct sockaddr_in *)sc->gif_psrc; 305276479Sdim dst = (struct sockaddr_in *)sc->gif_pdst; 306276479Sdim 307251607Sdim /* check for address match */ 308251607Sdim if (src->sin_addr.s_addr != ip->ip_dst.s_addr || 309251607Sdim dst->sin_addr.s_addr != ip->ip_src.s_addr) 310251607Sdim return 0; 311251607Sdim 312251607Sdim /* martian filters on outer source - NOT done in ip_input! */ 313251607Sdim if (IN_MULTICAST(ntohl(ip->ip_src.s_addr))) 314251607Sdim return 0; 315251607Sdim switch ((ntohl(ip->ip_src.s_addr) & 0xff000000) >> 24) { 316251607Sdim case 0: case 127: case 255: 317288943Sdim return 0; 318288943Sdim } 319288943Sdim /* reject packets with broadcast on source */ 320288943Sdim TAILQ_FOREACH(ia4, &in_ifaddrhead, ia_link) 321251607Sdim { 322251607Sdim if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) 323251607Sdim continue; 324251607Sdim if (ip->ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr) 325251607Sdim return 0; 326251607Sdim } 327251607Sdim 328251607Sdim /* ingress filters on outer source */ 329251607Sdim if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) { 330251607Sdim struct sockaddr_in sin; 331251607Sdim struct rtentry *rt; 332251607Sdim 333251607Sdim bzero(&sin, sizeof(sin)); 334251607Sdim sin.sin_family = AF_INET; 335251607Sdim sin.sin_len = sizeof(struct sockaddr_in); 336251607Sdim sin.sin_addr = ip->ip_src; 337251607Sdim rt = rtalloc1((struct sockaddr *)&sin, 0, 0UL); 338251607Sdim if (!rt || rt->rt_ifp != ifp) { 339251607Sdim#if 0 340251607Sdim log(LOG_WARNING, "%s: packet from 0x%x dropped " 341251607Sdim "due to ingress filter\n", if_name(&sc->gif_if), 342251607Sdim (u_int32_t)ntohl(sin.sin_addr.s_addr)); 343251607Sdim#endif 344251607Sdim if (rt) 345251607Sdim rtfree(rt); 346251607Sdim return 0; 347251607Sdim } 348251607Sdim rtfree(rt); 349251607Sdim } 350251607Sdim 351251607Sdim return 32 * 2; 352251607Sdim} 353251607Sdim 354251607Sdim/* 355251607Sdim * we know that we are in IFF_UP, outer address available, and outer family 356251607Sdim * matched the physical addr family. see gif_encapcheck(). 357251607Sdim */ 358251607Sdimint 359251607Sdimgif_encapcheck4(m, off, proto, arg) 360251607Sdim const struct mbuf *m; 361251607Sdim int off; 362251607Sdim int proto; 363251607Sdim void *arg; 364251607Sdim{ 365251607Sdim struct ip ip; 366251607Sdim struct gif_softc *sc; 367251607Sdim struct ifnet *ifp; 368251607Sdim 369251607Sdim /* sanity check done in caller */ 370251607Sdim sc = (struct gif_softc *)arg; 371251607Sdim 372251607Sdim /* LINTED const cast */ 373251607Sdim m_copydata(m, 0, sizeof(ip), (caddr_t)&ip); 374251607Sdim ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL; 375251607Sdim 376251607Sdim return gif_validate4(&ip, sc, ifp); 377251607Sdim} 378251607Sdim 379251607Sdimint 380251607Sdimin_gif_attach(sc) 381251607Sdim struct gif_softc *sc; 382251607Sdim{ 383251607Sdim#ifndef USE_ENCAPCHECK 384251607Sdim struct sockaddr_in mask4; 385251607Sdim 386251607Sdim bzero(&mask4, sizeof(mask4)); 387251607Sdim mask4.sin_len = sizeof(struct sockaddr_in); 388251607Sdim mask4.sin_addr.s_addr = ~0; 389251607Sdim 390251607Sdim if (!sc->gif_psrc || !sc->gif_pdst) 391251607Sdim return EINVAL; 392251607Sdim sc->encap_cookie4 = encap_attach(AF_INET, -1, sc->gif_psrc, 393251607Sdim (struct sockaddr *)&mask4, sc->gif_pdst, (struct sockaddr *)&mask4, 394251607Sdim (struct protosw *)&in_gif_protosw, sc); 395251607Sdim#else 396251607Sdim sc->encap_cookie4 = encap_attach_func(AF_INET, -1, gif_encapcheck, 397251607Sdim &in_gif_protosw, sc); 398251607Sdim#endif 399251607Sdim if (sc->encap_cookie4 == NULL) 400251607Sdim return EEXIST; 401251607Sdim return 0; 402251607Sdim} 403251607Sdim 404261991Sdimint 405261991Sdimin_gif_detach(sc) 406261991Sdim struct gif_softc *sc; 407261991Sdim{ 408251607Sdim int error; 409251607Sdim 410251607Sdim error = encap_detach(sc->encap_cookie4); 411251607Sdim if (error == 0) 412251607Sdim sc->encap_cookie4 = NULL; 413251607Sdim return error; 414251607Sdim} 415251607Sdim