1 /* $OpenBSD: ipx_ip.c,v 1.19 2004/09/20 23:10:47 drahn Exp $ */
2
3 /*-
4 *
5 * Copyright (c) 1996 Michael Shalayeff
6 * Copyright (c) 1995, Mike Mitchell
7 * Copyright (c) 1984, 1985, 1986, 1987, 1993
8 * The Regents of the University of California. All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. 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 * from FreeBSD Id: ipx_ip.c,v 1.7 1996/03/11 15:13:50 davidg Exp
37 */
38
39 /*
40 * Software interface driver for encapsulating IPX in IP.
41 */
42
43 #ifdef IPXIP
44 #ifndef INET
45 #error The option IPXIP requires option INET.
46 #endif
47
48 #include <sys/param.h>
49 #include <sys/queue.h>
50 #include <sys/systm.h>
51 #include <sys/malloc.h>
52 #include <sys/mbuf.h>
53 #include <sys/socket.h>
54 #include <sys/socketvar.h>
55 #include <sys/errno.h>
56 #include <sys/ioctl.h>
57 #include <sys/protosw.h>
58
59 #include <net/if.h>
60 #include <net/if_types.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
74 #include <sys/stdarg.h>
75
76 struct ifnet ipxipif;
77 struct ifnet_en *ipxip_list; /* list of all hosts and gateways or broadcast addrs */
78
79 void
ipxipprotoinit(void)80 ipxipprotoinit(void)
81 {
82 (void) ipxipattach();
83 }
84
85 struct ifnet_en *
ipxipattach(void)86 ipxipattach(void)
87 {
88 struct ifnet_en *m;
89 struct ifnet *ifp;
90
91 if (ipxipif.if_mtu == 0) {
92 ifp = &ipxipif;
93 snprintf(ifp->if_xname, sizeof ifp->if_xname, "ipx0");
94 ifp->if_mtu = LOMTU;
95 ifp->if_ioctl = ipxipioctl;
96 ifp->if_output = ipxipoutput;
97 ifp->if_start = ipxipstart;
98 ifp->if_flags = IFF_POINTOPOINT;
99 ifp->if_type = IFT_NSIP;
100 }
101
102 MALLOC((m), struct ifnet_en *, sizeof(*m), M_PCB, M_NOWAIT);
103 if (m == NULL)
104 return (NULL);
105 bzero(m, sizeof(*m));
106 m->ifen_next = ipxip_list;
107 ipxip_list = m;
108 ifp = &m->ifen_ifnet;
109
110 snprintf(ifp->if_xname, sizeof ifp->if_xname, "ipx0");
111 ifp->if_mtu = LOMTU;
112 ifp->if_ioctl = ipxipioctl;
113 ifp->if_output = ipxipoutput;
114 ifp->if_start = ipxipstart;
115 ifp->if_flags = IFF_POINTOPOINT;
116 if_attach(ifp);
117
118 return (m);
119 }
120
121
122 /*
123 * Process an ioctl request.
124 */
125 /* ARGSUSED */
126 int
ipxipioctl(ifp,cmd,data)127 ipxipioctl(ifp, cmd, data)
128 struct ifnet *ifp;
129 u_long cmd;
130 caddr_t data;
131 {
132 int error = 0;
133 struct ifreq *ifr;
134
135 switch (cmd) {
136
137 case SIOCSIFADDR:
138 ifp->if_flags |= IFF_UP;
139 /* FALLTHROUGH */
140
141 case SIOCSIFDSTADDR:
142 /*
143 * Everything else is done at a higher level.
144 */
145 break;
146
147 case SIOCSIFFLAGS:
148 ifr = (struct ifreq *)data;
149 if ((ifr->ifr_flags & IFF_UP) == 0)
150 error = ipxip_free(ifp);
151
152 default:
153 error = EINVAL;
154 }
155 return (error);
156 }
157
158 struct mbuf *ipxip_badlen;
159
160 void
ipxip_input(struct mbuf * m,...)161 ipxip_input( struct mbuf *m, ...)
162 {
163 struct ifnet *ifp;
164 struct ip *ip;
165 struct ipx *ipx;
166 struct ifqueue *ifq = &ipxintrq;
167 int len, s;
168 va_list ap;
169
170 va_start(ap, m);
171 ifp = va_arg(ap, struct ifnet *);
172 va_end(ap);
173
174 /*
175 * Get IP and IPX header together in first mbuf.
176 */
177 ipxipif.if_ipackets++;
178 s = sizeof(struct ip) + sizeof(struct ipx);
179 if (((m->m_flags & M_EXT) || m->m_len < s) &&
180 (m = m_pullup(m, s)) == NULL) {
181 ipxipif.if_ierrors++;
182 return;
183 }
184 ip = mtod(m, struct ip *);
185 if (ip->ip_hl > (sizeof(struct ip) >> 2)) {
186 ip_stripoptions(m, (struct mbuf *)0);
187 if (m->m_len < s) {
188 if ((m = m_pullup(m, s)) == NULL) {
189 ipxipif.if_ierrors++;
190 return;
191 }
192 ip = mtod(m, struct ip *);
193 }
194 }
195
196 /*
197 * Make mbuf data length reflect IPX length.
198 * If not enough data to reflect IPX length, drop.
199 */
200 m->m_data += sizeof(struct ip);
201 m->m_len -= sizeof(struct ip);
202 m->m_pkthdr.len -= sizeof(struct ip);
203 ipx = mtod(m, struct ipx *);
204 len = ntohs(ipx->ipx_len);
205 if (len & 1)
206 len++; /* Preserve Garbage Byte */
207 if (ntohs(ip->ip_len) - (ip->ip_hl << 2) != len) {
208 if (len > ntohs(ip->ip_len) - (ip->ip_hl << 2)) {
209 ipxipif.if_ierrors++;
210 if (ipxip_badlen)
211 m_freem(ipxip_badlen);
212 ipxip_badlen = m;
213 return;
214 }
215 /* Any extra will be trimmed off by the IPX routines */
216 }
217
218 /*
219 * Place interface pointer before the data
220 * for the receiving protocol.
221 */
222 m->m_pkthdr.rcvif = ifp;
223 /*
224 * Deliver to IPX
225 */
226 s = splimp();
227 if (IF_QFULL(ifq)) {
228 IF_DROP(ifq);
229 m_freem(m);
230 splx(s);
231 return;
232 }
233 IF_ENQUEUE(ifq, m);
234 schednetisr(NETISR_IPX);
235 splx(s);
236 return;
237 }
238
239 /* ARGSUSED */
240 int
ipxipoutput(ifp,m,dst,rt)241 ipxipoutput(ifp, m, dst, rt)
242 struct ifnet *ifp;
243 struct mbuf *m;
244 struct sockaddr *dst;
245 struct rtentry *rt;
246 {
247 struct ifnet_en *ifn = (struct ifnet_en *)ifp;
248 struct ip *ip;
249 struct route *ro = &(ifn->ifen_route);
250 int len = 0;
251 struct ipx *ipx = mtod(m, struct ipx *);
252 int error;
253
254 ifn->ifen_ifnet.if_opackets++;
255 ipxipif.if_opackets++;
256
257 /*
258 * Calculate data length and make space
259 * for IP header.
260 */
261 len = ntohs(ipx->ipx_len);
262 if (len & 1)
263 len++; /* Preserve Garbage Byte */
264 /* following clause not necessary on vax */
265 if (3 & (long)m->m_data) {
266 /* force longword alignment of ip hdr */
267 struct mbuf *m0 = m_gethdr(M_DONTWAIT, MT_HEADER);
268 if (m0 == NULL) {
269 m_freem(m);
270 return (ENOBUFS);
271 }
272 MH_ALIGN(m0, sizeof(struct ip));
273 m0->m_flags = m->m_flags & M_COPYFLAGS;
274 m0->m_next = m;
275 m0->m_len = sizeof(struct ip);
276 m0->m_pkthdr.len = m0->m_len + m->m_len;
277 m0->m_pkthdr.tags = m->m_pkthdr.tags;
278 m->m_flags &= ~M_PKTHDR;
279 m_tag_init(m);
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 if (len + sizeof(struct ip) > IP_MAXPACKET) {
294 m_freem(m);
295 return EMSGSIZE;
296 }
297 ip->ip_len = htons(len + sizeof(struct ip));
298 ip->ip_ttl = MAXTTL;
299
300 /*
301 * Output final datagram.
302 */
303 error = ip_output(m, NULL, ro, SO_BROADCAST, NULL, NULL, NULL);
304 if (error) {
305 ifn->ifen_ifnet.if_oerrors++;
306 ifn->ifen_ifnet.if_ierrors = error;
307 }
308 return (error);
309 }
310
311 void
ipxipstart(ifp)312 ipxipstart(ifp)
313 struct ifnet *ifp;
314 {
315 panic("ipxip_start called");
316 }
317
318 struct ifreq ifr_ipxip = {"ipx0"};
319
320 int
ipxip_route(m)321 ipxip_route(m)
322 struct mbuf *m;
323 {
324 struct ipxip_req *rq = mtod(m, struct ipxip_req *);
325 struct sockaddr_ipx *ipx_dst = (struct sockaddr_ipx *)&rq->rq_ipx;
326 struct sockaddr_in *ip_dst = (struct sockaddr_in *)&rq->rq_ip;
327 struct route ro;
328 struct ifnet_en *ifn;
329 struct sockaddr_in *src;
330
331 /*
332 * First, make sure we already have an IPX address.
333 */
334 if (ipx_ifaddr.tqh_first == NULL)
335 return (EADDRNOTAVAIL);
336 /*
337 * Now, determine if we can get to the destination
338 */
339 bzero((caddr_t)&ro, sizeof(ro));
340 ro.ro_dst = *(struct sockaddr *)ip_dst;
341 rtalloc(&ro);
342 if (ro.ro_rt == NULL || ro.ro_rt->rt_ifp == NULL) {
343 return (ENETUNREACH);
344 }
345
346 /*
347 * And see how he's going to get back to us:
348 * i.e., what return ip address do we use?
349 */
350 {
351 struct in_ifaddr *ia;
352 struct ifnet *ifp = ro.ro_rt->rt_ifp;
353
354 for (ia = in_ifaddr.tqh_first; ia; ia = ia->ia_list.tqe_next)
355 if (ia->ia_ifp == ifp)
356 break;
357 if (ia == NULL)
358 ia = in_ifaddr.tqh_first;
359 if (ia == NULL) {
360 RTFREE(ro.ro_rt);
361 return (EADDRNOTAVAIL);
362 }
363 src = (struct sockaddr_in *)&ia->ia_addr;
364 }
365
366 /*
367 * Is there a free (pseudo-)interface or space?
368 */
369 for (ifn = ipxip_list; ifn; ifn = ifn->ifen_next) {
370 if ((ifn->ifen_ifnet.if_flags & IFF_UP) == 0)
371 break;
372 }
373 if (ifn == NULL)
374 ifn = ipxipattach();
375 if (ifn == NULL) {
376 RTFREE(ro.ro_rt);
377 return (ENOBUFS);
378 }
379 ifn->ifen_route = ro;
380 ifn->ifen_dst = ip_dst->sin_addr;
381 ifn->ifen_src = src->sin_addr;
382
383 /*
384 * now configure this as a point to point link
385 */
386 ifr_ipxip.ifr_dstaddr = * (struct sockaddr *) ipx_dst;
387 ipx_control((struct socket *)0, (int)SIOCSIFDSTADDR,
388 (caddr_t)&ifr_ipxip, (struct ifnet *)ifn);
389
390 satoipx_addr(ifr_ipxip.ifr_addr).ipx_host =
391 ipx_ifaddr.tqh_first->ia_addr.sipx_addr.ipx_host;
392
393 return (ipx_control((struct socket *)0, (int)SIOCSIFADDR,
394 (caddr_t)&ifr_ipxip, (struct ifnet *)ifn));
395 }
396
397 int
ipxip_free(ifp)398 ipxip_free(ifp)
399 struct ifnet *ifp;
400 {
401 struct ifnet_en *ifn = (struct ifnet_en *)ifp;
402 struct route *ro = & ifn->ifen_route;
403
404 if (ro->ro_rt) {
405 RTFREE(ro->ro_rt);
406 ro->ro_rt = NULL;
407 }
408 ifp->if_flags &= ~IFF_UP;
409 return (0);
410 }
411
412 void *
ipxip_ctlinput(cmd,sa,dummy)413 ipxip_ctlinput(cmd, sa, dummy)
414 int cmd;
415 struct sockaddr *sa;
416 void *dummy;
417 {
418 struct sockaddr_in *sin;
419
420 if ((unsigned)cmd >= PRC_NCMDS)
421 return NULL;
422 if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK)
423 return NULL;
424 sin = (struct sockaddr_in *)sa;
425 if (sin->sin_addr.s_addr == INADDR_ANY)
426 return NULL;
427
428 switch (cmd) {
429
430 case PRC_ROUTEDEAD:
431 case PRC_REDIRECT_NET:
432 case PRC_REDIRECT_HOST:
433 case PRC_REDIRECT_TOSNET:
434 case PRC_REDIRECT_TOSHOST:
435 ipxip_rtchange(&sin->sin_addr);
436 break;
437 }
438 return NULL;
439 }
440
441 void
ipxip_rtchange(dst)442 ipxip_rtchange(dst)
443 struct in_addr *dst;
444 {
445 struct ifnet_en *ifn;
446
447 for (ifn = ipxip_list; ifn; ifn = ifn->ifen_next) {
448 if (ifn->ifen_dst.s_addr == dst->s_addr &&
449 ifn->ifen_route.ro_rt) {
450 RTFREE(ifn->ifen_route.ro_rt);
451 ifn->ifen_route.ro_rt = NULL;
452 }
453 }
454 }
455 #endif /* IPXIP */
456