xref: /386bsd/usr/src/kernel/inet/in_pcb.c (revision dc8b130e)
1 /*
2  * Copyright (c) 1982, 1986, 1991 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  *	$Id: in_pcb.c,v 1.1 94/10/20 10:53:27 root Exp Locker: bill $
34  */
35 
36 #include "sys/param.h"
37 #include "sys/errno.h"
38 #include "systm.h"
39 #include "malloc.h"
40 #include "mbuf.h"
41 #include "sys/file.h"
42 #include "socketvar.h"
43 #include "protosw.h"
44 #include "sys/ioctl.h"
45 #include "prototypes.h"
46 
47 #include "if.h"
48 #include "route.h"
49 
50 #include "in.h"
51 #include "in_systm.h"
52 #include "ip.h"
53 #include "in_pcb.h"
54 #include "in_var.h"
55 
56 struct	in_addr zeroin_addr;
57 
58 in_pcballoc(so, head)
59 	struct socket *so;
60 	struct inpcb *head;
61 {
62 	struct mbuf *m;
63 	register struct inpcb *inp;
64 
65 	m = m_getclr(M_DONTWAIT, MT_PCB);
66 	if (m == NULL)
67 		return (ENOBUFS);
68 	inp = mtod(m, struct inpcb *);
69 	inp->inp_head = head;
70 	inp->inp_socket = so;
71 	insque(inp, head);
72 	so->so_pcb = (caddr_t)inp;
73 	return (0);
74 }
75 
in_pcbbind(inp,nam)76 in_pcbbind(inp, nam)
77 	register struct inpcb *inp;
78 	struct mbuf *nam;
79 {
80 	register struct socket *so = inp->inp_socket;
81 	register struct inpcb *head = inp->inp_head;
82 	register struct sockaddr_in *sin;
83 	u_short lport = 0;
84 	int wild = 0;
85 
86 	if (in_ifaddr == 0)
87 		return (EADDRNOTAVAIL);
88 	if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
89 		return (EINVAL);
90 
91 	if ((so->so_options & SO_REUSEADDR) == 0 &&
92 	    ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
93 	     (so->so_options & SO_ACCEPTCONN) == 0))
94 		wild = INPLOOKUP_WILDCARD;
95 
96 	if (nam == 0)
97 		goto noname;
98 	sin = mtod(nam, struct sockaddr_in *);
99 	if (nam->m_len != sizeof (*sin))
100 		return (EINVAL);
101 	if (sin->sin_addr.s_addr != INADDR_ANY) {
102 		int tport = sin->sin_port;
103 
104 		sin->sin_port = 0;		/* yech... */
105 		if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
106 			return (EADDRNOTAVAIL);
107 		sin->sin_port = tport;
108 	}
109 	lport = sin->sin_port;
110 	if (lport) {
111 		u_short aport = ntohs(lport);
112 
113 		/* GROSS */
114 		if (aport < IPPORT_RESERVED && (so->so_state & SS_PRIV) == 0)
115 			return (EACCES);
116 		if (in_pcblookup(head,
117 		    zeroin_addr, 0, sin->sin_addr, lport, wild))
118 			return (EADDRINUSE);
119 	}
120 	inp->inp_laddr = sin->sin_addr;
121 noname:
122 	if (lport == 0)
123 		do {
124 			if (head->inp_lport++ < IPPORT_RESERVED ||
125 			    head->inp_lport > IPPORT_USERRESERVED)
126 				head->inp_lport = IPPORT_RESERVED;
127 			lport = htons(head->inp_lport);
128 		} while (in_pcblookup(head,
129 			    zeroin_addr, 0, inp->inp_laddr, lport, wild));
130 	inp->inp_lport = lport;
131 	return (0);
132 }
133 
134 /*
135  * Connect from a socket to a specified address.
136  * Both address and port must be specified in argument sin.
137  * If don't have a local address for this socket yet,
138  * then pick one.
139  */
in_pcbconnect(inp,nam)140 in_pcbconnect(inp, nam)
141 	register struct inpcb *inp;
142 	struct mbuf *nam;
143 {
144 	struct in_ifaddr *ia;
145 	struct sockaddr_in *ifaddr;
146 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
147 
148 	if (nam->m_len != sizeof (*sin))
149 		return (EINVAL);
150 	if (sin->sin_family != AF_INET)
151 		return (EAFNOSUPPORT);
152 	if (sin->sin_port == 0)
153 		return (EADDRNOTAVAIL);
154 	if (in_ifaddr) {
155 		/*
156 		 * If the destination address is INADDR_ANY,
157 		 * use the primary local address.
158 		 * If the supplied address is INADDR_BROADCAST,
159 		 * and the primary interface supports broadcast,
160 		 * choose the broadcast address for that interface.
161 		 */
162 #define	satosin(sa)	((struct sockaddr_in *)(sa))
163 		if (sin->sin_addr.s_addr == INADDR_ANY)
164 		    sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
165 		else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
166 		  (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST))
167 		    sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr;
168 	}
169 	if (inp->inp_laddr.s_addr == INADDR_ANY) {
170 		register struct route *ro;
171 		struct ifnet *ifp;
172 
173 		ia = (struct in_ifaddr *)0;
174 		/*
175 		 * If route is known or can be allocated now,
176 		 * our src addr is taken from the i/f, else punt.
177 		 */
178 		ro = &inp->inp_route;
179 		if (ro->ro_rt &&
180 		    (satosin(&ro->ro_dst)->sin_addr.s_addr !=
181 			sin->sin_addr.s_addr ||
182 		    inp->inp_socket->so_options & SO_DONTROUTE)) {
183 			RTFREE_(ro->ro_rt);
184 			ro->ro_rt = (struct rtentry *)0;
185 		}
186 		if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
187 		    (ro->ro_rt == (struct rtentry *)0 ||
188 		    ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
189 			/* No route yet, so try to acquire one */
190 			ro->ro_dst.sa_family = AF_INET;
191 			ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
192 			((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
193 				sin->sin_addr;
194 			rtalloc(ro);
195 		}
196 		/*
197 		 * If we found a route, use the address
198 		 * corresponding to the outgoing interface
199 		 * unless it is the loopback (in case a route
200 		 * to our address on another net goes to loopback).
201 		 */
202 		if (ro->ro_rt && (ifp = ro->ro_rt->rt_ifp) &&
203 		    (ifp->if_flags & IFF_LOOPBACK) == 0)
204 			for (ia = in_ifaddr; ia; ia = ia->ia_next)
205 				if (ia->ia_ifp == ifp)
206 					break;
207 		if (ia == 0) {
208 			int fport = sin->sin_port;
209 
210 			sin->sin_port = 0;
211 			ia = (struct in_ifaddr *)
212 			    ifa_ifwithdstaddr((struct sockaddr *)sin);
213 			sin->sin_port = fport;
214 			if (ia == 0)
215 				ia = in_iaonnetof(in_netof(sin->sin_addr));
216 			if (ia == 0)
217 				ia = in_ifaddr;
218 			if (ia == 0)
219 				return (EADDRNOTAVAIL);
220 		}
221 		ifaddr = (struct sockaddr_in *)&ia->ia_addr;
222 	}
223 	if (in_pcblookup(inp->inp_head,
224 	    sin->sin_addr,
225 	    sin->sin_port,
226 	    inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
227 	    inp->inp_lport,
228 	    0))
229 		return (EADDRINUSE);
230 	if (inp->inp_laddr.s_addr == INADDR_ANY) {
231 		if (inp->inp_lport == 0)
232 			(void)in_pcbbind(inp, (struct mbuf *)0);
233 		inp->inp_laddr = ifaddr->sin_addr;
234 	}
235 	inp->inp_faddr = sin->sin_addr;
236 	inp->inp_fport = sin->sin_port;
237 	return (0);
238 }
239 
240 in_pcbdisconnect(inp)
241 	struct inpcb *inp;
242 {
243 
244 	inp->inp_faddr.s_addr = INADDR_ANY;
245 	inp->inp_fport = 0;
246 	if (inp->inp_socket->so_state & SS_NOFDREF)
247 		in_pcbdetach(inp);
248 }
249 
250 in_pcbdetach(inp)
251 	struct inpcb *inp;
252 {
253 	struct socket *so = inp->inp_socket;
254 
255 	so->so_pcb = 0;
256 	sofree(so);
257 	if (inp->inp_options)
258 		(void)m_free(inp->inp_options);
259 	if (inp->inp_route.ro_rt)
260 		rtfree(inp->inp_route.ro_rt);
261 	remque(inp);
262 	(void) m_free(dtom(inp));
263 }
264 
in_setsockaddr(inp,nam)265 in_setsockaddr(inp, nam)
266 	register struct inpcb *inp;
267 	struct mbuf *nam;
268 {
269 	register struct sockaddr_in *sin;
270 
271 	nam->m_len = sizeof (*sin);
272 	sin = mtod(nam, struct sockaddr_in *);
273 	(void) memset((caddr_t)sin, 0, sizeof (*sin));
274 	sin->sin_family = AF_INET;
275 	sin->sin_len = sizeof(*sin);
276 	sin->sin_port = inp->inp_lport;
277 	sin->sin_addr = inp->inp_laddr;
278 }
279 
280 in_setpeeraddr(inp, nam)
281 	struct inpcb *inp;
282 	struct mbuf *nam;
283 {
284 	register struct sockaddr_in *sin;
285 
286 	nam->m_len = sizeof (*sin);
287 	sin = mtod(nam, struct sockaddr_in *);
288 	(void) memset((caddr_t)sin, 0, sizeof (*sin));
289 	sin->sin_family = AF_INET;
290 	sin->sin_len = sizeof(*sin);
291 	sin->sin_port = inp->inp_fport;
292 	sin->sin_addr = inp->inp_faddr;
293 }
294 
295 /*
296  * Pass some notification to all connections of a protocol
297  * associated with address dst.  The local address and/or port numbers
298  * may be specified to limit the search.  The "usual action" will be
299  * taken, depending on the ctlinput cmd.  The caller must filter any
300  * cmds that are uninteresting (e.g., no error in the map).
301  * Call the protocol specific routine (if any) to report
302  * any errors for each matching socket.
303  *
304  * Must be called at splnet.
305  */
306 in_pcbnotify(head, dst, fport, laddr, lport, cmd, notify)
307 	struct inpcb *head;
308 	struct sockaddr *dst;
309 	u_short fport, lport;
310 	struct in_addr laddr;
311 	int cmd, (*notify)();
312 {
313 	register struct inpcb *inp, *oinp;
314 	struct in_addr faddr;
315 	int errno;
316 	int in_rtchange();
317 	extern u_char inetctlerrmap[];
318 
319 	if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
320 		return;
321 	faddr = ((struct sockaddr_in *)dst)->sin_addr;
322 	if (faddr.s_addr == INADDR_ANY)
323 		return;
324 
325 	/*
326 	 * Redirects go to all references to the destination,
327 	 * and use in_rtchange to invalidate the route cache.
328 	 * Dead host indications: notify all references to the destination.
329 	 * Otherwise, if we have knowledge of the local port and address,
330 	 * deliver only to that socket.
331 	 */
332 	if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
333 		fport = 0;
334 		lport = 0;
335 		laddr.s_addr = 0;
336 		if (cmd != PRC_HOSTDEAD)
337 			notify = in_rtchange;
338 	}
339 	errno = inetctlerrmap[cmd];
340 	for (inp = head->inp_next; inp != head;) {
341 		if (inp->inp_faddr.s_addr != faddr.s_addr ||
342 		    inp->inp_socket == 0 ||
343 		    (lport && inp->inp_lport != lport) ||
344 		    (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) ||
345 		    (fport && inp->inp_fport != fport)) {
346 			inp = inp->inp_next;
347 			continue;
348 		}
349 		oinp = inp;
350 		inp = inp->inp_next;
351 		if (notify)
352 			(*notify)(oinp, errno);
353 	}
354 }
355 
356 /*
357  * Check for alternatives when higher level complains
358  * about service problems.  For now, invalidate cached
359  * routing information.  If the route was created dynamically
360  * (by a redirect), time to try a default gateway again.
361  */
362 in_losing(inp)
363 	struct inpcb *inp;
364 {
365 	register struct rtentry *rt;
366 
367 	if ((rt = inp->inp_route.ro_rt)) {
368 		rtmissmsg(RTM_LOSING, &inp->inp_route.ro_dst,
369 			rt->rt_gateway, (struct sockaddr *)rt_mask(rt),
370 			    (struct sockaddr *)0, rt->rt_flags, 0);
371 		if (rt->rt_flags & RTF_DYNAMIC)
372 			(void) rtrequest(RTM_DELETE, rt_key(rt),
373 				rt->rt_gateway, rt_mask(rt), rt->rt_flags,
374 				(struct rtentry **)0);
375 		inp->inp_route.ro_rt = 0;
376 		rtfree(rt);
377 		/*
378 		 * A new route can be allocated
379 		 * the next time output is attempted.
380 		 */
381 	}
382 }
383 
384 /*
385  * After a routing change, flush old routing
386  * and allocate a (hopefully) better one.
387  */
in_rtchange(inp)388 in_rtchange(inp)
389 	register struct inpcb *inp;
390 {
391 	if (inp->inp_route.ro_rt) {
392 		rtfree(inp->inp_route.ro_rt);
393 		inp->inp_route.ro_rt = 0;
394 		/*
395 		 * A new route can be allocated the next time
396 		 * output is attempted.
397 		 */
398 	}
399 }
400 
401 struct inpcb *
in_pcblookup(head,faddr,fport,laddr,lport,flags)402 in_pcblookup(head, faddr, fport, laddr, lport, flags)
403 	struct inpcb *head;
404 	struct in_addr faddr, laddr;
405 	u_short fport, lport;
406 	int flags;
407 {
408 	register struct inpcb *inp, *match = 0;
409 	int matchwild = 3, wildcard;
410 
411 	for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
412 		if (inp->inp_lport != lport)
413 			continue;
414 		wildcard = 0;
415 		if (inp->inp_laddr.s_addr != INADDR_ANY) {
416 			if (laddr.s_addr == INADDR_ANY)
417 				wildcard++;
418 			else if (inp->inp_laddr.s_addr != laddr.s_addr)
419 				continue;
420 		} else {
421 			if (laddr.s_addr != INADDR_ANY)
422 				wildcard++;
423 		}
424 		if (inp->inp_faddr.s_addr != INADDR_ANY) {
425 			if (faddr.s_addr == INADDR_ANY)
426 				wildcard++;
427 			else if (inp->inp_faddr.s_addr != faddr.s_addr ||
428 			    inp->inp_fport != fport)
429 				continue;
430 		} else {
431 			if (faddr.s_addr != INADDR_ANY)
432 				wildcard++;
433 		}
434 		if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
435 			continue;
436 		if (wildcard < matchwild) {
437 			match = inp;
438 			matchwild = wildcard;
439 			if (matchwild == 0)
440 				break;
441 		}
442 	}
443 	return (match);
444 }
445