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