1c398230bSWarner Losh /*- 28e96e13eSMaxim Sobolev * Copyright (c) 1998 The NetBSD Foundation, Inc. 3*f325335cSAndrey V. Elsukov * Copyright (c) 2014 Andrey V. Elsukov <ae@FreeBSD.org> 48e96e13eSMaxim Sobolev * All rights reserved. 58e96e13eSMaxim Sobolev * 68e96e13eSMaxim Sobolev * This code is derived from software contributed to The NetBSD Foundation 78e96e13eSMaxim Sobolev * by Heiko W.Rupp <hwr@pilhuhn.de> 88e96e13eSMaxim Sobolev * 99e669156SBjoern A. Zeeb * IPv6-over-GRE contributed by Gert Doering <gert@greenie.muc.de> 109e669156SBjoern A. Zeeb * 118e96e13eSMaxim Sobolev * Redistribution and use in source and binary forms, with or without 128e96e13eSMaxim Sobolev * modification, are permitted provided that the following conditions 138e96e13eSMaxim Sobolev * are met: 148e96e13eSMaxim Sobolev * 1. Redistributions of source code must retain the above copyright 158e96e13eSMaxim Sobolev * notice, this list of conditions and the following disclaimer. 168e96e13eSMaxim Sobolev * 2. Redistributions in binary form must reproduce the above copyright 178e96e13eSMaxim Sobolev * notice, this list of conditions and the following disclaimer in the 188e96e13eSMaxim Sobolev * documentation and/or other materials provided with the distribution. 198e96e13eSMaxim Sobolev * 208e96e13eSMaxim Sobolev * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 218e96e13eSMaxim Sobolev * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 228e96e13eSMaxim Sobolev * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 238e96e13eSMaxim Sobolev * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 248e96e13eSMaxim Sobolev * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 258e96e13eSMaxim Sobolev * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 268e96e13eSMaxim Sobolev * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 278e96e13eSMaxim Sobolev * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 288e96e13eSMaxim Sobolev * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 298e96e13eSMaxim Sobolev * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 308e96e13eSMaxim Sobolev * POSSIBILITY OF SUCH DAMAGE. 31*f325335cSAndrey V. Elsukov * 32*f325335cSAndrey V. Elsukov * $NetBSD: if_gre.c,v 1.49 2003/12/11 00:22:29 itojun Exp $ 338e96e13eSMaxim Sobolev */ 348e96e13eSMaxim Sobolev 35*f325335cSAndrey V. Elsukov #include <sys/cdefs.h> 36*f325335cSAndrey V. Elsukov __FBSDID("$FreeBSD$"); 378e96e13eSMaxim Sobolev 388e96e13eSMaxim Sobolev #include "opt_inet.h" 39f16770aeSBruce M Simpson #include "opt_inet6.h" 408e96e13eSMaxim Sobolev 418e96e13eSMaxim Sobolev #include <sys/param.h> 42e3416ab0SBjoern A. Zeeb #include <sys/jail.h> 438e96e13eSMaxim Sobolev #include <sys/kernel.h> 44*f325335cSAndrey V. Elsukov #include <sys/lock.h> 4552dcd04bSBjoern A. Zeeb #include <sys/libkern.h> 468e96e13eSMaxim Sobolev #include <sys/malloc.h> 475dba30f1SPoul-Henning Kamp #include <sys/module.h> 488e96e13eSMaxim Sobolev #include <sys/mbuf.h> 49acd3428bSRobert Watson #include <sys/priv.h> 508b07e49aSJulian Elischer #include <sys/proc.h> 518e96e13eSMaxim Sobolev #include <sys/protosw.h> 52*f325335cSAndrey V. Elsukov #include <sys/rmlock.h> 538e96e13eSMaxim Sobolev #include <sys/socket.h> 548e96e13eSMaxim Sobolev #include <sys/sockio.h> 55*f325335cSAndrey V. Elsukov #include <sys/sx.h> 568e96e13eSMaxim Sobolev #include <sys/sysctl.h> 57*f325335cSAndrey V. Elsukov #include <sys/syslog.h> 581b861caaSBruce Evans #include <sys/systm.h> 598e96e13eSMaxim Sobolev 608e96e13eSMaxim Sobolev #include <net/ethernet.h> 618e96e13eSMaxim Sobolev #include <net/if.h> 6276039bc8SGleb Smirnoff #include <net/if_var.h> 63f889d2efSBrooks Davis #include <net/if_clone.h> 648e96e13eSMaxim Sobolev #include <net/if_types.h> 65*f325335cSAndrey V. Elsukov #include <net/netisr.h> 66530c0060SRobert Watson #include <net/vnet.h> 678e96e13eSMaxim Sobolev 688e96e13eSMaxim Sobolev #include <netinet/in.h> 69*f325335cSAndrey V. Elsukov #ifdef INET 708e96e13eSMaxim Sobolev #include <netinet/in_systm.h> 718e96e13eSMaxim Sobolev #include <netinet/in_var.h> 728e96e13eSMaxim Sobolev #include <netinet/ip.h> 738e96e13eSMaxim Sobolev #include <netinet/ip_var.h> 748e96e13eSMaxim Sobolev #endif 758e96e13eSMaxim Sobolev 76*f325335cSAndrey V. Elsukov #ifdef INET6 77*f325335cSAndrey V. Elsukov #include <netinet/ip6.h> 78*f325335cSAndrey V. Elsukov #include <netinet6/in6_var.h> 79*f325335cSAndrey V. Elsukov #include <netinet6/ip6_var.h> 80*f325335cSAndrey V. Elsukov #include <netinet6/scope6_var.h> 81*f325335cSAndrey V. Elsukov #endif 828e96e13eSMaxim Sobolev 83*f325335cSAndrey V. Elsukov #include <netinet/ip_encap.h> 84*f325335cSAndrey V. Elsukov #include <net/bpf.h> 858e96e13eSMaxim Sobolev #include <net/if_gre.h> 868e96e13eSMaxim Sobolev 87*f325335cSAndrey V. Elsukov #include <machine/in_cksum.h> 88*f325335cSAndrey V. Elsukov #include <security/mac/mac_framework.h> 898e96e13eSMaxim Sobolev 90*f325335cSAndrey V. Elsukov #define GREMTU 1500 9142a58907SGleb Smirnoff static const char grename[] = "gre"; 9242a58907SGleb Smirnoff static MALLOC_DEFINE(M_GRE, grename, "Generic Routing Encapsulation"); 93*f325335cSAndrey V. Elsukov static VNET_DEFINE(struct mtx, gre_mtx); 94*f325335cSAndrey V. Elsukov #define V_gre_mtx VNET(gre_mtx) 95*f325335cSAndrey V. Elsukov #define GRE_LIST_LOCK_INIT(x) mtx_init(&V_gre_mtx, "gre_mtx", NULL, \ 96*f325335cSAndrey V. Elsukov MTX_DEF) 97*f325335cSAndrey V. Elsukov #define GRE_LIST_LOCK_DESTROY(x) mtx_destroy(&V_gre_mtx) 98*f325335cSAndrey V. Elsukov #define GRE_LIST_LOCK(x) mtx_lock(&V_gre_mtx) 99*f325335cSAndrey V. Elsukov #define GRE_LIST_UNLOCK(x) mtx_unlock(&V_gre_mtx) 100*f325335cSAndrey V. Elsukov 101*f325335cSAndrey V. Elsukov static VNET_DEFINE(LIST_HEAD(, gre_softc), gre_softc_list); 102*f325335cSAndrey V. Elsukov #define V_gre_softc_list VNET(gre_softc_list) 103*f325335cSAndrey V. Elsukov static struct sx gre_ioctl_sx; 104*f325335cSAndrey V. Elsukov SX_SYSINIT(gre_ioctl_sx, &gre_ioctl_sx, "gre_ioctl"); 1058e96e13eSMaxim Sobolev 1066b7330e2SSam Leffler static int gre_clone_create(struct if_clone *, int, caddr_t); 1079ee35470SAlfred Perlstein static void gre_clone_destroy(struct ifnet *); 10889c58b73SHiroki Sato static VNET_DEFINE(struct if_clone *, gre_cloner); 10989c58b73SHiroki Sato #define V_gre_cloner VNET(gre_cloner) 11042a58907SGleb Smirnoff 111*f325335cSAndrey V. Elsukov static void gre_qflush(struct ifnet *); 112*f325335cSAndrey V. Elsukov static int gre_transmit(struct ifnet *, struct mbuf *); 113c23d234cSMaxim Sobolev static int gre_ioctl(struct ifnet *, u_long, caddr_t); 11447e8d432SGleb Smirnoff static int gre_output(struct ifnet *, struct mbuf *, 11547e8d432SGleb Smirnoff const struct sockaddr *, struct route *); 1168e96e13eSMaxim Sobolev 117*f325335cSAndrey V. Elsukov static void gre_updatehdr(struct gre_softc *); 118*f325335cSAndrey V. Elsukov static int gre_set_tunnel(struct ifnet *, struct sockaddr *, 119*f325335cSAndrey V. Elsukov struct sockaddr *); 120*f325335cSAndrey V. Elsukov static void gre_delete_tunnel(struct ifnet *); 1218e96e13eSMaxim Sobolev 122*f325335cSAndrey V. Elsukov int gre_input(struct mbuf **, int *, int); 1238e96e13eSMaxim Sobolev #ifdef INET 124*f325335cSAndrey V. Elsukov extern int in_gre_attach(struct gre_softc *); 125*f325335cSAndrey V. Elsukov extern int in_gre_output(struct mbuf *, int, int); 126*f325335cSAndrey V. Elsukov #endif 127*f325335cSAndrey V. Elsukov #ifdef INET6 128*f325335cSAndrey V. Elsukov extern int in6_gre_attach(struct gre_softc *); 129*f325335cSAndrey V. Elsukov extern int in6_gre_output(struct mbuf *, int, int); 1308e96e13eSMaxim Sobolev #endif 1318e96e13eSMaxim Sobolev 1328e96e13eSMaxim Sobolev SYSCTL_DECL(_net_link); 1336472ac3dSEd Schouten static SYSCTL_NODE(_net_link, IFT_TUNNEL, gre, CTLFLAG_RW, 0, 1348e96e13eSMaxim Sobolev "Generic Routing Encapsulation"); 1358e96e13eSMaxim Sobolev #ifndef MAX_GRE_NEST 1368e96e13eSMaxim Sobolev /* 1378e96e13eSMaxim Sobolev * This macro controls the default upper limitation on nesting of gre tunnels. 1388e96e13eSMaxim Sobolev * Since, setting a large value to this macro with a careless configuration 1398e96e13eSMaxim Sobolev * may introduce system crash, we don't allow any nestings by default. 1408e96e13eSMaxim Sobolev * If you need to configure nested gre tunnels, you can define this macro 1418e96e13eSMaxim Sobolev * in your kernel configuration file. However, if you do so, please be 1428e96e13eSMaxim Sobolev * careful to configure the tunnels so that it won't make a loop. 1438e96e13eSMaxim Sobolev */ 1448e96e13eSMaxim Sobolev #define MAX_GRE_NEST 1 1458e96e13eSMaxim Sobolev #endif 146*f325335cSAndrey V. Elsukov 14789c58b73SHiroki Sato static VNET_DEFINE(int, max_gre_nesting) = MAX_GRE_NEST; 14889c58b73SHiroki Sato #define V_max_gre_nesting VNET(max_gre_nesting) 14989c58b73SHiroki Sato SYSCTL_INT(_net_link_gre, OID_AUTO, max_nesting, CTLFLAG_RW | CTLFLAG_VNET, 15089c58b73SHiroki Sato &VNET_NAME(max_gre_nesting), 0, "Max nested tunnels"); 1518e96e13eSMaxim Sobolev 152c23d234cSMaxim Sobolev static void 15389c58b73SHiroki Sato vnet_gre_init(const void *unused __unused) 1548e96e13eSMaxim Sobolev { 15589c58b73SHiroki Sato LIST_INIT(&V_gre_softc_list); 15689c58b73SHiroki Sato GRE_LIST_LOCK_INIT(); 15789c58b73SHiroki Sato V_gre_cloner = if_clone_simple(grename, gre_clone_create, 15842a58907SGleb Smirnoff gre_clone_destroy, 0); 1598e96e13eSMaxim Sobolev } 16089c58b73SHiroki Sato VNET_SYSINIT(vnet_gre_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 16189c58b73SHiroki Sato vnet_gre_init, NULL); 16289c58b73SHiroki Sato 16389c58b73SHiroki Sato static void 16489c58b73SHiroki Sato vnet_gre_uninit(const void *unused __unused) 16589c58b73SHiroki Sato { 16689c58b73SHiroki Sato 16789c58b73SHiroki Sato if_clone_detach(V_gre_cloner); 16889c58b73SHiroki Sato GRE_LIST_LOCK_DESTROY(); 16989c58b73SHiroki Sato } 17089c58b73SHiroki Sato VNET_SYSUNINIT(vnet_gre_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 17189c58b73SHiroki Sato vnet_gre_uninit, NULL); 1728e96e13eSMaxim Sobolev 173c23d234cSMaxim Sobolev static int 17489c58b73SHiroki Sato gre_clone_create(struct if_clone *ifc, int unit, caddr_t params) 1758e96e13eSMaxim Sobolev { 1768e96e13eSMaxim Sobolev struct gre_softc *sc; 1778e96e13eSMaxim Sobolev 178b3c9a01eSBruce M Simpson sc = malloc(sizeof(struct gre_softc), M_GRE, M_WAITOK | M_ZERO); 179*f325335cSAndrey V. Elsukov sc->gre_fibnum = curthread->td_proc->p_fibnum; 180066b192eSBjoern A. Zeeb GRE2IFP(sc) = if_alloc(IFT_TUNNEL); 181*f325335cSAndrey V. Elsukov GRE_LOCK_INIT(sc); 182fc74a9f9SBrooks Davis GRE2IFP(sc)->if_softc = sc; 18342a58907SGleb Smirnoff if_initname(GRE2IFP(sc), grename, unit); 184066b192eSBjoern A. Zeeb 185*f325335cSAndrey V. Elsukov GRE2IFP(sc)->if_mtu = sc->gre_mtu = GREMTU; 186fc74a9f9SBrooks Davis GRE2IFP(sc)->if_flags = IFF_POINTOPOINT|IFF_MULTICAST; 187fc74a9f9SBrooks Davis GRE2IFP(sc)->if_output = gre_output; 188fc74a9f9SBrooks Davis GRE2IFP(sc)->if_ioctl = gre_ioctl; 189*f325335cSAndrey V. Elsukov GRE2IFP(sc)->if_transmit = gre_transmit; 190*f325335cSAndrey V. Elsukov GRE2IFP(sc)->if_qflush = gre_qflush; 191fc74a9f9SBrooks Davis if_attach(GRE2IFP(sc)); 192fc74a9f9SBrooks Davis bpfattach(GRE2IFP(sc), DLT_NULL, sizeof(u_int32_t)); 19389c58b73SHiroki Sato GRE_LIST_LOCK(); 194*f325335cSAndrey V. Elsukov LIST_INSERT_HEAD(&V_gre_softc_list, sc, gre_list); 19589c58b73SHiroki Sato GRE_LIST_UNLOCK(); 1968e96e13eSMaxim Sobolev return (0); 1978e96e13eSMaxim Sobolev } 1988e96e13eSMaxim Sobolev 199c23d234cSMaxim Sobolev static void 20089c58b73SHiroki Sato gre_clone_destroy(struct ifnet *ifp) 2018e96e13eSMaxim Sobolev { 202*f325335cSAndrey V. Elsukov struct gre_softc *sc; 2038e96e13eSMaxim Sobolev 204*f325335cSAndrey V. Elsukov sx_xlock(&gre_ioctl_sx); 205*f325335cSAndrey V. Elsukov sc = ifp->if_softc; 206*f325335cSAndrey V. Elsukov gre_delete_tunnel(ifp); 20789c58b73SHiroki Sato GRE_LIST_LOCK(); 208*f325335cSAndrey V. Elsukov LIST_REMOVE(sc, gre_list); 20989c58b73SHiroki Sato GRE_LIST_UNLOCK(); 210febd0759SAndrew Thompson bpfdetach(ifp); 211febd0759SAndrew Thompson if_detach(ifp); 212*f325335cSAndrey V. Elsukov ifp->if_softc = NULL; 213*f325335cSAndrey V. Elsukov sx_xunlock(&gre_ioctl_sx); 214*f325335cSAndrey V. Elsukov 215febd0759SAndrew Thompson if_free(ifp); 216*f325335cSAndrey V. Elsukov GRE_LOCK_DESTROY(sc); 217febd0759SAndrew Thompson free(sc, M_GRE); 2188e96e13eSMaxim Sobolev } 2198e96e13eSMaxim Sobolev 220c23d234cSMaxim Sobolev static int 2218e96e13eSMaxim Sobolev gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 2228e96e13eSMaxim Sobolev { 223*f325335cSAndrey V. Elsukov GRE_RLOCK_TRACKER; 2248e96e13eSMaxim Sobolev struct ifreq *ifr = (struct ifreq *)data; 225*f325335cSAndrey V. Elsukov struct sockaddr *src, *dst; 226*f325335cSAndrey V. Elsukov struct gre_softc *sc; 227*f325335cSAndrey V. Elsukov #ifdef INET 228*f325335cSAndrey V. Elsukov struct sockaddr_in *sin = NULL; 229*f325335cSAndrey V. Elsukov #endif 230*f325335cSAndrey V. Elsukov #ifdef INET6 231*f325335cSAndrey V. Elsukov struct sockaddr_in6 *sin6 = NULL; 232*f325335cSAndrey V. Elsukov #endif 233*f325335cSAndrey V. Elsukov uint32_t opt; 234*f325335cSAndrey V. Elsukov int error; 2358e96e13eSMaxim Sobolev 2368e96e13eSMaxim Sobolev switch (cmd) { 237*f325335cSAndrey V. Elsukov case SIOCSIFMTU: 238*f325335cSAndrey V. Elsukov /* XXX: */ 239*f325335cSAndrey V. Elsukov if (ifr->ifr_mtu < 576) 240*f325335cSAndrey V. Elsukov return (EINVAL); 241*f325335cSAndrey V. Elsukov break; 2428e96e13eSMaxim Sobolev case SIOCSIFADDR: 2438e96e13eSMaxim Sobolev ifp->if_flags |= IFF_UP; 2448e96e13eSMaxim Sobolev case SIOCSIFFLAGS: 2458e96e13eSMaxim Sobolev case SIOCADDMULTI: 2468e96e13eSMaxim Sobolev case SIOCDELMULTI: 247*f325335cSAndrey V. Elsukov return (0); 2488e96e13eSMaxim Sobolev case GRESADDRS: 2498e96e13eSMaxim Sobolev case GRESADDRD: 2508e96e13eSMaxim Sobolev case GREGADDRS: 2518e96e13eSMaxim Sobolev case GREGADDRD: 252*f325335cSAndrey V. Elsukov case GRESPROTO: 253*f325335cSAndrey V. Elsukov case GREGPROTO: 254*f325335cSAndrey V. Elsukov return (EOPNOTSUPP); 255*f325335cSAndrey V. Elsukov } 256*f325335cSAndrey V. Elsukov src = dst = NULL; 257*f325335cSAndrey V. Elsukov sx_xlock(&gre_ioctl_sx); 258*f325335cSAndrey V. Elsukov sc = ifp->if_softc; 259*f325335cSAndrey V. Elsukov if (sc == NULL) { 260*f325335cSAndrey V. Elsukov error = ENXIO; 261*f325335cSAndrey V. Elsukov goto end; 262*f325335cSAndrey V. Elsukov } 263*f325335cSAndrey V. Elsukov error = 0; 264*f325335cSAndrey V. Elsukov switch (cmd) { 265*f325335cSAndrey V. Elsukov case SIOCSIFMTU: 266*f325335cSAndrey V. Elsukov GRE_WLOCK(sc); 267*f325335cSAndrey V. Elsukov sc->gre_mtu = ifr->ifr_mtu; 268*f325335cSAndrey V. Elsukov gre_updatehdr(sc); 269*f325335cSAndrey V. Elsukov GRE_WUNLOCK(sc); 270*f325335cSAndrey V. Elsukov goto end; 2718e96e13eSMaxim Sobolev case SIOCSIFPHYADDR: 272f16770aeSBruce M Simpson #ifdef INET6 273*f325335cSAndrey V. Elsukov case SIOCSIFPHYADDR_IN6: 274f16770aeSBruce M Simpson #endif 275*f325335cSAndrey V. Elsukov error = EINVAL; 276*f325335cSAndrey V. Elsukov switch (cmd) { 277*f325335cSAndrey V. Elsukov #ifdef INET 278*f325335cSAndrey V. Elsukov case SIOCSIFPHYADDR: 279*f325335cSAndrey V. Elsukov src = (struct sockaddr *) 280*f325335cSAndrey V. Elsukov &(((struct in_aliasreq *)data)->ifra_addr); 281*f325335cSAndrey V. Elsukov dst = (struct sockaddr *) 282*f325335cSAndrey V. Elsukov &(((struct in_aliasreq *)data)->ifra_dstaddr); 2838e96e13eSMaxim Sobolev break; 284*f325335cSAndrey V. Elsukov #endif 285*f325335cSAndrey V. Elsukov #ifdef INET6 286*f325335cSAndrey V. Elsukov case SIOCSIFPHYADDR_IN6: 287*f325335cSAndrey V. Elsukov src = (struct sockaddr *) 288*f325335cSAndrey V. Elsukov &(((struct in6_aliasreq *)data)->ifra_addr); 289*f325335cSAndrey V. Elsukov dst = (struct sockaddr *) 290*f325335cSAndrey V. Elsukov &(((struct in6_aliasreq *)data)->ifra_dstaddr); 291*f325335cSAndrey V. Elsukov break; 292*f325335cSAndrey V. Elsukov #endif 293*f325335cSAndrey V. Elsukov default: 294*f325335cSAndrey V. Elsukov error = EAFNOSUPPORT; 295*f325335cSAndrey V. Elsukov goto end; 2968e96e13eSMaxim Sobolev } 297*f325335cSAndrey V. Elsukov /* sa_family must be equal */ 298*f325335cSAndrey V. Elsukov if (src->sa_family != dst->sa_family || 299*f325335cSAndrey V. Elsukov src->sa_len != dst->sa_len) 300*f325335cSAndrey V. Elsukov goto end; 301*f325335cSAndrey V. Elsukov 302*f325335cSAndrey V. Elsukov /* validate sa_len */ 303*f325335cSAndrey V. Elsukov switch (src->sa_family) { 304*f325335cSAndrey V. Elsukov #ifdef INET 305*f325335cSAndrey V. Elsukov case AF_INET: 306*f325335cSAndrey V. Elsukov if (src->sa_len != sizeof(struct sockaddr_in)) 307*f325335cSAndrey V. Elsukov goto end; 308*f325335cSAndrey V. Elsukov break; 309*f325335cSAndrey V. Elsukov #endif 310*f325335cSAndrey V. Elsukov #ifdef INET6 311*f325335cSAndrey V. Elsukov case AF_INET6: 312*f325335cSAndrey V. Elsukov if (src->sa_len != sizeof(struct sockaddr_in6)) 313*f325335cSAndrey V. Elsukov goto end; 314*f325335cSAndrey V. Elsukov break; 315*f325335cSAndrey V. Elsukov #endif 316*f325335cSAndrey V. Elsukov default: 317*f325335cSAndrey V. Elsukov error = EAFNOSUPPORT; 318*f325335cSAndrey V. Elsukov goto end; 319*f325335cSAndrey V. Elsukov } 320*f325335cSAndrey V. Elsukov /* check sa_family looks sane for the cmd */ 321*f325335cSAndrey V. Elsukov error = EAFNOSUPPORT; 322*f325335cSAndrey V. Elsukov switch (cmd) { 323*f325335cSAndrey V. Elsukov #ifdef INET 324*f325335cSAndrey V. Elsukov case SIOCSIFPHYADDR: 325*f325335cSAndrey V. Elsukov if (src->sa_family == AF_INET) 326*f325335cSAndrey V. Elsukov break; 327*f325335cSAndrey V. Elsukov goto end; 328*f325335cSAndrey V. Elsukov #endif 329*f325335cSAndrey V. Elsukov #ifdef INET6 330*f325335cSAndrey V. Elsukov case SIOCSIFPHYADDR_IN6: 331*f325335cSAndrey V. Elsukov if (src->sa_family == AF_INET6) 332*f325335cSAndrey V. Elsukov break; 333*f325335cSAndrey V. Elsukov goto end; 334*f325335cSAndrey V. Elsukov #endif 335*f325335cSAndrey V. Elsukov } 336*f325335cSAndrey V. Elsukov error = EADDRNOTAVAIL; 337*f325335cSAndrey V. Elsukov switch (src->sa_family) { 338*f325335cSAndrey V. Elsukov #ifdef INET 339*f325335cSAndrey V. Elsukov case AF_INET: 340*f325335cSAndrey V. Elsukov if (satosin(src)->sin_addr.s_addr == INADDR_ANY || 341*f325335cSAndrey V. Elsukov satosin(dst)->sin_addr.s_addr == INADDR_ANY) 342*f325335cSAndrey V. Elsukov goto end; 343*f325335cSAndrey V. Elsukov break; 344*f325335cSAndrey V. Elsukov #endif 345*f325335cSAndrey V. Elsukov #ifdef INET6 346*f325335cSAndrey V. Elsukov case AF_INET6: 347*f325335cSAndrey V. Elsukov if (IN6_IS_ADDR_UNSPECIFIED(&satosin6(src)->sin6_addr) 348*f325335cSAndrey V. Elsukov || 349*f325335cSAndrey V. Elsukov IN6_IS_ADDR_UNSPECIFIED(&satosin6(dst)->sin6_addr)) 350*f325335cSAndrey V. Elsukov goto end; 351*f325335cSAndrey V. Elsukov /* 352*f325335cSAndrey V. Elsukov * Check validity of the scope zone ID of the 353*f325335cSAndrey V. Elsukov * addresses, and convert it into the kernel 354*f325335cSAndrey V. Elsukov * internal form if necessary. 355*f325335cSAndrey V. Elsukov */ 356*f325335cSAndrey V. Elsukov error = sa6_embedscope(satosin6(src), 0); 357e3416ab0SBjoern A. Zeeb if (error != 0) 358*f325335cSAndrey V. Elsukov goto end; 359*f325335cSAndrey V. Elsukov error = sa6_embedscope(satosin6(dst), 0); 360*f325335cSAndrey V. Elsukov if (error != 0) 361*f325335cSAndrey V. Elsukov goto end; 362*f325335cSAndrey V. Elsukov #endif 363*f325335cSAndrey V. Elsukov }; 364*f325335cSAndrey V. Elsukov error = gre_set_tunnel(ifp, src, dst); 365e3416ab0SBjoern A. Zeeb break; 366*f325335cSAndrey V. Elsukov case SIOCDIFPHYADDR: 367*f325335cSAndrey V. Elsukov gre_delete_tunnel(ifp); 3688e96e13eSMaxim Sobolev break; 369*f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR: 3708e96e13eSMaxim Sobolev case SIOCGIFPDSTADDR: 371f16770aeSBruce M Simpson #ifdef INET6 372*f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR_IN6: 373f16770aeSBruce M Simpson case SIOCGIFPDSTADDR_IN6: 374f16770aeSBruce M Simpson #endif 375*f325335cSAndrey V. Elsukov if (sc->gre_family == 0) { 3768e96e13eSMaxim Sobolev error = EADDRNOTAVAIL; 3778e96e13eSMaxim Sobolev break; 3788e96e13eSMaxim Sobolev } 379*f325335cSAndrey V. Elsukov GRE_RLOCK(sc); 380*f325335cSAndrey V. Elsukov switch (cmd) { 381*f325335cSAndrey V. Elsukov #ifdef INET 382*f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR: 383*f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR: 384*f325335cSAndrey V. Elsukov if (sc->gre_family != AF_INET) { 385*f325335cSAndrey V. Elsukov error = EADDRNOTAVAIL; 386131c55bcSAndrew Thompson break; 387131c55bcSAndrew Thompson } 388*f325335cSAndrey V. Elsukov sin = (struct sockaddr_in *)&ifr->ifr_addr; 389*f325335cSAndrey V. Elsukov memset(sin, 0, sizeof(*sin)); 390*f325335cSAndrey V. Elsukov sin->sin_family = AF_INET; 391*f325335cSAndrey V. Elsukov sin->sin_len = sizeof(*sin); 392*f325335cSAndrey V. Elsukov break; 393*f325335cSAndrey V. Elsukov #endif 394*f325335cSAndrey V. Elsukov #ifdef INET6 395*f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR_IN6: 396*f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR_IN6: 397*f325335cSAndrey V. Elsukov if (sc->gre_family != AF_INET6) { 398*f325335cSAndrey V. Elsukov error = EADDRNOTAVAIL; 399*f325335cSAndrey V. Elsukov break; 400*f325335cSAndrey V. Elsukov } 401*f325335cSAndrey V. Elsukov sin6 = (struct sockaddr_in6 *) 402*f325335cSAndrey V. Elsukov &(((struct in6_ifreq *)data)->ifr_addr); 403*f325335cSAndrey V. Elsukov memset(sin6, 0, sizeof(*sin6)); 404*f325335cSAndrey V. Elsukov sin6->sin6_family = AF_INET6; 405*f325335cSAndrey V. Elsukov sin6->sin6_len = sizeof(*sin6); 406*f325335cSAndrey V. Elsukov break; 407*f325335cSAndrey V. Elsukov #endif 408*f325335cSAndrey V. Elsukov } 409*f325335cSAndrey V. Elsukov if (error == 0) { 410*f325335cSAndrey V. Elsukov switch (cmd) { 411*f325335cSAndrey V. Elsukov #ifdef INET 412*f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR: 413*f325335cSAndrey V. Elsukov sin->sin_addr = sc->gre_oip.ip_src; 414*f325335cSAndrey V. Elsukov break; 415*f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR: 416*f325335cSAndrey V. Elsukov sin->sin_addr = sc->gre_oip.ip_dst; 417*f325335cSAndrey V. Elsukov break; 418*f325335cSAndrey V. Elsukov #endif 419*f325335cSAndrey V. Elsukov #ifdef INET6 420*f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR_IN6: 421*f325335cSAndrey V. Elsukov sin6->sin6_addr = sc->gre_oip6.ip6_src; 422*f325335cSAndrey V. Elsukov break; 423*f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR_IN6: 424*f325335cSAndrey V. Elsukov sin6->sin6_addr = sc->gre_oip6.ip6_dst; 425*f325335cSAndrey V. Elsukov break; 426*f325335cSAndrey V. Elsukov #endif 427*f325335cSAndrey V. Elsukov } 428*f325335cSAndrey V. Elsukov } 429*f325335cSAndrey V. Elsukov GRE_RUNLOCK(sc); 430*f325335cSAndrey V. Elsukov if (error != 0) 431*f325335cSAndrey V. Elsukov break; 432*f325335cSAndrey V. Elsukov switch (cmd) { 433*f325335cSAndrey V. Elsukov #ifdef INET 434*f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR: 435*f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR: 436*f325335cSAndrey V. Elsukov error = prison_if(curthread->td_ucred, 437*f325335cSAndrey V. Elsukov (struct sockaddr *)sin); 438*f325335cSAndrey V. Elsukov if (error != 0) 439*f325335cSAndrey V. Elsukov memset(sin, 0, sizeof(*sin)); 440*f325335cSAndrey V. Elsukov break; 441*f325335cSAndrey V. Elsukov #endif 442*f325335cSAndrey V. Elsukov #ifdef INET6 443*f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR_IN6: 444*f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR_IN6: 445*f325335cSAndrey V. Elsukov error = prison_if(curthread->td_ucred, 446*f325335cSAndrey V. Elsukov (struct sockaddr *)sin6); 447*f325335cSAndrey V. Elsukov if (error == 0) 448*f325335cSAndrey V. Elsukov error = sa6_recoverscope(sin6); 449*f325335cSAndrey V. Elsukov if (error != 0) 450*f325335cSAndrey V. Elsukov memset(sin6, 0, sizeof(*sin6)); 451*f325335cSAndrey V. Elsukov #endif 452*f325335cSAndrey V. Elsukov } 453*f325335cSAndrey V. Elsukov break; 454*f325335cSAndrey V. Elsukov case GRESKEY: 455*f325335cSAndrey V. Elsukov if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0) 456*f325335cSAndrey V. Elsukov break; 457*f325335cSAndrey V. Elsukov if ((error = copyin(ifr->ifr_data, &opt, sizeof(opt))) != 0) 458*f325335cSAndrey V. Elsukov break; 459*f325335cSAndrey V. Elsukov if (sc->gre_key != opt) { 460*f325335cSAndrey V. Elsukov GRE_WLOCK(sc); 461*f325335cSAndrey V. Elsukov sc->gre_key = opt; 462*f325335cSAndrey V. Elsukov gre_updatehdr(sc); 463*f325335cSAndrey V. Elsukov GRE_WUNLOCK(sc); 464*f325335cSAndrey V. Elsukov } 465131c55bcSAndrew Thompson break; 466131c55bcSAndrew Thompson case GREGKEY: 467*f325335cSAndrey V. Elsukov error = copyout(&sc->gre_key, ifr->ifr_data, sizeof(sc->gre_key)); 468*f325335cSAndrey V. Elsukov break; 469*f325335cSAndrey V. Elsukov case GRESOPTS: 470*f325335cSAndrey V. Elsukov if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0) 471*f325335cSAndrey V. Elsukov break; 472*f325335cSAndrey V. Elsukov if ((error = copyin(ifr->ifr_data, &opt, sizeof(opt))) != 0) 473*f325335cSAndrey V. Elsukov break; 474*f325335cSAndrey V. Elsukov if (opt & ~GRE_OPTMASK) 475*f325335cSAndrey V. Elsukov error = EINVAL; 476*f325335cSAndrey V. Elsukov else { 477*f325335cSAndrey V. Elsukov if (sc->gre_options != opt) { 478*f325335cSAndrey V. Elsukov GRE_WLOCK(sc); 479*f325335cSAndrey V. Elsukov sc->gre_options = opt; 480*f325335cSAndrey V. Elsukov gre_updatehdr(sc); 481*f325335cSAndrey V. Elsukov GRE_WUNLOCK(sc); 482*f325335cSAndrey V. Elsukov } 483*f325335cSAndrey V. Elsukov } 484131c55bcSAndrew Thompson break; 485131c55bcSAndrew Thompson 486*f325335cSAndrey V. Elsukov case GREGOPTS: 487*f325335cSAndrey V. Elsukov error = copyout(&sc->gre_options, ifr->ifr_data, 488*f325335cSAndrey V. Elsukov sizeof(sc->gre_options)); 489*f325335cSAndrey V. Elsukov break; 4908e96e13eSMaxim Sobolev default: 4918e96e13eSMaxim Sobolev error = EINVAL; 4928e96e13eSMaxim Sobolev break; 4938e96e13eSMaxim Sobolev } 494*f325335cSAndrey V. Elsukov end: 495*f325335cSAndrey V. Elsukov sx_xunlock(&gre_ioctl_sx); 4968e96e13eSMaxim Sobolev return (error); 4978e96e13eSMaxim Sobolev } 4988e96e13eSMaxim Sobolev 499*f325335cSAndrey V. Elsukov static void 500*f325335cSAndrey V. Elsukov gre_updatehdr(struct gre_softc *sc) 501*f325335cSAndrey V. Elsukov { 502*f325335cSAndrey V. Elsukov struct grehdr *gh = NULL; 503*f325335cSAndrey V. Elsukov uint32_t *opts; 504*f325335cSAndrey V. Elsukov uint16_t flags; 505*f325335cSAndrey V. Elsukov 506*f325335cSAndrey V. Elsukov GRE_WLOCK_ASSERT(sc); 507*f325335cSAndrey V. Elsukov switch (sc->gre_family) { 508*f325335cSAndrey V. Elsukov #ifdef INET 509*f325335cSAndrey V. Elsukov case AF_INET: 510*f325335cSAndrey V. Elsukov sc->gre_hlen = sizeof(struct greip); 511*f325335cSAndrey V. Elsukov sc->gre_oip.ip_v = IPPROTO_IPV4; 512*f325335cSAndrey V. Elsukov sc->gre_oip.ip_hl = sizeof(struct ip) >> 2; 513*f325335cSAndrey V. Elsukov sc->gre_oip.ip_p = IPPROTO_GRE; 514*f325335cSAndrey V. Elsukov gh = &sc->gre_gihdr->gi_gre; 515*f325335cSAndrey V. Elsukov break; 516*f325335cSAndrey V. Elsukov #endif 517*f325335cSAndrey V. Elsukov #ifdef INET6 518*f325335cSAndrey V. Elsukov case AF_INET6: 519*f325335cSAndrey V. Elsukov sc->gre_hlen = sizeof(struct greip6); 520*f325335cSAndrey V. Elsukov sc->gre_oip6.ip6_vfc = IPV6_VERSION; 521*f325335cSAndrey V. Elsukov sc->gre_oip6.ip6_nxt = IPPROTO_GRE; 522*f325335cSAndrey V. Elsukov gh = &sc->gre_gi6hdr->gi6_gre; 523*f325335cSAndrey V. Elsukov break; 524*f325335cSAndrey V. Elsukov #endif 525*f325335cSAndrey V. Elsukov default: 526*f325335cSAndrey V. Elsukov return; 527*f325335cSAndrey V. Elsukov } 528*f325335cSAndrey V. Elsukov flags = 0; 529*f325335cSAndrey V. Elsukov opts = gh->gre_opts; 530*f325335cSAndrey V. Elsukov if (sc->gre_options & GRE_ENABLE_CSUM) { 531*f325335cSAndrey V. Elsukov flags |= GRE_FLAGS_CP; 532*f325335cSAndrey V. Elsukov sc->gre_hlen += 2 * sizeof(uint16_t); 533*f325335cSAndrey V. Elsukov *opts++ = 0; 534*f325335cSAndrey V. Elsukov } 535*f325335cSAndrey V. Elsukov if (sc->gre_key != 0) { 536*f325335cSAndrey V. Elsukov flags |= GRE_FLAGS_KP; 537*f325335cSAndrey V. Elsukov sc->gre_hlen += sizeof(uint32_t); 538*f325335cSAndrey V. Elsukov *opts++ = htonl(sc->gre_key); 539*f325335cSAndrey V. Elsukov } 540*f325335cSAndrey V. Elsukov if (sc->gre_options & GRE_ENABLE_SEQ) { 541*f325335cSAndrey V. Elsukov flags |= GRE_FLAGS_SP; 542*f325335cSAndrey V. Elsukov sc->gre_hlen += sizeof(uint32_t); 543*f325335cSAndrey V. Elsukov *opts++ = 0; 544*f325335cSAndrey V. Elsukov } else 545*f325335cSAndrey V. Elsukov sc->gre_oseq = 0; 546*f325335cSAndrey V. Elsukov gh->gre_flags = htons(flags); 547*f325335cSAndrey V. Elsukov GRE2IFP(sc)->if_mtu = sc->gre_mtu - sc->gre_hlen; 548*f325335cSAndrey V. Elsukov } 549*f325335cSAndrey V. Elsukov 550*f325335cSAndrey V. Elsukov static void 551*f325335cSAndrey V. Elsukov gre_detach(struct gre_softc *sc) 552*f325335cSAndrey V. Elsukov { 553*f325335cSAndrey V. Elsukov 554*f325335cSAndrey V. Elsukov sx_assert(&gre_ioctl_sx, SA_XLOCKED); 555*f325335cSAndrey V. Elsukov if (sc->gre_ecookie != NULL) 556*f325335cSAndrey V. Elsukov encap_detach(sc->gre_ecookie); 557*f325335cSAndrey V. Elsukov sc->gre_ecookie = NULL; 558*f325335cSAndrey V. Elsukov } 559*f325335cSAndrey V. Elsukov 560c23d234cSMaxim Sobolev static int 561*f325335cSAndrey V. Elsukov gre_set_tunnel(struct ifnet *ifp, struct sockaddr *src, 562*f325335cSAndrey V. Elsukov struct sockaddr *dst) 5638e96e13eSMaxim Sobolev { 564*f325335cSAndrey V. Elsukov struct gre_softc *sc, *tsc; 565*f325335cSAndrey V. Elsukov #ifdef INET6 566*f325335cSAndrey V. Elsukov struct ip6_hdr *ip6; 567*f325335cSAndrey V. Elsukov #endif 568*f325335cSAndrey V. Elsukov #ifdef INET 569*f325335cSAndrey V. Elsukov struct ip *ip; 570*f325335cSAndrey V. Elsukov #endif 571*f325335cSAndrey V. Elsukov void *hdr; 572*f325335cSAndrey V. Elsukov int error; 5738e96e13eSMaxim Sobolev 574*f325335cSAndrey V. Elsukov sx_assert(&gre_ioctl_sx, SA_XLOCKED); 575*f325335cSAndrey V. Elsukov GRE_LIST_LOCK(); 576*f325335cSAndrey V. Elsukov sc = ifp->if_softc; 577*f325335cSAndrey V. Elsukov LIST_FOREACH(tsc, &V_gre_softc_list, gre_list) { 578*f325335cSAndrey V. Elsukov if (tsc == sc || tsc->gre_family != src->sa_family) 579*f325335cSAndrey V. Elsukov continue; 580*f325335cSAndrey V. Elsukov #ifdef INET 581*f325335cSAndrey V. Elsukov if (tsc->gre_family == AF_INET && 582*f325335cSAndrey V. Elsukov tsc->gre_oip.ip_src.s_addr == 583*f325335cSAndrey V. Elsukov satosin(src)->sin_addr.s_addr && 584*f325335cSAndrey V. Elsukov tsc->gre_oip.ip_dst.s_addr == 585*f325335cSAndrey V. Elsukov satosin(dst)->sin_addr.s_addr) { 586*f325335cSAndrey V. Elsukov GRE_LIST_UNLOCK(); 587*f325335cSAndrey V. Elsukov return (EADDRNOTAVAIL); 588*f325335cSAndrey V. Elsukov } 589*f325335cSAndrey V. Elsukov #endif 590*f325335cSAndrey V. Elsukov #ifdef INET6 591*f325335cSAndrey V. Elsukov if (tsc->gre_family == AF_INET6 && 592*f325335cSAndrey V. Elsukov IN6_ARE_ADDR_EQUAL(&tsc->gre_oip6.ip6_src, 593*f325335cSAndrey V. Elsukov &satosin6(src)->sin6_addr) && 594*f325335cSAndrey V. Elsukov IN6_ARE_ADDR_EQUAL(&tsc->gre_oip6.ip6_dst, 595*f325335cSAndrey V. Elsukov &satosin6(dst)->sin6_addr)) { 596*f325335cSAndrey V. Elsukov GRE_LIST_UNLOCK(); 597*f325335cSAndrey V. Elsukov return (EADDRNOTAVAIL); 598*f325335cSAndrey V. Elsukov } 599*f325335cSAndrey V. Elsukov #endif 600*f325335cSAndrey V. Elsukov } 601*f325335cSAndrey V. Elsukov GRE_LIST_UNLOCK(); 6028e96e13eSMaxim Sobolev 603*f325335cSAndrey V. Elsukov switch (src->sa_family) { 604*f325335cSAndrey V. Elsukov #ifdef INET 605*f325335cSAndrey V. Elsukov case AF_INET: 606*f325335cSAndrey V. Elsukov hdr = ip = malloc(sizeof(struct greip) + 607*f325335cSAndrey V. Elsukov 3 * sizeof(uint32_t), M_GRE, M_WAITOK | M_ZERO); 608*f325335cSAndrey V. Elsukov ip->ip_src = satosin(src)->sin_addr; 609*f325335cSAndrey V. Elsukov ip->ip_dst = satosin(dst)->sin_addr; 610*f325335cSAndrey V. Elsukov break; 611*f325335cSAndrey V. Elsukov #endif 612*f325335cSAndrey V. Elsukov #ifdef INET6 613*f325335cSAndrey V. Elsukov case AF_INET6: 614*f325335cSAndrey V. Elsukov hdr = ip6 = malloc(sizeof(struct greip6) + 615*f325335cSAndrey V. Elsukov 3 * sizeof(uint32_t), M_GRE, M_WAITOK | M_ZERO); 616*f325335cSAndrey V. Elsukov ip6->ip6_src = satosin6(src)->sin6_addr; 617*f325335cSAndrey V. Elsukov ip6->ip6_dst = satosin6(dst)->sin6_addr; 618*f325335cSAndrey V. Elsukov break; 619*f325335cSAndrey V. Elsukov #endif 620*f325335cSAndrey V. Elsukov default: 621*f325335cSAndrey V. Elsukov return (EAFNOSUPPORT); 622*f325335cSAndrey V. Elsukov } 623*f325335cSAndrey V. Elsukov if (sc->gre_family != src->sa_family) 624*f325335cSAndrey V. Elsukov gre_detach(sc); 625*f325335cSAndrey V. Elsukov GRE_WLOCK(sc); 626*f325335cSAndrey V. Elsukov if (sc->gre_family != 0) 627*f325335cSAndrey V. Elsukov free(sc->gre_hdr, M_GRE); 628*f325335cSAndrey V. Elsukov sc->gre_family = src->sa_family; 629*f325335cSAndrey V. Elsukov sc->gre_hdr = hdr; 630*f325335cSAndrey V. Elsukov sc->gre_oseq = 0; 631*f325335cSAndrey V. Elsukov sc->gre_iseq = UINT32_MAX; 632*f325335cSAndrey V. Elsukov gre_updatehdr(sc); 633*f325335cSAndrey V. Elsukov GRE_WUNLOCK(sc); 6348e96e13eSMaxim Sobolev 635*f325335cSAndrey V. Elsukov switch (src->sa_family) { 636*f325335cSAndrey V. Elsukov #ifdef INET 637*f325335cSAndrey V. Elsukov case AF_INET: 638*f325335cSAndrey V. Elsukov error = in_gre_attach(sc); 639*f325335cSAndrey V. Elsukov break; 640*f325335cSAndrey V. Elsukov #endif 641*f325335cSAndrey V. Elsukov #ifdef INET6 642*f325335cSAndrey V. Elsukov case AF_INET6: 643*f325335cSAndrey V. Elsukov error = in6_gre_attach(sc); 644*f325335cSAndrey V. Elsukov break; 645*f325335cSAndrey V. Elsukov #endif 646*f325335cSAndrey V. Elsukov } 647*f325335cSAndrey V. Elsukov if (error == 0) 648*f325335cSAndrey V. Elsukov ifp->if_drv_flags |= IFF_DRV_RUNNING; 649*f325335cSAndrey V. Elsukov return (error); 6508e96e13eSMaxim Sobolev } 6518e96e13eSMaxim Sobolev 652*f325335cSAndrey V. Elsukov static void 653*f325335cSAndrey V. Elsukov gre_delete_tunnel(struct ifnet *ifp) 654*f325335cSAndrey V. Elsukov { 655*f325335cSAndrey V. Elsukov struct gre_softc *sc = ifp->if_softc; 656*f325335cSAndrey V. Elsukov int family; 6578e96e13eSMaxim Sobolev 658*f325335cSAndrey V. Elsukov GRE_WLOCK(sc); 659*f325335cSAndrey V. Elsukov family = sc->gre_family; 660*f325335cSAndrey V. Elsukov sc->gre_family = 0; 661*f325335cSAndrey V. Elsukov GRE_WUNLOCK(sc); 662*f325335cSAndrey V. Elsukov if (family != 0) { 663*f325335cSAndrey V. Elsukov gre_detach(sc); 664*f325335cSAndrey V. Elsukov free(sc->gre_hdr, M_GRE); 665*f325335cSAndrey V. Elsukov } 666*f325335cSAndrey V. Elsukov ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 667*f325335cSAndrey V. Elsukov } 6688e96e13eSMaxim Sobolev 669*f325335cSAndrey V. Elsukov int 670*f325335cSAndrey V. Elsukov gre_input(struct mbuf **mp, int *offp, int proto) 671*f325335cSAndrey V. Elsukov { 672*f325335cSAndrey V. Elsukov struct gre_softc *sc; 673*f325335cSAndrey V. Elsukov struct grehdr *gh; 674*f325335cSAndrey V. Elsukov struct ifnet *ifp; 675*f325335cSAndrey V. Elsukov struct mbuf *m; 676*f325335cSAndrey V. Elsukov uint32_t *opts, key; 677*f325335cSAndrey V. Elsukov uint16_t flags; 678*f325335cSAndrey V. Elsukov int hlen, isr, af; 679*f325335cSAndrey V. Elsukov 680*f325335cSAndrey V. Elsukov m = *mp; 681*f325335cSAndrey V. Elsukov sc = encap_getarg(m); 682*f325335cSAndrey V. Elsukov KASSERT(sc != NULL, ("encap_getarg returned NULL")); 683*f325335cSAndrey V. Elsukov 684*f325335cSAndrey V. Elsukov ifp = GRE2IFP(sc); 685*f325335cSAndrey V. Elsukov gh = (struct grehdr *)mtodo(m, *offp); 686*f325335cSAndrey V. Elsukov flags = ntohs(gh->gre_flags); 687*f325335cSAndrey V. Elsukov if (flags & ~GRE_FLAGS_MASK) 688*f325335cSAndrey V. Elsukov goto drop; 689*f325335cSAndrey V. Elsukov opts = gh->gre_opts; 690*f325335cSAndrey V. Elsukov hlen = 2 * sizeof(uint16_t); 691*f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_CP) { 692*f325335cSAndrey V. Elsukov /* reserved1 field must be zero */ 693*f325335cSAndrey V. Elsukov if (((uint16_t *)opts)[1] != 0) 694*f325335cSAndrey V. Elsukov goto drop; 695*f325335cSAndrey V. Elsukov if (in_cksum_skip(m, m->m_pkthdr.len, *offp) != 0) 696*f325335cSAndrey V. Elsukov goto drop; 697*f325335cSAndrey V. Elsukov hlen += 2 * sizeof(uint16_t); 698*f325335cSAndrey V. Elsukov opts++; 699*f325335cSAndrey V. Elsukov } 700*f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_KP) { 701*f325335cSAndrey V. Elsukov key = ntohl(*opts); 702*f325335cSAndrey V. Elsukov hlen += sizeof(uint32_t); 703*f325335cSAndrey V. Elsukov opts++; 704*f325335cSAndrey V. Elsukov } else 705*f325335cSAndrey V. Elsukov key = 0; 7068e96e13eSMaxim Sobolev /* 707*f325335cSAndrey V. Elsukov if (sc->gre_key != 0 && (key != sc->gre_key || key != 0)) 708*f325335cSAndrey V. Elsukov goto drop; 7098e96e13eSMaxim Sobolev */ 710*f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_SP) { 711*f325335cSAndrey V. Elsukov /* seq = ntohl(*opts); */ 712*f325335cSAndrey V. Elsukov hlen += sizeof(uint32_t); 713*f325335cSAndrey V. Elsukov } 714*f325335cSAndrey V. Elsukov switch (ntohs(gh->gre_proto)) { 715*f325335cSAndrey V. Elsukov case ETHERTYPE_WCCP: 716*f325335cSAndrey V. Elsukov /* 717*f325335cSAndrey V. Elsukov * For WCCP skip an additional 4 bytes if after GRE header 718*f325335cSAndrey V. Elsukov * doesn't follow an IP header. 719*f325335cSAndrey V. Elsukov */ 720*f325335cSAndrey V. Elsukov if (flags == 0 && (*(uint8_t *)gh->gre_opts & 0xF0) != 0x40) 721*f325335cSAndrey V. Elsukov hlen += sizeof(uint32_t); 722*f325335cSAndrey V. Elsukov /* FALLTHROUGH */ 723*f325335cSAndrey V. Elsukov case ETHERTYPE_IP: 724*f325335cSAndrey V. Elsukov isr = NETISR_IP; 725*f325335cSAndrey V. Elsukov af = AF_INET; 726*f325335cSAndrey V. Elsukov break; 727*f325335cSAndrey V. Elsukov case ETHERTYPE_IPV6: 728*f325335cSAndrey V. Elsukov isr = NETISR_IPV6; 729*f325335cSAndrey V. Elsukov af = AF_INET6; 730*f325335cSAndrey V. Elsukov break; 731*f325335cSAndrey V. Elsukov default: 732*f325335cSAndrey V. Elsukov goto drop; 733*f325335cSAndrey V. Elsukov } 734*f325335cSAndrey V. Elsukov m_adj(m, *offp + hlen); 735*f325335cSAndrey V. Elsukov m_clrprotoflags(m); 736*f325335cSAndrey V. Elsukov m->m_pkthdr.rcvif = ifp; 737*f325335cSAndrey V. Elsukov M_SETFIB(m, sc->gre_fibnum); 738*f325335cSAndrey V. Elsukov #ifdef MAC 739*f325335cSAndrey V. Elsukov mac_ifnet_create_mbuf(ifp, m); 740*f325335cSAndrey V. Elsukov #endif 741*f325335cSAndrey V. Elsukov BPF_MTAP2(ifp, &af, sizeof(af), m); 742*f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 743*f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); 744*f325335cSAndrey V. Elsukov if ((ifp->if_flags & IFF_MONITOR) != 0) 745*f325335cSAndrey V. Elsukov m_freem(m); 7468e96e13eSMaxim Sobolev else 747*f325335cSAndrey V. Elsukov netisr_dispatch(isr, m); 748*f325335cSAndrey V. Elsukov return (IPPROTO_DONE); 749*f325335cSAndrey V. Elsukov drop: 750*f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 751*f325335cSAndrey V. Elsukov m_freem(m); 752*f325335cSAndrey V. Elsukov return (IPPROTO_DONE); 7538e96e13eSMaxim Sobolev } 7548e96e13eSMaxim Sobolev 755*f325335cSAndrey V. Elsukov #define MTAG_GRE 1307983903 756*f325335cSAndrey V. Elsukov static int 757*f325335cSAndrey V. Elsukov gre_check_nesting(struct ifnet *ifp, struct mbuf *m) 7588e96e13eSMaxim Sobolev { 759*f325335cSAndrey V. Elsukov struct m_tag *mtag; 760*f325335cSAndrey V. Elsukov int count; 7618e96e13eSMaxim Sobolev 762*f325335cSAndrey V. Elsukov count = 1; 763*f325335cSAndrey V. Elsukov mtag = NULL; 764*f325335cSAndrey V. Elsukov while ((mtag = m_tag_locate(m, MTAG_GRE, 0, NULL)) != NULL) { 765*f325335cSAndrey V. Elsukov if (*(struct ifnet **)(mtag + 1) == ifp) { 766*f325335cSAndrey V. Elsukov log(LOG_NOTICE, "%s: loop detected\n", ifp->if_xname); 767*f325335cSAndrey V. Elsukov return (EIO); 768*f325335cSAndrey V. Elsukov } 769*f325335cSAndrey V. Elsukov count++; 770*f325335cSAndrey V. Elsukov } 771*f325335cSAndrey V. Elsukov if (count > V_max_gre_nesting) { 772*f325335cSAndrey V. Elsukov log(LOG_NOTICE, 773*f325335cSAndrey V. Elsukov "%s: if_output recursively called too many times(%d)\n", 774*f325335cSAndrey V. Elsukov ifp->if_xname, count); 775*f325335cSAndrey V. Elsukov return (EIO); 776*f325335cSAndrey V. Elsukov } 777*f325335cSAndrey V. Elsukov mtag = m_tag_alloc(MTAG_GRE, 0, sizeof(struct ifnet *), M_NOWAIT); 778*f325335cSAndrey V. Elsukov if (mtag == NULL) 779*f325335cSAndrey V. Elsukov return (ENOMEM); 780*f325335cSAndrey V. Elsukov *(struct ifnet **)(mtag + 1) = ifp; 781*f325335cSAndrey V. Elsukov m_tag_prepend(m, mtag); 782*f325335cSAndrey V. Elsukov return (0); 7838e96e13eSMaxim Sobolev } 7848e96e13eSMaxim Sobolev 785*f325335cSAndrey V. Elsukov static int 786*f325335cSAndrey V. Elsukov gre_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, 787*f325335cSAndrey V. Elsukov struct route *ro) 788*f325335cSAndrey V. Elsukov { 789*f325335cSAndrey V. Elsukov uint32_t af; 790*f325335cSAndrey V. Elsukov int error; 791*f325335cSAndrey V. Elsukov 792*f325335cSAndrey V. Elsukov #ifdef MAC 793*f325335cSAndrey V. Elsukov error = mac_ifnet_check_transmit(ifp, m); 794*f325335cSAndrey V. Elsukov if (error != 0) 795*f325335cSAndrey V. Elsukov goto drop; 796*f325335cSAndrey V. Elsukov #endif 797*f325335cSAndrey V. Elsukov if ((ifp->if_flags & IFF_MONITOR) != 0 || 798*f325335cSAndrey V. Elsukov (ifp->if_flags & IFF_UP) == 0) { 799*f325335cSAndrey V. Elsukov error = ENETDOWN; 800*f325335cSAndrey V. Elsukov goto drop; 801*f325335cSAndrey V. Elsukov } 802*f325335cSAndrey V. Elsukov 803*f325335cSAndrey V. Elsukov error = gre_check_nesting(ifp, m); 804*f325335cSAndrey V. Elsukov if (error != 0) 805*f325335cSAndrey V. Elsukov goto drop; 806*f325335cSAndrey V. Elsukov 807*f325335cSAndrey V. Elsukov m->m_flags &= ~(M_BCAST|M_MCAST); 808*f325335cSAndrey V. Elsukov if (dst->sa_family == AF_UNSPEC) 809*f325335cSAndrey V. Elsukov bcopy(dst->sa_data, &af, sizeof(af)); 810*f325335cSAndrey V. Elsukov else 811*f325335cSAndrey V. Elsukov af = dst->sa_family; 812*f325335cSAndrey V. Elsukov BPF_MTAP2(ifp, &af, sizeof(af), m); 813*f325335cSAndrey V. Elsukov m->m_pkthdr.csum_data = af; /* save af for if_transmit */ 814*f325335cSAndrey V. Elsukov return (ifp->if_transmit(ifp, m)); 815*f325335cSAndrey V. Elsukov drop: 816*f325335cSAndrey V. Elsukov m_freem(m); 817*f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 818*f325335cSAndrey V. Elsukov return (error); 819*f325335cSAndrey V. Elsukov } 820*f325335cSAndrey V. Elsukov 821*f325335cSAndrey V. Elsukov static void 822*f325335cSAndrey V. Elsukov gre_setseqn(struct grehdr *gh, uint32_t seq) 823*f325335cSAndrey V. Elsukov { 824*f325335cSAndrey V. Elsukov uint32_t *opts; 825*f325335cSAndrey V. Elsukov uint16_t flags; 826*f325335cSAndrey V. Elsukov 827*f325335cSAndrey V. Elsukov opts = gh->gre_opts; 828*f325335cSAndrey V. Elsukov flags = ntohs(gh->gre_flags); 829*f325335cSAndrey V. Elsukov KASSERT((flags & GRE_FLAGS_SP) != 0, 830*f325335cSAndrey V. Elsukov ("gre_setseqn called, but GRE_FLAGS_SP isn't set ")); 831*f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_CP) 832*f325335cSAndrey V. Elsukov opts++; 833*f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_KP) 834*f325335cSAndrey V. Elsukov opts++; 835*f325335cSAndrey V. Elsukov *opts = htonl(seq); 836*f325335cSAndrey V. Elsukov } 837*f325335cSAndrey V. Elsukov 838*f325335cSAndrey V. Elsukov static int 839*f325335cSAndrey V. Elsukov gre_transmit(struct ifnet *ifp, struct mbuf *m) 840*f325335cSAndrey V. Elsukov { 841*f325335cSAndrey V. Elsukov GRE_RLOCK_TRACKER; 842*f325335cSAndrey V. Elsukov struct gre_softc *sc; 843*f325335cSAndrey V. Elsukov struct grehdr *gh; 844*f325335cSAndrey V. Elsukov uint32_t iaf, oaf, oseq; 845*f325335cSAndrey V. Elsukov int error, hlen, olen, plen; 846*f325335cSAndrey V. Elsukov int want_seq, want_csum; 847*f325335cSAndrey V. Elsukov 848*f325335cSAndrey V. Elsukov plen = 0; 849*f325335cSAndrey V. Elsukov sc = ifp->if_softc; 850*f325335cSAndrey V. Elsukov if (sc == NULL) { 851*f325335cSAndrey V. Elsukov error = ENETDOWN; 852*f325335cSAndrey V. Elsukov m_freem(m); 853*f325335cSAndrey V. Elsukov goto drop; 854*f325335cSAndrey V. Elsukov } 855*f325335cSAndrey V. Elsukov GRE_RLOCK(sc); 856*f325335cSAndrey V. Elsukov if (sc->gre_family == 0) { 857*f325335cSAndrey V. Elsukov GRE_RUNLOCK(sc); 858*f325335cSAndrey V. Elsukov error = ENETDOWN; 859*f325335cSAndrey V. Elsukov m_freem(m); 860*f325335cSAndrey V. Elsukov goto drop; 861*f325335cSAndrey V. Elsukov } 862*f325335cSAndrey V. Elsukov iaf = m->m_pkthdr.csum_data; 863*f325335cSAndrey V. Elsukov oaf = sc->gre_family; 864*f325335cSAndrey V. Elsukov hlen = sc->gre_hlen; 865*f325335cSAndrey V. Elsukov want_seq = (sc->gre_options & GRE_ENABLE_SEQ) != 0; 866*f325335cSAndrey V. Elsukov if (want_seq) 867*f325335cSAndrey V. Elsukov oseq = sc->gre_oseq++; /* XXX */ 868*f325335cSAndrey V. Elsukov want_csum = (sc->gre_options & GRE_ENABLE_CSUM) != 0; 869*f325335cSAndrey V. Elsukov M_SETFIB(m, sc->gre_fibnum); 870*f325335cSAndrey V. Elsukov M_PREPEND(m, hlen, M_NOWAIT); 871*f325335cSAndrey V. Elsukov if (m == NULL) { 872*f325335cSAndrey V. Elsukov GRE_RUNLOCK(sc); 873*f325335cSAndrey V. Elsukov error = ENOBUFS; 874*f325335cSAndrey V. Elsukov goto drop; 875*f325335cSAndrey V. Elsukov } 876*f325335cSAndrey V. Elsukov bcopy(sc->gre_hdr, mtod(m, void *), hlen); 877*f325335cSAndrey V. Elsukov GRE_RUNLOCK(sc); 878*f325335cSAndrey V. Elsukov switch (oaf) { 879*f325335cSAndrey V. Elsukov #ifdef INET 880*f325335cSAndrey V. Elsukov case AF_INET: 881*f325335cSAndrey V. Elsukov olen = sizeof(struct ip); 882*f325335cSAndrey V. Elsukov break; 883*f325335cSAndrey V. Elsukov #endif 884*f325335cSAndrey V. Elsukov #ifdef INET6 885*f325335cSAndrey V. Elsukov case AF_INET6: 886*f325335cSAndrey V. Elsukov olen = sizeof(struct ip6_hdr); 887*f325335cSAndrey V. Elsukov break; 888*f325335cSAndrey V. Elsukov #endif 889*f325335cSAndrey V. Elsukov default: 890*f325335cSAndrey V. Elsukov error = ENETDOWN; 891*f325335cSAndrey V. Elsukov goto drop; 892*f325335cSAndrey V. Elsukov } 893*f325335cSAndrey V. Elsukov gh = (struct grehdr *)mtodo(m, olen); 894*f325335cSAndrey V. Elsukov switch (iaf) { 895*f325335cSAndrey V. Elsukov #ifdef INET 896*f325335cSAndrey V. Elsukov case AF_INET: 897*f325335cSAndrey V. Elsukov gh->gre_proto = htons(ETHERTYPE_IP); 898*f325335cSAndrey V. Elsukov break; 899*f325335cSAndrey V. Elsukov #endif 900*f325335cSAndrey V. Elsukov #ifdef INET6 901*f325335cSAndrey V. Elsukov case AF_INET6: 902*f325335cSAndrey V. Elsukov gh->gre_proto = htons(ETHERTYPE_IPV6); 903*f325335cSAndrey V. Elsukov break; 904*f325335cSAndrey V. Elsukov #endif 905*f325335cSAndrey V. Elsukov default: 906*f325335cSAndrey V. Elsukov error = ENETDOWN; 907*f325335cSAndrey V. Elsukov goto drop; 908*f325335cSAndrey V. Elsukov } 909*f325335cSAndrey V. Elsukov if (want_seq) 910*f325335cSAndrey V. Elsukov gre_setseqn(gh, oseq); 911*f325335cSAndrey V. Elsukov if (want_csum) { 912*f325335cSAndrey V. Elsukov *(uint16_t *)gh->gre_opts = in_cksum_skip(m, 913*f325335cSAndrey V. Elsukov m->m_pkthdr.len, olen); 914*f325335cSAndrey V. Elsukov } 915*f325335cSAndrey V. Elsukov plen = m->m_pkthdr.len - hlen; 916*f325335cSAndrey V. Elsukov switch (oaf) { 917*f325335cSAndrey V. Elsukov #ifdef INET 918*f325335cSAndrey V. Elsukov case AF_INET: 919*f325335cSAndrey V. Elsukov error = in_gre_output(m, iaf, hlen); 920*f325335cSAndrey V. Elsukov break; 921*f325335cSAndrey V. Elsukov #endif 922*f325335cSAndrey V. Elsukov #ifdef INET6 923*f325335cSAndrey V. Elsukov case AF_INET6: 924*f325335cSAndrey V. Elsukov error = in6_gre_output(m, iaf, hlen); 925*f325335cSAndrey V. Elsukov break; 926*f325335cSAndrey V. Elsukov #endif 927*f325335cSAndrey V. Elsukov default: 928*f325335cSAndrey V. Elsukov m_freem(m); 929*f325335cSAndrey V. Elsukov error = ENETDOWN; 930*f325335cSAndrey V. Elsukov }; 931*f325335cSAndrey V. Elsukov drop: 932*f325335cSAndrey V. Elsukov if (error) 933*f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 934*f325335cSAndrey V. Elsukov else { 935*f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 936*f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OBYTES, plen); 937*f325335cSAndrey V. Elsukov } 938*f325335cSAndrey V. Elsukov return (error); 939*f325335cSAndrey V. Elsukov } 940*f325335cSAndrey V. Elsukov 941*f325335cSAndrey V. Elsukov static void 942*f325335cSAndrey V. Elsukov gre_qflush(struct ifnet *ifp __unused) 943*f325335cSAndrey V. Elsukov { 944*f325335cSAndrey V. Elsukov 9458e96e13eSMaxim Sobolev } 9468e96e13eSMaxim Sobolev 9478e96e13eSMaxim Sobolev static int 9488e96e13eSMaxim Sobolev gremodevent(module_t mod, int type, void *data) 9498e96e13eSMaxim Sobolev { 9508e96e13eSMaxim Sobolev 9518e96e13eSMaxim Sobolev switch (type) { 9528e96e13eSMaxim Sobolev case MOD_LOAD: 9538e96e13eSMaxim Sobolev case MOD_UNLOAD: 9548e96e13eSMaxim Sobolev break; 9553e019deaSPoul-Henning Kamp default: 95689c58b73SHiroki Sato return (EOPNOTSUPP); 9578e96e13eSMaxim Sobolev } 95889c58b73SHiroki Sato return (0); 9598e96e13eSMaxim Sobolev } 9608e96e13eSMaxim Sobolev 9618e96e13eSMaxim Sobolev static moduledata_t gre_mod = { 9628e96e13eSMaxim Sobolev "if_gre", 9638e96e13eSMaxim Sobolev gremodevent, 9649823d527SKevin Lo 0 9658e96e13eSMaxim Sobolev }; 9668e96e13eSMaxim Sobolev 9678e96e13eSMaxim Sobolev DECLARE_MODULE(if_gre, gre_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 9688e96e13eSMaxim Sobolev MODULE_VERSION(if_gre, 1); 969