xref: /original-bsd/sys/netinet/in_pcb.c (revision fbed46ce)
1 /*	in_pcb.c	4.26	82/04/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_USERGONE 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 /*
52  * Allocate a protocol control block, space
53  * for send and receive data, and local host information.
54  * Return error.  If no error make socket point at pcb.
55  */
56 in_pcbattach(so, head, sndcc, rcvcc, sin)
57 	struct socket *so;
58 	struct inpcb *head;
59 	int sndcc, rcvcc;
60 	struct sockaddr_in *sin;
61 {
62 	struct mbuf *m;
63 	register struct inpcb *inp;
64 	u_short lport = 0;
65 
66 COUNT(IN_PCBATTACH);
67 	if (ifnet == 0)
68 		return (EADDRNOTAVAIL);
69 	if (sin) {
70 		if (sin->sin_family != AF_INET)
71 			return (EAFNOSUPPORT);
72 		if (sin->sin_addr.s_addr) {
73 			int tport = sin->sin_port;
74 
75 			sin->sin_port = 0;		/* yech... */
76 			if (if_ifwithaddr((struct sockaddr *)sin) == 0)
77 				return (EADDRNOTAVAIL);
78 			sin->sin_port = tport;
79 		}
80 		lport = sin->sin_port;
81 		if (lport) {
82 			u_short aport = lport;
83 			int wild = 0;
84 #if vax
85 			aport = htons(aport);
86 #endif
87 			/* GROSS */
88 			if (aport < IPPORT_RESERVED && u.u_uid != 0)
89 				return (EACCES);
90 			if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
91 			    (so->so_options & SO_ACCEPTCONN) == 0)
92 				wild = INPLOOKUP_WILDCARD;
93 			if (in_pcblookup(head,
94 			    zeroin_addr, 0, sin->sin_addr, lport, wild))
95 				return (EADDRINUSE);
96 		}
97 	}
98 	m = m_getclr(M_DONTWAIT);
99 	if (m == 0)
100 		return (ENOBUFS);
101 	if (sbreserve(&so->so_snd, sndcc) == 0)
102 		goto bad;
103 	if (sbreserve(&so->so_rcv, rcvcc) == 0)
104 		goto bad2;
105 	inp = mtod(m, struct inpcb *);
106 	inp->inp_head = head;
107 	if (sin)
108 		inp->inp_laddr = sin->sin_addr;
109 	if (lport == 0)
110 		do {
111 			if (head->inp_lport++ < IPPORT_RESERVED)
112 				head->inp_lport = IPPORT_RESERVED;
113 			lport = htons(head->inp_lport);
114 		} while (in_pcblookup(head,
115 			    zeroin_addr, 0, inp->inp_laddr, lport, 0));
116 	inp->inp_lport = lport;
117 	inp->inp_socket = so;
118 	insque(inp, head);
119 	so->so_pcb = (caddr_t)inp;
120 	return (0);
121 bad2:
122 	sbrelease(&so->so_snd);
123 bad:
124 	(void) m_free(m);
125 	return (ENOBUFS);
126 }
127 
128 /*
129  * Connect from a socket to a specified address.
130  * Both address and port must be specified in argument sin.
131  * If don't have a local address for this socket yet,
132  * then pick one.
133  */
134 in_pcbconnect(inp, sin)
135 	struct inpcb *inp;
136 	struct sockaddr_in *sin;
137 {
138 	struct ifnet *ifp;
139 	struct sockaddr_in *ifaddr;
140 
141 COUNT(IN_PCBCONNECT);
142 	if (sin->sin_family != AF_INET)
143 		return (EAFNOSUPPORT);
144 	if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0)
145 		return (EADDRNOTAVAIL);
146 	if (inp->inp_laddr.s_addr == 0) {
147 		ifp = if_ifonnetof(sin->sin_addr.s_net);
148 		if (ifp == 0) {
149 			/*
150 			 * We should select the interface based on
151 			 * the route to be used, but for udp this would
152 			 * result in two calls to rtalloc for each packet
153 			 * sent; hardly worthwhile...
154 			 */
155 			ifp = if_ifwithaf(AF_INET);
156 			if (ifp == 0)
157 				return (EADDRNOTAVAIL);
158 		}
159 		ifaddr = (struct sockaddr_in *)&ifp->if_addr;
160 	}
161 	if (in_pcblookup(inp->inp_head,
162 	    sin->sin_addr,
163 	    sin->sin_port,
164 	    inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
165 	    inp->inp_lport,
166 	    0))
167 		return (EADDRINUSE);
168 	if (inp->inp_laddr.s_addr == 0)
169 		inp->inp_laddr = ifaddr->sin_addr;
170 	inp->inp_faddr = sin->sin_addr;
171 	inp->inp_fport = sin->sin_port;
172 	return (0);
173 }
174 
175 in_pcbdisconnect(inp)
176 	struct inpcb *inp;
177 {
178 
179 COUNT(IN_PCBDISCONNECT);
180 	inp->inp_faddr.s_addr = 0;
181 	inp->inp_fport = 0;
182 	if (inp->inp_socket->so_state & SS_USERGONE)
183 		in_pcbdetach(inp);
184 }
185 
186 in_pcbdetach(inp)
187 	struct inpcb *inp;
188 {
189 	struct socket *so = inp->inp_socket;
190 
191 COUNT(IN_PCBDETACH);
192 	so->so_pcb = 0;
193 	sofree(so);
194 	if (inp->inp_route.ro_rt)
195 		rtfree(inp->inp_route.ro_rt);
196 	remque(inp);
197 	(void) m_free(dtom(inp));
198 }
199 
200 in_setsockaddr(sin, inp)
201 	register struct sockaddr_in *sin;
202 	register struct inpcb *inp;
203 {
204 COUNT(IN_SETSOCKADDR);
205 	if (sin == 0 || inp == 0)
206 		panic("setsockaddr_in");
207 	bzero((caddr_t)sin, sizeof (*sin));
208 	sin->sin_family = AF_INET;
209 	sin->sin_port = inp->inp_lport;
210 	sin->sin_addr = inp->inp_laddr;
211 }
212 
213 /*
214  * Pass an error to all internet connections
215  * associated with address sin.  Call the
216  * protocol specific routine to clean up the
217  * mess afterwards.
218  */
219 in_pcbnotify(head, dst, errno, abort)
220 	struct inpcb *head;
221 	register struct in_addr *dst;
222 	int errno, (*abort)();
223 {
224 	register struct inpcb *inp, *oinp;
225 	int s = splimp();
226 
227 COUNT(INP_PCBNOTIFY);
228 	for (inp = head->inp_next; inp != head;) {
229 		if (inp->inp_faddr.s_addr != dst->s_addr) {
230 	next:
231 			inp = inp->inp_next;
232 			continue;
233 		}
234 		if (inp->inp_socket == 0)
235 			goto next;
236 		inp->inp_socket->so_error = errno;
237 		oinp = inp;
238 		inp = inp->inp_next;
239 		(*abort)(oinp);
240 	}
241 	splx(s);
242 }
243 
244 /*
245  * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY
246  */
247 struct inpcb *
248 in_pcblookup(head, faddr, fport, laddr, lport, flags)
249 	struct inpcb *head;
250 	struct in_addr faddr, laddr;
251 	u_short fport, lport;
252 	int flags;
253 {
254 	register struct inpcb *inp, *match = 0;
255 	int matchwild = 3, wildcard;
256 
257 	for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
258 		if (inp->inp_lport != lport)
259 			continue;
260 		wildcard = 0;
261 		if (inp->inp_laddr.s_addr != 0) {
262 			if (laddr.s_addr == 0)
263 				wildcard++;
264 			else if (inp->inp_laddr.s_addr != laddr.s_addr)
265 				continue;
266 		} else {
267 			if (laddr.s_addr != 0)
268 				wildcard++;
269 		}
270 		if (inp->inp_faddr.s_addr != 0) {
271 			if (faddr.s_addr == 0)
272 				wildcard++;
273 			else if (inp->inp_faddr.s_addr != faddr.s_addr ||
274 			    inp->inp_fport != fport)
275 				continue;
276 		} else {
277 			if (faddr.s_addr != 0)
278 				wildcard++;
279 		}
280 		if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
281 			continue;
282 		if (wildcard < matchwild) {
283 			match = inp;
284 			matchwild = wildcard;
285 			if (matchwild == 0)
286 				break;
287 		}
288 	}
289 	return (match);
290 }
291