/* tcp_output.c 4.20 81/11/29 */
#include "../h/socketvar.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"
* Tcp output routine: figure out what should be sent
* and, if nothing, send a null segment anyways if force is nonzero
* (e.g. to be sure to send an ACK).
* This routine can be called only after SYNs have been exchanged.
register struct tcpcb
*tp
;
register struct socket
*so
= tp
->t_inpcb
->inp_socket
;
register struct tcpiphdr
*ti
;
* Determine length of data that can be transmitted,
* and flags that will be used.
* If there is some data or critical controls (SYN, RST)
* to send, then transmit; otherwise, investigate further.
off
= tp
->snd_nxt
- tp
->snd_una
;
len
= MIN(so
->so_snd
.sb_cc
, tp
->snd_wnd
) - off
;
flags
= tcp_outflags
[tp
->t_state
];
if (len
|| (flags
& (TH_SYN
|TH_RST
)))
* See if we owe peer an ACK or have a unacked FIN to send.
if (tp
->t_flags
& TF_ACKNOW
)
if ((so
->so_state
& SS_CANTSENDMORE
) &&
TCPS_OURFINNOTACKED(tp
->t_state
))
* Calculate available window in i, and also amount
* of window known to peer (as advertised window less
* next expected input.) If this is 35% or more of the
* maximum possible window, then want to send a segment to peer.
win
= sbspace(&so
->so_rcv
);
((100*(win
-(tp
->rcv_adv
-tp
->rcv_nxt
))/so
->so_rcv
.sb_hiwat
) >= 35))
* No reason to send a segment, just return.
* Grab a header mbuf, attaching a copy of data to
* be transmitted, and initialize the header from
* the template for sends on this connection.
m
->m_off
= MMAXOFF
- sizeof(struct tcpiphdr
);
m
->m_len
= sizeof (struct tcpiphdr
);
m
->m_next
= m_copy(so
->so_snd
.sb_mb
, off
, len
);
ti
= mtod(m
, struct tcpiphdr
*);
bcopy((caddr_t
)tp
->t_template
, (caddr_t
)ti
, sizeof (struct tcpiphdr
));
* Fill in fields, remembering maximum advertised
* window for use in delaying messages about window sizes.
ti
->ti_seq
= htonl(tp
->snd_nxt
);
ti
->ti_ack
= htonl(tp
->rcv_nxt
);
m
->m_len
= tp
->t_tcpopt
->m_len
;
bcopy(mtod(tp
->t_tcpopt
, caddr_t
), mtod(m
, caddr_t
),
(unsigned)tp
->t_tcpopt
->m_len
);
ti
->ti_off
= (sizeof (struct tcphdr
)+tp
->t_tcpopt
->m_len
) >> 2;
win
= sbspace(&so
->so_rcv
);
ti
->ti_win
= htons((u_short
)win
);
if (SEQ_GT(tp
->snd_up
, tp
->snd_nxt
)) {
ti
->ti_urp
= htons((u_short
)(tp
->snd_up
- tp
->snd_nxt
));
* If no urgent pointer to send, then we pull
* the urgent pointer to the left edge of the send window
* so that it doesn't drift into the send window on sequence
tp
->snd_up
= tp
->snd_una
; /* drag it along */
* Put TCP length in extended header, and then
* checksum extended header and data.
ti
->ti_len
= htons((u_short
)(len
+ sizeof (struct tcphdr
)));
ti
->ti_sum
= in_cksum(m
, sizeof (struct tcpiphdr
) + len
);
* Advance snd_nxt over sequence space of this segment
if (flags
& (TH_SYN
|TH_FIN
))
* Arrange for retransmit and time this transmit if
* not already a retransmit and sending either data,
if (SEQ_GT(tp
->snd_nxt
, tp
->snd_max
)) {
tp
->rxt_seq
= tp
->snd_nxt
- len
;
tp
->t_timer
[TCPT_REXMT
] = 0; /* XXX */
* Fill in IP length and desired time to live and
((struct ip
*)ti
)->ip_len
= len
+ sizeof (struct tcpiphdr
);
((struct ip
*)ti
)->ip_ttl
= TCP_TTL
;
if (ip_output(m
, tp
->t_ipopt
) == 0)
* Data sent (as far as we can tell).
* If this advertises a larger window than any other segment,
* then record its sequence to be used in suppressing messages.
* Drop send for purpose of ACK requirements.
if (win
> 0 && SEQ_GT(tp
->rcv_nxt
+win
, tp
->rcv_adv
))
tp
->rcv_adv
= tp
->rcv_nxt
+ win
;
tp
->t_flags
&= ~(TF_ACKNOW
|TF_DELACK
);
tp
->snd_max
= tp
->snd_nxt
;