1df8bae1dSRodney W. Grimes /* 22469dd60SGarrett Wollman * Copyright (c) 1982, 1986, 1991, 1993, 1995 3df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 4df8bae1dSRodney W. Grimes * 5df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 6df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 7df8bae1dSRodney W. Grimes * are met: 8df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 9df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 10df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 11df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 12df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 13df8bae1dSRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 14df8bae1dSRodney W. Grimes * must display the following acknowledgement: 15df8bae1dSRodney W. Grimes * This product includes software developed by the University of 16df8bae1dSRodney W. Grimes * California, Berkeley and its contributors. 17df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 18df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 19df8bae1dSRodney W. Grimes * without specific prior written permission. 20df8bae1dSRodney W. Grimes * 21df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31df8bae1dSRodney W. Grimes * SUCH DAMAGE. 32df8bae1dSRodney W. Grimes * 332469dd60SGarrett Wollman * @(#)in_pcb.c 8.4 (Berkeley) 5/24/95 34c3229e05SDavid Greenman * $Id: in_pcb.c,v 1.37 1997/12/25 06:57:36 davidg Exp $ 35df8bae1dSRodney W. Grimes */ 36df8bae1dSRodney W. Grimes 37df8bae1dSRodney W. Grimes #include <sys/param.h> 38df8bae1dSRodney W. Grimes #include <sys/systm.h> 39df8bae1dSRodney W. Grimes #include <sys/malloc.h> 40df8bae1dSRodney W. Grimes #include <sys/mbuf.h> 41df8bae1dSRodney W. Grimes #include <sys/protosw.h> 42df8bae1dSRodney W. Grimes #include <sys/socket.h> 43df8bae1dSRodney W. Grimes #include <sys/socketvar.h> 44df8bae1dSRodney W. Grimes #include <sys/proc.h> 45101f9fc8SPeter Wemm #include <sys/kernel.h> 46101f9fc8SPeter Wemm #include <sys/sysctl.h> 47df8bae1dSRodney W. Grimes 48df8bae1dSRodney W. Grimes #include <net/if.h> 49df8bae1dSRodney W. Grimes #include <net/route.h> 50df8bae1dSRodney W. Grimes 51df8bae1dSRodney W. Grimes #include <netinet/in.h> 52df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h> 53df8bae1dSRodney W. Grimes #include <netinet/in_var.h> 54df8bae1dSRodney W. Grimes #include <netinet/ip_var.h> 55df8bae1dSRodney W. Grimes 56df8bae1dSRodney W. Grimes struct in_addr zeroin_addr; 57df8bae1dSRodney W. Grimes 58c3229e05SDavid Greenman static void in_pcbremlists __P((struct inpcb *)); 59bbd42ad0SPeter Wemm static void in_rtchange __P((struct inpcb *, int)); 60bbd42ad0SPeter Wemm 61101f9fc8SPeter Wemm /* 62101f9fc8SPeter Wemm * These configure the range of local port addresses assigned to 63101f9fc8SPeter Wemm * "unspecified" outgoing connections/packets/whatever. 64101f9fc8SPeter Wemm */ 65bbd42ad0SPeter Wemm static int ipport_lowfirstauto = IPPORT_RESERVED - 1; /* 1023 */ 66bbd42ad0SPeter Wemm static int ipport_lowlastauto = IPPORT_RESERVEDSTART; /* 600 */ 6733b3ac06SPeter Wemm static int ipport_firstauto = IPPORT_RESERVED; /* 1024 */ 6833b3ac06SPeter Wemm static int ipport_lastauto = IPPORT_USERRESERVED; /* 5000 */ 6933b3ac06SPeter Wemm static int ipport_hifirstauto = IPPORT_HIFIRSTAUTO; /* 40000 */ 7033b3ac06SPeter Wemm static int ipport_hilastauto = IPPORT_HILASTAUTO; /* 44999 */ 71101f9fc8SPeter Wemm 72bbd42ad0SPeter Wemm #define RANGECHK(var, min, max) \ 73bbd42ad0SPeter Wemm if ((var) < (min)) { (var) = (min); } \ 74bbd42ad0SPeter Wemm else if ((var) > (max)) { (var) = (max); } 75bbd42ad0SPeter Wemm 76bbd42ad0SPeter Wemm static int 77bbd42ad0SPeter Wemm sysctl_net_ipport_check SYSCTL_HANDLER_ARGS 78bbd42ad0SPeter Wemm { 79bbd42ad0SPeter Wemm int error = sysctl_handle_int(oidp, 80bbd42ad0SPeter Wemm oidp->oid_arg1, oidp->oid_arg2, req); 81bbd42ad0SPeter Wemm if (!error) { 82bbd42ad0SPeter Wemm RANGECHK(ipport_lowfirstauto, 1, IPPORT_RESERVED - 1); 83bbd42ad0SPeter Wemm RANGECHK(ipport_lowlastauto, 1, IPPORT_RESERVED - 1); 84bbd42ad0SPeter Wemm RANGECHK(ipport_firstauto, IPPORT_RESERVED, USHRT_MAX); 85bbd42ad0SPeter Wemm RANGECHK(ipport_lastauto, IPPORT_RESERVED, USHRT_MAX); 86bbd42ad0SPeter Wemm RANGECHK(ipport_hifirstauto, IPPORT_RESERVED, USHRT_MAX); 87bbd42ad0SPeter Wemm RANGECHK(ipport_hilastauto, IPPORT_RESERVED, USHRT_MAX); 88bbd42ad0SPeter Wemm } 89bbd42ad0SPeter Wemm return error; 90bbd42ad0SPeter Wemm } 91bbd42ad0SPeter Wemm 92bbd42ad0SPeter Wemm #undef RANGECHK 93bbd42ad0SPeter Wemm 9433b3ac06SPeter Wemm SYSCTL_NODE(_net_inet_ip, IPPROTO_IP, portrange, CTLFLAG_RW, 0, "IP Ports"); 9533b3ac06SPeter Wemm 96bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowfirst, CTLTYPE_INT|CTLFLAG_RW, 97bbd42ad0SPeter Wemm &ipport_lowfirstauto, 0, &sysctl_net_ipport_check, "I", ""); 98bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowlast, CTLTYPE_INT|CTLFLAG_RW, 99bbd42ad0SPeter Wemm &ipport_lowlastauto, 0, &sysctl_net_ipport_check, "I", ""); 100bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, first, CTLTYPE_INT|CTLFLAG_RW, 101bbd42ad0SPeter Wemm &ipport_firstauto, 0, &sysctl_net_ipport_check, "I", ""); 102bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, last, CTLTYPE_INT|CTLFLAG_RW, 103bbd42ad0SPeter Wemm &ipport_lastauto, 0, &sysctl_net_ipport_check, "I", ""); 104bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hifirst, CTLTYPE_INT|CTLFLAG_RW, 105bbd42ad0SPeter Wemm &ipport_hifirstauto, 0, &sysctl_net_ipport_check, "I", ""); 106bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hilast, CTLTYPE_INT|CTLFLAG_RW, 107bbd42ad0SPeter Wemm &ipport_hilastauto, 0, &sysctl_net_ipport_check, "I", ""); 1080312fbe9SPoul-Henning Kamp 109c3229e05SDavid Greenman /* 110c3229e05SDavid Greenman * in_pcb.c: manage the Protocol Control Blocks. 111c3229e05SDavid Greenman * 112c3229e05SDavid Greenman * NOTE: It is assumed that most of these functions will be called at 113c3229e05SDavid Greenman * splnet(). XXX - There are, unfortunately, a few exceptions to this 114c3229e05SDavid Greenman * rule that should be fixed. 115c3229e05SDavid Greenman */ 116c3229e05SDavid Greenman 117c3229e05SDavid Greenman /* 118c3229e05SDavid Greenman * Allocate a PCB and associate it with the socket. 119c3229e05SDavid Greenman */ 120df8bae1dSRodney W. Grimes int 121a29f300eSGarrett Wollman in_pcballoc(so, pcbinfo, p) 122df8bae1dSRodney W. Grimes struct socket *so; 12315bd2b43SDavid Greenman struct inpcbinfo *pcbinfo; 124a29f300eSGarrett Wollman struct proc *p; 125df8bae1dSRodney W. Grimes { 126df8bae1dSRodney W. Grimes register struct inpcb *inp; 127df8bae1dSRodney W. Grimes 128a29f300eSGarrett Wollman MALLOC(inp, struct inpcb *, sizeof(*inp), M_PCB, 129a29f300eSGarrett Wollman p ? M_WAITOK : M_NOWAIT); 130df8bae1dSRodney W. Grimes if (inp == NULL) 131df8bae1dSRodney W. Grimes return (ENOBUFS); 132df8bae1dSRodney W. Grimes bzero((caddr_t)inp, sizeof(*inp)); 13315bd2b43SDavid Greenman inp->inp_pcbinfo = pcbinfo; 134df8bae1dSRodney W. Grimes inp->inp_socket = so; 13515bd2b43SDavid Greenman LIST_INSERT_HEAD(pcbinfo->listhead, inp, inp_list); 136df8bae1dSRodney W. Grimes so->so_pcb = (caddr_t)inp; 137df8bae1dSRodney W. Grimes return (0); 138df8bae1dSRodney W. Grimes } 139df8bae1dSRodney W. Grimes 140df8bae1dSRodney W. Grimes int 141a29f300eSGarrett Wollman in_pcbbind(inp, nam, p) 142df8bae1dSRodney W. Grimes register struct inpcb *inp; 14357bf258eSGarrett Wollman struct sockaddr *nam; 144a29f300eSGarrett Wollman struct proc *p; 145df8bae1dSRodney W. Grimes { 146df8bae1dSRodney W. Grimes register struct socket *so = inp->inp_socket; 14737bd2b30SPeter Wemm unsigned short *lastport; 14815bd2b43SDavid Greenman struct sockaddr_in *sin; 149c3229e05SDavid Greenman struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; 150df8bae1dSRodney W. Grimes u_short lport = 0; 151df8bae1dSRodney W. Grimes int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); 152df8bae1dSRodney W. Grimes int error; 153df8bae1dSRodney W. Grimes 15459562606SGarrett Wollman if (TAILQ_EMPTY(&in_ifaddrhead)) /* XXX broken! */ 155df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 156df8bae1dSRodney W. Grimes if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY) 157df8bae1dSRodney W. Grimes return (EINVAL); 158c3229e05SDavid Greenman if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) 1596d6a026bSDavid Greenman wild = 1; 160df8bae1dSRodney W. Grimes if (nam) { 16157bf258eSGarrett Wollman sin = (struct sockaddr_in *)nam; 16257bf258eSGarrett Wollman if (nam->sa_len != sizeof (*sin)) 163df8bae1dSRodney W. Grimes return (EINVAL); 164df8bae1dSRodney W. Grimes #ifdef notdef 165df8bae1dSRodney W. Grimes /* 166df8bae1dSRodney W. Grimes * We should check the family, but old programs 167df8bae1dSRodney W. Grimes * incorrectly fail to initialize it. 168df8bae1dSRodney W. Grimes */ 169df8bae1dSRodney W. Grimes if (sin->sin_family != AF_INET) 170df8bae1dSRodney W. Grimes return (EAFNOSUPPORT); 171df8bae1dSRodney W. Grimes #endif 172df8bae1dSRodney W. Grimes lport = sin->sin_port; 173df8bae1dSRodney W. Grimes if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { 174df8bae1dSRodney W. Grimes /* 175df8bae1dSRodney W. Grimes * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; 176df8bae1dSRodney W. Grimes * allow complete duplication of binding if 177df8bae1dSRodney W. Grimes * SO_REUSEPORT is set, or if SO_REUSEADDR is set 178df8bae1dSRodney W. Grimes * and a multicast address is bound on both 179df8bae1dSRodney W. Grimes * new and duplicated sockets. 180df8bae1dSRodney W. Grimes */ 181df8bae1dSRodney W. Grimes if (so->so_options & SO_REUSEADDR) 182df8bae1dSRodney W. Grimes reuseport = SO_REUSEADDR|SO_REUSEPORT; 183df8bae1dSRodney W. Grimes } else if (sin->sin_addr.s_addr != INADDR_ANY) { 184df8bae1dSRodney W. Grimes sin->sin_port = 0; /* yech... */ 185df8bae1dSRodney W. Grimes if (ifa_ifwithaddr((struct sockaddr *)sin) == 0) 186df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 187df8bae1dSRodney W. Grimes } 188df8bae1dSRodney W. Grimes if (lport) { 189df8bae1dSRodney W. Grimes struct inpcb *t; 190df8bae1dSRodney W. Grimes 191df8bae1dSRodney W. Grimes /* GROSS */ 19257bf258eSGarrett Wollman if (ntohs(lport) < IPPORT_RESERVED && p && 19390d0144cSAlexander Langer suser(p->p_ucred, &p->p_acflag)) 1942469dd60SGarrett Wollman return (EACCES); 195c3229e05SDavid Greenman t = in_pcblookup_local(pcbinfo, sin->sin_addr, 196c3229e05SDavid Greenman lport, wild); 197df8bae1dSRodney W. Grimes if (t && (reuseport & t->inp_socket->so_options) == 0) 198df8bae1dSRodney W. Grimes return (EADDRINUSE); 199df8bae1dSRodney W. Grimes } 200df8bae1dSRodney W. Grimes inp->inp_laddr = sin->sin_addr; 201df8bae1dSRodney W. Grimes } 20233b3ac06SPeter Wemm if (lport == 0) { 20333b3ac06SPeter Wemm ushort first, last; 20433b3ac06SPeter Wemm int count; 20533b3ac06SPeter Wemm 206321a2846SPoul-Henning Kamp inp->inp_flags |= INP_ANONPORT; 207321a2846SPoul-Henning Kamp 20833b3ac06SPeter Wemm if (inp->inp_flags & INP_HIGHPORT) { 20933b3ac06SPeter Wemm first = ipport_hifirstauto; /* sysctl */ 21033b3ac06SPeter Wemm last = ipport_hilastauto; 211c3229e05SDavid Greenman lastport = &pcbinfo->lasthi; 21233b3ac06SPeter Wemm } else if (inp->inp_flags & INP_LOWPORT) { 21357bf258eSGarrett Wollman if (p && (error = suser(p->p_ucred, &p->p_acflag))) 214a29f300eSGarrett Wollman return error; 215bbd42ad0SPeter Wemm first = ipport_lowfirstauto; /* 1023 */ 216bbd42ad0SPeter Wemm last = ipport_lowlastauto; /* 600 */ 217c3229e05SDavid Greenman lastport = &pcbinfo->lastlow; 21833b3ac06SPeter Wemm } else { 21933b3ac06SPeter Wemm first = ipport_firstauto; /* sysctl */ 22033b3ac06SPeter Wemm last = ipport_lastauto; 221c3229e05SDavid Greenman lastport = &pcbinfo->lastport; 22233b3ac06SPeter Wemm } 22333b3ac06SPeter Wemm /* 22433b3ac06SPeter Wemm * Simple check to ensure all ports are not used up causing 22533b3ac06SPeter Wemm * a deadlock here. 22633b3ac06SPeter Wemm * 22733b3ac06SPeter Wemm * We split the two cases (up and down) so that the direction 22833b3ac06SPeter Wemm * is not being tested on each round of the loop. 22933b3ac06SPeter Wemm */ 23033b3ac06SPeter Wemm if (first > last) { 23133b3ac06SPeter Wemm /* 23233b3ac06SPeter Wemm * counting down 23333b3ac06SPeter Wemm */ 23433b3ac06SPeter Wemm count = first - last; 23533b3ac06SPeter Wemm 236df8bae1dSRodney W. Grimes do { 237c3229e05SDavid Greenman if (count-- < 0) { /* completely used? */ 238c3229e05SDavid Greenman /* 239c3229e05SDavid Greenman * Undo any address bind that may have 240c3229e05SDavid Greenman * occurred above. 241c3229e05SDavid Greenman */ 242c3229e05SDavid Greenman inp->inp_laddr.s_addr = INADDR_ANY; 243c3229e05SDavid Greenman return (EAGAIN); 244c3229e05SDavid Greenman } 24533b3ac06SPeter Wemm --*lastport; 24633b3ac06SPeter Wemm if (*lastport > first || *lastport < last) 24733b3ac06SPeter Wemm *lastport = first; 24815bd2b43SDavid Greenman lport = htons(*lastport); 249c3229e05SDavid Greenman } while (in_pcblookup_local(pcbinfo, 250c3229e05SDavid Greenman inp->inp_laddr, lport, wild)); 25133b3ac06SPeter Wemm } else { 25233b3ac06SPeter Wemm /* 25333b3ac06SPeter Wemm * counting up 25433b3ac06SPeter Wemm */ 25533b3ac06SPeter Wemm count = last - first; 25633b3ac06SPeter Wemm 25733b3ac06SPeter Wemm do { 258c3229e05SDavid Greenman if (count-- < 0) { /* completely used? */ 259c3229e05SDavid Greenman /* 260c3229e05SDavid Greenman * Undo any address bind that may have 261c3229e05SDavid Greenman * occurred above. 262c3229e05SDavid Greenman */ 263c3229e05SDavid Greenman inp->inp_laddr.s_addr = INADDR_ANY; 264c3229e05SDavid Greenman return (EAGAIN); 265c3229e05SDavid Greenman } 26633b3ac06SPeter Wemm ++*lastport; 26733b3ac06SPeter Wemm if (*lastport < first || *lastport > last) 26833b3ac06SPeter Wemm *lastport = first; 26933b3ac06SPeter Wemm lport = htons(*lastport); 270c3229e05SDavid Greenman } while (in_pcblookup_local(pcbinfo, 271c3229e05SDavid Greenman inp->inp_laddr, lport, wild)); 27233b3ac06SPeter Wemm } 27333b3ac06SPeter Wemm } 274df8bae1dSRodney W. Grimes inp->inp_lport = lport; 275c3229e05SDavid Greenman if (in_pcbinshash(inp) != 0) { 276c3229e05SDavid Greenman inp->inp_laddr.s_addr = INADDR_ANY; 277c3229e05SDavid Greenman inp->inp_lport = 0; 278c3229e05SDavid Greenman return (EAGAIN); 279c3229e05SDavid Greenman } 280df8bae1dSRodney W. Grimes return (0); 281df8bae1dSRodney W. Grimes } 282df8bae1dSRodney W. Grimes 283999f1343SGarrett Wollman /* 284999f1343SGarrett Wollman * Transform old in_pcbconnect() into an inner subroutine for new 285999f1343SGarrett Wollman * in_pcbconnect(): Do some validity-checking on the remote 286999f1343SGarrett Wollman * address (in mbuf 'nam') and then determine local host address 287999f1343SGarrett Wollman * (i.e., which interface) to use to access that remote host. 288999f1343SGarrett Wollman * 289999f1343SGarrett Wollman * This preserves definition of in_pcbconnect(), while supporting a 290999f1343SGarrett Wollman * slightly different version for T/TCP. (This is more than 291999f1343SGarrett Wollman * a bit of a kludge, but cleaning up the internal interfaces would 292999f1343SGarrett Wollman * have forced minor changes in every protocol). 293999f1343SGarrett Wollman */ 294999f1343SGarrett Wollman 295999f1343SGarrett Wollman int 296999f1343SGarrett Wollman in_pcbladdr(inp, nam, plocal_sin) 297999f1343SGarrett Wollman register struct inpcb *inp; 29857bf258eSGarrett Wollman struct sockaddr *nam; 299999f1343SGarrett Wollman struct sockaddr_in **plocal_sin; 300999f1343SGarrett Wollman { 301df8bae1dSRodney W. Grimes struct in_ifaddr *ia; 30257bf258eSGarrett Wollman register struct sockaddr_in *sin = (struct sockaddr_in *)nam; 303df8bae1dSRodney W. Grimes 30457bf258eSGarrett Wollman if (nam->sa_len != sizeof (*sin)) 305df8bae1dSRodney W. Grimes return (EINVAL); 306df8bae1dSRodney W. Grimes if (sin->sin_family != AF_INET) 307df8bae1dSRodney W. Grimes return (EAFNOSUPPORT); 308df8bae1dSRodney W. Grimes if (sin->sin_port == 0) 309df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 31059562606SGarrett Wollman if (!TAILQ_EMPTY(&in_ifaddrhead)) { 311df8bae1dSRodney W. Grimes /* 312df8bae1dSRodney W. Grimes * If the destination address is INADDR_ANY, 313df8bae1dSRodney W. Grimes * use the primary local address. 314df8bae1dSRodney W. Grimes * If the supplied address is INADDR_BROADCAST, 315df8bae1dSRodney W. Grimes * and the primary interface supports broadcast, 316df8bae1dSRodney W. Grimes * choose the broadcast address for that interface. 317df8bae1dSRodney W. Grimes */ 318df8bae1dSRodney W. Grimes #define satosin(sa) ((struct sockaddr_in *)(sa)) 319df8bae1dSRodney W. Grimes #define sintosa(sin) ((struct sockaddr *)(sin)) 320df8bae1dSRodney W. Grimes #define ifatoia(ifa) ((struct in_ifaddr *)(ifa)) 321df8bae1dSRodney W. Grimes if (sin->sin_addr.s_addr == INADDR_ANY) 32259562606SGarrett Wollman sin->sin_addr = IA_SIN(in_ifaddrhead.tqh_first)->sin_addr; 323df8bae1dSRodney W. Grimes else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST && 32459562606SGarrett Wollman (in_ifaddrhead.tqh_first->ia_ifp->if_flags & IFF_BROADCAST)) 32559562606SGarrett Wollman sin->sin_addr = satosin(&in_ifaddrhead.tqh_first->ia_broadaddr)->sin_addr; 326df8bae1dSRodney W. Grimes } 327df8bae1dSRodney W. Grimes if (inp->inp_laddr.s_addr == INADDR_ANY) { 328df8bae1dSRodney W. Grimes register struct route *ro; 329df8bae1dSRodney W. Grimes 330df8bae1dSRodney W. Grimes ia = (struct in_ifaddr *)0; 331df8bae1dSRodney W. Grimes /* 332df8bae1dSRodney W. Grimes * If route is known or can be allocated now, 333df8bae1dSRodney W. Grimes * our src addr is taken from the i/f, else punt. 334df8bae1dSRodney W. Grimes */ 335df8bae1dSRodney W. Grimes ro = &inp->inp_route; 336df8bae1dSRodney W. Grimes if (ro->ro_rt && 337df8bae1dSRodney W. Grimes (satosin(&ro->ro_dst)->sin_addr.s_addr != 338df8bae1dSRodney W. Grimes sin->sin_addr.s_addr || 339df8bae1dSRodney W. Grimes inp->inp_socket->so_options & SO_DONTROUTE)) { 340df8bae1dSRodney W. Grimes RTFREE(ro->ro_rt); 341df8bae1dSRodney W. Grimes ro->ro_rt = (struct rtentry *)0; 342df8bae1dSRodney W. Grimes } 343df8bae1dSRodney W. Grimes if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/ 344df8bae1dSRodney W. Grimes (ro->ro_rt == (struct rtentry *)0 || 345df8bae1dSRodney W. Grimes ro->ro_rt->rt_ifp == (struct ifnet *)0)) { 346df8bae1dSRodney W. Grimes /* No route yet, so try to acquire one */ 347df8bae1dSRodney W. Grimes ro->ro_dst.sa_family = AF_INET; 348df8bae1dSRodney W. Grimes ro->ro_dst.sa_len = sizeof(struct sockaddr_in); 349df8bae1dSRodney W. Grimes ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = 350df8bae1dSRodney W. Grimes sin->sin_addr; 351df8bae1dSRodney W. Grimes rtalloc(ro); 352df8bae1dSRodney W. Grimes } 353df8bae1dSRodney W. Grimes /* 354df8bae1dSRodney W. Grimes * If we found a route, use the address 355df8bae1dSRodney W. Grimes * corresponding to the outgoing interface 356df8bae1dSRodney W. Grimes * unless it is the loopback (in case a route 357df8bae1dSRodney W. Grimes * to our address on another net goes to loopback). 358df8bae1dSRodney W. Grimes */ 359df8bae1dSRodney W. Grimes if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK)) 360df8bae1dSRodney W. Grimes ia = ifatoia(ro->ro_rt->rt_ifa); 361df8bae1dSRodney W. Grimes if (ia == 0) { 362df8bae1dSRodney W. Grimes u_short fport = sin->sin_port; 363df8bae1dSRodney W. Grimes 364df8bae1dSRodney W. Grimes sin->sin_port = 0; 365df8bae1dSRodney W. Grimes ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin))); 366df8bae1dSRodney W. Grimes if (ia == 0) 367df8bae1dSRodney W. Grimes ia = ifatoia(ifa_ifwithnet(sintosa(sin))); 368df8bae1dSRodney W. Grimes sin->sin_port = fport; 369df8bae1dSRodney W. Grimes if (ia == 0) 37059562606SGarrett Wollman ia = in_ifaddrhead.tqh_first; 371df8bae1dSRodney W. Grimes if (ia == 0) 372df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 373df8bae1dSRodney W. Grimes } 374df8bae1dSRodney W. Grimes /* 375df8bae1dSRodney W. Grimes * If the destination address is multicast and an outgoing 376df8bae1dSRodney W. Grimes * interface has been set as a multicast option, use the 377df8bae1dSRodney W. Grimes * address of that interface as our source address. 378df8bae1dSRodney W. Grimes */ 379df8bae1dSRodney W. Grimes if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) && 380df8bae1dSRodney W. Grimes inp->inp_moptions != NULL) { 381df8bae1dSRodney W. Grimes struct ip_moptions *imo; 382df8bae1dSRodney W. Grimes struct ifnet *ifp; 383df8bae1dSRodney W. Grimes 384df8bae1dSRodney W. Grimes imo = inp->inp_moptions; 385df8bae1dSRodney W. Grimes if (imo->imo_multicast_ifp != NULL) { 386df8bae1dSRodney W. Grimes ifp = imo->imo_multicast_ifp; 38759562606SGarrett Wollman for (ia = in_ifaddrhead.tqh_first; ia; 38859562606SGarrett Wollman ia = ia->ia_link.tqe_next) 389df8bae1dSRodney W. Grimes if (ia->ia_ifp == ifp) 390df8bae1dSRodney W. Grimes break; 391df8bae1dSRodney W. Grimes if (ia == 0) 392df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 393df8bae1dSRodney W. Grimes } 394df8bae1dSRodney W. Grimes } 395999f1343SGarrett Wollman /* 396999f1343SGarrett Wollman * Don't do pcblookup call here; return interface in plocal_sin 397999f1343SGarrett Wollman * and exit to caller, that will do the lookup. 398999f1343SGarrett Wollman */ 399999f1343SGarrett Wollman *plocal_sin = &ia->ia_addr; 400999f1343SGarrett Wollman 401999f1343SGarrett Wollman } 402999f1343SGarrett Wollman return(0); 403999f1343SGarrett Wollman } 404999f1343SGarrett Wollman 405999f1343SGarrett Wollman /* 406999f1343SGarrett Wollman * Outer subroutine: 407999f1343SGarrett Wollman * Connect from a socket to a specified address. 408999f1343SGarrett Wollman * Both address and port must be specified in argument sin. 409999f1343SGarrett Wollman * If don't have a local address for this socket yet, 410999f1343SGarrett Wollman * then pick one. 411999f1343SGarrett Wollman */ 412999f1343SGarrett Wollman int 413a29f300eSGarrett Wollman in_pcbconnect(inp, nam, p) 414999f1343SGarrett Wollman register struct inpcb *inp; 41557bf258eSGarrett Wollman struct sockaddr *nam; 416a29f300eSGarrett Wollman struct proc *p; 417999f1343SGarrett Wollman { 418999f1343SGarrett Wollman struct sockaddr_in *ifaddr; 41957bf258eSGarrett Wollman register struct sockaddr_in *sin = (struct sockaddr_in *)nam; 420999f1343SGarrett Wollman int error; 421999f1343SGarrett Wollman 422999f1343SGarrett Wollman /* 423999f1343SGarrett Wollman * Call inner routine, to assign local interface address. 424999f1343SGarrett Wollman */ 425999f1343SGarrett Wollman if (error = in_pcbladdr(inp, nam, &ifaddr)) 426999f1343SGarrett Wollman return(error); 427999f1343SGarrett Wollman 428c3229e05SDavid Greenman if (in_pcblookup_hash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port, 429df8bae1dSRodney W. Grimes inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 430c3229e05SDavid Greenman inp->inp_lport, 0) != NULL) { 431df8bae1dSRodney W. Grimes return (EADDRINUSE); 432c3229e05SDavid Greenman } 433df8bae1dSRodney W. Grimes if (inp->inp_laddr.s_addr == INADDR_ANY) { 434df8bae1dSRodney W. Grimes if (inp->inp_lport == 0) 43557bf258eSGarrett Wollman (void)in_pcbbind(inp, (struct sockaddr *)0, p); 436df8bae1dSRodney W. Grimes inp->inp_laddr = ifaddr->sin_addr; 437df8bae1dSRodney W. Grimes } 438df8bae1dSRodney W. Grimes inp->inp_faddr = sin->sin_addr; 439df8bae1dSRodney W. Grimes inp->inp_fport = sin->sin_port; 44015bd2b43SDavid Greenman in_pcbrehash(inp); 441df8bae1dSRodney W. Grimes return (0); 442df8bae1dSRodney W. Grimes } 443df8bae1dSRodney W. Grimes 44426f9a767SRodney W. Grimes void 445df8bae1dSRodney W. Grimes in_pcbdisconnect(inp) 446df8bae1dSRodney W. Grimes struct inpcb *inp; 447df8bae1dSRodney W. Grimes { 448df8bae1dSRodney W. Grimes 449df8bae1dSRodney W. Grimes inp->inp_faddr.s_addr = INADDR_ANY; 450df8bae1dSRodney W. Grimes inp->inp_fport = 0; 45115bd2b43SDavid Greenman in_pcbrehash(inp); 452df8bae1dSRodney W. Grimes if (inp->inp_socket->so_state & SS_NOFDREF) 453df8bae1dSRodney W. Grimes in_pcbdetach(inp); 454df8bae1dSRodney W. Grimes } 455df8bae1dSRodney W. Grimes 45626f9a767SRodney W. Grimes void 457df8bae1dSRodney W. Grimes in_pcbdetach(inp) 458df8bae1dSRodney W. Grimes struct inpcb *inp; 459df8bae1dSRodney W. Grimes { 460df8bae1dSRodney W. Grimes struct socket *so = inp->inp_socket; 461df8bae1dSRodney W. Grimes 462c3229e05SDavid Greenman in_pcbremlists(inp); 463df8bae1dSRodney W. Grimes so->so_pcb = 0; 464df8bae1dSRodney W. Grimes sofree(so); 465df8bae1dSRodney W. Grimes if (inp->inp_options) 466df8bae1dSRodney W. Grimes (void)m_free(inp->inp_options); 467df8bae1dSRodney W. Grimes if (inp->inp_route.ro_rt) 468df8bae1dSRodney W. Grimes rtfree(inp->inp_route.ro_rt); 469df8bae1dSRodney W. Grimes ip_freemoptions(inp->inp_moptions); 470df8bae1dSRodney W. Grimes FREE(inp, M_PCB); 471df8bae1dSRodney W. Grimes } 472df8bae1dSRodney W. Grimes 473117bcae7SGarrett Wollman /* 474117bcae7SGarrett Wollman * The calling convention of in_setsockaddr() and in_setpeeraddr() was 475117bcae7SGarrett Wollman * modified to match the pru_sockaddr() and pru_peeraddr() entry points 476117bcae7SGarrett Wollman * in struct pr_usrreqs, so that protocols can just reference then directly 477117bcae7SGarrett Wollman * without the need for a wrapper function. The socket must have a valid 478117bcae7SGarrett Wollman * (i.e., non-nil) PCB, but it should be impossible to get an invalid one 479117bcae7SGarrett Wollman * except through a kernel programming error, so it is acceptable to panic 48057bf258eSGarrett Wollman * (or in this case trap) if the PCB is invalid. (Actually, we don't trap 48157bf258eSGarrett Wollman * because there actually /is/ a programming error somewhere... XXX) 482117bcae7SGarrett Wollman */ 483117bcae7SGarrett Wollman int 484117bcae7SGarrett Wollman in_setsockaddr(so, nam) 485117bcae7SGarrett Wollman struct socket *so; 48657bf258eSGarrett Wollman struct sockaddr **nam; 487df8bae1dSRodney W. Grimes { 488fdc984f7STor Egge int s; 489fdc984f7STor Egge register struct inpcb *inp; 490df8bae1dSRodney W. Grimes register struct sockaddr_in *sin; 491df8bae1dSRodney W. Grimes 492c3229e05SDavid Greenman /* 493c3229e05SDavid Greenman * Do the malloc first in case it blocks. 494c3229e05SDavid Greenman */ 49542fa505bSDavid Greenman MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_WAITOK); 49642fa505bSDavid Greenman bzero(sin, sizeof *sin); 49742fa505bSDavid Greenman sin->sin_family = AF_INET; 49842fa505bSDavid Greenman sin->sin_len = sizeof(*sin); 49942fa505bSDavid Greenman 500fdc984f7STor Egge s = splnet(); 501fdc984f7STor Egge inp = sotoinpcb(so); 502db112f04STor Egge if (!inp) { 503db112f04STor Egge splx(s); 50442fa505bSDavid Greenman free(sin, M_SONAME); 505db112f04STor Egge return EINVAL; 506db112f04STor Egge } 507df8bae1dSRodney W. Grimes sin->sin_port = inp->inp_lport; 508df8bae1dSRodney W. Grimes sin->sin_addr = inp->inp_laddr; 509db112f04STor Egge splx(s); 51042fa505bSDavid Greenman 51142fa505bSDavid Greenman *nam = (struct sockaddr *)sin; 512117bcae7SGarrett Wollman return 0; 513df8bae1dSRodney W. Grimes } 514df8bae1dSRodney W. Grimes 515117bcae7SGarrett Wollman int 516117bcae7SGarrett Wollman in_setpeeraddr(so, nam) 517117bcae7SGarrett Wollman struct socket *so; 51857bf258eSGarrett Wollman struct sockaddr **nam; 519df8bae1dSRodney W. Grimes { 520fdc984f7STor Egge int s; 521fdc984f7STor Egge struct inpcb *inp; 522df8bae1dSRodney W. Grimes register struct sockaddr_in *sin; 523df8bae1dSRodney W. Grimes 524c3229e05SDavid Greenman /* 525c3229e05SDavid Greenman * Do the malloc first in case it blocks. 526c3229e05SDavid Greenman */ 52742fa505bSDavid Greenman MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_WAITOK); 52842fa505bSDavid Greenman bzero((caddr_t)sin, sizeof (*sin)); 52942fa505bSDavid Greenman sin->sin_family = AF_INET; 53042fa505bSDavid Greenman sin->sin_len = sizeof(*sin); 53142fa505bSDavid Greenman 532fdc984f7STor Egge s = splnet(); 533fdc984f7STor Egge inp = sotoinpcb(so); 534db112f04STor Egge if (!inp) { 535db112f04STor Egge splx(s); 53642fa505bSDavid Greenman free(sin, M_SONAME); 537db112f04STor Egge return EINVAL; 538db112f04STor Egge } 539df8bae1dSRodney W. Grimes sin->sin_port = inp->inp_fport; 540df8bae1dSRodney W. Grimes sin->sin_addr = inp->inp_faddr; 541db112f04STor Egge splx(s); 54242fa505bSDavid Greenman 54342fa505bSDavid Greenman *nam = (struct sockaddr *)sin; 544117bcae7SGarrett Wollman return 0; 545df8bae1dSRodney W. Grimes } 546df8bae1dSRodney W. Grimes 547df8bae1dSRodney W. Grimes /* 548df8bae1dSRodney W. Grimes * Pass some notification to all connections of a protocol 549df8bae1dSRodney W. Grimes * associated with address dst. The local address and/or port numbers 550df8bae1dSRodney W. Grimes * may be specified to limit the search. The "usual action" will be 551df8bae1dSRodney W. Grimes * taken, depending on the ctlinput cmd. The caller must filter any 552df8bae1dSRodney W. Grimes * cmds that are uninteresting (e.g., no error in the map). 553df8bae1dSRodney W. Grimes * Call the protocol specific routine (if any) to report 554df8bae1dSRodney W. Grimes * any errors for each matching socket. 555df8bae1dSRodney W. Grimes */ 55626f9a767SRodney W. Grimes void 557df8bae1dSRodney W. Grimes in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify) 55815bd2b43SDavid Greenman struct inpcbhead *head; 559df8bae1dSRodney W. Grimes struct sockaddr *dst; 560df8bae1dSRodney W. Grimes u_int fport_arg, lport_arg; 561df8bae1dSRodney W. Grimes struct in_addr laddr; 562df8bae1dSRodney W. Grimes int cmd; 563df8bae1dSRodney W. Grimes void (*notify) __P((struct inpcb *, int)); 564df8bae1dSRodney W. Grimes { 565df8bae1dSRodney W. Grimes register struct inpcb *inp, *oinp; 566df8bae1dSRodney W. Grimes struct in_addr faddr; 567df8bae1dSRodney W. Grimes u_short fport = fport_arg, lport = lport_arg; 5687bc4aca7SDavid Greenman int errno, s; 569df8bae1dSRodney W. Grimes 570df8bae1dSRodney W. Grimes if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET) 571df8bae1dSRodney W. Grimes return; 572df8bae1dSRodney W. Grimes faddr = ((struct sockaddr_in *)dst)->sin_addr; 573df8bae1dSRodney W. Grimes if (faddr.s_addr == INADDR_ANY) 574df8bae1dSRodney W. Grimes return; 575df8bae1dSRodney W. Grimes 576df8bae1dSRodney W. Grimes /* 577df8bae1dSRodney W. Grimes * Redirects go to all references to the destination, 578df8bae1dSRodney W. Grimes * and use in_rtchange to invalidate the route cache. 579df8bae1dSRodney W. Grimes * Dead host indications: notify all references to the destination. 580df8bae1dSRodney W. Grimes * Otherwise, if we have knowledge of the local port and address, 581df8bae1dSRodney W. Grimes * deliver only to that socket. 582df8bae1dSRodney W. Grimes */ 583df8bae1dSRodney W. Grimes if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) { 584df8bae1dSRodney W. Grimes fport = 0; 585df8bae1dSRodney W. Grimes lport = 0; 586df8bae1dSRodney W. Grimes laddr.s_addr = 0; 587df8bae1dSRodney W. Grimes if (cmd != PRC_HOSTDEAD) 588df8bae1dSRodney W. Grimes notify = in_rtchange; 589df8bae1dSRodney W. Grimes } 590df8bae1dSRodney W. Grimes errno = inetctlerrmap[cmd]; 5917bc4aca7SDavid Greenman s = splnet(); 59215bd2b43SDavid Greenman for (inp = head->lh_first; inp != NULL;) { 593df8bae1dSRodney W. Grimes if (inp->inp_faddr.s_addr != faddr.s_addr || 594df8bae1dSRodney W. Grimes inp->inp_socket == 0 || 595df8bae1dSRodney W. Grimes (lport && inp->inp_lport != lport) || 596df8bae1dSRodney W. Grimes (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) || 597df8bae1dSRodney W. Grimes (fport && inp->inp_fport != fport)) { 59815bd2b43SDavid Greenman inp = inp->inp_list.le_next; 599df8bae1dSRodney W. Grimes continue; 600df8bae1dSRodney W. Grimes } 601df8bae1dSRodney W. Grimes oinp = inp; 60215bd2b43SDavid Greenman inp = inp->inp_list.le_next; 603df8bae1dSRodney W. Grimes if (notify) 604df8bae1dSRodney W. Grimes (*notify)(oinp, errno); 605df8bae1dSRodney W. Grimes } 6067bc4aca7SDavid Greenman splx(s); 607df8bae1dSRodney W. Grimes } 608df8bae1dSRodney W. Grimes 609df8bae1dSRodney W. Grimes /* 610df8bae1dSRodney W. Grimes * Check for alternatives when higher level complains 611df8bae1dSRodney W. Grimes * about service problems. For now, invalidate cached 612df8bae1dSRodney W. Grimes * routing information. If the route was created dynamically 613df8bae1dSRodney W. Grimes * (by a redirect), time to try a default gateway again. 614df8bae1dSRodney W. Grimes */ 61526f9a767SRodney W. Grimes void 616df8bae1dSRodney W. Grimes in_losing(inp) 617df8bae1dSRodney W. Grimes struct inpcb *inp; 618df8bae1dSRodney W. Grimes { 619df8bae1dSRodney W. Grimes register struct rtentry *rt; 620df8bae1dSRodney W. Grimes struct rt_addrinfo info; 621df8bae1dSRodney W. Grimes 622df8bae1dSRodney W. Grimes if ((rt = inp->inp_route.ro_rt)) { 623df8bae1dSRodney W. Grimes inp->inp_route.ro_rt = 0; 624df8bae1dSRodney W. Grimes bzero((caddr_t)&info, sizeof(info)); 625df8bae1dSRodney W. Grimes info.rti_info[RTAX_DST] = 626df8bae1dSRodney W. Grimes (struct sockaddr *)&inp->inp_route.ro_dst; 627df8bae1dSRodney W. Grimes info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; 628df8bae1dSRodney W. Grimes info.rti_info[RTAX_NETMASK] = rt_mask(rt); 629df8bae1dSRodney W. Grimes rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0); 630df8bae1dSRodney W. Grimes if (rt->rt_flags & RTF_DYNAMIC) 631df8bae1dSRodney W. Grimes (void) rtrequest(RTM_DELETE, rt_key(rt), 632df8bae1dSRodney W. Grimes rt->rt_gateway, rt_mask(rt), rt->rt_flags, 633df8bae1dSRodney W. Grimes (struct rtentry **)0); 634df8bae1dSRodney W. Grimes else 635df8bae1dSRodney W. Grimes /* 636df8bae1dSRodney W. Grimes * A new route can be allocated 637df8bae1dSRodney W. Grimes * the next time output is attempted. 638df8bae1dSRodney W. Grimes */ 639df8bae1dSRodney W. Grimes rtfree(rt); 640df8bae1dSRodney W. Grimes } 641df8bae1dSRodney W. Grimes } 642df8bae1dSRodney W. Grimes 643df8bae1dSRodney W. Grimes /* 644df8bae1dSRodney W. Grimes * After a routing change, flush old routing 645df8bae1dSRodney W. Grimes * and allocate a (hopefully) better one. 646df8bae1dSRodney W. Grimes */ 6470312fbe9SPoul-Henning Kamp static void 648df8bae1dSRodney W. Grimes in_rtchange(inp, errno) 649df8bae1dSRodney W. Grimes register struct inpcb *inp; 650df8bae1dSRodney W. Grimes int errno; 651df8bae1dSRodney W. Grimes { 652df8bae1dSRodney W. Grimes if (inp->inp_route.ro_rt) { 653df8bae1dSRodney W. Grimes rtfree(inp->inp_route.ro_rt); 654df8bae1dSRodney W. Grimes inp->inp_route.ro_rt = 0; 655df8bae1dSRodney W. Grimes /* 656df8bae1dSRodney W. Grimes * A new route can be allocated the next time 657df8bae1dSRodney W. Grimes * output is attempted. 658df8bae1dSRodney W. Grimes */ 659df8bae1dSRodney W. Grimes } 660df8bae1dSRodney W. Grimes } 661df8bae1dSRodney W. Grimes 662c3229e05SDavid Greenman /* 663c3229e05SDavid Greenman * Lookup a PCB based on the local address and port. 664c3229e05SDavid Greenman */ 665df8bae1dSRodney W. Grimes struct inpcb * 666c3229e05SDavid Greenman in_pcblookup_local(pcbinfo, laddr, lport_arg, wild_okay) 6676d6a026bSDavid Greenman struct inpcbinfo *pcbinfo; 668c3229e05SDavid Greenman struct in_addr laddr; 669c3229e05SDavid Greenman u_int lport_arg; 6706d6a026bSDavid Greenman int wild_okay; 671df8bae1dSRodney W. Grimes { 6723dbdc25cSDavid Greenman register struct inpcb *inp, *match = NULL; 673df8bae1dSRodney W. Grimes int matchwild = 3, wildcard; 674c3229e05SDavid Greenman u_short lport = lport_arg; 6757bc4aca7SDavid Greenman 676c3229e05SDavid Greenman if (!wild_okay) { 677c3229e05SDavid Greenman struct inpcbhead *head; 678c3229e05SDavid Greenman /* 679c3229e05SDavid Greenman * Look for an unconnected (wildcard foreign addr) PCB that 680c3229e05SDavid Greenman * matches the local address and port we're looking for. 681c3229e05SDavid Greenman */ 682c3229e05SDavid Greenman head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)]; 683c3229e05SDavid Greenman for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { 684c3229e05SDavid Greenman if (inp->inp_faddr.s_addr == INADDR_ANY && 685c3229e05SDavid Greenman inp->inp_laddr.s_addr == laddr.s_addr && 686c3229e05SDavid Greenman inp->inp_lport == lport) { 687c3229e05SDavid Greenman /* 688c3229e05SDavid Greenman * Found. 689c3229e05SDavid Greenman */ 690c3229e05SDavid Greenman return (inp); 691df8bae1dSRodney W. Grimes } 692c3229e05SDavid Greenman } 693c3229e05SDavid Greenman /* 694c3229e05SDavid Greenman * Not found. 695c3229e05SDavid Greenman */ 696c3229e05SDavid Greenman return (NULL); 697c3229e05SDavid Greenman } else { 698c3229e05SDavid Greenman struct inpcbporthead *porthash; 699c3229e05SDavid Greenman struct inpcbport *phd; 700c3229e05SDavid Greenman struct inpcb *match = NULL; 701c3229e05SDavid Greenman /* 702c3229e05SDavid Greenman * Best fit PCB lookup. 703c3229e05SDavid Greenman * 704c3229e05SDavid Greenman * First see if this local port is in use by looking on the 705c3229e05SDavid Greenman * port hash list. 706c3229e05SDavid Greenman */ 707c3229e05SDavid Greenman porthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(lport, 708c3229e05SDavid Greenman pcbinfo->porthashmask)]; 709c3229e05SDavid Greenman for (phd = porthash->lh_first; phd != NULL; phd = phd->phd_hash.le_next) { 710c3229e05SDavid Greenman if (phd->phd_port == lport) 711c3229e05SDavid Greenman break; 712c3229e05SDavid Greenman } 713c3229e05SDavid Greenman if (phd != NULL) { 714c3229e05SDavid Greenman /* 715c3229e05SDavid Greenman * Port is in use by one or more PCBs. Look for best 716c3229e05SDavid Greenman * fit. 717c3229e05SDavid Greenman */ 718c3229e05SDavid Greenman for (inp = phd->phd_pcblist.lh_first; inp != NULL; 719c3229e05SDavid Greenman inp = inp->inp_portlist.le_next) { 720c3229e05SDavid Greenman wildcard = 0; 721c3229e05SDavid Greenman if (inp->inp_faddr.s_addr != INADDR_ANY) 722c3229e05SDavid Greenman wildcard++; 72315bd2b43SDavid Greenman if (inp->inp_laddr.s_addr != INADDR_ANY) { 72415bd2b43SDavid Greenman if (laddr.s_addr == INADDR_ANY) 72515bd2b43SDavid Greenman wildcard++; 72615bd2b43SDavid Greenman else if (inp->inp_laddr.s_addr != laddr.s_addr) 72715bd2b43SDavid Greenman continue; 72815bd2b43SDavid Greenman } else { 72915bd2b43SDavid Greenman if (laddr.s_addr != INADDR_ANY) 73015bd2b43SDavid Greenman wildcard++; 73115bd2b43SDavid Greenman } 732df8bae1dSRodney W. Grimes if (wildcard < matchwild) { 733df8bae1dSRodney W. Grimes match = inp; 734df8bae1dSRodney W. Grimes matchwild = wildcard; 7353dbdc25cSDavid Greenman if (matchwild == 0) { 736df8bae1dSRodney W. Grimes break; 737df8bae1dSRodney W. Grimes } 738df8bae1dSRodney W. Grimes } 7393dbdc25cSDavid Greenman } 740c3229e05SDavid Greenman } 741df8bae1dSRodney W. Grimes return (match); 742df8bae1dSRodney W. Grimes } 743c3229e05SDavid Greenman } 74415bd2b43SDavid Greenman 74515bd2b43SDavid Greenman /* 74615bd2b43SDavid Greenman * Lookup PCB in hash list. 74715bd2b43SDavid Greenman */ 74815bd2b43SDavid Greenman struct inpcb * 749c3229e05SDavid Greenman in_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard) 75015bd2b43SDavid Greenman struct inpcbinfo *pcbinfo; 75115bd2b43SDavid Greenman struct in_addr faddr, laddr; 75215bd2b43SDavid Greenman u_int fport_arg, lport_arg; 7536d6a026bSDavid Greenman int wildcard; 75415bd2b43SDavid Greenman { 75515bd2b43SDavid Greenman struct inpcbhead *head; 75615bd2b43SDavid Greenman register struct inpcb *inp; 75715bd2b43SDavid Greenman u_short fport = fport_arg, lport = lport_arg; 75815bd2b43SDavid Greenman 75915bd2b43SDavid Greenman /* 76015bd2b43SDavid Greenman * First look for an exact match. 76115bd2b43SDavid Greenman */ 762ddd79a97SDavid Greenman head = &pcbinfo->hashbase[INP_PCBHASH(faddr.s_addr, lport, fport, pcbinfo->hashmask)]; 76315bd2b43SDavid Greenman for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { 7646d6a026bSDavid Greenman if (inp->inp_faddr.s_addr == faddr.s_addr && 765ca98b82cSDavid Greenman inp->inp_laddr.s_addr == laddr.s_addr && 766ca98b82cSDavid Greenman inp->inp_fport == fport && 767c3229e05SDavid Greenman inp->inp_lport == lport) { 768c3229e05SDavid Greenman /* 769c3229e05SDavid Greenman * Found. 770c3229e05SDavid Greenman */ 771c3229e05SDavid Greenman return (inp); 772c3229e05SDavid Greenman } 7736d6a026bSDavid Greenman } 7746d6a026bSDavid Greenman if (wildcard) { 7756d6a026bSDavid Greenman struct inpcb *local_wild = NULL; 7766d6a026bSDavid Greenman 777ddd79a97SDavid Greenman head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)]; 7786d6a026bSDavid Greenman for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { 7796d6a026bSDavid Greenman if (inp->inp_faddr.s_addr == INADDR_ANY && 780c3229e05SDavid Greenman inp->inp_lport == lport) { 7816d6a026bSDavid Greenman if (inp->inp_laddr.s_addr == laddr.s_addr) 782c3229e05SDavid Greenman return (inp); 7836d6a026bSDavid Greenman else if (inp->inp_laddr.s_addr == INADDR_ANY) 7846d6a026bSDavid Greenman local_wild = inp; 7856d6a026bSDavid Greenman } 7866d6a026bSDavid Greenman } 787c3229e05SDavid Greenman return (local_wild); 7886d6a026bSDavid Greenman } 789c3229e05SDavid Greenman 790c3229e05SDavid Greenman /* 791c3229e05SDavid Greenman * Not found. 792c3229e05SDavid Greenman */ 7936d6a026bSDavid Greenman return (NULL); 79415bd2b43SDavid Greenman } 79515bd2b43SDavid Greenman 7967bc4aca7SDavid Greenman /* 797c3229e05SDavid Greenman * Insert PCB onto various hash lists. 7987bc4aca7SDavid Greenman */ 799c3229e05SDavid Greenman int 80015bd2b43SDavid Greenman in_pcbinshash(inp) 80115bd2b43SDavid Greenman struct inpcb *inp; 80215bd2b43SDavid Greenman { 803c3229e05SDavid Greenman struct inpcbhead *pcbhash; 804c3229e05SDavid Greenman struct inpcbporthead *pcbporthash; 805c3229e05SDavid Greenman struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; 806c3229e05SDavid Greenman struct inpcbport *phd; 80715bd2b43SDavid Greenman 808c3229e05SDavid Greenman pcbhash = &pcbinfo->hashbase[INP_PCBHASH(inp->inp_faddr.s_addr, 809c3229e05SDavid Greenman inp->inp_lport, inp->inp_fport, pcbinfo->hashmask)]; 81015bd2b43SDavid Greenman 811c3229e05SDavid Greenman pcbporthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(inp->inp_lport, 812c3229e05SDavid Greenman pcbinfo->porthashmask)]; 813c3229e05SDavid Greenman 814c3229e05SDavid Greenman /* 815c3229e05SDavid Greenman * Go through port list and look for a head for this lport. 816c3229e05SDavid Greenman */ 817c3229e05SDavid Greenman for (phd = pcbporthash->lh_first; phd != NULL; phd = phd->phd_hash.le_next) { 818c3229e05SDavid Greenman if (phd->phd_port == inp->inp_lport) 819c3229e05SDavid Greenman break; 820c3229e05SDavid Greenman } 821c3229e05SDavid Greenman /* 822c3229e05SDavid Greenman * If none exists, malloc one and tack it on. 823c3229e05SDavid Greenman */ 824c3229e05SDavid Greenman if (phd == NULL) { 825c3229e05SDavid Greenman MALLOC(phd, struct inpcbport *, sizeof(struct inpcbport), M_PCB, M_NOWAIT); 826c3229e05SDavid Greenman if (phd == NULL) { 827c3229e05SDavid Greenman return (ENOBUFS); /* XXX */ 828c3229e05SDavid Greenman } 829c3229e05SDavid Greenman phd->phd_port = inp->inp_lport; 830c3229e05SDavid Greenman LIST_INIT(&phd->phd_pcblist); 831c3229e05SDavid Greenman LIST_INSERT_HEAD(pcbporthash, phd, phd_hash); 832c3229e05SDavid Greenman } 833c3229e05SDavid Greenman inp->inp_phd = phd; 834c3229e05SDavid Greenman LIST_INSERT_HEAD(&phd->phd_pcblist, inp, inp_portlist); 835c3229e05SDavid Greenman LIST_INSERT_HEAD(pcbhash, inp, inp_hash); 836c3229e05SDavid Greenman return (0); 83715bd2b43SDavid Greenman } 83815bd2b43SDavid Greenman 839c3229e05SDavid Greenman /* 840c3229e05SDavid Greenman * Move PCB to the proper hash bucket when { faddr, fport } have been 841c3229e05SDavid Greenman * changed. NOTE: This does not handle the case of the lport changing (the 842c3229e05SDavid Greenman * hashed port list would have to be updated as well), so the lport must 843c3229e05SDavid Greenman * not change after in_pcbinshash() has been called. 844c3229e05SDavid Greenman */ 84515bd2b43SDavid Greenman void 84615bd2b43SDavid Greenman in_pcbrehash(inp) 84715bd2b43SDavid Greenman struct inpcb *inp; 84815bd2b43SDavid Greenman { 84915bd2b43SDavid Greenman struct inpcbhead *head; 85015bd2b43SDavid Greenman 851ddd79a97SDavid Greenman head = &inp->inp_pcbinfo->hashbase[INP_PCBHASH(inp->inp_faddr.s_addr, 852ddd79a97SDavid Greenman inp->inp_lport, inp->inp_fport, inp->inp_pcbinfo->hashmask)]; 85315bd2b43SDavid Greenman 854c3229e05SDavid Greenman LIST_REMOVE(inp, inp_hash); 85515bd2b43SDavid Greenman LIST_INSERT_HEAD(head, inp, inp_hash); 856c3229e05SDavid Greenman } 857c3229e05SDavid Greenman 858c3229e05SDavid Greenman /* 859c3229e05SDavid Greenman * Remove PCB from various lists. 860c3229e05SDavid Greenman */ 861c3229e05SDavid Greenman static void 862c3229e05SDavid Greenman in_pcbremlists(inp) 863c3229e05SDavid Greenman struct inpcb *inp; 864c3229e05SDavid Greenman { 865c3229e05SDavid Greenman if (inp->inp_lport) { 866c3229e05SDavid Greenman struct inpcbport *phd = inp->inp_phd; 867c3229e05SDavid Greenman 868c3229e05SDavid Greenman LIST_REMOVE(inp, inp_hash); 869c3229e05SDavid Greenman LIST_REMOVE(inp, inp_portlist); 870c3229e05SDavid Greenman if (phd->phd_pcblist.lh_first == NULL) { 871c3229e05SDavid Greenman LIST_REMOVE(phd, phd_hash); 872c3229e05SDavid Greenman free(phd, M_PCB); 873c3229e05SDavid Greenman } 874c3229e05SDavid Greenman } 875c3229e05SDavid Greenman LIST_REMOVE(inp, inp_list); 87615bd2b43SDavid Greenman } 877