1 /*	$OpenBSD: ipx_input.c,v 1.19 2004/06/20 21:33:00 tholo 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_input.c
35  *
36  * from FreeBSD Id: ipx_input.c,v 1.8 1996/03/11 15:13:48 davidg Exp
37  */
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/mbuf.h>
43 #include <sys/protosw.h>
44 #include <sys/socket.h>
45 #include <sys/socketvar.h>
46 
47 #include <net/if.h>
48 #include <net/route.h>
49 #include <net/netisr.h>
50 #include <dev/rndvar.h>
51 
52 #include <netipx/ipx.h>
53 #include <netipx/spx.h>
54 #include <netipx/ipx_if.h>
55 #include <netipx/ipx_pcb.h>
56 #include <netipx/ipx_var.h>
57 
58 #ifndef IPXPRINTFS
59 #define IPXPRINTFS	1	/* printing forwarding information */
60 #endif
61 
62 int ipxprintfs = IPXPRINTFS;
63 int ipxcksum = 0;
64 int ipxdonosocks = 0;
65 int ipxforwarding = 0;
66 int ipxnetbios = 0;
67 
68 union ipx_net	ipx_zeronet;
69 union ipx_host	ipx_zerohost;
70 
71 union ipx_net	ipx_broadnet;
72 union ipx_host	ipx_broadhost;
73 
74 struct ipxstat ipxstat;
75 struct sockaddr_ipx ipx_netmask, ipx_hostmask;
76 
77 static u_int16_t	allones[] = {0xffff, 0xffff, 0xffff};
78 
79 #ifndef	IPXCBHASHSIZE
80 #define	IPXCBHASHSIZE	32
81 #endif
82 struct ipxpcbtable	ipxrawcbtable;
83 int			ipxrawcbhashsize = IPXCBHASHSIZE;
84 
85 struct ipx_ifaddrhead	ipx_ifaddr;
86 struct ifqueue	ipxintrq;
87 int	ipxqmaxlen = IFQ_MAXLEN;
88 
89 u_long	ipx_pexseq;
90 
91 /*
92  * IPX initialization.
93  */
94 
95 void
ipx_init()96 ipx_init()
97 {
98 	ipx_broadnet = * (union ipx_net *) allones;
99 	ipx_broadhost = * (union ipx_host *) allones;
100 
101 	ipx_pexseq = arc4random();
102 	ipxintrq.ifq_maxlen = ipxqmaxlen;
103 
104 	ipx_netmask.sipx_len = 6;
105 	ipx_netmask.sipx_addr.ipx_net = ipx_broadnet;
106 
107 	ipx_hostmask.sipx_len = 12;
108 	ipx_hostmask.sipx_addr.ipx_net = ipx_broadnet;
109 	ipx_hostmask.sipx_addr.ipx_host = ipx_broadhost;
110 
111 	ipx_pcbinit(&ipxrawcbtable, ipxrawcbhashsize);
112 
113 	TAILQ_INIT(&ipx_ifaddr);
114 }
115 
116 /*
117  * IPX input routine.  Pass to next level.
118  */
119 void
ipxintr()120 ipxintr()
121 {
122 	struct ipx *ipx;
123 	struct mbuf *m;
124 	struct ipxpcb *ipxp;
125 	struct ipx_ifaddr *ia;
126 	int len, s;
127 
128 next:
129 	/*
130 	 * Get next datagram off input queue and get IPX header
131 	 * in first mbuf.
132 	 */
133 	s = splimp();
134 	IF_DEQUEUE(&ipxintrq, m);
135 	splx(s);
136 	if (m == NULL) {
137 		return;
138 	}
139 
140 	ipxstat.ipxs_total++;
141 
142 	if ((m->m_flags & M_EXT || m->m_len < sizeof(struct ipx)) &&
143 	    (m = m_pullup(m, sizeof(struct ipx))) == 0) {
144 		ipxstat.ipxs_toosmall++;
145 		goto next;
146 	}
147 
148 	/*
149 	 * Give any raw listeners a crack at the packet
150 	 */
151 	for (ipxp = ipxrawcbtable.ipxpt_queue.cqh_first;
152 	     ipxp != (struct ipxpcb *)&ipxrawcbtable.ipxpt_queue;
153 	     ipxp = ipxp->ipxp_queue.cqe_next) {
154 		struct mbuf *m1 = m_copy(m, 0, (int)M_COPYALL);
155 		if (m1)
156 			ipx_input(m1, ipxp);
157 	}
158 
159 	ipx = mtod(m, struct ipx *);
160 	len = ntohs(ipx->ipx_len);
161 	/*
162 	 * Check that the amount of data in the buffers
163 	 * is as at least much as the IPX header would have us expect.
164 	 * Trim mbufs if longer than we expect.
165 	 * Drop packet if shorter than we expect.
166 	 */
167 	if (m->m_pkthdr.len < len) {
168 		ipxstat.ipxs_tooshort++;
169 		goto bad;
170 	}
171 	if (m->m_pkthdr.len > len) {
172 		if (m->m_len == m->m_pkthdr.len) {
173 			m->m_len = len;
174 			m->m_pkthdr.len = len;
175 		} else
176 			m_adj(m, len - m->m_pkthdr.len);
177 	}
178 	if (ipxcksum && ipx->ipx_sum != 0xffff) {
179 		if (ipx->ipx_sum != ipx_cksum(m, len)) {
180 			ipxstat.ipxs_badsum++;
181 			goto bad;
182 		}
183 	}
184 
185 	/*
186 	 * Propagated (Netbios) packets (type 20) has to be handled
187 	 * different. :-(
188 	 */
189 	if (ipx->ipx_pt == IPXPROTO_NETBIOS) {
190 		if (ipxnetbios) {
191 			ipx_output_type20(m);
192 			goto next;
193 		} else
194 			goto bad;
195 	}
196 
197 	/*
198 	 * Is this a directed broadcast?
199 	 */
200 	if (ipx_hosteqnh(ipx_broadhost,ipx->ipx_dna.ipx_host)) {
201 		if ((!ipx_neteq(ipx->ipx_dna, ipx->ipx_sna)) &&
202 		    (!ipx_neteqnn(ipx->ipx_dna.ipx_net, ipx_broadnet)) &&
203 		    (!ipx_neteqnn(ipx->ipx_sna.ipx_net, ipx_zeronet)) &&
204 		    (!ipx_neteqnn(ipx->ipx_dna.ipx_net, ipx_zeronet)) ) {
205 			/*
206 			 * If it is a broadcast to the net where it was
207 			 * received from, treat it as ours.
208 			 */
209 			for (ia = ipx_ifaddr.tqh_first; ia;
210 			    ia = ia->ia_list.tqe_next)
211 				if((ia->ia_ifa.ifa_ifp == m->m_pkthdr.rcvif) &&
212 				    ipx_neteq(ia->ia_addr.sipx_addr,
213 				    ipx->ipx_dna))
214                                        goto ours;
215 
216 			/*
217 			 * Look to see if I need to eat this packet.
218 			 * Algorithm is to forward all young packets
219 			 * and prematurely age any packets which will
220 			 * by physically broadcasted.
221 			 * Any very old packets eaten without forwarding
222 			 * would die anyway.
223 			 *
224 			 * Suggestion of Bill Nesheim, Cornell U.
225 			 */
226 			if (ipx->ipx_tc < IPX_MAXHOPS) {
227 				ipx_forward(m);
228 				goto next;
229 			}
230 		}
231 	/*
232 	 * Is this our packet? If not, forward.
233 	 */
234 	} else {
235 		for (ia = ipx_ifaddr.tqh_first; ia; ia = ia->ia_list.tqe_next)
236 			if (ipx_hosteq(ipx->ipx_dna, ia->ia_addr.sipx_addr) &&
237 			    (ipx_neteq(ipx->ipx_dna, ia->ia_addr.sipx_addr) ||
238 			    ipx_neteqnn(ipx->ipx_dna.ipx_net, ipx_zeronet)))
239 				break;
240 
241 		if (ia == NULL) {
242 			ipx_forward(m);
243 			goto next;
244 		}
245 	}
246 ours:
247 	/*
248 	 * Locate pcb for datagram.
249 	 */
250 	ipxp = ipx_pcblookup(&ipx->ipx_sna, ipx->ipx_dna.ipx_port,
251 	    IPX_WILDCARD);
252 	/*
253 	 * Switch out to protocol's input routine.
254 	 */
255 	if (ipxp) {
256 		ipxstat.ipxs_delivered++;
257 		if ((ipxp->ipxp_flags & IPXP_ALL_PACKETS) == 0)
258 			switch (ipx->ipx_pt) {
259 
260 			    case IPXPROTO_SPX:
261 				    spx_input(m, ipxp);
262 				    goto next;
263 			}
264 		ipx_input(m, ipxp);
265 	} else
266 		goto bad;
267 
268 	goto next;
269 
270 bad:
271 	m_freem(m);
272 	goto next;
273 }
274 
275 void *
ipx_ctlinput(cmd,arg_as_sa,dummy)276 ipx_ctlinput(cmd, arg_as_sa, dummy)
277 	int cmd;
278 	struct sockaddr *arg_as_sa;	/* XXX should be swapped with dummy */
279 	void *dummy;
280 {
281 	caddr_t arg = (/* XXX */ caddr_t)arg_as_sa;
282 	struct ipx_addr *ipx;
283 
284 	if (cmd < 0 || cmd >= PRC_NCMDS)
285 		return NULL;
286 	switch (cmd) {
287 		struct sockaddr_ipx *sipx;
288 
289 	case PRC_IFDOWN:
290 	case PRC_HOSTDEAD:
291 	case PRC_HOSTUNREACH:
292 		sipx = (struct sockaddr_ipx *)arg;
293 		if (sipx == NULL || sipx->sipx_family != AF_IPX)
294 			return NULL;
295 		ipx = &sipx->sipx_addr;
296 		break;
297 
298 	default:
299 		break;
300 	}
301 	return NULL;
302 }
303 
304 /*
305  * Forward a packet.  If some error occurs return the sender
306  * an error packet.  Note we can't always generate a meaningful
307  * error message because the IPX errors don't have a large enough repetoire
308  * of codes and types.
309  */
310 struct route ipx_droute;
311 struct route ipx_sroute;
312 
313 void
ipx_forward(m)314 ipx_forward(m)
315 struct mbuf *m;
316 {
317 	struct ipx *ipx = mtod(m, struct ipx *);
318 	int error;
319 	int agedelta = 1;
320 	int flags = IPX_FORWARDING;
321 	int ok_there = 0;
322 	int ok_back = 0;
323 
324 	if (ipxforwarding == 0) {
325 		ipxstat.ipxs_cantforward++;
326 		m_freem(m);
327 		goto cleanup;
328 	}
329 	ipx->ipx_tc++;
330 	if (ipx->ipx_tc > IPX_MAXHOPS) {
331 		ipxstat.ipxs_cantforward++;
332 		m_freem(m);
333 		goto cleanup;
334 	}
335 
336 	if ((ok_there = ipx_do_route(&ipx->ipx_dna,&ipx_droute)) == 0) {
337 		ipxstat.ipxs_noroute++;
338 		m_freem(m);
339 		goto cleanup;
340 	}
341 	/*
342 	 * Here we think about  forwarding  broadcast packets,
343 	 * so we try to insure that it doesn't go back out
344 	 * on the interface it came in on.  Also, if we
345 	 * are going to physically broadcast this, let us
346 	 * age the packet so we can eat it safely the second time around.
347 	 */
348 	if (ipx->ipx_dna.ipx_host.c_host[0] & 0x1) {
349 		struct ipx_ifaddr *ia = ipx_iaonnetof(&ipx->ipx_dna);
350 		struct ifnet *ifp;
351 		if (ia) {
352 			/* I'm gonna hafta eat this packet */
353 			agedelta += IPX_MAXHOPS - ipx->ipx_tc;
354 			ipx->ipx_tc = IPX_MAXHOPS;
355 		}
356 		if ((ok_back = ipx_do_route(&ipx->ipx_sna,&ipx_sroute)) == 0) {
357 			/* error = ENETUNREACH; He'll never get it! */
358 			ipxstat.ipxs_noroute++;
359 			m_freem(m);
360 			goto cleanup;
361 		}
362 		if (ipx_droute.ro_rt &&
363 		    (ifp=ipx_droute.ro_rt->rt_ifp) &&
364 		    ipx_sroute.ro_rt &&
365 		    (ifp!=ipx_sroute.ro_rt->rt_ifp)) {
366 			flags |= IPX_ALLOWBROADCAST;
367 		} else {
368 			ipxstat.ipxs_noroute++;
369 			m_freem(m);
370 			goto cleanup;
371 		}
372 	}
373 	/*
374 	 * We don't need to recompute checksum because ipx_tc field
375 	 * is ignored by checksum calculation routine, however
376 	 * it may be desirable to reset checksum if ipxcksum == 0
377 	 */
378 #if 0
379 	if (!ipxcksum)
380 		ipx->ipx_sum = 0xffff;
381 #endif
382 
383 	error = ipx_outputfl(m, &ipx_droute, flags);
384 	if (error == 0) {
385 		ipxstat.ipxs_forward++;
386 
387 		if (ipxprintfs) {
388 			printf("forward: ");
389 			ipx_printhost(&ipx->ipx_sna);
390 			printf(" to ");
391 			ipx_printhost(&ipx->ipx_dna);
392 			printf(" hops %d\n", ipx->ipx_tc);
393 		}
394 	} else {
395 		switch (error) {
396 
397 		case ENETUNREACH:
398 		case EHOSTDOWN:
399 		case EHOSTUNREACH:
400 		case ENETDOWN:
401 		case EPERM:
402 			ipxstat.ipxs_noroute++;
403 			break;
404 
405 		case EMSGSIZE:
406 			ipxstat.ipxs_mtutoosmall++;
407 			break;
408 
409 		case ENOBUFS:
410 			ipxstat.ipxs_odropped++;
411 			break;
412 		}
413 		m_freem(m);
414 	}
415 cleanup:
416 	if (ok_there)
417 		ipx_undo_route(&ipx_droute);
418 	if (ok_back)
419 		ipx_undo_route(&ipx_sroute);
420 }
421 
422 int
ipx_do_route(src,ro)423 ipx_do_route(src, ro)
424 struct ipx_addr *src;
425 struct route *ro;
426 {
427 	struct sockaddr_ipx *dst;
428 
429 	bzero((caddr_t)ro, sizeof(*ro));
430 	dst = (struct sockaddr_ipx *)&ro->ro_dst;
431 
432 	dst->sipx_len = sizeof(*dst);
433 	dst->sipx_family = AF_IPX;
434 	dst->sipx_addr = *src;
435 	dst->sipx_addr.ipx_port = 0;
436 	rtalloc(ro);
437 	if (ro->ro_rt == 0 || ro->ro_rt->rt_ifp == 0) {
438 		return (0);
439 	}
440 	ro->ro_rt->rt_use++;
441 	return (1);
442 }
443 
444 void
ipx_undo_route(ro)445 ipx_undo_route(ro)
446 register struct route *ro;
447 {
448 	if (ro->ro_rt) {
449 		RTFREE(ro->ro_rt);
450 	}
451 }
452 
453 void
ipx_watch_output(m,ifp)454 ipx_watch_output(m, ifp)
455 struct mbuf *m;
456 struct ifnet *ifp;
457 {
458 	struct ipxpcb *ipxp;
459 	struct ifaddr *ifa;
460 	struct ipx_ifaddr *ia;
461 	/*
462 	 * Give any raw listeners a crack at the packet
463 	 */
464 	for (ipxp = ipxrawcbtable.ipxpt_queue.cqh_first;
465 	    ipxp != (struct ipxpcb *)&ipxrawcbtable.ipxpt_queue;
466 	    ipxp = ipxp->ipxp_queue.cqe_next) {
467 		struct mbuf *m0 = m_copy(m, 0, (int)M_COPYALL);
468 		if (m0) {
469 			struct ipx *ipx;
470 
471 			M_PREPEND(m0, sizeof(*ipx), M_DONTWAIT);
472 			if (m0 == NULL)
473 				continue;
474 			ipx = mtod(m0, struct ipx *);
475 			ipx->ipx_sna.ipx_net = ipx_zeronet;
476 			for (ia = ipx_ifaddr.tqh_first; ia;
477 			    ia = ia->ia_list.tqe_next)
478 				if (ifp == ia->ia_ifp)
479 					break;
480 			if (ia == NULL)
481 				ipx->ipx_sna.ipx_host = ipx_zerohost;
482 			else
483 				ipx->ipx_sna.ipx_host =
484 				    ia->ia_addr.sipx_addr.ipx_host;
485 
486 			if (ifp && (ifp->if_flags & IFF_POINTOPOINT))
487 			    for(ifa = ifp->if_addrlist.tqh_first; ifa;
488 					ifa = ifa->ifa_list.tqe_next) {
489 				if (ifa->ifa_addr->sa_family == AF_IPX) {
490 				    ipx->ipx_sna = IA_SIPX(ifa)->sipx_addr;
491 				    break;
492 				}
493 			    }
494 			ipx->ipx_len = ntohl(m0->m_pkthdr.len);
495 			ipx_input(m0, ipxp);
496 		}
497 	}
498 }
499