xref: /freebsd/sys/netinet/in_pcb.c (revision 0312fbe97df2f35ff9269929cbc685f5784620fb)
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
340312fbe9SPoul-Henning Kamp  *	$Id: in_pcb.c,v 1.14 1995/10/29 15:32:25 phk Exp $
35df8bae1dSRodney W. Grimes  */
36df8bae1dSRodney W. Grimes 
37df8bae1dSRodney W. Grimes #include <sys/param.h>
38df8bae1dSRodney W. Grimes #include <sys/systm.h>
39df8bae1dSRodney W. Grimes #include <sys/malloc.h>
40df8bae1dSRodney W. Grimes #include <sys/mbuf.h>
41df8bae1dSRodney W. Grimes #include <sys/protosw.h>
42df8bae1dSRodney W. Grimes #include <sys/socket.h>
43df8bae1dSRodney W. Grimes #include <sys/socketvar.h>
44df8bae1dSRodney W. Grimes #include <sys/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 
620312fbe9SPoul-Henning Kamp static void	 in_pcbinshash __P((struct inpcb *));
630312fbe9SPoul-Henning Kamp static void	 in_rtchange __P((struct inpcb *, int));
640312fbe9SPoul-Henning Kamp 
65df8bae1dSRodney W. Grimes int
6615bd2b43SDavid Greenman in_pcballoc(so, pcbinfo)
67df8bae1dSRodney W. Grimes 	struct socket *so;
6815bd2b43SDavid Greenman 	struct inpcbinfo *pcbinfo;
69df8bae1dSRodney W. Grimes {
70df8bae1dSRodney W. Grimes 	register struct inpcb *inp;
717bc4aca7SDavid Greenman 	int s;
72df8bae1dSRodney W. Grimes 
732c8fe19fSDavid Greenman 	MALLOC(inp, struct inpcb *, sizeof(*inp), M_PCB, M_NOWAIT);
74df8bae1dSRodney W. Grimes 	if (inp == NULL)
75df8bae1dSRodney W. Grimes 		return (ENOBUFS);
76df8bae1dSRodney W. Grimes 	bzero((caddr_t)inp, sizeof(*inp));
7715bd2b43SDavid Greenman 	inp->inp_pcbinfo = pcbinfo;
78df8bae1dSRodney W. Grimes 	inp->inp_socket = so;
797bc4aca7SDavid Greenman 	s = splnet();
8015bd2b43SDavid Greenman 	LIST_INSERT_HEAD(pcbinfo->listhead, inp, inp_list);
8115bd2b43SDavid Greenman 	in_pcbinshash(inp);
827bc4aca7SDavid Greenman 	splx(s);
83df8bae1dSRodney W. Grimes 	so->so_pcb = (caddr_t)inp;
84df8bae1dSRodney W. Grimes 	return (0);
85df8bae1dSRodney W. Grimes }
86df8bae1dSRodney W. Grimes 
87df8bae1dSRodney W. Grimes int
88df8bae1dSRodney W. Grimes in_pcbbind(inp, nam)
89df8bae1dSRodney W. Grimes 	register struct inpcb *inp;
90df8bae1dSRodney W. Grimes 	struct mbuf *nam;
91df8bae1dSRodney W. Grimes {
92df8bae1dSRodney W. Grimes 	register struct socket *so = inp->inp_socket;
9315bd2b43SDavid Greenman 	struct inpcbhead *head = inp->inp_pcbinfo->listhead;
9415bd2b43SDavid Greenman 	unsigned short *lastport = &inp->inp_pcbinfo->lastport;
9515bd2b43SDavid Greenman 	struct sockaddr_in *sin;
96df8bae1dSRodney W. Grimes 	struct proc *p = curproc;		/* XXX */
97df8bae1dSRodney W. Grimes 	u_short lport = 0;
98df8bae1dSRodney W. Grimes 	int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
99df8bae1dSRodney W. Grimes 	int error;
100df8bae1dSRodney W. Grimes 
101df8bae1dSRodney W. Grimes 	if (in_ifaddr == 0)
102df8bae1dSRodney W. Grimes 		return (EADDRNOTAVAIL);
103df8bae1dSRodney W. Grimes 	if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
104df8bae1dSRodney W. Grimes 		return (EINVAL);
105df8bae1dSRodney W. Grimes 	if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 &&
106df8bae1dSRodney W. Grimes 	    ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
107df8bae1dSRodney W. Grimes 	     (so->so_options & SO_ACCEPTCONN) == 0))
108df8bae1dSRodney W. Grimes 		wild = INPLOOKUP_WILDCARD;
109df8bae1dSRodney W. Grimes 	if (nam) {
110df8bae1dSRodney W. Grimes 		sin = mtod(nam, struct sockaddr_in *);
111df8bae1dSRodney W. Grimes 		if (nam->m_len != sizeof (*sin))
112df8bae1dSRodney W. Grimes 			return (EINVAL);
113df8bae1dSRodney W. Grimes #ifdef notdef
114df8bae1dSRodney W. Grimes 		/*
115df8bae1dSRodney W. Grimes 		 * We should check the family, but old programs
116df8bae1dSRodney W. Grimes 		 * incorrectly fail to initialize it.
117df8bae1dSRodney W. Grimes 		 */
118df8bae1dSRodney W. Grimes 		if (sin->sin_family != AF_INET)
119df8bae1dSRodney W. Grimes 			return (EAFNOSUPPORT);
120df8bae1dSRodney W. Grimes #endif
121df8bae1dSRodney W. Grimes 		lport = sin->sin_port;
122df8bae1dSRodney W. Grimes 		if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
123df8bae1dSRodney W. Grimes 			/*
124df8bae1dSRodney W. Grimes 			 * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
125df8bae1dSRodney W. Grimes 			 * allow complete duplication of binding if
126df8bae1dSRodney W. Grimes 			 * SO_REUSEPORT is set, or if SO_REUSEADDR is set
127df8bae1dSRodney W. Grimes 			 * and a multicast address is bound on both
128df8bae1dSRodney W. Grimes 			 * new and duplicated sockets.
129df8bae1dSRodney W. Grimes 			 */
130df8bae1dSRodney W. Grimes 			if (so->so_options & SO_REUSEADDR)
131df8bae1dSRodney W. Grimes 				reuseport = SO_REUSEADDR|SO_REUSEPORT;
132df8bae1dSRodney W. Grimes 		} else if (sin->sin_addr.s_addr != INADDR_ANY) {
133df8bae1dSRodney W. Grimes 			sin->sin_port = 0;		/* yech... */
134df8bae1dSRodney W. Grimes 			if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
135df8bae1dSRodney W. Grimes 				return (EADDRNOTAVAIL);
136df8bae1dSRodney W. Grimes 		}
137df8bae1dSRodney W. Grimes 		if (lport) {
138df8bae1dSRodney W. Grimes 			struct inpcb *t;
139df8bae1dSRodney W. Grimes 
140df8bae1dSRodney W. Grimes 			/* GROSS */
141df8bae1dSRodney W. Grimes 			if (ntohs(lport) < IPPORT_RESERVED &&
142df8bae1dSRodney W. Grimes 			    (error = suser(p->p_ucred, &p->p_acflag)))
1432469dd60SGarrett Wollman 				return (EACCES);
144df8bae1dSRodney W. Grimes 			t = in_pcblookup(head, zeroin_addr, 0,
145df8bae1dSRodney W. Grimes 			    sin->sin_addr, lport, wild);
146df8bae1dSRodney W. Grimes 			if (t && (reuseport & t->inp_socket->so_options) == 0)
147df8bae1dSRodney W. Grimes 				return (EADDRINUSE);
148df8bae1dSRodney W. Grimes 		}
149df8bae1dSRodney W. Grimes 		inp->inp_laddr = sin->sin_addr;
150df8bae1dSRodney W. Grimes 	}
151df8bae1dSRodney W. Grimes 	if (lport == 0)
152df8bae1dSRodney W. Grimes 		do {
15315bd2b43SDavid Greenman 			++*lastport;
15415bd2b43SDavid Greenman 			if (*lastport < IPPORT_RESERVED ||
15515bd2b43SDavid Greenman 			    *lastport > IPPORT_USERRESERVED)
15615bd2b43SDavid Greenman 				*lastport = IPPORT_RESERVED;
15715bd2b43SDavid Greenman 			lport = htons(*lastport);
158df8bae1dSRodney W. Grimes 		} while (in_pcblookup(head,
159df8bae1dSRodney W. Grimes 			    zeroin_addr, 0, inp->inp_laddr, lport, wild));
160df8bae1dSRodney W. Grimes 	inp->inp_lport = lport;
16115bd2b43SDavid Greenman 	in_pcbrehash(inp);
162df8bae1dSRodney W. Grimes 	return (0);
163df8bae1dSRodney W. Grimes }
164df8bae1dSRodney W. Grimes 
165999f1343SGarrett Wollman /*
166999f1343SGarrett Wollman  *   Transform old in_pcbconnect() into an inner subroutine for new
167999f1343SGarrett Wollman  *   in_pcbconnect(): Do some validity-checking on the remote
168999f1343SGarrett Wollman  *   address (in mbuf 'nam') and then determine local host address
169999f1343SGarrett Wollman  *   (i.e., which interface) to use to access that remote host.
170999f1343SGarrett Wollman  *
171999f1343SGarrett Wollman  *   This preserves definition of in_pcbconnect(), while supporting a
172999f1343SGarrett Wollman  *   slightly different version for T/TCP.  (This is more than
173999f1343SGarrett Wollman  *   a bit of a kludge, but cleaning up the internal interfaces would
174999f1343SGarrett Wollman  *   have forced minor changes in every protocol).
175999f1343SGarrett Wollman  */
176999f1343SGarrett Wollman 
177999f1343SGarrett Wollman int
178999f1343SGarrett Wollman in_pcbladdr(inp, nam, plocal_sin)
179999f1343SGarrett Wollman 	register struct inpcb *inp;
180999f1343SGarrett Wollman 	struct mbuf *nam;
181999f1343SGarrett Wollman 	struct sockaddr_in **plocal_sin;
182999f1343SGarrett Wollman {
183df8bae1dSRodney W. Grimes 	struct in_ifaddr *ia;
184df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
185df8bae1dSRodney W. Grimes 
186df8bae1dSRodney W. Grimes 	if (nam->m_len != sizeof (*sin))
187df8bae1dSRodney W. Grimes 		return (EINVAL);
188df8bae1dSRodney W. Grimes 	if (sin->sin_family != AF_INET)
189df8bae1dSRodney W. Grimes 		return (EAFNOSUPPORT);
190df8bae1dSRodney W. Grimes 	if (sin->sin_port == 0)
191df8bae1dSRodney W. Grimes 		return (EADDRNOTAVAIL);
192df8bae1dSRodney W. Grimes 	if (in_ifaddr) {
193df8bae1dSRodney W. Grimes 		/*
194df8bae1dSRodney W. Grimes 		 * If the destination address is INADDR_ANY,
195df8bae1dSRodney W. Grimes 		 * use the primary local address.
196df8bae1dSRodney W. Grimes 		 * If the supplied address is INADDR_BROADCAST,
197df8bae1dSRodney W. Grimes 		 * and the primary interface supports broadcast,
198df8bae1dSRodney W. Grimes 		 * choose the broadcast address for that interface.
199df8bae1dSRodney W. Grimes 		 */
200df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
201df8bae1dSRodney W. Grimes #define sintosa(sin)	((struct sockaddr *)(sin))
202df8bae1dSRodney W. Grimes #define ifatoia(ifa)	((struct in_ifaddr *)(ifa))
203df8bae1dSRodney W. Grimes 		if (sin->sin_addr.s_addr == INADDR_ANY)
204df8bae1dSRodney W. Grimes 		    sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
205df8bae1dSRodney W. Grimes 		else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
206df8bae1dSRodney W. Grimes 		  (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST))
207df8bae1dSRodney W. Grimes 		    sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr;
208df8bae1dSRodney W. Grimes 	}
209df8bae1dSRodney W. Grimes 	if (inp->inp_laddr.s_addr == INADDR_ANY) {
210df8bae1dSRodney W. Grimes 		register struct route *ro;
211df8bae1dSRodney W. Grimes 
212df8bae1dSRodney W. Grimes 		ia = (struct in_ifaddr *)0;
213df8bae1dSRodney W. Grimes 		/*
214df8bae1dSRodney W. Grimes 		 * If route is known or can be allocated now,
215df8bae1dSRodney W. Grimes 		 * our src addr is taken from the i/f, else punt.
216df8bae1dSRodney W. Grimes 		 */
217df8bae1dSRodney W. Grimes 		ro = &inp->inp_route;
218df8bae1dSRodney W. Grimes 		if (ro->ro_rt &&
219df8bae1dSRodney W. Grimes 		    (satosin(&ro->ro_dst)->sin_addr.s_addr !=
220df8bae1dSRodney W. Grimes 			sin->sin_addr.s_addr ||
221df8bae1dSRodney W. Grimes 		    inp->inp_socket->so_options & SO_DONTROUTE)) {
222df8bae1dSRodney W. Grimes 			RTFREE(ro->ro_rt);
223df8bae1dSRodney W. Grimes 			ro->ro_rt = (struct rtentry *)0;
224df8bae1dSRodney W. Grimes 		}
225df8bae1dSRodney W. Grimes 		if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
226df8bae1dSRodney W. Grimes 		    (ro->ro_rt == (struct rtentry *)0 ||
227df8bae1dSRodney W. Grimes 		    ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
228df8bae1dSRodney W. Grimes 			/* No route yet, so try to acquire one */
229df8bae1dSRodney W. Grimes 			ro->ro_dst.sa_family = AF_INET;
230df8bae1dSRodney W. Grimes 			ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
231df8bae1dSRodney W. Grimes 			((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
232df8bae1dSRodney W. Grimes 				sin->sin_addr;
233df8bae1dSRodney W. Grimes 			rtalloc(ro);
234df8bae1dSRodney W. Grimes 		}
235df8bae1dSRodney W. Grimes 		/*
236df8bae1dSRodney W. Grimes 		 * If we found a route, use the address
237df8bae1dSRodney W. Grimes 		 * corresponding to the outgoing interface
238df8bae1dSRodney W. Grimes 		 * unless it is the loopback (in case a route
239df8bae1dSRodney W. Grimes 		 * to our address on another net goes to loopback).
240df8bae1dSRodney W. Grimes 		 */
241df8bae1dSRodney W. Grimes 		if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
242df8bae1dSRodney W. Grimes 			ia = ifatoia(ro->ro_rt->rt_ifa);
243df8bae1dSRodney W. Grimes 		if (ia == 0) {
244df8bae1dSRodney W. Grimes 			u_short fport = sin->sin_port;
245df8bae1dSRodney W. Grimes 
246df8bae1dSRodney W. Grimes 			sin->sin_port = 0;
247df8bae1dSRodney W. Grimes 			ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin)));
248df8bae1dSRodney W. Grimes 			if (ia == 0)
249df8bae1dSRodney W. Grimes 				ia = ifatoia(ifa_ifwithnet(sintosa(sin)));
250df8bae1dSRodney W. Grimes 			sin->sin_port = fport;
251df8bae1dSRodney W. Grimes 			if (ia == 0)
252df8bae1dSRodney W. Grimes 				ia = in_ifaddr;
253df8bae1dSRodney W. Grimes 			if (ia == 0)
254df8bae1dSRodney W. Grimes 				return (EADDRNOTAVAIL);
255df8bae1dSRodney W. Grimes 		}
256df8bae1dSRodney W. Grimes 		/*
257df8bae1dSRodney W. Grimes 		 * If the destination address is multicast and an outgoing
258df8bae1dSRodney W. Grimes 		 * interface has been set as a multicast option, use the
259df8bae1dSRodney W. Grimes 		 * address of that interface as our source address.
260df8bae1dSRodney W. Grimes 		 */
261df8bae1dSRodney W. Grimes 		if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
262df8bae1dSRodney W. Grimes 		    inp->inp_moptions != NULL) {
263df8bae1dSRodney W. Grimes 			struct ip_moptions *imo;
264df8bae1dSRodney W. Grimes 			struct ifnet *ifp;
265df8bae1dSRodney W. Grimes 
266df8bae1dSRodney W. Grimes 			imo = inp->inp_moptions;
267df8bae1dSRodney W. Grimes 			if (imo->imo_multicast_ifp != NULL) {
268df8bae1dSRodney W. Grimes 				ifp = imo->imo_multicast_ifp;
269df8bae1dSRodney W. Grimes 				for (ia = in_ifaddr; ia; ia = ia->ia_next)
270df8bae1dSRodney W. Grimes 					if (ia->ia_ifp == ifp)
271df8bae1dSRodney W. Grimes 						break;
272df8bae1dSRodney W. Grimes 				if (ia == 0)
273df8bae1dSRodney W. Grimes 					return (EADDRNOTAVAIL);
274df8bae1dSRodney W. Grimes 			}
275df8bae1dSRodney W. Grimes 		}
276999f1343SGarrett Wollman 	/*
277999f1343SGarrett Wollman 	 * Don't do pcblookup call here; return interface in plocal_sin
278999f1343SGarrett Wollman 	 * and exit to caller, that will do the lookup.
279999f1343SGarrett Wollman 	 */
280999f1343SGarrett Wollman 		*plocal_sin = &ia->ia_addr;
281999f1343SGarrett Wollman 
282999f1343SGarrett Wollman 	}
283999f1343SGarrett Wollman 	return(0);
284999f1343SGarrett Wollman }
285999f1343SGarrett Wollman 
286999f1343SGarrett Wollman /*
287999f1343SGarrett Wollman  * Outer subroutine:
288999f1343SGarrett Wollman  * Connect from a socket to a specified address.
289999f1343SGarrett Wollman  * Both address and port must be specified in argument sin.
290999f1343SGarrett Wollman  * If don't have a local address for this socket yet,
291999f1343SGarrett Wollman  * then pick one.
292999f1343SGarrett Wollman  */
293999f1343SGarrett Wollman int
294999f1343SGarrett Wollman in_pcbconnect(inp, nam)
295999f1343SGarrett Wollman 	register struct inpcb *inp;
296999f1343SGarrett Wollman 	struct mbuf *nam;
297999f1343SGarrett Wollman {
298999f1343SGarrett Wollman 	struct sockaddr_in *ifaddr;
299999f1343SGarrett Wollman 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
300999f1343SGarrett Wollman 	int error;
301999f1343SGarrett Wollman 
302999f1343SGarrett Wollman 	/*
303999f1343SGarrett Wollman 	 *   Call inner routine, to assign local interface address.
304999f1343SGarrett Wollman 	 */
305999f1343SGarrett Wollman 	if (error = in_pcbladdr(inp, nam, &ifaddr))
306999f1343SGarrett Wollman 		return(error);
307999f1343SGarrett Wollman 
3080d7b7d3eSDavid Greenman 	if (in_pcblookuphash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port,
309df8bae1dSRodney W. Grimes 	    inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
3100d7b7d3eSDavid Greenman 	    inp->inp_lport) != NULL)
311df8bae1dSRodney W. Grimes 		return (EADDRINUSE);
312df8bae1dSRodney W. Grimes 	if (inp->inp_laddr.s_addr == INADDR_ANY) {
313df8bae1dSRodney W. Grimes 		if (inp->inp_lport == 0)
314df8bae1dSRodney W. Grimes 			(void)in_pcbbind(inp, (struct mbuf *)0);
315df8bae1dSRodney W. Grimes 		inp->inp_laddr = ifaddr->sin_addr;
316df8bae1dSRodney W. Grimes 	}
317df8bae1dSRodney W. Grimes 	inp->inp_faddr = sin->sin_addr;
318df8bae1dSRodney W. Grimes 	inp->inp_fport = sin->sin_port;
31915bd2b43SDavid Greenman 	in_pcbrehash(inp);
320df8bae1dSRodney W. Grimes 	return (0);
321df8bae1dSRodney W. Grimes }
322df8bae1dSRodney W. Grimes 
32326f9a767SRodney W. Grimes void
324df8bae1dSRodney W. Grimes in_pcbdisconnect(inp)
325df8bae1dSRodney W. Grimes 	struct inpcb *inp;
326df8bae1dSRodney W. Grimes {
327df8bae1dSRodney W. Grimes 
328df8bae1dSRodney W. Grimes 	inp->inp_faddr.s_addr = INADDR_ANY;
329df8bae1dSRodney W. Grimes 	inp->inp_fport = 0;
33015bd2b43SDavid Greenman 	in_pcbrehash(inp);
331df8bae1dSRodney W. Grimes 	if (inp->inp_socket->so_state & SS_NOFDREF)
332df8bae1dSRodney W. Grimes 		in_pcbdetach(inp);
333df8bae1dSRodney W. Grimes }
334df8bae1dSRodney W. Grimes 
33526f9a767SRodney W. Grimes void
336df8bae1dSRodney W. Grimes in_pcbdetach(inp)
337df8bae1dSRodney W. Grimes 	struct inpcb *inp;
338df8bae1dSRodney W. Grimes {
339df8bae1dSRodney W. Grimes 	struct socket *so = inp->inp_socket;
3407bc4aca7SDavid Greenman 	int s;
341df8bae1dSRodney W. Grimes 
342df8bae1dSRodney W. Grimes 	so->so_pcb = 0;
343df8bae1dSRodney W. Grimes 	sofree(so);
344df8bae1dSRodney W. Grimes 	if (inp->inp_options)
345df8bae1dSRodney W. Grimes 		(void)m_free(inp->inp_options);
346df8bae1dSRodney W. Grimes 	if (inp->inp_route.ro_rt)
347df8bae1dSRodney W. Grimes 		rtfree(inp->inp_route.ro_rt);
348df8bae1dSRodney W. Grimes 	ip_freemoptions(inp->inp_moptions);
3497bc4aca7SDavid Greenman 	s = splnet();
35015bd2b43SDavid Greenman 	LIST_REMOVE(inp, inp_hash);
35115bd2b43SDavid Greenman 	LIST_REMOVE(inp, inp_list);
3527bc4aca7SDavid Greenman 	splx(s);
353df8bae1dSRodney W. Grimes 	FREE(inp, M_PCB);
354df8bae1dSRodney W. Grimes }
355df8bae1dSRodney W. Grimes 
35626f9a767SRodney W. Grimes void
357df8bae1dSRodney W. Grimes in_setsockaddr(inp, nam)
358df8bae1dSRodney W. Grimes 	register struct inpcb *inp;
359df8bae1dSRodney W. Grimes 	struct mbuf *nam;
360df8bae1dSRodney W. Grimes {
361df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
362df8bae1dSRodney W. Grimes 
363df8bae1dSRodney W. Grimes 	nam->m_len = sizeof (*sin);
364df8bae1dSRodney W. Grimes 	sin = mtod(nam, struct sockaddr_in *);
365df8bae1dSRodney W. Grimes 	bzero((caddr_t)sin, sizeof (*sin));
366df8bae1dSRodney W. Grimes 	sin->sin_family = AF_INET;
367df8bae1dSRodney W. Grimes 	sin->sin_len = sizeof(*sin);
368df8bae1dSRodney W. Grimes 	sin->sin_port = inp->inp_lport;
369df8bae1dSRodney W. Grimes 	sin->sin_addr = inp->inp_laddr;
370df8bae1dSRodney W. Grimes }
371df8bae1dSRodney W. Grimes 
37226f9a767SRodney W. Grimes void
373df8bae1dSRodney W. Grimes in_setpeeraddr(inp, nam)
374df8bae1dSRodney W. Grimes 	struct inpcb *inp;
375df8bae1dSRodney W. Grimes 	struct mbuf *nam;
376df8bae1dSRodney W. Grimes {
377df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
378df8bae1dSRodney W. Grimes 
379df8bae1dSRodney W. Grimes 	nam->m_len = sizeof (*sin);
380df8bae1dSRodney W. Grimes 	sin = mtod(nam, struct sockaddr_in *);
381df8bae1dSRodney W. Grimes 	bzero((caddr_t)sin, sizeof (*sin));
382df8bae1dSRodney W. Grimes 	sin->sin_family = AF_INET;
383df8bae1dSRodney W. Grimes 	sin->sin_len = sizeof(*sin);
384df8bae1dSRodney W. Grimes 	sin->sin_port = inp->inp_fport;
385df8bae1dSRodney W. Grimes 	sin->sin_addr = inp->inp_faddr;
386df8bae1dSRodney W. Grimes }
387df8bae1dSRodney W. Grimes 
388df8bae1dSRodney W. Grimes /*
389df8bae1dSRodney W. Grimes  * Pass some notification to all connections of a protocol
390df8bae1dSRodney W. Grimes  * associated with address dst.  The local address and/or port numbers
391df8bae1dSRodney W. Grimes  * may be specified to limit the search.  The "usual action" will be
392df8bae1dSRodney W. Grimes  * taken, depending on the ctlinput cmd.  The caller must filter any
393df8bae1dSRodney W. Grimes  * cmds that are uninteresting (e.g., no error in the map).
394df8bae1dSRodney W. Grimes  * Call the protocol specific routine (if any) to report
395df8bae1dSRodney W. Grimes  * any errors for each matching socket.
396df8bae1dSRodney W. Grimes  *
397df8bae1dSRodney W. Grimes  * Must be called at splnet.
398df8bae1dSRodney W. Grimes  */
39926f9a767SRodney W. Grimes void
400df8bae1dSRodney W. Grimes in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify)
40115bd2b43SDavid Greenman 	struct inpcbhead *head;
402df8bae1dSRodney W. Grimes 	struct sockaddr *dst;
403df8bae1dSRodney W. Grimes 	u_int fport_arg, lport_arg;
404df8bae1dSRodney W. Grimes 	struct in_addr laddr;
405df8bae1dSRodney W. Grimes 	int cmd;
406df8bae1dSRodney W. Grimes 	void (*notify) __P((struct inpcb *, int));
407df8bae1dSRodney W. Grimes {
408df8bae1dSRodney W. Grimes 	register struct inpcb *inp, *oinp;
409df8bae1dSRodney W. Grimes 	struct in_addr faddr;
410df8bae1dSRodney W. Grimes 	u_short fport = fport_arg, lport = lport_arg;
4117bc4aca7SDavid Greenman 	int errno, s;
412df8bae1dSRodney W. Grimes 
413df8bae1dSRodney W. Grimes 	if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
414df8bae1dSRodney W. Grimes 		return;
415df8bae1dSRodney W. Grimes 	faddr = ((struct sockaddr_in *)dst)->sin_addr;
416df8bae1dSRodney W. Grimes 	if (faddr.s_addr == INADDR_ANY)
417df8bae1dSRodney W. Grimes 		return;
418df8bae1dSRodney W. Grimes 
419df8bae1dSRodney W. Grimes 	/*
420df8bae1dSRodney W. Grimes 	 * Redirects go to all references to the destination,
421df8bae1dSRodney W. Grimes 	 * and use in_rtchange to invalidate the route cache.
422df8bae1dSRodney W. Grimes 	 * Dead host indications: notify all references to the destination.
423df8bae1dSRodney W. Grimes 	 * Otherwise, if we have knowledge of the local port and address,
424df8bae1dSRodney W. Grimes 	 * deliver only to that socket.
425df8bae1dSRodney W. Grimes 	 */
426df8bae1dSRodney W. Grimes 	if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
427df8bae1dSRodney W. Grimes 		fport = 0;
428df8bae1dSRodney W. Grimes 		lport = 0;
429df8bae1dSRodney W. Grimes 		laddr.s_addr = 0;
430df8bae1dSRodney W. Grimes 		if (cmd != PRC_HOSTDEAD)
431df8bae1dSRodney W. Grimes 			notify = in_rtchange;
432df8bae1dSRodney W. Grimes 	}
433df8bae1dSRodney W. Grimes 	errno = inetctlerrmap[cmd];
4347bc4aca7SDavid Greenman 	s = splnet();
43515bd2b43SDavid Greenman 	for (inp = head->lh_first; inp != NULL;) {
436df8bae1dSRodney W. Grimes 		if (inp->inp_faddr.s_addr != faddr.s_addr ||
437df8bae1dSRodney W. Grimes 		    inp->inp_socket == 0 ||
438df8bae1dSRodney W. Grimes 		    (lport && inp->inp_lport != lport) ||
439df8bae1dSRodney W. Grimes 		    (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) ||
440df8bae1dSRodney W. Grimes 		    (fport && inp->inp_fport != fport)) {
44115bd2b43SDavid Greenman 			inp = inp->inp_list.le_next;
442df8bae1dSRodney W. Grimes 			continue;
443df8bae1dSRodney W. Grimes 		}
444df8bae1dSRodney W. Grimes 		oinp = inp;
44515bd2b43SDavid Greenman 		inp = inp->inp_list.le_next;
446df8bae1dSRodney W. Grimes 		if (notify)
447df8bae1dSRodney W. Grimes 			(*notify)(oinp, errno);
448df8bae1dSRodney W. Grimes 	}
4497bc4aca7SDavid Greenman 	splx(s);
450df8bae1dSRodney W. Grimes }
451df8bae1dSRodney W. Grimes 
452df8bae1dSRodney W. Grimes /*
453df8bae1dSRodney W. Grimes  * Check for alternatives when higher level complains
454df8bae1dSRodney W. Grimes  * about service problems.  For now, invalidate cached
455df8bae1dSRodney W. Grimes  * routing information.  If the route was created dynamically
456df8bae1dSRodney W. Grimes  * (by a redirect), time to try a default gateway again.
457df8bae1dSRodney W. Grimes  */
45826f9a767SRodney W. Grimes void
459df8bae1dSRodney W. Grimes in_losing(inp)
460df8bae1dSRodney W. Grimes 	struct inpcb *inp;
461df8bae1dSRodney W. Grimes {
462df8bae1dSRodney W. Grimes 	register struct rtentry *rt;
463df8bae1dSRodney W. Grimes 	struct rt_addrinfo info;
464df8bae1dSRodney W. Grimes 
465df8bae1dSRodney W. Grimes 	if ((rt = inp->inp_route.ro_rt)) {
466df8bae1dSRodney W. Grimes 		inp->inp_route.ro_rt = 0;
467df8bae1dSRodney W. Grimes 		bzero((caddr_t)&info, sizeof(info));
468df8bae1dSRodney W. Grimes 		info.rti_info[RTAX_DST] =
469df8bae1dSRodney W. Grimes 			(struct sockaddr *)&inp->inp_route.ro_dst;
470df8bae1dSRodney W. Grimes 		info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
471df8bae1dSRodney W. Grimes 		info.rti_info[RTAX_NETMASK] = rt_mask(rt);
472df8bae1dSRodney W. Grimes 		rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
473df8bae1dSRodney W. Grimes 		if (rt->rt_flags & RTF_DYNAMIC)
474df8bae1dSRodney W. Grimes 			(void) rtrequest(RTM_DELETE, rt_key(rt),
475df8bae1dSRodney W. Grimes 				rt->rt_gateway, rt_mask(rt), rt->rt_flags,
476df8bae1dSRodney W. Grimes 				(struct rtentry **)0);
477df8bae1dSRodney W. Grimes 		else
478df8bae1dSRodney W. Grimes 		/*
479df8bae1dSRodney W. Grimes 		 * A new route can be allocated
480df8bae1dSRodney W. Grimes 		 * the next time output is attempted.
481df8bae1dSRodney W. Grimes 		 */
482df8bae1dSRodney W. Grimes 			rtfree(rt);
483df8bae1dSRodney W. Grimes 	}
484df8bae1dSRodney W. Grimes }
485df8bae1dSRodney W. Grimes 
486df8bae1dSRodney W. Grimes /*
487df8bae1dSRodney W. Grimes  * After a routing change, flush old routing
488df8bae1dSRodney W. Grimes  * and allocate a (hopefully) better one.
489df8bae1dSRodney W. Grimes  */
4900312fbe9SPoul-Henning Kamp static void
491df8bae1dSRodney W. Grimes in_rtchange(inp, errno)
492df8bae1dSRodney W. Grimes 	register struct inpcb *inp;
493df8bae1dSRodney W. Grimes 	int errno;
494df8bae1dSRodney W. Grimes {
495df8bae1dSRodney W. Grimes 	if (inp->inp_route.ro_rt) {
496df8bae1dSRodney W. Grimes 		rtfree(inp->inp_route.ro_rt);
497df8bae1dSRodney W. Grimes 		inp->inp_route.ro_rt = 0;
498df8bae1dSRodney W. Grimes 		/*
499df8bae1dSRodney W. Grimes 		 * A new route can be allocated the next time
500df8bae1dSRodney W. Grimes 		 * output is attempted.
501df8bae1dSRodney W. Grimes 		 */
502df8bae1dSRodney W. Grimes 	}
503df8bae1dSRodney W. Grimes }
504df8bae1dSRodney W. Grimes 
505df8bae1dSRodney W. Grimes struct inpcb *
506df8bae1dSRodney W. Grimes in_pcblookup(head, faddr, fport_arg, laddr, lport_arg, flags)
50715bd2b43SDavid Greenman 	struct inpcbhead *head;
508df8bae1dSRodney W. Grimes 	struct in_addr faddr, laddr;
509df8bae1dSRodney W. Grimes 	u_int fport_arg, lport_arg;
510df8bae1dSRodney W. Grimes 	int flags;
511df8bae1dSRodney W. Grimes {
5123dbdc25cSDavid Greenman 	register struct inpcb *inp, *match = NULL;
513df8bae1dSRodney W. Grimes 	int matchwild = 3, wildcard;
514df8bae1dSRodney W. Grimes 	u_short fport = fport_arg, lport = lport_arg;
5157bc4aca7SDavid Greenman 	int s;
5167bc4aca7SDavid Greenman 
5177bc4aca7SDavid Greenman 	s = splnet();
518df8bae1dSRodney W. Grimes 
51915bd2b43SDavid Greenman 	for (inp = head->lh_first; inp != NULL; inp = inp->inp_list.le_next) {
520df8bae1dSRodney W. Grimes 		if (inp->inp_lport != lport)
521df8bae1dSRodney W. Grimes 			continue;
522df8bae1dSRodney W. Grimes 		wildcard = 0;
523df8bae1dSRodney W. Grimes 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
524df8bae1dSRodney W. Grimes 			if (faddr.s_addr == INADDR_ANY)
525df8bae1dSRodney W. Grimes 				wildcard++;
526df8bae1dSRodney W. Grimes 			else if (inp->inp_faddr.s_addr != faddr.s_addr ||
527df8bae1dSRodney W. Grimes 			    inp->inp_fport != fport)
528df8bae1dSRodney W. Grimes 				continue;
529df8bae1dSRodney W. Grimes 		} else {
530df8bae1dSRodney W. Grimes 			if (faddr.s_addr != INADDR_ANY)
531df8bae1dSRodney W. Grimes 				wildcard++;
532df8bae1dSRodney W. Grimes 		}
53315bd2b43SDavid Greenman 		if (inp->inp_laddr.s_addr != INADDR_ANY) {
53415bd2b43SDavid Greenman 			if (laddr.s_addr == INADDR_ANY)
53515bd2b43SDavid Greenman 				wildcard++;
53615bd2b43SDavid Greenman 			else if (inp->inp_laddr.s_addr != laddr.s_addr)
53715bd2b43SDavid Greenman 				continue;
53815bd2b43SDavid Greenman 		} else {
53915bd2b43SDavid Greenman 			if (laddr.s_addr != INADDR_ANY)
54015bd2b43SDavid Greenman 				wildcard++;
54115bd2b43SDavid Greenman 		}
542df8bae1dSRodney W. Grimes 		if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
543df8bae1dSRodney W. Grimes 			continue;
544df8bae1dSRodney W. Grimes 		if (wildcard < matchwild) {
545df8bae1dSRodney W. Grimes 			match = inp;
546df8bae1dSRodney W. Grimes 			matchwild = wildcard;
5473dbdc25cSDavid Greenman 			if (matchwild == 0) {
548df8bae1dSRodney W. Grimes 				break;
549df8bae1dSRodney W. Grimes 			}
550df8bae1dSRodney W. Grimes 		}
5513dbdc25cSDavid Greenman 	}
5527bc4aca7SDavid Greenman 	splx(s);
553df8bae1dSRodney W. Grimes 	return (match);
554df8bae1dSRodney W. Grimes }
55515bd2b43SDavid Greenman 
55615bd2b43SDavid Greenman /*
55715bd2b43SDavid Greenman  * Lookup PCB in hash list.
55815bd2b43SDavid Greenman  */
55915bd2b43SDavid Greenman struct inpcb *
56015bd2b43SDavid Greenman in_pcblookuphash(pcbinfo, faddr, fport_arg, laddr, lport_arg)
56115bd2b43SDavid Greenman 	struct inpcbinfo *pcbinfo;
56215bd2b43SDavid Greenman 	struct in_addr faddr, laddr;
56315bd2b43SDavid Greenman 	u_int fport_arg, lport_arg;
56415bd2b43SDavid Greenman {
56515bd2b43SDavid Greenman 	struct inpcbhead *head;
56615bd2b43SDavid Greenman 	register struct inpcb *inp;
56715bd2b43SDavid Greenman 	u_short fport = fport_arg, lport = lport_arg;
5687bc4aca7SDavid Greenman 	int s;
56915bd2b43SDavid Greenman 
5707bc4aca7SDavid Greenman 	s = splnet();
57115bd2b43SDavid Greenman 	/*
57215bd2b43SDavid Greenman 	 * First look for an exact match.
57315bd2b43SDavid Greenman 	 */
57415bd2b43SDavid Greenman 	head = &pcbinfo->hashbase[(faddr.s_addr + lport + fport) % pcbinfo->hashsize];
57515bd2b43SDavid Greenman 
57615bd2b43SDavid Greenman 	for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) {
57715bd2b43SDavid Greenman 		if (inp->inp_faddr.s_addr != faddr.s_addr ||
57815bd2b43SDavid Greenman 		    inp->inp_fport != fport ||
57915bd2b43SDavid Greenman 		    inp->inp_lport != lport ||
58015bd2b43SDavid Greenman 		    inp->inp_laddr.s_addr != laddr.s_addr)
58115bd2b43SDavid Greenman 			continue;
58215bd2b43SDavid Greenman 		/*
58315bd2b43SDavid Greenman 		 * Move PCB to head of this hash chain so that it can be
58415bd2b43SDavid Greenman 		 * found more quickly in the future.
58515bd2b43SDavid Greenman 		 */
58615bd2b43SDavid Greenman 		if (inp != head->lh_first) {
58715bd2b43SDavid Greenman 			LIST_REMOVE(inp, inp_hash);
58815bd2b43SDavid Greenman 			LIST_INSERT_HEAD(head, inp, inp_hash);
58915bd2b43SDavid Greenman 		}
5900d7b7d3eSDavid Greenman 		break;
59115bd2b43SDavid Greenman 	}
5927bc4aca7SDavid Greenman 	splx(s);
5930d7b7d3eSDavid Greenman 	return (inp);
59415bd2b43SDavid Greenman }
59515bd2b43SDavid Greenman 
5967bc4aca7SDavid Greenman /*
5977bc4aca7SDavid Greenman  * Insert PCB into hash chain. Must be called at splnet.
5987bc4aca7SDavid Greenman  */
5990312fbe9SPoul-Henning Kamp static void
60015bd2b43SDavid Greenman in_pcbinshash(inp)
60115bd2b43SDavid Greenman 	struct inpcb *inp;
60215bd2b43SDavid Greenman {
60315bd2b43SDavid Greenman 	struct inpcbhead *head;
60415bd2b43SDavid Greenman 
60515bd2b43SDavid Greenman 	head = &inp->inp_pcbinfo->hashbase[(inp->inp_faddr.s_addr +
60615bd2b43SDavid Greenman 		inp->inp_lport + inp->inp_fport) % inp->inp_pcbinfo->hashsize];
60715bd2b43SDavid Greenman 
60815bd2b43SDavid Greenman 	LIST_INSERT_HEAD(head, inp, inp_hash);
60915bd2b43SDavid Greenman }
61015bd2b43SDavid Greenman 
61115bd2b43SDavid Greenman void
61215bd2b43SDavid Greenman in_pcbrehash(inp)
61315bd2b43SDavid Greenman 	struct inpcb *inp;
61415bd2b43SDavid Greenman {
61515bd2b43SDavid Greenman 	struct inpcbhead *head;
6167bc4aca7SDavid Greenman 	int s;
61715bd2b43SDavid Greenman 
6187bc4aca7SDavid Greenman 	s = splnet();
61915bd2b43SDavid Greenman 	LIST_REMOVE(inp, inp_hash);
62015bd2b43SDavid Greenman 
62115bd2b43SDavid Greenman 	head = &inp->inp_pcbinfo->hashbase[(inp->inp_faddr.s_addr +
62215bd2b43SDavid Greenman 		inp->inp_lport + inp->inp_fport) % inp->inp_pcbinfo->hashsize];
62315bd2b43SDavid Greenman 
62415bd2b43SDavid Greenman 	LIST_INSERT_HEAD(head, inp, inp_hash);
6257bc4aca7SDavid Greenman 	splx(s);
62615bd2b43SDavid Greenman }
627