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 110dd4490fdSAndrey V. Elsukov #ifdef VIMAGE 111dd4490fdSAndrey V. Elsukov static void gre_reassign(struct ifnet *, struct vnet *, char *); 112dd4490fdSAndrey V. Elsukov #endif 113f325335cSAndrey V. Elsukov static void gre_qflush(struct ifnet *); 114f325335cSAndrey V. Elsukov static int gre_transmit(struct ifnet *, struct mbuf *); 115c23d234cSMaxim Sobolev static int gre_ioctl(struct ifnet *, u_long, caddr_t); 11647e8d432SGleb Smirnoff static int gre_output(struct ifnet *, struct mbuf *, 11747e8d432SGleb Smirnoff const struct sockaddr *, struct route *); 118a5185adeSAndrey V. Elsukov static void gre_delete_tunnel(struct gre_softc *); 1198e96e13eSMaxim Sobolev 1208e96e13eSMaxim Sobolev SYSCTL_DECL(_net_link); 1217029da5cSPawel Biernacki static SYSCTL_NODE(_net_link, IFT_TUNNEL, gre, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 1228e96e13eSMaxim Sobolev "Generic Routing Encapsulation"); 1238e96e13eSMaxim Sobolev #ifndef MAX_GRE_NEST 1248e96e13eSMaxim Sobolev /* 1258e96e13eSMaxim Sobolev * This macro controls the default upper limitation on nesting of gre tunnels. 1268e96e13eSMaxim Sobolev * Since, setting a large value to this macro with a careless configuration 1278e96e13eSMaxim Sobolev * may introduce system crash, we don't allow any nestings by default. 1288e96e13eSMaxim Sobolev * If you need to configure nested gre tunnels, you can define this macro 1298e96e13eSMaxim Sobolev * in your kernel configuration file. However, if you do so, please be 1308e96e13eSMaxim Sobolev * careful to configure the tunnels so that it won't make a loop. 1318e96e13eSMaxim Sobolev */ 1328e96e13eSMaxim Sobolev #define MAX_GRE_NEST 1 1338e96e13eSMaxim Sobolev #endif 134f325335cSAndrey V. Elsukov 1355f901c92SAndrew Turner VNET_DEFINE_STATIC(int, max_gre_nesting) = MAX_GRE_NEST; 13689c58b73SHiroki Sato #define V_max_gre_nesting VNET(max_gre_nesting) 13789c58b73SHiroki Sato SYSCTL_INT(_net_link_gre, OID_AUTO, max_nesting, CTLFLAG_RW | CTLFLAG_VNET, 13889c58b73SHiroki Sato &VNET_NAME(max_gre_nesting), 0, "Max nested tunnels"); 1398e96e13eSMaxim Sobolev 140c23d234cSMaxim Sobolev static void 14189c58b73SHiroki Sato vnet_gre_init(const void *unused __unused) 1428e96e13eSMaxim Sobolev { 143a5185adeSAndrey V. Elsukov 14489c58b73SHiroki Sato V_gre_cloner = if_clone_simple(grename, gre_clone_create, 14542a58907SGleb Smirnoff gre_clone_destroy, 0); 146a5185adeSAndrey V. Elsukov #ifdef INET 147a5185adeSAndrey V. Elsukov in_gre_init(); 148a5185adeSAndrey V. Elsukov #endif 149a5185adeSAndrey V. Elsukov #ifdef INET6 150a5185adeSAndrey V. Elsukov in6_gre_init(); 151a5185adeSAndrey V. Elsukov #endif 1528e96e13eSMaxim Sobolev } 15389c58b73SHiroki Sato VNET_SYSINIT(vnet_gre_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 15489c58b73SHiroki Sato vnet_gre_init, NULL); 15589c58b73SHiroki Sato 15689c58b73SHiroki Sato static void 15789c58b73SHiroki Sato vnet_gre_uninit(const void *unused __unused) 15889c58b73SHiroki Sato { 15989c58b73SHiroki Sato 16089c58b73SHiroki Sato if_clone_detach(V_gre_cloner); 161a5185adeSAndrey V. Elsukov #ifdef INET 162a5185adeSAndrey V. Elsukov in_gre_uninit(); 163a5185adeSAndrey V. Elsukov #endif 164a5185adeSAndrey V. Elsukov #ifdef INET6 165a5185adeSAndrey V. Elsukov in6_gre_uninit(); 166a5185adeSAndrey V. Elsukov #endif 167aee793eeSAndrey V. Elsukov /* XXX: epoch_call drain */ 16889c58b73SHiroki Sato } 16989c58b73SHiroki Sato VNET_SYSUNINIT(vnet_gre_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 17089c58b73SHiroki Sato vnet_gre_uninit, NULL); 1718e96e13eSMaxim Sobolev 172c23d234cSMaxim Sobolev static int 17389c58b73SHiroki Sato gre_clone_create(struct if_clone *ifc, int unit, caddr_t params) 1748e96e13eSMaxim Sobolev { 1758e96e13eSMaxim Sobolev struct gre_softc *sc; 1768e96e13eSMaxim Sobolev 177b3c9a01eSBruce M Simpson sc = malloc(sizeof(struct gre_softc), M_GRE, M_WAITOK | M_ZERO); 178f325335cSAndrey V. Elsukov sc->gre_fibnum = curthread->td_proc->p_fibnum; 179066b192eSBjoern A. Zeeb GRE2IFP(sc) = if_alloc(IFT_TUNNEL); 180fc74a9f9SBrooks Davis GRE2IFP(sc)->if_softc = sc; 18142a58907SGleb Smirnoff if_initname(GRE2IFP(sc), grename, unit); 182066b192eSBjoern A. Zeeb 183c00bf730SAndrey V. Elsukov GRE2IFP(sc)->if_mtu = GREMTU; 184fc74a9f9SBrooks Davis GRE2IFP(sc)->if_flags = IFF_POINTOPOINT|IFF_MULTICAST; 185fc74a9f9SBrooks Davis GRE2IFP(sc)->if_output = gre_output; 186fc74a9f9SBrooks Davis GRE2IFP(sc)->if_ioctl = gre_ioctl; 187f325335cSAndrey V. Elsukov GRE2IFP(sc)->if_transmit = gre_transmit; 188f325335cSAndrey V. Elsukov GRE2IFP(sc)->if_qflush = gre_qflush; 189dd4490fdSAndrey V. Elsukov #ifdef VIMAGE 190dd4490fdSAndrey V. Elsukov GRE2IFP(sc)->if_reassign = gre_reassign; 191dd4490fdSAndrey V. Elsukov #endif 192f1aaad0cSHiroki Sato GRE2IFP(sc)->if_capabilities |= IFCAP_LINKSTATE; 193f1aaad0cSHiroki Sato GRE2IFP(sc)->if_capenable |= IFCAP_LINKSTATE; 194fc74a9f9SBrooks Davis if_attach(GRE2IFP(sc)); 195fc74a9f9SBrooks Davis bpfattach(GRE2IFP(sc), DLT_NULL, sizeof(u_int32_t)); 1968e96e13eSMaxim Sobolev return (0); 1978e96e13eSMaxim Sobolev } 1988e96e13eSMaxim Sobolev 199dd4490fdSAndrey V. Elsukov #ifdef VIMAGE 200dd4490fdSAndrey V. Elsukov static void 201dd4490fdSAndrey V. Elsukov gre_reassign(struct ifnet *ifp, struct vnet *new_vnet __unused, 202dd4490fdSAndrey V. Elsukov char *unused __unused) 203dd4490fdSAndrey V. Elsukov { 204dd4490fdSAndrey V. Elsukov struct gre_softc *sc; 205dd4490fdSAndrey V. Elsukov 206dd4490fdSAndrey V. Elsukov sx_xlock(&gre_ioctl_sx); 207dd4490fdSAndrey V. Elsukov sc = ifp->if_softc; 208dd4490fdSAndrey V. Elsukov if (sc != NULL) 209dd4490fdSAndrey V. Elsukov gre_delete_tunnel(sc); 210dd4490fdSAndrey V. Elsukov sx_xunlock(&gre_ioctl_sx); 211dd4490fdSAndrey V. Elsukov } 212dd4490fdSAndrey V. Elsukov #endif /* VIMAGE */ 213dd4490fdSAndrey V. Elsukov 214c23d234cSMaxim Sobolev static void 21589c58b73SHiroki Sato gre_clone_destroy(struct ifnet *ifp) 2168e96e13eSMaxim Sobolev { 217f325335cSAndrey V. Elsukov struct gre_softc *sc; 2188e96e13eSMaxim Sobolev 219f325335cSAndrey V. Elsukov sx_xlock(&gre_ioctl_sx); 220f325335cSAndrey V. Elsukov sc = ifp->if_softc; 221a5185adeSAndrey V. Elsukov gre_delete_tunnel(sc); 222febd0759SAndrew Thompson bpfdetach(ifp); 223febd0759SAndrew Thompson if_detach(ifp); 224f325335cSAndrey V. Elsukov ifp->if_softc = NULL; 225f325335cSAndrey V. Elsukov sx_xunlock(&gre_ioctl_sx); 226f325335cSAndrey V. Elsukov 227a5185adeSAndrey V. Elsukov GRE_WAIT(); 228febd0759SAndrew Thompson if_free(ifp); 229febd0759SAndrew Thompson free(sc, M_GRE); 2308e96e13eSMaxim Sobolev } 2318e96e13eSMaxim Sobolev 232c23d234cSMaxim Sobolev static int 2338e96e13eSMaxim Sobolev gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 2348e96e13eSMaxim Sobolev { 2358e96e13eSMaxim Sobolev struct ifreq *ifr = (struct ifreq *)data; 236f325335cSAndrey V. Elsukov struct gre_softc *sc; 237f325335cSAndrey V. Elsukov uint32_t opt; 238f325335cSAndrey V. Elsukov int error; 2398e96e13eSMaxim Sobolev 2408e96e13eSMaxim Sobolev switch (cmd) { 241f325335cSAndrey V. Elsukov case SIOCSIFMTU: 242f325335cSAndrey V. Elsukov /* XXX: */ 243f325335cSAndrey V. Elsukov if (ifr->ifr_mtu < 576) 244f325335cSAndrey V. Elsukov return (EINVAL); 245c00bf730SAndrey V. Elsukov ifp->if_mtu = ifr->ifr_mtu; 246c00bf730SAndrey V. Elsukov return (0); 2478e96e13eSMaxim Sobolev case SIOCSIFADDR: 2488e96e13eSMaxim Sobolev ifp->if_flags |= IFF_UP; 2498e96e13eSMaxim Sobolev case SIOCSIFFLAGS: 2508e96e13eSMaxim Sobolev case SIOCADDMULTI: 2518e96e13eSMaxim Sobolev case SIOCDELMULTI: 252f325335cSAndrey V. Elsukov return (0); 2538e96e13eSMaxim Sobolev case GRESADDRS: 2548e96e13eSMaxim Sobolev case GRESADDRD: 2558e96e13eSMaxim Sobolev case GREGADDRS: 2568e96e13eSMaxim Sobolev case GREGADDRD: 257f325335cSAndrey V. Elsukov case GRESPROTO: 258f325335cSAndrey V. Elsukov case GREGPROTO: 259f325335cSAndrey V. Elsukov return (EOPNOTSUPP); 260f325335cSAndrey V. Elsukov } 261f325335cSAndrey V. Elsukov sx_xlock(&gre_ioctl_sx); 262f325335cSAndrey V. Elsukov sc = ifp->if_softc; 263f325335cSAndrey V. Elsukov if (sc == NULL) { 264f325335cSAndrey V. Elsukov error = ENXIO; 265f325335cSAndrey V. Elsukov goto end; 266f325335cSAndrey V. Elsukov } 267f325335cSAndrey V. Elsukov error = 0; 268f325335cSAndrey V. Elsukov switch (cmd) { 269f325335cSAndrey V. Elsukov case SIOCDIFPHYADDR: 270a5185adeSAndrey V. Elsukov if (sc->gre_family == 0) 2718e96e13eSMaxim Sobolev break; 272a5185adeSAndrey V. Elsukov gre_delete_tunnel(sc); 2738e96e13eSMaxim Sobolev break; 274f325335cSAndrey V. Elsukov #ifdef INET 275a5185adeSAndrey V. Elsukov case SIOCSIFPHYADDR: 276f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR: 277f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR: 278a5185adeSAndrey V. Elsukov error = in_gre_ioctl(sc, cmd, data); 279f325335cSAndrey V. Elsukov break; 280f325335cSAndrey V. Elsukov #endif 281f325335cSAndrey V. Elsukov #ifdef INET6 282a5185adeSAndrey V. Elsukov case SIOCSIFPHYADDR_IN6: 283f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR_IN6: 284f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR_IN6: 285a5185adeSAndrey V. Elsukov error = in6_gre_ioctl(sc, cmd, data); 286f325335cSAndrey V. Elsukov break; 287f325335cSAndrey V. Elsukov #endif 288eccfe69aSAndrey V. Elsukov case SIOCGTUNFIB: 289eccfe69aSAndrey V. Elsukov ifr->ifr_fib = sc->gre_fibnum; 290eccfe69aSAndrey V. Elsukov break; 291eccfe69aSAndrey V. Elsukov case SIOCSTUNFIB: 292eccfe69aSAndrey V. Elsukov if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0) 293eccfe69aSAndrey V. Elsukov break; 294eccfe69aSAndrey V. Elsukov if (ifr->ifr_fib >= rt_numfibs) 295eccfe69aSAndrey V. Elsukov error = EINVAL; 296eccfe69aSAndrey V. Elsukov else 297eccfe69aSAndrey V. Elsukov sc->gre_fibnum = ifr->ifr_fib; 298eccfe69aSAndrey V. Elsukov break; 299f325335cSAndrey V. Elsukov case GRESKEY: 300f325335cSAndrey V. Elsukov case GRESOPTS: 301aee793eeSAndrey V. Elsukov case GRESPORT: 302f325335cSAndrey V. Elsukov if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0) 303f325335cSAndrey V. Elsukov break; 304541d96aaSBrooks Davis if ((error = copyin(ifr_data_get_ptr(ifr), &opt, 305541d96aaSBrooks Davis sizeof(opt))) != 0) 306f325335cSAndrey V. Elsukov break; 307a5185adeSAndrey V. Elsukov if (cmd == GRESKEY) { 308a5185adeSAndrey V. Elsukov if (sc->gre_key == opt) 309131c55bcSAndrew Thompson break; 310a5185adeSAndrey V. Elsukov } else if (cmd == GRESOPTS) { 311a5185adeSAndrey V. Elsukov if (opt & ~GRE_OPTMASK) { 312a5185adeSAndrey V. Elsukov error = EINVAL; 313a5185adeSAndrey V. Elsukov break; 314a5185adeSAndrey V. Elsukov } 315a5185adeSAndrey V. Elsukov if (sc->gre_options == opt) 316a5185adeSAndrey V. Elsukov break; 317aee793eeSAndrey V. Elsukov } else if (cmd == GRESPORT) { 318aee793eeSAndrey V. Elsukov if (opt != 0 && (opt < V_ipport_hifirstauto || 319aee793eeSAndrey V. Elsukov opt > V_ipport_hilastauto)) { 320aee793eeSAndrey V. Elsukov error = EINVAL; 321aee793eeSAndrey V. Elsukov break; 322aee793eeSAndrey V. Elsukov } 323aee793eeSAndrey V. Elsukov if (sc->gre_port == opt) 324aee793eeSAndrey V. Elsukov break; 325aee793eeSAndrey V. Elsukov if ((sc->gre_options & GRE_UDPENCAP) == 0) { 326aee793eeSAndrey V. Elsukov /* 327aee793eeSAndrey V. Elsukov * UDP encapsulation is not enabled, thus 328aee793eeSAndrey V. Elsukov * there is no need to reattach softc. 329aee793eeSAndrey V. Elsukov */ 330aee793eeSAndrey V. Elsukov sc->gre_port = opt; 331aee793eeSAndrey V. Elsukov break; 332aee793eeSAndrey V. Elsukov } 333a5185adeSAndrey V. Elsukov } 334a5185adeSAndrey V. Elsukov switch (sc->gre_family) { 335a5185adeSAndrey V. Elsukov #ifdef INET 336a5185adeSAndrey V. Elsukov case AF_INET: 337aee793eeSAndrey V. Elsukov error = in_gre_setopts(sc, cmd, opt); 338a5185adeSAndrey V. Elsukov break; 339a5185adeSAndrey V. Elsukov #endif 340a5185adeSAndrey V. Elsukov #ifdef INET6 341a5185adeSAndrey V. Elsukov case AF_INET6: 342aee793eeSAndrey V. Elsukov error = in6_gre_setopts(sc, cmd, opt); 343a5185adeSAndrey V. Elsukov break; 344a5185adeSAndrey V. Elsukov #endif 345a5185adeSAndrey V. Elsukov default: 346aee793eeSAndrey V. Elsukov /* 347aee793eeSAndrey V. Elsukov * Tunnel is not yet configured. 348aee793eeSAndrey V. Elsukov * We can just change any parameters. 349aee793eeSAndrey V. Elsukov */ 350a5185adeSAndrey V. Elsukov if (cmd == GRESKEY) 351a5185adeSAndrey V. Elsukov sc->gre_key = opt; 352aee793eeSAndrey V. Elsukov if (cmd == GRESOPTS) 353a5185adeSAndrey V. Elsukov sc->gre_options = opt; 354aee793eeSAndrey V. Elsukov if (cmd == GRESPORT) 355aee793eeSAndrey V. Elsukov sc->gre_port = opt; 356a5185adeSAndrey V. Elsukov break; 357a5185adeSAndrey V. Elsukov } 358a5185adeSAndrey V. Elsukov /* 359a5185adeSAndrey V. Elsukov * XXX: Do we need to initiate change of interface 360a5185adeSAndrey V. Elsukov * state here? 361a5185adeSAndrey V. Elsukov */ 362a5185adeSAndrey V. Elsukov break; 363a5185adeSAndrey V. Elsukov case GREGKEY: 364a5185adeSAndrey V. Elsukov error = copyout(&sc->gre_key, ifr_data_get_ptr(ifr), 365a5185adeSAndrey V. Elsukov sizeof(sc->gre_key)); 366a5185adeSAndrey V. Elsukov break; 367f325335cSAndrey V. Elsukov case GREGOPTS: 368541d96aaSBrooks Davis error = copyout(&sc->gre_options, ifr_data_get_ptr(ifr), 369f325335cSAndrey V. Elsukov sizeof(sc->gre_options)); 370f325335cSAndrey V. Elsukov break; 371aee793eeSAndrey V. Elsukov case GREGPORT: 372aee793eeSAndrey V. Elsukov error = copyout(&sc->gre_port, ifr_data_get_ptr(ifr), 373aee793eeSAndrey V. Elsukov sizeof(sc->gre_port)); 374aee793eeSAndrey V. Elsukov break; 3758e96e13eSMaxim Sobolev default: 3768e96e13eSMaxim Sobolev error = EINVAL; 3778e96e13eSMaxim Sobolev break; 3788e96e13eSMaxim Sobolev } 379a5185adeSAndrey V. Elsukov if (error == 0 && sc->gre_family != 0) { 380a5185adeSAndrey V. Elsukov if ( 381a5185adeSAndrey V. Elsukov #ifdef INET 382a5185adeSAndrey V. Elsukov cmd == SIOCSIFPHYADDR || 383a5185adeSAndrey V. Elsukov #endif 384a5185adeSAndrey V. Elsukov #ifdef INET6 385a5185adeSAndrey V. Elsukov cmd == SIOCSIFPHYADDR_IN6 || 386a5185adeSAndrey V. Elsukov #endif 387a5185adeSAndrey V. Elsukov 0) { 388a5185adeSAndrey V. Elsukov if_link_state_change(ifp, LINK_STATE_UP); 389a5185adeSAndrey V. Elsukov } 390a5185adeSAndrey V. Elsukov } 391f325335cSAndrey V. Elsukov end: 392f325335cSAndrey V. Elsukov sx_xunlock(&gre_ioctl_sx); 3938e96e13eSMaxim Sobolev return (error); 3948e96e13eSMaxim Sobolev } 3958e96e13eSMaxim Sobolev 396f325335cSAndrey V. Elsukov static void 397a5185adeSAndrey V. Elsukov gre_delete_tunnel(struct gre_softc *sc) 398f325335cSAndrey V. Elsukov { 399aee793eeSAndrey V. Elsukov struct gre_socket *gs; 400a5185adeSAndrey V. Elsukov 401a5185adeSAndrey V. Elsukov sx_assert(&gre_ioctl_sx, SA_XLOCKED); 402a5185adeSAndrey V. Elsukov if (sc->gre_family != 0) { 403a5185adeSAndrey V. Elsukov CK_LIST_REMOVE(sc, chain); 40419873f47SAndrey V. Elsukov CK_LIST_REMOVE(sc, srchash); 405a5185adeSAndrey V. Elsukov GRE_WAIT(); 406a5185adeSAndrey V. Elsukov free(sc->gre_hdr, M_GRE); 407a5185adeSAndrey V. Elsukov sc->gre_family = 0; 408a5185adeSAndrey V. Elsukov } 409aee793eeSAndrey V. Elsukov /* 410aee793eeSAndrey V. Elsukov * If this Tunnel was the last one that could use UDP socket, 411aee793eeSAndrey V. Elsukov * we should unlink socket from hash table and close it. 412aee793eeSAndrey V. Elsukov */ 413aee793eeSAndrey V. Elsukov if ((gs = sc->gre_so) != NULL && CK_LIST_EMPTY(&gs->list)) { 414aee793eeSAndrey V. Elsukov CK_LIST_REMOVE(gs, chain); 415aee793eeSAndrey V. Elsukov soclose(gs->so); 4162a4bd982SGleb Smirnoff NET_EPOCH_CALL(gre_sofree, &gs->epoch_ctx); 417aee793eeSAndrey V. Elsukov sc->gre_so = NULL; 418aee793eeSAndrey V. Elsukov } 419a5185adeSAndrey V. Elsukov GRE2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 420a5185adeSAndrey V. Elsukov if_link_state_change(GRE2IFP(sc), LINK_STATE_DOWN); 421a5185adeSAndrey V. Elsukov } 422a5185adeSAndrey V. Elsukov 423a5185adeSAndrey V. Elsukov struct gre_list * 424a5185adeSAndrey V. Elsukov gre_hashinit(void) 425a5185adeSAndrey V. Elsukov { 426a5185adeSAndrey V. Elsukov struct gre_list *hash; 427a5185adeSAndrey V. Elsukov int i; 428a5185adeSAndrey V. Elsukov 429a5185adeSAndrey V. Elsukov hash = malloc(sizeof(struct gre_list) * GRE_HASH_SIZE, 430a5185adeSAndrey V. Elsukov M_GRE, M_WAITOK); 431a5185adeSAndrey V. Elsukov for (i = 0; i < GRE_HASH_SIZE; i++) 432a5185adeSAndrey V. Elsukov CK_LIST_INIT(&hash[i]); 433a5185adeSAndrey V. Elsukov 434a5185adeSAndrey V. Elsukov return (hash); 435a5185adeSAndrey V. Elsukov } 436a5185adeSAndrey V. Elsukov 437a5185adeSAndrey V. Elsukov void 438a5185adeSAndrey V. Elsukov gre_hashdestroy(struct gre_list *hash) 439a5185adeSAndrey V. Elsukov { 440a5185adeSAndrey V. Elsukov 441a5185adeSAndrey V. Elsukov free(hash, M_GRE); 442a5185adeSAndrey V. Elsukov } 443a5185adeSAndrey V. Elsukov 444a5185adeSAndrey V. Elsukov void 445aee793eeSAndrey V. Elsukov gre_sofree(epoch_context_t ctx) 446aee793eeSAndrey V. Elsukov { 447aee793eeSAndrey V. Elsukov struct gre_socket *gs; 448aee793eeSAndrey V. Elsukov 449aee793eeSAndrey V. Elsukov gs = __containerof(ctx, struct gre_socket, epoch_ctx); 450aee793eeSAndrey V. Elsukov free(gs, M_GRE); 451aee793eeSAndrey V. Elsukov } 452aee793eeSAndrey V. Elsukov 453aee793eeSAndrey V. Elsukov static __inline uint16_t 454aee793eeSAndrey V. Elsukov gre_cksum_add(uint16_t sum, uint16_t a) 455aee793eeSAndrey V. Elsukov { 456aee793eeSAndrey V. Elsukov uint16_t res; 457aee793eeSAndrey V. Elsukov 458aee793eeSAndrey V. Elsukov res = sum + a; 459aee793eeSAndrey V. Elsukov return (res + (res < a)); 460aee793eeSAndrey V. Elsukov } 461aee793eeSAndrey V. Elsukov 462aee793eeSAndrey V. Elsukov void 463aee793eeSAndrey V. Elsukov gre_update_udphdr(struct gre_softc *sc, struct udphdr *udp, uint16_t csum) 464aee793eeSAndrey V. Elsukov { 465aee793eeSAndrey V. Elsukov 466aee793eeSAndrey V. Elsukov sx_assert(&gre_ioctl_sx, SA_XLOCKED); 467aee793eeSAndrey V. Elsukov MPASS(sc->gre_options & GRE_UDPENCAP); 468aee793eeSAndrey V. Elsukov 469aee793eeSAndrey V. Elsukov udp->uh_dport = htons(GRE_UDPPORT); 470aee793eeSAndrey V. Elsukov udp->uh_sport = htons(sc->gre_port); 471aee793eeSAndrey V. Elsukov udp->uh_sum = csum; 472aee793eeSAndrey V. Elsukov udp->uh_ulen = 0; 473aee793eeSAndrey V. Elsukov } 474aee793eeSAndrey V. Elsukov 475aee793eeSAndrey V. Elsukov void 476aee793eeSAndrey V. Elsukov gre_update_hdr(struct gre_softc *sc, struct grehdr *gh) 477a5185adeSAndrey V. Elsukov { 478f325335cSAndrey V. Elsukov uint32_t *opts; 479f325335cSAndrey V. Elsukov uint16_t flags; 480f325335cSAndrey V. Elsukov 481a5185adeSAndrey V. Elsukov sx_assert(&gre_ioctl_sx, SA_XLOCKED); 482a5185adeSAndrey V. Elsukov 483f325335cSAndrey V. Elsukov flags = 0; 484f325335cSAndrey V. Elsukov opts = gh->gre_opts; 485f325335cSAndrey V. Elsukov if (sc->gre_options & GRE_ENABLE_CSUM) { 486f325335cSAndrey V. Elsukov flags |= GRE_FLAGS_CP; 487f325335cSAndrey V. Elsukov sc->gre_hlen += 2 * sizeof(uint16_t); 488f325335cSAndrey V. Elsukov *opts++ = 0; 489f325335cSAndrey V. Elsukov } 490f325335cSAndrey V. Elsukov if (sc->gre_key != 0) { 491f325335cSAndrey V. Elsukov flags |= GRE_FLAGS_KP; 492f325335cSAndrey V. Elsukov sc->gre_hlen += sizeof(uint32_t); 493f325335cSAndrey V. Elsukov *opts++ = htonl(sc->gre_key); 494f325335cSAndrey V. Elsukov } 495f325335cSAndrey V. Elsukov if (sc->gre_options & GRE_ENABLE_SEQ) { 496f325335cSAndrey V. Elsukov flags |= GRE_FLAGS_SP; 497f325335cSAndrey V. Elsukov sc->gre_hlen += sizeof(uint32_t); 498f325335cSAndrey V. Elsukov *opts++ = 0; 499f325335cSAndrey V. Elsukov } else 500f325335cSAndrey V. Elsukov sc->gre_oseq = 0; 501f325335cSAndrey V. Elsukov gh->gre_flags = htons(flags); 502f325335cSAndrey V. Elsukov } 503f325335cSAndrey V. Elsukov 504f325335cSAndrey V. Elsukov int 5056d8fdfa9SAndrey V. Elsukov gre_input(struct mbuf *m, int off, int proto, void *arg) 506f325335cSAndrey V. Elsukov { 5076d8fdfa9SAndrey V. Elsukov struct gre_softc *sc = arg; 508f325335cSAndrey V. Elsukov struct grehdr *gh; 509f325335cSAndrey V. Elsukov struct ifnet *ifp; 5102bfd3dfbSMarcelo Araujo uint32_t *opts; 5112bfd3dfbSMarcelo Araujo #ifdef notyet 5122bfd3dfbSMarcelo Araujo uint32_t key; 5132bfd3dfbSMarcelo Araujo #endif 514f325335cSAndrey V. Elsukov uint16_t flags; 515f325335cSAndrey V. Elsukov int hlen, isr, af; 516f325335cSAndrey V. Elsukov 517f325335cSAndrey V. Elsukov ifp = GRE2IFP(sc); 5186d8fdfa9SAndrey V. Elsukov hlen = off + sizeof(struct grehdr) + 4 * sizeof(uint32_t); 51950bc87bcSAndrey V. Elsukov if (m->m_pkthdr.len < hlen) 52050bc87bcSAndrey V. Elsukov goto drop; 52150bc87bcSAndrey V. Elsukov if (m->m_len < hlen) { 52250bc87bcSAndrey V. Elsukov m = m_pullup(m, hlen); 52350bc87bcSAndrey V. Elsukov if (m == NULL) 52450bc87bcSAndrey V. Elsukov goto drop; 52550bc87bcSAndrey V. Elsukov } 5266d8fdfa9SAndrey V. Elsukov gh = (struct grehdr *)mtodo(m, off); 527f325335cSAndrey V. Elsukov flags = ntohs(gh->gre_flags); 528f325335cSAndrey V. Elsukov if (flags & ~GRE_FLAGS_MASK) 529f325335cSAndrey V. Elsukov goto drop; 530f325335cSAndrey V. Elsukov opts = gh->gre_opts; 531f325335cSAndrey V. Elsukov hlen = 2 * sizeof(uint16_t); 532f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_CP) { 533f325335cSAndrey V. Elsukov /* reserved1 field must be zero */ 534f325335cSAndrey V. Elsukov if (((uint16_t *)opts)[1] != 0) 535f325335cSAndrey V. Elsukov goto drop; 5366d8fdfa9SAndrey V. Elsukov if (in_cksum_skip(m, m->m_pkthdr.len, off) != 0) 537f325335cSAndrey V. Elsukov goto drop; 538f325335cSAndrey V. Elsukov hlen += 2 * sizeof(uint16_t); 539f325335cSAndrey V. Elsukov opts++; 540f325335cSAndrey V. Elsukov } 541f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_KP) { 5422bfd3dfbSMarcelo Araujo #ifdef notyet 5432bfd3dfbSMarcelo Araujo /* 5442bfd3dfbSMarcelo Araujo * XXX: The current implementation uses the key only for outgoing 5452bfd3dfbSMarcelo Araujo * packets. But we can check the key value here, or even in the 5462bfd3dfbSMarcelo Araujo * encapcheck function. 5472bfd3dfbSMarcelo Araujo */ 548f325335cSAndrey V. Elsukov key = ntohl(*opts); 5492bfd3dfbSMarcelo Araujo #endif 550f325335cSAndrey V. Elsukov hlen += sizeof(uint32_t); 551f325335cSAndrey V. Elsukov opts++; 5522bfd3dfbSMarcelo Araujo } 5532bfd3dfbSMarcelo Araujo #ifdef notyet 554f325335cSAndrey V. Elsukov } else 555f325335cSAndrey V. Elsukov key = 0; 5562bfd3dfbSMarcelo Araujo 557f325335cSAndrey V. Elsukov if (sc->gre_key != 0 && (key != sc->gre_key || key != 0)) 558f325335cSAndrey V. Elsukov goto drop; 5592bfd3dfbSMarcelo Araujo #endif 560f325335cSAndrey V. Elsukov if (flags & GRE_FLAGS_SP) { 5612bfd3dfbSMarcelo Araujo #ifdef notyet 5622bfd3dfbSMarcelo Araujo seq = ntohl(*opts); 5632bfd3dfbSMarcelo Araujo #endif 564f325335cSAndrey V. Elsukov hlen += sizeof(uint32_t); 565f325335cSAndrey V. Elsukov } 566f325335cSAndrey V. Elsukov switch (ntohs(gh->gre_proto)) { 567f325335cSAndrey V. Elsukov case ETHERTYPE_WCCP: 568f325335cSAndrey V. Elsukov /* 569f325335cSAndrey V. Elsukov * For WCCP skip an additional 4 bytes if after GRE header 570f325335cSAndrey V. Elsukov * doesn't follow an IP header. 571f325335cSAndrey V. Elsukov */ 572f325335cSAndrey V. Elsukov if (flags == 0 && (*(uint8_t *)gh->gre_opts & 0xF0) != 0x40) 573f325335cSAndrey V. Elsukov hlen += sizeof(uint32_t); 574f325335cSAndrey V. Elsukov /* FALLTHROUGH */ 575f325335cSAndrey V. Elsukov case ETHERTYPE_IP: 576f325335cSAndrey V. Elsukov isr = NETISR_IP; 577f325335cSAndrey V. Elsukov af = AF_INET; 578f325335cSAndrey V. Elsukov break; 579f325335cSAndrey V. Elsukov case ETHERTYPE_IPV6: 580f325335cSAndrey V. Elsukov isr = NETISR_IPV6; 581f325335cSAndrey V. Elsukov af = AF_INET6; 582f325335cSAndrey V. Elsukov break; 583f325335cSAndrey V. Elsukov default: 584f325335cSAndrey V. Elsukov goto drop; 585f325335cSAndrey V. Elsukov } 5866d8fdfa9SAndrey V. Elsukov m_adj(m, off + hlen); 587f325335cSAndrey V. Elsukov m_clrprotoflags(m); 588f325335cSAndrey V. Elsukov m->m_pkthdr.rcvif = ifp; 589eccfe69aSAndrey V. Elsukov M_SETFIB(m, ifp->if_fib); 590f325335cSAndrey V. Elsukov #ifdef MAC 591f325335cSAndrey V. Elsukov mac_ifnet_create_mbuf(ifp, m); 592f325335cSAndrey V. Elsukov #endif 593f325335cSAndrey V. Elsukov BPF_MTAP2(ifp, &af, sizeof(af), m); 594f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 595f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); 596f325335cSAndrey V. Elsukov if ((ifp->if_flags & IFF_MONITOR) != 0) 597f325335cSAndrey V. Elsukov m_freem(m); 5988e96e13eSMaxim Sobolev else 599f325335cSAndrey V. Elsukov netisr_dispatch(isr, m); 600f325335cSAndrey V. Elsukov return (IPPROTO_DONE); 601f325335cSAndrey V. Elsukov drop: 602f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 603f325335cSAndrey V. Elsukov m_freem(m); 604f325335cSAndrey V. Elsukov return (IPPROTO_DONE); 6058e96e13eSMaxim Sobolev } 6068e96e13eSMaxim Sobolev 607f325335cSAndrey V. Elsukov static int 608f325335cSAndrey V. Elsukov gre_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, 609f325335cSAndrey V. Elsukov struct route *ro) 610f325335cSAndrey V. Elsukov { 611f325335cSAndrey V. Elsukov uint32_t af; 612f325335cSAndrey V. Elsukov 613f325335cSAndrey V. Elsukov if (dst->sa_family == AF_UNSPEC) 614f325335cSAndrey V. Elsukov bcopy(dst->sa_data, &af, sizeof(af)); 615f325335cSAndrey V. Elsukov else 616*62e1a437SZhenlei 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