1c398230bSWarner Losh /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 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> 388e96e13eSMaxim Sobolev #include "opt_inet.h" 39f16770aeSBruce M Simpson #include "opt_inet6.h" 40aee793eeSAndrey V. Elsukov #include "opt_rss.h" 418e96e13eSMaxim Sobolev 428e96e13eSMaxim Sobolev #include <sys/param.h> 438e96e13eSMaxim Sobolev #include <sys/kernel.h> 44f325335cSAndrey V. Elsukov #include <sys/lock.h> 458e96e13eSMaxim Sobolev #include <sys/malloc.h> 465dba30f1SPoul-Henning Kamp #include <sys/module.h> 478e96e13eSMaxim Sobolev #include <sys/mbuf.h> 48acd3428bSRobert Watson #include <sys/priv.h> 498b07e49aSJulian Elischer #include <sys/proc.h> 508e96e13eSMaxim Sobolev #include <sys/socket.h> 51aee793eeSAndrey V. Elsukov #include <sys/socketvar.h> 528e96e13eSMaxim Sobolev #include <sys/sockio.h> 53f325335cSAndrey V. Elsukov #include <sys/sx.h> 548e96e13eSMaxim Sobolev #include <sys/sysctl.h> 55f325335cSAndrey V. Elsukov #include <sys/syslog.h> 561b861caaSBruce Evans #include <sys/systm.h> 578e96e13eSMaxim Sobolev 588e96e13eSMaxim Sobolev #include <net/ethernet.h> 598e96e13eSMaxim Sobolev #include <net/if.h> 6076039bc8SGleb Smirnoff #include <net/if_var.h> 612c2b37adSJustin Hibbits #include <net/if_private.h> 62f889d2efSBrooks Davis #include <net/if_clone.h> 638e96e13eSMaxim Sobolev #include <net/if_types.h> 64f325335cSAndrey V. Elsukov #include <net/netisr.h> 65530c0060SRobert Watson #include <net/vnet.h> 66eccfe69aSAndrey V. Elsukov #include <net/route.h> 678e96e13eSMaxim Sobolev 688e96e13eSMaxim Sobolev #include <netinet/in.h> 69aee793eeSAndrey V. Elsukov #include <netinet/in_pcb.h> 70f325335cSAndrey V. Elsukov #ifdef INET 718e96e13eSMaxim Sobolev #include <netinet/in_var.h> 728e96e13eSMaxim Sobolev #include <netinet/ip.h> 738e96e13eSMaxim Sobolev #include <netinet/ip_var.h> 74aee793eeSAndrey V. Elsukov #ifdef RSS 75aee793eeSAndrey V. Elsukov #include <netinet/in_rss.h> 76aee793eeSAndrey V. Elsukov #endif 778e96e13eSMaxim Sobolev #endif 788e96e13eSMaxim Sobolev 79f325335cSAndrey V. Elsukov #ifdef INET6 80f325335cSAndrey V. Elsukov #include <netinet/ip6.h> 81f325335cSAndrey V. Elsukov #include <netinet6/in6_var.h> 82f325335cSAndrey V. Elsukov #include <netinet6/ip6_var.h> 83aee793eeSAndrey V. Elsukov #ifdef RSS 84aee793eeSAndrey V. Elsukov #include <netinet6/in6_rss.h> 85aee793eeSAndrey V. Elsukov #endif 86f325335cSAndrey V. Elsukov #endif 878e96e13eSMaxim Sobolev 88f325335cSAndrey V. Elsukov #include <netinet/ip_encap.h> 89aee793eeSAndrey V. Elsukov #include <netinet/udp.h> 90f325335cSAndrey V. Elsukov #include <net/bpf.h> 918e96e13eSMaxim Sobolev #include <net/if_gre.h> 928e96e13eSMaxim Sobolev 93f325335cSAndrey V. Elsukov #include <machine/in_cksum.h> 94f325335cSAndrey V. Elsukov #include <security/mac/mac_framework.h> 958e96e13eSMaxim Sobolev 96c00bf730SAndrey V. Elsukov #define GREMTU 1476 97f325335cSAndrey V. Elsukov 98a5185adeSAndrey V. Elsukov static const char grename[] = "gre"; 99a5185adeSAndrey V. Elsukov MALLOC_DEFINE(M_GRE, grename, "Generic Routing Encapsulation"); 100a5185adeSAndrey V. Elsukov 101f325335cSAndrey V. Elsukov static struct sx gre_ioctl_sx; 102f325335cSAndrey V. Elsukov SX_SYSINIT(gre_ioctl_sx, &gre_ioctl_sx, "gre_ioctl"); 1038e96e13eSMaxim Sobolev 1046b7330e2SSam Leffler static int gre_clone_create(struct if_clone *, int, caddr_t); 1059ee35470SAlfred Perlstein static void gre_clone_destroy(struct ifnet *); 1065f901c92SAndrew Turner VNET_DEFINE_STATIC(struct if_clone *, gre_cloner); 10789c58b73SHiroki Sato #define V_gre_cloner VNET(gre_cloner) 10842a58907SGleb Smirnoff 109dd4490fdSAndrey V. Elsukov #ifdef VIMAGE 110dd4490fdSAndrey V. Elsukov static void gre_reassign(struct ifnet *, struct vnet *, char *); 111dd4490fdSAndrey V. Elsukov #endif 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 *); 117a5185adeSAndrey V. Elsukov static void gre_delete_tunnel(struct gre_softc *); 1188e96e13eSMaxim Sobolev 1198e96e13eSMaxim Sobolev SYSCTL_DECL(_net_link); 1207029da5cSPawel Biernacki static SYSCTL_NODE(_net_link, IFT_TUNNEL, gre, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 1218e96e13eSMaxim Sobolev "Generic Routing Encapsulation"); 1228e96e13eSMaxim Sobolev #ifndef MAX_GRE_NEST 1238e96e13eSMaxim Sobolev /* 1248e96e13eSMaxim Sobolev * This macro controls the default upper limitation on nesting of gre tunnels. 1258e96e13eSMaxim Sobolev * Since, setting a large value to this macro with a careless configuration 1268e96e13eSMaxim Sobolev * may introduce system crash, we don't allow any nestings by default. 1278e96e13eSMaxim Sobolev * If you need to configure nested gre tunnels, you can define this macro 1288e96e13eSMaxim Sobolev * in your kernel configuration file. However, if you do so, please be 1298e96e13eSMaxim Sobolev * careful to configure the tunnels so that it won't make a loop. 1308e96e13eSMaxim Sobolev */ 1318e96e13eSMaxim Sobolev #define MAX_GRE_NEST 1 1328e96e13eSMaxim Sobolev #endif 133f325335cSAndrey V. Elsukov 1345f901c92SAndrew Turner VNET_DEFINE_STATIC(int, max_gre_nesting) = MAX_GRE_NEST; 13589c58b73SHiroki Sato #define V_max_gre_nesting VNET(max_gre_nesting) 13689c58b73SHiroki Sato SYSCTL_INT(_net_link_gre, OID_AUTO, max_nesting, CTLFLAG_RW | CTLFLAG_VNET, 13789c58b73SHiroki Sato &VNET_NAME(max_gre_nesting), 0, "Max nested tunnels"); 1388e96e13eSMaxim Sobolev 139c23d234cSMaxim Sobolev static void 14089c58b73SHiroki Sato vnet_gre_init(const void *unused __unused) 1418e96e13eSMaxim Sobolev { 142a5185adeSAndrey V. Elsukov 14389c58b73SHiroki Sato V_gre_cloner = if_clone_simple(grename, gre_clone_create, 14442a58907SGleb Smirnoff gre_clone_destroy, 0); 145a5185adeSAndrey V. Elsukov #ifdef INET 146a5185adeSAndrey V. Elsukov in_gre_init(); 147a5185adeSAndrey V. Elsukov #endif 148a5185adeSAndrey V. Elsukov #ifdef INET6 149a5185adeSAndrey V. Elsukov in6_gre_init(); 150a5185adeSAndrey V. Elsukov #endif 1518e96e13eSMaxim Sobolev } 15289c58b73SHiroki Sato VNET_SYSINIT(vnet_gre_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 15389c58b73SHiroki Sato vnet_gre_init, NULL); 15489c58b73SHiroki Sato 15589c58b73SHiroki Sato static void 15689c58b73SHiroki Sato vnet_gre_uninit(const void *unused __unused) 15789c58b73SHiroki Sato { 15889c58b73SHiroki Sato 15989c58b73SHiroki Sato if_clone_detach(V_gre_cloner); 160a5185adeSAndrey V. Elsukov #ifdef INET 161a5185adeSAndrey V. Elsukov in_gre_uninit(); 162a5185adeSAndrey V. Elsukov #endif 163a5185adeSAndrey V. Elsukov #ifdef INET6 164a5185adeSAndrey V. Elsukov in6_gre_uninit(); 165a5185adeSAndrey V. Elsukov #endif 166aee793eeSAndrey V. Elsukov /* XXX: epoch_call drain */ 16789c58b73SHiroki Sato } 16889c58b73SHiroki Sato VNET_SYSUNINIT(vnet_gre_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 16989c58b73SHiroki Sato vnet_gre_uninit, NULL); 1708e96e13eSMaxim Sobolev 171c23d234cSMaxim Sobolev static int 17289c58b73SHiroki Sato gre_clone_create(struct if_clone *ifc, int unit, caddr_t params) 1738e96e13eSMaxim Sobolev { 1748e96e13eSMaxim Sobolev struct gre_softc *sc; 1758e96e13eSMaxim Sobolev 176b3c9a01eSBruce M Simpson sc = malloc(sizeof(struct gre_softc), M_GRE, M_WAITOK | M_ZERO); 177f325335cSAndrey V. Elsukov sc->gre_fibnum = curthread->td_proc->p_fibnum; 178066b192eSBjoern A. Zeeb GRE2IFP(sc) = if_alloc(IFT_TUNNEL); 179fc74a9f9SBrooks Davis GRE2IFP(sc)->if_softc = sc; 18042a58907SGleb Smirnoff if_initname(GRE2IFP(sc), grename, unit); 181066b192eSBjoern A. Zeeb 182c00bf730SAndrey V. Elsukov GRE2IFP(sc)->if_mtu = GREMTU; 183fc74a9f9SBrooks Davis GRE2IFP(sc)->if_flags = IFF_POINTOPOINT|IFF_MULTICAST; 184fc74a9f9SBrooks Davis GRE2IFP(sc)->if_output = gre_output; 185fc74a9f9SBrooks Davis GRE2IFP(sc)->if_ioctl = gre_ioctl; 186f325335cSAndrey V. Elsukov GRE2IFP(sc)->if_transmit = gre_transmit; 187f325335cSAndrey V. Elsukov GRE2IFP(sc)->if_qflush = gre_qflush; 188dd4490fdSAndrey V. Elsukov #ifdef VIMAGE 189dd4490fdSAndrey V. Elsukov GRE2IFP(sc)->if_reassign = gre_reassign; 190dd4490fdSAndrey V. Elsukov #endif 191f1aaad0cSHiroki Sato GRE2IFP(sc)->if_capabilities |= IFCAP_LINKSTATE; 192f1aaad0cSHiroki Sato GRE2IFP(sc)->if_capenable |= IFCAP_LINKSTATE; 193fc74a9f9SBrooks Davis if_attach(GRE2IFP(sc)); 194fc74a9f9SBrooks Davis bpfattach(GRE2IFP(sc), DLT_NULL, sizeof(u_int32_t)); 1958e96e13eSMaxim Sobolev return (0); 1968e96e13eSMaxim Sobolev } 1978e96e13eSMaxim Sobolev 198dd4490fdSAndrey V. Elsukov #ifdef VIMAGE 199dd4490fdSAndrey V. Elsukov static void 200dd4490fdSAndrey V. Elsukov gre_reassign(struct ifnet *ifp, struct vnet *new_vnet __unused, 201dd4490fdSAndrey V. Elsukov char *unused __unused) 202dd4490fdSAndrey V. Elsukov { 203dd4490fdSAndrey V. Elsukov struct gre_softc *sc; 204dd4490fdSAndrey V. Elsukov 205dd4490fdSAndrey V. Elsukov sx_xlock(&gre_ioctl_sx); 206dd4490fdSAndrey V. Elsukov sc = ifp->if_softc; 207dd4490fdSAndrey V. Elsukov if (sc != NULL) 208dd4490fdSAndrey V. Elsukov gre_delete_tunnel(sc); 209dd4490fdSAndrey V. Elsukov sx_xunlock(&gre_ioctl_sx); 210dd4490fdSAndrey V. Elsukov } 211dd4490fdSAndrey V. Elsukov #endif /* VIMAGE */ 212dd4490fdSAndrey V. Elsukov 213c23d234cSMaxim Sobolev static void 21489c58b73SHiroki Sato gre_clone_destroy(struct ifnet *ifp) 2158e96e13eSMaxim Sobolev { 216f325335cSAndrey V. Elsukov struct gre_softc *sc; 2178e96e13eSMaxim Sobolev 218f325335cSAndrey V. Elsukov sx_xlock(&gre_ioctl_sx); 219f325335cSAndrey V. Elsukov sc = ifp->if_softc; 220a5185adeSAndrey V. Elsukov gre_delete_tunnel(sc); 221febd0759SAndrew Thompson bpfdetach(ifp); 222febd0759SAndrew Thompson if_detach(ifp); 223f325335cSAndrey V. Elsukov ifp->if_softc = NULL; 224f325335cSAndrey V. Elsukov sx_xunlock(&gre_ioctl_sx); 225f325335cSAndrey V. Elsukov 226a5185adeSAndrey V. Elsukov GRE_WAIT(); 227febd0759SAndrew Thompson if_free(ifp); 228febd0759SAndrew Thompson free(sc, M_GRE); 2298e96e13eSMaxim Sobolev } 2308e96e13eSMaxim Sobolev 231c23d234cSMaxim Sobolev static int 2328e96e13eSMaxim Sobolev gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 2338e96e13eSMaxim Sobolev { 2348e96e13eSMaxim Sobolev struct ifreq *ifr = (struct ifreq *)data; 235f325335cSAndrey V. Elsukov struct gre_softc *sc; 236f325335cSAndrey V. Elsukov uint32_t opt; 237f325335cSAndrey V. Elsukov int error; 2388e96e13eSMaxim Sobolev 2398e96e13eSMaxim Sobolev switch (cmd) { 240f325335cSAndrey V. Elsukov case SIOCSIFMTU: 241f325335cSAndrey V. Elsukov /* XXX: */ 242f325335cSAndrey V. Elsukov if (ifr->ifr_mtu < 576) 243f325335cSAndrey V. Elsukov return (EINVAL); 244c00bf730SAndrey V. Elsukov ifp->if_mtu = ifr->ifr_mtu; 245c00bf730SAndrey V. Elsukov return (0); 2468e96e13eSMaxim Sobolev case SIOCSIFADDR: 2478e96e13eSMaxim Sobolev ifp->if_flags |= IFF_UP; 2488e96e13eSMaxim Sobolev case SIOCSIFFLAGS: 2498e96e13eSMaxim Sobolev case SIOCADDMULTI: 2508e96e13eSMaxim Sobolev case SIOCDELMULTI: 251f325335cSAndrey V. Elsukov return (0); 2528e96e13eSMaxim Sobolev case GRESADDRS: 2538e96e13eSMaxim Sobolev case GRESADDRD: 2548e96e13eSMaxim Sobolev case GREGADDRS: 2558e96e13eSMaxim Sobolev case GREGADDRD: 256f325335cSAndrey V. Elsukov case GRESPROTO: 257f325335cSAndrey V. Elsukov case GREGPROTO: 258f325335cSAndrey V. Elsukov return (EOPNOTSUPP); 259f325335cSAndrey V. Elsukov } 260f325335cSAndrey V. Elsukov sx_xlock(&gre_ioctl_sx); 261f325335cSAndrey V. Elsukov sc = ifp->if_softc; 262f325335cSAndrey V. Elsukov if (sc == NULL) { 263f325335cSAndrey V. Elsukov error = ENXIO; 264f325335cSAndrey V. Elsukov goto end; 265f325335cSAndrey V. Elsukov } 266f325335cSAndrey V. Elsukov error = 0; 267f325335cSAndrey V. Elsukov switch (cmd) { 268f325335cSAndrey V. Elsukov case SIOCDIFPHYADDR: 269a5185adeSAndrey V. Elsukov if (sc->gre_family == 0) 2708e96e13eSMaxim Sobolev break; 271a5185adeSAndrey V. Elsukov gre_delete_tunnel(sc); 2728e96e13eSMaxim Sobolev break; 273f325335cSAndrey V. Elsukov #ifdef INET 274a5185adeSAndrey V. Elsukov case SIOCSIFPHYADDR: 275f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR: 276f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR: 277a5185adeSAndrey V. Elsukov error = in_gre_ioctl(sc, cmd, data); 278f325335cSAndrey V. Elsukov break; 279f325335cSAndrey V. Elsukov #endif 280f325335cSAndrey V. Elsukov #ifdef INET6 281a5185adeSAndrey V. Elsukov case SIOCSIFPHYADDR_IN6: 282f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR_IN6: 283f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR_IN6: 284a5185adeSAndrey V. Elsukov error = in6_gre_ioctl(sc, cmd, data); 285f325335cSAndrey V. Elsukov break; 286f325335cSAndrey V. Elsukov #endif 287eccfe69aSAndrey V. Elsukov case SIOCGTUNFIB: 288eccfe69aSAndrey V. Elsukov ifr->ifr_fib = sc->gre_fibnum; 289eccfe69aSAndrey V. Elsukov break; 290eccfe69aSAndrey V. Elsukov case SIOCSTUNFIB: 291eccfe69aSAndrey V. Elsukov if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0) 292eccfe69aSAndrey V. Elsukov break; 293eccfe69aSAndrey V. Elsukov if (ifr->ifr_fib >= rt_numfibs) 294eccfe69aSAndrey V. Elsukov error = EINVAL; 295eccfe69aSAndrey V. Elsukov else 296eccfe69aSAndrey V. Elsukov sc->gre_fibnum = ifr->ifr_fib; 297eccfe69aSAndrey V. Elsukov break; 298f325335cSAndrey V. Elsukov case GRESKEY: 299f325335cSAndrey V. Elsukov case GRESOPTS: 300aee793eeSAndrey V. Elsukov case GRESPORT: 301f325335cSAndrey V. Elsukov if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0) 302f325335cSAndrey V. Elsukov break; 303541d96aaSBrooks Davis if ((error = copyin(ifr_data_get_ptr(ifr), &opt, 304541d96aaSBrooks Davis sizeof(opt))) != 0) 305f325335cSAndrey V. Elsukov break; 306a5185adeSAndrey V. Elsukov if (cmd == GRESKEY) { 307a5185adeSAndrey V. Elsukov if (sc->gre_key == opt) 308131c55bcSAndrew Thompson break; 309a5185adeSAndrey V. Elsukov } else if (cmd == GRESOPTS) { 310a5185adeSAndrey V. Elsukov if (opt & ~GRE_OPTMASK) { 311a5185adeSAndrey V. Elsukov error = EINVAL; 312a5185adeSAndrey V. Elsukov break; 313a5185adeSAndrey V. Elsukov } 314a5185adeSAndrey V. Elsukov if (sc->gre_options == opt) 315a5185adeSAndrey V. Elsukov break; 316aee793eeSAndrey V. Elsukov } else if (cmd == GRESPORT) { 317aee793eeSAndrey V. Elsukov if (opt != 0 && (opt < V_ipport_hifirstauto || 318aee793eeSAndrey V. Elsukov opt > V_ipport_hilastauto)) { 319aee793eeSAndrey V. Elsukov error = EINVAL; 320aee793eeSAndrey V. Elsukov break; 321aee793eeSAndrey V. Elsukov } 322aee793eeSAndrey V. Elsukov if (sc->gre_port == opt) 323aee793eeSAndrey V. Elsukov break; 324aee793eeSAndrey V. Elsukov if ((sc->gre_options & GRE_UDPENCAP) == 0) { 325aee793eeSAndrey V. Elsukov /* 326aee793eeSAndrey V. Elsukov * UDP encapsulation is not enabled, thus 327aee793eeSAndrey V. Elsukov * there is no need to reattach softc. 328aee793eeSAndrey V. Elsukov */ 329aee793eeSAndrey V. Elsukov sc->gre_port = opt; 330aee793eeSAndrey V. Elsukov break; 331aee793eeSAndrey V. Elsukov } 332a5185adeSAndrey V. Elsukov } 333a5185adeSAndrey V. Elsukov switch (sc->gre_family) { 334a5185adeSAndrey V. Elsukov #ifdef INET 335a5185adeSAndrey V. Elsukov case AF_INET: 336aee793eeSAndrey V. Elsukov error = in_gre_setopts(sc, cmd, opt); 337a5185adeSAndrey V. Elsukov break; 338a5185adeSAndrey V. Elsukov #endif 339a5185adeSAndrey V. Elsukov #ifdef INET6 340a5185adeSAndrey V. Elsukov case AF_INET6: 341aee793eeSAndrey V. Elsukov error = in6_gre_setopts(sc, cmd, opt); 342a5185adeSAndrey V. Elsukov break; 343a5185adeSAndrey V. Elsukov #endif 344a5185adeSAndrey V. Elsukov default: 345aee793eeSAndrey V. Elsukov /* 346aee793eeSAndrey V. Elsukov * Tunnel is not yet configured. 347aee793eeSAndrey V. Elsukov * We can just change any parameters. 348aee793eeSAndrey V. Elsukov */ 349a5185adeSAndrey V. Elsukov if (cmd == GRESKEY) 350a5185adeSAndrey V. Elsukov sc->gre_key = opt; 351aee793eeSAndrey V. Elsukov if (cmd == GRESOPTS) 352a5185adeSAndrey V. Elsukov sc->gre_options = opt; 353aee793eeSAndrey V. Elsukov if (cmd == GRESPORT) 354aee793eeSAndrey V. Elsukov sc->gre_port = opt; 355a5185adeSAndrey V. Elsukov break; 356a5185adeSAndrey V. Elsukov } 357a5185adeSAndrey V. Elsukov /* 358a5185adeSAndrey V. Elsukov * XXX: Do we need to initiate change of interface 359a5185adeSAndrey V. Elsukov * state here? 360a5185adeSAndrey V. Elsukov */ 361a5185adeSAndrey V. Elsukov break; 362a5185adeSAndrey V. Elsukov case GREGKEY: 363a5185adeSAndrey V. Elsukov error = copyout(&sc->gre_key, ifr_data_get_ptr(ifr), 364a5185adeSAndrey V. Elsukov sizeof(sc->gre_key)); 365a5185adeSAndrey V. Elsukov break; 366f325335cSAndrey V. Elsukov case GREGOPTS: 367541d96aaSBrooks Davis error = copyout(&sc->gre_options, ifr_data_get_ptr(ifr), 368f325335cSAndrey V. Elsukov sizeof(sc->gre_options)); 369f325335cSAndrey V. Elsukov break; 370aee793eeSAndrey V. Elsukov case GREGPORT: 371aee793eeSAndrey V. Elsukov error = copyout(&sc->gre_port, ifr_data_get_ptr(ifr), 372aee793eeSAndrey V. Elsukov sizeof(sc->gre_port)); 373aee793eeSAndrey V. Elsukov break; 3748e96e13eSMaxim Sobolev default: 3758e96e13eSMaxim Sobolev error = EINVAL; 3768e96e13eSMaxim Sobolev break; 3778e96e13eSMaxim Sobolev } 378a5185adeSAndrey V. Elsukov if (error == 0 && sc->gre_family != 0) { 379a5185adeSAndrey V. Elsukov if ( 380a5185adeSAndrey V. Elsukov #ifdef INET 381a5185adeSAndrey V. Elsukov cmd == SIOCSIFPHYADDR || 382a5185adeSAndrey V. Elsukov #endif 383a5185adeSAndrey V. Elsukov #ifdef INET6 384a5185adeSAndrey V. Elsukov cmd == SIOCSIFPHYADDR_IN6 || 385a5185adeSAndrey V. Elsukov #endif 386a5185adeSAndrey V. Elsukov 0) { 387a5185adeSAndrey V. Elsukov if_link_state_change(ifp, LINK_STATE_UP); 388a5185adeSAndrey V. Elsukov } 389a5185adeSAndrey V. Elsukov } 390f325335cSAndrey V. Elsukov end: 391f325335cSAndrey V. Elsukov sx_xunlock(&gre_ioctl_sx); 3928e96e13eSMaxim Sobolev return (error); 3938e96e13eSMaxim Sobolev } 3948e96e13eSMaxim Sobolev 395f325335cSAndrey V. Elsukov static void 396a5185adeSAndrey V. Elsukov gre_delete_tunnel(struct gre_softc *sc) 397f325335cSAndrey V. Elsukov { 398aee793eeSAndrey V. Elsukov struct gre_socket *gs; 399a5185adeSAndrey V. Elsukov 400a5185adeSAndrey V. Elsukov sx_assert(&gre_ioctl_sx, SA_XLOCKED); 401a5185adeSAndrey V. Elsukov if (sc->gre_family != 0) { 402a5185adeSAndrey V. Elsukov CK_LIST_REMOVE(sc, chain); 40319873f47SAndrey V. Elsukov CK_LIST_REMOVE(sc, srchash); 404a5185adeSAndrey V. Elsukov GRE_WAIT(); 405a5185adeSAndrey V. Elsukov free(sc->gre_hdr, M_GRE); 406a5185adeSAndrey V. Elsukov sc->gre_family = 0; 407a5185adeSAndrey V. Elsukov } 408aee793eeSAndrey V. Elsukov /* 409aee793eeSAndrey V. Elsukov * If this Tunnel was the last one that could use UDP socket, 410aee793eeSAndrey V. Elsukov * we should unlink socket from hash table and close it. 411aee793eeSAndrey V. Elsukov */ 412aee793eeSAndrey V. Elsukov if ((gs = sc->gre_so) != NULL && CK_LIST_EMPTY(&gs->list)) { 413aee793eeSAndrey V. Elsukov CK_LIST_REMOVE(gs, chain); 414aee793eeSAndrey V. Elsukov soclose(gs->so); 4152a4bd982SGleb Smirnoff NET_EPOCH_CALL(gre_sofree, &gs->epoch_ctx); 416aee793eeSAndrey V. Elsukov sc->gre_so = NULL; 417aee793eeSAndrey V. Elsukov } 418a5185adeSAndrey V. Elsukov GRE2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 419a5185adeSAndrey V. Elsukov if_link_state_change(GRE2IFP(sc), LINK_STATE_DOWN); 420a5185adeSAndrey V. Elsukov } 421a5185adeSAndrey V. Elsukov 422a5185adeSAndrey V. Elsukov struct gre_list * 423a5185adeSAndrey V. Elsukov gre_hashinit(void) 424a5185adeSAndrey V. Elsukov { 425a5185adeSAndrey V. Elsukov struct gre_list *hash; 426a5185adeSAndrey V. Elsukov int i; 427a5185adeSAndrey V. Elsukov 428a5185adeSAndrey V. Elsukov hash = malloc(sizeof(struct gre_list) * GRE_HASH_SIZE, 429a5185adeSAndrey V. Elsukov M_GRE, M_WAITOK); 430a5185adeSAndrey V. Elsukov for (i = 0; i < GRE_HASH_SIZE; i++) 431a5185adeSAndrey V. Elsukov CK_LIST_INIT(&hash[i]); 432a5185adeSAndrey V. Elsukov 433a5185adeSAndrey V. Elsukov return (hash); 434a5185adeSAndrey V. Elsukov } 435a5185adeSAndrey V. Elsukov 436a5185adeSAndrey V. Elsukov void 437a5185adeSAndrey V. Elsukov gre_hashdestroy(struct gre_list *hash) 438a5185adeSAndrey V. Elsukov { 439a5185adeSAndrey V. Elsukov 440a5185adeSAndrey V. Elsukov free(hash, M_GRE); 441a5185adeSAndrey V. Elsukov } 442a5185adeSAndrey V. Elsukov 443a5185adeSAndrey V. Elsukov void 444aee793eeSAndrey V. Elsukov gre_sofree(epoch_context_t ctx) 445aee793eeSAndrey V. Elsukov { 446aee793eeSAndrey V. Elsukov struct gre_socket *gs; 447aee793eeSAndrey V. Elsukov 448aee793eeSAndrey V. Elsukov gs = __containerof(ctx, struct gre_socket, epoch_ctx); 449aee793eeSAndrey V. Elsukov free(gs, M_GRE); 450aee793eeSAndrey V. Elsukov } 451aee793eeSAndrey V. Elsukov 452aee793eeSAndrey V. Elsukov static __inline uint16_t 453aee793eeSAndrey V. Elsukov gre_cksum_add(uint16_t sum, uint16_t a) 454aee793eeSAndrey V. Elsukov { 455aee793eeSAndrey V. Elsukov uint16_t res; 456aee793eeSAndrey V. Elsukov 457aee793eeSAndrey V. Elsukov res = sum + a; 458aee793eeSAndrey V. Elsukov return (res + (res < a)); 459aee793eeSAndrey V. Elsukov } 460aee793eeSAndrey V. Elsukov 461aee793eeSAndrey V. Elsukov void 462aee793eeSAndrey V. Elsukov gre_update_udphdr(struct gre_softc *sc, struct udphdr *udp, uint16_t csum) 463aee793eeSAndrey V. Elsukov { 464aee793eeSAndrey V. Elsukov 465aee793eeSAndrey V. Elsukov sx_assert(&gre_ioctl_sx, SA_XLOCKED); 466aee793eeSAndrey V. Elsukov MPASS(sc->gre_options & GRE_UDPENCAP); 467aee793eeSAndrey V. Elsukov 468aee793eeSAndrey V. Elsukov udp->uh_dport = htons(GRE_UDPPORT); 469aee793eeSAndrey V. Elsukov udp->uh_sport = htons(sc->gre_port); 470aee793eeSAndrey V. Elsukov udp->uh_sum = csum; 471aee793eeSAndrey V. Elsukov udp->uh_ulen = 0; 472aee793eeSAndrey V. Elsukov } 473aee793eeSAndrey V. Elsukov 474aee793eeSAndrey V. Elsukov void 475aee793eeSAndrey V. Elsukov gre_update_hdr(struct gre_softc *sc, struct grehdr *gh) 476a5185adeSAndrey V. Elsukov { 477f325335cSAndrey V. Elsukov uint32_t *opts; 478f325335cSAndrey V. Elsukov uint16_t flags; 479f325335cSAndrey V. Elsukov 480a5185adeSAndrey V. Elsukov sx_assert(&gre_ioctl_sx, SA_XLOCKED); 481a5185adeSAndrey V. Elsukov 482f325335cSAndrey V. Elsukov flags = 0; 483f325335cSAndrey V. Elsukov opts = gh->gre_opts; 484f325335cSAndrey V. Elsukov if (sc->gre_options & GRE_ENABLE_CSUM) { 485f325335cSAndrey V. Elsukov flags |= GRE_FLAGS_CP; 486f325335cSAndrey V. Elsukov sc->gre_hlen += 2 * sizeof(uint16_t); 487f325335cSAndrey V. Elsukov *opts++ = 0; 488f325335cSAndrey V. Elsukov } 489f325335cSAndrey V. Elsukov if (sc->gre_key != 0) { 490f325335cSAndrey V. Elsukov flags |= GRE_FLAGS_KP; 491f325335cSAndrey V. Elsukov sc->gre_hlen += sizeof(uint32_t); 492f325335cSAndrey V. Elsukov *opts++ = htonl(sc->gre_key); 493f325335cSAndrey V. Elsukov } 494f325335cSAndrey V. Elsukov if (sc->gre_options & GRE_ENABLE_SEQ) { 495f325335cSAndrey V. Elsukov flags |= GRE_FLAGS_SP; 496f325335cSAndrey V. Elsukov sc->gre_hlen += sizeof(uint32_t); 497f325335cSAndrey V. Elsukov *opts++ = 0; 498f325335cSAndrey V. Elsukov } else 499f325335cSAndrey V. Elsukov sc->gre_oseq = 0; 500f325335cSAndrey V. Elsukov gh->gre_flags = htons(flags); 501f325335cSAndrey V. Elsukov } 502f325335cSAndrey V. Elsukov 503f325335cSAndrey V. Elsukov int 5046d8fdfa9SAndrey V. Elsukov gre_input(struct mbuf *m, int off, int proto, void *arg) 505f325335cSAndrey V. Elsukov { 5066d8fdfa9SAndrey V. Elsukov struct gre_softc *sc = arg; 507f325335cSAndrey V. Elsukov struct grehdr *gh; 508f325335cSAndrey V. Elsukov struct ifnet *ifp; 5092bfd3dfbSMarcelo Araujo uint32_t *opts; 5102bfd3dfbSMarcelo Araujo #ifdef notyet 5112bfd3dfbSMarcelo Araujo uint32_t key; 5122bfd3dfbSMarcelo Araujo #endif 513f325335cSAndrey V. Elsukov uint16_t flags; 514f325335cSAndrey V. Elsukov int hlen, isr, af; 515f325335cSAndrey V. Elsukov 516f325335cSAndrey V. Elsukov ifp = GRE2IFP(sc); 5176d8fdfa9SAndrey V. Elsukov hlen = off + sizeof(struct grehdr) + 4 * sizeof(uint32_t); 51850bc87bcSAndrey V. Elsukov if (m->m_pkthdr.len < hlen) 51950bc87bcSAndrey V. Elsukov goto drop; 52050bc87bcSAndrey V. Elsukov if (m->m_len < hlen) { 52150bc87bcSAndrey V. Elsukov m = m_pullup(m, hlen); 52250bc87bcSAndrey V. Elsukov if (m == NULL) 52350bc87bcSAndrey V. Elsukov goto drop; 52450bc87bcSAndrey V. Elsukov } 5256d8fdfa9SAndrey V. Elsukov gh = (struct grehdr *)mtodo(m, off); 526f325335cSAndrey V. Elsukov flags = ntohs(gh->gre_flags); 527f325335cSAndrey V. Elsukov if (flags & ~GRE_FLAGS_MASK) 528f325335cSAndrey V. Elsukov goto drop; 529f325335cSAndrey V. Elsukov opts = gh->gre_opts; 530f325335cSAndrey V. Elsukov hlen = 2 * sizeof(uint16_t); 531f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_CP) { 532f325335cSAndrey V. Elsukov /* reserved1 field must be zero */ 533f325335cSAndrey V. Elsukov if (((uint16_t *)opts)[1] != 0) 534f325335cSAndrey V. Elsukov goto drop; 5356d8fdfa9SAndrey V. Elsukov if (in_cksum_skip(m, m->m_pkthdr.len, off) != 0) 536f325335cSAndrey V. Elsukov goto drop; 537f325335cSAndrey V. Elsukov hlen += 2 * sizeof(uint16_t); 538f325335cSAndrey V. Elsukov opts++; 539f325335cSAndrey V. Elsukov } 540f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_KP) { 5412bfd3dfbSMarcelo Araujo #ifdef notyet 5422bfd3dfbSMarcelo Araujo /* 5432bfd3dfbSMarcelo Araujo * XXX: The current implementation uses the key only for outgoing 5442bfd3dfbSMarcelo Araujo * packets. But we can check the key value here, or even in the 5452bfd3dfbSMarcelo Araujo * encapcheck function. 5462bfd3dfbSMarcelo Araujo */ 547f325335cSAndrey V. Elsukov key = ntohl(*opts); 5482bfd3dfbSMarcelo Araujo #endif 549f325335cSAndrey V. Elsukov hlen += sizeof(uint32_t); 550f325335cSAndrey V. Elsukov opts++; 5512bfd3dfbSMarcelo Araujo } 5522bfd3dfbSMarcelo Araujo #ifdef notyet 553f325335cSAndrey V. Elsukov } else 554f325335cSAndrey V. Elsukov key = 0; 5552bfd3dfbSMarcelo Araujo 556f325335cSAndrey V. Elsukov if (sc->gre_key != 0 && (key != sc->gre_key || key != 0)) 557f325335cSAndrey V. Elsukov goto drop; 5582bfd3dfbSMarcelo Araujo #endif 559f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_SP) { 5602bfd3dfbSMarcelo Araujo #ifdef notyet 5612bfd3dfbSMarcelo Araujo seq = ntohl(*opts); 5622bfd3dfbSMarcelo Araujo #endif 563f325335cSAndrey V. Elsukov hlen += sizeof(uint32_t); 564f325335cSAndrey V. Elsukov } 565f325335cSAndrey V. Elsukov switch (ntohs(gh->gre_proto)) { 566f325335cSAndrey V. Elsukov case ETHERTYPE_WCCP: 567f325335cSAndrey V. Elsukov /* 568f325335cSAndrey V. Elsukov * For WCCP skip an additional 4 bytes if after GRE header 569f325335cSAndrey V. Elsukov * doesn't follow an IP header. 570f325335cSAndrey V. Elsukov */ 571f325335cSAndrey V. Elsukov if (flags == 0 && (*(uint8_t *)gh->gre_opts & 0xF0) != 0x40) 572f325335cSAndrey V. Elsukov hlen += sizeof(uint32_t); 573f325335cSAndrey V. Elsukov /* FALLTHROUGH */ 574f325335cSAndrey V. Elsukov case ETHERTYPE_IP: 575f325335cSAndrey V. Elsukov isr = NETISR_IP; 576f325335cSAndrey V. Elsukov af = AF_INET; 577f325335cSAndrey V. Elsukov break; 578f325335cSAndrey V. Elsukov case ETHERTYPE_IPV6: 579f325335cSAndrey V. Elsukov isr = NETISR_IPV6; 580f325335cSAndrey V. Elsukov af = AF_INET6; 581f325335cSAndrey V. Elsukov break; 582f325335cSAndrey V. Elsukov default: 583f325335cSAndrey V. Elsukov goto drop; 584f325335cSAndrey V. Elsukov } 5856d8fdfa9SAndrey V. Elsukov m_adj(m, off + hlen); 586f325335cSAndrey V. Elsukov m_clrprotoflags(m); 587f325335cSAndrey V. Elsukov m->m_pkthdr.rcvif = ifp; 588eccfe69aSAndrey V. Elsukov M_SETFIB(m, ifp->if_fib); 589f325335cSAndrey V. Elsukov #ifdef MAC 590f325335cSAndrey V. Elsukov mac_ifnet_create_mbuf(ifp, m); 591f325335cSAndrey V. Elsukov #endif 592f325335cSAndrey V. Elsukov BPF_MTAP2(ifp, &af, sizeof(af), m); 593f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 594f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); 595f325335cSAndrey V. Elsukov if ((ifp->if_flags & IFF_MONITOR) != 0) 596f325335cSAndrey V. Elsukov m_freem(m); 5978e96e13eSMaxim Sobolev else 598f325335cSAndrey V. Elsukov netisr_dispatch(isr, m); 599f325335cSAndrey V. Elsukov return (IPPROTO_DONE); 600f325335cSAndrey V. Elsukov drop: 601f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 602f325335cSAndrey V. Elsukov m_freem(m); 603f325335cSAndrey V. Elsukov return (IPPROTO_DONE); 6048e96e13eSMaxim Sobolev } 6058e96e13eSMaxim Sobolev 606f325335cSAndrey V. Elsukov static int 607f325335cSAndrey V. Elsukov gre_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, 608f325335cSAndrey V. Elsukov struct route *ro) 609f325335cSAndrey V. Elsukov { 610f325335cSAndrey V. Elsukov uint32_t af; 611f325335cSAndrey V. Elsukov 612*2cb0fce2SSeth Hoffert /* BPF writes need to be handled specially. */ 613*2cb0fce2SSeth Hoffert if (dst->sa_family == AF_UNSPEC || dst->sa_family == pseudo_AF_HDRCMPLT) 614f325335cSAndrey V. Elsukov bcopy(dst->sa_data, &af, sizeof(af)); 615f325335cSAndrey V. Elsukov else 61662e1a437SZhenlei Huang af = RO_GET_FAMILY(ro, dst); 617a5185adeSAndrey V. Elsukov /* 618a5185adeSAndrey V. Elsukov * Now save the af in the inbound pkt csum data, this is a cheat since 619a5185adeSAndrey V. Elsukov * we are using the inbound csum_data field to carry the af over to 620a5185adeSAndrey V. Elsukov * the gre_transmit() routine, avoiding using yet another mtag. 621a5185adeSAndrey V. Elsukov */ 622a5185adeSAndrey V. Elsukov m->m_pkthdr.csum_data = af; 623f325335cSAndrey V. Elsukov return (ifp->if_transmit(ifp, m)); 624f325335cSAndrey V. Elsukov } 625f325335cSAndrey V. Elsukov 626f325335cSAndrey V. Elsukov static void 627f325335cSAndrey V. Elsukov gre_setseqn(struct grehdr *gh, uint32_t seq) 628f325335cSAndrey V. Elsukov { 629f325335cSAndrey V. Elsukov uint32_t *opts; 630f325335cSAndrey V. Elsukov uint16_t flags; 631f325335cSAndrey V. Elsukov 632f325335cSAndrey V. Elsukov opts = gh->gre_opts; 633f325335cSAndrey V. Elsukov flags = ntohs(gh->gre_flags); 634f325335cSAndrey V. Elsukov KASSERT((flags & GRE_FLAGS_SP) != 0, 635f325335cSAndrey V. Elsukov ("gre_setseqn called, but GRE_FLAGS_SP isn't set ")); 636f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_CP) 637f325335cSAndrey V. Elsukov opts++; 638f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_KP) 639f325335cSAndrey V. Elsukov opts++; 640f325335cSAndrey V. Elsukov *opts = htonl(seq); 641f325335cSAndrey V. Elsukov } 642f325335cSAndrey V. Elsukov 643aee793eeSAndrey V. Elsukov static uint32_t 644aee793eeSAndrey V. Elsukov gre_flowid(struct gre_softc *sc, struct mbuf *m, uint32_t af) 645aee793eeSAndrey V. Elsukov { 646bb250faeSFranco Fichtner uint32_t flowid = 0; 647aee793eeSAndrey V. Elsukov 648aee793eeSAndrey V. Elsukov if ((sc->gre_options & GRE_UDPENCAP) == 0 || sc->gre_port != 0) 649bb250faeSFranco Fichtner return (flowid); 650aee793eeSAndrey V. Elsukov switch (af) { 651aee793eeSAndrey V. Elsukov #ifdef INET 652aee793eeSAndrey V. Elsukov case AF_INET: 653bb250faeSFranco Fichtner #ifdef RSS 654bb250faeSFranco Fichtner flowid = rss_hash_ip4_2tuple(mtod(m, struct ip *)->ip_src, 655bb250faeSFranco Fichtner mtod(m, struct ip *)->ip_dst); 656bb250faeSFranco Fichtner break; 657bb250faeSFranco Fichtner #endif 658aee793eeSAndrey V. Elsukov flowid = mtod(m, struct ip *)->ip_src.s_addr ^ 659aee793eeSAndrey V. Elsukov mtod(m, struct ip *)->ip_dst.s_addr; 660aee793eeSAndrey V. Elsukov break; 661aee793eeSAndrey V. Elsukov #endif 662aee793eeSAndrey V. Elsukov #ifdef INET6 663aee793eeSAndrey V. Elsukov case AF_INET6: 664bb250faeSFranco Fichtner #ifdef RSS 665aee793eeSAndrey V. Elsukov flowid = rss_hash_ip6_2tuple( 666aee793eeSAndrey V. Elsukov &mtod(m, struct ip6_hdr *)->ip6_src, 667aee793eeSAndrey V. Elsukov &mtod(m, struct ip6_hdr *)->ip6_dst); 668aee793eeSAndrey V. Elsukov break; 669aee793eeSAndrey V. Elsukov #endif 670bb250faeSFranco Fichtner flowid = mtod(m, struct ip6_hdr *)->ip6_src.s6_addr32[3] ^ 671bb250faeSFranco Fichtner mtod(m, struct ip6_hdr *)->ip6_dst.s6_addr32[3]; 672bb250faeSFranco Fichtner break; 673aee793eeSAndrey V. Elsukov #endif 674bb250faeSFranco Fichtner default: 675bb250faeSFranco Fichtner break; 676bb250faeSFranco Fichtner } 677aee793eeSAndrey V. Elsukov return (flowid); 678aee793eeSAndrey V. Elsukov } 679aee793eeSAndrey V. Elsukov 68098a8fdf6SAndrey V. Elsukov #define MTAG_GRE 1307983903 681f325335cSAndrey V. Elsukov static int 682f325335cSAndrey V. Elsukov gre_transmit(struct ifnet *ifp, struct mbuf *m) 683f325335cSAndrey V. Elsukov { 68419873f47SAndrey V. Elsukov GRE_RLOCK_TRACKER; 685f325335cSAndrey V. Elsukov struct gre_softc *sc; 686f325335cSAndrey V. Elsukov struct grehdr *gh; 687aee793eeSAndrey V. Elsukov struct udphdr *uh; 688aee793eeSAndrey V. Elsukov uint32_t af, flowid; 689a5185adeSAndrey V. Elsukov int error, len; 690a5185adeSAndrey V. Elsukov uint16_t proto; 691f325335cSAndrey V. Elsukov 692a5185adeSAndrey V. Elsukov len = 0; 6930a27163fSGleb Smirnoff GRE_RLOCK(); 694a5185adeSAndrey V. Elsukov #ifdef MAC 695a5185adeSAndrey V. Elsukov error = mac_ifnet_check_transmit(ifp, m); 696a5185adeSAndrey V. Elsukov if (error) { 697a5185adeSAndrey V. Elsukov m_freem(m); 698a5185adeSAndrey V. Elsukov goto drop; 699a5185adeSAndrey V. Elsukov } 700a5185adeSAndrey V. Elsukov #endif 701a5185adeSAndrey V. Elsukov error = ENETDOWN; 702f325335cSAndrey V. Elsukov sc = ifp->if_softc; 703a5185adeSAndrey V. Elsukov if ((ifp->if_flags & IFF_MONITOR) != 0 || 704a5185adeSAndrey V. Elsukov (ifp->if_flags & IFF_UP) == 0 || 70519873f47SAndrey V. Elsukov (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || 706a5185adeSAndrey V. Elsukov sc->gre_family == 0 || 70798a8fdf6SAndrey V. Elsukov (error = if_tunnel_check_nesting(ifp, m, MTAG_GRE, 70898a8fdf6SAndrey V. Elsukov V_max_gre_nesting)) != 0) { 709f325335cSAndrey V. Elsukov m_freem(m); 710f325335cSAndrey V. Elsukov goto drop; 711f325335cSAndrey V. Elsukov } 712a5185adeSAndrey V. Elsukov af = m->m_pkthdr.csum_data; 713c6851ad0SAndrey V. Elsukov BPF_MTAP2(ifp, &af, sizeof(af), m); 714c6851ad0SAndrey V. Elsukov m->m_flags &= ~(M_BCAST|M_MCAST); 715aee793eeSAndrey V. Elsukov flowid = gre_flowid(sc, m, af); 716f325335cSAndrey V. Elsukov M_SETFIB(m, sc->gre_fibnum); 717a5185adeSAndrey V. Elsukov M_PREPEND(m, sc->gre_hlen, M_NOWAIT); 718f325335cSAndrey V. Elsukov if (m == NULL) { 719f325335cSAndrey V. Elsukov error = ENOBUFS; 720f325335cSAndrey V. Elsukov goto drop; 721f325335cSAndrey V. Elsukov } 722a5185adeSAndrey V. Elsukov bcopy(sc->gre_hdr, mtod(m, void *), sc->gre_hlen); 723a5185adeSAndrey V. Elsukov /* Determine GRE proto */ 724a5185adeSAndrey V. Elsukov switch (af) { 725f325335cSAndrey V. Elsukov #ifdef INET 726f325335cSAndrey V. Elsukov case AF_INET: 727a5185adeSAndrey V. Elsukov proto = htons(ETHERTYPE_IP); 728f325335cSAndrey V. Elsukov break; 729f325335cSAndrey V. Elsukov #endif 730f325335cSAndrey V. Elsukov #ifdef INET6 731f325335cSAndrey V. Elsukov case AF_INET6: 732a5185adeSAndrey V. Elsukov proto = htons(ETHERTYPE_IPV6); 733f325335cSAndrey V. Elsukov break; 734f325335cSAndrey V. Elsukov #endif 735f325335cSAndrey V. Elsukov default: 736a5185adeSAndrey V. Elsukov m_freem(m); 737f325335cSAndrey V. Elsukov error = ENETDOWN; 738f325335cSAndrey V. Elsukov goto drop; 739f325335cSAndrey V. Elsukov } 740a5185adeSAndrey V. Elsukov /* Determine offset of GRE header */ 741a5185adeSAndrey V. Elsukov switch (sc->gre_family) { 742f325335cSAndrey V. Elsukov #ifdef INET 743f325335cSAndrey V. Elsukov case AF_INET: 744a5185adeSAndrey V. Elsukov len = sizeof(struct ip); 745f325335cSAndrey V. Elsukov break; 746f325335cSAndrey V. Elsukov #endif 747f325335cSAndrey V. Elsukov #ifdef INET6 748f325335cSAndrey V. Elsukov case AF_INET6: 749a5185adeSAndrey V. Elsukov len = sizeof(struct ip6_hdr); 750f325335cSAndrey V. Elsukov break; 751f325335cSAndrey V. Elsukov #endif 752f325335cSAndrey V. Elsukov default: 753a5185adeSAndrey V. Elsukov m_freem(m); 754f325335cSAndrey V. Elsukov error = ENETDOWN; 755f325335cSAndrey V. Elsukov goto drop; 756f325335cSAndrey V. Elsukov } 757aee793eeSAndrey V. Elsukov if (sc->gre_options & GRE_UDPENCAP) { 758aee793eeSAndrey V. Elsukov uh = (struct udphdr *)mtodo(m, len); 759aee793eeSAndrey V. Elsukov uh->uh_sport |= htons(V_ipport_hifirstauto) | 760aee793eeSAndrey V. Elsukov (flowid >> 16) | (flowid & 0xFFFF); 761aee793eeSAndrey V. Elsukov uh->uh_sport = htons(ntohs(uh->uh_sport) % 762aee793eeSAndrey V. Elsukov V_ipport_hilastauto); 763aee793eeSAndrey V. Elsukov uh->uh_ulen = htons(m->m_pkthdr.len - len); 764aee793eeSAndrey V. Elsukov uh->uh_sum = gre_cksum_add(uh->uh_sum, 765aee793eeSAndrey V. Elsukov htons(m->m_pkthdr.len - len + IPPROTO_UDP)); 766aee793eeSAndrey V. Elsukov m->m_pkthdr.csum_flags = sc->gre_csumflags; 767aee793eeSAndrey V. Elsukov m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); 768aee793eeSAndrey V. Elsukov len += sizeof(struct udphdr); 769aee793eeSAndrey V. Elsukov } 770a5185adeSAndrey V. Elsukov gh = (struct grehdr *)mtodo(m, len); 771a5185adeSAndrey V. Elsukov gh->gre_proto = proto; 772a5185adeSAndrey V. Elsukov if (sc->gre_options & GRE_ENABLE_SEQ) 773a5185adeSAndrey V. Elsukov gre_setseqn(gh, sc->gre_oseq++); 774a5185adeSAndrey V. Elsukov if (sc->gre_options & GRE_ENABLE_CSUM) { 775f325335cSAndrey V. Elsukov *(uint16_t *)gh->gre_opts = in_cksum_skip(m, 776a5185adeSAndrey V. Elsukov m->m_pkthdr.len, len); 777f325335cSAndrey V. Elsukov } 778a5185adeSAndrey V. Elsukov len = m->m_pkthdr.len - len; 779a5185adeSAndrey V. Elsukov switch (sc->gre_family) { 780f325335cSAndrey V. Elsukov #ifdef INET 781f325335cSAndrey V. Elsukov case AF_INET: 782a5185adeSAndrey V. Elsukov error = in_gre_output(m, af, sc->gre_hlen); 783f325335cSAndrey V. Elsukov break; 784f325335cSAndrey V. Elsukov #endif 785f325335cSAndrey V. Elsukov #ifdef INET6 786f325335cSAndrey V. Elsukov case AF_INET6: 787aee793eeSAndrey V. Elsukov error = in6_gre_output(m, af, sc->gre_hlen, flowid); 788f325335cSAndrey V. Elsukov break; 789f325335cSAndrey V. Elsukov #endif 790f325335cSAndrey V. Elsukov default: 791f325335cSAndrey V. Elsukov m_freem(m); 792f325335cSAndrey V. Elsukov error = ENETDOWN; 79374b8d63dSPedro F. Giffuni } 794f325335cSAndrey V. Elsukov drop: 795f325335cSAndrey V. Elsukov if (error) 796f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 797f325335cSAndrey V. Elsukov else { 798f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 799a5185adeSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OBYTES, len); 800f325335cSAndrey V. Elsukov } 801a5185adeSAndrey V. Elsukov GRE_RUNLOCK(); 802f325335cSAndrey V. Elsukov return (error); 803f325335cSAndrey V. Elsukov } 804f325335cSAndrey V. Elsukov 805f325335cSAndrey V. Elsukov static void 806f325335cSAndrey V. Elsukov gre_qflush(struct ifnet *ifp __unused) 807f325335cSAndrey V. Elsukov { 808f325335cSAndrey V. Elsukov 8098e96e13eSMaxim Sobolev } 8108e96e13eSMaxim Sobolev 8118e96e13eSMaxim Sobolev static int 8128e96e13eSMaxim Sobolev gremodevent(module_t mod, int type, void *data) 8138e96e13eSMaxim Sobolev { 8148e96e13eSMaxim Sobolev 8158e96e13eSMaxim Sobolev switch (type) { 8168e96e13eSMaxim Sobolev case MOD_LOAD: 8178e96e13eSMaxim Sobolev case MOD_UNLOAD: 8188e96e13eSMaxim Sobolev break; 8193e019deaSPoul-Henning Kamp default: 82089c58b73SHiroki Sato return (EOPNOTSUPP); 8218e96e13eSMaxim Sobolev } 82289c58b73SHiroki Sato return (0); 8238e96e13eSMaxim Sobolev } 8248e96e13eSMaxim Sobolev 8258e96e13eSMaxim Sobolev static moduledata_t gre_mod = { 8268e96e13eSMaxim Sobolev "if_gre", 8278e96e13eSMaxim Sobolev gremodevent, 8289823d527SKevin Lo 0 8298e96e13eSMaxim Sobolev }; 8308e96e13eSMaxim Sobolev 8318e96e13eSMaxim Sobolev DECLARE_MODULE(if_gre, gre_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 8328e96e13eSMaxim Sobolev MODULE_VERSION(if_gre, 1); 833