📄 tcp_output.c
字号:
#ifndef lintstatic char *sccsid = "@(#)tcp_output.c 4.4 (ULTRIX) 3/7/91";#endif lint/************************************************************************ * * * Copyright (c) 1985 by * * Digital Equipment Corporation, Maynard, MA * * All rights reserved. * * * * This software is furnished under a license and may be used and * * copied only in accordance with the terms of such license and * * with the inclusion of the above copyright notice. This * * software or any other copies thereof may not be provided or * * otherwise made available to any other person. No title to and * * ownership of the software is hereby transferred. * * * * This software is derived from software received from the * * University of California, Berkeley, and from Bell * * Laboratories. Use, duplication, or disclosure is subject to * * restrictions under license agreements with University of * * California and with AT&T. * * * * The information in this software is subject to change without * * notice and should not be construed as a commitment by Digital * * Equipment Corporation. * * * * Digital assumes no responsibility for the use or reliability * * of its software on equipment which is not supplied by Digital. * * * ************************************************************************//************************************************************************ * Modification History * * * * 03-07-91 Michael G. Mc Menemy * Force traffic to loopback driver if connection is * being set-up or torn-down. This will prevent a socket * dangling reference to happen. * * * 08-20-90 Uttam Shikarpur Nadig * Added tcps_sndrst counter. * * 30-May-89 U. Sinkewicz * Added SO_LOCK and so->ref to fix smp problem caused by * unlocking then locking the socket to accommodate the * lock hierarchy or sleeps. Changed ip_output() call * interface. Needed to support asymmetric network * drivers. * * 27-Mar-89 U. Sinkewicz * Lowered ipl on lk_rtentry, lk_ifnet, lk_in_ifaddr; replaced * tcpstatistics with a macro, as per lp changes made 3.16.89. * * 3-Mar-89 U. Sinkewicz * New directory layout to smp file. * 15-Jan-88 lp * Merge of final 43BSD changes. * * Larry Cohen - 01/28/87 * Send packets immediately if TCP_NODELAY option is set * * Larry Cohen - 09/16/85 * * Add 43bsd alpha tape changes for subnet routing * * * ************************************************************************//* * 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. * */#include "../h/param.h"#include "../h/systm.h"#include "../h/smp_lock.h"#include "../h/mbuf.h"#include "../h/protosw.h"#include "../h/socket.h"#include "../h/socketvar.h"#include "../h/errno.h"#include "../net/net/route.h"#include "../net/net/if.h"#include "../net/netinet/in.h"#include "../net/netinet/in_pcb.h"#include "../net/netinet/in_systm.h"#include "../net/netinet/ip.h"#include "../net/netinet/ip_var.h"#include "../net/netinet/tcp.h"#define TCPOUTFLAGS#include "../net/netinet/tcp_fsm.h"#include "../net/netinet/tcp_seq.h"#include "../net/netinet/tcp_timer.h"#include "../net/netinet/tcp_var.h"#include "../net/netinet/tcpip.h"#include "../net/netinet/tcp_debug.h"/* * Initial options. */u_char tcp_initopt[4] = { TCPOPT_MAXSEG, 4, 0x0, 0x0, };extern int tcp_slow_active; /* whether tcp_slowtimo() is active now *//* * Tcp output routine: figure out what should be sent and send it. *//* * SMP: called with socket lock set. Called from tcp_usrreq, * tcp_input, tcp_fasttimeo, tcp_disconnect. Already at splnet. */tcp_output(tp) register struct tcpcb *tp;{ register struct socket *so = tp->t_inpcb->inp_socket; register int len, win; struct mbuf *m0; int off, flags, error; register struct mbuf *m; register struct tcpiphdr *ti; u_char *opt; unsigned optlen = 0; int idle, sendalot; int s = splnet(); /* SMP */#ifdef TCPLOOPBACK int loopback = 0;#endif /* * 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. */#ifdef TCPLOOPBACK smp_lock(&lk_tcpiss, LK_RETRY); if (tp->t_template->ti_src.s_addr == tp->t_template->ti_dst.s_addr) { if (tcp_slow_active == 0 && tp->t_state == TCPS_ESTABLISHED) /* Only attempt short-cut loopback if tcp_slowtimo() * routine is NOT active and tcp state is TCPS_ESTABLISHED; * else we could delete the next * pcb in the tcb chain and return to use a just free'd * mbuf, which will cause a segmentation fault panic. * Just use normal loopback channel in this case. */ loopback++; } smp_unlock(&lk_tcpiss);#endif idle = (tp->snd_max == tp->snd_una);again: sendalot = 0; off = tp->snd_nxt - tp->snd_una; win = MIN(tp->snd_wnd, tp->snd_cwnd); /* * If in persist timeout with window of 0, send 1 byte. * Otherwise, if window is small but nonzero * and timer expired, we will send what we can * and go to transmit state. */ if (tp->t_force) { if (win == 0) win = 1; else { tp->t_timer[TCPT_PERSIST] = 0; tp->t_rxtshift = 0; } } len = MIN(so->so_snd.sb_cc, win) - off; flags = tcp_outflags[tp->t_state]; 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 > tp->t_maxseg) { len = tp->t_maxseg; sendalot = 1; } if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc)) flags &= ~TH_FIN; win = sbspace(&so->so_rcv); /* * 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 && ((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) 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 transmitting via local loopback and dst is the same as * the local host then transmit imediately. */ if (len) { if (#ifdef TCPLOOPBACK loopback || len == tp->t_maxseg#else len == tp->t_maxseg #endif ) goto send; if ((idle || tp->t_flags & TF_NODELAY) && len + off >= so->so_snd.sb_cc) goto send; if (tp->t_force) goto send; if (len >= 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 = win - (tp->rcv_adv - tp->rcv_nxt); if (so->so_rcv.sb_cc == 0 && adv >= 2 * tp->t_maxseg) goto send; if (100 * adv / so->so_rcv.sb_hiwat >= 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->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 && tp->t_timer[TCPT_PERSIST] == 0) { tp->t_rxtshift = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -