in_gif.c revision 172307
1207753Smm/*	$FreeBSD: head/sys/netinet/in_gif.c 172307 2007-09-23 17:50:17Z csjp $	*/
2207753Smm/*	$KAME: in_gif.c,v 1.54 2001/05/14 14:02:16 itojun Exp $	*/
3207753Smm
4207753Smm/*-
5207753Smm * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6207753Smm * All rights reserved.
7207753Smm *
8207753Smm * Redistribution and use in source and binary forms, with or without
9207753Smm * modification, are permitted provided that the following conditions
10207753Smm * are met:
11207753Smm * 1. Redistributions of source code must retain the above copyright
12207753Smm *    notice, this list of conditions and the following disclaimer.
13207753Smm * 2. Redistributions in binary form must reproduce the above copyright
14207753Smm *    notice, this list of conditions and the following disclaimer in the
15207753Smm *    documentation and/or other materials provided with the distribution.
16207753Smm * 3. Neither the name of the project nor the names of its contributors
17207753Smm *    may be used to endorse or promote products derived from this software
18207753Smm *    without specific prior written permission.
19207753Smm *
20207753Smm * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21207753Smm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22207753Smm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23207753Smm * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24207753Smm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25207753Smm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26207753Smm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27207753Smm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28207753Smm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29207753Smm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30207753Smm * SUCH DAMAGE.
31207753Smm */
32207753Smm
33207753Smm#include "opt_mrouting.h"
34207753Smm#include "opt_inet.h"
35207753Smm#include "opt_inet6.h"
36207753Smm
37207753Smm#include <sys/param.h>
38207753Smm#include <sys/systm.h>
39207753Smm#include <sys/socket.h>
40207753Smm#include <sys/sockio.h>
41207753Smm#include <sys/mbuf.h>
42207753Smm#include <sys/errno.h>
43207753Smm#include <sys/kernel.h>
44207753Smm#include <sys/sysctl.h>
45207753Smm#include <sys/protosw.h>
46207753Smm
47207753Smm#include <sys/malloc.h>
48207753Smm
49207753Smm#include <net/if.h>
50207753Smm#include <net/route.h>
51207753Smm
52207753Smm#include <netinet/in.h>
53207753Smm#include <netinet/in_systm.h>
54207753Smm#include <netinet/ip.h>
55207753Smm#include <netinet/ip_var.h>
56207753Smm#include <netinet/in_gif.h>
57207753Smm#include <netinet/in_var.h>
58207753Smm#include <netinet/ip_encap.h>
59207753Smm#include <netinet/ip_ecn.h>
60207753Smm
61207753Smm#ifdef INET6
62207753Smm#include <netinet/ip6.h>
63207753Smm#endif
64207753Smm
65207753Smm#ifdef MROUTING
66207753Smm#include <netinet/ip_mroute.h>
67207753Smm#endif /* MROUTING */
68207753Smm
69207753Smm#include <net/if_gif.h>
70207753Smm
71207753Smmstatic int gif_validate4(const struct ip *, struct gif_softc *,
72207753Smm	struct ifnet *);
73215187Smm
74215187Smmextern  struct domain inetdomain;
75215187Smmstruct protosw in_gif_protosw = {
76215187Smm	.pr_type =		SOCK_RAW,
77215187Smm	.pr_domain =		&inetdomain,
78215187Smm	.pr_protocol =		0/* IPPROTO_IPV[46] */,
79215187Smm	.pr_flags =		PR_ATOMIC|PR_ADDR,
80215187Smm	.pr_input =		in_gif_input,
81215187Smm	.pr_output =		(pr_output_t*)rip_output,
82215187Smm	.pr_ctloutput =		rip_ctloutput,
83215187Smm	.pr_usrreqs =		&rip_usrreqs
84207753Smm};
85207753Smm
86207753Smmstatic int ip_gif_ttl = GIF_TTL;
87207753SmmSYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW,
88207753Smm	&ip_gif_ttl,	0, "");
89207753Smm
90223935Smmint
91207753Smmin_gif_output(struct ifnet *ifp, int family, struct mbuf *m)
92207753Smm{
93207753Smm	struct gif_softc *sc = ifp->if_softc;
94207753Smm	struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst;
95207753Smm	struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc;
96207753Smm	struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst;
97207753Smm	struct ip iphdr;	/* capsule IP header, host byte ordered */
98207753Smm	struct etherip_header eiphdr;
99207753Smm	int proto, error;
100207753Smm	u_int8_t tos;
101207753Smm
102207753Smm	GIF_LOCK_ASSERT(sc);
103207753Smm
104207753Smm	if (sin_src == NULL || sin_dst == NULL ||
105207753Smm	    sin_src->sin_family != AF_INET ||
106207753Smm	    sin_dst->sin_family != AF_INET) {
107207753Smm		m_freem(m);
108207753Smm		return EAFNOSUPPORT;
109207753Smm	}
110207753Smm
111207753Smm	switch (family) {
112207753Smm#ifdef INET
113207753Smm	case AF_INET:
114207753Smm	    {
115207753Smm		struct ip *ip;
116207753Smm
117207753Smm		proto = IPPROTO_IPV4;
118207753Smm		if (m->m_len < sizeof(*ip)) {
119207753Smm			m = m_pullup(m, sizeof(*ip));
120207753Smm			if (!m)
121207753Smm				return ENOBUFS;
122207753Smm		}
123207753Smm		ip = mtod(m, struct ip *);
124207753Smm		tos = ip->ip_tos;
125207753Smm		break;
126207753Smm	    }
127207753Smm#endif /* INET */
128207753Smm#ifdef INET6
129207753Smm	case AF_INET6:
130207753Smm	    {
131207753Smm		struct ip6_hdr *ip6;
132207753Smm		proto = IPPROTO_IPV6;
133207753Smm		if (m->m_len < sizeof(*ip6)) {
134207753Smm			m = m_pullup(m, sizeof(*ip6));
135207753Smm			if (!m)
136207753Smm				return ENOBUFS;
137207753Smm		}
138207753Smm		ip6 = mtod(m, struct ip6_hdr *);
139207753Smm		tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
140215187Smm		break;
141207753Smm	    }
142207753Smm#endif /* INET6 */
143215187Smm	case AF_LINK:
144207753Smm 		proto = IPPROTO_ETHERIP;
145207753Smm 		eiphdr.eip_ver = ETHERIP_VERSION & ETHERIP_VER_VERS_MASK;
146215187Smm 		eiphdr.eip_pad = 0;
147215187Smm 		/* prepend Ethernet-in-IP header */
148207753Smm 		M_PREPEND(m, sizeof(struct etherip_header), M_DONTWAIT);
149207753Smm 		if (m && m->m_len < sizeof(struct etherip_header))
150207753Smm 			m = m_pullup(m, sizeof(struct etherip_header));
151207753Smm 		if (m == NULL)
152207753Smm 			return ENOBUFS;
153207753Smm 		bcopy(&eiphdr, mtod(m, struct etherip_header *),
154207753Smm		    sizeof(struct etherip_header));
155207753Smm		break;
156207753Smm
157207753Smm	default:
158207753Smm#ifdef DEBUG
159207753Smm		printf("in_gif_output: warning: unknown family %d passed\n",
160207753Smm			family);
161207753Smm#endif
162207753Smm		m_freem(m);
163207753Smm		return EAFNOSUPPORT;
164207753Smm	}
165207753Smm
166207753Smm	bzero(&iphdr, sizeof(iphdr));
167207753Smm	iphdr.ip_src = sin_src->sin_addr;
168207753Smm	/* bidirectional configured tunnel mode */
169207753Smm	if (sin_dst->sin_addr.s_addr != INADDR_ANY)
170207753Smm		iphdr.ip_dst = sin_dst->sin_addr;
171207753Smm	else {
172207753Smm		m_freem(m);
173207753Smm		return ENETUNREACH;
174207753Smm	}
175207753Smm	iphdr.ip_p = proto;
176207753Smm	/* version will be set in ip_output() */
177207753Smm	iphdr.ip_ttl = ip_gif_ttl;
178207753Smm	iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip);
179207753Smm	ip_ecn_ingress((ifp->if_flags & IFF_LINK1) ? ECN_ALLOWED : ECN_NOCARE,
180207753Smm		       &iphdr.ip_tos, &tos);
181207753Smm
182207753Smm	/* prepend new IP header */
183207753Smm	M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
184207753Smm	if (m && m->m_len < sizeof(struct ip))
185207753Smm		m = m_pullup(m, sizeof(struct ip));
186207753Smm	if (m == NULL) {
187207753Smm		printf("ENOBUFS in in_gif_output %d\n", __LINE__);
188207753Smm		return ENOBUFS;
189207753Smm	}
190207753Smm	bcopy(&iphdr, mtod(m, struct ip *), sizeof(struct ip));
191207753Smm
192207753Smm	if (dst->sin_family != sin_dst->sin_family ||
193207753Smm	    dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) {
194207753Smm		/* cache route doesn't match */
195207753Smm		bzero(dst, sizeof(*dst));
196207753Smm		dst->sin_family = sin_dst->sin_family;
197244601Smm		dst->sin_len = sizeof(struct sockaddr_in);
198207753Smm		dst->sin_addr = sin_dst->sin_addr;
199207753Smm		if (sc->gif_ro.ro_rt) {
200207753Smm			RTFREE(sc->gif_ro.ro_rt);
201207753Smm			sc->gif_ro.ro_rt = NULL;
202207753Smm		}
203207753Smm#if 0
204207753Smm		GIF2IFP(sc)->if_mtu = GIF_MTU;
205207753Smm#endif
206207753Smm	}
207207753Smm
208207753Smm	if (sc->gif_ro.ro_rt == NULL) {
209207753Smm		rtalloc_ign(&sc->gif_ro, 0);
210207753Smm		if (sc->gif_ro.ro_rt == NULL) {
211207753Smm			m_freem(m);
212207753Smm			return ENETUNREACH;
213207753Smm		}
214207753Smm
215207753Smm		/* if it constitutes infinite encapsulation, punt. */
216207753Smm		if (sc->gif_ro.ro_rt->rt_ifp == ifp) {
217207753Smm			m_freem(m);
218207753Smm			return ENETUNREACH;	/* XXX */
219207753Smm		}
220207753Smm#if 0
221207753Smm		ifp->if_mtu = sc->gif_ro.ro_rt->rt_ifp->if_mtu
222207753Smm			- sizeof(struct ip);
223207753Smm#endif
224207753Smm	}
225207753Smm
226207753Smm	error = ip_output(m, NULL, &sc->gif_ro, 0, NULL, NULL);
227207753Smm
228207753Smm	if (!(GIF2IFP(sc)->if_flags & IFF_LINK0) &&
229207753Smm	    sc->gif_ro.ro_rt != NULL) {
230207753Smm		RTFREE(sc->gif_ro.ro_rt);
231207753Smm		sc->gif_ro.ro_rt = NULL;
232207753Smm	}
233207753Smm
234207753Smm	return (error);
235207753Smm}
236207753Smm
237207753Smmvoid
238207753Smmin_gif_input(struct mbuf *m, int off)
239207753Smm{
240207753Smm	struct ifnet *gifp = NULL;
241207753Smm	struct gif_softc *sc;
242207753Smm	struct ip *ip;
243207753Smm	int af;
244207753Smm	u_int8_t otos;
245207753Smm	int proto;
246207753Smm
247207753Smm	ip = mtod(m, struct ip *);
248207753Smm	proto = ip->ip_p;
249207753Smm
250292588Sdelphij	sc = (struct gif_softc *)encap_getarg(m);
251292588Sdelphij	if (sc == NULL) {
252292588Sdelphij		m_freem(m);
253292588Sdelphij		ipstat.ips_nogif++;
254207753Smm		return;
255207753Smm	}
256207753Smm
257207753Smm	gifp = GIF2IFP(sc);
258207753Smm	if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) {
259207753Smm		m_freem(m);
260207753Smm		ipstat.ips_nogif++;
261207753Smm		return;
262207753Smm	}
263207753Smm
264207753Smm	otos = ip->ip_tos;
265207753Smm	m_adj(m, off);
266207753Smm
267207753Smm	switch (proto) {
268207753Smm#ifdef INET
269207753Smm	case IPPROTO_IPV4:
270207753Smm	    {
271207753Smm		struct ip *ip;
272207753Smm		af = AF_INET;
273207753Smm		if (m->m_len < sizeof(*ip)) {
274207753Smm			m = m_pullup(m, sizeof(*ip));
275292588Sdelphij			if (!m)
276207753Smm				return;
277207753Smm		}
278207753Smm		ip = mtod(m, struct ip *);
279207753Smm		if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ?
280207753Smm				  ECN_ALLOWED : ECN_NOCARE,
281207753Smm				  &otos, &ip->ip_tos) == 0) {
282207753Smm			m_freem(m);
283207753Smm			return;
284207753Smm		}
285207753Smm		break;
286207753Smm	    }
287207753Smm#endif
288207753Smm#ifdef INET6
289207753Smm	case IPPROTO_IPV6:
290207753Smm	    {
291207753Smm		struct ip6_hdr *ip6;
292207753Smm		u_int8_t itos, oitos;
293207753Smm
294207753Smm		af = AF_INET6;
295207753Smm		if (m->m_len < sizeof(*ip6)) {
296207753Smm			m = m_pullup(m, sizeof(*ip6));
297207753Smm			if (!m)
298213700Smm				return;
299213700Smm		}
300207753Smm		ip6 = mtod(m, struct ip6_hdr *);
301207753Smm		itos = oitos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
302207753Smm		if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ?
303207753Smm				  ECN_ALLOWED : ECN_NOCARE,
304207753Smm				  &otos, &itos) == 0) {
305207753Smm			m_freem(m);
306207753Smm			return;
307207753Smm		}
308207753Smm		if (itos != oitos) {
309207753Smm			ip6->ip6_flow &= ~htonl(0xff << 20);
310207753Smm			ip6->ip6_flow |= htonl((u_int32_t)itos << 20);
311213700Smm		}
312213700Smm		break;
313207753Smm	    }
314207753Smm#endif /* INET6 */
315207753Smm 	case IPPROTO_ETHERIP:
316207753Smm 		af = AF_LINK;
317207753Smm 		break;
318207753Smm
319207753Smm	default:
320207753Smm		ipstat.ips_nogif++;
321207753Smm		m_freem(m);
322207753Smm		return;
323207753Smm	}
324207753Smm	gif_input(m, af, gifp);
325207753Smm	return;
326207753Smm}
327207753Smm
328207753Smm/*
329207753Smm * validate outer address.
330207753Smm */
331207753Smmstatic int
332207753Smmgif_validate4(const struct ip *ip, struct gif_softc *sc, struct ifnet *ifp)
333207753Smm{
334207753Smm	struct sockaddr_in *src, *dst;
335207753Smm	struct in_ifaddr *ia4;
336207753Smm
337207753Smm	src = (struct sockaddr_in *)sc->gif_psrc;
338207753Smm	dst = (struct sockaddr_in *)sc->gif_pdst;
339207753Smm
340207753Smm	/* check for address match */
341207753Smm	if (src->sin_addr.s_addr != ip->ip_dst.s_addr ||
342207753Smm	    dst->sin_addr.s_addr != ip->ip_src.s_addr)
343207753Smm		return 0;
344207753Smm
345207753Smm	/* martian filters on outer source - NOT done in ip_input! */
346207753Smm	if (IN_MULTICAST(ntohl(ip->ip_src.s_addr)))
347207753Smm		return 0;
348207753Smm	switch ((ntohl(ip->ip_src.s_addr) & 0xff000000) >> 24) {
349292588Sdelphij	case 0: case 127: case 255:
350207753Smm		return 0;
351207753Smm	}
352207753Smm	/* reject packets with broadcast on source */
353207753Smm	TAILQ_FOREACH(ia4, &in_ifaddrhead, ia_link) {
354207753Smm		if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0)
355207753Smm			continue;
356207753Smm		if (ip->ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr)
357207753Smm			return 0;
358207753Smm	}
359207753Smm
360207753Smm	/* ingress filters on outer source */
361207753Smm	if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0 && ifp) {
362292588Sdelphij		struct sockaddr_in sin;
363207753Smm		struct rtentry *rt;
364207753Smm
365207753Smm		bzero(&sin, sizeof(sin));
366207753Smm		sin.sin_family = AF_INET;
367207753Smm		sin.sin_len = sizeof(struct sockaddr_in);
368207753Smm		sin.sin_addr = ip->ip_src;
369207753Smm		rt = rtalloc1((struct sockaddr *)&sin, 0, 0UL);
370207753Smm		if (!rt || rt->rt_ifp != ifp) {
371207753Smm#if 0
372207753Smm			log(LOG_WARNING, "%s: packet from 0x%x dropped "
373207753Smm			    "due to ingress filter\n", if_name(GIF2IFP(sc)),
374207753Smm			    (u_int32_t)ntohl(sin.sin_addr.s_addr));
375207753Smm#endif
376213700Smm			if (rt)
377213700Smm				RTFREE_LOCKED(rt);
378207753Smm			return 0;
379207753Smm		}
380207753Smm		RTFREE_LOCKED(rt);
381207753Smm	}
382207753Smm
383207753Smm	return 32 * 2;
384292588Sdelphij}
385207753Smm
386207753Smm/*
387292588Sdelphij * we know that we are in IFF_UP, outer address available, and outer family
388207753Smm * matched the physical addr family.  see gif_encapcheck().
389207753Smm */
390207753Smmint
391207753Smmgif_encapcheck4(const struct mbuf *m, int off, int proto, void *arg)
392292588Sdelphij{
393292588Sdelphij	struct ip ip;
394207753Smm	struct gif_softc *sc;
395207753Smm	struct ifnet *ifp;
396207753Smm
397207753Smm	/* sanity check done in caller */
398207753Smm	sc = (struct gif_softc *)arg;
399207753Smm
400207753Smm	/* LINTED const cast */
401207753Smm	m_copydata(m, 0, sizeof(ip), (caddr_t)&ip);
402207753Smm	ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL;
403207753Smm
404207753Smm	return gif_validate4(&ip, sc, ifp);
405207753Smm}
406207753Smm
407207753Smmint
408207753Smmin_gif_attach(struct gif_softc *sc)
409207753Smm{
410207753Smm	sc->encap_cookie4 = encap_attach_func(AF_INET, -1, gif_encapcheck,
411207753Smm	    &in_gif_protosw, sc);
412207753Smm	if (sc->encap_cookie4 == NULL)
413207753Smm		return EEXIST;
414207753Smm	return 0;
415207753Smm}
416207753Smm
417207753Smmint
418207753Smmin_gif_detach(struct gif_softc *sc)
419207753Smm{
420207753Smm	int error;
421207753Smm
422207753Smm	error = encap_detach(sc->encap_cookie4);
423207753Smm	if (error == 0)
424207753Smm		sc->encap_cookie4 = NULL;
425207753Smm	return error;
426207753Smm}
427207753Smm