xref: /freebsd/sys/netinet/in_pcb.c (revision bbd42ad0e5b62079f25c8d23408162ef065c1a0f)
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
34bbd42ad0SPeter Wemm  *	$Id: in_pcb.c,v 1.19 1996/05/31 05:11:22 peter 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/ioctl.h>
46df8bae1dSRodney W. Grimes #include <sys/errno.h>
47df8bae1dSRodney W. Grimes #include <sys/time.h>
48df8bae1dSRodney W. Grimes #include <sys/proc.h>
49101f9fc8SPeter Wemm #include <sys/kernel.h>
50101f9fc8SPeter Wemm #include <sys/sysctl.h>
51df8bae1dSRodney W. Grimes 
52df8bae1dSRodney W. Grimes #include <net/if.h>
53df8bae1dSRodney W. Grimes #include <net/route.h>
54df8bae1dSRodney W. Grimes 
55df8bae1dSRodney W. Grimes #include <netinet/in.h>
56df8bae1dSRodney W. Grimes #include <netinet/in_systm.h>
57df8bae1dSRodney W. Grimes #include <netinet/ip.h>
58df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h>
59df8bae1dSRodney W. Grimes #include <netinet/in_var.h>
60df8bae1dSRodney W. Grimes #include <netinet/ip_var.h>
61df8bae1dSRodney W. Grimes 
62df8bae1dSRodney W. Grimes struct	in_addr zeroin_addr;
63df8bae1dSRodney W. Grimes 
64bbd42ad0SPeter Wemm static void	 in_pcbinshash __P((struct inpcb *));
65bbd42ad0SPeter Wemm static void	 in_rtchange __P((struct inpcb *, int));
66bbd42ad0SPeter Wemm 
67101f9fc8SPeter Wemm /*
68101f9fc8SPeter Wemm  * These configure the range of local port addresses assigned to
69101f9fc8SPeter Wemm  * "unspecified" outgoing connections/packets/whatever.
70101f9fc8SPeter Wemm  */
71bbd42ad0SPeter Wemm static int ipport_lowfirstauto  = IPPORT_RESERVED - 1;	/* 1023 */
72bbd42ad0SPeter Wemm static int ipport_lowlastauto = IPPORT_RESERVEDSTART;	/* 600 */
7333b3ac06SPeter Wemm static int ipport_firstauto = IPPORT_RESERVED;		/* 1024 */
7433b3ac06SPeter Wemm static int ipport_lastauto  = IPPORT_USERRESERVED;	/* 5000 */
7533b3ac06SPeter Wemm static int ipport_hifirstauto = IPPORT_HIFIRSTAUTO;	/* 40000 */
7633b3ac06SPeter Wemm static int ipport_hilastauto  = IPPORT_HILASTAUTO;	/* 44999 */
77101f9fc8SPeter Wemm 
78bbd42ad0SPeter Wemm #define RANGECHK(var, min, max) \
79bbd42ad0SPeter Wemm 	if ((var) < (min)) { (var) = (min); } \
80bbd42ad0SPeter Wemm 	else if ((var) > (max)) { (var) = (max); }
81bbd42ad0SPeter Wemm 
82bbd42ad0SPeter Wemm static int
83bbd42ad0SPeter Wemm sysctl_net_ipport_check SYSCTL_HANDLER_ARGS
84bbd42ad0SPeter Wemm {
85bbd42ad0SPeter Wemm 	int error = sysctl_handle_int(oidp,
86bbd42ad0SPeter Wemm 		oidp->oid_arg1, oidp->oid_arg2, req);
87bbd42ad0SPeter Wemm 	if (!error) {
88bbd42ad0SPeter Wemm 		RANGECHK(ipport_lowfirstauto, 1, IPPORT_RESERVED - 1);
89bbd42ad0SPeter Wemm 		RANGECHK(ipport_lowlastauto, 1, IPPORT_RESERVED - 1);
90bbd42ad0SPeter Wemm 		RANGECHK(ipport_firstauto, IPPORT_RESERVED, USHRT_MAX);
91bbd42ad0SPeter Wemm 		RANGECHK(ipport_lastauto, IPPORT_RESERVED, USHRT_MAX);
92bbd42ad0SPeter Wemm 		RANGECHK(ipport_hifirstauto, IPPORT_RESERVED, USHRT_MAX);
93bbd42ad0SPeter Wemm 		RANGECHK(ipport_hilastauto, IPPORT_RESERVED, USHRT_MAX);
94bbd42ad0SPeter Wemm 	}
95bbd42ad0SPeter Wemm 	return error;
96bbd42ad0SPeter Wemm }
97bbd42ad0SPeter Wemm 
98bbd42ad0SPeter Wemm #undef RANGECHK
99bbd42ad0SPeter Wemm 
10033b3ac06SPeter Wemm SYSCTL_NODE(_net_inet_ip, IPPROTO_IP, portrange, CTLFLAG_RW, 0, "IP Ports");
10133b3ac06SPeter Wemm 
102bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowfirst, CTLTYPE_INT|CTLFLAG_RW,
103bbd42ad0SPeter Wemm 	   &ipport_lowfirstauto, 0, &sysctl_net_ipport_check, "I", "");
104bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowlast, CTLTYPE_INT|CTLFLAG_RW,
105bbd42ad0SPeter Wemm 	   &ipport_lowlastauto, 0, &sysctl_net_ipport_check, "I", "");
106bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, first, CTLTYPE_INT|CTLFLAG_RW,
107bbd42ad0SPeter Wemm 	   &ipport_firstauto, 0, &sysctl_net_ipport_check, "I", "");
108bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, last, CTLTYPE_INT|CTLFLAG_RW,
109bbd42ad0SPeter Wemm 	   &ipport_lastauto, 0, &sysctl_net_ipport_check, "I", "");
110bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hifirst, CTLTYPE_INT|CTLFLAG_RW,
111bbd42ad0SPeter Wemm 	   &ipport_hifirstauto, 0, &sysctl_net_ipport_check, "I", "");
112bbd42ad0SPeter Wemm SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hilast, CTLTYPE_INT|CTLFLAG_RW,
113bbd42ad0SPeter Wemm 	   &ipport_hilastauto, 0, &sysctl_net_ipport_check, "I", "");
1140312fbe9SPoul-Henning Kamp 
115df8bae1dSRodney W. Grimes int
11615bd2b43SDavid Greenman in_pcballoc(so, pcbinfo)
117df8bae1dSRodney W. Grimes 	struct socket *so;
11815bd2b43SDavid Greenman 	struct inpcbinfo *pcbinfo;
119df8bae1dSRodney W. Grimes {
120df8bae1dSRodney W. Grimes 	register struct inpcb *inp;
1217bc4aca7SDavid Greenman 	int s;
122df8bae1dSRodney W. Grimes 
1232c8fe19fSDavid Greenman 	MALLOC(inp, struct inpcb *, sizeof(*inp), M_PCB, M_NOWAIT);
124df8bae1dSRodney W. Grimes 	if (inp == NULL)
125df8bae1dSRodney W. Grimes 		return (ENOBUFS);
126df8bae1dSRodney W. Grimes 	bzero((caddr_t)inp, sizeof(*inp));
12715bd2b43SDavid Greenman 	inp->inp_pcbinfo = pcbinfo;
128df8bae1dSRodney W. Grimes 	inp->inp_socket = so;
1297bc4aca7SDavid Greenman 	s = splnet();
13015bd2b43SDavid Greenman 	LIST_INSERT_HEAD(pcbinfo->listhead, inp, inp_list);
13115bd2b43SDavid Greenman 	in_pcbinshash(inp);
1327bc4aca7SDavid Greenman 	splx(s);
133df8bae1dSRodney W. Grimes 	so->so_pcb = (caddr_t)inp;
134df8bae1dSRodney W. Grimes 	return (0);
135df8bae1dSRodney W. Grimes }
136df8bae1dSRodney W. Grimes 
137df8bae1dSRodney W. Grimes int
138df8bae1dSRodney W. Grimes in_pcbbind(inp, nam)
139df8bae1dSRodney W. Grimes 	register struct inpcb *inp;
140df8bae1dSRodney W. Grimes 	struct mbuf *nam;
141df8bae1dSRodney W. Grimes {
142df8bae1dSRodney W. Grimes 	register struct socket *so = inp->inp_socket;
14315bd2b43SDavid Greenman 	struct inpcbhead *head = inp->inp_pcbinfo->listhead;
14415bd2b43SDavid Greenman 	unsigned short *lastport = &inp->inp_pcbinfo->lastport;
14515bd2b43SDavid Greenman 	struct sockaddr_in *sin;
146df8bae1dSRodney W. Grimes 	struct proc *p = curproc;		/* XXX */
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 
151df8bae1dSRodney W. Grimes 	if (in_ifaddr == 0)
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))
158df8bae1dSRodney W. Grimes 		wild = INPLOOKUP_WILDCARD;
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);
194df8bae1dSRodney W. Grimes 			t = in_pcblookup(head, 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 
20533b3ac06SPeter Wemm 		if (inp->inp_flags & INP_HIGHPORT) {
20633b3ac06SPeter Wemm 			first = ipport_hifirstauto;	/* sysctl */
20733b3ac06SPeter Wemm 			last  = ipport_hilastauto;
20833b3ac06SPeter Wemm 		} else if (inp->inp_flags & INP_LOWPORT) {
20933b3ac06SPeter Wemm 			if (error = suser(p->p_ucred, &p->p_acflag))
21033b3ac06SPeter Wemm 				return (EACCES);
211bbd42ad0SPeter Wemm 			first = ipport_lowfirstauto;	/* 1023 */
212bbd42ad0SPeter Wemm 			last  = ipport_lowlastauto;	/* 600 */
21333b3ac06SPeter Wemm 		} else {
21433b3ac06SPeter Wemm 			first = ipport_firstauto;	/* sysctl */
21533b3ac06SPeter Wemm 			last  = ipport_lastauto;
21633b3ac06SPeter Wemm 		}
21733b3ac06SPeter Wemm 		/*
21833b3ac06SPeter Wemm 		 * Simple check to ensure all ports are not used up causing
21933b3ac06SPeter Wemm 		 * a deadlock here.
22033b3ac06SPeter Wemm 		 *
22133b3ac06SPeter Wemm 		 * We split the two cases (up and down) so that the direction
22233b3ac06SPeter Wemm 		 * is not being tested on each round of the loop.
22333b3ac06SPeter Wemm 		 */
22433b3ac06SPeter Wemm 		if (first > last) {
22533b3ac06SPeter Wemm 			/*
22633b3ac06SPeter Wemm 			 * counting down
22733b3ac06SPeter Wemm 			 */
22833b3ac06SPeter Wemm 			count = first - last;
22933b3ac06SPeter Wemm 
230df8bae1dSRodney W. Grimes 			do {
23133b3ac06SPeter Wemm 				if (count-- <= 0)	/* completely used? */
23233b3ac06SPeter Wemm 					return (EADDRNOTAVAIL);
23333b3ac06SPeter Wemm 				--*lastport;
23433b3ac06SPeter Wemm 				if (*lastport > first || *lastport < last)
23533b3ac06SPeter Wemm 					*lastport = first;
23615bd2b43SDavid Greenman 				lport = htons(*lastport);
237df8bae1dSRodney W. Grimes 			} while (in_pcblookup(head,
238df8bae1dSRodney W. Grimes 				 zeroin_addr, 0, inp->inp_laddr, lport, wild));
23933b3ac06SPeter Wemm 		} else {
24033b3ac06SPeter Wemm 			/*
24133b3ac06SPeter Wemm 			 * counting up
24233b3ac06SPeter Wemm 			 */
24333b3ac06SPeter Wemm 			count = last - first;
24433b3ac06SPeter Wemm 
24533b3ac06SPeter Wemm 			do {
24633b3ac06SPeter Wemm 				if (count-- <= 0)	/* completely used? */
24733b3ac06SPeter Wemm 					return (EADDRNOTAVAIL);
24833b3ac06SPeter Wemm 				++*lastport;
24933b3ac06SPeter Wemm 				if (*lastport < first || *lastport > last)
25033b3ac06SPeter Wemm 					*lastport = first;
25133b3ac06SPeter Wemm 				lport = htons(*lastport);
25233b3ac06SPeter Wemm 			} while (in_pcblookup(head,
25333b3ac06SPeter Wemm 				 zeroin_addr, 0, inp->inp_laddr, lport, wild));
25433b3ac06SPeter Wemm 		}
25533b3ac06SPeter Wemm 	}
256df8bae1dSRodney W. Grimes 	inp->inp_lport = lport;
25715bd2b43SDavid Greenman 	in_pcbrehash(inp);
258df8bae1dSRodney W. Grimes 	return (0);
259df8bae1dSRodney W. Grimes }
260df8bae1dSRodney W. Grimes 
261999f1343SGarrett Wollman /*
262999f1343SGarrett Wollman  *   Transform old in_pcbconnect() into an inner subroutine for new
263999f1343SGarrett Wollman  *   in_pcbconnect(): Do some validity-checking on the remote
264999f1343SGarrett Wollman  *   address (in mbuf 'nam') and then determine local host address
265999f1343SGarrett Wollman  *   (i.e., which interface) to use to access that remote host.
266999f1343SGarrett Wollman  *
267999f1343SGarrett Wollman  *   This preserves definition of in_pcbconnect(), while supporting a
268999f1343SGarrett Wollman  *   slightly different version for T/TCP.  (This is more than
269999f1343SGarrett Wollman  *   a bit of a kludge, but cleaning up the internal interfaces would
270999f1343SGarrett Wollman  *   have forced minor changes in every protocol).
271999f1343SGarrett Wollman  */
272999f1343SGarrett Wollman 
273999f1343SGarrett Wollman int
274999f1343SGarrett Wollman in_pcbladdr(inp, nam, plocal_sin)
275999f1343SGarrett Wollman 	register struct inpcb *inp;
276999f1343SGarrett Wollman 	struct mbuf *nam;
277999f1343SGarrett Wollman 	struct sockaddr_in **plocal_sin;
278999f1343SGarrett Wollman {
279df8bae1dSRodney W. Grimes 	struct in_ifaddr *ia;
280df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
281df8bae1dSRodney W. Grimes 
282df8bae1dSRodney W. Grimes 	if (nam->m_len != sizeof (*sin))
283df8bae1dSRodney W. Grimes 		return (EINVAL);
284df8bae1dSRodney W. Grimes 	if (sin->sin_family != AF_INET)
285df8bae1dSRodney W. Grimes 		return (EAFNOSUPPORT);
286df8bae1dSRodney W. Grimes 	if (sin->sin_port == 0)
287df8bae1dSRodney W. Grimes 		return (EADDRNOTAVAIL);
288df8bae1dSRodney W. Grimes 	if (in_ifaddr) {
289df8bae1dSRodney W. Grimes 		/*
290df8bae1dSRodney W. Grimes 		 * If the destination address is INADDR_ANY,
291df8bae1dSRodney W. Grimes 		 * use the primary local address.
292df8bae1dSRodney W. Grimes 		 * If the supplied address is INADDR_BROADCAST,
293df8bae1dSRodney W. Grimes 		 * and the primary interface supports broadcast,
294df8bae1dSRodney W. Grimes 		 * choose the broadcast address for that interface.
295df8bae1dSRodney W. Grimes 		 */
296df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
297df8bae1dSRodney W. Grimes #define sintosa(sin)	((struct sockaddr *)(sin))
298df8bae1dSRodney W. Grimes #define ifatoia(ifa)	((struct in_ifaddr *)(ifa))
299df8bae1dSRodney W. Grimes 		if (sin->sin_addr.s_addr == INADDR_ANY)
300df8bae1dSRodney W. Grimes 		    sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
301df8bae1dSRodney W. Grimes 		else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
302df8bae1dSRodney W. Grimes 		  (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST))
303df8bae1dSRodney W. Grimes 		    sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr;
304df8bae1dSRodney W. Grimes 	}
305df8bae1dSRodney W. Grimes 	if (inp->inp_laddr.s_addr == INADDR_ANY) {
306df8bae1dSRodney W. Grimes 		register struct route *ro;
307df8bae1dSRodney W. Grimes 
308df8bae1dSRodney W. Grimes 		ia = (struct in_ifaddr *)0;
309df8bae1dSRodney W. Grimes 		/*
310df8bae1dSRodney W. Grimes 		 * If route is known or can be allocated now,
311df8bae1dSRodney W. Grimes 		 * our src addr is taken from the i/f, else punt.
312df8bae1dSRodney W. Grimes 		 */
313df8bae1dSRodney W. Grimes 		ro = &inp->inp_route;
314df8bae1dSRodney W. Grimes 		if (ro->ro_rt &&
315df8bae1dSRodney W. Grimes 		    (satosin(&ro->ro_dst)->sin_addr.s_addr !=
316df8bae1dSRodney W. Grimes 			sin->sin_addr.s_addr ||
317df8bae1dSRodney W. Grimes 		    inp->inp_socket->so_options & SO_DONTROUTE)) {
318df8bae1dSRodney W. Grimes 			RTFREE(ro->ro_rt);
319df8bae1dSRodney W. Grimes 			ro->ro_rt = (struct rtentry *)0;
320df8bae1dSRodney W. Grimes 		}
321df8bae1dSRodney W. Grimes 		if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
322df8bae1dSRodney W. Grimes 		    (ro->ro_rt == (struct rtentry *)0 ||
323df8bae1dSRodney W. Grimes 		    ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
324df8bae1dSRodney W. Grimes 			/* No route yet, so try to acquire one */
325df8bae1dSRodney W. Grimes 			ro->ro_dst.sa_family = AF_INET;
326df8bae1dSRodney W. Grimes 			ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
327df8bae1dSRodney W. Grimes 			((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
328df8bae1dSRodney W. Grimes 				sin->sin_addr;
329df8bae1dSRodney W. Grimes 			rtalloc(ro);
330df8bae1dSRodney W. Grimes 		}
331df8bae1dSRodney W. Grimes 		/*
332df8bae1dSRodney W. Grimes 		 * If we found a route, use the address
333df8bae1dSRodney W. Grimes 		 * corresponding to the outgoing interface
334df8bae1dSRodney W. Grimes 		 * unless it is the loopback (in case a route
335df8bae1dSRodney W. Grimes 		 * to our address on another net goes to loopback).
336df8bae1dSRodney W. Grimes 		 */
337df8bae1dSRodney W. Grimes 		if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
338df8bae1dSRodney W. Grimes 			ia = ifatoia(ro->ro_rt->rt_ifa);
339df8bae1dSRodney W. Grimes 		if (ia == 0) {
340df8bae1dSRodney W. Grimes 			u_short fport = sin->sin_port;
341df8bae1dSRodney W. Grimes 
342df8bae1dSRodney W. Grimes 			sin->sin_port = 0;
343df8bae1dSRodney W. Grimes 			ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin)));
344df8bae1dSRodney W. Grimes 			if (ia == 0)
345df8bae1dSRodney W. Grimes 				ia = ifatoia(ifa_ifwithnet(sintosa(sin)));
346df8bae1dSRodney W. Grimes 			sin->sin_port = fport;
347df8bae1dSRodney W. Grimes 			if (ia == 0)
348df8bae1dSRodney W. Grimes 				ia = in_ifaddr;
349df8bae1dSRodney W. Grimes 			if (ia == 0)
350df8bae1dSRodney W. Grimes 				return (EADDRNOTAVAIL);
351df8bae1dSRodney W. Grimes 		}
352df8bae1dSRodney W. Grimes 		/*
353df8bae1dSRodney W. Grimes 		 * If the destination address is multicast and an outgoing
354df8bae1dSRodney W. Grimes 		 * interface has been set as a multicast option, use the
355df8bae1dSRodney W. Grimes 		 * address of that interface as our source address.
356df8bae1dSRodney W. Grimes 		 */
357df8bae1dSRodney W. Grimes 		if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
358df8bae1dSRodney W. Grimes 		    inp->inp_moptions != NULL) {
359df8bae1dSRodney W. Grimes 			struct ip_moptions *imo;
360df8bae1dSRodney W. Grimes 			struct ifnet *ifp;
361df8bae1dSRodney W. Grimes 
362df8bae1dSRodney W. Grimes 			imo = inp->inp_moptions;
363df8bae1dSRodney W. Grimes 			if (imo->imo_multicast_ifp != NULL) {
364df8bae1dSRodney W. Grimes 				ifp = imo->imo_multicast_ifp;
365df8bae1dSRodney W. Grimes 				for (ia = in_ifaddr; ia; ia = ia->ia_next)
366df8bae1dSRodney W. Grimes 					if (ia->ia_ifp == ifp)
367df8bae1dSRodney W. Grimes 						break;
368df8bae1dSRodney W. Grimes 				if (ia == 0)
369df8bae1dSRodney W. Grimes 					return (EADDRNOTAVAIL);
370df8bae1dSRodney W. Grimes 			}
371df8bae1dSRodney W. Grimes 		}
372999f1343SGarrett Wollman 	/*
373999f1343SGarrett Wollman 	 * Don't do pcblookup call here; return interface in plocal_sin
374999f1343SGarrett Wollman 	 * and exit to caller, that will do the lookup.
375999f1343SGarrett Wollman 	 */
376999f1343SGarrett Wollman 		*plocal_sin = &ia->ia_addr;
377999f1343SGarrett Wollman 
378999f1343SGarrett Wollman 	}
379999f1343SGarrett Wollman 	return(0);
380999f1343SGarrett Wollman }
381999f1343SGarrett Wollman 
382999f1343SGarrett Wollman /*
383999f1343SGarrett Wollman  * Outer subroutine:
384999f1343SGarrett Wollman  * Connect from a socket to a specified address.
385999f1343SGarrett Wollman  * Both address and port must be specified in argument sin.
386999f1343SGarrett Wollman  * If don't have a local address for this socket yet,
387999f1343SGarrett Wollman  * then pick one.
388999f1343SGarrett Wollman  */
389999f1343SGarrett Wollman int
390999f1343SGarrett Wollman in_pcbconnect(inp, nam)
391999f1343SGarrett Wollman 	register struct inpcb *inp;
392999f1343SGarrett Wollman 	struct mbuf *nam;
393999f1343SGarrett Wollman {
394999f1343SGarrett Wollman 	struct sockaddr_in *ifaddr;
395999f1343SGarrett Wollman 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
396999f1343SGarrett Wollman 	int error;
397999f1343SGarrett Wollman 
398999f1343SGarrett Wollman 	/*
399999f1343SGarrett Wollman 	 *   Call inner routine, to assign local interface address.
400999f1343SGarrett Wollman 	 */
401999f1343SGarrett Wollman 	if (error = in_pcbladdr(inp, nam, &ifaddr))
402999f1343SGarrett Wollman 		return(error);
403999f1343SGarrett Wollman 
4040d7b7d3eSDavid Greenman 	if (in_pcblookuphash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port,
405df8bae1dSRodney W. Grimes 	    inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
4060d7b7d3eSDavid Greenman 	    inp->inp_lport) != NULL)
407df8bae1dSRodney W. Grimes 		return (EADDRINUSE);
408df8bae1dSRodney W. Grimes 	if (inp->inp_laddr.s_addr == INADDR_ANY) {
409df8bae1dSRodney W. Grimes 		if (inp->inp_lport == 0)
410df8bae1dSRodney W. Grimes 			(void)in_pcbbind(inp, (struct mbuf *)0);
411df8bae1dSRodney W. Grimes 		inp->inp_laddr = ifaddr->sin_addr;
412df8bae1dSRodney W. Grimes 	}
413df8bae1dSRodney W. Grimes 	inp->inp_faddr = sin->sin_addr;
414df8bae1dSRodney W. Grimes 	inp->inp_fport = sin->sin_port;
41515bd2b43SDavid Greenman 	in_pcbrehash(inp);
416df8bae1dSRodney W. Grimes 	return (0);
417df8bae1dSRodney W. Grimes }
418df8bae1dSRodney W. Grimes 
41926f9a767SRodney W. Grimes void
420df8bae1dSRodney W. Grimes in_pcbdisconnect(inp)
421df8bae1dSRodney W. Grimes 	struct inpcb *inp;
422df8bae1dSRodney W. Grimes {
423df8bae1dSRodney W. Grimes 
424df8bae1dSRodney W. Grimes 	inp->inp_faddr.s_addr = INADDR_ANY;
425df8bae1dSRodney W. Grimes 	inp->inp_fport = 0;
42615bd2b43SDavid Greenman 	in_pcbrehash(inp);
427df8bae1dSRodney W. Grimes 	if (inp->inp_socket->so_state & SS_NOFDREF)
428df8bae1dSRodney W. Grimes 		in_pcbdetach(inp);
429df8bae1dSRodney W. Grimes }
430df8bae1dSRodney W. Grimes 
43126f9a767SRodney W. Grimes void
432df8bae1dSRodney W. Grimes in_pcbdetach(inp)
433df8bae1dSRodney W. Grimes 	struct inpcb *inp;
434df8bae1dSRodney W. Grimes {
435df8bae1dSRodney W. Grimes 	struct socket *so = inp->inp_socket;
4367bc4aca7SDavid Greenman 	int s;
437df8bae1dSRodney W. Grimes 
438df8bae1dSRodney W. Grimes 	so->so_pcb = 0;
439df8bae1dSRodney W. Grimes 	sofree(so);
440df8bae1dSRodney W. Grimes 	if (inp->inp_options)
441df8bae1dSRodney W. Grimes 		(void)m_free(inp->inp_options);
442df8bae1dSRodney W. Grimes 	if (inp->inp_route.ro_rt)
443df8bae1dSRodney W. Grimes 		rtfree(inp->inp_route.ro_rt);
444df8bae1dSRodney W. Grimes 	ip_freemoptions(inp->inp_moptions);
4457bc4aca7SDavid Greenman 	s = splnet();
44615bd2b43SDavid Greenman 	LIST_REMOVE(inp, inp_hash);
44715bd2b43SDavid Greenman 	LIST_REMOVE(inp, inp_list);
4487bc4aca7SDavid Greenman 	splx(s);
449df8bae1dSRodney W. Grimes 	FREE(inp, M_PCB);
450df8bae1dSRodney W. Grimes }
451df8bae1dSRodney W. Grimes 
45226f9a767SRodney W. Grimes void
453df8bae1dSRodney W. Grimes in_setsockaddr(inp, nam)
454df8bae1dSRodney W. Grimes 	register struct inpcb *inp;
455df8bae1dSRodney W. Grimes 	struct mbuf *nam;
456df8bae1dSRodney W. Grimes {
457df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
458df8bae1dSRodney W. Grimes 
459df8bae1dSRodney W. Grimes 	nam->m_len = sizeof (*sin);
460df8bae1dSRodney W. Grimes 	sin = mtod(nam, struct sockaddr_in *);
461df8bae1dSRodney W. Grimes 	bzero((caddr_t)sin, sizeof (*sin));
462df8bae1dSRodney W. Grimes 	sin->sin_family = AF_INET;
463df8bae1dSRodney W. Grimes 	sin->sin_len = sizeof(*sin);
464df8bae1dSRodney W. Grimes 	sin->sin_port = inp->inp_lport;
465df8bae1dSRodney W. Grimes 	sin->sin_addr = inp->inp_laddr;
466df8bae1dSRodney W. Grimes }
467df8bae1dSRodney W. Grimes 
46826f9a767SRodney W. Grimes void
469df8bae1dSRodney W. Grimes in_setpeeraddr(inp, nam)
470df8bae1dSRodney W. Grimes 	struct inpcb *inp;
471df8bae1dSRodney W. Grimes 	struct mbuf *nam;
472df8bae1dSRodney W. Grimes {
473df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
474df8bae1dSRodney W. Grimes 
475df8bae1dSRodney W. Grimes 	nam->m_len = sizeof (*sin);
476df8bae1dSRodney W. Grimes 	sin = mtod(nam, struct sockaddr_in *);
477df8bae1dSRodney W. Grimes 	bzero((caddr_t)sin, sizeof (*sin));
478df8bae1dSRodney W. Grimes 	sin->sin_family = AF_INET;
479df8bae1dSRodney W. Grimes 	sin->sin_len = sizeof(*sin);
480df8bae1dSRodney W. Grimes 	sin->sin_port = inp->inp_fport;
481df8bae1dSRodney W. Grimes 	sin->sin_addr = inp->inp_faddr;
482df8bae1dSRodney W. Grimes }
483df8bae1dSRodney W. Grimes 
484df8bae1dSRodney W. Grimes /*
485df8bae1dSRodney W. Grimes  * Pass some notification to all connections of a protocol
486df8bae1dSRodney W. Grimes  * associated with address dst.  The local address and/or port numbers
487df8bae1dSRodney W. Grimes  * may be specified to limit the search.  The "usual action" will be
488df8bae1dSRodney W. Grimes  * taken, depending on the ctlinput cmd.  The caller must filter any
489df8bae1dSRodney W. Grimes  * cmds that are uninteresting (e.g., no error in the map).
490df8bae1dSRodney W. Grimes  * Call the protocol specific routine (if any) to report
491df8bae1dSRodney W. Grimes  * any errors for each matching socket.
492df8bae1dSRodney W. Grimes  *
493df8bae1dSRodney W. Grimes  * Must be called at splnet.
494df8bae1dSRodney W. Grimes  */
49526f9a767SRodney W. Grimes void
496df8bae1dSRodney W. Grimes in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify)
49715bd2b43SDavid Greenman 	struct inpcbhead *head;
498df8bae1dSRodney W. Grimes 	struct sockaddr *dst;
499df8bae1dSRodney W. Grimes 	u_int fport_arg, lport_arg;
500df8bae1dSRodney W. Grimes 	struct in_addr laddr;
501df8bae1dSRodney W. Grimes 	int cmd;
502df8bae1dSRodney W. Grimes 	void (*notify) __P((struct inpcb *, int));
503df8bae1dSRodney W. Grimes {
504df8bae1dSRodney W. Grimes 	register struct inpcb *inp, *oinp;
505df8bae1dSRodney W. Grimes 	struct in_addr faddr;
506df8bae1dSRodney W. Grimes 	u_short fport = fport_arg, lport = lport_arg;
5077bc4aca7SDavid Greenman 	int errno, s;
508df8bae1dSRodney W. Grimes 
509df8bae1dSRodney W. Grimes 	if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
510df8bae1dSRodney W. Grimes 		return;
511df8bae1dSRodney W. Grimes 	faddr = ((struct sockaddr_in *)dst)->sin_addr;
512df8bae1dSRodney W. Grimes 	if (faddr.s_addr == INADDR_ANY)
513df8bae1dSRodney W. Grimes 		return;
514df8bae1dSRodney W. Grimes 
515df8bae1dSRodney W. Grimes 	/*
516df8bae1dSRodney W. Grimes 	 * Redirects go to all references to the destination,
517df8bae1dSRodney W. Grimes 	 * and use in_rtchange to invalidate the route cache.
518df8bae1dSRodney W. Grimes 	 * Dead host indications: notify all references to the destination.
519df8bae1dSRodney W. Grimes 	 * Otherwise, if we have knowledge of the local port and address,
520df8bae1dSRodney W. Grimes 	 * deliver only to that socket.
521df8bae1dSRodney W. Grimes 	 */
522df8bae1dSRodney W. Grimes 	if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
523df8bae1dSRodney W. Grimes 		fport = 0;
524df8bae1dSRodney W. Grimes 		lport = 0;
525df8bae1dSRodney W. Grimes 		laddr.s_addr = 0;
526df8bae1dSRodney W. Grimes 		if (cmd != PRC_HOSTDEAD)
527df8bae1dSRodney W. Grimes 			notify = in_rtchange;
528df8bae1dSRodney W. Grimes 	}
529df8bae1dSRodney W. Grimes 	errno = inetctlerrmap[cmd];
5307bc4aca7SDavid Greenman 	s = splnet();
53115bd2b43SDavid Greenman 	for (inp = head->lh_first; inp != NULL;) {
532df8bae1dSRodney W. Grimes 		if (inp->inp_faddr.s_addr != faddr.s_addr ||
533df8bae1dSRodney W. Grimes 		    inp->inp_socket == 0 ||
534df8bae1dSRodney W. Grimes 		    (lport && inp->inp_lport != lport) ||
535df8bae1dSRodney W. Grimes 		    (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) ||
536df8bae1dSRodney W. Grimes 		    (fport && inp->inp_fport != fport)) {
53715bd2b43SDavid Greenman 			inp = inp->inp_list.le_next;
538df8bae1dSRodney W. Grimes 			continue;
539df8bae1dSRodney W. Grimes 		}
540df8bae1dSRodney W. Grimes 		oinp = inp;
54115bd2b43SDavid Greenman 		inp = inp->inp_list.le_next;
542df8bae1dSRodney W. Grimes 		if (notify)
543df8bae1dSRodney W. Grimes 			(*notify)(oinp, errno);
544df8bae1dSRodney W. Grimes 	}
5457bc4aca7SDavid Greenman 	splx(s);
546df8bae1dSRodney W. Grimes }
547df8bae1dSRodney W. Grimes 
548df8bae1dSRodney W. Grimes /*
549df8bae1dSRodney W. Grimes  * Check for alternatives when higher level complains
550df8bae1dSRodney W. Grimes  * about service problems.  For now, invalidate cached
551df8bae1dSRodney W. Grimes  * routing information.  If the route was created dynamically
552df8bae1dSRodney W. Grimes  * (by a redirect), time to try a default gateway again.
553df8bae1dSRodney W. Grimes  */
55426f9a767SRodney W. Grimes void
555df8bae1dSRodney W. Grimes in_losing(inp)
556df8bae1dSRodney W. Grimes 	struct inpcb *inp;
557df8bae1dSRodney W. Grimes {
558df8bae1dSRodney W. Grimes 	register struct rtentry *rt;
559df8bae1dSRodney W. Grimes 	struct rt_addrinfo info;
560df8bae1dSRodney W. Grimes 
561df8bae1dSRodney W. Grimes 	if ((rt = inp->inp_route.ro_rt)) {
562df8bae1dSRodney W. Grimes 		inp->inp_route.ro_rt = 0;
563df8bae1dSRodney W. Grimes 		bzero((caddr_t)&info, sizeof(info));
564df8bae1dSRodney W. Grimes 		info.rti_info[RTAX_DST] =
565df8bae1dSRodney W. Grimes 			(struct sockaddr *)&inp->inp_route.ro_dst;
566df8bae1dSRodney W. Grimes 		info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
567df8bae1dSRodney W. Grimes 		info.rti_info[RTAX_NETMASK] = rt_mask(rt);
568df8bae1dSRodney W. Grimes 		rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
569df8bae1dSRodney W. Grimes 		if (rt->rt_flags & RTF_DYNAMIC)
570df8bae1dSRodney W. Grimes 			(void) rtrequest(RTM_DELETE, rt_key(rt),
571df8bae1dSRodney W. Grimes 				rt->rt_gateway, rt_mask(rt), rt->rt_flags,
572df8bae1dSRodney W. Grimes 				(struct rtentry **)0);
573df8bae1dSRodney W. Grimes 		else
574df8bae1dSRodney W. Grimes 		/*
575df8bae1dSRodney W. Grimes 		 * A new route can be allocated
576df8bae1dSRodney W. Grimes 		 * the next time output is attempted.
577df8bae1dSRodney W. Grimes 		 */
578df8bae1dSRodney W. Grimes 			rtfree(rt);
579df8bae1dSRodney W. Grimes 	}
580df8bae1dSRodney W. Grimes }
581df8bae1dSRodney W. Grimes 
582df8bae1dSRodney W. Grimes /*
583df8bae1dSRodney W. Grimes  * After a routing change, flush old routing
584df8bae1dSRodney W. Grimes  * and allocate a (hopefully) better one.
585df8bae1dSRodney W. Grimes  */
5860312fbe9SPoul-Henning Kamp static void
587df8bae1dSRodney W. Grimes in_rtchange(inp, errno)
588df8bae1dSRodney W. Grimes 	register struct inpcb *inp;
589df8bae1dSRodney W. Grimes 	int errno;
590df8bae1dSRodney W. Grimes {
591df8bae1dSRodney W. Grimes 	if (inp->inp_route.ro_rt) {
592df8bae1dSRodney W. Grimes 		rtfree(inp->inp_route.ro_rt);
593df8bae1dSRodney W. Grimes 		inp->inp_route.ro_rt = 0;
594df8bae1dSRodney W. Grimes 		/*
595df8bae1dSRodney W. Grimes 		 * A new route can be allocated the next time
596df8bae1dSRodney W. Grimes 		 * output is attempted.
597df8bae1dSRodney W. Grimes 		 */
598df8bae1dSRodney W. Grimes 	}
599df8bae1dSRodney W. Grimes }
600df8bae1dSRodney W. Grimes 
601df8bae1dSRodney W. Grimes struct inpcb *
602df8bae1dSRodney W. Grimes in_pcblookup(head, faddr, fport_arg, laddr, lport_arg, flags)
60315bd2b43SDavid Greenman 	struct inpcbhead *head;
604df8bae1dSRodney W. Grimes 	struct in_addr faddr, laddr;
605df8bae1dSRodney W. Grimes 	u_int fport_arg, lport_arg;
606df8bae1dSRodney W. Grimes 	int flags;
607df8bae1dSRodney W. Grimes {
6083dbdc25cSDavid Greenman 	register struct inpcb *inp, *match = NULL;
609df8bae1dSRodney W. Grimes 	int matchwild = 3, wildcard;
610df8bae1dSRodney W. Grimes 	u_short fport = fport_arg, lport = lport_arg;
6117bc4aca7SDavid Greenman 	int s;
6127bc4aca7SDavid Greenman 
6137bc4aca7SDavid Greenman 	s = splnet();
614df8bae1dSRodney W. Grimes 
61515bd2b43SDavid Greenman 	for (inp = head->lh_first; inp != NULL; inp = inp->inp_list.le_next) {
616df8bae1dSRodney W. Grimes 		if (inp->inp_lport != lport)
617df8bae1dSRodney W. Grimes 			continue;
618df8bae1dSRodney W. Grimes 		wildcard = 0;
619df8bae1dSRodney W. Grimes 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
620df8bae1dSRodney W. Grimes 			if (faddr.s_addr == INADDR_ANY)
621df8bae1dSRodney W. Grimes 				wildcard++;
622df8bae1dSRodney W. Grimes 			else if (inp->inp_faddr.s_addr != faddr.s_addr ||
623df8bae1dSRodney W. Grimes 			    inp->inp_fport != fport)
624df8bae1dSRodney W. Grimes 				continue;
625df8bae1dSRodney W. Grimes 		} else {
626df8bae1dSRodney W. Grimes 			if (faddr.s_addr != INADDR_ANY)
627df8bae1dSRodney W. Grimes 				wildcard++;
628df8bae1dSRodney W. Grimes 		}
62915bd2b43SDavid Greenman 		if (inp->inp_laddr.s_addr != INADDR_ANY) {
63015bd2b43SDavid Greenman 			if (laddr.s_addr == INADDR_ANY)
63115bd2b43SDavid Greenman 				wildcard++;
63215bd2b43SDavid Greenman 			else if (inp->inp_laddr.s_addr != laddr.s_addr)
63315bd2b43SDavid Greenman 				continue;
63415bd2b43SDavid Greenman 		} else {
63515bd2b43SDavid Greenman 			if (laddr.s_addr != INADDR_ANY)
63615bd2b43SDavid Greenman 				wildcard++;
63715bd2b43SDavid Greenman 		}
638df8bae1dSRodney W. Grimes 		if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
639df8bae1dSRodney W. Grimes 			continue;
640df8bae1dSRodney W. Grimes 		if (wildcard < matchwild) {
641df8bae1dSRodney W. Grimes 			match = inp;
642df8bae1dSRodney W. Grimes 			matchwild = wildcard;
6433dbdc25cSDavid Greenman 			if (matchwild == 0) {
644df8bae1dSRodney W. Grimes 				break;
645df8bae1dSRodney W. Grimes 			}
646df8bae1dSRodney W. Grimes 		}
6473dbdc25cSDavid Greenman 	}
6487bc4aca7SDavid Greenman 	splx(s);
649df8bae1dSRodney W. Grimes 	return (match);
650df8bae1dSRodney W. Grimes }
65115bd2b43SDavid Greenman 
65215bd2b43SDavid Greenman /*
65315bd2b43SDavid Greenman  * Lookup PCB in hash list.
65415bd2b43SDavid Greenman  */
65515bd2b43SDavid Greenman struct inpcb *
65615bd2b43SDavid Greenman in_pcblookuphash(pcbinfo, faddr, fport_arg, laddr, lport_arg)
65715bd2b43SDavid Greenman 	struct inpcbinfo *pcbinfo;
65815bd2b43SDavid Greenman 	struct in_addr faddr, laddr;
65915bd2b43SDavid Greenman 	u_int fport_arg, lport_arg;
66015bd2b43SDavid Greenman {
66115bd2b43SDavid Greenman 	struct inpcbhead *head;
66215bd2b43SDavid Greenman 	register struct inpcb *inp;
66315bd2b43SDavid Greenman 	u_short fport = fport_arg, lport = lport_arg;
6647bc4aca7SDavid Greenman 	int s;
66515bd2b43SDavid Greenman 
6667bc4aca7SDavid Greenman 	s = splnet();
66715bd2b43SDavid Greenman 	/*
66815bd2b43SDavid Greenman 	 * First look for an exact match.
66915bd2b43SDavid Greenman 	 */
67015bd2b43SDavid Greenman 	head = &pcbinfo->hashbase[(faddr.s_addr + lport + fport) % pcbinfo->hashsize];
67115bd2b43SDavid Greenman 
67215bd2b43SDavid Greenman 	for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) {
67315bd2b43SDavid Greenman 		if (inp->inp_faddr.s_addr != faddr.s_addr ||
67415bd2b43SDavid Greenman 		    inp->inp_fport != fport ||
67515bd2b43SDavid Greenman 		    inp->inp_lport != lport ||
67615bd2b43SDavid Greenman 		    inp->inp_laddr.s_addr != laddr.s_addr)
67715bd2b43SDavid Greenman 			continue;
67815bd2b43SDavid Greenman 		/*
67915bd2b43SDavid Greenman 		 * Move PCB to head of this hash chain so that it can be
68015bd2b43SDavid Greenman 		 * found more quickly in the future.
68115bd2b43SDavid Greenman 		 */
68215bd2b43SDavid Greenman 		if (inp != head->lh_first) {
68315bd2b43SDavid Greenman 			LIST_REMOVE(inp, inp_hash);
68415bd2b43SDavid Greenman 			LIST_INSERT_HEAD(head, inp, inp_hash);
68515bd2b43SDavid Greenman 		}
6860d7b7d3eSDavid Greenman 		break;
68715bd2b43SDavid Greenman 	}
6887bc4aca7SDavid Greenman 	splx(s);
6890d7b7d3eSDavid Greenman 	return (inp);
69015bd2b43SDavid Greenman }
69115bd2b43SDavid Greenman 
6927bc4aca7SDavid Greenman /*
6937bc4aca7SDavid Greenman  * Insert PCB into hash chain. Must be called at splnet.
6947bc4aca7SDavid Greenman  */
6950312fbe9SPoul-Henning Kamp static void
69615bd2b43SDavid Greenman in_pcbinshash(inp)
69715bd2b43SDavid Greenman 	struct inpcb *inp;
69815bd2b43SDavid Greenman {
69915bd2b43SDavid Greenman 	struct inpcbhead *head;
70015bd2b43SDavid Greenman 
70115bd2b43SDavid Greenman 	head = &inp->inp_pcbinfo->hashbase[(inp->inp_faddr.s_addr +
70215bd2b43SDavid Greenman 		inp->inp_lport + inp->inp_fport) % inp->inp_pcbinfo->hashsize];
70315bd2b43SDavid Greenman 
70415bd2b43SDavid Greenman 	LIST_INSERT_HEAD(head, inp, inp_hash);
70515bd2b43SDavid Greenman }
70615bd2b43SDavid Greenman 
70715bd2b43SDavid Greenman void
70815bd2b43SDavid Greenman in_pcbrehash(inp)
70915bd2b43SDavid Greenman 	struct inpcb *inp;
71015bd2b43SDavid Greenman {
71115bd2b43SDavid Greenman 	struct inpcbhead *head;
7127bc4aca7SDavid Greenman 	int s;
71315bd2b43SDavid Greenman 
7147bc4aca7SDavid Greenman 	s = splnet();
71515bd2b43SDavid Greenman 	LIST_REMOVE(inp, inp_hash);
71615bd2b43SDavid Greenman 
71715bd2b43SDavid Greenman 	head = &inp->inp_pcbinfo->hashbase[(inp->inp_faddr.s_addr +
71815bd2b43SDavid Greenman 		inp->inp_lport + inp->inp_fport) % inp->inp_pcbinfo->hashsize];
71915bd2b43SDavid Greenman 
72015bd2b43SDavid Greenman 	LIST_INSERT_HEAD(head, inp, inp_hash);
7217bc4aca7SDavid Greenman 	splx(s);
72215bd2b43SDavid Greenman }
723