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 * $FreeBSD$
37 */
38
39 /*
40 * Software interface driver for encapsulating IPX in IP.
41 */
42
43 #include "opt_inet.h"
44 #include "opt_ipx.h"
45
46 #ifdef IPXIP
47 #ifndef INET
48 #error The option IPXIP requires option INET.
49 #endif
50
51 #include <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/malloc.h>
54 #include <sys/mbuf.h>
55 #include <sys/protosw.h>
56 #include <sys/socket.h>
57 #include <sys/socketvar.h>
58 #include <sys/sockio.h>
59
60 #include <net/if.h>
61 #include <net/netisr.h>
62 #include <net/route.h>
63
64 #include <netinet/in.h>
65 #include <netinet/in_systm.h>
66 #include <netinet/in_var.h>
67 #include <netinet/ip.h>
68 #include <netinet/ip_var.h>
69
70 #include <netipx/ipx.h>
71 #include <netipx/ipx_if.h>
72 #include <netipx/ipx_ip.h>
73 #include <netipx/ipx_var.h>
74
75 static struct ifnet ipxipif;
76
77 /* list of all hosts and gateways or broadcast addrs */
78 static struct ifnet_en *ipxip_list;
79
80 static struct ifnet_en *ipxipattach(void);
81 static int ipxip_free(struct ifnet *ifp);
82 static int ipxipioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
83 static int ipxipoutput(struct ifnet *ifp, struct mbuf *m,
84 struct sockaddr *dst, struct rtentry *rt);
85 static void ipxip_rtchange(struct in_addr *dst);
86 static void ipxipstart(struct ifnet *ifp);
87
88 static struct ifnet_en *
89 ipxipattach()
90 {
91 register struct ifnet_en *m;
92 register struct ifnet *ifp;
93
94 if (ipxipif.if_mtu == 0) {
95 ifp = &ipxipif;
96 ifp->if_name = "ipxip";
97 ifp->if_mtu = LOMTU;
98 ifp->if_ioctl = ipxipioctl;
99 ifp->if_output = ipxipoutput;
100 ifp->if_start = ipxipstart;
101 ifp->if_flags = IFF_POINTOPOINT;
102 }
103
104 MALLOC((m), struct ifnet_en *, sizeof(*m), M_PCB, M_NOWAIT);
105 if (m == NULL)
106 return (NULL);
107 bzero(m, sizeof(*m));
108 m->ifen_next = ipxip_list;
109 ipxip_list = m;
110 ifp = &m->ifen_ifnet;
111
112 ifp->if_name = "ipxip";
113 ifp->if_mtu = LOMTU;
114 ifp->if_ioctl = ipxipioctl;
115 ifp->if_output = ipxipoutput;
116 ifp->if_start = ipxipstart;
117 ifp->if_flags = IFF_POINTOPOINT;
118 ifp->if_unit = ipxipif.if_unit++;
119 if_attach(ifp);
120
121 return (m);
122 }
123
124
125 /*
126 * Process an ioctl request.
127 */
128 static int
129 ipxipioctl(ifp, cmd, data)
130 register struct ifnet *ifp;
131 u_long cmd;
132 caddr_t data;
133 {
134 int error = 0;
135 struct ifreq *ifr;
136
137 switch (cmd) {
138
139 case SIOCSIFADDR:
140 ifp->if_flags |= IFF_UP;
141 /* fall into: */
142
143 case SIOCSIFDSTADDR:
144 /*
145 * Everything else is done at a higher level.
146 */
147 break;
148
149 case SIOCSIFFLAGS:
150 ifr = (struct ifreq *)data;
151 if ((ifr->ifr_flags & IFF_UP) == 0)
152 error = ipxip_free(ifp);
153
154
155 default:
156 error = EINVAL;
157 }
158 return (error);
159 }
160
161 static struct mbuf *ipxip_badlen;
162 static struct mbuf *ipxip_lastin;
163 static int ipxip_hold_input;
164
165 void
166 ipxip_input(m, hlen)
167 register struct mbuf *m;
168 int hlen;
169 {
170 register struct ip *ip;
171 register struct ipx *ipx;
172 register struct ifqueue *ifq = &ipxintrq;
173 int len, s;
174
175 if (ipxip_hold_input) {
176 if (ipxip_lastin != NULL) {
177 m_freem(ipxip_lastin);
178 }
179 ipxip_lastin = m_copym(m, 0, (int)M_COPYALL, M_DONTWAIT);
180 }
181 /*
182 * Get IP and IPX header together in first mbuf.
183 */
184 ipxipif.if_ipackets++;
185 s = sizeof(struct ip) + sizeof(struct ipx);
186 if (((m->m_flags & M_EXT) || m->m_len < s) &&
187 (m = m_pullup(m, s)) == NULL) {
188 ipxipif.if_ierrors++;
189 return;
190 }
191 ip = mtod(m, struct ip *);
192 if (ip->ip_hl > (sizeof(struct ip) >> 2)) {
193 ip_stripoptions(m, (struct mbuf *)NULL);
194 if (m->m_len < s) {
195 if ((m = m_pullup(m, s)) == NULL) {
196 ipxipif.if_ierrors++;
197 return;
198 }
199 ip = mtod(m, struct ip *);
200 }
201 }
202
203 /*
204 * Make mbuf data length reflect IPX length.
205 * If not enough data to reflect IPX length, drop.
206 */
207 m->m_data += sizeof(struct ip);
208 m->m_len -= sizeof(struct ip);
209 m->m_pkthdr.len -= sizeof(struct ip);
210 ipx = mtod(m, struct ipx *);
211 len = ntohs(ipx->ipx_len);
212 if (len & 1)
213 len++; /* Preserve Garbage Byte */
214 if (ip->ip_len != len) {
215 if (len > ip->ip_len) {
216 ipxipif.if_ierrors++;
217 if (ipxip_badlen)
218 m_freem(ipxip_badlen);
219 ipxip_badlen = m;
220 return;
221 }
222 /* Any extra will be trimmed off by the IPX routines */
223 }
224
225 /*
226 * Deliver to IPX
227 */
228 s = splimp();
229 if (IF_QFULL(ifq)) {
230 IF_DROP(ifq);
231 m_freem(m);
232 splx(s);
233 return;
234 }
235 IF_ENQUEUE(ifq, m);
236 schednetisr(NETISR_IPX);
237 splx(s);
238 return;
239 }
240
241 static int
242 ipxipoutput(ifp, m, dst, rt)
243 struct ifnet *ifp;
244 register struct mbuf *m;
245 struct sockaddr *dst;
246 struct rtentry *rt;
247 {
248 register struct ifnet_en *ifn = (struct ifnet_en *)ifp;
249 register struct ip *ip;
250 register struct route *ro = &(ifn->ifen_route);
251 register int len = 0;
252 register struct ipx *ipx = mtod(m, struct ipx *);
253 int error;
254
255 ifn->ifen_ifnet.if_opackets++;
256 ipxipif.if_opackets++;
257
258 /*
259 * Calculate data length and make space
260 * for IP header.
261 */
262 len = ntohs(ipx->ipx_len);
263 if (len & 1)
264 len++; /* Preserve Garbage Byte */
265 /* following clause not necessary on vax */
266 if (3 & (int)m->m_data) {
267 /* force longword alignment of ip hdr */
268 struct mbuf *m0 = m_gethdr(MT_HEADER, M_DONTWAIT);
269 if (m0 == NULL) {
270 m_freem(m);
271 return (ENOBUFS);
272 }
273 MH_ALIGN(m0, sizeof(struct ip));
274 m0->m_flags = m->m_flags & M_COPYFLAGS;
275 m0->m_next = m;
276 m0->m_len = sizeof(struct ip);
277 m0->m_pkthdr.len = m0->m_len + m->m_len;
278 m->m_flags &= ~M_PKTHDR;
279 m = m0;
280 } else {
281 M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
282 if (m == NULL)
283 return (ENOBUFS);
284 }
285 /*
286 * Fill in IP header.
287 */
288 ip = mtod(m, struct ip *);
289 *(long *)ip = 0;
290 ip->ip_p = IPPROTO_IDP;
291 ip->ip_src = ifn->ifen_src;
292 ip->ip_dst = ifn->ifen_dst;
293 ip->ip_len = (u_short)len + sizeof(struct ip);
294 ip->ip_ttl = MAXTTL;
295
296 /*
297 * Output final datagram.
298 */
299 error = (ip_output(m, (struct mbuf *)NULL, ro, SO_BROADCAST, NULL));
300 if (error) {
301 ifn->ifen_ifnet.if_oerrors++;
302 ifn->ifen_ifnet.if_ierrors = error;
303 }
304 return (error);
305 m_freem(m);
306 return (ENETUNREACH);
307 }
308
309 static void
310 ipxipstart(ifp)
311 struct ifnet *ifp;
312 {
313 panic("ipxip_start called\n");
314 }
315
316 static struct ifreq ifr_ipxip = {"ipxip0"};
317
318 int
319 ipxip_route(so, sopt)
320 struct socket *so;
321 struct sockopt *sopt;
322 {
323 int error;
324 struct ifnet_en *ifn;
325 struct sockaddr_in *src;
326 struct ipxip_req rq;
327 struct sockaddr_ipx *ipx_dst;
328 struct sockaddr_in *ip_dst;
329 struct route ro;
330
331 error = sooptcopyin(sopt, &rq, sizeof rq, sizeof rq);
332 if (error)
333 return (error);
334 ipx_dst = (struct sockaddr_ipx *)&rq.rq_ipx;
335 ip_dst = (struct sockaddr_in *)&rq.rq_ip;
336
337 /*
338 * First, make sure we already have an IPX address:
339 */
340 if (ipx_ifaddr == NULL)
341 return (EADDRNOTAVAIL);
342 /*
343 * Now, determine if we can get to the destination
344 */
345 bzero((caddr_t)&ro, sizeof(ro));
346 ro.ro_dst = *(struct sockaddr *)ip_dst;
347 rtalloc(&ro);
348 if (ro.ro_rt == NULL || ro.ro_rt->rt_ifp == NULL) {
349 return (ENETUNREACH);
350 }
351
352 /*
353 * And see how he's going to get back to us:
354 * i.e., what return ip address do we use?
355 */
356 {
357 register struct in_ifaddr *ia;
358 struct ifnet *ifp = ro.ro_rt->rt_ifp;
359
360 for (ia = in_ifaddrhead.tqh_first; ia != NULL;
361 ia = ia->ia_link.tqe_next)
362 if (ia->ia_ifp == ifp)
363 break;
364 if (ia == NULL)
365 ia = in_ifaddrhead.tqh_first;
366 if (ia == NULL) {
367 RTFREE(ro.ro_rt);
368 return (EADDRNOTAVAIL);
369 }
370 src = (struct sockaddr_in *)&ia->ia_addr;
371 }
372
373 /*
374 * Is there a free (pseudo-)interface or space?
375 */
376 for (ifn = ipxip_list; ifn != NULL; ifn = ifn->ifen_next) {
377 if ((ifn->ifen_ifnet.if_flags & IFF_UP) == 0)
378 break;
379 }
380 if (ifn == NULL)
381 ifn = ipxipattach();
382 if (ifn == NULL) {
383 RTFREE(ro.ro_rt);
384 return (ENOBUFS);
385 }
386 ifn->ifen_route = ro;
387 ifn->ifen_dst = ip_dst->sin_addr;
388 ifn->ifen_src = src->sin_addr;
389
390 /*
391 * now configure this as a point to point link
392 */
393 ifr_ipxip.ifr_name[4] = '' + ipxipif.if_unit - 1;
394 ifr_ipxip.ifr_dstaddr = *(struct sockaddr *)ipx_dst;
395 ipx_control(so, (int)SIOCSIFDSTADDR, (caddr_t)&ifr_ipxip,
396 (struct ifnet *)ifn, sopt->sopt_p);
397
398 /* use any of our addresses */
399 satoipx_addr(ifr_ipxip.ifr_addr).x_host =
400 ipx_ifaddr->ia_addr.sipx_addr.x_host;
401
402 return (ipx_control(so, (int)SIOCSIFADDR, (caddr_t)&ifr_ipxip,
403 (struct ifnet *)ifn, sopt->sopt_p));
404 }
405
406 static int
407 ipxip_free(ifp)
408 struct ifnet *ifp;
409 {
410 register struct ifnet_en *ifn = (struct ifnet_en *)ifp;
411 struct route *ro = & ifn->ifen_route;
412
413 if (ro->ro_rt != NULL) {
414 RTFREE(ro->ro_rt);
415 ro->ro_rt = NULL;
416 }
417 ifp->if_flags &= ~IFF_UP;
418 return (0);
419 }
420
421 void
422 ipxip_ctlinput(cmd, sa, dummy)
423 int cmd;
424 struct sockaddr *sa;
425 void *dummy;
426 {
427 struct sockaddr_in *sin;
428
429 if ((unsigned)cmd >= PRC_NCMDS)
430 return;
431 if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK)
432 return;
433 sin = (struct sockaddr_in *)sa;
434 if (sin->sin_addr.s_addr == INADDR_ANY)
435 return;
436
437 switch (cmd) {
438
439 case PRC_ROUTEDEAD:
440 case PRC_REDIRECT_NET:
441 case PRC_REDIRECT_HOST:
442 case PRC_REDIRECT_TOSNET:
443 case PRC_REDIRECT_TOSHOST:
444 ipxip_rtchange(&sin->sin_addr);
445 break;
446 }
447 }
448
449 static void
450 ipxip_rtchange(dst)
451 register struct in_addr *dst;
452 {
453 register struct ifnet_en *ifn;
454
455 for (ifn = ipxip_list; ifn != NULL; ifn = ifn->ifen_next) {
456 if (ifn->ifen_dst.s_addr == dst->s_addr &&
457 ifn->ifen_route.ro_rt != NULL) {
458 RTFREE(ifn->ifen_route.ro_rt);
459 ifn->ifen_route.ro_rt = NULL;
460 }
461 }
462 }
463 #endif /* IPXIP */
Cache object: 0ebbc00b7f60a0aa3d52a978eb28fabe
|