1 | /* $NetBSD: if_gif.c,v 1.123 2016/09/15 06:59:32 knakahara Exp $ */ |
2 | /* $KAME: if_gif.c,v 1.76 2001/08/20 02:01:02 kjc Exp $ */ |
3 | |
4 | /* |
5 | * Copyright (C) 1995, 1996, 1997, and 1998 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 | #include <sys/cdefs.h> |
34 | __KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1.123 2016/09/15 06:59:32 knakahara Exp $" ); |
35 | |
36 | #ifdef _KERNEL_OPT |
37 | #include "opt_inet.h" |
38 | #include "opt_net_mpsafe.h" |
39 | #endif |
40 | |
41 | #include <sys/param.h> |
42 | #include <sys/systm.h> |
43 | #include <sys/kernel.h> |
44 | #include <sys/mbuf.h> |
45 | #include <sys/socket.h> |
46 | #include <sys/sockio.h> |
47 | #include <sys/errno.h> |
48 | #include <sys/ioctl.h> |
49 | #include <sys/time.h> |
50 | #include <sys/socketvar.h> |
51 | #include <sys/syslog.h> |
52 | #include <sys/proc.h> |
53 | #include <sys/cpu.h> |
54 | #include <sys/intr.h> |
55 | #include <sys/kmem.h> |
56 | #include <sys/sysctl.h> |
57 | #include <sys/xcall.h> |
58 | #include <sys/device.h> |
59 | #include <sys/module.h> |
60 | |
61 | #include <net/if.h> |
62 | #include <net/if_types.h> |
63 | #include <net/netisr.h> |
64 | #include <net/route.h> |
65 | #include <net/bpf.h> |
66 | |
67 | #include <netinet/in.h> |
68 | #include <netinet/in_systm.h> |
69 | #include <netinet/ip.h> |
70 | #ifdef INET |
71 | #include <netinet/in_var.h> |
72 | #endif /* INET */ |
73 | #include <netinet/in_gif.h> |
74 | |
75 | #ifdef INET6 |
76 | #ifndef INET |
77 | #include <netinet/in.h> |
78 | #endif |
79 | #include <netinet6/in6_var.h> |
80 | #include <netinet/ip6.h> |
81 | #include <netinet6/ip6_var.h> |
82 | #include <netinet6/in6_gif.h> |
83 | #endif /* INET6 */ |
84 | |
85 | #include <netinet/ip_encap.h> |
86 | #include <net/if_gif.h> |
87 | |
88 | #include <net/net_osdep.h> |
89 | |
90 | #include "ioconf.h" |
91 | |
92 | #ifdef NET_MPSAFE |
93 | #define GIF_MPSAFE 1 |
94 | #endif |
95 | |
96 | /* |
97 | * gif global variable definitions |
98 | */ |
99 | static LIST_HEAD(, gif_softc) gif_softc_list; |
100 | |
101 | static void gifattach0(struct gif_softc *); |
102 | static int gif_output(struct ifnet *, struct mbuf *, |
103 | const struct sockaddr *, const struct rtentry *); |
104 | static void gif_start(struct ifnet *); |
105 | static int gif_transmit(struct ifnet *, struct mbuf *); |
106 | static int gif_ioctl(struct ifnet *, u_long, void *); |
107 | static int gif_set_tunnel(struct ifnet *, struct sockaddr *, |
108 | struct sockaddr *); |
109 | static void gif_delete_tunnel(struct ifnet *); |
110 | |
111 | static int gif_clone_create(struct if_clone *, int); |
112 | static int gif_clone_destroy(struct ifnet *); |
113 | static int gif_check_nesting(struct ifnet *, struct mbuf *); |
114 | |
115 | static int gif_encap_attach(struct gif_softc *); |
116 | static int gif_encap_detach(struct gif_softc *); |
117 | static void gif_encap_pause(struct gif_softc *); |
118 | |
119 | static struct if_clone gif_cloner = |
120 | IF_CLONE_INITIALIZER("gif" , gif_clone_create, gif_clone_destroy); |
121 | |
122 | #ifndef MAX_GIF_NEST |
123 | /* |
124 | * This macro controls the upper limitation on nesting of gif tunnels. |
125 | * Since, setting a large value to this macro with a careless configuration |
126 | * may introduce system crash, we don't allow any nestings by default. |
127 | * If you need to configure nested gif tunnels, you can define this macro |
128 | * in your kernel configuration file. However, if you do so, please be |
129 | * careful to configure the tunnels so that it won't make a loop. |
130 | */ |
131 | #define MAX_GIF_NEST 1 |
132 | #endif |
133 | static int max_gif_nesting = MAX_GIF_NEST; |
134 | |
135 | static struct sysctllog *gif_sysctl; |
136 | |
137 | static void |
138 | gif_sysctl_setup(void) |
139 | { |
140 | gif_sysctl = NULL; |
141 | |
142 | #ifdef INET |
143 | /* |
144 | * Previously create "net.inet.ip" entry to avoid sysctl_createv error. |
145 | */ |
146 | sysctl_createv(NULL, 0, NULL, NULL, |
147 | CTLFLAG_PERMANENT, |
148 | CTLTYPE_NODE, "inet" , |
149 | SYSCTL_DESCR("PF_INET related settings" ), |
150 | NULL, 0, NULL, 0, |
151 | CTL_NET, PF_INET, CTL_EOL); |
152 | sysctl_createv(NULL, 0, NULL, NULL, |
153 | CTLFLAG_PERMANENT, |
154 | CTLTYPE_NODE, "ip" , |
155 | SYSCTL_DESCR("IPv4 related settings" ), |
156 | NULL, 0, NULL, 0, |
157 | CTL_NET, PF_INET, IPPROTO_IP, CTL_EOL); |
158 | |
159 | sysctl_createv(&gif_sysctl, 0, NULL, NULL, |
160 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
161 | CTLTYPE_INT, "gifttl" , |
162 | SYSCTL_DESCR("Default TTL for a gif tunnel datagram" ), |
163 | NULL, 0, &ip_gif_ttl, 0, |
164 | CTL_NET, PF_INET, IPPROTO_IP, |
165 | IPCTL_GIF_TTL, CTL_EOL); |
166 | #endif |
167 | #ifdef INET6 |
168 | /* |
169 | * Previously create "net.inet6.ip6" entry to avoid sysctl_createv error. |
170 | */ |
171 | sysctl_createv(NULL, 0, NULL, NULL, |
172 | CTLFLAG_PERMANENT, |
173 | CTLTYPE_NODE, "inet6" , |
174 | SYSCTL_DESCR("PF_INET6 related settings" ), |
175 | NULL, 0, NULL, 0, |
176 | CTL_NET, PF_INET6, CTL_EOL); |
177 | sysctl_createv(NULL, 0, NULL, NULL, |
178 | CTLFLAG_PERMANENT, |
179 | CTLTYPE_NODE, "ip6" , |
180 | SYSCTL_DESCR("IPv6 related settings" ), |
181 | NULL, 0, NULL, 0, |
182 | CTL_NET, PF_INET6, IPPROTO_IPV6, CTL_EOL); |
183 | |
184 | sysctl_createv(&gif_sysctl, 0, NULL, NULL, |
185 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
186 | CTLTYPE_INT, "gifhlim" , |
187 | SYSCTL_DESCR("Default hop limit for a gif tunnel datagram" ), |
188 | NULL, 0, &ip6_gif_hlim, 0, |
189 | CTL_NET, PF_INET6, IPPROTO_IPV6, |
190 | IPV6CTL_GIF_HLIM, CTL_EOL); |
191 | #endif |
192 | } |
193 | |
194 | /* ARGSUSED */ |
195 | void |
196 | gifattach(int count) |
197 | { |
198 | /* |
199 | * Nothing to do here, initialization is handled by the |
200 | * module initialization code in gifinit() below). |
201 | */ |
202 | } |
203 | |
204 | static void |
205 | gifinit(void) |
206 | { |
207 | |
208 | LIST_INIT(&gif_softc_list); |
209 | if_clone_attach(&gif_cloner); |
210 | |
211 | gif_sysctl_setup(); |
212 | } |
213 | |
214 | static int |
215 | gifdetach(void) |
216 | { |
217 | int error = 0; |
218 | |
219 | if (!LIST_EMPTY(&gif_softc_list)) |
220 | error = EBUSY; |
221 | |
222 | if (error == 0) { |
223 | if_clone_detach(&gif_cloner); |
224 | sysctl_teardown(&gif_sysctl); |
225 | } |
226 | |
227 | return error; |
228 | } |
229 | |
230 | static int |
231 | gif_clone_create(struct if_clone *ifc, int unit) |
232 | { |
233 | struct gif_softc *sc; |
234 | |
235 | sc = kmem_zalloc(sizeof(struct gif_softc), KM_SLEEP); |
236 | if (sc == NULL) |
237 | return ENOMEM; |
238 | |
239 | if_initname(&sc->gif_if, ifc->ifc_name, unit); |
240 | |
241 | gifattach0(sc); |
242 | |
243 | LIST_INSERT_HEAD(&gif_softc_list, sc, gif_list); |
244 | return (0); |
245 | } |
246 | |
247 | static void |
248 | gifattach0(struct gif_softc *sc) |
249 | { |
250 | |
251 | sc->encap_cookie4 = sc->encap_cookie6 = NULL; |
252 | |
253 | sc->gif_if.if_addrlen = 0; |
254 | sc->gif_if.if_mtu = GIF_MTU; |
255 | sc->gif_if.if_flags = IFF_POINTOPOINT | IFF_MULTICAST; |
256 | sc->gif_if.if_extflags = IFEF_NO_LINK_STATE_CHANGE; |
257 | #ifdef GIF_MPSAFE |
258 | sc->gif_if.if_extflags |= IFEF_OUTPUT_MPSAFE; |
259 | #endif |
260 | sc->gif_if.if_ioctl = gif_ioctl; |
261 | sc->gif_if.if_output = gif_output; |
262 | sc->gif_if.if_start = gif_start; |
263 | sc->gif_if.if_transmit = gif_transmit; |
264 | sc->gif_if.if_type = IFT_GIF; |
265 | sc->gif_if.if_dlt = DLT_NULL; |
266 | sc->gif_if.if_softc = sc; |
267 | IFQ_SET_READY(&sc->gif_if.if_snd); |
268 | if_initialize(&sc->gif_if); |
269 | if_register(&sc->gif_if); |
270 | if_alloc_sadl(&sc->gif_if); |
271 | bpf_attach(&sc->gif_if, DLT_NULL, sizeof(u_int)); |
272 | } |
273 | |
274 | static int |
275 | gif_clone_destroy(struct ifnet *ifp) |
276 | { |
277 | struct gif_softc *sc = (void *) ifp; |
278 | |
279 | LIST_REMOVE(sc, gif_list); |
280 | |
281 | gif_delete_tunnel(&sc->gif_if); |
282 | bpf_detach(ifp); |
283 | if_detach(ifp); |
284 | rtcache_free(&sc->gif_ro); |
285 | |
286 | kmem_free(sc, sizeof(struct gif_softc)); |
287 | |
288 | return (0); |
289 | } |
290 | |
291 | #ifdef GIF_ENCAPCHECK |
292 | int |
293 | gif_encapcheck(struct mbuf *m, int off, int proto, void *arg) |
294 | { |
295 | struct ip ip; |
296 | struct gif_softc *sc; |
297 | |
298 | sc = arg; |
299 | if (sc == NULL) |
300 | return 0; |
301 | |
302 | if ((sc->gif_if.if_flags & (IFF_UP|IFF_RUNNING)) |
303 | != (IFF_UP|IFF_RUNNING)) |
304 | return 0; |
305 | |
306 | /* no physical address */ |
307 | if (!sc->gif_psrc || !sc->gif_pdst) |
308 | return 0; |
309 | |
310 | switch (proto) { |
311 | #ifdef INET |
312 | case IPPROTO_IPV4: |
313 | break; |
314 | #endif |
315 | #ifdef INET6 |
316 | case IPPROTO_IPV6: |
317 | break; |
318 | #endif |
319 | default: |
320 | return 0; |
321 | } |
322 | |
323 | /* Bail on short packets */ |
324 | KASSERT(m->m_flags & M_PKTHDR); |
325 | if (m->m_pkthdr.len < sizeof(ip)) |
326 | return 0; |
327 | |
328 | m_copydata(m, 0, sizeof(ip), &ip); |
329 | |
330 | switch (ip.ip_v) { |
331 | #ifdef INET |
332 | case 4: |
333 | if (sc->gif_psrc->sa_family != AF_INET || |
334 | sc->gif_pdst->sa_family != AF_INET) |
335 | return 0; |
336 | return gif_encapcheck4(m, off, proto, arg); |
337 | #endif |
338 | #ifdef INET6 |
339 | case 6: |
340 | if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) |
341 | return 0; |
342 | if (sc->gif_psrc->sa_family != AF_INET6 || |
343 | sc->gif_pdst->sa_family != AF_INET6) |
344 | return 0; |
345 | return gif_encapcheck6(m, off, proto, arg); |
346 | #endif |
347 | default: |
348 | return 0; |
349 | } |
350 | } |
351 | #endif |
352 | |
353 | /* |
354 | * gif may cause infinite recursion calls when misconfigured. |
355 | * We'll prevent this by introducing upper limit. |
356 | */ |
357 | static int |
358 | gif_check_nesting(struct ifnet *ifp, struct mbuf *m) |
359 | { |
360 | struct m_tag *mtag; |
361 | int *count; |
362 | |
363 | mtag = m_tag_find(m, PACKET_TAG_TUNNEL_INFO, NULL); |
364 | if (mtag != NULL) { |
365 | count = (int *)(mtag + 1); |
366 | if (++(*count) > max_gif_nesting) { |
367 | log(LOG_NOTICE, |
368 | "%s: recursively called too many times(%d)\n" , |
369 | if_name(ifp), |
370 | *count); |
371 | return EIO; |
372 | } |
373 | } else { |
374 | mtag = m_tag_get(PACKET_TAG_TUNNEL_INFO, sizeof(*count), |
375 | M_NOWAIT); |
376 | if (mtag != NULL) { |
377 | m_tag_prepend(m, mtag); |
378 | count = (int *)(mtag + 1); |
379 | *count = 0; |
380 | } else { |
381 | log(LOG_DEBUG, |
382 | "%s: m_tag_get() failed, recursion calls are not prevented.\n" , |
383 | if_name(ifp)); |
384 | } |
385 | } |
386 | |
387 | return 0; |
388 | } |
389 | |
390 | static int |
391 | gif_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, |
392 | const struct rtentry *rt) |
393 | { |
394 | struct gif_softc *sc = ifp->if_softc; |
395 | int error = 0; |
396 | |
397 | IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family); |
398 | |
399 | if ((error = gif_check_nesting(ifp, m)) != 0) { |
400 | m_free(m); |
401 | goto end; |
402 | } |
403 | |
404 | m->m_flags &= ~(M_BCAST|M_MCAST); |
405 | if (((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) || |
406 | sc->gif_psrc == NULL || sc->gif_pdst == NULL) { |
407 | m_freem(m); |
408 | error = ENETDOWN; |
409 | goto end; |
410 | } |
411 | |
412 | /* XXX should we check if our outer source is legal? */ |
413 | |
414 | /* use DLT_NULL encapsulation here to pass inner af type */ |
415 | M_PREPEND(m, sizeof(int), M_DONTWAIT); |
416 | if (!m) { |
417 | error = ENOBUFS; |
418 | goto end; |
419 | } |
420 | *mtod(m, int *) = dst->sa_family; |
421 | |
422 | /* Clear checksum-offload flags. */ |
423 | m->m_pkthdr.csum_flags = 0; |
424 | m->m_pkthdr.csum_data = 0; |
425 | |
426 | error = if_transmit_lock(ifp, m); |
427 | end: |
428 | if (error) |
429 | ifp->if_oerrors++; |
430 | return error; |
431 | } |
432 | |
433 | static void |
434 | gif_start(struct ifnet *ifp) |
435 | { |
436 | struct gif_softc *sc; |
437 | struct mbuf *m; |
438 | int family; |
439 | int len; |
440 | #ifndef GIF_MPSAFE |
441 | int s; |
442 | #endif |
443 | int error; |
444 | |
445 | sc = ifp->if_softc; |
446 | |
447 | /* output processing */ |
448 | while (1) { |
449 | #ifndef GIF_MPSAFE |
450 | s = splnet(); |
451 | #endif |
452 | IFQ_DEQUEUE(&sc->gif_if.if_snd, m); |
453 | #ifndef GIF_MPSAFE |
454 | splx(s); |
455 | #endif |
456 | if (m == NULL) |
457 | break; |
458 | |
459 | /* grab and chop off inner af type */ |
460 | if (sizeof(int) > m->m_len) { |
461 | m = m_pullup(m, sizeof(int)); |
462 | if (!m) { |
463 | ifp->if_oerrors++; |
464 | continue; |
465 | } |
466 | } |
467 | family = *mtod(m, int *); |
468 | bpf_mtap(ifp, m); |
469 | m_adj(m, sizeof(int)); |
470 | |
471 | len = m->m_pkthdr.len; |
472 | |
473 | /* dispatch to output logic based on outer AF */ |
474 | switch (sc->gif_psrc->sa_family) { |
475 | #ifdef INET |
476 | case AF_INET: |
477 | /* XXX |
478 | * To add mutex_enter(softnet_lock) or |
479 | * KASSERT(mutex_owned(softnet_lock)) here, we shold |
480 | * coordinate softnet_lock between in6_if_up() and |
481 | * in6_purgeif(). |
482 | */ |
483 | error = in_gif_output(ifp, family, m); |
484 | break; |
485 | #endif |
486 | #ifdef INET6 |
487 | case AF_INET6: |
488 | /* XXX |
489 | * the same as in_gif_output() |
490 | */ |
491 | error = in6_gif_output(ifp, family, m); |
492 | break; |
493 | #endif |
494 | default: |
495 | m_freem(m); |
496 | error = ENETDOWN; |
497 | break; |
498 | } |
499 | |
500 | if (error) |
501 | ifp->if_oerrors++; |
502 | else { |
503 | ifp->if_opackets++; |
504 | ifp->if_obytes += len; |
505 | } |
506 | } |
507 | } |
508 | |
509 | static int |
510 | gif_transmit(struct ifnet *ifp, struct mbuf *m) |
511 | { |
512 | struct gif_softc *sc; |
513 | int family; |
514 | int len; |
515 | int error; |
516 | |
517 | sc = ifp->if_softc; |
518 | |
519 | /* output processing */ |
520 | if (m == NULL) |
521 | return EINVAL; |
522 | |
523 | /* grab and chop off inner af type */ |
524 | if (sizeof(int) > m->m_len) { |
525 | m = m_pullup(m, sizeof(int)); |
526 | if (!m) { |
527 | ifp->if_oerrors++; |
528 | return ENOBUFS; |
529 | } |
530 | } |
531 | family = *mtod(m, int *); |
532 | bpf_mtap(ifp, m); |
533 | m_adj(m, sizeof(int)); |
534 | |
535 | len = m->m_pkthdr.len; |
536 | |
537 | /* dispatch to output logic based on outer AF */ |
538 | switch (sc->gif_psrc->sa_family) { |
539 | #ifdef INET |
540 | case AF_INET: |
541 | /* XXX |
542 | * To add mutex_enter(softnet_lock) or |
543 | * KASSERT(mutex_owned(softnet_lock)) here, we shold |
544 | * coordinate softnet_lock between in6_if_up() and |
545 | * in6_purgeif(). |
546 | */ |
547 | error = in_gif_output(ifp, family, m); |
548 | break; |
549 | #endif |
550 | #ifdef INET6 |
551 | case AF_INET6: |
552 | /* XXX |
553 | * the same as in_gif_output() |
554 | */ |
555 | error = in6_gif_output(ifp, family, m); |
556 | break; |
557 | #endif |
558 | default: |
559 | m_freem(m); |
560 | error = ENETDOWN; |
561 | break; |
562 | } |
563 | |
564 | if (error) |
565 | ifp->if_oerrors++; |
566 | else { |
567 | ifp->if_opackets++; |
568 | ifp->if_obytes += len; |
569 | } |
570 | |
571 | return error; |
572 | } |
573 | |
574 | void |
575 | gif_input(struct mbuf *m, int af, struct ifnet *ifp) |
576 | { |
577 | pktqueue_t *pktq; |
578 | size_t pktlen; |
579 | #ifndef GIF_MPSAFE |
580 | int s; |
581 | #endif |
582 | |
583 | if (ifp == NULL) { |
584 | /* just in case */ |
585 | m_freem(m); |
586 | return; |
587 | } |
588 | |
589 | m_set_rcvif(m, ifp); |
590 | pktlen = m->m_pkthdr.len; |
591 | |
592 | bpf_mtap_af(ifp, af, m); |
593 | |
594 | /* |
595 | * Put the packet to the network layer input queue according to the |
596 | * specified address family. Note: we avoid direct call to the |
597 | * input function of the network layer in order to avoid recursion. |
598 | * This may be revisited in the future. |
599 | */ |
600 | switch (af) { |
601 | #ifdef INET |
602 | case AF_INET: |
603 | pktq = ip_pktq; |
604 | break; |
605 | #endif |
606 | #ifdef INET6 |
607 | case AF_INET6: |
608 | pktq = ip6_pktq; |
609 | break; |
610 | #endif |
611 | default: |
612 | m_freem(m); |
613 | return; |
614 | } |
615 | |
616 | #ifndef GIF_MPSAFE |
617 | s = splnet(); |
618 | #endif |
619 | if (__predict_true(pktq_enqueue(pktq, m, 0))) { |
620 | ifp->if_ibytes += pktlen; |
621 | ifp->if_ipackets++; |
622 | } else { |
623 | m_freem(m); |
624 | } |
625 | #ifndef GIF_MPSAFE |
626 | splx(s); |
627 | #endif |
628 | } |
629 | |
630 | /* XXX how should we handle IPv6 scope on SIOC[GS]IFPHYADDR? */ |
631 | static int |
632 | gif_ioctl(struct ifnet *ifp, u_long cmd, void *data) |
633 | { |
634 | struct gif_softc *sc = ifp->if_softc; |
635 | struct ifreq *ifr = (struct ifreq*)data; |
636 | struct ifaddr *ifa = (struct ifaddr*)data; |
637 | int error = 0, size; |
638 | struct sockaddr *dst, *src; |
639 | |
640 | switch (cmd) { |
641 | case SIOCINITIFADDR: |
642 | ifp->if_flags |= IFF_UP; |
643 | ifa->ifa_rtrequest = p2p_rtrequest; |
644 | break; |
645 | |
646 | case SIOCADDMULTI: |
647 | case SIOCDELMULTI: |
648 | switch (ifr->ifr_addr.sa_family) { |
649 | #ifdef INET |
650 | case AF_INET: /* IP supports Multicast */ |
651 | break; |
652 | #endif /* INET */ |
653 | #ifdef INET6 |
654 | case AF_INET6: /* IP6 supports Multicast */ |
655 | break; |
656 | #endif /* INET6 */ |
657 | default: /* Other protocols doesn't support Multicast */ |
658 | error = EAFNOSUPPORT; |
659 | break; |
660 | } |
661 | break; |
662 | |
663 | case SIOCSIFMTU: |
664 | if (ifr->ifr_mtu < GIF_MTU_MIN || ifr->ifr_mtu > GIF_MTU_MAX) |
665 | return EINVAL; |
666 | else if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET) |
667 | error = 0; |
668 | break; |
669 | |
670 | #ifdef INET |
671 | case SIOCSIFPHYADDR: |
672 | #endif |
673 | #ifdef INET6 |
674 | case SIOCSIFPHYADDR_IN6: |
675 | #endif /* INET6 */ |
676 | case SIOCSLIFPHYADDR: |
677 | switch (cmd) { |
678 | #ifdef INET |
679 | case SIOCSIFPHYADDR: |
680 | src = (struct sockaddr *) |
681 | &(((struct in_aliasreq *)data)->ifra_addr); |
682 | dst = (struct sockaddr *) |
683 | &(((struct in_aliasreq *)data)->ifra_dstaddr); |
684 | break; |
685 | #endif |
686 | #ifdef INET6 |
687 | case SIOCSIFPHYADDR_IN6: |
688 | src = (struct sockaddr *) |
689 | &(((struct in6_aliasreq *)data)->ifra_addr); |
690 | dst = (struct sockaddr *) |
691 | &(((struct in6_aliasreq *)data)->ifra_dstaddr); |
692 | break; |
693 | #endif |
694 | case SIOCSLIFPHYADDR: |
695 | src = (struct sockaddr *) |
696 | &(((struct if_laddrreq *)data)->addr); |
697 | dst = (struct sockaddr *) |
698 | &(((struct if_laddrreq *)data)->dstaddr); |
699 | break; |
700 | default: |
701 | return EINVAL; |
702 | } |
703 | |
704 | /* sa_family must be equal */ |
705 | if (src->sa_family != dst->sa_family) |
706 | return EINVAL; |
707 | |
708 | /* validate sa_len */ |
709 | switch (src->sa_family) { |
710 | #ifdef INET |
711 | case AF_INET: |
712 | if (src->sa_len != sizeof(struct sockaddr_in)) |
713 | return EINVAL; |
714 | break; |
715 | #endif |
716 | #ifdef INET6 |
717 | case AF_INET6: |
718 | if (src->sa_len != sizeof(struct sockaddr_in6)) |
719 | return EINVAL; |
720 | break; |
721 | #endif |
722 | default: |
723 | return EAFNOSUPPORT; |
724 | } |
725 | switch (dst->sa_family) { |
726 | #ifdef INET |
727 | case AF_INET: |
728 | if (dst->sa_len != sizeof(struct sockaddr_in)) |
729 | return EINVAL; |
730 | break; |
731 | #endif |
732 | #ifdef INET6 |
733 | case AF_INET6: |
734 | if (dst->sa_len != sizeof(struct sockaddr_in6)) |
735 | return EINVAL; |
736 | break; |
737 | #endif |
738 | default: |
739 | return EAFNOSUPPORT; |
740 | } |
741 | |
742 | /* check sa_family looks sane for the cmd */ |
743 | switch (cmd) { |
744 | case SIOCSIFPHYADDR: |
745 | if (src->sa_family == AF_INET) |
746 | break; |
747 | return EAFNOSUPPORT; |
748 | #ifdef INET6 |
749 | case SIOCSIFPHYADDR_IN6: |
750 | if (src->sa_family == AF_INET6) |
751 | break; |
752 | return EAFNOSUPPORT; |
753 | #endif /* INET6 */ |
754 | case SIOCSLIFPHYADDR: |
755 | /* checks done in the above */ |
756 | break; |
757 | } |
758 | |
759 | error = gif_set_tunnel(&sc->gif_if, src, dst); |
760 | break; |
761 | |
762 | #ifdef SIOCDIFPHYADDR |
763 | case SIOCDIFPHYADDR: |
764 | gif_delete_tunnel(&sc->gif_if); |
765 | break; |
766 | #endif |
767 | |
768 | case SIOCGIFPSRCADDR: |
769 | #ifdef INET6 |
770 | case SIOCGIFPSRCADDR_IN6: |
771 | #endif /* INET6 */ |
772 | if (sc->gif_psrc == NULL) { |
773 | error = EADDRNOTAVAIL; |
774 | goto bad; |
775 | } |
776 | src = sc->gif_psrc; |
777 | switch (cmd) { |
778 | #ifdef INET |
779 | case SIOCGIFPSRCADDR: |
780 | dst = &ifr->ifr_addr; |
781 | size = sizeof(ifr->ifr_addr); |
782 | break; |
783 | #endif /* INET */ |
784 | #ifdef INET6 |
785 | case SIOCGIFPSRCADDR_IN6: |
786 | dst = (struct sockaddr *) |
787 | &(((struct in6_ifreq *)data)->ifr_addr); |
788 | size = sizeof(((struct in6_ifreq *)data)->ifr_addr); |
789 | break; |
790 | #endif /* INET6 */ |
791 | default: |
792 | error = EADDRNOTAVAIL; |
793 | goto bad; |
794 | } |
795 | if (src->sa_len > size) |
796 | return EINVAL; |
797 | memcpy(dst, src, src->sa_len); |
798 | break; |
799 | |
800 | case SIOCGIFPDSTADDR: |
801 | #ifdef INET6 |
802 | case SIOCGIFPDSTADDR_IN6: |
803 | #endif /* INET6 */ |
804 | if (sc->gif_pdst == NULL) { |
805 | error = EADDRNOTAVAIL; |
806 | goto bad; |
807 | } |
808 | src = sc->gif_pdst; |
809 | switch (cmd) { |
810 | #ifdef INET |
811 | case SIOCGIFPDSTADDR: |
812 | dst = &ifr->ifr_addr; |
813 | size = sizeof(ifr->ifr_addr); |
814 | break; |
815 | #endif /* INET */ |
816 | #ifdef INET6 |
817 | case SIOCGIFPDSTADDR_IN6: |
818 | dst = (struct sockaddr *) |
819 | &(((struct in6_ifreq *)data)->ifr_addr); |
820 | size = sizeof(((struct in6_ifreq *)data)->ifr_addr); |
821 | break; |
822 | #endif /* INET6 */ |
823 | default: |
824 | error = EADDRNOTAVAIL; |
825 | goto bad; |
826 | } |
827 | if (src->sa_len > size) |
828 | return EINVAL; |
829 | memcpy(dst, src, src->sa_len); |
830 | break; |
831 | |
832 | case SIOCGLIFPHYADDR: |
833 | if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) { |
834 | error = EADDRNOTAVAIL; |
835 | goto bad; |
836 | } |
837 | |
838 | /* copy src */ |
839 | src = sc->gif_psrc; |
840 | dst = (struct sockaddr *) |
841 | &(((struct if_laddrreq *)data)->addr); |
842 | size = sizeof(((struct if_laddrreq *)data)->addr); |
843 | if (src->sa_len > size) |
844 | return EINVAL; |
845 | memcpy(dst, src, src->sa_len); |
846 | |
847 | /* copy dst */ |
848 | src = sc->gif_pdst; |
849 | dst = (struct sockaddr *) |
850 | &(((struct if_laddrreq *)data)->dstaddr); |
851 | size = sizeof(((struct if_laddrreq *)data)->dstaddr); |
852 | if (src->sa_len > size) |
853 | return EINVAL; |
854 | memcpy(dst, src, src->sa_len); |
855 | break; |
856 | |
857 | default: |
858 | return ifioctl_common(ifp, cmd, data); |
859 | } |
860 | bad: |
861 | return error; |
862 | } |
863 | |
864 | static int |
865 | gif_encap_attach(struct gif_softc *sc) |
866 | { |
867 | int error; |
868 | |
869 | if (sc == NULL || sc->gif_psrc == NULL) |
870 | return EINVAL; |
871 | |
872 | switch (sc->gif_psrc->sa_family) { |
873 | #ifdef INET |
874 | case AF_INET: |
875 | error = in_gif_attach(sc); |
876 | break; |
877 | #endif |
878 | #ifdef INET6 |
879 | case AF_INET6: |
880 | error = in6_gif_attach(sc); |
881 | break; |
882 | #endif |
883 | default: |
884 | error = EINVAL; |
885 | break; |
886 | } |
887 | |
888 | return error; |
889 | } |
890 | |
891 | static int |
892 | gif_encap_detach(struct gif_softc *sc) |
893 | { |
894 | int error; |
895 | |
896 | if (sc == NULL || sc->gif_psrc == NULL) |
897 | return EINVAL; |
898 | |
899 | switch (sc->gif_psrc->sa_family) { |
900 | #ifdef INET |
901 | case AF_INET: |
902 | error = in_gif_detach(sc); |
903 | break; |
904 | #endif |
905 | #ifdef INET6 |
906 | case AF_INET6: |
907 | error = in6_gif_detach(sc); |
908 | break; |
909 | #endif |
910 | default: |
911 | error = EINVAL; |
912 | break; |
913 | } |
914 | |
915 | return error; |
916 | } |
917 | |
918 | static void |
919 | gif_encap_pause(struct gif_softc *sc) |
920 | { |
921 | struct ifnet *ifp; |
922 | uint64_t where; |
923 | |
924 | if (sc == NULL || sc->gif_psrc == NULL) |
925 | return; |
926 | |
927 | ifp = &sc->gif_if; |
928 | if ((ifp->if_flags & IFF_RUNNING) == 0) |
929 | return; |
930 | |
931 | switch (sc->gif_psrc->sa_family) { |
932 | #ifdef INET |
933 | case AF_INET: |
934 | (void)in_gif_pause(sc); |
935 | break; |
936 | #endif |
937 | #ifdef INET6 |
938 | case AF_INET6: |
939 | (void)in6_gif_pause(sc); |
940 | break; |
941 | #endif |
942 | } |
943 | |
944 | ifp->if_flags &= ~IFF_RUNNING; |
945 | /* membar_sync() is done in xc_broadcast(). */ |
946 | |
947 | /* |
948 | * Wait for softint_execute()(ipintr() or ip6intr()) |
949 | * completion done by other CPUs which already run over if_flags |
950 | * check in in_gif_input() or in6_gif_input(). |
951 | * Furthermore, wait for gif_output() completion too. |
952 | */ |
953 | where = xc_broadcast(0, (xcfunc_t)nullop, NULL, NULL); |
954 | xc_wait(where); |
955 | } |
956 | |
957 | static int |
958 | gif_set_tunnel(struct ifnet *ifp, struct sockaddr *src, struct sockaddr *dst) |
959 | { |
960 | struct gif_softc *sc = ifp->if_softc; |
961 | struct gif_softc *sc2; |
962 | struct sockaddr *osrc, *odst; |
963 | struct sockaddr *nsrc, *ndst; |
964 | int error; |
965 | #ifndef GIF_MPSAFE |
966 | int s; |
967 | |
968 | s = splsoftnet(); |
969 | #endif |
970 | error = encap_lock_enter(); |
971 | if (error) { |
972 | #ifndef GIF_MPSAFE |
973 | splx(s); |
974 | #endif |
975 | return error; |
976 | } |
977 | |
978 | LIST_FOREACH(sc2, &gif_softc_list, gif_list) { |
979 | if (sc2 == sc) |
980 | continue; |
981 | if (!sc2->gif_pdst || !sc2->gif_psrc) |
982 | continue; |
983 | /* can't configure same pair of address onto two gifs */ |
984 | if (sockaddr_cmp(sc2->gif_pdst, dst) == 0 && |
985 | sockaddr_cmp(sc2->gif_psrc, src) == 0) { |
986 | /* continue to use the old configureation. */ |
987 | error = EADDRNOTAVAIL; |
988 | goto out; |
989 | } |
990 | |
991 | /* XXX both end must be valid? (I mean, not 0.0.0.0) */ |
992 | } |
993 | |
994 | nsrc = sockaddr_dup(src, M_WAITOK); |
995 | ndst = sockaddr_dup(dst, M_WAITOK); |
996 | |
997 | gif_encap_pause(sc); |
998 | |
999 | /* Firstly, clear old configurations. */ |
1000 | /* XXX we can detach from both, but be polite just in case */ |
1001 | if (sc->gif_psrc) |
1002 | (void)gif_encap_detach(sc); |
1003 | |
1004 | /* |
1005 | * Secondly, try to set new configurations. |
1006 | * If the setup failed, rollback to old configurations. |
1007 | */ |
1008 | do { |
1009 | osrc = sc->gif_psrc; |
1010 | odst = sc->gif_pdst; |
1011 | sc->gif_psrc = nsrc; |
1012 | sc->gif_pdst = ndst; |
1013 | |
1014 | error = gif_encap_attach(sc); |
1015 | if (error) { |
1016 | /* rollback to the last configuration. */ |
1017 | nsrc = osrc; |
1018 | ndst = odst; |
1019 | osrc = sc->gif_psrc; |
1020 | odst = sc->gif_pdst; |
1021 | |
1022 | continue; |
1023 | } |
1024 | } while (error != 0 && (nsrc != NULL && ndst != NULL)); |
1025 | /* Thirdly, even rollback failed, clear configurations. */ |
1026 | if (error) { |
1027 | osrc = sc->gif_psrc; |
1028 | odst = sc->gif_pdst; |
1029 | sc->gif_psrc = NULL; |
1030 | sc->gif_pdst = NULL; |
1031 | } |
1032 | |
1033 | if (osrc) |
1034 | sockaddr_free(osrc); |
1035 | if (odst) |
1036 | sockaddr_free(odst); |
1037 | |
1038 | if (sc->gif_psrc && sc->gif_pdst) |
1039 | ifp->if_flags |= IFF_RUNNING; |
1040 | else |
1041 | ifp->if_flags &= ~IFF_RUNNING; |
1042 | |
1043 | out: |
1044 | encap_lock_exit(); |
1045 | #ifndef GIF_MPSAFE |
1046 | splx(s); |
1047 | #endif |
1048 | return error; |
1049 | } |
1050 | |
1051 | static void |
1052 | gif_delete_tunnel(struct ifnet *ifp) |
1053 | { |
1054 | struct gif_softc *sc = ifp->if_softc; |
1055 | int error; |
1056 | #ifndef GIF_MPSAFE |
1057 | int s; |
1058 | |
1059 | s = splsoftnet(); |
1060 | #endif |
1061 | error = encap_lock_enter(); |
1062 | if (error) { |
1063 | #ifndef GIF_MPSAFE |
1064 | splx(s); |
1065 | #endif |
1066 | return; |
1067 | } |
1068 | |
1069 | gif_encap_pause(sc); |
1070 | if (sc->gif_psrc) { |
1071 | sockaddr_free(sc->gif_psrc); |
1072 | sc->gif_psrc = NULL; |
1073 | } |
1074 | if (sc->gif_pdst) { |
1075 | sockaddr_free(sc->gif_pdst); |
1076 | sc->gif_pdst = NULL; |
1077 | } |
1078 | /* it is safe to detach from both */ |
1079 | #ifdef INET |
1080 | (void)in_gif_detach(sc); |
1081 | #endif |
1082 | #ifdef INET6 |
1083 | (void)in6_gif_detach(sc); |
1084 | #endif |
1085 | |
1086 | if (sc->gif_psrc && sc->gif_pdst) |
1087 | ifp->if_flags |= IFF_RUNNING; |
1088 | else |
1089 | ifp->if_flags &= ~IFF_RUNNING; |
1090 | |
1091 | encap_lock_exit(); |
1092 | #ifndef GIF_MPSAFE |
1093 | splx(s); |
1094 | #endif |
1095 | } |
1096 | |
1097 | /* |
1098 | * Module infrastructure |
1099 | */ |
1100 | #include "if_module.h" |
1101 | |
1102 | IF_MODULE(MODULE_CLASS_DRIVER, gif, "" ) |
1103 | |