ipx_pcb.c revision 139559
1/* 2 * Copyright (c) 2004 Robert N. M. Watson 3 * Copyright (c) 1995, Mike Mitchell 4 * Copyright (c) 1984, 1985, 1986, 1987, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * @(#)ipx_pcb.c 36 */ 37 38#include <sys/cdefs.h> 39__FBSDID("$FreeBSD: head/sys/netipx/ipx_pcb.c 139559 2005-01-02 01:51:18Z rwatson $"); 40 41#include <sys/param.h> 42#include <sys/systm.h> 43#include <sys/malloc.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 <netipx/ipx.h> 51#include <netipx/ipx_if.h> 52#include <netipx/ipx_pcb.h> 53#include <netipx/ipx_var.h> 54 55static struct ipx_addr zeroipx_addr; 56static u_short ipxpcb_lport_cache; 57 58int 59ipx_pcballoc(so, head, td) 60 struct socket *so; 61 struct ipxpcbhead *head; 62 struct thread *td; 63{ 64 register struct ipxpcb *ipxp; 65 66 MALLOC(ipxp, struct ipxpcb *, sizeof *ipxp, M_PCB, M_NOWAIT | M_ZERO); 67 if (ipxp == NULL) 68 return (ENOBUFS); 69 ipxp->ipxp_socket = so; 70 if (ipxcksum) 71 ipxp->ipxp_flags |= IPXP_CHECKSUM; 72 LIST_INSERT_HEAD(head, ipxp, ipxp_list); 73 so->so_pcb = (caddr_t)ipxp; 74 return (0); 75} 76 77int 78ipx_pcbbind(ipxp, nam, td) 79 register struct ipxpcb *ipxp; 80 struct sockaddr *nam; 81 struct thread *td; 82{ 83 register struct sockaddr_ipx *sipx; 84 u_short lport = 0; 85 86 if (ipxp->ipxp_lport || !ipx_nullhost(ipxp->ipxp_laddr)) 87 return (EINVAL); 88 if (nam == NULL) 89 goto noname; 90 sipx = (struct sockaddr_ipx *)nam; 91 if (!ipx_nullhost(sipx->sipx_addr)) { 92 int tport = sipx->sipx_port; 93 94 sipx->sipx_port = 0; /* yech... */ 95 if (ifa_ifwithaddr((struct sockaddr *)sipx) == NULL) 96 return (EADDRNOTAVAIL); 97 sipx->sipx_port = tport; 98 } 99 lport = sipx->sipx_port; 100 if (lport) { 101 u_short aport = ntohs(lport); 102 int error; 103 104 if (aport < IPXPORT_RESERVED && 105 td != NULL && (error = suser(td)) != 0) 106 return (error); 107 if (ipx_pcblookup(&zeroipx_addr, lport, 0)) 108 return (EADDRINUSE); 109 } 110 ipxp->ipxp_laddr = sipx->sipx_addr; 111noname: 112 if (lport == 0) 113 do { 114 ipxpcb_lport_cache++; 115 if ((ipxpcb_lport_cache < IPXPORT_RESERVED) || 116 (ipxpcb_lport_cache >= IPXPORT_WELLKNOWN)) 117 ipxpcb_lport_cache = IPXPORT_RESERVED; 118 lport = htons(ipxpcb_lport_cache); 119 } while (ipx_pcblookup(&zeroipx_addr, lport, 0)); 120 ipxp->ipxp_lport = lport; 121 return (0); 122} 123 124/* 125 * Connect from a socket to a specified address. 126 * Both address and port must be specified in argument sipx. 127 * If don't have a local address for this socket yet, 128 * then pick one. 129 */ 130int 131ipx_pcbconnect(ipxp, nam, td) 132 struct ipxpcb *ipxp; 133 struct sockaddr *nam; 134 struct thread *td; 135{ 136 struct ipx_ifaddr *ia; 137 register struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)nam; 138 register struct ipx_addr *dst; 139 register struct route *ro; 140 struct ifnet *ifp; 141 142 ia = NULL; 143 144 if (sipx->sipx_family != AF_IPX) 145 return (EAFNOSUPPORT); 146 if (sipx->sipx_port == 0 || ipx_nullhost(sipx->sipx_addr)) 147 return (EADDRNOTAVAIL); 148 /* 149 * If we haven't bound which network number to use as ours, 150 * we will use the number of the outgoing interface. 151 * This depends on having done a routing lookup, which 152 * we will probably have to do anyway, so we might 153 * as well do it now. On the other hand if we are 154 * sending to multiple destinations we may have already 155 * done the lookup, so see if we can use the route 156 * from before. In any case, we only 157 * chose a port number once, even if sending to multiple 158 * destinations. 159 */ 160 ro = &ipxp->ipxp_route; 161 dst = &satoipx_addr(ro->ro_dst); 162 if (ipxp->ipxp_socket->so_options & SO_DONTROUTE) 163 goto flush; 164 if (!ipx_neteq(ipxp->ipxp_lastdst, sipx->sipx_addr)) 165 goto flush; 166 if (!ipx_hosteq(ipxp->ipxp_lastdst, sipx->sipx_addr)) { 167 if (ro->ro_rt != NULL && !(ro->ro_rt->rt_flags & RTF_HOST)) { 168 /* can patch route to avoid rtalloc */ 169 *dst = sipx->sipx_addr; 170 } else { 171 flush: 172 if (ro->ro_rt != NULL) 173 RTFREE(ro->ro_rt); 174 ro->ro_rt = NULL; 175 } 176 }/* else cached route is ok; do nothing */ 177 ipxp->ipxp_lastdst = sipx->sipx_addr; 178 if ((ipxp->ipxp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/ 179 (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL)) { 180 /* No route yet, so try to acquire one */ 181 ro->ro_dst.sa_family = AF_IPX; 182 ro->ro_dst.sa_len = sizeof(ro->ro_dst); 183 *dst = sipx->sipx_addr; 184 dst->x_port = 0; 185 rtalloc_ign(ro, 0); 186 } 187 if (ipx_neteqnn(ipxp->ipxp_laddr.x_net, ipx_zeronet)) { 188 /* 189 * If route is known or can be allocated now, 190 * our src addr is taken from the i/f, else punt. 191 */ 192 193 /* 194 * If we found a route, use the address 195 * corresponding to the outgoing interface 196 */ 197 if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL) 198 for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next) 199 if (ia->ia_ifp == ifp) 200 break; 201 if (ia == NULL) { 202 u_short fport = sipx->sipx_addr.x_port; 203 sipx->sipx_addr.x_port = 0; 204 ia = (struct ipx_ifaddr *) 205 ifa_ifwithdstaddr((struct sockaddr *)sipx); 206 sipx->sipx_addr.x_port = fport; 207 if (ia == NULL) 208 ia = ipx_iaonnetof(&sipx->sipx_addr); 209 if (ia == NULL) 210 ia = ipx_ifaddr; 211 if (ia == NULL) 212 return (EADDRNOTAVAIL); 213 } 214 ipxp->ipxp_laddr.x_net = satoipx_addr(ia->ia_addr).x_net; 215 } 216 if (ipx_nullhost(ipxp->ipxp_laddr)) { 217 /* 218 * If route is known or can be allocated now, 219 * our src addr is taken from the i/f, else punt. 220 */ 221 222 /* 223 * If we found a route, use the address 224 * corresponding to the outgoing interface 225 */ 226 if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL) 227 for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next) 228 if (ia->ia_ifp == ifp) 229 break; 230 if (ia == NULL) { 231 u_short fport = sipx->sipx_addr.x_port; 232 sipx->sipx_addr.x_port = 0; 233 ia = (struct ipx_ifaddr *) 234 ifa_ifwithdstaddr((struct sockaddr *)sipx); 235 sipx->sipx_addr.x_port = fport; 236 if (ia == NULL) 237 ia = ipx_iaonnetof(&sipx->sipx_addr); 238 if (ia == NULL) 239 ia = ipx_ifaddr; 240 if (ia == NULL) 241 return (EADDRNOTAVAIL); 242 } 243 ipxp->ipxp_laddr.x_host = satoipx_addr(ia->ia_addr).x_host; 244 } 245 if (ipx_pcblookup(&sipx->sipx_addr, ipxp->ipxp_lport, 0)) 246 return (EADDRINUSE); 247 if (ipxp->ipxp_lport == 0) 248 ipx_pcbbind(ipxp, (struct sockaddr *)NULL, td); 249 250 /* XXX just leave it zero if we can't find a route */ 251 252 ipxp->ipxp_faddr = sipx->sipx_addr; 253 /* Includes ipxp->ipxp_fport = sipx->sipx_port; */ 254 return (0); 255} 256 257void 258ipx_pcbdisconnect(ipxp) 259 struct ipxpcb *ipxp; 260{ 261 262 ipxp->ipxp_faddr = zeroipx_addr; 263 if (ipxp->ipxp_socket->so_state & SS_NOFDREF) 264 ipx_pcbdetach(ipxp); 265} 266 267void 268ipx_pcbdetach(ipxp) 269 struct ipxpcb *ipxp; 270{ 271 struct socket *so = ipxp->ipxp_socket; 272 273 ACCEPT_LOCK(); 274 SOCK_LOCK(so); 275 so->so_pcb = NULL; 276 sotryfree(so); 277 if (ipxp->ipxp_route.ro_rt != NULL) 278 RTFREE(ipxp->ipxp_route.ro_rt); 279 LIST_REMOVE(ipxp, ipxp_list); 280 FREE(ipxp, M_PCB); 281} 282 283void 284ipx_setsockaddr(ipxp, nam) 285 register struct ipxpcb *ipxp; 286 struct sockaddr **nam; 287{ 288 struct sockaddr_ipx *sipx, ssipx; 289 290 sipx = &ssipx; 291 bzero((caddr_t)sipx, sizeof(*sipx)); 292 sipx->sipx_len = sizeof(*sipx); 293 sipx->sipx_family = AF_IPX; 294 sipx->sipx_addr = ipxp->ipxp_laddr; 295 *nam = sodupsockaddr((struct sockaddr *)sipx, M_NOWAIT); 296} 297 298void 299ipx_setpeeraddr(ipxp, nam) 300 register struct ipxpcb *ipxp; 301 struct sockaddr **nam; 302{ 303 struct sockaddr_ipx *sipx, ssipx; 304 305 sipx = &ssipx; 306 bzero((caddr_t)sipx, sizeof(*sipx)); 307 sipx->sipx_len = sizeof(*sipx); 308 sipx->sipx_family = AF_IPX; 309 sipx->sipx_addr = ipxp->ipxp_faddr; 310 *nam = sodupsockaddr((struct sockaddr *)sipx, M_NOWAIT); 311} 312 313struct ipxpcb * 314ipx_pcblookup(faddr, lport, wildp) 315 struct ipx_addr *faddr; 316 u_short lport; 317 int wildp; 318{ 319 register struct ipxpcb *ipxp, *match = NULL; 320 int matchwild = 3, wildcard; 321 u_short fport; 322 323 fport = faddr->x_port; 324 LIST_FOREACH(ipxp, &ipxpcb_list, ipxp_list) { 325 if (ipxp->ipxp_lport != lport) 326 continue; 327 wildcard = 0; 328 if (ipx_nullhost(ipxp->ipxp_faddr)) { 329 if (!ipx_nullhost(*faddr)) 330 wildcard++; 331 } else { 332 if (ipx_nullhost(*faddr)) 333 wildcard++; 334 else { 335 if (!ipx_hosteq(ipxp->ipxp_faddr, *faddr)) 336 continue; 337 if (ipxp->ipxp_fport != fport) { 338 if (ipxp->ipxp_fport != 0) 339 continue; 340 else 341 wildcard++; 342 } 343 } 344 } 345 if (wildcard && wildp == 0) 346 continue; 347 if (wildcard < matchwild) { 348 match = ipxp; 349 matchwild = wildcard; 350 if (wildcard == 0) 351 break; 352 } 353 } 354 return (match); 355} 356