in_gif.c revision 54263
1/*
2 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the project nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: head/sys/netinet/in_gif.c 54263 1999-12-07 17:39:16Z shin $
30 */
31
32/*
33 * in_gif.c
34 */
35
36#include "opt_mrouting.h"
37#include "opt_inet6.h"
38
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/socket.h>
42#include <sys/sockio.h>
43#include <sys/mbuf.h>
44#include <sys/errno.h>
45#include <sys/kernel.h>
46#include <sys/sysctl.h>
47#include <sys/protosw.h>
48
49#include <net/if.h>
50#include <net/route.h>
51
52#include <netinet/in.h>
53#include <netinet/in_systm.h>
54#include <netinet/ip.h>
55#include <netinet/ip_var.h>
56#include <netinet/in_gif.h>
57
58#ifdef INET6
59#include <netinet/ip6.h>
60#endif
61
62#ifdef MROUTING
63#include <netinet/ip_mroute.h>
64#endif /* MROUTING */
65
66#include <net/if_gif.h>
67
68#include "gif.h"
69
70#include <machine/stdarg.h>
71
72#include <net/net_osdep.h>
73
74#if NGIF > 0
75int ip_gif_ttl = GIF_TTL;
76#else
77int ip_gif_ttl = 0;
78#endif
79
80SYSCTL_DECL(_net_inet_ip);
81SYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW,
82	&ip_gif_ttl,	0, "");
83
84int
85in_gif_output(ifp, family, m, rt)
86	struct ifnet	*ifp;
87	int		family;
88	struct mbuf	*m;
89	struct rtentry *rt;
90{
91	register struct gif_softc *sc = (struct gif_softc*)ifp;
92	struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst;
93	struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc;
94	struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst;
95	struct ip iphdr;	/* capsule IP header, host byte ordered */
96	int proto, error;
97	u_int8_t tos;
98
99	if (sin_src == NULL || sin_dst == NULL ||
100	    sin_src->sin_family != AF_INET ||
101	    sin_dst->sin_family != AF_INET) {
102		m_freem(m);
103		return EAFNOSUPPORT;
104	}
105
106	switch (family) {
107	case AF_INET:
108	    {
109		struct ip *ip;
110
111		proto = IPPROTO_IPV4;
112		if (m->m_len < sizeof(*ip)) {
113			m = m_pullup(m, sizeof(*ip));
114			if (!m)
115				return ENOBUFS;
116		}
117		ip = mtod(m, struct ip *);
118		tos = ip->ip_tos;
119		break;
120	    }
121#ifdef INET6
122	case AF_INET6:
123	    {
124		struct ip6_hdr *ip6;
125		proto = IPPROTO_IPV6;
126		if (m->m_len < sizeof(*ip6)) {
127			m = m_pullup(m, sizeof(*ip6));
128			if (!m)
129				return ENOBUFS;
130		}
131		ip6 = mtod(m, struct ip6_hdr *);
132		tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
133		break;
134	    }
135#endif /*INET6*/
136	default:
137#ifdef DIAGNOSTIC
138		printf("in_gif_output: warning: unknown family %d passed\n",
139			family);
140#endif
141		m_freem(m);
142		return EAFNOSUPPORT;
143	}
144
145	bzero(&iphdr, sizeof(iphdr));
146	iphdr.ip_src = sin_src->sin_addr;
147	if (ifp->if_flags & IFF_LINK0) {
148		/* multi-destination mode */
149		if (sin_dst->sin_addr.s_addr != INADDR_ANY)
150			iphdr.ip_dst = sin_dst->sin_addr;
151		else if (rt) {
152			iphdr.ip_dst = ((struct sockaddr_in *)
153					(rt->rt_gateway))->sin_addr;
154		} else {
155			m_freem(m);
156			return ENETUNREACH;
157		}
158	} else {
159		/* bidirectional configured tunnel mode */
160		if (sin_dst->sin_addr.s_addr != INADDR_ANY)
161			iphdr.ip_dst = sin_dst->sin_addr;
162		else {
163			m_freem(m);
164			return ENETUNREACH;
165		}
166	}
167	iphdr.ip_p = proto;
168	/* version will be set in ip_output() */
169	iphdr.ip_ttl = ip_gif_ttl;
170	iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip);
171
172	/* prepend new IP header */
173	M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
174	if (m && m->m_len < sizeof(struct ip))
175		m = m_pullup(m, sizeof(struct ip));
176	if (m == NULL) {
177		printf("ENOBUFS in in_gif_output %d\n", __LINE__);
178		return ENOBUFS;
179	}
180
181	*(mtod(m, struct ip *)) = iphdr;
182
183	if (dst->sin_family != sin_dst->sin_family ||
184	    dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) {
185		/* cache route doesn't match */
186		dst->sin_family = sin_dst->sin_family;
187		dst->sin_len = sizeof(struct sockaddr_in);
188		dst->sin_addr = sin_dst->sin_addr;
189		if (sc->gif_ro.ro_rt) {
190			RTFREE(sc->gif_ro.ro_rt);
191			sc->gif_ro.ro_rt = NULL;
192		}
193	}
194
195	if (sc->gif_ro.ro_rt == NULL) {
196		rtalloc(&sc->gif_ro);
197		if (sc->gif_ro.ro_rt == NULL) {
198			m_freem(m);
199			return ENETUNREACH;
200		}
201	}
202
203#ifdef IPSEC
204	m->m_pkthdr.rcvif = NULL;
205#endif /*IPSEC*/
206	error = ip_output(m, 0, &sc->gif_ro, 0, 0);
207	return(error);
208}
209
210void
211#if __STDC__
212in_gif_input(struct mbuf *m, ...)
213#else
214in_gif_input(m, va_alist)
215	struct mbuf *m;
216	va_dcl
217#endif
218{
219	int off, proto;
220	struct gif_softc *sc;
221	struct ifnet *gifp = NULL;
222	struct ip *ip;
223	int i, af;
224	va_list ap;
225
226	va_start(ap, m);
227	off = va_arg(ap, int);
228	proto = va_arg(ap, int);
229	va_end(ap);
230
231	ip = mtod(m, struct ip *);
232
233	/* this code will be soon improved. */
234#define	satosin(sa)	((struct sockaddr_in *)(sa))
235	for (i = 0, sc = gif; i < ngif; i++, sc++) {
236		if (sc->gif_psrc == NULL
237		 || sc->gif_pdst == NULL
238		 || sc->gif_psrc->sa_family != AF_INET
239		 || sc->gif_pdst->sa_family != AF_INET) {
240			continue;
241		}
242
243		if ((sc->gif_if.if_flags & IFF_UP) == 0)
244			continue;
245
246		if ((sc->gif_if.if_flags & IFF_LINK0)
247		 && satosin(sc->gif_psrc)->sin_addr.s_addr == ip->ip_dst.s_addr
248		 && satosin(sc->gif_pdst)->sin_addr.s_addr == INADDR_ANY) {
249			gifp = &sc->gif_if;
250			continue;
251		}
252
253		if (satosin(sc->gif_psrc)->sin_addr.s_addr == ip->ip_dst.s_addr
254		 && satosin(sc->gif_pdst)->sin_addr.s_addr == ip->ip_src.s_addr)
255		{
256			gifp = &sc->gif_if;
257			break;
258		}
259	}
260
261	if (gifp == NULL) {
262#ifdef MROUTING
263		/* for backward compatibility */
264		if (proto == IPPROTO_IPV4) {
265			ipip_input(m, off, proto);
266			return;
267		}
268#endif /*MROUTING*/
269		m_freem(m);
270		ipstat.ips_nogif++;
271		return;
272	}
273
274	m_adj(m, off);
275
276	switch (proto) {
277	case IPPROTO_IPV4:
278	    {
279		struct ip *ip;
280		af = AF_INET;
281		if (m->m_len < sizeof(*ip)) {
282			m = m_pullup(m, sizeof(*ip));
283			if (!m)
284				return;
285		}
286		ip = mtod(m, struct ip *);
287		break;
288	    }
289#ifdef INET6
290	case IPPROTO_IPV6:
291	    {
292		struct ip6_hdr *ip6;
293		af = AF_INET6;
294		if (m->m_len < sizeof(*ip6)) {
295			m = m_pullup(m, sizeof(*ip6));
296			if (!m)
297				return;
298		}
299		ip6 = mtod(m, struct ip6_hdr *);
300		ip6->ip6_flow &= ~htonl(0xff << 20);
301		break;
302	    }
303#endif /* INET6 */
304	default:
305		ipstat.ips_nogif++;
306		m_freem(m);
307		return;
308	}
309	gif_input(m, af, gifp);
310	return;
311}
312