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