V1.55 ((tcp 1 of 2) updated IP/TCP and XNS sources for 4.3BSD)

Keith Bostic bostic at OKEEFFE.BERKELEY.EDU
Tue Apr 5 13:18:43 AEST 1988


Subject: (tcp 1 of 2) updated IP/TCP and XNS sources for 4.3BSD
Index: sys 4.3BSD

Description:
	This is number 10 of 11 total articles posted to the newsgroup
	comp.bugs.4bsd.ucb-fixes.  This archive is number 1 of the 2
	articles that make up the tcp posting.

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	Makefile.sun
#	README
#	TCP_INSTALL
#	netinet
#	netinet/tcp.h
#	netinet/tcp_debug.c
#	netinet/tcp_debug.h
#	netinet/tcp_fsm.h
#	netinet/tcp_input.c
#	netinet/tcp_output.c
#	netstat
#	netstat/inet.c
#
echo x - Makefile.sun
sed 's/^X//' >Makefile.sun << 'END-of-Makefile.sun'
X# @(#) $Header: Makefile,v 1.5 87/06/24 02:56:18 van Locked $ (LBL)
X
XCC= cc -m68020 -fsoft
XCFLAGS= -O -c -I/sys/OBJ -I/sys/h\
X    -DKERNEL -Dsun3 -DSUN3_160 -DSUN3_50 -DSUN3_260 -DSUN3_110\
X    -DGENERIC -DINET -DSYSACCT -DQUOTA -DNFS -DNIT\
X    -DIPCMESSAGE -DIPCSEMAPHORE -DIPCSHMEM
X
Xall: tcp_input.o tcp_output.o tcp_subr.o tcp_timer.o tcp_debug.o\
X    tcp_usrreq.o
X
Xtcp_debug.o: ../netinet/tcp_debug.c
X	${CC} ${CFLAGS} $?
X
Xtcp_input.o: ../netinet/tcp_input.c
X	${CC} ${CFLAGS} $?
X
Xtcp_output.o: ../netinet/tcp_output.c
X	${CC} ${CFLAGS} $?
X
Xtcp_subr.o: ../netinet/tcp_subr.c
X	${CC} ${CFLAGS} $?
X
Xtcp_timer.o: ../netinet/tcp_timer.c
X	${CC} ${CFLAGS} $?
X
Xtcp_usrreq.o: ../netinet/tcp_usrreq.c
X	${CC} ${CFLAGS} $?
END-of-Makefile.sun
echo x - README
sed 's/^X//' >README << 'END-of-README'
XThis is the description of a release of updated networking software
Xfor the 4.3BSD distribution from the University of California, Berkeley.
XThese changes are part of the current Berkeley operating system and
Xwill be included in future tape releases.  This release is being
Xmade available by anonymous FTP from the ARPANET and on the Usenet
Xnewsgroup comp.bugs.4bsd.ucb-fixes.
X
XThe major changes in this release are in the TCP send policy.
XBecause the improvements in the send policy could significantly
Xreduce congestion on the ARPANET and the NSFNET, all sites with
Xdirect or indirect connections to long-haul nets are urged to upgrade
Xas quickly as possible.  Vendors supplying TCP products based on 4.2BSD
Xor 4.3BSD are strongly urged to update as quickly as possible.  Vendors
Xusing other TCP implementations should consider the use of the new algorithms
Xas well, and may find the current Berkeley source code useful as a guide
Xto their implementation.
X
XThe FTP release consists of five files: tcp.tar, inet.tar, netns.tar,
Xsocket.tar and imp.tar.  They are all present on host ucbarpa.Berkeley.EDU
Xin the directory pub/4.3.  (Each is also available in compressed form,
Xindicated by a trailing ".Z".)  Each archive file includes a copy of this
Xfile (called README).
X
XThe first file, tcp.tar, contains sources for the current version of TCP,
Xincluding the slow start algorithm and other work by Van Jacobson of LBL
Xand a retransmission timer algorithm suggested by Phil Karn.  It is designed
Xto replace the 4.3BSD TCP, although it also has #ifdef's for installation
Xin a 4.2-based system (including SunOS versions up to 3.6).  The changes made
Xsince the release of 4.3 dramatically improve performance over slow and/or
Xlossy networks such as the ARPANET/Milnet and Satnet, and also reduce the
Xnumber of unnecessary retransmissions nearly to zero.  Performance on
Xfast, local-area networks is also somewhat improved, especially on faster
Xprocessors when larger buffers are used.  Several new bug fixes have
Xalso been made.  The file TCP_INSTALL contains some hints on configuring
XTCP for systems other than standard 4.3BSD and 4.2BSD.
X
XThe second file, inet.tar, contains sources for IP, ICMP, UDP and common
Xinternet code (all of the netinet directory except the TCP sources).
XIt also includes a few files from the sys and h directories that have
Xbeen changed since the 4.3BSD release.  There are changes in the processing
Xof IP record-route and timestamp options and in handling of certain broadcast
XUDP requests.  The mbuf allocation routines include a fix for a race and
Xchanges to call the protocol drain routines when appropriate, and will
Xno longer panic when new allocation requests discover that the mbuf map
Xhas been exhausted.  A recent problem in the code for fragmenting IP packets
Xwith options is fixed.  The complete source for the netstat program is also
Xincluded in inet.tar.  It will be usable only on 4.3BSD systems without
Xmodification.  (Note: there are two versions of main.c and host.c in
Xthe netstat directory.  Unless you are installing the new imp code,
Xyou must use main.c.oldimp and host.c.oldimp.)
X
XThe combination of tcp.tar and inet.tar is sufficient to upgrade a 4.3BSD
Xsystem to the current level of IP/TCP.  It is strongly recommended that
X4.3BSD sites that are connected to the Internet, directly or indirectly,
Xas well as 4.3BSD gateway sites, should upgrade as quickly as possible.
X
XThe third file, netns.tar, contains the current version of the Xerox NS
Xprotocols from the netns source directory.  The Sequenced Packet Protocol
Xhas modifications similar to those in TCP, as well as several bug fixes.
XSites that use XNS must upgrade it at the same time as TCP, as the old
XXNS code used the old tcp_timer.h.
X
XThe fourth file, socket.tar, includes the remainder of the socket and generic
Xnetwork source files.  These files are identical to those in the 4.3BSD
Xrelease.  They are provided for completeness for those who do not have access
Xto the original 4.3BSD sources.  They are not required for installation
Xof the TCP and other internet fixes in a 4.3BSD system, nor for installation
Xof the new internet code into most 4.2BSD-derived systems.  They may
Xbe useful for upgrading the socket or network code in a 4.2BSD-derived
Xsystem.  We cannot provide any assistance in such upgrades, but we
Xare interested in hearing about any successful upgrades.
X
XThe fifth file, imp.tar, includes recent modifications to the code for
Xhandling an ARPANET/Milnet IMP using an AHIP (1822) interface.  It was not
Xquite ready for distribution when the rest of the update was finished, and
Xmay not be present in the anonymous FTP area immediately.  If you want
Xit, check back later or watch for an announcement on the tcp-ip mailing
Xlist.
X
XNote that the Berkeley network source code is *not* public-domain.
XHowever, as it contains no code licensed by AT&T or others, it is owned
Xby the Regents of the University of California, Berkeley.  It is provided
Xas-is, without any warranty, for any purpose.  It may be used, modified or
Xredistributed in source or binary forms, as long as due credit is given
Xto the University and the University copyright notices are retained.
X
XThese sources may be updated from time to time as improvements or additions
Xare made.  The next update will include support for IP multicast done
Xby Steve Deering at Stanford.  Updates will be announced on the tcp-ip
Xmailing list, which is redistributed on Usenet.
X
X	Mike Karels	karels at Berkeley.EDU
X	Van Jacobson	van at lbl-csam.arpa
END-of-README
echo x - TCP_INSTALL
sed 's/^X//' >TCP_INSTALL << 'END-of-TCP_INSTALL'
XThis tcp is believed to run on Sun-3/x systems running Sun OS v3.3,
XVax 7xx and CCI Tahoe systems running 4.3BSD, VAX VMS systems
Xrunning the most recent Wollongong WIN software and Gould systems
Xrunning ?? (contact dpk at brl.arpa for Gould details).  It should
Xrun, unmodified, on any stock 4.2 or 4.3BSD machine.  Various
Xdefines to account for system dependencies are at the front of
Xtcp_var.h.  If tcp won't work on your system, check the comments
Xin tcp_var.h to see if there's some obvious change you should
Xmake.  If you have to tweak the configuration to make things
Xwork on your machine, please let van at lbl-csam.arpa know.  We'd
Xlike to make the public release of the code easy-to-install on
Xas many machines as possible.  That means that any notes you
Xcan send us on installation problems will be invaluable.
X
XThe tcp* files in the netinet subdirectory are replacements for the
Xtcp* files of your 4.[23] distribution (i.e., they should replace the
Xtcp files in /sys/netinet).  "Makefile.sun" is a makefile to construct
Xtcp binaries on an object-only Sun system.  The "netstat" subdirectory
Xcontains a modification to "netstat" to print out the new
Xtcp statistics.  It is a replacement for /usr/src/ucb/netstat/inet.c.
XYou will need source for your current version of netstat to make use of
Xthis.  If you don't, be sure that OLDSTAT is defined in tcp_var.h
Xso that the old TCP statistics are available from netstat.  The remaining
Xsource files for netstat are in the inet portion of this network
Xrelease, but these will work without modification only on 4.3BSD systems.
END-of-TCP_INSTALL
echo c - netinet
mkdir netinet > /dev/null 2>&1
echo x - netinet/tcp.h
sed 's/^X//' >netinet/tcp.h << 'END-of-netinet/tcp.h'
X/*
X * Copyright (c) 1982, 1986 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that this notice is preserved and that due credit is given
X * to the University of California at Berkeley. The name of the University
X * may not be used to endorse or promote products derived from this
X * software without specific prior written permission. This software
X * is provided ``as is'' without express or implied warranty.
X *
X *	@(#)tcp.h	7.4.1.1 (Berkeley) 2/7/88
X */
X#ifndef BYTE_ORDER
X/*
X * Definitions for byte order,
X * according to byte significance from low address to high.
X */
X#define	LITTLE_ENDIAN	1234	/* least-significant byte first (vax) */
X#define	BIG_ENDIAN	4321	/* most-significant byte first (IBM, net) */
X#define	PDP_ENDIAN	3412	/* LSB first in word, MSW first in long (pdp) */
X
X#ifdef vax
X#define	BYTE_ORDER	LITTLE_ENDIAN
X#else
X#define	BYTE_ORDER	BIG_ENDIAN	/* mc68000, tahoe, most others */
X#endif
X#endif BYTE_ORDER
X
Xtypedef	u_long	tcp_seq;
X/*
X * TCP header.
X * Per RFC 793, September, 1981.
X */
Xstruct tcphdr {
X	u_short	th_sport;		/* source port */
X	u_short	th_dport;		/* destination port */
X	tcp_seq	th_seq;			/* sequence number */
X	tcp_seq	th_ack;			/* acknowledgement number */
X#if BYTE_ORDER == LITTLE_ENDIAN
X	u_char	th_x2:4,		/* (unused) */
X		th_off:4;		/* data offset */
X#endif
X#if BYTE_ORDER == BIG_ENDIAN
X	u_char	th_off:4,		/* data offset */
X		th_x2:4;		/* (unused) */
X#endif
X	u_char	th_flags;
X#define	TH_FIN	0x01
X#define	TH_SYN	0x02
X#define	TH_RST	0x04
X#define	TH_PUSH	0x08
X#define	TH_ACK	0x10
X#define	TH_URG	0x20
X	u_short	th_win;			/* window */
X	u_short	th_sum;			/* checksum */
X	u_short	th_urp;			/* urgent pointer */
X};
X
X#define	TCPOPT_EOL	0
X#define	TCPOPT_NOP	1
X#define	TCPOPT_MAXSEG	2
X
X/*
X * Default maximum segment size for TCP.
X * With an IP MSS of 576, this is 536,
X * but 512 is probably more convenient.
X */
X#ifdef	lint
X#define	TCP_MSS	536
X#else
X#ifndef IP_MSS
X#define	IP_MSS	576
X#endif
X#define	TCP_MSS	MIN(512, IP_MSS - sizeof (struct tcpiphdr))
X#endif
X
X/*
X * User-settable options (used with setsockopt).
X */
X#define	TCP_NODELAY	0x01	/* don't delay send to coalesce packets */
X#define	TCP_MAXSEG	0x02	/* set maximum segment size */
END-of-netinet/tcp.h
echo x - netinet/tcp_debug.c
sed 's/^X//' >netinet/tcp_debug.c << 'END-of-netinet/tcp_debug.c'
X/*
X * Copyright (c) 1982, 1986 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that this notice is preserved and that due credit is given
X * to the University of California at Berkeley. The name of the University
X * may not be used to endorse or promote products derived from this
X * software without specific prior written permission. This software
X * is provided ``as is'' without express or implied warranty.
X *
X *	@(#)tcp_debug.c	7.2 (Berkeley) 12/7/87
X */
X
X#include "param.h"
X#include "systm.h"
X#include "mbuf.h"
X#include "socket.h"
X#include "socketvar.h"
X#define PRUREQUESTS
X#include "protosw.h"
X#include "errno.h"
X
X#include "../net/route.h"
X#include "../net/if.h"
X
X#include "in.h"
X#include "in_pcb.h"
X#include "in_systm.h"
X#include "ip.h"
X#include "ip_var.h"
X#include "tcp.h"
X#define TCPSTATES
X#include "tcp_fsm.h"
X#include "tcp_seq.h"
X#define	TCPTIMERS
X#include "tcp_timer.h"
X#include "tcp_var.h"
X#include "tcpip.h"
X#define	TANAMES
X#include "tcp_debug.h"
X
Xint	tcpconsdebug = 0;
X/*
X * Tcp debug routines
X */
Xtcp_trace(act, ostate, tp, ti, req)
X	short act, ostate;
X	struct tcpcb *tp;
X	struct tcpiphdr *ti;
X	int req;
X{
X	tcp_seq seq, ack;
X	int len, flags;
X	struct tcp_debug *td = &tcp_debug[tcp_debx++];
X
X	if (tcp_debx == TCP_NDEBUG)
X		tcp_debx = 0;
X	td->td_time = iptime();
X	td->td_act = act;
X	td->td_ostate = ostate;
X	td->td_tcb = (caddr_t)tp;
X	if (tp)
X		td->td_cb = *tp;
X	else
X		bzero((caddr_t)&td->td_cb, sizeof (*tp));
X	if (ti)
X		td->td_ti = *ti;
X	else
X		bzero((caddr_t)&td->td_ti, sizeof (*ti));
X	td->td_req = req;
X	if (tcpconsdebug == 0)
X		return;
X	if (tp)
X		printf("%x %s:", tp, tcpstates[ostate]);
X	else
X		printf("???????? ");
X	printf("%s ", tanames[act]);
X	switch (act) {
X
X	case TA_INPUT:
X	case TA_OUTPUT:
X	case TA_DROP:
X		if (ti == 0)
X			break;
X		seq = ti->ti_seq;
X		ack = ti->ti_ack;
X		len = ti->ti_len;
X		if (act == TA_OUTPUT) {
X			seq = ntohl(seq);
X			ack = ntohl(ack);
X			len = ntohs((u_short)len);
X		}
X		if (act == TA_OUTPUT)
X			len -= sizeof (struct tcphdr);
X		if (len)
X			printf("[%x..%x)", seq, seq+len);
X		else
X			printf("%x", seq);
X		printf("@%x, urp=%x", ack, ti->ti_urp);
X		flags = ti->ti_flags;
X		if (flags) {
X#ifndef lint
X			char *cp = "<";
X#define pf(f) { if (ti->ti_flags&TH_/**/f) { printf("%s%s", cp, "f"); cp = ","; } }
X			pf(SYN); pf(ACK); pf(FIN); pf(RST); pf(PUSH); pf(URG);
X#endif
X			printf(">");
X		}
X		break;
X
X	case TA_USER:
X		printf("%s", prurequests[req&0xff]);
X		if ((req & 0xff) == PRU_SLOWTIMO)
X			printf("<%s>", tcptimers[req>>8]);
X		break;
X	}
X	if (tp)
X		printf(" -> %s", tcpstates[tp->t_state]);
X	/* print out internal state of tp !?! */
X	printf("\n");
X	if (tp == 0)
X		return;
X	printf("\trcv_(nxt,wnd,up) (%x,%x,%x) snd_(una,nxt,max) (%x,%x,%x)\n",
X	    tp->rcv_nxt, tp->rcv_wnd, tp->rcv_up, tp->snd_una, tp->snd_nxt,
X	    tp->snd_max);
X	printf("\tsnd_(wl1,wl2,wnd) (%x,%x,%x)\n",
X	    tp->snd_wl1, tp->snd_wl2, tp->snd_wnd);
X}
END-of-netinet/tcp_debug.c
echo x - netinet/tcp_debug.h
sed 's/^X//' >netinet/tcp_debug.h << 'END-of-netinet/tcp_debug.h'
X/*
X * Copyright (c) 1982, 1986 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that this notice is preserved and that due credit is given
X * to the University of California at Berkeley. The name of the University
X * may not be used to endorse or promote products derived from this
X * software without specific prior written permission. This software
X * is provided ``as is'' without express or implied warranty.
X *
X *	@(#)tcp_debug.h	7.2 (Berkeley) 12/7/87
X */
X
Xstruct	tcp_debug {
X	n_time	td_time;
X	short	td_act;
X	short	td_ostate;
X	caddr_t	td_tcb;
X	struct	tcpiphdr td_ti;
X	short	td_req;
X	struct	tcpcb td_cb;
X};
X
X#define	TA_INPUT 	0
X#define	TA_OUTPUT	1
X#define	TA_USER		2
X#define	TA_RESPOND	3
X#define	TA_DROP		4
X
X#ifdef TANAMES
Xchar	*tanames[] =
X    { "input", "output", "user", "respond", "drop" };
X#endif
X
X#define	TCP_NDEBUG 100
Xstruct	tcp_debug tcp_debug[TCP_NDEBUG];
Xint	tcp_debx;
END-of-netinet/tcp_debug.h
echo x - netinet/tcp_fsm.h
sed 's/^X//' >netinet/tcp_fsm.h << 'END-of-netinet/tcp_fsm.h'
X/*
X * Copyright (c) 1982, 1986 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that this notice is preserved and that due credit is given
X * to the University of California at Berkeley. The name of the University
X * may not be used to endorse or promote products derived from this
X * software without specific prior written permission. This software
X * is provided ``as is'' without express or implied warranty.
X *
X *	@(#)tcp_fsm.h	7.2 (Berkeley) 12/7/87
X */
X
X/*
X * TCP FSM state definitions.
X * Per RFC793, September, 1981.
X */
X
X#define	TCP_NSTATES	11
X
X#define	TCPS_CLOSED		0	/* closed */
X#define	TCPS_LISTEN		1	/* listening for connection */
X#define	TCPS_SYN_SENT		2	/* active, have sent syn */
X#define	TCPS_SYN_RECEIVED	3	/* have send and received syn */
X/* states < TCPS_ESTABLISHED are those where connections not established */
X#define	TCPS_ESTABLISHED	4	/* established */
X#define	TCPS_CLOSE_WAIT		5	/* rcvd fin, waiting for close */
X/* states > TCPS_CLOSE_WAIT are those where user has closed */
X#define	TCPS_FIN_WAIT_1		6	/* have closed, sent fin */
X#define	TCPS_CLOSING		7	/* closed xchd FIN; await FIN ACK */
X#define	TCPS_LAST_ACK		8	/* had fin and close; await FIN ACK */
X/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */
X#define	TCPS_FIN_WAIT_2		9	/* have closed, fin is acked */
X#define	TCPS_TIME_WAIT		10	/* in 2*msl quiet wait after close */
X
X#define	TCPS_HAVERCVDSYN(s)	((s) >= TCPS_SYN_RECEIVED)
X#define	TCPS_HAVERCVDFIN(s)	((s) >= TCPS_TIME_WAIT)
X
X#ifdef	TCPOUTFLAGS
X/*
X * Flags used when sending segments in tcp_output.
X * Basic flags (TH_RST,TH_ACK,TH_SYN,TH_FIN) are totally
X * determined by state, with the proviso that TH_FIN is sent only
X * if all data queued for output is included in the segment.
X */
Xu_char	tcp_outflags[TCP_NSTATES] = {
X    TH_RST|TH_ACK, 0, TH_SYN, TH_SYN|TH_ACK,
X    TH_ACK, TH_ACK,
X    TH_FIN|TH_ACK, TH_FIN|TH_ACK, TH_FIN|TH_ACK, TH_ACK, TH_ACK,
X};
X#endif
X
X#ifdef KPROF
Xint	tcp_acounts[TCP_NSTATES][PRU_NREQ];
X#endif
X
X#ifdef	TCPSTATES
Xchar *tcpstates[] = {
X	"CLOSED",	"LISTEN",	"SYN_SENT",	"SYN_RCVD",
X	"ESTABLISHED",	"CLOSE_WAIT",	"FIN_WAIT_1",	"CLOSING",
X	"LAST_ACK",	"FIN_WAIT_2",	"TIME_WAIT",
X};
X#endif
END-of-netinet/tcp_fsm.h
echo x - netinet/tcp_input.c
sed 's/^X//' >netinet/tcp_input.c << 'END-of-netinet/tcp_input.c'
X/*
X * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that this notice is preserved and that due credit is given
X * to the University of California at Berkeley. The name of the University
X * may not be used to endorse or promote products derived from this
X * software without specific prior written permission. This software
X * is provided ``as is'' without express or implied warranty.
X *
X *	@(#)tcp_input.c	7.15.1.2 (Berkeley) 3/16/88
X */
X
X#include "param.h"
X#include "systm.h"
X#include "mbuf.h"
X#include "protosw.h"
X#include "socket.h"
X#include "socketvar.h"
X#include "errno.h"
X
X#include "../net/if.h"
X#include "../net/route.h"
X
X#include "in.h"
X#include "in_pcb.h"
X#include "in_systm.h"
X#include "ip.h"
X#include "ip_var.h"
X#include "tcp.h"
X#include "tcp_fsm.h"
X#include "tcp_seq.h"
X#include "tcp_timer.h"
X#include "tcp_var.h"
X#include "tcpip.h"
X#include "tcp_debug.h"
X
Xint	tcpprintfs = 0;
Xint	tcpcksum = 1;
Xint	tcprexmtthresh = 3;
Xstruct	tcpiphdr tcp_saveti;
Xextern	tcpnodelack;
X
Xstruct	tcpcb *tcp_newtcpcb();
X
X/*
X * Insert segment ti into reassembly queue of tcp with
X * control block tp.  Return TH_FIN if reassembly now includes
X * a segment with FIN.  The macro form does the common case inline
X * (segment is the next to be received on an established connection,
X * and the queue is empty), avoiding linkage into and removal
X * from the queue and repetition of various conversions.
X */
X#define	TCP_REASS(tp, ti, m, so, flags) { \
X	if ((ti)->ti_seq == (tp)->rcv_nxt && \
X	    (tp)->seg_next == (struct tcpiphdr *)(tp) && \
X	    (tp)->t_state == TCPS_ESTABLISHED) { \
X		(tp)->rcv_nxt += (ti)->ti_len; \
X		flags = (ti)->ti_flags & TH_FIN; \
X		tcpstat.tcps_rcvpack++;\
X		tcpstat.tcps_rcvbyte += (ti)->ti_len;\
X		sbappend(&(so)->so_rcv, (m)); \
X		sorwakeup(so); \
X	} else \
X		(flags) = tcp_reass((tp), (ti)); \
X}
X
Xtcp_reass(tp, ti)
X	register struct tcpcb *tp;
X	register struct tcpiphdr *ti;
X{
X	register struct tcpiphdr *q;
X	struct socket *so = tp->t_inpcb->inp_socket;
X	struct mbuf *m;
X	int flags;
X
X	/*
X	 * Call with ti==0 after become established to
X	 * force pre-ESTABLISHED data up to user socket.
X	 */
X	if (ti == 0)
X		goto present;
X
X	/*
X	 * Find a segment which begins after this one does.
X	 */
X	for (q = tp->seg_next; q != (struct tcpiphdr *)tp;
X	    q = (struct tcpiphdr *)q->ti_next)
X		if (SEQ_GT(q->ti_seq, ti->ti_seq))
X			break;
X
X	/*
X	 * If there is a preceding segment, it may provide some of
X	 * our data already.  If so, drop the data from the incoming
X	 * segment.  If it provides all of our data, drop us.
X	 */
X	if ((struct tcpiphdr *)q->ti_prev != (struct tcpiphdr *)tp) {
X		register int i;
X		q = (struct tcpiphdr *)q->ti_prev;
X		/* conversion to int (in i) handles seq wraparound */
X		i = q->ti_seq + q->ti_len - ti->ti_seq;
X		if (i > 0) {
X			if (i >= ti->ti_len) {
X				tcpstat.tcps_rcvduppack++;
X				tcpstat.tcps_rcvdupbyte += ti->ti_len;
X				goto drop;
X			}
X			m_adj(dtom(ti), i);
X			ti->ti_len -= i;
X			ti->ti_seq += i;
X		}
X		q = (struct tcpiphdr *)(q->ti_next);
X	}
X	tcpstat.tcps_rcvoopack++;
X	tcpstat.tcps_rcvoobyte += ti->ti_len;
X
X	/*
X	 * While we overlap succeeding segments trim them or,
X	 * if they are completely covered, dequeue them.
X	 */
X	while (q != (struct tcpiphdr *)tp) {
X		register int i = (ti->ti_seq + ti->ti_len) - q->ti_seq;
X		if (i <= 0)
X			break;
X		if (i < q->ti_len) {
X			q->ti_seq += i;
X			q->ti_len -= i;
X			m_adj(dtom(q), i);
X			break;
X		}
X		q = (struct tcpiphdr *)q->ti_next;
X		m = dtom(q->ti_prev);
X		remque(q->ti_prev);
X		m_freem(m);
X	}
X
X	/*
X	 * Stick new segment in its place.
X	 */
X	insque(ti, q->ti_prev);
X
Xpresent:
X	/*
X	 * Present data to user, advancing rcv_nxt through
X	 * completed sequence space.
X	 */
X	if (TCPS_HAVERCVDSYN(tp->t_state) == 0)
X		return (0);
X	ti = tp->seg_next;
X	if (ti == (struct tcpiphdr *)tp || ti->ti_seq != tp->rcv_nxt)
X		return (0);
X	if (tp->t_state == TCPS_SYN_RECEIVED && ti->ti_len)
X		return (0);
X	do {
X		tp->rcv_nxt += ti->ti_len;
X		flags = ti->ti_flags & TH_FIN;
X		remque(ti);
X		m = dtom(ti);
X		ti = (struct tcpiphdr *)ti->ti_next;
X		if (so->so_state & SS_CANTRCVMORE)
X			m_freem(m);
X		else
X			sbappend(&so->so_rcv, m);
X	} while (ti != (struct tcpiphdr *)tp && ti->ti_seq == tp->rcv_nxt);
X	sorwakeup(so);
X	return (flags);
Xdrop:
X	m_freem(dtom(ti));
X	return (0);
X}
X
X/*
X * TCP input routine, follows pages 65-76 of the
X * protocol specification dated September, 1981 very closely.
X */
Xtcp_input(m0)
X	struct mbuf *m0;
X{
X	register struct tcpiphdr *ti;
X	struct inpcb *inp;
X	register struct mbuf *m;
X	struct mbuf *om = 0;
X	int len, tlen, off;
X	register struct tcpcb *tp = 0;
X	register int tiflags;
X	struct socket *so;
X	int todrop, acked, ourfinisacked, needoutput = 0;
X	short ostate;
X	struct in_addr laddr;
X	int dropsocket = 0;
X	int iss = 0;
X
X	tcpstat.tcps_rcvtotal++;
X	/*
X	 * Get IP and TCP header together in first mbuf.
X	 * Note: IP leaves IP header in first mbuf.
X	 */
X	m = m0;
X	ti = mtod(m, struct tcpiphdr *);
X	if (((struct ip *)ti)->ip_hl > (sizeof (struct ip) >> 2))
X		ip_stripoptions((struct ip *)ti, (struct mbuf *)0);
X	if (m->m_off > MMAXOFF || m->m_len < sizeof (struct tcpiphdr)) {
X		if ((m = m_pullup(m, sizeof (struct tcpiphdr))) == 0) {
X			tcpstat.tcps_rcvshort++;
X			return;
X		}
X		ti = mtod(m, struct tcpiphdr *);
X	}
X
X	/*
X	 * Checksum extended TCP header and data.
X	 */
X	tlen = ((struct ip *)ti)->ip_len;
X	len = sizeof (struct ip) + tlen;
X	if (tcpcksum) {
X		ti->ti_next = ti->ti_prev = 0;
X		ti->ti_x1 = 0;
X		ti->ti_len = (u_short)tlen;
X		ti->ti_len = htons((u_short)ti->ti_len);
X		if (ti->ti_sum = in_cksum(m, len)) {
X			if (tcpprintfs)
X				printf("tcp sum: src %x\n", ti->ti_src);
X			tcpstat.tcps_rcvbadsum++;
X			goto drop;
X		}
X	}
X
X	/*
X	 * Check that TCP offset makes sense,
X	 * pull out TCP options and adjust length.
X	 */
X	off = ti->ti_off << 2;
X	if (off < sizeof (struct tcphdr) || off > tlen) {
X		if (tcpprintfs)
X			printf("tcp off: src %x off %d\n", ti->ti_src, off);
X		tcpstat.tcps_rcvbadoff++;
X		goto drop;
X	}
X	tlen -= off;
X	ti->ti_len = tlen;
X	if (off > sizeof (struct tcphdr)) {
X		if (m->m_len < sizeof(struct ip) + off) {
X			if ((m = m_pullup(m, sizeof (struct ip) + off)) == 0) {
X				tcpstat.tcps_rcvshort++;
X				return;
X			}
X			ti = mtod(m, struct tcpiphdr *);
X		}
X		om = m_get(M_DONTWAIT, MT_DATA);
X		if (om == 0)
X			goto drop;
X		om->m_len = off - sizeof (struct tcphdr);
X		{ caddr_t op = mtod(m, caddr_t) + sizeof (struct tcpiphdr);
X		  bcopy(op, mtod(om, caddr_t), (unsigned)om->m_len);
X		  m->m_len -= om->m_len;
X		  bcopy(op+om->m_len, op,
X		   (unsigned)(m->m_len-sizeof (struct tcpiphdr)));
X		}
X	}
X	tiflags = ti->ti_flags;
X
X	/*
X	 * Drop TCP and IP headers; TCP options were dropped above.
X	 */
X	m->m_off += sizeof(struct tcpiphdr);
X	m->m_len -= sizeof(struct tcpiphdr);
X
X	/*
X	 * Convert TCP protocol specific fields to host format.
X	 */
X	ti->ti_seq = ntohl(ti->ti_seq);
X	ti->ti_ack = ntohl(ti->ti_ack);
X	ti->ti_win = ntohs(ti->ti_win);
X	ti->ti_urp = ntohs(ti->ti_urp);
X
X	/*
X	 * Locate pcb for segment.
X	 */
Xfindpcb:
X	inp = in_pcblookup
X		(&tcb, ti->ti_src, ti->ti_sport, ti->ti_dst, ti->ti_dport,
X		INPLOOKUP_WILDCARD);
X
X	/*
X	 * If the state is CLOSED (i.e., TCB does not exist) then
X	 * all data in the incoming segment is discarded.
X	 * If the TCB exists but is in CLOSED state, it is embryonic,
X	 * but should either do a listen or a connect soon.
X	 */
X	if (inp == 0)
X		goto dropwithreset;
X	tp = intotcpcb(inp);
X	if (tp == 0)
X		goto dropwithreset;
X	if (tp->t_state == TCPS_CLOSED)
X		goto drop;
X	so = inp->inp_socket;
X	if (so->so_options & SO_DEBUG) {
X		ostate = tp->t_state;
X		tcp_saveti = *ti;
X	}
X	if (so->so_options & SO_ACCEPTCONN) {
X		so = sonewconn(so);
X		if (so == 0)
X			goto drop;
X		/*
X		 * This is ugly, but ....
X		 *
X		 * Mark socket as temporary until we're
X		 * committed to keeping it.  The code at
X		 * ``drop'' and ``dropwithreset'' check the
X		 * flag dropsocket to see if the temporary
X		 * socket created here should be discarded.
X		 * We mark the socket as discardable until
X		 * we're committed to it below in TCPS_LISTEN.
X		 */
X		dropsocket++;
X		inp = (struct inpcb *)so->so_pcb;
X		inp->inp_laddr = ti->ti_dst;
X		inp->inp_lport = ti->ti_dport;
X#if BSD>=43
X		inp->inp_options = ip_srcroute();
X#endif
X		tp = intotcpcb(inp);
X		tp->t_state = TCPS_LISTEN;
X	}
X
X	/*
X	 * Segment received on connection.
X	 * Reset idle time and keep-alive timer.
X	 */
X	tp->t_idle = 0;
X	tp->t_timer[TCPT_KEEP] = tcp_keepidle;
X
X	/*
X	 * Process options if not in LISTEN state,
X	 * else do it below (after getting remote address).
X	 */
X	if (om && tp->t_state != TCPS_LISTEN) {
X		tcp_dooptions(tp, om, ti);
X		om = 0;
X	}
X
X	/*
X	 * Calculate amount of space in receive window,
X	 * and then do TCP input processing.
X	 * Receive window is amount of space in rcv queue,
X	 * but not less than advertised window.
X	 */
X	{ int win;
X
X	win = sbspace(&so->so_rcv);
X	if (win < 0)
X		win = 0;
X	tp->rcv_wnd = MAX(win, (int)(tp->rcv_adv - tp->rcv_nxt));
X	}
X
X	switch (tp->t_state) {
X
X	/*
X	 * If the state is LISTEN then ignore segment if it contains an RST.
X	 * If the segment contains an ACK then it is bad and send a RST.
X	 * If it does not contain a SYN then it is not interesting; drop it.
X	 * Don't bother responding if the destination was a broadcast.
X	 * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial
X	 * tp->iss, and send a segment:
X	 *     <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK>
X	 * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss.
X	 * Fill in remote peer address fields if not previously specified.
X	 * Enter SYN_RECEIVED state, and process any other fields of this
X	 * segment in this state.
X	 */
X	case TCPS_LISTEN: {
X		struct mbuf *am;
X		register struct sockaddr_in *sin;
X
X		if (tiflags & TH_RST)
X			goto drop;
X		if (tiflags & TH_ACK)
X			goto dropwithreset;
X		if ((tiflags & TH_SYN) == 0)
X			goto drop;
X		if (in_broadcast(ti->ti_dst))
X			goto drop;
X		am = m_get(M_DONTWAIT, MT_SONAME);
X		if (am == NULL)
X			goto drop;
X		am->m_len = sizeof (struct sockaddr_in);
X		sin = mtod(am, struct sockaddr_in *);
X		sin->sin_family = AF_INET;
X		sin->sin_addr = ti->ti_src;
X		sin->sin_port = ti->ti_sport;
X		laddr = inp->inp_laddr;
X		if (inp->inp_laddr.s_addr == INADDR_ANY)
X			inp->inp_laddr = ti->ti_dst;
X		if (in_pcbconnect(inp, am)) {
X			inp->inp_laddr = laddr;
X			(void) m_free(am);
X			goto drop;
X		}
X		(void) m_free(am);
X		tp->t_template = tcp_template(tp);
X		if (tp->t_template == 0) {
X			tp = tcp_drop(tp, ENOBUFS);
X			dropsocket = 0;		/* socket is already gone */
X			goto drop;
X		}
X		if (om) {
X			tcp_dooptions(tp, om, ti);
X			om = 0;
X		}
X		if (iss)
X			tp->iss = iss;
X		else
X			tp->iss = tcp_iss;
X		tcp_iss += TCP_ISSINCR/2;
X		tp->irs = ti->ti_seq;
X		tcp_sendseqinit(tp);
X		tcp_rcvseqinit(tp);
X		tp->t_flags |= TF_ACKNOW;
X		tp->t_state = TCPS_SYN_RECEIVED;
X		tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
X		dropsocket = 0;		/* committed to socket */
X		tcpstat.tcps_accepts++;
X		goto trimthenstep6;
X		}
X
X	/*
X	 * If the state is SYN_SENT:
X	 *	if seg contains an ACK, but not for our SYN, drop the input.
X	 *	if seg contains a RST, then drop the connection.
X	 *	if seg does not contain SYN, then drop it.
X	 * Otherwise this is an acceptable SYN segment
X	 *	initialize tp->rcv_nxt and tp->irs
X	 *	if seg contains ack then advance tp->snd_una
X	 *	if SYN has been acked change to ESTABLISHED else SYN_RCVD state
X	 *	arrange for segment to be acked (eventually)
X	 *	continue processing rest of data/controls, beginning with URG
X	 */
X	case TCPS_SYN_SENT:
X		if ((tiflags & TH_ACK) &&
X		    (SEQ_LEQ(ti->ti_ack, tp->iss) ||
X		     SEQ_GT(ti->ti_ack, tp->snd_max)))
X			goto dropwithreset;
X		if (tiflags & TH_RST) {
X			if (tiflags & TH_ACK)
X				tp = tcp_drop(tp, ECONNREFUSED);
X			goto drop;
X		}
X		if ((tiflags & TH_SYN) == 0)
X			goto drop;
X		if (tiflags & TH_ACK) {
X			tp->snd_una = ti->ti_ack;
X			if (SEQ_LT(tp->snd_nxt, tp->snd_una))
X				tp->snd_nxt = tp->snd_una;
X		}
X		tp->t_timer[TCPT_REXMT] = 0;
X		tp->irs = ti->ti_seq;
X		tcp_rcvseqinit(tp);
X		tp->t_flags |= TF_ACKNOW;
X		if (tiflags & TH_ACK && SEQ_GT(tp->snd_una, tp->iss)) {
X			tcpstat.tcps_connects++;
X			soisconnected(so);
X			tp->t_state = TCPS_ESTABLISHED;
X			tp->t_maxseg = MIN(tp->t_maxseg, tcp_mss(tp));
X			(void) tcp_reass(tp, (struct tcpiphdr *)0);
X			/*
X			 * if we didn't have to retransmit the SYN,
X			 * use its rtt as our initial srtt & rtt var.
X			 */
X			if (tp->t_rtt) {
X				tp->t_srtt = tp->t_rtt << 3;
X				tp->t_rttvar = tp->t_rtt << 1;
X				TCPT_RANGESET(tp->t_rxtcur, 
X				    ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1,
X				    TCPTV_MIN, TCPTV_REXMTMAX);
X				tp->t_rtt = 0;
X			}
X		} else
X			tp->t_state = TCPS_SYN_RECEIVED;
X
Xtrimthenstep6:
X		/*
X		 * Advance ti->ti_seq to correspond to first data byte.
X		 * If data, trim to stay within window,
X		 * dropping FIN if necessary.
X		 */
X		ti->ti_seq++;
X		if (ti->ti_len > tp->rcv_wnd) {
X			todrop = ti->ti_len - tp->rcv_wnd;
X#if BSD>=43
X			m_adj(m, -todrop);
X#else
X			/* XXX work around 4.2 m_adj bug */
X			if (m->m_len) {
X				m_adj(m, -todrop);
X			} else {
X				/* skip tcp/ip header in first mbuf */
X				m_adj(m->m_next, -todrop);
X			}
X#endif
X			ti->ti_len = tp->rcv_wnd;
X			tiflags &= ~TH_FIN;
X			tcpstat.tcps_rcvpackafterwin++;
X			tcpstat.tcps_rcvbyteafterwin += todrop;
X		}
X		tp->snd_wl1 = ti->ti_seq - 1;
X		tp->rcv_up = ti->ti_seq;
X		goto step6;
X	}
X
X	/*
X	 * States other than LISTEN or SYN_SENT.
X	 * First check that at least some bytes of segment are within 
X	 * receive window.  If segment begins before rcv_nxt,
X	 * drop leading data (and SYN); if nothing left, just ack.
X	 */
X	todrop = tp->rcv_nxt - ti->ti_seq;
X	if (todrop > 0) {
X		if (tiflags & TH_SYN) {
X			tiflags &= ~TH_SYN;
X			ti->ti_seq++;
X			if (ti->ti_urp > 1) 
X				ti->ti_urp--;
X			else
X				tiflags &= ~TH_URG;
X			todrop--;
X		}
X		if (todrop > ti->ti_len ||
X		    todrop == ti->ti_len && (tiflags&TH_FIN) == 0) {
X			tcpstat.tcps_rcvduppack++;
X			tcpstat.tcps_rcvdupbyte += ti->ti_len;
X			/*
X			 * If segment is just one to the left of the window,
X			 * check two special cases:
X			 * 1. Don't toss RST in response to 4.2-style keepalive.
X			 * 2. If the only thing to drop is a FIN, we can drop
X			 *    it, but check the ACK or we will get into FIN
X			 *    wars if our FINs crossed (both CLOSING).
X			 * In either case, send ACK to resynchronize,
X			 * but keep on processing for RST or ACK.
X			 */
X			if ((tiflags & TH_FIN && todrop == ti->ti_len + 1)
X#ifdef TCP_COMPAT_42
X			  || (tiflags & TH_RST && ti->ti_seq == tp->rcv_nxt - 1)
X#endif
X			   ) {
X				todrop = ti->ti_len;
X				tiflags &= ~TH_FIN;
X				tp->t_flags |= TF_ACKNOW;
X			} else
X				goto dropafterack;
X		} else {
X			tcpstat.tcps_rcvpartduppack++;
X			tcpstat.tcps_rcvpartdupbyte += todrop;
X		}
X		m_adj(m, todrop);
X		ti->ti_seq += todrop;
X		ti->ti_len -= todrop;
X		if (ti->ti_urp > todrop)
X			ti->ti_urp -= todrop;
X		else {
X			tiflags &= ~TH_URG;
X			ti->ti_urp = 0;
X		}
X	}
X
X	/*
X	 * If new data are received on a connection after the
X	 * user processes are gone, then RST the other end.
X	 */
X	if ((so->so_state & SS_NOFDREF) &&
X	    tp->t_state > TCPS_CLOSE_WAIT && ti->ti_len) {
X		tp = tcp_close(tp);
X		tcpstat.tcps_rcvafterclose++;
X		goto dropwithreset;
X	}
X
X	/*
X	 * If segment ends after window, drop trailing data
X	 * (and PUSH and FIN); if nothing left, just ACK.
X	 */
X	todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd);
X	if (todrop > 0) {
X		tcpstat.tcps_rcvpackafterwin++;
X		if (todrop >= ti->ti_len) {
X			tcpstat.tcps_rcvbyteafterwin += ti->ti_len;
X			/*
X			 * If a new connection request is received
X			 * while in TIME_WAIT, drop the old connection
X			 * and start over if the sequence numbers
X			 * are above the previous ones.
X			 */
X			if (tiflags & TH_SYN &&
X			    tp->t_state == TCPS_TIME_WAIT &&
X			    SEQ_GT(ti->ti_seq, tp->rcv_nxt)) {
X				iss = tp->rcv_nxt + TCP_ISSINCR;
X				(void) tcp_close(tp);
X				goto findpcb;
X			}
X			/*
X			 * If window is closed can only take segments at
X			 * window edge, and have to drop data and PUSH from
X			 * incoming segments.  Continue processing, but
X			 * remember to ack.  Otherwise, drop segment
X			 * and ack.
X			 */
X			if (tp->rcv_wnd == 0 && ti->ti_seq == tp->rcv_nxt) {
X				tp->t_flags |= TF_ACKNOW;
X				tcpstat.tcps_rcvwinprobe++;
X			} else
X				goto dropafterack;
X		} else
X			tcpstat.tcps_rcvbyteafterwin += todrop;
X#if BSD>=43
X		m_adj(m, -todrop);
X#else
X		/* XXX work around m_adj bug */
X		if (m->m_len) {
X			m_adj(m, -todrop);
X		} else {
X			/* skip tcp/ip header in first mbuf */
X			m_adj(m->m_next, -todrop);
X		}
X#endif
X		ti->ti_len -= todrop;
X		tiflags &= ~(TH_PUSH|TH_FIN);
X	}
X
X	/*
X	 * If the RST bit is set examine the state:
X	 *    SYN_RECEIVED STATE:
X	 *	If passive open, return to LISTEN state.
X	 *	If active open, inform user that connection was refused.
X	 *    ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES:
X	 *	Inform user that connection was reset, and close tcb.
X	 *    CLOSING, LAST_ACK, TIME_WAIT STATES
X	 *	Close the tcb.
X	 */
X	if (tiflags&TH_RST) switch (tp->t_state) {
X
X	case TCPS_SYN_RECEIVED:
X		so->so_error = ECONNREFUSED;
X		goto close;
X
X	case TCPS_ESTABLISHED:
X	case TCPS_FIN_WAIT_1:
X	case TCPS_FIN_WAIT_2:
X	case TCPS_CLOSE_WAIT:
X		so->so_error = ECONNRESET;
X	close:
X		tp->t_state = TCPS_CLOSED;
X		tcpstat.tcps_drops++;
X		tp = tcp_close(tp);
X		goto drop;
X
X	case TCPS_CLOSING:
X	case TCPS_LAST_ACK:
X	case TCPS_TIME_WAIT:
X		tp = tcp_close(tp);
X		goto drop;
X	}
X
X	/*
X	 * If a SYN is in the window, then this is an
X	 * error and we send an RST and drop the connection.
X	 */
X	if (tiflags & TH_SYN) {
X		tp = tcp_drop(tp, ECONNRESET);
X		goto dropwithreset;
X	}
X
X	/*
X	 * If the ACK bit is off we drop the segment and return.
X	 */
X	if ((tiflags & TH_ACK) == 0)
X		goto drop;
X	
X	/*
X	 * Ack processing.
X	 */
X	switch (tp->t_state) {
X
X	/*
X	 * In SYN_RECEIVED state if the ack ACKs our SYN then enter
X	 * ESTABLISHED state and continue processing, otherwise
X	 * send an RST.
X	 */
X	case TCPS_SYN_RECEIVED:
X		if (SEQ_GT(tp->snd_una, ti->ti_ack) ||
X		    SEQ_GT(ti->ti_ack, tp->snd_max))
X			goto dropwithreset;
X		tcpstat.tcps_connects++;
X		soisconnected(so);
X		tp->t_state = TCPS_ESTABLISHED;
X		tp->t_maxseg = MIN(tp->t_maxseg, tcp_mss(tp));
X		(void) tcp_reass(tp, (struct tcpiphdr *)0);
X		tp->snd_wl1 = ti->ti_seq - 1;
X		/* fall into ... */
X
X	/*
X	 * In ESTABLISHED state: drop duplicate ACKs; ACK out of range
X	 * ACKs.  If the ack is in the range
X	 *	tp->snd_una < ti->ti_ack <= tp->snd_max
X	 * then advance tp->snd_una to ti->ti_ack and drop
X	 * data from the retransmission queue.  If this ACK reflects
X	 * more up to date window information we update our window information.
X	 */
X	case TCPS_ESTABLISHED:
X	case TCPS_FIN_WAIT_1:
X	case TCPS_FIN_WAIT_2:
X	case TCPS_CLOSE_WAIT:
X	case TCPS_CLOSING:
X	case TCPS_LAST_ACK:
X	case TCPS_TIME_WAIT:
X
X		if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) {
X			if (ti->ti_len == 0 && ti->ti_win == tp->snd_wnd) {
X				tcpstat.tcps_rcvdupack++;
X				/*
X				 * If we have outstanding data (not a
X				 * window probe), this is a completely
X				 * duplicate ack (ie, window info didn't
X				 * change), the ack is the biggest we've
X				 * seen and we've seen exactly our rexmt
X				 * threshhold of them, assume a packet
X				 * has been dropped and retransmit it.
X				 * Kludge snd_nxt & the congestion
X				 * window so we send only this one
X				 * packet.  If this packet fills the
X				 * only hole in the receiver's seq.
X				 * space, the next real ack will fully
X				 * open our window.  This means we
X				 * have to do the usual slow-start to
X				 * not overwhelm an intermediate gateway
X				 * with a burst of packets.  Leave
X				 * here with the congestion window set
X				 * to allow 2 packets on the next real
X				 * ack and the exp-to-linear thresh
X				 * set for half the current window
X				 * size (since we know we're losing at
X				 * the current window size).
X				 */
X				if (tp->t_timer[TCPT_REXMT] == 0 ||
X				    ti->ti_ack != tp->snd_una)
X					tp->t_dupacks = 0;
X				else if (++tp->t_dupacks == tcprexmtthresh) {
X					tcp_seq onxt = tp->snd_nxt;
X					u_int win =
X					    MIN(tp->snd_wnd, tp->snd_cwnd) / 2 /
X						tp->t_maxseg;
X
X					if (win < 2)
X						win = 2;
X					tp->snd_ssthresh = win * tp->t_maxseg;
X
X					tp->t_timer[TCPT_REXMT] = 0;
X					tp->t_rtt = 0;
X					tp->snd_nxt = ti->ti_ack;
X					tp->snd_cwnd = tp->t_maxseg;
X					(void) tcp_output(tp);
X
X					if (SEQ_GT(onxt, tp->snd_nxt))
X						tp->snd_nxt = onxt;
X					goto drop;
X				}
X			} else
X				tp->t_dupacks = 0;
X			break;
X		}
X		tp->t_dupacks = 0;
X		if (SEQ_GT(ti->ti_ack, tp->snd_max)) {
X			tcpstat.tcps_rcvacktoomuch++;
X			goto dropafterack;
X		}
X		acked = ti->ti_ack - tp->snd_una;
X		tcpstat.tcps_rcvackpack++;
X		tcpstat.tcps_rcvackbyte += acked;
X
X		/*
X		 * If transmit timer is running and timed sequence
X		 * number was acked, update smoothed round trip time.
X		 * Since we now have an rtt measurement, cancel the
X		 * timer backoff (cf., Phil Karn's retransmit alg.).
X		 * Recompute the initial retransmit timer.
X		 */
X		if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq)) {
X			tcpstat.tcps_rttupdated++;
X			if (tp->t_srtt != 0) {
X				register short delta;
X
X				/*
X				 * srtt is stored as fixed point with 3 bits
X				 * after the binary point (i.e., scaled by 8).
X				 * The following magic is equivalent
X				 * to the smoothing algorithm in rfc793
X				 * with an alpha of .875
X				 * (srtt = rtt/8 + srtt*7/8 in fixed point).
X				 * Adjust t_rtt to origin 0.
X				 */
X				delta = tp->t_rtt - 1 - (tp->t_srtt >> 3);
X				if ((tp->t_srtt += delta) <= 0)
X					tp->t_srtt = 1;
X				/*
X				 * We accumulate a smoothed rtt variance
X				 * (actually, a smoothed mean difference),
X				 * then set the retransmit timer to smoothed
X				 * rtt + 2 times the smoothed variance.
X				 * rttvar is stored as fixed point
X				 * with 2 bits after the binary point
X				 * (scaled by 4).  The following is equivalent
X				 * to rfc793 smoothing with an alpha of .75
X				 * (rttvar = rttvar*3/4 + |delta| / 4).
X				 * This replaces rfc793's wired-in beta.
X				 */
X				if (delta < 0)
X					delta = -delta;
X				delta -= (tp->t_rttvar >> 2);
X				if ((tp->t_rttvar += delta) <= 0)
X					tp->t_rttvar = 1;
X			} else {
X				/* 
X				 * No rtt measurement yet - use the
X				 * unsmoothed rtt.  Set the variance
X				 * to half the rtt (so our first
X				 * retransmit happens at 2*rtt)
X				 */
X				tp->t_srtt = tp->t_rtt << 3;
X				tp->t_rttvar = tp->t_rtt << 1;
X			}
X			tp->t_rtt = 0;
X			tp->t_rxtshift = 0;
X			TCPT_RANGESET(tp->t_rxtcur, 
X			    ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1,
X			    TCPTV_MIN, TCPTV_REXMTMAX);
X		}
X
X		/*
X		 * If all outstanding data is acked, stop retransmit
X		 * timer and remember to restart (more output or persist).
X		 * If there is more data to be acked, restart retransmit
X		 * timer, using current (possibly backed-off) value.
X		 */
X		if (ti->ti_ack == tp->snd_max) {
X			tp->t_timer[TCPT_REXMT] = 0;
X			needoutput = 1;
X		} else if (tp->t_timer[TCPT_PERSIST] == 0)
X			tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
X		/*
X		 * When new data is acked, open the congestion window.
X		 * If the window gives us less than ssthresh packets
X		 * in flight, open exponentially (maxseg per packet).
X		 * Otherwise open linearly (maxseg per window,
X		 * or maxseg^2 / cwnd per packet).
X		 */
X		{
X		u_int incr = tp->t_maxseg;
X
X		if (tp->snd_cwnd > tp->snd_ssthresh)
X			incr = MAX(incr * incr / tp->snd_cwnd, 1);
X
X		tp->snd_cwnd = MIN(tp->snd_cwnd + incr, IP_MAXPACKET); /* XXX */
X		}
X		if (acked > so->so_snd.sb_cc) {
X			tp->snd_wnd -= so->so_snd.sb_cc;
X			sbdrop(&so->so_snd, (int)so->so_snd.sb_cc);
X			ourfinisacked = 1;
X		} else {
X			sbdrop(&so->so_snd, acked);
X			tp->snd_wnd -= acked;
X			ourfinisacked = 0;
X		}
X		if ((so->so_snd.sb_flags & SB_WAIT) || so->so_snd.sb_sel)
X			sowwakeup(so);
X		tp->snd_una = ti->ti_ack;
X		if (SEQ_LT(tp->snd_nxt, tp->snd_una))
X			tp->snd_nxt = tp->snd_una;
X
X		switch (tp->t_state) {
X
X		/*
X		 * In FIN_WAIT_1 STATE in addition to the processing
X		 * for the ESTABLISHED state if our FIN is now acknowledged
X		 * then enter FIN_WAIT_2.
X		 */
X		case TCPS_FIN_WAIT_1:
X			if (ourfinisacked) {
X				/*
X				 * If we can't receive any more
X				 * data, then closing user can proceed.
X				 * Starting the timer is contrary to the
X				 * specification, but if we don't get a FIN
X				 * we'll hang forever.
X				 */
X				if (so->so_state & SS_CANTRCVMORE) {
X					soisdisconnected(so);
X					tp->t_timer[TCPT_2MSL] = tcp_maxidle;
X				}
X				tp->t_state = TCPS_FIN_WAIT_2;
X			}
X			break;
X
X	 	/*
X		 * In CLOSING STATE in addition to the processing for
X		 * the ESTABLISHED state if the ACK acknowledges our FIN
X		 * then enter the TIME-WAIT state, otherwise ignore
X		 * the segment.
X		 */
X		case TCPS_CLOSING:
X			if (ourfinisacked) {
X				tp->t_state = TCPS_TIME_WAIT;
X				tcp_canceltimers(tp);
X				tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
X				soisdisconnected(so);
X			}
X			break;
X
X		/*
X		 * In LAST_ACK, we may still be waiting for data to drain
X		 * and/or to be acked, as well as for the ack of our FIN.
X		 * If our FIN is now acknowledged, delete the TCB,
X		 * enter the closed state and return.
X		 */
X		case TCPS_LAST_ACK:
X			if (ourfinisacked) {
X				tp = tcp_close(tp);
X				goto drop;
X			}
X			break;
X
X		/*
X		 * In TIME_WAIT state the only thing that should arrive
X		 * is a retransmission of the remote FIN.  Acknowledge
X		 * it and restart the finack timer.
X		 */
X		case TCPS_TIME_WAIT:
X			tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
X			goto dropafterack;
X		}
X	}
X
Xstep6:
X	/*
X	 * Update window information.
X	 * Don't look at window if no ACK: TAC's send garbage on first SYN.
X	 */
X	if ((tiflags & TH_ACK) &&
X	    (SEQ_LT(tp->snd_wl1, ti->ti_seq) || tp->snd_wl1 == ti->ti_seq &&
X	    (SEQ_LT(tp->snd_wl2, ti->ti_ack) ||
X	     tp->snd_wl2 == ti->ti_ack && ti->ti_win > tp->snd_wnd))) {
X		/* keep track of pure window updates */
X		if (ti->ti_len == 0 &&
X		    tp->snd_wl2 == ti->ti_ack && ti->ti_win > tp->snd_wnd)
X			tcpstat.tcps_rcvwinupd++;
X		tp->snd_wnd = ti->ti_win;
X		tp->snd_wl1 = ti->ti_seq;
X		tp->snd_wl2 = ti->ti_ack;
X		if (tp->snd_wnd > tp->max_sndwnd)
X			tp->max_sndwnd = tp->snd_wnd;
X		needoutput = 1;
X	}
X
X	/*
X	 * Process segments with URG.
X	 */
X	if ((tiflags & TH_URG) && ti->ti_urp &&
X	    TCPS_HAVERCVDFIN(tp->t_state) == 0) {
X		/*
X		 * This is a kludge, but if we receive and accept
X		 * random urgent pointers, we'll crash in
X		 * soreceive.  It's hard to imagine someone
X		 * actually wanting to send this much urgent data.
X		 */
X		if (ti->ti_urp + so->so_rcv.sb_cc > SB_MAX) {
X			ti->ti_urp = 0;			/* XXX */
X			tiflags &= ~TH_URG;		/* XXX */
X			goto dodata;			/* XXX */
X		}
X		/*
X		 * If this segment advances the known urgent pointer,
X		 * then mark the data stream.  This should not happen
X		 * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since
X		 * a FIN has been received from the remote side. 
X		 * In these states we ignore the URG.
X		 *
X		 * According to RFC961 (Assigned Protocols),
X		 * the urgent pointer points to the last octet
X		 * of urgent data.  We continue, however,
X		 * to consider it to indicate the first octet
X		 * of data past the urgent section
X		 * as the original spec states.
X		 */
X		if (SEQ_GT(ti->ti_seq+ti->ti_urp, tp->rcv_up)) {
X			tp->rcv_up = ti->ti_seq + ti->ti_urp;
X			so->so_oobmark = so->so_rcv.sb_cc +
X			    (tp->rcv_up - tp->rcv_nxt) - 1;
X			if (so->so_oobmark == 0)
X				so->so_state |= SS_RCVATMARK;
X			sohasoutofband(so);
X			tp->t_oobflags &= ~(TCPOOB_HAVEDATA | TCPOOB_HADDATA);
X		}
X		/*
X		 * Remove out of band data so doesn't get presented to user.
X		 * This can happen independent of advancing the URG pointer,
X		 * but if two URG's are pending at once, some out-of-band
X		 * data may creep in... ick.
X		 */
X		if (ti->ti_urp <= ti->ti_len
X#ifdef SO_OOBINLINE
X		     && (so->so_options & SO_OOBINLINE) == 0
X#endif
X							   )
X			tcp_pulloutofband(so, ti);
X	} else
X		/*
X		 * If no out of band data is expected,
X		 * pull receive urgent pointer along
X		 * with the receive window.
X		 */
X		if (SEQ_GT(tp->rcv_nxt, tp->rcv_up))
X			tp->rcv_up = tp->rcv_nxt;
Xdodata:							/* XXX */
X
X	/*
X	 * Process the segment text, merging it into the TCP sequencing queue,
X	 * and arranging for acknowledgment of receipt if necessary.
X	 * This process logically involves adjusting tp->rcv_wnd as data
X	 * is presented to the user (this happens in tcp_usrreq.c,
X	 * case PRU_RCVD).  If a FIN has already been received on this
X	 * connection then we just ignore the text.
X	 */
X	if ((ti->ti_len || (tiflags&TH_FIN)) &&
X	    TCPS_HAVERCVDFIN(tp->t_state) == 0) {
X		TCP_REASS(tp, ti, m, so, tiflags);
X		if (tcpnodelack == 0)
X			tp->t_flags |= TF_DELACK;
X		else
X			tp->t_flags |= TF_ACKNOW;
X		/*
X		 * Note the amount of data that peer has sent into
X		 * our window, in order to estimate the sender's
X		 * buffer size.
X		 */
X		len = so->so_rcv.sb_hiwat - (tp->rcv_adv - tp->rcv_nxt);
X		if (len > tp->max_rcvd)
X			tp->max_rcvd = len;
X	} else {
X		m_freem(m);
X		tiflags &= ~TH_FIN;
X	}
X
X	/*
X	 * If FIN is received ACK the FIN and let the user know
X	 * that the connection is closing.
X	 */
X	if (tiflags & TH_FIN) {
X		if (TCPS_HAVERCVDFIN(tp->t_state) == 0) {
X			socantrcvmore(so);
X			tp->t_flags |= TF_ACKNOW;
X			tp->rcv_nxt++;
X		}
X		switch (tp->t_state) {
X
X	 	/*
X		 * In SYN_RECEIVED and ESTABLISHED STATES
X		 * enter the CLOSE_WAIT state.
X		 */
X		case TCPS_SYN_RECEIVED:
X		case TCPS_ESTABLISHED:
X			tp->t_state = TCPS_CLOSE_WAIT;
X			break;
X
X	 	/*
X		 * If still in FIN_WAIT_1 STATE FIN has not been acked so
X		 * enter the CLOSING state.
X		 */
X		case TCPS_FIN_WAIT_1:
X			tp->t_state = TCPS_CLOSING;
X			break;
X
X	 	/*
X		 * In FIN_WAIT_2 state enter the TIME_WAIT state,
X		 * starting the time-wait timer, turning off the other 
X		 * standard timers.
X		 */
X		case TCPS_FIN_WAIT_2:
X			tp->t_state = TCPS_TIME_WAIT;
X			tcp_canceltimers(tp);
X			tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
X			soisdisconnected(so);
X			break;
X
X		/*
X		 * In TIME_WAIT state restart the 2 MSL time_wait timer.
X		 */
X		case TCPS_TIME_WAIT:
X			tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
X			break;
X		}
X	}
X	if (so->so_options & SO_DEBUG)
X		tcp_trace(TA_INPUT, ostate, tp, &tcp_saveti, 0);
X
X	/*
X	 * Return any desired output.
X	 */
X	if (needoutput || (tp->t_flags & TF_ACKNOW))
X		(void) tcp_output(tp);
X	return;
X
Xdropafterack:
X	/*
X	 * Generate an ACK dropping incoming segment if it occupies
X	 * sequence space, where the ACK reflects our state.
X	 */
X	if (tiflags & TH_RST)
X		goto drop;
X	m_freem(m);
X	tp->t_flags |= TF_ACKNOW;
X	(void) tcp_output(tp);
X	return;
X
Xdropwithreset:
X	if (om) {
X		(void) m_free(om);
X		om = 0;
X	}
X	/*
X	 * Generate a RST, dropping incoming segment.
X	 * Make ACK acceptable to originator of segment.
X	 * Don't bother to respond if destination was broadcast.
X	 */
X	if ((tiflags & TH_RST) || in_broadcast(ti->ti_dst))
X		goto drop;
X	if (tiflags & TH_ACK)
X		tcp_respond(tp, ti, (tcp_seq)0, ti->ti_ack, TH_RST);
X	else {
X		if (tiflags & TH_SYN)
X			ti->ti_len++;
X		tcp_respond(tp, ti, ti->ti_seq+ti->ti_len, (tcp_seq)0,
X		    TH_RST|TH_ACK);
X	}
X	/* destroy temporarily created socket */
X	if (dropsocket)
X		(void) soabort(so);
X	return;
X
Xdrop:
X	if (om)
X		(void) m_free(om);
X	/*
X	 * Drop space held by incoming segment and return.
X	 */
X	if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))
X		tcp_trace(TA_DROP, ostate, tp, &tcp_saveti, 0);
X	m_freem(m);
X	/* destroy temporarily created socket */
X	if (dropsocket)
X		(void) soabort(so);
X	return;
X}
X
Xtcp_dooptions(tp, om, ti)
X	struct tcpcb *tp;
X	struct mbuf *om;
X	struct tcpiphdr *ti;
X{
X	register u_char *cp;
X	int opt, optlen, cnt;
X
X	cp = mtod(om, u_char *);
X	cnt = om->m_len;
X	for (; cnt > 0; cnt -= optlen, cp += optlen) {
X		opt = cp[0];
X		if (opt == TCPOPT_EOL)
X			break;
X		if (opt == TCPOPT_NOP)
X			optlen = 1;
X		else {
X			optlen = cp[1];
X			if (optlen <= 0)
X				break;
X		}
X		switch (opt) {
X
X		default:
X			break;
X
X		case TCPOPT_MAXSEG:
X			if (optlen != 4)
X				continue;
X			if (!(ti->ti_flags & TH_SYN))
X				continue;
X			tp->t_maxseg = *(u_short *)(cp + 2);
X			tp->t_maxseg = ntohs((u_short)tp->t_maxseg);
X			tp->t_maxseg = MIN(tp->t_maxseg, tcp_mss(tp));
X			break;
X		}
X	}
X	(void) m_free(om);
X}
X
X/*
X * Pull out of band byte out of a segment so
X * it doesn't appear in the user's data queue.
X * It is still reflected in the segment length for
X * sequencing purposes.
X */
Xtcp_pulloutofband(so, ti)
X	struct socket *so;
X	struct tcpiphdr *ti;
X{
X	register struct mbuf *m;
X	int cnt = ti->ti_urp - 1;
X	
X	m = dtom(ti);
X	while (cnt >= 0) {
X		if (m->m_len > cnt) {
X			char *cp = mtod(m, caddr_t) + cnt;
X			struct tcpcb *tp = sototcpcb(so);
X
X			tp->t_iobc = *cp;
X			tp->t_oobflags |= TCPOOB_HAVEDATA;
X			bcopy(cp+1, cp, (unsigned)(m->m_len - cnt - 1));
X			m->m_len--;
X			return;
X		}
X		cnt -= m->m_len;
X		m = m->m_next;
X		if (m == 0)
X			break;
X	}
X	panic("tcp_pulloutofband");
X}
X
X/*
X *  Determine a reasonable value for maxseg size.
X *  If the route is known, use one that can be handled
X *  on the given interface without forcing IP to fragment.
X *  If bigger than an mbuf cluster (MCLBYTES), round down to nearest size
X *  to utilize large mbufs.
X *  If interface pointer is unavailable, or the destination isn't local,
X *  use a conservative size (512 or the default IP max size, but no more
X *  than the mtu of the interface through which we route),
X *  as we can't discover anything about intervening gateways or networks.
X *  We also initialize the congestion/slow start window to be a single
X *  segment if the destination isn't local; this information should
X *  probably all be saved with the routing entry at the transport level.
X *
X *  This is ugly, and doesn't belong at this level, but has to happen somehow.
X */
Xtcp_mss(tp)
X	register struct tcpcb *tp;
X{
X	struct route *ro;
X	struct ifnet *ifp;
X	int mss;
X	struct inpcb *inp;
X
X	inp = tp->t_inpcb;
X	ro = &inp->inp_route;
X	if ((ro->ro_rt == (struct rtentry *)0) ||
X	    (ifp = ro->ro_rt->rt_ifp) == (struct ifnet *)0) {
X		/* No route yet, so try to acquire one */
X		if (inp->inp_faddr.s_addr != INADDR_ANY) {
X			ro->ro_dst.sa_family = AF_INET;
X			((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
X				inp->inp_faddr;
X			rtalloc(ro);
X		}
X		if ((ro->ro_rt == 0) || (ifp = ro->ro_rt->rt_ifp) == 0)
X			return (TCP_MSS);
X	}
X
X	mss = ifp->if_mtu - sizeof(struct tcpiphdr);
X#if	(MCLBYTES & (MCLBYTES - 1)) == 0
X	if (mss > MCLBYTES)
X		mss &= ~(MCLBYTES-1);
X#else
X	if (mss > MCLBYTES)
X		mss = mss / MCLBYTES * MCLBYTES;
X#endif
X	if (in_localaddr(inp->inp_faddr))
X		return (mss);
X
X	mss = MIN(mss, TCP_MSS);
X	tp->snd_cwnd = mss;
X	return (mss);
X}
X
X#if BSD<43
X/* XXX this belongs in netinet/in.c */
Xin_localaddr(in)
X	struct in_addr in;
X{
X	register u_long i = ntohl(in.s_addr);
X	register struct ifnet *ifp;
X	register struct sockaddr_in *sin;
X	register u_long mask;
X
X	if (IN_CLASSA(i))
X		mask = IN_CLASSA_NET;
X	else if (IN_CLASSB(i))
X		mask = IN_CLASSB_NET;
X	else if (IN_CLASSC(i))
X		mask = IN_CLASSC_NET;
X	else
X		return (0);
X
X	i &= mask;
X	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
X		if (ifp->if_addr.sa_family != AF_INET)
X			continue;
X		sin = (struct sockaddr_in *)&ifp->if_addr;
X		if ((sin->sin_addr.s_addr & mask) == i)
X			return (1);
X	}
X	return (0);
X}
X#endif
END-of-netinet/tcp_input.c
echo x - netinet/tcp_output.c
sed 's/^X//' >netinet/tcp_output.c << 'END-of-netinet/tcp_output.c'
X/*
X * Copyright (c) 1982, 1986 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that this notice is preserved and that due credit is given
X * to the University of California at Berkeley. The name of the University
X * may not be used to endorse or promote products derived from this
X * software without specific prior written permission. This software
X * is provided ``as is'' without express or implied warranty.
X *
X *	@(#)tcp_output.c	7.13.1.3 (Berkeley) 3/24/88
X */
X
X#include "param.h"
X#include "systm.h"
X#include "mbuf.h"
X#include "protosw.h"
X#include "socket.h"
X#include "socketvar.h"
X#include "errno.h"
X
X#include "../net/route.h"
X
X#include "in.h"
X#include "in_pcb.h"
X#include "in_systm.h"
X#include "ip.h"
X#include "ip_var.h"
X#include "tcp.h"
X#define	TCPOUTFLAGS
X#include "tcp_fsm.h"
X#include "tcp_seq.h"
X#include "tcp_timer.h"
X#include "tcp_var.h"
X#include "tcpip.h"
X#include "tcp_debug.h"
X
X/*
X * Initial options.
X */
Xu_char	tcp_initopt[4] = { TCPOPT_MAXSEG, 4, 0x0, 0x0, };
X
X/*
X * Tcp output routine: figure out what should be sent and send it.
X */
Xtcp_output(tp)
X	register struct tcpcb *tp;
X{
X	register struct socket *so = tp->t_inpcb->inp_socket;
X	register int len, win;
X	struct mbuf *m0;
X	int off, flags, error;
X	register struct mbuf *m;
X	register struct tcpiphdr *ti;
X	u_char *opt;
X	unsigned optlen = 0;
X	int idle, sendalot;
X
X	/*
X	 * Determine length of data that should be transmitted,
X	 * and flags that will be used.
X	 * If there is some data or critical controls (SYN, RST)
X	 * to send, then transmit; otherwise, investigate further.
X	 */
X	idle = (tp->snd_max == tp->snd_una);
Xagain:
X	sendalot = 0;
X	off = tp->snd_nxt - tp->snd_una;
X	win = MIN(tp->snd_wnd, tp->snd_cwnd);
X
X	/*
X	 * If in persist timeout with window of 0, send 1 byte.
X	 * Otherwise, if window is small but nonzero
X	 * and timer expired, we will send what we can
X	 * and go to transmit state.
X	 */
X	if (tp->t_force) {
X		if (win == 0)
X			win = 1;
X		else {
X			tp->t_timer[TCPT_PERSIST] = 0;
X			tp->t_rxtshift = 0;
X		}
X	}
X
X	len = MIN(so->so_snd.sb_cc, win) - off;
X	flags = tcp_outflags[tp->t_state];
X
X	if (len < 0) {
X		/*
X		 * If FIN has been sent but not acked,
X		 * but we haven't been called to retransmit,
X		 * len will be -1.  Otherwise, window shrank
X		 * after we sent into it.  If window shrank to 0,
X		 * cancel pending retransmit and pull snd_nxt
X		 * back to (closed) window.  We will enter persist
X		 * state below.  If the window didn't close completely,
X		 * just wait for an ACK.
X		 */
X		len = 0;
X		if (win == 0) {
X			tp->t_timer[TCPT_REXMT] = 0;
X			tp->snd_nxt = tp->snd_una;
X		}
X	}
X	if (len > tp->t_maxseg) {
X		len = tp->t_maxseg;
X		sendalot = 1;
X	}
X	if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc))
X		flags &= ~TH_FIN;
X	win = sbspace(&so->so_rcv);
X
X
X	/*
X	 * If our state indicates that FIN should be sent
X	 * and we have not yet done so, or we're retransmitting the FIN,
X	 * then we need to send.
X	 */
X	if (flags & TH_FIN &&
X	    ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una))
X		goto send;
X	/*
X	 * Send if we owe peer an ACK.
X	 */
X	if (tp->t_flags & TF_ACKNOW)
X		goto send;
X	if (flags & (TH_SYN|TH_RST))
X		goto send;
X	if (SEQ_GT(tp->snd_up, tp->snd_una))
X		goto send;
X
X	/*
X	 * Sender silly window avoidance.  If connection is idle
X	 * and can send all data, a maximum segment,
X	 * at least a maximum default-size segment do it,
X	 * or are forced, do it; otherwise don't bother.
X	 * If peer's buffer is tiny, then send
X	 * when window is at least half open.
X	 * If retransmitting (possibly after persist timer forced us
X	 * to send into a small window), then must resend.
X	 */
X	if (len) {
X		if (len == tp->t_maxseg)
X			goto send;
X		if ((idle || tp->t_flags & TF_NODELAY) &&
X		    len + off >= so->so_snd.sb_cc)
X			goto send;
X		if (tp->t_force)
X			goto send;
X		if (len >= tp->max_sndwnd / 2)
X			goto send;
X		if (SEQ_LT(tp->snd_nxt, tp->snd_max))
X			goto send;
X	}
X
X	/*
X	 * Compare available window to amount of window
X	 * known to peer (as advertised window less
X	 * next expected input).  If the difference is at least two
X	 * max size segments or at least 35% of the maximum possible
X	 * window, then want to send a window update to peer.
X	 */
X	if (win > 0) {
X		int adv = win - (tp->rcv_adv - tp->rcv_nxt);
X
X		if (so->so_rcv.sb_cc == 0 && adv >= 2 * tp->t_maxseg)
X			goto send;
X		if (100 * adv / so->so_rcv.sb_hiwat >= 35)
X			goto send;
X	}
X
X	/*
X	 * TCP window updates are not reliable, rather a polling protocol
X	 * using ``persist'' packets is used to insure receipt of window
X	 * updates.  The three ``states'' for the output side are:
X	 *	idle			not doing retransmits or persists
X	 *	persisting		to move a small or zero window
X	 *	(re)transmitting	and thereby not persisting
X	 *
X	 * tp->t_timer[TCPT_PERSIST]
X	 *	is set when we are in persist state.
X	 * tp->t_force
X	 *	is set when we are called to send a persist packet.
X	 * tp->t_timer[TCPT_REXMT]
X	 *	is set when we are retransmitting
X	 * The output side is idle when both timers are zero.
X	 *
X	 * If send window is too small, there is data to transmit, and no
X	 * retransmit or persist is pending, then go to persist state.
X	 * If nothing happens soon, send when timer expires:
X	 * if window is nonzero, transmit what we can,
X	 * otherwise force out a byte.
X	 */
X	if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 &&
X	    tp->t_timer[TCPT_PERSIST] == 0) {
X		tp->t_rxtshift = 0;
X		tcp_setpersist(tp);
X	}
X
X	/*
X	 * No reason to send a segment, just return.
X	 */
X	return (0);
X
Xsend:
X	/*
X	 * Grab a header mbuf, attaching a copy of data to
X	 * be transmitted, and initialize the header from
X	 * the template for sends on this connection.
X	 */
X	MGET(m, M_DONTWAIT, MT_HEADER);
X	if (m == NULL)
X		return (ENOBUFS);
X	m->m_off = MMAXOFF - sizeof (struct tcpiphdr);
X	m->m_len = sizeof (struct tcpiphdr);
X	if (len) {
X		if (tp->t_force && len == 1)
X			tcpstat.tcps_sndprobe++;
X		else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) {
X			tcpstat.tcps_sndrexmitpack++;
X			tcpstat.tcps_sndrexmitbyte += len;
X		} else {
X			tcpstat.tcps_sndpack++;
X			tcpstat.tcps_sndbyte += len;
X		}
X		m->m_next = m_copy(so->so_snd.sb_mb, off, len);
X		if (m->m_next == 0)
X			len = 0;
X	} else if (tp->t_flags & TF_ACKNOW)
X		tcpstat.tcps_sndacks++;
X	else if (flags & (TH_SYN|TH_FIN|TH_RST))
X		tcpstat.tcps_sndctrl++;
X	else if (SEQ_GT(tp->snd_up, tp->snd_una))
X		tcpstat.tcps_sndurg++;
X	else
X		tcpstat.tcps_sndwinup++;
X
X	ti = mtod(m, struct tcpiphdr *);
X	if (tp->t_template == 0)
X		panic("tcp_output");
X	bcopy((caddr_t)tp->t_template, (caddr_t)ti, sizeof (struct tcpiphdr));
X
X	/*
X	 * Fill in fields, remembering maximum advertised
X	 * window for use in delaying messages about window sizes.
X	 * If resending a FIN, be sure not to use a new sequence number.
X	 */
X	if (flags & TH_FIN && tp->t_flags & TF_SENTFIN && 
X	    tp->snd_nxt == tp->snd_max)
X		tp->snd_nxt--;
X	ti->ti_seq = htonl(tp->snd_nxt);
X	ti->ti_ack = htonl(tp->rcv_nxt);
X	/*
X	 * Before ESTABLISHED, force sending of initial options
X	 * unless TCP set to not do any options.
X	 */
X	opt = NULL;
X	if (flags & TH_SYN && (tp->t_flags & TF_NOOPT) == 0) {
X		u_short mss;
X
X		mss = MIN(so->so_rcv.sb_hiwat / 2, tcp_mss(tp));
X		if (mss > IP_MSS - sizeof(struct tcpiphdr)) {
X			opt = tcp_initopt;
X			optlen = sizeof (tcp_initopt);
X			*(u_short *)(opt + 2) = htons(mss);
X		}
X	}
X	if (opt) {
X		m0 = m->m_next;
X		m->m_next = m_get(M_DONTWAIT, MT_DATA);
X		if (m->m_next == 0) {
X			(void) m_free(m);
X			m_freem(m0);
X			return (ENOBUFS);
X		}
X		m->m_next->m_next = m0;
X		m0 = m->m_next;
X		m0->m_len = optlen;
X		bcopy((caddr_t)opt, mtod(m0, caddr_t), optlen);
X		opt = (u_char *)(mtod(m0, caddr_t) + optlen);
X		while (m0->m_len & 0x3) {
X			*opt++ = TCPOPT_EOL;
X			m0->m_len++;
X		}
X		optlen = m0->m_len;
X		ti->ti_off = (sizeof (struct tcphdr) + optlen) >> 2;
X	}
X	ti->ti_flags = flags;
X	/*
X	 * Calculate receive window.  Don't shrink window,
X	 * but avoid silly window syndrome.
X	 */
X	if (win < (long)(so->so_rcv.sb_hiwat / 4) && win < (long)tp->t_maxseg)
X		win = 0;
X	if (win < (long)(tp->rcv_adv - tp->rcv_nxt))
X		win = (long)(tp->rcv_adv - tp->rcv_nxt);
X	if (win > IP_MAXPACKET)
X		win = IP_MAXPACKET;
X	ti->ti_win = htons((u_short)win);
X	if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {
X		ti->ti_urp = htons((u_short)(tp->snd_up - tp->snd_nxt));
X		ti->ti_flags |= TH_URG;
X	} else
X		/*
X		 * If no urgent pointer to send, then we pull
X		 * the urgent pointer to the left edge of the send window
X		 * so that it doesn't drift into the send window on sequence
X		 * number wraparound.
X		 */
X		tp->snd_up = tp->snd_una;		/* drag it along */
X	/*
X	 * If anything to send and we can send it all, set PUSH.
X	 * (This will keep happy those implementations which only
X	 * give data to the user when a buffer fills or a PUSH comes in.)
X	 */
X	if (len && off+len == so->so_snd.sb_cc)
X		ti->ti_flags |= TH_PUSH;
X
X	/*
X	 * Put TCP length in extended header, and then
X	 * checksum extended header and data.
X	 */
X	if (len + optlen)
X		ti->ti_len = htons((u_short)(sizeof(struct tcphdr) +
X		    optlen + len));
X	ti->ti_sum = in_cksum(m, sizeof (struct tcpiphdr) + (int)optlen + len);
X
X	/*
X	 * In transmit state, time the transmission and arrange for
X	 * the retransmit.  In persist state, just set snd_max.
X	 */
X	if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) {
X		tcp_seq startseq = tp->snd_nxt;
X
X		/*
X		 * Advance snd_nxt over sequence space of this segment.
X		 */
X		if (flags & TH_SYN)
X			tp->snd_nxt++;
X		if (flags & TH_FIN) {
X			tp->snd_nxt++;
X			tp->t_flags |= TF_SENTFIN;
X		}
X		tp->snd_nxt += len;
X		if (SEQ_GT(tp->snd_nxt, tp->snd_max)) {
X			tp->snd_max = tp->snd_nxt;
X			/*
X			 * Time this transmission if not a retransmission and
X			 * not currently timing anything.
X			 */
X			if (tp->t_rtt == 0) {
X				tp->t_rtt = 1;
X				tp->t_rtseq = startseq;
X				tcpstat.tcps_segstimed++;
X			}
X		}
X
X		/*
X		 * Set retransmit timer if not currently set,
X		 * and not doing an ack or a keep-alive probe.
X		 * Initial value for retransmit timer is smoothed
X		 * round-trip time + 2 * round-trip time variance.
X		 * Initialize shift counter which is used for backoff
X		 * of retransmit time.
X		 */
X		if (tp->t_timer[TCPT_REXMT] == 0 &&
X		    tp->snd_nxt != tp->snd_una) {
X			tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
X			if (tp->t_timer[TCPT_PERSIST]) {
X				tp->t_timer[TCPT_PERSIST] = 0;
X				tp->t_rxtshift = 0;
X			}
X		}
X	} else
X		if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))
X			tp->snd_max = tp->snd_nxt + len;
X
X	/*
X	 * Trace.
X	 */
X	if (so->so_options & SO_DEBUG)
X		tcp_trace(TA_OUTPUT, tp->t_state, tp, ti, 0);
X
X	/*
X	 * Fill in IP length and desired time to live and
X	 * send to IP level.
X	 */
X	((struct ip *)ti)->ip_len = sizeof (struct tcpiphdr) + optlen + len;
X	((struct ip *)ti)->ip_ttl = TCP_TTL;
X#if BSD>=43
X	error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
X	    so->so_options & SO_DONTROUTE);
X#else
X	error = ip_output(m, (struct mbuf *)0, &tp->t_inpcb->inp_route, 
X			  so->so_options & SO_DONTROUTE);
X#endif
X	if (error) {
X		if (error == ENOBUFS) {
X			tcp_quench(tp->t_inpcb);
X			return (0);
X		}
X		return (error);
X	}
X	tcpstat.tcps_sndtotal++;
X
X	/*
X	 * Data sent (as far as we can tell).
X	 * If this advertises a larger window than any other segment,
X	 * then remember the size of the advertised window.
X	 * Any pending ACK has now been sent.
X	 */
X	if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
X		tp->rcv_adv = tp->rcv_nxt + win;
X	tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
X	if (sendalot)
X		goto again;
X	return (0);
X}
X
Xtcp_setpersist(tp)
X	register struct tcpcb *tp;
X{
X	register t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
X
X	if (tp->t_timer[TCPT_REXMT])
X		panic("tcp_output REXMT");
X	/*
X	 * Start/restart persistance timer.
X	 */
X	TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
X	    t * tcp_backoff[tp->t_rxtshift],
X	    TCPTV_PERSMIN, TCPTV_PERSMAX);
X	if (tp->t_rxtshift < TCP_MAXRXTSHIFT)
X		tp->t_rxtshift++;
X}
END-of-netinet/tcp_output.c
echo c - netstat
mkdir netstat > /dev/null 2>&1
echo x - netstat/inet.c
sed 's/^X//' >netstat/inet.c << 'END-of-netstat/inet.c'
X/*
X * Copyright (c) 1983,1988 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that this notice is preserved and that due credit is given
X * to the University of California at Berkeley. The name of the University
X * may not be used to endorse or promote products derived from this
X * software without specific prior written permission. This software
X * is provided ``as is'' without express or implied warranty.
X */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)inet.c	5.9.1.1 (Berkeley) 2/7/88";
X#endif not lint
X
X#include <strings.h>
X#include <stdio.h>
X
X#include <sys/param.h>
X#include <sys/socket.h>
X#include <sys/socketvar.h>
X#include <sys/mbuf.h>
X#include <sys/protosw.h>
X
X#include <net/route.h>
X#include <netinet/in.h>
X#include <netinet/in_systm.h>
X#include <netinet/in_pcb.h>
X#include <netinet/ip.h>
X#include <netinet/ip_icmp.h>
X#include <netinet/icmp_var.h>
X#include <netinet/ip_var.h>
X#include <netinet/tcp.h>
X#include <netinet/tcpip.h>
X#include <netinet/tcp_seq.h>
X#define TCPSTATES
X#include <netinet/tcp_fsm.h>
X#include <netinet/tcp_timer.h>
X#include <netinet/tcp_var.h>
X#include <netinet/tcp_debug.h>
X#include <netinet/udp.h>
X#include <netinet/udp_var.h>
X
X#include <netdb.h>
X
Xstruct	inpcb inpcb;
Xstruct	tcpcb tcpcb;
Xstruct	socket sockb;
Xextern	int kmem;
Xextern	int Aflag;
Xextern	int aflag;
Xextern	int nflag;
Xextern	char *plural();
X
Xchar	*inetname();
X
X/*
X * Print a summary of connections related to an Internet
X * protocol.  For TCP, also give state of connection.
X * Listening processes (aflag) are suppressed unless the
X * -a (all) flag is specified.
X */
Xprotopr(off, name)
X	off_t off;
X	char *name;
X{
X	struct inpcb cb;
X	register struct inpcb *prev, *next;
X	int istcp;
X	static int first = 1;
X
X	if (off == 0)
X		return;
X	istcp = strcmp(name, "tcp") == 0;
X	klseek(kmem, off, 0);
X	read(kmem, (char *)&cb, sizeof (struct inpcb));
X	inpcb = cb;
X	prev = (struct inpcb *)off;
X	if (inpcb.inp_next == (struct inpcb *)off)
X		return;
X	while (inpcb.inp_next != (struct inpcb *)off) {
X
X		next = inpcb.inp_next;
X		klseek(kmem, (off_t)next, 0);
X		read(kmem, (char *)&inpcb, sizeof (inpcb));
X		if (inpcb.inp_prev != prev) {
X			printf("???\n");
X			break;
X		}
X		if (!aflag &&
X		  inet_lnaof(inpcb.inp_laddr) == INADDR_ANY) {
X			prev = next;
X			continue;
X		}
X		klseek(kmem, (off_t)inpcb.inp_socket, 0);
X		read(kmem, (char *)&sockb, sizeof (sockb));
X		if (istcp) {
X			klseek(kmem, (off_t)inpcb.inp_ppcb, 0);
X			read(kmem, (char *)&tcpcb, sizeof (tcpcb));
X		}
X		if (first) {
X			printf("Active Internet connections");
X			if (aflag)
X				printf(" (including servers)");
X			putchar('\n');
X			if (Aflag)
X				printf("%-8.8s ", "PCB");
X			printf(Aflag ?
X				"%-5.5s %-6.6s %-6.6s  %-18.18s %-18.18s %s\n" :
X				"%-5.5s %-6.6s %-6.6s  %-22.22s %-22.22s %s\n",
X				"Proto", "Recv-Q", "Send-Q",
X				"Local Address", "Foreign Address", "(state)");
X			first = 0;
X		}
X		if (Aflag)
X			if (istcp)
X				printf("%8x ", inpcb.inp_ppcb);
X			else
X				printf("%8x ", next);
X		printf("%-5.5s %6d %6d ", name, sockb.so_rcv.sb_cc,
X			sockb.so_snd.sb_cc);
X		inetprint(&inpcb.inp_laddr, inpcb.inp_lport, name);
X		inetprint(&inpcb.inp_faddr, inpcb.inp_fport, name);
X		if (istcp) {
X			if (tcpcb.t_state < 0 || tcpcb.t_state >= TCP_NSTATES)
X				printf(" %d", tcpcb.t_state);
X			else
X				printf(" %s", tcpstates[tcpcb.t_state]);
X		}
X		putchar('\n');
X		prev = next;
X	}
X}
X
X/*
X * Dump TCP statistics structure.
X */
Xtcp_stats(off, name)
X	off_t off;
X	char *name;
X{
X	struct tcpstat tcpstat;
X
X	if (off == 0)
X		return;
X	printf ("%s:\n", name);
X	klseek(kmem, off, 0);
X	read(kmem, (char *)&tcpstat, sizeof (tcpstat));
X
X#define	p(f, m)		printf(m, tcpstat.f, plural(tcpstat.f))
X#define	p2(f1, f2, m)	printf(m, tcpstat.f1, plural(tcpstat.f1), tcpstat.f2, plural(tcpstat.f2))
X  
X	p(tcps_sndtotal, "\t%d packet%s sent\n");
X	p2(tcps_sndpack,tcps_sndbyte,
X		"\t\t%d data packet%s (%d byte%s)\n");
X	p2(tcps_sndrexmitpack, tcps_sndrexmitbyte,
X		"\t\t%d data packet%s (%d byte%s) retransmitted\n");
X	p2(tcps_sndacks, tcps_delack,
X		"\t\t%d ack-only packet%s (%d delayed)\n");
X	p(tcps_sndurg, "\t\t%d URG only packet%s\n");
X	p(tcps_sndprobe, "\t\t%d window probe packet%s\n");
X	p(tcps_sndwinup, "\t\t%d window update packet%s\n");
X	p(tcps_sndctrl, "\t\t%d control packet%s\n");
X	p(tcps_rcvtotal, "\t%d packet%s received\n");
X	p2(tcps_rcvackpack, tcps_rcvackbyte, "\t\t%d ack%s (for %d byte%s)\n");
X	p(tcps_rcvdupack, "\t\t%d duplicate ack%s\n");
X	p(tcps_rcvacktoomuch, "\t\t%d ack%s for unsent data\n");
X	p2(tcps_rcvpack, tcps_rcvbyte,
X		"\t\t%d packet%s (%d byte%s) received in-sequence\n");
X	p2(tcps_rcvduppack, tcps_rcvdupbyte,
X		"\t\t%d completely duplicate packet%s (%d byte%s)\n");
X	p2(tcps_rcvpartduppack, tcps_rcvpartdupbyte,
X		"\t\t%d packet%s with some dup. data (%d byte%s duped)\n");
X	p2(tcps_rcvoopack, tcps_rcvoobyte,
X		"\t\t%d out-of-order packet%s (%d byte%s)\n");
X	p2(tcps_rcvpackafterwin, tcps_rcvbyteafterwin,
X		"\t\t%d packet%s (%d byte%s) of data after window\n");
X	p(tcps_rcvwinprobe, "\t\t%d window probe%s\n");
X	p(tcps_rcvwinupd, "\t\t%d window update packet%s\n");
X	p(tcps_rcvafterclose, "\t\t%d packet%s received after close\n");
X	p(tcps_rcvbadsum, "\t\t%d discarded for bad checksum%s\n");
X	p(tcps_rcvbadoff, "\t\t%d discarded for bad header offset field%s\n");
X	p(tcps_rcvshort, "\t\t%d discarded because packet too short\n");
X	p(tcps_connattempt, "\t%d connection request%s\n");
X	p(tcps_accepts, "\t%d connection accept%s\n");
X	p(tcps_connects, "\t%d connection%s established (including accepts)\n");
X	p2(tcps_closed, tcps_drops,
X		"\t%d connection%s closed (including %d drop%s)\n");
X	p(tcps_conndrops, "\t%d embryonic connection%s dropped\n");
X	p2(tcps_rttupdated, tcps_segstimed,
X		"\t%d segment%s updated rtt (of %d attempt%s)\n");
X	p(tcps_rexmttimeo, "\t%d retransmit timeout%s\n");
X	p(tcps_timeoutdrop, "\t\t%d connection%s dropped by rexmit timeout\n");
X	p(tcps_persisttimeo, "\t%d persist timeout%s\n");
X	p(tcps_keeptimeo, "\t%d keepalive timeout%s\n");
X	p(tcps_keepprobe, "\t\t%d keepalive probe%s sent\n");
X	p(tcps_keepdrops, "\t\t%d connection%s dropped by keepalive\n");
X#undef p
X#undef p2
X}
X
X/*
X * Dump UDP statistics structure.
X */
Xudp_stats(off, name)
X	off_t off;
X	char *name;
X{
X	struct udpstat udpstat;
X
X	if (off == 0)
X		return;
X	klseek(kmem, off, 0);
X	read(kmem, (char *)&udpstat, sizeof (udpstat));
X	printf("%s:\n\t%u incomplete header%s\n", name,
X		udpstat.udps_hdrops, plural(udpstat.udps_hdrops));
X	printf("\t%u bad data length field%s\n",
X		udpstat.udps_badlen, plural(udpstat.udps_badlen));
X	printf("\t%u bad checksum%s\n",
X		udpstat.udps_badsum, plural(udpstat.udps_badsum));
X#ifdef sun
X	printf("\t%d socket overflow%s\n",
X		udpstat.udps_fullsock, plural(udpstat.udps_fullsock));
X#endif
X}
X
X/*
X * Dump IP statistics structure.
X */
Xip_stats(off, name)
X	off_t off;
X	char *name;
X{
X	struct ipstat ipstat;
X
X	if (off == 0)
X		return;
X	klseek(kmem, off, 0);
X	read(kmem, (char *)&ipstat, sizeof (ipstat));
X#if BSD>=43
X	printf("%s:\n\t%u total packets received\n", name,
X		ipstat.ips_total);
X#endif
X	printf("\t%u bad header checksum%s\n",
X		ipstat.ips_badsum, plural(ipstat.ips_badsum));
X	printf("\t%u with size smaller than minimum\n", ipstat.ips_tooshort);
X	printf("\t%u with data size < data length\n", ipstat.ips_toosmall);
X	printf("\t%u with header length < data size\n", ipstat.ips_badhlen);
X	printf("\t%u with data length < header length\n", ipstat.ips_badlen);
X#if BSD>=43
X	printf("\t%u fragment%s received\n",
X		ipstat.ips_fragments, plural(ipstat.ips_fragments));
X	printf("\t%u fragment%s dropped (dup or out of space)\n",
X		ipstat.ips_fragdropped, plural(ipstat.ips_fragdropped));
X	printf("\t%u fragment%s dropped after timeout\n",
X		ipstat.ips_fragtimeout, plural(ipstat.ips_fragtimeout));
X	printf("\t%u packet%s forwarded\n",
X		ipstat.ips_forward, plural(ipstat.ips_forward));
X	printf("\t%u packet%s not forwardable\n",
X		ipstat.ips_cantforward, plural(ipstat.ips_cantforward));
X	printf("\t%u redirect%s sent\n",
X		ipstat.ips_redirectsent, plural(ipstat.ips_redirectsent));
X#endif
X}
X
Xstatic	char *icmpnames[] = {
X	"echo reply",
X	"#1",
X	"#2",
X	"destination unreachable",
X	"source quench",
X	"routing redirect",
X	"#6",
X	"#7",
X	"echo",
X	"#9",
X	"#10",
X	"time exceeded",
X	"parameter problem",
X	"time stamp",
X	"time stamp reply",
X	"information request",
X	"information request reply",
X	"address mask request",
X	"address mask reply",
X};
X
X/*
X * Dump ICMP statistics.
X */
Xicmp_stats(off, name)
X	off_t off;
X	char *name;
X{
X	struct icmpstat icmpstat;
X	register int i, first;
X
X	if (off == 0)
X		return;
X	klseek(kmem, off, 0);
X	read(kmem, (char *)&icmpstat, sizeof (icmpstat));
X	printf("%s:\n\t%u call%s to icmp_error\n", name,
X		icmpstat.icps_error, plural(icmpstat.icps_error));
X	printf("\t%u error%s not generated 'cuz old message was icmp\n",
X		icmpstat.icps_oldicmp, plural(icmpstat.icps_oldicmp));
X	for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++)
X		if (icmpstat.icps_outhist[i] != 0) {
X			if (first) {
X				printf("\tOutput histogram:\n");
X				first = 0;
X			}
X			printf("\t\t%s: %u\n", icmpnames[i],
X				icmpstat.icps_outhist[i]);
X		}
X	printf("\t%u message%s with bad code fields\n",
X		icmpstat.icps_badcode, plural(icmpstat.icps_badcode));
X	printf("\t%u message%s < minimum length\n",
X		icmpstat.icps_tooshort, plural(icmpstat.icps_tooshort));
X	printf("\t%u bad checksum%s\n",
X		icmpstat.icps_checksum, plural(icmpstat.icps_checksum));
X	printf("\t%u message%s with bad length\n",
X		icmpstat.icps_badlen, plural(icmpstat.icps_badlen));
X	for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++)
X		if (icmpstat.icps_inhist[i] != 0) {
X			if (first) {
X				printf("\tInput histogram:\n");
X				first = 0;
X			}
X			printf("\t\t%s: %u\n", icmpnames[i],
X				icmpstat.icps_inhist[i]);
X		}
X	printf("\t%u message response%s generated\n",
X		icmpstat.icps_reflect, plural(icmpstat.icps_reflect));
X}
X
X/*
X * Pretty print an Internet address (net address + port).
X * If the nflag was specified, use numbers instead of names.
X */
Xinetprint(in, port, proto)
X	register struct in_addr *in;
X	u_short port; 
X	char *proto;
X{
X	struct servent *sp = 0;
X	char line[80], *cp, *index();
X	int width;
X
X	sprintf(line, "%.*s.", (Aflag && !nflag) ? 12 : 16, inetname(*in));
X	cp = index(line, '\0');
X	if (!nflag && port)
X		sp = getservbyport((int)port, proto);
X	if (sp || port == 0)
X		sprintf(cp, "%.8s", sp ? sp->s_name : "*");
X	else
X		sprintf(cp, "%d", ntohs((u_short)port));
X	width = Aflag ? 18 : 22;
X	printf(" %-*.*s", width, width, line);
X}
X
X/*
X * Construct an Internet address representation.
X * If the nflag has been supplied, give 
X * numeric value, otherwise try for symbolic name.
X */
Xchar *
Xinetname(in)
X	struct in_addr in;
X{
X	register char *cp;
X	static char line[50];
X	struct hostent *hp;
X	struct netent *np;
X	static char domain[MAXHOSTNAMELEN + 1];
X	static int first = 1;
X
X	if (first && !nflag) {
X		first = 0;
X		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
X		    (cp = index(domain, '.')))
X			(void) strcpy(domain, cp + 1);
X		else
X			domain[0] = 0;
X	}
X	cp = 0;
X	if (!nflag && in.s_addr != INADDR_ANY) {
X		int net = inet_netof(in);
X		int lna = inet_lnaof(in);
X
X		if (lna == INADDR_ANY) {
X			np = getnetbyaddr(net, AF_INET);
X			if (np)
X				cp = np->n_name;
X		}
X		if (cp == 0) {
X			hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
X			if (hp) {
X				if ((cp = index(hp->h_name, '.')) &&
X				    !strcmp(cp + 1, domain))
X					*cp = 0;
X				cp = hp->h_name;
X			}
X		}
X	}
X	if (in.s_addr == INADDR_ANY)
X		strcpy(line, "*");
X	else if (cp)
X		strcpy(line, cp);
X	else {
X		in.s_addr = ntohl(in.s_addr);
X#define C(x)	((x) & 0xff)
X		sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
X			C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
X	}
X	return (line);
X}
END-of-netstat/inet.c
exit



More information about the Comp.bugs.4bsd.ucb-fixes mailing list