ip_gre.c revision 103026
1103026Ssobomax/* $NetBSD: ip_gre.c,v 1.21 2002/08/14 00:23:30 itojun Exp $ */ 2103026Ssobomax/* $FreeBSD: head/sys/netinet/ip_gre.c 103026 2002-09-06 17:12:50Z sobomax $ */ 3103026Ssobomax 4103026Ssobomax/* 5103026Ssobomax * Copyright (c) 1998 The NetBSD Foundation, Inc. 6103026Ssobomax * All rights reserved. 7103026Ssobomax * 8103026Ssobomax * This code is derived from software contributed to The NetBSD Foundation 9103026Ssobomax * by Heiko W.Rupp <hwr@pilhuhn.de> 10103026Ssobomax * 11103026Ssobomax * Redistribution and use in source and binary forms, with or without 12103026Ssobomax * modification, are permitted provided that the following conditions 13103026Ssobomax * are met: 14103026Ssobomax * 1. Redistributions of source code must retain the above copyright 15103026Ssobomax * notice, this list of conditions and the following disclaimer. 16103026Ssobomax * 2. Redistributions in binary form must reproduce the above copyright 17103026Ssobomax * notice, this list of conditions and the following disclaimer in the 18103026Ssobomax * documentation and/or other materials provided with the distribution. 19103026Ssobomax * 3. All advertising materials mentioning features or use of this software 20103026Ssobomax * must display the following acknowledgement: 21103026Ssobomax * This product includes software developed by the NetBSD 22103026Ssobomax * Foundation, Inc. and its contributors. 23103026Ssobomax * 4. Neither the name of The NetBSD Foundation nor the names of its 24103026Ssobomax * contributors may be used to endorse or promote products derived 25103026Ssobomax * from this software without specific prior written permission. 26103026Ssobomax * 27103026Ssobomax * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28103026Ssobomax * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29103026Ssobomax * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30103026Ssobomax * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31103026Ssobomax * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32103026Ssobomax * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33103026Ssobomax * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34103026Ssobomax * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35103026Ssobomax * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36103026Ssobomax * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37103026Ssobomax * POSSIBILITY OF SUCH DAMAGE. 38103026Ssobomax */ 39103026Ssobomax 40103026Ssobomax/* 41103026Ssobomax * deencapsulate tunneled packets and send them on 42103026Ssobomax * output half is in net/if_gre.[ch] 43103026Ssobomax * This currently handles IPPROTO_GRE, IPPROTO_MOBILE 44103026Ssobomax */ 45103026Ssobomax 46103026Ssobomax#include <sys/cdefs.h> 47103026Ssobomax__RCSID("@(#) $FreeBSD: head/sys/netinet/ip_gre.c 103026 2002-09-06 17:12:50Z sobomax $"); 48103026Ssobomax 49103026Ssobomax#include "opt_inet.h" 50103026Ssobomax#include "opt_ns.h" 51103026Ssobomax#include "opt_atalk.h" 52103026Ssobomax#include "bpf.h" 53103026Ssobomax 54103026Ssobomax#include <sys/param.h> 55103026Ssobomax#include <sys/systm.h> 56103026Ssobomax#include <sys/mbuf.h> 57103026Ssobomax#include <sys/socket.h> 58103026Ssobomax#include <sys/socketvar.h> 59103026Ssobomax#include <sys/protosw.h> 60103026Ssobomax#include <sys/errno.h> 61103026Ssobomax#include <sys/time.h> 62103026Ssobomax#include <sys/kernel.h> 63103026Ssobomax#include <sys/syslog.h> 64103026Ssobomax#include <net/bpf.h> 65103026Ssobomax#include <net/ethernet.h> 66103026Ssobomax#include <net/if.h> 67103026Ssobomax#include <net/netisr.h> 68103026Ssobomax#include <net/route.h> 69103026Ssobomax#include <net/raw_cb.h> 70103026Ssobomax 71103026Ssobomax#ifdef INET 72103026Ssobomax#include <netinet/in.h> 73103026Ssobomax#include <netinet/in_var.h> 74103026Ssobomax#include <netinet/in_systm.h> 75103026Ssobomax#include <netinet/ip.h> 76103026Ssobomax#include <netinet/ip_var.h> 77103026Ssobomax#include <netinet/ip_gre.h> 78103026Ssobomax#include <machine/in_cksum.h> 79103026Ssobomax#else 80103026Ssobomax#error ip_gre input without IP? 81103026Ssobomax#endif 82103026Ssobomax 83103026Ssobomax#ifdef NS 84103026Ssobomax#include <netns/ns.h> 85103026Ssobomax#include <netns/ns_if.h> 86103026Ssobomax#endif 87103026Ssobomax 88103026Ssobomax#ifdef NETATALK 89103026Ssobomax#include <netatalk/at.h> 90103026Ssobomax#include <netatalk/at_var.h> 91103026Ssobomax#include <netatalk/at_extern.h> 92103026Ssobomax#endif 93103026Ssobomax 94103026Ssobomax/* Needs IP headers. */ 95103026Ssobomax#include <net/if_gre.h> 96103026Ssobomax 97103026Ssobomax#include <machine/stdarg.h> 98103026Ssobomax 99103026Ssobomax#if 1 100103026Ssobomaxvoid gre_inet_ntoa(struct in_addr in); /* XXX */ 101103026Ssobomax#endif 102103026Ssobomax 103103026Ssobomaxstruct gre_softc *gre_lookup __P((struct mbuf *, u_int8_t)); 104103026Ssobomax 105103026Ssobomaxint gre_input2 __P((struct mbuf *, int, u_char)); 106103026Ssobomax 107103026Ssobomax/* 108103026Ssobomax * De-encapsulate a packet and feed it back through ip input (this 109103026Ssobomax * routine is called whenever IP gets a packet with proto type 110103026Ssobomax * IPPROTO_GRE and a local destination address). 111103026Ssobomax * This really is simple 112103026Ssobomax */ 113103026Ssobomaxvoid 114103026Ssobomax#if __STDC__ 115103026Ssobomaxgre_input(struct mbuf *m, ...) 116103026Ssobomax#else 117103026Ssobomaxgre_input(m, va_alist) 118103026Ssobomax struct mbuf *m; 119103026Ssobomax va_dcl 120103026Ssobomax#endif 121103026Ssobomax{ 122103026Ssobomax int off, ret, proto; 123103026Ssobomax va_list ap; 124103026Ssobomax 125103026Ssobomax va_start(ap, m); 126103026Ssobomax off = va_arg(ap, int); 127103026Ssobomax va_end(ap); 128103026Ssobomax proto = (mtod(m, struct ip *))->ip_p; 129103026Ssobomax 130103026Ssobomax ret = gre_input2(m, off, proto); 131103026Ssobomax /* 132103026Ssobomax * ret == 0 : packet not processed, meaning that 133103026Ssobomax * no matching tunnel that is up is found. 134103026Ssobomax * we inject it to raw ip socket to see if anyone picks it up. 135103026Ssobomax */ 136103026Ssobomax if (ret == 0) 137103026Ssobomax rip_input(m, off); 138103026Ssobomax} 139103026Ssobomax 140103026Ssobomax/* 141103026Ssobomax * decapsulate. 142103026Ssobomax * Does the real work and is called from gre_input() (above) 143103026Ssobomax * returns 0 if packet is not yet processed 144103026Ssobomax * and 1 if it needs no further processing 145103026Ssobomax * proto is the protocol number of the "calling" foo_input() 146103026Ssobomax * routine. 147103026Ssobomax */ 148103026Ssobomax 149103026Ssobomaxint 150103026Ssobomaxgre_input2(struct mbuf *m ,int hlen, u_char proto) 151103026Ssobomax{ 152103026Ssobomax struct greip *gip = mtod(m, struct greip *); 153103026Ssobomax int s; 154103026Ssobomax struct ifqueue *ifq; 155103026Ssobomax struct gre_softc *sc; 156103026Ssobomax u_short flags; 157103026Ssobomax 158103026Ssobomax if ((sc = gre_lookup(m, proto)) == NULL) { 159103026Ssobomax /* No matching tunnel or tunnel is down. */ 160103026Ssobomax return (0); 161103026Ssobomax } 162103026Ssobomax 163103026Ssobomax sc->sc_if.if_ipackets++; 164103026Ssobomax sc->sc_if.if_ibytes += m->m_pkthdr.len; 165103026Ssobomax 166103026Ssobomax switch (proto) { 167103026Ssobomax case IPPROTO_GRE: 168103026Ssobomax hlen += sizeof (struct gre_h); 169103026Ssobomax 170103026Ssobomax /* process GRE flags as packet can be of variable len */ 171103026Ssobomax flags = ntohs(gip->gi_flags); 172103026Ssobomax 173103026Ssobomax /* Checksum & Offset are present */ 174103026Ssobomax if ((flags & GRE_CP) | (flags & GRE_RP)) 175103026Ssobomax hlen += 4; 176103026Ssobomax /* We don't support routing fields (variable length) */ 177103026Ssobomax if (flags & GRE_RP) 178103026Ssobomax return(0); 179103026Ssobomax if (flags & GRE_KP) 180103026Ssobomax hlen += 4; 181103026Ssobomax if (flags & GRE_SP) 182103026Ssobomax hlen +=4; 183103026Ssobomax 184103026Ssobomax switch (ntohs(gip->gi_ptype)) { /* ethertypes */ 185103026Ssobomax case ETHERTYPE_IP: /* shouldn't need a schednetisr(), as */ 186103026Ssobomax ifq = &ipintrq; /* we are in ip_input */ 187103026Ssobomax break; 188103026Ssobomax break; 189103026Ssobomax#ifdef NS 190103026Ssobomax case ETHERTYPE_NS: 191103026Ssobomax ifq = &nsintrq; 192103026Ssobomax schednetisr(NETISR_NS); 193103026Ssobomax break; 194103026Ssobomax#endif 195103026Ssobomax#ifdef NETATALK 196103026Ssobomax case ETHERTYPE_ATALK: 197103026Ssobomax ifq = &atintrq1; 198103026Ssobomax schednetisr(NETISR_ATALK); 199103026Ssobomax break; 200103026Ssobomax#endif 201103026Ssobomax case ETHERTYPE_IPV6: 202103026Ssobomax /* FALLTHROUGH */ 203103026Ssobomax default: /* others not yet supported */ 204103026Ssobomax return(0); 205103026Ssobomax } 206103026Ssobomax break; 207103026Ssobomax default: 208103026Ssobomax /* others not yet supported */ 209103026Ssobomax return(0); 210103026Ssobomax } 211103026Ssobomax 212103026Ssobomax m->m_data += hlen; 213103026Ssobomax m->m_len -= hlen; 214103026Ssobomax m->m_pkthdr.len -= hlen; 215103026Ssobomax 216103026Ssobomax#if NBPF > 0 217103026Ssobomax if (sc->sc_if.if_bpf) { 218103026Ssobomax struct mbuf m0; 219103026Ssobomax u_int32_t af = AF_INET; 220103026Ssobomax 221103026Ssobomax m0.m_next = m; 222103026Ssobomax m0.m_len = 4; 223103026Ssobomax m0.m_data = (char *)⁡ 224103026Ssobomax 225103026Ssobomax bpf_mtap(&(sc->sc_if), &m0); 226103026Ssobomax } 227103026Ssobomax#endif /*NBPF > 0*/ 228103026Ssobomax 229103026Ssobomax m->m_pkthdr.rcvif = &sc->sc_if; 230103026Ssobomax 231103026Ssobomax s = splnet(); /* possible */ 232103026Ssobomax if (_IF_QFULL(ifq)) { 233103026Ssobomax _IF_DROP(ifq); 234103026Ssobomax m_freem(m); 235103026Ssobomax } else { 236103026Ssobomax IF_ENQUEUE(ifq,m); 237103026Ssobomax } 238103026Ssobomax splx(s); 239103026Ssobomax 240103026Ssobomax return(1); /* packet is done, no further processing needed */ 241103026Ssobomax} 242103026Ssobomax 243103026Ssobomax/* 244103026Ssobomax * input routine for IPPRPOTO_MOBILE 245103026Ssobomax * This is a little bit diffrent from the other modes, as the 246103026Ssobomax * encapsulating header was not prepended, but instead inserted 247103026Ssobomax * between IP header and payload 248103026Ssobomax */ 249103026Ssobomax 250103026Ssobomaxvoid 251103026Ssobomax#if __STDC__ 252103026Ssobomaxgre_mobile_input(struct mbuf *m, ...) 253103026Ssobomax#else 254103026Ssobomaxgre_mobile_input(m, va_alist) 255103026Ssobomax struct mbuf *m; 256103026Ssobomax va_dcl 257103026Ssobomax#endif 258103026Ssobomax{ 259103026Ssobomax struct ip *ip = mtod(m, struct ip *); 260103026Ssobomax struct mobip_h *mip = mtod(m, struct mobip_h *); 261103026Ssobomax struct ifqueue *ifq; 262103026Ssobomax struct gre_softc *sc; 263103026Ssobomax int hlen,s; 264103026Ssobomax va_list ap; 265103026Ssobomax u_char osrc = 0; 266103026Ssobomax int msiz; 267103026Ssobomax 268103026Ssobomax va_start(ap,m); 269103026Ssobomax hlen = va_arg(ap, int); 270103026Ssobomax va_end(ap); 271103026Ssobomax 272103026Ssobomax if ((sc = gre_lookup(m, IPPROTO_MOBILE)) == NULL) { 273103026Ssobomax /* No matching tunnel or tunnel is down. */ 274103026Ssobomax m_freem(m); 275103026Ssobomax return; 276103026Ssobomax } 277103026Ssobomax 278103026Ssobomax sc->sc_if.if_ipackets++; 279103026Ssobomax sc->sc_if.if_ibytes += m->m_pkthdr.len; 280103026Ssobomax 281103026Ssobomax if(ntohs(mip->mh.proto) & MOB_H_SBIT) { 282103026Ssobomax osrc = 1; 283103026Ssobomax msiz = MOB_H_SIZ_L; 284103026Ssobomax mip->mi.ip_src.s_addr = mip->mh.osrc; 285103026Ssobomax } else { 286103026Ssobomax msiz = MOB_H_SIZ_S; 287103026Ssobomax } 288103026Ssobomax mip->mi.ip_dst.s_addr = mip->mh.odst; 289103026Ssobomax mip->mi.ip_p = (ntohs(mip->mh.proto) >> 8); 290103026Ssobomax 291103026Ssobomax if (gre_in_cksum((u_short*)&mip->mh,msiz) != 0) { 292103026Ssobomax m_freem(m); 293103026Ssobomax return; 294103026Ssobomax } 295103026Ssobomax 296103026Ssobomax bcopy((caddr_t)(ip) + (ip->ip_hl << 2) + msiz, (caddr_t)(ip) + 297103026Ssobomax (ip->ip_hl << 2), m->m_len - msiz - (ip->ip_hl << 2)); 298103026Ssobomax m->m_len -= msiz; 299103026Ssobomax m->m_pkthdr.len -= msiz; 300103026Ssobomax 301103026Ssobomax /* 302103026Ssobomax * On FreeBSD, rip_input() supplies us with ip->ip_len 303103026Ssobomax * already converted into host byteorder and also decreases 304103026Ssobomax * it by the lengh of IP header, however, ip_input() expects 305103026Ssobomax * that this field is in the original format (network byteorder 306103026Ssobomax * and full size of IP packet), so that adjust accordingly. 307103026Ssobomax */ 308103026Ssobomax ip->ip_len = htons(ip->ip_len + sizeof(struct ip) - msiz); 309103026Ssobomax 310103026Ssobomax ip->ip_sum = 0; 311103026Ssobomax ip->ip_sum = in_cksum(m, (ip->ip_hl << 2)); 312103026Ssobomax 313103026Ssobomax#if NBPF > 0 314103026Ssobomax if (sc->sc_if.if_bpf) { 315103026Ssobomax struct mbuf m0; 316103026Ssobomax u_int af = AF_INET; 317103026Ssobomax 318103026Ssobomax m0.m_next = m; 319103026Ssobomax m0.m_len = 4; 320103026Ssobomax m0.m_data = (char *)⁡ 321103026Ssobomax 322103026Ssobomax bpf_mtap(&(sc->sc_if), &m0); 323103026Ssobomax } 324103026Ssobomax#endif /*NBPFILTER > 0*/ 325103026Ssobomax 326103026Ssobomax m->m_pkthdr.rcvif = &sc->sc_if; 327103026Ssobomax 328103026Ssobomax ifq = &ipintrq; 329103026Ssobomax s = splnet(); /* possible */ 330103026Ssobomax if (_IF_QFULL(ifq)) { 331103026Ssobomax _IF_DROP(ifq); 332103026Ssobomax m_freem(m); 333103026Ssobomax } else { 334103026Ssobomax IF_ENQUEUE(ifq,m); 335103026Ssobomax } 336103026Ssobomax splx(s); 337103026Ssobomax} 338103026Ssobomax 339103026Ssobomax/* 340103026Ssobomax * Find the gre interface associated with our src/dst/proto set. 341103026Ssobomax */ 342103026Ssobomaxstruct gre_softc * 343103026Ssobomaxgre_lookup(m, proto) 344103026Ssobomax struct mbuf *m; 345103026Ssobomax u_int8_t proto; 346103026Ssobomax{ 347103026Ssobomax struct ip *ip = mtod(m, struct ip *); 348103026Ssobomax struct gre_softc *sc; 349103026Ssobomax 350103026Ssobomax for (sc = LIST_FIRST(&gre_softc_list); sc != NULL; 351103026Ssobomax sc = LIST_NEXT(sc, sc_list)) { 352103026Ssobomax if ((sc->g_dst.s_addr == ip->ip_src.s_addr) && 353103026Ssobomax (sc->g_src.s_addr == ip->ip_dst.s_addr) && 354103026Ssobomax (sc->g_proto == proto) && 355103026Ssobomax ((sc->sc_if.if_flags & IFF_UP) != 0)) 356103026Ssobomax return (sc); 357103026Ssobomax } 358103026Ssobomax 359103026Ssobomax return (NULL); 360103026Ssobomax} 361