1c398230bSWarner Losh /*- 2fe267a55SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3fe267a55SPedro F. Giffuni * 48e96e13eSMaxim Sobolev * Copyright (c) 1998 The NetBSD Foundation, Inc. 5a5185adeSAndrey V. Elsukov * Copyright (c) 2014, 2018 Andrey V. Elsukov <ae@FreeBSD.org> 68e96e13eSMaxim Sobolev * All rights reserved. 78e96e13eSMaxim Sobolev * 88e96e13eSMaxim Sobolev * This code is derived from software contributed to The NetBSD Foundation 98e96e13eSMaxim Sobolev * by Heiko W.Rupp <hwr@pilhuhn.de> 108e96e13eSMaxim Sobolev * 119e669156SBjoern A. Zeeb * IPv6-over-GRE contributed by Gert Doering <gert@greenie.muc.de> 129e669156SBjoern A. Zeeb * 138e96e13eSMaxim Sobolev * Redistribution and use in source and binary forms, with or without 148e96e13eSMaxim Sobolev * modification, are permitted provided that the following conditions 158e96e13eSMaxim Sobolev * are met: 168e96e13eSMaxim Sobolev * 1. Redistributions of source code must retain the above copyright 178e96e13eSMaxim Sobolev * notice, this list of conditions and the following disclaimer. 188e96e13eSMaxim Sobolev * 2. Redistributions in binary form must reproduce the above copyright 198e96e13eSMaxim Sobolev * notice, this list of conditions and the following disclaimer in the 208e96e13eSMaxim Sobolev * documentation and/or other materials provided with the distribution. 218e96e13eSMaxim Sobolev * 228e96e13eSMaxim Sobolev * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 238e96e13eSMaxim Sobolev * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 248e96e13eSMaxim Sobolev * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 258e96e13eSMaxim Sobolev * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 268e96e13eSMaxim Sobolev * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 278e96e13eSMaxim Sobolev * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 288e96e13eSMaxim Sobolev * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 298e96e13eSMaxim Sobolev * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 308e96e13eSMaxim Sobolev * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 318e96e13eSMaxim Sobolev * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 328e96e13eSMaxim Sobolev * POSSIBILITY OF SUCH DAMAGE. 33f325335cSAndrey V. Elsukov * 34f325335cSAndrey V. Elsukov * $NetBSD: if_gre.c,v 1.49 2003/12/11 00:22:29 itojun Exp $ 358e96e13eSMaxim Sobolev */ 368e96e13eSMaxim Sobolev 37f325335cSAndrey V. Elsukov #include <sys/cdefs.h> 38f325335cSAndrey V. Elsukov __FBSDID("$FreeBSD$"); 398e96e13eSMaxim Sobolev 408e96e13eSMaxim Sobolev #include "opt_inet.h" 41f16770aeSBruce M Simpson #include "opt_inet6.h" 42aee793eeSAndrey V. Elsukov #include "opt_rss.h" 438e96e13eSMaxim Sobolev 448e96e13eSMaxim Sobolev #include <sys/param.h> 458e96e13eSMaxim Sobolev #include <sys/kernel.h> 46f325335cSAndrey V. Elsukov #include <sys/lock.h> 478e96e13eSMaxim Sobolev #include <sys/malloc.h> 485dba30f1SPoul-Henning Kamp #include <sys/module.h> 498e96e13eSMaxim Sobolev #include <sys/mbuf.h> 50acd3428bSRobert Watson #include <sys/priv.h> 518b07e49aSJulian Elischer #include <sys/proc.h> 528e96e13eSMaxim Sobolev #include <sys/socket.h> 53aee793eeSAndrey V. Elsukov #include <sys/socketvar.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> 70aee793eeSAndrey V. Elsukov #include <netinet/in_pcb.h> 71f325335cSAndrey V. Elsukov #ifdef INET 728e96e13eSMaxim Sobolev #include <netinet/in_var.h> 738e96e13eSMaxim Sobolev #include <netinet/ip.h> 748e96e13eSMaxim Sobolev #include <netinet/ip_var.h> 75aee793eeSAndrey V. Elsukov #ifdef RSS 76aee793eeSAndrey V. Elsukov #include <netinet/in_rss.h> 77aee793eeSAndrey V. Elsukov #endif 788e96e13eSMaxim Sobolev #endif 798e96e13eSMaxim Sobolev 80f325335cSAndrey V. Elsukov #ifdef INET6 81f325335cSAndrey V. Elsukov #include <netinet/ip6.h> 82f325335cSAndrey V. Elsukov #include <netinet6/in6_var.h> 83f325335cSAndrey V. Elsukov #include <netinet6/ip6_var.h> 84aee793eeSAndrey V. Elsukov #ifdef RSS 85aee793eeSAndrey V. Elsukov #include <netinet6/in6_rss.h> 86aee793eeSAndrey V. Elsukov #endif 87f325335cSAndrey V. Elsukov #endif 888e96e13eSMaxim Sobolev 89f325335cSAndrey V. Elsukov #include <netinet/ip_encap.h> 90aee793eeSAndrey V. Elsukov #include <netinet/udp.h> 91f325335cSAndrey V. Elsukov #include <net/bpf.h> 928e96e13eSMaxim Sobolev #include <net/if_gre.h> 938e96e13eSMaxim Sobolev 94f325335cSAndrey V. Elsukov #include <machine/in_cksum.h> 95f325335cSAndrey V. Elsukov #include <security/mac/mac_framework.h> 968e96e13eSMaxim Sobolev 97c00bf730SAndrey V. Elsukov #define GREMTU 1476 98f325335cSAndrey V. Elsukov 99a5185adeSAndrey V. Elsukov static const char grename[] = "gre"; 100a5185adeSAndrey V. Elsukov MALLOC_DEFINE(M_GRE, grename, "Generic Routing Encapsulation"); 101a5185adeSAndrey V. Elsukov 102f325335cSAndrey V. Elsukov static struct sx gre_ioctl_sx; 103f325335cSAndrey V. Elsukov SX_SYSINIT(gre_ioctl_sx, &gre_ioctl_sx, "gre_ioctl"); 1048e96e13eSMaxim Sobolev 1056b7330e2SSam Leffler static int gre_clone_create(struct if_clone *, int, caddr_t); 1069ee35470SAlfred Perlstein static void gre_clone_destroy(struct ifnet *); 1075f901c92SAndrew Turner VNET_DEFINE_STATIC(struct if_clone *, gre_cloner); 10889c58b73SHiroki Sato #define V_gre_cloner VNET(gre_cloner) 10942a58907SGleb Smirnoff 110f325335cSAndrey V. Elsukov static void gre_qflush(struct ifnet *); 111f325335cSAndrey V. Elsukov static int gre_transmit(struct ifnet *, struct mbuf *); 112c23d234cSMaxim Sobolev static int gre_ioctl(struct ifnet *, u_long, caddr_t); 11347e8d432SGleb Smirnoff static int gre_output(struct ifnet *, struct mbuf *, 11447e8d432SGleb Smirnoff const struct sockaddr *, struct route *); 115a5185adeSAndrey V. Elsukov static void gre_delete_tunnel(struct gre_softc *); 1168e96e13eSMaxim Sobolev 1178e96e13eSMaxim Sobolev SYSCTL_DECL(_net_link); 118*7029da5cSPawel Biernacki static SYSCTL_NODE(_net_link, IFT_TUNNEL, gre, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 1198e96e13eSMaxim Sobolev "Generic Routing Encapsulation"); 1208e96e13eSMaxim Sobolev #ifndef MAX_GRE_NEST 1218e96e13eSMaxim Sobolev /* 1228e96e13eSMaxim Sobolev * This macro controls the default upper limitation on nesting of gre tunnels. 1238e96e13eSMaxim Sobolev * Since, setting a large value to this macro with a careless configuration 1248e96e13eSMaxim Sobolev * may introduce system crash, we don't allow any nestings by default. 1258e96e13eSMaxim Sobolev * If you need to configure nested gre tunnels, you can define this macro 1268e96e13eSMaxim Sobolev * in your kernel configuration file. However, if you do so, please be 1278e96e13eSMaxim Sobolev * careful to configure the tunnels so that it won't make a loop. 1288e96e13eSMaxim Sobolev */ 1298e96e13eSMaxim Sobolev #define MAX_GRE_NEST 1 1308e96e13eSMaxim Sobolev #endif 131f325335cSAndrey V. Elsukov 1325f901c92SAndrew Turner VNET_DEFINE_STATIC(int, max_gre_nesting) = MAX_GRE_NEST; 13389c58b73SHiroki Sato #define V_max_gre_nesting VNET(max_gre_nesting) 13489c58b73SHiroki Sato SYSCTL_INT(_net_link_gre, OID_AUTO, max_nesting, CTLFLAG_RW | CTLFLAG_VNET, 13589c58b73SHiroki Sato &VNET_NAME(max_gre_nesting), 0, "Max nested tunnels"); 1368e96e13eSMaxim Sobolev 137c23d234cSMaxim Sobolev static void 13889c58b73SHiroki Sato vnet_gre_init(const void *unused __unused) 1398e96e13eSMaxim Sobolev { 140a5185adeSAndrey V. Elsukov 14189c58b73SHiroki Sato V_gre_cloner = if_clone_simple(grename, gre_clone_create, 14242a58907SGleb Smirnoff gre_clone_destroy, 0); 143a5185adeSAndrey V. Elsukov #ifdef INET 144a5185adeSAndrey V. Elsukov in_gre_init(); 145a5185adeSAndrey V. Elsukov #endif 146a5185adeSAndrey V. Elsukov #ifdef INET6 147a5185adeSAndrey V. Elsukov in6_gre_init(); 148a5185adeSAndrey V. Elsukov #endif 1498e96e13eSMaxim Sobolev } 15089c58b73SHiroki Sato VNET_SYSINIT(vnet_gre_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 15189c58b73SHiroki Sato vnet_gre_init, NULL); 15289c58b73SHiroki Sato 15389c58b73SHiroki Sato static void 15489c58b73SHiroki Sato vnet_gre_uninit(const void *unused __unused) 15589c58b73SHiroki Sato { 15689c58b73SHiroki Sato 15789c58b73SHiroki Sato if_clone_detach(V_gre_cloner); 158a5185adeSAndrey V. Elsukov #ifdef INET 159a5185adeSAndrey V. Elsukov in_gre_uninit(); 160a5185adeSAndrey V. Elsukov #endif 161a5185adeSAndrey V. Elsukov #ifdef INET6 162a5185adeSAndrey V. Elsukov in6_gre_uninit(); 163a5185adeSAndrey V. Elsukov #endif 164aee793eeSAndrey V. Elsukov /* XXX: epoch_call drain */ 16589c58b73SHiroki Sato } 16689c58b73SHiroki Sato VNET_SYSUNINIT(vnet_gre_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 16789c58b73SHiroki Sato vnet_gre_uninit, NULL); 1688e96e13eSMaxim Sobolev 169c23d234cSMaxim Sobolev static int 17089c58b73SHiroki Sato gre_clone_create(struct if_clone *ifc, int unit, caddr_t params) 1718e96e13eSMaxim Sobolev { 1728e96e13eSMaxim Sobolev struct gre_softc *sc; 1738e96e13eSMaxim Sobolev 174b3c9a01eSBruce M Simpson sc = malloc(sizeof(struct gre_softc), M_GRE, M_WAITOK | M_ZERO); 175f325335cSAndrey V. Elsukov sc->gre_fibnum = curthread->td_proc->p_fibnum; 176066b192eSBjoern A. Zeeb GRE2IFP(sc) = if_alloc(IFT_TUNNEL); 177fc74a9f9SBrooks Davis GRE2IFP(sc)->if_softc = sc; 17842a58907SGleb Smirnoff if_initname(GRE2IFP(sc), grename, unit); 179066b192eSBjoern A. Zeeb 180c00bf730SAndrey V. Elsukov GRE2IFP(sc)->if_mtu = GREMTU; 181fc74a9f9SBrooks Davis GRE2IFP(sc)->if_flags = IFF_POINTOPOINT|IFF_MULTICAST; 182fc74a9f9SBrooks Davis GRE2IFP(sc)->if_output = gre_output; 183fc74a9f9SBrooks Davis GRE2IFP(sc)->if_ioctl = gre_ioctl; 184f325335cSAndrey V. Elsukov GRE2IFP(sc)->if_transmit = gre_transmit; 185f325335cSAndrey V. Elsukov GRE2IFP(sc)->if_qflush = gre_qflush; 186f1aaad0cSHiroki Sato GRE2IFP(sc)->if_capabilities |= IFCAP_LINKSTATE; 187f1aaad0cSHiroki Sato GRE2IFP(sc)->if_capenable |= IFCAP_LINKSTATE; 188fc74a9f9SBrooks Davis if_attach(GRE2IFP(sc)); 189fc74a9f9SBrooks Davis bpfattach(GRE2IFP(sc), DLT_NULL, sizeof(u_int32_t)); 1908e96e13eSMaxim Sobolev return (0); 1918e96e13eSMaxim Sobolev } 1928e96e13eSMaxim Sobolev 193c23d234cSMaxim Sobolev static void 19489c58b73SHiroki Sato gre_clone_destroy(struct ifnet *ifp) 1958e96e13eSMaxim Sobolev { 196f325335cSAndrey V. Elsukov struct gre_softc *sc; 1978e96e13eSMaxim Sobolev 198f325335cSAndrey V. Elsukov sx_xlock(&gre_ioctl_sx); 199f325335cSAndrey V. Elsukov sc = ifp->if_softc; 200a5185adeSAndrey V. Elsukov gre_delete_tunnel(sc); 201febd0759SAndrew Thompson bpfdetach(ifp); 202febd0759SAndrew Thompson if_detach(ifp); 203f325335cSAndrey V. Elsukov ifp->if_softc = NULL; 204f325335cSAndrey V. Elsukov sx_xunlock(&gre_ioctl_sx); 205f325335cSAndrey V. Elsukov 206a5185adeSAndrey V. Elsukov GRE_WAIT(); 207febd0759SAndrew Thompson if_free(ifp); 208febd0759SAndrew Thompson free(sc, M_GRE); 2098e96e13eSMaxim Sobolev } 2108e96e13eSMaxim Sobolev 211c23d234cSMaxim Sobolev static int 2128e96e13eSMaxim Sobolev gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 2138e96e13eSMaxim Sobolev { 2148e96e13eSMaxim Sobolev struct ifreq *ifr = (struct ifreq *)data; 215f325335cSAndrey V. Elsukov struct gre_softc *sc; 216f325335cSAndrey V. Elsukov uint32_t opt; 217f325335cSAndrey V. Elsukov int error; 2188e96e13eSMaxim Sobolev 2198e96e13eSMaxim Sobolev switch (cmd) { 220f325335cSAndrey V. Elsukov case SIOCSIFMTU: 221f325335cSAndrey V. Elsukov /* XXX: */ 222f325335cSAndrey V. Elsukov if (ifr->ifr_mtu < 576) 223f325335cSAndrey V. Elsukov return (EINVAL); 224c00bf730SAndrey V. Elsukov ifp->if_mtu = ifr->ifr_mtu; 225c00bf730SAndrey V. Elsukov return (0); 2268e96e13eSMaxim Sobolev case SIOCSIFADDR: 2278e96e13eSMaxim Sobolev ifp->if_flags |= IFF_UP; 2288e96e13eSMaxim Sobolev case SIOCSIFFLAGS: 2298e96e13eSMaxim Sobolev case SIOCADDMULTI: 2308e96e13eSMaxim Sobolev case SIOCDELMULTI: 231f325335cSAndrey V. Elsukov return (0); 2328e96e13eSMaxim Sobolev case GRESADDRS: 2338e96e13eSMaxim Sobolev case GRESADDRD: 2348e96e13eSMaxim Sobolev case GREGADDRS: 2358e96e13eSMaxim Sobolev case GREGADDRD: 236f325335cSAndrey V. Elsukov case GRESPROTO: 237f325335cSAndrey V. Elsukov case GREGPROTO: 238f325335cSAndrey V. Elsukov return (EOPNOTSUPP); 239f325335cSAndrey V. Elsukov } 240f325335cSAndrey V. Elsukov sx_xlock(&gre_ioctl_sx); 241f325335cSAndrey V. Elsukov sc = ifp->if_softc; 242f325335cSAndrey V. Elsukov if (sc == NULL) { 243f325335cSAndrey V. Elsukov error = ENXIO; 244f325335cSAndrey V. Elsukov goto end; 245f325335cSAndrey V. Elsukov } 246f325335cSAndrey V. Elsukov error = 0; 247f325335cSAndrey V. Elsukov switch (cmd) { 248f325335cSAndrey V. Elsukov case SIOCDIFPHYADDR: 249a5185adeSAndrey V. Elsukov if (sc->gre_family == 0) 2508e96e13eSMaxim Sobolev break; 251a5185adeSAndrey V. Elsukov gre_delete_tunnel(sc); 2528e96e13eSMaxim Sobolev break; 253f325335cSAndrey V. Elsukov #ifdef INET 254a5185adeSAndrey V. Elsukov case SIOCSIFPHYADDR: 255f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR: 256f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR: 257a5185adeSAndrey V. Elsukov error = in_gre_ioctl(sc, cmd, data); 258f325335cSAndrey V. Elsukov break; 259f325335cSAndrey V. Elsukov #endif 260f325335cSAndrey V. Elsukov #ifdef INET6 261a5185adeSAndrey V. Elsukov case SIOCSIFPHYADDR_IN6: 262f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR_IN6: 263f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR_IN6: 264a5185adeSAndrey V. Elsukov error = in6_gre_ioctl(sc, cmd, data); 265f325335cSAndrey V. Elsukov break; 266f325335cSAndrey V. Elsukov #endif 267eccfe69aSAndrey V. Elsukov case SIOCGTUNFIB: 268eccfe69aSAndrey V. Elsukov ifr->ifr_fib = sc->gre_fibnum; 269eccfe69aSAndrey V. Elsukov break; 270eccfe69aSAndrey V. Elsukov case SIOCSTUNFIB: 271eccfe69aSAndrey V. Elsukov if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0) 272eccfe69aSAndrey V. Elsukov break; 273eccfe69aSAndrey V. Elsukov if (ifr->ifr_fib >= rt_numfibs) 274eccfe69aSAndrey V. Elsukov error = EINVAL; 275eccfe69aSAndrey V. Elsukov else 276eccfe69aSAndrey V. Elsukov sc->gre_fibnum = ifr->ifr_fib; 277eccfe69aSAndrey V. Elsukov break; 278f325335cSAndrey V. Elsukov case GRESKEY: 279f325335cSAndrey V. Elsukov case GRESOPTS: 280aee793eeSAndrey V. Elsukov case GRESPORT: 281f325335cSAndrey V. Elsukov if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0) 282f325335cSAndrey V. Elsukov break; 283541d96aaSBrooks Davis if ((error = copyin(ifr_data_get_ptr(ifr), &opt, 284541d96aaSBrooks Davis sizeof(opt))) != 0) 285f325335cSAndrey V. Elsukov break; 286a5185adeSAndrey V. Elsukov if (cmd == GRESKEY) { 287a5185adeSAndrey V. Elsukov if (sc->gre_key == opt) 288131c55bcSAndrew Thompson break; 289a5185adeSAndrey V. Elsukov } else if (cmd == GRESOPTS) { 290a5185adeSAndrey V. Elsukov if (opt & ~GRE_OPTMASK) { 291a5185adeSAndrey V. Elsukov error = EINVAL; 292a5185adeSAndrey V. Elsukov break; 293a5185adeSAndrey V. Elsukov } 294a5185adeSAndrey V. Elsukov if (sc->gre_options == opt) 295a5185adeSAndrey V. Elsukov break; 296aee793eeSAndrey V. Elsukov } else if (cmd == GRESPORT) { 297aee793eeSAndrey V. Elsukov if (opt != 0 && (opt < V_ipport_hifirstauto || 298aee793eeSAndrey V. Elsukov opt > V_ipport_hilastauto)) { 299aee793eeSAndrey V. Elsukov error = EINVAL; 300aee793eeSAndrey V. Elsukov break; 301aee793eeSAndrey V. Elsukov } 302aee793eeSAndrey V. Elsukov if (sc->gre_port == opt) 303aee793eeSAndrey V. Elsukov break; 304aee793eeSAndrey V. Elsukov if ((sc->gre_options & GRE_UDPENCAP) == 0) { 305aee793eeSAndrey V. Elsukov /* 306aee793eeSAndrey V. Elsukov * UDP encapsulation is not enabled, thus 307aee793eeSAndrey V. Elsukov * there is no need to reattach softc. 308aee793eeSAndrey V. Elsukov */ 309aee793eeSAndrey V. Elsukov sc->gre_port = opt; 310aee793eeSAndrey V. Elsukov break; 311aee793eeSAndrey V. Elsukov } 312a5185adeSAndrey V. Elsukov } 313a5185adeSAndrey V. Elsukov switch (sc->gre_family) { 314a5185adeSAndrey V. Elsukov #ifdef INET 315a5185adeSAndrey V. Elsukov case AF_INET: 316aee793eeSAndrey V. Elsukov error = in_gre_setopts(sc, cmd, opt); 317a5185adeSAndrey V. Elsukov break; 318a5185adeSAndrey V. Elsukov #endif 319a5185adeSAndrey V. Elsukov #ifdef INET6 320a5185adeSAndrey V. Elsukov case AF_INET6: 321aee793eeSAndrey V. Elsukov error = in6_gre_setopts(sc, cmd, opt); 322a5185adeSAndrey V. Elsukov break; 323a5185adeSAndrey V. Elsukov #endif 324a5185adeSAndrey V. Elsukov default: 325aee793eeSAndrey V. Elsukov /* 326aee793eeSAndrey V. Elsukov * Tunnel is not yet configured. 327aee793eeSAndrey V. Elsukov * We can just change any parameters. 328aee793eeSAndrey V. Elsukov */ 329a5185adeSAndrey V. Elsukov if (cmd == GRESKEY) 330a5185adeSAndrey V. Elsukov sc->gre_key = opt; 331aee793eeSAndrey V. Elsukov if (cmd == GRESOPTS) 332a5185adeSAndrey V. Elsukov sc->gre_options = opt; 333aee793eeSAndrey V. Elsukov if (cmd == GRESPORT) 334aee793eeSAndrey V. Elsukov sc->gre_port = opt; 335a5185adeSAndrey V. Elsukov break; 336a5185adeSAndrey V. Elsukov } 337a5185adeSAndrey V. Elsukov /* 338a5185adeSAndrey V. Elsukov * XXX: Do we need to initiate change of interface 339a5185adeSAndrey V. Elsukov * state here? 340a5185adeSAndrey V. Elsukov */ 341a5185adeSAndrey V. Elsukov break; 342a5185adeSAndrey V. Elsukov case GREGKEY: 343a5185adeSAndrey V. Elsukov error = copyout(&sc->gre_key, ifr_data_get_ptr(ifr), 344a5185adeSAndrey V. Elsukov sizeof(sc->gre_key)); 345a5185adeSAndrey V. Elsukov break; 346f325335cSAndrey V. Elsukov case GREGOPTS: 347541d96aaSBrooks Davis error = copyout(&sc->gre_options, ifr_data_get_ptr(ifr), 348f325335cSAndrey V. Elsukov sizeof(sc->gre_options)); 349f325335cSAndrey V. Elsukov break; 350aee793eeSAndrey V. Elsukov case GREGPORT: 351aee793eeSAndrey V. Elsukov error = copyout(&sc->gre_port, ifr_data_get_ptr(ifr), 352aee793eeSAndrey V. Elsukov sizeof(sc->gre_port)); 353aee793eeSAndrey V. Elsukov break; 3548e96e13eSMaxim Sobolev default: 3558e96e13eSMaxim Sobolev error = EINVAL; 3568e96e13eSMaxim Sobolev break; 3578e96e13eSMaxim Sobolev } 358a5185adeSAndrey V. Elsukov if (error == 0 && sc->gre_family != 0) { 359a5185adeSAndrey V. Elsukov if ( 360a5185adeSAndrey V. Elsukov #ifdef INET 361a5185adeSAndrey V. Elsukov cmd == SIOCSIFPHYADDR || 362a5185adeSAndrey V. Elsukov #endif 363a5185adeSAndrey V. Elsukov #ifdef INET6 364a5185adeSAndrey V. Elsukov cmd == SIOCSIFPHYADDR_IN6 || 365a5185adeSAndrey V. Elsukov #endif 366a5185adeSAndrey V. Elsukov 0) { 367a5185adeSAndrey V. Elsukov if_link_state_change(ifp, LINK_STATE_UP); 368a5185adeSAndrey V. Elsukov } 369a5185adeSAndrey V. Elsukov } 370f325335cSAndrey V. Elsukov end: 371f325335cSAndrey V. Elsukov sx_xunlock(&gre_ioctl_sx); 3728e96e13eSMaxim Sobolev return (error); 3738e96e13eSMaxim Sobolev } 3748e96e13eSMaxim Sobolev 375f325335cSAndrey V. Elsukov static void 376a5185adeSAndrey V. Elsukov gre_delete_tunnel(struct gre_softc *sc) 377f325335cSAndrey V. Elsukov { 378aee793eeSAndrey V. Elsukov struct gre_socket *gs; 379a5185adeSAndrey V. Elsukov 380a5185adeSAndrey V. Elsukov sx_assert(&gre_ioctl_sx, SA_XLOCKED); 381a5185adeSAndrey V. Elsukov if (sc->gre_family != 0) { 382a5185adeSAndrey V. Elsukov CK_LIST_REMOVE(sc, chain); 38319873f47SAndrey V. Elsukov CK_LIST_REMOVE(sc, srchash); 384a5185adeSAndrey V. Elsukov GRE_WAIT(); 385a5185adeSAndrey V. Elsukov free(sc->gre_hdr, M_GRE); 386a5185adeSAndrey V. Elsukov sc->gre_family = 0; 387a5185adeSAndrey V. Elsukov } 388aee793eeSAndrey V. Elsukov /* 389aee793eeSAndrey V. Elsukov * If this Tunnel was the last one that could use UDP socket, 390aee793eeSAndrey V. Elsukov * we should unlink socket from hash table and close it. 391aee793eeSAndrey V. Elsukov */ 392aee793eeSAndrey V. Elsukov if ((gs = sc->gre_so) != NULL && CK_LIST_EMPTY(&gs->list)) { 393aee793eeSAndrey V. Elsukov CK_LIST_REMOVE(gs, chain); 394aee793eeSAndrey V. Elsukov soclose(gs->so); 3952a4bd982SGleb Smirnoff NET_EPOCH_CALL(gre_sofree, &gs->epoch_ctx); 396aee793eeSAndrey V. Elsukov sc->gre_so = NULL; 397aee793eeSAndrey V. Elsukov } 398a5185adeSAndrey V. Elsukov GRE2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 399a5185adeSAndrey V. Elsukov if_link_state_change(GRE2IFP(sc), LINK_STATE_DOWN); 400a5185adeSAndrey V. Elsukov } 401a5185adeSAndrey V. Elsukov 402a5185adeSAndrey V. Elsukov struct gre_list * 403a5185adeSAndrey V. Elsukov gre_hashinit(void) 404a5185adeSAndrey V. Elsukov { 405a5185adeSAndrey V. Elsukov struct gre_list *hash; 406a5185adeSAndrey V. Elsukov int i; 407a5185adeSAndrey V. Elsukov 408a5185adeSAndrey V. Elsukov hash = malloc(sizeof(struct gre_list) * GRE_HASH_SIZE, 409a5185adeSAndrey V. Elsukov M_GRE, M_WAITOK); 410a5185adeSAndrey V. Elsukov for (i = 0; i < GRE_HASH_SIZE; i++) 411a5185adeSAndrey V. Elsukov CK_LIST_INIT(&hash[i]); 412a5185adeSAndrey V. Elsukov 413a5185adeSAndrey V. Elsukov return (hash); 414a5185adeSAndrey V. Elsukov } 415a5185adeSAndrey V. Elsukov 416a5185adeSAndrey V. Elsukov void 417a5185adeSAndrey V. Elsukov gre_hashdestroy(struct gre_list *hash) 418a5185adeSAndrey V. Elsukov { 419a5185adeSAndrey V. Elsukov 420a5185adeSAndrey V. Elsukov free(hash, M_GRE); 421a5185adeSAndrey V. Elsukov } 422a5185adeSAndrey V. Elsukov 423a5185adeSAndrey V. Elsukov void 424aee793eeSAndrey V. Elsukov gre_sofree(epoch_context_t ctx) 425aee793eeSAndrey V. Elsukov { 426aee793eeSAndrey V. Elsukov struct gre_socket *gs; 427aee793eeSAndrey V. Elsukov 428aee793eeSAndrey V. Elsukov gs = __containerof(ctx, struct gre_socket, epoch_ctx); 429aee793eeSAndrey V. Elsukov free(gs, M_GRE); 430aee793eeSAndrey V. Elsukov } 431aee793eeSAndrey V. Elsukov 432aee793eeSAndrey V. Elsukov static __inline uint16_t 433aee793eeSAndrey V. Elsukov gre_cksum_add(uint16_t sum, uint16_t a) 434aee793eeSAndrey V. Elsukov { 435aee793eeSAndrey V. Elsukov uint16_t res; 436aee793eeSAndrey V. Elsukov 437aee793eeSAndrey V. Elsukov res = sum + a; 438aee793eeSAndrey V. Elsukov return (res + (res < a)); 439aee793eeSAndrey V. Elsukov } 440aee793eeSAndrey V. Elsukov 441aee793eeSAndrey V. Elsukov void 442aee793eeSAndrey V. Elsukov gre_update_udphdr(struct gre_softc *sc, struct udphdr *udp, uint16_t csum) 443aee793eeSAndrey V. Elsukov { 444aee793eeSAndrey V. Elsukov 445aee793eeSAndrey V. Elsukov sx_assert(&gre_ioctl_sx, SA_XLOCKED); 446aee793eeSAndrey V. Elsukov MPASS(sc->gre_options & GRE_UDPENCAP); 447aee793eeSAndrey V. Elsukov 448aee793eeSAndrey V. Elsukov udp->uh_dport = htons(GRE_UDPPORT); 449aee793eeSAndrey V. Elsukov udp->uh_sport = htons(sc->gre_port); 450aee793eeSAndrey V. Elsukov udp->uh_sum = csum; 451aee793eeSAndrey V. Elsukov udp->uh_ulen = 0; 452aee793eeSAndrey V. Elsukov } 453aee793eeSAndrey V. Elsukov 454aee793eeSAndrey V. Elsukov void 455aee793eeSAndrey V. Elsukov gre_update_hdr(struct gre_softc *sc, struct grehdr *gh) 456a5185adeSAndrey V. Elsukov { 457f325335cSAndrey V. Elsukov uint32_t *opts; 458f325335cSAndrey V. Elsukov uint16_t flags; 459f325335cSAndrey V. Elsukov 460a5185adeSAndrey V. Elsukov sx_assert(&gre_ioctl_sx, SA_XLOCKED); 461a5185adeSAndrey V. Elsukov 462f325335cSAndrey V. Elsukov flags = 0; 463f325335cSAndrey V. Elsukov opts = gh->gre_opts; 464f325335cSAndrey V. Elsukov if (sc->gre_options & GRE_ENABLE_CSUM) { 465f325335cSAndrey V. Elsukov flags |= GRE_FLAGS_CP; 466f325335cSAndrey V. Elsukov sc->gre_hlen += 2 * sizeof(uint16_t); 467f325335cSAndrey V. Elsukov *opts++ = 0; 468f325335cSAndrey V. Elsukov } 469f325335cSAndrey V. Elsukov if (sc->gre_key != 0) { 470f325335cSAndrey V. Elsukov flags |= GRE_FLAGS_KP; 471f325335cSAndrey V. Elsukov sc->gre_hlen += sizeof(uint32_t); 472f325335cSAndrey V. Elsukov *opts++ = htonl(sc->gre_key); 473f325335cSAndrey V. Elsukov } 474f325335cSAndrey V. Elsukov if (sc->gre_options & GRE_ENABLE_SEQ) { 475f325335cSAndrey V. Elsukov flags |= GRE_FLAGS_SP; 476f325335cSAndrey V. Elsukov sc->gre_hlen += sizeof(uint32_t); 477f325335cSAndrey V. Elsukov *opts++ = 0; 478f325335cSAndrey V. Elsukov } else 479f325335cSAndrey V. Elsukov sc->gre_oseq = 0; 480f325335cSAndrey V. Elsukov gh->gre_flags = htons(flags); 481f325335cSAndrey V. Elsukov } 482f325335cSAndrey V. Elsukov 483f325335cSAndrey V. Elsukov int 4846d8fdfa9SAndrey V. Elsukov gre_input(struct mbuf *m, int off, int proto, void *arg) 485f325335cSAndrey V. Elsukov { 4866d8fdfa9SAndrey V. Elsukov struct gre_softc *sc = arg; 487f325335cSAndrey V. Elsukov struct grehdr *gh; 488f325335cSAndrey V. Elsukov struct ifnet *ifp; 4892bfd3dfbSMarcelo Araujo uint32_t *opts; 4902bfd3dfbSMarcelo Araujo #ifdef notyet 4912bfd3dfbSMarcelo Araujo uint32_t key; 4922bfd3dfbSMarcelo Araujo #endif 493f325335cSAndrey V. Elsukov uint16_t flags; 494f325335cSAndrey V. Elsukov int hlen, isr, af; 495f325335cSAndrey V. Elsukov 496f325335cSAndrey V. Elsukov ifp = GRE2IFP(sc); 4976d8fdfa9SAndrey V. Elsukov hlen = off + sizeof(struct grehdr) + 4 * sizeof(uint32_t); 49850bc87bcSAndrey V. Elsukov if (m->m_pkthdr.len < hlen) 49950bc87bcSAndrey V. Elsukov goto drop; 50050bc87bcSAndrey V. Elsukov if (m->m_len < hlen) { 50150bc87bcSAndrey V. Elsukov m = m_pullup(m, hlen); 50250bc87bcSAndrey V. Elsukov if (m == NULL) 50350bc87bcSAndrey V. Elsukov goto drop; 50450bc87bcSAndrey V. Elsukov } 5056d8fdfa9SAndrey V. Elsukov gh = (struct grehdr *)mtodo(m, off); 506f325335cSAndrey V. Elsukov flags = ntohs(gh->gre_flags); 507f325335cSAndrey V. Elsukov if (flags & ~GRE_FLAGS_MASK) 508f325335cSAndrey V. Elsukov goto drop; 509f325335cSAndrey V. Elsukov opts = gh->gre_opts; 510f325335cSAndrey V. Elsukov hlen = 2 * sizeof(uint16_t); 511f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_CP) { 512f325335cSAndrey V. Elsukov /* reserved1 field must be zero */ 513f325335cSAndrey V. Elsukov if (((uint16_t *)opts)[1] != 0) 514f325335cSAndrey V. Elsukov goto drop; 5156d8fdfa9SAndrey V. Elsukov if (in_cksum_skip(m, m->m_pkthdr.len, off) != 0) 516f325335cSAndrey V. Elsukov goto drop; 517f325335cSAndrey V. Elsukov hlen += 2 * sizeof(uint16_t); 518f325335cSAndrey V. Elsukov opts++; 519f325335cSAndrey V. Elsukov } 520f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_KP) { 5212bfd3dfbSMarcelo Araujo #ifdef notyet 5222bfd3dfbSMarcelo Araujo /* 5232bfd3dfbSMarcelo Araujo * XXX: The current implementation uses the key only for outgoing 5242bfd3dfbSMarcelo Araujo * packets. But we can check the key value here, or even in the 5252bfd3dfbSMarcelo Araujo * encapcheck function. 5262bfd3dfbSMarcelo Araujo */ 527f325335cSAndrey V. Elsukov key = ntohl(*opts); 5282bfd3dfbSMarcelo Araujo #endif 529f325335cSAndrey V. Elsukov hlen += sizeof(uint32_t); 530f325335cSAndrey V. Elsukov opts++; 5312bfd3dfbSMarcelo Araujo } 5322bfd3dfbSMarcelo Araujo #ifdef notyet 533f325335cSAndrey V. Elsukov } else 534f325335cSAndrey V. Elsukov key = 0; 5352bfd3dfbSMarcelo Araujo 536f325335cSAndrey V. Elsukov if (sc->gre_key != 0 && (key != sc->gre_key || key != 0)) 537f325335cSAndrey V. Elsukov goto drop; 5382bfd3dfbSMarcelo Araujo #endif 539f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_SP) { 5402bfd3dfbSMarcelo Araujo #ifdef notyet 5412bfd3dfbSMarcelo Araujo seq = ntohl(*opts); 5422bfd3dfbSMarcelo Araujo #endif 543f325335cSAndrey V. Elsukov hlen += sizeof(uint32_t); 544f325335cSAndrey V. Elsukov } 545f325335cSAndrey V. Elsukov switch (ntohs(gh->gre_proto)) { 546f325335cSAndrey V. Elsukov case ETHERTYPE_WCCP: 547f325335cSAndrey V. Elsukov /* 548f325335cSAndrey V. Elsukov * For WCCP skip an additional 4 bytes if after GRE header 549f325335cSAndrey V. Elsukov * doesn't follow an IP header. 550f325335cSAndrey V. Elsukov */ 551f325335cSAndrey V. Elsukov if (flags == 0 && (*(uint8_t *)gh->gre_opts & 0xF0) != 0x40) 552f325335cSAndrey V. Elsukov hlen += sizeof(uint32_t); 553f325335cSAndrey V. Elsukov /* FALLTHROUGH */ 554f325335cSAndrey V. Elsukov case ETHERTYPE_IP: 555f325335cSAndrey V. Elsukov isr = NETISR_IP; 556f325335cSAndrey V. Elsukov af = AF_INET; 557f325335cSAndrey V. Elsukov break; 558f325335cSAndrey V. Elsukov case ETHERTYPE_IPV6: 559f325335cSAndrey V. Elsukov isr = NETISR_IPV6; 560f325335cSAndrey V. Elsukov af = AF_INET6; 561f325335cSAndrey V. Elsukov break; 562f325335cSAndrey V. Elsukov default: 563f325335cSAndrey V. Elsukov goto drop; 564f325335cSAndrey V. Elsukov } 5656d8fdfa9SAndrey V. Elsukov m_adj(m, off + hlen); 566f325335cSAndrey V. Elsukov m_clrprotoflags(m); 567f325335cSAndrey V. Elsukov m->m_pkthdr.rcvif = ifp; 568eccfe69aSAndrey V. Elsukov M_SETFIB(m, ifp->if_fib); 569f325335cSAndrey V. Elsukov #ifdef MAC 570f325335cSAndrey V. Elsukov mac_ifnet_create_mbuf(ifp, m); 571f325335cSAndrey V. Elsukov #endif 572f325335cSAndrey V. Elsukov BPF_MTAP2(ifp, &af, sizeof(af), m); 573f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 574f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); 575f325335cSAndrey V. Elsukov if ((ifp->if_flags & IFF_MONITOR) != 0) 576f325335cSAndrey V. Elsukov m_freem(m); 5778e96e13eSMaxim Sobolev else 578f325335cSAndrey V. Elsukov netisr_dispatch(isr, m); 579f325335cSAndrey V. Elsukov return (IPPROTO_DONE); 580f325335cSAndrey V. Elsukov drop: 581f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 582f325335cSAndrey V. Elsukov m_freem(m); 583f325335cSAndrey V. Elsukov return (IPPROTO_DONE); 5848e96e13eSMaxim Sobolev } 5858e96e13eSMaxim Sobolev 586f325335cSAndrey V. Elsukov static int 587f325335cSAndrey V. Elsukov gre_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, 588f325335cSAndrey V. Elsukov struct route *ro) 589f325335cSAndrey V. Elsukov { 590f325335cSAndrey V. Elsukov uint32_t af; 591f325335cSAndrey V. Elsukov 592f325335cSAndrey V. Elsukov if (dst->sa_family == AF_UNSPEC) 593f325335cSAndrey V. Elsukov bcopy(dst->sa_data, &af, sizeof(af)); 594f325335cSAndrey V. Elsukov else 595f325335cSAndrey V. Elsukov af = dst->sa_family; 596a5185adeSAndrey V. Elsukov /* 597a5185adeSAndrey V. Elsukov * Now save the af in the inbound pkt csum data, this is a cheat since 598a5185adeSAndrey V. Elsukov * we are using the inbound csum_data field to carry the af over to 599a5185adeSAndrey V. Elsukov * the gre_transmit() routine, avoiding using yet another mtag. 600a5185adeSAndrey V. Elsukov */ 601a5185adeSAndrey V. Elsukov m->m_pkthdr.csum_data = af; 602f325335cSAndrey V. Elsukov return (ifp->if_transmit(ifp, m)); 603f325335cSAndrey V. Elsukov } 604f325335cSAndrey V. Elsukov 605f325335cSAndrey V. Elsukov static void 606f325335cSAndrey V. Elsukov gre_setseqn(struct grehdr *gh, uint32_t seq) 607f325335cSAndrey V. Elsukov { 608f325335cSAndrey V. Elsukov uint32_t *opts; 609f325335cSAndrey V. Elsukov uint16_t flags; 610f325335cSAndrey V. Elsukov 611f325335cSAndrey V. Elsukov opts = gh->gre_opts; 612f325335cSAndrey V. Elsukov flags = ntohs(gh->gre_flags); 613f325335cSAndrey V. Elsukov KASSERT((flags & GRE_FLAGS_SP) != 0, 614f325335cSAndrey V. Elsukov ("gre_setseqn called, but GRE_FLAGS_SP isn't set ")); 615f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_CP) 616f325335cSAndrey V. Elsukov opts++; 617f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_KP) 618f325335cSAndrey V. Elsukov opts++; 619f325335cSAndrey V. Elsukov *opts = htonl(seq); 620f325335cSAndrey V. Elsukov } 621f325335cSAndrey V. Elsukov 622aee793eeSAndrey V. Elsukov static uint32_t 623aee793eeSAndrey V. Elsukov gre_flowid(struct gre_softc *sc, struct mbuf *m, uint32_t af) 624aee793eeSAndrey V. Elsukov { 625aee793eeSAndrey V. Elsukov uint32_t flowid; 626aee793eeSAndrey V. Elsukov 627aee793eeSAndrey V. Elsukov if ((sc->gre_options & GRE_UDPENCAP) == 0 || sc->gre_port != 0) 628aee793eeSAndrey V. Elsukov return (0); 629aee793eeSAndrey V. Elsukov #ifndef RSS 630aee793eeSAndrey V. Elsukov switch (af) { 631aee793eeSAndrey V. Elsukov #ifdef INET 632aee793eeSAndrey V. Elsukov case AF_INET: 633aee793eeSAndrey V. Elsukov flowid = mtod(m, struct ip *)->ip_src.s_addr ^ 634aee793eeSAndrey V. Elsukov mtod(m, struct ip *)->ip_dst.s_addr; 635aee793eeSAndrey V. Elsukov break; 636aee793eeSAndrey V. Elsukov #endif 637aee793eeSAndrey V. Elsukov #ifdef INET6 638aee793eeSAndrey V. Elsukov case AF_INET6: 639aee793eeSAndrey V. Elsukov flowid = mtod(m, struct ip6_hdr *)->ip6_src.s6_addr32[3] ^ 640aee793eeSAndrey V. Elsukov mtod(m, struct ip6_hdr *)->ip6_dst.s6_addr32[3]; 641aee793eeSAndrey V. Elsukov break; 642aee793eeSAndrey V. Elsukov #endif 643aee793eeSAndrey V. Elsukov default: 644aee793eeSAndrey V. Elsukov flowid = 0; 645aee793eeSAndrey V. Elsukov } 646aee793eeSAndrey V. Elsukov #else /* RSS */ 647aee793eeSAndrey V. Elsukov switch (af) { 648aee793eeSAndrey V. Elsukov #ifdef INET 649aee793eeSAndrey V. Elsukov case AF_INET: 650aee793eeSAndrey V. Elsukov flowid = rss_hash_ip4_2tuple(mtod(m, struct ip *)->ip_src, 651aee793eeSAndrey V. Elsukov mtod(m, struct ip *)->ip_dst); 652aee793eeSAndrey V. Elsukov break; 653aee793eeSAndrey V. Elsukov #endif 654aee793eeSAndrey V. Elsukov #ifdef INET6 655aee793eeSAndrey V. Elsukov case AF_INET6: 656aee793eeSAndrey V. Elsukov flowid = rss_hash_ip6_2tuple( 657aee793eeSAndrey V. Elsukov &mtod(m, struct ip6_hdr *)->ip6_src, 658aee793eeSAndrey V. Elsukov &mtod(m, struct ip6_hdr *)->ip6_dst); 659aee793eeSAndrey V. Elsukov break; 660aee793eeSAndrey V. Elsukov #endif 661aee793eeSAndrey V. Elsukov default: 662aee793eeSAndrey V. Elsukov flowid = 0; 663aee793eeSAndrey V. Elsukov } 664aee793eeSAndrey V. Elsukov #endif 665aee793eeSAndrey V. Elsukov return (flowid); 666aee793eeSAndrey V. Elsukov } 667aee793eeSAndrey V. Elsukov 66898a8fdf6SAndrey V. Elsukov #define MTAG_GRE 1307983903 669f325335cSAndrey V. Elsukov static int 670f325335cSAndrey V. Elsukov gre_transmit(struct ifnet *ifp, struct mbuf *m) 671f325335cSAndrey V. Elsukov { 67219873f47SAndrey V. Elsukov GRE_RLOCK_TRACKER; 673f325335cSAndrey V. Elsukov struct gre_softc *sc; 674f325335cSAndrey V. Elsukov struct grehdr *gh; 675aee793eeSAndrey V. Elsukov struct udphdr *uh; 676aee793eeSAndrey V. Elsukov uint32_t af, flowid; 677a5185adeSAndrey V. Elsukov int error, len; 678a5185adeSAndrey V. Elsukov uint16_t proto; 679f325335cSAndrey V. Elsukov 680a5185adeSAndrey V. Elsukov len = 0; 6810a27163fSGleb Smirnoff GRE_RLOCK(); 682a5185adeSAndrey V. Elsukov #ifdef MAC 683a5185adeSAndrey V. Elsukov error = mac_ifnet_check_transmit(ifp, m); 684a5185adeSAndrey V. Elsukov if (error) { 685a5185adeSAndrey V. Elsukov m_freem(m); 686a5185adeSAndrey V. Elsukov goto drop; 687a5185adeSAndrey V. Elsukov } 688a5185adeSAndrey V. Elsukov #endif 689a5185adeSAndrey V. Elsukov error = ENETDOWN; 690f325335cSAndrey V. Elsukov sc = ifp->if_softc; 691a5185adeSAndrey V. Elsukov if ((ifp->if_flags & IFF_MONITOR) != 0 || 692a5185adeSAndrey V. Elsukov (ifp->if_flags & IFF_UP) == 0 || 69319873f47SAndrey V. Elsukov (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || 694a5185adeSAndrey V. Elsukov sc->gre_family == 0 || 69598a8fdf6SAndrey V. Elsukov (error = if_tunnel_check_nesting(ifp, m, MTAG_GRE, 69698a8fdf6SAndrey V. Elsukov V_max_gre_nesting)) != 0) { 697f325335cSAndrey V. Elsukov m_freem(m); 698f325335cSAndrey V. Elsukov goto drop; 699f325335cSAndrey V. Elsukov } 700a5185adeSAndrey V. Elsukov af = m->m_pkthdr.csum_data; 701c6851ad0SAndrey V. Elsukov BPF_MTAP2(ifp, &af, sizeof(af), m); 702c6851ad0SAndrey V. Elsukov m->m_flags &= ~(M_BCAST|M_MCAST); 703aee793eeSAndrey V. Elsukov flowid = gre_flowid(sc, m, af); 704f325335cSAndrey V. Elsukov M_SETFIB(m, sc->gre_fibnum); 705a5185adeSAndrey V. Elsukov M_PREPEND(m, sc->gre_hlen, M_NOWAIT); 706f325335cSAndrey V. Elsukov if (m == NULL) { 707f325335cSAndrey V. Elsukov error = ENOBUFS; 708f325335cSAndrey V. Elsukov goto drop; 709f325335cSAndrey V. Elsukov } 710a5185adeSAndrey V. Elsukov bcopy(sc->gre_hdr, mtod(m, void *), sc->gre_hlen); 711a5185adeSAndrey V. Elsukov /* Determine GRE proto */ 712a5185adeSAndrey V. Elsukov switch (af) { 713f325335cSAndrey V. Elsukov #ifdef INET 714f325335cSAndrey V. Elsukov case AF_INET: 715a5185adeSAndrey V. Elsukov proto = htons(ETHERTYPE_IP); 716f325335cSAndrey V. Elsukov break; 717f325335cSAndrey V. Elsukov #endif 718f325335cSAndrey V. Elsukov #ifdef INET6 719f325335cSAndrey V. Elsukov case AF_INET6: 720a5185adeSAndrey V. Elsukov proto = htons(ETHERTYPE_IPV6); 721f325335cSAndrey V. Elsukov break; 722f325335cSAndrey V. Elsukov #endif 723f325335cSAndrey V. Elsukov default: 724a5185adeSAndrey V. Elsukov m_freem(m); 725f325335cSAndrey V. Elsukov error = ENETDOWN; 726f325335cSAndrey V. Elsukov goto drop; 727f325335cSAndrey V. Elsukov } 728a5185adeSAndrey V. Elsukov /* Determine offset of GRE header */ 729a5185adeSAndrey V. Elsukov switch (sc->gre_family) { 730f325335cSAndrey V. Elsukov #ifdef INET 731f325335cSAndrey V. Elsukov case AF_INET: 732a5185adeSAndrey V. Elsukov len = sizeof(struct ip); 733f325335cSAndrey V. Elsukov break; 734f325335cSAndrey V. Elsukov #endif 735f325335cSAndrey V. Elsukov #ifdef INET6 736f325335cSAndrey V. Elsukov case AF_INET6: 737a5185adeSAndrey V. Elsukov len = sizeof(struct ip6_hdr); 738f325335cSAndrey V. Elsukov break; 739f325335cSAndrey V. Elsukov #endif 740f325335cSAndrey V. Elsukov default: 741a5185adeSAndrey V. Elsukov m_freem(m); 742f325335cSAndrey V. Elsukov error = ENETDOWN; 743f325335cSAndrey V. Elsukov goto drop; 744f325335cSAndrey V. Elsukov } 745aee793eeSAndrey V. Elsukov if (sc->gre_options & GRE_UDPENCAP) { 746aee793eeSAndrey V. Elsukov uh = (struct udphdr *)mtodo(m, len); 747aee793eeSAndrey V. Elsukov uh->uh_sport |= htons(V_ipport_hifirstauto) | 748aee793eeSAndrey V. Elsukov (flowid >> 16) | (flowid & 0xFFFF); 749aee793eeSAndrey V. Elsukov uh->uh_sport = htons(ntohs(uh->uh_sport) % 750aee793eeSAndrey V. Elsukov V_ipport_hilastauto); 751aee793eeSAndrey V. Elsukov uh->uh_ulen = htons(m->m_pkthdr.len - len); 752aee793eeSAndrey V. Elsukov uh->uh_sum = gre_cksum_add(uh->uh_sum, 753aee793eeSAndrey V. Elsukov htons(m->m_pkthdr.len - len + IPPROTO_UDP)); 754aee793eeSAndrey V. Elsukov m->m_pkthdr.csum_flags = sc->gre_csumflags; 755aee793eeSAndrey V. Elsukov m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); 756aee793eeSAndrey V. Elsukov len += sizeof(struct udphdr); 757aee793eeSAndrey V. Elsukov } 758a5185adeSAndrey V. Elsukov gh = (struct grehdr *)mtodo(m, len); 759a5185adeSAndrey V. Elsukov gh->gre_proto = proto; 760a5185adeSAndrey V. Elsukov if (sc->gre_options & GRE_ENABLE_SEQ) 761a5185adeSAndrey V. Elsukov gre_setseqn(gh, sc->gre_oseq++); 762a5185adeSAndrey V. Elsukov if (sc->gre_options & GRE_ENABLE_CSUM) { 763f325335cSAndrey V. Elsukov *(uint16_t *)gh->gre_opts = in_cksum_skip(m, 764a5185adeSAndrey V. Elsukov m->m_pkthdr.len, len); 765f325335cSAndrey V. Elsukov } 766a5185adeSAndrey V. Elsukov len = m->m_pkthdr.len - len; 767a5185adeSAndrey V. Elsukov switch (sc->gre_family) { 768f325335cSAndrey V. Elsukov #ifdef INET 769f325335cSAndrey V. Elsukov case AF_INET: 770a5185adeSAndrey V. Elsukov error = in_gre_output(m, af, sc->gre_hlen); 771f325335cSAndrey V. Elsukov break; 772f325335cSAndrey V. Elsukov #endif 773f325335cSAndrey V. Elsukov #ifdef INET6 774f325335cSAndrey V. Elsukov case AF_INET6: 775aee793eeSAndrey V. Elsukov error = in6_gre_output(m, af, sc->gre_hlen, flowid); 776f325335cSAndrey V. Elsukov break; 777f325335cSAndrey V. Elsukov #endif 778f325335cSAndrey V. Elsukov default: 779f325335cSAndrey V. Elsukov m_freem(m); 780f325335cSAndrey V. Elsukov error = ENETDOWN; 78174b8d63dSPedro F. Giffuni } 782f325335cSAndrey V. Elsukov drop: 783f325335cSAndrey V. Elsukov if (error) 784f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 785f325335cSAndrey V. Elsukov else { 786f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 787a5185adeSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OBYTES, len); 788f325335cSAndrey V. Elsukov } 789a5185adeSAndrey V. Elsukov GRE_RUNLOCK(); 790f325335cSAndrey V. Elsukov return (error); 791f325335cSAndrey V. Elsukov } 792f325335cSAndrey V. Elsukov 793f325335cSAndrey V. Elsukov static void 794f325335cSAndrey V. Elsukov gre_qflush(struct ifnet *ifp __unused) 795f325335cSAndrey V. Elsukov { 796f325335cSAndrey V. Elsukov 7978e96e13eSMaxim Sobolev } 7988e96e13eSMaxim Sobolev 7998e96e13eSMaxim Sobolev static int 8008e96e13eSMaxim Sobolev gremodevent(module_t mod, int type, void *data) 8018e96e13eSMaxim Sobolev { 8028e96e13eSMaxim Sobolev 8038e96e13eSMaxim Sobolev switch (type) { 8048e96e13eSMaxim Sobolev case MOD_LOAD: 8058e96e13eSMaxim Sobolev case MOD_UNLOAD: 8068e96e13eSMaxim Sobolev break; 8073e019deaSPoul-Henning Kamp default: 80889c58b73SHiroki Sato return (EOPNOTSUPP); 8098e96e13eSMaxim Sobolev } 81089c58b73SHiroki Sato return (0); 8118e96e13eSMaxim Sobolev } 8128e96e13eSMaxim Sobolev 8138e96e13eSMaxim Sobolev static moduledata_t gre_mod = { 8148e96e13eSMaxim Sobolev "if_gre", 8158e96e13eSMaxim Sobolev gremodevent, 8169823d527SKevin Lo 0 8178e96e13eSMaxim Sobolev }; 8188e96e13eSMaxim Sobolev 8198e96e13eSMaxim Sobolev DECLARE_MODULE(if_gre, gre_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 8208e96e13eSMaxim Sobolev MODULE_VERSION(if_gre, 1); 821