in_gif.c revision 79106
1/*	$FreeBSD: head/sys/netinet/in_gif.c 79106 2001-07-02 21:02:09Z brooks $	*/
2/*	$KAME: in_gif.c,v 1.54 2001/05/14 14:02:16 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 "opt_mrouting.h"
34#include "opt_inet.h"
35#include "opt_inet6.h"
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/socket.h>
40#include <sys/sockio.h>
41#include <sys/mbuf.h>
42#include <sys/errno.h>
43#include <sys/kernel.h>
44#include <sys/sysctl.h>
45
46#include <sys/malloc.h>
47
48#include <net/if.h>
49#include <net/route.h>
50
51#include <netinet/in.h>
52#include <netinet/in_systm.h>
53#include <netinet/ip.h>
54#include <netinet/ip_var.h>
55#include <netinet/in_gif.h>
56#include <netinet/in_var.h>
57#include <netinet/ip_encap.h>
58#include <netinet/ip_ecn.h>
59
60#ifdef INET6
61#include <netinet/ip6.h>
62#endif
63
64#ifdef MROUTING
65#include <netinet/ip_mroute.h>
66#endif /* MROUTING */
67
68#include <net/if_gif.h>
69
70#include <net/net_osdep.h>
71
72int ip_gif_ttl = GIF_TTL;
73SYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW,
74	&ip_gif_ttl,	0, "");
75
76int
77in_gif_output(ifp, family, m, rt)
78	struct ifnet	*ifp;
79	int		family;
80	struct mbuf	*m;
81	struct rtentry *rt;
82{
83	struct gif_softc *sc = (struct gif_softc*)ifp;
84	struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst;
85	struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc;
86	struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst;
87	struct ip iphdr;	/* capsule IP header, host byte ordered */
88	int proto, error;
89	u_int8_t tos;
90
91	if (sin_src == NULL || sin_dst == NULL ||
92	    sin_src->sin_family != AF_INET ||
93	    sin_dst->sin_family != AF_INET) {
94		m_freem(m);
95		return EAFNOSUPPORT;
96	}
97
98	switch (family) {
99#ifdef INET
100	case AF_INET:
101	    {
102		struct ip *ip;
103
104		proto = IPPROTO_IPV4;
105		if (m->m_len < sizeof(*ip)) {
106			m = m_pullup(m, sizeof(*ip));
107			if (!m)
108				return ENOBUFS;
109		}
110		ip = mtod(m, struct ip *);
111		tos = ip->ip_tos;
112		break;
113	    }
114#endif /*INET*/
115#ifdef INET6
116	case AF_INET6:
117	    {
118		struct ip6_hdr *ip6;
119		proto = IPPROTO_IPV6;
120		if (m->m_len < sizeof(*ip6)) {
121			m = m_pullup(m, sizeof(*ip6));
122			if (!m)
123				return ENOBUFS;
124		}
125		ip6 = mtod(m, struct ip6_hdr *);
126		tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
127		break;
128	    }
129#endif /*INET6*/
130	default:
131#ifdef DEBUG
132		printf("in_gif_output: warning: unknown family %d passed\n",
133			family);
134#endif
135		m_freem(m);
136		return EAFNOSUPPORT;
137	}
138
139	bzero(&iphdr, sizeof(iphdr));
140	iphdr.ip_src = sin_src->sin_addr;
141	/* bidirectional configured tunnel mode */
142	if (sin_dst->sin_addr.s_addr != INADDR_ANY)
143		iphdr.ip_dst = sin_dst->sin_addr;
144	else {
145		m_freem(m);
146		return ENETUNREACH;
147	}
148	iphdr.ip_p = proto;
149	/* version will be set in ip_output() */
150	iphdr.ip_ttl = ip_gif_ttl;
151	iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip);
152	if (ifp->if_flags & IFF_LINK1)
153		ip_ecn_ingress(ECN_ALLOWED, &iphdr.ip_tos, &tos);
154	else
155		ip_ecn_ingress(ECN_NOCARE, &iphdr.ip_tos, &tos);
156
157	/* prepend new IP header */
158	M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
159	if (m && m->m_len < sizeof(struct ip))
160		m = m_pullup(m, sizeof(struct ip));
161	if (m == NULL) {
162		printf("ENOBUFS in in_gif_output %d\n", __LINE__);
163		return ENOBUFS;
164	}
165	bcopy(&iphdr, mtod(m, struct ip *), sizeof(struct ip));
166
167	if (dst->sin_family != sin_dst->sin_family ||
168	    dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) {
169		/* cache route doesn't match */
170		dst->sin_family = sin_dst->sin_family;
171		dst->sin_len = sizeof(struct sockaddr_in);
172		dst->sin_addr = sin_dst->sin_addr;
173		if (sc->gif_ro.ro_rt) {
174			RTFREE(sc->gif_ro.ro_rt);
175			sc->gif_ro.ro_rt = NULL;
176		}
177#if 0
178		sc->gif_if.if_mtu = GIF_MTU;
179#endif
180	}
181
182	if (sc->gif_ro.ro_rt == NULL) {
183		rtalloc(&sc->gif_ro);
184		if (sc->gif_ro.ro_rt == NULL) {
185			m_freem(m);
186			return ENETUNREACH;
187		}
188
189		/* if it constitutes infinite encapsulation, punt. */
190		if (sc->gif_ro.ro_rt->rt_ifp == ifp) {
191			m_freem(m);
192			return ENETUNREACH;	/*XXX*/
193		}
194#if 0
195		ifp->if_mtu = sc->gif_ro.ro_rt->rt_ifp->if_mtu
196			- sizeof(struct ip);
197#endif
198	}
199
200	error = ip_output(m, NULL, &sc->gif_ro, 0, NULL);
201	return(error);
202}
203
204void
205in_gif_input(m, off, proto)
206	struct mbuf *m;
207	int off;
208	int proto;
209{
210	struct ifnet *gifp = NULL;
211	struct ip *ip;
212	int af;
213	u_int8_t otos;
214
215	ip = mtod(m, struct ip *);
216
217	gifp = (struct ifnet *)encap_getarg(m);
218
219	if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) {
220		m_freem(m);
221		ipstat.ips_nogif++;
222		return;
223	}
224
225	otos = ip->ip_tos;
226	m_adj(m, off);
227
228	switch (proto) {
229#ifdef INET
230	case IPPROTO_IPV4:
231	    {
232		struct ip *ip;
233		af = AF_INET;
234		if (m->m_len < sizeof(*ip)) {
235			m = m_pullup(m, sizeof(*ip));
236			if (!m)
237				return;
238		}
239		ip = mtod(m, struct ip *);
240		if (gifp->if_flags & IFF_LINK1)
241			ip_ecn_egress(ECN_ALLOWED, &otos, &ip->ip_tos);
242		else
243			ip_ecn_egress(ECN_NOCARE, &otos, &ip->ip_tos);
244		break;
245	    }
246#endif
247#ifdef INET6
248	case IPPROTO_IPV6:
249	    {
250		struct ip6_hdr *ip6;
251		u_int8_t itos;
252		af = AF_INET6;
253		if (m->m_len < sizeof(*ip6)) {
254			m = m_pullup(m, sizeof(*ip6));
255			if (!m)
256				return;
257		}
258		ip6 = mtod(m, struct ip6_hdr *);
259		itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
260		if (gifp->if_flags & IFF_LINK1)
261			ip_ecn_egress(ECN_ALLOWED, &otos, &itos);
262		else
263			ip_ecn_egress(ECN_NOCARE, &otos, &itos);
264		ip6->ip6_flow &= ~htonl(0xff << 20);
265		ip6->ip6_flow |= htonl((u_int32_t)itos << 20);
266		break;
267	    }
268#endif /* INET6 */
269	default:
270		ipstat.ips_nogif++;
271		m_freem(m);
272		return;
273	}
274	gif_input(m, af, gifp);
275	return;
276}
277
278/*
279 * we know that we are in IFF_UP, outer address available, and outer family
280 * matched the physical addr family.  see gif_encapcheck().
281 */
282int
283gif_encapcheck4(m, off, proto, arg)
284	const struct mbuf *m;
285	int off;
286	int proto;
287	void *arg;
288{
289	struct ip ip;
290	struct gif_softc *sc;
291	struct sockaddr_in *src, *dst;
292	int addrmatch;
293	struct in_ifaddr *ia4;
294
295	/* sanity check done in caller */
296	sc = (struct gif_softc *)arg;
297	src = (struct sockaddr_in *)sc->gif_psrc;
298	dst = (struct sockaddr_in *)sc->gif_pdst;
299
300	/* LINTED const cast */
301	m_copydata((struct mbuf *)m, 0, sizeof(ip), (caddr_t)&ip);
302
303	/* check for address match */
304	addrmatch = 0;
305	if (src->sin_addr.s_addr == ip.ip_dst.s_addr)
306		addrmatch |= 1;
307	if (dst->sin_addr.s_addr == ip.ip_src.s_addr)
308		addrmatch |= 2;
309	if (addrmatch != 3)
310		return 0;
311
312	/* martian filters on outer source - NOT done in ip_input! */
313	if (IN_MULTICAST(ntohl(ip.ip_src.s_addr)))
314		return 0;
315	switch ((ntohl(ip.ip_src.s_addr) & 0xff000000) >> 24) {
316	case 0: case 127: case 255:
317		return 0;
318	}
319	/* reject packets with broadcast on source */
320	TAILQ_FOREACH(ia4, &in_ifaddrhead, ia_link)
321	{
322		if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0)
323			continue;
324		if (ip.ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr)
325			return 0;
326	}
327
328	/* ingress filters on outer source */
329	if ((sc->gif_if.if_flags & IFF_LINK2) == 0 &&
330	    (m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) {
331		struct sockaddr_in sin;
332		struct rtentry *rt;
333
334		bzero(&sin, sizeof(sin));
335		sin.sin_family = AF_INET;
336		sin.sin_len = sizeof(struct sockaddr_in);
337		sin.sin_addr = ip.ip_src;
338		rt = rtalloc1((struct sockaddr *)&sin, 0, 0UL);
339		if (!rt || rt->rt_ifp != m->m_pkthdr.rcvif) {
340#if 0
341			log(LOG_WARNING, "%s: packet from 0x%x dropped "
342			    "due to ingress filter\n", if_name(&sc->gif_if),
343			    (u_int32_t)ntohl(sin.sin_addr.s_addr));
344#endif
345			if (rt)
346				rtfree(rt);
347			return 0;
348		}
349		rtfree(rt);
350	}
351
352	return 32 * 2;
353}
354