1f325335cSAndrey V. Elsukov /*- 2a5185adeSAndrey V. Elsukov * Copyright (c) 2014, 2018 Andrey V. Elsukov <ae@FreeBSD.org> 3f325335cSAndrey V. Elsukov * All rights reserved. 4f325335cSAndrey V. Elsukov * 5f325335cSAndrey V. Elsukov * Redistribution and use in source and binary forms, with or without 6f325335cSAndrey V. Elsukov * modification, are permitted provided that the following conditions 7f325335cSAndrey V. Elsukov * are met: 8f325335cSAndrey V. Elsukov * 9f325335cSAndrey V. Elsukov * 1. Redistributions of source code must retain the above copyright 10f325335cSAndrey V. Elsukov * notice, this list of conditions and the following disclaimer. 11f325335cSAndrey V. Elsukov * 2. Redistributions in binary form must reproduce the above copyright 12f325335cSAndrey V. Elsukov * notice, this list of conditions and the following disclaimer in the 13f325335cSAndrey V. Elsukov * documentation and/or other materials provided with the distribution. 14f325335cSAndrey V. Elsukov * 15f325335cSAndrey V. Elsukov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16f325335cSAndrey V. Elsukov * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17f325335cSAndrey V. Elsukov * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18f325335cSAndrey V. Elsukov * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19f325335cSAndrey V. Elsukov * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20f325335cSAndrey V. Elsukov * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21f325335cSAndrey V. Elsukov * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22f325335cSAndrey V. Elsukov * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23f325335cSAndrey V. Elsukov * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24f325335cSAndrey V. Elsukov * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25f325335cSAndrey V. Elsukov */ 26f325335cSAndrey V. Elsukov 27f325335cSAndrey V. Elsukov #include <sys/cdefs.h> 28f325335cSAndrey V. Elsukov __FBSDID("$FreeBSD$"); 29f325335cSAndrey V. Elsukov 30f325335cSAndrey V. Elsukov #include "opt_inet.h" 31f325335cSAndrey V. Elsukov #include "opt_inet6.h" 32f325335cSAndrey V. Elsukov 33f325335cSAndrey V. Elsukov #include <sys/param.h> 34a5185adeSAndrey V. Elsukov #include <sys/jail.h> 35f325335cSAndrey V. Elsukov #include <sys/systm.h> 36f325335cSAndrey V. Elsukov #include <sys/socket.h> 37aee793eeSAndrey V. Elsukov #include <sys/socketvar.h> 38f325335cSAndrey V. Elsukov #include <sys/sockio.h> 39f325335cSAndrey V. Elsukov #include <sys/mbuf.h> 40f325335cSAndrey V. Elsukov #include <sys/errno.h> 41f325335cSAndrey V. Elsukov #include <sys/kernel.h> 42f325335cSAndrey V. Elsukov #include <sys/sysctl.h> 43f325335cSAndrey V. Elsukov #include <sys/malloc.h> 446573d758SMatt Macy #include <sys/proc.h> 45f325335cSAndrey V. Elsukov 46f325335cSAndrey V. Elsukov #include <net/if.h> 47f325335cSAndrey V. Elsukov #include <net/if_var.h> 48f325335cSAndrey V. Elsukov #include <net/vnet.h> 49f325335cSAndrey V. Elsukov 50f325335cSAndrey V. Elsukov #include <netinet/in.h> 51f325335cSAndrey V. Elsukov #ifdef INET 52f325335cSAndrey V. Elsukov #include <net/ethernet.h> 53f325335cSAndrey V. Elsukov #include <netinet/ip.h> 54f325335cSAndrey V. Elsukov #endif 55aee793eeSAndrey V. Elsukov #include <netinet/in_pcb.h> 56f325335cSAndrey V. Elsukov #include <netinet/ip_encap.h> 57aee793eeSAndrey V. Elsukov #include <netinet/ip_var.h> 58f325335cSAndrey V. Elsukov #include <netinet/ip6.h> 59aee793eeSAndrey V. Elsukov #include <netinet/udp.h> 60aee793eeSAndrey V. Elsukov #include <netinet/udp_var.h> 61f325335cSAndrey V. Elsukov #include <netinet6/ip6_var.h> 62f325335cSAndrey V. Elsukov #include <netinet6/in6_var.h> 63a5185adeSAndrey V. Elsukov #include <netinet6/scope6_var.h> 64f325335cSAndrey V. Elsukov #include <net/if_gre.h> 65f325335cSAndrey V. Elsukov 66f325335cSAndrey V. Elsukov VNET_DEFINE(int, ip6_gre_hlim) = IPV6_DEFHLIM; 67f325335cSAndrey V. Elsukov #define V_ip6_gre_hlim VNET(ip6_gre_hlim) 68f325335cSAndrey V. Elsukov 69f325335cSAndrey V. Elsukov SYSCTL_DECL(_net_inet6_ip6); 70f325335cSAndrey V. Elsukov SYSCTL_INT(_net_inet6_ip6, OID_AUTO, grehlim, CTLFLAG_VNET | CTLFLAG_RW, 71f325335cSAndrey V. Elsukov &VNET_NAME(ip6_gre_hlim), 0, "Default hop limit for encapsulated packets"); 72f325335cSAndrey V. Elsukov 73aee793eeSAndrey V. Elsukov struct in6_gre_socket { 74aee793eeSAndrey V. Elsukov struct gre_socket base; 75aee793eeSAndrey V. Elsukov struct in6_addr addr; /* scope zone id is embedded */ 76aee793eeSAndrey V. Elsukov }; 77aee793eeSAndrey V. Elsukov VNET_DEFINE_STATIC(struct gre_sockets *, ipv6_sockets) = NULL; 785f901c92SAndrew Turner VNET_DEFINE_STATIC(struct gre_list *, ipv6_hashtbl) = NULL; 7919873f47SAndrey V. Elsukov VNET_DEFINE_STATIC(struct gre_list *, ipv6_srchashtbl) = NULL; 80aee793eeSAndrey V. Elsukov #define V_ipv6_sockets VNET(ipv6_sockets) 81a5185adeSAndrey V. Elsukov #define V_ipv6_hashtbl VNET(ipv6_hashtbl) 8219873f47SAndrey V. Elsukov #define V_ipv6_srchashtbl VNET(ipv6_srchashtbl) 83a5185adeSAndrey V. Elsukov #define GRE_HASH(src, dst) (V_ipv6_hashtbl[\ 84a5185adeSAndrey V. Elsukov in6_gre_hashval((src), (dst)) & (GRE_HASH_SIZE - 1)]) 8519873f47SAndrey V. Elsukov #define GRE_SRCHASH(src) (V_ipv6_srchashtbl[\ 8619873f47SAndrey V. Elsukov fnv_32_buf((src), sizeof(*src), FNV1_32_INIT) & (GRE_HASH_SIZE - 1)]) 87aee793eeSAndrey V. Elsukov #define GRE_SOCKHASH(src) (V_ipv6_sockets[\ 88aee793eeSAndrey V. Elsukov fnv_32_buf((src), sizeof(*src), FNV1_32_INIT) & (GRE_HASH_SIZE - 1)]) 89a5185adeSAndrey V. Elsukov #define GRE_HASH_SC(sc) GRE_HASH(&(sc)->gre_oip6.ip6_src,\ 90a5185adeSAndrey V. Elsukov &(sc)->gre_oip6.ip6_dst) 91a5185adeSAndrey V. Elsukov 92a5185adeSAndrey V. Elsukov static uint32_t 93a5185adeSAndrey V. Elsukov in6_gre_hashval(const struct in6_addr *src, const struct in6_addr *dst) 94f325335cSAndrey V. Elsukov { 95a5185adeSAndrey V. Elsukov uint32_t ret; 96f325335cSAndrey V. Elsukov 97a5185adeSAndrey V. Elsukov ret = fnv_32_buf(src, sizeof(*src), FNV1_32_INIT); 98a5185adeSAndrey V. Elsukov return (fnv_32_buf(dst, sizeof(*dst), ret)); 99a5185adeSAndrey V. Elsukov } 100f325335cSAndrey V. Elsukov 101aee793eeSAndrey V. Elsukov static struct gre_socket* 102aee793eeSAndrey V. Elsukov in6_gre_lookup_socket(const struct in6_addr *addr) 103aee793eeSAndrey V. Elsukov { 104aee793eeSAndrey V. Elsukov struct gre_socket *gs; 105aee793eeSAndrey V. Elsukov struct in6_gre_socket *s; 106aee793eeSAndrey V. Elsukov 107aee793eeSAndrey V. Elsukov CK_LIST_FOREACH(gs, &GRE_SOCKHASH(addr), chain) { 108aee793eeSAndrey V. Elsukov s = __containerof(gs, struct in6_gre_socket, base); 109aee793eeSAndrey V. Elsukov if (IN6_ARE_ADDR_EQUAL(&s->addr, addr)) 110aee793eeSAndrey V. Elsukov break; 111aee793eeSAndrey V. Elsukov } 112aee793eeSAndrey V. Elsukov return (gs); 113aee793eeSAndrey V. Elsukov } 114aee793eeSAndrey V. Elsukov 115a5185adeSAndrey V. Elsukov static int 116a5185adeSAndrey V. Elsukov in6_gre_checkdup(const struct gre_softc *sc, const struct in6_addr *src, 117aee793eeSAndrey V. Elsukov const struct in6_addr *dst, uint32_t opts) 118a5185adeSAndrey V. Elsukov { 119aee793eeSAndrey V. Elsukov struct gre_list *head; 120a5185adeSAndrey V. Elsukov struct gre_softc *tmp; 121aee793eeSAndrey V. Elsukov struct gre_socket *gs; 122f325335cSAndrey V. Elsukov 123a5185adeSAndrey V. Elsukov if (sc->gre_family == AF_INET6 && 124a5185adeSAndrey V. Elsukov IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_src, src) && 125aee793eeSAndrey V. Elsukov IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_dst, dst) && 126aee793eeSAndrey V. Elsukov (sc->gre_options & GRE_UDPENCAP) == (opts & GRE_UDPENCAP)) 127a5185adeSAndrey V. Elsukov return (EEXIST); 128f325335cSAndrey V. Elsukov 129aee793eeSAndrey V. Elsukov if (opts & GRE_UDPENCAP) { 130aee793eeSAndrey V. Elsukov gs = in6_gre_lookup_socket(src); 131aee793eeSAndrey V. Elsukov if (gs == NULL) 132aee793eeSAndrey V. Elsukov return (0); 133aee793eeSAndrey V. Elsukov head = &gs->list; 134aee793eeSAndrey V. Elsukov } else 135aee793eeSAndrey V. Elsukov head = &GRE_HASH(src, dst); 136aee793eeSAndrey V. Elsukov 137aee793eeSAndrey V. Elsukov CK_LIST_FOREACH(tmp, head, chain) { 138a5185adeSAndrey V. Elsukov if (tmp == sc) 139a5185adeSAndrey V. Elsukov continue; 140a5185adeSAndrey V. Elsukov if (IN6_ARE_ADDR_EQUAL(&tmp->gre_oip6.ip6_src, src) && 141a5185adeSAndrey V. Elsukov IN6_ARE_ADDR_EQUAL(&tmp->gre_oip6.ip6_dst, dst)) 142a5185adeSAndrey V. Elsukov return (EADDRNOTAVAIL); 143a5185adeSAndrey V. Elsukov } 144f325335cSAndrey V. Elsukov return (0); 145f325335cSAndrey V. Elsukov } 146f325335cSAndrey V. Elsukov 147a5185adeSAndrey V. Elsukov static int 148a5185adeSAndrey V. Elsukov in6_gre_lookup(const struct mbuf *m, int off, int proto, void **arg) 149a5185adeSAndrey V. Elsukov { 150a5185adeSAndrey V. Elsukov const struct ip6_hdr *ip6; 151a5185adeSAndrey V. Elsukov struct gre_softc *sc; 152a5185adeSAndrey V. Elsukov 1536e081509SAndrey V. Elsukov if (V_ipv6_hashtbl == NULL) 1546e081509SAndrey V. Elsukov return (0); 1556e081509SAndrey V. Elsukov 15697168be8SGleb Smirnoff NET_EPOCH_ASSERT(); 157a5185adeSAndrey V. Elsukov ip6 = mtod(m, const struct ip6_hdr *); 158a5185adeSAndrey V. Elsukov CK_LIST_FOREACH(sc, &GRE_HASH(&ip6->ip6_dst, &ip6->ip6_src), chain) { 159a5185adeSAndrey V. Elsukov /* 160a5185adeSAndrey V. Elsukov * This is an inbound packet, its ip6_dst is source address 161a5185adeSAndrey V. Elsukov * in softc. 162a5185adeSAndrey V. Elsukov */ 163a5185adeSAndrey V. Elsukov if (IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_src, 164a5185adeSAndrey V. Elsukov &ip6->ip6_dst) && 165a5185adeSAndrey V. Elsukov IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_dst, 166a5185adeSAndrey V. Elsukov &ip6->ip6_src)) { 167a5185adeSAndrey V. Elsukov if ((GRE2IFP(sc)->if_flags & IFF_UP) == 0) 168a5185adeSAndrey V. Elsukov return (0); 169a5185adeSAndrey V. Elsukov *arg = sc; 170a5185adeSAndrey V. Elsukov return (ENCAP_DRV_LOOKUP); 171a5185adeSAndrey V. Elsukov } 172a5185adeSAndrey V. Elsukov } 173a5185adeSAndrey V. Elsukov return (0); 174a5185adeSAndrey V. Elsukov } 175a5185adeSAndrey V. Elsukov 17619873f47SAndrey V. Elsukov /* 17719873f47SAndrey V. Elsukov * Check that ingress address belongs to local host. 17819873f47SAndrey V. Elsukov */ 17919873f47SAndrey V. Elsukov static void 18019873f47SAndrey V. Elsukov in6_gre_set_running(struct gre_softc *sc) 18119873f47SAndrey V. Elsukov { 18219873f47SAndrey V. Elsukov 18319873f47SAndrey V. Elsukov if (in6_localip(&sc->gre_oip6.ip6_src)) 18419873f47SAndrey V. Elsukov GRE2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING; 18519873f47SAndrey V. Elsukov else 18619873f47SAndrey V. Elsukov GRE2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING; 18719873f47SAndrey V. Elsukov } 18819873f47SAndrey V. Elsukov 18919873f47SAndrey V. Elsukov /* 19019873f47SAndrey V. Elsukov * ifaddr_event handler. 19119873f47SAndrey V. Elsukov * Clear IFF_DRV_RUNNING flag when ingress address disappears to prevent 19219873f47SAndrey V. Elsukov * source address spoofing. 19319873f47SAndrey V. Elsukov */ 19419873f47SAndrey V. Elsukov static void 19519873f47SAndrey V. Elsukov in6_gre_srcaddr(void *arg __unused, const struct sockaddr *sa, 19619873f47SAndrey V. Elsukov int event __unused) 19719873f47SAndrey V. Elsukov { 19819873f47SAndrey V. Elsukov const struct sockaddr_in6 *sin; 19919873f47SAndrey V. Elsukov struct gre_softc *sc; 20019873f47SAndrey V. Elsukov 2018796e291SAndrey V. Elsukov /* Check that VNET is ready */ 2028796e291SAndrey V. Elsukov if (V_ipv6_hashtbl == NULL) 20319873f47SAndrey V. Elsukov return; 20419873f47SAndrey V. Elsukov 20597168be8SGleb Smirnoff NET_EPOCH_ASSERT(); 20619873f47SAndrey V. Elsukov sin = (const struct sockaddr_in6 *)sa; 20719873f47SAndrey V. Elsukov CK_LIST_FOREACH(sc, &GRE_SRCHASH(&sin->sin6_addr), srchash) { 20819873f47SAndrey V. Elsukov if (IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_src, 20919873f47SAndrey V. Elsukov &sin->sin6_addr) == 0) 21019873f47SAndrey V. Elsukov continue; 21119873f47SAndrey V. Elsukov in6_gre_set_running(sc); 21219873f47SAndrey V. Elsukov } 21319873f47SAndrey V. Elsukov } 21419873f47SAndrey V. Elsukov 215742e7210SKristof Provost static bool 216aee793eeSAndrey V. Elsukov in6_gre_udp_input(struct mbuf *m, int off, struct inpcb *inp, 217aee793eeSAndrey V. Elsukov const struct sockaddr *sa, void *ctx) 218a5185adeSAndrey V. Elsukov { 219aee793eeSAndrey V. Elsukov struct gre_socket *gs; 220aee793eeSAndrey V. Elsukov struct gre_softc *sc; 221aee793eeSAndrey V. Elsukov struct sockaddr_in6 dst; 222a5185adeSAndrey V. Elsukov 223db0ac6deSCy Schubert NET_EPOCH_ASSERT(); 224a5185adeSAndrey V. Elsukov 225aee793eeSAndrey V. Elsukov gs = (struct gre_socket *)ctx; 226aee793eeSAndrey V. Elsukov dst = *(const struct sockaddr_in6 *)sa; 227aee793eeSAndrey V. Elsukov if (sa6_embedscope(&dst, 0)) { 228aee793eeSAndrey V. Elsukov m_freem(m); 229742e7210SKristof Provost return (true); 230aee793eeSAndrey V. Elsukov } 231aee793eeSAndrey V. Elsukov CK_LIST_FOREACH(sc, &gs->list, chain) { 232aee793eeSAndrey V. Elsukov if (IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_dst, &dst.sin6_addr)) 233aee793eeSAndrey V. Elsukov break; 234aee793eeSAndrey V. Elsukov } 235aee793eeSAndrey V. Elsukov if (sc != NULL && (GRE2IFP(sc)->if_flags & IFF_UP) != 0){ 236aee793eeSAndrey V. Elsukov gre_input(m, off + sizeof(struct udphdr), IPPROTO_UDP, sc); 237742e7210SKristof Provost return (true); 238aee793eeSAndrey V. Elsukov } 239aee793eeSAndrey V. Elsukov m_freem(m); 240742e7210SKristof Provost 241742e7210SKristof Provost return (true); 242aee793eeSAndrey V. Elsukov } 243aee793eeSAndrey V. Elsukov 244aee793eeSAndrey V. Elsukov static int 245aee793eeSAndrey V. Elsukov in6_gre_setup_socket(struct gre_softc *sc) 246aee793eeSAndrey V. Elsukov { 247aee793eeSAndrey V. Elsukov struct sockopt sopt; 248aee793eeSAndrey V. Elsukov struct sockaddr_in6 sin6; 249aee793eeSAndrey V. Elsukov struct in6_gre_socket *s; 250aee793eeSAndrey V. Elsukov struct gre_socket *gs; 251aee793eeSAndrey V. Elsukov int error, value; 252aee793eeSAndrey V. Elsukov 253aee793eeSAndrey V. Elsukov /* 254aee793eeSAndrey V. Elsukov * NOTE: we are protected with gre_ioctl_sx lock. 255aee793eeSAndrey V. Elsukov * 256aee793eeSAndrey V. Elsukov * First check that socket is already configured. 257*fa7de6dcSGordon Bergling * If so, check that source address was not changed. 258aee793eeSAndrey V. Elsukov * If address is different, check that there are no other tunnels 259aee793eeSAndrey V. Elsukov * and close socket. 260aee793eeSAndrey V. Elsukov */ 261aee793eeSAndrey V. Elsukov gs = sc->gre_so; 262aee793eeSAndrey V. Elsukov if (gs != NULL) { 263aee793eeSAndrey V. Elsukov s = __containerof(gs, struct in6_gre_socket, base); 264aee793eeSAndrey V. Elsukov if (!IN6_ARE_ADDR_EQUAL(&s->addr, &sc->gre_oip6.ip6_src)) { 265aee793eeSAndrey V. Elsukov if (CK_LIST_EMPTY(&gs->list)) { 266aee793eeSAndrey V. Elsukov CK_LIST_REMOVE(gs, chain); 267aee793eeSAndrey V. Elsukov soclose(gs->so); 2682a4bd982SGleb Smirnoff NET_EPOCH_CALL(gre_sofree, &gs->epoch_ctx); 269aee793eeSAndrey V. Elsukov } 270aee793eeSAndrey V. Elsukov gs = sc->gre_so = NULL; 271aee793eeSAndrey V. Elsukov } 272aee793eeSAndrey V. Elsukov } 273aee793eeSAndrey V. Elsukov 274aee793eeSAndrey V. Elsukov if (gs == NULL) { 275aee793eeSAndrey V. Elsukov /* 276aee793eeSAndrey V. Elsukov * Check that socket for given address is already 277aee793eeSAndrey V. Elsukov * configured. 278aee793eeSAndrey V. Elsukov */ 279aee793eeSAndrey V. Elsukov gs = in6_gre_lookup_socket(&sc->gre_oip6.ip6_src); 280aee793eeSAndrey V. Elsukov if (gs == NULL) { 281aee793eeSAndrey V. Elsukov s = malloc(sizeof(*s), M_GRE, M_WAITOK | M_ZERO); 282aee793eeSAndrey V. Elsukov s->addr = sc->gre_oip6.ip6_src; 283aee793eeSAndrey V. Elsukov gs = &s->base; 284aee793eeSAndrey V. Elsukov 285aee793eeSAndrey V. Elsukov error = socreate(sc->gre_family, &gs->so, 286aee793eeSAndrey V. Elsukov SOCK_DGRAM, IPPROTO_UDP, curthread->td_ucred, 287aee793eeSAndrey V. Elsukov curthread); 288aee793eeSAndrey V. Elsukov if (error != 0) { 289aee793eeSAndrey V. Elsukov if_printf(GRE2IFP(sc), 290aee793eeSAndrey V. Elsukov "cannot create socket: %d\n", error); 291aee793eeSAndrey V. Elsukov free(s, M_GRE); 292aee793eeSAndrey V. Elsukov return (error); 293aee793eeSAndrey V. Elsukov } 294aee793eeSAndrey V. Elsukov 295aee793eeSAndrey V. Elsukov error = udp_set_kernel_tunneling(gs->so, 296aee793eeSAndrey V. Elsukov in6_gre_udp_input, NULL, gs); 297aee793eeSAndrey V. Elsukov if (error != 0) { 298aee793eeSAndrey V. Elsukov if_printf(GRE2IFP(sc), 299aee793eeSAndrey V. Elsukov "cannot set UDP tunneling: %d\n", error); 300aee793eeSAndrey V. Elsukov goto fail; 301aee793eeSAndrey V. Elsukov } 302aee793eeSAndrey V. Elsukov 303aee793eeSAndrey V. Elsukov memset(&sopt, 0, sizeof(sopt)); 304aee793eeSAndrey V. Elsukov sopt.sopt_dir = SOPT_SET; 305aee793eeSAndrey V. Elsukov sopt.sopt_level = IPPROTO_IPV6; 306aee793eeSAndrey V. Elsukov sopt.sopt_name = IPV6_BINDANY; 307aee793eeSAndrey V. Elsukov sopt.sopt_val = &value; 308aee793eeSAndrey V. Elsukov sopt.sopt_valsize = sizeof(value); 309aee793eeSAndrey V. Elsukov value = 1; 310aee793eeSAndrey V. Elsukov error = sosetopt(gs->so, &sopt); 311aee793eeSAndrey V. Elsukov if (error != 0) { 312aee793eeSAndrey V. Elsukov if_printf(GRE2IFP(sc), 313aee793eeSAndrey V. Elsukov "cannot set IPV6_BINDANY opt: %d\n", 314aee793eeSAndrey V. Elsukov error); 315aee793eeSAndrey V. Elsukov goto fail; 316aee793eeSAndrey V. Elsukov } 317aee793eeSAndrey V. Elsukov 318aee793eeSAndrey V. Elsukov memset(&sin6, 0, sizeof(sin6)); 319aee793eeSAndrey V. Elsukov sin6.sin6_family = AF_INET6; 320aee793eeSAndrey V. Elsukov sin6.sin6_len = sizeof(sin6); 321aee793eeSAndrey V. Elsukov sin6.sin6_addr = sc->gre_oip6.ip6_src; 322aee793eeSAndrey V. Elsukov sin6.sin6_port = htons(GRE_UDPPORT); 323aee793eeSAndrey V. Elsukov error = sa6_recoverscope(&sin6); 324aee793eeSAndrey V. Elsukov if (error != 0) { 325aee793eeSAndrey V. Elsukov if_printf(GRE2IFP(sc), 326aee793eeSAndrey V. Elsukov "cannot determine scope zone id: %d\n", 327aee793eeSAndrey V. Elsukov error); 328aee793eeSAndrey V. Elsukov goto fail; 329aee793eeSAndrey V. Elsukov } 330aee793eeSAndrey V. Elsukov error = sobind(gs->so, (struct sockaddr *)&sin6, 331aee793eeSAndrey V. Elsukov curthread); 332aee793eeSAndrey V. Elsukov if (error != 0) { 333aee793eeSAndrey V. Elsukov if_printf(GRE2IFP(sc), 334aee793eeSAndrey V. Elsukov "cannot bind socket: %d\n", error); 335aee793eeSAndrey V. Elsukov goto fail; 336aee793eeSAndrey V. Elsukov } 337aee793eeSAndrey V. Elsukov /* Add socket to the chain */ 338aee793eeSAndrey V. Elsukov CK_LIST_INSERT_HEAD( 339aee793eeSAndrey V. Elsukov &GRE_SOCKHASH(&sc->gre_oip6.ip6_src), gs, chain); 340aee793eeSAndrey V. Elsukov } 341aee793eeSAndrey V. Elsukov } 342aee793eeSAndrey V. Elsukov 343aee793eeSAndrey V. Elsukov /* Add softc to the socket's list */ 344aee793eeSAndrey V. Elsukov CK_LIST_INSERT_HEAD(&gs->list, sc, chain); 345aee793eeSAndrey V. Elsukov sc->gre_so = gs; 346aee793eeSAndrey V. Elsukov return (0); 347aee793eeSAndrey V. Elsukov fail: 348aee793eeSAndrey V. Elsukov soclose(gs->so); 349aee793eeSAndrey V. Elsukov free(s, M_GRE); 350aee793eeSAndrey V. Elsukov return (error); 351aee793eeSAndrey V. Elsukov } 352aee793eeSAndrey V. Elsukov 353aee793eeSAndrey V. Elsukov static int 354aee793eeSAndrey V. Elsukov in6_gre_attach(struct gre_softc *sc) 355aee793eeSAndrey V. Elsukov { 356aee793eeSAndrey V. Elsukov struct grehdr *gh; 357aee793eeSAndrey V. Elsukov int error; 358aee793eeSAndrey V. Elsukov 359aee793eeSAndrey V. Elsukov if (sc->gre_options & GRE_UDPENCAP) { 360aee793eeSAndrey V. Elsukov sc->gre_csumflags = CSUM_UDP_IPV6; 361aee793eeSAndrey V. Elsukov sc->gre_hlen = sizeof(struct greudp6); 362aee793eeSAndrey V. Elsukov sc->gre_oip6.ip6_nxt = IPPROTO_UDP; 363aee793eeSAndrey V. Elsukov gh = &sc->gre_udp6hdr->gi6_gre; 364aee793eeSAndrey V. Elsukov gre_update_udphdr(sc, &sc->gre_udp6, 365aee793eeSAndrey V. Elsukov in6_cksum_pseudo(&sc->gre_oip6, 0, 0, 0)); 366aee793eeSAndrey V. Elsukov } else { 367aee793eeSAndrey V. Elsukov sc->gre_hlen = sizeof(struct greip6); 368aee793eeSAndrey V. Elsukov sc->gre_oip6.ip6_nxt = IPPROTO_GRE; 369aee793eeSAndrey V. Elsukov gh = &sc->gre_ip6hdr->gi6_gre; 370aee793eeSAndrey V. Elsukov } 371aee793eeSAndrey V. Elsukov sc->gre_oip6.ip6_vfc = IPV6_VERSION; 372aee793eeSAndrey V. Elsukov gre_update_hdr(sc, gh); 373aee793eeSAndrey V. Elsukov 374aee793eeSAndrey V. Elsukov /* 375aee793eeSAndrey V. Elsukov * If we return error, this means that sc is not linked, 376aee793eeSAndrey V. Elsukov * and caller should reset gre_family and free(sc->gre_hdr). 377aee793eeSAndrey V. Elsukov */ 378aee793eeSAndrey V. Elsukov if (sc->gre_options & GRE_UDPENCAP) { 379aee793eeSAndrey V. Elsukov error = in6_gre_setup_socket(sc); 380aee793eeSAndrey V. Elsukov if (error != 0) 381aee793eeSAndrey V. Elsukov return (error); 382aee793eeSAndrey V. Elsukov } else 383aee793eeSAndrey V. Elsukov CK_LIST_INSERT_HEAD(&GRE_HASH_SC(sc), sc, chain); 384aee793eeSAndrey V. Elsukov CK_LIST_INSERT_HEAD(&GRE_SRCHASH(&sc->gre_oip6.ip6_src), sc, srchash); 385aee793eeSAndrey V. Elsukov 386aee793eeSAndrey V. Elsukov /* Set IFF_DRV_RUNNING if interface is ready */ 387aee793eeSAndrey V. Elsukov in6_gre_set_running(sc); 388aee793eeSAndrey V. Elsukov return (0); 389aee793eeSAndrey V. Elsukov } 390aee793eeSAndrey V. Elsukov 391aee793eeSAndrey V. Elsukov int 392a5185adeSAndrey V. Elsukov in6_gre_setopts(struct gre_softc *sc, u_long cmd, uint32_t value) 393a5185adeSAndrey V. Elsukov { 394aee793eeSAndrey V. Elsukov int error; 395a5185adeSAndrey V. Elsukov 396a5185adeSAndrey V. Elsukov /* NOTE: we are protected with gre_ioctl_sx lock */ 397aee793eeSAndrey V. Elsukov MPASS(cmd == GRESKEY || cmd == GRESOPTS || cmd == GRESPORT); 398a5185adeSAndrey V. Elsukov MPASS(sc->gre_family == AF_INET6); 399aee793eeSAndrey V. Elsukov 400aee793eeSAndrey V. Elsukov /* 401aee793eeSAndrey V. Elsukov * If we are going to change encapsulation protocol, do check 402aee793eeSAndrey V. Elsukov * for duplicate tunnels. Return EEXIST here to do not confuse 403aee793eeSAndrey V. Elsukov * user. 404aee793eeSAndrey V. Elsukov */ 405aee793eeSAndrey V. Elsukov if (cmd == GRESOPTS && 406aee793eeSAndrey V. Elsukov (sc->gre_options & GRE_UDPENCAP) != (value & GRE_UDPENCAP) && 407aee793eeSAndrey V. Elsukov in6_gre_checkdup(sc, &sc->gre_oip6.ip6_src, 408aee793eeSAndrey V. Elsukov &sc->gre_oip6.ip6_dst, value) == EADDRNOTAVAIL) 409aee793eeSAndrey V. Elsukov return (EEXIST); 410aee793eeSAndrey V. Elsukov 411a5185adeSAndrey V. Elsukov CK_LIST_REMOVE(sc, chain); 41219873f47SAndrey V. Elsukov CK_LIST_REMOVE(sc, srchash); 413a5185adeSAndrey V. Elsukov GRE_WAIT(); 414aee793eeSAndrey V. Elsukov switch (cmd) { 415aee793eeSAndrey V. Elsukov case GRESKEY: 416a5185adeSAndrey V. Elsukov sc->gre_key = value; 417aee793eeSAndrey V. Elsukov break; 418aee793eeSAndrey V. Elsukov case GRESOPTS: 419a5185adeSAndrey V. Elsukov sc->gre_options = value; 420aee793eeSAndrey V. Elsukov break; 421aee793eeSAndrey V. Elsukov case GRESPORT: 422aee793eeSAndrey V. Elsukov sc->gre_port = value; 423aee793eeSAndrey V. Elsukov break; 424aee793eeSAndrey V. Elsukov } 425aee793eeSAndrey V. Elsukov error = in6_gre_attach(sc); 426aee793eeSAndrey V. Elsukov if (error != 0) { 427aee793eeSAndrey V. Elsukov sc->gre_family = 0; 428aee793eeSAndrey V. Elsukov free(sc->gre_hdr, M_GRE); 429aee793eeSAndrey V. Elsukov } 430aee793eeSAndrey V. Elsukov return (error); 431a5185adeSAndrey V. Elsukov } 432a5185adeSAndrey V. Elsukov 433f325335cSAndrey V. Elsukov int 434a5185adeSAndrey V. Elsukov in6_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_t data) 435a5185adeSAndrey V. Elsukov { 436a5185adeSAndrey V. Elsukov struct in6_ifreq *ifr = (struct in6_ifreq *)data; 437a5185adeSAndrey V. Elsukov struct sockaddr_in6 *dst, *src; 438a5185adeSAndrey V. Elsukov struct ip6_hdr *ip6; 439a5185adeSAndrey V. Elsukov int error; 440a5185adeSAndrey V. Elsukov 441a5185adeSAndrey V. Elsukov /* NOTE: we are protected with gre_ioctl_sx lock */ 442a5185adeSAndrey V. Elsukov error = EINVAL; 443a5185adeSAndrey V. Elsukov switch (cmd) { 444a5185adeSAndrey V. Elsukov case SIOCSIFPHYADDR_IN6: 445a5185adeSAndrey V. Elsukov src = &((struct in6_aliasreq *)data)->ifra_addr; 446a5185adeSAndrey V. Elsukov dst = &((struct in6_aliasreq *)data)->ifra_dstaddr; 447a5185adeSAndrey V. Elsukov 448a5185adeSAndrey V. Elsukov /* sanity checks */ 449a5185adeSAndrey V. Elsukov if (src->sin6_family != dst->sin6_family || 450a5185adeSAndrey V. Elsukov src->sin6_family != AF_INET6 || 451a5185adeSAndrey V. Elsukov src->sin6_len != dst->sin6_len || 452a5185adeSAndrey V. Elsukov src->sin6_len != sizeof(*src)) 453a5185adeSAndrey V. Elsukov break; 454a5185adeSAndrey V. Elsukov if (IN6_IS_ADDR_UNSPECIFIED(&src->sin6_addr) || 455a5185adeSAndrey V. Elsukov IN6_IS_ADDR_UNSPECIFIED(&dst->sin6_addr)) { 456a5185adeSAndrey V. Elsukov error = EADDRNOTAVAIL; 457a5185adeSAndrey V. Elsukov break; 458a5185adeSAndrey V. Elsukov } 459a5185adeSAndrey V. Elsukov /* 460a5185adeSAndrey V. Elsukov * Check validity of the scope zone ID of the 461a5185adeSAndrey V. Elsukov * addresses, and convert it into the kernel 462a5185adeSAndrey V. Elsukov * internal form if necessary. 463a5185adeSAndrey V. Elsukov */ 464a5185adeSAndrey V. Elsukov if ((error = sa6_embedscope(src, 0)) != 0 || 465a5185adeSAndrey V. Elsukov (error = sa6_embedscope(dst, 0)) != 0) 466a5185adeSAndrey V. Elsukov break; 467a5185adeSAndrey V. Elsukov 46819873f47SAndrey V. Elsukov if (V_ipv6_hashtbl == NULL) { 469a5185adeSAndrey V. Elsukov V_ipv6_hashtbl = gre_hashinit(); 47019873f47SAndrey V. Elsukov V_ipv6_srchashtbl = gre_hashinit(); 471aee793eeSAndrey V. Elsukov V_ipv6_sockets = (struct gre_sockets *)gre_hashinit(); 47219873f47SAndrey V. Elsukov } 473a5185adeSAndrey V. Elsukov error = in6_gre_checkdup(sc, &src->sin6_addr, 474aee793eeSAndrey V. Elsukov &dst->sin6_addr, sc->gre_options); 475a5185adeSAndrey V. Elsukov if (error == EADDRNOTAVAIL) 476a5185adeSAndrey V. Elsukov break; 477a5185adeSAndrey V. Elsukov if (error == EEXIST) { 478a5185adeSAndrey V. Elsukov /* Addresses are the same. Just return. */ 479a5185adeSAndrey V. Elsukov error = 0; 480a5185adeSAndrey V. Elsukov break; 481a5185adeSAndrey V. Elsukov } 482aee793eeSAndrey V. Elsukov ip6 = malloc(sizeof(struct greudp6) + 3 * sizeof(uint32_t), 483a5185adeSAndrey V. Elsukov M_GRE, M_WAITOK | M_ZERO); 484a5185adeSAndrey V. Elsukov ip6->ip6_src = src->sin6_addr; 485a5185adeSAndrey V. Elsukov ip6->ip6_dst = dst->sin6_addr; 486a5185adeSAndrey V. Elsukov if (sc->gre_family != 0) { 487a5185adeSAndrey V. Elsukov /* Detach existing tunnel first */ 488a5185adeSAndrey V. Elsukov CK_LIST_REMOVE(sc, chain); 48919873f47SAndrey V. Elsukov CK_LIST_REMOVE(sc, srchash); 490a5185adeSAndrey V. Elsukov GRE_WAIT(); 491a5185adeSAndrey V. Elsukov free(sc->gre_hdr, M_GRE); 492a5185adeSAndrey V. Elsukov /* XXX: should we notify about link state change? */ 493a5185adeSAndrey V. Elsukov } 494a5185adeSAndrey V. Elsukov sc->gre_family = AF_INET6; 495a5185adeSAndrey V. Elsukov sc->gre_hdr = ip6; 496a5185adeSAndrey V. Elsukov sc->gre_oseq = 0; 497a5185adeSAndrey V. Elsukov sc->gre_iseq = UINT32_MAX; 498aee793eeSAndrey V. Elsukov error = in6_gre_attach(sc); 499aee793eeSAndrey V. Elsukov if (error != 0) { 500aee793eeSAndrey V. Elsukov sc->gre_family = 0; 501aee793eeSAndrey V. Elsukov free(sc->gre_hdr, M_GRE); 502aee793eeSAndrey V. Elsukov } 503a5185adeSAndrey V. Elsukov break; 504a5185adeSAndrey V. Elsukov case SIOCGIFPSRCADDR_IN6: 505a5185adeSAndrey V. Elsukov case SIOCGIFPDSTADDR_IN6: 506a5185adeSAndrey V. Elsukov if (sc->gre_family != AF_INET6) { 507a5185adeSAndrey V. Elsukov error = EADDRNOTAVAIL; 508a5185adeSAndrey V. Elsukov break; 509a5185adeSAndrey V. Elsukov } 510a5185adeSAndrey V. Elsukov src = (struct sockaddr_in6 *)&ifr->ifr_addr; 511a5185adeSAndrey V. Elsukov memset(src, 0, sizeof(*src)); 512a5185adeSAndrey V. Elsukov src->sin6_family = AF_INET6; 513a5185adeSAndrey V. Elsukov src->sin6_len = sizeof(*src); 514a5185adeSAndrey V. Elsukov src->sin6_addr = (cmd == SIOCGIFPSRCADDR_IN6) ? 515a5185adeSAndrey V. Elsukov sc->gre_oip6.ip6_src: sc->gre_oip6.ip6_dst; 516a5185adeSAndrey V. Elsukov error = prison_if(curthread->td_ucred, (struct sockaddr *)src); 517a5185adeSAndrey V. Elsukov if (error == 0) 518a5185adeSAndrey V. Elsukov error = sa6_recoverscope(src); 519a5185adeSAndrey V. Elsukov if (error != 0) 520a5185adeSAndrey V. Elsukov memset(src, 0, sizeof(*src)); 521a5185adeSAndrey V. Elsukov break; 522a5185adeSAndrey V. Elsukov } 523a5185adeSAndrey V. Elsukov return (error); 524a5185adeSAndrey V. Elsukov } 525a5185adeSAndrey V. Elsukov 526a5185adeSAndrey V. Elsukov int 527aee793eeSAndrey V. Elsukov in6_gre_output(struct mbuf *m, int af __unused, int hlen __unused, 528aee793eeSAndrey V. Elsukov uint32_t flowid) 529f325335cSAndrey V. Elsukov { 530f325335cSAndrey V. Elsukov struct greip6 *gi6; 531f325335cSAndrey V. Elsukov 532f325335cSAndrey V. Elsukov gi6 = mtod(m, struct greip6 *); 533f325335cSAndrey V. Elsukov gi6->gi6_ip6.ip6_hlim = V_ip6_gre_hlim; 534aee793eeSAndrey V. Elsukov gi6->gi6_ip6.ip6_flow |= flowid & IPV6_FLOWLABEL_MASK; 535f325335cSAndrey V. Elsukov return (ip6_output(m, NULL, NULL, IPV6_MINMTU, NULL, NULL, NULL)); 536f325335cSAndrey V. Elsukov } 537f325335cSAndrey V. Elsukov 53819873f47SAndrey V. Elsukov static const struct srcaddrtab *ipv6_srcaddrtab = NULL; 539a5185adeSAndrey V. Elsukov static const struct encaptab *ecookie = NULL; 5406d8fdfa9SAndrey V. Elsukov static const struct encap_config ipv6_encap_cfg = { 5416d8fdfa9SAndrey V. Elsukov .proto = IPPROTO_GRE, 542a41372abSAndrey V. Elsukov .min_length = sizeof(struct greip6) + 543a41372abSAndrey V. Elsukov #ifdef INET 544a41372abSAndrey V. Elsukov sizeof(struct ip), 545a41372abSAndrey V. Elsukov #else 546a41372abSAndrey V. Elsukov sizeof(struct ip6_hdr), 547a41372abSAndrey V. Elsukov #endif 548a5185adeSAndrey V. Elsukov .exact_match = ENCAP_DRV_LOOKUP, 549a5185adeSAndrey V. Elsukov .lookup = in6_gre_lookup, 5506d8fdfa9SAndrey V. Elsukov .input = gre_input 5516d8fdfa9SAndrey V. Elsukov }; 5526d8fdfa9SAndrey V. Elsukov 553a5185adeSAndrey V. Elsukov void 554a5185adeSAndrey V. Elsukov in6_gre_init(void) 555f325335cSAndrey V. Elsukov { 556f325335cSAndrey V. Elsukov 557a5185adeSAndrey V. Elsukov if (!IS_DEFAULT_VNET(curvnet)) 558a5185adeSAndrey V. Elsukov return; 55919873f47SAndrey V. Elsukov ipv6_srcaddrtab = ip6_encap_register_srcaddr(in6_gre_srcaddr, 56019873f47SAndrey V. Elsukov NULL, M_WAITOK); 561a5185adeSAndrey V. Elsukov ecookie = ip6_encap_attach(&ipv6_encap_cfg, NULL, M_WAITOK); 562a5185adeSAndrey V. Elsukov } 563a5185adeSAndrey V. Elsukov 564a5185adeSAndrey V. Elsukov void 565a5185adeSAndrey V. Elsukov in6_gre_uninit(void) 566a5185adeSAndrey V. Elsukov { 567a5185adeSAndrey V. Elsukov 56819873f47SAndrey V. Elsukov if (IS_DEFAULT_VNET(curvnet)) { 569a5185adeSAndrey V. Elsukov ip6_encap_detach(ecookie); 57019873f47SAndrey V. Elsukov ip6_encap_unregister_srcaddr(ipv6_srcaddrtab); 57119873f47SAndrey V. Elsukov } 57219873f47SAndrey V. Elsukov if (V_ipv6_hashtbl != NULL) { 573a5185adeSAndrey V. Elsukov gre_hashdestroy(V_ipv6_hashtbl); 5748796e291SAndrey V. Elsukov V_ipv6_hashtbl = NULL; 5758796e291SAndrey V. Elsukov GRE_WAIT(); 57619873f47SAndrey V. Elsukov gre_hashdestroy(V_ipv6_srchashtbl); 577aee793eeSAndrey V. Elsukov gre_hashdestroy((struct gre_list *)V_ipv6_sockets); 57819873f47SAndrey V. Elsukov } 579f325335cSAndrey V. Elsukov } 580