ipx_pcb.c revision 136682
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: head/sys/netipx/ipx_pcb.c 136682 2004-10-18 22:19:43Z rwatson $"); 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 54static struct ipx_addr zeroipx_addr; 55 56int 57ipx_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 75int 76ipx_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; 109noname: 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 */ 128int 129ipx_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 255void 256ipx_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 265void 266ipx_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 281void 282ipx_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 296void 297ipx_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 */ 318void 319ipx_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 */ 351ipx_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 366struct ipxpcb * 367ipx_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} 409