1c398230bSWarner Losh /*- 2fe267a55SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-NetBSD 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: ip_gre.c,v 1.29 2003/09/05 23:02:43 itojun Exp $ 358e96e13eSMaxim Sobolev */ 368e96e13eSMaxim Sobolev 374b421e2dSMike Silbersack #include <sys/cdefs.h> 384b421e2dSMike Silbersack __FBSDID("$FreeBSD$"); 394b421e2dSMike Silbersack 408e96e13eSMaxim Sobolev #include "opt_inet.h" 419e669156SBjoern A. Zeeb #include "opt_inet6.h" 428e96e13eSMaxim Sobolev 438e96e13eSMaxim Sobolev #include <sys/param.h> 44a5185adeSAndrey V. Elsukov #include <sys/jail.h> 458e96e13eSMaxim Sobolev #include <sys/systm.h> 468e96e13eSMaxim Sobolev #include <sys/socket.h> 47a5185adeSAndrey V. Elsukov #include <sys/sockio.h> 48a5185adeSAndrey V. Elsukov #include <sys/mbuf.h> 498e96e13eSMaxim Sobolev #include <sys/errno.h> 508e96e13eSMaxim Sobolev #include <sys/kernel.h> 51f325335cSAndrey V. Elsukov #include <sys/sysctl.h> 52a5185adeSAndrey V. Elsukov #include <sys/malloc.h> 53*6573d758SMatt Macy #include <sys/proc.h> 54a5185adeSAndrey V. Elsukov 558e96e13eSMaxim Sobolev #include <net/if.h> 5676039bc8SGleb Smirnoff #include <net/if_var.h> 57f325335cSAndrey V. Elsukov #include <net/vnet.h> 588e96e13eSMaxim Sobolev 598e96e13eSMaxim Sobolev #include <netinet/in.h> 608e96e13eSMaxim Sobolev #include <netinet/in_var.h> 618e96e13eSMaxim Sobolev #include <netinet/ip.h> 62f325335cSAndrey V. Elsukov #include <netinet/ip_encap.h> 638e96e13eSMaxim Sobolev #include <netinet/ip_var.h> 64f325335cSAndrey V. Elsukov 65f325335cSAndrey V. Elsukov #ifdef INET6 66f325335cSAndrey V. Elsukov #include <netinet/ip6.h> 678e96e13eSMaxim Sobolev #endif 688e96e13eSMaxim Sobolev 698e96e13eSMaxim Sobolev #include <net/if_gre.h> 708e96e13eSMaxim Sobolev 71f325335cSAndrey V. Elsukov #define GRE_TTL 30 72f325335cSAndrey V. Elsukov VNET_DEFINE(int, ip_gre_ttl) = GRE_TTL; 73f325335cSAndrey V. Elsukov #define V_ip_gre_ttl VNET(ip_gre_ttl) 74f325335cSAndrey V. Elsukov SYSCTL_INT(_net_inet_ip, OID_AUTO, grettl, CTLFLAG_VNET | CTLFLAG_RW, 756d8fdfa9SAndrey V. Elsukov &VNET_NAME(ip_gre_ttl), 0, "Default TTL value for encapsulated packets"); 768e96e13eSMaxim Sobolev 77a5185adeSAndrey V. Elsukov static VNET_DEFINE(struct gre_list *, ipv4_hashtbl) = NULL; 78a5185adeSAndrey V. Elsukov #define V_ipv4_hashtbl VNET(ipv4_hashtbl) 79a5185adeSAndrey V. Elsukov #define GRE_HASH(src, dst) (V_ipv4_hashtbl[\ 80a5185adeSAndrey V. Elsukov in_gre_hashval((src), (dst)) & (GRE_HASH_SIZE - 1)]) 81a5185adeSAndrey V. Elsukov #define GRE_HASH_SC(sc) GRE_HASH((sc)->gre_oip.ip_src.s_addr,\ 82a5185adeSAndrey V. Elsukov (sc)->gre_oip.ip_dst.s_addr) 838e96e13eSMaxim Sobolev 84a5185adeSAndrey V. Elsukov static uint32_t 85a5185adeSAndrey V. Elsukov in_gre_hashval(in_addr_t src, in_addr_t dst) 86a5185adeSAndrey V. Elsukov { 87a5185adeSAndrey V. Elsukov uint32_t ret; 88a5185adeSAndrey V. Elsukov 89a5185adeSAndrey V. Elsukov ret = fnv_32_buf(&src, sizeof(src), FNV1_32_INIT); 90a5185adeSAndrey V. Elsukov return (fnv_32_buf(&dst, sizeof(dst), ret)); 91a5185adeSAndrey V. Elsukov } 92a5185adeSAndrey V. Elsukov 93a5185adeSAndrey V. Elsukov static int 94a5185adeSAndrey V. Elsukov in_gre_checkdup(const struct gre_softc *sc, in_addr_t src, in_addr_t dst) 95a5185adeSAndrey V. Elsukov { 96a5185adeSAndrey V. Elsukov struct gre_softc *tmp; 97a5185adeSAndrey V. Elsukov 98a5185adeSAndrey V. Elsukov if (sc->gre_family == AF_INET && 99a5185adeSAndrey V. Elsukov sc->gre_oip.ip_src.s_addr == src && 100a5185adeSAndrey V. Elsukov sc->gre_oip.ip_dst.s_addr == dst) 101a5185adeSAndrey V. Elsukov return (EEXIST); 102a5185adeSAndrey V. Elsukov 103a5185adeSAndrey V. Elsukov CK_LIST_FOREACH(tmp, &GRE_HASH(src, dst), chain) { 104a5185adeSAndrey V. Elsukov if (tmp == sc) 105a5185adeSAndrey V. Elsukov continue; 106a5185adeSAndrey V. Elsukov if (tmp->gre_oip.ip_src.s_addr == src && 107a5185adeSAndrey V. Elsukov tmp->gre_oip.ip_dst.s_addr == dst) 108a5185adeSAndrey V. Elsukov return (EADDRNOTAVAIL); 109a5185adeSAndrey V. Elsukov } 110a5185adeSAndrey V. Elsukov return (0); 111a5185adeSAndrey V. Elsukov } 112a5185adeSAndrey V. Elsukov 113a5185adeSAndrey V. Elsukov static int 114a5185adeSAndrey V. Elsukov in_gre_lookup(const struct mbuf *m, int off, int proto, void **arg) 115a5185adeSAndrey V. Elsukov { 116a5185adeSAndrey V. Elsukov const struct ip *ip; 117a5185adeSAndrey V. Elsukov struct gre_softc *sc; 118a5185adeSAndrey V. Elsukov 1196e081509SAndrey V. Elsukov if (V_ipv4_hashtbl == NULL) 1206e081509SAndrey V. Elsukov return (0); 1216e081509SAndrey V. Elsukov 122*6573d758SMatt Macy MPASS(in_epoch(net_epoch_preempt)); 123a5185adeSAndrey V. Elsukov ip = mtod(m, const struct ip *); 124a5185adeSAndrey V. Elsukov CK_LIST_FOREACH(sc, &GRE_HASH(ip->ip_dst.s_addr, 125a5185adeSAndrey V. Elsukov ip->ip_src.s_addr), chain) { 126a5185adeSAndrey V. Elsukov /* 127a5185adeSAndrey V. Elsukov * This is an inbound packet, its ip_dst is source address 128a5185adeSAndrey V. Elsukov * in softc. 129a5185adeSAndrey V. Elsukov */ 130a5185adeSAndrey V. Elsukov if (sc->gre_oip.ip_src.s_addr == ip->ip_dst.s_addr && 131a5185adeSAndrey V. Elsukov sc->gre_oip.ip_dst.s_addr == ip->ip_src.s_addr) { 132f325335cSAndrey V. Elsukov if ((GRE2IFP(sc)->if_flags & IFF_UP) == 0) 133f325335cSAndrey V. Elsukov return (0); 134a5185adeSAndrey V. Elsukov *arg = sc; 135a5185adeSAndrey V. Elsukov return (ENCAP_DRV_LOOKUP); 136a5185adeSAndrey V. Elsukov } 137a5185adeSAndrey V. Elsukov } 138f325335cSAndrey V. Elsukov return (0); 1398e96e13eSMaxim Sobolev } 1408e96e13eSMaxim Sobolev 141a5185adeSAndrey V. Elsukov static void 142a5185adeSAndrey V. Elsukov in_gre_attach(struct gre_softc *sc) 143a5185adeSAndrey V. Elsukov { 144a5185adeSAndrey V. Elsukov 145a5185adeSAndrey V. Elsukov sc->gre_hlen = sizeof(struct greip); 146a5185adeSAndrey V. Elsukov sc->gre_oip.ip_v = IPVERSION; 147a5185adeSAndrey V. Elsukov sc->gre_oip.ip_hl = sizeof(struct ip) >> 2; 148a5185adeSAndrey V. Elsukov sc->gre_oip.ip_p = IPPROTO_GRE; 149a5185adeSAndrey V. Elsukov gre_updatehdr(sc, &sc->gre_gihdr->gi_gre); 150a5185adeSAndrey V. Elsukov CK_LIST_INSERT_HEAD(&GRE_HASH_SC(sc), sc, chain); 151a5185adeSAndrey V. Elsukov } 152a5185adeSAndrey V. Elsukov 153a5185adeSAndrey V. Elsukov void 154a5185adeSAndrey V. Elsukov in_gre_setopts(struct gre_softc *sc, u_long cmd, uint32_t value) 155a5185adeSAndrey V. Elsukov { 156a5185adeSAndrey V. Elsukov 157a5185adeSAndrey V. Elsukov MPASS(cmd == GRESKEY || cmd == GRESOPTS); 158a5185adeSAndrey V. Elsukov 159a5185adeSAndrey V. Elsukov /* NOTE: we are protected with gre_ioctl_sx lock */ 160a5185adeSAndrey V. Elsukov MPASS(sc->gre_family == AF_INET); 161a5185adeSAndrey V. Elsukov CK_LIST_REMOVE(sc, chain); 162a5185adeSAndrey V. Elsukov GRE_WAIT(); 163a5185adeSAndrey V. Elsukov if (cmd == GRESKEY) 164a5185adeSAndrey V. Elsukov sc->gre_key = value; 165a5185adeSAndrey V. Elsukov else 166a5185adeSAndrey V. Elsukov sc->gre_options = value; 167a5185adeSAndrey V. Elsukov in_gre_attach(sc); 168a5185adeSAndrey V. Elsukov } 169a5185adeSAndrey V. Elsukov 170a5185adeSAndrey V. Elsukov int 171a5185adeSAndrey V. Elsukov in_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_t data) 172a5185adeSAndrey V. Elsukov { 173a5185adeSAndrey V. Elsukov struct ifreq *ifr = (struct ifreq *)data; 174a5185adeSAndrey V. Elsukov struct sockaddr_in *dst, *src; 175a5185adeSAndrey V. Elsukov struct ip *ip; 176a5185adeSAndrey V. Elsukov int error; 177a5185adeSAndrey V. Elsukov 178a5185adeSAndrey V. Elsukov /* NOTE: we are protected with gre_ioctl_sx lock */ 179a5185adeSAndrey V. Elsukov error = EINVAL; 180a5185adeSAndrey V. Elsukov switch (cmd) { 181a5185adeSAndrey V. Elsukov case SIOCSIFPHYADDR: 182a5185adeSAndrey V. Elsukov src = &((struct in_aliasreq *)data)->ifra_addr; 183a5185adeSAndrey V. Elsukov dst = &((struct in_aliasreq *)data)->ifra_dstaddr; 184a5185adeSAndrey V. Elsukov 185a5185adeSAndrey V. Elsukov /* sanity checks */ 186a5185adeSAndrey V. Elsukov if (src->sin_family != dst->sin_family || 187a5185adeSAndrey V. Elsukov src->sin_family != AF_INET || 188a5185adeSAndrey V. Elsukov src->sin_len != dst->sin_len || 189a5185adeSAndrey V. Elsukov src->sin_len != sizeof(*src)) 190a5185adeSAndrey V. Elsukov break; 191a5185adeSAndrey V. Elsukov if (src->sin_addr.s_addr == INADDR_ANY || 192a5185adeSAndrey V. Elsukov dst->sin_addr.s_addr == INADDR_ANY) { 193a5185adeSAndrey V. Elsukov error = EADDRNOTAVAIL; 194a5185adeSAndrey V. Elsukov break; 195a5185adeSAndrey V. Elsukov } 196a5185adeSAndrey V. Elsukov if (V_ipv4_hashtbl == NULL) 197a5185adeSAndrey V. Elsukov V_ipv4_hashtbl = gre_hashinit(); 198a5185adeSAndrey V. Elsukov error = in_gre_checkdup(sc, src->sin_addr.s_addr, 199a5185adeSAndrey V. Elsukov dst->sin_addr.s_addr); 200a5185adeSAndrey V. Elsukov if (error == EADDRNOTAVAIL) 201a5185adeSAndrey V. Elsukov break; 202a5185adeSAndrey V. Elsukov if (error == EEXIST) { 203a5185adeSAndrey V. Elsukov /* Addresses are the same. Just return. */ 204a5185adeSAndrey V. Elsukov error = 0; 205a5185adeSAndrey V. Elsukov break; 206a5185adeSAndrey V. Elsukov } 207a5185adeSAndrey V. Elsukov ip = malloc(sizeof(struct greip) + 3 * sizeof(uint32_t), 208a5185adeSAndrey V. Elsukov M_GRE, M_WAITOK | M_ZERO); 209a5185adeSAndrey V. Elsukov ip->ip_src.s_addr = src->sin_addr.s_addr; 210a5185adeSAndrey V. Elsukov ip->ip_dst.s_addr = dst->sin_addr.s_addr; 211a5185adeSAndrey V. Elsukov if (sc->gre_family != 0) { 212a5185adeSAndrey V. Elsukov /* Detach existing tunnel first */ 213a5185adeSAndrey V. Elsukov CK_LIST_REMOVE(sc, chain); 214a5185adeSAndrey V. Elsukov GRE_WAIT(); 215a5185adeSAndrey V. Elsukov free(sc->gre_hdr, M_GRE); 216a5185adeSAndrey V. Elsukov /* XXX: should we notify about link state change? */ 217a5185adeSAndrey V. Elsukov } 218a5185adeSAndrey V. Elsukov sc->gre_family = AF_INET; 219a5185adeSAndrey V. Elsukov sc->gre_hdr = ip; 220a5185adeSAndrey V. Elsukov sc->gre_oseq = 0; 221a5185adeSAndrey V. Elsukov sc->gre_iseq = UINT32_MAX; 222a5185adeSAndrey V. Elsukov in_gre_attach(sc); 223a5185adeSAndrey V. Elsukov break; 224a5185adeSAndrey V. Elsukov case SIOCGIFPSRCADDR: 225a5185adeSAndrey V. Elsukov case SIOCGIFPDSTADDR: 226a5185adeSAndrey V. Elsukov if (sc->gre_family != AF_INET) { 227a5185adeSAndrey V. Elsukov error = EADDRNOTAVAIL; 228a5185adeSAndrey V. Elsukov break; 229a5185adeSAndrey V. Elsukov } 230a5185adeSAndrey V. Elsukov src = (struct sockaddr_in *)&ifr->ifr_addr; 231a5185adeSAndrey V. Elsukov memset(src, 0, sizeof(*src)); 232a5185adeSAndrey V. Elsukov src->sin_family = AF_INET; 233a5185adeSAndrey V. Elsukov src->sin_len = sizeof(*src); 234a5185adeSAndrey V. Elsukov src->sin_addr = (cmd == SIOCGIFPSRCADDR) ? 235a5185adeSAndrey V. Elsukov sc->gre_oip.ip_src: sc->gre_oip.ip_dst; 236a5185adeSAndrey V. Elsukov error = prison_if(curthread->td_ucred, (struct sockaddr *)src); 237a5185adeSAndrey V. Elsukov if (error != 0) 238a5185adeSAndrey V. Elsukov memset(src, 0, sizeof(*src)); 239a5185adeSAndrey V. Elsukov break; 240a5185adeSAndrey V. Elsukov } 241a5185adeSAndrey V. Elsukov return (error); 242a5185adeSAndrey V. Elsukov } 243a5185adeSAndrey V. Elsukov 244f325335cSAndrey V. Elsukov int 245f325335cSAndrey V. Elsukov in_gre_output(struct mbuf *m, int af, int hlen) 246f325335cSAndrey V. Elsukov { 247f325335cSAndrey V. Elsukov struct greip *gi; 24873d7ddbcSMaxim Sobolev 249f325335cSAndrey V. Elsukov gi = mtod(m, struct greip *); 250f325335cSAndrey V. Elsukov switch (af) { 251f325335cSAndrey V. Elsukov case AF_INET: 252f325335cSAndrey V. Elsukov /* 253f325335cSAndrey V. Elsukov * gre_transmit() has used M_PREPEND() that doesn't guarantee 254f325335cSAndrey V. Elsukov * m_data is contiguous more than hlen bytes. Use m_copydata() 255f325335cSAndrey V. Elsukov * here to avoid m_pullup(). 256f325335cSAndrey V. Elsukov */ 257f325335cSAndrey V. Elsukov m_copydata(m, hlen + offsetof(struct ip, ip_tos), 258f325335cSAndrey V. Elsukov sizeof(u_char), &gi->gi_ip.ip_tos); 259f325335cSAndrey V. Elsukov m_copydata(m, hlen + offsetof(struct ip, ip_id), 260f325335cSAndrey V. Elsukov sizeof(u_short), (caddr_t)&gi->gi_ip.ip_id); 2618e96e13eSMaxim Sobolev break; 2629e669156SBjoern A. Zeeb #ifdef INET6 263f325335cSAndrey V. Elsukov case AF_INET6: 264f325335cSAndrey V. Elsukov gi->gi_ip.ip_tos = 0; /* XXX */ 2656d947416SGleb Smirnoff ip_fillid(&gi->gi_ip); 2669e669156SBjoern A. Zeeb break; 2679e669156SBjoern A. Zeeb #endif 2688e96e13eSMaxim Sobolev } 269f325335cSAndrey V. Elsukov gi->gi_ip.ip_ttl = V_ip_gre_ttl; 270f325335cSAndrey V. Elsukov gi->gi_ip.ip_len = htons(m->m_pkthdr.len); 271f325335cSAndrey V. Elsukov return (ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL)); 2728e96e13eSMaxim Sobolev } 2738e96e13eSMaxim Sobolev 274a5185adeSAndrey V. Elsukov static const struct encaptab *ecookie = NULL; 2756d8fdfa9SAndrey V. Elsukov static const struct encap_config ipv4_encap_cfg = { 2766d8fdfa9SAndrey V. Elsukov .proto = IPPROTO_GRE, 2776d8fdfa9SAndrey V. Elsukov .min_length = sizeof(struct greip) + sizeof(struct ip), 278a5185adeSAndrey V. Elsukov .exact_match = ENCAP_DRV_LOOKUP, 279a5185adeSAndrey V. Elsukov .lookup = in_gre_lookup, 2806d8fdfa9SAndrey V. Elsukov .input = gre_input 2816d8fdfa9SAndrey V. Elsukov }; 2826d8fdfa9SAndrey V. Elsukov 283a5185adeSAndrey V. Elsukov void 284a5185adeSAndrey V. Elsukov in_gre_init(void) 2858e96e13eSMaxim Sobolev { 2868e96e13eSMaxim Sobolev 287a5185adeSAndrey V. Elsukov if (!IS_DEFAULT_VNET(curvnet)) 288a5185adeSAndrey V. Elsukov return; 289a5185adeSAndrey V. Elsukov ecookie = ip_encap_attach(&ipv4_encap_cfg, NULL, M_WAITOK); 290a5185adeSAndrey V. Elsukov } 291a5185adeSAndrey V. Elsukov 292a5185adeSAndrey V. Elsukov void 293a5185adeSAndrey V. Elsukov in_gre_uninit(void) 294a5185adeSAndrey V. Elsukov { 295a5185adeSAndrey V. Elsukov 296a5185adeSAndrey V. Elsukov if (IS_DEFAULT_VNET(curvnet)) 297a5185adeSAndrey V. Elsukov ip_encap_detach(ecookie); 298a5185adeSAndrey V. Elsukov if (V_ipv4_hashtbl != NULL) 299a5185adeSAndrey V. Elsukov gre_hashdestroy(V_ipv4_hashtbl); 3008e96e13eSMaxim Sobolev } 301