1 | #include <machine/rtems-bsd-config.h> |
---|
2 | |
---|
3 | /* $NetBSD: if_gre.c,v 1.49 2003/12/11 00:22:29 itojun Exp $ */ |
---|
4 | /* $FreeBSD$ */ |
---|
5 | |
---|
6 | /*- |
---|
7 | * Copyright (c) 1998 The NetBSD Foundation, Inc. |
---|
8 | * All rights reserved. |
---|
9 | * |
---|
10 | * This code is derived from software contributed to The NetBSD Foundation |
---|
11 | * by Heiko W.Rupp <hwr@pilhuhn.de> |
---|
12 | * |
---|
13 | * IPv6-over-GRE contributed by Gert Doering <gert@greenie.muc.de> |
---|
14 | * |
---|
15 | * Redistribution and use in source and binary forms, with or without |
---|
16 | * modification, are permitted provided that the following conditions |
---|
17 | * are met: |
---|
18 | * 1. Redistributions of source code must retain the above copyright |
---|
19 | * notice, this list of conditions and the following disclaimer. |
---|
20 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
21 | * notice, this list of conditions and the following disclaimer in the |
---|
22 | * documentation and/or other materials provided with the distribution. |
---|
23 | * 3. All advertising materials mentioning features or use of this software |
---|
24 | * must display the following acknowledgement: |
---|
25 | * This product includes software developed by the NetBSD |
---|
26 | * Foundation, Inc. and its contributors. |
---|
27 | * 4. Neither the name of The NetBSD Foundation nor the names of its |
---|
28 | * contributors may be used to endorse or promote products derived |
---|
29 | * from this software without specific prior written permission. |
---|
30 | * |
---|
31 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
---|
32 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
---|
33 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
---|
34 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
---|
35 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
---|
36 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
---|
37 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
---|
38 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
---|
39 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
---|
40 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
---|
41 | * POSSIBILITY OF SUCH DAMAGE. |
---|
42 | */ |
---|
43 | |
---|
44 | /* |
---|
45 | * Encapsulate L3 protocols into IP |
---|
46 | * See RFC 2784 (successor of RFC 1701 and 1702) for more details. |
---|
47 | * If_gre is compatible with Cisco GRE tunnels, so you can |
---|
48 | * have a NetBSD box as the other end of a tunnel interface of a Cisco |
---|
49 | * router. See gre(4) for more details. |
---|
50 | * Also supported: IP in IP encaps (proto 55) as of RFC 2004 |
---|
51 | */ |
---|
52 | |
---|
53 | #include <rtems/bsd/local/opt_atalk.h> |
---|
54 | #include <rtems/bsd/local/opt_inet.h> |
---|
55 | #include <rtems/bsd/local/opt_inet6.h> |
---|
56 | |
---|
57 | #include <rtems/bsd/sys/param.h> |
---|
58 | #include <sys/kernel.h> |
---|
59 | #include <sys/malloc.h> |
---|
60 | #include <sys/module.h> |
---|
61 | #include <sys/mbuf.h> |
---|
62 | #include <sys/priv.h> |
---|
63 | #include <sys/proc.h> |
---|
64 | #include <sys/protosw.h> |
---|
65 | #include <sys/socket.h> |
---|
66 | #include <sys/sockio.h> |
---|
67 | #include <sys/sysctl.h> |
---|
68 | #include <sys/systm.h> |
---|
69 | |
---|
70 | #include <net/ethernet.h> |
---|
71 | #include <net/if.h> |
---|
72 | #include <net/if_clone.h> |
---|
73 | #include <net/if_types.h> |
---|
74 | #include <net/route.h> |
---|
75 | #include <net/vnet.h> |
---|
76 | |
---|
77 | #ifdef INET |
---|
78 | #include <netinet/in.h> |
---|
79 | #include <netinet/in_systm.h> |
---|
80 | #include <netinet/in_var.h> |
---|
81 | #include <netinet/ip.h> |
---|
82 | #include <netinet/ip_gre.h> |
---|
83 | #include <netinet/ip_var.h> |
---|
84 | #include <netinet/ip_encap.h> |
---|
85 | #else |
---|
86 | #error "Huh? if_gre without inet?" |
---|
87 | #endif |
---|
88 | |
---|
89 | #include <net/bpf.h> |
---|
90 | |
---|
91 | #include <net/if_gre.h> |
---|
92 | |
---|
93 | /* |
---|
94 | * It is not easy to calculate the right value for a GRE MTU. |
---|
95 | * We leave this task to the admin and use the same default that |
---|
96 | * other vendors use. |
---|
97 | */ |
---|
98 | #define GREMTU 1476 |
---|
99 | |
---|
100 | #define GRENAME "gre" |
---|
101 | |
---|
102 | /* |
---|
103 | * gre_mtx protects all global variables in if_gre.c. |
---|
104 | * XXX: gre_softc data not protected yet. |
---|
105 | */ |
---|
106 | struct mtx gre_mtx; |
---|
107 | static MALLOC_DEFINE(M_GRE, GRENAME, "Generic Routing Encapsulation"); |
---|
108 | |
---|
109 | struct gre_softc_head gre_softc_list; |
---|
110 | |
---|
111 | static int gre_clone_create(struct if_clone *, int, caddr_t); |
---|
112 | static void gre_clone_destroy(struct ifnet *); |
---|
113 | static int gre_ioctl(struct ifnet *, u_long, caddr_t); |
---|
114 | static int gre_output(struct ifnet *, struct mbuf *, struct sockaddr *, |
---|
115 | struct route *ro); |
---|
116 | |
---|
117 | IFC_SIMPLE_DECLARE(gre, 0); |
---|
118 | |
---|
119 | static int gre_compute_route(struct gre_softc *sc); |
---|
120 | |
---|
121 | static void greattach(void); |
---|
122 | |
---|
123 | #ifdef INET |
---|
124 | extern struct domain inetdomain; |
---|
125 | static const struct protosw in_gre_protosw = { |
---|
126 | .pr_type = SOCK_RAW, |
---|
127 | .pr_domain = &inetdomain, |
---|
128 | .pr_protocol = IPPROTO_GRE, |
---|
129 | .pr_flags = PR_ATOMIC|PR_ADDR, |
---|
130 | .pr_input = gre_input, |
---|
131 | .pr_output = (pr_output_t *)rip_output, |
---|
132 | .pr_ctlinput = rip_ctlinput, |
---|
133 | .pr_ctloutput = rip_ctloutput, |
---|
134 | .pr_usrreqs = &rip_usrreqs |
---|
135 | }; |
---|
136 | static const struct protosw in_mobile_protosw = { |
---|
137 | .pr_type = SOCK_RAW, |
---|
138 | .pr_domain = &inetdomain, |
---|
139 | .pr_protocol = IPPROTO_MOBILE, |
---|
140 | .pr_flags = PR_ATOMIC|PR_ADDR, |
---|
141 | .pr_input = gre_mobile_input, |
---|
142 | .pr_output = (pr_output_t *)rip_output, |
---|
143 | .pr_ctlinput = rip_ctlinput, |
---|
144 | .pr_ctloutput = rip_ctloutput, |
---|
145 | .pr_usrreqs = &rip_usrreqs |
---|
146 | }; |
---|
147 | #endif |
---|
148 | |
---|
149 | SYSCTL_DECL(_net_link); |
---|
150 | SYSCTL_NODE(_net_link, IFT_TUNNEL, gre, CTLFLAG_RW, 0, |
---|
151 | "Generic Routing Encapsulation"); |
---|
152 | #ifndef MAX_GRE_NEST |
---|
153 | /* |
---|
154 | * This macro controls the default upper limitation on nesting of gre tunnels. |
---|
155 | * Since, setting a large value to this macro with a careless configuration |
---|
156 | * may introduce system crash, we don't allow any nestings by default. |
---|
157 | * If you need to configure nested gre tunnels, you can define this macro |
---|
158 | * in your kernel configuration file. However, if you do so, please be |
---|
159 | * careful to configure the tunnels so that it won't make a loop. |
---|
160 | */ |
---|
161 | #define MAX_GRE_NEST 1 |
---|
162 | #endif |
---|
163 | static int max_gre_nesting = MAX_GRE_NEST; |
---|
164 | SYSCTL_INT(_net_link_gre, OID_AUTO, max_nesting, CTLFLAG_RW, |
---|
165 | &max_gre_nesting, 0, "Max nested tunnels"); |
---|
166 | |
---|
167 | /* ARGSUSED */ |
---|
168 | static void |
---|
169 | greattach(void) |
---|
170 | { |
---|
171 | |
---|
172 | mtx_init(&gre_mtx, "gre_mtx", NULL, MTX_DEF); |
---|
173 | LIST_INIT(&gre_softc_list); |
---|
174 | if_clone_attach(&gre_cloner); |
---|
175 | } |
---|
176 | |
---|
177 | static int |
---|
178 | gre_clone_create(ifc, unit, params) |
---|
179 | struct if_clone *ifc; |
---|
180 | int unit; |
---|
181 | caddr_t params; |
---|
182 | { |
---|
183 | struct gre_softc *sc; |
---|
184 | |
---|
185 | sc = malloc(sizeof(struct gre_softc), M_GRE, M_WAITOK | M_ZERO); |
---|
186 | |
---|
187 | GRE2IFP(sc) = if_alloc(IFT_TUNNEL); |
---|
188 | if (GRE2IFP(sc) == NULL) { |
---|
189 | free(sc, M_GRE); |
---|
190 | return (ENOSPC); |
---|
191 | } |
---|
192 | |
---|
193 | GRE2IFP(sc)->if_softc = sc; |
---|
194 | if_initname(GRE2IFP(sc), ifc->ifc_name, unit); |
---|
195 | |
---|
196 | GRE2IFP(sc)->if_snd.ifq_maxlen = ifqmaxlen; |
---|
197 | GRE2IFP(sc)->if_addrlen = 0; |
---|
198 | GRE2IFP(sc)->if_hdrlen = 24; /* IP + GRE */ |
---|
199 | GRE2IFP(sc)->if_mtu = GREMTU; |
---|
200 | GRE2IFP(sc)->if_flags = IFF_POINTOPOINT|IFF_MULTICAST; |
---|
201 | GRE2IFP(sc)->if_output = gre_output; |
---|
202 | GRE2IFP(sc)->if_ioctl = gre_ioctl; |
---|
203 | sc->g_dst.s_addr = sc->g_src.s_addr = INADDR_ANY; |
---|
204 | sc->g_proto = IPPROTO_GRE; |
---|
205 | GRE2IFP(sc)->if_flags |= IFF_LINK0; |
---|
206 | sc->encap = NULL; |
---|
207 | sc->called = 0; |
---|
208 | sc->gre_fibnum = curthread->td_proc->p_fibnum; |
---|
209 | sc->wccp_ver = WCCP_V1; |
---|
210 | sc->key = 0; |
---|
211 | if_attach(GRE2IFP(sc)); |
---|
212 | bpfattach(GRE2IFP(sc), DLT_NULL, sizeof(u_int32_t)); |
---|
213 | mtx_lock(&gre_mtx); |
---|
214 | LIST_INSERT_HEAD(&gre_softc_list, sc, sc_list); |
---|
215 | mtx_unlock(&gre_mtx); |
---|
216 | return (0); |
---|
217 | } |
---|
218 | |
---|
219 | static void |
---|
220 | gre_clone_destroy(ifp) |
---|
221 | struct ifnet *ifp; |
---|
222 | { |
---|
223 | struct gre_softc *sc = ifp->if_softc; |
---|
224 | |
---|
225 | mtx_lock(&gre_mtx); |
---|
226 | LIST_REMOVE(sc, sc_list); |
---|
227 | mtx_unlock(&gre_mtx); |
---|
228 | |
---|
229 | #ifdef INET |
---|
230 | if (sc->encap != NULL) |
---|
231 | encap_detach(sc->encap); |
---|
232 | #endif |
---|
233 | bpfdetach(ifp); |
---|
234 | if_detach(ifp); |
---|
235 | if_free(ifp); |
---|
236 | free(sc, M_GRE); |
---|
237 | } |
---|
238 | |
---|
239 | /* |
---|
240 | * The output routine. Takes a packet and encapsulates it in the protocol |
---|
241 | * given by sc->g_proto. See also RFC 1701 and RFC 2004 |
---|
242 | */ |
---|
243 | static int |
---|
244 | gre_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, |
---|
245 | struct route *ro) |
---|
246 | { |
---|
247 | int error = 0; |
---|
248 | struct gre_softc *sc = ifp->if_softc; |
---|
249 | struct greip *gh; |
---|
250 | struct ip *ip; |
---|
251 | u_short gre_ip_id = 0; |
---|
252 | uint8_t gre_ip_tos = 0; |
---|
253 | u_int16_t etype = 0; |
---|
254 | struct mobile_h mob_h; |
---|
255 | u_int32_t af; |
---|
256 | int extra = 0; |
---|
257 | |
---|
258 | /* |
---|
259 | * gre may cause infinite recursion calls when misconfigured. |
---|
260 | * We'll prevent this by introducing upper limit. |
---|
261 | */ |
---|
262 | if (++(sc->called) > max_gre_nesting) { |
---|
263 | printf("%s: gre_output: recursively called too many " |
---|
264 | "times(%d)\n", if_name(GRE2IFP(sc)), sc->called); |
---|
265 | m_freem(m); |
---|
266 | error = EIO; /* is there better errno? */ |
---|
267 | goto end; |
---|
268 | } |
---|
269 | |
---|
270 | if (!((ifp->if_flags & IFF_UP) && |
---|
271 | (ifp->if_drv_flags & IFF_DRV_RUNNING)) || |
---|
272 | sc->g_src.s_addr == INADDR_ANY || sc->g_dst.s_addr == INADDR_ANY) { |
---|
273 | m_freem(m); |
---|
274 | error = ENETDOWN; |
---|
275 | goto end; |
---|
276 | } |
---|
277 | |
---|
278 | gh = NULL; |
---|
279 | ip = NULL; |
---|
280 | |
---|
281 | /* BPF writes need to be handled specially. */ |
---|
282 | if (dst->sa_family == AF_UNSPEC) { |
---|
283 | bcopy(dst->sa_data, &af, sizeof(af)); |
---|
284 | dst->sa_family = af; |
---|
285 | } |
---|
286 | |
---|
287 | if (bpf_peers_present(ifp->if_bpf)) { |
---|
288 | af = dst->sa_family; |
---|
289 | bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m); |
---|
290 | } |
---|
291 | |
---|
292 | m->m_flags &= ~(M_BCAST|M_MCAST); |
---|
293 | |
---|
294 | if (sc->g_proto == IPPROTO_MOBILE) { |
---|
295 | if (dst->sa_family == AF_INET) { |
---|
296 | struct mbuf *m0; |
---|
297 | int msiz; |
---|
298 | |
---|
299 | ip = mtod(m, struct ip *); |
---|
300 | |
---|
301 | /* |
---|
302 | * RFC2004 specifies that fragmented diagrams shouldn't |
---|
303 | * be encapsulated. |
---|
304 | */ |
---|
305 | if (ip->ip_off & (IP_MF | IP_OFFMASK)) { |
---|
306 | _IF_DROP(&ifp->if_snd); |
---|
307 | m_freem(m); |
---|
308 | error = EINVAL; /* is there better errno? */ |
---|
309 | goto end; |
---|
310 | } |
---|
311 | memset(&mob_h, 0, MOB_HH_SIZ_L); |
---|
312 | mob_h.proto = (ip->ip_p) << 8; |
---|
313 | mob_h.odst = ip->ip_dst.s_addr; |
---|
314 | ip->ip_dst.s_addr = sc->g_dst.s_addr; |
---|
315 | |
---|
316 | /* |
---|
317 | * If the packet comes from our host, we only change |
---|
318 | * the destination address in the IP header. |
---|
319 | * Else we also need to save and change the source |
---|
320 | */ |
---|
321 | if (in_hosteq(ip->ip_src, sc->g_src)) { |
---|
322 | msiz = MOB_HH_SIZ_S; |
---|
323 | } else { |
---|
324 | mob_h.proto |= MOB_HH_SBIT; |
---|
325 | mob_h.osrc = ip->ip_src.s_addr; |
---|
326 | ip->ip_src.s_addr = sc->g_src.s_addr; |
---|
327 | msiz = MOB_HH_SIZ_L; |
---|
328 | } |
---|
329 | mob_h.proto = htons(mob_h.proto); |
---|
330 | mob_h.hcrc = gre_in_cksum((u_int16_t *)&mob_h, msiz); |
---|
331 | |
---|
332 | if ((m->m_data - msiz) < m->m_pktdat) { |
---|
333 | /* need new mbuf */ |
---|
334 | MGETHDR(m0, M_DONTWAIT, MT_DATA); |
---|
335 | if (m0 == NULL) { |
---|
336 | _IF_DROP(&ifp->if_snd); |
---|
337 | m_freem(m); |
---|
338 | error = ENOBUFS; |
---|
339 | goto end; |
---|
340 | } |
---|
341 | m0->m_next = m; |
---|
342 | m->m_data += sizeof(struct ip); |
---|
343 | m->m_len -= sizeof(struct ip); |
---|
344 | m0->m_pkthdr.len = m->m_pkthdr.len + msiz; |
---|
345 | m0->m_len = msiz + sizeof(struct ip); |
---|
346 | m0->m_data += max_linkhdr; |
---|
347 | memcpy(mtod(m0, caddr_t), (caddr_t)ip, |
---|
348 | sizeof(struct ip)); |
---|
349 | m = m0; |
---|
350 | } else { /* we have some space left in the old one */ |
---|
351 | m->m_data -= msiz; |
---|
352 | m->m_len += msiz; |
---|
353 | m->m_pkthdr.len += msiz; |
---|
354 | bcopy(ip, mtod(m, caddr_t), |
---|
355 | sizeof(struct ip)); |
---|
356 | } |
---|
357 | ip = mtod(m, struct ip *); |
---|
358 | memcpy((caddr_t)(ip + 1), &mob_h, (unsigned)msiz); |
---|
359 | ip->ip_len = ntohs(ip->ip_len) + msiz; |
---|
360 | } else { /* AF_INET */ |
---|
361 | _IF_DROP(&ifp->if_snd); |
---|
362 | m_freem(m); |
---|
363 | error = EINVAL; |
---|
364 | goto end; |
---|
365 | } |
---|
366 | } else if (sc->g_proto == IPPROTO_GRE) { |
---|
367 | switch (dst->sa_family) { |
---|
368 | case AF_INET: |
---|
369 | ip = mtod(m, struct ip *); |
---|
370 | gre_ip_tos = ip->ip_tos; |
---|
371 | gre_ip_id = ip->ip_id; |
---|
372 | if (sc->wccp_ver == WCCP_V2) { |
---|
373 | extra = sizeof(uint32_t); |
---|
374 | etype = WCCP_PROTOCOL_TYPE; |
---|
375 | } else { |
---|
376 | etype = ETHERTYPE_IP; |
---|
377 | } |
---|
378 | break; |
---|
379 | #ifdef INET6 |
---|
380 | case AF_INET6: |
---|
381 | gre_ip_id = ip_newid(); |
---|
382 | etype = ETHERTYPE_IPV6; |
---|
383 | break; |
---|
384 | #endif |
---|
385 | #ifdef NETATALK |
---|
386 | case AF_APPLETALK: |
---|
387 | etype = ETHERTYPE_ATALK; |
---|
388 | break; |
---|
389 | #endif |
---|
390 | default: |
---|
391 | _IF_DROP(&ifp->if_snd); |
---|
392 | m_freem(m); |
---|
393 | error = EAFNOSUPPORT; |
---|
394 | goto end; |
---|
395 | } |
---|
396 | |
---|
397 | /* Reserve space for GRE header + optional GRE key */ |
---|
398 | int hdrlen = sizeof(struct greip) + extra; |
---|
399 | if (sc->key) |
---|
400 | hdrlen += sizeof(uint32_t); |
---|
401 | M_PREPEND(m, hdrlen, M_DONTWAIT); |
---|
402 | } else { |
---|
403 | _IF_DROP(&ifp->if_snd); |
---|
404 | m_freem(m); |
---|
405 | error = EINVAL; |
---|
406 | goto end; |
---|
407 | } |
---|
408 | |
---|
409 | if (m == NULL) { /* mbuf allocation failed */ |
---|
410 | _IF_DROP(&ifp->if_snd); |
---|
411 | error = ENOBUFS; |
---|
412 | goto end; |
---|
413 | } |
---|
414 | |
---|
415 | M_SETFIB(m, sc->gre_fibnum); /* The envelope may use a different FIB */ |
---|
416 | |
---|
417 | gh = mtod(m, struct greip *); |
---|
418 | if (sc->g_proto == IPPROTO_GRE) { |
---|
419 | uint32_t *options = gh->gi_options; |
---|
420 | |
---|
421 | memset((void *)gh, 0, sizeof(struct greip) + extra); |
---|
422 | gh->gi_ptype = htons(etype); |
---|
423 | gh->gi_flags = 0; |
---|
424 | |
---|
425 | /* Add key option */ |
---|
426 | if (sc->key) |
---|
427 | { |
---|
428 | gh->gi_flags |= htons(GRE_KP); |
---|
429 | *(options++) = htonl(sc->key); |
---|
430 | } |
---|
431 | } |
---|
432 | |
---|
433 | gh->gi_pr = sc->g_proto; |
---|
434 | if (sc->g_proto != IPPROTO_MOBILE) { |
---|
435 | gh->gi_src = sc->g_src; |
---|
436 | gh->gi_dst = sc->g_dst; |
---|
437 | ((struct ip*)gh)->ip_v = IPPROTO_IPV4; |
---|
438 | ((struct ip*)gh)->ip_hl = (sizeof(struct ip)) >> 2; |
---|
439 | ((struct ip*)gh)->ip_ttl = GRE_TTL; |
---|
440 | ((struct ip*)gh)->ip_tos = gre_ip_tos; |
---|
441 | ((struct ip*)gh)->ip_id = gre_ip_id; |
---|
442 | gh->gi_len = m->m_pkthdr.len; |
---|
443 | } |
---|
444 | |
---|
445 | ifp->if_opackets++; |
---|
446 | ifp->if_obytes += m->m_pkthdr.len; |
---|
447 | /* |
---|
448 | * Send it off and with IP_FORWARD flag to prevent it from |
---|
449 | * overwriting the ip_id again. ip_id is already set to the |
---|
450 | * ip_id of the encapsulated packet. |
---|
451 | */ |
---|
452 | error = ip_output(m, NULL, &sc->route, IP_FORWARDING, |
---|
453 | (struct ip_moptions *)NULL, (struct inpcb *)NULL); |
---|
454 | end: |
---|
455 | sc->called = 0; |
---|
456 | if (error) |
---|
457 | ifp->if_oerrors++; |
---|
458 | return (error); |
---|
459 | } |
---|
460 | |
---|
461 | static int |
---|
462 | gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) |
---|
463 | { |
---|
464 | struct ifreq *ifr = (struct ifreq *)data; |
---|
465 | struct if_laddrreq *lifr = (struct if_laddrreq *)data; |
---|
466 | struct in_aliasreq *aifr = (struct in_aliasreq *)data; |
---|
467 | struct gre_softc *sc = ifp->if_softc; |
---|
468 | int s; |
---|
469 | struct sockaddr_in si; |
---|
470 | struct sockaddr *sa = NULL; |
---|
471 | int error, adj; |
---|
472 | struct sockaddr_in sp, sm, dp, dm; |
---|
473 | uint32_t key; |
---|
474 | |
---|
475 | error = 0; |
---|
476 | adj = 0; |
---|
477 | |
---|
478 | s = splnet(); |
---|
479 | switch (cmd) { |
---|
480 | case SIOCSIFADDR: |
---|
481 | ifp->if_flags |= IFF_UP; |
---|
482 | break; |
---|
483 | case SIOCSIFDSTADDR: |
---|
484 | break; |
---|
485 | case SIOCSIFFLAGS: |
---|
486 | /* |
---|
487 | * XXXRW: Isn't this priv_check() redundant to the ifnet |
---|
488 | * layer check? |
---|
489 | */ |
---|
490 | if ((error = priv_check(curthread, PRIV_NET_SETIFFLAGS)) != 0) |
---|
491 | break; |
---|
492 | if ((ifr->ifr_flags & IFF_LINK0) != 0) |
---|
493 | sc->g_proto = IPPROTO_GRE; |
---|
494 | else |
---|
495 | sc->g_proto = IPPROTO_MOBILE; |
---|
496 | if ((ifr->ifr_flags & IFF_LINK2) != 0) |
---|
497 | sc->wccp_ver = WCCP_V2; |
---|
498 | else |
---|
499 | sc->wccp_ver = WCCP_V1; |
---|
500 | goto recompute; |
---|
501 | case SIOCSIFMTU: |
---|
502 | /* |
---|
503 | * XXXRW: Isn't this priv_check() redundant to the ifnet |
---|
504 | * layer check? |
---|
505 | */ |
---|
506 | if ((error = priv_check(curthread, PRIV_NET_SETIFMTU)) != 0) |
---|
507 | break; |
---|
508 | if (ifr->ifr_mtu < 576) { |
---|
509 | error = EINVAL; |
---|
510 | break; |
---|
511 | } |
---|
512 | ifp->if_mtu = ifr->ifr_mtu; |
---|
513 | break; |
---|
514 | case SIOCGIFMTU: |
---|
515 | ifr->ifr_mtu = GRE2IFP(sc)->if_mtu; |
---|
516 | break; |
---|
517 | case SIOCADDMULTI: |
---|
518 | /* |
---|
519 | * XXXRW: Isn't this priv_checkr() redundant to the ifnet |
---|
520 | * layer check? |
---|
521 | */ |
---|
522 | if ((error = priv_check(curthread, PRIV_NET_ADDMULTI)) != 0) |
---|
523 | break; |
---|
524 | if (ifr == 0) { |
---|
525 | error = EAFNOSUPPORT; |
---|
526 | break; |
---|
527 | } |
---|
528 | switch (ifr->ifr_addr.sa_family) { |
---|
529 | #ifdef INET |
---|
530 | case AF_INET: |
---|
531 | break; |
---|
532 | #endif |
---|
533 | #ifdef INET6 |
---|
534 | case AF_INET6: |
---|
535 | break; |
---|
536 | #endif |
---|
537 | default: |
---|
538 | error = EAFNOSUPPORT; |
---|
539 | break; |
---|
540 | } |
---|
541 | break; |
---|
542 | case SIOCDELMULTI: |
---|
543 | /* |
---|
544 | * XXXRW: Isn't this priv_check() redundant to the ifnet |
---|
545 | * layer check? |
---|
546 | */ |
---|
547 | if ((error = priv_check(curthread, PRIV_NET_DELIFGROUP)) != 0) |
---|
548 | break; |
---|
549 | if (ifr == 0) { |
---|
550 | error = EAFNOSUPPORT; |
---|
551 | break; |
---|
552 | } |
---|
553 | switch (ifr->ifr_addr.sa_family) { |
---|
554 | #ifdef INET |
---|
555 | case AF_INET: |
---|
556 | break; |
---|
557 | #endif |
---|
558 | #ifdef INET6 |
---|
559 | case AF_INET6: |
---|
560 | break; |
---|
561 | #endif |
---|
562 | default: |
---|
563 | error = EAFNOSUPPORT; |
---|
564 | break; |
---|
565 | } |
---|
566 | break; |
---|
567 | case GRESPROTO: |
---|
568 | /* |
---|
569 | * XXXRW: Isn't this priv_check() redundant to the ifnet |
---|
570 | * layer check? |
---|
571 | */ |
---|
572 | if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0) |
---|
573 | break; |
---|
574 | sc->g_proto = ifr->ifr_flags; |
---|
575 | switch (sc->g_proto) { |
---|
576 | case IPPROTO_GRE: |
---|
577 | ifp->if_flags |= IFF_LINK0; |
---|
578 | break; |
---|
579 | case IPPROTO_MOBILE: |
---|
580 | ifp->if_flags &= ~IFF_LINK0; |
---|
581 | break; |
---|
582 | default: |
---|
583 | error = EPROTONOSUPPORT; |
---|
584 | break; |
---|
585 | } |
---|
586 | goto recompute; |
---|
587 | case GREGPROTO: |
---|
588 | ifr->ifr_flags = sc->g_proto; |
---|
589 | break; |
---|
590 | case GRESADDRS: |
---|
591 | case GRESADDRD: |
---|
592 | error = priv_check(curthread, PRIV_NET_GRE); |
---|
593 | if (error) |
---|
594 | return (error); |
---|
595 | /* |
---|
596 | * set tunnel endpoints, compute a less specific route |
---|
597 | * to the remote end and mark if as up |
---|
598 | */ |
---|
599 | sa = &ifr->ifr_addr; |
---|
600 | if (cmd == GRESADDRS) |
---|
601 | sc->g_src = (satosin(sa))->sin_addr; |
---|
602 | if (cmd == GRESADDRD) |
---|
603 | sc->g_dst = (satosin(sa))->sin_addr; |
---|
604 | recompute: |
---|
605 | #ifdef INET |
---|
606 | if (sc->encap != NULL) { |
---|
607 | encap_detach(sc->encap); |
---|
608 | sc->encap = NULL; |
---|
609 | } |
---|
610 | #endif |
---|
611 | if ((sc->g_src.s_addr != INADDR_ANY) && |
---|
612 | (sc->g_dst.s_addr != INADDR_ANY)) { |
---|
613 | bzero(&sp, sizeof(sp)); |
---|
614 | bzero(&sm, sizeof(sm)); |
---|
615 | bzero(&dp, sizeof(dp)); |
---|
616 | bzero(&dm, sizeof(dm)); |
---|
617 | sp.sin_len = sm.sin_len = dp.sin_len = dm.sin_len = |
---|
618 | sizeof(struct sockaddr_in); |
---|
619 | sp.sin_family = sm.sin_family = dp.sin_family = |
---|
620 | dm.sin_family = AF_INET; |
---|
621 | sp.sin_addr = sc->g_src; |
---|
622 | dp.sin_addr = sc->g_dst; |
---|
623 | sm.sin_addr.s_addr = dm.sin_addr.s_addr = |
---|
624 | INADDR_BROADCAST; |
---|
625 | #ifdef INET |
---|
626 | sc->encap = encap_attach(AF_INET, sc->g_proto, |
---|
627 | sintosa(&sp), sintosa(&sm), sintosa(&dp), |
---|
628 | sintosa(&dm), (sc->g_proto == IPPROTO_GRE) ? |
---|
629 | &in_gre_protosw : &in_mobile_protosw, sc); |
---|
630 | if (sc->encap == NULL) |
---|
631 | printf("%s: unable to attach encap\n", |
---|
632 | if_name(GRE2IFP(sc))); |
---|
633 | #endif |
---|
634 | if (sc->route.ro_rt != 0) /* free old route */ |
---|
635 | RTFREE(sc->route.ro_rt); |
---|
636 | if (gre_compute_route(sc) == 0) |
---|
637 | ifp->if_drv_flags |= IFF_DRV_RUNNING; |
---|
638 | else |
---|
639 | ifp->if_drv_flags &= ~IFF_DRV_RUNNING; |
---|
640 | } |
---|
641 | break; |
---|
642 | case GREGADDRS: |
---|
643 | memset(&si, 0, sizeof(si)); |
---|
644 | si.sin_family = AF_INET; |
---|
645 | si.sin_len = sizeof(struct sockaddr_in); |
---|
646 | si.sin_addr.s_addr = sc->g_src.s_addr; |
---|
647 | sa = sintosa(&si); |
---|
648 | ifr->ifr_addr = *sa; |
---|
649 | break; |
---|
650 | case GREGADDRD: |
---|
651 | memset(&si, 0, sizeof(si)); |
---|
652 | si.sin_family = AF_INET; |
---|
653 | si.sin_len = sizeof(struct sockaddr_in); |
---|
654 | si.sin_addr.s_addr = sc->g_dst.s_addr; |
---|
655 | sa = sintosa(&si); |
---|
656 | ifr->ifr_addr = *sa; |
---|
657 | break; |
---|
658 | case SIOCSIFPHYADDR: |
---|
659 | /* |
---|
660 | * XXXRW: Isn't this priv_check() redundant to the ifnet |
---|
661 | * layer check? |
---|
662 | */ |
---|
663 | if ((error = priv_check(curthread, PRIV_NET_SETIFPHYS)) != 0) |
---|
664 | break; |
---|
665 | if (aifr->ifra_addr.sin_family != AF_INET || |
---|
666 | aifr->ifra_dstaddr.sin_family != AF_INET) { |
---|
667 | error = EAFNOSUPPORT; |
---|
668 | break; |
---|
669 | } |
---|
670 | if (aifr->ifra_addr.sin_len != sizeof(si) || |
---|
671 | aifr->ifra_dstaddr.sin_len != sizeof(si)) { |
---|
672 | error = EINVAL; |
---|
673 | break; |
---|
674 | } |
---|
675 | sc->g_src = aifr->ifra_addr.sin_addr; |
---|
676 | sc->g_dst = aifr->ifra_dstaddr.sin_addr; |
---|
677 | goto recompute; |
---|
678 | case SIOCSLIFPHYADDR: |
---|
679 | /* |
---|
680 | * XXXRW: Isn't this priv_check() redundant to the ifnet |
---|
681 | * layer check? |
---|
682 | */ |
---|
683 | if ((error = priv_check(curthread, PRIV_NET_SETIFPHYS)) != 0) |
---|
684 | break; |
---|
685 | if (lifr->addr.ss_family != AF_INET || |
---|
686 | lifr->dstaddr.ss_family != AF_INET) { |
---|
687 | error = EAFNOSUPPORT; |
---|
688 | break; |
---|
689 | } |
---|
690 | if (lifr->addr.ss_len != sizeof(si) || |
---|
691 | lifr->dstaddr.ss_len != sizeof(si)) { |
---|
692 | error = EINVAL; |
---|
693 | break; |
---|
694 | } |
---|
695 | sc->g_src = (satosin(&lifr->addr))->sin_addr; |
---|
696 | sc->g_dst = |
---|
697 | (satosin(&lifr->dstaddr))->sin_addr; |
---|
698 | goto recompute; |
---|
699 | case SIOCDIFPHYADDR: |
---|
700 | /* |
---|
701 | * XXXRW: Isn't this priv_check() redundant to the ifnet |
---|
702 | * layer check? |
---|
703 | */ |
---|
704 | if ((error = priv_check(curthread, PRIV_NET_SETIFPHYS)) != 0) |
---|
705 | break; |
---|
706 | sc->g_src.s_addr = INADDR_ANY; |
---|
707 | sc->g_dst.s_addr = INADDR_ANY; |
---|
708 | goto recompute; |
---|
709 | case SIOCGLIFPHYADDR: |
---|
710 | if (sc->g_src.s_addr == INADDR_ANY || |
---|
711 | sc->g_dst.s_addr == INADDR_ANY) { |
---|
712 | error = EADDRNOTAVAIL; |
---|
713 | break; |
---|
714 | } |
---|
715 | memset(&si, 0, sizeof(si)); |
---|
716 | si.sin_family = AF_INET; |
---|
717 | si.sin_len = sizeof(struct sockaddr_in); |
---|
718 | si.sin_addr.s_addr = sc->g_src.s_addr; |
---|
719 | memcpy(&lifr->addr, &si, sizeof(si)); |
---|
720 | si.sin_addr.s_addr = sc->g_dst.s_addr; |
---|
721 | memcpy(&lifr->dstaddr, &si, sizeof(si)); |
---|
722 | break; |
---|
723 | case SIOCGIFPSRCADDR: |
---|
724 | #ifdef INET6 |
---|
725 | case SIOCGIFPSRCADDR_IN6: |
---|
726 | #endif |
---|
727 | if (sc->g_src.s_addr == INADDR_ANY) { |
---|
728 | error = EADDRNOTAVAIL; |
---|
729 | break; |
---|
730 | } |
---|
731 | memset(&si, 0, sizeof(si)); |
---|
732 | si.sin_family = AF_INET; |
---|
733 | si.sin_len = sizeof(struct sockaddr_in); |
---|
734 | si.sin_addr.s_addr = sc->g_src.s_addr; |
---|
735 | bcopy(&si, &ifr->ifr_addr, sizeof(ifr->ifr_addr)); |
---|
736 | break; |
---|
737 | case SIOCGIFPDSTADDR: |
---|
738 | #ifdef INET6 |
---|
739 | case SIOCGIFPDSTADDR_IN6: |
---|
740 | #endif |
---|
741 | if (sc->g_dst.s_addr == INADDR_ANY) { |
---|
742 | error = EADDRNOTAVAIL; |
---|
743 | break; |
---|
744 | } |
---|
745 | memset(&si, 0, sizeof(si)); |
---|
746 | si.sin_family = AF_INET; |
---|
747 | si.sin_len = sizeof(struct sockaddr_in); |
---|
748 | si.sin_addr.s_addr = sc->g_dst.s_addr; |
---|
749 | bcopy(&si, &ifr->ifr_addr, sizeof(ifr->ifr_addr)); |
---|
750 | break; |
---|
751 | case GRESKEY: |
---|
752 | error = priv_check(curthread, PRIV_NET_GRE); |
---|
753 | if (error) |
---|
754 | break; |
---|
755 | error = copyin(ifr->ifr_data, &key, sizeof(key)); |
---|
756 | if (error) |
---|
757 | break; |
---|
758 | /* adjust MTU for option header */ |
---|
759 | if (key == 0 && sc->key != 0) /* clear */ |
---|
760 | adj += sizeof(key); |
---|
761 | else if (key != 0 && sc->key == 0) /* set */ |
---|
762 | adj -= sizeof(key); |
---|
763 | |
---|
764 | if (ifp->if_mtu + adj < 576) { |
---|
765 | error = EINVAL; |
---|
766 | break; |
---|
767 | } |
---|
768 | ifp->if_mtu += adj; |
---|
769 | sc->key = key; |
---|
770 | break; |
---|
771 | case GREGKEY: |
---|
772 | error = copyout(&sc->key, ifr->ifr_data, sizeof(sc->key)); |
---|
773 | break; |
---|
774 | |
---|
775 | default: |
---|
776 | error = EINVAL; |
---|
777 | break; |
---|
778 | } |
---|
779 | |
---|
780 | splx(s); |
---|
781 | return (error); |
---|
782 | } |
---|
783 | |
---|
784 | /* |
---|
785 | * computes a route to our destination that is not the one |
---|
786 | * which would be taken by ip_output(), as this one will loop back to |
---|
787 | * us. If the interface is p2p as a--->b, then a routing entry exists |
---|
788 | * If we now send a packet to b (e.g. ping b), this will come down here |
---|
789 | * gets src=a, dst=b tacked on and would from ip_output() sent back to |
---|
790 | * if_gre. |
---|
791 | * Goal here is to compute a route to b that is less specific than |
---|
792 | * a-->b. We know that this one exists as in normal operation we have |
---|
793 | * at least a default route which matches. |
---|
794 | */ |
---|
795 | static int |
---|
796 | gre_compute_route(struct gre_softc *sc) |
---|
797 | { |
---|
798 | struct route *ro; |
---|
799 | |
---|
800 | ro = &sc->route; |
---|
801 | |
---|
802 | memset(ro, 0, sizeof(struct route)); |
---|
803 | ((struct sockaddr_in *)&ro->ro_dst)->sin_addr = sc->g_dst; |
---|
804 | ro->ro_dst.sa_family = AF_INET; |
---|
805 | ro->ro_dst.sa_len = sizeof(ro->ro_dst); |
---|
806 | |
---|
807 | /* |
---|
808 | * toggle last bit, so our interface is not found, but a less |
---|
809 | * specific route. I'd rather like to specify a shorter mask, |
---|
810 | * but this is not possible. Should work though. XXX |
---|
811 | * XXX MRT Use a different FIB for the tunnel to solve this problem. |
---|
812 | */ |
---|
813 | if ((GRE2IFP(sc)->if_flags & IFF_LINK1) == 0) { |
---|
814 | ((struct sockaddr_in *)&ro->ro_dst)->sin_addr.s_addr ^= |
---|
815 | htonl(0x01); |
---|
816 | } |
---|
817 | |
---|
818 | #ifdef DIAGNOSTIC |
---|
819 | printf("%s: searching for a route to %s", if_name(GRE2IFP(sc)), |
---|
820 | inet_ntoa(((struct sockaddr_in *)&ro->ro_dst)->sin_addr)); |
---|
821 | #endif |
---|
822 | |
---|
823 | rtalloc_fib(ro, sc->gre_fibnum); |
---|
824 | |
---|
825 | /* |
---|
826 | * check if this returned a route at all and this route is no |
---|
827 | * recursion to ourself |
---|
828 | */ |
---|
829 | if (ro->ro_rt == NULL || ro->ro_rt->rt_ifp->if_softc == sc) { |
---|
830 | #ifdef DIAGNOSTIC |
---|
831 | if (ro->ro_rt == NULL) |
---|
832 | printf(" - no route found!\n"); |
---|
833 | else |
---|
834 | printf(" - route loops back to ourself!\n"); |
---|
835 | #endif |
---|
836 | return EADDRNOTAVAIL; |
---|
837 | } |
---|
838 | |
---|
839 | /* |
---|
840 | * now change it back - else ip_output will just drop |
---|
841 | * the route and search one to this interface ... |
---|
842 | */ |
---|
843 | if ((GRE2IFP(sc)->if_flags & IFF_LINK1) == 0) |
---|
844 | ((struct sockaddr_in *)&ro->ro_dst)->sin_addr = sc->g_dst; |
---|
845 | |
---|
846 | #ifdef DIAGNOSTIC |
---|
847 | printf(", choosing %s with gateway %s", if_name(ro->ro_rt->rt_ifp), |
---|
848 | inet_ntoa(((struct sockaddr_in *)(ro->ro_rt->rt_gateway))->sin_addr)); |
---|
849 | printf("\n"); |
---|
850 | #endif |
---|
851 | |
---|
852 | return 0; |
---|
853 | } |
---|
854 | |
---|
855 | /* |
---|
856 | * do a checksum of a buffer - much like in_cksum, which operates on |
---|
857 | * mbufs. |
---|
858 | */ |
---|
859 | u_int16_t |
---|
860 | gre_in_cksum(u_int16_t *p, u_int len) |
---|
861 | { |
---|
862 | u_int32_t sum = 0; |
---|
863 | int nwords = len >> 1; |
---|
864 | |
---|
865 | while (nwords-- != 0) |
---|
866 | sum += *p++; |
---|
867 | |
---|
868 | if (len & 1) { |
---|
869 | union { |
---|
870 | u_short w; |
---|
871 | u_char c[2]; |
---|
872 | } u; |
---|
873 | u.c[0] = *(u_char *)p; |
---|
874 | u.c[1] = 0; |
---|
875 | sum += u.w; |
---|
876 | } |
---|
877 | |
---|
878 | /* end-around-carry */ |
---|
879 | sum = (sum >> 16) + (sum & 0xffff); |
---|
880 | sum += (sum >> 16); |
---|
881 | return (~sum); |
---|
882 | } |
---|
883 | |
---|
884 | static int |
---|
885 | gremodevent(module_t mod, int type, void *data) |
---|
886 | { |
---|
887 | |
---|
888 | switch (type) { |
---|
889 | case MOD_LOAD: |
---|
890 | greattach(); |
---|
891 | break; |
---|
892 | case MOD_UNLOAD: |
---|
893 | if_clone_detach(&gre_cloner); |
---|
894 | mtx_destroy(&gre_mtx); |
---|
895 | break; |
---|
896 | default: |
---|
897 | return EOPNOTSUPP; |
---|
898 | } |
---|
899 | return 0; |
---|
900 | } |
---|
901 | |
---|
902 | static moduledata_t gre_mod = { |
---|
903 | "if_gre", |
---|
904 | gremodevent, |
---|
905 | 0 |
---|
906 | }; |
---|
907 | |
---|
908 | DECLARE_MODULE(if_gre, gre_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); |
---|
909 | MODULE_VERSION(if_gre, 1); |
---|