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 348781d8e9SBruce Evans * $Id: in_pcb.c,v 1.40 1998/03/24 18:06:08 wollman 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> 478781d8e9SBruce Evans 488781d8e9SBruce Evans #include <vm/vm_zone.h> 49df8bae1dSRodney W. Grimes 50df8bae1dSRodney W. Grimes #include <net/if.h> 51df8bae1dSRodney W. Grimes #include <net/route.h> 52df8bae1dSRodney W. Grimes 53df8bae1dSRodney W. Grimes #include <netinet/in.h> 54df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h> 55df8bae1dSRodney W. Grimes #include <netinet/in_var.h> 56df8bae1dSRodney W. Grimes #include <netinet/ip_var.h> 57df8bae1dSRodney W. Grimes 58df8bae1dSRodney W. Grimes struct in_addr zeroin_addr; 59df8bae1dSRodney W. Grimes 60c3229e05SDavid Greenman static void in_pcbremlists __P((struct inpcb *)); 61bbd42ad0SPeter Wemm static void in_rtchange __P((struct inpcb *, int)); 62bbd42ad0SPeter Wemm 63101f9fc8SPeter Wemm /* 64101f9fc8SPeter Wemm * These configure the range of local port addresses assigned to 65101f9fc8SPeter Wemm * "unspecified" outgoing connections/packets/whatever. 66101f9fc8SPeter Wemm */ 67bbd42ad0SPeter Wemm static int ipport_lowfirstauto = IPPORT_RESERVED - 1; /* 1023 */ 68bbd42ad0SPeter Wemm static int ipport_lowlastauto = IPPORT_RESERVEDSTART; /* 600 */ 6933b3ac06SPeter Wemm static int ipport_firstauto = IPPORT_RESERVED; /* 1024 */ 7033b3ac06SPeter Wemm static int ipport_lastauto = IPPORT_USERRESERVED; /* 5000 */ 7133b3ac06SPeter Wemm static int ipport_hifirstauto = IPPORT_HIFIRSTAUTO; /* 40000 */ 7233b3ac06SPeter Wemm static int ipport_hilastauto = IPPORT_HILASTAUTO; /* 44999 */ 73101f9fc8SPeter Wemm 74bbd42ad0SPeter Wemm #define RANGECHK(var, min, max) \ 75bbd42ad0SPeter Wemm if ((var) < (min)) { (var) = (min); } \ 76bbd42ad0SPeter Wemm else if ((var) > (max)) { (var) = (max); } 77bbd42ad0SPeter Wemm 78bbd42ad0SPeter Wemm static int 79bbd42ad0SPeter Wemm sysctl_net_ipport_check SYSCTL_HANDLER_ARGS 80bbd42ad0SPeter Wemm { 81bbd42ad0SPeter Wemm int error = sysctl_handle_int(oidp, 82bbd42ad0SPeter Wemm oidp->oid_arg1, oidp->oid_arg2, req); 83bbd42ad0SPeter Wemm if (!error) { 84bbd42ad0SPeter Wemm RANGECHK(ipport_lowfirstauto, 1, IPPORT_RESERVED - 1); 85bbd42ad0SPeter Wemm RANGECHK(ipport_lowlastauto, 1, IPPORT_RESERVED - 1); 86bbd42ad0SPeter Wemm RANGECHK(ipport_firstauto, IPPORT_RESERVED, USHRT_MAX); 87bbd42ad0SPeter Wemm RANGECHK(ipport_lastauto, IPPORT_RESERVED, USHRT_MAX); 88bbd42ad0SPeter Wemm RANGECHK(ipport_hifirstauto, IPPORT_RESERVED, USHRT_MAX); 89bbd42ad0SPeter Wemm RANGECHK(ipport_hilastauto, IPPORT_RESERVED, USHRT_MAX); 90bbd42ad0SPeter Wemm } 91bbd42ad0SPeter Wemm return error; 92bbd42ad0SPeter Wemm } 93bbd42ad0SPeter Wemm 94bbd42ad0SPeter Wemm #undef RANGECHK 95bbd42ad0SPeter Wemm 9633b3ac06SPeter Wemm SYSCTL_NODE(_net_inet_ip, IPPROTO_IP, portrange, CTLFLAG_RW, 0, "IP Ports"); 9733b3ac06SPeter Wemm 98bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowfirst, CTLTYPE_INT|CTLFLAG_RW, 99bbd42ad0SPeter Wemm &ipport_lowfirstauto, 0, &sysctl_net_ipport_check, "I", ""); 100bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowlast, CTLTYPE_INT|CTLFLAG_RW, 101bbd42ad0SPeter Wemm &ipport_lowlastauto, 0, &sysctl_net_ipport_check, "I", ""); 102bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, first, CTLTYPE_INT|CTLFLAG_RW, 103bbd42ad0SPeter Wemm &ipport_firstauto, 0, &sysctl_net_ipport_check, "I", ""); 104bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, last, CTLTYPE_INT|CTLFLAG_RW, 105bbd42ad0SPeter Wemm &ipport_lastauto, 0, &sysctl_net_ipport_check, "I", ""); 106bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hifirst, CTLTYPE_INT|CTLFLAG_RW, 107bbd42ad0SPeter Wemm &ipport_hifirstauto, 0, &sysctl_net_ipport_check, "I", ""); 108bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hilast, CTLTYPE_INT|CTLFLAG_RW, 109bbd42ad0SPeter Wemm &ipport_hilastauto, 0, &sysctl_net_ipport_check, "I", ""); 1100312fbe9SPoul-Henning Kamp 111c3229e05SDavid Greenman /* 112c3229e05SDavid Greenman * in_pcb.c: manage the Protocol Control Blocks. 113c3229e05SDavid Greenman * 114c3229e05SDavid Greenman * NOTE: It is assumed that most of these functions will be called at 115c3229e05SDavid Greenman * splnet(). XXX - There are, unfortunately, a few exceptions to this 116c3229e05SDavid Greenman * rule that should be fixed. 117c3229e05SDavid Greenman */ 118c3229e05SDavid Greenman 119c3229e05SDavid Greenman /* 120c3229e05SDavid Greenman * Allocate a PCB and associate it with the socket. 121c3229e05SDavid Greenman */ 122df8bae1dSRodney W. Grimes int 123a29f300eSGarrett Wollman in_pcballoc(so, pcbinfo, p) 124df8bae1dSRodney W. Grimes struct socket *so; 12515bd2b43SDavid Greenman struct inpcbinfo *pcbinfo; 126a29f300eSGarrett Wollman struct proc *p; 127df8bae1dSRodney W. Grimes { 128df8bae1dSRodney W. Grimes register struct inpcb *inp; 129df8bae1dSRodney W. Grimes 1303d4d47f3SGarrett Wollman inp = zalloci(pcbinfo->ipi_zone); 131df8bae1dSRodney W. Grimes if (inp == NULL) 132df8bae1dSRodney W. Grimes return (ENOBUFS); 133df8bae1dSRodney W. Grimes bzero((caddr_t)inp, sizeof(*inp)); 1343d4d47f3SGarrett Wollman inp->inp_gencnt = ++pcbinfo->ipi_gencnt; 13515bd2b43SDavid Greenman inp->inp_pcbinfo = pcbinfo; 136df8bae1dSRodney W. Grimes inp->inp_socket = so; 13715bd2b43SDavid Greenman LIST_INSERT_HEAD(pcbinfo->listhead, inp, inp_list); 1383d4d47f3SGarrett Wollman pcbinfo->ipi_count++; 139df8bae1dSRodney W. Grimes so->so_pcb = (caddr_t)inp; 140df8bae1dSRodney W. Grimes return (0); 141df8bae1dSRodney W. Grimes } 142df8bae1dSRodney W. Grimes 143df8bae1dSRodney W. Grimes int 144a29f300eSGarrett Wollman in_pcbbind(inp, nam, p) 145df8bae1dSRodney W. Grimes register struct inpcb *inp; 14657bf258eSGarrett Wollman struct sockaddr *nam; 147a29f300eSGarrett Wollman struct proc *p; 148df8bae1dSRodney W. Grimes { 149df8bae1dSRodney W. Grimes register struct socket *so = inp->inp_socket; 15037bd2b30SPeter Wemm unsigned short *lastport; 15115bd2b43SDavid Greenman struct sockaddr_in *sin; 152c3229e05SDavid Greenman struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; 153df8bae1dSRodney W. Grimes u_short lport = 0; 154df8bae1dSRodney W. Grimes int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); 155df8bae1dSRodney W. Grimes int error; 156df8bae1dSRodney W. Grimes 15759562606SGarrett Wollman if (TAILQ_EMPTY(&in_ifaddrhead)) /* XXX broken! */ 158df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 159df8bae1dSRodney W. Grimes if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY) 160df8bae1dSRodney W. Grimes return (EINVAL); 161c3229e05SDavid Greenman if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) 1626d6a026bSDavid Greenman wild = 1; 163df8bae1dSRodney W. Grimes if (nam) { 16457bf258eSGarrett Wollman sin = (struct sockaddr_in *)nam; 16557bf258eSGarrett Wollman if (nam->sa_len != sizeof (*sin)) 166df8bae1dSRodney W. Grimes return (EINVAL); 167df8bae1dSRodney W. Grimes #ifdef notdef 168df8bae1dSRodney W. Grimes /* 169df8bae1dSRodney W. Grimes * We should check the family, but old programs 170df8bae1dSRodney W. Grimes * incorrectly fail to initialize it. 171df8bae1dSRodney W. Grimes */ 172df8bae1dSRodney W. Grimes if (sin->sin_family != AF_INET) 173df8bae1dSRodney W. Grimes return (EAFNOSUPPORT); 174df8bae1dSRodney W. Grimes #endif 175df8bae1dSRodney W. Grimes lport = sin->sin_port; 176df8bae1dSRodney W. Grimes if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { 177df8bae1dSRodney W. Grimes /* 178df8bae1dSRodney W. Grimes * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; 179df8bae1dSRodney W. Grimes * allow complete duplication of binding if 180df8bae1dSRodney W. Grimes * SO_REUSEPORT is set, or if SO_REUSEADDR is set 181df8bae1dSRodney W. Grimes * and a multicast address is bound on both 182df8bae1dSRodney W. Grimes * new and duplicated sockets. 183df8bae1dSRodney W. Grimes */ 184df8bae1dSRodney W. Grimes if (so->so_options & SO_REUSEADDR) 185df8bae1dSRodney W. Grimes reuseport = SO_REUSEADDR|SO_REUSEPORT; 186df8bae1dSRodney W. Grimes } else if (sin->sin_addr.s_addr != INADDR_ANY) { 187df8bae1dSRodney W. Grimes sin->sin_port = 0; /* yech... */ 188df8bae1dSRodney W. Grimes if (ifa_ifwithaddr((struct sockaddr *)sin) == 0) 189df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 190df8bae1dSRodney W. Grimes } 191df8bae1dSRodney W. Grimes if (lport) { 192df8bae1dSRodney W. Grimes struct inpcb *t; 193df8bae1dSRodney W. Grimes 194df8bae1dSRodney W. Grimes /* GROSS */ 19557bf258eSGarrett Wollman if (ntohs(lport) < IPPORT_RESERVED && p && 19690d0144cSAlexander Langer suser(p->p_ucred, &p->p_acflag)) 1972469dd60SGarrett Wollman return (EACCES); 1984049a042SGuido van Rooij if (so->so_uid) { 1994049a042SGuido van Rooij t = in_pcblookup_local(inp->inp_pcbinfo, 2004049a042SGuido van Rooij sin->sin_addr, lport, INPLOOKUP_WILDCARD); 2014049a042SGuido van Rooij if (t && (so->so_uid != t->inp_socket->so_uid)) 2024049a042SGuido van Rooij return (EADDRINUSE); 2034049a042SGuido van Rooij } 204c3229e05SDavid Greenman t = in_pcblookup_local(pcbinfo, sin->sin_addr, 205c3229e05SDavid Greenman lport, wild); 206df8bae1dSRodney W. Grimes if (t && (reuseport & t->inp_socket->so_options) == 0) 207df8bae1dSRodney W. Grimes return (EADDRINUSE); 208df8bae1dSRodney W. Grimes } 209df8bae1dSRodney W. Grimes inp->inp_laddr = sin->sin_addr; 210df8bae1dSRodney W. Grimes } 21133b3ac06SPeter Wemm if (lport == 0) { 21233b3ac06SPeter Wemm ushort first, last; 21333b3ac06SPeter Wemm int count; 21433b3ac06SPeter Wemm 215321a2846SPoul-Henning Kamp inp->inp_flags |= INP_ANONPORT; 216321a2846SPoul-Henning Kamp 21733b3ac06SPeter Wemm if (inp->inp_flags & INP_HIGHPORT) { 21833b3ac06SPeter Wemm first = ipport_hifirstauto; /* sysctl */ 21933b3ac06SPeter Wemm last = ipport_hilastauto; 220c3229e05SDavid Greenman lastport = &pcbinfo->lasthi; 22133b3ac06SPeter Wemm } else if (inp->inp_flags & INP_LOWPORT) { 22257bf258eSGarrett Wollman if (p && (error = suser(p->p_ucred, &p->p_acflag))) 223a29f300eSGarrett Wollman return error; 224bbd42ad0SPeter Wemm first = ipport_lowfirstauto; /* 1023 */ 225bbd42ad0SPeter Wemm last = ipport_lowlastauto; /* 600 */ 226c3229e05SDavid Greenman lastport = &pcbinfo->lastlow; 22733b3ac06SPeter Wemm } else { 22833b3ac06SPeter Wemm first = ipport_firstauto; /* sysctl */ 22933b3ac06SPeter Wemm last = ipport_lastauto; 230c3229e05SDavid Greenman lastport = &pcbinfo->lastport; 23133b3ac06SPeter Wemm } 23233b3ac06SPeter Wemm /* 23333b3ac06SPeter Wemm * Simple check to ensure all ports are not used up causing 23433b3ac06SPeter Wemm * a deadlock here. 23533b3ac06SPeter Wemm * 23633b3ac06SPeter Wemm * We split the two cases (up and down) so that the direction 23733b3ac06SPeter Wemm * is not being tested on each round of the loop. 23833b3ac06SPeter Wemm */ 23933b3ac06SPeter Wemm if (first > last) { 24033b3ac06SPeter Wemm /* 24133b3ac06SPeter Wemm * counting down 24233b3ac06SPeter Wemm */ 24333b3ac06SPeter Wemm count = first - last; 24433b3ac06SPeter Wemm 245df8bae1dSRodney W. Grimes do { 246c3229e05SDavid Greenman if (count-- < 0) { /* completely used? */ 247c3229e05SDavid Greenman /* 248c3229e05SDavid Greenman * Undo any address bind that may have 249c3229e05SDavid Greenman * occurred above. 250c3229e05SDavid Greenman */ 251c3229e05SDavid Greenman inp->inp_laddr.s_addr = INADDR_ANY; 252c3229e05SDavid Greenman return (EAGAIN); 253c3229e05SDavid Greenman } 25433b3ac06SPeter Wemm --*lastport; 25533b3ac06SPeter Wemm if (*lastport > first || *lastport < last) 25633b3ac06SPeter Wemm *lastport = first; 25715bd2b43SDavid Greenman lport = htons(*lastport); 258c3229e05SDavid Greenman } while (in_pcblookup_local(pcbinfo, 259c3229e05SDavid Greenman inp->inp_laddr, lport, wild)); 26033b3ac06SPeter Wemm } else { 26133b3ac06SPeter Wemm /* 26233b3ac06SPeter Wemm * counting up 26333b3ac06SPeter Wemm */ 26433b3ac06SPeter Wemm count = last - first; 26533b3ac06SPeter Wemm 26633b3ac06SPeter Wemm do { 267c3229e05SDavid Greenman if (count-- < 0) { /* completely used? */ 268c3229e05SDavid Greenman /* 269c3229e05SDavid Greenman * Undo any address bind that may have 270c3229e05SDavid Greenman * occurred above. 271c3229e05SDavid Greenman */ 272c3229e05SDavid Greenman inp->inp_laddr.s_addr = INADDR_ANY; 273c3229e05SDavid Greenman return (EAGAIN); 274c3229e05SDavid Greenman } 27533b3ac06SPeter Wemm ++*lastport; 27633b3ac06SPeter Wemm if (*lastport < first || *lastport > last) 27733b3ac06SPeter Wemm *lastport = first; 27833b3ac06SPeter Wemm lport = htons(*lastport); 279c3229e05SDavid Greenman } while (in_pcblookup_local(pcbinfo, 280c3229e05SDavid Greenman inp->inp_laddr, lport, wild)); 28133b3ac06SPeter Wemm } 28233b3ac06SPeter Wemm } 283df8bae1dSRodney W. Grimes inp->inp_lport = lport; 284c3229e05SDavid Greenman if (in_pcbinshash(inp) != 0) { 285c3229e05SDavid Greenman inp->inp_laddr.s_addr = INADDR_ANY; 286c3229e05SDavid Greenman inp->inp_lport = 0; 287c3229e05SDavid Greenman return (EAGAIN); 288c3229e05SDavid Greenman } 289df8bae1dSRodney W. Grimes return (0); 290df8bae1dSRodney W. Grimes } 291df8bae1dSRodney W. Grimes 292999f1343SGarrett Wollman /* 293999f1343SGarrett Wollman * Transform old in_pcbconnect() into an inner subroutine for new 294999f1343SGarrett Wollman * in_pcbconnect(): Do some validity-checking on the remote 295999f1343SGarrett Wollman * address (in mbuf 'nam') and then determine local host address 296999f1343SGarrett Wollman * (i.e., which interface) to use to access that remote host. 297999f1343SGarrett Wollman * 298999f1343SGarrett Wollman * This preserves definition of in_pcbconnect(), while supporting a 299999f1343SGarrett Wollman * slightly different version for T/TCP. (This is more than 300999f1343SGarrett Wollman * a bit of a kludge, but cleaning up the internal interfaces would 301999f1343SGarrett Wollman * have forced minor changes in every protocol). 302999f1343SGarrett Wollman */ 303999f1343SGarrett Wollman 304999f1343SGarrett Wollman int 305999f1343SGarrett Wollman in_pcbladdr(inp, nam, plocal_sin) 306999f1343SGarrett Wollman register struct inpcb *inp; 30757bf258eSGarrett Wollman struct sockaddr *nam; 308999f1343SGarrett Wollman struct sockaddr_in **plocal_sin; 309999f1343SGarrett Wollman { 310df8bae1dSRodney W. Grimes struct in_ifaddr *ia; 31157bf258eSGarrett Wollman register struct sockaddr_in *sin = (struct sockaddr_in *)nam; 312df8bae1dSRodney W. Grimes 31357bf258eSGarrett Wollman if (nam->sa_len != sizeof (*sin)) 314df8bae1dSRodney W. Grimes return (EINVAL); 315df8bae1dSRodney W. Grimes if (sin->sin_family != AF_INET) 316df8bae1dSRodney W. Grimes return (EAFNOSUPPORT); 317df8bae1dSRodney W. Grimes if (sin->sin_port == 0) 318df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 31959562606SGarrett Wollman if (!TAILQ_EMPTY(&in_ifaddrhead)) { 320df8bae1dSRodney W. Grimes /* 321df8bae1dSRodney W. Grimes * If the destination address is INADDR_ANY, 322df8bae1dSRodney W. Grimes * use the primary local address. 323df8bae1dSRodney W. Grimes * If the supplied address is INADDR_BROADCAST, 324df8bae1dSRodney W. Grimes * and the primary interface supports broadcast, 325df8bae1dSRodney W. Grimes * choose the broadcast address for that interface. 326df8bae1dSRodney W. Grimes */ 327df8bae1dSRodney W. Grimes #define satosin(sa) ((struct sockaddr_in *)(sa)) 328df8bae1dSRodney W. Grimes #define sintosa(sin) ((struct sockaddr *)(sin)) 329df8bae1dSRodney W. Grimes #define ifatoia(ifa) ((struct in_ifaddr *)(ifa)) 330df8bae1dSRodney W. Grimes if (sin->sin_addr.s_addr == INADDR_ANY) 33159562606SGarrett Wollman sin->sin_addr = IA_SIN(in_ifaddrhead.tqh_first)->sin_addr; 332df8bae1dSRodney W. Grimes else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST && 33359562606SGarrett Wollman (in_ifaddrhead.tqh_first->ia_ifp->if_flags & IFF_BROADCAST)) 33459562606SGarrett Wollman sin->sin_addr = satosin(&in_ifaddrhead.tqh_first->ia_broadaddr)->sin_addr; 335df8bae1dSRodney W. Grimes } 336df8bae1dSRodney W. Grimes if (inp->inp_laddr.s_addr == INADDR_ANY) { 337df8bae1dSRodney W. Grimes register struct route *ro; 338df8bae1dSRodney W. Grimes 339df8bae1dSRodney W. Grimes ia = (struct in_ifaddr *)0; 340df8bae1dSRodney W. Grimes /* 341df8bae1dSRodney W. Grimes * If route is known or can be allocated now, 342df8bae1dSRodney W. Grimes * our src addr is taken from the i/f, else punt. 343df8bae1dSRodney W. Grimes */ 344df8bae1dSRodney W. Grimes ro = &inp->inp_route; 345df8bae1dSRodney W. Grimes if (ro->ro_rt && 346df8bae1dSRodney W. Grimes (satosin(&ro->ro_dst)->sin_addr.s_addr != 347df8bae1dSRodney W. Grimes sin->sin_addr.s_addr || 348df8bae1dSRodney W. Grimes inp->inp_socket->so_options & SO_DONTROUTE)) { 349df8bae1dSRodney W. Grimes RTFREE(ro->ro_rt); 350df8bae1dSRodney W. Grimes ro->ro_rt = (struct rtentry *)0; 351df8bae1dSRodney W. Grimes } 352df8bae1dSRodney W. Grimes if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/ 353df8bae1dSRodney W. Grimes (ro->ro_rt == (struct rtentry *)0 || 354df8bae1dSRodney W. Grimes ro->ro_rt->rt_ifp == (struct ifnet *)0)) { 355df8bae1dSRodney W. Grimes /* No route yet, so try to acquire one */ 356df8bae1dSRodney W. Grimes ro->ro_dst.sa_family = AF_INET; 357df8bae1dSRodney W. Grimes ro->ro_dst.sa_len = sizeof(struct sockaddr_in); 358df8bae1dSRodney W. Grimes ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = 359df8bae1dSRodney W. Grimes sin->sin_addr; 360df8bae1dSRodney W. Grimes rtalloc(ro); 361df8bae1dSRodney W. Grimes } 362df8bae1dSRodney W. Grimes /* 363df8bae1dSRodney W. Grimes * If we found a route, use the address 364df8bae1dSRodney W. Grimes * corresponding to the outgoing interface 365df8bae1dSRodney W. Grimes * unless it is the loopback (in case a route 366df8bae1dSRodney W. Grimes * to our address on another net goes to loopback). 367df8bae1dSRodney W. Grimes */ 368df8bae1dSRodney W. Grimes if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK)) 369df8bae1dSRodney W. Grimes ia = ifatoia(ro->ro_rt->rt_ifa); 370df8bae1dSRodney W. Grimes if (ia == 0) { 371df8bae1dSRodney W. Grimes u_short fport = sin->sin_port; 372df8bae1dSRodney W. Grimes 373df8bae1dSRodney W. Grimes sin->sin_port = 0; 374df8bae1dSRodney W. Grimes ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin))); 375df8bae1dSRodney W. Grimes if (ia == 0) 376df8bae1dSRodney W. Grimes ia = ifatoia(ifa_ifwithnet(sintosa(sin))); 377df8bae1dSRodney W. Grimes sin->sin_port = fport; 378df8bae1dSRodney W. Grimes if (ia == 0) 37959562606SGarrett Wollman ia = in_ifaddrhead.tqh_first; 380df8bae1dSRodney W. Grimes if (ia == 0) 381df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 382df8bae1dSRodney W. Grimes } 383df8bae1dSRodney W. Grimes /* 384df8bae1dSRodney W. Grimes * If the destination address is multicast and an outgoing 385df8bae1dSRodney W. Grimes * interface has been set as a multicast option, use the 386df8bae1dSRodney W. Grimes * address of that interface as our source address. 387df8bae1dSRodney W. Grimes */ 388df8bae1dSRodney W. Grimes if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) && 389df8bae1dSRodney W. Grimes inp->inp_moptions != NULL) { 390df8bae1dSRodney W. Grimes struct ip_moptions *imo; 391df8bae1dSRodney W. Grimes struct ifnet *ifp; 392df8bae1dSRodney W. Grimes 393df8bae1dSRodney W. Grimes imo = inp->inp_moptions; 394df8bae1dSRodney W. Grimes if (imo->imo_multicast_ifp != NULL) { 395df8bae1dSRodney W. Grimes ifp = imo->imo_multicast_ifp; 39659562606SGarrett Wollman for (ia = in_ifaddrhead.tqh_first; ia; 39759562606SGarrett Wollman ia = ia->ia_link.tqe_next) 398df8bae1dSRodney W. Grimes if (ia->ia_ifp == ifp) 399df8bae1dSRodney W. Grimes break; 400df8bae1dSRodney W. Grimes if (ia == 0) 401df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 402df8bae1dSRodney W. Grimes } 403df8bae1dSRodney W. Grimes } 404999f1343SGarrett Wollman /* 405999f1343SGarrett Wollman * Don't do pcblookup call here; return interface in plocal_sin 406999f1343SGarrett Wollman * and exit to caller, that will do the lookup. 407999f1343SGarrett Wollman */ 408999f1343SGarrett Wollman *plocal_sin = &ia->ia_addr; 409999f1343SGarrett Wollman 410999f1343SGarrett Wollman } 411999f1343SGarrett Wollman return(0); 412999f1343SGarrett Wollman } 413999f1343SGarrett Wollman 414999f1343SGarrett Wollman /* 415999f1343SGarrett Wollman * Outer subroutine: 416999f1343SGarrett Wollman * Connect from a socket to a specified address. 417999f1343SGarrett Wollman * Both address and port must be specified in argument sin. 418999f1343SGarrett Wollman * If don't have a local address for this socket yet, 419999f1343SGarrett Wollman * then pick one. 420999f1343SGarrett Wollman */ 421999f1343SGarrett Wollman int 422a29f300eSGarrett Wollman in_pcbconnect(inp, nam, p) 423999f1343SGarrett Wollman register struct inpcb *inp; 42457bf258eSGarrett Wollman struct sockaddr *nam; 425a29f300eSGarrett Wollman struct proc *p; 426999f1343SGarrett Wollman { 427999f1343SGarrett Wollman struct sockaddr_in *ifaddr; 42857bf258eSGarrett Wollman register struct sockaddr_in *sin = (struct sockaddr_in *)nam; 429999f1343SGarrett Wollman int error; 430999f1343SGarrett Wollman 431999f1343SGarrett Wollman /* 432999f1343SGarrett Wollman * Call inner routine, to assign local interface address. 433999f1343SGarrett Wollman */ 434999f1343SGarrett Wollman if (error = in_pcbladdr(inp, nam, &ifaddr)) 435999f1343SGarrett Wollman return(error); 436999f1343SGarrett Wollman 437c3229e05SDavid Greenman if (in_pcblookup_hash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port, 438df8bae1dSRodney W. Grimes inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 439c3229e05SDavid Greenman inp->inp_lport, 0) != NULL) { 440df8bae1dSRodney W. Grimes return (EADDRINUSE); 441c3229e05SDavid Greenman } 442df8bae1dSRodney W. Grimes if (inp->inp_laddr.s_addr == INADDR_ANY) { 443df8bae1dSRodney W. Grimes if (inp->inp_lport == 0) 44457bf258eSGarrett Wollman (void)in_pcbbind(inp, (struct sockaddr *)0, p); 445df8bae1dSRodney W. Grimes inp->inp_laddr = ifaddr->sin_addr; 446df8bae1dSRodney W. Grimes } 447df8bae1dSRodney W. Grimes inp->inp_faddr = sin->sin_addr; 448df8bae1dSRodney W. Grimes inp->inp_fport = sin->sin_port; 44915bd2b43SDavid Greenman in_pcbrehash(inp); 450df8bae1dSRodney W. Grimes return (0); 451df8bae1dSRodney W. Grimes } 452df8bae1dSRodney W. Grimes 45326f9a767SRodney W. Grimes void 454df8bae1dSRodney W. Grimes in_pcbdisconnect(inp) 455df8bae1dSRodney W. Grimes struct inpcb *inp; 456df8bae1dSRodney W. Grimes { 457df8bae1dSRodney W. Grimes 458df8bae1dSRodney W. Grimes inp->inp_faddr.s_addr = INADDR_ANY; 459df8bae1dSRodney W. Grimes inp->inp_fport = 0; 46015bd2b43SDavid Greenman in_pcbrehash(inp); 461df8bae1dSRodney W. Grimes if (inp->inp_socket->so_state & SS_NOFDREF) 462df8bae1dSRodney W. Grimes in_pcbdetach(inp); 463df8bae1dSRodney W. Grimes } 464df8bae1dSRodney W. Grimes 46526f9a767SRodney W. Grimes void 466df8bae1dSRodney W. Grimes in_pcbdetach(inp) 467df8bae1dSRodney W. Grimes struct inpcb *inp; 468df8bae1dSRodney W. Grimes { 469df8bae1dSRodney W. Grimes struct socket *so = inp->inp_socket; 4703d4d47f3SGarrett Wollman struct inpcbinfo *ipi = inp->inp_pcbinfo; 471df8bae1dSRodney W. Grimes 4723d4d47f3SGarrett Wollman inp->inp_gencnt = ++ipi->ipi_gencnt; 473c3229e05SDavid Greenman in_pcbremlists(inp); 474df8bae1dSRodney W. Grimes so->so_pcb = 0; 475df8bae1dSRodney W. Grimes sofree(so); 476df8bae1dSRodney W. Grimes if (inp->inp_options) 477df8bae1dSRodney W. Grimes (void)m_free(inp->inp_options); 478df8bae1dSRodney W. Grimes if (inp->inp_route.ro_rt) 479df8bae1dSRodney W. Grimes rtfree(inp->inp_route.ro_rt); 480df8bae1dSRodney W. Grimes ip_freemoptions(inp->inp_moptions); 4813d4d47f3SGarrett Wollman zfreei(ipi->ipi_zone, inp); 482df8bae1dSRodney W. Grimes } 483df8bae1dSRodney W. Grimes 484117bcae7SGarrett Wollman /* 485117bcae7SGarrett Wollman * The calling convention of in_setsockaddr() and in_setpeeraddr() was 486117bcae7SGarrett Wollman * modified to match the pru_sockaddr() and pru_peeraddr() entry points 487117bcae7SGarrett Wollman * in struct pr_usrreqs, so that protocols can just reference then directly 488117bcae7SGarrett Wollman * without the need for a wrapper function. The socket must have a valid 489117bcae7SGarrett Wollman * (i.e., non-nil) PCB, but it should be impossible to get an invalid one 490117bcae7SGarrett Wollman * except through a kernel programming error, so it is acceptable to panic 49157bf258eSGarrett Wollman * (or in this case trap) if the PCB is invalid. (Actually, we don't trap 49257bf258eSGarrett Wollman * because there actually /is/ a programming error somewhere... XXX) 493117bcae7SGarrett Wollman */ 494117bcae7SGarrett Wollman int 495117bcae7SGarrett Wollman in_setsockaddr(so, nam) 496117bcae7SGarrett Wollman struct socket *so; 49757bf258eSGarrett Wollman struct sockaddr **nam; 498df8bae1dSRodney W. Grimes { 499fdc984f7STor Egge int s; 500fdc984f7STor Egge register struct inpcb *inp; 501df8bae1dSRodney W. Grimes register struct sockaddr_in *sin; 502df8bae1dSRodney W. Grimes 503c3229e05SDavid Greenman /* 504c3229e05SDavid Greenman * Do the malloc first in case it blocks. 505c3229e05SDavid Greenman */ 50642fa505bSDavid Greenman MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_WAITOK); 50742fa505bSDavid Greenman bzero(sin, sizeof *sin); 50842fa505bSDavid Greenman sin->sin_family = AF_INET; 50942fa505bSDavid Greenman sin->sin_len = sizeof(*sin); 51042fa505bSDavid Greenman 511fdc984f7STor Egge s = splnet(); 512fdc984f7STor Egge inp = sotoinpcb(so); 513db112f04STor Egge if (!inp) { 514db112f04STor Egge splx(s); 51542fa505bSDavid Greenman free(sin, M_SONAME); 516db112f04STor Egge return EINVAL; 517db112f04STor Egge } 518df8bae1dSRodney W. Grimes sin->sin_port = inp->inp_lport; 519df8bae1dSRodney W. Grimes sin->sin_addr = inp->inp_laddr; 520db112f04STor Egge splx(s); 52142fa505bSDavid Greenman 52242fa505bSDavid Greenman *nam = (struct sockaddr *)sin; 523117bcae7SGarrett Wollman return 0; 524df8bae1dSRodney W. Grimes } 525df8bae1dSRodney W. Grimes 526117bcae7SGarrett Wollman int 527117bcae7SGarrett Wollman in_setpeeraddr(so, nam) 528117bcae7SGarrett Wollman struct socket *so; 52957bf258eSGarrett Wollman struct sockaddr **nam; 530df8bae1dSRodney W. Grimes { 531fdc984f7STor Egge int s; 532fdc984f7STor Egge struct inpcb *inp; 533df8bae1dSRodney W. Grimes register struct sockaddr_in *sin; 534df8bae1dSRodney W. Grimes 535c3229e05SDavid Greenman /* 536c3229e05SDavid Greenman * Do the malloc first in case it blocks. 537c3229e05SDavid Greenman */ 53842fa505bSDavid Greenman MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_WAITOK); 53942fa505bSDavid Greenman bzero((caddr_t)sin, sizeof (*sin)); 54042fa505bSDavid Greenman sin->sin_family = AF_INET; 54142fa505bSDavid Greenman sin->sin_len = sizeof(*sin); 54242fa505bSDavid Greenman 543fdc984f7STor Egge s = splnet(); 544fdc984f7STor Egge inp = sotoinpcb(so); 545db112f04STor Egge if (!inp) { 546db112f04STor Egge splx(s); 54742fa505bSDavid Greenman free(sin, M_SONAME); 548db112f04STor Egge return EINVAL; 549db112f04STor Egge } 550df8bae1dSRodney W. Grimes sin->sin_port = inp->inp_fport; 551df8bae1dSRodney W. Grimes sin->sin_addr = inp->inp_faddr; 552db112f04STor Egge splx(s); 55342fa505bSDavid Greenman 55442fa505bSDavid Greenman *nam = (struct sockaddr *)sin; 555117bcae7SGarrett Wollman return 0; 556df8bae1dSRodney W. Grimes } 557df8bae1dSRodney W. Grimes 558df8bae1dSRodney W. Grimes /* 559df8bae1dSRodney W. Grimes * Pass some notification to all connections of a protocol 560df8bae1dSRodney W. Grimes * associated with address dst. The local address and/or port numbers 561df8bae1dSRodney W. Grimes * may be specified to limit the search. The "usual action" will be 562df8bae1dSRodney W. Grimes * taken, depending on the ctlinput cmd. The caller must filter any 563df8bae1dSRodney W. Grimes * cmds that are uninteresting (e.g., no error in the map). 564df8bae1dSRodney W. Grimes * Call the protocol specific routine (if any) to report 565df8bae1dSRodney W. Grimes * any errors for each matching socket. 566df8bae1dSRodney W. Grimes */ 56726f9a767SRodney W. Grimes void 568df8bae1dSRodney W. Grimes in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify) 56915bd2b43SDavid Greenman struct inpcbhead *head; 570df8bae1dSRodney W. Grimes struct sockaddr *dst; 571df8bae1dSRodney W. Grimes u_int fport_arg, lport_arg; 572df8bae1dSRodney W. Grimes struct in_addr laddr; 573df8bae1dSRodney W. Grimes int cmd; 574df8bae1dSRodney W. Grimes void (*notify) __P((struct inpcb *, int)); 575df8bae1dSRodney W. Grimes { 576df8bae1dSRodney W. Grimes register struct inpcb *inp, *oinp; 577df8bae1dSRodney W. Grimes struct in_addr faddr; 578df8bae1dSRodney W. Grimes u_short fport = fport_arg, lport = lport_arg; 5797bc4aca7SDavid Greenman int errno, s; 580df8bae1dSRodney W. Grimes 581df8bae1dSRodney W. Grimes if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET) 582df8bae1dSRodney W. Grimes return; 583df8bae1dSRodney W. Grimes faddr = ((struct sockaddr_in *)dst)->sin_addr; 584df8bae1dSRodney W. Grimes if (faddr.s_addr == INADDR_ANY) 585df8bae1dSRodney W. Grimes return; 586df8bae1dSRodney W. Grimes 587df8bae1dSRodney W. Grimes /* 588df8bae1dSRodney W. Grimes * Redirects go to all references to the destination, 589df8bae1dSRodney W. Grimes * and use in_rtchange to invalidate the route cache. 590df8bae1dSRodney W. Grimes * Dead host indications: notify all references to the destination. 591df8bae1dSRodney W. Grimes * Otherwise, if we have knowledge of the local port and address, 592df8bae1dSRodney W. Grimes * deliver only to that socket. 593df8bae1dSRodney W. Grimes */ 594df8bae1dSRodney W. Grimes if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) { 595df8bae1dSRodney W. Grimes fport = 0; 596df8bae1dSRodney W. Grimes lport = 0; 597df8bae1dSRodney W. Grimes laddr.s_addr = 0; 598df8bae1dSRodney W. Grimes if (cmd != PRC_HOSTDEAD) 599df8bae1dSRodney W. Grimes notify = in_rtchange; 600df8bae1dSRodney W. Grimes } 601df8bae1dSRodney W. Grimes errno = inetctlerrmap[cmd]; 6027bc4aca7SDavid Greenman s = splnet(); 60315bd2b43SDavid Greenman for (inp = head->lh_first; inp != NULL;) { 604df8bae1dSRodney W. Grimes if (inp->inp_faddr.s_addr != faddr.s_addr || 605df8bae1dSRodney W. Grimes inp->inp_socket == 0 || 606df8bae1dSRodney W. Grimes (lport && inp->inp_lport != lport) || 607df8bae1dSRodney W. Grimes (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) || 608df8bae1dSRodney W. Grimes (fport && inp->inp_fport != fport)) { 60915bd2b43SDavid Greenman inp = inp->inp_list.le_next; 610df8bae1dSRodney W. Grimes continue; 611df8bae1dSRodney W. Grimes } 612df8bae1dSRodney W. Grimes oinp = inp; 61315bd2b43SDavid Greenman inp = inp->inp_list.le_next; 614df8bae1dSRodney W. Grimes if (notify) 615df8bae1dSRodney W. Grimes (*notify)(oinp, errno); 616df8bae1dSRodney W. Grimes } 6177bc4aca7SDavid Greenman splx(s); 618df8bae1dSRodney W. Grimes } 619df8bae1dSRodney W. Grimes 620df8bae1dSRodney W. Grimes /* 621df8bae1dSRodney W. Grimes * Check for alternatives when higher level complains 622df8bae1dSRodney W. Grimes * about service problems. For now, invalidate cached 623df8bae1dSRodney W. Grimes * routing information. If the route was created dynamically 624df8bae1dSRodney W. Grimes * (by a redirect), time to try a default gateway again. 625df8bae1dSRodney W. Grimes */ 62626f9a767SRodney W. Grimes void 627df8bae1dSRodney W. Grimes in_losing(inp) 628df8bae1dSRodney W. Grimes struct inpcb *inp; 629df8bae1dSRodney W. Grimes { 630df8bae1dSRodney W. Grimes register struct rtentry *rt; 631df8bae1dSRodney W. Grimes struct rt_addrinfo info; 632df8bae1dSRodney W. Grimes 633df8bae1dSRodney W. Grimes if ((rt = inp->inp_route.ro_rt)) { 634df8bae1dSRodney W. Grimes inp->inp_route.ro_rt = 0; 635df8bae1dSRodney W. Grimes bzero((caddr_t)&info, sizeof(info)); 636df8bae1dSRodney W. Grimes info.rti_info[RTAX_DST] = 637df8bae1dSRodney W. Grimes (struct sockaddr *)&inp->inp_route.ro_dst; 638df8bae1dSRodney W. Grimes info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; 639df8bae1dSRodney W. Grimes info.rti_info[RTAX_NETMASK] = rt_mask(rt); 640df8bae1dSRodney W. Grimes rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0); 641df8bae1dSRodney W. Grimes if (rt->rt_flags & RTF_DYNAMIC) 642df8bae1dSRodney W. Grimes (void) rtrequest(RTM_DELETE, rt_key(rt), 643df8bae1dSRodney W. Grimes rt->rt_gateway, rt_mask(rt), rt->rt_flags, 644df8bae1dSRodney W. Grimes (struct rtentry **)0); 645df8bae1dSRodney W. Grimes else 646df8bae1dSRodney W. Grimes /* 647df8bae1dSRodney W. Grimes * A new route can be allocated 648df8bae1dSRodney W. Grimes * the next time output is attempted. 649df8bae1dSRodney W. Grimes */ 650df8bae1dSRodney W. Grimes rtfree(rt); 651df8bae1dSRodney W. Grimes } 652df8bae1dSRodney W. Grimes } 653df8bae1dSRodney W. Grimes 654df8bae1dSRodney W. Grimes /* 655df8bae1dSRodney W. Grimes * After a routing change, flush old routing 656df8bae1dSRodney W. Grimes * and allocate a (hopefully) better one. 657df8bae1dSRodney W. Grimes */ 6580312fbe9SPoul-Henning Kamp static void 659df8bae1dSRodney W. Grimes in_rtchange(inp, errno) 660df8bae1dSRodney W. Grimes register struct inpcb *inp; 661df8bae1dSRodney W. Grimes int errno; 662df8bae1dSRodney W. Grimes { 663df8bae1dSRodney W. Grimes if (inp->inp_route.ro_rt) { 664df8bae1dSRodney W. Grimes rtfree(inp->inp_route.ro_rt); 665df8bae1dSRodney W. Grimes inp->inp_route.ro_rt = 0; 666df8bae1dSRodney W. Grimes /* 667df8bae1dSRodney W. Grimes * A new route can be allocated the next time 668df8bae1dSRodney W. Grimes * output is attempted. 669df8bae1dSRodney W. Grimes */ 670df8bae1dSRodney W. Grimes } 671df8bae1dSRodney W. Grimes } 672df8bae1dSRodney W. Grimes 673c3229e05SDavid Greenman /* 674c3229e05SDavid Greenman * Lookup a PCB based on the local address and port. 675c3229e05SDavid Greenman */ 676df8bae1dSRodney W. Grimes struct inpcb * 677c3229e05SDavid Greenman in_pcblookup_local(pcbinfo, laddr, lport_arg, wild_okay) 6786d6a026bSDavid Greenman struct inpcbinfo *pcbinfo; 679c3229e05SDavid Greenman struct in_addr laddr; 680c3229e05SDavid Greenman u_int lport_arg; 6816d6a026bSDavid Greenman int wild_okay; 682df8bae1dSRodney W. Grimes { 6833dbdc25cSDavid Greenman register struct inpcb *inp, *match = NULL; 684df8bae1dSRodney W. Grimes int matchwild = 3, wildcard; 685c3229e05SDavid Greenman u_short lport = lport_arg; 6867bc4aca7SDavid Greenman 687c3229e05SDavid Greenman if (!wild_okay) { 688c3229e05SDavid Greenman struct inpcbhead *head; 689c3229e05SDavid Greenman /* 690c3229e05SDavid Greenman * Look for an unconnected (wildcard foreign addr) PCB that 691c3229e05SDavid Greenman * matches the local address and port we're looking for. 692c3229e05SDavid Greenman */ 693c3229e05SDavid Greenman head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)]; 694c3229e05SDavid Greenman for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { 695c3229e05SDavid Greenman if (inp->inp_faddr.s_addr == INADDR_ANY && 696c3229e05SDavid Greenman inp->inp_laddr.s_addr == laddr.s_addr && 697c3229e05SDavid Greenman inp->inp_lport == lport) { 698c3229e05SDavid Greenman /* 699c3229e05SDavid Greenman * Found. 700c3229e05SDavid Greenman */ 701c3229e05SDavid Greenman return (inp); 702df8bae1dSRodney W. Grimes } 703c3229e05SDavid Greenman } 704c3229e05SDavid Greenman /* 705c3229e05SDavid Greenman * Not found. 706c3229e05SDavid Greenman */ 707c3229e05SDavid Greenman return (NULL); 708c3229e05SDavid Greenman } else { 709c3229e05SDavid Greenman struct inpcbporthead *porthash; 710c3229e05SDavid Greenman struct inpcbport *phd; 711c3229e05SDavid Greenman struct inpcb *match = NULL; 712c3229e05SDavid Greenman /* 713c3229e05SDavid Greenman * Best fit PCB lookup. 714c3229e05SDavid Greenman * 715c3229e05SDavid Greenman * First see if this local port is in use by looking on the 716c3229e05SDavid Greenman * port hash list. 717c3229e05SDavid Greenman */ 718c3229e05SDavid Greenman porthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(lport, 719c3229e05SDavid Greenman pcbinfo->porthashmask)]; 720c3229e05SDavid Greenman for (phd = porthash->lh_first; phd != NULL; phd = phd->phd_hash.le_next) { 721c3229e05SDavid Greenman if (phd->phd_port == lport) 722c3229e05SDavid Greenman break; 723c3229e05SDavid Greenman } 724c3229e05SDavid Greenman if (phd != NULL) { 725c3229e05SDavid Greenman /* 726c3229e05SDavid Greenman * Port is in use by one or more PCBs. Look for best 727c3229e05SDavid Greenman * fit. 728c3229e05SDavid Greenman */ 729c3229e05SDavid Greenman for (inp = phd->phd_pcblist.lh_first; inp != NULL; 730c3229e05SDavid Greenman inp = inp->inp_portlist.le_next) { 731c3229e05SDavid Greenman wildcard = 0; 732c3229e05SDavid Greenman if (inp->inp_faddr.s_addr != INADDR_ANY) 733c3229e05SDavid Greenman wildcard++; 73415bd2b43SDavid Greenman if (inp->inp_laddr.s_addr != INADDR_ANY) { 73515bd2b43SDavid Greenman if (laddr.s_addr == INADDR_ANY) 73615bd2b43SDavid Greenman wildcard++; 73715bd2b43SDavid Greenman else if (inp->inp_laddr.s_addr != laddr.s_addr) 73815bd2b43SDavid Greenman continue; 73915bd2b43SDavid Greenman } else { 74015bd2b43SDavid Greenman if (laddr.s_addr != INADDR_ANY) 74115bd2b43SDavid Greenman wildcard++; 74215bd2b43SDavid Greenman } 743df8bae1dSRodney W. Grimes if (wildcard < matchwild) { 744df8bae1dSRodney W. Grimes match = inp; 745df8bae1dSRodney W. Grimes matchwild = wildcard; 7463dbdc25cSDavid Greenman if (matchwild == 0) { 747df8bae1dSRodney W. Grimes break; 748df8bae1dSRodney W. Grimes } 749df8bae1dSRodney W. Grimes } 7503dbdc25cSDavid Greenman } 751c3229e05SDavid Greenman } 752df8bae1dSRodney W. Grimes return (match); 753df8bae1dSRodney W. Grimes } 754c3229e05SDavid Greenman } 75515bd2b43SDavid Greenman 75615bd2b43SDavid Greenman /* 75715bd2b43SDavid Greenman * Lookup PCB in hash list. 75815bd2b43SDavid Greenman */ 75915bd2b43SDavid Greenman struct inpcb * 760c3229e05SDavid Greenman in_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard) 76115bd2b43SDavid Greenman struct inpcbinfo *pcbinfo; 76215bd2b43SDavid Greenman struct in_addr faddr, laddr; 76315bd2b43SDavid Greenman u_int fport_arg, lport_arg; 7646d6a026bSDavid Greenman int wildcard; 76515bd2b43SDavid Greenman { 76615bd2b43SDavid Greenman struct inpcbhead *head; 76715bd2b43SDavid Greenman register struct inpcb *inp; 76815bd2b43SDavid Greenman u_short fport = fport_arg, lport = lport_arg; 76915bd2b43SDavid Greenman 77015bd2b43SDavid Greenman /* 77115bd2b43SDavid Greenman * First look for an exact match. 77215bd2b43SDavid Greenman */ 773ddd79a97SDavid Greenman head = &pcbinfo->hashbase[INP_PCBHASH(faddr.s_addr, lport, fport, pcbinfo->hashmask)]; 77415bd2b43SDavid Greenman for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { 7756d6a026bSDavid Greenman if (inp->inp_faddr.s_addr == faddr.s_addr && 776ca98b82cSDavid Greenman inp->inp_laddr.s_addr == laddr.s_addr && 777ca98b82cSDavid Greenman inp->inp_fport == fport && 778c3229e05SDavid Greenman inp->inp_lport == lport) { 779c3229e05SDavid Greenman /* 780c3229e05SDavid Greenman * Found. 781c3229e05SDavid Greenman */ 782c3229e05SDavid Greenman return (inp); 783c3229e05SDavid Greenman } 7846d6a026bSDavid Greenman } 7856d6a026bSDavid Greenman if (wildcard) { 7866d6a026bSDavid Greenman struct inpcb *local_wild = NULL; 7876d6a026bSDavid Greenman 788ddd79a97SDavid Greenman head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)]; 7896d6a026bSDavid Greenman for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { 7906d6a026bSDavid Greenman if (inp->inp_faddr.s_addr == INADDR_ANY && 791c3229e05SDavid Greenman inp->inp_lport == lport) { 7926d6a026bSDavid Greenman if (inp->inp_laddr.s_addr == laddr.s_addr) 793c3229e05SDavid Greenman return (inp); 7946d6a026bSDavid Greenman else if (inp->inp_laddr.s_addr == INADDR_ANY) 7956d6a026bSDavid Greenman local_wild = inp; 7966d6a026bSDavid Greenman } 7976d6a026bSDavid Greenman } 798c3229e05SDavid Greenman return (local_wild); 7996d6a026bSDavid Greenman } 800c3229e05SDavid Greenman 801c3229e05SDavid Greenman /* 802c3229e05SDavid Greenman * Not found. 803c3229e05SDavid Greenman */ 8046d6a026bSDavid Greenman return (NULL); 80515bd2b43SDavid Greenman } 80615bd2b43SDavid Greenman 8077bc4aca7SDavid Greenman /* 808c3229e05SDavid Greenman * Insert PCB onto various hash lists. 8097bc4aca7SDavid Greenman */ 810c3229e05SDavid Greenman int 81115bd2b43SDavid Greenman in_pcbinshash(inp) 81215bd2b43SDavid Greenman struct inpcb *inp; 81315bd2b43SDavid Greenman { 814c3229e05SDavid Greenman struct inpcbhead *pcbhash; 815c3229e05SDavid Greenman struct inpcbporthead *pcbporthash; 816c3229e05SDavid Greenman struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; 817c3229e05SDavid Greenman struct inpcbport *phd; 81815bd2b43SDavid Greenman 819c3229e05SDavid Greenman pcbhash = &pcbinfo->hashbase[INP_PCBHASH(inp->inp_faddr.s_addr, 820c3229e05SDavid Greenman inp->inp_lport, inp->inp_fport, pcbinfo->hashmask)]; 82115bd2b43SDavid Greenman 822c3229e05SDavid Greenman pcbporthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(inp->inp_lport, 823c3229e05SDavid Greenman pcbinfo->porthashmask)]; 824c3229e05SDavid Greenman 825c3229e05SDavid Greenman /* 826c3229e05SDavid Greenman * Go through port list and look for a head for this lport. 827c3229e05SDavid Greenman */ 828c3229e05SDavid Greenman for (phd = pcbporthash->lh_first; phd != NULL; phd = phd->phd_hash.le_next) { 829c3229e05SDavid Greenman if (phd->phd_port == inp->inp_lport) 830c3229e05SDavid Greenman break; 831c3229e05SDavid Greenman } 832c3229e05SDavid Greenman /* 833c3229e05SDavid Greenman * If none exists, malloc one and tack it on. 834c3229e05SDavid Greenman */ 835c3229e05SDavid Greenman if (phd == NULL) { 836c3229e05SDavid Greenman MALLOC(phd, struct inpcbport *, sizeof(struct inpcbport), M_PCB, M_NOWAIT); 837c3229e05SDavid Greenman if (phd == NULL) { 838c3229e05SDavid Greenman return (ENOBUFS); /* XXX */ 839c3229e05SDavid Greenman } 840c3229e05SDavid Greenman phd->phd_port = inp->inp_lport; 841c3229e05SDavid Greenman LIST_INIT(&phd->phd_pcblist); 842c3229e05SDavid Greenman LIST_INSERT_HEAD(pcbporthash, phd, phd_hash); 843c3229e05SDavid Greenman } 844c3229e05SDavid Greenman inp->inp_phd = phd; 845c3229e05SDavid Greenman LIST_INSERT_HEAD(&phd->phd_pcblist, inp, inp_portlist); 846c3229e05SDavid Greenman LIST_INSERT_HEAD(pcbhash, inp, inp_hash); 847c3229e05SDavid Greenman return (0); 84815bd2b43SDavid Greenman } 84915bd2b43SDavid Greenman 850c3229e05SDavid Greenman /* 851c3229e05SDavid Greenman * Move PCB to the proper hash bucket when { faddr, fport } have been 852c3229e05SDavid Greenman * changed. NOTE: This does not handle the case of the lport changing (the 853c3229e05SDavid Greenman * hashed port list would have to be updated as well), so the lport must 854c3229e05SDavid Greenman * not change after in_pcbinshash() has been called. 855c3229e05SDavid Greenman */ 85615bd2b43SDavid Greenman void 85715bd2b43SDavid Greenman in_pcbrehash(inp) 85815bd2b43SDavid Greenman struct inpcb *inp; 85915bd2b43SDavid Greenman { 86015bd2b43SDavid Greenman struct inpcbhead *head; 86115bd2b43SDavid Greenman 862ddd79a97SDavid Greenman head = &inp->inp_pcbinfo->hashbase[INP_PCBHASH(inp->inp_faddr.s_addr, 863ddd79a97SDavid Greenman inp->inp_lport, inp->inp_fport, inp->inp_pcbinfo->hashmask)]; 86415bd2b43SDavid Greenman 865c3229e05SDavid Greenman LIST_REMOVE(inp, inp_hash); 86615bd2b43SDavid Greenman LIST_INSERT_HEAD(head, inp, inp_hash); 867c3229e05SDavid Greenman } 868c3229e05SDavid Greenman 869c3229e05SDavid Greenman /* 870c3229e05SDavid Greenman * Remove PCB from various lists. 871c3229e05SDavid Greenman */ 872c3229e05SDavid Greenman static void 873c3229e05SDavid Greenman in_pcbremlists(inp) 874c3229e05SDavid Greenman struct inpcb *inp; 875c3229e05SDavid Greenman { 876c3229e05SDavid Greenman if (inp->inp_lport) { 877c3229e05SDavid Greenman struct inpcbport *phd = inp->inp_phd; 878c3229e05SDavid Greenman 879c3229e05SDavid Greenman LIST_REMOVE(inp, inp_hash); 880c3229e05SDavid Greenman LIST_REMOVE(inp, inp_portlist); 881c3229e05SDavid Greenman if (phd->phd_pcblist.lh_first == NULL) { 882c3229e05SDavid Greenman LIST_REMOVE(phd, phd_hash); 883c3229e05SDavid Greenman free(phd, M_PCB); 884c3229e05SDavid Greenman } 885c3229e05SDavid Greenman } 886c3229e05SDavid Greenman LIST_REMOVE(inp, inp_list); 8873d4d47f3SGarrett Wollman inp->inp_pcbinfo->ipi_count--; 88815bd2b43SDavid Greenman } 889