126236Swpaul/*	$NetBSD: in_gif.c,v 1.61 2011/07/17 20:54:53 joerg Exp $	*/
226236Swpaul/*	$KAME: in_gif.c,v 1.66 2001/07/29 04:46:09 itojun Exp $	*/
326236Swpaul
426236Swpaul/*
526236Swpaul * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
626236Swpaul * All rights reserved.
726236Swpaul *
826236Swpaul * Redistribution and use in source and binary forms, with or without
926236Swpaul * modification, are permitted provided that the following conditions
1026236Swpaul * are met:
1126236Swpaul * 1. Redistributions of source code must retain the above copyright
1226236Swpaul *    notice, this list of conditions and the following disclaimer.
1326236Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1426236Swpaul *    notice, this list of conditions and the following disclaimer in the
1526236Swpaul *    documentation and/or other materials provided with the distribution.
1626236Swpaul * 3. Neither the name of the project nor the names of its contributors
1726236Swpaul *    may be used to endorse or promote products derived from this software
1826236Swpaul *    without specific prior written permission.
1926236Swpaul *
2026236Swpaul * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2126236Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2226236Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2326236Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2426236Swpaul * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2526236Swpaul * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2626236Swpaul * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2726236Swpaul * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2826236Swpaul * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2926236Swpaul * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3026236Swpaul * SUCH DAMAGE.
3126236Swpaul */
3226236Swpaul
3326236Swpaul#include <sys/cdefs.h>
3426236Swpaul__KERNEL_RCSID(0, "$NetBSD: in_gif.c,v 1.61 2011/07/17 20:54:53 joerg Exp $");
3526236Swpaul
3626236Swpaul#include "opt_inet.h"
3726236Swpaul#include "opt_iso.h"
3826236Swpaul
3926236Swpaul#include <sys/param.h>
4026236Swpaul#include <sys/systm.h>
4126236Swpaul#include <sys/socket.h>
4226236Swpaul#include <sys/sockio.h>
4326236Swpaul#include <sys/mbuf.h>
4426236Swpaul#include <sys/errno.h>
4526236Swpaul#include <sys/ioctl.h>
4626236Swpaul#include <sys/syslog.h>
4726236Swpaul#include <sys/protosw.h>
4826236Swpaul#include <sys/kernel.h>
4926236Swpaul
5026236Swpaul#include <net/if.h>
5126236Swpaul#include <net/route.h>
5226236Swpaul
5326236Swpaul#include <netinet/in.h>
5426236Swpaul#include <netinet/in_systm.h>
5526236Swpaul#include <netinet/ip.h>
5626236Swpaul#include <netinet/ip_var.h>
5726236Swpaul#include <netinet/in_gif.h>
5826236Swpaul#include <netinet/in_var.h>
5926236Swpaul#include <netinet/ip_encap.h>
6026236Swpaul#include <netinet/ip_ecn.h>
6126236Swpaul
6226236Swpaul#ifdef INET6
6326236Swpaul#include <netinet/ip6.h>
6426236Swpaul#endif
6526236Swpaul
6626236Swpaul#include <net/if_gif.h>
6726236Swpaul
6826236Swpaul#include "gif.h"
6926236Swpaul
7026236Swpaul#include <net/net_osdep.h>
7126236Swpaul
7226236Swpaulstatic int gif_validate4(const struct ip *, struct gif_softc *,
7326236Swpaul	struct ifnet *);
7426236Swpaul
7526236Swpaul#if NGIF > 0
7626236Swpaulint ip_gif_ttl = GIF_TTL;
7726236Swpaul#else
7826236Swpaulint ip_gif_ttl = 0;
7926236Swpaul#endif
8026236Swpaul
8126236Swpaulconst struct protosw in_gif_protosw =
8226236Swpaul{ SOCK_RAW,	&inetdomain,	0/* IPPROTO_IPV[46] */,	PR_ATOMIC|PR_ADDR,
8326236Swpaul  in_gif_input, rip_output,	0,		rip_ctloutput,
8426236Swpaul  rip_usrreq,
8526236Swpaul  0,            0,              0,              0,
8626236Swpaul};
8726236Swpaul
8826236Swpaulint
8926236Swpaulin_gif_output(struct ifnet *ifp, int family, struct mbuf *m)
9026236Swpaul{
9126236Swpaul	struct rtentry *rt;
9226236Swpaul	struct gif_softc *sc = ifp->if_softc;
9326236Swpaul	struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc;
9426236Swpaul	struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst;
9526236Swpaul	struct ip iphdr;	/* capsule IP header, host byte ordered */
9626236Swpaul	int proto, error;
9726236Swpaul	u_int8_t tos;
9826236Swpaul	union {
9926236Swpaul		struct sockaddr		dst;
10026236Swpaul		struct sockaddr_in	dst4;
10126236Swpaul	} u;
10226236Swpaul
10326236Swpaul	if (sin_src == NULL || sin_dst == NULL ||
10426236Swpaul	    sin_src->sin_family != AF_INET ||
10526236Swpaul	    sin_dst->sin_family != AF_INET) {
10626236Swpaul		m_freem(m);
10726236Swpaul		return EAFNOSUPPORT;
10826236Swpaul	}
10926236Swpaul
11026236Swpaul	switch (family) {
11126236Swpaul#ifdef INET
11226236Swpaul	case AF_INET:
11326236Swpaul	    {
11426236Swpaul		const struct ip *ip;
11526236Swpaul
11626236Swpaul		proto = IPPROTO_IPV4;
11726236Swpaul		if (m->m_len < sizeof(*ip)) {
11826236Swpaul			m = m_pullup(m, sizeof(*ip));
11926236Swpaul			if (m == NULL)
12026236Swpaul				return ENOBUFS;
12126236Swpaul		}
12226236Swpaul		ip = mtod(m, const struct ip *);
12326236Swpaul		tos = ip->ip_tos;
12426236Swpaul		break;
12526236Swpaul	    }
12626236Swpaul#endif /* INET */
12726236Swpaul#ifdef INET6
12826236Swpaul	case AF_INET6:
12926236Swpaul	    {
13026236Swpaul		const struct ip6_hdr *ip6;
13126236Swpaul		proto = IPPROTO_IPV6;
13226236Swpaul		if (m->m_len < sizeof(*ip6)) {
13326236Swpaul			m = m_pullup(m, sizeof(*ip6));
13426236Swpaul			if (m == NULL)
13526236Swpaul				return ENOBUFS;
13626236Swpaul		}
13726236Swpaul		ip6 = mtod(m, const struct ip6_hdr *);
13826236Swpaul		tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
13926236Swpaul		break;
14026236Swpaul	    }
14126236Swpaul#endif /* INET6 */
14226236Swpaul#ifdef ISO
14326236Swpaul	case AF_ISO:
14426236Swpaul		proto = IPPROTO_EON;
14526236Swpaul		tos = 0;
14626236Swpaul		break;
14726236Swpaul#endif
14826236Swpaul	default:
14926236Swpaul#ifdef DEBUG
15026236Swpaul		printf("in_gif_output: warning: unknown family %d passed\n",
15126236Swpaul			family);
15226236Swpaul#endif
15326236Swpaul		m_freem(m);
15426236Swpaul		return EAFNOSUPPORT;
15526236Swpaul	}
15626236Swpaul
15726236Swpaul	memset(&iphdr, 0, sizeof(iphdr));
15826236Swpaul	iphdr.ip_src = sin_src->sin_addr;
15926236Swpaul	/* bidirectional configured tunnel mode */
16026236Swpaul	if (sin_dst->sin_addr.s_addr != INADDR_ANY)
16126236Swpaul		iphdr.ip_dst = sin_dst->sin_addr;
16226236Swpaul	else {
16326236Swpaul		m_freem(m);
16426236Swpaul		return ENETUNREACH;
16526236Swpaul	}
16626236Swpaul	iphdr.ip_p = proto;
16726236Swpaul	/* version will be set in ip_output() */
16826236Swpaul	iphdr.ip_ttl = ip_gif_ttl;
16926236Swpaul	iphdr.ip_len = htons(m->m_pkthdr.len + sizeof(struct ip));
17026236Swpaul	if (ifp->if_flags & IFF_LINK1)
17126236Swpaul		ip_ecn_ingress(ECN_ALLOWED, &iphdr.ip_tos, &tos);
17226236Swpaul	else
17326236Swpaul		ip_ecn_ingress(ECN_NOCARE, &iphdr.ip_tos, &tos);
17426236Swpaul
17526236Swpaul	/* prepend new IP header */
17626236Swpaul	M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
17726236Swpaul	/* XXX Is m_pullup really necessary after M_PREPEND? */
17826236Swpaul	if (m != NULL && M_UNWRITABLE(m, sizeof(struct ip)))
17926236Swpaul		m = m_pullup(m, sizeof(struct ip));
18026236Swpaul	if (m == NULL)
18126236Swpaul		return ENOBUFS;
18226236Swpaul	bcopy(&iphdr, mtod(m, struct ip *), sizeof(struct ip));
18326236Swpaul
18426236Swpaul	sockaddr_in_init(&u.dst4, &sin_dst->sin_addr, 0);
18526236Swpaul	if ((rt = rtcache_lookup(&sc->gif_ro, &u.dst)) == NULL) {
18626236Swpaul		m_freem(m);
18726236Swpaul		return ENETUNREACH;
18826236Swpaul	}
18926236Swpaul
19026236Swpaul	/* If the route constitutes infinite encapsulation, punt. */
19126236Swpaul	if (rt->rt_ifp == ifp) {
19226236Swpaul		rtcache_free(&sc->gif_ro);
19326236Swpaul		m_freem(m);
19426236Swpaul		return ENETUNREACH;	/*XXX*/
19526236Swpaul	}
19626236Swpaul
19726236Swpaul	error = ip_output(m, NULL, &sc->gif_ro, 0, NULL, NULL);
19826236Swpaul	return (error);
19926236Swpaul}
20026236Swpaul
20126236Swpaulvoid
20226236Swpaulin_gif_input(struct mbuf *m, ...)
20326236Swpaul{
20426236Swpaul	int off, proto;
20526236Swpaul	struct ifnet *gifp = NULL;
20626236Swpaul	const struct ip *ip;
20726236Swpaul	va_list ap;
20826236Swpaul	int af;
20926236Swpaul	u_int8_t otos;
21026236Swpaul
21126236Swpaul	va_start(ap, m);
21226236Swpaul	off = va_arg(ap, int);
21326236Swpaul	proto = va_arg(ap, int);
21426236Swpaul	va_end(ap);
21526236Swpaul
21626236Swpaul	ip = mtod(m, const struct ip *);
21726236Swpaul
21826236Swpaul	gifp = (struct ifnet *)encap_getarg(m);
21926236Swpaul
22026236Swpaul	if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) {
22126236Swpaul		m_freem(m);
22226236Swpaul		ip_statinc(IP_STAT_NOGIF);
22326236Swpaul		return;
22426236Swpaul	}
22526236Swpaul#ifndef GIF_ENCAPCHECK
22626236Swpaul	if (!gif_validate4(ip, gifp->if_softc, m->m_pkthdr.rcvif)) {
22726236Swpaul		m_freem(m);
22826236Swpaul		ip_statinc(IP_STAT_NOGIF);
22926236Swpaul		return;
23026236Swpaul	}
23126236Swpaul#endif
23226236Swpaul
23326236Swpaul	otos = ip->ip_tos;
23426236Swpaul	m_adj(m, off);
23526236Swpaul
23626236Swpaul	switch (proto) {
237#ifdef INET
238	case IPPROTO_IPV4:
239	    {
240		struct ip *xip;
241		af = AF_INET;
242		if (M_UNWRITABLE(m, sizeof(*xip))) {
243			if ((m = m_pullup(m, sizeof(*xip))) == NULL)
244				return;
245		}
246		xip = mtod(m, struct ip *);
247		if (gifp->if_flags & IFF_LINK1)
248			ip_ecn_egress(ECN_ALLOWED, &otos, &xip->ip_tos);
249		else
250			ip_ecn_egress(ECN_NOCARE, &otos, &xip->ip_tos);
251		break;
252	    }
253#endif
254#ifdef INET6
255	case IPPROTO_IPV6:
256	    {
257		struct ip6_hdr *ip6;
258		u_int8_t itos;
259		af = AF_INET6;
260		if (M_UNWRITABLE(m, sizeof(*ip6))) {
261			if ((m = m_pullup(m, sizeof(*ip6))) == NULL)
262				return;
263		}
264		ip6 = mtod(m, struct ip6_hdr *);
265		itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
266		if (gifp->if_flags & IFF_LINK1)
267			ip_ecn_egress(ECN_ALLOWED, &otos, &itos);
268		else
269			ip_ecn_egress(ECN_NOCARE, &otos, &itos);
270		ip6->ip6_flow &= ~htonl(0xff << 20);
271		ip6->ip6_flow |= htonl((u_int32_t)itos << 20);
272		break;
273	    }
274#endif /* INET6 */
275#ifdef ISO
276	case IPPROTO_EON:
277		af = AF_ISO;
278		break;
279#endif
280	default:
281		ip_statinc(IP_STAT_NOGIF);
282		m_freem(m);
283		return;
284	}
285	gif_input(m, af, gifp);
286	return;
287}
288
289/*
290 * validate outer address.
291 */
292static int
293gif_validate4(const struct ip *ip, struct gif_softc *sc, struct ifnet *ifp)
294{
295	struct sockaddr_in *src, *dst;
296	struct in_ifaddr *ia4;
297
298	src = (struct sockaddr_in *)sc->gif_psrc;
299	dst = (struct sockaddr_in *)sc->gif_pdst;
300
301	/* check for address match */
302	if (src->sin_addr.s_addr != ip->ip_dst.s_addr ||
303	    dst->sin_addr.s_addr != ip->ip_src.s_addr)
304		return 0;
305
306	/* martian filters on outer source - NOT done in ip_input! */
307	if (IN_MULTICAST(ip->ip_src.s_addr))
308		return 0;
309	switch ((ntohl(ip->ip_src.s_addr) & 0xff000000) >> 24) {
310	case 0: case 127: case 255:
311		return 0;
312	}
313	/* reject packets with broadcast on source */
314	TAILQ_FOREACH(ia4, &in_ifaddrhead, ia_list) {
315		if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0)
316			continue;
317		if (ip->ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr)
318			return 0;
319	}
320
321	/* ingress filters on outer source */
322	if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) {
323		union {
324			struct sockaddr sa;
325			struct sockaddr_in sin;
326		} u;
327		struct rtentry *rt;
328
329		sockaddr_in_init(&u.sin, &ip->ip_src, 0);
330		rt = rtalloc1(&u.sa, 0);
331		if (rt == NULL || rt->rt_ifp != ifp) {
332#if 0
333			log(LOG_WARNING, "%s: packet from 0x%x dropped "
334			    "due to ingress filter\n", if_name(&sc->gif_if),
335			    (u_int32_t)ntohl(u.sin.sin_addr.s_addr));
336#endif
337			if (rt != NULL)
338				rtfree(rt);
339			return 0;
340		}
341		rtfree(rt);
342	}
343
344	return 32 * 2;
345}
346
347#ifdef GIF_ENCAPCHECK
348/*
349 * we know that we are in IFF_UP, outer address available, and outer family
350 * matched the physical addr family.  see gif_encapcheck().
351 */
352int
353gif_encapcheck4(struct mbuf *m, int off, int proto, void *arg)
354{
355	struct ip ip;
356	struct gif_softc *sc;
357	struct ifnet *ifp;
358
359	/* sanity check done in caller */
360	sc = arg;
361
362	m_copydata(m, 0, sizeof(ip), &ip);
363	ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL;
364
365	return gif_validate4(&ip, sc, ifp);
366}
367#endif
368
369int
370in_gif_attach(struct gif_softc *sc)
371{
372#ifndef GIF_ENCAPCHECK
373	struct sockaddr_in mask4;
374
375	memset(&mask4, 0, sizeof(mask4));
376	mask4.sin_len = sizeof(struct sockaddr_in);
377	mask4.sin_addr.s_addr = ~0;
378
379	if (!sc->gif_psrc || !sc->gif_pdst)
380		return EINVAL;
381	sc->encap_cookie4 = encap_attach(AF_INET, -1, sc->gif_psrc,
382	    (struct sockaddr *)&mask4, sc->gif_pdst, (struct sockaddr *)&mask4,
383	    (const struct protosw *)&in_gif_protosw, sc);
384#else
385	sc->encap_cookie4 = encap_attach_func(AF_INET, -1, gif_encapcheck,
386	    &in_gif_protosw, sc);
387#endif
388	if (sc->encap_cookie4 == NULL)
389		return EEXIST;
390	return 0;
391}
392
393int
394in_gif_detach(struct gif_softc *sc)
395{
396	int error;
397
398	error = encap_detach(sc->encap_cookie4);
399	if (error == 0)
400		sc->encap_cookie4 = NULL;
401
402	rtcache_free(&sc->gif_ro);
403
404	return error;
405}
406