ipx_pcb.c revision 96972
1167612Ssimon/* 2167612Ssimon * Copyright (c) 1995, Mike Mitchell 3167612Ssimon * Copyright (c) 1984, 1985, 1986, 1987, 1993 4167612Ssimon * The Regents of the University of California. All rights reserved. 5167612Ssimon * 6167612Ssimon * Redistribution and use in source and binary forms, with or without 7167612Ssimon * modification, are permitted provided that the following conditions 8167612Ssimon * are met: 9167612Ssimon * 1. Redistributions of source code must retain the above copyright 10167612Ssimon * notice, this list of conditions and the following disclaimer. 11167612Ssimon * 2. Redistributions in binary form must reproduce the above copyright 12167612Ssimon * notice, this list of conditions and the following disclaimer in the 13167612Ssimon * documentation and/or other materials provided with the distribution. 14167612Ssimon * 3. All advertising materials mentioning features or use of this software 15167612Ssimon * must display the following acknowledgement: 16167612Ssimon * This product includes software developed by the University of 17167612Ssimon * California, Berkeley and its contributors. 18167612Ssimon * 4. Neither the name of the University nor the names of its contributors 19167612Ssimon * may be used to endorse or promote products derived from this software 20167612Ssimon * without specific prior written permission. 21167612Ssimon * 22167612Ssimon * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23167612Ssimon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24167612Ssimon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25167612Ssimon * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26167612Ssimon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27167612Ssimon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28167612Ssimon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29167612Ssimon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30167612Ssimon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31167612Ssimon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32167612Ssimon * SUCH DAMAGE. 33167612Ssimon * 34167612Ssimon * @(#)ipx_pcb.c 35167612Ssimon * 36167612Ssimon * $FreeBSD: head/sys/netipx/ipx_pcb.c 96972 2002-05-20 05:41:09Z tanimura $ 37167612Ssimon */ 38167612Ssimon 39167612Ssimon#include <sys/param.h> 40167612Ssimon#include <sys/systm.h> 41167612Ssimon#include <sys/malloc.h> 42167612Ssimon#include <sys/socket.h> 43167612Ssimon#include <sys/socketvar.h> 44167612Ssimon 45167612Ssimon#include <net/if.h> 46167612Ssimon#include <net/route.h> 47167612Ssimon 48167612Ssimon#include <netipx/ipx.h> 49167612Ssimon#include <netipx/ipx_if.h> 50167612Ssimon#include <netipx/ipx_pcb.h> 51167612Ssimon#include <netipx/ipx_var.h> 52167612Ssimon 53167612Ssimonstatic struct ipx_addr zeroipx_addr; 54167612Ssimon 55167612Ssimonint 56167612Ssimonipx_pcballoc(so, head, td) 57167612Ssimon struct socket *so; 58167612Ssimon struct ipxpcb *head; 59167612Ssimon struct thread *td; 60167612Ssimon{ 61167612Ssimon register struct ipxpcb *ipxp; 62167612Ssimon 63167612Ssimon MALLOC(ipxp, struct ipxpcb *, sizeof *ipxp, M_PCB, M_NOWAIT | M_ZERO); 64167612Ssimon if (ipxp == NULL) 65167612Ssimon return (ENOBUFS); 66167612Ssimon ipxp->ipxp_socket = so; 67167612Ssimon if (ipxcksum) 68167612Ssimon ipxp->ipxp_flags |= IPXP_CHECKSUM; 69167612Ssimon insque(ipxp, head); 70167612Ssimon so->so_pcb = (caddr_t)ipxp; 71167612Ssimon return (0); 72167612Ssimon} 73167612Ssimon 74167612Ssimonint 75167612Ssimonipx_pcbbind(ipxp, nam, td) 76167612Ssimon register struct ipxpcb *ipxp; 77167612Ssimon struct sockaddr *nam; 78167612Ssimon struct thread *td; 79167612Ssimon{ 80167612Ssimon register struct sockaddr_ipx *sipx; 81167612Ssimon u_short lport = 0; 82167612Ssimon 83167612Ssimon if (ipxp->ipxp_lport || !ipx_nullhost(ipxp->ipxp_laddr)) 84167612Ssimon return (EINVAL); 85167612Ssimon if (nam == NULL) 86167612Ssimon goto noname; 87167612Ssimon sipx = (struct sockaddr_ipx *)nam; 88167612Ssimon if (!ipx_nullhost(sipx->sipx_addr)) { 89167612Ssimon int tport = sipx->sipx_port; 90167612Ssimon 91167612Ssimon sipx->sipx_port = 0; /* yech... */ 92167612Ssimon if (ifa_ifwithaddr((struct sockaddr *)sipx) == 0) 93167612Ssimon return (EADDRNOTAVAIL); 94167612Ssimon sipx->sipx_port = tport; 95167612Ssimon } 96167612Ssimon lport = sipx->sipx_port; 97167612Ssimon if (lport) { 98167612Ssimon u_short aport = ntohs(lport); 99167612Ssimon int error; 100167612Ssimon 101167612Ssimon if (aport < IPXPORT_RESERVED && 102167612Ssimon td != NULL && (error = suser(td)) != 0) 103167612Ssimon return (error); 104167612Ssimon if (ipx_pcblookup(&zeroipx_addr, lport, 0)) 105167612Ssimon return (EADDRINUSE); 106167612Ssimon } 107167612Ssimon ipxp->ipxp_laddr = sipx->sipx_addr; 108167612Ssimonnoname: 109167612Ssimon if (lport == 0) 110167612Ssimon do { 111167612Ssimon ipxpcb.ipxp_lport++; 112167612Ssimon if ((ipxpcb.ipxp_lport < IPXPORT_RESERVED) || 113167612Ssimon (ipxpcb.ipxp_lport >= IPXPORT_WELLKNOWN)) 114167612Ssimon ipxpcb.ipxp_lport = IPXPORT_RESERVED; 115167612Ssimon lport = htons(ipxpcb.ipxp_lport); 116167612Ssimon } while (ipx_pcblookup(&zeroipx_addr, lport, 0)); 117167612Ssimon ipxp->ipxp_lport = lport; 118167612Ssimon return (0); 119167612Ssimon} 120167612Ssimon 121167612Ssimon/* 122167612Ssimon * Connect from a socket to a specified address. 123167612Ssimon * Both address and port must be specified in argument sipx. 124167612Ssimon * If don't have a local address for this socket yet, 125167612Ssimon * then pick one. 126167612Ssimon */ 127167612Ssimonint 128167612Ssimonipx_pcbconnect(ipxp, nam, td) 129167612Ssimon struct ipxpcb *ipxp; 130167612Ssimon struct sockaddr *nam; 131167612Ssimon struct thread *td; 132167612Ssimon{ 133167612Ssimon struct ipx_ifaddr *ia; 134167612Ssimon register struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)nam; 135167612Ssimon register struct ipx_addr *dst; 136167612Ssimon register struct route *ro; 137167612Ssimon struct ifnet *ifp; 138167612Ssimon 139167612Ssimon ia = NULL; 140167612Ssimon 141167612Ssimon if (sipx->sipx_family != AF_IPX) 142167612Ssimon return (EAFNOSUPPORT); 143167612Ssimon if (sipx->sipx_port == 0 || ipx_nullhost(sipx->sipx_addr)) 144167612Ssimon return (EADDRNOTAVAIL); 145167612Ssimon /* 146167612Ssimon * If we haven't bound which network number to use as ours, 147167612Ssimon * we will use the number of the outgoing interface. 148167612Ssimon * This depends on having done a routing lookup, which 149167612Ssimon * we will probably have to do anyway, so we might 150167612Ssimon * as well do it now. On the other hand if we are 151167612Ssimon * sending to multiple destinations we may have already 152167612Ssimon * done the lookup, so see if we can use the route 153167612Ssimon * from before. In any case, we only 154167612Ssimon * chose a port number once, even if sending to multiple 155167612Ssimon * destinations. 156167612Ssimon */ 157167612Ssimon ro = &ipxp->ipxp_route; 158167612Ssimon dst = &satoipx_addr(ro->ro_dst); 159167612Ssimon SOCK_LOCK(ipxp->ipxp_socket); 160167612Ssimon if (ipxp->ipxp_socket->so_options & SO_DONTROUTE) { 161167612Ssimon SOCK_UNLOCK(ipxp->ipxp_socket); 162167612Ssimon goto flush; 163167612Ssimon } 164167612Ssimon SOCK_UNLOCK(ipxp->ipxp_socket); 165167612Ssimon if (!ipx_neteq(ipxp->ipxp_lastdst, sipx->sipx_addr)) 166167612Ssimon goto flush; 167167612Ssimon if (!ipx_hosteq(ipxp->ipxp_lastdst, sipx->sipx_addr)) { 168167612Ssimon if (ro->ro_rt != NULL && !(ro->ro_rt->rt_flags & RTF_HOST)) { 169167612Ssimon /* can patch route to avoid rtalloc */ 170167612Ssimon *dst = sipx->sipx_addr; 171167612Ssimon } else { 172167612Ssimon flush: 173167612Ssimon if (ro->ro_rt != NULL) 174167612Ssimon RTFREE(ro->ro_rt); 175167612Ssimon ro->ro_rt = NULL; 176167612Ssimon } 177167612Ssimon }/* else cached route is ok; do nothing */ 178167612Ssimon ipxp->ipxp_lastdst = sipx->sipx_addr; 179167612Ssimon SOCK_LOCK(ipxp->ipxp_socket); 180167612Ssimon if ((ipxp->ipxp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/ 181167612Ssimon (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL)) { 182167612Ssimon SOCK_UNLOCK(ipxp->ipxp_socket); 183167612Ssimon /* No route yet, so try to acquire one */ 184167612Ssimon ro->ro_dst.sa_family = AF_IPX; 185167612Ssimon ro->ro_dst.sa_len = sizeof(ro->ro_dst); 186167612Ssimon *dst = sipx->sipx_addr; 187167612Ssimon dst->x_port = 0; 188167612Ssimon rtalloc(ro); 189167612Ssimon } else 190167612Ssimon SOCK_UNLOCK(ipxp->ipxp_socket); 191167612Ssimon if (ipx_neteqnn(ipxp->ipxp_laddr.x_net, ipx_zeronet)) { 192167612Ssimon /* 193167612Ssimon * If route is known or can be allocated now, 194167612Ssimon * our src addr is taken from the i/f, else punt. 195167612Ssimon */ 196167612Ssimon 197167612Ssimon /* 198167612Ssimon * If we found a route, use the address 199167612Ssimon * corresponding to the outgoing interface 200167612Ssimon */ 201167612Ssimon if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL) 202167612Ssimon for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next) 203167612Ssimon if (ia->ia_ifp == ifp) 204167612Ssimon break; 205167612Ssimon if (ia == NULL) { 206167612Ssimon u_short fport = sipx->sipx_addr.x_port; 207167612Ssimon sipx->sipx_addr.x_port = 0; 208167612Ssimon ia = (struct ipx_ifaddr *) 209167612Ssimon ifa_ifwithdstaddr((struct sockaddr *)sipx); 210167612Ssimon sipx->sipx_addr.x_port = fport; 211167612Ssimon if (ia == NULL) 212167612Ssimon ia = ipx_iaonnetof(&sipx->sipx_addr); 213167612Ssimon if (ia == NULL) 214167612Ssimon ia = ipx_ifaddr; 215167612Ssimon if (ia == NULL) 216167612Ssimon return (EADDRNOTAVAIL); 217167612Ssimon } 218167612Ssimon ipxp->ipxp_laddr.x_net = satoipx_addr(ia->ia_addr).x_net; 219167612Ssimon } 220167612Ssimon if (ipx_nullhost(ipxp->ipxp_laddr)) { 221167612Ssimon /* 222167612Ssimon * If route is known or can be allocated now, 223167612Ssimon * our src addr is taken from the i/f, else punt. 224167612Ssimon */ 225167612Ssimon 226167612Ssimon /* 227167612Ssimon * If we found a route, use the address 228167612Ssimon * corresponding to the outgoing interface 229167612Ssimon */ 230167612Ssimon if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL) 231167612Ssimon for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next) 232167612Ssimon if (ia->ia_ifp == ifp) 233167612Ssimon break; 234167612Ssimon if (ia == NULL) { 235167612Ssimon u_short fport = sipx->sipx_addr.x_port; 236167612Ssimon sipx->sipx_addr.x_port = 0; 237167612Ssimon ia = (struct ipx_ifaddr *) 238167612Ssimon ifa_ifwithdstaddr((struct sockaddr *)sipx); 239167612Ssimon sipx->sipx_addr.x_port = fport; 240167612Ssimon if (ia == NULL) 241167612Ssimon ia = ipx_iaonnetof(&sipx->sipx_addr); 242167612Ssimon if (ia == NULL) 243167612Ssimon ia = ipx_ifaddr; 244167612Ssimon if (ia == NULL) 245167612Ssimon return (EADDRNOTAVAIL); 246167612Ssimon } 247167612Ssimon ipxp->ipxp_laddr.x_host = satoipx_addr(ia->ia_addr).x_host; 248167612Ssimon } 249167612Ssimon if (ipx_pcblookup(&sipx->sipx_addr, ipxp->ipxp_lport, 0)) 250167612Ssimon return (EADDRINUSE); 251167612Ssimon if (ipxp->ipxp_lport == 0) 252167612Ssimon ipx_pcbbind(ipxp, (struct sockaddr *)NULL, td); 253167612Ssimon 254167612Ssimon /* XXX just leave it zero if we can't find a route */ 255167612Ssimon 256167612Ssimon ipxp->ipxp_faddr = sipx->sipx_addr; 257167612Ssimon /* Includes ipxp->ipxp_fport = sipx->sipx_port; */ 258167612Ssimon return (0); 259167612Ssimon} 260167612Ssimon 261167612Ssimonvoid 262167612Ssimonipx_pcbdisconnect(ipxp) 263167612Ssimon struct ipxpcb *ipxp; 264167612Ssimon{ 265167612Ssimon 266167612Ssimon ipxp->ipxp_faddr = zeroipx_addr; 267167612Ssimon SOCK_LOCK(ipxp->ipxp_socket); 268167612Ssimon if (ipxp->ipxp_socket->so_state & SS_NOFDREF) { 269167612Ssimon SOCK_UNLOCK(ipxp->ipxp_socket); 270167612Ssimon ipx_pcbdetach(ipxp); 271167612Ssimon } else 272167612Ssimon SOCK_UNLOCK(ipxp->ipxp_socket); 273167612Ssimon} 274167612Ssimon 275167612Ssimonvoid 276167612Ssimonipx_pcbdetach(ipxp) 277167612Ssimon struct ipxpcb *ipxp; 278167612Ssimon{ 279167612Ssimon struct socket *so = ipxp->ipxp_socket; 280167612Ssimon 281167612Ssimon so->so_pcb = 0; 282167612Ssimon SOCK_LOCK(so); 283167612Ssimon sotryfree(so); 284167612Ssimon if (ipxp->ipxp_route.ro_rt != NULL) 285167612Ssimon rtfree(ipxp->ipxp_route.ro_rt); 286167612Ssimon remque(ipxp); 287167612Ssimon FREE(ipxp, M_PCB); 288167612Ssimon} 289167612Ssimon 290167612Ssimonvoid 291167612Ssimonipx_setsockaddr(ipxp, nam) 292167612Ssimon register struct ipxpcb *ipxp; 293167612Ssimon struct sockaddr **nam; 294167612Ssimon{ 295167612Ssimon struct sockaddr_ipx *sipx, ssipx; 296167612Ssimon 297167612Ssimon sipx = &ssipx; 298167612Ssimon bzero((caddr_t)sipx, sizeof(*sipx)); 299167612Ssimon sipx->sipx_len = sizeof(*sipx); 300167612Ssimon sipx->sipx_family = AF_IPX; 301167612Ssimon sipx->sipx_addr = ipxp->ipxp_laddr; 302167612Ssimon *nam = dup_sockaddr((struct sockaddr *)sipx, 0); 303167612Ssimon} 304167612Ssimon 305167612Ssimonvoid 306167612Ssimonipx_setpeeraddr(ipxp, nam) 307167612Ssimon register struct ipxpcb *ipxp; 308167612Ssimon struct sockaddr **nam; 309167612Ssimon{ 310167612Ssimon struct sockaddr_ipx *sipx, ssipx; 311167612Ssimon 312167612Ssimon sipx = &ssipx; 313167612Ssimon bzero((caddr_t)sipx, sizeof(*sipx)); 314167612Ssimon sipx->sipx_len = sizeof(*sipx); 315167612Ssimon sipx->sipx_family = AF_IPX; 316167612Ssimon sipx->sipx_addr = ipxp->ipxp_faddr; 317167612Ssimon *nam = dup_sockaddr((struct sockaddr *)sipx, 0); 318167612Ssimon} 319167612Ssimon 320167612Ssimon/* 321167612Ssimon * Pass some notification to all connections of a protocol 322167612Ssimon * associated with address dst. Call the 323167612Ssimon * protocol specific routine to handle each connection. 324167612Ssimon * Also pass an extra paramter via the ipxpcb. (which may in fact 325167612Ssimon * be a parameter list!) 326167612Ssimon */ 327167612Ssimonvoid 328167612Ssimonipx_pcbnotify(dst, errno, notify, param) 329167612Ssimon register struct ipx_addr *dst; 330167612Ssimon int errno; 331167612Ssimon void (*notify)(struct ipxpcb *); 332167612Ssimon long param; 333167612Ssimon{ 334167612Ssimon register struct ipxpcb *ipxp, *oinp; 335167612Ssimon int s = splimp(); 336167612Ssimon 337167612Ssimon for (ipxp = (&ipxpcb)->ipxp_next; ipxp != (&ipxpcb);) { 338167612Ssimon if (!ipx_hosteq(*dst,ipxp->ipxp_faddr)) { 339167612Ssimon next: 340167612Ssimon ipxp = ipxp->ipxp_next; 341167612Ssimon continue; 342167612Ssimon } 343167612Ssimon if (ipxp->ipxp_socket == 0) 344167612Ssimon goto next; 345167612Ssimon if (errno) 346167612Ssimon ipxp->ipxp_socket->so_error = errno; 347167612Ssimon oinp = ipxp; 348167612Ssimon ipxp = ipxp->ipxp_next; 349167612Ssimon oinp->ipxp_notify_param = param; 350167612Ssimon (*notify)(oinp); 351167612Ssimon } 352167612Ssimon splx(s); 353167612Ssimon} 354167612Ssimon 355167612Ssimon#ifdef notdef 356167612Ssimon/* 357167612Ssimon * After a routing change, flush old routing 358167612Ssimon * and allocate a (hopefully) better one. 359167612Ssimon */ 360167612Ssimonipx_rtchange(ipxp) 361167612Ssimon struct ipxpcb *ipxp; 362167612Ssimon{ 363167612Ssimon if (ipxp->ipxp_route.ro_rt != NULL) { 364167612Ssimon rtfree(ipxp->ipxp_route.ro_rt); 365167612Ssimon ipxp->ipxp_route.ro_rt = NULL; 366167612Ssimon /* 367167612Ssimon * A new route can be allocated the next time 368167612Ssimon * output is attempted. 369167612Ssimon */ 370167612Ssimon } 371167612Ssimon /* SHOULD NOTIFY HIGHER-LEVEL PROTOCOLS */ 372167612Ssimon} 373167612Ssimon#endif 374167612Ssimon 375167612Ssimonstruct ipxpcb * 376167612Ssimonipx_pcblookup(faddr, lport, wildp) 377167612Ssimon struct ipx_addr *faddr; 378167612Ssimon u_short lport; 379167612Ssimon int wildp; 380167612Ssimon{ 381167612Ssimon register struct ipxpcb *ipxp, *match = 0; 382167612Ssimon int matchwild = 3, wildcard; 383167612Ssimon u_short fport; 384167612Ssimon 385167612Ssimon fport = faddr->x_port; 386167612Ssimon for (ipxp = (&ipxpcb)->ipxp_next; ipxp != (&ipxpcb); ipxp = ipxp->ipxp_next) { 387167612Ssimon if (ipxp->ipxp_lport != lport) 388167612Ssimon continue; 389167612Ssimon wildcard = 0; 390167612Ssimon if (ipx_nullhost(ipxp->ipxp_faddr)) { 391167612Ssimon if (!ipx_nullhost(*faddr)) 392167612Ssimon wildcard++; 393167612Ssimon } else { 394167612Ssimon if (ipx_nullhost(*faddr)) 395167612Ssimon wildcard++; 396167612Ssimon else { 397167612Ssimon if (!ipx_hosteq(ipxp->ipxp_faddr, *faddr)) 398167612Ssimon continue; 399167612Ssimon if (ipxp->ipxp_fport != fport) { 400167612Ssimon if (ipxp->ipxp_fport != 0) 401167612Ssimon continue; 402167612Ssimon else 403167612Ssimon wildcard++; 404167612Ssimon } 405167612Ssimon } 406167612Ssimon } 407167612Ssimon if (wildcard && wildp == 0) 408167612Ssimon continue; 409167612Ssimon if (wildcard < matchwild) { 410167612Ssimon match = ipxp; 411167612Ssimon matchwild = wildcard; 412167612Ssimon if (wildcard == 0) 413167612Ssimon break; 414167612Ssimon } 415167612Ssimon } 416167612Ssimon return (match); 417167612Ssimon} 418167612Ssimon