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