in_gif.c revision 55009
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 55009 1999-12-22 19:13:38Z 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>
5555009Sshin#ifdef INET6
5655009Sshin#include <netinet/ip6.h>
5755009Sshin#endif
5854263Sshin#include <netinet/ip_var.h>
5954263Sshin#include <netinet/in_gif.h>
6055009Sshin#include <netinet/ip_ecn.h>
6154263Sshin#ifdef INET6
6255009Sshin#include <netinet6/ip6_ecn.h>
6354263Sshin#endif
6454263Sshin
6554263Sshin#ifdef MROUTING
6654263Sshin#include <netinet/ip_mroute.h>
6754263Sshin#endif /* MROUTING */
6854263Sshin
6954263Sshin#include <net/if_gif.h>
7054263Sshin
7154263Sshin#include "gif.h"
7254263Sshin
7354263Sshin#include <machine/stdarg.h>
7454263Sshin
7554263Sshin#include <net/net_osdep.h>
7654263Sshin
7754263Sshin#if NGIF > 0
7854263Sshinint ip_gif_ttl = GIF_TTL;
7954263Sshin#else
8054263Sshinint ip_gif_ttl = 0;
8154263Sshin#endif
8254263Sshin
8354263SshinSYSCTL_DECL(_net_inet_ip);
8454263SshinSYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW,
8554263Sshin	&ip_gif_ttl,	0, "");
8654263Sshin
8754263Sshinint
8854263Sshinin_gif_output(ifp, family, m, rt)
8954263Sshin	struct ifnet	*ifp;
9054263Sshin	int		family;
9154263Sshin	struct mbuf	*m;
9254263Sshin	struct rtentry *rt;
9354263Sshin{
9454263Sshin	register struct gif_softc *sc = (struct gif_softc*)ifp;
9554263Sshin	struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst;
9654263Sshin	struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc;
9754263Sshin	struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst;
9854263Sshin	struct ip iphdr;	/* capsule IP header, host byte ordered */
9954263Sshin	int proto, error;
10054263Sshin	u_int8_t tos;
10154263Sshin
10254263Sshin	if (sin_src == NULL || sin_dst == NULL ||
10354263Sshin	    sin_src->sin_family != AF_INET ||
10454263Sshin	    sin_dst->sin_family != AF_INET) {
10554263Sshin		m_freem(m);
10654263Sshin		return EAFNOSUPPORT;
10754263Sshin	}
10854263Sshin
10954263Sshin	switch (family) {
11054263Sshin	case AF_INET:
11154263Sshin	    {
11254263Sshin		struct ip *ip;
11354263Sshin
11454263Sshin		proto = IPPROTO_IPV4;
11554263Sshin		if (m->m_len < sizeof(*ip)) {
11654263Sshin			m = m_pullup(m, sizeof(*ip));
11754263Sshin			if (!m)
11854263Sshin				return ENOBUFS;
11954263Sshin		}
12054263Sshin		ip = mtod(m, struct ip *);
12154263Sshin		tos = ip->ip_tos;
12254263Sshin		break;
12354263Sshin	    }
12454263Sshin#ifdef INET6
12554263Sshin	case AF_INET6:
12654263Sshin	    {
12754263Sshin		struct ip6_hdr *ip6;
12854263Sshin		proto = IPPROTO_IPV6;
12954263Sshin		if (m->m_len < sizeof(*ip6)) {
13054263Sshin			m = m_pullup(m, sizeof(*ip6));
13154263Sshin			if (!m)
13254263Sshin				return ENOBUFS;
13354263Sshin		}
13454263Sshin		ip6 = mtod(m, struct ip6_hdr *);
13554263Sshin		tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
13654263Sshin		break;
13754263Sshin	    }
13854263Sshin#endif /*INET6*/
13954263Sshin	default:
14054263Sshin#ifdef DIAGNOSTIC
14154263Sshin		printf("in_gif_output: warning: unknown family %d passed\n",
14254263Sshin			family);
14354263Sshin#endif
14454263Sshin		m_freem(m);
14554263Sshin		return EAFNOSUPPORT;
14654263Sshin	}
14754263Sshin
14854263Sshin	bzero(&iphdr, sizeof(iphdr));
14954263Sshin	iphdr.ip_src = sin_src->sin_addr;
15054263Sshin	if (ifp->if_flags & IFF_LINK0) {
15154263Sshin		/* multi-destination mode */
15254263Sshin		if (sin_dst->sin_addr.s_addr != INADDR_ANY)
15354263Sshin			iphdr.ip_dst = sin_dst->sin_addr;
15454263Sshin		else if (rt) {
15554263Sshin			iphdr.ip_dst = ((struct sockaddr_in *)
15654263Sshin					(rt->rt_gateway))->sin_addr;
15754263Sshin		} else {
15854263Sshin			m_freem(m);
15954263Sshin			return ENETUNREACH;
16054263Sshin		}
16154263Sshin	} else {
16254263Sshin		/* bidirectional configured tunnel mode */
16354263Sshin		if (sin_dst->sin_addr.s_addr != INADDR_ANY)
16454263Sshin			iphdr.ip_dst = sin_dst->sin_addr;
16554263Sshin		else {
16654263Sshin			m_freem(m);
16754263Sshin			return ENETUNREACH;
16854263Sshin		}
16954263Sshin	}
17054263Sshin	iphdr.ip_p = proto;
17154263Sshin	/* version will be set in ip_output() */
17254263Sshin	iphdr.ip_ttl = ip_gif_ttl;
17354263Sshin	iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip);
17455009Sshin	if (ifp->if_flags & IFF_LINK1)
17555009Sshin		ip_ecn_ingress(ECN_ALLOWED, &iphdr.ip_tos, &tos);
17654263Sshin
17754263Sshin	/* prepend new IP header */
17854263Sshin	M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
17954263Sshin	if (m && m->m_len < sizeof(struct ip))
18054263Sshin		m = m_pullup(m, sizeof(struct ip));
18154263Sshin	if (m == NULL) {
18254263Sshin		printf("ENOBUFS in in_gif_output %d\n", __LINE__);
18354263Sshin		return ENOBUFS;
18454263Sshin	}
18554263Sshin
18654263Sshin	*(mtod(m, struct ip *)) = iphdr;
18754263Sshin
18854263Sshin	if (dst->sin_family != sin_dst->sin_family ||
18954263Sshin	    dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) {
19054263Sshin		/* cache route doesn't match */
19154263Sshin		dst->sin_family = sin_dst->sin_family;
19254263Sshin		dst->sin_len = sizeof(struct sockaddr_in);
19354263Sshin		dst->sin_addr = sin_dst->sin_addr;
19454263Sshin		if (sc->gif_ro.ro_rt) {
19554263Sshin			RTFREE(sc->gif_ro.ro_rt);
19654263Sshin			sc->gif_ro.ro_rt = NULL;
19754263Sshin		}
19854263Sshin	}
19954263Sshin
20054263Sshin	if (sc->gif_ro.ro_rt == NULL) {
20154263Sshin		rtalloc(&sc->gif_ro);
20254263Sshin		if (sc->gif_ro.ro_rt == NULL) {
20354263Sshin			m_freem(m);
20454263Sshin			return ENETUNREACH;
20554263Sshin		}
20654263Sshin	}
20754263Sshin
20854263Sshin	error = ip_output(m, 0, &sc->gif_ro, 0, 0);
20954263Sshin	return(error);
21054263Sshin}
21154263Sshin
21254263Sshinvoid
21355009Sshinin_gif_input(struct mbuf *m, int off, int proto)
21454263Sshin{
21554263Sshin	struct gif_softc *sc;
21654263Sshin	struct ifnet *gifp = NULL;
21754263Sshin	struct ip *ip;
21854263Sshin	int i, af;
21955009Sshin	u_int8_t otos;
22054263Sshin
22154263Sshin	ip = mtod(m, struct ip *);
22254263Sshin
22354263Sshin	/* this code will be soon improved. */
22454263Sshin#define	satosin(sa)	((struct sockaddr_in *)(sa))
22554263Sshin	for (i = 0, sc = gif; i < ngif; i++, sc++) {
22654263Sshin		if (sc->gif_psrc == NULL
22754263Sshin		 || sc->gif_pdst == NULL
22854263Sshin		 || sc->gif_psrc->sa_family != AF_INET
22954263Sshin		 || sc->gif_pdst->sa_family != AF_INET) {
23054263Sshin			continue;
23154263Sshin		}
23254263Sshin
23354263Sshin		if ((sc->gif_if.if_flags & IFF_UP) == 0)
23454263Sshin			continue;
23554263Sshin
23654263Sshin		if ((sc->gif_if.if_flags & IFF_LINK0)
23754263Sshin		 && satosin(sc->gif_psrc)->sin_addr.s_addr == ip->ip_dst.s_addr
23854263Sshin		 && satosin(sc->gif_pdst)->sin_addr.s_addr == INADDR_ANY) {
23954263Sshin			gifp = &sc->gif_if;
24054263Sshin			continue;
24154263Sshin		}
24254263Sshin
24354263Sshin		if (satosin(sc->gif_psrc)->sin_addr.s_addr == ip->ip_dst.s_addr
24454263Sshin		 && satosin(sc->gif_pdst)->sin_addr.s_addr == ip->ip_src.s_addr)
24554263Sshin		{
24654263Sshin			gifp = &sc->gif_if;
24754263Sshin			break;
24854263Sshin		}
24954263Sshin	}
25054263Sshin
25154263Sshin	if (gifp == NULL) {
25254263Sshin#ifdef MROUTING
25354263Sshin		/* for backward compatibility */
25454263Sshin		if (proto == IPPROTO_IPV4) {
25555009Sshin			ipip_input(m, off, proto);
25654263Sshin			return;
25754263Sshin		}
25854263Sshin#endif /*MROUTING*/
25954263Sshin		m_freem(m);
26054263Sshin		ipstat.ips_nogif++;
26154263Sshin		return;
26254263Sshin	}
26354263Sshin
26455009Sshin	otos = ip->ip_tos;
26554263Sshin	m_adj(m, off);
26654263Sshin
26754263Sshin	switch (proto) {
26854263Sshin	case IPPROTO_IPV4:
26954263Sshin	    {
27054263Sshin		struct ip *ip;
27154263Sshin		af = AF_INET;
27254263Sshin		if (m->m_len < sizeof(*ip)) {
27354263Sshin			m = m_pullup(m, sizeof(*ip));
27454263Sshin			if (!m)
27554263Sshin				return;
27654263Sshin		}
27754263Sshin		ip = mtod(m, struct ip *);
27855009Sshin		if (gifp->if_flags & IFF_LINK1)
27955009Sshin			ip_ecn_egress(ECN_ALLOWED, &otos, &ip->ip_tos);
28054263Sshin		break;
28154263Sshin	    }
28254263Sshin#ifdef INET6
28354263Sshin	case IPPROTO_IPV6:
28454263Sshin	    {
28554263Sshin		struct ip6_hdr *ip6;
28655009Sshin		u_int8_t itos;
28754263Sshin		af = AF_INET6;
28854263Sshin		if (m->m_len < sizeof(*ip6)) {
28954263Sshin			m = m_pullup(m, sizeof(*ip6));
29054263Sshin			if (!m)
29154263Sshin				return;
29254263Sshin		}
29354263Sshin		ip6 = mtod(m, struct ip6_hdr *);
29455009Sshin		itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
29555009Sshin		if (gifp->if_flags & IFF_LINK1)
29655009Sshin			ip_ecn_egress(ECN_ALLOWED, &otos, &itos);
29754263Sshin		ip6->ip6_flow &= ~htonl(0xff << 20);
29855009Sshin		ip6->ip6_flow |= htonl((u_int32_t)itos << 20);
29954263Sshin		break;
30054263Sshin	    }
30154263Sshin#endif /* INET6 */
30254263Sshin	default:
30354263Sshin		ipstat.ips_nogif++;
30454263Sshin		m_freem(m);
30554263Sshin		return;
30654263Sshin	}
30754263Sshin	gif_input(m, af, gifp);
30854263Sshin	return;
30954263Sshin}
310