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 34c3aac50fSPeter Wemm * $FreeBSD$ 35df8bae1dSRodney W. Grimes */ 36df8bae1dSRodney W. Grimes 376a800098SYoshinobu Inoue #include "opt_ipsec.h" 38cfa1ca9dSYoshinobu Inoue #include "opt_inet6.h" 39cfa1ca9dSYoshinobu Inoue 40df8bae1dSRodney W. Grimes #include <sys/param.h> 41df8bae1dSRodney W. Grimes #include <sys/systm.h> 42df8bae1dSRodney W. Grimes #include <sys/malloc.h> 43df8bae1dSRodney W. Grimes #include <sys/mbuf.h> 44cfa1ca9dSYoshinobu Inoue #include <sys/domain.h> 45df8bae1dSRodney W. Grimes #include <sys/protosw.h> 46df8bae1dSRodney W. Grimes #include <sys/socket.h> 47df8bae1dSRodney W. Grimes #include <sys/socketvar.h> 48df8bae1dSRodney W. Grimes #include <sys/proc.h> 4975c13541SPoul-Henning Kamp #include <sys/jail.h> 50101f9fc8SPeter Wemm #include <sys/kernel.h> 51101f9fc8SPeter Wemm #include <sys/sysctl.h> 528781d8e9SBruce Evans 5308637435SBruce Evans #include <machine/limits.h> 5408637435SBruce Evans 558781d8e9SBruce Evans #include <vm/vm_zone.h> 56df8bae1dSRodney W. Grimes 57df8bae1dSRodney W. Grimes #include <net/if.h> 58cfa1ca9dSYoshinobu Inoue #include <net/if_types.h> 59df8bae1dSRodney W. Grimes #include <net/route.h> 60df8bae1dSRodney W. Grimes 61df8bae1dSRodney W. Grimes #include <netinet/in.h> 62df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h> 63df8bae1dSRodney W. Grimes #include <netinet/in_var.h> 64df8bae1dSRodney W. Grimes #include <netinet/ip_var.h> 65cfa1ca9dSYoshinobu Inoue #ifdef INET6 66cfa1ca9dSYoshinobu Inoue #include <netinet/ip6.h> 67cfa1ca9dSYoshinobu Inoue #include <netinet6/ip6_var.h> 68cfa1ca9dSYoshinobu Inoue #endif /* INET6 */ 69cfa1ca9dSYoshinobu Inoue 70cfa1ca9dSYoshinobu Inoue #include "faith.h" 71cfa1ca9dSYoshinobu Inoue 72cfa1ca9dSYoshinobu Inoue #ifdef IPSEC 73cfa1ca9dSYoshinobu Inoue #include <netinet6/ipsec.h> 74cfa1ca9dSYoshinobu Inoue #include <netkey/key.h> 75cfa1ca9dSYoshinobu Inoue #endif /* IPSEC */ 76df8bae1dSRodney W. Grimes 77df8bae1dSRodney W. Grimes struct in_addr zeroin_addr; 78df8bae1dSRodney W. Grimes 79101f9fc8SPeter Wemm /* 80101f9fc8SPeter Wemm * These configure the range of local port addresses assigned to 81101f9fc8SPeter Wemm * "unspecified" outgoing connections/packets/whatever. 82101f9fc8SPeter Wemm */ 8382cd038dSYoshinobu Inoue int ipport_lowfirstauto = IPPORT_RESERVED - 1; /* 1023 */ 8482cd038dSYoshinobu Inoue int ipport_lowlastauto = IPPORT_RESERVEDSTART; /* 600 */ 8582cd038dSYoshinobu Inoue int ipport_firstauto = IPPORT_RESERVED; /* 1024 */ 8682cd038dSYoshinobu Inoue int ipport_lastauto = IPPORT_USERRESERVED; /* 5000 */ 8782cd038dSYoshinobu Inoue int ipport_hifirstauto = IPPORT_HIFIRSTAUTO; /* 49152 */ 8882cd038dSYoshinobu Inoue int ipport_hilastauto = IPPORT_HILASTAUTO; /* 65535 */ 89101f9fc8SPeter Wemm 90bbd42ad0SPeter Wemm #define RANGECHK(var, min, max) \ 91bbd42ad0SPeter Wemm if ((var) < (min)) { (var) = (min); } \ 92bbd42ad0SPeter Wemm else if ((var) > (max)) { (var) = (max); } 93bbd42ad0SPeter Wemm 94bbd42ad0SPeter Wemm static int 9582d9ae4eSPoul-Henning Kamp sysctl_net_ipport_check(SYSCTL_HANDLER_ARGS) 96bbd42ad0SPeter Wemm { 97bbd42ad0SPeter Wemm int error = sysctl_handle_int(oidp, 98bbd42ad0SPeter Wemm oidp->oid_arg1, oidp->oid_arg2, req); 99bbd42ad0SPeter Wemm if (!error) { 100bbd42ad0SPeter Wemm RANGECHK(ipport_lowfirstauto, 1, IPPORT_RESERVED - 1); 101bbd42ad0SPeter Wemm RANGECHK(ipport_lowlastauto, 1, IPPORT_RESERVED - 1); 102bbd42ad0SPeter Wemm RANGECHK(ipport_firstauto, IPPORT_RESERVED, USHRT_MAX); 103bbd42ad0SPeter Wemm RANGECHK(ipport_lastauto, IPPORT_RESERVED, USHRT_MAX); 104bbd42ad0SPeter Wemm RANGECHK(ipport_hifirstauto, IPPORT_RESERVED, USHRT_MAX); 105bbd42ad0SPeter Wemm RANGECHK(ipport_hilastauto, IPPORT_RESERVED, USHRT_MAX); 106bbd42ad0SPeter Wemm } 107bbd42ad0SPeter Wemm return error; 108bbd42ad0SPeter Wemm } 109bbd42ad0SPeter Wemm 110bbd42ad0SPeter Wemm #undef RANGECHK 111bbd42ad0SPeter Wemm 11233b3ac06SPeter Wemm SYSCTL_NODE(_net_inet_ip, IPPROTO_IP, portrange, CTLFLAG_RW, 0, "IP Ports"); 11333b3ac06SPeter Wemm 114bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowfirst, CTLTYPE_INT|CTLFLAG_RW, 115bbd42ad0SPeter Wemm &ipport_lowfirstauto, 0, &sysctl_net_ipport_check, "I", ""); 116bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowlast, CTLTYPE_INT|CTLFLAG_RW, 117bbd42ad0SPeter Wemm &ipport_lowlastauto, 0, &sysctl_net_ipport_check, "I", ""); 118bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, first, CTLTYPE_INT|CTLFLAG_RW, 119bbd42ad0SPeter Wemm &ipport_firstauto, 0, &sysctl_net_ipport_check, "I", ""); 120bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, last, CTLTYPE_INT|CTLFLAG_RW, 121bbd42ad0SPeter Wemm &ipport_lastauto, 0, &sysctl_net_ipport_check, "I", ""); 122bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hifirst, CTLTYPE_INT|CTLFLAG_RW, 123bbd42ad0SPeter Wemm &ipport_hifirstauto, 0, &sysctl_net_ipport_check, "I", ""); 124bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hilast, CTLTYPE_INT|CTLFLAG_RW, 125bbd42ad0SPeter Wemm &ipport_hilastauto, 0, &sysctl_net_ipport_check, "I", ""); 1260312fbe9SPoul-Henning Kamp 127c3229e05SDavid Greenman /* 128c3229e05SDavid Greenman * in_pcb.c: manage the Protocol Control Blocks. 129c3229e05SDavid Greenman * 130c3229e05SDavid Greenman * NOTE: It is assumed that most of these functions will be called at 131c3229e05SDavid Greenman * splnet(). XXX - There are, unfortunately, a few exceptions to this 132c3229e05SDavid Greenman * rule that should be fixed. 133c3229e05SDavid Greenman */ 134c3229e05SDavid Greenman 135c3229e05SDavid Greenman /* 136c3229e05SDavid Greenman * Allocate a PCB and associate it with the socket. 137c3229e05SDavid Greenman */ 138df8bae1dSRodney W. Grimes int 139a29f300eSGarrett Wollman in_pcballoc(so, pcbinfo, p) 140df8bae1dSRodney W. Grimes struct socket *so; 14115bd2b43SDavid Greenman struct inpcbinfo *pcbinfo; 142a29f300eSGarrett Wollman struct proc *p; 143df8bae1dSRodney W. Grimes { 144df8bae1dSRodney W. Grimes register struct inpcb *inp; 145df8bae1dSRodney W. Grimes 146a3ea6d41SDag-Erling Smørgrav inp = zalloc(pcbinfo->ipi_zone); 147df8bae1dSRodney W. Grimes if (inp == NULL) 148df8bae1dSRodney W. Grimes return (ENOBUFS); 149df8bae1dSRodney W. Grimes bzero((caddr_t)inp, sizeof(*inp)); 1503d4d47f3SGarrett Wollman inp->inp_gencnt = ++pcbinfo->ipi_gencnt; 15115bd2b43SDavid Greenman inp->inp_pcbinfo = pcbinfo; 152df8bae1dSRodney W. Grimes inp->inp_socket = so; 15375daea93SPaul Saab #if defined(INET6) 15433841545SHajimu UMEMOTO if (INP_SOCKAF(so) == AF_INET6 && !ip6_mapped_addr_on) 15533841545SHajimu UMEMOTO inp->inp_flags |= IN6P_IPV6_V6ONLY; 15675daea93SPaul Saab #endif 15715bd2b43SDavid Greenman LIST_INSERT_HEAD(pcbinfo->listhead, inp, inp_list); 1583d4d47f3SGarrett Wollman pcbinfo->ipi_count++; 159df8bae1dSRodney W. Grimes so->so_pcb = (caddr_t)inp; 16033841545SHajimu UMEMOTO #ifdef INET6 16133841545SHajimu UMEMOTO if (ip6_auto_flowlabel) 16233841545SHajimu UMEMOTO inp->inp_flags |= IN6P_AUTOFLOWLABEL; 16333841545SHajimu UMEMOTO #endif 164df8bae1dSRodney W. Grimes return (0); 165df8bae1dSRodney W. Grimes } 166df8bae1dSRodney W. Grimes 167df8bae1dSRodney W. Grimes int 168a29f300eSGarrett Wollman in_pcbbind(inp, nam, p) 169df8bae1dSRodney W. Grimes register struct inpcb *inp; 17057bf258eSGarrett Wollman struct sockaddr *nam; 171a29f300eSGarrett Wollman struct proc *p; 172df8bae1dSRodney W. Grimes { 173df8bae1dSRodney W. Grimes register struct socket *so = inp->inp_socket; 17437bd2b30SPeter Wemm unsigned short *lastport; 17515bd2b43SDavid Greenman struct sockaddr_in *sin; 176c3229e05SDavid Greenman struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; 177df8bae1dSRodney W. Grimes u_short lport = 0; 178df8bae1dSRodney W. Grimes int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); 17975c13541SPoul-Henning Kamp int error, prison = 0; 180df8bae1dSRodney W. Grimes 18159562606SGarrett Wollman if (TAILQ_EMPTY(&in_ifaddrhead)) /* XXX broken! */ 182df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 183df8bae1dSRodney W. Grimes if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY) 184df8bae1dSRodney W. Grimes return (EINVAL); 185c3229e05SDavid Greenman if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) 1866d6a026bSDavid Greenman wild = 1; 187df8bae1dSRodney W. Grimes if (nam) { 18857bf258eSGarrett Wollman sin = (struct sockaddr_in *)nam; 18957bf258eSGarrett Wollman if (nam->sa_len != sizeof (*sin)) 190df8bae1dSRodney W. Grimes return (EINVAL); 191df8bae1dSRodney W. Grimes #ifdef notdef 192df8bae1dSRodney W. Grimes /* 193df8bae1dSRodney W. Grimes * We should check the family, but old programs 194df8bae1dSRodney W. Grimes * incorrectly fail to initialize it. 195df8bae1dSRodney W. Grimes */ 196df8bae1dSRodney W. Grimes if (sin->sin_family != AF_INET) 197df8bae1dSRodney W. Grimes return (EAFNOSUPPORT); 198df8bae1dSRodney W. Grimes #endif 199e4bdf25dSPoul-Henning Kamp if (sin->sin_addr.s_addr != INADDR_ANY) 20091421ba2SRobert Watson if (prison_ip(p->p_ucred, 0, &sin->sin_addr.s_addr)) 20175c13541SPoul-Henning Kamp return(EINVAL); 202df8bae1dSRodney W. Grimes lport = sin->sin_port; 203df8bae1dSRodney W. Grimes if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { 204df8bae1dSRodney W. Grimes /* 205df8bae1dSRodney W. Grimes * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; 206df8bae1dSRodney W. Grimes * allow complete duplication of binding if 207df8bae1dSRodney W. Grimes * SO_REUSEPORT is set, or if SO_REUSEADDR is set 208df8bae1dSRodney W. Grimes * and a multicast address is bound on both 209df8bae1dSRodney W. Grimes * new and duplicated sockets. 210df8bae1dSRodney W. Grimes */ 211df8bae1dSRodney W. Grimes if (so->so_options & SO_REUSEADDR) 212df8bae1dSRodney W. Grimes reuseport = SO_REUSEADDR|SO_REUSEPORT; 213df8bae1dSRodney W. Grimes } else if (sin->sin_addr.s_addr != INADDR_ANY) { 214df8bae1dSRodney W. Grimes sin->sin_port = 0; /* yech... */ 215df8bae1dSRodney W. Grimes if (ifa_ifwithaddr((struct sockaddr *)sin) == 0) 216df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 217df8bae1dSRodney W. Grimes } 218df8bae1dSRodney W. Grimes if (lport) { 219df8bae1dSRodney W. Grimes struct inpcb *t; 220df8bae1dSRodney W. Grimes /* GROSS */ 22157bf258eSGarrett Wollman if (ntohs(lport) < IPPORT_RESERVED && p && 22275c13541SPoul-Henning Kamp suser_xxx(0, p, PRISON_ROOT)) 2232469dd60SGarrett Wollman return (EACCES); 22491421ba2SRobert Watson if (p && jailed(p->p_ucred)) 22575c13541SPoul-Henning Kamp prison = 1; 2262f9a2132SBrian Feldman if (so->so_cred->cr_uid != 0 && 22752b65dbeSBill Fenner !IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { 2284049a042SGuido van Rooij t = in_pcblookup_local(inp->inp_pcbinfo, 22975c13541SPoul-Henning Kamp sin->sin_addr, lport, 23075c13541SPoul-Henning Kamp prison ? 0 : INPLOOKUP_WILDCARD); 23152b65dbeSBill Fenner if (t && 23252b65dbeSBill Fenner (ntohl(sin->sin_addr.s_addr) != INADDR_ANY || 23352b65dbeSBill Fenner ntohl(t->inp_laddr.s_addr) != INADDR_ANY || 23452b65dbeSBill Fenner (t->inp_socket->so_options & 23552b65dbeSBill Fenner SO_REUSEPORT) == 0) && 2362f9a2132SBrian Feldman (so->so_cred->cr_uid != 237cfa1ca9dSYoshinobu Inoue t->inp_socket->so_cred->cr_uid)) { 238cfa1ca9dSYoshinobu Inoue #if defined(INET6) 23933841545SHajimu UMEMOTO if (ntohl(sin->sin_addr.s_addr) != 240cfa1ca9dSYoshinobu Inoue INADDR_ANY || 241cfa1ca9dSYoshinobu Inoue ntohl(t->inp_laddr.s_addr) != 242cfa1ca9dSYoshinobu Inoue INADDR_ANY || 243cfa1ca9dSYoshinobu Inoue INP_SOCKAF(so) == 244cfa1ca9dSYoshinobu Inoue INP_SOCKAF(t->inp_socket)) 245cfa1ca9dSYoshinobu Inoue #endif /* defined(INET6) */ 2464049a042SGuido van Rooij return (EADDRINUSE); 2474049a042SGuido van Rooij } 248cfa1ca9dSYoshinobu Inoue } 249970680faSPoul-Henning Kamp if (prison && 250970680faSPoul-Henning Kamp prison_ip(p->p_ucred, 0, &sin->sin_addr.s_addr)) 251970680faSPoul-Henning Kamp return (EADDRNOTAVAIL); 252c3229e05SDavid Greenman t = in_pcblookup_local(pcbinfo, sin->sin_addr, 25375c13541SPoul-Henning Kamp lport, prison ? 0 : wild); 254cfa1ca9dSYoshinobu Inoue if (t && 255cfa1ca9dSYoshinobu Inoue (reuseport & t->inp_socket->so_options) == 0) { 256cfa1ca9dSYoshinobu Inoue #if defined(INET6) 25733841545SHajimu UMEMOTO if (ntohl(sin->sin_addr.s_addr) != 258cfa1ca9dSYoshinobu Inoue INADDR_ANY || 259cfa1ca9dSYoshinobu Inoue ntohl(t->inp_laddr.s_addr) != 260cfa1ca9dSYoshinobu Inoue INADDR_ANY || 261cfa1ca9dSYoshinobu Inoue INP_SOCKAF(so) == 262cfa1ca9dSYoshinobu Inoue INP_SOCKAF(t->inp_socket)) 263cfa1ca9dSYoshinobu Inoue #endif /* defined(INET6) */ 264df8bae1dSRodney W. Grimes return (EADDRINUSE); 265df8bae1dSRodney W. Grimes } 266cfa1ca9dSYoshinobu Inoue } 267df8bae1dSRodney W. Grimes inp->inp_laddr = sin->sin_addr; 268df8bae1dSRodney W. Grimes } 26933b3ac06SPeter Wemm if (lport == 0) { 27033b3ac06SPeter Wemm ushort first, last; 27133b3ac06SPeter Wemm int count; 27233b3ac06SPeter Wemm 273e4bdf25dSPoul-Henning Kamp if (inp->inp_laddr.s_addr != INADDR_ANY) 274503d3c02SPoul-Henning Kamp if (prison_ip(p->p_ucred, 0, &inp->inp_laddr.s_addr )) { 275503d3c02SPoul-Henning Kamp inp->inp_laddr.s_addr = INADDR_ANY; 27675c13541SPoul-Henning Kamp return (EINVAL); 277503d3c02SPoul-Henning Kamp } 278321a2846SPoul-Henning Kamp inp->inp_flags |= INP_ANONPORT; 279321a2846SPoul-Henning Kamp 28033b3ac06SPeter Wemm if (inp->inp_flags & INP_HIGHPORT) { 28133b3ac06SPeter Wemm first = ipport_hifirstauto; /* sysctl */ 28233b3ac06SPeter Wemm last = ipport_hilastauto; 283c3229e05SDavid Greenman lastport = &pcbinfo->lasthi; 28433b3ac06SPeter Wemm } else if (inp->inp_flags & INP_LOWPORT) { 285503d3c02SPoul-Henning Kamp if (p && (error = suser_xxx(0, p, PRISON_ROOT))) { 286503d3c02SPoul-Henning Kamp inp->inp_laddr.s_addr = INADDR_ANY; 287a29f300eSGarrett Wollman return error; 288503d3c02SPoul-Henning Kamp } 289bbd42ad0SPeter Wemm first = ipport_lowfirstauto; /* 1023 */ 290bbd42ad0SPeter Wemm last = ipport_lowlastauto; /* 600 */ 291c3229e05SDavid Greenman lastport = &pcbinfo->lastlow; 29233b3ac06SPeter Wemm } else { 29333b3ac06SPeter Wemm first = ipport_firstauto; /* sysctl */ 29433b3ac06SPeter Wemm last = ipport_lastauto; 295c3229e05SDavid Greenman lastport = &pcbinfo->lastport; 29633b3ac06SPeter Wemm } 29733b3ac06SPeter Wemm /* 29833b3ac06SPeter Wemm * Simple check to ensure all ports are not used up causing 29933b3ac06SPeter Wemm * a deadlock here. 30033b3ac06SPeter Wemm * 30133b3ac06SPeter Wemm * We split the two cases (up and down) so that the direction 30233b3ac06SPeter Wemm * is not being tested on each round of the loop. 30333b3ac06SPeter Wemm */ 30433b3ac06SPeter Wemm if (first > last) { 30533b3ac06SPeter Wemm /* 30633b3ac06SPeter Wemm * counting down 30733b3ac06SPeter Wemm */ 30833b3ac06SPeter Wemm count = first - last; 30933b3ac06SPeter Wemm 310df8bae1dSRodney W. Grimes do { 311c3229e05SDavid Greenman if (count-- < 0) { /* completely used? */ 312c3229e05SDavid Greenman inp->inp_laddr.s_addr = INADDR_ANY; 313550b1518SWes Peters return (EADDRNOTAVAIL); 314c3229e05SDavid Greenman } 31533b3ac06SPeter Wemm --*lastport; 31633b3ac06SPeter Wemm if (*lastport > first || *lastport < last) 31733b3ac06SPeter Wemm *lastport = first; 31815bd2b43SDavid Greenman lport = htons(*lastport); 319c3229e05SDavid Greenman } while (in_pcblookup_local(pcbinfo, 320c3229e05SDavid Greenman inp->inp_laddr, lport, wild)); 32133b3ac06SPeter Wemm } else { 32233b3ac06SPeter Wemm /* 32333b3ac06SPeter Wemm * counting up 32433b3ac06SPeter Wemm */ 32533b3ac06SPeter Wemm count = last - first; 32633b3ac06SPeter Wemm 32733b3ac06SPeter Wemm do { 328c3229e05SDavid Greenman if (count-- < 0) { /* completely used? */ 329c3229e05SDavid Greenman /* 330c3229e05SDavid Greenman * Undo any address bind that may have 331c3229e05SDavid Greenman * occurred above. 332c3229e05SDavid Greenman */ 333c3229e05SDavid Greenman inp->inp_laddr.s_addr = INADDR_ANY; 334550b1518SWes Peters return (EADDRNOTAVAIL); 335c3229e05SDavid Greenman } 33633b3ac06SPeter Wemm ++*lastport; 33733b3ac06SPeter Wemm if (*lastport < first || *lastport > last) 33833b3ac06SPeter Wemm *lastport = first; 33933b3ac06SPeter Wemm lport = htons(*lastport); 340c3229e05SDavid Greenman } while (in_pcblookup_local(pcbinfo, 341c3229e05SDavid Greenman inp->inp_laddr, lport, wild)); 34233b3ac06SPeter Wemm } 34333b3ac06SPeter Wemm } 344df8bae1dSRodney W. Grimes inp->inp_lport = lport; 345503d3c02SPoul-Henning Kamp if (prison_ip(p->p_ucred, 0, &inp->inp_laddr.s_addr)) { 346503d3c02SPoul-Henning Kamp inp->inp_laddr.s_addr = INADDR_ANY; 347503d3c02SPoul-Henning Kamp inp->inp_lport = 0; 348e4bdf25dSPoul-Henning Kamp return(EINVAL); 349503d3c02SPoul-Henning Kamp } 350c3229e05SDavid Greenman if (in_pcbinshash(inp) != 0) { 351c3229e05SDavid Greenman inp->inp_laddr.s_addr = INADDR_ANY; 352c3229e05SDavid Greenman inp->inp_lport = 0; 353c3229e05SDavid Greenman return (EAGAIN); 354c3229e05SDavid Greenman } 355df8bae1dSRodney W. Grimes return (0); 356df8bae1dSRodney W. Grimes } 357df8bae1dSRodney W. Grimes 358999f1343SGarrett Wollman /* 359999f1343SGarrett Wollman * Transform old in_pcbconnect() into an inner subroutine for new 360999f1343SGarrett Wollman * in_pcbconnect(): Do some validity-checking on the remote 361999f1343SGarrett Wollman * address (in mbuf 'nam') and then determine local host address 362999f1343SGarrett Wollman * (i.e., which interface) to use to access that remote host. 363999f1343SGarrett Wollman * 364999f1343SGarrett Wollman * This preserves definition of in_pcbconnect(), while supporting a 365999f1343SGarrett Wollman * slightly different version for T/TCP. (This is more than 366999f1343SGarrett Wollman * a bit of a kludge, but cleaning up the internal interfaces would 367999f1343SGarrett Wollman * have forced minor changes in every protocol). 368999f1343SGarrett Wollman */ 369999f1343SGarrett Wollman 370999f1343SGarrett Wollman int 371999f1343SGarrett Wollman in_pcbladdr(inp, nam, plocal_sin) 372999f1343SGarrett Wollman register struct inpcb *inp; 37357bf258eSGarrett Wollman struct sockaddr *nam; 374999f1343SGarrett Wollman struct sockaddr_in **plocal_sin; 375999f1343SGarrett Wollman { 376df8bae1dSRodney W. Grimes struct in_ifaddr *ia; 37757bf258eSGarrett Wollman register struct sockaddr_in *sin = (struct sockaddr_in *)nam; 378df8bae1dSRodney W. Grimes 37957bf258eSGarrett Wollman if (nam->sa_len != sizeof (*sin)) 380df8bae1dSRodney W. Grimes return (EINVAL); 381df8bae1dSRodney W. Grimes if (sin->sin_family != AF_INET) 382df8bae1dSRodney W. Grimes return (EAFNOSUPPORT); 383df8bae1dSRodney W. Grimes if (sin->sin_port == 0) 384df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 38559562606SGarrett Wollman if (!TAILQ_EMPTY(&in_ifaddrhead)) { 386df8bae1dSRodney W. Grimes /* 387df8bae1dSRodney W. Grimes * If the destination address is INADDR_ANY, 388df8bae1dSRodney W. Grimes * use the primary local address. 389df8bae1dSRodney W. Grimes * If the supplied address is INADDR_BROADCAST, 390df8bae1dSRodney W. Grimes * and the primary interface supports broadcast, 391df8bae1dSRodney W. Grimes * choose the broadcast address for that interface. 392df8bae1dSRodney W. Grimes */ 393df8bae1dSRodney W. Grimes #define satosin(sa) ((struct sockaddr_in *)(sa)) 394df8bae1dSRodney W. Grimes #define sintosa(sin) ((struct sockaddr *)(sin)) 395df8bae1dSRodney W. Grimes #define ifatoia(ifa) ((struct in_ifaddr *)(ifa)) 396df8bae1dSRodney W. Grimes if (sin->sin_addr.s_addr == INADDR_ANY) 397fc2ffbe6SPoul-Henning Kamp sin->sin_addr = IA_SIN(TAILQ_FIRST(&in_ifaddrhead))->sin_addr; 398df8bae1dSRodney W. Grimes else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST && 399fc2ffbe6SPoul-Henning Kamp (TAILQ_FIRST(&in_ifaddrhead)->ia_ifp->if_flags & IFF_BROADCAST)) 400fc2ffbe6SPoul-Henning Kamp sin->sin_addr = satosin(&TAILQ_FIRST(&in_ifaddrhead)->ia_broadaddr)->sin_addr; 401df8bae1dSRodney W. Grimes } 402df8bae1dSRodney W. Grimes if (inp->inp_laddr.s_addr == INADDR_ANY) { 403df8bae1dSRodney W. Grimes register struct route *ro; 404df8bae1dSRodney W. Grimes 405df8bae1dSRodney W. Grimes ia = (struct in_ifaddr *)0; 406df8bae1dSRodney W. Grimes /* 407df8bae1dSRodney W. Grimes * If route is known or can be allocated now, 408df8bae1dSRodney W. Grimes * our src addr is taken from the i/f, else punt. 409df8bae1dSRodney W. Grimes */ 410df8bae1dSRodney W. Grimes ro = &inp->inp_route; 411df8bae1dSRodney W. Grimes if (ro->ro_rt && 412df8bae1dSRodney W. Grimes (satosin(&ro->ro_dst)->sin_addr.s_addr != 413df8bae1dSRodney W. Grimes sin->sin_addr.s_addr || 414df8bae1dSRodney W. Grimes inp->inp_socket->so_options & SO_DONTROUTE)) { 415df8bae1dSRodney W. Grimes RTFREE(ro->ro_rt); 416df8bae1dSRodney W. Grimes ro->ro_rt = (struct rtentry *)0; 417df8bae1dSRodney W. Grimes } 418df8bae1dSRodney W. Grimes if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/ 419df8bae1dSRodney W. Grimes (ro->ro_rt == (struct rtentry *)0 || 420df8bae1dSRodney W. Grimes ro->ro_rt->rt_ifp == (struct ifnet *)0)) { 421df8bae1dSRodney W. Grimes /* No route yet, so try to acquire one */ 422df8bae1dSRodney W. Grimes ro->ro_dst.sa_family = AF_INET; 423df8bae1dSRodney W. Grimes ro->ro_dst.sa_len = sizeof(struct sockaddr_in); 424df8bae1dSRodney W. Grimes ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = 425df8bae1dSRodney W. Grimes sin->sin_addr; 426df8bae1dSRodney W. Grimes rtalloc(ro); 427df8bae1dSRodney W. Grimes } 428df8bae1dSRodney W. Grimes /* 429df8bae1dSRodney W. Grimes * If we found a route, use the address 430df8bae1dSRodney W. Grimes * corresponding to the outgoing interface 431df8bae1dSRodney W. Grimes * unless it is the loopback (in case a route 432df8bae1dSRodney W. Grimes * to our address on another net goes to loopback). 433df8bae1dSRodney W. Grimes */ 434df8bae1dSRodney W. Grimes if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK)) 435df8bae1dSRodney W. Grimes ia = ifatoia(ro->ro_rt->rt_ifa); 436df8bae1dSRodney W. Grimes if (ia == 0) { 437df8bae1dSRodney W. Grimes u_short fport = sin->sin_port; 438df8bae1dSRodney W. Grimes 439df8bae1dSRodney W. Grimes sin->sin_port = 0; 440df8bae1dSRodney W. Grimes ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin))); 441df8bae1dSRodney W. Grimes if (ia == 0) 442df8bae1dSRodney W. Grimes ia = ifatoia(ifa_ifwithnet(sintosa(sin))); 443df8bae1dSRodney W. Grimes sin->sin_port = fport; 444df8bae1dSRodney W. Grimes if (ia == 0) 445fc2ffbe6SPoul-Henning Kamp ia = TAILQ_FIRST(&in_ifaddrhead); 446df8bae1dSRodney W. Grimes if (ia == 0) 447df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 448df8bae1dSRodney W. Grimes } 449df8bae1dSRodney W. Grimes /* 450df8bae1dSRodney W. Grimes * If the destination address is multicast and an outgoing 451df8bae1dSRodney W. Grimes * interface has been set as a multicast option, use the 452df8bae1dSRodney W. Grimes * address of that interface as our source address. 453df8bae1dSRodney W. Grimes */ 454df8bae1dSRodney W. Grimes if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) && 455df8bae1dSRodney W. Grimes inp->inp_moptions != NULL) { 456df8bae1dSRodney W. Grimes struct ip_moptions *imo; 457df8bae1dSRodney W. Grimes struct ifnet *ifp; 458df8bae1dSRodney W. Grimes 459df8bae1dSRodney W. Grimes imo = inp->inp_moptions; 460df8bae1dSRodney W. Grimes if (imo->imo_multicast_ifp != NULL) { 461df8bae1dSRodney W. Grimes ifp = imo->imo_multicast_ifp; 46237d40066SPoul-Henning Kamp TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) 463df8bae1dSRodney W. Grimes if (ia->ia_ifp == ifp) 464df8bae1dSRodney W. Grimes break; 465df8bae1dSRodney W. Grimes if (ia == 0) 466df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 467df8bae1dSRodney W. Grimes } 468df8bae1dSRodney W. Grimes } 469999f1343SGarrett Wollman /* 470999f1343SGarrett Wollman * Don't do pcblookup call here; return interface in plocal_sin 471999f1343SGarrett Wollman * and exit to caller, that will do the lookup. 472999f1343SGarrett Wollman */ 473999f1343SGarrett Wollman *plocal_sin = &ia->ia_addr; 474999f1343SGarrett Wollman 475999f1343SGarrett Wollman } 476999f1343SGarrett Wollman return(0); 477999f1343SGarrett Wollman } 478999f1343SGarrett Wollman 479999f1343SGarrett Wollman /* 480999f1343SGarrett Wollman * Outer subroutine: 481999f1343SGarrett Wollman * Connect from a socket to a specified address. 482999f1343SGarrett Wollman * Both address and port must be specified in argument sin. 483999f1343SGarrett Wollman * If don't have a local address for this socket yet, 484999f1343SGarrett Wollman * then pick one. 485999f1343SGarrett Wollman */ 486999f1343SGarrett Wollman int 487a29f300eSGarrett Wollman in_pcbconnect(inp, nam, p) 488999f1343SGarrett Wollman register struct inpcb *inp; 48957bf258eSGarrett Wollman struct sockaddr *nam; 490a29f300eSGarrett Wollman struct proc *p; 491999f1343SGarrett Wollman { 492999f1343SGarrett Wollman struct sockaddr_in *ifaddr; 493e4bdf25dSPoul-Henning Kamp struct sockaddr_in *sin = (struct sockaddr_in *)nam; 494e4bdf25dSPoul-Henning Kamp struct sockaddr_in sa; 49591421ba2SRobert Watson struct ucred *cred; 496999f1343SGarrett Wollman int error; 497999f1343SGarrett Wollman 49891421ba2SRobert Watson cred = inp->inp_socket->so_cred; 49991421ba2SRobert Watson if (inp->inp_laddr.s_addr == INADDR_ANY && jailed(cred)) { 500e4bdf25dSPoul-Henning Kamp bzero(&sa, sizeof (sa)); 50191421ba2SRobert Watson sa.sin_addr.s_addr = htonl(cred->cr_prison->pr_ip); 502e4bdf25dSPoul-Henning Kamp sa.sin_len=sizeof (sa); 503e4bdf25dSPoul-Henning Kamp sa.sin_family = AF_INET; 504e4bdf25dSPoul-Henning Kamp error = in_pcbbind(inp, (struct sockaddr *)&sa, p); 505e4bdf25dSPoul-Henning Kamp if (error) 506e4bdf25dSPoul-Henning Kamp return (error); 507e4bdf25dSPoul-Henning Kamp } 508999f1343SGarrett Wollman /* 509999f1343SGarrett Wollman * Call inner routine, to assign local interface address. 510999f1343SGarrett Wollman */ 511831a80b0SMatthew Dillon if ((error = in_pcbladdr(inp, nam, &ifaddr)) != 0) 512999f1343SGarrett Wollman return(error); 513999f1343SGarrett Wollman 514c3229e05SDavid Greenman if (in_pcblookup_hash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port, 515df8bae1dSRodney W. Grimes inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 516cfa1ca9dSYoshinobu Inoue inp->inp_lport, 0, NULL) != NULL) { 517df8bae1dSRodney W. Grimes return (EADDRINUSE); 518c3229e05SDavid Greenman } 519df8bae1dSRodney W. Grimes if (inp->inp_laddr.s_addr == INADDR_ANY) { 5205a903f8dSPierre Beyssac if (inp->inp_lport == 0) { 5215a903f8dSPierre Beyssac error = in_pcbbind(inp, (struct sockaddr *)0, p); 5225a903f8dSPierre Beyssac if (error) 5235a903f8dSPierre Beyssac return (error); 5245a903f8dSPierre Beyssac } 525df8bae1dSRodney W. Grimes inp->inp_laddr = ifaddr->sin_addr; 526df8bae1dSRodney W. Grimes } 527df8bae1dSRodney W. Grimes inp->inp_faddr = sin->sin_addr; 528df8bae1dSRodney W. Grimes inp->inp_fport = sin->sin_port; 52915bd2b43SDavid Greenman in_pcbrehash(inp); 530df8bae1dSRodney W. Grimes return (0); 531df8bae1dSRodney W. Grimes } 532df8bae1dSRodney W. Grimes 53326f9a767SRodney W. Grimes void 534df8bae1dSRodney W. Grimes in_pcbdisconnect(inp) 535df8bae1dSRodney W. Grimes struct inpcb *inp; 536df8bae1dSRodney W. Grimes { 537df8bae1dSRodney W. Grimes 538df8bae1dSRodney W. Grimes inp->inp_faddr.s_addr = INADDR_ANY; 539df8bae1dSRodney W. Grimes inp->inp_fport = 0; 54015bd2b43SDavid Greenman in_pcbrehash(inp); 541df8bae1dSRodney W. Grimes if (inp->inp_socket->so_state & SS_NOFDREF) 542df8bae1dSRodney W. Grimes in_pcbdetach(inp); 543df8bae1dSRodney W. Grimes } 544df8bae1dSRodney W. Grimes 54526f9a767SRodney W. Grimes void 546df8bae1dSRodney W. Grimes in_pcbdetach(inp) 547df8bae1dSRodney W. Grimes struct inpcb *inp; 548df8bae1dSRodney W. Grimes { 549df8bae1dSRodney W. Grimes struct socket *so = inp->inp_socket; 5503d4d47f3SGarrett Wollman struct inpcbinfo *ipi = inp->inp_pcbinfo; 551e7f32693SJayanth Vijayaraghavan struct rtentry *rt = inp->inp_route.ro_rt; 552df8bae1dSRodney W. Grimes 553cfa1ca9dSYoshinobu Inoue #ifdef IPSEC 554cfa1ca9dSYoshinobu Inoue ipsec4_delete_pcbpolicy(inp); 555cfa1ca9dSYoshinobu Inoue #endif /*IPSEC*/ 5563d4d47f3SGarrett Wollman inp->inp_gencnt = ++ipi->ipi_gencnt; 557c3229e05SDavid Greenman in_pcbremlists(inp); 558df8bae1dSRodney W. Grimes so->so_pcb = 0; 559df8bae1dSRodney W. Grimes sofree(so); 560df8bae1dSRodney W. Grimes if (inp->inp_options) 561df8bae1dSRodney W. Grimes (void)m_free(inp->inp_options); 562e7f32693SJayanth Vijayaraghavan if (rt) { 563e7f32693SJayanth Vijayaraghavan /* 564e7f32693SJayanth Vijayaraghavan * route deletion requires reference count to be <= zero 565e7f32693SJayanth Vijayaraghavan */ 566e7f32693SJayanth Vijayaraghavan if ((rt->rt_flags & RTF_DELCLONE) && 567234ff7c4SBosko Milekic (rt->rt_flags & RTF_WASCLONED) && 568234ff7c4SBosko Milekic (rt->rt_refcnt <= 1)) { 569234ff7c4SBosko Milekic rt->rt_refcnt--; 570e7f32693SJayanth Vijayaraghavan rt->rt_flags &= ~RTF_UP; 571e7f32693SJayanth Vijayaraghavan rtrequest(RTM_DELETE, rt_key(rt), 572e7f32693SJayanth Vijayaraghavan rt->rt_gateway, rt_mask(rt), 573e7f32693SJayanth Vijayaraghavan rt->rt_flags, (struct rtentry **)0); 574e7f32693SJayanth Vijayaraghavan } 575e7f32693SJayanth Vijayaraghavan else 576e7f32693SJayanth Vijayaraghavan rtfree(rt); 577e7f32693SJayanth Vijayaraghavan } 578df8bae1dSRodney W. Grimes ip_freemoptions(inp->inp_moptions); 579cfa1ca9dSYoshinobu Inoue inp->inp_vflag = 0; 580a3ea6d41SDag-Erling Smørgrav zfree(ipi->ipi_zone, inp); 581df8bae1dSRodney W. Grimes } 582df8bae1dSRodney W. Grimes 583117bcae7SGarrett Wollman /* 584117bcae7SGarrett Wollman * The calling convention of in_setsockaddr() and in_setpeeraddr() was 585117bcae7SGarrett Wollman * modified to match the pru_sockaddr() and pru_peeraddr() entry points 586117bcae7SGarrett Wollman * in struct pr_usrreqs, so that protocols can just reference then directly 587117bcae7SGarrett Wollman * without the need for a wrapper function. The socket must have a valid 588117bcae7SGarrett Wollman * (i.e., non-nil) PCB, but it should be impossible to get an invalid one 589117bcae7SGarrett Wollman * except through a kernel programming error, so it is acceptable to panic 59057bf258eSGarrett Wollman * (or in this case trap) if the PCB is invalid. (Actually, we don't trap 59157bf258eSGarrett Wollman * because there actually /is/ a programming error somewhere... XXX) 592117bcae7SGarrett Wollman */ 593117bcae7SGarrett Wollman int 594117bcae7SGarrett Wollman in_setsockaddr(so, nam) 595117bcae7SGarrett Wollman struct socket *so; 59657bf258eSGarrett Wollman struct sockaddr **nam; 597df8bae1dSRodney W. Grimes { 598fdc984f7STor Egge int s; 599fdc984f7STor Egge register struct inpcb *inp; 600df8bae1dSRodney W. Grimes register struct sockaddr_in *sin; 601df8bae1dSRodney W. Grimes 602c3229e05SDavid Greenman /* 603c3229e05SDavid Greenman * Do the malloc first in case it blocks. 604c3229e05SDavid Greenman */ 6057cc0979fSDavid Malone MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, 6067cc0979fSDavid Malone M_WAITOK | M_ZERO); 60742fa505bSDavid Greenman sin->sin_family = AF_INET; 60842fa505bSDavid Greenman sin->sin_len = sizeof(*sin); 60942fa505bSDavid Greenman 610fdc984f7STor Egge s = splnet(); 611fdc984f7STor Egge inp = sotoinpcb(so); 612db112f04STor Egge if (!inp) { 613db112f04STor Egge splx(s); 61442fa505bSDavid Greenman free(sin, M_SONAME); 615ff079ca4SPeter Wemm return ECONNRESET; 616db112f04STor Egge } 617df8bae1dSRodney W. Grimes sin->sin_port = inp->inp_lport; 618df8bae1dSRodney W. Grimes sin->sin_addr = inp->inp_laddr; 619db112f04STor Egge splx(s); 62042fa505bSDavid Greenman 62142fa505bSDavid Greenman *nam = (struct sockaddr *)sin; 622117bcae7SGarrett Wollman return 0; 623df8bae1dSRodney W. Grimes } 624df8bae1dSRodney W. Grimes 625117bcae7SGarrett Wollman int 626117bcae7SGarrett Wollman in_setpeeraddr(so, nam) 627117bcae7SGarrett Wollman struct socket *so; 62857bf258eSGarrett Wollman struct sockaddr **nam; 629df8bae1dSRodney W. Grimes { 630fdc984f7STor Egge int s; 631fdc984f7STor Egge struct inpcb *inp; 632df8bae1dSRodney W. Grimes register struct sockaddr_in *sin; 633df8bae1dSRodney W. Grimes 634c3229e05SDavid Greenman /* 635c3229e05SDavid Greenman * Do the malloc first in case it blocks. 636c3229e05SDavid Greenman */ 6377cc0979fSDavid Malone MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, 6387cc0979fSDavid Malone M_WAITOK | M_ZERO); 63942fa505bSDavid Greenman sin->sin_family = AF_INET; 64042fa505bSDavid Greenman sin->sin_len = sizeof(*sin); 64142fa505bSDavid Greenman 642fdc984f7STor Egge s = splnet(); 643fdc984f7STor Egge inp = sotoinpcb(so); 644db112f04STor Egge if (!inp) { 645db112f04STor Egge splx(s); 64642fa505bSDavid Greenman free(sin, M_SONAME); 647ff079ca4SPeter Wemm return ECONNRESET; 648db112f04STor Egge } 649df8bae1dSRodney W. Grimes sin->sin_port = inp->inp_fport; 650df8bae1dSRodney W. Grimes sin->sin_addr = inp->inp_faddr; 651db112f04STor Egge splx(s); 65242fa505bSDavid Greenman 65342fa505bSDavid Greenman *nam = (struct sockaddr *)sin; 654117bcae7SGarrett Wollman return 0; 655df8bae1dSRodney W. Grimes } 656df8bae1dSRodney W. Grimes 65726f9a767SRodney W. Grimes void 658c693a045SJonathan Lemon in_pcbnotifyall(head, faddr, errno, notify) 65915bd2b43SDavid Greenman struct inpcbhead *head; 660df8bae1dSRodney W. Grimes struct in_addr faddr; 661c693a045SJonathan Lemon int errno; 662d1c54148SJesper Skriver void (*notify) __P((struct inpcb *, int)); 663d1c54148SJesper Skriver { 664c693a045SJonathan Lemon struct inpcb *inp, *ninp; 665c693a045SJonathan Lemon int s; 666d1c54148SJesper Skriver 667d1c54148SJesper Skriver s = splnet(); 668c693a045SJonathan Lemon for (inp = LIST_FIRST(head); inp != NULL; inp = ninp) { 669c693a045SJonathan Lemon ninp = LIST_NEXT(inp, inp_list); 670d1c54148SJesper Skriver #ifdef INET6 671c693a045SJonathan Lemon if ((inp->inp_vflag & INP_IPV4) == 0) 672d1c54148SJesper Skriver continue; 673d1c54148SJesper Skriver #endif 674d1c54148SJesper Skriver if (inp->inp_faddr.s_addr != faddr.s_addr || 675c693a045SJonathan Lemon inp->inp_socket == NULL) 676d1c54148SJesper Skriver continue; 677c693a045SJonathan Lemon (*notify)(inp, errno); 678d1c54148SJesper Skriver } 679d1c54148SJesper Skriver splx(s); 680d1c54148SJesper Skriver } 681d1c54148SJesper Skriver 682df8bae1dSRodney W. Grimes /* 683df8bae1dSRodney W. Grimes * Check for alternatives when higher level complains 684df8bae1dSRodney W. Grimes * about service problems. For now, invalidate cached 685df8bae1dSRodney W. Grimes * routing information. If the route was created dynamically 686df8bae1dSRodney W. Grimes * (by a redirect), time to try a default gateway again. 687df8bae1dSRodney W. Grimes */ 68826f9a767SRodney W. Grimes void 689df8bae1dSRodney W. Grimes in_losing(inp) 690df8bae1dSRodney W. Grimes struct inpcb *inp; 691df8bae1dSRodney W. Grimes { 692df8bae1dSRodney W. Grimes register struct rtentry *rt; 693df8bae1dSRodney W. Grimes struct rt_addrinfo info; 694df8bae1dSRodney W. Grimes 695df8bae1dSRodney W. Grimes if ((rt = inp->inp_route.ro_rt)) { 696df8bae1dSRodney W. Grimes inp->inp_route.ro_rt = 0; 697df8bae1dSRodney W. Grimes bzero((caddr_t)&info, sizeof(info)); 698df8bae1dSRodney W. Grimes info.rti_info[RTAX_DST] = 699df8bae1dSRodney W. Grimes (struct sockaddr *)&inp->inp_route.ro_dst; 700df8bae1dSRodney W. Grimes info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; 701df8bae1dSRodney W. Grimes info.rti_info[RTAX_NETMASK] = rt_mask(rt); 702df8bae1dSRodney W. Grimes rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0); 703df8bae1dSRodney W. Grimes if (rt->rt_flags & RTF_DYNAMIC) 704df8bae1dSRodney W. Grimes (void) rtrequest(RTM_DELETE, rt_key(rt), 705df8bae1dSRodney W. Grimes rt->rt_gateway, rt_mask(rt), rt->rt_flags, 706df8bae1dSRodney W. Grimes (struct rtentry **)0); 707df8bae1dSRodney W. Grimes else 708df8bae1dSRodney W. Grimes /* 709df8bae1dSRodney W. Grimes * A new route can be allocated 710df8bae1dSRodney W. Grimes * the next time output is attempted. 711df8bae1dSRodney W. Grimes */ 712df8bae1dSRodney W. Grimes rtfree(rt); 713df8bae1dSRodney W. Grimes } 714df8bae1dSRodney W. Grimes } 715df8bae1dSRodney W. Grimes 716df8bae1dSRodney W. Grimes /* 717df8bae1dSRodney W. Grimes * After a routing change, flush old routing 718df8bae1dSRodney W. Grimes * and allocate a (hopefully) better one. 719df8bae1dSRodney W. Grimes */ 720d1c54148SJesper Skriver void 721df8bae1dSRodney W. Grimes in_rtchange(inp, errno) 722df8bae1dSRodney W. Grimes register struct inpcb *inp; 723df8bae1dSRodney W. Grimes int errno; 724df8bae1dSRodney W. Grimes { 725df8bae1dSRodney W. Grimes if (inp->inp_route.ro_rt) { 726df8bae1dSRodney W. Grimes rtfree(inp->inp_route.ro_rt); 727df8bae1dSRodney W. Grimes inp->inp_route.ro_rt = 0; 728df8bae1dSRodney W. Grimes /* 729df8bae1dSRodney W. Grimes * A new route can be allocated the next time 730df8bae1dSRodney W. Grimes * output is attempted. 731df8bae1dSRodney W. Grimes */ 732df8bae1dSRodney W. Grimes } 733df8bae1dSRodney W. Grimes } 734df8bae1dSRodney W. Grimes 735c3229e05SDavid Greenman /* 736c3229e05SDavid Greenman * Lookup a PCB based on the local address and port. 737c3229e05SDavid Greenman */ 738df8bae1dSRodney W. Grimes struct inpcb * 739c3229e05SDavid Greenman in_pcblookup_local(pcbinfo, laddr, lport_arg, wild_okay) 7406d6a026bSDavid Greenman struct inpcbinfo *pcbinfo; 741c3229e05SDavid Greenman struct in_addr laddr; 742c3229e05SDavid Greenman u_int lport_arg; 7436d6a026bSDavid Greenman int wild_okay; 744df8bae1dSRodney W. Grimes { 745f1d19042SArchie Cobbs register struct inpcb *inp; 746df8bae1dSRodney W. Grimes int matchwild = 3, wildcard; 747c3229e05SDavid Greenman u_short lport = lport_arg; 7487bc4aca7SDavid Greenman 749c3229e05SDavid Greenman if (!wild_okay) { 750c3229e05SDavid Greenman struct inpcbhead *head; 751c3229e05SDavid Greenman /* 752c3229e05SDavid Greenman * Look for an unconnected (wildcard foreign addr) PCB that 753c3229e05SDavid Greenman * matches the local address and port we're looking for. 754c3229e05SDavid Greenman */ 755c3229e05SDavid Greenman head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)]; 756fc2ffbe6SPoul-Henning Kamp LIST_FOREACH(inp, head, inp_hash) { 757cfa1ca9dSYoshinobu Inoue #ifdef INET6 758369dc8ceSEivind Eklund if ((inp->inp_vflag & INP_IPV4) == 0) 759cfa1ca9dSYoshinobu Inoue continue; 760cfa1ca9dSYoshinobu Inoue #endif 761c3229e05SDavid Greenman if (inp->inp_faddr.s_addr == INADDR_ANY && 762c3229e05SDavid Greenman inp->inp_laddr.s_addr == laddr.s_addr && 763c3229e05SDavid Greenman inp->inp_lport == lport) { 764c3229e05SDavid Greenman /* 765c3229e05SDavid Greenman * Found. 766c3229e05SDavid Greenman */ 767c3229e05SDavid Greenman return (inp); 768df8bae1dSRodney W. Grimes } 769c3229e05SDavid Greenman } 770c3229e05SDavid Greenman /* 771c3229e05SDavid Greenman * Not found. 772c3229e05SDavid Greenman */ 773c3229e05SDavid Greenman return (NULL); 774c3229e05SDavid Greenman } else { 775c3229e05SDavid Greenman struct inpcbporthead *porthash; 776c3229e05SDavid Greenman struct inpcbport *phd; 777c3229e05SDavid Greenman struct inpcb *match = NULL; 778c3229e05SDavid Greenman /* 779c3229e05SDavid Greenman * Best fit PCB lookup. 780c3229e05SDavid Greenman * 781c3229e05SDavid Greenman * First see if this local port is in use by looking on the 782c3229e05SDavid Greenman * port hash list. 783c3229e05SDavid Greenman */ 784c3229e05SDavid Greenman porthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(lport, 785c3229e05SDavid Greenman pcbinfo->porthashmask)]; 786fc2ffbe6SPoul-Henning Kamp LIST_FOREACH(phd, porthash, phd_hash) { 787c3229e05SDavid Greenman if (phd->phd_port == lport) 788c3229e05SDavid Greenman break; 789c3229e05SDavid Greenman } 790c3229e05SDavid Greenman if (phd != NULL) { 791c3229e05SDavid Greenman /* 792c3229e05SDavid Greenman * Port is in use by one or more PCBs. Look for best 793c3229e05SDavid Greenman * fit. 794c3229e05SDavid Greenman */ 79537d40066SPoul-Henning Kamp LIST_FOREACH(inp, &phd->phd_pcblist, inp_portlist) { 796c3229e05SDavid Greenman wildcard = 0; 797cfa1ca9dSYoshinobu Inoue #ifdef INET6 798369dc8ceSEivind Eklund if ((inp->inp_vflag & INP_IPV4) == 0) 799cfa1ca9dSYoshinobu Inoue continue; 800cfa1ca9dSYoshinobu Inoue #endif 801c3229e05SDavid Greenman if (inp->inp_faddr.s_addr != INADDR_ANY) 802c3229e05SDavid Greenman wildcard++; 80315bd2b43SDavid Greenman if (inp->inp_laddr.s_addr != INADDR_ANY) { 80415bd2b43SDavid Greenman if (laddr.s_addr == INADDR_ANY) 80515bd2b43SDavid Greenman wildcard++; 80615bd2b43SDavid Greenman else if (inp->inp_laddr.s_addr != laddr.s_addr) 80715bd2b43SDavid Greenman continue; 80815bd2b43SDavid Greenman } else { 80915bd2b43SDavid Greenman if (laddr.s_addr != INADDR_ANY) 81015bd2b43SDavid Greenman wildcard++; 81115bd2b43SDavid Greenman } 812df8bae1dSRodney W. Grimes if (wildcard < matchwild) { 813df8bae1dSRodney W. Grimes match = inp; 814df8bae1dSRodney W. Grimes matchwild = wildcard; 8153dbdc25cSDavid Greenman if (matchwild == 0) { 816df8bae1dSRodney W. Grimes break; 817df8bae1dSRodney W. Grimes } 818df8bae1dSRodney W. Grimes } 8193dbdc25cSDavid Greenman } 820c3229e05SDavid Greenman } 821df8bae1dSRodney W. Grimes return (match); 822df8bae1dSRodney W. Grimes } 823c3229e05SDavid Greenman } 82415bd2b43SDavid Greenman 82515bd2b43SDavid Greenman /* 82615bd2b43SDavid Greenman * Lookup PCB in hash list. 82715bd2b43SDavid Greenman */ 82815bd2b43SDavid Greenman struct inpcb * 829cfa1ca9dSYoshinobu Inoue in_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard, 830cfa1ca9dSYoshinobu Inoue ifp) 83115bd2b43SDavid Greenman struct inpcbinfo *pcbinfo; 83215bd2b43SDavid Greenman struct in_addr faddr, laddr; 83315bd2b43SDavid Greenman u_int fport_arg, lport_arg; 8346d6a026bSDavid Greenman int wildcard; 835cfa1ca9dSYoshinobu Inoue struct ifnet *ifp; 83615bd2b43SDavid Greenman { 83715bd2b43SDavid Greenman struct inpcbhead *head; 83815bd2b43SDavid Greenman register struct inpcb *inp; 83915bd2b43SDavid Greenman u_short fport = fport_arg, lport = lport_arg; 84015bd2b43SDavid Greenman 84115bd2b43SDavid Greenman /* 84215bd2b43SDavid Greenman * First look for an exact match. 84315bd2b43SDavid Greenman */ 844ddd79a97SDavid Greenman head = &pcbinfo->hashbase[INP_PCBHASH(faddr.s_addr, lport, fport, pcbinfo->hashmask)]; 845fc2ffbe6SPoul-Henning Kamp LIST_FOREACH(inp, head, inp_hash) { 846cfa1ca9dSYoshinobu Inoue #ifdef INET6 847369dc8ceSEivind Eklund if ((inp->inp_vflag & INP_IPV4) == 0) 848cfa1ca9dSYoshinobu Inoue continue; 849cfa1ca9dSYoshinobu Inoue #endif 8506d6a026bSDavid Greenman if (inp->inp_faddr.s_addr == faddr.s_addr && 851ca98b82cSDavid Greenman inp->inp_laddr.s_addr == laddr.s_addr && 852ca98b82cSDavid Greenman inp->inp_fport == fport && 853c3229e05SDavid Greenman inp->inp_lport == lport) { 854c3229e05SDavid Greenman /* 855c3229e05SDavid Greenman * Found. 856c3229e05SDavid Greenman */ 857c3229e05SDavid Greenman return (inp); 858c3229e05SDavid Greenman } 8596d6a026bSDavid Greenman } 8606d6a026bSDavid Greenman if (wildcard) { 8616d6a026bSDavid Greenman struct inpcb *local_wild = NULL; 862cfa1ca9dSYoshinobu Inoue #if defined(INET6) 863cfa1ca9dSYoshinobu Inoue struct inpcb *local_wild_mapped = NULL; 864cfa1ca9dSYoshinobu Inoue #endif /* defined(INET6) */ 8656d6a026bSDavid Greenman 866ddd79a97SDavid Greenman head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)]; 867fc2ffbe6SPoul-Henning Kamp LIST_FOREACH(inp, head, inp_hash) { 868cfa1ca9dSYoshinobu Inoue #ifdef INET6 869369dc8ceSEivind Eklund if ((inp->inp_vflag & INP_IPV4) == 0) 870cfa1ca9dSYoshinobu Inoue continue; 871cfa1ca9dSYoshinobu Inoue #endif 8726d6a026bSDavid Greenman if (inp->inp_faddr.s_addr == INADDR_ANY && 873c3229e05SDavid Greenman inp->inp_lport == lport) { 874cfa1ca9dSYoshinobu Inoue #if defined(NFAITH) && NFAITH > 0 875cfa1ca9dSYoshinobu Inoue if (ifp && ifp->if_type == IFT_FAITH && 876cfa1ca9dSYoshinobu Inoue (inp->inp_flags & INP_FAITH) == 0) 877cfa1ca9dSYoshinobu Inoue continue; 878cfa1ca9dSYoshinobu Inoue #endif 8796d6a026bSDavid Greenman if (inp->inp_laddr.s_addr == laddr.s_addr) 880c3229e05SDavid Greenman return (inp); 881cfa1ca9dSYoshinobu Inoue else if (inp->inp_laddr.s_addr == INADDR_ANY) { 882cfa1ca9dSYoshinobu Inoue #if defined(INET6) 883cfa1ca9dSYoshinobu Inoue if (INP_CHECK_SOCKAF(inp->inp_socket, 884cfa1ca9dSYoshinobu Inoue AF_INET6)) 885cfa1ca9dSYoshinobu Inoue local_wild_mapped = inp; 886cfa1ca9dSYoshinobu Inoue else 887cfa1ca9dSYoshinobu Inoue #endif /* defined(INET6) */ 8886d6a026bSDavid Greenman local_wild = inp; 8896d6a026bSDavid Greenman } 8906d6a026bSDavid Greenman } 891cfa1ca9dSYoshinobu Inoue } 892cfa1ca9dSYoshinobu Inoue #if defined(INET6) 893cfa1ca9dSYoshinobu Inoue if (local_wild == NULL) 894cfa1ca9dSYoshinobu Inoue return (local_wild_mapped); 895cfa1ca9dSYoshinobu Inoue #endif /* defined(INET6) */ 896c3229e05SDavid Greenman return (local_wild); 8976d6a026bSDavid Greenman } 898c3229e05SDavid Greenman 899c3229e05SDavid Greenman /* 900c3229e05SDavid Greenman * Not found. 901c3229e05SDavid Greenman */ 9026d6a026bSDavid Greenman return (NULL); 90315bd2b43SDavid Greenman } 90415bd2b43SDavid Greenman 9057bc4aca7SDavid Greenman /* 906c3229e05SDavid Greenman * Insert PCB onto various hash lists. 9077bc4aca7SDavid Greenman */ 908c3229e05SDavid Greenman int 90915bd2b43SDavid Greenman in_pcbinshash(inp) 91015bd2b43SDavid Greenman struct inpcb *inp; 91115bd2b43SDavid Greenman { 912c3229e05SDavid Greenman struct inpcbhead *pcbhash; 913c3229e05SDavid Greenman struct inpcbporthead *pcbporthash; 914c3229e05SDavid Greenman struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; 915c3229e05SDavid Greenman struct inpcbport *phd; 916cfa1ca9dSYoshinobu Inoue u_int32_t hashkey_faddr; 91715bd2b43SDavid Greenman 918cfa1ca9dSYoshinobu Inoue #ifdef INET6 919cfa1ca9dSYoshinobu Inoue if (inp->inp_vflag & INP_IPV6) 920cfa1ca9dSYoshinobu Inoue hashkey_faddr = inp->in6p_faddr.s6_addr32[3] /* XXX */; 921cfa1ca9dSYoshinobu Inoue else 922cfa1ca9dSYoshinobu Inoue #endif /* INET6 */ 923cfa1ca9dSYoshinobu Inoue hashkey_faddr = inp->inp_faddr.s_addr; 924cfa1ca9dSYoshinobu Inoue 925cfa1ca9dSYoshinobu Inoue pcbhash = &pcbinfo->hashbase[INP_PCBHASH(hashkey_faddr, 926c3229e05SDavid Greenman inp->inp_lport, inp->inp_fport, pcbinfo->hashmask)]; 92715bd2b43SDavid Greenman 928c3229e05SDavid Greenman pcbporthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(inp->inp_lport, 929c3229e05SDavid Greenman pcbinfo->porthashmask)]; 930c3229e05SDavid Greenman 931c3229e05SDavid Greenman /* 932c3229e05SDavid Greenman * Go through port list and look for a head for this lport. 933c3229e05SDavid Greenman */ 934fc2ffbe6SPoul-Henning Kamp LIST_FOREACH(phd, pcbporthash, phd_hash) { 935c3229e05SDavid Greenman if (phd->phd_port == inp->inp_lport) 936c3229e05SDavid Greenman break; 937c3229e05SDavid Greenman } 938c3229e05SDavid Greenman /* 939c3229e05SDavid Greenman * If none exists, malloc one and tack it on. 940c3229e05SDavid Greenman */ 941c3229e05SDavid Greenman if (phd == NULL) { 942c3229e05SDavid Greenman MALLOC(phd, struct inpcbport *, sizeof(struct inpcbport), M_PCB, M_NOWAIT); 943c3229e05SDavid Greenman if (phd == NULL) { 944c3229e05SDavid Greenman return (ENOBUFS); /* XXX */ 945c3229e05SDavid Greenman } 946c3229e05SDavid Greenman phd->phd_port = inp->inp_lport; 947c3229e05SDavid Greenman LIST_INIT(&phd->phd_pcblist); 948c3229e05SDavid Greenman LIST_INSERT_HEAD(pcbporthash, phd, phd_hash); 949c3229e05SDavid Greenman } 950c3229e05SDavid Greenman inp->inp_phd = phd; 951c3229e05SDavid Greenman LIST_INSERT_HEAD(&phd->phd_pcblist, inp, inp_portlist); 952c3229e05SDavid Greenman LIST_INSERT_HEAD(pcbhash, inp, inp_hash); 953c3229e05SDavid Greenman return (0); 95415bd2b43SDavid Greenman } 95515bd2b43SDavid Greenman 956c3229e05SDavid Greenman /* 957c3229e05SDavid Greenman * Move PCB to the proper hash bucket when { faddr, fport } have been 958c3229e05SDavid Greenman * changed. NOTE: This does not handle the case of the lport changing (the 959c3229e05SDavid Greenman * hashed port list would have to be updated as well), so the lport must 960c3229e05SDavid Greenman * not change after in_pcbinshash() has been called. 961c3229e05SDavid Greenman */ 96215bd2b43SDavid Greenman void 96315bd2b43SDavid Greenman in_pcbrehash(inp) 96415bd2b43SDavid Greenman struct inpcb *inp; 96515bd2b43SDavid Greenman { 96615bd2b43SDavid Greenman struct inpcbhead *head; 967cfa1ca9dSYoshinobu Inoue u_int32_t hashkey_faddr; 96815bd2b43SDavid Greenman 969cfa1ca9dSYoshinobu Inoue #ifdef INET6 970cfa1ca9dSYoshinobu Inoue if (inp->inp_vflag & INP_IPV6) 971cfa1ca9dSYoshinobu Inoue hashkey_faddr = inp->in6p_faddr.s6_addr32[3] /* XXX */; 972cfa1ca9dSYoshinobu Inoue else 973cfa1ca9dSYoshinobu Inoue #endif /* INET6 */ 974cfa1ca9dSYoshinobu Inoue hashkey_faddr = inp->inp_faddr.s_addr; 975cfa1ca9dSYoshinobu Inoue 976cfa1ca9dSYoshinobu Inoue head = &inp->inp_pcbinfo->hashbase[INP_PCBHASH(hashkey_faddr, 977ddd79a97SDavid Greenman inp->inp_lport, inp->inp_fport, inp->inp_pcbinfo->hashmask)]; 97815bd2b43SDavid Greenman 979c3229e05SDavid Greenman LIST_REMOVE(inp, inp_hash); 98015bd2b43SDavid Greenman LIST_INSERT_HEAD(head, inp, inp_hash); 981c3229e05SDavid Greenman } 982c3229e05SDavid Greenman 983c3229e05SDavid Greenman /* 984c3229e05SDavid Greenman * Remove PCB from various lists. 985c3229e05SDavid Greenman */ 98676429de4SYoshinobu Inoue void 987c3229e05SDavid Greenman in_pcbremlists(inp) 988c3229e05SDavid Greenman struct inpcb *inp; 989c3229e05SDavid Greenman { 99098271db4SGarrett Wollman inp->inp_gencnt = ++inp->inp_pcbinfo->ipi_gencnt; 991c3229e05SDavid Greenman if (inp->inp_lport) { 992c3229e05SDavid Greenman struct inpcbport *phd = inp->inp_phd; 993c3229e05SDavid Greenman 994c3229e05SDavid Greenman LIST_REMOVE(inp, inp_hash); 995c3229e05SDavid Greenman LIST_REMOVE(inp, inp_portlist); 996fc2ffbe6SPoul-Henning Kamp if (LIST_FIRST(&phd->phd_pcblist) == NULL) { 997c3229e05SDavid Greenman LIST_REMOVE(phd, phd_hash); 998c3229e05SDavid Greenman free(phd, M_PCB); 999c3229e05SDavid Greenman } 1000c3229e05SDavid Greenman } 1001c3229e05SDavid Greenman LIST_REMOVE(inp, inp_list); 10023d4d47f3SGarrett Wollman inp->inp_pcbinfo->ipi_count--; 100315bd2b43SDavid Greenman } 100475c13541SPoul-Henning Kamp 100575c13541SPoul-Henning Kamp int 100675c13541SPoul-Henning Kamp prison_xinpcb(struct proc *p, struct inpcb *inp) 100775c13541SPoul-Henning Kamp { 100891421ba2SRobert Watson if (!jailed(p->p_ucred)) 100975c13541SPoul-Henning Kamp return (0); 101091421ba2SRobert Watson if (ntohl(inp->inp_laddr.s_addr) == p->p_ucred->cr_prison->pr_ip) 101175c13541SPoul-Henning Kamp return (0); 101275c13541SPoul-Henning Kamp return (1); 101375c13541SPoul-Henning Kamp } 1014