]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netipx/ipx_pcb.c
This commit was generated by cvs2svn to compensate for changes in r137015,
[FreeBSD/FreeBSD.git] / sys / netipx / ipx_pcb.c
1 /*
2  * Copyright (c) 1995, Mike Mitchell
3  * Copyright (c) 1984, 1985, 1986, 1987, 1993
4  *      The Regents of the University of California.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *      This product includes software developed by the University of
17  *      California, Berkeley and its contributors.
18  * 4. 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_pcb.c
35  */
36
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/malloc.h>
43 #include <sys/socket.h>
44 #include <sys/socketvar.h>
45
46 #include <net/if.h>
47 #include <net/route.h>
48
49 #include <netipx/ipx.h>
50 #include <netipx/ipx_if.h>
51 #include <netipx/ipx_pcb.h>
52 #include <netipx/ipx_var.h>
53
54 static struct   ipx_addr zeroipx_addr;
55
56 int
57 ipx_pcballoc(so, head, td)
58         struct socket *so;
59         struct ipxpcb *head;
60         struct thread *td;
61 {
62         register struct ipxpcb *ipxp;
63
64         MALLOC(ipxp, struct ipxpcb *, sizeof *ipxp, M_PCB, M_NOWAIT | M_ZERO);
65         if (ipxp == NULL)
66                 return (ENOBUFS);
67         ipxp->ipxp_socket = so;
68         if (ipxcksum)
69                 ipxp->ipxp_flags |= IPXP_CHECKSUM;
70         insque(ipxp, head);
71         so->so_pcb = (caddr_t)ipxp;
72         return (0);
73 }
74         
75 int
76 ipx_pcbbind(ipxp, nam, td)
77         register struct ipxpcb *ipxp;
78         struct sockaddr *nam;
79         struct thread *td;
80 {
81         register struct sockaddr_ipx *sipx;
82         u_short lport = 0;
83
84         if (ipxp->ipxp_lport || !ipx_nullhost(ipxp->ipxp_laddr))
85                 return (EINVAL);
86         if (nam == NULL)
87                 goto noname;
88         sipx = (struct sockaddr_ipx *)nam;
89         if (!ipx_nullhost(sipx->sipx_addr)) {
90                 int tport = sipx->sipx_port;
91
92                 sipx->sipx_port = 0;            /* yech... */
93                 if (ifa_ifwithaddr((struct sockaddr *)sipx) == 0)
94                         return (EADDRNOTAVAIL);
95                 sipx->sipx_port = tport;
96         }
97         lport = sipx->sipx_port;
98         if (lport) {
99                 u_short aport = ntohs(lport);
100                 int error;
101
102                 if (aport < IPXPORT_RESERVED &&
103                     td != NULL && (error = suser(td)) != 0)
104                         return (error);
105                 if (ipx_pcblookup(&zeroipx_addr, lport, 0))
106                         return (EADDRINUSE);
107         }
108         ipxp->ipxp_laddr = sipx->sipx_addr;
109 noname:
110         if (lport == 0)
111                 do {
112                         ipxpcb.ipxp_lport++;
113                         if ((ipxpcb.ipxp_lport < IPXPORT_RESERVED) ||
114                             (ipxpcb.ipxp_lport >= IPXPORT_WELLKNOWN))
115                                 ipxpcb.ipxp_lport = IPXPORT_RESERVED;
116                         lport = htons(ipxpcb.ipxp_lport);
117                 } while (ipx_pcblookup(&zeroipx_addr, lport, 0));
118         ipxp->ipxp_lport = lport;
119         return (0);
120 }
121
122 /*
123  * Connect from a socket to a specified address.
124  * Both address and port must be specified in argument sipx.
125  * If don't have a local address for this socket yet,
126  * then pick one.
127  */
128 int
129 ipx_pcbconnect(ipxp, nam, td)
130         struct ipxpcb *ipxp;
131         struct sockaddr *nam;
132         struct thread *td;
133 {
134         struct ipx_ifaddr *ia;
135         register struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)nam;
136         register struct ipx_addr *dst;
137         register struct route *ro;
138         struct ifnet *ifp;
139
140         ia = NULL;
141
142         if (sipx->sipx_family != AF_IPX)
143                 return (EAFNOSUPPORT);
144         if (sipx->sipx_port == 0 || ipx_nullhost(sipx->sipx_addr))
145                 return (EADDRNOTAVAIL);
146         /*
147          * If we haven't bound which network number to use as ours,
148          * we will use the number of the outgoing interface.
149          * This depends on having done a routing lookup, which
150          * we will probably have to do anyway, so we might
151          * as well do it now.  On the other hand if we are
152          * sending to multiple destinations we may have already
153          * done the lookup, so see if we can use the route
154          * from before.  In any case, we only
155          * chose a port number once, even if sending to multiple
156          * destinations.
157          */
158         ro = &ipxp->ipxp_route;
159         dst = &satoipx_addr(ro->ro_dst);
160         if (ipxp->ipxp_socket->so_options & SO_DONTROUTE)
161                 goto flush;
162         if (!ipx_neteq(ipxp->ipxp_lastdst, sipx->sipx_addr))
163                 goto flush;
164         if (!ipx_hosteq(ipxp->ipxp_lastdst, sipx->sipx_addr)) {
165                 if (ro->ro_rt != NULL && !(ro->ro_rt->rt_flags & RTF_HOST)) {
166                         /* can patch route to avoid rtalloc */
167                         *dst = sipx->sipx_addr;
168                 } else {
169         flush:
170                         if (ro->ro_rt != NULL)
171                                 RTFREE(ro->ro_rt);
172                         ro->ro_rt = NULL;
173                 }
174         }/* else cached route is ok; do nothing */
175         ipxp->ipxp_lastdst = sipx->sipx_addr;
176         if ((ipxp->ipxp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
177             (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL)) {
178                     /* No route yet, so try to acquire one */
179                     ro->ro_dst.sa_family = AF_IPX;
180                     ro->ro_dst.sa_len = sizeof(ro->ro_dst);
181                     *dst = sipx->sipx_addr;
182                     dst->x_port = 0;
183                     rtalloc(ro);
184         }
185         if (ipx_neteqnn(ipxp->ipxp_laddr.x_net, ipx_zeronet)) {
186                 /* 
187                  * If route is known or can be allocated now,
188                  * our src addr is taken from the i/f, else punt.
189                  */
190
191                 /*
192                  * If we found a route, use the address
193                  * corresponding to the outgoing interface
194                  */
195                 if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL)
196                         for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
197                                 if (ia->ia_ifp == ifp)
198                                         break;
199                 if (ia == NULL) {
200                         u_short fport = sipx->sipx_addr.x_port;
201                         sipx->sipx_addr.x_port = 0;
202                         ia = (struct ipx_ifaddr *)
203                                 ifa_ifwithdstaddr((struct sockaddr *)sipx);
204                         sipx->sipx_addr.x_port = fport;
205                         if (ia == NULL)
206                                 ia = ipx_iaonnetof(&sipx->sipx_addr);
207                         if (ia == NULL)
208                                 ia = ipx_ifaddr;
209                         if (ia == NULL)
210                                 return (EADDRNOTAVAIL);
211                 }
212                 ipxp->ipxp_laddr.x_net = satoipx_addr(ia->ia_addr).x_net;
213         }
214         if (ipx_nullhost(ipxp->ipxp_laddr)) {
215                 /* 
216                  * If route is known or can be allocated now,
217                  * our src addr is taken from the i/f, else punt.
218                  */
219
220                 /*
221                  * If we found a route, use the address
222                  * corresponding to the outgoing interface
223                  */
224                 if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL)
225                         for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
226                                 if (ia->ia_ifp == ifp)
227                                         break;
228                 if (ia == NULL) {
229                         u_short fport = sipx->sipx_addr.x_port;
230                         sipx->sipx_addr.x_port = 0;
231                         ia = (struct ipx_ifaddr *)
232                                 ifa_ifwithdstaddr((struct sockaddr *)sipx);
233                         sipx->sipx_addr.x_port = fport;
234                         if (ia == NULL)
235                                 ia = ipx_iaonnetof(&sipx->sipx_addr);
236                         if (ia == NULL)
237                                 ia = ipx_ifaddr;
238                         if (ia == NULL)
239                                 return (EADDRNOTAVAIL);
240                 }
241                 ipxp->ipxp_laddr.x_host = satoipx_addr(ia->ia_addr).x_host;
242         }
243         if (ipx_pcblookup(&sipx->sipx_addr, ipxp->ipxp_lport, 0))
244                 return (EADDRINUSE);
245         if (ipxp->ipxp_lport == 0)
246                 ipx_pcbbind(ipxp, (struct sockaddr *)NULL, td);
247
248         /* XXX just leave it zero if we can't find a route */
249
250         ipxp->ipxp_faddr = sipx->sipx_addr;
251         /* Includes ipxp->ipxp_fport = sipx->sipx_port; */
252         return (0);
253 }
254
255 void
256 ipx_pcbdisconnect(ipxp)
257         struct ipxpcb *ipxp;
258 {
259
260         ipxp->ipxp_faddr = zeroipx_addr;
261         if (ipxp->ipxp_socket->so_state & SS_NOFDREF)
262                 ipx_pcbdetach(ipxp);
263 }
264
265 void
266 ipx_pcbdetach(ipxp)
267         struct ipxpcb *ipxp;
268 {
269         struct socket *so = ipxp->ipxp_socket;
270
271         ACCEPT_LOCK();
272         SOCK_LOCK(so);
273         so->so_pcb = 0;
274         sotryfree(so);
275         if (ipxp->ipxp_route.ro_rt != NULL)
276                 rtfree(ipxp->ipxp_route.ro_rt);
277         remque(ipxp);
278         FREE(ipxp, M_PCB);
279 }
280
281 void
282 ipx_setsockaddr(ipxp, nam)
283         register struct ipxpcb *ipxp;
284         struct sockaddr **nam;
285 {
286         struct sockaddr_ipx *sipx, ssipx;
287         
288         sipx = &ssipx;
289         bzero((caddr_t)sipx, sizeof(*sipx));
290         sipx->sipx_len = sizeof(*sipx);
291         sipx->sipx_family = AF_IPX;
292         sipx->sipx_addr = ipxp->ipxp_laddr;
293         *nam = sodupsockaddr((struct sockaddr *)sipx, M_NOWAIT);
294 }
295
296 void
297 ipx_setpeeraddr(ipxp, nam)
298         register struct ipxpcb *ipxp;
299         struct sockaddr **nam;
300 {
301         struct sockaddr_ipx *sipx, ssipx;
302         
303         sipx = &ssipx;
304         bzero((caddr_t)sipx, sizeof(*sipx));
305         sipx->sipx_len = sizeof(*sipx);
306         sipx->sipx_family = AF_IPX;
307         sipx->sipx_addr = ipxp->ipxp_faddr;
308         *nam = sodupsockaddr((struct sockaddr *)sipx, M_NOWAIT);
309 }
310
311 /*
312  * Pass some notification to all connections of a protocol
313  * associated with address dst.  Call the
314  * protocol specific routine to handle each connection.
315  * Also pass an extra paramter via the ipxpcb. (which may in fact
316  * be a parameter list!)
317  */
318 void
319 ipx_pcbnotify(dst, errno, notify, param)
320         register struct ipx_addr *dst;
321         int errno;
322         void (*notify)(struct ipxpcb *);
323         long param;
324 {
325         register struct ipxpcb *ipxp, *oinp;
326         int s = splimp();
327
328         for (ipxp = (&ipxpcb)->ipxp_next; ipxp != (&ipxpcb);) {
329                 if (!ipx_hosteq(*dst,ipxp->ipxp_faddr)) {
330         next:
331                         ipxp = ipxp->ipxp_next;
332                         continue;
333                 }
334                 if (ipxp->ipxp_socket == 0)
335                         goto next;
336                 if (errno) 
337                         ipxp->ipxp_socket->so_error = errno;
338                 oinp = ipxp;
339                 ipxp = ipxp->ipxp_next;
340                 oinp->ipxp_notify_param = param;
341                 (*notify)(oinp);
342         }
343         splx(s);
344 }
345
346 #ifdef notdef
347 /*
348  * After a routing change, flush old routing
349  * and allocate a (hopefully) better one.
350  */
351 ipx_rtchange(ipxp)
352         struct ipxpcb *ipxp;
353 {
354         if (ipxp->ipxp_route.ro_rt != NULL) {
355                 rtfree(ipxp->ipxp_route.ro_rt);
356                 ipxp->ipxp_route.ro_rt = NULL;
357                 /*
358                  * A new route can be allocated the next time
359                  * output is attempted.
360                  */
361         }
362         /* SHOULD NOTIFY HIGHER-LEVEL PROTOCOLS */
363 }
364 #endif
365
366 struct ipxpcb *
367 ipx_pcblookup(faddr, lport, wildp)
368         struct ipx_addr *faddr;
369         u_short lport;
370         int wildp;
371 {
372         register struct ipxpcb *ipxp, *match = 0;
373         int matchwild = 3, wildcard;
374         u_short fport;
375
376         fport = faddr->x_port;
377         for (ipxp = (&ipxpcb)->ipxp_next; ipxp != (&ipxpcb); ipxp = ipxp->ipxp_next) {
378                 if (ipxp->ipxp_lport != lport)
379                         continue;
380                 wildcard = 0;
381                 if (ipx_nullhost(ipxp->ipxp_faddr)) {
382                         if (!ipx_nullhost(*faddr))
383                                 wildcard++;
384                 } else {
385                         if (ipx_nullhost(*faddr))
386                                 wildcard++;
387                         else {
388                                 if (!ipx_hosteq(ipxp->ipxp_faddr, *faddr))
389                                         continue;
390                                 if (ipxp->ipxp_fport != fport) {
391                                         if (ipxp->ipxp_fport != 0)
392                                                 continue;
393                                         else
394                                                 wildcard++;
395                                 }
396                         }
397                 }
398                 if (wildcard && wildp == 0)
399                         continue;
400                 if (wildcard < matchwild) {
401                         match = ipxp;
402                         matchwild = wildcard;
403                         if (wildcard == 0)
404                                 break;
405                 }
406         }
407         return (match);
408 }