in6_gif.c revision 70f0bdf6818a73c858bc47a23afc1e9d7c56d716
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$
30 */
31
32/*
33 * in6_gif.c
34 */
35
36#include "opt_inet.h"
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/socket.h>
41#include <sys/sockio.h>
42#include <sys/mbuf.h>
43#include <sys/errno.h>
44#include <sys/protosw.h>
45
46#include <net/if.h>
47#include <net/route.h>
48
49#include <netinet/in.h>
50#include <netinet/in_systm.h>
51#ifdef INET
52#include <netinet/ip.h>
53#endif
54#include <netinet6/ip6.h>
55#include <netinet6/ip6_var.h>
56#include <netinet6/in6_gif.h>
57#include <netinet6/ip6.h>
58
59#include <net/if_gif.h>
60
61#include <net/net_osdep.h>
62
63int
64in6_gif_output(ifp, family, m, rt)
65	struct ifnet *ifp;
66	int family; /* family of the packet to be encapsulate. */
67	struct mbuf *m;
68	struct rtentry *rt;
69{
70	struct gif_softc *sc = (struct gif_softc*)ifp;
71	struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst;
72	struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc;
73	struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst;
74	struct ip6_hdr *ip6;
75	int proto;
76
77	if (sin6_src == NULL || sin6_dst == NULL ||
78	    sin6_src->sin6_family != AF_INET6 ||
79	    sin6_dst->sin6_family != AF_INET6) {
80		m_freem(m);
81		return EAFNOSUPPORT;
82	}
83
84	switch (family) {
85#ifdef INET
86	case AF_INET:
87	    {
88		struct ip *ip;
89
90		proto = IPPROTO_IPV4;
91		if (m->m_len < sizeof(*ip)) {
92			m = m_pullup(m, sizeof(*ip));
93			if (!m)
94				return ENOBUFS;
95		}
96		ip = mtod(m, struct ip *);
97		break;
98	    }
99#endif
100	case AF_INET6:
101	    {
102		struct ip6_hdr *ip6;
103		proto = IPPROTO_IPV6;
104		if (m->m_len < sizeof(*ip6)) {
105			m = m_pullup(m, sizeof(*ip6));
106			if (!m)
107				return ENOBUFS;
108		}
109		ip6 = mtod(m, struct ip6_hdr *);
110		break;
111	    }
112	default:
113#ifdef DIAGNOSTIC
114		printf("in6_gif_output: warning: unknown family %d passed\n",
115			family);
116#endif
117		m_freem(m);
118		return EAFNOSUPPORT;
119	}
120
121	/* prepend new IP header */
122	M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
123	if (m && m->m_len < sizeof(struct ip6_hdr))
124		m = m_pullup(m, sizeof(struct ip6_hdr));
125	if (m == NULL) {
126		printf("ENOBUFS in in6_gif_output %d\n", __LINE__);
127		return ENOBUFS;
128	}
129
130	ip6 = mtod(m, struct ip6_hdr *);
131	ip6->ip6_flow	= 0;
132	ip6->ip6_vfc	= IPV6_VERSION;
133	ip6->ip6_plen	= htons((u_short)m->m_pkthdr.len);
134	ip6->ip6_nxt	= proto;
135	ip6->ip6_hlim	= ip6_gif_hlim;
136	ip6->ip6_src	= sin6_src->sin6_addr;
137	if (ifp->if_flags & IFF_LINK0) {
138		/* multi-destination mode */
139		if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
140			ip6->ip6_dst = sin6_dst->sin6_addr;
141		else if (rt) {
142			ip6->ip6_dst = ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr;
143		} else {
144			m_freem(m);
145			return ENETUNREACH;
146		}
147	} else {
148		/* bidirectional configured tunnel mode */
149		if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
150			ip6->ip6_dst = sin6_dst->sin6_addr;
151		else  {
152			m_freem(m);
153			return ENETUNREACH;
154		}
155	}
156
157	if (dst->sin6_family != sin6_dst->sin6_family ||
158	     !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) {
159		/* cache route doesn't match */
160		bzero(dst, sizeof(*dst));
161		dst->sin6_family = sin6_dst->sin6_family;
162		dst->sin6_len = sizeof(struct sockaddr_in6);
163		dst->sin6_addr = sin6_dst->sin6_addr;
164		if (sc->gif_ro6.ro_rt) {
165			RTFREE(sc->gif_ro6.ro_rt);
166			sc->gif_ro6.ro_rt = NULL;
167		}
168	}
169
170	if (sc->gif_ro6.ro_rt == NULL) {
171		rtalloc((struct route *)&sc->gif_ro6);
172		if (sc->gif_ro6.ro_rt == NULL) {
173			m_freem(m);
174			return ENETUNREACH;
175		}
176	}
177
178#ifdef IPSEC
179	m->m_pkthdr.rcvif = NULL;
180#endif /*IPSEC*/
181	return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL));
182}
183
184int in6_gif_input(mp, offp, proto)
185	struct mbuf **mp;
186	int *offp, proto;
187{
188	struct mbuf *m = *mp;
189	struct gif_softc *sc;
190	struct ifnet *gifp = NULL;
191	struct ip6_hdr *ip6;
192	int i;
193	int af = 0;
194
195	ip6 = mtod(m, struct ip6_hdr *);
196
197#define	satoin6(sa)	(((struct sockaddr_in6 *)(sa))->sin6_addr)
198	for (i = 0, sc = gif; i < ngif; i++, sc++) {
199		if (sc->gif_psrc == NULL ||
200		    sc->gif_pdst == NULL ||
201		    sc->gif_psrc->sa_family != AF_INET6 ||
202		    sc->gif_pdst->sa_family != AF_INET6) {
203			continue;
204		}
205		if ((sc->gif_if.if_flags & IFF_UP) == 0)
206			continue;
207		if ((sc->gif_if.if_flags & IFF_LINK0) &&
208		    IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) &&
209		    IN6_IS_ADDR_UNSPECIFIED(&satoin6(sc->gif_pdst))) {
210			gifp = &sc->gif_if;
211			continue;
212		}
213		if (IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) &&
214		    IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_pdst), &ip6->ip6_src)) {
215			gifp = &sc->gif_if;
216			break;
217		}
218	}
219
220	if (gifp == NULL) {
221		m_freem(m);
222		ip6stat.ip6s_nogif++;
223		return IPPROTO_DONE;
224	}
225
226	m_adj(m, *offp);
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 IPPROTO_DONE;
238		}
239		ip = mtod(m, struct ip *);
240		break;
241	    }
242#endif /* INET */
243	case IPPROTO_IPV6:
244	    {
245		struct ip6_hdr *ip6;
246		af = AF_INET6;
247		if (m->m_len < sizeof(*ip6)) {
248			m = m_pullup(m, sizeof(*ip6));
249			if (!m)
250				return IPPROTO_DONE;
251		}
252		ip6 = mtod(m, struct ip6_hdr *);
253		break;
254	    }
255	default:
256		ip6stat.ip6s_nogif++;
257		m_freem(m);
258		return IPPROTO_DONE;
259	}
260
261	gif_input(m, af, gifp);
262	return IPPROTO_DONE;
263}
264