1/* $NetBSD: in_gif.c,v 1.96 2022/12/07 08:30:15 knakahara Exp $ */ 2/* $KAME: in_gif.c,v 1.66 2001/07/29 04:46:09 itojun Exp $ */ 3 4/* 5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#include <sys/cdefs.h> 34__KERNEL_RCSID(0, "$NetBSD: in_gif.c,v 1.96 2022/12/07 08:30:15 knakahara Exp $"); 35 36#ifdef _KERNEL_OPT 37#include "opt_inet.h" 38#endif 39 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/socket.h> 43#include <sys/sockio.h> 44#include <sys/mbuf.h> 45#include <sys/errno.h> 46#include <sys/ioctl.h> 47#include <sys/syslog.h> 48#include <sys/kernel.h> 49 50#include <net/if.h> 51#include <net/route.h> 52 53#include <netinet/in.h> 54#include <netinet/in_systm.h> 55#include <netinet/ip.h> 56#include <netinet/ip_var.h> 57#include <netinet/in_gif.h> 58#include <netinet/in_var.h> 59#include <netinet/ip_encap.h> 60#include <netinet/ip_ecn.h> 61 62#ifdef INET6 63#include <netinet/ip6.h> 64#endif 65 66#include <net/if_gif.h> 67 68static int gif_validate4(const struct ip *, struct gif_variant *, 69 struct ifnet *); 70 71int ip_gif_ttl = GIF_TTL; 72 73static const struct encapsw in_gif_encapsw = { 74 .encapsw4 = { 75 .pr_input = in_gif_input, 76 .pr_ctlinput = NULL, 77 } 78}; 79 80static int 81in_gif_output(struct gif_variant *var, int family, struct mbuf *m) 82{ 83 struct rtentry *rt; 84 struct gif_softc *sc; 85 struct sockaddr_in *sin_src; 86 struct sockaddr_in *sin_dst; 87 struct ifnet *ifp; 88 struct route *ro_pc; 89 kmutex_t *lock_pc; 90 struct ip iphdr; /* capsule IP header, host byte ordered */ 91 int proto, error; 92 u_int8_t tos; 93 94 KASSERT(gif_heldref_variant(var)); 95 96 sin_src = satosin(var->gv_psrc); 97 sin_dst = satosin(var->gv_pdst); 98 ifp = &var->gv_softc->gif_if; 99 100 if (sin_src == NULL || sin_dst == NULL || 101 sin_src->sin_family != AF_INET || 102 sin_dst->sin_family != AF_INET) { 103 m_freem(m); 104 return EAFNOSUPPORT; 105 } 106 107 switch (family) { 108#ifdef INET 109 case AF_INET: 110 { 111 const struct ip *ip; 112 113 proto = IPPROTO_IPV4; 114 if (m->m_len < sizeof(*ip)) { 115 m = m_pullup(m, sizeof(*ip)); 116 if (m == NULL) 117 return ENOBUFS; 118 } 119 ip = mtod(m, const struct ip *); 120 tos = ip->ip_tos; 121 break; 122 } 123#endif /* INET */ 124#ifdef INET6 125 case AF_INET6: 126 { 127 const struct ip6_hdr *ip6; 128 proto = IPPROTO_IPV6; 129 if (m->m_len < sizeof(*ip6)) { 130 m = m_pullup(m, sizeof(*ip6)); 131 if (m == NULL) 132 return ENOBUFS; 133 } 134 ip6 = mtod(m, const struct ip6_hdr *); 135 tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 136 break; 137 } 138#endif /* INET6 */ 139 default: 140#ifdef DEBUG 141 printf("in_gif_output: warning: unknown family %d passed\n", 142 family); 143#endif 144 m_freem(m); 145 return EAFNOSUPPORT; 146 } 147 148 memset(&iphdr, 0, sizeof(iphdr)); 149 iphdr.ip_src = sin_src->sin_addr; 150 /* bidirectional configured tunnel mode */ 151 if (sin_dst->sin_addr.s_addr != INADDR_ANY) 152 iphdr.ip_dst = sin_dst->sin_addr; 153 else { 154 m_freem(m); 155 return ENETUNREACH; 156 } 157 iphdr.ip_p = proto; 158 /* version will be set in ip_output() */ 159 iphdr.ip_ttl = ip_gif_ttl; 160 iphdr.ip_len = htons(m->m_pkthdr.len + sizeof(struct ip)); 161 if (ifp->if_flags & IFF_LINK1) 162 ip_ecn_ingress(ECN_ALLOWED, &iphdr.ip_tos, &tos); 163 else 164 ip_ecn_ingress(ECN_NOCARE, &iphdr.ip_tos, &tos); 165 166 /* prepend new IP header */ 167 M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); 168 /* XXX Is m_pullup really necessary after M_PREPEND? */ 169 if (m != NULL && M_UNWRITABLE(m, sizeof(struct ip))) 170 m = m_pullup(m, sizeof(struct ip)); 171 if (m == NULL) 172 return ENOBUFS; 173 bcopy(&iphdr, mtod(m, struct ip *), sizeof(struct ip)); 174 175 sc = var->gv_softc; 176 if_tunnel_get_ro(sc->gif_ro_percpu, &ro_pc, &lock_pc); 177 if ((rt = rtcache_lookup(ro_pc, var->gv_pdst)) == NULL) { 178 if_tunnel_put_ro(sc->gif_ro_percpu, lock_pc); 179 m_freem(m); 180 return ENETUNREACH; 181 } 182 183 /* If the route constitutes infinite encapsulation, punt. */ 184 if (rt->rt_ifp == ifp) { 185 rtcache_unref(rt, ro_pc); 186 rtcache_free(ro_pc); 187 if_tunnel_put_ro(sc->gif_ro_percpu, lock_pc); 188 m_freem(m); 189 return ENETUNREACH; /*XXX*/ 190 } 191 rtcache_unref(rt, ro_pc); 192 193 error = ip_output(m, NULL, ro_pc, 0, NULL, NULL); 194 if_tunnel_put_ro(sc->gif_ro_percpu, lock_pc); 195 return (error); 196} 197 198void 199in_gif_input(struct mbuf *m, int off, int proto, void *eparg) 200{ 201 struct gif_softc *sc = eparg; 202 struct ifnet *gifp = &sc->gif_if; 203 const struct ip *ip; 204 int af; 205 u_int8_t otos; 206 207 KASSERT(sc != NULL); 208 209 ip = mtod(m, const struct ip *); 210 211 gifp = &sc->gif_if; 212 if ((gifp->if_flags & IFF_UP) == 0) { 213 m_freem(m); 214 ip_statinc(IP_STAT_NOGIF); 215 return; 216 } 217#ifndef GIF_ENCAPCHECK 218 struct psref psref_var; 219 struct gif_variant *var = gif_getref_variant(sc, &psref_var); 220 /* other CPU do delete_tunnel */ 221 if (var->gv_psrc == NULL || var->gv_pdst == NULL) { 222 gif_putref_variant(var, &psref_var); 223 m_freem(m); 224 ip_statinc(IP_STAT_NOGIF); 225 return; 226 } 227 228 struct ifnet *rcvif; 229 struct psref psref_rcvif; 230 rcvif = m_get_rcvif_psref(m, &psref_rcvif); 231 if (!gif_validate4(ip, var, rcvif)) { 232 m_put_rcvif_psref(rcvif, &psref_rcvif); 233 gif_putref_variant(var, &psref_var); 234 m_freem(m); 235 ip_statinc(IP_STAT_NOGIF); 236 return; 237 } 238 m_put_rcvif_psref(rcvif, &psref_rcvif); 239 gif_putref_variant(var, &psref_var); 240#endif 241 otos = ip->ip_tos; 242 m_adj(m, off); 243 244 switch (proto) { 245#ifdef INET 246 case IPPROTO_IPV4: 247 { 248 struct ip *xip; 249 af = AF_INET; 250 if (M_UNWRITABLE(m, sizeof(*xip))) { 251 if ((m = m_pullup(m, sizeof(*xip))) == NULL) 252 return; 253 } 254 xip = mtod(m, struct ip *); 255 if (gifp->if_flags & IFF_LINK1) 256 ip_ecn_egress(ECN_ALLOWED, &otos, &xip->ip_tos); 257 else 258 ip_ecn_egress(ECN_NOCARE, &otos, &xip->ip_tos); 259 break; 260 } 261#endif 262#ifdef INET6 263 case IPPROTO_IPV6: 264 { 265 struct ip6_hdr *ip6; 266 u_int8_t itos; 267 af = AF_INET6; 268 if (M_UNWRITABLE(m, sizeof(*ip6))) { 269 if ((m = m_pullup(m, sizeof(*ip6))) == NULL) 270 return; 271 } 272 ip6 = mtod(m, struct ip6_hdr *); 273 itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 274 if (gifp->if_flags & IFF_LINK1) 275 ip_ecn_egress(ECN_ALLOWED, &otos, &itos); 276 else 277 ip_ecn_egress(ECN_NOCARE, &otos, &itos); 278 ip6->ip6_flow &= ~htonl(0xff << 20); 279 ip6->ip6_flow |= htonl((u_int32_t)itos << 20); 280 break; 281 } 282#endif /* INET6 */ 283 default: 284 ip_statinc(IP_STAT_NOGIF); 285 m_freem(m); 286 return; 287 } 288 gif_input(m, af, gifp); 289 return; 290} 291 292/* 293 * validate outer address. 294 */ 295static int 296gif_validate4(const struct ip *ip, struct gif_variant *var, struct ifnet *ifp) 297{ 298 struct sockaddr_in *src, *dst; 299 int ret; 300 301 src = satosin(var->gv_psrc); 302 dst = satosin(var->gv_pdst); 303 304 ret = in_tunnel_validate(ip, src->sin_addr, dst->sin_addr); 305 if (ret == 0) 306 return 0; 307 308 /* ingress filters on outer source */ 309 if ((var->gv_softc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) { 310 union { 311 struct sockaddr sa; 312 struct sockaddr_in sin; 313 } u; 314 struct rtentry *rt; 315 316 sockaddr_in_init(&u.sin, &ip->ip_src, 0); 317 rt = rtalloc1(&u.sa, 0); 318 if (rt == NULL || rt->rt_ifp != ifp) { 319#if 0 320 log(LOG_WARNING, "%s: packet from 0x%x dropped " 321 "due to ingress filter\n", 322 if_name(&var->gv_softc->gif_if), 323 (u_int32_t)ntohl(u.sin.sin_addr.s_addr)); 324#endif 325 if (rt != NULL) 326 rt_unref(rt); 327 return 0; 328 } 329 rt_unref(rt); 330 } 331 332 return ret; 333} 334 335#ifdef GIF_ENCAPCHECK 336/* 337 * we know that we are in IFF_UP, outer address available, and outer family 338 * matched the physical addr family. see gif_encapcheck(). 339 */ 340int 341gif_encapcheck4(struct mbuf *m, int off, int proto, struct gif_variant *var) 342{ 343 struct ip ip; 344 345 struct ifnet *ifp = NULL; 346 int r; 347 struct psref psref; 348 349 m_copydata(m, 0, sizeof(ip), &ip); 350 if ((m->m_flags & M_PKTHDR) != 0) 351 ifp = m_get_rcvif_psref(m, &psref); 352 353 r = gif_validate4(&ip, var, ifp); 354 355 m_put_rcvif_psref(ifp, &psref); 356 return r; 357} 358#endif 359 360int 361in_gif_attach(struct gif_variant *var) 362{ 363#ifndef GIF_ENCAPCHECK 364 struct sockaddr_in mask4; 365 366 memset(&mask4, 0, sizeof(mask4)); 367 mask4.sin_len = sizeof(struct sockaddr_in); 368 mask4.sin_addr.s_addr = ~0; 369 370 if (!var->gv_psrc || !var->gv_pdst) 371 return EINVAL; 372 373 var->gv_encap_cookie4 = encap_attach_addr(AF_INET, -1, var->gv_psrc, 374 var->gv_pdst, NULL, &in_gif_encapsw, var->gv_softc); 375#else 376 var->gv_encap_cookie4 = encap_attach_addr(AF_INET, -1, var->gv_psrc, 377 var->gv_pdst, gif_encapcheck, &in_gif_encapsw, var->gv_softc); 378#endif 379 if (var->gv_encap_cookie4 == NULL) 380 return EEXIST; 381 382 var->gv_output = in_gif_output; 383 return 0; 384} 385 386int 387in_gif_detach(struct gif_variant *var) 388{ 389 int error; 390 struct gif_softc *sc = var->gv_softc; 391 392 error = encap_detach(var->gv_encap_cookie4); 393 if (error == 0) 394 var->gv_encap_cookie4 = NULL; 395 396 if_tunnel_ro_percpu_rtcache_free(sc->gif_ro_percpu); 397 398 return error; 399} 400