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