FreeBSD/Linux Kernel Cross Reference
sys/netipx/ipx_ip.c
1 /*-
2 * Copyright (c) 1995, Mike Mitchell
3 * Copyright (c) 1984, 1985, 1986, 1987, 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * @(#)ipx_ip.c
35 */
36
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD: releng/6.0/sys/netipx/ipx_ip.c 147256 2005-06-10 16:49:24Z brooks $");
39
40 /*
41 * Software interface driver for encapsulating IPX in IP.
42 */
43
44 #include "opt_inet.h"
45 #include "opt_ipx.h"
46
47 #ifdef IPXIP
48 #ifndef INET
49 #error The option IPXIP requires option INET.
50 #endif
51
52 #include <sys/param.h>
53 #include <sys/systm.h>
54 #include <sys/malloc.h>
55 #include <sys/mbuf.h>
56 #include <sys/protosw.h>
57 #include <sys/socket.h>
58 #include <sys/socketvar.h>
59 #include <sys/sockio.h>
60
61 #include <net/if.h>
62 #include <net/if_types.h>
63 #include <net/netisr.h>
64 #include <net/route.h>
65
66 #include <netinet/in.h>
67 #include <netinet/in_systm.h>
68 #include <netinet/in_var.h>
69 #include <netinet/ip.h>
70 #include <netinet/ip_var.h>
71
72 #include <netipx/ipx.h>
73 #include <netipx/ipx_if.h>
74 #include <netipx/ipx_ip.h>
75 #include <netipx/ipx_var.h>
76
77 static struct ifnet ipxipif;
78 static int ipxipif_units;
79
80 /* list of all hosts and gateways or broadcast addrs */
81 static struct ifnet_en *ipxip_list;
82
83 static struct ifnet_en *ipxipattach(void);
84 static int ipxip_free(struct ifnet *ifp);
85 static int ipxipioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
86 static int ipxipoutput(struct ifnet *ifp, struct mbuf *m,
87 struct sockaddr *dst, struct rtentry *rt);
88 static void ipxip_rtchange(struct in_addr *dst);
89 static void ipxipstart(struct ifnet *ifp);
90
91 static struct ifnet_en *
92 ipxipattach()
93 {
94 register struct ifnet_en *m;
95 register struct ifnet *ifp;
96
97 if (ipxipif.if_mtu == 0) {
98 ifp = &ipxipif;
99 if_initname(ifp, "ipxip", ipxipif_units);
100 ifp->if_mtu = LOMTU;
101 ifp->if_ioctl = ipxipioctl;
102 ifp->if_output = ipxipoutput;
103 ifp->if_start = ipxipstart;
104 ifp->if_flags = IFF_POINTOPOINT;
105 }
106
107 MALLOC((m), struct ifnet_en *, sizeof(*m), M_PCB, M_NOWAIT | M_ZERO);
108 if (m == NULL)
109 return (NULL);
110 m->ifen_next = ipxip_list;
111 ipxip_list = m;
112 ifp = m->ifen_ifp = if_alloc(IFT_IPXIP);
113 if (ifp == NULL) {
114 FREE(m, M_PCB);
115 return (NULL);
116 }
117
118 if_initname(ifp, "ipxip", ipxipif_units++);
119 ifp->if_mtu = LOMTU;
120 ifp->if_ioctl = ipxipioctl;
121 ifp->if_output = ipxipoutput;
122 ifp->if_start = ipxipstart;
123 ifp->if_flags = IFF_POINTOPOINT;
124 ifp->if_softc = m;
125 if_attach(ifp);
126
127 return (m);
128 }
129
130
131 /*
132 * Process an ioctl request.
133 */
134 static int
135 ipxipioctl(ifp, cmd, data)
136 register struct ifnet *ifp;
137 u_long cmd;
138 caddr_t data;
139 {
140 int error = 0;
141 struct ifreq *ifr;
142
143 switch (cmd) {
144
145 case SIOCSIFADDR:
146 ifp->if_flags |= IFF_UP;
147 /* FALLTHROUGH */
148
149 case SIOCSIFDSTADDR:
150 /*
151 * Everything else is done at a higher level.
152 */
153 break;
154
155 case SIOCSIFFLAGS:
156 ifr = (struct ifreq *)data;
157 if ((ifr->ifr_flags & IFF_UP) == 0)
158 error = ipxip_free(ifp);
159
160
161 default:
162 error = EINVAL;
163 }
164 return (error);
165 }
166
167 static struct mbuf *ipxip_badlen;
168 static struct mbuf *ipxip_lastin;
169 static int ipxip_hold_input;
170
171 void
172 ipxip_input(m, hlen)
173 register struct mbuf *m;
174 int hlen;
175 {
176 register struct ip *ip;
177 register struct ipx *ipx;
178 int len, s;
179
180 if (ipxip_hold_input) {
181 if (ipxip_lastin != NULL) {
182 m_freem(ipxip_lastin);
183 }
184 ipxip_lastin = m_copym(m, 0, (int)M_COPYALL, M_DONTWAIT);
185 }
186 /*
187 * Get IP and IPX header together in first mbuf.
188 */
189 ipxipif.if_ipackets++;
190 s = sizeof(struct ip) + sizeof(struct ipx);
191 if (((m->m_flags & M_EXT) || m->m_len < s) &&
192 (m = m_pullup(m, s)) == NULL) {
193 ipxipif.if_ierrors++;
194 return;
195 }
196 ip = mtod(m, struct ip *);
197 if (ip->ip_hl > (sizeof(struct ip) >> 2)) {
198 ip_stripoptions(m, (struct mbuf *)NULL);
199 if (m->m_len < s) {
200 if ((m = m_pullup(m, s)) == NULL) {
201 ipxipif.if_ierrors++;
202 return;
203 }
204 ip = mtod(m, struct ip *);
205 }
206 }
207
208 /*
209 * Make mbuf data length reflect IPX length.
210 * If not enough data to reflect IPX length, drop.
211 */
212 m->m_data += sizeof(struct ip);
213 m->m_len -= sizeof(struct ip);
214 m->m_pkthdr.len -= sizeof(struct ip);
215 ipx = mtod(m, struct ipx *);
216 len = ntohs(ipx->ipx_len);
217 if (len & 1)
218 len++; /* Preserve Garbage Byte */
219 if (ip->ip_len != len) {
220 if (len > ip->ip_len) {
221 ipxipif.if_ierrors++;
222 if (ipxip_badlen)
223 m_freem(ipxip_badlen);
224 ipxip_badlen = m;
225 return;
226 }
227 /* Any extra will be trimmed off by the IPX routines */
228 }
229
230 /*
231 * Deliver to IPX
232 */
233 netisr_dispatch(NETISR_IPX, m);
234 }
235
236 static int
237 ipxipoutput(ifp, m, dst, rt)
238 struct ifnet *ifp;
239 struct mbuf *m;
240 struct sockaddr *dst;
241 struct rtentry *rt;
242 {
243 register struct ifnet_en *ifn = (struct ifnet_en *)ifp->if_softc;
244 register struct ip *ip;
245 register struct route *ro = &(ifn->ifen_route);
246 register int len = 0;
247 register struct ipx *ipx = mtod(m, struct ipx *);
248 int error;
249
250 ifn->ifen_ifp->if_opackets++;
251 ipxipif.if_opackets++;
252
253 /*
254 * Calculate data length and make space
255 * for IP header.
256 */
257 len = ntohs(ipx->ipx_len);
258 if (len & 1)
259 len++; /* Preserve Garbage Byte */
260 /* following clause not necessary on vax */
261 if (3 & (intptr_t)m->m_data) {
262 /* force longword alignment of ip hdr */
263 struct mbuf *m0 = m_gethdr(MT_HEADER, M_DONTWAIT);
264 if (m0 == NULL) {
265 m_freem(m);
266 return (ENOBUFS);
267 }
268 MH_ALIGN(m0, sizeof(struct ip));
269 m0->m_flags = m->m_flags & M_COPYFLAGS;
270 m0->m_next = m;
271 m0->m_len = sizeof(struct ip);
272 m0->m_pkthdr.len = m0->m_len + m->m_len;
273 m->m_flags &= ~M_PKTHDR;
274 m = m0;
275 } else {
276 M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
277 if (m == NULL)
278 return (ENOBUFS);
279 }
280 /*
281 * Fill in IP header.
282 */
283 ip = mtod(m, struct ip *);
284 *(long *)ip = 0;
285 ip->ip_p = IPPROTO_IDP;
286 ip->ip_src = ifn->ifen_src;
287 ip->ip_dst = ifn->ifen_dst;
288 ip->ip_len = (u_short)len + sizeof(struct ip);
289 ip->ip_ttl = MAXTTL;
290
291 /*
292 * Output final datagram.
293 */
294 error = (ip_output(m, (struct mbuf *)NULL, ro, SO_BROADCAST, NULL, NULL));
295 if (error) {
296 ifn->ifen_ifp->if_oerrors++;
297 ifn->ifen_ifp->if_ierrors = error;
298 }
299 return (error);
300 m_freem(m);
301 return (ENETUNREACH);
302 }
303
304 static void
305 ipxipstart(ifp)
306 struct ifnet *ifp;
307 {
308 panic("ipxip_start called\n");
309 }
310
311 static struct ifreq ifr_ipxip = {"ipxip0"};
312
313 int
314 ipxip_route(so, sopt)
315 struct socket *so;
316 struct sockopt *sopt;
317 {
318 int error;
319 struct ifnet_en *ifn;
320 struct sockaddr_in *src;
321 struct ipxip_req rq;
322 struct sockaddr_ipx *ipx_dst;
323 struct sockaddr_in *ip_dst;
324 struct route ro;
325
326 error = sooptcopyin(sopt, &rq, sizeof rq, sizeof rq);
327 if (error)
328 return (error);
329 ipx_dst = (struct sockaddr_ipx *)&rq.rq_ipx;
330 ip_dst = (struct sockaddr_in *)&rq.rq_ip;
331
332 /*
333 * First, make sure we already have an IPX address:
334 */
335 if (ipx_ifaddr == NULL)
336 return (EADDRNOTAVAIL);
337 /*
338 * Now, determine if we can get to the destination
339 */
340 bzero((caddr_t)&ro, sizeof(ro));
341 ro.ro_dst = *(struct sockaddr *)ip_dst;
342 rtalloc_ign(&ro, 0);
343 if (ro.ro_rt == NULL || ro.ro_rt->rt_ifp == NULL) {
344 return (ENETUNREACH);
345 }
346
347 /*
348 * And see how he's going to get back to us:
349 * i.e., what return ip address do we use?
350 */
351 {
352 register struct in_ifaddr *ia;
353 struct ifnet *ifp = ro.ro_rt->rt_ifp;
354
355 for (ia = TAILQ_FIRST(&in_ifaddrhead); ia != NULL;
356 ia = TAILQ_NEXT(ia, ia_link))
357 if (ia->ia_ifp == ifp)
358 break;
359 if (ia == NULL)
360 ia = TAILQ_FIRST(&in_ifaddrhead);
361 if (ia == NULL) {
362 RTFREE(ro.ro_rt);
363 return (EADDRNOTAVAIL);
364 }
365 src = (struct sockaddr_in *)&ia->ia_addr;
366 }
367
368 /*
369 * Is there a free (pseudo-)interface or space?
370 */
371 for (ifn = ipxip_list; ifn != NULL; ifn = ifn->ifen_next) {
372 if ((ifn->ifen_ifp->if_flags & IFF_UP) == 0)
373 break;
374 }
375 if (ifn == NULL)
376 ifn = ipxipattach();
377 if (ifn == NULL) {
378 RTFREE(ro.ro_rt);
379 return (ENOBUFS);
380 }
381 ifn->ifen_route = ro;
382 ifn->ifen_dst = ip_dst->sin_addr;
383 ifn->ifen_src = src->sin_addr;
384
385 /*
386 * now configure this as a point to point link
387 */
388 ifr_ipxip.ifr_name[4] = '' + ipxipif_units - 1;
389 ifr_ipxip.ifr_dstaddr = *(struct sockaddr *)ipx_dst;
390 ipx_control(so, (int)SIOCSIFDSTADDR, (caddr_t)&ifr_ipxip,
391 (struct ifnet *)ifn, sopt->sopt_td);
392
393 /* use any of our addresses */
394 satoipx_addr(ifr_ipxip.ifr_addr).x_host =
395 ipx_ifaddr->ia_addr.sipx_addr.x_host;
396
397 return (ipx_control(so, (int)SIOCSIFADDR, (caddr_t)&ifr_ipxip,
398 (struct ifnet *)ifn, sopt->sopt_td));
399 }
400
401 static int
402 ipxip_free(ifp)
403 struct ifnet *ifp;
404 {
405 register struct ifnet_en *ifn = (struct ifnet_en *)ifp->if_softc;
406 struct route *ro = & ifn->ifen_route;
407
408 if (ro->ro_rt != NULL) {
409 RTFREE(ro->ro_rt);
410 ro->ro_rt = NULL;
411 }
412 ifp->if_flags &= ~IFF_UP;
413 return (0);
414 }
415
416 void
417 ipxip_ctlinput(cmd, sa, dummy)
418 int cmd;
419 struct sockaddr *sa;
420 void *dummy;
421 {
422 struct sockaddr_in *sin;
423
424 if ((unsigned)cmd >= PRC_NCMDS)
425 return;
426 if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK)
427 return;
428 sin = (struct sockaddr_in *)sa;
429 if (sin->sin_addr.s_addr == INADDR_ANY)
430 return;
431
432 switch (cmd) {
433
434 case PRC_ROUTEDEAD:
435 case PRC_REDIRECT_NET:
436 case PRC_REDIRECT_HOST:
437 case PRC_REDIRECT_TOSNET:
438 case PRC_REDIRECT_TOSHOST:
439 ipxip_rtchange(&sin->sin_addr);
440 break;
441 }
442 }
443
444 static void
445 ipxip_rtchange(dst)
446 register struct in_addr *dst;
447 {
448 register struct ifnet_en *ifn;
449
450 for (ifn = ipxip_list; ifn != NULL; ifn = ifn->ifen_next) {
451 if (ifn->ifen_dst.s_addr == dst->s_addr &&
452 ifn->ifen_route.ro_rt != NULL) {
453 RTFREE(ifn->ifen_route.ro_rt);
454 ifn->ifen_route.ro_rt = NULL;
455 }
456 }
457 }
458 #endif /* IPXIP */
Cache object: 23147dd032e1d9d2c7c0103765d439c9
|