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