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 37cfa1ca9dSYoshinobu Inoue #include "opt_inet6.h" 38cfa1ca9dSYoshinobu Inoue 39df8bae1dSRodney W. Grimes #include <sys/param.h> 40df8bae1dSRodney W. Grimes #include <sys/systm.h> 41df8bae1dSRodney W. Grimes #include <sys/malloc.h> 42df8bae1dSRodney W. Grimes #include <sys/mbuf.h> 43cfa1ca9dSYoshinobu Inoue #include <sys/domain.h> 44df8bae1dSRodney W. Grimes #include <sys/protosw.h> 45df8bae1dSRodney W. Grimes #include <sys/socket.h> 46df8bae1dSRodney W. Grimes #include <sys/socketvar.h> 47df8bae1dSRodney W. Grimes #include <sys/proc.h> 4875c13541SPoul-Henning Kamp #include <sys/jail.h> 49101f9fc8SPeter Wemm #include <sys/kernel.h> 50101f9fc8SPeter Wemm #include <sys/sysctl.h> 518781d8e9SBruce Evans 5208637435SBruce Evans #include <machine/limits.h> 5308637435SBruce Evans 548781d8e9SBruce Evans #include <vm/vm_zone.h> 55df8bae1dSRodney W. Grimes 56df8bae1dSRodney W. Grimes #include <net/if.h> 57cfa1ca9dSYoshinobu Inoue #include <net/if_types.h> 58df8bae1dSRodney W. Grimes #include <net/route.h> 59df8bae1dSRodney W. Grimes 60df8bae1dSRodney W. Grimes #include <netinet/in.h> 61df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h> 62df8bae1dSRodney W. Grimes #include <netinet/in_var.h> 63df8bae1dSRodney W. Grimes #include <netinet/ip_var.h> 64cfa1ca9dSYoshinobu Inoue #ifdef INET6 65cfa1ca9dSYoshinobu Inoue #include <netinet/ip6.h> 66cfa1ca9dSYoshinobu Inoue #include <netinet6/ip6_var.h> 67cfa1ca9dSYoshinobu Inoue #endif /* INET6 */ 68cfa1ca9dSYoshinobu Inoue 69cfa1ca9dSYoshinobu Inoue #include "faith.h" 70cfa1ca9dSYoshinobu Inoue 71cfa1ca9dSYoshinobu Inoue #ifdef IPSEC 72cfa1ca9dSYoshinobu Inoue #include <netinet6/ipsec.h> 73cfa1ca9dSYoshinobu Inoue #include <netkey/key.h> 74cfa1ca9dSYoshinobu Inoue #include <netkey/key_debug.h> 75cfa1ca9dSYoshinobu Inoue #endif /* IPSEC */ 76df8bae1dSRodney W. Grimes 77df8bae1dSRodney W. Grimes struct in_addr zeroin_addr; 78df8bae1dSRodney W. Grimes 79bbd42ad0SPeter Wemm static void in_rtchange __P((struct inpcb *, int)); 80bbd42ad0SPeter Wemm 81101f9fc8SPeter Wemm /* 82101f9fc8SPeter Wemm * These configure the range of local port addresses assigned to 83101f9fc8SPeter Wemm * "unspecified" outgoing connections/packets/whatever. 84101f9fc8SPeter Wemm */ 8582cd038dSYoshinobu Inoue int ipport_lowfirstauto = IPPORT_RESERVED - 1; /* 1023 */ 8682cd038dSYoshinobu Inoue int ipport_lowlastauto = IPPORT_RESERVEDSTART; /* 600 */ 8782cd038dSYoshinobu Inoue int ipport_firstauto = IPPORT_RESERVED; /* 1024 */ 8882cd038dSYoshinobu Inoue int ipport_lastauto = IPPORT_USERRESERVED; /* 5000 */ 8982cd038dSYoshinobu Inoue int ipport_hifirstauto = IPPORT_HIFIRSTAUTO; /* 49152 */ 9082cd038dSYoshinobu Inoue int ipport_hilastauto = IPPORT_HILASTAUTO; /* 65535 */ 91101f9fc8SPeter Wemm 92bbd42ad0SPeter Wemm #define RANGECHK(var, min, max) \ 93bbd42ad0SPeter Wemm if ((var) < (min)) { (var) = (min); } \ 94bbd42ad0SPeter Wemm else if ((var) > (max)) { (var) = (max); } 95bbd42ad0SPeter Wemm 96bbd42ad0SPeter Wemm static int 97bbd42ad0SPeter Wemm sysctl_net_ipport_check SYSCTL_HANDLER_ARGS 98bbd42ad0SPeter Wemm { 99bbd42ad0SPeter Wemm int error = sysctl_handle_int(oidp, 100bbd42ad0SPeter Wemm oidp->oid_arg1, oidp->oid_arg2, req); 101bbd42ad0SPeter Wemm if (!error) { 102bbd42ad0SPeter Wemm RANGECHK(ipport_lowfirstauto, 1, IPPORT_RESERVED - 1); 103bbd42ad0SPeter Wemm RANGECHK(ipport_lowlastauto, 1, IPPORT_RESERVED - 1); 104bbd42ad0SPeter Wemm RANGECHK(ipport_firstauto, IPPORT_RESERVED, USHRT_MAX); 105bbd42ad0SPeter Wemm RANGECHK(ipport_lastauto, IPPORT_RESERVED, USHRT_MAX); 106bbd42ad0SPeter Wemm RANGECHK(ipport_hifirstauto, IPPORT_RESERVED, USHRT_MAX); 107bbd42ad0SPeter Wemm RANGECHK(ipport_hilastauto, IPPORT_RESERVED, USHRT_MAX); 108bbd42ad0SPeter Wemm } 109bbd42ad0SPeter Wemm return error; 110bbd42ad0SPeter Wemm } 111bbd42ad0SPeter Wemm 112bbd42ad0SPeter Wemm #undef RANGECHK 113bbd42ad0SPeter Wemm 11433b3ac06SPeter Wemm SYSCTL_NODE(_net_inet_ip, IPPROTO_IP, portrange, CTLFLAG_RW, 0, "IP Ports"); 11533b3ac06SPeter Wemm 116bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowfirst, CTLTYPE_INT|CTLFLAG_RW, 117bbd42ad0SPeter Wemm &ipport_lowfirstauto, 0, &sysctl_net_ipport_check, "I", ""); 118bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowlast, CTLTYPE_INT|CTLFLAG_RW, 119bbd42ad0SPeter Wemm &ipport_lowlastauto, 0, &sysctl_net_ipport_check, "I", ""); 120bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, first, CTLTYPE_INT|CTLFLAG_RW, 121bbd42ad0SPeter Wemm &ipport_firstauto, 0, &sysctl_net_ipport_check, "I", ""); 122bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, last, CTLTYPE_INT|CTLFLAG_RW, 123bbd42ad0SPeter Wemm &ipport_lastauto, 0, &sysctl_net_ipport_check, "I", ""); 124bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hifirst, CTLTYPE_INT|CTLFLAG_RW, 125bbd42ad0SPeter Wemm &ipport_hifirstauto, 0, &sysctl_net_ipport_check, "I", ""); 126bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hilast, CTLTYPE_INT|CTLFLAG_RW, 127bbd42ad0SPeter Wemm &ipport_hilastauto, 0, &sysctl_net_ipport_check, "I", ""); 1280312fbe9SPoul-Henning Kamp 129c3229e05SDavid Greenman /* 130c3229e05SDavid Greenman * in_pcb.c: manage the Protocol Control Blocks. 131c3229e05SDavid Greenman * 132c3229e05SDavid Greenman * NOTE: It is assumed that most of these functions will be called at 133c3229e05SDavid Greenman * splnet(). XXX - There are, unfortunately, a few exceptions to this 134c3229e05SDavid Greenman * rule that should be fixed. 135c3229e05SDavid Greenman */ 136c3229e05SDavid Greenman 137c3229e05SDavid Greenman /* 138c3229e05SDavid Greenman * Allocate a PCB and associate it with the socket. 139c3229e05SDavid Greenman */ 140df8bae1dSRodney W. Grimes int 141a29f300eSGarrett Wollman in_pcballoc(so, pcbinfo, p) 142df8bae1dSRodney W. Grimes struct socket *so; 14315bd2b43SDavid Greenman struct inpcbinfo *pcbinfo; 144a29f300eSGarrett Wollman struct proc *p; 145df8bae1dSRodney W. Grimes { 146df8bae1dSRodney W. Grimes register struct inpcb *inp; 147df8bae1dSRodney W. Grimes 1483d4d47f3SGarrett Wollman inp = zalloci(pcbinfo->ipi_zone); 149df8bae1dSRodney W. Grimes if (inp == NULL) 150df8bae1dSRodney W. Grimes return (ENOBUFS); 151df8bae1dSRodney W. Grimes bzero((caddr_t)inp, sizeof(*inp)); 1523d4d47f3SGarrett Wollman inp->inp_gencnt = ++pcbinfo->ipi_gencnt; 15315bd2b43SDavid Greenman inp->inp_pcbinfo = pcbinfo; 154df8bae1dSRodney W. Grimes inp->inp_socket = so; 15515bd2b43SDavid Greenman LIST_INSERT_HEAD(pcbinfo->listhead, inp, inp_list); 1563d4d47f3SGarrett Wollman pcbinfo->ipi_count++; 157df8bae1dSRodney W. Grimes so->so_pcb = (caddr_t)inp; 158df8bae1dSRodney W. Grimes return (0); 159df8bae1dSRodney W. Grimes } 160df8bae1dSRodney W. Grimes 161df8bae1dSRodney W. Grimes int 162a29f300eSGarrett Wollman in_pcbbind(inp, nam, p) 163df8bae1dSRodney W. Grimes register struct inpcb *inp; 16457bf258eSGarrett Wollman struct sockaddr *nam; 165a29f300eSGarrett Wollman struct proc *p; 166df8bae1dSRodney W. Grimes { 167df8bae1dSRodney W. Grimes register struct socket *so = inp->inp_socket; 16837bd2b30SPeter Wemm unsigned short *lastport; 16915bd2b43SDavid Greenman struct sockaddr_in *sin; 170c3229e05SDavid Greenman struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; 171df8bae1dSRodney W. Grimes u_short lport = 0; 172df8bae1dSRodney W. Grimes int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); 17375c13541SPoul-Henning Kamp int error, prison = 0; 174df8bae1dSRodney W. Grimes 17559562606SGarrett Wollman if (TAILQ_EMPTY(&in_ifaddrhead)) /* XXX broken! */ 176df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 177df8bae1dSRodney W. Grimes if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY) 178df8bae1dSRodney W. Grimes return (EINVAL); 179c3229e05SDavid Greenman if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) 1806d6a026bSDavid Greenman wild = 1; 181df8bae1dSRodney W. Grimes if (nam) { 18257bf258eSGarrett Wollman sin = (struct sockaddr_in *)nam; 18357bf258eSGarrett Wollman if (nam->sa_len != sizeof (*sin)) 184df8bae1dSRodney W. Grimes return (EINVAL); 185df8bae1dSRodney W. Grimes #ifdef notdef 186df8bae1dSRodney W. Grimes /* 187df8bae1dSRodney W. Grimes * We should check the family, but old programs 188df8bae1dSRodney W. Grimes * incorrectly fail to initialize it. 189df8bae1dSRodney W. Grimes */ 190df8bae1dSRodney W. Grimes if (sin->sin_family != AF_INET) 191df8bae1dSRodney W. Grimes return (EAFNOSUPPORT); 192df8bae1dSRodney W. Grimes #endif 19375c13541SPoul-Henning Kamp if (prison_ip(p, 0, &sin->sin_addr.s_addr)) 19475c13541SPoul-Henning Kamp return(EINVAL); 195df8bae1dSRodney W. Grimes lport = sin->sin_port; 196df8bae1dSRodney W. Grimes if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { 197df8bae1dSRodney W. Grimes /* 198df8bae1dSRodney W. Grimes * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; 199df8bae1dSRodney W. Grimes * allow complete duplication of binding if 200df8bae1dSRodney W. Grimes * SO_REUSEPORT is set, or if SO_REUSEADDR is set 201df8bae1dSRodney W. Grimes * and a multicast address is bound on both 202df8bae1dSRodney W. Grimes * new and duplicated sockets. 203df8bae1dSRodney W. Grimes */ 204df8bae1dSRodney W. Grimes if (so->so_options & SO_REUSEADDR) 205df8bae1dSRodney W. Grimes reuseport = SO_REUSEADDR|SO_REUSEPORT; 206df8bae1dSRodney W. Grimes } else if (sin->sin_addr.s_addr != INADDR_ANY) { 207df8bae1dSRodney W. Grimes sin->sin_port = 0; /* yech... */ 208df8bae1dSRodney W. Grimes if (ifa_ifwithaddr((struct sockaddr *)sin) == 0) 209df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 210df8bae1dSRodney W. Grimes } 211df8bae1dSRodney W. Grimes if (lport) { 212df8bae1dSRodney W. Grimes struct inpcb *t; 213df8bae1dSRodney W. Grimes 214df8bae1dSRodney W. Grimes /* GROSS */ 21557bf258eSGarrett Wollman if (ntohs(lport) < IPPORT_RESERVED && p && 21675c13541SPoul-Henning Kamp suser_xxx(0, p, PRISON_ROOT)) 2172469dd60SGarrett Wollman return (EACCES); 21875c13541SPoul-Henning Kamp if (p && p->p_prison) 21975c13541SPoul-Henning Kamp prison = 1; 2202f9a2132SBrian Feldman if (so->so_cred->cr_uid != 0 && 22152b65dbeSBill Fenner !IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { 2224049a042SGuido van Rooij t = in_pcblookup_local(inp->inp_pcbinfo, 22375c13541SPoul-Henning Kamp sin->sin_addr, lport, 22475c13541SPoul-Henning Kamp prison ? 0 : INPLOOKUP_WILDCARD); 22552b65dbeSBill Fenner if (t && 22652b65dbeSBill Fenner (ntohl(sin->sin_addr.s_addr) != INADDR_ANY || 22752b65dbeSBill Fenner ntohl(t->inp_laddr.s_addr) != INADDR_ANY || 22852b65dbeSBill Fenner (t->inp_socket->so_options & 22952b65dbeSBill Fenner SO_REUSEPORT) == 0) && 2302f9a2132SBrian Feldman (so->so_cred->cr_uid != 231cfa1ca9dSYoshinobu Inoue t->inp_socket->so_cred->cr_uid)) { 232cfa1ca9dSYoshinobu Inoue #if defined(INET6) 233cfa1ca9dSYoshinobu Inoue if (ip6_mapped_addr_on == 0 || 234cfa1ca9dSYoshinobu Inoue ntohl(sin->sin_addr.s_addr) != 235cfa1ca9dSYoshinobu Inoue INADDR_ANY || 236cfa1ca9dSYoshinobu Inoue ntohl(t->inp_laddr.s_addr) != 237cfa1ca9dSYoshinobu Inoue INADDR_ANY || 238cfa1ca9dSYoshinobu Inoue INP_SOCKAF(so) == 239cfa1ca9dSYoshinobu Inoue INP_SOCKAF(t->inp_socket)) 240cfa1ca9dSYoshinobu Inoue #endif /* defined(INET6) */ 2414049a042SGuido van Rooij return (EADDRINUSE); 2424049a042SGuido van Rooij } 243cfa1ca9dSYoshinobu Inoue } 244c3229e05SDavid Greenman t = in_pcblookup_local(pcbinfo, sin->sin_addr, 24575c13541SPoul-Henning Kamp lport, prison ? 0 : wild); 246cfa1ca9dSYoshinobu Inoue if (t && 247cfa1ca9dSYoshinobu Inoue (reuseport & t->inp_socket->so_options) == 0) { 248cfa1ca9dSYoshinobu Inoue #if defined(INET6) 249cfa1ca9dSYoshinobu Inoue if (ip6_mapped_addr_on == 0 || 250cfa1ca9dSYoshinobu Inoue ntohl(sin->sin_addr.s_addr) != 251cfa1ca9dSYoshinobu Inoue INADDR_ANY || 252cfa1ca9dSYoshinobu Inoue ntohl(t->inp_laddr.s_addr) != 253cfa1ca9dSYoshinobu Inoue INADDR_ANY || 254cfa1ca9dSYoshinobu Inoue INP_SOCKAF(so) == 255cfa1ca9dSYoshinobu Inoue INP_SOCKAF(t->inp_socket)) 256cfa1ca9dSYoshinobu Inoue #endif /* defined(INET6) */ 257df8bae1dSRodney W. Grimes return (EADDRINUSE); 258df8bae1dSRodney W. Grimes } 259cfa1ca9dSYoshinobu Inoue } 260df8bae1dSRodney W. Grimes inp->inp_laddr = sin->sin_addr; 261df8bae1dSRodney W. Grimes } 26233b3ac06SPeter Wemm if (lport == 0) { 26333b3ac06SPeter Wemm ushort first, last; 26433b3ac06SPeter Wemm int count; 26533b3ac06SPeter Wemm 26675c13541SPoul-Henning Kamp if (prison_ip(p, 0, &inp->inp_laddr.s_addr )) 26775c13541SPoul-Henning Kamp return (EINVAL); 268321a2846SPoul-Henning Kamp inp->inp_flags |= INP_ANONPORT; 269321a2846SPoul-Henning Kamp 27033b3ac06SPeter Wemm if (inp->inp_flags & INP_HIGHPORT) { 27133b3ac06SPeter Wemm first = ipport_hifirstauto; /* sysctl */ 27233b3ac06SPeter Wemm last = ipport_hilastauto; 273c3229e05SDavid Greenman lastport = &pcbinfo->lasthi; 27433b3ac06SPeter Wemm } else if (inp->inp_flags & INP_LOWPORT) { 27575c13541SPoul-Henning Kamp if (p && (error = suser_xxx(0, p, PRISON_ROOT))) 276a29f300eSGarrett Wollman return error; 277bbd42ad0SPeter Wemm first = ipport_lowfirstauto; /* 1023 */ 278bbd42ad0SPeter Wemm last = ipport_lowlastauto; /* 600 */ 279c3229e05SDavid Greenman lastport = &pcbinfo->lastlow; 28033b3ac06SPeter Wemm } else { 28133b3ac06SPeter Wemm first = ipport_firstauto; /* sysctl */ 28233b3ac06SPeter Wemm last = ipport_lastauto; 283c3229e05SDavid Greenman lastport = &pcbinfo->lastport; 28433b3ac06SPeter Wemm } 28533b3ac06SPeter Wemm /* 28633b3ac06SPeter Wemm * Simple check to ensure all ports are not used up causing 28733b3ac06SPeter Wemm * a deadlock here. 28833b3ac06SPeter Wemm * 28933b3ac06SPeter Wemm * We split the two cases (up and down) so that the direction 29033b3ac06SPeter Wemm * is not being tested on each round of the loop. 29133b3ac06SPeter Wemm */ 29233b3ac06SPeter Wemm if (first > last) { 29333b3ac06SPeter Wemm /* 29433b3ac06SPeter Wemm * counting down 29533b3ac06SPeter Wemm */ 29633b3ac06SPeter Wemm count = first - last; 29733b3ac06SPeter Wemm 298df8bae1dSRodney W. Grimes do { 299c3229e05SDavid Greenman if (count-- < 0) { /* completely used? */ 300c3229e05SDavid Greenman /* 301c3229e05SDavid Greenman * Undo any address bind that may have 302c3229e05SDavid Greenman * occurred above. 303c3229e05SDavid Greenman */ 304c3229e05SDavid Greenman inp->inp_laddr.s_addr = INADDR_ANY; 305c3229e05SDavid Greenman return (EAGAIN); 306c3229e05SDavid Greenman } 30733b3ac06SPeter Wemm --*lastport; 30833b3ac06SPeter Wemm if (*lastport > first || *lastport < last) 30933b3ac06SPeter Wemm *lastport = first; 31015bd2b43SDavid Greenman lport = htons(*lastport); 311c3229e05SDavid Greenman } while (in_pcblookup_local(pcbinfo, 312c3229e05SDavid Greenman inp->inp_laddr, lport, wild)); 31333b3ac06SPeter Wemm } else { 31433b3ac06SPeter Wemm /* 31533b3ac06SPeter Wemm * counting up 31633b3ac06SPeter Wemm */ 31733b3ac06SPeter Wemm count = last - first; 31833b3ac06SPeter Wemm 31933b3ac06SPeter Wemm do { 320c3229e05SDavid Greenman if (count-- < 0) { /* completely used? */ 321c3229e05SDavid Greenman /* 322c3229e05SDavid Greenman * Undo any address bind that may have 323c3229e05SDavid Greenman * occurred above. 324c3229e05SDavid Greenman */ 325c3229e05SDavid Greenman inp->inp_laddr.s_addr = INADDR_ANY; 326c3229e05SDavid Greenman return (EAGAIN); 327c3229e05SDavid Greenman } 32833b3ac06SPeter Wemm ++*lastport; 32933b3ac06SPeter Wemm if (*lastport < first || *lastport > last) 33033b3ac06SPeter Wemm *lastport = first; 33133b3ac06SPeter Wemm lport = htons(*lastport); 332c3229e05SDavid Greenman } while (in_pcblookup_local(pcbinfo, 333c3229e05SDavid Greenman inp->inp_laddr, lport, wild)); 33433b3ac06SPeter Wemm } 33533b3ac06SPeter Wemm } 336df8bae1dSRodney W. Grimes inp->inp_lport = lport; 337c3229e05SDavid Greenman if (in_pcbinshash(inp) != 0) { 338c3229e05SDavid Greenman inp->inp_laddr.s_addr = INADDR_ANY; 339c3229e05SDavid Greenman inp->inp_lport = 0; 340c3229e05SDavid Greenman return (EAGAIN); 341c3229e05SDavid Greenman } 342df8bae1dSRodney W. Grimes return (0); 343df8bae1dSRodney W. Grimes } 344df8bae1dSRodney W. Grimes 345999f1343SGarrett Wollman /* 346999f1343SGarrett Wollman * Transform old in_pcbconnect() into an inner subroutine for new 347999f1343SGarrett Wollman * in_pcbconnect(): Do some validity-checking on the remote 348999f1343SGarrett Wollman * address (in mbuf 'nam') and then determine local host address 349999f1343SGarrett Wollman * (i.e., which interface) to use to access that remote host. 350999f1343SGarrett Wollman * 351999f1343SGarrett Wollman * This preserves definition of in_pcbconnect(), while supporting a 352999f1343SGarrett Wollman * slightly different version for T/TCP. (This is more than 353999f1343SGarrett Wollman * a bit of a kludge, but cleaning up the internal interfaces would 354999f1343SGarrett Wollman * have forced minor changes in every protocol). 355999f1343SGarrett Wollman */ 356999f1343SGarrett Wollman 357999f1343SGarrett Wollman int 358999f1343SGarrett Wollman in_pcbladdr(inp, nam, plocal_sin) 359999f1343SGarrett Wollman register struct inpcb *inp; 36057bf258eSGarrett Wollman struct sockaddr *nam; 361999f1343SGarrett Wollman struct sockaddr_in **plocal_sin; 362999f1343SGarrett Wollman { 363df8bae1dSRodney W. Grimes struct in_ifaddr *ia; 36457bf258eSGarrett Wollman register struct sockaddr_in *sin = (struct sockaddr_in *)nam; 365df8bae1dSRodney W. Grimes 36657bf258eSGarrett Wollman if (nam->sa_len != sizeof (*sin)) 367df8bae1dSRodney W. Grimes return (EINVAL); 368df8bae1dSRodney W. Grimes if (sin->sin_family != AF_INET) 369df8bae1dSRodney W. Grimes return (EAFNOSUPPORT); 370df8bae1dSRodney W. Grimes if (sin->sin_port == 0) 371df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 37259562606SGarrett Wollman if (!TAILQ_EMPTY(&in_ifaddrhead)) { 373df8bae1dSRodney W. Grimes /* 374df8bae1dSRodney W. Grimes * If the destination address is INADDR_ANY, 375df8bae1dSRodney W. Grimes * use the primary local address. 376df8bae1dSRodney W. Grimes * If the supplied address is INADDR_BROADCAST, 377df8bae1dSRodney W. Grimes * and the primary interface supports broadcast, 378df8bae1dSRodney W. Grimes * choose the broadcast address for that interface. 379df8bae1dSRodney W. Grimes */ 380df8bae1dSRodney W. Grimes #define satosin(sa) ((struct sockaddr_in *)(sa)) 381df8bae1dSRodney W. Grimes #define sintosa(sin) ((struct sockaddr *)(sin)) 382df8bae1dSRodney W. Grimes #define ifatoia(ifa) ((struct in_ifaddr *)(ifa)) 383df8bae1dSRodney W. Grimes if (sin->sin_addr.s_addr == INADDR_ANY) 38459562606SGarrett Wollman sin->sin_addr = IA_SIN(in_ifaddrhead.tqh_first)->sin_addr; 385df8bae1dSRodney W. Grimes else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST && 38659562606SGarrett Wollman (in_ifaddrhead.tqh_first->ia_ifp->if_flags & IFF_BROADCAST)) 38759562606SGarrett Wollman sin->sin_addr = satosin(&in_ifaddrhead.tqh_first->ia_broadaddr)->sin_addr; 388df8bae1dSRodney W. Grimes } 389df8bae1dSRodney W. Grimes if (inp->inp_laddr.s_addr == INADDR_ANY) { 390df8bae1dSRodney W. Grimes register struct route *ro; 391df8bae1dSRodney W. Grimes 392df8bae1dSRodney W. Grimes ia = (struct in_ifaddr *)0; 393df8bae1dSRodney W. Grimes /* 394df8bae1dSRodney W. Grimes * If route is known or can be allocated now, 395df8bae1dSRodney W. Grimes * our src addr is taken from the i/f, else punt. 396df8bae1dSRodney W. Grimes */ 397df8bae1dSRodney W. Grimes ro = &inp->inp_route; 398df8bae1dSRodney W. Grimes if (ro->ro_rt && 399df8bae1dSRodney W. Grimes (satosin(&ro->ro_dst)->sin_addr.s_addr != 400df8bae1dSRodney W. Grimes sin->sin_addr.s_addr || 401df8bae1dSRodney W. Grimes inp->inp_socket->so_options & SO_DONTROUTE)) { 402df8bae1dSRodney W. Grimes RTFREE(ro->ro_rt); 403df8bae1dSRodney W. Grimes ro->ro_rt = (struct rtentry *)0; 404df8bae1dSRodney W. Grimes } 405df8bae1dSRodney W. Grimes if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/ 406df8bae1dSRodney W. Grimes (ro->ro_rt == (struct rtentry *)0 || 407df8bae1dSRodney W. Grimes ro->ro_rt->rt_ifp == (struct ifnet *)0)) { 408df8bae1dSRodney W. Grimes /* No route yet, so try to acquire one */ 409df8bae1dSRodney W. Grimes ro->ro_dst.sa_family = AF_INET; 410df8bae1dSRodney W. Grimes ro->ro_dst.sa_len = sizeof(struct sockaddr_in); 411df8bae1dSRodney W. Grimes ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = 412df8bae1dSRodney W. Grimes sin->sin_addr; 413df8bae1dSRodney W. Grimes rtalloc(ro); 414df8bae1dSRodney W. Grimes } 415df8bae1dSRodney W. Grimes /* 416df8bae1dSRodney W. Grimes * If we found a route, use the address 417df8bae1dSRodney W. Grimes * corresponding to the outgoing interface 418df8bae1dSRodney W. Grimes * unless it is the loopback (in case a route 419df8bae1dSRodney W. Grimes * to our address on another net goes to loopback). 420df8bae1dSRodney W. Grimes */ 421df8bae1dSRodney W. Grimes if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK)) 422df8bae1dSRodney W. Grimes ia = ifatoia(ro->ro_rt->rt_ifa); 423df8bae1dSRodney W. Grimes if (ia == 0) { 424df8bae1dSRodney W. Grimes u_short fport = sin->sin_port; 425df8bae1dSRodney W. Grimes 426df8bae1dSRodney W. Grimes sin->sin_port = 0; 427df8bae1dSRodney W. Grimes ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin))); 428df8bae1dSRodney W. Grimes if (ia == 0) 429df8bae1dSRodney W. Grimes ia = ifatoia(ifa_ifwithnet(sintosa(sin))); 430df8bae1dSRodney W. Grimes sin->sin_port = fport; 431df8bae1dSRodney W. Grimes if (ia == 0) 43259562606SGarrett Wollman ia = in_ifaddrhead.tqh_first; 433df8bae1dSRodney W. Grimes if (ia == 0) 434df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 435df8bae1dSRodney W. Grimes } 436df8bae1dSRodney W. Grimes /* 437df8bae1dSRodney W. Grimes * If the destination address is multicast and an outgoing 438df8bae1dSRodney W. Grimes * interface has been set as a multicast option, use the 439df8bae1dSRodney W. Grimes * address of that interface as our source address. 440df8bae1dSRodney W. Grimes */ 441df8bae1dSRodney W. Grimes if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) && 442df8bae1dSRodney W. Grimes inp->inp_moptions != NULL) { 443df8bae1dSRodney W. Grimes struct ip_moptions *imo; 444df8bae1dSRodney W. Grimes struct ifnet *ifp; 445df8bae1dSRodney W. Grimes 446df8bae1dSRodney W. Grimes imo = inp->inp_moptions; 447df8bae1dSRodney W. Grimes if (imo->imo_multicast_ifp != NULL) { 448df8bae1dSRodney W. Grimes ifp = imo->imo_multicast_ifp; 44959562606SGarrett Wollman for (ia = in_ifaddrhead.tqh_first; ia; 45059562606SGarrett Wollman ia = ia->ia_link.tqe_next) 451df8bae1dSRodney W. Grimes if (ia->ia_ifp == ifp) 452df8bae1dSRodney W. Grimes break; 453df8bae1dSRodney W. Grimes if (ia == 0) 454df8bae1dSRodney W. Grimes return (EADDRNOTAVAIL); 455df8bae1dSRodney W. Grimes } 456df8bae1dSRodney W. Grimes } 457999f1343SGarrett Wollman /* 458999f1343SGarrett Wollman * Don't do pcblookup call here; return interface in plocal_sin 459999f1343SGarrett Wollman * and exit to caller, that will do the lookup. 460999f1343SGarrett Wollman */ 461999f1343SGarrett Wollman *plocal_sin = &ia->ia_addr; 462999f1343SGarrett Wollman 463999f1343SGarrett Wollman } 464999f1343SGarrett Wollman return(0); 465999f1343SGarrett Wollman } 466999f1343SGarrett Wollman 467999f1343SGarrett Wollman /* 468999f1343SGarrett Wollman * Outer subroutine: 469999f1343SGarrett Wollman * Connect from a socket to a specified address. 470999f1343SGarrett Wollman * Both address and port must be specified in argument sin. 471999f1343SGarrett Wollman * If don't have a local address for this socket yet, 472999f1343SGarrett Wollman * then pick one. 473999f1343SGarrett Wollman */ 474999f1343SGarrett Wollman int 475a29f300eSGarrett Wollman in_pcbconnect(inp, nam, p) 476999f1343SGarrett Wollman register struct inpcb *inp; 47757bf258eSGarrett Wollman struct sockaddr *nam; 478a29f300eSGarrett Wollman struct proc *p; 479999f1343SGarrett Wollman { 480999f1343SGarrett Wollman struct sockaddr_in *ifaddr; 48157bf258eSGarrett Wollman register struct sockaddr_in *sin = (struct sockaddr_in *)nam; 482999f1343SGarrett Wollman int error; 483999f1343SGarrett Wollman 484999f1343SGarrett Wollman /* 485999f1343SGarrett Wollman * Call inner routine, to assign local interface address. 486999f1343SGarrett Wollman */ 487831a80b0SMatthew Dillon if ((error = in_pcbladdr(inp, nam, &ifaddr)) != 0) 488999f1343SGarrett Wollman return(error); 489999f1343SGarrett Wollman 490c3229e05SDavid Greenman if (in_pcblookup_hash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port, 491df8bae1dSRodney W. Grimes inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 492cfa1ca9dSYoshinobu Inoue inp->inp_lport, 0, NULL) != NULL) { 493df8bae1dSRodney W. Grimes return (EADDRINUSE); 494c3229e05SDavid Greenman } 495df8bae1dSRodney W. Grimes if (inp->inp_laddr.s_addr == INADDR_ANY) { 4965a903f8dSPierre Beyssac if (inp->inp_lport == 0) { 4975a903f8dSPierre Beyssac error = in_pcbbind(inp, (struct sockaddr *)0, p); 4985a903f8dSPierre Beyssac if (error) 4995a903f8dSPierre Beyssac return (error); 5005a903f8dSPierre Beyssac } 501df8bae1dSRodney W. Grimes inp->inp_laddr = ifaddr->sin_addr; 502df8bae1dSRodney W. Grimes } 503df8bae1dSRodney W. Grimes inp->inp_faddr = sin->sin_addr; 504df8bae1dSRodney W. Grimes inp->inp_fport = sin->sin_port; 50515bd2b43SDavid Greenman in_pcbrehash(inp); 506df8bae1dSRodney W. Grimes return (0); 507df8bae1dSRodney W. Grimes } 508df8bae1dSRodney W. Grimes 50926f9a767SRodney W. Grimes void 510df8bae1dSRodney W. Grimes in_pcbdisconnect(inp) 511df8bae1dSRodney W. Grimes struct inpcb *inp; 512df8bae1dSRodney W. Grimes { 513df8bae1dSRodney W. Grimes 514df8bae1dSRodney W. Grimes inp->inp_faddr.s_addr = INADDR_ANY; 515df8bae1dSRodney W. Grimes inp->inp_fport = 0; 51615bd2b43SDavid Greenman in_pcbrehash(inp); 517df8bae1dSRodney W. Grimes if (inp->inp_socket->so_state & SS_NOFDREF) 518df8bae1dSRodney W. Grimes in_pcbdetach(inp); 519df8bae1dSRodney W. Grimes } 520df8bae1dSRodney W. Grimes 52126f9a767SRodney W. Grimes void 522df8bae1dSRodney W. Grimes in_pcbdetach(inp) 523df8bae1dSRodney W. Grimes struct inpcb *inp; 524df8bae1dSRodney W. Grimes { 525df8bae1dSRodney W. Grimes struct socket *so = inp->inp_socket; 5263d4d47f3SGarrett Wollman struct inpcbinfo *ipi = inp->inp_pcbinfo; 527df8bae1dSRodney W. Grimes 528cfa1ca9dSYoshinobu Inoue #ifdef IPSEC 529cfa1ca9dSYoshinobu Inoue ipsec4_delete_pcbpolicy(inp); 530cfa1ca9dSYoshinobu Inoue #endif /*IPSEC*/ 5313d4d47f3SGarrett Wollman inp->inp_gencnt = ++ipi->ipi_gencnt; 532c3229e05SDavid Greenman in_pcbremlists(inp); 533df8bae1dSRodney W. Grimes so->so_pcb = 0; 534df8bae1dSRodney W. Grimes sofree(so); 535df8bae1dSRodney W. Grimes if (inp->inp_options) 536df8bae1dSRodney W. Grimes (void)m_free(inp->inp_options); 537df8bae1dSRodney W. Grimes if (inp->inp_route.ro_rt) 538df8bae1dSRodney W. Grimes rtfree(inp->inp_route.ro_rt); 539df8bae1dSRodney W. Grimes ip_freemoptions(inp->inp_moptions); 540cfa1ca9dSYoshinobu Inoue inp->inp_vflag = 0; 5413d4d47f3SGarrett Wollman zfreei(ipi->ipi_zone, inp); 542df8bae1dSRodney W. Grimes } 543df8bae1dSRodney W. Grimes 544117bcae7SGarrett Wollman /* 545117bcae7SGarrett Wollman * The calling convention of in_setsockaddr() and in_setpeeraddr() was 546117bcae7SGarrett Wollman * modified to match the pru_sockaddr() and pru_peeraddr() entry points 547117bcae7SGarrett Wollman * in struct pr_usrreqs, so that protocols can just reference then directly 548117bcae7SGarrett Wollman * without the need for a wrapper function. The socket must have a valid 549117bcae7SGarrett Wollman * (i.e., non-nil) PCB, but it should be impossible to get an invalid one 550117bcae7SGarrett Wollman * except through a kernel programming error, so it is acceptable to panic 55157bf258eSGarrett Wollman * (or in this case trap) if the PCB is invalid. (Actually, we don't trap 55257bf258eSGarrett Wollman * because there actually /is/ a programming error somewhere... XXX) 553117bcae7SGarrett Wollman */ 554117bcae7SGarrett Wollman int 555117bcae7SGarrett Wollman in_setsockaddr(so, nam) 556117bcae7SGarrett Wollman struct socket *so; 55757bf258eSGarrett Wollman struct sockaddr **nam; 558df8bae1dSRodney W. Grimes { 559fdc984f7STor Egge int s; 560fdc984f7STor Egge register struct inpcb *inp; 561df8bae1dSRodney W. Grimes register struct sockaddr_in *sin; 562df8bae1dSRodney W. Grimes 563c3229e05SDavid Greenman /* 564c3229e05SDavid Greenman * Do the malloc first in case it blocks. 565c3229e05SDavid Greenman */ 56642fa505bSDavid Greenman MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_WAITOK); 56742fa505bSDavid Greenman bzero(sin, sizeof *sin); 56842fa505bSDavid Greenman sin->sin_family = AF_INET; 56942fa505bSDavid Greenman sin->sin_len = sizeof(*sin); 57042fa505bSDavid Greenman 571fdc984f7STor Egge s = splnet(); 572fdc984f7STor Egge inp = sotoinpcb(so); 573db112f04STor Egge if (!inp) { 574db112f04STor Egge splx(s); 57542fa505bSDavid Greenman free(sin, M_SONAME); 576db112f04STor Egge return EINVAL; 577db112f04STor Egge } 578df8bae1dSRodney W. Grimes sin->sin_port = inp->inp_lport; 579df8bae1dSRodney W. Grimes sin->sin_addr = inp->inp_laddr; 580db112f04STor Egge splx(s); 58142fa505bSDavid Greenman 58242fa505bSDavid Greenman *nam = (struct sockaddr *)sin; 583117bcae7SGarrett Wollman return 0; 584df8bae1dSRodney W. Grimes } 585df8bae1dSRodney W. Grimes 586117bcae7SGarrett Wollman int 587117bcae7SGarrett Wollman in_setpeeraddr(so, nam) 588117bcae7SGarrett Wollman struct socket *so; 58957bf258eSGarrett Wollman struct sockaddr **nam; 590df8bae1dSRodney W. Grimes { 591fdc984f7STor Egge int s; 592fdc984f7STor Egge struct inpcb *inp; 593df8bae1dSRodney W. Grimes register struct sockaddr_in *sin; 594df8bae1dSRodney W. Grimes 595c3229e05SDavid Greenman /* 596c3229e05SDavid Greenman * Do the malloc first in case it blocks. 597c3229e05SDavid Greenman */ 59842fa505bSDavid Greenman MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_WAITOK); 59942fa505bSDavid Greenman bzero((caddr_t)sin, sizeof (*sin)); 60042fa505bSDavid Greenman sin->sin_family = AF_INET; 60142fa505bSDavid Greenman sin->sin_len = sizeof(*sin); 60242fa505bSDavid Greenman 603fdc984f7STor Egge s = splnet(); 604fdc984f7STor Egge inp = sotoinpcb(so); 605db112f04STor Egge if (!inp) { 606db112f04STor Egge splx(s); 60742fa505bSDavid Greenman free(sin, M_SONAME); 608db112f04STor Egge return EINVAL; 609db112f04STor Egge } 610df8bae1dSRodney W. Grimes sin->sin_port = inp->inp_fport; 611df8bae1dSRodney W. Grimes sin->sin_addr = inp->inp_faddr; 612db112f04STor Egge splx(s); 61342fa505bSDavid Greenman 61442fa505bSDavid Greenman *nam = (struct sockaddr *)sin; 615117bcae7SGarrett Wollman return 0; 616df8bae1dSRodney W. Grimes } 617df8bae1dSRodney W. Grimes 618df8bae1dSRodney W. Grimes /* 619df8bae1dSRodney W. Grimes * Pass some notification to all connections of a protocol 620df8bae1dSRodney W. Grimes * associated with address dst. The local address and/or port numbers 621df8bae1dSRodney W. Grimes * may be specified to limit the search. The "usual action" will be 622df8bae1dSRodney W. Grimes * taken, depending on the ctlinput cmd. The caller must filter any 623df8bae1dSRodney W. Grimes * cmds that are uninteresting (e.g., no error in the map). 624df8bae1dSRodney W. Grimes * Call the protocol specific routine (if any) to report 625df8bae1dSRodney W. Grimes * any errors for each matching socket. 626df8bae1dSRodney W. Grimes */ 62726f9a767SRodney W. Grimes void 628df8bae1dSRodney W. Grimes in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify) 62915bd2b43SDavid Greenman struct inpcbhead *head; 630df8bae1dSRodney W. Grimes struct sockaddr *dst; 631df8bae1dSRodney W. Grimes u_int fport_arg, lport_arg; 632df8bae1dSRodney W. Grimes struct in_addr laddr; 633df8bae1dSRodney W. Grimes int cmd; 634df8bae1dSRodney W. Grimes void (*notify) __P((struct inpcb *, int)); 635df8bae1dSRodney W. Grimes { 636df8bae1dSRodney W. Grimes register struct inpcb *inp, *oinp; 637df8bae1dSRodney W. Grimes struct in_addr faddr; 638df8bae1dSRodney W. Grimes u_short fport = fport_arg, lport = lport_arg; 6397bc4aca7SDavid Greenman int errno, s; 640df8bae1dSRodney W. Grimes 641df8bae1dSRodney W. Grimes if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET) 642df8bae1dSRodney W. Grimes return; 643df8bae1dSRodney W. Grimes faddr = ((struct sockaddr_in *)dst)->sin_addr; 644df8bae1dSRodney W. Grimes if (faddr.s_addr == INADDR_ANY) 645df8bae1dSRodney W. Grimes return; 646df8bae1dSRodney W. Grimes 647df8bae1dSRodney W. Grimes /* 648df8bae1dSRodney W. Grimes * Redirects go to all references to the destination, 649df8bae1dSRodney W. Grimes * and use in_rtchange to invalidate the route cache. 650df8bae1dSRodney W. Grimes * Dead host indications: notify all references to the destination. 651df8bae1dSRodney W. Grimes * Otherwise, if we have knowledge of the local port and address, 652df8bae1dSRodney W. Grimes * deliver only to that socket. 653df8bae1dSRodney W. Grimes */ 654df8bae1dSRodney W. Grimes if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) { 655df8bae1dSRodney W. Grimes fport = 0; 656df8bae1dSRodney W. Grimes lport = 0; 657df8bae1dSRodney W. Grimes laddr.s_addr = 0; 658df8bae1dSRodney W. Grimes if (cmd != PRC_HOSTDEAD) 659df8bae1dSRodney W. Grimes notify = in_rtchange; 660df8bae1dSRodney W. Grimes } 661df8bae1dSRodney W. Grimes errno = inetctlerrmap[cmd]; 6627bc4aca7SDavid Greenman s = splnet(); 66315bd2b43SDavid Greenman for (inp = head->lh_first; inp != NULL;) { 664cfa1ca9dSYoshinobu Inoue #ifdef INET6 665cfa1ca9dSYoshinobu Inoue if ((inp->inp_vflag & INP_IPV4) == NULL) { 666cfa1ca9dSYoshinobu Inoue inp = LIST_NEXT(inp, inp_list); 667cfa1ca9dSYoshinobu Inoue continue; 668cfa1ca9dSYoshinobu Inoue } 669cfa1ca9dSYoshinobu Inoue #endif 670df8bae1dSRodney W. Grimes if (inp->inp_faddr.s_addr != faddr.s_addr || 671df8bae1dSRodney W. Grimes inp->inp_socket == 0 || 672df8bae1dSRodney W. Grimes (lport && inp->inp_lport != lport) || 673df8bae1dSRodney W. Grimes (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) || 674df8bae1dSRodney W. Grimes (fport && inp->inp_fport != fport)) { 67515bd2b43SDavid Greenman inp = inp->inp_list.le_next; 676df8bae1dSRodney W. Grimes continue; 677df8bae1dSRodney W. Grimes } 678df8bae1dSRodney W. Grimes oinp = inp; 67915bd2b43SDavid Greenman inp = inp->inp_list.le_next; 680df8bae1dSRodney W. Grimes if (notify) 681df8bae1dSRodney W. Grimes (*notify)(oinp, errno); 682df8bae1dSRodney W. Grimes } 6837bc4aca7SDavid Greenman splx(s); 684df8bae1dSRodney W. Grimes } 685df8bae1dSRodney W. Grimes 686df8bae1dSRodney W. Grimes /* 687df8bae1dSRodney W. Grimes * Check for alternatives when higher level complains 688df8bae1dSRodney W. Grimes * about service problems. For now, invalidate cached 689df8bae1dSRodney W. Grimes * routing information. If the route was created dynamically 690df8bae1dSRodney W. Grimes * (by a redirect), time to try a default gateway again. 691df8bae1dSRodney W. Grimes */ 69226f9a767SRodney W. Grimes void 693df8bae1dSRodney W. Grimes in_losing(inp) 694df8bae1dSRodney W. Grimes struct inpcb *inp; 695df8bae1dSRodney W. Grimes { 696df8bae1dSRodney W. Grimes register struct rtentry *rt; 697df8bae1dSRodney W. Grimes struct rt_addrinfo info; 698df8bae1dSRodney W. Grimes 699df8bae1dSRodney W. Grimes if ((rt = inp->inp_route.ro_rt)) { 700df8bae1dSRodney W. Grimes inp->inp_route.ro_rt = 0; 701df8bae1dSRodney W. Grimes bzero((caddr_t)&info, sizeof(info)); 702df8bae1dSRodney W. Grimes info.rti_info[RTAX_DST] = 703df8bae1dSRodney W. Grimes (struct sockaddr *)&inp->inp_route.ro_dst; 704df8bae1dSRodney W. Grimes info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; 705df8bae1dSRodney W. Grimes info.rti_info[RTAX_NETMASK] = rt_mask(rt); 706df8bae1dSRodney W. Grimes rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0); 707df8bae1dSRodney W. Grimes if (rt->rt_flags & RTF_DYNAMIC) 708df8bae1dSRodney W. Grimes (void) rtrequest(RTM_DELETE, rt_key(rt), 709df8bae1dSRodney W. Grimes rt->rt_gateway, rt_mask(rt), rt->rt_flags, 710df8bae1dSRodney W. Grimes (struct rtentry **)0); 711df8bae1dSRodney W. Grimes else 712df8bae1dSRodney W. Grimes /* 713df8bae1dSRodney W. Grimes * A new route can be allocated 714df8bae1dSRodney W. Grimes * the next time output is attempted. 715df8bae1dSRodney W. Grimes */ 716df8bae1dSRodney W. Grimes rtfree(rt); 717df8bae1dSRodney W. Grimes } 718df8bae1dSRodney W. Grimes } 719df8bae1dSRodney W. Grimes 720df8bae1dSRodney W. Grimes /* 721df8bae1dSRodney W. Grimes * After a routing change, flush old routing 722df8bae1dSRodney W. Grimes * and allocate a (hopefully) better one. 723df8bae1dSRodney W. Grimes */ 7240312fbe9SPoul-Henning Kamp static void 725df8bae1dSRodney W. Grimes in_rtchange(inp, errno) 726df8bae1dSRodney W. Grimes register struct inpcb *inp; 727df8bae1dSRodney W. Grimes int errno; 728df8bae1dSRodney W. Grimes { 729df8bae1dSRodney W. Grimes if (inp->inp_route.ro_rt) { 730df8bae1dSRodney W. Grimes rtfree(inp->inp_route.ro_rt); 731df8bae1dSRodney W. Grimes inp->inp_route.ro_rt = 0; 732df8bae1dSRodney W. Grimes /* 733df8bae1dSRodney W. Grimes * A new route can be allocated the next time 734df8bae1dSRodney W. Grimes * output is attempted. 735df8bae1dSRodney W. Grimes */ 736df8bae1dSRodney W. Grimes } 737df8bae1dSRodney W. Grimes } 738df8bae1dSRodney W. Grimes 739c3229e05SDavid Greenman /* 740c3229e05SDavid Greenman * Lookup a PCB based on the local address and port. 741c3229e05SDavid Greenman */ 742df8bae1dSRodney W. Grimes struct inpcb * 743c3229e05SDavid Greenman in_pcblookup_local(pcbinfo, laddr, lport_arg, wild_okay) 7446d6a026bSDavid Greenman struct inpcbinfo *pcbinfo; 745c3229e05SDavid Greenman struct in_addr laddr; 746c3229e05SDavid Greenman u_int lport_arg; 7476d6a026bSDavid Greenman int wild_okay; 748df8bae1dSRodney W. Grimes { 749f1d19042SArchie Cobbs register struct inpcb *inp; 750df8bae1dSRodney W. Grimes int matchwild = 3, wildcard; 751c3229e05SDavid Greenman u_short lport = lport_arg; 7527bc4aca7SDavid Greenman 753c3229e05SDavid Greenman if (!wild_okay) { 754c3229e05SDavid Greenman struct inpcbhead *head; 755c3229e05SDavid Greenman /* 756c3229e05SDavid Greenman * Look for an unconnected (wildcard foreign addr) PCB that 757c3229e05SDavid Greenman * matches the local address and port we're looking for. 758c3229e05SDavid Greenman */ 759c3229e05SDavid Greenman head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)]; 760c3229e05SDavid Greenman for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { 761cfa1ca9dSYoshinobu Inoue #ifdef INET6 762cfa1ca9dSYoshinobu Inoue if ((inp->inp_vflag & INP_IPV4) == NULL) 763cfa1ca9dSYoshinobu Inoue continue; 764cfa1ca9dSYoshinobu Inoue #endif 765c3229e05SDavid Greenman if (inp->inp_faddr.s_addr == INADDR_ANY && 766c3229e05SDavid Greenman inp->inp_laddr.s_addr == laddr.s_addr && 767c3229e05SDavid Greenman inp->inp_lport == lport) { 768c3229e05SDavid Greenman /* 769c3229e05SDavid Greenman * Found. 770c3229e05SDavid Greenman */ 771c3229e05SDavid Greenman return (inp); 772df8bae1dSRodney W. Grimes } 773c3229e05SDavid Greenman } 774c3229e05SDavid Greenman /* 775c3229e05SDavid Greenman * Not found. 776c3229e05SDavid Greenman */ 777c3229e05SDavid Greenman return (NULL); 778c3229e05SDavid Greenman } else { 779c3229e05SDavid Greenman struct inpcbporthead *porthash; 780c3229e05SDavid Greenman struct inpcbport *phd; 781c3229e05SDavid Greenman struct inpcb *match = NULL; 782c3229e05SDavid Greenman /* 783c3229e05SDavid Greenman * Best fit PCB lookup. 784c3229e05SDavid Greenman * 785c3229e05SDavid Greenman * First see if this local port is in use by looking on the 786c3229e05SDavid Greenman * port hash list. 787c3229e05SDavid Greenman */ 788c3229e05SDavid Greenman porthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(lport, 789c3229e05SDavid Greenman pcbinfo->porthashmask)]; 790c3229e05SDavid Greenman for (phd = porthash->lh_first; phd != NULL; phd = phd->phd_hash.le_next) { 791c3229e05SDavid Greenman if (phd->phd_port == lport) 792c3229e05SDavid Greenman break; 793c3229e05SDavid Greenman } 794c3229e05SDavid Greenman if (phd != NULL) { 795c3229e05SDavid Greenman /* 796c3229e05SDavid Greenman * Port is in use by one or more PCBs. Look for best 797c3229e05SDavid Greenman * fit. 798c3229e05SDavid Greenman */ 799c3229e05SDavid Greenman for (inp = phd->phd_pcblist.lh_first; inp != NULL; 800c3229e05SDavid Greenman inp = inp->inp_portlist.le_next) { 801c3229e05SDavid Greenman wildcard = 0; 802cfa1ca9dSYoshinobu Inoue #ifdef INET6 803cfa1ca9dSYoshinobu Inoue if ((inp->inp_vflag & INP_IPV4) == NULL) 804cfa1ca9dSYoshinobu Inoue continue; 805cfa1ca9dSYoshinobu Inoue #endif 806c3229e05SDavid Greenman if (inp->inp_faddr.s_addr != INADDR_ANY) 807c3229e05SDavid Greenman wildcard++; 80815bd2b43SDavid Greenman if (inp->inp_laddr.s_addr != INADDR_ANY) { 80915bd2b43SDavid Greenman if (laddr.s_addr == INADDR_ANY) 81015bd2b43SDavid Greenman wildcard++; 81115bd2b43SDavid Greenman else if (inp->inp_laddr.s_addr != laddr.s_addr) 81215bd2b43SDavid Greenman continue; 81315bd2b43SDavid Greenman } else { 81415bd2b43SDavid Greenman if (laddr.s_addr != INADDR_ANY) 81515bd2b43SDavid Greenman wildcard++; 81615bd2b43SDavid Greenman } 817df8bae1dSRodney W. Grimes if (wildcard < matchwild) { 818df8bae1dSRodney W. Grimes match = inp; 819df8bae1dSRodney W. Grimes matchwild = wildcard; 8203dbdc25cSDavid Greenman if (matchwild == 0) { 821df8bae1dSRodney W. Grimes break; 822df8bae1dSRodney W. Grimes } 823df8bae1dSRodney W. Grimes } 8243dbdc25cSDavid Greenman } 825c3229e05SDavid Greenman } 826df8bae1dSRodney W. Grimes return (match); 827df8bae1dSRodney W. Grimes } 828c3229e05SDavid Greenman } 82915bd2b43SDavid Greenman 83015bd2b43SDavid Greenman /* 83115bd2b43SDavid Greenman * Lookup PCB in hash list. 83215bd2b43SDavid Greenman */ 83315bd2b43SDavid Greenman struct inpcb * 834cfa1ca9dSYoshinobu Inoue in_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard, 835cfa1ca9dSYoshinobu Inoue ifp) 83615bd2b43SDavid Greenman struct inpcbinfo *pcbinfo; 83715bd2b43SDavid Greenman struct in_addr faddr, laddr; 83815bd2b43SDavid Greenman u_int fport_arg, lport_arg; 8396d6a026bSDavid Greenman int wildcard; 840cfa1ca9dSYoshinobu Inoue struct ifnet *ifp; 84115bd2b43SDavid Greenman { 84215bd2b43SDavid Greenman struct inpcbhead *head; 84315bd2b43SDavid Greenman register struct inpcb *inp; 84415bd2b43SDavid Greenman u_short fport = fport_arg, lport = lport_arg; 84515bd2b43SDavid Greenman 84615bd2b43SDavid Greenman /* 84715bd2b43SDavid Greenman * First look for an exact match. 84815bd2b43SDavid Greenman */ 849ddd79a97SDavid Greenman head = &pcbinfo->hashbase[INP_PCBHASH(faddr.s_addr, lport, fport, pcbinfo->hashmask)]; 85015bd2b43SDavid Greenman for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { 851cfa1ca9dSYoshinobu Inoue #ifdef INET6 852cfa1ca9dSYoshinobu Inoue if ((inp->inp_vflag & INP_IPV4) == NULL) 853cfa1ca9dSYoshinobu Inoue continue; 854cfa1ca9dSYoshinobu Inoue #endif 8556d6a026bSDavid Greenman if (inp->inp_faddr.s_addr == faddr.s_addr && 856ca98b82cSDavid Greenman inp->inp_laddr.s_addr == laddr.s_addr && 857ca98b82cSDavid Greenman inp->inp_fport == fport && 858c3229e05SDavid Greenman inp->inp_lport == lport) { 859c3229e05SDavid Greenman /* 860c3229e05SDavid Greenman * Found. 861c3229e05SDavid Greenman */ 862c3229e05SDavid Greenman return (inp); 863c3229e05SDavid Greenman } 8646d6a026bSDavid Greenman } 8656d6a026bSDavid Greenman if (wildcard) { 8666d6a026bSDavid Greenman struct inpcb *local_wild = NULL; 867cfa1ca9dSYoshinobu Inoue #if defined(INET6) 868cfa1ca9dSYoshinobu Inoue struct inpcb *local_wild_mapped = NULL; 869cfa1ca9dSYoshinobu Inoue #endif /* defined(INET6) */ 8706d6a026bSDavid Greenman 871ddd79a97SDavid Greenman head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)]; 8726d6a026bSDavid Greenman for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { 873cfa1ca9dSYoshinobu Inoue #ifdef INET6 874cfa1ca9dSYoshinobu Inoue if ((inp->inp_vflag & INP_IPV4) == NULL) 875cfa1ca9dSYoshinobu Inoue continue; 876cfa1ca9dSYoshinobu Inoue #endif 8776d6a026bSDavid Greenman if (inp->inp_faddr.s_addr == INADDR_ANY && 878c3229e05SDavid Greenman inp->inp_lport == lport) { 879cfa1ca9dSYoshinobu Inoue #if defined(NFAITH) && NFAITH > 0 880cfa1ca9dSYoshinobu Inoue if (ifp && ifp->if_type == IFT_FAITH && 881cfa1ca9dSYoshinobu Inoue (inp->inp_flags & INP_FAITH) == 0) 882cfa1ca9dSYoshinobu Inoue continue; 883cfa1ca9dSYoshinobu Inoue #endif 8846d6a026bSDavid Greenman if (inp->inp_laddr.s_addr == laddr.s_addr) 885c3229e05SDavid Greenman return (inp); 886cfa1ca9dSYoshinobu Inoue else if (inp->inp_laddr.s_addr == INADDR_ANY) { 887cfa1ca9dSYoshinobu Inoue #if defined(INET6) 888cfa1ca9dSYoshinobu Inoue if (INP_CHECK_SOCKAF(inp->inp_socket, 889cfa1ca9dSYoshinobu Inoue AF_INET6)) 890cfa1ca9dSYoshinobu Inoue local_wild_mapped = inp; 891cfa1ca9dSYoshinobu Inoue else 892cfa1ca9dSYoshinobu Inoue #endif /* defined(INET6) */ 8936d6a026bSDavid Greenman local_wild = inp; 8946d6a026bSDavid Greenman } 8956d6a026bSDavid Greenman } 896cfa1ca9dSYoshinobu Inoue } 897cfa1ca9dSYoshinobu Inoue #if defined(INET6) 898cfa1ca9dSYoshinobu Inoue if (local_wild == NULL) 899cfa1ca9dSYoshinobu Inoue return (local_wild_mapped); 900cfa1ca9dSYoshinobu Inoue #endif /* defined(INET6) */ 901c3229e05SDavid Greenman return (local_wild); 9026d6a026bSDavid Greenman } 903c3229e05SDavid Greenman 904c3229e05SDavid Greenman /* 905c3229e05SDavid Greenman * Not found. 906c3229e05SDavid Greenman */ 9076d6a026bSDavid Greenman return (NULL); 90815bd2b43SDavid Greenman } 90915bd2b43SDavid Greenman 9107bc4aca7SDavid Greenman /* 911c3229e05SDavid Greenman * Insert PCB onto various hash lists. 9127bc4aca7SDavid Greenman */ 913c3229e05SDavid Greenman int 91415bd2b43SDavid Greenman in_pcbinshash(inp) 91515bd2b43SDavid Greenman struct inpcb *inp; 91615bd2b43SDavid Greenman { 917c3229e05SDavid Greenman struct inpcbhead *pcbhash; 918c3229e05SDavid Greenman struct inpcbporthead *pcbporthash; 919c3229e05SDavid Greenman struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; 920c3229e05SDavid Greenman struct inpcbport *phd; 921cfa1ca9dSYoshinobu Inoue u_int32_t hashkey_faddr; 92215bd2b43SDavid Greenman 923cfa1ca9dSYoshinobu Inoue #ifdef INET6 924cfa1ca9dSYoshinobu Inoue if (inp->inp_vflag & INP_IPV6) 925cfa1ca9dSYoshinobu Inoue hashkey_faddr = inp->in6p_faddr.s6_addr32[3] /* XXX */; 926cfa1ca9dSYoshinobu Inoue else 927cfa1ca9dSYoshinobu Inoue #endif /* INET6 */ 928cfa1ca9dSYoshinobu Inoue hashkey_faddr = inp->inp_faddr.s_addr; 929cfa1ca9dSYoshinobu Inoue 930cfa1ca9dSYoshinobu Inoue pcbhash = &pcbinfo->hashbase[INP_PCBHASH(hashkey_faddr, 931c3229e05SDavid Greenman inp->inp_lport, inp->inp_fport, pcbinfo->hashmask)]; 93215bd2b43SDavid Greenman 933c3229e05SDavid Greenman pcbporthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(inp->inp_lport, 934c3229e05SDavid Greenman pcbinfo->porthashmask)]; 935c3229e05SDavid Greenman 936c3229e05SDavid Greenman /* 937c3229e05SDavid Greenman * Go through port list and look for a head for this lport. 938c3229e05SDavid Greenman */ 939c3229e05SDavid Greenman for (phd = pcbporthash->lh_first; phd != NULL; phd = phd->phd_hash.le_next) { 940c3229e05SDavid Greenman if (phd->phd_port == inp->inp_lport) 941c3229e05SDavid Greenman break; 942c3229e05SDavid Greenman } 943c3229e05SDavid Greenman /* 944c3229e05SDavid Greenman * If none exists, malloc one and tack it on. 945c3229e05SDavid Greenman */ 946c3229e05SDavid Greenman if (phd == NULL) { 947c3229e05SDavid Greenman MALLOC(phd, struct inpcbport *, sizeof(struct inpcbport), M_PCB, M_NOWAIT); 948c3229e05SDavid Greenman if (phd == NULL) { 949c3229e05SDavid Greenman return (ENOBUFS); /* XXX */ 950c3229e05SDavid Greenman } 951c3229e05SDavid Greenman phd->phd_port = inp->inp_lport; 952c3229e05SDavid Greenman LIST_INIT(&phd->phd_pcblist); 953c3229e05SDavid Greenman LIST_INSERT_HEAD(pcbporthash, phd, phd_hash); 954c3229e05SDavid Greenman } 955c3229e05SDavid Greenman inp->inp_phd = phd; 956c3229e05SDavid Greenman LIST_INSERT_HEAD(&phd->phd_pcblist, inp, inp_portlist); 957c3229e05SDavid Greenman LIST_INSERT_HEAD(pcbhash, inp, inp_hash); 958c3229e05SDavid Greenman return (0); 95915bd2b43SDavid Greenman } 96015bd2b43SDavid Greenman 961c3229e05SDavid Greenman /* 962c3229e05SDavid Greenman * Move PCB to the proper hash bucket when { faddr, fport } have been 963c3229e05SDavid Greenman * changed. NOTE: This does not handle the case of the lport changing (the 964c3229e05SDavid Greenman * hashed port list would have to be updated as well), so the lport must 965c3229e05SDavid Greenman * not change after in_pcbinshash() has been called. 966c3229e05SDavid Greenman */ 96715bd2b43SDavid Greenman void 96815bd2b43SDavid Greenman in_pcbrehash(inp) 96915bd2b43SDavid Greenman struct inpcb *inp; 97015bd2b43SDavid Greenman { 97115bd2b43SDavid Greenman struct inpcbhead *head; 972cfa1ca9dSYoshinobu Inoue u_int32_t hashkey_faddr; 97315bd2b43SDavid Greenman 974cfa1ca9dSYoshinobu Inoue #ifdef INET6 975cfa1ca9dSYoshinobu Inoue if (inp->inp_vflag & INP_IPV6) 976cfa1ca9dSYoshinobu Inoue hashkey_faddr = inp->in6p_faddr.s6_addr32[3] /* XXX */; 977cfa1ca9dSYoshinobu Inoue else 978cfa1ca9dSYoshinobu Inoue #endif /* INET6 */ 979cfa1ca9dSYoshinobu Inoue hashkey_faddr = inp->inp_faddr.s_addr; 980cfa1ca9dSYoshinobu Inoue 981cfa1ca9dSYoshinobu Inoue head = &inp->inp_pcbinfo->hashbase[INP_PCBHASH(hashkey_faddr, 982ddd79a97SDavid Greenman inp->inp_lport, inp->inp_fport, inp->inp_pcbinfo->hashmask)]; 98315bd2b43SDavid Greenman 984c3229e05SDavid Greenman LIST_REMOVE(inp, inp_hash); 98515bd2b43SDavid Greenman LIST_INSERT_HEAD(head, inp, inp_hash); 986c3229e05SDavid Greenman } 987c3229e05SDavid Greenman 988c3229e05SDavid Greenman /* 989c3229e05SDavid Greenman * Remove PCB from various lists. 990c3229e05SDavid Greenman */ 99176429de4SYoshinobu Inoue void 992c3229e05SDavid Greenman in_pcbremlists(inp) 993c3229e05SDavid Greenman struct inpcb *inp; 994c3229e05SDavid Greenman { 99598271db4SGarrett Wollman inp->inp_gencnt = ++inp->inp_pcbinfo->ipi_gencnt; 996c3229e05SDavid Greenman if (inp->inp_lport) { 997c3229e05SDavid Greenman struct inpcbport *phd = inp->inp_phd; 998c3229e05SDavid Greenman 999c3229e05SDavid Greenman LIST_REMOVE(inp, inp_hash); 1000c3229e05SDavid Greenman LIST_REMOVE(inp, inp_portlist); 1001c3229e05SDavid Greenman if (phd->phd_pcblist.lh_first == NULL) { 1002c3229e05SDavid Greenman LIST_REMOVE(phd, phd_hash); 1003c3229e05SDavid Greenman free(phd, M_PCB); 1004c3229e05SDavid Greenman } 1005c3229e05SDavid Greenman } 1006c3229e05SDavid Greenman LIST_REMOVE(inp, inp_list); 10073d4d47f3SGarrett Wollman inp->inp_pcbinfo->ipi_count--; 100815bd2b43SDavid Greenman } 100975c13541SPoul-Henning Kamp 101075c13541SPoul-Henning Kamp int 101175c13541SPoul-Henning Kamp prison_xinpcb(struct proc *p, struct inpcb *inp) 101275c13541SPoul-Henning Kamp { 101375c13541SPoul-Henning Kamp if (!p->p_prison) 101475c13541SPoul-Henning Kamp return (0); 101575c13541SPoul-Henning Kamp if (ntohl(inp->inp_laddr.s_addr) == p->p_prison->pr_ip) 101675c13541SPoul-Henning Kamp return (0); 101775c13541SPoul-Henning Kamp return (1); 101875c13541SPoul-Henning Kamp } 1019