in_gif.c revision 54263
154263Sshin/*
254263Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
354263Sshin * All rights reserved.
454263Sshin *
554263Sshin * Redistribution and use in source and binary forms, with or without
654263Sshin * modification, are permitted provided that the following conditions
754263Sshin * are met:
854263Sshin * 1. Redistributions of source code must retain the above copyright
954263Sshin *    notice, this list of conditions and the following disclaimer.
1054263Sshin * 2. Redistributions in binary form must reproduce the above copyright
1154263Sshin *    notice, this list of conditions and the following disclaimer in the
1254263Sshin *    documentation and/or other materials provided with the distribution.
1354263Sshin * 3. Neither the name of the project nor the names of its contributors
1454263Sshin *    may be used to endorse or promote products derived from this software
1554263Sshin *    without specific prior written permission.
1654263Sshin *
1754263Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
1854263Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1954263Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2054263Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2154263Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2254263Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2354263Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2454263Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2554263Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2654263Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2754263Sshin * SUCH DAMAGE.
2854263Sshin *
2954263Sshin * $FreeBSD: head/sys/netinet/in_gif.c 54263 1999-12-07 17:39:16Z shin $
3054263Sshin */
3154263Sshin
3254263Sshin/*
3354263Sshin * in_gif.c
3454263Sshin */
3554263Sshin
3654263Sshin#include "opt_mrouting.h"
3754263Sshin#include "opt_inet6.h"
3854263Sshin
3954263Sshin#include <sys/param.h>
4054263Sshin#include <sys/systm.h>
4154263Sshin#include <sys/socket.h>
4254263Sshin#include <sys/sockio.h>
4354263Sshin#include <sys/mbuf.h>
4454263Sshin#include <sys/errno.h>
4554263Sshin#include <sys/kernel.h>
4654263Sshin#include <sys/sysctl.h>
4754263Sshin#include <sys/protosw.h>
4854263Sshin
4954263Sshin#include <net/if.h>
5054263Sshin#include <net/route.h>
5154263Sshin
5254263Sshin#include <netinet/in.h>
5354263Sshin#include <netinet/in_systm.h>
5454263Sshin#include <netinet/ip.h>
5554263Sshin#include <netinet/ip_var.h>
5654263Sshin#include <netinet/in_gif.h>
5754263Sshin
5854263Sshin#ifdef INET6
5954263Sshin#include <netinet/ip6.h>
6054263Sshin#endif
6154263Sshin
6254263Sshin#ifdef MROUTING
6354263Sshin#include <netinet/ip_mroute.h>
6454263Sshin#endif /* MROUTING */
6554263Sshin
6654263Sshin#include <net/if_gif.h>
6754263Sshin
6854263Sshin#include "gif.h"
6954263Sshin
7054263Sshin#include <machine/stdarg.h>
7154263Sshin
7254263Sshin#include <net/net_osdep.h>
7354263Sshin
7454263Sshin#if NGIF > 0
7554263Sshinint ip_gif_ttl = GIF_TTL;
7654263Sshin#else
7754263Sshinint ip_gif_ttl = 0;
7854263Sshin#endif
7954263Sshin
8054263SshinSYSCTL_DECL(_net_inet_ip);
8154263SshinSYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW,
8254263Sshin	&ip_gif_ttl,	0, "");
8354263Sshin
8454263Sshinint
8554263Sshinin_gif_output(ifp, family, m, rt)
8654263Sshin	struct ifnet	*ifp;
8754263Sshin	int		family;
8854263Sshin	struct mbuf	*m;
8954263Sshin	struct rtentry *rt;
9054263Sshin{
9154263Sshin	register struct gif_softc *sc = (struct gif_softc*)ifp;
9254263Sshin	struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst;
9354263Sshin	struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc;
9454263Sshin	struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst;
9554263Sshin	struct ip iphdr;	/* capsule IP header, host byte ordered */
9654263Sshin	int proto, error;
9754263Sshin	u_int8_t tos;
9854263Sshin
9954263Sshin	if (sin_src == NULL || sin_dst == NULL ||
10054263Sshin	    sin_src->sin_family != AF_INET ||
10154263Sshin	    sin_dst->sin_family != AF_INET) {
10254263Sshin		m_freem(m);
10354263Sshin		return EAFNOSUPPORT;
10454263Sshin	}
10554263Sshin
10654263Sshin	switch (family) {
10754263Sshin	case AF_INET:
10854263Sshin	    {
10954263Sshin		struct ip *ip;
11054263Sshin
11154263Sshin		proto = IPPROTO_IPV4;
11254263Sshin		if (m->m_len < sizeof(*ip)) {
11354263Sshin			m = m_pullup(m, sizeof(*ip));
11454263Sshin			if (!m)
11554263Sshin				return ENOBUFS;
11654263Sshin		}
11754263Sshin		ip = mtod(m, struct ip *);
11854263Sshin		tos = ip->ip_tos;
11954263Sshin		break;
12054263Sshin	    }
12154263Sshin#ifdef INET6
12254263Sshin	case AF_INET6:
12354263Sshin	    {
12454263Sshin		struct ip6_hdr *ip6;
12554263Sshin		proto = IPPROTO_IPV6;
12654263Sshin		if (m->m_len < sizeof(*ip6)) {
12754263Sshin			m = m_pullup(m, sizeof(*ip6));
12854263Sshin			if (!m)
12954263Sshin				return ENOBUFS;
13054263Sshin		}
13154263Sshin		ip6 = mtod(m, struct ip6_hdr *);
13254263Sshin		tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
13354263Sshin		break;
13454263Sshin	    }
13554263Sshin#endif /*INET6*/
13654263Sshin	default:
13754263Sshin#ifdef DIAGNOSTIC
13854263Sshin		printf("in_gif_output: warning: unknown family %d passed\n",
13954263Sshin			family);
14054263Sshin#endif
14154263Sshin		m_freem(m);
14254263Sshin		return EAFNOSUPPORT;
14354263Sshin	}
14454263Sshin
14554263Sshin	bzero(&iphdr, sizeof(iphdr));
14654263Sshin	iphdr.ip_src = sin_src->sin_addr;
14754263Sshin	if (ifp->if_flags & IFF_LINK0) {
14854263Sshin		/* multi-destination mode */
14954263Sshin		if (sin_dst->sin_addr.s_addr != INADDR_ANY)
15054263Sshin			iphdr.ip_dst = sin_dst->sin_addr;
15154263Sshin		else if (rt) {
15254263Sshin			iphdr.ip_dst = ((struct sockaddr_in *)
15354263Sshin					(rt->rt_gateway))->sin_addr;
15454263Sshin		} else {
15554263Sshin			m_freem(m);
15654263Sshin			return ENETUNREACH;
15754263Sshin		}
15854263Sshin	} else {
15954263Sshin		/* bidirectional configured tunnel mode */
16054263Sshin		if (sin_dst->sin_addr.s_addr != INADDR_ANY)
16154263Sshin			iphdr.ip_dst = sin_dst->sin_addr;
16254263Sshin		else {
16354263Sshin			m_freem(m);
16454263Sshin			return ENETUNREACH;
16554263Sshin		}
16654263Sshin	}
16754263Sshin	iphdr.ip_p = proto;
16854263Sshin	/* version will be set in ip_output() */
16954263Sshin	iphdr.ip_ttl = ip_gif_ttl;
17054263Sshin	iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip);
17154263Sshin
17254263Sshin	/* prepend new IP header */
17354263Sshin	M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
17454263Sshin	if (m && m->m_len < sizeof(struct ip))
17554263Sshin		m = m_pullup(m, sizeof(struct ip));
17654263Sshin	if (m == NULL) {
17754263Sshin		printf("ENOBUFS in in_gif_output %d\n", __LINE__);
17854263Sshin		return ENOBUFS;
17954263Sshin	}
18054263Sshin
18154263Sshin	*(mtod(m, struct ip *)) = iphdr;
18254263Sshin
18354263Sshin	if (dst->sin_family != sin_dst->sin_family ||
18454263Sshin	    dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) {
18554263Sshin		/* cache route doesn't match */
18654263Sshin		dst->sin_family = sin_dst->sin_family;
18754263Sshin		dst->sin_len = sizeof(struct sockaddr_in);
18854263Sshin		dst->sin_addr = sin_dst->sin_addr;
18954263Sshin		if (sc->gif_ro.ro_rt) {
19054263Sshin			RTFREE(sc->gif_ro.ro_rt);
19154263Sshin			sc->gif_ro.ro_rt = NULL;
19254263Sshin		}
19354263Sshin	}
19454263Sshin
19554263Sshin	if (sc->gif_ro.ro_rt == NULL) {
19654263Sshin		rtalloc(&sc->gif_ro);
19754263Sshin		if (sc->gif_ro.ro_rt == NULL) {
19854263Sshin			m_freem(m);
19954263Sshin			return ENETUNREACH;
20054263Sshin		}
20154263Sshin	}
20254263Sshin
20354263Sshin#ifdef IPSEC
20454263Sshin	m->m_pkthdr.rcvif = NULL;
20554263Sshin#endif /*IPSEC*/
20654263Sshin	error = ip_output(m, 0, &sc->gif_ro, 0, 0);
20754263Sshin	return(error);
20854263Sshin}
20954263Sshin
21054263Sshinvoid
21154263Sshin#if __STDC__
21254263Sshinin_gif_input(struct mbuf *m, ...)
21354263Sshin#else
21454263Sshinin_gif_input(m, va_alist)
21554263Sshin	struct mbuf *m;
21654263Sshin	va_dcl
21754263Sshin#endif
21854263Sshin{
21954263Sshin	int off, proto;
22054263Sshin	struct gif_softc *sc;
22154263Sshin	struct ifnet *gifp = NULL;
22254263Sshin	struct ip *ip;
22354263Sshin	int i, af;
22454263Sshin	va_list ap;
22554263Sshin
22654263Sshin	va_start(ap, m);
22754263Sshin	off = va_arg(ap, int);
22854263Sshin	proto = va_arg(ap, int);
22954263Sshin	va_end(ap);
23054263Sshin
23154263Sshin	ip = mtod(m, struct ip *);
23254263Sshin
23354263Sshin	/* this code will be soon improved. */
23454263Sshin#define	satosin(sa)	((struct sockaddr_in *)(sa))
23554263Sshin	for (i = 0, sc = gif; i < ngif; i++, sc++) {
23654263Sshin		if (sc->gif_psrc == NULL
23754263Sshin		 || sc->gif_pdst == NULL
23854263Sshin		 || sc->gif_psrc->sa_family != AF_INET
23954263Sshin		 || sc->gif_pdst->sa_family != AF_INET) {
24054263Sshin			continue;
24154263Sshin		}
24254263Sshin
24354263Sshin		if ((sc->gif_if.if_flags & IFF_UP) == 0)
24454263Sshin			continue;
24554263Sshin
24654263Sshin		if ((sc->gif_if.if_flags & IFF_LINK0)
24754263Sshin		 && satosin(sc->gif_psrc)->sin_addr.s_addr == ip->ip_dst.s_addr
24854263Sshin		 && satosin(sc->gif_pdst)->sin_addr.s_addr == INADDR_ANY) {
24954263Sshin			gifp = &sc->gif_if;
25054263Sshin			continue;
25154263Sshin		}
25254263Sshin
25354263Sshin		if (satosin(sc->gif_psrc)->sin_addr.s_addr == ip->ip_dst.s_addr
25454263Sshin		 && satosin(sc->gif_pdst)->sin_addr.s_addr == ip->ip_src.s_addr)
25554263Sshin		{
25654263Sshin			gifp = &sc->gif_if;
25754263Sshin			break;
25854263Sshin		}
25954263Sshin	}
26054263Sshin
26154263Sshin	if (gifp == NULL) {
26254263Sshin#ifdef MROUTING
26354263Sshin		/* for backward compatibility */
26454263Sshin		if (proto == IPPROTO_IPV4) {
26554263Sshin			ipip_input(m, off, proto);
26654263Sshin			return;
26754263Sshin		}
26854263Sshin#endif /*MROUTING*/
26954263Sshin		m_freem(m);
27054263Sshin		ipstat.ips_nogif++;
27154263Sshin		return;
27254263Sshin	}
27354263Sshin
27454263Sshin	m_adj(m, off);
27554263Sshin
27654263Sshin	switch (proto) {
27754263Sshin	case IPPROTO_IPV4:
27854263Sshin	    {
27954263Sshin		struct ip *ip;
28054263Sshin		af = AF_INET;
28154263Sshin		if (m->m_len < sizeof(*ip)) {
28254263Sshin			m = m_pullup(m, sizeof(*ip));
28354263Sshin			if (!m)
28454263Sshin				return;
28554263Sshin		}
28654263Sshin		ip = mtod(m, struct ip *);
28754263Sshin		break;
28854263Sshin	    }
28954263Sshin#ifdef INET6
29054263Sshin	case IPPROTO_IPV6:
29154263Sshin	    {
29254263Sshin		struct ip6_hdr *ip6;
29354263Sshin		af = AF_INET6;
29454263Sshin		if (m->m_len < sizeof(*ip6)) {
29554263Sshin			m = m_pullup(m, sizeof(*ip6));
29654263Sshin			if (!m)
29754263Sshin				return;
29854263Sshin		}
29954263Sshin		ip6 = mtod(m, struct ip6_hdr *);
30054263Sshin		ip6->ip6_flow &= ~htonl(0xff << 20);
30154263Sshin		break;
30254263Sshin	    }
30354263Sshin#endif /* INET6 */
30454263Sshin	default:
30554263Sshin		ipstat.ips_nogif++;
30654263Sshin		m_freem(m);
30754263Sshin		return;
30854263Sshin	}
30954263Sshin	gif_input(m, af, gifp);
31054263Sshin	return;
31154263Sshin}
312