xref: /csrg-svn/sys/netinet/in_pcb.c (revision 7512)
1 /*	in_pcb.c	4.30	82/07/24	*/
2 
3 #include "../h/param.h"
4 #include "../h/systm.h"
5 #include "../h/dir.h"
6 #include "../h/user.h"
7 #include "../h/mbuf.h"
8 #include "../h/socket.h"
9 #include "../h/socketvar.h"
10 #include "../net/in.h"
11 #include "../net/in_systm.h"
12 #include "../net/if.h"
13 #include "../net/route.h"
14 #include "../net/in_pcb.h"
15 #include "../h/protosw.h"
16 
17 /*
18  * Routines to manage internet protocol control blocks.
19  *
20  * At PRU_ATTACH time a protocol control block is allocated in
21  * in_pcballoc() and inserted on a doubly-linked list of such blocks
22  * for the protocol.  A port address is either requested (and verified
23  * to not be in use) or assigned at this time.  We also allocate
24  * space in the socket sockbuf structures here, although this is
25  * not a clearly correct place to put this function.
26  *
27  * A connectionless protocol will have its protocol control block
28  * removed at PRU_DETACH time, when the socket will be freed (freeing
29  * the space reserved) and the block will be removed from the list of
30  * blocks for its protocol.
31  *
32  * A connection-based protocol may be connected to a remote peer at
33  * PRU_CONNECT time through the routine in_pcbconnect().  In the normal
34  * case a PRU_DISCONNECT occurs causing a in_pcbdisconnect().
35  * It is also possible that higher-level routines will opt out of the
36  * relationship with the connection before the connection shut down
37  * is complete.  This often occurs in protocols like TCP where we must
38  * hold on to the protocol control block for a unreasonably long time
39  * after the connection is used up to avoid races in later connection
40  * establishment.  To handle this we allow higher-level routines to
41  * disassociate themselves from the socket, marking it SS_NOFDREF while
42  * the disconnect is in progress.  We notice that this has happened
43  * when the disconnect is complete, and perform the PRU_DETACH operation,
44  * freeing the socket.
45  *
46  * TODO:
47  *	use hashing
48  */
49 struct	in_addr zeroin_addr;
50 
51 in_pcbreserve(so, sndcc, rcvcc)
52 	struct socket *so;
53 	int sndcc, rcvcc;
54 {
55 
56 	if (sbreserve(&so->so_snd, sndcc) == 0)
57 		goto bad;
58 	if (sbreserve(&so->so_rcv, rcvcc) == 0)
59 		goto bad2;
60 	return (0);
61 bad2:
62 	sbrelease(&so->so_snd);
63 bad:
64 	return (ENOBUFS);
65 }
66 
67 in_pcballoc(so, head)
68 	struct socket *so;
69 	struct inpcb *head;
70 {
71 	struct mbuf *m;
72 	register struct inpcb *inp;
73 
74 	m = m_getclr(M_DONTWAIT);
75 	if (m == 0)
76 		return (ENOBUFS);
77 	inp = mtod(m, struct inpcb *);
78 	inp->inp_head = head;
79 	inp->inp_socket = so;
80 	insque(inp, head);
81 	so->so_pcb = (caddr_t)inp;
82 	return (0);
83 }
84 
85 in_pcbbind(inp, sin)
86 	register struct inpcb *inp;
87 	struct sockaddr_in *sin;
88 {
89 	register struct socket *so = inp->inp_socket;
90 	register struct inpcb *head = inp->inp_head;
91 	u_short lport = 0;
92 
93 	if (ifnet == 0)
94 		return (EADDRNOTAVAIL);
95 	if (sin) {
96 		if (sin->sin_family != AF_INET)
97 			return (EAFNOSUPPORT);
98 		if (sin->sin_addr.s_addr) {
99 			int tport = sin->sin_port;
100 
101 			sin->sin_port = 0;		/* yech... */
102 			if (if_ifwithaddr((struct sockaddr *)sin) == 0)
103 				return (EADDRNOTAVAIL);
104 			sin->sin_port = tport;
105 		}
106 		lport = sin->sin_port;
107 		if (lport) {
108 			u_short aport = lport;
109 			int wild = 0;
110 #if vax
111 			aport = htons(aport);
112 #endif
113 			/* GROSS */
114 			if (aport < IPPORT_RESERVED && u.u_uid != 0)
115 				return (EACCES);
116 			if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
117 			    (so->so_options & SO_ACCEPTCONN) == 0)
118 				wild = INPLOOKUP_WILDCARD;
119 			if (in_pcblookup(head,
120 			    zeroin_addr, 0, sin->sin_addr, lport, wild))
121 				return (EADDRINUSE);
122 		}
123 	}
124 	if (sin)
125 		inp->inp_laddr = sin->sin_addr;
126 	if (lport == 0)
127 		do {
128 			if (head->inp_lport++ < IPPORT_RESERVED)
129 				head->inp_lport = IPPORT_RESERVED;
130 			lport = htons(head->inp_lport);
131 		} while (in_pcblookup(head,
132 			    zeroin_addr, 0, inp->inp_laddr, lport, 0));
133 	inp->inp_lport = lport;
134 	return (0);
135 }
136 
137 /* BEGIN DEPRECATED */
138 /*
139  * Allocate a protocol control block, space
140  * for send and receive data, and local host information.
141  * Return error.  If no error make socket point at pcb.
142  */
143 in_pcbattach(so, head, sndcc, rcvcc, sin)
144 	struct socket *so;
145 	struct inpcb *head;
146 	int sndcc, rcvcc;
147 	struct sockaddr_in *sin;
148 {
149 	struct mbuf *m;
150 	register struct inpcb *inp;
151 	u_short lport = 0;
152 
153 	if (ifnet == 0)
154 		return (EADDRNOTAVAIL);
155 	if (sin) {
156 		if (sin->sin_family != AF_INET)
157 			return (EAFNOSUPPORT);
158 		if (sin->sin_addr.s_addr) {
159 			int tport = sin->sin_port;
160 
161 			sin->sin_port = 0;		/* yech... */
162 			if (if_ifwithaddr((struct sockaddr *)sin) == 0)
163 				return (EADDRNOTAVAIL);
164 			sin->sin_port = tport;
165 		}
166 		lport = sin->sin_port;
167 		if (lport) {
168 			u_short aport = lport;
169 			int wild = 0;
170 #if vax
171 			aport = htons(aport);
172 #endif
173 			/* GROSS */
174 			if (aport < IPPORT_RESERVED && u.u_uid != 0)
175 				return (EACCES);
176 			if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
177 			    (so->so_options & SO_ACCEPTCONN) == 0)
178 				wild = INPLOOKUP_WILDCARD;
179 			if (in_pcblookup(head,
180 			    zeroin_addr, 0, sin->sin_addr, lport, wild))
181 				return (EADDRINUSE);
182 		}
183 	}
184 	m = m_getclr(M_DONTWAIT);
185 	if (m == 0)
186 		return (ENOBUFS);
187 	if (sbreserve(&so->so_snd, sndcc) == 0)
188 		goto bad;
189 	if (sbreserve(&so->so_rcv, rcvcc) == 0)
190 		goto bad2;
191 	inp = mtod(m, struct inpcb *);
192 	inp->inp_head = head;
193 	if (sin)
194 		inp->inp_laddr = sin->sin_addr;
195 	if (lport == 0)
196 		do {
197 			if (head->inp_lport++ < IPPORT_RESERVED)
198 				head->inp_lport = IPPORT_RESERVED;
199 			lport = htons(head->inp_lport);
200 		} while (in_pcblookup(head,
201 			    zeroin_addr, 0, inp->inp_laddr, lport, 0));
202 	inp->inp_lport = lport;
203 	inp->inp_socket = so;
204 	insque(inp, head);
205 	so->so_pcb = (caddr_t)inp;
206 	return (0);
207 bad2:
208 	sbrelease(&so->so_snd);
209 bad:
210 	(void) m_free(m);
211 	return (ENOBUFS);
212 }
213 /* END DEPRECATED */
214 
215 /*
216  * Connect from a socket to a specified address.
217  * Both address and port must be specified in argument sin.
218  * If don't have a local address for this socket yet,
219  * then pick one.
220  */
221 in_pcbconnect(inp, sin)
222 	struct inpcb *inp;
223 	struct sockaddr_in *sin;
224 {
225 	struct ifnet *ifp;
226 	struct sockaddr_in *ifaddr;
227 
228 	if (sin->sin_family != AF_INET)
229 		return (EAFNOSUPPORT);
230 	if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0)
231 		return (EADDRNOTAVAIL);
232 	if (inp->inp_laddr.s_addr == 0) {
233 		ifp = if_ifonnetof(in_netof(sin->sin_addr));
234 		if (ifp == 0) {
235 			/*
236 			 * We should select the interface based on
237 			 * the route to be used, but for udp this would
238 			 * result in two calls to rtalloc for each packet
239 			 * sent; hardly worthwhile...
240 			 */
241 			ifp = if_ifwithaf(AF_INET);
242 			if (ifp == 0)
243 				return (EADDRNOTAVAIL);
244 		}
245 		ifaddr = (struct sockaddr_in *)&ifp->if_addr;
246 	}
247 	if (in_pcblookup(inp->inp_head,
248 	    sin->sin_addr,
249 	    sin->sin_port,
250 	    inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
251 	    inp->inp_lport,
252 	    0))
253 		return (EADDRINUSE);
254 	if (inp->inp_laddr.s_addr == 0)
255 		inp->inp_laddr = ifaddr->sin_addr;
256 	inp->inp_faddr = sin->sin_addr;
257 	inp->inp_fport = sin->sin_port;
258 	return (0);
259 }
260 
261 in_pcbdisconnect(inp)
262 	struct inpcb *inp;
263 {
264 
265 	inp->inp_faddr.s_addr = 0;
266 	inp->inp_fport = 0;
267 	if (inp->inp_socket->so_state & SS_NOFDREF)
268 		in_pcbdetach(inp);
269 }
270 
271 in_pcbdetach(inp)
272 	struct inpcb *inp;
273 {
274 	struct socket *so = inp->inp_socket;
275 
276 	so->so_pcb = 0;
277 	sofree(so);
278 	if (inp->inp_route.ro_rt)
279 		rtfree(inp->inp_route.ro_rt);
280 	remque(inp);
281 	(void) m_free(dtom(inp));
282 }
283 
284 in_setsockaddr(sin, inp)
285 	register struct sockaddr_in *sin;
286 	register struct inpcb *inp;
287 {
288 	if (sin == 0 || inp == 0)
289 		panic("setsockaddr_in");
290 	bzero((caddr_t)sin, sizeof (*sin));
291 	sin->sin_family = AF_INET;
292 	sin->sin_port = inp->inp_lport;
293 	sin->sin_addr = inp->inp_laddr;
294 }
295 
296 /*
297  * Pass an error to all internet connections
298  * associated with address sin.  Call the
299  * protocol specific routine to clean up the
300  * mess afterwards.
301  */
302 in_pcbnotify(head, dst, errno, abort)
303 	struct inpcb *head;
304 	register struct in_addr *dst;
305 	int errno, (*abort)();
306 {
307 	register struct inpcb *inp, *oinp;
308 	int s = splimp();
309 
310 	for (inp = head->inp_next; inp != head;) {
311 		if (inp->inp_faddr.s_addr != dst->s_addr) {
312 	next:
313 			inp = inp->inp_next;
314 			continue;
315 		}
316 		if (inp->inp_socket == 0)
317 			goto next;
318 		inp->inp_socket->so_error = errno;
319 		oinp = inp;
320 		inp = inp->inp_next;
321 		(*abort)(oinp);
322 	}
323 	splx(s);
324 }
325 
326 /*
327  * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY
328  */
329 struct inpcb *
330 in_pcblookup(head, faddr, fport, laddr, lport, flags)
331 	struct inpcb *head;
332 	struct in_addr faddr, laddr;
333 	u_short fport, lport;
334 	int flags;
335 {
336 	register struct inpcb *inp, *match = 0;
337 	int matchwild = 3, wildcard;
338 
339 	for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
340 		if (inp->inp_lport != lport)
341 			continue;
342 		wildcard = 0;
343 		if (inp->inp_laddr.s_addr != 0) {
344 			if (laddr.s_addr == 0)
345 				wildcard++;
346 			else if (inp->inp_laddr.s_addr != laddr.s_addr)
347 				continue;
348 		} else {
349 			if (laddr.s_addr != 0)
350 				wildcard++;
351 		}
352 		if (inp->inp_faddr.s_addr != 0) {
353 			if (faddr.s_addr == 0)
354 				wildcard++;
355 			else if (inp->inp_faddr.s_addr != faddr.s_addr ||
356 			    inp->inp_fport != fport)
357 				continue;
358 		} else {
359 			if (faddr.s_addr != 0)
360 				wildcard++;
361 		}
362 		if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
363 			continue;
364 		if (wildcard < matchwild) {
365 			match = inp;
366 			matchwild = wildcard;
367 			if (matchwild == 0)
368 				break;
369 		}
370 	}
371 	return (match);
372 }
373