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