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