2 * Copyright (c) 2014, 2018 Andrey V. Elsukov <ae@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
31 #include "opt_inet6.h"
33 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/socket.h>
37 #include <sys/sockio.h>
39 #include <sys/errno.h>
40 #include <sys/kernel.h>
41 #include <sys/sysctl.h>
42 #include <sys/malloc.h>
46 #include <net/if_var.h>
49 #include <netinet/in.h>
51 #include <net/ethernet.h>
52 #include <netinet/ip.h>
54 #include <netinet/ip_encap.h>
55 #include <netinet/ip6.h>
56 #include <netinet6/ip6_var.h>
57 #include <netinet6/in6_var.h>
58 #include <netinet6/scope6_var.h>
59 #include <net/if_gre.h>
61 VNET_DEFINE(int, ip6_gre_hlim) = IPV6_DEFHLIM;
62 #define V_ip6_gre_hlim VNET(ip6_gre_hlim)
64 SYSCTL_DECL(_net_inet6_ip6);
65 SYSCTL_INT(_net_inet6_ip6, OID_AUTO, grehlim, CTLFLAG_VNET | CTLFLAG_RW,
66 &VNET_NAME(ip6_gre_hlim), 0, "Default hop limit for encapsulated packets");
68 VNET_DEFINE_STATIC(struct gre_list *, ipv6_hashtbl) = NULL;
69 VNET_DEFINE_STATIC(struct gre_list *, ipv6_srchashtbl) = NULL;
70 #define V_ipv6_hashtbl VNET(ipv6_hashtbl)
71 #define V_ipv6_srchashtbl VNET(ipv6_srchashtbl)
72 #define GRE_HASH(src, dst) (V_ipv6_hashtbl[\
73 in6_gre_hashval((src), (dst)) & (GRE_HASH_SIZE - 1)])
74 #define GRE_SRCHASH(src) (V_ipv6_srchashtbl[\
75 fnv_32_buf((src), sizeof(*src), FNV1_32_INIT) & (GRE_HASH_SIZE - 1)])
76 #define GRE_HASH_SC(sc) GRE_HASH(&(sc)->gre_oip6.ip6_src,\
77 &(sc)->gre_oip6.ip6_dst)
80 in6_gre_hashval(const struct in6_addr *src, const struct in6_addr *dst)
84 ret = fnv_32_buf(src, sizeof(*src), FNV1_32_INIT);
85 return (fnv_32_buf(dst, sizeof(*dst), ret));
89 in6_gre_checkdup(const struct gre_softc *sc, const struct in6_addr *src,
90 const struct in6_addr *dst)
92 struct gre_softc *tmp;
94 if (sc->gre_family == AF_INET6 &&
95 IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_src, src) &&
96 IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_dst, dst))
99 CK_LIST_FOREACH(tmp, &GRE_HASH(src, dst), chain) {
102 if (IN6_ARE_ADDR_EQUAL(&tmp->gre_oip6.ip6_src, src) &&
103 IN6_ARE_ADDR_EQUAL(&tmp->gre_oip6.ip6_dst, dst))
104 return (EADDRNOTAVAIL);
110 in6_gre_lookup(const struct mbuf *m, int off, int proto, void **arg)
112 const struct ip6_hdr *ip6;
113 struct gre_softc *sc;
115 if (V_ipv6_hashtbl == NULL)
118 MPASS(in_epoch(net_epoch_preempt));
119 ip6 = mtod(m, const struct ip6_hdr *);
120 CK_LIST_FOREACH(sc, &GRE_HASH(&ip6->ip6_dst, &ip6->ip6_src), chain) {
122 * This is an inbound packet, its ip6_dst is source address
125 if (IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_src,
127 IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_dst,
129 if ((GRE2IFP(sc)->if_flags & IFF_UP) == 0)
132 return (ENCAP_DRV_LOOKUP);
139 * Check that ingress address belongs to local host.
142 in6_gre_set_running(struct gre_softc *sc)
145 if (in6_localip(&sc->gre_oip6.ip6_src))
146 GRE2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING;
148 GRE2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
152 * ifaddr_event handler.
153 * Clear IFF_DRV_RUNNING flag when ingress address disappears to prevent
154 * source address spoofing.
157 in6_gre_srcaddr(void *arg __unused, const struct sockaddr *sa,
160 const struct sockaddr_in6 *sin;
161 struct gre_softc *sc;
163 /* Check that VNET is ready */
164 if (V_ipv6_hashtbl == NULL)
167 MPASS(in_epoch(net_epoch_preempt));
168 sin = (const struct sockaddr_in6 *)sa;
169 CK_LIST_FOREACH(sc, &GRE_SRCHASH(&sin->sin6_addr), srchash) {
170 if (IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_src,
171 &sin->sin6_addr) == 0)
173 in6_gre_set_running(sc);
178 in6_gre_attach(struct gre_softc *sc)
181 sc->gre_hlen = sizeof(struct greip6);
182 sc->gre_oip6.ip6_vfc = IPV6_VERSION;
183 sc->gre_oip6.ip6_nxt = IPPROTO_GRE;
184 gre_updatehdr(sc, &sc->gre_gi6hdr->gi6_gre);
185 CK_LIST_INSERT_HEAD(&GRE_HASH_SC(sc), sc, chain);
186 CK_LIST_INSERT_HEAD(&GRE_SRCHASH(&sc->gre_oip6.ip6_src), sc, srchash);
190 in6_gre_setopts(struct gre_softc *sc, u_long cmd, uint32_t value)
193 MPASS(cmd == GRESKEY || cmd == GRESOPTS);
195 /* NOTE: we are protected with gre_ioctl_sx lock */
196 MPASS(sc->gre_family == AF_INET6);
197 CK_LIST_REMOVE(sc, chain);
198 CK_LIST_REMOVE(sc, srchash);
203 sc->gre_options = value;
208 in6_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_t data)
210 struct in6_ifreq *ifr = (struct in6_ifreq *)data;
211 struct sockaddr_in6 *dst, *src;
215 /* NOTE: we are protected with gre_ioctl_sx lock */
218 case SIOCSIFPHYADDR_IN6:
219 src = &((struct in6_aliasreq *)data)->ifra_addr;
220 dst = &((struct in6_aliasreq *)data)->ifra_dstaddr;
223 if (src->sin6_family != dst->sin6_family ||
224 src->sin6_family != AF_INET6 ||
225 src->sin6_len != dst->sin6_len ||
226 src->sin6_len != sizeof(*src))
228 if (IN6_IS_ADDR_UNSPECIFIED(&src->sin6_addr) ||
229 IN6_IS_ADDR_UNSPECIFIED(&dst->sin6_addr)) {
230 error = EADDRNOTAVAIL;
234 * Check validity of the scope zone ID of the
235 * addresses, and convert it into the kernel
236 * internal form if necessary.
238 if ((error = sa6_embedscope(src, 0)) != 0 ||
239 (error = sa6_embedscope(dst, 0)) != 0)
242 if (V_ipv6_hashtbl == NULL) {
243 V_ipv6_hashtbl = gre_hashinit();
244 V_ipv6_srchashtbl = gre_hashinit();
246 error = in6_gre_checkdup(sc, &src->sin6_addr,
248 if (error == EADDRNOTAVAIL)
250 if (error == EEXIST) {
251 /* Addresses are the same. Just return. */
255 ip6 = malloc(sizeof(struct greip6) + 3 * sizeof(uint32_t),
256 M_GRE, M_WAITOK | M_ZERO);
257 ip6->ip6_src = src->sin6_addr;
258 ip6->ip6_dst = dst->sin6_addr;
259 if (sc->gre_family != 0) {
260 /* Detach existing tunnel first */
261 CK_LIST_REMOVE(sc, chain);
262 CK_LIST_REMOVE(sc, srchash);
264 free(sc->gre_hdr, M_GRE);
265 /* XXX: should we notify about link state change? */
267 sc->gre_family = AF_INET6;
270 sc->gre_iseq = UINT32_MAX;
272 in6_gre_set_running(sc);
274 case SIOCGIFPSRCADDR_IN6:
275 case SIOCGIFPDSTADDR_IN6:
276 if (sc->gre_family != AF_INET6) {
277 error = EADDRNOTAVAIL;
280 src = (struct sockaddr_in6 *)&ifr->ifr_addr;
281 memset(src, 0, sizeof(*src));
282 src->sin6_family = AF_INET6;
283 src->sin6_len = sizeof(*src);
284 src->sin6_addr = (cmd == SIOCGIFPSRCADDR_IN6) ?
285 sc->gre_oip6.ip6_src: sc->gre_oip6.ip6_dst;
286 error = prison_if(curthread->td_ucred, (struct sockaddr *)src);
288 error = sa6_recoverscope(src);
290 memset(src, 0, sizeof(*src));
297 in6_gre_output(struct mbuf *m, int af __unused, int hlen __unused)
301 gi6 = mtod(m, struct greip6 *);
302 gi6->gi6_ip6.ip6_hlim = V_ip6_gre_hlim;
303 return (ip6_output(m, NULL, NULL, IPV6_MINMTU, NULL, NULL, NULL));
306 static const struct srcaddrtab *ipv6_srcaddrtab = NULL;
307 static const struct encaptab *ecookie = NULL;
308 static const struct encap_config ipv6_encap_cfg = {
309 .proto = IPPROTO_GRE,
310 .min_length = sizeof(struct greip6) +
314 sizeof(struct ip6_hdr),
316 .exact_match = ENCAP_DRV_LOOKUP,
317 .lookup = in6_gre_lookup,
325 if (!IS_DEFAULT_VNET(curvnet))
327 ipv6_srcaddrtab = ip6_encap_register_srcaddr(in6_gre_srcaddr,
329 ecookie = ip6_encap_attach(&ipv6_encap_cfg, NULL, M_WAITOK);
336 if (IS_DEFAULT_VNET(curvnet)) {
337 ip6_encap_detach(ecookie);
338 ip6_encap_unregister_srcaddr(ipv6_srcaddrtab);
340 if (V_ipv6_hashtbl != NULL) {
341 gre_hashdestroy(V_ipv6_hashtbl);
342 V_ipv6_hashtbl = NULL;
344 gre_hashdestroy(V_ipv6_srchashtbl);