Commit | Line | Data |
---|---|---|
984263bc MD |
1 | /* $FreeBSD: src/sys/net/if_stf.c,v 1.1.2.11 2003/01/23 21:06:44 sam Exp $ */ |
2 | /* $KAME: if_stf.c,v 1.73 2001/12/03 11:08:30 keiichi Exp $ */ | |
3 | ||
4 | /* | |
5 | * Copyright (C) 2000 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 | /* | |
34 | * 6to4 interface, based on RFC3056. | |
35 | * | |
36 | * 6to4 interface is NOT capable of link-layer (I mean, IPv4) multicasting. | |
37 | * There is no address mapping defined from IPv6 multicast address to IPv4 | |
38 | * address. Therefore, we do not have IFF_MULTICAST on the interface. | |
39 | * | |
40 | * Due to the lack of address mapping for link-local addresses, we cannot | |
41 | * throw packets toward link-local addresses (fe80::x). Also, we cannot throw | |
42 | * packets to link-local multicast addresses (ff02::x). | |
43 | * | |
44 | * Here are interesting symptoms due to the lack of link-local address: | |
45 | * | |
46 | * Unicast routing exchange: | |
47 | * - RIPng: Impossible. Uses link-local multicast packet toward ff02::9, | |
48 | * and link-local addresses as nexthop. | |
49 | * - OSPFv6: Impossible. OSPFv6 assumes that there's link-local address | |
50 | * assigned to the link, and makes use of them. Also, HELLO packets use | |
51 | * link-local multicast addresses (ff02::5 and ff02::6). | |
52 | * - BGP4+: Maybe. You can only use global address as nexthop, and global | |
53 | * address as TCP endpoint address. | |
54 | * | |
55 | * Multicast routing protocols: | |
56 | * - PIM: Hello packet cannot be used to discover adjacent PIM routers. | |
57 | * Adjacent PIM routers must be configured manually (is it really spec-wise | |
58 | * correct thing to do?). | |
59 | * | |
60 | * ICMPv6: | |
61 | * - Redirects cannot be used due to the lack of link-local address. | |
62 | * | |
b272101a | 63 | * stf interface does not have, and will not need, a link-local address. |
984263bc MD |
64 | * It seems to have no real benefit and does not help the above symptoms much. |
65 | * Even if we assign link-locals to interface, we cannot really | |
66 | * use link-local unicast/multicast on top of 6to4 cloud (since there's no | |
67 | * encapsulation defined for link-local address), and the above analysis does | |
68 | * not change. RFC3056 does not mandate the assignment of link-local address | |
69 | * either. | |
70 | * | |
71 | * 6to4 interface has security issues. Refer to | |
72 | * http://playground.iijlab.net/i-d/draft-itojun-ipv6-transition-abuse-00.txt | |
73 | * for details. The code tries to filter out some of malicious packets. | |
74 | * Note that there is no way to be 100% secure. | |
75 | */ | |
76 | ||
77 | #include "opt_inet.h" | |
78 | #include "opt_inet6.h" | |
79 | ||
80 | #include <sys/param.h> | |
81 | #include <sys/systm.h> | |
82 | #include <sys/socket.h> | |
83 | #include <sys/sockio.h> | |
84 | #include <sys/mbuf.h> | |
85 | #include <sys/errno.h> | |
86 | #include <sys/protosw.h> | |
87 | #include <sys/kernel.h> | |
88 | #include <machine/cpu.h> | |
89 | ||
90 | #include <sys/malloc.h> | |
91 | ||
92 | #include <net/if.h> | |
93 | #include <net/route.h> | |
94 | #include <net/netisr.h> | |
95 | #include <net/if_types.h> | |
ef9870ec | 96 | #include <net/ifq_var.h> |
3b0b9567 | 97 | #include <net/netisr2.h> |
1f2de5d4 | 98 | #include "if_stf.h" |
984263bc MD |
99 | |
100 | #include <netinet/in.h> | |
101 | #include <netinet/in_systm.h> | |
102 | #include <netinet/ip.h> | |
984263bc MD |
103 | #include <netinet/ip_var.h> |
104 | #include <netinet/in_var.h> | |
105 | ||
106 | #include <netinet/ip6.h> | |
107 | #include <netinet6/ip6_var.h> | |
108 | #include <netinet6/in6_var.h> | |
f9be6a1c | 109 | #include <netinet6/nd6.h> |
984263bc MD |
110 | #include <netinet/ip_ecn.h> |
111 | ||
112 | #include <netinet/ip_encap.h> | |
113 | ||
114 | #include <machine/stdarg.h> | |
115 | ||
116 | #include <net/net_osdep.h> | |
117 | ||
118 | #include <net/bpf.h> | |
119 | ||
120 | #define IN6_IS_ADDR_6TO4(x) (ntohs((x)->s6_addr16[0]) == 0x2002) | |
121 | #define GET_V4(x) ((struct in_addr *)(&(x)->s6_addr16[1])) | |
122 | ||
123 | struct stf_softc { | |
124 | struct ifnet sc_if; /* common area */ | |
3b0b9567 | 125 | struct route *route_pcpu; |
984263bc MD |
126 | const struct encaptab *encap_cookie; |
127 | }; | |
128 | ||
129 | static struct stf_softc *stf; | |
130 | ||
131 | static MALLOC_DEFINE(M_STF, "stf", "6to4 Tunnel Interface"); | |
132 | static int ip_stf_ttl = 40; | |
133 | ||
134 | extern struct domain inetdomain; | |
4468b0b4 | 135 | struct protosw in_stf_protosw = |
002c1265 MD |
136 | { |
137 | .pr_type = SOCK_RAW, | |
138 | .pr_domain = &inetdomain, | |
139 | .pr_protocol = IPPROTO_IPV6, | |
140 | .pr_flags = PR_ATOMIC|PR_ADDR, | |
141 | ||
142 | .pr_input = in_stf_input, | |
143 | .pr_output = rip_output, | |
144 | .pr_ctlinput = NULL, | |
145 | .pr_ctloutput = rip_ctloutput, | |
146 | ||
147 | .pr_usrreqs = &rip_usrreqs | |
148 | }; | |
984263bc | 149 | |
158abb01 RG |
150 | static int stfmodevent (module_t, int, void *); |
151 | static int stf_encapcheck (const struct mbuf *, int, int, void *); | |
152 | static struct in6_ifaddr *stf_getsrcifa6 (struct ifnet *); | |
153 | static int stf_output (struct ifnet *, struct mbuf *, struct sockaddr *, | |
154 | struct rtentry *); | |
155 | static int stf_checkaddr4 (struct stf_softc *, struct in_addr *, | |
156 | struct ifnet *); | |
157 | static int stf_checkaddr6 (struct stf_softc *, struct in6_addr *, | |
158 | struct ifnet *); | |
3ffea39d | 159 | static void stf_rtrequest (int, struct rtentry *); |
bd4539cc | 160 | static int stf_ioctl (struct ifnet *, u_long, caddr_t, struct ucred *); |
984263bc MD |
161 | |
162 | static int | |
bf8c57c6 | 163 | stfmodevent(module_t mod, int type, void *data) |
984263bc MD |
164 | { |
165 | struct stf_softc *sc; | |
3b0b9567 | 166 | int err, cpu; |
984263bc MD |
167 | const struct encaptab *p; |
168 | ||
169 | switch (type) { | |
170 | case MOD_LOAD: | |
e7b4468c SW |
171 | stf = kmalloc(sizeof(struct stf_softc), M_STF, |
172 | M_WAITOK | M_ZERO); | |
984263bc MD |
173 | sc = stf; |
174 | ||
175 | bzero(sc, sizeof(*sc)); | |
cdb7d804 | 176 | if_initname(&(sc->sc_if), "stf", 0); |
984263bc MD |
177 | |
178 | p = encap_attach_func(AF_INET, IPPROTO_IPV6, stf_encapcheck, | |
f15db79e | 179 | (void *)&in_stf_protosw, sc); |
984263bc | 180 | if (p == NULL) { |
4b1cf444 | 181 | kprintf("%s: attach failed\n", if_name(&sc->sc_if)); |
984263bc MD |
182 | return (ENOMEM); |
183 | } | |
184 | sc->encap_cookie = p; | |
3b0b9567 SZ |
185 | sc->route_pcpu = kmalloc(netisr_ncpus * sizeof(struct route), |
186 | M_STF, M_WAITOK | M_ZERO); | |
984263bc MD |
187 | |
188 | sc->sc_if.if_mtu = IPV6_MMTU; | |
189 | sc->sc_if.if_flags = 0; | |
190 | sc->sc_if.if_ioctl = stf_ioctl; | |
191 | sc->sc_if.if_output = stf_output; | |
192 | sc->sc_if.if_type = IFT_STF; | |
193 | #if 0 | |
194 | /* turn off ingress filter */ | |
195 | sc->sc_if.if_flags |= IFF_LINK2; | |
196 | #endif | |
ef9870ec | 197 | ifq_set_maxlen(&sc->sc_if.if_snd, IFQ_MAXLEN); |
78195a76 | 198 | if_attach(&sc->sc_if, NULL); |
3bd69058 | 199 | bpfattach(&sc->sc_if, DLT_NULL, sizeof(uint32_t)); |
f9be6a1c AL |
200 | /* |
201 | * 6to4 interface is very special: no multicast, no linklocal. | |
202 | * RFC2529 specifies how to make linklocals, but there's no | |
203 | * use and it is rather harmful to have one. | |
204 | * | |
205 | * NOTE: ND_IFINFO() is only available after if_attach(). | |
206 | */ | |
207 | ND_IFINFO(&sc->sc_if)->flags &= ~ND6_IFF_AUTO_LINKLOCAL; | |
208 | ND_IFINFO(&sc->sc_if)->flags |= ND6_IFF_NO_DAD; | |
984263bc MD |
209 | break; |
210 | case MOD_UNLOAD: | |
211 | sc = stf; | |
212 | bpfdetach(&sc->sc_if); | |
213 | if_detach(&sc->sc_if); | |
214 | err = encap_detach(sc->encap_cookie); | |
215 | KASSERT(err == 0, ("Unexpected error detaching encap_cookie")); | |
3b0b9567 SZ |
216 | for (cpu = 0; cpu < netisr_ncpus; ++cpu) { |
217 | if (sc->route_pcpu[cpu].ro_rt != NULL) { | |
218 | rtfree_async(sc->route_pcpu[cpu].ro_rt); | |
219 | sc->route_pcpu[cpu].ro_rt = NULL; | |
220 | } | |
221 | } | |
222 | kfree(sc->route_pcpu, M_STF); | |
efda3bd0 | 223 | kfree(sc, M_STF); |
984263bc MD |
224 | break; |
225 | } | |
226 | ||
227 | return (0); | |
228 | } | |
229 | ||
230 | static moduledata_t stf_mod = { | |
231 | "if_stf", | |
232 | stfmodevent, | |
233 | 0 | |
234 | }; | |
235 | ||
236 | DECLARE_MODULE(if_stf, stf_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); | |
237 | ||
238 | static int | |
bf8c57c6 | 239 | stf_encapcheck(const struct mbuf *m, int off, int proto, void *arg) |
984263bc MD |
240 | { |
241 | struct ip ip; | |
242 | struct in6_ifaddr *ia6; | |
243 | struct stf_softc *sc; | |
244 | struct in_addr a, b; | |
245 | ||
246 | sc = (struct stf_softc *)arg; | |
247 | if (sc == NULL) | |
248 | return 0; | |
249 | ||
250 | if ((sc->sc_if.if_flags & IFF_UP) == 0) | |
251 | return 0; | |
252 | ||
253 | /* IFF_LINK0 means "no decapsulation" */ | |
254 | if ((sc->sc_if.if_flags & IFF_LINK0) != 0) | |
255 | return 0; | |
256 | ||
257 | if (proto != IPPROTO_IPV6) | |
258 | return 0; | |
259 | ||
05d02a38 | 260 | m_copydata(m, 0, sizeof(ip), &ip); |
984263bc MD |
261 | |
262 | if (ip.ip_v != 4) | |
263 | return 0; | |
264 | ||
265 | ia6 = stf_getsrcifa6(&sc->sc_if); | |
266 | if (ia6 == NULL) | |
267 | return 0; | |
268 | ||
269 | /* | |
270 | * check if IPv4 dst matches the IPv4 address derived from the | |
271 | * local 6to4 address. | |
272 | * success on: dst = 10.1.1.1, ia6->ia_addr = 2002:0a01:0101:... | |
273 | */ | |
274 | if (bcmp(GET_V4(&ia6->ia_addr.sin6_addr), &ip.ip_dst, | |
275 | sizeof(ip.ip_dst)) != 0) | |
276 | return 0; | |
277 | ||
278 | /* | |
279 | * check if IPv4 src matches the IPv4 address derived from the | |
280 | * local 6to4 address masked by prefixmask. | |
281 | * success on: src = 10.1.1.1, ia6->ia_addr = 2002:0a00:.../24 | |
282 | * fail on: src = 10.1.1.1, ia6->ia_addr = 2002:0b00:.../24 | |
283 | */ | |
284 | bzero(&a, sizeof(a)); | |
285 | a.s_addr = GET_V4(&ia6->ia_addr.sin6_addr)->s_addr; | |
286 | a.s_addr &= GET_V4(&ia6->ia_prefixmask.sin6_addr)->s_addr; | |
287 | b = ip.ip_src; | |
288 | b.s_addr &= GET_V4(&ia6->ia_prefixmask.sin6_addr)->s_addr; | |
289 | if (a.s_addr != b.s_addr) | |
290 | return 0; | |
291 | ||
292 | /* stf interface makes single side match only */ | |
293 | return 32; | |
294 | } | |
295 | ||
296 | static struct in6_ifaddr * | |
bf8c57c6 | 297 | stf_getsrcifa6(struct ifnet *ifp) |
984263bc | 298 | { |
b2632176 | 299 | struct ifaddr_container *ifac; |
984263bc MD |
300 | struct sockaddr_in6 *sin6; |
301 | struct in_addr in; | |
302 | ||
b2632176 SZ |
303 | TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) { |
304 | struct ifaddr *ia = ifac->ifa; | |
f8983475 | 305 | struct in_ifaddr_container *iac; |
b2632176 | 306 | |
984263bc MD |
307 | if (ia->ifa_addr == NULL) |
308 | continue; | |
309 | if (ia->ifa_addr->sa_family != AF_INET6) | |
310 | continue; | |
311 | sin6 = (struct sockaddr_in6 *)ia->ifa_addr; | |
312 | if (!IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) | |
313 | continue; | |
314 | ||
315 | bcopy(GET_V4(&sin6->sin6_addr), &in, sizeof(in)); | |
f8983475 SZ |
316 | LIST_FOREACH(iac, INADDR_HASH(in.s_addr), ia_hash) { |
317 | if (iac->ia->ia_addr.sin_addr.s_addr == in.s_addr) | |
984263bc | 318 | break; |
f8983475 SZ |
319 | } |
320 | if (iac == NULL) | |
984263bc MD |
321 | continue; |
322 | ||
323 | return (struct in6_ifaddr *)ia; | |
324 | } | |
325 | ||
326 | return NULL; | |
327 | } | |
328 | ||
329 | static int | |
9db4b353 SZ |
330 | stf_output_serialized(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, |
331 | struct rtentry *rt) | |
984263bc MD |
332 | { |
333 | struct stf_softc *sc; | |
334 | struct sockaddr_in6 *dst6; | |
335 | struct in_addr *in4; | |
336 | struct sockaddr_in *dst4; | |
337 | u_int8_t tos; | |
338 | struct ip *ip; | |
339 | struct ip6_hdr *ip6; | |
340 | struct in6_ifaddr *ia6; | |
3b0b9567 | 341 | struct route *ro; |
1f8e62c9 | 342 | static const uint32_t af = AF_INET6; |
984263bc | 343 | |
5204e13c | 344 | ASSERT_NETISR_NCPUS(mycpuid); |
3b0b9567 | 345 | |
984263bc MD |
346 | sc = (struct stf_softc*)ifp; |
347 | dst6 = (struct sockaddr_in6 *)dst; | |
348 | ||
349 | /* just in case */ | |
350 | if ((ifp->if_flags & IFF_UP) == 0) { | |
351 | m_freem(m); | |
352 | return ENETDOWN; | |
353 | } | |
354 | ||
355 | /* | |
356 | * If we don't have an ip4 address that match my inner ip6 address, | |
357 | * we shouldn't generate output. Without this check, we'll end up | |
358 | * using wrong IPv4 source. | |
359 | */ | |
360 | ia6 = stf_getsrcifa6(ifp); | |
361 | if (ia6 == NULL) { | |
362 | m_freem(m); | |
363 | return ENETDOWN; | |
364 | } | |
365 | ||
366 | if (m->m_len < sizeof(*ip6)) { | |
367 | m = m_pullup(m, sizeof(*ip6)); | |
368 | if (!m) | |
369 | return ENOBUFS; | |
370 | } | |
371 | ip6 = mtod(m, struct ip6_hdr *); | |
372 | tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; | |
373 | ||
374 | /* | |
375 | * Pickup the right outer dst addr from the list of candidates. | |
376 | * ip6_dst has priority as it may be able to give us shorter IPv4 hops. | |
377 | */ | |
378 | if (IN6_IS_ADDR_6TO4(&ip6->ip6_dst)) | |
379 | in4 = GET_V4(&ip6->ip6_dst); | |
380 | else if (IN6_IS_ADDR_6TO4(&dst6->sin6_addr)) | |
381 | in4 = GET_V4(&dst6->sin6_addr); | |
382 | else { | |
383 | m_freem(m); | |
384 | return ENETUNREACH; | |
385 | } | |
386 | ||
fda7d388 SZ |
387 | if (ifp->if_bpf) { |
388 | bpf_gettoken(); | |
389 | if (ifp->if_bpf) | |
390 | bpf_ptap(ifp->if_bpf, m, &af, sizeof(af)); | |
391 | bpf_reltoken(); | |
392 | } | |
984263bc | 393 | |
b5523eac | 394 | M_PREPEND(m, sizeof(struct ip), M_NOWAIT); |
984263bc MD |
395 | if (m && m->m_len < sizeof(struct ip)) |
396 | m = m_pullup(m, sizeof(struct ip)); | |
397 | if (m == NULL) | |
398 | return ENOBUFS; | |
399 | ip = mtod(m, struct ip *); | |
400 | ||
401 | bzero(ip, sizeof(*ip)); | |
402 | ||
403 | bcopy(GET_V4(&((struct sockaddr_in6 *)&ia6->ia_addr)->sin6_addr), | |
404 | &ip->ip_src, sizeof(ip->ip_src)); | |
405 | bcopy(in4, &ip->ip_dst, sizeof(ip->ip_dst)); | |
406 | ip->ip_p = IPPROTO_IPV6; | |
407 | ip->ip_ttl = ip_stf_ttl; | |
8a93af2a | 408 | ip->ip_len = htons(m->m_pkthdr.len); /* network order */ |
984263bc MD |
409 | if (ifp->if_flags & IFF_LINK1) |
410 | ip_ecn_ingress(ECN_ALLOWED, &ip->ip_tos, &tos); | |
411 | else | |
412 | ip_ecn_ingress(ECN_NOCARE, &ip->ip_tos, &tos); | |
413 | ||
3b0b9567 SZ |
414 | ro = &sc->route_pcpu[mycpuid]; |
415 | dst4 = (struct sockaddr_in *)&ro->ro_dst; | |
984263bc MD |
416 | if (dst4->sin_family != AF_INET || |
417 | bcmp(&dst4->sin_addr, &ip->ip_dst, sizeof(ip->ip_dst)) != 0) { | |
418 | /* cache route doesn't match */ | |
419 | dst4->sin_family = AF_INET; | |
420 | dst4->sin_len = sizeof(struct sockaddr_in); | |
421 | bcopy(&ip->ip_dst, &dst4->sin_addr, sizeof(dst4->sin_addr)); | |
3b0b9567 SZ |
422 | if (ro->ro_rt) { |
423 | RTFREE(ro->ro_rt); | |
424 | ro->ro_rt = NULL; | |
984263bc MD |
425 | } |
426 | } | |
3b0b9567 SZ |
427 | if (ro->ro_rt != NULL && (ro->ro_rt->rt_flags & RTF_UP) == 0) { |
428 | RTFREE(ro->ro_rt); | |
429 | ro->ro_rt = NULL; | |
430 | } | |
984263bc | 431 | |
3b0b9567 SZ |
432 | if (ro->ro_rt == NULL) { |
433 | rtalloc(ro); | |
434 | if (ro->ro_rt == NULL) { | |
984263bc MD |
435 | m_freem(m); |
436 | return ENETUNREACH; | |
437 | } | |
438 | } | |
439 | ||
3b0b9567 | 440 | return ip_output(m, NULL, ro, IP_DEBUGROUTE, NULL, NULL); |
984263bc MD |
441 | } |
442 | ||
9db4b353 SZ |
443 | static int |
444 | stf_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, | |
445 | struct rtentry *rt) | |
446 | { | |
bfefe4a6 | 447 | struct ifaltq_subque *ifsq = ifq_get_subq_default(&ifp->if_snd); |
9db4b353 SZ |
448 | int error; |
449 | ||
bfefe4a6 | 450 | ifsq_serialize_hw(ifsq); |
9db4b353 | 451 | error = stf_output_serialized(ifp, m, dst, rt); |
bfefe4a6 | 452 | ifsq_deserialize_hw(ifsq); |
9db4b353 SZ |
453 | |
454 | return error; | |
455 | } | |
456 | ||
bf8c57c6 SW |
457 | /* |
458 | * Parameters: | |
459 | * inifp: incoming interface | |
460 | */ | |
984263bc | 461 | static int |
bf8c57c6 | 462 | stf_checkaddr4(struct stf_softc *sc, struct in_addr *in, struct ifnet *inifp) |
984263bc | 463 | { |
1b562c24 | 464 | struct in_ifaddr_container *iac; |
984263bc MD |
465 | |
466 | /* | |
467 | * reject packets with the following address: | |
468 | * 224.0.0.0/4 0.0.0.0/8 127.0.0.0/8 255.0.0.0/8 | |
469 | */ | |
470 | if (IN_MULTICAST(ntohl(in->s_addr))) | |
471 | return -1; | |
472 | switch ((ntohl(in->s_addr) & 0xff000000) >> 24) { | |
473 | case 0: case 127: case 255: | |
474 | return -1; | |
475 | } | |
476 | ||
477 | /* | |
478 | * reject packets with broadcast | |
479 | */ | |
1b562c24 SZ |
480 | TAILQ_FOREACH(iac, &in_ifaddrheads[mycpuid], ia_link) { |
481 | struct in_ifaddr *ia4 = iac->ia; | |
482 | ||
984263bc MD |
483 | if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) |
484 | continue; | |
485 | if (in->s_addr == ia4->ia_broadaddr.sin_addr.s_addr) | |
486 | return -1; | |
487 | } | |
488 | ||
489 | /* | |
490 | * perform ingress filter | |
491 | */ | |
492 | if (sc && (sc->sc_if.if_flags & IFF_LINK2) == 0 && inifp) { | |
493 | struct sockaddr_in sin; | |
494 | struct rtentry *rt; | |
495 | ||
496 | bzero(&sin, sizeof(sin)); | |
497 | sin.sin_family = AF_INET; | |
498 | sin.sin_len = sizeof(struct sockaddr_in); | |
499 | sin.sin_addr = *in; | |
a0720452 | 500 | rt = rtpurelookup((struct sockaddr *)&sin); |
984263bc MD |
501 | if (!rt || rt->rt_ifp != inifp) { |
502 | #if 0 | |
503 | log(LOG_WARNING, "%s: packet from 0x%x dropped " | |
504 | "due to ingress filter\n", if_name(&sc->sc_if), | |
505 | (u_int32_t)ntohl(sin.sin_addr.s_addr)); | |
506 | #endif | |
507 | if (rt) | |
508 | rtfree(rt); | |
509 | return -1; | |
510 | } | |
511 | rtfree(rt); | |
512 | } | |
513 | ||
514 | return 0; | |
515 | } | |
516 | ||
bf8c57c6 SW |
517 | /* |
518 | * Parameters: | |
519 | * inifp: incoming interface | |
520 | */ | |
984263bc | 521 | static int |
bf8c57c6 | 522 | stf_checkaddr6(struct stf_softc *sc, struct in6_addr *in6, struct ifnet *inifp) |
984263bc MD |
523 | { |
524 | /* | |
525 | * check 6to4 addresses | |
526 | */ | |
527 | if (IN6_IS_ADDR_6TO4(in6)) | |
528 | return stf_checkaddr4(sc, GET_V4(in6), inifp); | |
529 | ||
530 | /* | |
531 | * reject anything that look suspicious. the test is implemented | |
532 | * in ip6_input too, but we check here as well to | |
533 | * (1) reject bad packets earlier, and | |
534 | * (2) to be safe against future ip6_input change. | |
535 | */ | |
536 | if (IN6_IS_ADDR_V4COMPAT(in6) || IN6_IS_ADDR_V4MAPPED(in6)) | |
537 | return -1; | |
538 | ||
539 | return 0; | |
540 | } | |
541 | ||
002c1265 MD |
542 | int |
543 | in_stf_input(struct mbuf **mp, int *offp, int proto) | |
984263bc | 544 | { |
002c1265 | 545 | struct mbuf *m; |
984263bc MD |
546 | struct stf_softc *sc; |
547 | struct ip *ip; | |
548 | struct ip6_hdr *ip6; | |
549 | u_int8_t otos, itos; | |
984263bc | 550 | struct ifnet *ifp; |
002c1265 | 551 | int off; |
1f8e62c9 | 552 | static const uint32_t af = AF_INET6; |
a00138cb | 553 | |
002c1265 MD |
554 | m = *mp; |
555 | off = *offp; | |
984263bc MD |
556 | |
557 | if (proto != IPPROTO_IPV6) { | |
558 | m_freem(m); | |
002c1265 | 559 | return(IPPROTO_DONE); |
984263bc MD |
560 | } |
561 | ||
562 | ip = mtod(m, struct ip *); | |
563 | ||
564 | sc = (struct stf_softc *)encap_getarg(m); | |
565 | ||
566 | if (sc == NULL || (sc->sc_if.if_flags & IFF_UP) == 0) { | |
567 | m_freem(m); | |
002c1265 | 568 | return(IPPROTO_DONE); |
984263bc MD |
569 | } |
570 | ||
571 | ifp = &sc->sc_if; | |
572 | ||
573 | /* | |
574 | * perform sanity check against outer src/dst. | |
575 | * for source, perform ingress filter as well. | |
576 | */ | |
577 | if (stf_checkaddr4(sc, &ip->ip_dst, NULL) < 0 || | |
578 | stf_checkaddr4(sc, &ip->ip_src, m->m_pkthdr.rcvif) < 0) { | |
579 | m_freem(m); | |
002c1265 | 580 | return(IPPROTO_DONE); |
984263bc MD |
581 | } |
582 | ||
583 | otos = ip->ip_tos; | |
584 | m_adj(m, off); | |
585 | ||
586 | if (m->m_len < sizeof(*ip6)) { | |
587 | m = m_pullup(m, sizeof(*ip6)); | |
588 | if (!m) | |
002c1265 | 589 | return(IPPROTO_DONE); |
984263bc MD |
590 | } |
591 | ip6 = mtod(m, struct ip6_hdr *); | |
592 | ||
593 | /* | |
594 | * perform sanity check against inner src/dst. | |
595 | * for source, perform ingress filter as well. | |
596 | */ | |
597 | if (stf_checkaddr6(sc, &ip6->ip6_dst, NULL) < 0 || | |
598 | stf_checkaddr6(sc, &ip6->ip6_src, m->m_pkthdr.rcvif) < 0) { | |
599 | m_freem(m); | |
002c1265 | 600 | return(IPPROTO_DONE); |
984263bc MD |
601 | } |
602 | ||
603 | itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; | |
604 | if ((ifp->if_flags & IFF_LINK1) != 0) | |
605 | ip_ecn_egress(ECN_ALLOWED, &otos, &itos); | |
606 | else | |
607 | ip_ecn_egress(ECN_NOCARE, &otos, &itos); | |
608 | ip6->ip6_flow &= ~htonl(0xff << 20); | |
609 | ip6->ip6_flow |= htonl((u_int32_t)itos << 20); | |
610 | ||
611 | m->m_pkthdr.rcvif = ifp; | |
1f8e62c9 | 612 | |
fda7d388 SZ |
613 | if (ifp->if_bpf) { |
614 | bpf_gettoken(); | |
615 | if (ifp->if_bpf) | |
616 | bpf_ptap(ifp->if_bpf, m, &af, sizeof(af)); | |
617 | bpf_reltoken(); | |
618 | } | |
984263bc MD |
619 | |
620 | /* | |
621 | * Put the packet to the network layer input queue according to the | |
622 | * specified address family. | |
623 | * See net/if_gif.c for possible issues with packet processing | |
624 | * reorder due to extra queueing. | |
625 | */ | |
d40991ef SZ |
626 | IFNET_STAT_INC(ifp, ipackets, 1); |
627 | IFNET_STAT_INC(ifp, ibytes, m->m_pkthdr.len); | |
769c79ae | 628 | m->m_flags &= ~M_HASH; |
c3c96e44 | 629 | netisr_queue(NETISR_IPV6, m); |
002c1265 | 630 | return(IPPROTO_DONE); |
984263bc MD |
631 | } |
632 | ||
633 | /* ARGSUSED */ | |
634 | static void | |
3ffea39d | 635 | stf_rtrequest(int cmd, struct rtentry *rt) |
984263bc MD |
636 | { |
637 | ||
638 | if (rt) | |
639 | rt->rt_rmx.rmx_mtu = IPV6_MMTU; | |
640 | } | |
641 | ||
642 | static int | |
bf8c57c6 | 643 | stf_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) |
984263bc MD |
644 | { |
645 | struct ifaddr *ifa; | |
646 | struct ifreq *ifr; | |
647 | struct sockaddr_in6 *sin6; | |
648 | int error; | |
649 | ||
650 | error = 0; | |
651 | switch (cmd) { | |
652 | case SIOCSIFADDR: | |
653 | ifa = (struct ifaddr *)data; | |
654 | if (ifa == NULL || ifa->ifa_addr->sa_family != AF_INET6) { | |
655 | error = EAFNOSUPPORT; | |
656 | break; | |
657 | } | |
658 | sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; | |
659 | if (IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) { | |
660 | ifa->ifa_rtrequest = stf_rtrequest; | |
661 | ifp->if_flags |= IFF_UP; | |
662 | } else | |
663 | error = EINVAL; | |
664 | break; | |
665 | ||
666 | case SIOCADDMULTI: | |
667 | case SIOCDELMULTI: | |
668 | ifr = (struct ifreq *)data; | |
669 | if (ifr && ifr->ifr_addr.sa_family == AF_INET6) | |
670 | ; | |
671 | else | |
672 | error = EAFNOSUPPORT; | |
673 | break; | |
674 | ||
675 | default: | |
676 | error = EINVAL; | |
677 | break; | |
678 | } | |
679 | ||
680 | return error; | |
681 | } |