/* tcp_usrreq.c 1.58 82/06/12 */
#include "../h/socketvar.h"
#include "../h/protosw.h"
#include "../net/route.h"
#include "../net/in_pcb.h"
#include "../net/in_systm.h"
#include "../net/ip_var.h"
#include "../net/tcp_fsm.h"
#include "../net/tcp_seq.h"
#include "../net/tcp_timer.h"
#include "../net/tcp_var.h"
#include "../net/tcpip.h"
#include "../net/tcp_debug.h"
* 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
* 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.
tcp_usrreq(so
, req
, m
, addr
)
register struct inpcb
*inp
= sotoinpcb(so
);
register struct tcpcb
*tp
;
* 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
) {
return (EINVAL
); /* XXX */
tcp_acounts
[tp
->t_state
][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.
error
= tcp_attach(so
, (struct sockaddr
*)addr
);
if ((so
->so_options
& SO_DONTLINGER
) == 0)
so
->so_linger
= TCP_LINGERTIME
;
* 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
if (tp
->t_state
> TCPS_LISTEN
)
* 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.
error
= in_pcbconnect(inp
, (struct sockaddr_in
*)addr
);
tp
->t_template
= tcp_template(tp
);
if (tp
->t_template
== 0) {
tp
->t_state
= TCPS_SYN_SENT
;
tp
->t_timer
[TCPT_KEEP
] = TCPTV_KEEP
;
tp
->iss
= tcp_iss
; tcp_iss
+= TCP_ISSINCR
/2;
* 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.
* Accept a connection. Essentially all the work is
* done at higher levels; just return the address
* of the peer, storing through addr.
struct sockaddr_in
*sin
= (struct sockaddr_in
*)addr
;
bzero((caddr_t
)sin
, sizeof (*sin
));
sin
->sin_family
= AF_INET
;
sin
->sin_port
= inp
->inp_fport
;
sin
->sin_addr
= inp
->inp_faddr
;
* Mark the connection as being incapable of further output.
* After a receive, possibly send window update to peer.
* Do a send by putting data in output queue and updating urgent
* marker if URG set. Possibly send more data.
sbappend(&so
->so_snd
, m
);
if (tp
->t_flags
& TF_PUSH
)
tp
->snd_end
= tp
->snd_una
+ so
->so_snd
.sb_cc
;
tcp_drop(tp
, ECONNABORTED
);
/* SOME AS YET UNIMPLEMENTED HOOKS */
/* END UNIMPLEMENTED HOOKS */
if (so
->so_oobmark
== 0 &&
(so
->so_state
& SS_RCVATMARK
) == 0) {
if ((tp
->t_oobflags
& TCPOOB_HAVEDATA
) == 0) {
*mtod(m
, caddr_t
) = tp
->t_iobc
;
if (tp
->t_flags
& TF_DOOOB
) {
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
;
if (error
= tcp_output(tp
))
if (sbspace(&so
->so_snd
) < -512) {
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
;
in_setsockaddr((struct sockaddr_in
*)addr
, inp
);
* TCP slow timer went off; going through this
* routine for tracing's sake.
tcp_timers(tp
, (int)addr
);
req
|= (int)addr
<< 8; /* for debug's sake */
if (tp
&& (so
->so_options
& SO_DEBUG
))
tcp_trace(TA_USER
, ostate
, tp
, (struct tcpiphdr
*)0, req
);
int tcp_sendspace
= 1024*2;
int tcp_recvspace
= 1024*2;
* Attach TCP protocol to socket, allocating
* internet protocol control block, tcp control block,
* bufer space, and entering LISTEN state if to accept connections.
register struct tcpcb
*tp
;
error
= in_pcbattach(so
, &tcb
,
tcp_sendspace
, tcp_recvspace
, (struct sockaddr_in
*)sa
);
inp
= (struct inpcb
*)so
->so_pcb
;
if (so
->so_options
& SO_ACCEPTCONN
) {
tp
->t_state
= TCPS_LISTEN
;
tp
->t_state
= TCPS_CLOSED
;
* 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).
struct socket
*so
= tp
->t_inpcb
->inp_socket
;
if (tp
->t_state
< TCPS_ESTABLISHED
)
else if (so
->so_linger
== 0)
* 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.
tp
->t_state
= TCPS_CLOSED
;
tp
->t_state
= TCPS_FIN_WAIT_1
;
tp
->t_state
= TCPS_LAST_ACK
;
if (tp
->t_state
>= TCPS_FIN_WAIT_2
)
soisdisconnected(tp
->t_inpcb
->inp_socket
);