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