📄 tcpout.c
字号:
/*
* FILENAME: tcp_out.c
*
* Copyright 1997- 2000 By InterNiche Technologies Inc. All rights reserved
*
*
* MODULE: MTCP
*
* ROUTINES: tcp_output(), tcp_send(),
*
* PORTABLE: yes
*/
/* Additional Copyrights: */
/*
* Copyright (c) 1982, 1986 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#define TCPOUTFLAGS 1
#include "ipport.h"
#include "mtcp.h"
#include "minip.h" /* (yaxon add) */
/*
* Initial options.
*/
u_char tcp_initopt[4] = { TCPOPT_MAXSEG, 4, 0x0, 0x0, };
/* Size limits on socket buffer data. If a small number of bugbufs per
* socket is used, these defaults should be adjusted downward by the port's
* initialization code.
*/
unsigned mt_deftxwin = 1024 * 8; /* default send window */
unsigned mt_defrxwin = 1024 * 8; /* default receive window */
/* Flags used when sending segments in tcp_output.
* Basic flags (TH_RST,TH_ACK,TH_SYN,TH_FIN) are totally
* determined by state, with the proviso that TH_FIN is sent only
* if all data queued for output is included in the segment.
*/
u_char tcp_outflags[TCP_NSTATES] =
{
TH_RST|TH_ACK, 0, TH_SYN, TH_SYN|TH_ACK,
TH_ACK, TH_ACK,
TH_FIN|TH_ACK, TH_FIN|TH_ACK, TH_FIN|TH_ACK, TH_ACK, TH_ACK,
};
/* FUNCTION: tcp_output()
*
* Tcp output routine: figure out what should be sent and send it.
*
* PARAM1: struct tcpcb *tp
*
* RETURNS: 0 if OK, else BSD sockets error code.
*/
int
tcp_output(struct tcpcb * tp)
{
struct msocket * so = (struct msocket *)tp->t_inpcb;
int len; /* length of data to try to send */
int win; /* scratch wind holder */
int off; /* 0ffset (in bytes) into sendq queue of first unsent data */
int flags, error; /* scratch */
struct ip * pip; /* pointer to IP header to send */
struct tcphdr * ptcp; /* tcp header to send */
u_char * opt;
unsigned optlen = 0;
int idle, sendalot;
PACKET sendp;
/*
* Determine length of data that should 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.
*/
idle = (tp->snd_max == tp->snd_una);
again:
sendalot = 0;
off = (int)(tp->snd_nxt - tp->snd_una);
/* figure out other guys window */
win = (int)tp->snd_wnd;
if (win > (long)tp->snd_cwnd) /* see if we need congestion control */
{
if (tp->snd_cwnd < tp->t_maxseg)
dprintf("tcp_out: congested; snd_cwnd %u\n", tp->snd_cwnd);
win = (int)(tp->snd_cwnd & ~(ALIGN_TYPE-1)); /* keep data aligned */
}
len = (int)MIN(so->sendq.sb_cc, (unsigned)win) - off;
#ifdef NPDEBUG
if(off < 0) { dtrap("tcpout 0\n"); }
#endif
flags = tcp_outflags[tp->t_state];
/* see if we want to reset peer */
if(tp->t_flags & TF_SENDRST)
{
flags |= TH_RST;
len = 0;
}
/*
* Before ESTABLISHED, force sending of initial options
* unless TCP set to not do any options.
*/
opt = NULL;
if (flags & TH_SYN && ((tp->t_flags & TF_NOOPT) == 0))
{
opt = tcp_initopt; /* always send MSS option! */
optlen = sizeof (tcp_initopt);
opt[2] = (u_char) ((TCP_MSS & 0xff00) >> 8);
opt[3] = (u_char) (TCP_MSS & 0xff);
}
if (len < 0)
{
/*
* If FIN has been sent but not acked,
* but we haven't been called to retransmit,
* len will be -1. Otherwise, window shrank
* after we sent into it. If window shrank to 0,
* cancel pending retransmit and pull snd_nxt
* back to (closed) window. We will enter persist
* state below. If the window didn't close completely,
* just wait for an ACK.
*/
len = 0;
if (win == 0)
{
tp->t_timer[TCPT_REXMT] = 0;
tp->snd_nxt = tp->snd_una;
}
}
if (len > (int)tp->t_maxseg)
{
len = tp->t_maxseg;
sendalot = 1;
}
if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->sendq.sb_cc))
flags &= ~TH_FIN;
/* figure out what OUR advertised window should be */
win = (int)(mt_defrxwin - so->rcvdq.sb_cc);
/*
* If our state indicates that FIN should be sent
* and we have not yet done so, or we're retransmitting the FIN,
* then we need to send.
*/
if ((flags & TH_FIN) &&
(so->sendq.sb_cc == 0) &&
((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una))
{
goto send;
}
/*
* Send if we owe peer an ACK.
*/
if (tp->t_flags & (TF_ACKNOW | TF_SENDKEEP))
goto send;
if (flags & (TH_SYN|TH_RST))
goto send;
if (SEQ_GT(tp->snd_up, tp->snd_una))
goto send;
/*
* Sender silly window avoidance. If connection is idle
* and can send all data, a maximum segment,
* at least a maximum default-size segment do it,
* or are forced, do it; otherwise don't bother.
* If peer's buffer is tiny, then send
* when window is at least half open.
* If retransmitting (possibly after persist timer forced us
* to send into a small window), then must resend.
*/
if (len)
{
if (len == (int)tp->t_maxseg)
goto send;
if ((idle || tp->t_flags & TF_NODELAY) &&
len + off >= (int)so->sendq.sb_cc)
{
goto send;
}
if (tp->t_force)
goto send;
if (len >= (int)(tp->max_sndwnd / 2))
goto send;
if (SEQ_LT(tp->snd_nxt, tp->snd_max))
goto send;
}
/*
* Compare available window to amount of window
* known to peer (as advertised window less
* next expected input). If the difference is at least two
* max size segments or at least 35% of the maximum possible
* window, then want to send a window update to peer.
*/
if (win > 0)
{
int adv = (int)win - (int)(tp->rcv_adv - tp->rcv_nxt);
if (so->sendq.sb_cc == 0 && adv >= (int)(tp->t_maxseg * 2))
goto send;
if ((100 * (unsigned)adv / mt_defrxwin) >= 35)
goto send;
}
/*
* TCP window updates are not reliable, rather a polling protocol
* using ``persist'' packets is used to insure receipt of window
* updates. The three ``states'' for the output side are:
* idle not doing retransmits or persists
* persisting to move a small or zero window
* (re)transmitting and thereby not persisting
*
* tp->t_timer[TCPT_PERSIST]
* is set when we are in persist state.
* tp->t_force
* is set when we are called to send a persist packet.
* tp->t_timer[TCPT_REXMT]
* is set when we are retransmitting
* The output side is idle when both timers are zero.
*
* If send window is too small, there is data to transmit, and no
* retransmit or persist is pending, then go to persist state.
* If nothing happens soon, send when timer expires:
* if window is nonzero, transmit what we can,
* otherwise force out a byte.
*/
if (so->sendq.sb_cc && tp->t_timer[TCPT_REXMT] == 0 &&
tp->t_timer[TCPT_PERSIST] == 0)
{
tp->t_rxtshift = 0;
tcp_setpersist(tp);
}
/*
* No reason to send a segment, just return.
*/
return (0);
send:
ENTER_CRIT_SECTION(tp);
/* Limit send length to the current buffer */
if (len)
{
int bufoff = off;
sendp = (PACKET)so->sendq.p_head;
/* find packet containing data to send (at "off") */
while (sendp) /* loop through socket send list */
{
if(bufoff <= 0)
break; /* off is in this buffer, break */
bufoff -= sendp->m_len;
sendp = sendp->m_next;
}
if (!sendp)
{
if(bufoff == 0) /* all data in queue is sent? */
{
len = 0;
goto sentall;
}
else
{
dtrap("tcpout 1\n"); /* shouldn't happen */
return EINVAL;
}
}
#ifdef NPDEBUG
if (bufoff != 0) { dtrap("tcpout 2\n"); /* shouldn't happen */ }
#endif /* NPDEBUG */
/* if socket has multiple unsent pkts, set flag for send to loop */
if ((so->sendq.p_head) && (len > (int)sendp->m_len))
{
flags &= ~TH_FIN; /* don't FIN on segment prior to last */
sendalot = 1; /* set to send more segments */
}
if((flags & TH_FIN) && (so->sendq.sb_cc > (unsigned)len))
{
/* This can happen on slow links (PPP) which retry the last
* segment - the one with the FIN bit attached to data.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -