xref: /freebsd/sys/netinet/in_pcb.c (revision 2469dd60b7a4946f7e0bd132166329af045a7324)
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
342469dd60SGarrett Wollman  *	$Id: in_pcb.c,v 1.12 1995/05/30 08:09:28 rgrimes 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/ioctl.h>
45df8bae1dSRodney W. Grimes #include <sys/errno.h>
46df8bae1dSRodney W. Grimes #include <sys/time.h>
47df8bae1dSRodney W. Grimes #include <sys/proc.h>
4815bd2b43SDavid Greenman #include <sys/queue.h>
49df8bae1dSRodney W. Grimes 
50df8bae1dSRodney W. Grimes #include <net/if.h>
51df8bae1dSRodney W. Grimes #include <net/route.h>
52df8bae1dSRodney W. Grimes 
53df8bae1dSRodney W. Grimes #include <netinet/in.h>
54df8bae1dSRodney W. Grimes #include <netinet/in_systm.h>
55df8bae1dSRodney W. Grimes #include <netinet/ip.h>
56df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h>
57df8bae1dSRodney W. Grimes #include <netinet/in_var.h>
58df8bae1dSRodney W. Grimes #include <netinet/ip_var.h>
59df8bae1dSRodney W. Grimes 
60df8bae1dSRodney W. Grimes struct	in_addr zeroin_addr;
61df8bae1dSRodney W. Grimes 
62df8bae1dSRodney W. Grimes int
6315bd2b43SDavid Greenman in_pcballoc(so, pcbinfo)
64df8bae1dSRodney W. Grimes 	struct socket *so;
6515bd2b43SDavid Greenman 	struct inpcbinfo *pcbinfo;
66df8bae1dSRodney W. Grimes {
67df8bae1dSRodney W. Grimes 	register struct inpcb *inp;
687bc4aca7SDavid Greenman 	int s;
69df8bae1dSRodney W. Grimes 
702c8fe19fSDavid Greenman 	MALLOC(inp, struct inpcb *, sizeof(*inp), M_PCB, M_NOWAIT);
71df8bae1dSRodney W. Grimes 	if (inp == NULL)
72df8bae1dSRodney W. Grimes 		return (ENOBUFS);
73df8bae1dSRodney W. Grimes 	bzero((caddr_t)inp, sizeof(*inp));
7415bd2b43SDavid Greenman 	inp->inp_pcbinfo = pcbinfo;
75df8bae1dSRodney W. Grimes 	inp->inp_socket = so;
767bc4aca7SDavid Greenman 	s = splnet();
7715bd2b43SDavid Greenman 	LIST_INSERT_HEAD(pcbinfo->listhead, inp, inp_list);
7815bd2b43SDavid Greenman 	in_pcbinshash(inp);
797bc4aca7SDavid Greenman 	splx(s);
80df8bae1dSRodney W. Grimes 	so->so_pcb = (caddr_t)inp;
81df8bae1dSRodney W. Grimes 	return (0);
82df8bae1dSRodney W. Grimes }
83df8bae1dSRodney W. Grimes 
84df8bae1dSRodney W. Grimes int
85df8bae1dSRodney W. Grimes in_pcbbind(inp, nam)
86df8bae1dSRodney W. Grimes 	register struct inpcb *inp;
87df8bae1dSRodney W. Grimes 	struct mbuf *nam;
88df8bae1dSRodney W. Grimes {
89df8bae1dSRodney W. Grimes 	register struct socket *so = inp->inp_socket;
9015bd2b43SDavid Greenman 	struct inpcbhead *head = inp->inp_pcbinfo->listhead;
9115bd2b43SDavid Greenman 	unsigned short *lastport = &inp->inp_pcbinfo->lastport;
9215bd2b43SDavid Greenman 	struct sockaddr_in *sin;
93df8bae1dSRodney W. Grimes 	struct proc *p = curproc;		/* XXX */
94df8bae1dSRodney W. Grimes 	u_short lport = 0;
95df8bae1dSRodney W. Grimes 	int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
96df8bae1dSRodney W. Grimes 	int error;
97df8bae1dSRodney W. Grimes 
98df8bae1dSRodney W. Grimes 	if (in_ifaddr == 0)
99df8bae1dSRodney W. Grimes 		return (EADDRNOTAVAIL);
100df8bae1dSRodney W. Grimes 	if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
101df8bae1dSRodney W. Grimes 		return (EINVAL);
102df8bae1dSRodney W. Grimes 	if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 &&
103df8bae1dSRodney W. Grimes 	    ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
104df8bae1dSRodney W. Grimes 	     (so->so_options & SO_ACCEPTCONN) == 0))
105df8bae1dSRodney W. Grimes 		wild = INPLOOKUP_WILDCARD;
106df8bae1dSRodney W. Grimes 	if (nam) {
107df8bae1dSRodney W. Grimes 		sin = mtod(nam, struct sockaddr_in *);
108df8bae1dSRodney W. Grimes 		if (nam->m_len != sizeof (*sin))
109df8bae1dSRodney W. Grimes 			return (EINVAL);
110df8bae1dSRodney W. Grimes #ifdef notdef
111df8bae1dSRodney W. Grimes 		/*
112df8bae1dSRodney W. Grimes 		 * We should check the family, but old programs
113df8bae1dSRodney W. Grimes 		 * incorrectly fail to initialize it.
114df8bae1dSRodney W. Grimes 		 */
115df8bae1dSRodney W. Grimes 		if (sin->sin_family != AF_INET)
116df8bae1dSRodney W. Grimes 			return (EAFNOSUPPORT);
117df8bae1dSRodney W. Grimes #endif
118df8bae1dSRodney W. Grimes 		lport = sin->sin_port;
119df8bae1dSRodney W. Grimes 		if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
120df8bae1dSRodney W. Grimes 			/*
121df8bae1dSRodney W. Grimes 			 * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
122df8bae1dSRodney W. Grimes 			 * allow complete duplication of binding if
123df8bae1dSRodney W. Grimes 			 * SO_REUSEPORT is set, or if SO_REUSEADDR is set
124df8bae1dSRodney W. Grimes 			 * and a multicast address is bound on both
125df8bae1dSRodney W. Grimes 			 * new and duplicated sockets.
126df8bae1dSRodney W. Grimes 			 */
127df8bae1dSRodney W. Grimes 			if (so->so_options & SO_REUSEADDR)
128df8bae1dSRodney W. Grimes 				reuseport = SO_REUSEADDR|SO_REUSEPORT;
129df8bae1dSRodney W. Grimes 		} else if (sin->sin_addr.s_addr != INADDR_ANY) {
130df8bae1dSRodney W. Grimes 			sin->sin_port = 0;		/* yech... */
131df8bae1dSRodney W. Grimes 			if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
132df8bae1dSRodney W. Grimes 				return (EADDRNOTAVAIL);
133df8bae1dSRodney W. Grimes 		}
134df8bae1dSRodney W. Grimes 		if (lport) {
135df8bae1dSRodney W. Grimes 			struct inpcb *t;
136df8bae1dSRodney W. Grimes 
137df8bae1dSRodney W. Grimes 			/* GROSS */
138df8bae1dSRodney W. Grimes 			if (ntohs(lport) < IPPORT_RESERVED &&
139df8bae1dSRodney W. Grimes 			    (error = suser(p->p_ucred, &p->p_acflag)))
1402469dd60SGarrett Wollman 				return (EACCES);
141df8bae1dSRodney W. Grimes 			t = in_pcblookup(head, zeroin_addr, 0,
142df8bae1dSRodney W. Grimes 			    sin->sin_addr, lport, wild);
143df8bae1dSRodney W. Grimes 			if (t && (reuseport & t->inp_socket->so_options) == 0)
144df8bae1dSRodney W. Grimes 				return (EADDRINUSE);
145df8bae1dSRodney W. Grimes 		}
146df8bae1dSRodney W. Grimes 		inp->inp_laddr = sin->sin_addr;
147df8bae1dSRodney W. Grimes 	}
148df8bae1dSRodney W. Grimes 	if (lport == 0)
149df8bae1dSRodney W. Grimes 		do {
15015bd2b43SDavid Greenman 			++*lastport;
15115bd2b43SDavid Greenman 			if (*lastport < IPPORT_RESERVED ||
15215bd2b43SDavid Greenman 			    *lastport > IPPORT_USERRESERVED)
15315bd2b43SDavid Greenman 				*lastport = IPPORT_RESERVED;
15415bd2b43SDavid Greenman 			lport = htons(*lastport);
155df8bae1dSRodney W. Grimes 		} while (in_pcblookup(head,
156df8bae1dSRodney W. Grimes 			    zeroin_addr, 0, inp->inp_laddr, lport, wild));
157df8bae1dSRodney W. Grimes 	inp->inp_lport = lport;
15815bd2b43SDavid Greenman 	in_pcbrehash(inp);
159df8bae1dSRodney W. Grimes 	return (0);
160df8bae1dSRodney W. Grimes }
161df8bae1dSRodney W. Grimes 
162999f1343SGarrett Wollman /*
163999f1343SGarrett Wollman  *   Transform old in_pcbconnect() into an inner subroutine for new
164999f1343SGarrett Wollman  *   in_pcbconnect(): Do some validity-checking on the remote
165999f1343SGarrett Wollman  *   address (in mbuf 'nam') and then determine local host address
166999f1343SGarrett Wollman  *   (i.e., which interface) to use to access that remote host.
167999f1343SGarrett Wollman  *
168999f1343SGarrett Wollman  *   This preserves definition of in_pcbconnect(), while supporting a
169999f1343SGarrett Wollman  *   slightly different version for T/TCP.  (This is more than
170999f1343SGarrett Wollman  *   a bit of a kludge, but cleaning up the internal interfaces would
171999f1343SGarrett Wollman  *   have forced minor changes in every protocol).
172999f1343SGarrett Wollman  */
173999f1343SGarrett Wollman 
174999f1343SGarrett Wollman int
175999f1343SGarrett Wollman in_pcbladdr(inp, nam, plocal_sin)
176999f1343SGarrett Wollman 	register struct inpcb *inp;
177999f1343SGarrett Wollman 	struct mbuf *nam;
178999f1343SGarrett Wollman 	struct sockaddr_in **plocal_sin;
179999f1343SGarrett Wollman {
180df8bae1dSRodney W. Grimes 	struct in_ifaddr *ia;
18126f9a767SRodney W. Grimes 	struct sockaddr_in *ifaddr = 0;
182df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
183df8bae1dSRodney W. Grimes 
184df8bae1dSRodney W. Grimes 	if (nam->m_len != sizeof (*sin))
185df8bae1dSRodney W. Grimes 		return (EINVAL);
186df8bae1dSRodney W. Grimes 	if (sin->sin_family != AF_INET)
187df8bae1dSRodney W. Grimes 		return (EAFNOSUPPORT);
188df8bae1dSRodney W. Grimes 	if (sin->sin_port == 0)
189df8bae1dSRodney W. Grimes 		return (EADDRNOTAVAIL);
190df8bae1dSRodney W. Grimes 	if (in_ifaddr) {
191df8bae1dSRodney W. Grimes 		/*
192df8bae1dSRodney W. Grimes 		 * If the destination address is INADDR_ANY,
193df8bae1dSRodney W. Grimes 		 * use the primary local address.
194df8bae1dSRodney W. Grimes 		 * If the supplied address is INADDR_BROADCAST,
195df8bae1dSRodney W. Grimes 		 * and the primary interface supports broadcast,
196df8bae1dSRodney W. Grimes 		 * choose the broadcast address for that interface.
197df8bae1dSRodney W. Grimes 		 */
198df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
199df8bae1dSRodney W. Grimes #define sintosa(sin)	((struct sockaddr *)(sin))
200df8bae1dSRodney W. Grimes #define ifatoia(ifa)	((struct in_ifaddr *)(ifa))
201df8bae1dSRodney W. Grimes 		if (sin->sin_addr.s_addr == INADDR_ANY)
202df8bae1dSRodney W. Grimes 		    sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
203df8bae1dSRodney W. Grimes 		else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
204df8bae1dSRodney W. Grimes 		  (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST))
205df8bae1dSRodney W. Grimes 		    sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr;
206df8bae1dSRodney W. Grimes 	}
207df8bae1dSRodney W. Grimes 	if (inp->inp_laddr.s_addr == INADDR_ANY) {
208df8bae1dSRodney W. Grimes 		register struct route *ro;
209df8bae1dSRodney W. Grimes 
210df8bae1dSRodney W. Grimes 		ia = (struct in_ifaddr *)0;
211df8bae1dSRodney W. Grimes 		/*
212df8bae1dSRodney W. Grimes 		 * If route is known or can be allocated now,
213df8bae1dSRodney W. Grimes 		 * our src addr is taken from the i/f, else punt.
214df8bae1dSRodney W. Grimes 		 */
215df8bae1dSRodney W. Grimes 		ro = &inp->inp_route;
216df8bae1dSRodney W. Grimes 		if (ro->ro_rt &&
217df8bae1dSRodney W. Grimes 		    (satosin(&ro->ro_dst)->sin_addr.s_addr !=
218df8bae1dSRodney W. Grimes 			sin->sin_addr.s_addr ||
219df8bae1dSRodney W. Grimes 		    inp->inp_socket->so_options & SO_DONTROUTE)) {
220df8bae1dSRodney W. Grimes 			RTFREE(ro->ro_rt);
221df8bae1dSRodney W. Grimes 			ro->ro_rt = (struct rtentry *)0;
222df8bae1dSRodney W. Grimes 		}
223df8bae1dSRodney W. Grimes 		if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
224df8bae1dSRodney W. Grimes 		    (ro->ro_rt == (struct rtentry *)0 ||
225df8bae1dSRodney W. Grimes 		    ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
226df8bae1dSRodney W. Grimes 			/* No route yet, so try to acquire one */
227df8bae1dSRodney W. Grimes 			ro->ro_dst.sa_family = AF_INET;
228df8bae1dSRodney W. Grimes 			ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
229df8bae1dSRodney W. Grimes 			((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
230df8bae1dSRodney W. Grimes 				sin->sin_addr;
231df8bae1dSRodney W. Grimes 			rtalloc(ro);
232df8bae1dSRodney W. Grimes 		}
233df8bae1dSRodney W. Grimes 		/*
234df8bae1dSRodney W. Grimes 		 * If we found a route, use the address
235df8bae1dSRodney W. Grimes 		 * corresponding to the outgoing interface
236df8bae1dSRodney W. Grimes 		 * unless it is the loopback (in case a route
237df8bae1dSRodney W. Grimes 		 * to our address on another net goes to loopback).
238df8bae1dSRodney W. Grimes 		 */
239df8bae1dSRodney W. Grimes 		if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
240df8bae1dSRodney W. Grimes 			ia = ifatoia(ro->ro_rt->rt_ifa);
241df8bae1dSRodney W. Grimes 		if (ia == 0) {
242df8bae1dSRodney W. Grimes 			u_short fport = sin->sin_port;
243df8bae1dSRodney W. Grimes 
244df8bae1dSRodney W. Grimes 			sin->sin_port = 0;
245df8bae1dSRodney W. Grimes 			ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin)));
246df8bae1dSRodney W. Grimes 			if (ia == 0)
247df8bae1dSRodney W. Grimes 				ia = ifatoia(ifa_ifwithnet(sintosa(sin)));
248df8bae1dSRodney W. Grimes 			sin->sin_port = fport;
249df8bae1dSRodney W. Grimes 			if (ia == 0)
250df8bae1dSRodney W. Grimes 				ia = in_ifaddr;
251df8bae1dSRodney W. Grimes 			if (ia == 0)
252df8bae1dSRodney W. Grimes 				return (EADDRNOTAVAIL);
253df8bae1dSRodney W. Grimes 		}
254df8bae1dSRodney W. Grimes 		/*
255df8bae1dSRodney W. Grimes 		 * If the destination address is multicast and an outgoing
256df8bae1dSRodney W. Grimes 		 * interface has been set as a multicast option, use the
257df8bae1dSRodney W. Grimes 		 * address of that interface as our source address.
258df8bae1dSRodney W. Grimes 		 */
259df8bae1dSRodney W. Grimes 		if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
260df8bae1dSRodney W. Grimes 		    inp->inp_moptions != NULL) {
261df8bae1dSRodney W. Grimes 			struct ip_moptions *imo;
262df8bae1dSRodney W. Grimes 			struct ifnet *ifp;
263df8bae1dSRodney W. Grimes 
264df8bae1dSRodney W. Grimes 			imo = inp->inp_moptions;
265df8bae1dSRodney W. Grimes 			if (imo->imo_multicast_ifp != NULL) {
266df8bae1dSRodney W. Grimes 				ifp = imo->imo_multicast_ifp;
267df8bae1dSRodney W. Grimes 				for (ia = in_ifaddr; ia; ia = ia->ia_next)
268df8bae1dSRodney W. Grimes 					if (ia->ia_ifp == ifp)
269df8bae1dSRodney W. Grimes 						break;
270df8bae1dSRodney W. Grimes 				if (ia == 0)
271df8bae1dSRodney W. Grimes 					return (EADDRNOTAVAIL);
272df8bae1dSRodney W. Grimes 			}
273df8bae1dSRodney W. Grimes 		}
274999f1343SGarrett Wollman 	/*
275999f1343SGarrett Wollman 	 * Don't do pcblookup call here; return interface in plocal_sin
276999f1343SGarrett Wollman 	 * and exit to caller, that will do the lookup.
277999f1343SGarrett Wollman 	 */
278999f1343SGarrett Wollman 		*plocal_sin = &ia->ia_addr;
279999f1343SGarrett Wollman 
280999f1343SGarrett Wollman 	}
281999f1343SGarrett Wollman 	return(0);
282999f1343SGarrett Wollman }
283999f1343SGarrett Wollman 
284999f1343SGarrett Wollman /*
285999f1343SGarrett Wollman  * Outer subroutine:
286999f1343SGarrett Wollman  * Connect from a socket to a specified address.
287999f1343SGarrett Wollman  * Both address and port must be specified in argument sin.
288999f1343SGarrett Wollman  * If don't have a local address for this socket yet,
289999f1343SGarrett Wollman  * then pick one.
290999f1343SGarrett Wollman  */
291999f1343SGarrett Wollman int
292999f1343SGarrett Wollman in_pcbconnect(inp, nam)
293999f1343SGarrett Wollman 	register struct inpcb *inp;
294999f1343SGarrett Wollman 	struct mbuf *nam;
295999f1343SGarrett Wollman {
296999f1343SGarrett Wollman 	struct sockaddr_in *ifaddr;
297999f1343SGarrett Wollman 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
298999f1343SGarrett Wollman 	int error;
299999f1343SGarrett Wollman 
300999f1343SGarrett Wollman 	/*
301999f1343SGarrett Wollman 	 *   Call inner routine, to assign local interface address.
302999f1343SGarrett Wollman 	 */
303999f1343SGarrett Wollman 	if (error = in_pcbladdr(inp, nam, &ifaddr))
304999f1343SGarrett Wollman 		return(error);
305999f1343SGarrett Wollman 
3060d7b7d3eSDavid Greenman 	if (in_pcblookuphash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port,
307df8bae1dSRodney W. Grimes 	    inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
3080d7b7d3eSDavid Greenman 	    inp->inp_lport) != NULL)
309df8bae1dSRodney W. Grimes 		return (EADDRINUSE);
310df8bae1dSRodney W. Grimes 	if (inp->inp_laddr.s_addr == INADDR_ANY) {
311df8bae1dSRodney W. Grimes 		if (inp->inp_lport == 0)
312df8bae1dSRodney W. Grimes 			(void)in_pcbbind(inp, (struct mbuf *)0);
313df8bae1dSRodney W. Grimes 		inp->inp_laddr = ifaddr->sin_addr;
314df8bae1dSRodney W. Grimes 	}
315df8bae1dSRodney W. Grimes 	inp->inp_faddr = sin->sin_addr;
316df8bae1dSRodney W. Grimes 	inp->inp_fport = sin->sin_port;
31715bd2b43SDavid Greenman 	in_pcbrehash(inp);
318df8bae1dSRodney W. Grimes 	return (0);
319df8bae1dSRodney W. Grimes }
320df8bae1dSRodney W. Grimes 
32126f9a767SRodney W. Grimes void
322df8bae1dSRodney W. Grimes in_pcbdisconnect(inp)
323df8bae1dSRodney W. Grimes 	struct inpcb *inp;
324df8bae1dSRodney W. Grimes {
325df8bae1dSRodney W. Grimes 
326df8bae1dSRodney W. Grimes 	inp->inp_faddr.s_addr = INADDR_ANY;
327df8bae1dSRodney W. Grimes 	inp->inp_fport = 0;
32815bd2b43SDavid Greenman 	in_pcbrehash(inp);
329df8bae1dSRodney W. Grimes 	if (inp->inp_socket->so_state & SS_NOFDREF)
330df8bae1dSRodney W. Grimes 		in_pcbdetach(inp);
331df8bae1dSRodney W. Grimes }
332df8bae1dSRodney W. Grimes 
33326f9a767SRodney W. Grimes void
334df8bae1dSRodney W. Grimes in_pcbdetach(inp)
335df8bae1dSRodney W. Grimes 	struct inpcb *inp;
336df8bae1dSRodney W. Grimes {
337df8bae1dSRodney W. Grimes 	struct socket *so = inp->inp_socket;
3387bc4aca7SDavid Greenman 	int s;
339df8bae1dSRodney W. Grimes 
340df8bae1dSRodney W. Grimes 	so->so_pcb = 0;
341df8bae1dSRodney W. Grimes 	sofree(so);
342df8bae1dSRodney W. Grimes 	if (inp->inp_options)
343df8bae1dSRodney W. Grimes 		(void)m_free(inp->inp_options);
344df8bae1dSRodney W. Grimes 	if (inp->inp_route.ro_rt)
345df8bae1dSRodney W. Grimes 		rtfree(inp->inp_route.ro_rt);
346df8bae1dSRodney W. Grimes 	ip_freemoptions(inp->inp_moptions);
3477bc4aca7SDavid Greenman 	s = splnet();
34815bd2b43SDavid Greenman 	LIST_REMOVE(inp, inp_hash);
34915bd2b43SDavid Greenman 	LIST_REMOVE(inp, inp_list);
3507bc4aca7SDavid Greenman 	splx(s);
351df8bae1dSRodney W. Grimes 	FREE(inp, M_PCB);
352df8bae1dSRodney W. Grimes }
353df8bae1dSRodney W. Grimes 
35426f9a767SRodney W. Grimes void
355df8bae1dSRodney W. Grimes in_setsockaddr(inp, nam)
356df8bae1dSRodney W. Grimes 	register struct inpcb *inp;
357df8bae1dSRodney W. Grimes 	struct mbuf *nam;
358df8bae1dSRodney W. Grimes {
359df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
360df8bae1dSRodney W. Grimes 
361df8bae1dSRodney W. Grimes 	nam->m_len = sizeof (*sin);
362df8bae1dSRodney W. Grimes 	sin = mtod(nam, struct sockaddr_in *);
363df8bae1dSRodney W. Grimes 	bzero((caddr_t)sin, sizeof (*sin));
364df8bae1dSRodney W. Grimes 	sin->sin_family = AF_INET;
365df8bae1dSRodney W. Grimes 	sin->sin_len = sizeof(*sin);
366df8bae1dSRodney W. Grimes 	sin->sin_port = inp->inp_lport;
367df8bae1dSRodney W. Grimes 	sin->sin_addr = inp->inp_laddr;
368df8bae1dSRodney W. Grimes }
369df8bae1dSRodney W. Grimes 
37026f9a767SRodney W. Grimes void
371df8bae1dSRodney W. Grimes in_setpeeraddr(inp, nam)
372df8bae1dSRodney W. Grimes 	struct inpcb *inp;
373df8bae1dSRodney W. Grimes 	struct mbuf *nam;
374df8bae1dSRodney W. Grimes {
375df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
376df8bae1dSRodney W. Grimes 
377df8bae1dSRodney W. Grimes 	nam->m_len = sizeof (*sin);
378df8bae1dSRodney W. Grimes 	sin = mtod(nam, struct sockaddr_in *);
379df8bae1dSRodney W. Grimes 	bzero((caddr_t)sin, sizeof (*sin));
380df8bae1dSRodney W. Grimes 	sin->sin_family = AF_INET;
381df8bae1dSRodney W. Grimes 	sin->sin_len = sizeof(*sin);
382df8bae1dSRodney W. Grimes 	sin->sin_port = inp->inp_fport;
383df8bae1dSRodney W. Grimes 	sin->sin_addr = inp->inp_faddr;
384df8bae1dSRodney W. Grimes }
385df8bae1dSRodney W. Grimes 
386df8bae1dSRodney W. Grimes /*
387df8bae1dSRodney W. Grimes  * Pass some notification to all connections of a protocol
388df8bae1dSRodney W. Grimes  * associated with address dst.  The local address and/or port numbers
389df8bae1dSRodney W. Grimes  * may be specified to limit the search.  The "usual action" will be
390df8bae1dSRodney W. Grimes  * taken, depending on the ctlinput cmd.  The caller must filter any
391df8bae1dSRodney W. Grimes  * cmds that are uninteresting (e.g., no error in the map).
392df8bae1dSRodney W. Grimes  * Call the protocol specific routine (if any) to report
393df8bae1dSRodney W. Grimes  * any errors for each matching socket.
394df8bae1dSRodney W. Grimes  *
395df8bae1dSRodney W. Grimes  * Must be called at splnet.
396df8bae1dSRodney W. Grimes  */
39726f9a767SRodney W. Grimes void
398df8bae1dSRodney W. Grimes in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify)
39915bd2b43SDavid Greenman 	struct inpcbhead *head;
400df8bae1dSRodney W. Grimes 	struct sockaddr *dst;
401df8bae1dSRodney W. Grimes 	u_int fport_arg, lport_arg;
402df8bae1dSRodney W. Grimes 	struct in_addr laddr;
403df8bae1dSRodney W. Grimes 	int cmd;
404df8bae1dSRodney W. Grimes 	void (*notify) __P((struct inpcb *, int));
405df8bae1dSRodney W. Grimes {
406df8bae1dSRodney W. Grimes 	register struct inpcb *inp, *oinp;
407df8bae1dSRodney W. Grimes 	struct in_addr faddr;
408df8bae1dSRodney W. Grimes 	u_short fport = fport_arg, lport = lport_arg;
4097bc4aca7SDavid Greenman 	int errno, s;
410df8bae1dSRodney W. Grimes 
411df8bae1dSRodney W. Grimes 	if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
412df8bae1dSRodney W. Grimes 		return;
413df8bae1dSRodney W. Grimes 	faddr = ((struct sockaddr_in *)dst)->sin_addr;
414df8bae1dSRodney W. Grimes 	if (faddr.s_addr == INADDR_ANY)
415df8bae1dSRodney W. Grimes 		return;
416df8bae1dSRodney W. Grimes 
417df8bae1dSRodney W. Grimes 	/*
418df8bae1dSRodney W. Grimes 	 * Redirects go to all references to the destination,
419df8bae1dSRodney W. Grimes 	 * and use in_rtchange to invalidate the route cache.
420df8bae1dSRodney W. Grimes 	 * Dead host indications: notify all references to the destination.
421df8bae1dSRodney W. Grimes 	 * Otherwise, if we have knowledge of the local port and address,
422df8bae1dSRodney W. Grimes 	 * deliver only to that socket.
423df8bae1dSRodney W. Grimes 	 */
424df8bae1dSRodney W. Grimes 	if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
425df8bae1dSRodney W. Grimes 		fport = 0;
426df8bae1dSRodney W. Grimes 		lport = 0;
427df8bae1dSRodney W. Grimes 		laddr.s_addr = 0;
428df8bae1dSRodney W. Grimes 		if (cmd != PRC_HOSTDEAD)
429df8bae1dSRodney W. Grimes 			notify = in_rtchange;
430df8bae1dSRodney W. Grimes 	}
431df8bae1dSRodney W. Grimes 	errno = inetctlerrmap[cmd];
4327bc4aca7SDavid Greenman 	s = splnet();
43315bd2b43SDavid Greenman 	for (inp = head->lh_first; inp != NULL;) {
434df8bae1dSRodney W. Grimes 		if (inp->inp_faddr.s_addr != faddr.s_addr ||
435df8bae1dSRodney W. Grimes 		    inp->inp_socket == 0 ||
436df8bae1dSRodney W. Grimes 		    (lport && inp->inp_lport != lport) ||
437df8bae1dSRodney W. Grimes 		    (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) ||
438df8bae1dSRodney W. Grimes 		    (fport && inp->inp_fport != fport)) {
43915bd2b43SDavid Greenman 			inp = inp->inp_list.le_next;
440df8bae1dSRodney W. Grimes 			continue;
441df8bae1dSRodney W. Grimes 		}
442df8bae1dSRodney W. Grimes 		oinp = inp;
44315bd2b43SDavid Greenman 		inp = inp->inp_list.le_next;
444df8bae1dSRodney W. Grimes 		if (notify)
445df8bae1dSRodney W. Grimes 			(*notify)(oinp, errno);
446df8bae1dSRodney W. Grimes 	}
4477bc4aca7SDavid Greenman 	splx(s);
448df8bae1dSRodney W. Grimes }
449df8bae1dSRodney W. Grimes 
450df8bae1dSRodney W. Grimes /*
451df8bae1dSRodney W. Grimes  * Check for alternatives when higher level complains
452df8bae1dSRodney W. Grimes  * about service problems.  For now, invalidate cached
453df8bae1dSRodney W. Grimes  * routing information.  If the route was created dynamically
454df8bae1dSRodney W. Grimes  * (by a redirect), time to try a default gateway again.
455df8bae1dSRodney W. Grimes  */
45626f9a767SRodney W. Grimes void
457df8bae1dSRodney W. Grimes in_losing(inp)
458df8bae1dSRodney W. Grimes 	struct inpcb *inp;
459df8bae1dSRodney W. Grimes {
460df8bae1dSRodney W. Grimes 	register struct rtentry *rt;
461df8bae1dSRodney W. Grimes 	struct rt_addrinfo info;
462df8bae1dSRodney W. Grimes 
463df8bae1dSRodney W. Grimes 	if ((rt = inp->inp_route.ro_rt)) {
464df8bae1dSRodney W. Grimes 		inp->inp_route.ro_rt = 0;
465df8bae1dSRodney W. Grimes 		bzero((caddr_t)&info, sizeof(info));
466df8bae1dSRodney W. Grimes 		info.rti_info[RTAX_DST] =
467df8bae1dSRodney W. Grimes 			(struct sockaddr *)&inp->inp_route.ro_dst;
468df8bae1dSRodney W. Grimes 		info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
469df8bae1dSRodney W. Grimes 		info.rti_info[RTAX_NETMASK] = rt_mask(rt);
470df8bae1dSRodney W. Grimes 		rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
471df8bae1dSRodney W. Grimes 		if (rt->rt_flags & RTF_DYNAMIC)
472df8bae1dSRodney W. Grimes 			(void) rtrequest(RTM_DELETE, rt_key(rt),
473df8bae1dSRodney W. Grimes 				rt->rt_gateway, rt_mask(rt), rt->rt_flags,
474df8bae1dSRodney W. Grimes 				(struct rtentry **)0);
475df8bae1dSRodney W. Grimes 		else
476df8bae1dSRodney W. Grimes 		/*
477df8bae1dSRodney W. Grimes 		 * A new route can be allocated
478df8bae1dSRodney W. Grimes 		 * the next time output is attempted.
479df8bae1dSRodney W. Grimes 		 */
480df8bae1dSRodney W. Grimes 			rtfree(rt);
481df8bae1dSRodney W. Grimes 	}
482df8bae1dSRodney W. Grimes }
483df8bae1dSRodney W. Grimes 
484df8bae1dSRodney W. Grimes /*
485df8bae1dSRodney W. Grimes  * After a routing change, flush old routing
486df8bae1dSRodney W. Grimes  * and allocate a (hopefully) better one.
487df8bae1dSRodney W. Grimes  */
488df8bae1dSRodney W. Grimes void
489df8bae1dSRodney W. Grimes in_rtchange(inp, errno)
490df8bae1dSRodney W. Grimes 	register struct inpcb *inp;
491df8bae1dSRodney W. Grimes 	int errno;
492df8bae1dSRodney W. Grimes {
493df8bae1dSRodney W. Grimes 	if (inp->inp_route.ro_rt) {
494df8bae1dSRodney W. Grimes 		rtfree(inp->inp_route.ro_rt);
495df8bae1dSRodney W. Grimes 		inp->inp_route.ro_rt = 0;
496df8bae1dSRodney W. Grimes 		/*
497df8bae1dSRodney W. Grimes 		 * A new route can be allocated the next time
498df8bae1dSRodney W. Grimes 		 * output is attempted.
499df8bae1dSRodney W. Grimes 		 */
500df8bae1dSRodney W. Grimes 	}
501df8bae1dSRodney W. Grimes }
502df8bae1dSRodney W. Grimes 
503df8bae1dSRodney W. Grimes struct inpcb *
504df8bae1dSRodney W. Grimes in_pcblookup(head, faddr, fport_arg, laddr, lport_arg, flags)
50515bd2b43SDavid Greenman 	struct inpcbhead *head;
506df8bae1dSRodney W. Grimes 	struct in_addr faddr, laddr;
507df8bae1dSRodney W. Grimes 	u_int fport_arg, lport_arg;
508df8bae1dSRodney W. Grimes 	int flags;
509df8bae1dSRodney W. Grimes {
5103dbdc25cSDavid Greenman 	register struct inpcb *inp, *match = NULL;
511df8bae1dSRodney W. Grimes 	int matchwild = 3, wildcard;
512df8bae1dSRodney W. Grimes 	u_short fport = fport_arg, lport = lport_arg;
5137bc4aca7SDavid Greenman 	int s;
5147bc4aca7SDavid Greenman 
5157bc4aca7SDavid Greenman 	s = splnet();
516df8bae1dSRodney W. Grimes 
51715bd2b43SDavid Greenman 	for (inp = head->lh_first; inp != NULL; inp = inp->inp_list.le_next) {
518df8bae1dSRodney W. Grimes 		if (inp->inp_lport != lport)
519df8bae1dSRodney W. Grimes 			continue;
520df8bae1dSRodney W. Grimes 		wildcard = 0;
521df8bae1dSRodney W. Grimes 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
522df8bae1dSRodney W. Grimes 			if (faddr.s_addr == INADDR_ANY)
523df8bae1dSRodney W. Grimes 				wildcard++;
524df8bae1dSRodney W. Grimes 			else if (inp->inp_faddr.s_addr != faddr.s_addr ||
525df8bae1dSRodney W. Grimes 			    inp->inp_fport != fport)
526df8bae1dSRodney W. Grimes 				continue;
527df8bae1dSRodney W. Grimes 		} else {
528df8bae1dSRodney W. Grimes 			if (faddr.s_addr != INADDR_ANY)
529df8bae1dSRodney W. Grimes 				wildcard++;
530df8bae1dSRodney W. Grimes 		}
53115bd2b43SDavid Greenman 		if (inp->inp_laddr.s_addr != INADDR_ANY) {
53215bd2b43SDavid Greenman 			if (laddr.s_addr == INADDR_ANY)
53315bd2b43SDavid Greenman 				wildcard++;
53415bd2b43SDavid Greenman 			else if (inp->inp_laddr.s_addr != laddr.s_addr)
53515bd2b43SDavid Greenman 				continue;
53615bd2b43SDavid Greenman 		} else {
53715bd2b43SDavid Greenman 			if (laddr.s_addr != INADDR_ANY)
53815bd2b43SDavid Greenman 				wildcard++;
53915bd2b43SDavid Greenman 		}
540df8bae1dSRodney W. Grimes 		if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
541df8bae1dSRodney W. Grimes 			continue;
542df8bae1dSRodney W. Grimes 		if (wildcard < matchwild) {
543df8bae1dSRodney W. Grimes 			match = inp;
544df8bae1dSRodney W. Grimes 			matchwild = wildcard;
5453dbdc25cSDavid Greenman 			if (matchwild == 0) {
546df8bae1dSRodney W. Grimes 				break;
547df8bae1dSRodney W. Grimes 			}
548df8bae1dSRodney W. Grimes 		}
5493dbdc25cSDavid Greenman 	}
5507bc4aca7SDavid Greenman 	splx(s);
551df8bae1dSRodney W. Grimes 	return (match);
552df8bae1dSRodney W. Grimes }
55315bd2b43SDavid Greenman 
55415bd2b43SDavid Greenman /*
55515bd2b43SDavid Greenman  * Lookup PCB in hash list.
55615bd2b43SDavid Greenman  */
55715bd2b43SDavid Greenman struct inpcb *
55815bd2b43SDavid Greenman in_pcblookuphash(pcbinfo, faddr, fport_arg, laddr, lport_arg)
55915bd2b43SDavid Greenman 	struct inpcbinfo *pcbinfo;
56015bd2b43SDavid Greenman 	struct in_addr faddr, laddr;
56115bd2b43SDavid Greenman 	u_int fport_arg, lport_arg;
56215bd2b43SDavid Greenman {
56315bd2b43SDavid Greenman 	struct inpcbhead *head;
56415bd2b43SDavid Greenman 	register struct inpcb *inp;
56515bd2b43SDavid Greenman 	u_short fport = fport_arg, lport = lport_arg;
5667bc4aca7SDavid Greenman 	int s;
56715bd2b43SDavid Greenman 
5687bc4aca7SDavid Greenman 	s = splnet();
56915bd2b43SDavid Greenman 	/*
57015bd2b43SDavid Greenman 	 * First look for an exact match.
57115bd2b43SDavid Greenman 	 */
57215bd2b43SDavid Greenman 	head = &pcbinfo->hashbase[(faddr.s_addr + lport + fport) % pcbinfo->hashsize];
57315bd2b43SDavid Greenman 
57415bd2b43SDavid Greenman 	for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) {
57515bd2b43SDavid Greenman 		if (inp->inp_faddr.s_addr != faddr.s_addr ||
57615bd2b43SDavid Greenman 		    inp->inp_fport != fport ||
57715bd2b43SDavid Greenman 		    inp->inp_lport != lport ||
57815bd2b43SDavid Greenman 		    inp->inp_laddr.s_addr != laddr.s_addr)
57915bd2b43SDavid Greenman 			continue;
58015bd2b43SDavid Greenman 		/*
58115bd2b43SDavid Greenman 		 * Move PCB to head of this hash chain so that it can be
58215bd2b43SDavid Greenman 		 * found more quickly in the future.
58315bd2b43SDavid Greenman 		 */
58415bd2b43SDavid Greenman 		if (inp != head->lh_first) {
58515bd2b43SDavid Greenman 			LIST_REMOVE(inp, inp_hash);
58615bd2b43SDavid Greenman 			LIST_INSERT_HEAD(head, inp, inp_hash);
58715bd2b43SDavid Greenman 		}
5880d7b7d3eSDavid Greenman 		break;
58915bd2b43SDavid Greenman 	}
5907bc4aca7SDavid Greenman 	splx(s);
5910d7b7d3eSDavid Greenman 	return (inp);
59215bd2b43SDavid Greenman }
59315bd2b43SDavid Greenman 
5947bc4aca7SDavid Greenman /*
5957bc4aca7SDavid Greenman  * Insert PCB into hash chain. Must be called at splnet.
5967bc4aca7SDavid Greenman  */
59715bd2b43SDavid Greenman void
59815bd2b43SDavid Greenman in_pcbinshash(inp)
59915bd2b43SDavid Greenman 	struct inpcb *inp;
60015bd2b43SDavid Greenman {
60115bd2b43SDavid Greenman 	struct inpcbhead *head;
60215bd2b43SDavid Greenman 
60315bd2b43SDavid Greenman 	head = &inp->inp_pcbinfo->hashbase[(inp->inp_faddr.s_addr +
60415bd2b43SDavid Greenman 		inp->inp_lport + inp->inp_fport) % inp->inp_pcbinfo->hashsize];
60515bd2b43SDavid Greenman 
60615bd2b43SDavid Greenman 	LIST_INSERT_HEAD(head, inp, inp_hash);
60715bd2b43SDavid Greenman }
60815bd2b43SDavid Greenman 
60915bd2b43SDavid Greenman void
61015bd2b43SDavid Greenman in_pcbrehash(inp)
61115bd2b43SDavid Greenman 	struct inpcb *inp;
61215bd2b43SDavid Greenman {
61315bd2b43SDavid Greenman 	struct inpcbhead *head;
6147bc4aca7SDavid Greenman 	int s;
61515bd2b43SDavid Greenman 
6167bc4aca7SDavid Greenman 	s = splnet();
61715bd2b43SDavid Greenman 	LIST_REMOVE(inp, inp_hash);
61815bd2b43SDavid Greenman 
61915bd2b43SDavid Greenman 	head = &inp->inp_pcbinfo->hashbase[(inp->inp_faddr.s_addr +
62015bd2b43SDavid Greenman 		inp->inp_lport + inp->inp_fport) % inp->inp_pcbinfo->hashsize];
62115bd2b43SDavid Greenman 
62215bd2b43SDavid Greenman 	LIST_INSERT_HEAD(head, inp, inp_hash);
6237bc4aca7SDavid Greenman 	splx(s);
62415bd2b43SDavid Greenman }
625