1 /* $NetBSD: in_gif.c,v 1.94 2018/05/01 07:21:39 maxv 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.94 2018/05/01 07:21:39 maxv 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 68 static int gif_validate4(const struct ip *, struct gif_variant *, 69 struct ifnet *); 70 71 int ip_gif_ttl = GIF_TTL; 72 73 static const struct encapsw in_gif_encapsw = { 74 .encapsw4 = { 75 .pr_input = in_gif_input, 76 .pr_ctlinput = NULL, 77 } 78 }; 79 80 static int 81 in_gif_output(struct gif_variant *var, int family, struct mbuf *m) 82 { 83 struct rtentry *rt; 84 struct route *ro; 85 struct gif_ro *gro; 86 struct gif_softc *sc; 87 struct sockaddr_in *sin_src; 88 struct sockaddr_in *sin_dst; 89 struct ifnet *ifp; 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 gro = percpu_getref(sc->gif_ro_percpu); 177 mutex_enter(gro->gr_lock); 178 ro = &gro->gr_ro; 179 if ((rt = rtcache_lookup(ro, var->gv_pdst)) == NULL) { 180 mutex_exit(gro->gr_lock); 181 percpu_putref(sc->gif_ro_percpu); 182 m_freem(m); 183 return ENETUNREACH; 184 } 185 186 /* If the route constitutes infinite encapsulation, punt. */ 187 if (rt->rt_ifp == ifp) { 188 rtcache_unref(rt, ro); 189 rtcache_free(ro); 190 mutex_exit(gro->gr_lock); 191 percpu_putref(sc->gif_ro_percpu); 192 m_freem(m); 193 return ENETUNREACH; /*XXX*/ 194 } 195 rtcache_unref(rt, ro); 196 197 error = ip_output(m, NULL, ro, 0, NULL, NULL); 198 mutex_exit(gro->gr_lock); 199 percpu_putref(sc->gif_ro_percpu); 200 return (error); 201 } 202 203 void 204 in_gif_input(struct mbuf *m, int off, int proto, void *eparg) 205 { 206 struct gif_softc *sc = eparg; 207 struct ifnet *gifp = &sc->gif_if; 208 const struct ip *ip; 209 int af; 210 u_int8_t otos; 211 212 KASSERT(sc != NULL); 213 214 ip = mtod(m, const struct ip *); 215 216 gifp = &sc->gif_if; 217 if ((gifp->if_flags & IFF_UP) == 0) { 218 m_freem(m); 219 ip_statinc(IP_STAT_NOGIF); 220 return; 221 } 222 #ifndef GIF_ENCAPCHECK 223 struct psref psref_var; 224 struct gif_variant *var = gif_getref_variant(sc, &psref_var); 225 /* other CPU do delete_tunnel */ 226 if (var->gv_psrc == NULL || var->gv_pdst == NULL) { 227 gif_putref_variant(var, &psref_var); 228 m_freem(m); 229 ip_statinc(IP_STAT_NOGIF); 230 return; 231 } 232 233 struct ifnet *rcvif; 234 struct psref psref_rcvif; 235 rcvif = m_get_rcvif_psref(m, &psref_rcvif); 236 if (!gif_validate4(ip, var, rcvif)) { 237 m_put_rcvif_psref(rcvif, &psref_rcvif); 238 gif_putref_variant(var, &psref_var); 239 m_freem(m); 240 ip_statinc(IP_STAT_NOGIF); 241 return; 242 } 243 m_put_rcvif_psref(rcvif, &psref_rcvif); 244 gif_putref_variant(var, &psref_var); 245 #endif 246 otos = ip->ip_tos; 247 m_adj(m, off); 248 249 switch (proto) { 250 #ifdef INET 251 case IPPROTO_IPV4: 252 { 253 struct ip *xip; 254 af = AF_INET; 255 if (M_UNWRITABLE(m, sizeof(*xip))) { 256 if ((m = m_pullup(m, sizeof(*xip))) == NULL) 257 return; 258 } 259 xip = mtod(m, struct ip *); 260 if (gifp->if_flags & IFF_LINK1) 261 ip_ecn_egress(ECN_ALLOWED, &otos, &xip->ip_tos); 262 else 263 ip_ecn_egress(ECN_NOCARE, &otos, &xip->ip_tos); 264 break; 265 } 266 #endif 267 #ifdef INET6 268 case IPPROTO_IPV6: 269 { 270 struct ip6_hdr *ip6; 271 u_int8_t itos; 272 af = AF_INET6; 273 if (M_UNWRITABLE(m, sizeof(*ip6))) { 274 if ((m = m_pullup(m, sizeof(*ip6))) == NULL) 275 return; 276 } 277 ip6 = mtod(m, struct ip6_hdr *); 278 itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 279 if (gifp->if_flags & IFF_LINK1) 280 ip_ecn_egress(ECN_ALLOWED, &otos, &itos); 281 else 282 ip_ecn_egress(ECN_NOCARE, &otos, &itos); 283 ip6->ip6_flow &= ~htonl(0xff << 20); 284 ip6->ip6_flow |= htonl((u_int32_t)itos << 20); 285 break; 286 } 287 #endif /* INET6 */ 288 default: 289 ip_statinc(IP_STAT_NOGIF); 290 m_freem(m); 291 return; 292 } 293 gif_input(m, af, gifp); 294 return; 295 } 296 297 /* 298 * validate outer address. 299 */ 300 static int 301 gif_validate4(const struct ip *ip, struct gif_variant *var, struct ifnet *ifp) 302 { 303 struct sockaddr_in *src, *dst; 304 int ret; 305 306 src = satosin(var->gv_psrc); 307 dst = satosin(var->gv_pdst); 308 309 ret = in_tunnel_validate(ip, src->sin_addr, dst->sin_addr); 310 if (ret == 0) 311 return 0; 312 313 /* ingress filters on outer source */ 314 if ((var->gv_softc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) { 315 union { 316 struct sockaddr sa; 317 struct sockaddr_in sin; 318 } u; 319 struct rtentry *rt; 320 321 sockaddr_in_init(&u.sin, &ip->ip_src, 0); 322 rt = rtalloc1(&u.sa, 0); 323 if (rt == NULL || rt->rt_ifp != ifp) { 324 #if 0 325 log(LOG_WARNING, "%s: packet from 0x%x dropped " 326 "due to ingress filter\n", 327 if_name(&var->gv_softc->gif_if), 328 (u_int32_t)ntohl(u.sin.sin_addr.s_addr)); 329 #endif 330 if (rt != NULL) 331 rt_unref(rt); 332 return 0; 333 } 334 rt_unref(rt); 335 } 336 337 return ret; 338 } 339 340 #ifdef GIF_ENCAPCHECK 341 /* 342 * we know that we are in IFF_UP, outer address available, and outer family 343 * matched the physical addr family. see gif_encapcheck(). 344 */ 345 int 346 gif_encapcheck4(struct mbuf *m, int off, int proto, struct gif_variant *var) 347 { 348 struct ip ip; 349 350 struct ifnet *ifp = NULL; 351 int r; 352 struct psref psref; 353 354 m_copydata(m, 0, sizeof(ip), &ip); 355 if ((m->m_flags & M_PKTHDR) != 0) 356 ifp = m_get_rcvif_psref(m, &psref); 357 358 r = gif_validate4(&ip, var, ifp); 359 360 m_put_rcvif_psref(ifp, &psref); 361 return r; 362 } 363 #endif 364 365 int 366 in_gif_attach(struct gif_variant *var) 367 { 368 #ifndef GIF_ENCAPCHECK 369 struct sockaddr_in mask4; 370 371 memset(&mask4, 0, sizeof(mask4)); 372 mask4.sin_len = sizeof(struct sockaddr_in); 373 mask4.sin_addr.s_addr = ~0; 374 375 if (!var->gv_psrc || !var->gv_pdst) 376 return EINVAL; 377 var->gv_encap_cookie4 = encap_attach(AF_INET, -1, var->gv_psrc, 378 (struct sockaddr *)&mask4, var->gv_pdst, (struct sockaddr *)&mask4, 379 &in_gif_encapsw, var->gv_softc); 380 #else 381 var->gv_encap_cookie4 = encap_attach_func(AF_INET, -1, gif_encapcheck, 382 &in_gif_encapsw, var->gv_softc); 383 #endif 384 if (var->gv_encap_cookie4 == NULL) 385 return EEXIST; 386 387 var->gv_output = in_gif_output; 388 return 0; 389 } 390 391 int 392 in_gif_detach(struct gif_variant *var) 393 { 394 int error; 395 struct gif_softc *sc = var->gv_softc; 396 397 error = encap_detach(var->gv_encap_cookie4); 398 if (error == 0) 399 var->gv_encap_cookie4 = NULL; 400 401 percpu_foreach(sc->gif_ro_percpu, gif_rtcache_free_pc, NULL); 402 403 return error; 404 } 405