* Copyright (c) 1982, 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
* @(#)tcp_usrreq.c 7.1 (Berkeley) %G%
#include "../net/route.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
, rights
)
struct mbuf
*m
, *nam
, *rights
;
register struct inpcb
*inp
= sotoinpcb(so
);
register struct tcpcb
*tp
;
return (in_control(so
, (int)m
, (caddr_t
)nam
,
(struct ifnet
*)rights
));
if (rights
&& rights
->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) {
tp
->t_state
= TCPS_SYN_SENT
;
tp
->t_timer
[TCPT_KEEP
] = TCPTV_KEEP
;
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.
struct sockaddr_in
*sin
= mtod(nam
, struct sockaddr_in
*);
nam
->m_len
= sizeof (struct sockaddr_in
);
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
;
if (error
) { /* XXX fix to use other path */
if (error
== ENOBUFS
) /* XXX */
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
)
struct inpcb
*inp
= sotoinpcb(so
);
register struct tcpcb
*tp
= intotcpcb(inp
);
if (level
!= IPPROTO_TCP
)
return (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
;
case TCP_MAXSEG
: /* not yet */
*mp
= m
= m_get(M_WAIT
, MT_SOOPTS
);
*mtod(m
, int *) = tp
->t_flags
& TF_NODELAY
;
*mtod(m
, int *) = tp
->t_maxseg
;
int tcp_sendspace
= 1024*4;
int tcp_recvspace
= 1024*4;
* 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
= 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
);