in_gif.c revision 105293
1251607Sdim/*	$FreeBSD: head/sys/netinet/in_gif.c 105293 2002-10-16 19:49:37Z ume $	*/
2251607Sdim/*	$KAME: in_gif.c,v 1.54 2001/05/14 14:02:16 itojun Exp $	*/
3251607Sdim
4251607Sdim/*
5251607Sdim * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6251607Sdim * All rights reserved.
7251607Sdim *
8251607Sdim * Redistribution and use in source and binary forms, with or without
9251607Sdim * modification, are permitted provided that the following conditions
10251607Sdim * are met:
11251607Sdim * 1. Redistributions of source code must retain the above copyright
12251607Sdim *    notice, this list of conditions and the following disclaimer.
13251607Sdim * 2. Redistributions in binary form must reproduce the above copyright
14251607Sdim *    notice, this list of conditions and the following disclaimer in the
15251607Sdim *    documentation and/or other materials provided with the distribution.
16251607Sdim * 3. Neither the name of the project nor the names of its contributors
17251607Sdim *    may be used to endorse or promote products derived from this software
18251607Sdim *    without specific prior written permission.
19288943Sdim *
20288943Sdim * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21288943Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22288943Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23288943Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24251607Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25251607Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26251607Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27251607Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28251607Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29251607Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30251607Sdim * SUCH DAMAGE.
31251607Sdim */
32261991Sdim
33251607Sdim#include "opt_mrouting.h"
34251607Sdim#include "opt_inet.h"
35251607Sdim#include "opt_inet6.h"
36261991Sdim
37261991Sdim#include <sys/param.h>
38261991Sdim#include <sys/systm.h>
39261991Sdim#include <sys/socket.h>
40261991Sdim#include <sys/sockio.h>
41261991Sdim#include <sys/mbuf.h>
42288943Sdim#include <sys/errno.h>
43288943Sdim#include <sys/kernel.h>
44288943Sdim#include <sys/sysctl.h>
45288943Sdim#include <sys/protosw.h>
46288943Sdim
47261991Sdim#include <sys/malloc.h>
48261991Sdim
49261991Sdim#include <net/if.h>
50261991Sdim#include <net/route.h>
51261991Sdim
52261991Sdim#include <netinet/in.h>
53261991Sdim#include <netinet/in_systm.h>
54288943Sdim#include <netinet/ip.h>
55288943Sdim#include <netinet/ip_var.h>
56288943Sdim#include <netinet/in_gif.h>
57288943Sdim#include <netinet/in_var.h>
58261991Sdim#include <netinet/ip_encap.h>
59251607Sdim#include <netinet/ip_ecn.h>
60261991Sdim
61261991Sdim#ifdef INET6
62261991Sdim#include <netinet/ip6.h>
63261991Sdim#endif
64261991Sdim
65261991Sdim#ifdef MROUTING
66251607Sdim#include <netinet/ip_mroute.h>
67251607Sdim#endif /* MROUTING */
68251607Sdim
69251607Sdim#include <net/if_gif.h>
70251607Sdim
71261991Sdim#include <net/net_osdep.h>
72261991Sdim
73261991Sdimstatic int gif_validate4(const struct ip *, struct gif_softc *,
74261991Sdim	struct ifnet *);
75251607Sdim
76261991Sdimextern  struct domain inetdomain;
77251607Sdimstruct protosw in_gif_protosw =
78251607Sdim{ SOCK_RAW,	&inetdomain,	0/*IPPROTO_IPV[46]*/,	PR_ATOMIC|PR_ADDR,
79251607Sdim  in_gif_input,	(pr_output_t*)rip_output, 0,		rip_ctloutput,
80251607Sdim  0,
81288943Sdim  0,		0,		0,		0,
82288943Sdim  &rip_usrreqs
83288943Sdim};
84288943Sdim
85288943Sdimstatic int ip_gif_ttl = GIF_TTL;
86288943SdimSYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW,
87288943Sdim	&ip_gif_ttl,	0, "");
88288943Sdim
89288943Sdimint
90288943Sdimin_gif_output(ifp, family, m, rt)
91288943Sdim	struct ifnet	*ifp;
92288943Sdim	int		family;
93288943Sdim	struct mbuf	*m;
94288943Sdim	struct rtentry *rt;
95288943Sdim{
96288943Sdim	struct gif_softc *sc = (struct gif_softc*)ifp;
97251607Sdim	struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst;
98261991Sdim	struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc;
99261991Sdim	struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst;
100261991Sdim	struct ip iphdr;	/* capsule IP header, host byte ordered */
101261991Sdim	int proto, error;
102261991Sdim	u_int8_t tos;
103261991Sdim
104261991Sdim	if (sin_src == NULL || sin_dst == NULL ||
105261991Sdim	    sin_src->sin_family != AF_INET ||
106261991Sdim	    sin_dst->sin_family != AF_INET) {
107261991Sdim		m_freem(m);
108261991Sdim		return EAFNOSUPPORT;
109251607Sdim	}
110261991Sdim
111251607Sdim	switch (family) {
112288943Sdim#ifdef INET
113251607Sdim	case AF_INET:
114251607Sdim	    {
115251607Sdim		struct ip *ip;
116261991Sdim
117251607Sdim		proto = IPPROTO_IPV4;
118251607Sdim		if (m->m_len < sizeof(*ip)) {
119251607Sdim			m = m_pullup(m, sizeof(*ip));
120251607Sdim			if (!m)
121251607Sdim				return ENOBUFS;
122261991Sdim		}
123251607Sdim		ip = mtod(m, struct ip *);
124251607Sdim		tos = ip->ip_tos;
125251607Sdim		break;
126251607Sdim	    }
127261991Sdim#endif /* INET */
128261991Sdim#ifdef INET6
129261991Sdim	case AF_INET6:
130261991Sdim	    {
131261991Sdim		struct ip6_hdr *ip6;
132261991Sdim		proto = IPPROTO_IPV6;
133261991Sdim		if (m->m_len < sizeof(*ip6)) {
134261991Sdim			m = m_pullup(m, sizeof(*ip6));
135261991Sdim			if (!m)
136288943Sdim				return ENOBUFS;
137288943Sdim		}
138288943Sdim		ip6 = mtod(m, struct ip6_hdr *);
139288943Sdim		tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
140288943Sdim		break;
141288943Sdim	    }
142288943Sdim#endif /* INET6 */
143251607Sdim	default:
144251607Sdim#ifdef DEBUG
145251607Sdim		printf("in_gif_output: warning: unknown family %d passed\n",
146251607Sdim			family);
147251607Sdim#endif
148251607Sdim		m_freem(m);
149251607Sdim		return EAFNOSUPPORT;
150251607Sdim	}
151251607Sdim
152288943Sdim	bzero(&iphdr, sizeof(iphdr));
153251607Sdim	iphdr.ip_src = sin_src->sin_addr;
154251607Sdim	/* bidirectional configured tunnel mode */
155251607Sdim	if (sin_dst->sin_addr.s_addr != INADDR_ANY)
156251607Sdim		iphdr.ip_dst = sin_dst->sin_addr;
157251607Sdim	else {
158288943Sdim		m_freem(m);
159251607Sdim		return ENETUNREACH;
160251607Sdim	}
161251607Sdim	iphdr.ip_p = proto;
162251607Sdim	/* version will be set in ip_output() */
163251607Sdim	iphdr.ip_ttl = ip_gif_ttl;
164288943Sdim	iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip);
165251607Sdim	if (ifp->if_flags & IFF_LINK1)
166251607Sdim		ip_ecn_ingress(ECN_ALLOWED, &iphdr.ip_tos, &tos);
167251607Sdim	else
168251607Sdim		ip_ecn_ingress(ECN_NOCARE, &iphdr.ip_tos, &tos);
169251607Sdim
170288943Sdim	/* prepend new IP header */
171251607Sdim	M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
172251607Sdim	if (m && m->m_len < sizeof(struct ip))
173251607Sdim		m = m_pullup(m, sizeof(struct ip));
174251607Sdim	if (m == NULL) {
175251607Sdim		printf("ENOBUFS in in_gif_output %d\n", __LINE__);
176288943Sdim		return ENOBUFS;
177251607Sdim	}
178251607Sdim	bcopy(&iphdr, mtod(m, struct ip *), sizeof(struct ip));
179251607Sdim
180251607Sdim	if (dst->sin_family != sin_dst->sin_family ||
181251607Sdim	    dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) {
182288943Sdim		/* cache route doesn't match */
183251607Sdim		dst->sin_family = sin_dst->sin_family;
184251607Sdim		dst->sin_len = sizeof(struct sockaddr_in);
185251607Sdim		dst->sin_addr = sin_dst->sin_addr;
186251607Sdim		if (sc->gif_ro.ro_rt) {
187288943Sdim			RTFREE(sc->gif_ro.ro_rt);
188288943Sdim			sc->gif_ro.ro_rt = NULL;
189251607Sdim		}
190251607Sdim#if 0
191251607Sdim		sc->gif_if.if_mtu = GIF_MTU;
192251607Sdim#endif
193288943Sdim	}
194288943Sdim
195251607Sdim	if (sc->gif_ro.ro_rt == NULL) {
196251607Sdim		rtalloc(&sc->gif_ro);
197288943Sdim		if (sc->gif_ro.ro_rt == NULL) {
198288943Sdim			m_freem(m);
199288943Sdim			return ENETUNREACH;
200288943Sdim		}
201288943Sdim
202288943Sdim		/* if it constitutes infinite encapsulation, punt. */
203288943Sdim		if (sc->gif_ro.ro_rt->rt_ifp == ifp) {
204288943Sdim			m_freem(m);
205288943Sdim			return ENETUNREACH;	/* XXX */
206288943Sdim		}
207288943Sdim#if 0
208288943Sdim		ifp->if_mtu = sc->gif_ro.ro_rt->rt_ifp->if_mtu
209251607Sdim			- sizeof(struct ip);
210251607Sdim#endif
211288943Sdim	}
212288943Sdim
213251607Sdim	error = ip_output(m, NULL, &sc->gif_ro, 0, NULL, NULL);
214251607Sdim	return(error);
215251607Sdim}
216251607Sdim
217288943Sdimvoid
218288943Sdimin_gif_input(m, off)
219251607Sdim	struct mbuf *m;
220251607Sdim	int off;
221251607Sdim{
222251607Sdim	struct ifnet *gifp = NULL;
223288943Sdim	struct ip *ip;
224288943Sdim	int af;
225251607Sdim	u_int8_t otos;
226251607Sdim	int proto;
227251607Sdim
228251607Sdim	ip = mtod(m, struct ip *);
229288943Sdim	proto = ip->ip_p;
230288943Sdim
231251607Sdim	gifp = (struct ifnet *)encap_getarg(m);
232251607Sdim
233251607Sdim	if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) {
234251607Sdim		m_freem(m);
235288943Sdim		ipstat.ips_nogif++;
236288943Sdim		return;
237251607Sdim	}
238251607Sdim
239251607Sdim	otos = ip->ip_tos;
240251607Sdim	m_adj(m, off);
241251607Sdim
242251607Sdim	switch (proto) {
243288943Sdim#ifdef INET
244288943Sdim	case IPPROTO_IPV4:
245288943Sdim	    {
246251607Sdim		struct ip *ip;
247251607Sdim		af = AF_INET;
248251607Sdim		if (m->m_len < sizeof(*ip)) {
249251607Sdim			m = m_pullup(m, sizeof(*ip));
250288943Sdim			if (!m)
251251607Sdim				return;
252251607Sdim		}
253251607Sdim		ip = mtod(m, struct ip *);
254251607Sdim		if (gifp->if_flags & IFF_LINK1)
255251607Sdim			ip_ecn_egress(ECN_ALLOWED, &otos, &ip->ip_tos);
256251607Sdim		else
257251607Sdim			ip_ecn_egress(ECN_NOCARE, &otos, &ip->ip_tos);
258251607Sdim		break;
259251607Sdim	    }
260251607Sdim#endif
261251607Sdim#ifdef INET6
262251607Sdim	case IPPROTO_IPV6:
263251607Sdim	    {
264251607Sdim		struct ip6_hdr *ip6;
265251607Sdim		u_int8_t itos;
266251607Sdim		af = AF_INET6;
267251607Sdim		if (m->m_len < sizeof(*ip6)) {
268251607Sdim			m = m_pullup(m, sizeof(*ip6));
269251607Sdim			if (!m)
270251607Sdim				return;
271251607Sdim		}
272251607Sdim		ip6 = mtod(m, struct ip6_hdr *);
273251607Sdim		itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
274251607Sdim		if (gifp->if_flags & IFF_LINK1)
275251607Sdim			ip_ecn_egress(ECN_ALLOWED, &otos, &itos);
276251607Sdim		else
277251607Sdim			ip_ecn_egress(ECN_NOCARE, &otos, &itos);
278251607Sdim		ip6->ip6_flow &= ~htonl(0xff << 20);
279251607Sdim		ip6->ip6_flow |= htonl((u_int32_t)itos << 20);
280251607Sdim		break;
281288943Sdim	    }
282288943Sdim#endif /* INET6 */
283288943Sdim	default:
284288943Sdim		ipstat.ips_nogif++;
285288943Sdim		m_freem(m);
286288943Sdim		return;
287288943Sdim	}
288288943Sdim	gif_input(m, af, gifp);
289288943Sdim	return;
290288943Sdim}
291288943Sdim
292288943Sdim/*
293276479Sdim * validate outer address.
294276479Sdim */
295276479Sdimstatic int
296276479Sdimgif_validate4(ip, sc, ifp)
297288943Sdim	const struct ip *ip;
298288943Sdim	struct gif_softc *sc;
299288943Sdim	struct ifnet *ifp;
300288943Sdim{
301288943Sdim	struct sockaddr_in *src, *dst;
302288943Sdim	struct in_ifaddr *ia4;
303276479Sdim
304276479Sdim	src = (struct sockaddr_in *)sc->gif_psrc;
305276479Sdim	dst = (struct sockaddr_in *)sc->gif_pdst;
306276479Sdim
307251607Sdim	/* check for address match */
308251607Sdim	if (src->sin_addr.s_addr != ip->ip_dst.s_addr ||
309251607Sdim	    dst->sin_addr.s_addr != ip->ip_src.s_addr)
310251607Sdim		return 0;
311251607Sdim
312251607Sdim	/* martian filters on outer source - NOT done in ip_input! */
313251607Sdim	if (IN_MULTICAST(ntohl(ip->ip_src.s_addr)))
314251607Sdim		return 0;
315251607Sdim	switch ((ntohl(ip->ip_src.s_addr) & 0xff000000) >> 24) {
316251607Sdim	case 0: case 127: case 255:
317288943Sdim		return 0;
318288943Sdim	}
319288943Sdim	/* reject packets with broadcast on source */
320288943Sdim	TAILQ_FOREACH(ia4, &in_ifaddrhead, ia_link)
321251607Sdim	{
322251607Sdim		if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0)
323251607Sdim			continue;
324251607Sdim		if (ip->ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr)
325251607Sdim			return 0;
326251607Sdim	}
327251607Sdim
328251607Sdim	/* ingress filters on outer source */
329251607Sdim	if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) {
330251607Sdim		struct sockaddr_in sin;
331251607Sdim		struct rtentry *rt;
332251607Sdim
333251607Sdim		bzero(&sin, sizeof(sin));
334251607Sdim		sin.sin_family = AF_INET;
335251607Sdim		sin.sin_len = sizeof(struct sockaddr_in);
336251607Sdim		sin.sin_addr = ip->ip_src;
337251607Sdim		rt = rtalloc1((struct sockaddr *)&sin, 0, 0UL);
338251607Sdim		if (!rt || rt->rt_ifp != ifp) {
339251607Sdim#if 0
340251607Sdim			log(LOG_WARNING, "%s: packet from 0x%x dropped "
341251607Sdim			    "due to ingress filter\n", if_name(&sc->gif_if),
342251607Sdim			    (u_int32_t)ntohl(sin.sin_addr.s_addr));
343251607Sdim#endif
344251607Sdim			if (rt)
345251607Sdim				rtfree(rt);
346251607Sdim			return 0;
347251607Sdim		}
348251607Sdim		rtfree(rt);
349251607Sdim	}
350251607Sdim
351251607Sdim	return 32 * 2;
352251607Sdim}
353251607Sdim
354251607Sdim/*
355251607Sdim * we know that we are in IFF_UP, outer address available, and outer family
356251607Sdim * matched the physical addr family.  see gif_encapcheck().
357251607Sdim */
358251607Sdimint
359251607Sdimgif_encapcheck4(m, off, proto, arg)
360251607Sdim	const struct mbuf *m;
361251607Sdim	int off;
362251607Sdim	int proto;
363251607Sdim	void *arg;
364251607Sdim{
365251607Sdim	struct ip ip;
366251607Sdim	struct gif_softc *sc;
367251607Sdim	struct ifnet *ifp;
368251607Sdim
369251607Sdim	/* sanity check done in caller */
370251607Sdim	sc = (struct gif_softc *)arg;
371251607Sdim
372251607Sdim	/* LINTED const cast */
373251607Sdim	m_copydata(m, 0, sizeof(ip), (caddr_t)&ip);
374251607Sdim	ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL;
375251607Sdim
376251607Sdim	return gif_validate4(&ip, sc, ifp);
377251607Sdim}
378251607Sdim
379251607Sdimint
380251607Sdimin_gif_attach(sc)
381251607Sdim	struct gif_softc *sc;
382251607Sdim{
383251607Sdim#ifndef USE_ENCAPCHECK
384251607Sdim	struct sockaddr_in mask4;
385251607Sdim
386251607Sdim	bzero(&mask4, sizeof(mask4));
387251607Sdim	mask4.sin_len = sizeof(struct sockaddr_in);
388251607Sdim	mask4.sin_addr.s_addr = ~0;
389251607Sdim
390251607Sdim	if (!sc->gif_psrc || !sc->gif_pdst)
391251607Sdim		return EINVAL;
392251607Sdim	sc->encap_cookie4 = encap_attach(AF_INET, -1, sc->gif_psrc,
393251607Sdim	    (struct sockaddr *)&mask4, sc->gif_pdst, (struct sockaddr *)&mask4,
394251607Sdim	    (struct protosw *)&in_gif_protosw, sc);
395251607Sdim#else
396251607Sdim	sc->encap_cookie4 = encap_attach_func(AF_INET, -1, gif_encapcheck,
397251607Sdim	    &in_gif_protosw, sc);
398251607Sdim#endif
399251607Sdim	if (sc->encap_cookie4 == NULL)
400251607Sdim		return EEXIST;
401251607Sdim	return 0;
402251607Sdim}
403251607Sdim
404261991Sdimint
405261991Sdimin_gif_detach(sc)
406261991Sdim	struct gif_softc *sc;
407261991Sdim{
408251607Sdim	int error;
409251607Sdim
410251607Sdim	error = encap_detach(sc->encap_cookie4);
411251607Sdim	if (error == 0)
412251607Sdim		sc->encap_cookie4 = NULL;
413251607Sdim	return error;
414251607Sdim}
415251607Sdim