]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/netipx/ipx_input.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / netipx / ipx_input.c
1 /*-
2  * Copyright (c) 1984, 1985, 1986, 1987, 1993
3  *      The Regents of the University of California.
4  * Copyright (c) 2004-2005 Robert N. M. Watson
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 4. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * Copyright (c) 1995, Mike Mitchell
32  * All rights reserved.
33  *
34  * Redistribution and use in source and binary forms, with or without
35  * modification, are permitted provided that the following conditions
36  * are met:
37  * 1. Redistributions of source code must retain the above copyright
38  *    notice, this list of conditions and the following disclaimer.
39  * 2. Redistributions in binary form must reproduce the above copyright
40  *    notice, this list of conditions and the following disclaimer in the
41  *    documentation and/or other materials provided with the distribution.
42  * 3. All advertising materials mentioning features or use of this software
43  *    must display the following acknowledgement:
44  *      This product includes software developed by the University of
45  *      California, Berkeley and its contributors.
46  * 4. Neither the name of the University nor the names of its contributors
47  *    may be used to endorse or promote products derived from this software
48  *    without specific prior written permission.
49  *
50  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
51  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
55  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
56  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
58  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
59  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60  * SUCH DAMAGE.
61  *
62  *      @(#)ipx_input.c
63  */
64
65 #include <sys/cdefs.h>
66 __FBSDID("$FreeBSD$");
67
68 #include <sys/param.h>
69 #include <sys/systm.h>
70 #include <sys/mbuf.h>
71 #include <sys/protosw.h>
72 #include <sys/socket.h>
73 #include <sys/kernel.h>
74 #include <sys/random.h>
75 #include <sys/sysctl.h>
76
77 #include <net/if.h>
78 #include <net/route.h>
79 #include <net/netisr.h>
80
81 #include <netipx/ipx.h>
82 #include <netipx/spx.h>
83 #include <netipx/ipx_if.h>
84 #include <netipx/ipx_pcb.h>
85 #include <netipx/ipx_var.h>
86
87 int     ipxcksum = 0;
88 SYSCTL_INT(_net_ipx_ipx, OID_AUTO, checksum, CTLFLAG_RW,
89            &ipxcksum, 0, "");
90
91 static int      ipxprintfs = 0;         /* printing forwarding information */
92 SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxprintfs, CTLFLAG_RW,
93            &ipxprintfs, 0, "");
94
95 static int      ipxforwarding = 0;
96 SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxforwarding, CTLFLAG_RW,
97             &ipxforwarding, 0, "");
98
99 static int      ipxnetbios = 0;
100 SYSCTL_INT(_net_ipx, OID_AUTO, ipxnetbios, CTLFLAG_RW,
101            &ipxnetbios, 0, "");
102
103 const union     ipx_net ipx_zeronet;
104 const union     ipx_host ipx_zerohost;
105
106 const union     ipx_net ipx_broadnet = { .s_net[0] = 0xffff,
107                                             .s_net[1] = 0xffff };
108 const union     ipx_host ipx_broadhost = { .s_host[0] = 0xffff,
109                                             .s_host[1] = 0xffff,
110                                             .s_host[2] = 0xffff };
111
112 struct  ipxstat ipxstat;
113 struct  sockaddr_ipx ipx_netmask, ipx_hostmask;
114
115 /*
116  * IPX protocol control block (pcb) lists.
117  */
118 struct mtx              ipxpcb_list_mtx;
119 struct ipxpcbhead       ipxpcb_list;
120 struct ipxpcbhead       ipxrawpcb_list;
121
122 static int ipxqmaxlen = IFQ_MAXLEN;
123 static  struct ifqueue ipxintrq;
124
125 long    ipx_pexseq;             /* Locked with ipxpcb_list_mtx. */
126
127 static  int ipx_do_route(struct ipx_addr *src, struct route *ro);
128 static  void ipx_undo_route(struct route *ro);
129 static  void ipx_forward(struct mbuf *m);
130 static  void ipxintr(struct mbuf *m);
131
132 /*
133  * IPX initialization.
134  */
135
136 void
137 ipx_init(void)
138 {
139
140         read_random(&ipx_pexseq, sizeof ipx_pexseq);
141
142         LIST_INIT(&ipxpcb_list);
143         LIST_INIT(&ipxrawpcb_list);
144
145         IPX_LIST_LOCK_INIT();
146
147         ipx_netmask.sipx_len = 6;
148         ipx_netmask.sipx_addr.x_net = ipx_broadnet;
149
150         ipx_hostmask.sipx_len = 12;
151         ipx_hostmask.sipx_addr.x_net = ipx_broadnet;
152         ipx_hostmask.sipx_addr.x_host = ipx_broadhost;
153
154         ipxintrq.ifq_maxlen = ipxqmaxlen;
155         mtx_init(&ipxintrq.ifq_mtx, "ipx_inq", NULL, MTX_DEF);
156         netisr_register(NETISR_IPX, ipxintr, &ipxintrq, NETISR_MPSAFE);
157 }
158
159 /*
160  * IPX input routine.  Pass to next level.
161  */
162 static void
163 ipxintr(struct mbuf *m)
164 {
165         struct ipx *ipx;
166         struct ipxpcb *ipxp;
167         struct ipx_ifaddr *ia;
168         int len;
169
170         /*
171          * If no IPX addresses have been set yet but the interfaces
172          * are receiving, can't do anything with incoming packets yet.
173          */
174         if (ipx_ifaddr == NULL) {
175                 m_freem(m);
176                 return;
177         }
178
179         ipxstat.ipxs_total++;
180
181         if ((m->m_flags & M_EXT || m->m_len < sizeof(struct ipx)) &&
182             (m = m_pullup(m, sizeof(struct ipx))) == NULL) {
183                 ipxstat.ipxs_toosmall++;
184                 return;
185         }
186
187         /*
188          * Give any raw listeners a crack at the packet
189          */
190         IPX_LIST_LOCK();
191         LIST_FOREACH(ipxp, &ipxrawpcb_list, ipxp_list) {
192                 struct mbuf *m1 = m_copy(m, 0, (int)M_COPYALL);
193                 if (m1 != NULL) {
194                         IPX_LOCK(ipxp);
195                         ipx_input(m1, ipxp);
196                         IPX_UNLOCK(ipxp);
197                 }
198         }
199         IPX_LIST_UNLOCK();
200
201         ipx = mtod(m, struct ipx *);
202         len = ntohs(ipx->ipx_len);
203         /*
204          * Check that the amount of data in the buffers
205          * is as at least much as the IPX header would have us expect.
206          * Trim mbufs if longer than we expect.
207          * Drop packet if shorter than we expect.
208          */
209         if (m->m_pkthdr.len < len) {
210                 ipxstat.ipxs_tooshort++;
211                 m_freem(m);
212                 return;
213         }
214         if (m->m_pkthdr.len > len) {
215                 if (m->m_len == m->m_pkthdr.len) {
216                         m->m_len = len;
217                         m->m_pkthdr.len = len;
218                 } else
219                         m_adj(m, len - m->m_pkthdr.len);
220         }
221         if (ipxcksum && ipx->ipx_sum != 0xffff) {
222                 if (ipx->ipx_sum != ipx_cksum(m, len)) {
223                         ipxstat.ipxs_badsum++;
224                         m_freem(m);
225                         return;
226                 }
227         }
228
229         /*
230          * Propagated (Netbios) packets (type 20) has to be handled
231          * different. :-(
232          */
233         if (ipx->ipx_pt == IPXPROTO_NETBIOS) {
234                 if (ipxnetbios) {
235                         ipx_output_type20(m);
236                         return;
237                 } else {
238                         m_freem(m);
239                         return;
240                 }
241         }
242
243         /*
244          * Is this a directed broadcast?
245          */
246         if (ipx_hosteqnh(ipx_broadhost,ipx->ipx_dna.x_host)) {
247                 if ((!ipx_neteq(ipx->ipx_dna, ipx->ipx_sna)) &&
248                     (!ipx_neteqnn(ipx->ipx_dna.x_net, ipx_broadnet)) &&
249                     (!ipx_neteqnn(ipx->ipx_sna.x_net, ipx_zeronet)) &&
250                     (!ipx_neteqnn(ipx->ipx_dna.x_net, ipx_zeronet)) ) {
251                         /*
252                          * If it is a broadcast to the net where it was
253                          * received from, treat it as ours.
254                          */
255                         for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
256                                 if((ia->ia_ifa.ifa_ifp == m->m_pkthdr.rcvif) &&
257                                    ipx_neteq(ia->ia_addr.sipx_addr,
258                                              ipx->ipx_dna))
259                                         goto ours;
260
261                         /*
262                          * Look to see if I need to eat this packet.
263                          * Algorithm is to forward all young packets
264                          * and prematurely age any packets which will
265                          * by physically broadcasted.
266                          * Any very old packets eaten without forwarding
267                          * would die anyway.
268                          *
269                          * Suggestion of Bill Nesheim, Cornell U.
270                          */
271                         if (ipx->ipx_tc < IPX_MAXHOPS) {
272                                 ipx_forward(m);
273                                 return;
274                         }
275                 }
276         /*
277          * Is this our packet? If not, forward.
278          */
279         } else {
280                 for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
281                         if (ipx_hosteq(ipx->ipx_dna, ia->ia_addr.sipx_addr) &&
282                             (ipx_neteq(ipx->ipx_dna, ia->ia_addr.sipx_addr) ||
283                              ipx_neteqnn(ipx->ipx_dna.x_net, ipx_zeronet)))
284                                 break;
285
286                 if (ia == NULL) {
287                         ipx_forward(m);
288                         return;
289                 }
290         }
291 ours:
292         /*
293          * Locate pcb for datagram.
294          */
295         IPX_LIST_LOCK();
296         ipxp = ipx_pcblookup(&ipx->ipx_sna, ipx->ipx_dna.x_port, IPX_WILDCARD);
297         /*
298          * Switch out to protocol's input routine.
299          */
300         if (ipxp != NULL) {
301                 ipxstat.ipxs_delivered++;
302                 if ((ipxp->ipxp_flags & IPXP_ALL_PACKETS) == 0)
303                         switch (ipx->ipx_pt) {
304                         case IPXPROTO_SPX:
305                                 IPX_LOCK(ipxp);
306                                 /* Will release both locks. */
307                                 spx_input(m, ipxp);
308                                 return;
309                         }
310                 IPX_LOCK(ipxp);
311                 ipx_input(m, ipxp);
312                 IPX_UNLOCK(ipxp);
313         } else
314                 m_freem(m);
315         IPX_LIST_UNLOCK();
316 }
317
318 void
319 ipx_ctlinput(cmd, arg_as_sa, dummy)
320         int cmd;
321         struct sockaddr *arg_as_sa;     /* XXX should be swapped with dummy */
322         void *dummy;
323 {
324
325         /* Currently, nothing. */
326 }
327
328 /*
329  * Forward a packet. If some error occurs drop the packet. IPX don't
330  * have a way to return errors to the sender.
331  */
332
333 static struct route ipx_droute;
334 static struct route ipx_sroute;
335
336 static void
337 ipx_forward(struct mbuf *m)
338 {
339         struct ipx *ipx = mtod(m, struct ipx *);
340         int error;
341         int agedelta = 1;
342         int flags = IPX_FORWARDING;
343         int ok_there = 0;
344         int ok_back = 0;
345
346         if (ipxforwarding == 0) {
347                 /* can't tell difference between net and host */
348                 ipxstat.ipxs_cantforward++;
349                 m_freem(m);
350                 goto cleanup;
351         }
352         ipx->ipx_tc++;
353         if (ipx->ipx_tc > IPX_MAXHOPS) {
354                 ipxstat.ipxs_cantforward++;
355                 m_freem(m);
356                 goto cleanup;
357         }
358
359         if ((ok_there = ipx_do_route(&ipx->ipx_dna,&ipx_droute)) == 0) {
360                 ipxstat.ipxs_noroute++;
361                 m_freem(m);
362                 goto cleanup;
363         }
364         /*
365          * Here we think about  forwarding  broadcast packets,
366          * so we try to insure that it doesn't go back out
367          * on the interface it came in on.  Also, if we
368          * are going to physically broadcast this, let us
369          * age the packet so we can eat it safely the second time around.
370          */
371         if (ipx->ipx_dna.x_host.c_host[0] & 0x1) {
372                 struct ipx_ifaddr *ia = ipx_iaonnetof(&ipx->ipx_dna);
373                 struct ifnet *ifp;
374                 if (ia != NULL) {
375                         /* I'm gonna hafta eat this packet */
376                         agedelta += IPX_MAXHOPS - ipx->ipx_tc;
377                         ipx->ipx_tc = IPX_MAXHOPS;
378                 }
379                 if ((ok_back = ipx_do_route(&ipx->ipx_sna,&ipx_sroute)) == 0) {
380                         /* error = ENETUNREACH; He'll never get it! */
381                         ipxstat.ipxs_noroute++;
382                         m_freem(m);
383                         goto cleanup;
384                 }
385                 if (ipx_droute.ro_rt &&
386                     (ifp = ipx_droute.ro_rt->rt_ifp) &&
387                     ipx_sroute.ro_rt &&
388                     (ifp != ipx_sroute.ro_rt->rt_ifp)) {
389                         flags |= IPX_ALLOWBROADCAST;
390                 } else {
391                         ipxstat.ipxs_noroute++;
392                         m_freem(m);
393                         goto cleanup;
394                 }
395         }
396         /*
397          * We don't need to recompute checksum because ipx_tc field
398          * is ignored by checksum calculation routine, however
399          * it may be desirable to reset checksum if ipxcksum == 0
400          */
401 #if 0
402         if (!ipxcksum)
403                 ipx->ipx_sum = 0xffff;
404 #endif
405
406         error = ipx_outputfl(m, &ipx_droute, flags);
407         if (error == 0) {
408                 ipxstat.ipxs_forward++;
409
410                 if (ipxprintfs) {
411                         printf("forward: ");
412                         ipx_printhost(&ipx->ipx_sna);
413                         printf(" to ");
414                         ipx_printhost(&ipx->ipx_dna);
415                         printf(" hops %d\n", ipx->ipx_tc);
416                 }
417         } 
418 cleanup:
419         if (ok_there)
420                 ipx_undo_route(&ipx_droute);
421         if (ok_back)
422                 ipx_undo_route(&ipx_sroute);
423 }
424
425 static int
426 ipx_do_route(struct ipx_addr *src, struct route *ro)
427 {
428         struct sockaddr_ipx *dst;
429
430         bzero((caddr_t)ro, sizeof(*ro));
431         dst = (struct sockaddr_ipx *)&ro->ro_dst;
432
433         dst->sipx_len = sizeof(*dst);
434         dst->sipx_family = AF_IPX;
435         dst->sipx_addr = *src;
436         dst->sipx_addr.x_port = 0;
437         rtalloc_ign(ro, 0);
438         if (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL) {
439                 return (0);
440         }
441         ro->ro_rt->rt_use++;
442         return (1);
443 }
444
445 static void
446 ipx_undo_route(struct route *ro)
447 {
448
449         if (ro->ro_rt != NULL) {
450                 RTFREE(ro->ro_rt);
451         }
452 }
453
454 /*
455  * XXXRW: This code should be run in its own netisr dispatch to avoid a call
456  * back into the socket code from the IPX output path.
457  */
458 void
459 ipx_watch_output(struct mbuf *m, struct ifnet *ifp)
460 {
461         struct ipxpcb *ipxp;
462         struct ifaddr *ifa;
463         struct ipx_ifaddr *ia;
464
465         /*
466          * Give any raw listeners a crack at the packet
467          */
468         IPX_LIST_LOCK();
469         LIST_FOREACH(ipxp, &ipxrawpcb_list, ipxp_list) {
470                 struct mbuf *m0 = m_copy(m, 0, (int)M_COPYALL);
471                 if (m0 != NULL) {
472                         struct ipx *ipx;
473
474                         M_PREPEND(m0, sizeof(*ipx), M_DONTWAIT);
475                         if (m0 == NULL)
476                                 continue;
477                         ipx = mtod(m0, struct ipx *);
478                         ipx->ipx_sna.x_net = ipx_zeronet;
479                         for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
480                                 if (ifp == ia->ia_ifp)
481                                         break;
482                         if (ia == NULL)
483                                 ipx->ipx_sna.x_host = ipx_zerohost;
484                         else
485                                 ipx->ipx_sna.x_host =
486                                     ia->ia_addr.sipx_addr.x_host;
487
488                         if (ifp != NULL && (ifp->if_flags & IFF_POINTOPOINT))
489                             TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
490                                 if (ifa->ifa_addr->sa_family == AF_IPX) {
491                                     ipx->ipx_sna = IA_SIPX(ifa)->sipx_addr;
492                                     break;
493                                 }
494                             }
495                         ipx->ipx_len = ntohl(m0->m_pkthdr.len);
496                         IPX_LOCK(ipxp);
497                         ipx_input(m0, ipxp);
498                         IPX_UNLOCK(ipxp);
499                 }
500         }
501         IPX_LIST_UNLOCK();
502 }