* Copyright (c) 1982, 1986, 1988 Regents of the University of California.
* %sccs.include.redist.c%
* @(#)tcp_usrreq.c 7.19 (Berkeley) %G%
#include <sys/socketvar.h>
#include <netinet/in_systm.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcpip.h>
#include <netinet/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
, nam
, control
)
struct mbuf
*m
, *nam
, *control
;
register struct inpcb
*inp
;
register struct tcpcb
*tp
;
return (in_control(so
, (int)m
, (caddr_t
)nam
,
(struct ifnet
*)control
));
if (control
&& control
->m_len
) {
* 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).
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 an internet control block.
if ((so
->so_options
& SO_LINGER
) && so
->so_linger
== 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
)
* Give the socket an address.
error
= in_pcbbind(inp
, nam
);
* Prepare to accept connections.
error
= in_pcbbind(inp
, (struct mbuf
*)0);
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.
if (inp
->inp_lport
== 0) {
error
= in_pcbbind(inp
, (struct mbuf
*)0);
error
= in_pcbconnect(inp
, nam
);
tp
->t_template
= tcp_template(tp
);
if (tp
->t_template
== 0) {
/* Compute window scaling to request. */
while (tp
->request_r_scale
< TCP_MAX_WINSHIFT
&&
(TCP_MAXWIN
<< tp
->request_r_scale
) < so
->so_rcv
.sb_hiwat
)
tcpstat
.tcps_connattempt
++;
tp
->t_state
= TCPS_SYN_SENT
;
tp
->t_timer
[TCPT_KEEP
] = TCPTV_KEEP_INIT
;
tp
->iss
= tcp_iss
; tcp_iss
+= TCP_ISSINCR
/2;
* Create a TCP connection between two sockets.
* 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.
in_setpeeraddr(inp
, nam
);
* 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
);
tp
= tcp_drop(tp
, ECONNABORTED
);
((struct stat
*) m
)->st_blksize
= so
->so_snd
.sb_hiwat
;
if ((so
->so_oobmark
== 0 &&
(so
->so_state
& SS_RCVATMARK
) == 0) ||
so
->so_options
& SO_OOBINLINE
||
tp
->t_oobflags
& TCPOOB_HADDATA
) {
if ((tp
->t_oobflags
& TCPOOB_HAVEDATA
) == 0) {
*mtod(m
, caddr_t
) = tp
->t_iobc
;
if (((int)nam
& MSG_PEEK
) == 0)
tp
->t_oobflags
^= (TCPOOB_HAVEDATA
| TCPOOB_HADDATA
);
if (sbspace(&so
->so_snd
) < -512) {
* According to RFC961 (Assigned Protocols),
* the urgent pointer points to the last octet
* of urgent data. We continue, however,
* to consider it to indicate the first octet
* of data past the urgent section.
* Otherwise, snd_up should be one lower.
sbappend(&so
->so_snd
, m
);
tp
->snd_up
= tp
->snd_una
+ so
->so_snd
.sb_cc
;
in_setsockaddr(inp
, nam
);
in_setpeeraddr(inp
, nam
);
* TCP slow timer went off; going through this
* routine for tracing's sake.
tp
= tcp_timers(tp
, (int)nam
);
req
|= (int)nam
<< 8; /* for debug's sake */
if (tp
&& (so
->so_options
& SO_DEBUG
))
tcp_trace(TA_USER
, ostate
, tp
, (struct tcpiphdr
*)0, req
);
tcp_ctloutput(op
, so
, level
, optname
, mp
)
register struct tcpcb
*tp
;
if (level
!= IPPROTO_TCP
) {
error
= ip_ctloutput(op
, so
, level
, optname
, mp
);
if (m
== NULL
|| m
->m_len
< sizeof (int))
else if (*mtod(m
, int *))
tp
->t_flags
|= TF_NODELAY
;
tp
->t_flags
&= ~TF_NODELAY
;
if (m
&& (i
= *mtod(m
, int *)) > 0 && i
<= tp
->t_maxseg
)
*mp
= m
= m_get(M_WAIT
, MT_SOOPTS
);
*mtod(m
, int *) = tp
->t_flags
& TF_NODELAY
;
*mtod(m
, int *) = tp
->t_maxseg
;
u_long tcp_sendspace
= 1024*8;
u_long tcp_recvspace
= 1024*8;
* 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
;
if (so
->so_snd
.sb_hiwat
== 0 || so
->so_rcv
.sb_hiwat
== 0) {
error
= soreserve(so
, tcp_sendspace
, tcp_recvspace
);
error
= in_pcballoc(so
, &tcb
);
int nofd
= so
->so_state
& SS_NOFDREF
; /* XXX */
so
->so_state
&= ~SS_NOFDREF
; /* don't free the socket yet */
tp
->t_state
= TCPS_CLOSED
;
* Initiate (or continue) disconnect.
* If embryonic state, just send reset (once).
* If in ``let data drain'' option and linger null, 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).
register struct tcpcb
*tp
;
struct socket
*so
= tp
->t_inpcb
->inp_socket
;
if (tp
->t_state
< TCPS_ESTABLISHED
)
else if ((so
->so_options
& SO_LINGER
) && 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.
register struct tcpcb
*tp
;
tp
->t_state
= TCPS_CLOSED
;
tp
->t_state
= TCPS_FIN_WAIT_1
;
tp
->t_state
= TCPS_LAST_ACK
;
if (tp
&& tp
->t_state
>= TCPS_FIN_WAIT_2
)
soisdisconnected(tp
->t_inpcb
->inp_socket
);