X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/be43ac7facfd4a7230116005871ee137645474e4..bb22887e0e6dc8b156b50f0d976e39a5df2b839e:/usr/src/sys/netinet/tcp_usrreq.c diff --git a/usr/src/sys/netinet/tcp_usrreq.c b/usr/src/sys/netinet/tcp_usrreq.c index 17007fa328..9250b1f006 100644 --- a/usr/src/sys/netinet/tcp_usrreq.c +++ b/usr/src/sys/netinet/tcp_usrreq.c @@ -1,4 +1,4 @@ -/* tcp_usrreq.c 1.41 81/12/12 */ +/* tcp_usrreq.c 1.53 82/03/11 */ #include "../h/param.h" #include "../h/systm.h" @@ -18,12 +18,17 @@ #include "../net/tcp_timer.h" #include "../net/tcp_var.h" #include "../net/tcpip.h" +#include "../net/tcp_debug.h" #include "../errno.h" -extern char *tcpstates[]; +/* + * TCP protocol interface to socket abstraction. + */ +extern char *tcpstates[]; struct tcpcb *tcp_newtcpcb(); + /* - * Process a TCP user request for tcp tb. If this is a send request + * Process a TCP user request for TCP tb. If this is a send request * then m is the mbuf chain of send data. If this is a timer expiration * (called from the software clock routine), then timertype tells which timer. */ @@ -37,61 +42,89 @@ tcp_usrreq(so, req, m, addr) register struct tcpcb *tp; int s = splnet(); int error = 0; + int ostate; COUNT(TCP_USRREQ); /* - * Make sure attached. If not, - * only PRU_ATTACH is valid. + * When a TCP is attached to a socket, then there will be + * a (struct inpcb) pointed at by the socket, and this + * structure will point at a subsidary (struct tcpcb). + * The normal sequence of events is: + * PRU_ATTACH creating these structures + * PRU_CONNECT connecting to a remote peer + * (PRU_SEND|PRU_RCVD)* exchanging data + * PRU_DISCONNECT disconnecting from remote peer + * PRU_DETACH deleting the structures + * With the operations from PRU_CONNECT through PRU_DISCONNECT + * possible repeated several times. + * + * MULTIPLE CONNECTS ARE NOT YET IMPLEMENTED. */ if (inp == 0 && req != PRU_ATTACH) { splx(s); - return (EINVAL); + return (EINVAL); /* XXX */ } if (inp) { tp = intotcpcb(inp); #ifdef KPROF tcp_acounts[tp->t_state][req]++; #endif + ostate = tp->t_state; } switch (req) { + /* + * TCP attaches to socket via PRU_ATTACH, reserving space, + * and internet and TCP control blocks. + * If the socket is to receive connections, + * then the LISTEN state is entered. + */ case PRU_ATTACH: if (inp) { error = EISCONN; break; } - error = in_pcbattach(so, &tcb, 2048, 2048, (struct sockaddr_in *)addr); + error = tcp_attach(so, (struct sockaddr *)addr); if (error) break; - inp = (struct inpcb *)so->so_pcb; - tp = tcp_newtcpcb(inp); - if (so->so_options & SO_ACCEPTCONN) { - if (tp == 0) { - in_pcbdetach(inp); - error = ENOBUFS; - break; - } - tp->t_state = TCPS_LISTEN; - } else - tp->t_state = TCPS_CLOSED; + if ((so->so_options & SO_DONTLINGER) == 0) + so->so_linger = TCP_LINGERTIME; + tp = sototcpcb(so); break; + /* + * PRU_DETACH detaches the TCP protocol from the socket. + * If the protocol state is non-embryonic, then can't + * do this directly: have to initiate a PRU_DISCONNECT, + * which may finish later; embryonic TCB's can just + * be discarded here. + */ case PRU_DETACH: - in_pcbdetach(inp); + if (tp->t_state > TCPS_LISTEN) + tcp_disconnect(tp); + else { + tcp_close(tp); + tp = 0; + } break; + /* + * Initiate connection to peer. + * Create a template for use in transmissions on this connection. + * Enter SYN_SENT state, and mark socket as connecting. + * Start keep-alive timer, and seed output sequence space. + * Send initial segment on connection. + */ case PRU_CONNECT: error = in_pcbconnect(inp, (struct sockaddr_in *)addr); if (error) break; - tp = tcp_newtcpcb(inp); - if (tp == 0) - goto badcon; tp->t_template = tcp_template(tp); - if (tp->t_template == 0) - goto badcon2; - tp->t_inpcb = inp; - inp->inp_ppcb = (caddr_t)tp; + if (tp->t_template == 0) { + in_pcbdisconnect(inp); + error = ENOBUFS; + break; + } soisconnecting(so); tp->t_state = TCPS_SYN_SENT; tp->t_timer[TCPT_KEEP] = TCPTV_KEEP; @@ -100,52 +133,75 @@ COUNT(TCP_USRREQ); (void) tcp_output(tp); break; -badcon2: - (void) m_free(dtom(tp)); -badcon: - in_pcbdisconnect(inp); - error = ENOBUFS; + /* + * Initiate disconnect from peer. + * If connection never passed embryonic stage, just drop; + * else if don't need to let data drain, then can just drop anyways, + * else have to begin TCP shutdown process: mark socket disconnecting, + * drain unread data, state switch to reflect user close, and + * send segment (e.g. FIN) to peer. Socket will be really disconnected + * when peer sends FIN and acks ours. + * + * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB. + */ + case PRU_DISCONNECT: + tcp_disconnect(tp); break; - case PRU_ACCEPT: - soisconnected(so); - break; + /* + * Accept a connection. Essentially all the work is + * done at higher levels; just return the address + * of the peer, storing through addr. + */ + case PRU_ACCEPT: { + struct sockaddr_in *sin = (struct sockaddr_in *)addr; - case PRU_DISCONNECT: - if (tp->t_state < TCPS_ESTABLISHED) - tcp_close(tp); - else { - soisdisconnecting(so); - tcp_usrclosed(tp); - (void) tcp_output(tp); + if (sin) { + bzero((caddr_t)sin, sizeof (*sin)); + sin->sin_family = AF_INET; + sin->sin_port = inp->inp_fport; + sin->sin_addr = inp->inp_faddr; + } } break; + /* + * Mark the connection as being incapable of further output. + */ case PRU_SHUTDOWN: socantsendmore(so); tcp_usrclosed(tp); (void) tcp_output(tp); break; + /* + * After a receive, possibly send window update to peer. + */ case PRU_RCVD: (void) tcp_output(tp); break; + /* + * Do a send by putting data in output queue and updating urgent + * marker if URG set. Possibly send more data. + */ case PRU_SEND: sbappend(&so->so_snd, m); /* if (tp->t_flags & TF_PUSH) tp->snd_end = tp->snd_una + so->so_snd.sb_cc; */ - if (tp->t_flags & TF_URG) - tp->snd_up = tp->snd_una + so->so_snd.sb_cc + 1; (void) tcp_output(tp); break; + /* + * Abort the TCP. + */ case PRU_ABORT: tcp_drop(tp, ECONNABORTED); break; +/* SOME AS YET UNIMPLEMENTED HOOKS */ case PRU_CONTROL: error = EOPNOTSUPP; break; @@ -153,26 +209,132 @@ badcon: case PRU_SENSE: error = EOPNOTSUPP; break; +/* END UNIMPLEMENTED HOOKS */ case PRU_RCVOOB: - error = EOPNOTSUPP; + if (so->so_oobmark == 0 && + (so->so_state & SS_RCVATMARK) == 0) { + error = EINVAL; + break; + } + if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) { + error = EWOULDBLOCK; + break; + } + *mtod(m, caddr_t) = tp->t_iobc; break; case PRU_SENDOOB: - error = EOPNOTSUPP; +#ifdef TCPTRUEOOB + if (tp->t_flags & TF_DOOOB) { + tp->t_oobseq++; + tp->t_oobc = *mtod(m, caddr_t); + tp->t_oobmark = tp->snd_una + so->so_snd.sb_cc; +printf("sendoob seq now %x oobc %x\n", tp->t_oobseq, tp->t_oobc); + tp->t_oobflags |= TCPOOB_NEEDACK; + (void) tcp_output(tp); + } +#endif + if (sbspace(&so->so_snd) < -512) { + error = ENOBUFS; + break; + } + tp->snd_up = tp->snd_una + so->so_snd.sb_cc + 1; + sbappend(&so->so_snd, m); +/* + if (tp->t_flags & TF_PUSH) + tp->snd_end = tp->snd_una + so->so_snd.sb_cc; + */ + tp->t_force = 1; + (void) tcp_output(tp); + tp->t_force = 0; break; + /* + * TCP slow timer went off; going through this + * routine for tracing's sake. + */ case PRU_SLOWTIMO: tcp_timers(tp, (int)addr); + req |= (int)addr << 8; /* for debug's sake */ break; default: panic("tcp_usrreq"); } + if (tp && (so->so_options & SO_DEBUG)) + tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0, req); splx(s); return (error); } +int tcp_sendspace = 1024*2; +int tcp_recvspace = 1024*3; +/* + * Attach TCP protocol to socket, allocating + * internet protocol control block, tcp control block, + * bufer space, and entering LISTEN state if to accept connections. + */ +tcp_attach(so, sa) + struct socket *so; + struct sockaddr *sa; +{ + register struct tcpcb *tp; + struct inpcb *inp; + int error; + + error = in_pcbattach(so, &tcb, + tcp_sendspace, tcp_recvspace, (struct sockaddr_in *)sa); + if (error) + return (error); + inp = (struct inpcb *)so->so_pcb; + tp = tcp_newtcpcb(inp); + if (so->so_options & SO_ACCEPTCONN) { + if (tp == 0) { + in_pcbdetach(inp); + return (ENOBUFS); + } + tp->t_state = TCPS_LISTEN; + } else + tp->t_state = TCPS_CLOSED; + return (0); +} + +/* + * Initiate (or continue) disconnect. + * If embryonic state, just send reset (once). + * If not in ``let data drain'' option, just drop. + * Otherwise (hard), mark socket disconnecting and drop + * current input data; switch states based on user close, and + * send segment to peer (with FIN). + */ +tcp_disconnect(tp) + struct tcpcb *tp; +{ + struct socket *so = tp->t_inpcb->inp_socket; + + if (tp->t_state < TCPS_ESTABLISHED) + tcp_close(tp); + else if (so->so_linger == 0) + tcp_drop(tp, 0); + else { + soisdisconnecting(so); + sbflush(&so->so_rcv); + tcp_usrclosed(tp); + (void) tcp_output(tp); + } +} + +/* + * User issued close, and wish to trail through shutdown states: + * if never received SYN, just forget it. If got a SYN from peer, + * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN. + * If already got a FIN from peer, then almost done; go to LAST_ACK + * state. In all other cases, have already sent FIN to peer (e.g. + * after PRU_SHUTDOWN), and just have to play tedious game waiting + * for peer to send FIN or not respond to keep-alives, etc. + * We can let the user exit from the close as soon as the FIN is acked. + */ tcp_usrclosed(tp) struct tcpcb *tp; { @@ -194,4 +356,6 @@ tcp_usrclosed(tp) tp->t_state = TCPS_LAST_ACK; break; } + if (tp->t_state >= TCPS_FIN_WAIT_2) + soisdisconnected(tp->t_inpcb->inp_socket); }