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