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 3475c13541SPoul-Henning Kamp * $Id: in_pcb.c,v 1.48 1999/04/27 11:17:31 phk 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> 4575c13541SPoul-Henning Kamp #include <sys/jail.h> 46101f9fc8SPeter Wemm #include <sys/kernel.h> 47101f9fc8SPeter Wemm #include <sys/sysctl.h> 488781d8e9SBruce Evans 4908637435SBruce Evans #include <machine/limits.h> 5008637435SBruce Evans 518781d8e9SBruce Evans #include <vm/vm_zone.h> 52df8bae1dSRodney W. Grimes 53df8bae1dSRodney W. Grimes #include <net/if.h> 54df8bae1dSRodney W. Grimes #include <net/route.h> 55df8bae1dSRodney W. Grimes 56df8bae1dSRodney W. Grimes #include <netinet/in.h> 57df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h> 58df8bae1dSRodney W. Grimes #include <netinet/in_var.h> 59df8bae1dSRodney W. Grimes #include <netinet/ip_var.h> 60df8bae1dSRodney W. Grimes 61df8bae1dSRodney W. Grimes struct in_addr zeroin_addr; 62df8bae1dSRodney W. Grimes 63c3229e05SDavid Greenman static void in_pcbremlists __P((struct inpcb *)); 64bbd42ad0SPeter Wemm static void in_rtchange __P((struct inpcb *, int)); 65bbd42ad0SPeter Wemm 66101f9fc8SPeter Wemm /* 67101f9fc8SPeter Wemm * These configure the range of local port addresses assigned to 68101f9fc8SPeter Wemm * "unspecified" outgoing connections/packets/whatever. 69101f9fc8SPeter Wemm */ 70bbd42ad0SPeter Wemm static int ipport_lowfirstauto = IPPORT_RESERVED - 1; /* 1023 */ 71bbd42ad0SPeter Wemm static int ipport_lowlastauto = IPPORT_RESERVEDSTART; /* 600 */ 7233b3ac06SPeter Wemm static int ipport_firstauto = IPPORT_RESERVED; /* 1024 */ 7333b3ac06SPeter Wemm static int ipport_lastauto = IPPORT_USERRESERVED; /* 5000 */ 744565cbeaSPoul-Henning Kamp static int ipport_hifirstauto = IPPORT_HIFIRSTAUTO; /* 49152 */ 754565cbeaSPoul-Henning Kamp static int ipport_hilastauto = IPPORT_HILASTAUTO; /* 65535 */ 76101f9fc8SPeter Wemm 77bbd42ad0SPeter Wemm #define RANGECHK(var, min, max) \ 78bbd42ad0SPeter Wemm if ((var) < (min)) { (var) = (min); } \ 79bbd42ad0SPeter Wemm else if ((var) > (max)) { (var) = (max); } 80bbd42ad0SPeter Wemm 81bbd42ad0SPeter Wemm static int 82bbd42ad0SPeter Wemm sysctl_net_ipport_check SYSCTL_HANDLER_ARGS 83bbd42ad0SPeter Wemm { 84bbd42ad0SPeter Wemm int error = sysctl_handle_int(oidp, 85bbd42ad0SPeter Wemm oidp->oid_arg1, oidp->oid_arg2, req); 86bbd42ad0SPeter Wemm if (!error) { 87bbd42ad0SPeter Wemm RANGECHK(ipport_lowfirstauto, 1, IPPORT_RESERVED - 1); 88bbd42ad0SPeter Wemm RANGECHK(ipport_lowlastauto, 1, IPPORT_RESERVED - 1); 89bbd42ad0SPeter Wemm RANGECHK(ipport_firstauto, IPPORT_RESERVED, USHRT_MAX); 90bbd42ad0SPeter Wemm RANGECHK(ipport_lastauto, IPPORT_RESERVED, USHRT_MAX); 91bbd42ad0SPeter Wemm RANGECHK(ipport_hifirstauto, IPPORT_RESERVED, USHRT_MAX); 92bbd42ad0SPeter Wemm RANGECHK(ipport_hilastauto, IPPORT_RESERVED, USHRT_MAX); 93bbd42ad0SPeter Wemm } 94bbd42ad0SPeter Wemm return error; 95bbd42ad0SPeter Wemm } 96bbd42ad0SPeter Wemm 97bbd42ad0SPeter Wemm #undef RANGECHK 98bbd42ad0SPeter Wemm 9933b3ac06SPeter Wemm SYSCTL_NODE(_net_inet_ip, IPPROTO_IP, portrange, CTLFLAG_RW, 0, "IP Ports"); 10033b3ac06SPeter Wemm 101bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowfirst, CTLTYPE_INT|CTLFLAG_RW, 102bbd42ad0SPeter Wemm &ipport_lowfirstauto, 0, &sysctl_net_ipport_check, "I", ""); 103bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowlast, CTLTYPE_INT|CTLFLAG_RW, 104bbd42ad0SPeter Wemm &ipport_lowlastauto, 0, &sysctl_net_ipport_check, "I", ""); 105bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, first, CTLTYPE_INT|CTLFLAG_RW, 106bbd42ad0SPeter Wemm &ipport_firstauto, 0, &sysctl_net_ipport_check, "I", ""); 107bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, last, CTLTYPE_INT|CTLFLAG_RW, 108bbd42ad0SPeter Wemm &ipport_lastauto, 0, &sysctl_net_ipport_check, "I", ""); 109bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hifirst, CTLTYPE_INT|CTLFLAG_RW, 110bbd42ad0SPeter Wemm &ipport_hifirstauto, 0, &sysctl_net_ipport_check, "I", ""); 111bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hilast, CTLTYPE_INT|CTLFLAG_RW, 112bbd42ad0SPeter Wemm &ipport_hilastauto, 0, &sysctl_net_ipport_check, "I", ""); 1130312fbe9SPoul-Henning Kamp 114c3229e05SDavid Greenman /* 115c3229e05SDavid Greenman * in_pcb.c: manage the Protocol Control Blocks. 116c3229e05SDavid Greenman * 117c3229e05SDavid Greenman * NOTE: It is assumed that most of these functions will be called at 118c3229e05SDavid Greenman * splnet(). XXX - There are, unfortunately, a few exceptions to this 119c3229e05SDavid Greenman * rule that should be fixed. 120c3229e05SDavid Greenman */ 121c3229e05SDavid Greenman 122c3229e05SDavid Greenman /* 123c3229e05SDavid Greenman * Allocate a PCB and associate it with the socket. 124c3229e05SDavid Greenman */ 125df8bae1dSRodney W. Grimes int 126a29f300eSGarrett Wollman in_pcballoc(so, pcbinfo, p) 127df8bae1dSRodney W. Grimes struct socket *so; 12815bd2b43SDavid Greenman struct inpcbinfo *pcbinfo; 129a29f300eSGarrett Wollman struct proc *p; 130df8bae1dSRodney W. Grimes { 131df8bae1dSRodney W. Grimes register struct inpcb *inp; 132df8bae1dSRodney W. Grimes 1333d4d47f3SGarrett Wollman inp = zalloci(pcbinfo->ipi_zone); 134df8bae1dSRodney W. Grimes if (inp == NULL) 135df8bae1dSRodney W. Grimes return (ENOBUFS); 136df8bae1dSRodney W. Grimes bzero((caddr_t)inp, sizeof(*inp)); 1373d4d47f3SGarrett Wollman inp->inp_gencnt = ++pcbinfo->ipi_gencnt; 13815bd2b43SDavid Greenman inp->inp_pcbinfo = pcbinfo; 139df8bae1dSRodney W. Grimes inp->inp_socket = so; 14015bd2b43SDavid Greenman LIST_INSERT_HEAD(pcbinfo->listhead, inp, inp_list); 1413d4d47f3SGarrett Wollman pcbinfo->ipi_count++; 142df8bae1dSRodney W. Grimes so->so_pcb = (caddr_t)inp; 143df8bae1dSRodney W. Grimes return (0); 144df8bae1dSRodney W. Grimes } 145df8bae1dSRodney W. Grimes 146df8bae1dSRodney W. Grimes int 147a29f300eSGarrett Wollman in_pcbbind(inp, nam, p) 148df8bae1dSRodney W. Grimes register struct inpcb *inp; 14957bf258eSGarrett Wollman struct sockaddr *nam; 150a29f300eSGarrett Wollman struct proc *p; 151df8bae1dSRodney W. Grimes { 152df8bae1dSRodney W. Grimes register struct socket *so = inp->inp_socket; 15337bd2b30SPeter Wemm unsigned short *lastport; 15415bd2b43SDavid Greenman struct sockaddr_in *sin; 155c3229e05SDavid Greenman struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; 156df8bae1dSRodney W. Grimes u_short lport = 0; 157df8bae1dSRodney W. Grimes int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); 15875c13541SPoul-Henning Kamp int error, prison = 0; 159df8bae1dSRodney W. Grimes 16059562606SGarrett Wollman if (TAILQ_EMPTY(&in_ifaddrhead)) /* XXX broken! */ 161df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 162df8bae1dSRodney W. Grimes if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY) 163df8bae1dSRodney W. Grimes return (EINVAL); 164c3229e05SDavid Greenman if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) 1656d6a026bSDavid Greenman wild = 1; 166df8bae1dSRodney W. Grimes if (nam) { 16757bf258eSGarrett Wollman sin = (struct sockaddr_in *)nam; 16857bf258eSGarrett Wollman if (nam->sa_len != sizeof (*sin)) 169df8bae1dSRodney W. Grimes return (EINVAL); 170df8bae1dSRodney W. Grimes #ifdef notdef 171df8bae1dSRodney W. Grimes /* 172df8bae1dSRodney W. Grimes * We should check the family, but old programs 173df8bae1dSRodney W. Grimes * incorrectly fail to initialize it. 174df8bae1dSRodney W. Grimes */ 175df8bae1dSRodney W. Grimes if (sin->sin_family != AF_INET) 176df8bae1dSRodney W. Grimes return (EAFNOSUPPORT); 177df8bae1dSRodney W. Grimes #endif 17875c13541SPoul-Henning Kamp if (prison_ip(p, 0, &sin->sin_addr.s_addr)) 17975c13541SPoul-Henning Kamp return(EINVAL); 180df8bae1dSRodney W. Grimes lport = sin->sin_port; 181df8bae1dSRodney W. Grimes if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { 182df8bae1dSRodney W. Grimes /* 183df8bae1dSRodney W. Grimes * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; 184df8bae1dSRodney W. Grimes * allow complete duplication of binding if 185df8bae1dSRodney W. Grimes * SO_REUSEPORT is set, or if SO_REUSEADDR is set 186df8bae1dSRodney W. Grimes * and a multicast address is bound on both 187df8bae1dSRodney W. Grimes * new and duplicated sockets. 188df8bae1dSRodney W. Grimes */ 189df8bae1dSRodney W. Grimes if (so->so_options & SO_REUSEADDR) 190df8bae1dSRodney W. Grimes reuseport = SO_REUSEADDR|SO_REUSEPORT; 191df8bae1dSRodney W. Grimes } else if (sin->sin_addr.s_addr != INADDR_ANY) { 192df8bae1dSRodney W. Grimes sin->sin_port = 0; /* yech... */ 193df8bae1dSRodney W. Grimes if (ifa_ifwithaddr((struct sockaddr *)sin) == 0) 194df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 195df8bae1dSRodney W. Grimes } 196df8bae1dSRodney W. Grimes if (lport) { 197df8bae1dSRodney W. Grimes struct inpcb *t; 198df8bae1dSRodney W. Grimes 199df8bae1dSRodney W. Grimes /* GROSS */ 20057bf258eSGarrett Wollman if (ntohs(lport) < IPPORT_RESERVED && p && 20175c13541SPoul-Henning Kamp suser_xxx(0, p, PRISON_ROOT)) 2022469dd60SGarrett Wollman return (EACCES); 20375c13541SPoul-Henning Kamp if (p && p->p_prison) 20475c13541SPoul-Henning Kamp prison = 1; 20552b65dbeSBill Fenner if (so->so_uid && 20652b65dbeSBill Fenner !IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { 2074049a042SGuido van Rooij t = in_pcblookup_local(inp->inp_pcbinfo, 20875c13541SPoul-Henning Kamp sin->sin_addr, lport, 20975c13541SPoul-Henning Kamp prison ? 0 : INPLOOKUP_WILDCARD); 21052b65dbeSBill Fenner if (t && 21152b65dbeSBill Fenner (ntohl(sin->sin_addr.s_addr) != INADDR_ANY || 21252b65dbeSBill Fenner ntohl(t->inp_laddr.s_addr) != INADDR_ANY || 21352b65dbeSBill Fenner (t->inp_socket->so_options & 21452b65dbeSBill Fenner SO_REUSEPORT) == 0) && 21552b65dbeSBill Fenner (so->so_uid != t->inp_socket->so_uid)) 2164049a042SGuido van Rooij return (EADDRINUSE); 2174049a042SGuido van Rooij } 218c3229e05SDavid Greenman t = in_pcblookup_local(pcbinfo, sin->sin_addr, 21975c13541SPoul-Henning Kamp lport, prison ? 0 : wild); 220df8bae1dSRodney W. Grimes if (t && (reuseport & t->inp_socket->so_options) == 0) 221df8bae1dSRodney W. Grimes return (EADDRINUSE); 222df8bae1dSRodney W. Grimes } 223df8bae1dSRodney W. Grimes inp->inp_laddr = sin->sin_addr; 224df8bae1dSRodney W. Grimes } 22533b3ac06SPeter Wemm if (lport == 0) { 22633b3ac06SPeter Wemm ushort first, last; 22733b3ac06SPeter Wemm int count; 22833b3ac06SPeter Wemm 22975c13541SPoul-Henning Kamp if (prison_ip(p, 0, &inp->inp_laddr.s_addr )) 23075c13541SPoul-Henning Kamp return (EINVAL); 231321a2846SPoul-Henning Kamp inp->inp_flags |= INP_ANONPORT; 232321a2846SPoul-Henning Kamp 23333b3ac06SPeter Wemm if (inp->inp_flags & INP_HIGHPORT) { 23433b3ac06SPeter Wemm first = ipport_hifirstauto; /* sysctl */ 23533b3ac06SPeter Wemm last = ipport_hilastauto; 236c3229e05SDavid Greenman lastport = &pcbinfo->lasthi; 23733b3ac06SPeter Wemm } else if (inp->inp_flags & INP_LOWPORT) { 23875c13541SPoul-Henning Kamp if (p && (error = suser_xxx(0, p, PRISON_ROOT))) 239a29f300eSGarrett Wollman return error; 240bbd42ad0SPeter Wemm first = ipport_lowfirstauto; /* 1023 */ 241bbd42ad0SPeter Wemm last = ipport_lowlastauto; /* 600 */ 242c3229e05SDavid Greenman lastport = &pcbinfo->lastlow; 24333b3ac06SPeter Wemm } else { 24433b3ac06SPeter Wemm first = ipport_firstauto; /* sysctl */ 24533b3ac06SPeter Wemm last = ipport_lastauto; 246c3229e05SDavid Greenman lastport = &pcbinfo->lastport; 24733b3ac06SPeter Wemm } 24833b3ac06SPeter Wemm /* 24933b3ac06SPeter Wemm * Simple check to ensure all ports are not used up causing 25033b3ac06SPeter Wemm * a deadlock here. 25133b3ac06SPeter Wemm * 25233b3ac06SPeter Wemm * We split the two cases (up and down) so that the direction 25333b3ac06SPeter Wemm * is not being tested on each round of the loop. 25433b3ac06SPeter Wemm */ 25533b3ac06SPeter Wemm if (first > last) { 25633b3ac06SPeter Wemm /* 25733b3ac06SPeter Wemm * counting down 25833b3ac06SPeter Wemm */ 25933b3ac06SPeter Wemm count = first - last; 26033b3ac06SPeter Wemm 261df8bae1dSRodney W. Grimes do { 262c3229e05SDavid Greenman if (count-- < 0) { /* completely used? */ 263c3229e05SDavid Greenman /* 264c3229e05SDavid Greenman * Undo any address bind that may have 265c3229e05SDavid Greenman * occurred above. 266c3229e05SDavid Greenman */ 267c3229e05SDavid Greenman inp->inp_laddr.s_addr = INADDR_ANY; 268c3229e05SDavid Greenman return (EAGAIN); 269c3229e05SDavid Greenman } 27033b3ac06SPeter Wemm --*lastport; 27133b3ac06SPeter Wemm if (*lastport > first || *lastport < last) 27233b3ac06SPeter Wemm *lastport = first; 27315bd2b43SDavid Greenman lport = htons(*lastport); 274c3229e05SDavid Greenman } while (in_pcblookup_local(pcbinfo, 275c3229e05SDavid Greenman inp->inp_laddr, lport, wild)); 27633b3ac06SPeter Wemm } else { 27733b3ac06SPeter Wemm /* 27833b3ac06SPeter Wemm * counting up 27933b3ac06SPeter Wemm */ 28033b3ac06SPeter Wemm count = last - first; 28133b3ac06SPeter Wemm 28233b3ac06SPeter Wemm do { 283c3229e05SDavid Greenman if (count-- < 0) { /* completely used? */ 284c3229e05SDavid Greenman /* 285c3229e05SDavid Greenman * Undo any address bind that may have 286c3229e05SDavid Greenman * occurred above. 287c3229e05SDavid Greenman */ 288c3229e05SDavid Greenman inp->inp_laddr.s_addr = INADDR_ANY; 289c3229e05SDavid Greenman return (EAGAIN); 290c3229e05SDavid Greenman } 29133b3ac06SPeter Wemm ++*lastport; 29233b3ac06SPeter Wemm if (*lastport < first || *lastport > last) 29333b3ac06SPeter Wemm *lastport = first; 29433b3ac06SPeter Wemm lport = htons(*lastport); 295c3229e05SDavid Greenman } while (in_pcblookup_local(pcbinfo, 296c3229e05SDavid Greenman inp->inp_laddr, lport, wild)); 29733b3ac06SPeter Wemm } 29833b3ac06SPeter Wemm } 299df8bae1dSRodney W. Grimes inp->inp_lport = lport; 300c3229e05SDavid Greenman if (in_pcbinshash(inp) != 0) { 301c3229e05SDavid Greenman inp->inp_laddr.s_addr = INADDR_ANY; 302c3229e05SDavid Greenman inp->inp_lport = 0; 303c3229e05SDavid Greenman return (EAGAIN); 304c3229e05SDavid Greenman } 305df8bae1dSRodney W. Grimes return (0); 306df8bae1dSRodney W. Grimes } 307df8bae1dSRodney W. Grimes 308999f1343SGarrett Wollman /* 309999f1343SGarrett Wollman * Transform old in_pcbconnect() into an inner subroutine for new 310999f1343SGarrett Wollman * in_pcbconnect(): Do some validity-checking on the remote 311999f1343SGarrett Wollman * address (in mbuf 'nam') and then determine local host address 312999f1343SGarrett Wollman * (i.e., which interface) to use to access that remote host. 313999f1343SGarrett Wollman * 314999f1343SGarrett Wollman * This preserves definition of in_pcbconnect(), while supporting a 315999f1343SGarrett Wollman * slightly different version for T/TCP. (This is more than 316999f1343SGarrett Wollman * a bit of a kludge, but cleaning up the internal interfaces would 317999f1343SGarrett Wollman * have forced minor changes in every protocol). 318999f1343SGarrett Wollman */ 319999f1343SGarrett Wollman 320999f1343SGarrett Wollman int 321999f1343SGarrett Wollman in_pcbladdr(inp, nam, plocal_sin) 322999f1343SGarrett Wollman register struct inpcb *inp; 32357bf258eSGarrett Wollman struct sockaddr *nam; 324999f1343SGarrett Wollman struct sockaddr_in **plocal_sin; 325999f1343SGarrett Wollman { 326df8bae1dSRodney W. Grimes struct in_ifaddr *ia; 32757bf258eSGarrett Wollman register struct sockaddr_in *sin = (struct sockaddr_in *)nam; 328df8bae1dSRodney W. Grimes 32957bf258eSGarrett Wollman if (nam->sa_len != sizeof (*sin)) 330df8bae1dSRodney W. Grimes return (EINVAL); 331df8bae1dSRodney W. Grimes if (sin->sin_family != AF_INET) 332df8bae1dSRodney W. Grimes return (EAFNOSUPPORT); 333df8bae1dSRodney W. Grimes if (sin->sin_port == 0) 334df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 33559562606SGarrett Wollman if (!TAILQ_EMPTY(&in_ifaddrhead)) { 336df8bae1dSRodney W. Grimes /* 337df8bae1dSRodney W. Grimes * If the destination address is INADDR_ANY, 338df8bae1dSRodney W. Grimes * use the primary local address. 339df8bae1dSRodney W. Grimes * If the supplied address is INADDR_BROADCAST, 340df8bae1dSRodney W. Grimes * and the primary interface supports broadcast, 341df8bae1dSRodney W. Grimes * choose the broadcast address for that interface. 342df8bae1dSRodney W. Grimes */ 343df8bae1dSRodney W. Grimes #define satosin(sa) ((struct sockaddr_in *)(sa)) 344df8bae1dSRodney W. Grimes #define sintosa(sin) ((struct sockaddr *)(sin)) 345df8bae1dSRodney W. Grimes #define ifatoia(ifa) ((struct in_ifaddr *)(ifa)) 346df8bae1dSRodney W. Grimes if (sin->sin_addr.s_addr == INADDR_ANY) 34759562606SGarrett Wollman sin->sin_addr = IA_SIN(in_ifaddrhead.tqh_first)->sin_addr; 348df8bae1dSRodney W. Grimes else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST && 34959562606SGarrett Wollman (in_ifaddrhead.tqh_first->ia_ifp->if_flags & IFF_BROADCAST)) 35059562606SGarrett Wollman sin->sin_addr = satosin(&in_ifaddrhead.tqh_first->ia_broadaddr)->sin_addr; 351df8bae1dSRodney W. Grimes } 352df8bae1dSRodney W. Grimes if (inp->inp_laddr.s_addr == INADDR_ANY) { 353df8bae1dSRodney W. Grimes register struct route *ro; 354df8bae1dSRodney W. Grimes 355df8bae1dSRodney W. Grimes ia = (struct in_ifaddr *)0; 356df8bae1dSRodney W. Grimes /* 357df8bae1dSRodney W. Grimes * If route is known or can be allocated now, 358df8bae1dSRodney W. Grimes * our src addr is taken from the i/f, else punt. 359df8bae1dSRodney W. Grimes */ 360df8bae1dSRodney W. Grimes ro = &inp->inp_route; 361df8bae1dSRodney W. Grimes if (ro->ro_rt && 362df8bae1dSRodney W. Grimes (satosin(&ro->ro_dst)->sin_addr.s_addr != 363df8bae1dSRodney W. Grimes sin->sin_addr.s_addr || 364df8bae1dSRodney W. Grimes inp->inp_socket->so_options & SO_DONTROUTE)) { 365df8bae1dSRodney W. Grimes RTFREE(ro->ro_rt); 366df8bae1dSRodney W. Grimes ro->ro_rt = (struct rtentry *)0; 367df8bae1dSRodney W. Grimes } 368df8bae1dSRodney W. Grimes if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/ 369df8bae1dSRodney W. Grimes (ro->ro_rt == (struct rtentry *)0 || 370df8bae1dSRodney W. Grimes ro->ro_rt->rt_ifp == (struct ifnet *)0)) { 371df8bae1dSRodney W. Grimes /* No route yet, so try to acquire one */ 372df8bae1dSRodney W. Grimes ro->ro_dst.sa_family = AF_INET; 373df8bae1dSRodney W. Grimes ro->ro_dst.sa_len = sizeof(struct sockaddr_in); 374df8bae1dSRodney W. Grimes ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = 375df8bae1dSRodney W. Grimes sin->sin_addr; 376df8bae1dSRodney W. Grimes rtalloc(ro); 377df8bae1dSRodney W. Grimes } 378df8bae1dSRodney W. Grimes /* 379df8bae1dSRodney W. Grimes * If we found a route, use the address 380df8bae1dSRodney W. Grimes * corresponding to the outgoing interface 381df8bae1dSRodney W. Grimes * unless it is the loopback (in case a route 382df8bae1dSRodney W. Grimes * to our address on another net goes to loopback). 383df8bae1dSRodney W. Grimes */ 384df8bae1dSRodney W. Grimes if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK)) 385df8bae1dSRodney W. Grimes ia = ifatoia(ro->ro_rt->rt_ifa); 386df8bae1dSRodney W. Grimes if (ia == 0) { 387df8bae1dSRodney W. Grimes u_short fport = sin->sin_port; 388df8bae1dSRodney W. Grimes 389df8bae1dSRodney W. Grimes sin->sin_port = 0; 390df8bae1dSRodney W. Grimes ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin))); 391df8bae1dSRodney W. Grimes if (ia == 0) 392df8bae1dSRodney W. Grimes ia = ifatoia(ifa_ifwithnet(sintosa(sin))); 393df8bae1dSRodney W. Grimes sin->sin_port = fport; 394df8bae1dSRodney W. Grimes if (ia == 0) 39559562606SGarrett Wollman ia = in_ifaddrhead.tqh_first; 396df8bae1dSRodney W. Grimes if (ia == 0) 397df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 398df8bae1dSRodney W. Grimes } 399df8bae1dSRodney W. Grimes /* 400df8bae1dSRodney W. Grimes * If the destination address is multicast and an outgoing 401df8bae1dSRodney W. Grimes * interface has been set as a multicast option, use the 402df8bae1dSRodney W. Grimes * address of that interface as our source address. 403df8bae1dSRodney W. Grimes */ 404df8bae1dSRodney W. Grimes if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) && 405df8bae1dSRodney W. Grimes inp->inp_moptions != NULL) { 406df8bae1dSRodney W. Grimes struct ip_moptions *imo; 407df8bae1dSRodney W. Grimes struct ifnet *ifp; 408df8bae1dSRodney W. Grimes 409df8bae1dSRodney W. Grimes imo = inp->inp_moptions; 410df8bae1dSRodney W. Grimes if (imo->imo_multicast_ifp != NULL) { 411df8bae1dSRodney W. Grimes ifp = imo->imo_multicast_ifp; 41259562606SGarrett Wollman for (ia = in_ifaddrhead.tqh_first; ia; 41359562606SGarrett Wollman ia = ia->ia_link.tqe_next) 414df8bae1dSRodney W. Grimes if (ia->ia_ifp == ifp) 415df8bae1dSRodney W. Grimes break; 416df8bae1dSRodney W. Grimes if (ia == 0) 417df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 418df8bae1dSRodney W. Grimes } 419df8bae1dSRodney W. Grimes } 420999f1343SGarrett Wollman /* 421999f1343SGarrett Wollman * Don't do pcblookup call here; return interface in plocal_sin 422999f1343SGarrett Wollman * and exit to caller, that will do the lookup. 423999f1343SGarrett Wollman */ 424999f1343SGarrett Wollman *plocal_sin = &ia->ia_addr; 425999f1343SGarrett Wollman 426999f1343SGarrett Wollman } 427999f1343SGarrett Wollman return(0); 428999f1343SGarrett Wollman } 429999f1343SGarrett Wollman 430999f1343SGarrett Wollman /* 431999f1343SGarrett Wollman * Outer subroutine: 432999f1343SGarrett Wollman * Connect from a socket to a specified address. 433999f1343SGarrett Wollman * Both address and port must be specified in argument sin. 434999f1343SGarrett Wollman * If don't have a local address for this socket yet, 435999f1343SGarrett Wollman * then pick one. 436999f1343SGarrett Wollman */ 437999f1343SGarrett Wollman int 438a29f300eSGarrett Wollman in_pcbconnect(inp, nam, p) 439999f1343SGarrett Wollman register struct inpcb *inp; 44057bf258eSGarrett Wollman struct sockaddr *nam; 441a29f300eSGarrett Wollman struct proc *p; 442999f1343SGarrett Wollman { 443999f1343SGarrett Wollman struct sockaddr_in *ifaddr; 44457bf258eSGarrett Wollman register struct sockaddr_in *sin = (struct sockaddr_in *)nam; 445999f1343SGarrett Wollman int error; 446999f1343SGarrett Wollman 447999f1343SGarrett Wollman /* 448999f1343SGarrett Wollman * Call inner routine, to assign local interface address. 449999f1343SGarrett Wollman */ 450831a80b0SMatthew Dillon if ((error = in_pcbladdr(inp, nam, &ifaddr)) != 0) 451999f1343SGarrett Wollman return(error); 452999f1343SGarrett Wollman 453c3229e05SDavid Greenman if (in_pcblookup_hash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port, 454df8bae1dSRodney W. Grimes inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 455c3229e05SDavid Greenman inp->inp_lport, 0) != NULL) { 456df8bae1dSRodney W. Grimes return (EADDRINUSE); 457c3229e05SDavid Greenman } 458df8bae1dSRodney W. Grimes if (inp->inp_laddr.s_addr == INADDR_ANY) { 459df8bae1dSRodney W. Grimes if (inp->inp_lport == 0) 46057bf258eSGarrett Wollman (void)in_pcbbind(inp, (struct sockaddr *)0, p); 461df8bae1dSRodney W. Grimes inp->inp_laddr = ifaddr->sin_addr; 462df8bae1dSRodney W. Grimes } 463df8bae1dSRodney W. Grimes inp->inp_faddr = sin->sin_addr; 464df8bae1dSRodney W. Grimes inp->inp_fport = sin->sin_port; 46515bd2b43SDavid Greenman in_pcbrehash(inp); 466df8bae1dSRodney W. Grimes return (0); 467df8bae1dSRodney W. Grimes } 468df8bae1dSRodney W. Grimes 46926f9a767SRodney W. Grimes void 470df8bae1dSRodney W. Grimes in_pcbdisconnect(inp) 471df8bae1dSRodney W. Grimes struct inpcb *inp; 472df8bae1dSRodney W. Grimes { 473df8bae1dSRodney W. Grimes 474df8bae1dSRodney W. Grimes inp->inp_faddr.s_addr = INADDR_ANY; 475df8bae1dSRodney W. Grimes inp->inp_fport = 0; 47615bd2b43SDavid Greenman in_pcbrehash(inp); 477df8bae1dSRodney W. Grimes if (inp->inp_socket->so_state & SS_NOFDREF) 478df8bae1dSRodney W. Grimes in_pcbdetach(inp); 479df8bae1dSRodney W. Grimes } 480df8bae1dSRodney W. Grimes 48126f9a767SRodney W. Grimes void 482df8bae1dSRodney W. Grimes in_pcbdetach(inp) 483df8bae1dSRodney W. Grimes struct inpcb *inp; 484df8bae1dSRodney W. Grimes { 485df8bae1dSRodney W. Grimes struct socket *so = inp->inp_socket; 4863d4d47f3SGarrett Wollman struct inpcbinfo *ipi = inp->inp_pcbinfo; 487df8bae1dSRodney W. Grimes 4883d4d47f3SGarrett Wollman inp->inp_gencnt = ++ipi->ipi_gencnt; 489c3229e05SDavid Greenman in_pcbremlists(inp); 490df8bae1dSRodney W. Grimes so->so_pcb = 0; 491df8bae1dSRodney W. Grimes sofree(so); 492df8bae1dSRodney W. Grimes if (inp->inp_options) 493df8bae1dSRodney W. Grimes (void)m_free(inp->inp_options); 494df8bae1dSRodney W. Grimes if (inp->inp_route.ro_rt) 495df8bae1dSRodney W. Grimes rtfree(inp->inp_route.ro_rt); 496df8bae1dSRodney W. Grimes ip_freemoptions(inp->inp_moptions); 4973d4d47f3SGarrett Wollman zfreei(ipi->ipi_zone, inp); 498df8bae1dSRodney W. Grimes } 499df8bae1dSRodney W. Grimes 500117bcae7SGarrett Wollman /* 501117bcae7SGarrett Wollman * The calling convention of in_setsockaddr() and in_setpeeraddr() was 502117bcae7SGarrett Wollman * modified to match the pru_sockaddr() and pru_peeraddr() entry points 503117bcae7SGarrett Wollman * in struct pr_usrreqs, so that protocols can just reference then directly 504117bcae7SGarrett Wollman * without the need for a wrapper function. The socket must have a valid 505117bcae7SGarrett Wollman * (i.e., non-nil) PCB, but it should be impossible to get an invalid one 506117bcae7SGarrett Wollman * except through a kernel programming error, so it is acceptable to panic 50757bf258eSGarrett Wollman * (or in this case trap) if the PCB is invalid. (Actually, we don't trap 50857bf258eSGarrett Wollman * because there actually /is/ a programming error somewhere... XXX) 509117bcae7SGarrett Wollman */ 510117bcae7SGarrett Wollman int 511117bcae7SGarrett Wollman in_setsockaddr(so, nam) 512117bcae7SGarrett Wollman struct socket *so; 51357bf258eSGarrett Wollman struct sockaddr **nam; 514df8bae1dSRodney W. Grimes { 515fdc984f7STor Egge int s; 516fdc984f7STor Egge register struct inpcb *inp; 517df8bae1dSRodney W. Grimes register struct sockaddr_in *sin; 518df8bae1dSRodney W. Grimes 519c3229e05SDavid Greenman /* 520c3229e05SDavid Greenman * Do the malloc first in case it blocks. 521c3229e05SDavid Greenman */ 52242fa505bSDavid Greenman MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_WAITOK); 52342fa505bSDavid Greenman bzero(sin, sizeof *sin); 52442fa505bSDavid Greenman sin->sin_family = AF_INET; 52542fa505bSDavid Greenman sin->sin_len = sizeof(*sin); 52642fa505bSDavid Greenman 527fdc984f7STor Egge s = splnet(); 528fdc984f7STor Egge inp = sotoinpcb(so); 529db112f04STor Egge if (!inp) { 530db112f04STor Egge splx(s); 53142fa505bSDavid Greenman free(sin, M_SONAME); 532db112f04STor Egge return EINVAL; 533db112f04STor Egge } 534df8bae1dSRodney W. Grimes sin->sin_port = inp->inp_lport; 535df8bae1dSRodney W. Grimes sin->sin_addr = inp->inp_laddr; 536db112f04STor Egge splx(s); 53742fa505bSDavid Greenman 53842fa505bSDavid Greenman *nam = (struct sockaddr *)sin; 539117bcae7SGarrett Wollman return 0; 540df8bae1dSRodney W. Grimes } 541df8bae1dSRodney W. Grimes 542117bcae7SGarrett Wollman int 543117bcae7SGarrett Wollman in_setpeeraddr(so, nam) 544117bcae7SGarrett Wollman struct socket *so; 54557bf258eSGarrett Wollman struct sockaddr **nam; 546df8bae1dSRodney W. Grimes { 547fdc984f7STor Egge int s; 548fdc984f7STor Egge struct inpcb *inp; 549df8bae1dSRodney W. Grimes register struct sockaddr_in *sin; 550df8bae1dSRodney W. Grimes 551c3229e05SDavid Greenman /* 552c3229e05SDavid Greenman * Do the malloc first in case it blocks. 553c3229e05SDavid Greenman */ 55442fa505bSDavid Greenman MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_WAITOK); 55542fa505bSDavid Greenman bzero((caddr_t)sin, sizeof (*sin)); 55642fa505bSDavid Greenman sin->sin_family = AF_INET; 55742fa505bSDavid Greenman sin->sin_len = sizeof(*sin); 55842fa505bSDavid Greenman 559fdc984f7STor Egge s = splnet(); 560fdc984f7STor Egge inp = sotoinpcb(so); 561db112f04STor Egge if (!inp) { 562db112f04STor Egge splx(s); 56342fa505bSDavid Greenman free(sin, M_SONAME); 564db112f04STor Egge return EINVAL; 565db112f04STor Egge } 566df8bae1dSRodney W. Grimes sin->sin_port = inp->inp_fport; 567df8bae1dSRodney W. Grimes sin->sin_addr = inp->inp_faddr; 568db112f04STor Egge splx(s); 56942fa505bSDavid Greenman 57042fa505bSDavid Greenman *nam = (struct sockaddr *)sin; 571117bcae7SGarrett Wollman return 0; 572df8bae1dSRodney W. Grimes } 573df8bae1dSRodney W. Grimes 574df8bae1dSRodney W. Grimes /* 575df8bae1dSRodney W. Grimes * Pass some notification to all connections of a protocol 576df8bae1dSRodney W. Grimes * associated with address dst. The local address and/or port numbers 577df8bae1dSRodney W. Grimes * may be specified to limit the search. The "usual action" will be 578df8bae1dSRodney W. Grimes * taken, depending on the ctlinput cmd. The caller must filter any 579df8bae1dSRodney W. Grimes * cmds that are uninteresting (e.g., no error in the map). 580df8bae1dSRodney W. Grimes * Call the protocol specific routine (if any) to report 581df8bae1dSRodney W. Grimes * any errors for each matching socket. 582df8bae1dSRodney W. Grimes */ 58326f9a767SRodney W. Grimes void 584df8bae1dSRodney W. Grimes in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify) 58515bd2b43SDavid Greenman struct inpcbhead *head; 586df8bae1dSRodney W. Grimes struct sockaddr *dst; 587df8bae1dSRodney W. Grimes u_int fport_arg, lport_arg; 588df8bae1dSRodney W. Grimes struct in_addr laddr; 589df8bae1dSRodney W. Grimes int cmd; 590df8bae1dSRodney W. Grimes void (*notify) __P((struct inpcb *, int)); 591df8bae1dSRodney W. Grimes { 592df8bae1dSRodney W. Grimes register struct inpcb *inp, *oinp; 593df8bae1dSRodney W. Grimes struct in_addr faddr; 594df8bae1dSRodney W. Grimes u_short fport = fport_arg, lport = lport_arg; 5957bc4aca7SDavid Greenman int errno, s; 596df8bae1dSRodney W. Grimes 597df8bae1dSRodney W. Grimes if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET) 598df8bae1dSRodney W. Grimes return; 599df8bae1dSRodney W. Grimes faddr = ((struct sockaddr_in *)dst)->sin_addr; 600df8bae1dSRodney W. Grimes if (faddr.s_addr == INADDR_ANY) 601df8bae1dSRodney W. Grimes return; 602df8bae1dSRodney W. Grimes 603df8bae1dSRodney W. Grimes /* 604df8bae1dSRodney W. Grimes * Redirects go to all references to the destination, 605df8bae1dSRodney W. Grimes * and use in_rtchange to invalidate the route cache. 606df8bae1dSRodney W. Grimes * Dead host indications: notify all references to the destination. 607df8bae1dSRodney W. Grimes * Otherwise, if we have knowledge of the local port and address, 608df8bae1dSRodney W. Grimes * deliver only to that socket. 609df8bae1dSRodney W. Grimes */ 610df8bae1dSRodney W. Grimes if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) { 611df8bae1dSRodney W. Grimes fport = 0; 612df8bae1dSRodney W. Grimes lport = 0; 613df8bae1dSRodney W. Grimes laddr.s_addr = 0; 614df8bae1dSRodney W. Grimes if (cmd != PRC_HOSTDEAD) 615df8bae1dSRodney W. Grimes notify = in_rtchange; 616df8bae1dSRodney W. Grimes } 617df8bae1dSRodney W. Grimes errno = inetctlerrmap[cmd]; 6187bc4aca7SDavid Greenman s = splnet(); 61915bd2b43SDavid Greenman for (inp = head->lh_first; inp != NULL;) { 620df8bae1dSRodney W. Grimes if (inp->inp_faddr.s_addr != faddr.s_addr || 621df8bae1dSRodney W. Grimes inp->inp_socket == 0 || 622df8bae1dSRodney W. Grimes (lport && inp->inp_lport != lport) || 623df8bae1dSRodney W. Grimes (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) || 624df8bae1dSRodney W. Grimes (fport && inp->inp_fport != fport)) { 62515bd2b43SDavid Greenman inp = inp->inp_list.le_next; 626df8bae1dSRodney W. Grimes continue; 627df8bae1dSRodney W. Grimes } 628df8bae1dSRodney W. Grimes oinp = inp; 62915bd2b43SDavid Greenman inp = inp->inp_list.le_next; 630df8bae1dSRodney W. Grimes if (notify) 631df8bae1dSRodney W. Grimes (*notify)(oinp, errno); 632df8bae1dSRodney W. Grimes } 6337bc4aca7SDavid Greenman splx(s); 634df8bae1dSRodney W. Grimes } 635df8bae1dSRodney W. Grimes 636df8bae1dSRodney W. Grimes /* 637df8bae1dSRodney W. Grimes * Check for alternatives when higher level complains 638df8bae1dSRodney W. Grimes * about service problems. For now, invalidate cached 639df8bae1dSRodney W. Grimes * routing information. If the route was created dynamically 640df8bae1dSRodney W. Grimes * (by a redirect), time to try a default gateway again. 641df8bae1dSRodney W. Grimes */ 64226f9a767SRodney W. Grimes void 643df8bae1dSRodney W. Grimes in_losing(inp) 644df8bae1dSRodney W. Grimes struct inpcb *inp; 645df8bae1dSRodney W. Grimes { 646df8bae1dSRodney W. Grimes register struct rtentry *rt; 647df8bae1dSRodney W. Grimes struct rt_addrinfo info; 648df8bae1dSRodney W. Grimes 649df8bae1dSRodney W. Grimes if ((rt = inp->inp_route.ro_rt)) { 650df8bae1dSRodney W. Grimes inp->inp_route.ro_rt = 0; 651df8bae1dSRodney W. Grimes bzero((caddr_t)&info, sizeof(info)); 652df8bae1dSRodney W. Grimes info.rti_info[RTAX_DST] = 653df8bae1dSRodney W. Grimes (struct sockaddr *)&inp->inp_route.ro_dst; 654df8bae1dSRodney W. Grimes info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; 655df8bae1dSRodney W. Grimes info.rti_info[RTAX_NETMASK] = rt_mask(rt); 656df8bae1dSRodney W. Grimes rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0); 657df8bae1dSRodney W. Grimes if (rt->rt_flags & RTF_DYNAMIC) 658df8bae1dSRodney W. Grimes (void) rtrequest(RTM_DELETE, rt_key(rt), 659df8bae1dSRodney W. Grimes rt->rt_gateway, rt_mask(rt), rt->rt_flags, 660df8bae1dSRodney W. Grimes (struct rtentry **)0); 661df8bae1dSRodney W. Grimes else 662df8bae1dSRodney W. Grimes /* 663df8bae1dSRodney W. Grimes * A new route can be allocated 664df8bae1dSRodney W. Grimes * the next time output is attempted. 665df8bae1dSRodney W. Grimes */ 666df8bae1dSRodney W. Grimes rtfree(rt); 667df8bae1dSRodney W. Grimes } 668df8bae1dSRodney W. Grimes } 669df8bae1dSRodney W. Grimes 670df8bae1dSRodney W. Grimes /* 671df8bae1dSRodney W. Grimes * After a routing change, flush old routing 672df8bae1dSRodney W. Grimes * and allocate a (hopefully) better one. 673df8bae1dSRodney W. Grimes */ 6740312fbe9SPoul-Henning Kamp static void 675df8bae1dSRodney W. Grimes in_rtchange(inp, errno) 676df8bae1dSRodney W. Grimes register struct inpcb *inp; 677df8bae1dSRodney W. Grimes int errno; 678df8bae1dSRodney W. Grimes { 679df8bae1dSRodney W. Grimes if (inp->inp_route.ro_rt) { 680df8bae1dSRodney W. Grimes rtfree(inp->inp_route.ro_rt); 681df8bae1dSRodney W. Grimes inp->inp_route.ro_rt = 0; 682df8bae1dSRodney W. Grimes /* 683df8bae1dSRodney W. Grimes * A new route can be allocated the next time 684df8bae1dSRodney W. Grimes * output is attempted. 685df8bae1dSRodney W. Grimes */ 686df8bae1dSRodney W. Grimes } 687df8bae1dSRodney W. Grimes } 688df8bae1dSRodney W. Grimes 689c3229e05SDavid Greenman /* 690c3229e05SDavid Greenman * Lookup a PCB based on the local address and port. 691c3229e05SDavid Greenman */ 692df8bae1dSRodney W. Grimes struct inpcb * 693c3229e05SDavid Greenman in_pcblookup_local(pcbinfo, laddr, lport_arg, wild_okay) 6946d6a026bSDavid Greenman struct inpcbinfo *pcbinfo; 695c3229e05SDavid Greenman struct in_addr laddr; 696c3229e05SDavid Greenman u_int lport_arg; 6976d6a026bSDavid Greenman int wild_okay; 698df8bae1dSRodney W. Grimes { 699f1d19042SArchie Cobbs register struct inpcb *inp; 700df8bae1dSRodney W. Grimes int matchwild = 3, wildcard; 701c3229e05SDavid Greenman u_short lport = lport_arg; 7027bc4aca7SDavid Greenman 703c3229e05SDavid Greenman if (!wild_okay) { 704c3229e05SDavid Greenman struct inpcbhead *head; 705c3229e05SDavid Greenman /* 706c3229e05SDavid Greenman * Look for an unconnected (wildcard foreign addr) PCB that 707c3229e05SDavid Greenman * matches the local address and port we're looking for. 708c3229e05SDavid Greenman */ 709c3229e05SDavid Greenman head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)]; 710c3229e05SDavid Greenman for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { 711c3229e05SDavid Greenman if (inp->inp_faddr.s_addr == INADDR_ANY && 712c3229e05SDavid Greenman inp->inp_laddr.s_addr == laddr.s_addr && 713c3229e05SDavid Greenman inp->inp_lport == lport) { 714c3229e05SDavid Greenman /* 715c3229e05SDavid Greenman * Found. 716c3229e05SDavid Greenman */ 717c3229e05SDavid Greenman return (inp); 718df8bae1dSRodney W. Grimes } 719c3229e05SDavid Greenman } 720c3229e05SDavid Greenman /* 721c3229e05SDavid Greenman * Not found. 722c3229e05SDavid Greenman */ 723c3229e05SDavid Greenman return (NULL); 724c3229e05SDavid Greenman } else { 725c3229e05SDavid Greenman struct inpcbporthead *porthash; 726c3229e05SDavid Greenman struct inpcbport *phd; 727c3229e05SDavid Greenman struct inpcb *match = NULL; 728c3229e05SDavid Greenman /* 729c3229e05SDavid Greenman * Best fit PCB lookup. 730c3229e05SDavid Greenman * 731c3229e05SDavid Greenman * First see if this local port is in use by looking on the 732c3229e05SDavid Greenman * port hash list. 733c3229e05SDavid Greenman */ 734c3229e05SDavid Greenman porthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(lport, 735c3229e05SDavid Greenman pcbinfo->porthashmask)]; 736c3229e05SDavid Greenman for (phd = porthash->lh_first; phd != NULL; phd = phd->phd_hash.le_next) { 737c3229e05SDavid Greenman if (phd->phd_port == lport) 738c3229e05SDavid Greenman break; 739c3229e05SDavid Greenman } 740c3229e05SDavid Greenman if (phd != NULL) { 741c3229e05SDavid Greenman /* 742c3229e05SDavid Greenman * Port is in use by one or more PCBs. Look for best 743c3229e05SDavid Greenman * fit. 744c3229e05SDavid Greenman */ 745c3229e05SDavid Greenman for (inp = phd->phd_pcblist.lh_first; inp != NULL; 746c3229e05SDavid Greenman inp = inp->inp_portlist.le_next) { 747c3229e05SDavid Greenman wildcard = 0; 748c3229e05SDavid Greenman if (inp->inp_faddr.s_addr != INADDR_ANY) 749c3229e05SDavid Greenman wildcard++; 75015bd2b43SDavid Greenman if (inp->inp_laddr.s_addr != INADDR_ANY) { 75115bd2b43SDavid Greenman if (laddr.s_addr == INADDR_ANY) 75215bd2b43SDavid Greenman wildcard++; 75315bd2b43SDavid Greenman else if (inp->inp_laddr.s_addr != laddr.s_addr) 75415bd2b43SDavid Greenman continue; 75515bd2b43SDavid Greenman } else { 75615bd2b43SDavid Greenman if (laddr.s_addr != INADDR_ANY) 75715bd2b43SDavid Greenman wildcard++; 75815bd2b43SDavid Greenman } 759df8bae1dSRodney W. Grimes if (wildcard < matchwild) { 760df8bae1dSRodney W. Grimes match = inp; 761df8bae1dSRodney W. Grimes matchwild = wildcard; 7623dbdc25cSDavid Greenman if (matchwild == 0) { 763df8bae1dSRodney W. Grimes break; 764df8bae1dSRodney W. Grimes } 765df8bae1dSRodney W. Grimes } 7663dbdc25cSDavid Greenman } 767c3229e05SDavid Greenman } 768df8bae1dSRodney W. Grimes return (match); 769df8bae1dSRodney W. Grimes } 770c3229e05SDavid Greenman } 77115bd2b43SDavid Greenman 77215bd2b43SDavid Greenman /* 77315bd2b43SDavid Greenman * Lookup PCB in hash list. 77415bd2b43SDavid Greenman */ 77515bd2b43SDavid Greenman struct inpcb * 776c3229e05SDavid Greenman in_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard) 77715bd2b43SDavid Greenman struct inpcbinfo *pcbinfo; 77815bd2b43SDavid Greenman struct in_addr faddr, laddr; 77915bd2b43SDavid Greenman u_int fport_arg, lport_arg; 7806d6a026bSDavid Greenman int wildcard; 78115bd2b43SDavid Greenman { 78215bd2b43SDavid Greenman struct inpcbhead *head; 78315bd2b43SDavid Greenman register struct inpcb *inp; 78415bd2b43SDavid Greenman u_short fport = fport_arg, lport = lport_arg; 78515bd2b43SDavid Greenman 78615bd2b43SDavid Greenman /* 78715bd2b43SDavid Greenman * First look for an exact match. 78815bd2b43SDavid Greenman */ 789ddd79a97SDavid Greenman head = &pcbinfo->hashbase[INP_PCBHASH(faddr.s_addr, lport, fport, pcbinfo->hashmask)]; 79015bd2b43SDavid Greenman for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { 7916d6a026bSDavid Greenman if (inp->inp_faddr.s_addr == faddr.s_addr && 792ca98b82cSDavid Greenman inp->inp_laddr.s_addr == laddr.s_addr && 793ca98b82cSDavid Greenman inp->inp_fport == fport && 794c3229e05SDavid Greenman inp->inp_lport == lport) { 795c3229e05SDavid Greenman /* 796c3229e05SDavid Greenman * Found. 797c3229e05SDavid Greenman */ 798c3229e05SDavid Greenman return (inp); 799c3229e05SDavid Greenman } 8006d6a026bSDavid Greenman } 8016d6a026bSDavid Greenman if (wildcard) { 8026d6a026bSDavid Greenman struct inpcb *local_wild = NULL; 8036d6a026bSDavid Greenman 804ddd79a97SDavid Greenman head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)]; 8056d6a026bSDavid Greenman for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { 8066d6a026bSDavid Greenman if (inp->inp_faddr.s_addr == INADDR_ANY && 807c3229e05SDavid Greenman inp->inp_lport == lport) { 8086d6a026bSDavid Greenman if (inp->inp_laddr.s_addr == laddr.s_addr) 809c3229e05SDavid Greenman return (inp); 8106d6a026bSDavid Greenman else if (inp->inp_laddr.s_addr == INADDR_ANY) 8116d6a026bSDavid Greenman local_wild = inp; 8126d6a026bSDavid Greenman } 8136d6a026bSDavid Greenman } 814c3229e05SDavid Greenman return (local_wild); 8156d6a026bSDavid Greenman } 816c3229e05SDavid Greenman 817c3229e05SDavid Greenman /* 818c3229e05SDavid Greenman * Not found. 819c3229e05SDavid Greenman */ 8206d6a026bSDavid Greenman return (NULL); 82115bd2b43SDavid Greenman } 82215bd2b43SDavid Greenman 8237bc4aca7SDavid Greenman /* 824c3229e05SDavid Greenman * Insert PCB onto various hash lists. 8257bc4aca7SDavid Greenman */ 826c3229e05SDavid Greenman int 82715bd2b43SDavid Greenman in_pcbinshash(inp) 82815bd2b43SDavid Greenman struct inpcb *inp; 82915bd2b43SDavid Greenman { 830c3229e05SDavid Greenman struct inpcbhead *pcbhash; 831c3229e05SDavid Greenman struct inpcbporthead *pcbporthash; 832c3229e05SDavid Greenman struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; 833c3229e05SDavid Greenman struct inpcbport *phd; 83415bd2b43SDavid Greenman 835c3229e05SDavid Greenman pcbhash = &pcbinfo->hashbase[INP_PCBHASH(inp->inp_faddr.s_addr, 836c3229e05SDavid Greenman inp->inp_lport, inp->inp_fport, pcbinfo->hashmask)]; 83715bd2b43SDavid Greenman 838c3229e05SDavid Greenman pcbporthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(inp->inp_lport, 839c3229e05SDavid Greenman pcbinfo->porthashmask)]; 840c3229e05SDavid Greenman 841c3229e05SDavid Greenman /* 842c3229e05SDavid Greenman * Go through port list and look for a head for this lport. 843c3229e05SDavid Greenman */ 844c3229e05SDavid Greenman for (phd = pcbporthash->lh_first; phd != NULL; phd = phd->phd_hash.le_next) { 845c3229e05SDavid Greenman if (phd->phd_port == inp->inp_lport) 846c3229e05SDavid Greenman break; 847c3229e05SDavid Greenman } 848c3229e05SDavid Greenman /* 849c3229e05SDavid Greenman * If none exists, malloc one and tack it on. 850c3229e05SDavid Greenman */ 851c3229e05SDavid Greenman if (phd == NULL) { 852c3229e05SDavid Greenman MALLOC(phd, struct inpcbport *, sizeof(struct inpcbport), M_PCB, M_NOWAIT); 853c3229e05SDavid Greenman if (phd == NULL) { 854c3229e05SDavid Greenman return (ENOBUFS); /* XXX */ 855c3229e05SDavid Greenman } 856c3229e05SDavid Greenman phd->phd_port = inp->inp_lport; 857c3229e05SDavid Greenman LIST_INIT(&phd->phd_pcblist); 858c3229e05SDavid Greenman LIST_INSERT_HEAD(pcbporthash, phd, phd_hash); 859c3229e05SDavid Greenman } 860c3229e05SDavid Greenman inp->inp_phd = phd; 861c3229e05SDavid Greenman LIST_INSERT_HEAD(&phd->phd_pcblist, inp, inp_portlist); 862c3229e05SDavid Greenman LIST_INSERT_HEAD(pcbhash, inp, inp_hash); 863c3229e05SDavid Greenman return (0); 86415bd2b43SDavid Greenman } 86515bd2b43SDavid Greenman 866c3229e05SDavid Greenman /* 867c3229e05SDavid Greenman * Move PCB to the proper hash bucket when { faddr, fport } have been 868c3229e05SDavid Greenman * changed. NOTE: This does not handle the case of the lport changing (the 869c3229e05SDavid Greenman * hashed port list would have to be updated as well), so the lport must 870c3229e05SDavid Greenman * not change after in_pcbinshash() has been called. 871c3229e05SDavid Greenman */ 87215bd2b43SDavid Greenman void 87315bd2b43SDavid Greenman in_pcbrehash(inp) 87415bd2b43SDavid Greenman struct inpcb *inp; 87515bd2b43SDavid Greenman { 87615bd2b43SDavid Greenman struct inpcbhead *head; 87715bd2b43SDavid Greenman 878ddd79a97SDavid Greenman head = &inp->inp_pcbinfo->hashbase[INP_PCBHASH(inp->inp_faddr.s_addr, 879ddd79a97SDavid Greenman inp->inp_lport, inp->inp_fport, inp->inp_pcbinfo->hashmask)]; 88015bd2b43SDavid Greenman 881c3229e05SDavid Greenman LIST_REMOVE(inp, inp_hash); 88215bd2b43SDavid Greenman LIST_INSERT_HEAD(head, inp, inp_hash); 883c3229e05SDavid Greenman } 884c3229e05SDavid Greenman 885c3229e05SDavid Greenman /* 886c3229e05SDavid Greenman * Remove PCB from various lists. 887c3229e05SDavid Greenman */ 888c3229e05SDavid Greenman static void 889c3229e05SDavid Greenman in_pcbremlists(inp) 890c3229e05SDavid Greenman struct inpcb *inp; 891c3229e05SDavid Greenman { 89298271db4SGarrett Wollman inp->inp_gencnt = ++inp->inp_pcbinfo->ipi_gencnt; 893c3229e05SDavid Greenman if (inp->inp_lport) { 894c3229e05SDavid Greenman struct inpcbport *phd = inp->inp_phd; 895c3229e05SDavid Greenman 896c3229e05SDavid Greenman LIST_REMOVE(inp, inp_hash); 897c3229e05SDavid Greenman LIST_REMOVE(inp, inp_portlist); 898c3229e05SDavid Greenman if (phd->phd_pcblist.lh_first == NULL) { 899c3229e05SDavid Greenman LIST_REMOVE(phd, phd_hash); 900c3229e05SDavid Greenman free(phd, M_PCB); 901c3229e05SDavid Greenman } 902c3229e05SDavid Greenman } 903c3229e05SDavid Greenman LIST_REMOVE(inp, inp_list); 9043d4d47f3SGarrett Wollman inp->inp_pcbinfo->ipi_count--; 90515bd2b43SDavid Greenman } 90675c13541SPoul-Henning Kamp 90775c13541SPoul-Henning Kamp int 90875c13541SPoul-Henning Kamp prison_xinpcb(struct proc *p, struct inpcb *inp) 90975c13541SPoul-Henning Kamp { 91075c13541SPoul-Henning Kamp if (!p->p_prison) 91175c13541SPoul-Henning Kamp return (0); 91275c13541SPoul-Henning Kamp if (ntohl(inp->inp_laddr.s_addr) == p->p_prison->pr_ip) 91375c13541SPoul-Henning Kamp return (0); 91475c13541SPoul-Henning Kamp return (1); 91575c13541SPoul-Henning Kamp } 916