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