1c398230bSWarner Losh /*- 28e96e13eSMaxim Sobolev * Copyright (c) 1998 The NetBSD Foundation, Inc. 3f325335cSAndrey 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. 31f325335cSAndrey V. Elsukov * 32f325335cSAndrey V. Elsukov * $NetBSD: if_gre.c,v 1.49 2003/12/11 00:22:29 itojun Exp $ 338e96e13eSMaxim Sobolev */ 348e96e13eSMaxim Sobolev 35f325335cSAndrey V. Elsukov #include <sys/cdefs.h> 36f325335cSAndrey 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> 44f325335cSAndrey 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> 52f325335cSAndrey V. Elsukov #include <sys/rmlock.h> 538e96e13eSMaxim Sobolev #include <sys/socket.h> 548e96e13eSMaxim Sobolev #include <sys/sockio.h> 55f325335cSAndrey V. Elsukov #include <sys/sx.h> 568e96e13eSMaxim Sobolev #include <sys/sysctl.h> 57f325335cSAndrey 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> 65f325335cSAndrey V. Elsukov #include <net/netisr.h> 66530c0060SRobert Watson #include <net/vnet.h> 67eccfe69aSAndrey V. Elsukov #include <net/route.h> 688e96e13eSMaxim Sobolev 698e96e13eSMaxim Sobolev #include <netinet/in.h> 70f325335cSAndrey V. Elsukov #ifdef INET 718e96e13eSMaxim Sobolev #include <netinet/in_systm.h> 728e96e13eSMaxim Sobolev #include <netinet/in_var.h> 738e96e13eSMaxim Sobolev #include <netinet/ip.h> 748e96e13eSMaxim Sobolev #include <netinet/ip_var.h> 758e96e13eSMaxim Sobolev #endif 768e96e13eSMaxim Sobolev 77f325335cSAndrey V. Elsukov #ifdef INET6 78f325335cSAndrey V. Elsukov #include <netinet/ip6.h> 79f325335cSAndrey V. Elsukov #include <netinet6/in6_var.h> 80f325335cSAndrey V. Elsukov #include <netinet6/ip6_var.h> 81f325335cSAndrey V. Elsukov #include <netinet6/scope6_var.h> 82f325335cSAndrey V. Elsukov #endif 838e96e13eSMaxim Sobolev 84f325335cSAndrey V. Elsukov #include <netinet/ip_encap.h> 85f325335cSAndrey V. Elsukov #include <net/bpf.h> 868e96e13eSMaxim Sobolev #include <net/if_gre.h> 878e96e13eSMaxim Sobolev 88f325335cSAndrey V. Elsukov #include <machine/in_cksum.h> 89f325335cSAndrey V. Elsukov #include <security/mac/mac_framework.h> 908e96e13eSMaxim Sobolev 91f325335cSAndrey V. Elsukov #define GREMTU 1500 9242a58907SGleb Smirnoff static const char grename[] = "gre"; 9342a58907SGleb Smirnoff static MALLOC_DEFINE(M_GRE, grename, "Generic Routing Encapsulation"); 94f325335cSAndrey V. Elsukov static VNET_DEFINE(struct mtx, gre_mtx); 95f325335cSAndrey V. Elsukov #define V_gre_mtx VNET(gre_mtx) 96f325335cSAndrey V. Elsukov #define GRE_LIST_LOCK_INIT(x) mtx_init(&V_gre_mtx, "gre_mtx", NULL, \ 97f325335cSAndrey V. Elsukov MTX_DEF) 98f325335cSAndrey V. Elsukov #define GRE_LIST_LOCK_DESTROY(x) mtx_destroy(&V_gre_mtx) 99f325335cSAndrey V. Elsukov #define GRE_LIST_LOCK(x) mtx_lock(&V_gre_mtx) 100f325335cSAndrey V. Elsukov #define GRE_LIST_UNLOCK(x) mtx_unlock(&V_gre_mtx) 101f325335cSAndrey V. Elsukov 102f325335cSAndrey V. Elsukov static VNET_DEFINE(LIST_HEAD(, gre_softc), gre_softc_list); 103f325335cSAndrey V. Elsukov #define V_gre_softc_list VNET(gre_softc_list) 104f325335cSAndrey V. Elsukov static struct sx gre_ioctl_sx; 105f325335cSAndrey V. Elsukov SX_SYSINIT(gre_ioctl_sx, &gre_ioctl_sx, "gre_ioctl"); 1068e96e13eSMaxim Sobolev 1076b7330e2SSam Leffler static int gre_clone_create(struct if_clone *, int, caddr_t); 1089ee35470SAlfred Perlstein static void gre_clone_destroy(struct ifnet *); 10989c58b73SHiroki Sato static VNET_DEFINE(struct if_clone *, gre_cloner); 11089c58b73SHiroki Sato #define V_gre_cloner VNET(gre_cloner) 11142a58907SGleb Smirnoff 112f325335cSAndrey V. Elsukov static void gre_qflush(struct ifnet *); 113f325335cSAndrey V. Elsukov static int gre_transmit(struct ifnet *, struct mbuf *); 114c23d234cSMaxim Sobolev static int gre_ioctl(struct ifnet *, u_long, caddr_t); 11547e8d432SGleb Smirnoff static int gre_output(struct ifnet *, struct mbuf *, 11647e8d432SGleb Smirnoff const struct sockaddr *, struct route *); 1178e96e13eSMaxim Sobolev 118f325335cSAndrey V. Elsukov static void gre_updatehdr(struct gre_softc *); 119f325335cSAndrey V. Elsukov static int gre_set_tunnel(struct ifnet *, struct sockaddr *, 120f325335cSAndrey V. Elsukov struct sockaddr *); 121f325335cSAndrey V. Elsukov static void gre_delete_tunnel(struct ifnet *); 1228e96e13eSMaxim Sobolev 1238e96e13eSMaxim Sobolev SYSCTL_DECL(_net_link); 1246472ac3dSEd Schouten static SYSCTL_NODE(_net_link, IFT_TUNNEL, gre, CTLFLAG_RW, 0, 1258e96e13eSMaxim Sobolev "Generic Routing Encapsulation"); 1268e96e13eSMaxim Sobolev #ifndef MAX_GRE_NEST 1278e96e13eSMaxim Sobolev /* 1288e96e13eSMaxim Sobolev * This macro controls the default upper limitation on nesting of gre tunnels. 1298e96e13eSMaxim Sobolev * Since, setting a large value to this macro with a careless configuration 1308e96e13eSMaxim Sobolev * may introduce system crash, we don't allow any nestings by default. 1318e96e13eSMaxim Sobolev * If you need to configure nested gre tunnels, you can define this macro 1328e96e13eSMaxim Sobolev * in your kernel configuration file. However, if you do so, please be 1338e96e13eSMaxim Sobolev * careful to configure the tunnels so that it won't make a loop. 1348e96e13eSMaxim Sobolev */ 1358e96e13eSMaxim Sobolev #define MAX_GRE_NEST 1 1368e96e13eSMaxim Sobolev #endif 137f325335cSAndrey V. Elsukov 13889c58b73SHiroki Sato static VNET_DEFINE(int, max_gre_nesting) = MAX_GRE_NEST; 13989c58b73SHiroki Sato #define V_max_gre_nesting VNET(max_gre_nesting) 14089c58b73SHiroki Sato SYSCTL_INT(_net_link_gre, OID_AUTO, max_nesting, CTLFLAG_RW | CTLFLAG_VNET, 14189c58b73SHiroki Sato &VNET_NAME(max_gre_nesting), 0, "Max nested tunnels"); 1428e96e13eSMaxim Sobolev 143c23d234cSMaxim Sobolev static void 14489c58b73SHiroki Sato vnet_gre_init(const void *unused __unused) 1458e96e13eSMaxim Sobolev { 14689c58b73SHiroki Sato LIST_INIT(&V_gre_softc_list); 14789c58b73SHiroki Sato GRE_LIST_LOCK_INIT(); 14889c58b73SHiroki Sato V_gre_cloner = if_clone_simple(grename, gre_clone_create, 14942a58907SGleb Smirnoff gre_clone_destroy, 0); 1508e96e13eSMaxim Sobolev } 15189c58b73SHiroki Sato VNET_SYSINIT(vnet_gre_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 15289c58b73SHiroki Sato vnet_gre_init, NULL); 15389c58b73SHiroki Sato 15489c58b73SHiroki Sato static void 15589c58b73SHiroki Sato vnet_gre_uninit(const void *unused __unused) 15689c58b73SHiroki Sato { 15789c58b73SHiroki Sato 15889c58b73SHiroki Sato if_clone_detach(V_gre_cloner); 15989c58b73SHiroki Sato GRE_LIST_LOCK_DESTROY(); 16089c58b73SHiroki Sato } 16189c58b73SHiroki Sato VNET_SYSUNINIT(vnet_gre_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 16289c58b73SHiroki Sato vnet_gre_uninit, NULL); 1638e96e13eSMaxim Sobolev 164c23d234cSMaxim Sobolev static int 16589c58b73SHiroki Sato gre_clone_create(struct if_clone *ifc, int unit, caddr_t params) 1668e96e13eSMaxim Sobolev { 1678e96e13eSMaxim Sobolev struct gre_softc *sc; 1688e96e13eSMaxim Sobolev 169b3c9a01eSBruce M Simpson sc = malloc(sizeof(struct gre_softc), M_GRE, M_WAITOK | M_ZERO); 170f325335cSAndrey V. Elsukov sc->gre_fibnum = curthread->td_proc->p_fibnum; 171066b192eSBjoern A. Zeeb GRE2IFP(sc) = if_alloc(IFT_TUNNEL); 172f325335cSAndrey V. Elsukov GRE_LOCK_INIT(sc); 173fc74a9f9SBrooks Davis GRE2IFP(sc)->if_softc = sc; 17442a58907SGleb Smirnoff if_initname(GRE2IFP(sc), grename, unit); 175066b192eSBjoern A. Zeeb 176f325335cSAndrey V. Elsukov GRE2IFP(sc)->if_mtu = sc->gre_mtu = GREMTU; 177fc74a9f9SBrooks Davis GRE2IFP(sc)->if_flags = IFF_POINTOPOINT|IFF_MULTICAST; 178fc74a9f9SBrooks Davis GRE2IFP(sc)->if_output = gre_output; 179fc74a9f9SBrooks Davis GRE2IFP(sc)->if_ioctl = gre_ioctl; 180f325335cSAndrey V. Elsukov GRE2IFP(sc)->if_transmit = gre_transmit; 181f325335cSAndrey V. Elsukov GRE2IFP(sc)->if_qflush = gre_qflush; 182*f1aaad0cSHiroki Sato GRE2IFP(sc)->if_capabilities |= IFCAP_LINKSTATE; 183*f1aaad0cSHiroki Sato GRE2IFP(sc)->if_capenable |= IFCAP_LINKSTATE; 184fc74a9f9SBrooks Davis if_attach(GRE2IFP(sc)); 185fc74a9f9SBrooks Davis bpfattach(GRE2IFP(sc), DLT_NULL, sizeof(u_int32_t)); 18689c58b73SHiroki Sato GRE_LIST_LOCK(); 187f325335cSAndrey V. Elsukov LIST_INSERT_HEAD(&V_gre_softc_list, sc, gre_list); 18889c58b73SHiroki Sato GRE_LIST_UNLOCK(); 1898e96e13eSMaxim Sobolev return (0); 1908e96e13eSMaxim Sobolev } 1918e96e13eSMaxim Sobolev 192c23d234cSMaxim Sobolev static void 19389c58b73SHiroki Sato gre_clone_destroy(struct ifnet *ifp) 1948e96e13eSMaxim Sobolev { 195f325335cSAndrey V. Elsukov struct gre_softc *sc; 1968e96e13eSMaxim Sobolev 197f325335cSAndrey V. Elsukov sx_xlock(&gre_ioctl_sx); 198f325335cSAndrey V. Elsukov sc = ifp->if_softc; 199f325335cSAndrey V. Elsukov gre_delete_tunnel(ifp); 20089c58b73SHiroki Sato GRE_LIST_LOCK(); 201f325335cSAndrey V. Elsukov LIST_REMOVE(sc, gre_list); 20289c58b73SHiroki Sato GRE_LIST_UNLOCK(); 203febd0759SAndrew Thompson bpfdetach(ifp); 204febd0759SAndrew Thompson if_detach(ifp); 205f325335cSAndrey V. Elsukov ifp->if_softc = NULL; 206f325335cSAndrey V. Elsukov sx_xunlock(&gre_ioctl_sx); 207f325335cSAndrey V. Elsukov 208febd0759SAndrew Thompson if_free(ifp); 209f325335cSAndrey V. Elsukov GRE_LOCK_DESTROY(sc); 210febd0759SAndrew Thompson free(sc, M_GRE); 2118e96e13eSMaxim Sobolev } 2128e96e13eSMaxim Sobolev 213c23d234cSMaxim Sobolev static int 2148e96e13eSMaxim Sobolev gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 2158e96e13eSMaxim Sobolev { 216f325335cSAndrey V. Elsukov GRE_RLOCK_TRACKER; 2178e96e13eSMaxim Sobolev struct ifreq *ifr = (struct ifreq *)data; 218f325335cSAndrey V. Elsukov struct sockaddr *src, *dst; 219f325335cSAndrey V. Elsukov struct gre_softc *sc; 220f325335cSAndrey V. Elsukov #ifdef INET 221f325335cSAndrey V. Elsukov struct sockaddr_in *sin = NULL; 222f325335cSAndrey V. Elsukov #endif 223f325335cSAndrey V. Elsukov #ifdef INET6 224f325335cSAndrey V. Elsukov struct sockaddr_in6 *sin6 = NULL; 225f325335cSAndrey V. Elsukov #endif 226f325335cSAndrey V. Elsukov uint32_t opt; 227f325335cSAndrey V. Elsukov int error; 2288e96e13eSMaxim Sobolev 2298e96e13eSMaxim Sobolev switch (cmd) { 230f325335cSAndrey V. Elsukov case SIOCSIFMTU: 231f325335cSAndrey V. Elsukov /* XXX: */ 232f325335cSAndrey V. Elsukov if (ifr->ifr_mtu < 576) 233f325335cSAndrey V. Elsukov return (EINVAL); 234f325335cSAndrey V. Elsukov break; 2358e96e13eSMaxim Sobolev case SIOCSIFADDR: 2368e96e13eSMaxim Sobolev ifp->if_flags |= IFF_UP; 2378e96e13eSMaxim Sobolev case SIOCSIFFLAGS: 2388e96e13eSMaxim Sobolev case SIOCADDMULTI: 2398e96e13eSMaxim Sobolev case SIOCDELMULTI: 240f325335cSAndrey V. Elsukov return (0); 2418e96e13eSMaxim Sobolev case GRESADDRS: 2428e96e13eSMaxim Sobolev case GRESADDRD: 2438e96e13eSMaxim Sobolev case GREGADDRS: 2448e96e13eSMaxim Sobolev case GREGADDRD: 245f325335cSAndrey V. Elsukov case GRESPROTO: 246f325335cSAndrey V. Elsukov case GREGPROTO: 247f325335cSAndrey V. Elsukov return (EOPNOTSUPP); 248f325335cSAndrey V. Elsukov } 249f325335cSAndrey V. Elsukov src = dst = NULL; 250f325335cSAndrey V. Elsukov sx_xlock(&gre_ioctl_sx); 251f325335cSAndrey V. Elsukov sc = ifp->if_softc; 252f325335cSAndrey V. Elsukov if (sc == NULL) { 253f325335cSAndrey V. Elsukov error = ENXIO; 254f325335cSAndrey V. Elsukov goto end; 255f325335cSAndrey V. Elsukov } 256f325335cSAndrey V. Elsukov error = 0; 257f325335cSAndrey V. Elsukov switch (cmd) { 258f325335cSAndrey V. Elsukov case SIOCSIFMTU: 259f325335cSAndrey V. Elsukov GRE_WLOCK(sc); 260f325335cSAndrey V. Elsukov sc->gre_mtu = ifr->ifr_mtu; 261f325335cSAndrey V. Elsukov gre_updatehdr(sc); 262f325335cSAndrey V. Elsukov GRE_WUNLOCK(sc); 263f325335cSAndrey V. Elsukov goto end; 2648e96e13eSMaxim Sobolev case SIOCSIFPHYADDR: 265f16770aeSBruce M Simpson #ifdef INET6 266f325335cSAndrey V. Elsukov case SIOCSIFPHYADDR_IN6: 267f16770aeSBruce M Simpson #endif 268f325335cSAndrey V. Elsukov error = EINVAL; 269f325335cSAndrey V. Elsukov switch (cmd) { 270f325335cSAndrey V. Elsukov #ifdef INET 271f325335cSAndrey V. Elsukov case SIOCSIFPHYADDR: 272f325335cSAndrey V. Elsukov src = (struct sockaddr *) 273f325335cSAndrey V. Elsukov &(((struct in_aliasreq *)data)->ifra_addr); 274f325335cSAndrey V. Elsukov dst = (struct sockaddr *) 275f325335cSAndrey V. Elsukov &(((struct in_aliasreq *)data)->ifra_dstaddr); 2768e96e13eSMaxim Sobolev break; 277f325335cSAndrey V. Elsukov #endif 278f325335cSAndrey V. Elsukov #ifdef INET6 279f325335cSAndrey V. Elsukov case SIOCSIFPHYADDR_IN6: 280f325335cSAndrey V. Elsukov src = (struct sockaddr *) 281f325335cSAndrey V. Elsukov &(((struct in6_aliasreq *)data)->ifra_addr); 282f325335cSAndrey V. Elsukov dst = (struct sockaddr *) 283f325335cSAndrey V. Elsukov &(((struct in6_aliasreq *)data)->ifra_dstaddr); 284f325335cSAndrey V. Elsukov break; 285f325335cSAndrey V. Elsukov #endif 286f325335cSAndrey V. Elsukov default: 287f325335cSAndrey V. Elsukov error = EAFNOSUPPORT; 288f325335cSAndrey V. Elsukov goto end; 2898e96e13eSMaxim Sobolev } 290f325335cSAndrey V. Elsukov /* sa_family must be equal */ 291f325335cSAndrey V. Elsukov if (src->sa_family != dst->sa_family || 292f325335cSAndrey V. Elsukov src->sa_len != dst->sa_len) 293f325335cSAndrey V. Elsukov goto end; 294f325335cSAndrey V. Elsukov 295f325335cSAndrey V. Elsukov /* validate sa_len */ 296f325335cSAndrey V. Elsukov switch (src->sa_family) { 297f325335cSAndrey V. Elsukov #ifdef INET 298f325335cSAndrey V. Elsukov case AF_INET: 299f325335cSAndrey V. Elsukov if (src->sa_len != sizeof(struct sockaddr_in)) 300f325335cSAndrey V. Elsukov goto end; 301f325335cSAndrey V. Elsukov break; 302f325335cSAndrey V. Elsukov #endif 303f325335cSAndrey V. Elsukov #ifdef INET6 304f325335cSAndrey V. Elsukov case AF_INET6: 305f325335cSAndrey V. Elsukov if (src->sa_len != sizeof(struct sockaddr_in6)) 306f325335cSAndrey V. Elsukov goto end; 307f325335cSAndrey V. Elsukov break; 308f325335cSAndrey V. Elsukov #endif 309f325335cSAndrey V. Elsukov default: 310f325335cSAndrey V. Elsukov error = EAFNOSUPPORT; 311f325335cSAndrey V. Elsukov goto end; 312f325335cSAndrey V. Elsukov } 313f325335cSAndrey V. Elsukov /* check sa_family looks sane for the cmd */ 314f325335cSAndrey V. Elsukov error = EAFNOSUPPORT; 315f325335cSAndrey V. Elsukov switch (cmd) { 316f325335cSAndrey V. Elsukov #ifdef INET 317f325335cSAndrey V. Elsukov case SIOCSIFPHYADDR: 318f325335cSAndrey V. Elsukov if (src->sa_family == AF_INET) 319f325335cSAndrey V. Elsukov break; 320f325335cSAndrey V. Elsukov goto end; 321f325335cSAndrey V. Elsukov #endif 322f325335cSAndrey V. Elsukov #ifdef INET6 323f325335cSAndrey V. Elsukov case SIOCSIFPHYADDR_IN6: 324f325335cSAndrey V. Elsukov if (src->sa_family == AF_INET6) 325f325335cSAndrey V. Elsukov break; 326f325335cSAndrey V. Elsukov goto end; 327f325335cSAndrey V. Elsukov #endif 328f325335cSAndrey V. Elsukov } 329f325335cSAndrey V. Elsukov error = EADDRNOTAVAIL; 330f325335cSAndrey V. Elsukov switch (src->sa_family) { 331f325335cSAndrey V. Elsukov #ifdef INET 332f325335cSAndrey V. Elsukov case AF_INET: 333f325335cSAndrey V. Elsukov if (satosin(src)->sin_addr.s_addr == INADDR_ANY || 334f325335cSAndrey V. Elsukov satosin(dst)->sin_addr.s_addr == INADDR_ANY) 335f325335cSAndrey V. Elsukov goto end; 336f325335cSAndrey V. Elsukov break; 337f325335cSAndrey V. Elsukov #endif 338f325335cSAndrey V. Elsukov #ifdef INET6 339f325335cSAndrey V. Elsukov case AF_INET6: 340f325335cSAndrey V. Elsukov if (IN6_IS_ADDR_UNSPECIFIED(&satosin6(src)->sin6_addr) 341f325335cSAndrey V. Elsukov || 342f325335cSAndrey V. Elsukov IN6_IS_ADDR_UNSPECIFIED(&satosin6(dst)->sin6_addr)) 343f325335cSAndrey V. Elsukov goto end; 344f325335cSAndrey V. Elsukov /* 345f325335cSAndrey V. Elsukov * Check validity of the scope zone ID of the 346f325335cSAndrey V. Elsukov * addresses, and convert it into the kernel 347f325335cSAndrey V. Elsukov * internal form if necessary. 348f325335cSAndrey V. Elsukov */ 349f325335cSAndrey V. Elsukov error = sa6_embedscope(satosin6(src), 0); 350e3416ab0SBjoern A. Zeeb if (error != 0) 351f325335cSAndrey V. Elsukov goto end; 352f325335cSAndrey V. Elsukov error = sa6_embedscope(satosin6(dst), 0); 353f325335cSAndrey V. Elsukov if (error != 0) 354f325335cSAndrey V. Elsukov goto end; 355f325335cSAndrey V. Elsukov #endif 356f325335cSAndrey V. Elsukov }; 357f325335cSAndrey V. Elsukov error = gre_set_tunnel(ifp, src, dst); 358e3416ab0SBjoern A. Zeeb break; 359f325335cSAndrey V. Elsukov case SIOCDIFPHYADDR: 360f325335cSAndrey V. Elsukov gre_delete_tunnel(ifp); 3618e96e13eSMaxim Sobolev break; 362f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR: 3638e96e13eSMaxim Sobolev case SIOCGIFPDSTADDR: 364f16770aeSBruce M Simpson #ifdef INET6 365f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR_IN6: 366f16770aeSBruce M Simpson case SIOCGIFPDSTADDR_IN6: 367f16770aeSBruce M Simpson #endif 368f325335cSAndrey V. Elsukov if (sc->gre_family == 0) { 3698e96e13eSMaxim Sobolev error = EADDRNOTAVAIL; 3708e96e13eSMaxim Sobolev break; 3718e96e13eSMaxim Sobolev } 372f325335cSAndrey V. Elsukov GRE_RLOCK(sc); 373f325335cSAndrey V. Elsukov switch (cmd) { 374f325335cSAndrey V. Elsukov #ifdef INET 375f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR: 376f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR: 377f325335cSAndrey V. Elsukov if (sc->gre_family != AF_INET) { 378f325335cSAndrey V. Elsukov error = EADDRNOTAVAIL; 379131c55bcSAndrew Thompson break; 380131c55bcSAndrew Thompson } 381f325335cSAndrey V. Elsukov sin = (struct sockaddr_in *)&ifr->ifr_addr; 382f325335cSAndrey V. Elsukov memset(sin, 0, sizeof(*sin)); 383f325335cSAndrey V. Elsukov sin->sin_family = AF_INET; 384f325335cSAndrey V. Elsukov sin->sin_len = sizeof(*sin); 385f325335cSAndrey V. Elsukov break; 386f325335cSAndrey V. Elsukov #endif 387f325335cSAndrey V. Elsukov #ifdef INET6 388f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR_IN6: 389f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR_IN6: 390f325335cSAndrey V. Elsukov if (sc->gre_family != AF_INET6) { 391f325335cSAndrey V. Elsukov error = EADDRNOTAVAIL; 392f325335cSAndrey V. Elsukov break; 393f325335cSAndrey V. Elsukov } 394f325335cSAndrey V. Elsukov sin6 = (struct sockaddr_in6 *) 395f325335cSAndrey V. Elsukov &(((struct in6_ifreq *)data)->ifr_addr); 396f325335cSAndrey V. Elsukov memset(sin6, 0, sizeof(*sin6)); 397f325335cSAndrey V. Elsukov sin6->sin6_family = AF_INET6; 398f325335cSAndrey V. Elsukov sin6->sin6_len = sizeof(*sin6); 399f325335cSAndrey V. Elsukov break; 400f325335cSAndrey V. Elsukov #endif 401f325335cSAndrey V. Elsukov } 402f325335cSAndrey V. Elsukov if (error == 0) { 403f325335cSAndrey V. Elsukov switch (cmd) { 404f325335cSAndrey V. Elsukov #ifdef INET 405f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR: 406f325335cSAndrey V. Elsukov sin->sin_addr = sc->gre_oip.ip_src; 407f325335cSAndrey V. Elsukov break; 408f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR: 409f325335cSAndrey V. Elsukov sin->sin_addr = sc->gre_oip.ip_dst; 410f325335cSAndrey V. Elsukov break; 411f325335cSAndrey V. Elsukov #endif 412f325335cSAndrey V. Elsukov #ifdef INET6 413f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR_IN6: 414f325335cSAndrey V. Elsukov sin6->sin6_addr = sc->gre_oip6.ip6_src; 415f325335cSAndrey V. Elsukov break; 416f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR_IN6: 417f325335cSAndrey V. Elsukov sin6->sin6_addr = sc->gre_oip6.ip6_dst; 418f325335cSAndrey V. Elsukov break; 419f325335cSAndrey V. Elsukov #endif 420f325335cSAndrey V. Elsukov } 421f325335cSAndrey V. Elsukov } 422f325335cSAndrey V. Elsukov GRE_RUNLOCK(sc); 423f325335cSAndrey V. Elsukov if (error != 0) 424f325335cSAndrey V. Elsukov break; 425f325335cSAndrey V. Elsukov switch (cmd) { 426f325335cSAndrey V. Elsukov #ifdef INET 427f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR: 428f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR: 429f325335cSAndrey V. Elsukov error = prison_if(curthread->td_ucred, 430f325335cSAndrey V. Elsukov (struct sockaddr *)sin); 431f325335cSAndrey V. Elsukov if (error != 0) 432f325335cSAndrey V. Elsukov memset(sin, 0, sizeof(*sin)); 433f325335cSAndrey V. Elsukov break; 434f325335cSAndrey V. Elsukov #endif 435f325335cSAndrey V. Elsukov #ifdef INET6 436f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR_IN6: 437f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR_IN6: 438f325335cSAndrey V. Elsukov error = prison_if(curthread->td_ucred, 439f325335cSAndrey V. Elsukov (struct sockaddr *)sin6); 440f325335cSAndrey V. Elsukov if (error == 0) 441f325335cSAndrey V. Elsukov error = sa6_recoverscope(sin6); 442f325335cSAndrey V. Elsukov if (error != 0) 443f325335cSAndrey V. Elsukov memset(sin6, 0, sizeof(*sin6)); 444f325335cSAndrey V. Elsukov #endif 445f325335cSAndrey V. Elsukov } 446f325335cSAndrey V. Elsukov break; 447eccfe69aSAndrey V. Elsukov case SIOCGTUNFIB: 448eccfe69aSAndrey V. Elsukov ifr->ifr_fib = sc->gre_fibnum; 449eccfe69aSAndrey V. Elsukov break; 450eccfe69aSAndrey V. Elsukov case SIOCSTUNFIB: 451eccfe69aSAndrey V. Elsukov if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0) 452eccfe69aSAndrey V. Elsukov break; 453eccfe69aSAndrey V. Elsukov if (ifr->ifr_fib >= rt_numfibs) 454eccfe69aSAndrey V. Elsukov error = EINVAL; 455eccfe69aSAndrey V. Elsukov else 456eccfe69aSAndrey V. Elsukov sc->gre_fibnum = ifr->ifr_fib; 457eccfe69aSAndrey V. Elsukov break; 458f325335cSAndrey V. Elsukov case GRESKEY: 459f325335cSAndrey V. Elsukov if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0) 460f325335cSAndrey V. Elsukov break; 461f325335cSAndrey V. Elsukov if ((error = copyin(ifr->ifr_data, &opt, sizeof(opt))) != 0) 462f325335cSAndrey V. Elsukov break; 463f325335cSAndrey V. Elsukov if (sc->gre_key != opt) { 464f325335cSAndrey V. Elsukov GRE_WLOCK(sc); 465f325335cSAndrey V. Elsukov sc->gre_key = opt; 466f325335cSAndrey V. Elsukov gre_updatehdr(sc); 467f325335cSAndrey V. Elsukov GRE_WUNLOCK(sc); 468f325335cSAndrey V. Elsukov } 469131c55bcSAndrew Thompson break; 470131c55bcSAndrew Thompson case GREGKEY: 471eccfe69aSAndrey V. Elsukov error = copyout(&sc->gre_key, ifr->ifr_data, 472eccfe69aSAndrey V. Elsukov sizeof(sc->gre_key)); 473f325335cSAndrey V. Elsukov break; 474f325335cSAndrey V. Elsukov case GRESOPTS: 475f325335cSAndrey V. Elsukov if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0) 476f325335cSAndrey V. Elsukov break; 477f325335cSAndrey V. Elsukov if ((error = copyin(ifr->ifr_data, &opt, sizeof(opt))) != 0) 478f325335cSAndrey V. Elsukov break; 479f325335cSAndrey V. Elsukov if (opt & ~GRE_OPTMASK) 480f325335cSAndrey V. Elsukov error = EINVAL; 481f325335cSAndrey V. Elsukov else { 482f325335cSAndrey V. Elsukov if (sc->gre_options != opt) { 483f325335cSAndrey V. Elsukov GRE_WLOCK(sc); 484f325335cSAndrey V. Elsukov sc->gre_options = opt; 485f325335cSAndrey V. Elsukov gre_updatehdr(sc); 486f325335cSAndrey V. Elsukov GRE_WUNLOCK(sc); 487f325335cSAndrey V. Elsukov } 488f325335cSAndrey V. Elsukov } 489131c55bcSAndrew Thompson break; 490131c55bcSAndrew Thompson 491f325335cSAndrey V. Elsukov case GREGOPTS: 492f325335cSAndrey V. Elsukov error = copyout(&sc->gre_options, ifr->ifr_data, 493f325335cSAndrey V. Elsukov sizeof(sc->gre_options)); 494f325335cSAndrey V. Elsukov break; 4958e96e13eSMaxim Sobolev default: 4968e96e13eSMaxim Sobolev error = EINVAL; 4978e96e13eSMaxim Sobolev break; 4988e96e13eSMaxim Sobolev } 499f325335cSAndrey V. Elsukov end: 500f325335cSAndrey V. Elsukov sx_xunlock(&gre_ioctl_sx); 5018e96e13eSMaxim Sobolev return (error); 5028e96e13eSMaxim Sobolev } 5038e96e13eSMaxim Sobolev 504f325335cSAndrey V. Elsukov static void 505f325335cSAndrey V. Elsukov gre_updatehdr(struct gre_softc *sc) 506f325335cSAndrey V. Elsukov { 507f325335cSAndrey V. Elsukov struct grehdr *gh = NULL; 508f325335cSAndrey V. Elsukov uint32_t *opts; 509f325335cSAndrey V. Elsukov uint16_t flags; 510f325335cSAndrey V. Elsukov 511f325335cSAndrey V. Elsukov GRE_WLOCK_ASSERT(sc); 512f325335cSAndrey V. Elsukov switch (sc->gre_family) { 513f325335cSAndrey V. Elsukov #ifdef INET 514f325335cSAndrey V. Elsukov case AF_INET: 515f325335cSAndrey V. Elsukov sc->gre_hlen = sizeof(struct greip); 516f325335cSAndrey V. Elsukov sc->gre_oip.ip_v = IPPROTO_IPV4; 517f325335cSAndrey V. Elsukov sc->gre_oip.ip_hl = sizeof(struct ip) >> 2; 518f325335cSAndrey V. Elsukov sc->gre_oip.ip_p = IPPROTO_GRE; 519f325335cSAndrey V. Elsukov gh = &sc->gre_gihdr->gi_gre; 520f325335cSAndrey V. Elsukov break; 521f325335cSAndrey V. Elsukov #endif 522f325335cSAndrey V. Elsukov #ifdef INET6 523f325335cSAndrey V. Elsukov case AF_INET6: 524f325335cSAndrey V. Elsukov sc->gre_hlen = sizeof(struct greip6); 525f325335cSAndrey V. Elsukov sc->gre_oip6.ip6_vfc = IPV6_VERSION; 526f325335cSAndrey V. Elsukov sc->gre_oip6.ip6_nxt = IPPROTO_GRE; 527f325335cSAndrey V. Elsukov gh = &sc->gre_gi6hdr->gi6_gre; 528f325335cSAndrey V. Elsukov break; 529f325335cSAndrey V. Elsukov #endif 530f325335cSAndrey V. Elsukov default: 531f325335cSAndrey V. Elsukov return; 532f325335cSAndrey V. Elsukov } 533f325335cSAndrey V. Elsukov flags = 0; 534f325335cSAndrey V. Elsukov opts = gh->gre_opts; 535f325335cSAndrey V. Elsukov if (sc->gre_options & GRE_ENABLE_CSUM) { 536f325335cSAndrey V. Elsukov flags |= GRE_FLAGS_CP; 537f325335cSAndrey V. Elsukov sc->gre_hlen += 2 * sizeof(uint16_t); 538f325335cSAndrey V. Elsukov *opts++ = 0; 539f325335cSAndrey V. Elsukov } 540f325335cSAndrey V. Elsukov if (sc->gre_key != 0) { 541f325335cSAndrey V. Elsukov flags |= GRE_FLAGS_KP; 542f325335cSAndrey V. Elsukov sc->gre_hlen += sizeof(uint32_t); 543f325335cSAndrey V. Elsukov *opts++ = htonl(sc->gre_key); 544f325335cSAndrey V. Elsukov } 545f325335cSAndrey V. Elsukov if (sc->gre_options & GRE_ENABLE_SEQ) { 546f325335cSAndrey V. Elsukov flags |= GRE_FLAGS_SP; 547f325335cSAndrey V. Elsukov sc->gre_hlen += sizeof(uint32_t); 548f325335cSAndrey V. Elsukov *opts++ = 0; 549f325335cSAndrey V. Elsukov } else 550f325335cSAndrey V. Elsukov sc->gre_oseq = 0; 551f325335cSAndrey V. Elsukov gh->gre_flags = htons(flags); 552f325335cSAndrey V. Elsukov GRE2IFP(sc)->if_mtu = sc->gre_mtu - sc->gre_hlen; 553f325335cSAndrey V. Elsukov } 554f325335cSAndrey V. Elsukov 555f325335cSAndrey V. Elsukov static void 556f325335cSAndrey V. Elsukov gre_detach(struct gre_softc *sc) 557f325335cSAndrey V. Elsukov { 558f325335cSAndrey V. Elsukov 559f325335cSAndrey V. Elsukov sx_assert(&gre_ioctl_sx, SA_XLOCKED); 560f325335cSAndrey V. Elsukov if (sc->gre_ecookie != NULL) 561f325335cSAndrey V. Elsukov encap_detach(sc->gre_ecookie); 562f325335cSAndrey V. Elsukov sc->gre_ecookie = NULL; 563f325335cSAndrey V. Elsukov } 564f325335cSAndrey V. Elsukov 565c23d234cSMaxim Sobolev static int 566f325335cSAndrey V. Elsukov gre_set_tunnel(struct ifnet *ifp, struct sockaddr *src, 567f325335cSAndrey V. Elsukov struct sockaddr *dst) 5688e96e13eSMaxim Sobolev { 569f325335cSAndrey V. Elsukov struct gre_softc *sc, *tsc; 570f325335cSAndrey V. Elsukov #ifdef INET6 571f325335cSAndrey V. Elsukov struct ip6_hdr *ip6; 572f325335cSAndrey V. Elsukov #endif 573f325335cSAndrey V. Elsukov #ifdef INET 574f325335cSAndrey V. Elsukov struct ip *ip; 575f325335cSAndrey V. Elsukov #endif 576f325335cSAndrey V. Elsukov void *hdr; 577f325335cSAndrey V. Elsukov int error; 5788e96e13eSMaxim Sobolev 579f325335cSAndrey V. Elsukov sx_assert(&gre_ioctl_sx, SA_XLOCKED); 580f325335cSAndrey V. Elsukov GRE_LIST_LOCK(); 581f325335cSAndrey V. Elsukov sc = ifp->if_softc; 582f325335cSAndrey V. Elsukov LIST_FOREACH(tsc, &V_gre_softc_list, gre_list) { 583f325335cSAndrey V. Elsukov if (tsc == sc || tsc->gre_family != src->sa_family) 584f325335cSAndrey V. Elsukov continue; 585f325335cSAndrey V. Elsukov #ifdef INET 586f325335cSAndrey V. Elsukov if (tsc->gre_family == AF_INET && 587f325335cSAndrey V. Elsukov tsc->gre_oip.ip_src.s_addr == 588f325335cSAndrey V. Elsukov satosin(src)->sin_addr.s_addr && 589f325335cSAndrey V. Elsukov tsc->gre_oip.ip_dst.s_addr == 590f325335cSAndrey V. Elsukov satosin(dst)->sin_addr.s_addr) { 591f325335cSAndrey V. Elsukov GRE_LIST_UNLOCK(); 592f325335cSAndrey V. Elsukov return (EADDRNOTAVAIL); 593f325335cSAndrey V. Elsukov } 594f325335cSAndrey V. Elsukov #endif 595f325335cSAndrey V. Elsukov #ifdef INET6 596f325335cSAndrey V. Elsukov if (tsc->gre_family == AF_INET6 && 597f325335cSAndrey V. Elsukov IN6_ARE_ADDR_EQUAL(&tsc->gre_oip6.ip6_src, 598f325335cSAndrey V. Elsukov &satosin6(src)->sin6_addr) && 599f325335cSAndrey V. Elsukov IN6_ARE_ADDR_EQUAL(&tsc->gre_oip6.ip6_dst, 600f325335cSAndrey V. Elsukov &satosin6(dst)->sin6_addr)) { 601f325335cSAndrey V. Elsukov GRE_LIST_UNLOCK(); 602f325335cSAndrey V. Elsukov return (EADDRNOTAVAIL); 603f325335cSAndrey V. Elsukov } 604f325335cSAndrey V. Elsukov #endif 605f325335cSAndrey V. Elsukov } 606f325335cSAndrey V. Elsukov GRE_LIST_UNLOCK(); 6078e96e13eSMaxim Sobolev 608f325335cSAndrey V. Elsukov switch (src->sa_family) { 609f325335cSAndrey V. Elsukov #ifdef INET 610f325335cSAndrey V. Elsukov case AF_INET: 611f325335cSAndrey V. Elsukov hdr = ip = malloc(sizeof(struct greip) + 612f325335cSAndrey V. Elsukov 3 * sizeof(uint32_t), M_GRE, M_WAITOK | M_ZERO); 613f325335cSAndrey V. Elsukov ip->ip_src = satosin(src)->sin_addr; 614f325335cSAndrey V. Elsukov ip->ip_dst = satosin(dst)->sin_addr; 615f325335cSAndrey V. Elsukov break; 616f325335cSAndrey V. Elsukov #endif 617f325335cSAndrey V. Elsukov #ifdef INET6 618f325335cSAndrey V. Elsukov case AF_INET6: 619f325335cSAndrey V. Elsukov hdr = ip6 = malloc(sizeof(struct greip6) + 620f325335cSAndrey V. Elsukov 3 * sizeof(uint32_t), M_GRE, M_WAITOK | M_ZERO); 621f325335cSAndrey V. Elsukov ip6->ip6_src = satosin6(src)->sin6_addr; 622f325335cSAndrey V. Elsukov ip6->ip6_dst = satosin6(dst)->sin6_addr; 623f325335cSAndrey V. Elsukov break; 624f325335cSAndrey V. Elsukov #endif 625f325335cSAndrey V. Elsukov default: 626f325335cSAndrey V. Elsukov return (EAFNOSUPPORT); 627f325335cSAndrey V. Elsukov } 6281a6fb597SAndrey V. Elsukov if (sc->gre_family != 0) 629f325335cSAndrey V. Elsukov gre_detach(sc); 630f325335cSAndrey V. Elsukov GRE_WLOCK(sc); 631f325335cSAndrey V. Elsukov if (sc->gre_family != 0) 632f325335cSAndrey V. Elsukov free(sc->gre_hdr, M_GRE); 633f325335cSAndrey V. Elsukov sc->gre_family = src->sa_family; 634f325335cSAndrey V. Elsukov sc->gre_hdr = hdr; 635f325335cSAndrey V. Elsukov sc->gre_oseq = 0; 636f325335cSAndrey V. Elsukov sc->gre_iseq = UINT32_MAX; 637f325335cSAndrey V. Elsukov gre_updatehdr(sc); 638f325335cSAndrey V. Elsukov GRE_WUNLOCK(sc); 6398e96e13eSMaxim Sobolev 6404dbd7c5dSBjoern A. Zeeb error = 0; 641f325335cSAndrey V. Elsukov switch (src->sa_family) { 642f325335cSAndrey V. Elsukov #ifdef INET 643f325335cSAndrey V. Elsukov case AF_INET: 644f325335cSAndrey V. Elsukov error = in_gre_attach(sc); 645f325335cSAndrey V. Elsukov break; 646f325335cSAndrey V. Elsukov #endif 647f325335cSAndrey V. Elsukov #ifdef INET6 648f325335cSAndrey V. Elsukov case AF_INET6: 649f325335cSAndrey V. Elsukov error = in6_gre_attach(sc); 650f325335cSAndrey V. Elsukov break; 651f325335cSAndrey V. Elsukov #endif 652f325335cSAndrey V. Elsukov } 653*f1aaad0cSHiroki Sato if (error == 0) { 654f325335cSAndrey V. Elsukov ifp->if_drv_flags |= IFF_DRV_RUNNING; 655*f1aaad0cSHiroki Sato if_link_state_change(ifp, LINK_STATE_UP); 656*f1aaad0cSHiroki Sato } 657f325335cSAndrey V. Elsukov return (error); 6588e96e13eSMaxim Sobolev } 6598e96e13eSMaxim Sobolev 660f325335cSAndrey V. Elsukov static void 661f325335cSAndrey V. Elsukov gre_delete_tunnel(struct ifnet *ifp) 662f325335cSAndrey V. Elsukov { 663f325335cSAndrey V. Elsukov struct gre_softc *sc = ifp->if_softc; 664f325335cSAndrey V. Elsukov int family; 6658e96e13eSMaxim Sobolev 666f325335cSAndrey V. Elsukov GRE_WLOCK(sc); 667f325335cSAndrey V. Elsukov family = sc->gre_family; 668f325335cSAndrey V. Elsukov sc->gre_family = 0; 669f325335cSAndrey V. Elsukov GRE_WUNLOCK(sc); 670f325335cSAndrey V. Elsukov if (family != 0) { 671f325335cSAndrey V. Elsukov gre_detach(sc); 672f325335cSAndrey V. Elsukov free(sc->gre_hdr, M_GRE); 673f325335cSAndrey V. Elsukov } 674f325335cSAndrey V. Elsukov ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 675*f1aaad0cSHiroki Sato if_link_state_change(ifp, LINK_STATE_DOWN); 676f325335cSAndrey V. Elsukov } 6778e96e13eSMaxim Sobolev 678f325335cSAndrey V. Elsukov int 679f325335cSAndrey V. Elsukov gre_input(struct mbuf **mp, int *offp, int proto) 680f325335cSAndrey V. Elsukov { 681f325335cSAndrey V. Elsukov struct gre_softc *sc; 682f325335cSAndrey V. Elsukov struct grehdr *gh; 683f325335cSAndrey V. Elsukov struct ifnet *ifp; 684f325335cSAndrey V. Elsukov struct mbuf *m; 685f325335cSAndrey V. Elsukov uint32_t *opts, key; 686f325335cSAndrey V. Elsukov uint16_t flags; 687f325335cSAndrey V. Elsukov int hlen, isr, af; 688f325335cSAndrey V. Elsukov 689f325335cSAndrey V. Elsukov m = *mp; 690f325335cSAndrey V. Elsukov sc = encap_getarg(m); 691f325335cSAndrey V. Elsukov KASSERT(sc != NULL, ("encap_getarg returned NULL")); 692f325335cSAndrey V. Elsukov 693f325335cSAndrey V. Elsukov ifp = GRE2IFP(sc); 694f325335cSAndrey V. Elsukov gh = (struct grehdr *)mtodo(m, *offp); 695f325335cSAndrey V. Elsukov flags = ntohs(gh->gre_flags); 696f325335cSAndrey V. Elsukov if (flags & ~GRE_FLAGS_MASK) 697f325335cSAndrey V. Elsukov goto drop; 698f325335cSAndrey V. Elsukov opts = gh->gre_opts; 699f325335cSAndrey V. Elsukov hlen = 2 * sizeof(uint16_t); 700f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_CP) { 701f325335cSAndrey V. Elsukov /* reserved1 field must be zero */ 702f325335cSAndrey V. Elsukov if (((uint16_t *)opts)[1] != 0) 703f325335cSAndrey V. Elsukov goto drop; 704f325335cSAndrey V. Elsukov if (in_cksum_skip(m, m->m_pkthdr.len, *offp) != 0) 705f325335cSAndrey V. Elsukov goto drop; 706f325335cSAndrey V. Elsukov hlen += 2 * sizeof(uint16_t); 707f325335cSAndrey V. Elsukov opts++; 708f325335cSAndrey V. Elsukov } 709f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_KP) { 710f325335cSAndrey V. Elsukov key = ntohl(*opts); 711f325335cSAndrey V. Elsukov hlen += sizeof(uint32_t); 712f325335cSAndrey V. Elsukov opts++; 713f325335cSAndrey V. Elsukov } else 714f325335cSAndrey V. Elsukov key = 0; 7158e96e13eSMaxim Sobolev /* 716f325335cSAndrey V. Elsukov if (sc->gre_key != 0 && (key != sc->gre_key || key != 0)) 717f325335cSAndrey V. Elsukov goto drop; 7188e96e13eSMaxim Sobolev */ 719f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_SP) { 720f325335cSAndrey V. Elsukov /* seq = ntohl(*opts); */ 721f325335cSAndrey V. Elsukov hlen += sizeof(uint32_t); 722f325335cSAndrey V. Elsukov } 723f325335cSAndrey V. Elsukov switch (ntohs(gh->gre_proto)) { 724f325335cSAndrey V. Elsukov case ETHERTYPE_WCCP: 725f325335cSAndrey V. Elsukov /* 726f325335cSAndrey V. Elsukov * For WCCP skip an additional 4 bytes if after GRE header 727f325335cSAndrey V. Elsukov * doesn't follow an IP header. 728f325335cSAndrey V. Elsukov */ 729f325335cSAndrey V. Elsukov if (flags == 0 && (*(uint8_t *)gh->gre_opts & 0xF0) != 0x40) 730f325335cSAndrey V. Elsukov hlen += sizeof(uint32_t); 731f325335cSAndrey V. Elsukov /* FALLTHROUGH */ 732f325335cSAndrey V. Elsukov case ETHERTYPE_IP: 733f325335cSAndrey V. Elsukov isr = NETISR_IP; 734f325335cSAndrey V. Elsukov af = AF_INET; 735f325335cSAndrey V. Elsukov break; 736f325335cSAndrey V. Elsukov case ETHERTYPE_IPV6: 737f325335cSAndrey V. Elsukov isr = NETISR_IPV6; 738f325335cSAndrey V. Elsukov af = AF_INET6; 739f325335cSAndrey V. Elsukov break; 740f325335cSAndrey V. Elsukov default: 741f325335cSAndrey V. Elsukov goto drop; 742f325335cSAndrey V. Elsukov } 743f325335cSAndrey V. Elsukov m_adj(m, *offp + hlen); 744f325335cSAndrey V. Elsukov m_clrprotoflags(m); 745f325335cSAndrey V. Elsukov m->m_pkthdr.rcvif = ifp; 746eccfe69aSAndrey V. Elsukov M_SETFIB(m, ifp->if_fib); 747f325335cSAndrey V. Elsukov #ifdef MAC 748f325335cSAndrey V. Elsukov mac_ifnet_create_mbuf(ifp, m); 749f325335cSAndrey V. Elsukov #endif 750f325335cSAndrey V. Elsukov BPF_MTAP2(ifp, &af, sizeof(af), m); 751f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 752f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); 753f325335cSAndrey V. Elsukov if ((ifp->if_flags & IFF_MONITOR) != 0) 754f325335cSAndrey V. Elsukov m_freem(m); 7558e96e13eSMaxim Sobolev else 756f325335cSAndrey V. Elsukov netisr_dispatch(isr, m); 757f325335cSAndrey V. Elsukov return (IPPROTO_DONE); 758f325335cSAndrey V. Elsukov drop: 759f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 760f325335cSAndrey V. Elsukov m_freem(m); 761f325335cSAndrey V. Elsukov return (IPPROTO_DONE); 7628e96e13eSMaxim Sobolev } 7638e96e13eSMaxim Sobolev 764f325335cSAndrey V. Elsukov #define MTAG_GRE 1307983903 765f325335cSAndrey V. Elsukov static int 766f325335cSAndrey V. Elsukov gre_check_nesting(struct ifnet *ifp, struct mbuf *m) 7678e96e13eSMaxim Sobolev { 768f325335cSAndrey V. Elsukov struct m_tag *mtag; 769f325335cSAndrey V. Elsukov int count; 7708e96e13eSMaxim Sobolev 771f325335cSAndrey V. Elsukov count = 1; 772f325335cSAndrey V. Elsukov mtag = NULL; 77384d03ddaSAndrey V. Elsukov while ((mtag = m_tag_locate(m, MTAG_GRE, 0, mtag)) != NULL) { 774f325335cSAndrey V. Elsukov if (*(struct ifnet **)(mtag + 1) == ifp) { 775f325335cSAndrey V. Elsukov log(LOG_NOTICE, "%s: loop detected\n", ifp->if_xname); 776f325335cSAndrey V. Elsukov return (EIO); 777f325335cSAndrey V. Elsukov } 778f325335cSAndrey V. Elsukov count++; 779f325335cSAndrey V. Elsukov } 780f325335cSAndrey V. Elsukov if (count > V_max_gre_nesting) { 781f325335cSAndrey V. Elsukov log(LOG_NOTICE, 782f325335cSAndrey V. Elsukov "%s: if_output recursively called too many times(%d)\n", 783f325335cSAndrey V. Elsukov ifp->if_xname, count); 784f325335cSAndrey V. Elsukov return (EIO); 785f325335cSAndrey V. Elsukov } 786f325335cSAndrey V. Elsukov mtag = m_tag_alloc(MTAG_GRE, 0, sizeof(struct ifnet *), M_NOWAIT); 787f325335cSAndrey V. Elsukov if (mtag == NULL) 788f325335cSAndrey V. Elsukov return (ENOMEM); 789f325335cSAndrey V. Elsukov *(struct ifnet **)(mtag + 1) = ifp; 790f325335cSAndrey V. Elsukov m_tag_prepend(m, mtag); 791f325335cSAndrey V. Elsukov return (0); 7928e96e13eSMaxim Sobolev } 7938e96e13eSMaxim Sobolev 794f325335cSAndrey V. Elsukov static int 795f325335cSAndrey V. Elsukov gre_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, 796f325335cSAndrey V. Elsukov struct route *ro) 797f325335cSAndrey V. Elsukov { 798f325335cSAndrey V. Elsukov uint32_t af; 799f325335cSAndrey V. Elsukov int error; 800f325335cSAndrey V. Elsukov 801f325335cSAndrey V. Elsukov #ifdef MAC 802f325335cSAndrey V. Elsukov error = mac_ifnet_check_transmit(ifp, m); 803f325335cSAndrey V. Elsukov if (error != 0) 804f325335cSAndrey V. Elsukov goto drop; 805f325335cSAndrey V. Elsukov #endif 806f325335cSAndrey V. Elsukov if ((ifp->if_flags & IFF_MONITOR) != 0 || 807f325335cSAndrey V. Elsukov (ifp->if_flags & IFF_UP) == 0) { 808f325335cSAndrey V. Elsukov error = ENETDOWN; 809f325335cSAndrey V. Elsukov goto drop; 810f325335cSAndrey V. Elsukov } 811f325335cSAndrey V. Elsukov 812f325335cSAndrey V. Elsukov error = gre_check_nesting(ifp, m); 813f325335cSAndrey V. Elsukov if (error != 0) 814f325335cSAndrey V. Elsukov goto drop; 815f325335cSAndrey V. Elsukov 816f325335cSAndrey V. Elsukov m->m_flags &= ~(M_BCAST|M_MCAST); 817f325335cSAndrey V. Elsukov if (dst->sa_family == AF_UNSPEC) 818f325335cSAndrey V. Elsukov bcopy(dst->sa_data, &af, sizeof(af)); 819f325335cSAndrey V. Elsukov else 820f325335cSAndrey V. Elsukov af = dst->sa_family; 821f325335cSAndrey V. Elsukov BPF_MTAP2(ifp, &af, sizeof(af), m); 822f325335cSAndrey V. Elsukov m->m_pkthdr.csum_data = af; /* save af for if_transmit */ 823f325335cSAndrey V. Elsukov return (ifp->if_transmit(ifp, m)); 824f325335cSAndrey V. Elsukov drop: 825f325335cSAndrey V. Elsukov m_freem(m); 826f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 827f325335cSAndrey V. Elsukov return (error); 828f325335cSAndrey V. Elsukov } 829f325335cSAndrey V. Elsukov 830f325335cSAndrey V. Elsukov static void 831f325335cSAndrey V. Elsukov gre_setseqn(struct grehdr *gh, uint32_t seq) 832f325335cSAndrey V. Elsukov { 833f325335cSAndrey V. Elsukov uint32_t *opts; 834f325335cSAndrey V. Elsukov uint16_t flags; 835f325335cSAndrey V. Elsukov 836f325335cSAndrey V. Elsukov opts = gh->gre_opts; 837f325335cSAndrey V. Elsukov flags = ntohs(gh->gre_flags); 838f325335cSAndrey V. Elsukov KASSERT((flags & GRE_FLAGS_SP) != 0, 839f325335cSAndrey V. Elsukov ("gre_setseqn called, but GRE_FLAGS_SP isn't set ")); 840f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_CP) 841f325335cSAndrey V. Elsukov opts++; 842f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_KP) 843f325335cSAndrey V. Elsukov opts++; 844f325335cSAndrey V. Elsukov *opts = htonl(seq); 845f325335cSAndrey V. Elsukov } 846f325335cSAndrey V. Elsukov 847f325335cSAndrey V. Elsukov static int 848f325335cSAndrey V. Elsukov gre_transmit(struct ifnet *ifp, struct mbuf *m) 849f325335cSAndrey V. Elsukov { 850f325335cSAndrey V. Elsukov GRE_RLOCK_TRACKER; 851f325335cSAndrey V. Elsukov struct gre_softc *sc; 852f325335cSAndrey V. Elsukov struct grehdr *gh; 853f325335cSAndrey V. Elsukov uint32_t iaf, oaf, oseq; 854f325335cSAndrey V. Elsukov int error, hlen, olen, plen; 855f325335cSAndrey V. Elsukov int want_seq, want_csum; 856f325335cSAndrey V. Elsukov 857f325335cSAndrey V. Elsukov plen = 0; 858f325335cSAndrey V. Elsukov sc = ifp->if_softc; 859f325335cSAndrey V. Elsukov if (sc == NULL) { 860f325335cSAndrey V. Elsukov error = ENETDOWN; 861f325335cSAndrey V. Elsukov m_freem(m); 862f325335cSAndrey V. Elsukov goto drop; 863f325335cSAndrey V. Elsukov } 864f325335cSAndrey V. Elsukov GRE_RLOCK(sc); 865f325335cSAndrey V. Elsukov if (sc->gre_family == 0) { 866f325335cSAndrey V. Elsukov GRE_RUNLOCK(sc); 867f325335cSAndrey V. Elsukov error = ENETDOWN; 868f325335cSAndrey V. Elsukov m_freem(m); 869f325335cSAndrey V. Elsukov goto drop; 870f325335cSAndrey V. Elsukov } 871f325335cSAndrey V. Elsukov iaf = m->m_pkthdr.csum_data; 872f325335cSAndrey V. Elsukov oaf = sc->gre_family; 873f325335cSAndrey V. Elsukov hlen = sc->gre_hlen; 874f325335cSAndrey V. Elsukov want_seq = (sc->gre_options & GRE_ENABLE_SEQ) != 0; 875f325335cSAndrey V. Elsukov if (want_seq) 876f325335cSAndrey V. Elsukov oseq = sc->gre_oseq++; /* XXX */ 8774dbd7c5dSBjoern A. Zeeb else 8784dbd7c5dSBjoern A. Zeeb oseq = 0; /* Make compiler happy. */ 879f325335cSAndrey V. Elsukov want_csum = (sc->gre_options & GRE_ENABLE_CSUM) != 0; 880f325335cSAndrey V. Elsukov M_SETFIB(m, sc->gre_fibnum); 881f325335cSAndrey V. Elsukov M_PREPEND(m, hlen, M_NOWAIT); 882f325335cSAndrey V. Elsukov if (m == NULL) { 883f325335cSAndrey V. Elsukov GRE_RUNLOCK(sc); 884f325335cSAndrey V. Elsukov error = ENOBUFS; 885f325335cSAndrey V. Elsukov goto drop; 886f325335cSAndrey V. Elsukov } 887f325335cSAndrey V. Elsukov bcopy(sc->gre_hdr, mtod(m, void *), hlen); 888f325335cSAndrey V. Elsukov GRE_RUNLOCK(sc); 889f325335cSAndrey V. Elsukov switch (oaf) { 890f325335cSAndrey V. Elsukov #ifdef INET 891f325335cSAndrey V. Elsukov case AF_INET: 892f325335cSAndrey V. Elsukov olen = sizeof(struct ip); 893f325335cSAndrey V. Elsukov break; 894f325335cSAndrey V. Elsukov #endif 895f325335cSAndrey V. Elsukov #ifdef INET6 896f325335cSAndrey V. Elsukov case AF_INET6: 897f325335cSAndrey V. Elsukov olen = sizeof(struct ip6_hdr); 898f325335cSAndrey V. Elsukov break; 899f325335cSAndrey V. Elsukov #endif 900f325335cSAndrey V. Elsukov default: 901f325335cSAndrey V. Elsukov error = ENETDOWN; 902f325335cSAndrey V. Elsukov goto drop; 903f325335cSAndrey V. Elsukov } 904f325335cSAndrey V. Elsukov gh = (struct grehdr *)mtodo(m, olen); 905f325335cSAndrey V. Elsukov switch (iaf) { 906f325335cSAndrey V. Elsukov #ifdef INET 907f325335cSAndrey V. Elsukov case AF_INET: 908f325335cSAndrey V. Elsukov gh->gre_proto = htons(ETHERTYPE_IP); 909f325335cSAndrey V. Elsukov break; 910f325335cSAndrey V. Elsukov #endif 911f325335cSAndrey V. Elsukov #ifdef INET6 912f325335cSAndrey V. Elsukov case AF_INET6: 913f325335cSAndrey V. Elsukov gh->gre_proto = htons(ETHERTYPE_IPV6); 914f325335cSAndrey V. Elsukov break; 915f325335cSAndrey V. Elsukov #endif 916f325335cSAndrey V. Elsukov default: 917f325335cSAndrey V. Elsukov error = ENETDOWN; 918f325335cSAndrey V. Elsukov goto drop; 919f325335cSAndrey V. Elsukov } 920f325335cSAndrey V. Elsukov if (want_seq) 921f325335cSAndrey V. Elsukov gre_setseqn(gh, oseq); 922f325335cSAndrey V. Elsukov if (want_csum) { 923f325335cSAndrey V. Elsukov *(uint16_t *)gh->gre_opts = in_cksum_skip(m, 924f325335cSAndrey V. Elsukov m->m_pkthdr.len, olen); 925f325335cSAndrey V. Elsukov } 926f325335cSAndrey V. Elsukov plen = m->m_pkthdr.len - hlen; 927f325335cSAndrey V. Elsukov switch (oaf) { 928f325335cSAndrey V. Elsukov #ifdef INET 929f325335cSAndrey V. Elsukov case AF_INET: 930f325335cSAndrey V. Elsukov error = in_gre_output(m, iaf, hlen); 931f325335cSAndrey V. Elsukov break; 932f325335cSAndrey V. Elsukov #endif 933f325335cSAndrey V. Elsukov #ifdef INET6 934f325335cSAndrey V. Elsukov case AF_INET6: 935f325335cSAndrey V. Elsukov error = in6_gre_output(m, iaf, hlen); 936f325335cSAndrey V. Elsukov break; 937f325335cSAndrey V. Elsukov #endif 938f325335cSAndrey V. Elsukov default: 939f325335cSAndrey V. Elsukov m_freem(m); 940f325335cSAndrey V. Elsukov error = ENETDOWN; 941f325335cSAndrey V. Elsukov }; 942f325335cSAndrey V. Elsukov drop: 943f325335cSAndrey V. Elsukov if (error) 944f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 945f325335cSAndrey V. Elsukov else { 946f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 947f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OBYTES, plen); 948f325335cSAndrey V. Elsukov } 949f325335cSAndrey V. Elsukov return (error); 950f325335cSAndrey V. Elsukov } 951f325335cSAndrey V. Elsukov 952f325335cSAndrey V. Elsukov static void 953f325335cSAndrey V. Elsukov gre_qflush(struct ifnet *ifp __unused) 954f325335cSAndrey V. Elsukov { 955f325335cSAndrey V. Elsukov 9568e96e13eSMaxim Sobolev } 9578e96e13eSMaxim Sobolev 9588e96e13eSMaxim Sobolev static int 9598e96e13eSMaxim Sobolev gremodevent(module_t mod, int type, void *data) 9608e96e13eSMaxim Sobolev { 9618e96e13eSMaxim Sobolev 9628e96e13eSMaxim Sobolev switch (type) { 9638e96e13eSMaxim Sobolev case MOD_LOAD: 9648e96e13eSMaxim Sobolev case MOD_UNLOAD: 9658e96e13eSMaxim Sobolev break; 9663e019deaSPoul-Henning Kamp default: 96789c58b73SHiroki Sato return (EOPNOTSUPP); 9688e96e13eSMaxim Sobolev } 96989c58b73SHiroki Sato return (0); 9708e96e13eSMaxim Sobolev } 9718e96e13eSMaxim Sobolev 9728e96e13eSMaxim Sobolev static moduledata_t gre_mod = { 9738e96e13eSMaxim Sobolev "if_gre", 9748e96e13eSMaxim Sobolev gremodevent, 9759823d527SKevin Lo 0 9768e96e13eSMaxim Sobolev }; 9778e96e13eSMaxim Sobolev 9788e96e13eSMaxim Sobolev DECLARE_MODULE(if_gre, gre_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 9798e96e13eSMaxim Sobolev MODULE_VERSION(if_gre, 1); 980