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