xref: /freebsd/sys/netinet/in_pcb.c (revision fdc984f7b6553fdf6be18a63d619a983e7f5b033)
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
34fdc984f7STor Egge  *	$Id: in_pcb.c,v 1.32 1997/05/19 00:18:30 tegge Exp $
35df8bae1dSRodney W. Grimes  */
36df8bae1dSRodney W. Grimes 
37df8bae1dSRodney W. Grimes #include <sys/param.h>
382ee45d7dSDavid Greenman #include <sys/queue.h>
39df8bae1dSRodney W. Grimes #include <sys/systm.h>
40df8bae1dSRodney W. Grimes #include <sys/malloc.h>
41df8bae1dSRodney W. Grimes #include <sys/mbuf.h>
42df8bae1dSRodney W. Grimes #include <sys/protosw.h>
43df8bae1dSRodney W. Grimes #include <sys/socket.h>
44df8bae1dSRodney W. Grimes #include <sys/socketvar.h>
45df8bae1dSRodney W. Grimes #include <sys/errno.h>
46df8bae1dSRodney W. Grimes #include <sys/time.h>
47df8bae1dSRodney W. Grimes #include <sys/proc.h>
48101f9fc8SPeter Wemm #include <sys/kernel.h>
49101f9fc8SPeter Wemm #include <sys/sysctl.h>
50df8bae1dSRodney W. Grimes 
51df8bae1dSRodney W. Grimes #include <net/if.h>
52df8bae1dSRodney W. Grimes #include <net/route.h>
53df8bae1dSRodney W. Grimes 
54df8bae1dSRodney W. Grimes #include <netinet/in.h>
55df8bae1dSRodney W. Grimes #include <netinet/in_systm.h>
56df8bae1dSRodney W. Grimes #include <netinet/ip.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 
63bbd42ad0SPeter Wemm static void	 in_pcbinshash __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 */
7433b3ac06SPeter Wemm static int ipport_hifirstauto = IPPORT_HIFIRSTAUTO;	/* 40000 */
7533b3ac06SPeter Wemm static int ipport_hilastauto  = IPPORT_HILASTAUTO;	/* 44999 */
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 
114df8bae1dSRodney W. Grimes int
115a29f300eSGarrett Wollman in_pcballoc(so, pcbinfo, p)
116df8bae1dSRodney W. Grimes 	struct socket *so;
11715bd2b43SDavid Greenman 	struct inpcbinfo *pcbinfo;
118a29f300eSGarrett Wollman 	struct proc *p;
119df8bae1dSRodney W. Grimes {
120df8bae1dSRodney W. Grimes 	register struct inpcb *inp;
1217bc4aca7SDavid Greenman 	int s;
122df8bae1dSRodney W. Grimes 
123a29f300eSGarrett Wollman 	MALLOC(inp, struct inpcb *, sizeof(*inp), M_PCB,
124a29f300eSGarrett Wollman 	       p ? M_WAITOK : M_NOWAIT);
125df8bae1dSRodney W. Grimes 	if (inp == NULL)
126df8bae1dSRodney W. Grimes 		return (ENOBUFS);
127df8bae1dSRodney W. Grimes 	bzero((caddr_t)inp, sizeof(*inp));
12815bd2b43SDavid Greenman 	inp->inp_pcbinfo = pcbinfo;
129df8bae1dSRodney W. Grimes 	inp->inp_socket = so;
1307bc4aca7SDavid Greenman 	s = splnet();
13115bd2b43SDavid Greenman 	LIST_INSERT_HEAD(pcbinfo->listhead, inp, inp_list);
13215bd2b43SDavid Greenman 	in_pcbinshash(inp);
1337bc4aca7SDavid Greenman 	splx(s);
134df8bae1dSRodney W. Grimes 	so->so_pcb = (caddr_t)inp;
135df8bae1dSRodney W. Grimes 	return (0);
136df8bae1dSRodney W. Grimes }
137df8bae1dSRodney W. Grimes 
138df8bae1dSRodney W. Grimes int
139a29f300eSGarrett Wollman in_pcbbind(inp, nam, p)
140df8bae1dSRodney W. Grimes 	register struct inpcb *inp;
141df8bae1dSRodney W. Grimes 	struct mbuf *nam;
142a29f300eSGarrett Wollman 	struct proc *p;
143df8bae1dSRodney W. Grimes {
144df8bae1dSRodney W. Grimes 	register struct socket *so = inp->inp_socket;
14537bd2b30SPeter Wemm 	unsigned short *lastport;
14615bd2b43SDavid Greenman 	struct sockaddr_in *sin;
147df8bae1dSRodney W. Grimes 	u_short lport = 0;
148df8bae1dSRodney W. Grimes 	int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
149df8bae1dSRodney W. Grimes 	int error;
150df8bae1dSRodney W. Grimes 
15159562606SGarrett Wollman 	if (TAILQ_EMPTY(&in_ifaddrhead)) /* XXX broken! */
152df8bae1dSRodney W. Grimes 		return (EADDRNOTAVAIL);
153df8bae1dSRodney W. Grimes 	if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
154df8bae1dSRodney W. Grimes 		return (EINVAL);
155df8bae1dSRodney W. Grimes 	if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 &&
156df8bae1dSRodney W. Grimes 	    ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
157df8bae1dSRodney W. Grimes 	     (so->so_options & SO_ACCEPTCONN) == 0))
1586d6a026bSDavid Greenman 		wild = 1;
159df8bae1dSRodney W. Grimes 	if (nam) {
160df8bae1dSRodney W. Grimes 		sin = mtod(nam, struct sockaddr_in *);
161df8bae1dSRodney W. Grimes 		if (nam->m_len != sizeof (*sin))
162df8bae1dSRodney W. Grimes 			return (EINVAL);
163df8bae1dSRodney W. Grimes #ifdef notdef
164df8bae1dSRodney W. Grimes 		/*
165df8bae1dSRodney W. Grimes 		 * We should check the family, but old programs
166df8bae1dSRodney W. Grimes 		 * incorrectly fail to initialize it.
167df8bae1dSRodney W. Grimes 		 */
168df8bae1dSRodney W. Grimes 		if (sin->sin_family != AF_INET)
169df8bae1dSRodney W. Grimes 			return (EAFNOSUPPORT);
170df8bae1dSRodney W. Grimes #endif
171df8bae1dSRodney W. Grimes 		lport = sin->sin_port;
172df8bae1dSRodney W. Grimes 		if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
173df8bae1dSRodney W. Grimes 			/*
174df8bae1dSRodney W. Grimes 			 * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
175df8bae1dSRodney W. Grimes 			 * allow complete duplication of binding if
176df8bae1dSRodney W. Grimes 			 * SO_REUSEPORT is set, or if SO_REUSEADDR is set
177df8bae1dSRodney W. Grimes 			 * and a multicast address is bound on both
178df8bae1dSRodney W. Grimes 			 * new and duplicated sockets.
179df8bae1dSRodney W. Grimes 			 */
180df8bae1dSRodney W. Grimes 			if (so->so_options & SO_REUSEADDR)
181df8bae1dSRodney W. Grimes 				reuseport = SO_REUSEADDR|SO_REUSEPORT;
182df8bae1dSRodney W. Grimes 		} else if (sin->sin_addr.s_addr != INADDR_ANY) {
183df8bae1dSRodney W. Grimes 			sin->sin_port = 0;		/* yech... */
184df8bae1dSRodney W. Grimes 			if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
185df8bae1dSRodney W. Grimes 				return (EADDRNOTAVAIL);
186df8bae1dSRodney W. Grimes 		}
187df8bae1dSRodney W. Grimes 		if (lport) {
188df8bae1dSRodney W. Grimes 			struct inpcb *t;
189df8bae1dSRodney W. Grimes 
190df8bae1dSRodney W. Grimes 			/* GROSS */
191df8bae1dSRodney W. Grimes 			if (ntohs(lport) < IPPORT_RESERVED &&
192df8bae1dSRodney W. Grimes 			    (error = suser(p->p_ucred, &p->p_acflag)))
1932469dd60SGarrett Wollman 				return (EACCES);
1946d6a026bSDavid Greenman 			t = in_pcblookup(inp->inp_pcbinfo, zeroin_addr, 0,
195df8bae1dSRodney W. Grimes 			    sin->sin_addr, lport, wild);
196df8bae1dSRodney W. Grimes 			if (t && (reuseport & t->inp_socket->so_options) == 0)
197df8bae1dSRodney W. Grimes 				return (EADDRINUSE);
198df8bae1dSRodney W. Grimes 		}
199df8bae1dSRodney W. Grimes 		inp->inp_laddr = sin->sin_addr;
200df8bae1dSRodney W. Grimes 	}
20133b3ac06SPeter Wemm 	if (lport == 0) {
20233b3ac06SPeter Wemm 		ushort first, last;
20333b3ac06SPeter Wemm 		int count;
20433b3ac06SPeter Wemm 
205321a2846SPoul-Henning Kamp 		inp->inp_flags |= INP_ANONPORT;
206321a2846SPoul-Henning Kamp 
20733b3ac06SPeter Wemm 		if (inp->inp_flags & INP_HIGHPORT) {
20833b3ac06SPeter Wemm 			first = ipport_hifirstauto;	/* sysctl */
20933b3ac06SPeter Wemm 			last  = ipport_hilastauto;
21037bd2b30SPeter Wemm 			lastport = &inp->inp_pcbinfo->lasthi;
21133b3ac06SPeter Wemm 		} else if (inp->inp_flags & INP_LOWPORT) {
21233b3ac06SPeter Wemm 			if (error = suser(p->p_ucred, &p->p_acflag))
213a29f300eSGarrett Wollman 				return error;
214bbd42ad0SPeter Wemm 			first = ipport_lowfirstauto;	/* 1023 */
215bbd42ad0SPeter Wemm 			last  = ipport_lowlastauto;	/* 600 */
21637bd2b30SPeter Wemm 			lastport = &inp->inp_pcbinfo->lastlow;
21733b3ac06SPeter Wemm 		} else {
21833b3ac06SPeter Wemm 			first = ipport_firstauto;	/* sysctl */
21933b3ac06SPeter Wemm 			last  = ipport_lastauto;
22037bd2b30SPeter Wemm 			lastport = &inp->inp_pcbinfo->lastport;
22133b3ac06SPeter Wemm 		}
22233b3ac06SPeter Wemm 		/*
22333b3ac06SPeter Wemm 		 * Simple check to ensure all ports are not used up causing
22433b3ac06SPeter Wemm 		 * a deadlock here.
22533b3ac06SPeter Wemm 		 *
22633b3ac06SPeter Wemm 		 * We split the two cases (up and down) so that the direction
22733b3ac06SPeter Wemm 		 * is not being tested on each round of the loop.
22833b3ac06SPeter Wemm 		 */
22933b3ac06SPeter Wemm 		if (first > last) {
23033b3ac06SPeter Wemm 			/*
23133b3ac06SPeter Wemm 			 * counting down
23233b3ac06SPeter Wemm 			 */
23333b3ac06SPeter Wemm 			count = first - last;
23433b3ac06SPeter Wemm 
235df8bae1dSRodney W. Grimes 			do {
23633b3ac06SPeter Wemm 				if (count-- <= 0)	/* completely used? */
23733b3ac06SPeter Wemm 					return (EADDRNOTAVAIL);
23833b3ac06SPeter Wemm 				--*lastport;
23933b3ac06SPeter Wemm 				if (*lastport > first || *lastport < last)
24033b3ac06SPeter Wemm 					*lastport = first;
24115bd2b43SDavid Greenman 				lport = htons(*lastport);
2426d6a026bSDavid Greenman 			} while (in_pcblookup(inp->inp_pcbinfo,
243df8bae1dSRodney W. Grimes 				 zeroin_addr, 0, inp->inp_laddr, lport, wild));
24433b3ac06SPeter Wemm 		} else {
24533b3ac06SPeter Wemm 			/*
24633b3ac06SPeter Wemm 			 * counting up
24733b3ac06SPeter Wemm 			 */
24833b3ac06SPeter Wemm 			count = last - first;
24933b3ac06SPeter Wemm 
25033b3ac06SPeter Wemm 			do {
25133b3ac06SPeter Wemm 				if (count-- <= 0)	/* completely used? */
25233b3ac06SPeter Wemm 					return (EADDRNOTAVAIL);
25333b3ac06SPeter Wemm 				++*lastport;
25433b3ac06SPeter Wemm 				if (*lastport < first || *lastport > last)
25533b3ac06SPeter Wemm 					*lastport = first;
25633b3ac06SPeter Wemm 				lport = htons(*lastport);
2576d6a026bSDavid Greenman 			} while (in_pcblookup(inp->inp_pcbinfo,
25833b3ac06SPeter Wemm 				 zeroin_addr, 0, inp->inp_laddr, lport, wild));
25933b3ac06SPeter Wemm 		}
26033b3ac06SPeter Wemm 	}
261df8bae1dSRodney W. Grimes 	inp->inp_lport = lport;
26215bd2b43SDavid Greenman 	in_pcbrehash(inp);
263df8bae1dSRodney W. Grimes 	return (0);
264df8bae1dSRodney W. Grimes }
265df8bae1dSRodney W. Grimes 
266999f1343SGarrett Wollman /*
267999f1343SGarrett Wollman  *   Transform old in_pcbconnect() into an inner subroutine for new
268999f1343SGarrett Wollman  *   in_pcbconnect(): Do some validity-checking on the remote
269999f1343SGarrett Wollman  *   address (in mbuf 'nam') and then determine local host address
270999f1343SGarrett Wollman  *   (i.e., which interface) to use to access that remote host.
271999f1343SGarrett Wollman  *
272999f1343SGarrett Wollman  *   This preserves definition of in_pcbconnect(), while supporting a
273999f1343SGarrett Wollman  *   slightly different version for T/TCP.  (This is more than
274999f1343SGarrett Wollman  *   a bit of a kludge, but cleaning up the internal interfaces would
275999f1343SGarrett Wollman  *   have forced minor changes in every protocol).
276999f1343SGarrett Wollman  */
277999f1343SGarrett Wollman 
278999f1343SGarrett Wollman int
279999f1343SGarrett Wollman in_pcbladdr(inp, nam, plocal_sin)
280999f1343SGarrett Wollman 	register struct inpcb *inp;
281999f1343SGarrett Wollman 	struct mbuf *nam;
282999f1343SGarrett Wollman 	struct sockaddr_in **plocal_sin;
283999f1343SGarrett Wollman {
284df8bae1dSRodney W. Grimes 	struct in_ifaddr *ia;
285df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
286df8bae1dSRodney W. Grimes 
287df8bae1dSRodney W. Grimes 	if (nam->m_len != sizeof (*sin))
288df8bae1dSRodney W. Grimes 		return (EINVAL);
289df8bae1dSRodney W. Grimes 	if (sin->sin_family != AF_INET)
290df8bae1dSRodney W. Grimes 		return (EAFNOSUPPORT);
291df8bae1dSRodney W. Grimes 	if (sin->sin_port == 0)
292df8bae1dSRodney W. Grimes 		return (EADDRNOTAVAIL);
29359562606SGarrett Wollman 	if (!TAILQ_EMPTY(&in_ifaddrhead)) {
294df8bae1dSRodney W. Grimes 		/*
295df8bae1dSRodney W. Grimes 		 * If the destination address is INADDR_ANY,
296df8bae1dSRodney W. Grimes 		 * use the primary local address.
297df8bae1dSRodney W. Grimes 		 * If the supplied address is INADDR_BROADCAST,
298df8bae1dSRodney W. Grimes 		 * and the primary interface supports broadcast,
299df8bae1dSRodney W. Grimes 		 * choose the broadcast address for that interface.
300df8bae1dSRodney W. Grimes 		 */
301df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
302df8bae1dSRodney W. Grimes #define sintosa(sin)	((struct sockaddr *)(sin))
303df8bae1dSRodney W. Grimes #define ifatoia(ifa)	((struct in_ifaddr *)(ifa))
304df8bae1dSRodney W. Grimes 		if (sin->sin_addr.s_addr == INADDR_ANY)
30559562606SGarrett Wollman 		    sin->sin_addr = IA_SIN(in_ifaddrhead.tqh_first)->sin_addr;
306df8bae1dSRodney W. Grimes 		else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
30759562606SGarrett Wollman 		  (in_ifaddrhead.tqh_first->ia_ifp->if_flags & IFF_BROADCAST))
30859562606SGarrett Wollman 		    sin->sin_addr = satosin(&in_ifaddrhead.tqh_first->ia_broadaddr)->sin_addr;
309df8bae1dSRodney W. Grimes 	}
310df8bae1dSRodney W. Grimes 	if (inp->inp_laddr.s_addr == INADDR_ANY) {
311df8bae1dSRodney W. Grimes 		register struct route *ro;
312df8bae1dSRodney W. Grimes 
313df8bae1dSRodney W. Grimes 		ia = (struct in_ifaddr *)0;
314df8bae1dSRodney W. Grimes 		/*
315df8bae1dSRodney W. Grimes 		 * If route is known or can be allocated now,
316df8bae1dSRodney W. Grimes 		 * our src addr is taken from the i/f, else punt.
317df8bae1dSRodney W. Grimes 		 */
318df8bae1dSRodney W. Grimes 		ro = &inp->inp_route;
319df8bae1dSRodney W. Grimes 		if (ro->ro_rt &&
320df8bae1dSRodney W. Grimes 		    (satosin(&ro->ro_dst)->sin_addr.s_addr !=
321df8bae1dSRodney W. Grimes 			sin->sin_addr.s_addr ||
322df8bae1dSRodney W. Grimes 		    inp->inp_socket->so_options & SO_DONTROUTE)) {
323df8bae1dSRodney W. Grimes 			RTFREE(ro->ro_rt);
324df8bae1dSRodney W. Grimes 			ro->ro_rt = (struct rtentry *)0;
325df8bae1dSRodney W. Grimes 		}
326df8bae1dSRodney W. Grimes 		if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
327df8bae1dSRodney W. Grimes 		    (ro->ro_rt == (struct rtentry *)0 ||
328df8bae1dSRodney W. Grimes 		    ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
329df8bae1dSRodney W. Grimes 			/* No route yet, so try to acquire one */
330df8bae1dSRodney W. Grimes 			ro->ro_dst.sa_family = AF_INET;
331df8bae1dSRodney W. Grimes 			ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
332df8bae1dSRodney W. Grimes 			((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
333df8bae1dSRodney W. Grimes 				sin->sin_addr;
334df8bae1dSRodney W. Grimes 			rtalloc(ro);
335df8bae1dSRodney W. Grimes 		}
336df8bae1dSRodney W. Grimes 		/*
337df8bae1dSRodney W. Grimes 		 * If we found a route, use the address
338df8bae1dSRodney W. Grimes 		 * corresponding to the outgoing interface
339df8bae1dSRodney W. Grimes 		 * unless it is the loopback (in case a route
340df8bae1dSRodney W. Grimes 		 * to our address on another net goes to loopback).
341df8bae1dSRodney W. Grimes 		 */
342df8bae1dSRodney W. Grimes 		if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
343df8bae1dSRodney W. Grimes 			ia = ifatoia(ro->ro_rt->rt_ifa);
344df8bae1dSRodney W. Grimes 		if (ia == 0) {
345df8bae1dSRodney W. Grimes 			u_short fport = sin->sin_port;
346df8bae1dSRodney W. Grimes 
347df8bae1dSRodney W. Grimes 			sin->sin_port = 0;
348df8bae1dSRodney W. Grimes 			ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin)));
349df8bae1dSRodney W. Grimes 			if (ia == 0)
350df8bae1dSRodney W. Grimes 				ia = ifatoia(ifa_ifwithnet(sintosa(sin)));
351df8bae1dSRodney W. Grimes 			sin->sin_port = fport;
352df8bae1dSRodney W. Grimes 			if (ia == 0)
35359562606SGarrett Wollman 				ia = in_ifaddrhead.tqh_first;
354df8bae1dSRodney W. Grimes 			if (ia == 0)
355df8bae1dSRodney W. Grimes 				return (EADDRNOTAVAIL);
356df8bae1dSRodney W. Grimes 		}
357df8bae1dSRodney W. Grimes 		/*
358df8bae1dSRodney W. Grimes 		 * If the destination address is multicast and an outgoing
359df8bae1dSRodney W. Grimes 		 * interface has been set as a multicast option, use the
360df8bae1dSRodney W. Grimes 		 * address of that interface as our source address.
361df8bae1dSRodney W. Grimes 		 */
362df8bae1dSRodney W. Grimes 		if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
363df8bae1dSRodney W. Grimes 		    inp->inp_moptions != NULL) {
364df8bae1dSRodney W. Grimes 			struct ip_moptions *imo;
365df8bae1dSRodney W. Grimes 			struct ifnet *ifp;
366df8bae1dSRodney W. Grimes 
367df8bae1dSRodney W. Grimes 			imo = inp->inp_moptions;
368df8bae1dSRodney W. Grimes 			if (imo->imo_multicast_ifp != NULL) {
369df8bae1dSRodney W. Grimes 				ifp = imo->imo_multicast_ifp;
37059562606SGarrett Wollman 				for (ia = in_ifaddrhead.tqh_first; ia;
37159562606SGarrett Wollman 				     ia = ia->ia_link.tqe_next)
372df8bae1dSRodney W. Grimes 					if (ia->ia_ifp == ifp)
373df8bae1dSRodney W. Grimes 						break;
374df8bae1dSRodney W. Grimes 				if (ia == 0)
375df8bae1dSRodney W. Grimes 					return (EADDRNOTAVAIL);
376df8bae1dSRodney W. Grimes 			}
377df8bae1dSRodney W. Grimes 		}
378999f1343SGarrett Wollman 	/*
379999f1343SGarrett Wollman 	 * Don't do pcblookup call here; return interface in plocal_sin
380999f1343SGarrett Wollman 	 * and exit to caller, that will do the lookup.
381999f1343SGarrett Wollman 	 */
382999f1343SGarrett Wollman 		*plocal_sin = &ia->ia_addr;
383999f1343SGarrett Wollman 
384999f1343SGarrett Wollman 	}
385999f1343SGarrett Wollman 	return(0);
386999f1343SGarrett Wollman }
387999f1343SGarrett Wollman 
388999f1343SGarrett Wollman /*
389999f1343SGarrett Wollman  * Outer subroutine:
390999f1343SGarrett Wollman  * Connect from a socket to a specified address.
391999f1343SGarrett Wollman  * Both address and port must be specified in argument sin.
392999f1343SGarrett Wollman  * If don't have a local address for this socket yet,
393999f1343SGarrett Wollman  * then pick one.
394999f1343SGarrett Wollman  */
395999f1343SGarrett Wollman int
396a29f300eSGarrett Wollman in_pcbconnect(inp, nam, p)
397999f1343SGarrett Wollman 	register struct inpcb *inp;
398999f1343SGarrett Wollman 	struct mbuf *nam;
399a29f300eSGarrett Wollman 	struct proc *p;
400999f1343SGarrett Wollman {
401999f1343SGarrett Wollman 	struct sockaddr_in *ifaddr;
402999f1343SGarrett Wollman 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
403999f1343SGarrett Wollman 	int error;
404999f1343SGarrett Wollman 
405999f1343SGarrett Wollman 	/*
406999f1343SGarrett Wollman 	 *   Call inner routine, to assign local interface address.
407999f1343SGarrett Wollman 	 */
408999f1343SGarrett Wollman 	if (error = in_pcbladdr(inp, nam, &ifaddr))
409999f1343SGarrett Wollman 		return(error);
410999f1343SGarrett Wollman 
4110d7b7d3eSDavid Greenman 	if (in_pcblookuphash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port,
412df8bae1dSRodney W. Grimes 	    inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
4136d6a026bSDavid Greenman 	    inp->inp_lport, 0) != NULL)
414df8bae1dSRodney W. Grimes 		return (EADDRINUSE);
415df8bae1dSRodney W. Grimes 	if (inp->inp_laddr.s_addr == INADDR_ANY) {
416df8bae1dSRodney W. Grimes 		if (inp->inp_lport == 0)
417a29f300eSGarrett Wollman 			(void)in_pcbbind(inp, (struct mbuf *)0, p);
418df8bae1dSRodney W. Grimes 		inp->inp_laddr = ifaddr->sin_addr;
419df8bae1dSRodney W. Grimes 	}
420df8bae1dSRodney W. Grimes 	inp->inp_faddr = sin->sin_addr;
421df8bae1dSRodney W. Grimes 	inp->inp_fport = sin->sin_port;
42215bd2b43SDavid Greenman 	in_pcbrehash(inp);
423df8bae1dSRodney W. Grimes 	return (0);
424df8bae1dSRodney W. Grimes }
425df8bae1dSRodney W. Grimes 
42626f9a767SRodney W. Grimes void
427df8bae1dSRodney W. Grimes in_pcbdisconnect(inp)
428df8bae1dSRodney W. Grimes 	struct inpcb *inp;
429df8bae1dSRodney W. Grimes {
430df8bae1dSRodney W. Grimes 
431df8bae1dSRodney W. Grimes 	inp->inp_faddr.s_addr = INADDR_ANY;
432df8bae1dSRodney W. Grimes 	inp->inp_fport = 0;
43315bd2b43SDavid Greenman 	in_pcbrehash(inp);
434df8bae1dSRodney W. Grimes 	if (inp->inp_socket->so_state & SS_NOFDREF)
435df8bae1dSRodney W. Grimes 		in_pcbdetach(inp);
436df8bae1dSRodney W. Grimes }
437df8bae1dSRodney W. Grimes 
43826f9a767SRodney W. Grimes void
439df8bae1dSRodney W. Grimes in_pcbdetach(inp)
440df8bae1dSRodney W. Grimes 	struct inpcb *inp;
441df8bae1dSRodney W. Grimes {
442df8bae1dSRodney W. Grimes 	struct socket *so = inp->inp_socket;
4437bc4aca7SDavid Greenman 	int s;
444df8bae1dSRodney W. Grimes 
445df8bae1dSRodney W. Grimes 	so->so_pcb = 0;
446df8bae1dSRodney W. Grimes 	sofree(so);
447df8bae1dSRodney W. Grimes 	if (inp->inp_options)
448df8bae1dSRodney W. Grimes 		(void)m_free(inp->inp_options);
449df8bae1dSRodney W. Grimes 	if (inp->inp_route.ro_rt)
450df8bae1dSRodney W. Grimes 		rtfree(inp->inp_route.ro_rt);
451df8bae1dSRodney W. Grimes 	ip_freemoptions(inp->inp_moptions);
4527bc4aca7SDavid Greenman 	s = splnet();
45315bd2b43SDavid Greenman 	LIST_REMOVE(inp, inp_hash);
45415bd2b43SDavid Greenman 	LIST_REMOVE(inp, inp_list);
4557bc4aca7SDavid Greenman 	splx(s);
456df8bae1dSRodney W. Grimes 	FREE(inp, M_PCB);
457df8bae1dSRodney W. Grimes }
458df8bae1dSRodney W. Grimes 
459117bcae7SGarrett Wollman /*
460117bcae7SGarrett Wollman  * The calling convention of in_setsockaddr() and in_setpeeraddr() was
461117bcae7SGarrett Wollman  * modified to match the pru_sockaddr() and pru_peeraddr() entry points
462117bcae7SGarrett Wollman  * in struct pr_usrreqs, so that protocols can just reference then directly
463117bcae7SGarrett Wollman  * without the need for a wrapper function.  The socket must have a valid
464117bcae7SGarrett Wollman  * (i.e., non-nil) PCB, but it should be impossible to get an invalid one
465117bcae7SGarrett Wollman  * except through a kernel programming error, so it is acceptable to panic
466117bcae7SGarrett Wollman  * (or in this case trap) if the PCB is invalid.
467117bcae7SGarrett Wollman  */
468117bcae7SGarrett Wollman int
469117bcae7SGarrett Wollman in_setsockaddr(so, nam)
470117bcae7SGarrett Wollman 	struct socket *so;
471df8bae1dSRodney W. Grimes 	struct mbuf *nam;
472df8bae1dSRodney W. Grimes {
473fdc984f7STor Egge 	int s;
474fdc984f7STor Egge 	register struct inpcb *inp;
475df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
476df8bae1dSRodney W. Grimes 
477fdc984f7STor Egge 	s = splnet();
478fdc984f7STor Egge 	inp = sotoinpcb(so);
479db112f04STor Egge 	if (!inp) {
480db112f04STor Egge 		splx(s);
481db112f04STor Egge 		return EINVAL;
482db112f04STor Egge 	}
483df8bae1dSRodney W. Grimes 	nam->m_len = sizeof (*sin);
484df8bae1dSRodney W. Grimes 	sin = mtod(nam, struct sockaddr_in *);
485df8bae1dSRodney W. Grimes 	bzero((caddr_t)sin, sizeof (*sin));
486df8bae1dSRodney W. Grimes 	sin->sin_family = AF_INET;
487df8bae1dSRodney W. Grimes 	sin->sin_len = sizeof(*sin);
488df8bae1dSRodney W. Grimes 	sin->sin_port = inp->inp_lport;
489df8bae1dSRodney W. Grimes 	sin->sin_addr = inp->inp_laddr;
490db112f04STor Egge 	splx(s);
491117bcae7SGarrett Wollman 	return 0;
492df8bae1dSRodney W. Grimes }
493df8bae1dSRodney W. Grimes 
494117bcae7SGarrett Wollman int
495117bcae7SGarrett Wollman in_setpeeraddr(so, nam)
496117bcae7SGarrett Wollman 	struct socket *so;
497df8bae1dSRodney W. Grimes 	struct mbuf *nam;
498df8bae1dSRodney W. Grimes {
499fdc984f7STor Egge 	int s;
500fdc984f7STor Egge 	struct inpcb *inp;
501df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
502df8bae1dSRodney W. Grimes 
503fdc984f7STor Egge 	s = splnet();
504fdc984f7STor Egge 	inp = sotoinpcb(so);
505db112f04STor Egge 	if (!inp) {
506db112f04STor Egge 		splx(s);
507db112f04STor Egge 		return EINVAL;
508db112f04STor Egge 	}
509df8bae1dSRodney W. Grimes 	nam->m_len = sizeof (*sin);
510df8bae1dSRodney W. Grimes 	sin = mtod(nam, struct sockaddr_in *);
511df8bae1dSRodney W. Grimes 	bzero((caddr_t)sin, sizeof (*sin));
512df8bae1dSRodney W. Grimes 	sin->sin_family = AF_INET;
513df8bae1dSRodney W. Grimes 	sin->sin_len = sizeof(*sin);
514df8bae1dSRodney W. Grimes 	sin->sin_port = inp->inp_fport;
515df8bae1dSRodney W. Grimes 	sin->sin_addr = inp->inp_faddr;
516db112f04STor Egge 	splx(s);
517117bcae7SGarrett Wollman 	return 0;
518df8bae1dSRodney W. Grimes }
519df8bae1dSRodney W. Grimes 
520df8bae1dSRodney W. Grimes /*
521df8bae1dSRodney W. Grimes  * Pass some notification to all connections of a protocol
522df8bae1dSRodney W. Grimes  * associated with address dst.  The local address and/or port numbers
523df8bae1dSRodney W. Grimes  * may be specified to limit the search.  The "usual action" will be
524df8bae1dSRodney W. Grimes  * taken, depending on the ctlinput cmd.  The caller must filter any
525df8bae1dSRodney W. Grimes  * cmds that are uninteresting (e.g., no error in the map).
526df8bae1dSRodney W. Grimes  * Call the protocol specific routine (if any) to report
527df8bae1dSRodney W. Grimes  * any errors for each matching socket.
528df8bae1dSRodney W. Grimes  *
529df8bae1dSRodney W. Grimes  * Must be called at splnet.
530df8bae1dSRodney W. Grimes  */
53126f9a767SRodney W. Grimes void
532df8bae1dSRodney W. Grimes in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify)
53315bd2b43SDavid Greenman 	struct inpcbhead *head;
534df8bae1dSRodney W. Grimes 	struct sockaddr *dst;
535df8bae1dSRodney W. Grimes 	u_int fport_arg, lport_arg;
536df8bae1dSRodney W. Grimes 	struct in_addr laddr;
537df8bae1dSRodney W. Grimes 	int cmd;
538df8bae1dSRodney W. Grimes 	void (*notify) __P((struct inpcb *, int));
539df8bae1dSRodney W. Grimes {
540df8bae1dSRodney W. Grimes 	register struct inpcb *inp, *oinp;
541df8bae1dSRodney W. Grimes 	struct in_addr faddr;
542df8bae1dSRodney W. Grimes 	u_short fport = fport_arg, lport = lport_arg;
5437bc4aca7SDavid Greenman 	int errno, s;
544df8bae1dSRodney W. Grimes 
545df8bae1dSRodney W. Grimes 	if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
546df8bae1dSRodney W. Grimes 		return;
547df8bae1dSRodney W. Grimes 	faddr = ((struct sockaddr_in *)dst)->sin_addr;
548df8bae1dSRodney W. Grimes 	if (faddr.s_addr == INADDR_ANY)
549df8bae1dSRodney W. Grimes 		return;
550df8bae1dSRodney W. Grimes 
551df8bae1dSRodney W. Grimes 	/*
552df8bae1dSRodney W. Grimes 	 * Redirects go to all references to the destination,
553df8bae1dSRodney W. Grimes 	 * and use in_rtchange to invalidate the route cache.
554df8bae1dSRodney W. Grimes 	 * Dead host indications: notify all references to the destination.
555df8bae1dSRodney W. Grimes 	 * Otherwise, if we have knowledge of the local port and address,
556df8bae1dSRodney W. Grimes 	 * deliver only to that socket.
557df8bae1dSRodney W. Grimes 	 */
558df8bae1dSRodney W. Grimes 	if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
559df8bae1dSRodney W. Grimes 		fport = 0;
560df8bae1dSRodney W. Grimes 		lport = 0;
561df8bae1dSRodney W. Grimes 		laddr.s_addr = 0;
562df8bae1dSRodney W. Grimes 		if (cmd != PRC_HOSTDEAD)
563df8bae1dSRodney W. Grimes 			notify = in_rtchange;
564df8bae1dSRodney W. Grimes 	}
565df8bae1dSRodney W. Grimes 	errno = inetctlerrmap[cmd];
5667bc4aca7SDavid Greenman 	s = splnet();
56715bd2b43SDavid Greenman 	for (inp = head->lh_first; inp != NULL;) {
568df8bae1dSRodney W. Grimes 		if (inp->inp_faddr.s_addr != faddr.s_addr ||
569df8bae1dSRodney W. Grimes 		    inp->inp_socket == 0 ||
570df8bae1dSRodney W. Grimes 		    (lport && inp->inp_lport != lport) ||
571df8bae1dSRodney W. Grimes 		    (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) ||
572df8bae1dSRodney W. Grimes 		    (fport && inp->inp_fport != fport)) {
57315bd2b43SDavid Greenman 			inp = inp->inp_list.le_next;
574df8bae1dSRodney W. Grimes 			continue;
575df8bae1dSRodney W. Grimes 		}
576df8bae1dSRodney W. Grimes 		oinp = inp;
57715bd2b43SDavid Greenman 		inp = inp->inp_list.le_next;
578df8bae1dSRodney W. Grimes 		if (notify)
579df8bae1dSRodney W. Grimes 			(*notify)(oinp, errno);
580df8bae1dSRodney W. Grimes 	}
5817bc4aca7SDavid Greenman 	splx(s);
582df8bae1dSRodney W. Grimes }
583df8bae1dSRodney W. Grimes 
584df8bae1dSRodney W. Grimes /*
585df8bae1dSRodney W. Grimes  * Check for alternatives when higher level complains
586df8bae1dSRodney W. Grimes  * about service problems.  For now, invalidate cached
587df8bae1dSRodney W. Grimes  * routing information.  If the route was created dynamically
588df8bae1dSRodney W. Grimes  * (by a redirect), time to try a default gateway again.
589df8bae1dSRodney W. Grimes  */
59026f9a767SRodney W. Grimes void
591df8bae1dSRodney W. Grimes in_losing(inp)
592df8bae1dSRodney W. Grimes 	struct inpcb *inp;
593df8bae1dSRodney W. Grimes {
594df8bae1dSRodney W. Grimes 	register struct rtentry *rt;
595df8bae1dSRodney W. Grimes 	struct rt_addrinfo info;
596df8bae1dSRodney W. Grimes 
597df8bae1dSRodney W. Grimes 	if ((rt = inp->inp_route.ro_rt)) {
598df8bae1dSRodney W. Grimes 		inp->inp_route.ro_rt = 0;
599df8bae1dSRodney W. Grimes 		bzero((caddr_t)&info, sizeof(info));
600df8bae1dSRodney W. Grimes 		info.rti_info[RTAX_DST] =
601df8bae1dSRodney W. Grimes 			(struct sockaddr *)&inp->inp_route.ro_dst;
602df8bae1dSRodney W. Grimes 		info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
603df8bae1dSRodney W. Grimes 		info.rti_info[RTAX_NETMASK] = rt_mask(rt);
604df8bae1dSRodney W. Grimes 		rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
605df8bae1dSRodney W. Grimes 		if (rt->rt_flags & RTF_DYNAMIC)
606df8bae1dSRodney W. Grimes 			(void) rtrequest(RTM_DELETE, rt_key(rt),
607df8bae1dSRodney W. Grimes 				rt->rt_gateway, rt_mask(rt), rt->rt_flags,
608df8bae1dSRodney W. Grimes 				(struct rtentry **)0);
609df8bae1dSRodney W. Grimes 		else
610df8bae1dSRodney W. Grimes 		/*
611df8bae1dSRodney W. Grimes 		 * A new route can be allocated
612df8bae1dSRodney W. Grimes 		 * the next time output is attempted.
613df8bae1dSRodney W. Grimes 		 */
614df8bae1dSRodney W. Grimes 			rtfree(rt);
615df8bae1dSRodney W. Grimes 	}
616df8bae1dSRodney W. Grimes }
617df8bae1dSRodney W. Grimes 
618df8bae1dSRodney W. Grimes /*
619df8bae1dSRodney W. Grimes  * After a routing change, flush old routing
620df8bae1dSRodney W. Grimes  * and allocate a (hopefully) better one.
621df8bae1dSRodney W. Grimes  */
6220312fbe9SPoul-Henning Kamp static void
623df8bae1dSRodney W. Grimes in_rtchange(inp, errno)
624df8bae1dSRodney W. Grimes 	register struct inpcb *inp;
625df8bae1dSRodney W. Grimes 	int errno;
626df8bae1dSRodney W. Grimes {
627df8bae1dSRodney W. Grimes 	if (inp->inp_route.ro_rt) {
628df8bae1dSRodney W. Grimes 		rtfree(inp->inp_route.ro_rt);
629df8bae1dSRodney W. Grimes 		inp->inp_route.ro_rt = 0;
630df8bae1dSRodney W. Grimes 		/*
631df8bae1dSRodney W. Grimes 		 * A new route can be allocated the next time
632df8bae1dSRodney W. Grimes 		 * output is attempted.
633df8bae1dSRodney W. Grimes 		 */
634df8bae1dSRodney W. Grimes 	}
635df8bae1dSRodney W. Grimes }
636df8bae1dSRodney W. Grimes 
637df8bae1dSRodney W. Grimes struct inpcb *
6386d6a026bSDavid Greenman in_pcblookup(pcbinfo, faddr, fport_arg, laddr, lport_arg, wild_okay)
6396d6a026bSDavid Greenman 	struct inpcbinfo *pcbinfo;
640df8bae1dSRodney W. Grimes 	struct in_addr faddr, laddr;
641df8bae1dSRodney W. Grimes 	u_int fport_arg, lport_arg;
6426d6a026bSDavid Greenman 	int wild_okay;
643df8bae1dSRodney W. Grimes {
6443dbdc25cSDavid Greenman 	register struct inpcb *inp, *match = NULL;
645df8bae1dSRodney W. Grimes 	int matchwild = 3, wildcard;
646df8bae1dSRodney W. Grimes 	u_short fport = fport_arg, lport = lport_arg;
6477bc4aca7SDavid Greenman 	int s;
6487bc4aca7SDavid Greenman 
6497bc4aca7SDavid Greenman 	s = splnet();
650df8bae1dSRodney W. Grimes 
6516d6a026bSDavid Greenman 	for (inp = pcbinfo->listhead->lh_first; inp != NULL; inp = inp->inp_list.le_next) {
652df8bae1dSRodney W. Grimes 		if (inp->inp_lport != lport)
653df8bae1dSRodney W. Grimes 			continue;
654df8bae1dSRodney W. Grimes 		wildcard = 0;
655df8bae1dSRodney W. Grimes 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
656df8bae1dSRodney W. Grimes 			if (faddr.s_addr == INADDR_ANY)
657df8bae1dSRodney W. Grimes 				wildcard++;
658df8bae1dSRodney W. Grimes 			else if (inp->inp_faddr.s_addr != faddr.s_addr ||
659df8bae1dSRodney W. Grimes 			    inp->inp_fport != fport)
660df8bae1dSRodney W. Grimes 				continue;
661df8bae1dSRodney W. Grimes 		} else {
662df8bae1dSRodney W. Grimes 			if (faddr.s_addr != INADDR_ANY)
663df8bae1dSRodney W. Grimes 				wildcard++;
664df8bae1dSRodney W. Grimes 		}
66515bd2b43SDavid Greenman 		if (inp->inp_laddr.s_addr != INADDR_ANY) {
66615bd2b43SDavid Greenman 			if (laddr.s_addr == INADDR_ANY)
66715bd2b43SDavid Greenman 				wildcard++;
66815bd2b43SDavid Greenman 			else if (inp->inp_laddr.s_addr != laddr.s_addr)
66915bd2b43SDavid Greenman 				continue;
67015bd2b43SDavid Greenman 		} else {
67115bd2b43SDavid Greenman 			if (laddr.s_addr != INADDR_ANY)
67215bd2b43SDavid Greenman 				wildcard++;
67315bd2b43SDavid Greenman 		}
6746d6a026bSDavid Greenman 		if (wildcard && wild_okay == 0)
675df8bae1dSRodney W. Grimes 			continue;
676df8bae1dSRodney W. Grimes 		if (wildcard < matchwild) {
677df8bae1dSRodney W. Grimes 			match = inp;
678df8bae1dSRodney W. Grimes 			matchwild = wildcard;
6793dbdc25cSDavid Greenman 			if (matchwild == 0) {
680df8bae1dSRodney W. Grimes 				break;
681df8bae1dSRodney W. Grimes 			}
682df8bae1dSRodney W. Grimes 		}
6833dbdc25cSDavid Greenman 	}
6847bc4aca7SDavid Greenman 	splx(s);
685df8bae1dSRodney W. Grimes 	return (match);
686df8bae1dSRodney W. Grimes }
68715bd2b43SDavid Greenman 
68815bd2b43SDavid Greenman /*
68915bd2b43SDavid Greenman  * Lookup PCB in hash list.
69015bd2b43SDavid Greenman  */
69115bd2b43SDavid Greenman struct inpcb *
6926d6a026bSDavid Greenman in_pcblookuphash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard)
69315bd2b43SDavid Greenman 	struct inpcbinfo *pcbinfo;
69415bd2b43SDavid Greenman 	struct in_addr faddr, laddr;
69515bd2b43SDavid Greenman 	u_int fport_arg, lport_arg;
6966d6a026bSDavid Greenman 	int wildcard;
69715bd2b43SDavid Greenman {
69815bd2b43SDavid Greenman 	struct inpcbhead *head;
69915bd2b43SDavid Greenman 	register struct inpcb *inp;
70015bd2b43SDavid Greenman 	u_short fport = fport_arg, lport = lport_arg;
7017bc4aca7SDavid Greenman 	int s;
70215bd2b43SDavid Greenman 
7037bc4aca7SDavid Greenman 	s = splnet();
70415bd2b43SDavid Greenman 	/*
70515bd2b43SDavid Greenman 	 * First look for an exact match.
70615bd2b43SDavid Greenman 	 */
707ddd79a97SDavid Greenman 	head = &pcbinfo->hashbase[INP_PCBHASH(faddr.s_addr, lport, fport, pcbinfo->hashmask)];
70815bd2b43SDavid Greenman 	for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) {
7096d6a026bSDavid Greenman 		if (inp->inp_faddr.s_addr == faddr.s_addr &&
710ca98b82cSDavid Greenman 		    inp->inp_laddr.s_addr == laddr.s_addr &&
711ca98b82cSDavid Greenman 		    inp->inp_fport == fport &&
712ca98b82cSDavid Greenman 		    inp->inp_lport == lport)
7136d6a026bSDavid Greenman 			goto found;
7146d6a026bSDavid Greenman 	}
7156d6a026bSDavid Greenman 	if (wildcard) {
7166d6a026bSDavid Greenman 		struct inpcb *local_wild = NULL;
7176d6a026bSDavid Greenman 
718ddd79a97SDavid Greenman 		head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)];
7196d6a026bSDavid Greenman 		for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) {
7206d6a026bSDavid Greenman 			if (inp->inp_faddr.s_addr == INADDR_ANY &&
7216d6a026bSDavid Greenman 			    inp->inp_fport == 0 && inp->inp_lport == lport) {
7226d6a026bSDavid Greenman 				if (inp->inp_laddr.s_addr == laddr.s_addr)
7236d6a026bSDavid Greenman 					goto found;
7246d6a026bSDavid Greenman 				else if (inp->inp_laddr.s_addr == INADDR_ANY)
7256d6a026bSDavid Greenman 					local_wild = inp;
7266d6a026bSDavid Greenman 			}
7276d6a026bSDavid Greenman 		}
7286d6a026bSDavid Greenman 		if (local_wild != NULL) {
7296d6a026bSDavid Greenman 			inp = local_wild;
7306d6a026bSDavid Greenman 			goto found;
7316d6a026bSDavid Greenman 		}
7326d6a026bSDavid Greenman 	}
7336d6a026bSDavid Greenman 	splx(s);
7346d6a026bSDavid Greenman 	return (NULL);
7356d6a026bSDavid Greenman 
7366d6a026bSDavid Greenman found:
73715bd2b43SDavid Greenman 	/*
73815bd2b43SDavid Greenman 	 * Move PCB to head of this hash chain so that it can be
73915bd2b43SDavid Greenman 	 * found more quickly in the future.
7406d6a026bSDavid Greenman 	 * XXX - this is a pessimization on machines with few
7416d6a026bSDavid Greenman 	 * concurrent connections.
74215bd2b43SDavid Greenman 	 */
74315bd2b43SDavid Greenman 	if (inp != head->lh_first) {
74415bd2b43SDavid Greenman 		LIST_REMOVE(inp, inp_hash);
74515bd2b43SDavid Greenman 		LIST_INSERT_HEAD(head, inp, inp_hash);
74615bd2b43SDavid Greenman 	}
7477bc4aca7SDavid Greenman 	splx(s);
7480d7b7d3eSDavid Greenman 	return (inp);
74915bd2b43SDavid Greenman }
75015bd2b43SDavid Greenman 
7517bc4aca7SDavid Greenman /*
7527bc4aca7SDavid Greenman  * Insert PCB into hash chain. Must be called at splnet.
7537bc4aca7SDavid Greenman  */
7540312fbe9SPoul-Henning Kamp static void
75515bd2b43SDavid Greenman in_pcbinshash(inp)
75615bd2b43SDavid Greenman 	struct inpcb *inp;
75715bd2b43SDavid Greenman {
75815bd2b43SDavid Greenman 	struct inpcbhead *head;
75915bd2b43SDavid Greenman 
760ddd79a97SDavid Greenman 	head = &inp->inp_pcbinfo->hashbase[INP_PCBHASH(inp->inp_faddr.s_addr,
761ddd79a97SDavid Greenman 		 inp->inp_lport, inp->inp_fport, inp->inp_pcbinfo->hashmask)];
76215bd2b43SDavid Greenman 
76315bd2b43SDavid Greenman 	LIST_INSERT_HEAD(head, inp, inp_hash);
76415bd2b43SDavid Greenman }
76515bd2b43SDavid Greenman 
76615bd2b43SDavid Greenman void
76715bd2b43SDavid Greenman in_pcbrehash(inp)
76815bd2b43SDavid Greenman 	struct inpcb *inp;
76915bd2b43SDavid Greenman {
77015bd2b43SDavid Greenman 	struct inpcbhead *head;
7717bc4aca7SDavid Greenman 	int s;
77215bd2b43SDavid Greenman 
7737bc4aca7SDavid Greenman 	s = splnet();
77415bd2b43SDavid Greenman 	LIST_REMOVE(inp, inp_hash);
77515bd2b43SDavid Greenman 
776ddd79a97SDavid Greenman 	head = &inp->inp_pcbinfo->hashbase[INP_PCBHASH(inp->inp_faddr.s_addr,
777ddd79a97SDavid Greenman 		inp->inp_lport, inp->inp_fport, inp->inp_pcbinfo->hashmask)];
77815bd2b43SDavid Greenman 
77915bd2b43SDavid Greenman 	LIST_INSERT_HEAD(head, inp, inp_hash);
7807bc4aca7SDavid Greenman 	splx(s);
78115bd2b43SDavid Greenman }
782