xref: /csrg-svn/sys/netinet/tcp_usrreq.c (revision 18367)
1 /*	tcp_usrreq.c	6.5	85/03/18	*/
2 
3 #include "param.h"
4 #include "systm.h"
5 #include "mbuf.h"
6 #include "socket.h"
7 #include "socketvar.h"
8 #include "protosw.h"
9 #include "errno.h"
10 #include "stat.h"
11 
12 #include "../net/if.h"
13 #include "../net/route.h"
14 
15 #include "in.h"
16 #include "in_pcb.h"
17 #include "in_systm.h"
18 #include "ip.h"
19 #include "ip_var.h"
20 #include "tcp.h"
21 #include "tcp_fsm.h"
22 #include "tcp_seq.h"
23 #include "tcp_timer.h"
24 #include "tcp_var.h"
25 #include "tcpip.h"
26 #include "tcp_debug.h"
27 
28 /*
29  * TCP protocol interface to socket abstraction.
30  */
31 extern	char *tcpstates[];
32 struct	tcpcb *tcp_newtcpcb();
33 int	tcpsenderrors;
34 
35 /*
36  * Process a TCP user request for TCP tb.  If this is a send request
37  * then m is the mbuf chain of send data.  If this is a timer expiration
38  * (called from the software clock routine), then timertype tells which timer.
39  */
40 /*ARGSUSED*/
41 tcp_usrreq(so, req, m, nam, rights)
42 	struct socket *so;
43 	int req;
44 	struct mbuf *m, *nam, *rights;
45 {
46 	register struct inpcb *inp = sotoinpcb(so);
47 	register struct tcpcb *tp;
48 	int s = splnet();
49 	int error = 0;
50 	int ostate;
51 
52 	if (req == PRU_CONTROL)
53 		return (in_control(so, (int)m, (caddr_t)nam,
54 			(struct ifnet *)rights));
55 	if (rights && rights->m_len) {
56 		splx(s);
57 		return (EINVAL);
58 	}
59 	/*
60 	 * When a TCP is attached to a socket, then there will be
61 	 * a (struct inpcb) pointed at by the socket, and this
62 	 * structure will point at a subsidary (struct tcpcb).
63 	 */
64 	if (inp == 0 && req != PRU_ATTACH) {
65 		splx(s);
66 		return (EINVAL);		/* XXX */
67 	}
68 	if (inp) {
69 		tp = intotcpcb(inp);
70 		/* WHAT IF TP IS 0? */
71 #ifdef KPROF
72 		tcp_acounts[tp->t_state][req]++;
73 #endif
74 		ostate = tp->t_state;
75 	} else
76 		ostate = 0;
77 	switch (req) {
78 
79 	/*
80 	 * TCP attaches to socket via PRU_ATTACH, reserving space,
81 	 * and an internet control block.
82 	 */
83 	case PRU_ATTACH:
84 		if (inp) {
85 			error = EISCONN;
86 			break;
87 		}
88 		error = tcp_attach(so);
89 		if (error)
90 			break;
91 		if ((so->so_options & SO_LINGER) && so->so_linger == 0)
92 			so->so_linger = TCP_LINGERTIME;
93 		tp = sototcpcb(so);
94 		break;
95 
96 	/*
97 	 * PRU_DETACH detaches the TCP protocol from the socket.
98 	 * If the protocol state is non-embryonic, then can't
99 	 * do this directly: have to initiate a PRU_DISCONNECT,
100 	 * which may finish later; embryonic TCB's can just
101 	 * be discarded here.
102 	 */
103 	case PRU_DETACH:
104 		if (tp->t_state > TCPS_LISTEN)
105 			tp = tcp_disconnect(tp);
106 		else
107 			tp = tcp_close(tp);
108 		break;
109 
110 	/*
111 	 * Give the socket an address.
112 	 */
113 	case PRU_BIND:
114 		error = in_pcbbind(inp, nam);
115 		if (error)
116 			break;
117 		break;
118 
119 	/*
120 	 * Prepare to accept connections.
121 	 */
122 	case PRU_LISTEN:
123 		if (inp->inp_lport == 0)
124 			error = in_pcbbind(inp, (struct mbuf *)0);
125 		if (error == 0)
126 			tp->t_state = TCPS_LISTEN;
127 		break;
128 
129 	/*
130 	 * Initiate connection to peer.
131 	 * Create a template for use in transmissions on this connection.
132 	 * Enter SYN_SENT state, and mark socket as connecting.
133 	 * Start keep-alive timer, and seed output sequence space.
134 	 * Send initial segment on connection.
135 	 */
136 	case PRU_CONNECT:
137 		if (inp->inp_lport == 0) {
138 			error = in_pcbbind(inp, (struct mbuf *)0);
139 			if (error)
140 				break;
141 		}
142 		error = in_pcbconnect(inp, nam);
143 		if (error)
144 			break;
145 		tp->t_template = tcp_template(tp);
146 		if (tp->t_template == 0) {
147 			in_pcbdisconnect(inp);
148 			error = ENOBUFS;
149 			break;
150 		}
151 		soisconnecting(so);
152 		tp->t_state = TCPS_SYN_SENT;
153 		tp->t_timer[TCPT_KEEP] = TCPTV_KEEP;
154 		tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2;
155 		tcp_sendseqinit(tp);
156 		error = tcp_output(tp);
157 		break;
158 
159 	/*
160 	 * Create a TCP connection between two sockets.
161 	 */
162 	case PRU_CONNECT2:
163 		error = EOPNOTSUPP;
164 		break;
165 
166 	/*
167 	 * Initiate disconnect from peer.
168 	 * If connection never passed embryonic stage, just drop;
169 	 * else if don't need to let data drain, then can just drop anyways,
170 	 * else have to begin TCP shutdown process: mark socket disconnecting,
171 	 * drain unread data, state switch to reflect user close, and
172 	 * send segment (e.g. FIN) to peer.  Socket will be really disconnected
173 	 * when peer sends FIN and acks ours.
174 	 *
175 	 * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB.
176 	 */
177 	case PRU_DISCONNECT:
178 		tp = tcp_disconnect(tp);
179 		break;
180 
181 	/*
182 	 * Accept a connection.  Essentially all the work is
183 	 * done at higher levels; just return the address
184 	 * of the peer, storing through addr.
185 	 */
186 	case PRU_ACCEPT: {
187 		struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
188 
189 		nam->m_len = sizeof (struct sockaddr_in);
190 		sin->sin_family = AF_INET;
191 		sin->sin_port = inp->inp_fport;
192 		sin->sin_addr = inp->inp_faddr;
193 		break;
194 		}
195 
196 	/*
197 	 * Mark the connection as being incapable of further output.
198 	 */
199 	case PRU_SHUTDOWN:
200 		socantsendmore(so);
201 		tp = tcp_usrclosed(tp);
202 		if (tp)
203 			error = tcp_output(tp);
204 		break;
205 
206 	/*
207 	 * After a receive, possibly send window update to peer.
208 	 */
209 	case PRU_RCVD:
210 		(void) tcp_output(tp);
211 		break;
212 
213 	/*
214 	 * Do a send by putting data in output queue and updating urgent
215 	 * marker if URG set.  Possibly send more data.
216 	 */
217 	case PRU_SEND:
218 		sbappend(&so->so_snd, m);
219 #ifdef notdef
220 		if (tp->t_flags & TF_PUSH)
221 			tp->snd_end = tp->snd_una + so->so_snd.sb_cc;
222 #endif
223 		error = tcp_output(tp);
224 		if (error) {		/* XXX fix to use other path */
225 			if (error == ENOBUFS)		/* XXX */
226 				error = 0;		/* XXX */
227 			tcpsenderrors++;
228 		}
229 		break;
230 
231 	/*
232 	 * Abort the TCP.
233 	 */
234 	case PRU_ABORT:
235 		tp = tcp_drop(tp, ECONNABORTED);
236 		break;
237 
238 	case PRU_SENSE:
239 		((struct stat *) m)->st_blksize = so->so_snd.sb_hiwat;
240 		return (0);
241 
242 	case PRU_RCVOOB:
243 		if (so->so_oobmark == 0 &&
244 		    (so->so_state & SS_RCVATMARK) == 0) {
245 			error = EINVAL;
246 			break;
247 		}
248 		if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) {
249 			error = EWOULDBLOCK;
250 			break;
251 		}
252 		m->m_len = 1;
253 		*mtod(m, caddr_t) = tp->t_iobc;
254 		break;
255 
256 	case PRU_SENDOOB:
257 		if (sbspace(&so->so_snd) < -512) {
258 			m_freem(m);
259 			error = ENOBUFS;
260 			break;
261 		}
262 		tp->snd_up = tp->snd_una + so->so_snd.sb_cc + 1;
263 		sbappend(&so->so_snd, m);
264 		tp->t_force = 1;
265 		error = tcp_output(tp);
266 		tp->t_force = 0;
267 		break;
268 
269 	case PRU_SOCKADDR:
270 		in_setsockaddr(inp, nam);
271 		break;
272 
273 	case PRU_PEERADDR:
274 		in_setpeeraddr(inp, nam);
275 		break;
276 
277 	/*
278 	 * TCP slow timer went off; going through this
279 	 * routine for tracing's sake.
280 	 */
281 	case PRU_SLOWTIMO:
282 		tp = tcp_timers(tp, (int)nam);
283 		req |= (int)nam << 8;		/* for debug's sake */
284 		break;
285 
286 	default:
287 		panic("tcp_usrreq");
288 	}
289 	if (tp && (so->so_options & SO_DEBUG))
290 		tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0, req);
291 	splx(s);
292 	return (error);
293 }
294 
295 int	tcp_sendspace = 1024*4;
296 int	tcp_recvspace = 1024*4;
297 /*
298  * Attach TCP protocol to socket, allocating
299  * internet protocol control block, tcp control block,
300  * bufer space, and entering LISTEN state if to accept connections.
301  */
302 tcp_attach(so)
303 	struct socket *so;
304 {
305 	register struct tcpcb *tp;
306 	struct inpcb *inp;
307 	int error;
308 
309 	error = soreserve(so, tcp_sendspace, tcp_recvspace);
310 	if (error)
311 		return (error);
312 	error = in_pcballoc(so, &tcb);
313 	if (error)
314 		return (error);
315 	inp = sotoinpcb(so);
316 	tp = tcp_newtcpcb(inp);
317 	if (tp == 0) {
318 		int nofd = so->so_state & SS_NOFDREF;	/* XXX */
319 
320 		so->so_state &= ~SS_NOFDREF;	/* don't free the socket yet */
321 		in_pcbdetach(inp);
322 		so->so_state |= nofd;
323 		return (ENOBUFS);
324 	}
325 	tp->t_state = TCPS_CLOSED;
326 	return (0);
327 }
328 
329 /*
330  * Initiate (or continue) disconnect.
331  * If embryonic state, just send reset (once).
332  * If in ``let data drain'' option and linger null, just drop.
333  * Otherwise (hard), mark socket disconnecting and drop
334  * current input data; switch states based on user close, and
335  * send segment to peer (with FIN).
336  */
337 struct tcpcb *
338 tcp_disconnect(tp)
339 	register struct tcpcb *tp;
340 {
341 	struct socket *so = tp->t_inpcb->inp_socket;
342 
343 	if (tp->t_state < TCPS_ESTABLISHED)
344 		tp = tcp_close(tp);
345 	else if ((so->so_options & SO_LINGER) && so->so_linger == 0)
346 		tp = tcp_drop(tp, 0);
347 	else {
348 		soisdisconnecting(so);
349 		sbflush(&so->so_rcv);
350 		tp = tcp_usrclosed(tp);
351 		if (tp)
352 			(void) tcp_output(tp);
353 	}
354 	return (tp);
355 }
356 
357 /*
358  * User issued close, and wish to trail through shutdown states:
359  * if never received SYN, just forget it.  If got a SYN from peer,
360  * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
361  * If already got a FIN from peer, then almost done; go to LAST_ACK
362  * state.  In all other cases, have already sent FIN to peer (e.g.
363  * after PRU_SHUTDOWN), and just have to play tedious game waiting
364  * for peer to send FIN or not respond to keep-alives, etc.
365  * We can let the user exit from the close as soon as the FIN is acked.
366  */
367 struct tcpcb *
368 tcp_usrclosed(tp)
369 	register struct tcpcb *tp;
370 {
371 
372 	switch (tp->t_state) {
373 
374 	case TCPS_CLOSED:
375 	case TCPS_LISTEN:
376 	case TCPS_SYN_SENT:
377 		tp->t_state = TCPS_CLOSED;
378 		tp = tcp_close(tp);
379 		break;
380 
381 	case TCPS_SYN_RECEIVED:
382 	case TCPS_ESTABLISHED:
383 		tp->t_state = TCPS_FIN_WAIT_1;
384 		break;
385 
386 	case TCPS_CLOSE_WAIT:
387 		tp->t_state = TCPS_LAST_ACK;
388 		break;
389 	}
390 	if (tp && tp->t_state >= TCPS_FIN_WAIT_2)
391 		soisdisconnected(tp->t_inpcb->inp_socket);
392 	return (tp);
393 }
394