17dd7cddfSDavid du Colombier #include "u.h"
27dd7cddfSDavid du Colombier #include "../port/lib.h"
37dd7cddfSDavid du Colombier #include "mem.h"
47dd7cddfSDavid du Colombier #include "dat.h"
57dd7cddfSDavid du Colombier #include "fns.h"
67dd7cddfSDavid du Colombier #include "../port/error.h"
77dd7cddfSDavid du Colombier
87dd7cddfSDavid du Colombier #include "ip.h"
97dd7cddfSDavid du Colombier
107dd7cddfSDavid du Colombier enum
117dd7cddfSDavid du Colombier {
127dd7cddfSDavid du Colombier QMAX = 64*1024-1,
137dd7cddfSDavid du Colombier IP_TCPPROTO = 6,
143ff48bf5SDavid du Colombier
153ff48bf5SDavid du Colombier TCP4_IPLEN = 8,
163ff48bf5SDavid du Colombier TCP4_PHDRSIZE = 12,
173ff48bf5SDavid du Colombier TCP4_HDRSIZE = 20,
183ff48bf5SDavid du Colombier TCP4_TCBPHDRSZ = 40,
193ff48bf5SDavid du Colombier TCP4_PKT = TCP4_IPLEN+TCP4_PHDRSIZE,
203ff48bf5SDavid du Colombier
213ff48bf5SDavid du Colombier TCP6_IPLEN = 0,
223ff48bf5SDavid du Colombier TCP6_PHDRSIZE = 40,
233ff48bf5SDavid du Colombier TCP6_HDRSIZE = 20,
243ff48bf5SDavid du Colombier TCP6_TCBPHDRSZ = 60,
253ff48bf5SDavid du Colombier TCP6_PKT = TCP6_IPLEN+TCP6_PHDRSIZE,
263ff48bf5SDavid du Colombier
279a747e4fSDavid du Colombier TcptimerOFF = 0,
289a747e4fSDavid du Colombier TcptimerON = 1,
299a747e4fSDavid du Colombier TcptimerDONE = 2,
307dd7cddfSDavid du Colombier MAX_TIME = (1<<20), /* Forever */
31ef9eff0bSDavid du Colombier TCP_ACK = 50, /* Timed ack sequence in ms */
321b0f96a1SDavid du Colombier MAXBACKMS = 9*60*1000, /* longest backoff time (ms) before hangup */
337dd7cddfSDavid du Colombier
347dd7cddfSDavid du Colombier URG = 0x20, /* Data marked urgent */
357dd7cddfSDavid du Colombier ACK = 0x10, /* Acknowledge is valid */
367dd7cddfSDavid du Colombier PSH = 0x08, /* Whole data pipe is pushed */
377dd7cddfSDavid du Colombier RST = 0x04, /* Reset connection */
387dd7cddfSDavid du Colombier SYN = 0x02, /* Pkt. is synchronise */
397dd7cddfSDavid du Colombier FIN = 0x01, /* Start close down */
407dd7cddfSDavid du Colombier
417dd7cddfSDavid du Colombier EOLOPT = 0,
427dd7cddfSDavid du Colombier NOOPOPT = 1,
437dd7cddfSDavid du Colombier MSSOPT = 2,
447ec5746aSDavid du Colombier MSS_LENGTH = 4, /* Maximum segment size */
453f695129SDavid du Colombier WSOPT = 3,
463f695129SDavid du Colombier WS_LENGTH = 3, /* Bits to scale window size by */
477dd7cddfSDavid du Colombier MSL2 = 10,
487dd7cddfSDavid du Colombier MSPTICK = 50, /* Milliseconds per timer tick */
497ec5746aSDavid du Colombier DEF_MSS = 1460, /* Default maximum segment */
507ec5746aSDavid du Colombier DEF_MSS6 = 1280, /* Default maximum segment (min) for v6 */
5180ee5cbfSDavid du Colombier DEF_RTT = 500, /* Default round trip */
52ed8191acSDavid du Colombier DEF_KAT = 120000, /* Default time (ms) between keep alives */
537dd7cddfSDavid du Colombier TCP_LISTEN = 0, /* Listen connection */
547dd7cddfSDavid du Colombier TCP_CONNECT = 1, /* Outgoing connection */
553ff48bf5SDavid du Colombier SYNACK_RXTIMER = 250, /* ms between SYNACK retransmits */
567dd7cddfSDavid du Colombier
577dd7cddfSDavid du Colombier TCPREXMTTHRESH = 3, /* dupack threshhold for rxt */
587dd7cddfSDavid du Colombier
597dd7cddfSDavid du Colombier FORCE = 1,
607dd7cddfSDavid du Colombier CLONE = 2,
617dd7cddfSDavid du Colombier RETRAN = 4,
627dd7cddfSDavid du Colombier ACTIVE = 8,
637dd7cddfSDavid du Colombier SYNACK = 16,
647dd7cddfSDavid du Colombier
657dd7cddfSDavid du Colombier LOGAGAIN = 3,
667dd7cddfSDavid du Colombier LOGDGAIN = 2,
673ff48bf5SDavid du Colombier
687dd7cddfSDavid du Colombier Closed = 0, /* Connection states */
697dd7cddfSDavid du Colombier Listen,
707dd7cddfSDavid du Colombier Syn_sent,
717dd7cddfSDavid du Colombier Syn_received,
727dd7cddfSDavid du Colombier Established,
737dd7cddfSDavid du Colombier Finwait1,
747dd7cddfSDavid du Colombier Finwait2,
757dd7cddfSDavid du Colombier Close_wait,
767dd7cddfSDavid du Colombier Closing,
777dd7cddfSDavid du Colombier Last_ack,
783ff48bf5SDavid du Colombier Time_wait,
793ff48bf5SDavid du Colombier
803ff48bf5SDavid du Colombier Maxlimbo = 1000, /* maximum procs waiting for response to SYN ACK */
813ff48bf5SDavid du Colombier NLHT = 256, /* hash table size, must be a power of 2 */
823f695129SDavid du Colombier LHTMASK = NLHT-1,
833f695129SDavid du Colombier
841ce6c95fSDavid du Colombier /*
851ce6c95fSDavid du Colombier * window is 64kb * 2ⁿ
861ce6c95fSDavid du Colombier * these factors determine the ultimate bandwidth-delay product.
871ce6c95fSDavid du Colombier * 64kb * 2⁵ = 2mb, or 2× overkill for 100mbps * 70ms.
881ce6c95fSDavid du Colombier */
891ce6c95fSDavid du Colombier Maxqscale = 4, /* maximum queuing scale */
901ce6c95fSDavid du Colombier Defadvscale = 4, /* default advertisement */
917dd7cddfSDavid du Colombier };
927dd7cddfSDavid du Colombier
937dd7cddfSDavid du Colombier /* Must correspond to the enumeration above */
947dd7cddfSDavid du Colombier char *tcpstates[] =
957dd7cddfSDavid du Colombier {
967dd7cddfSDavid du Colombier "Closed", "Listen", "Syn_sent", "Syn_received",
977dd7cddfSDavid du Colombier "Established", "Finwait1", "Finwait2", "Close_wait",
987dd7cddfSDavid du Colombier "Closing", "Last_ack", "Time_wait"
997dd7cddfSDavid du Colombier };
1007dd7cddfSDavid du Colombier
1019a747e4fSDavid du Colombier typedef struct Tcptimer Tcptimer;
1029a747e4fSDavid du Colombier struct Tcptimer
1037dd7cddfSDavid du Colombier {
1049a747e4fSDavid du Colombier Tcptimer *next;
1059a747e4fSDavid du Colombier Tcptimer *prev;
1069a747e4fSDavid du Colombier Tcptimer *readynext;
1077dd7cddfSDavid du Colombier int state;
1087dd7cddfSDavid du Colombier int start;
1097dd7cddfSDavid du Colombier int count;
1107dd7cddfSDavid du Colombier void (*func)(void*);
1117dd7cddfSDavid du Colombier void *arg;
1127dd7cddfSDavid du Colombier };
1137dd7cddfSDavid du Colombier
1143ff48bf5SDavid du Colombier /*
1153ff48bf5SDavid du Colombier * v4 and v6 pseudo headers used for
1163ff48bf5SDavid du Colombier * checksuming tcp
1173ff48bf5SDavid du Colombier */
1183ff48bf5SDavid du Colombier typedef struct Tcp4hdr Tcp4hdr;
1193ff48bf5SDavid du Colombier struct Tcp4hdr
1207dd7cddfSDavid du Colombier {
1217dd7cddfSDavid du Colombier uchar vihl; /* Version and header length */
1227dd7cddfSDavid du Colombier uchar tos; /* Type of service */
1237dd7cddfSDavid du Colombier uchar length[2]; /* packet length */
1247dd7cddfSDavid du Colombier uchar id[2]; /* Identification */
1257dd7cddfSDavid du Colombier uchar frag[2]; /* Fragment information */
1267dd7cddfSDavid du Colombier uchar Unused;
1277dd7cddfSDavid du Colombier uchar proto;
1287dd7cddfSDavid du Colombier uchar tcplen[2];
1297dd7cddfSDavid du Colombier uchar tcpsrc[4];
1307dd7cddfSDavid du Colombier uchar tcpdst[4];
1318e860520SDavid du Colombier /* same as v6 from here on */
1327dd7cddfSDavid du Colombier uchar tcpsport[2];
1337dd7cddfSDavid du Colombier uchar tcpdport[2];
1347dd7cddfSDavid du Colombier uchar tcpseq[4];
1357dd7cddfSDavid du Colombier uchar tcpack[4];
1367dd7cddfSDavid du Colombier uchar tcpflag[2];
1377dd7cddfSDavid du Colombier uchar tcpwin[2];
1387dd7cddfSDavid du Colombier uchar tcpcksum[2];
1397dd7cddfSDavid du Colombier uchar tcpurg[2];
1407dd7cddfSDavid du Colombier /* Options segment */
1413f695129SDavid du Colombier uchar tcpopt[1];
1427dd7cddfSDavid du Colombier };
1437dd7cddfSDavid du Colombier
1443ff48bf5SDavid du Colombier typedef struct Tcp6hdr Tcp6hdr;
1453ff48bf5SDavid du Colombier struct Tcp6hdr
1463ff48bf5SDavid du Colombier {
1473ff48bf5SDavid du Colombier uchar vcf[4];
1483ff48bf5SDavid du Colombier uchar ploadlen[2];
1493ff48bf5SDavid du Colombier uchar proto;
1503ff48bf5SDavid du Colombier uchar ttl;
1513ff48bf5SDavid du Colombier uchar tcpsrc[IPaddrlen];
1523ff48bf5SDavid du Colombier uchar tcpdst[IPaddrlen];
1538e860520SDavid du Colombier /* same as v4 from here on */
1543ff48bf5SDavid du Colombier uchar tcpsport[2];
1553ff48bf5SDavid du Colombier uchar tcpdport[2];
1563ff48bf5SDavid du Colombier uchar tcpseq[4];
1573ff48bf5SDavid du Colombier uchar tcpack[4];
1583ff48bf5SDavid du Colombier uchar tcpflag[2];
1593ff48bf5SDavid du Colombier uchar tcpwin[2];
1603ff48bf5SDavid du Colombier uchar tcpcksum[2];
1613ff48bf5SDavid du Colombier uchar tcpurg[2];
1623ff48bf5SDavid du Colombier /* Options segment */
1633f695129SDavid du Colombier uchar tcpopt[1];
1643ff48bf5SDavid du Colombier };
1653ff48bf5SDavid du Colombier
1663ff48bf5SDavid du Colombier /*
1673ff48bf5SDavid du Colombier * this represents the control info
1683ff48bf5SDavid du Colombier * for a single packet. It is derived from
1693ff48bf5SDavid du Colombier * a packet in ntohtcp{4,6}() and stuck into
1703ff48bf5SDavid du Colombier * a packet in htontcp{4,6}().
1713ff48bf5SDavid du Colombier */
1727dd7cddfSDavid du Colombier typedef struct Tcp Tcp;
1737dd7cddfSDavid du Colombier struct Tcp
1747dd7cddfSDavid du Colombier {
1757dd7cddfSDavid du Colombier ushort source;
1767dd7cddfSDavid du Colombier ushort dest;
1777dd7cddfSDavid du Colombier ulong seq;
1787dd7cddfSDavid du Colombier ulong ack;
1797dd7cddfSDavid du Colombier uchar flags;
1801ce6c95fSDavid du Colombier uchar update;
1811ce6c95fSDavid du Colombier ushort ws; /* window scale option */
1821ce6c95fSDavid du Colombier ulong wnd; /* prescaled window*/
1837dd7cddfSDavid du Colombier ushort urg;
1843f695129SDavid du Colombier ushort mss; /* max segment size option (if not zero) */
1857dd7cddfSDavid du Colombier ushort len; /* size of data */
1867dd7cddfSDavid du Colombier };
1877dd7cddfSDavid du Colombier
1883ff48bf5SDavid du Colombier /*
1893ff48bf5SDavid du Colombier * this header is malloc'd to thread together fragments
1903ff48bf5SDavid du Colombier * waiting to be coalesced
1913ff48bf5SDavid du Colombier */
1927dd7cddfSDavid du Colombier typedef struct Reseq Reseq;
1937dd7cddfSDavid du Colombier struct Reseq
1947dd7cddfSDavid du Colombier {
1957dd7cddfSDavid du Colombier Reseq *next;
1967dd7cddfSDavid du Colombier Tcp seg;
1977dd7cddfSDavid du Colombier Block *bp;
1987dd7cddfSDavid du Colombier ushort length;
1997dd7cddfSDavid du Colombier };
2007dd7cddfSDavid du Colombier
2017dd7cddfSDavid du Colombier /*
2027dd7cddfSDavid du Colombier * the qlock in the Conv locks this structure
2037dd7cddfSDavid du Colombier */
2047dd7cddfSDavid du Colombier typedef struct Tcpctl Tcpctl;
2057dd7cddfSDavid du Colombier struct Tcpctl
2067dd7cddfSDavid du Colombier {
2077dd7cddfSDavid du Colombier uchar state; /* Connection state */
2087dd7cddfSDavid du Colombier uchar type; /* Listening or active connection */
2097dd7cddfSDavid du Colombier uchar code; /* Icmp code */
2107dd7cddfSDavid du Colombier struct {
2117dd7cddfSDavid du Colombier ulong una; /* Unacked data pointer */
2127dd7cddfSDavid du Colombier ulong nxt; /* Next sequence expected */
2137dd7cddfSDavid du Colombier ulong ptr; /* Data pointer */
2143f695129SDavid du Colombier ulong wnd; /* Tcp send window */
2157dd7cddfSDavid du Colombier ulong urg; /* Urgent data pointer */
2167dd7cddfSDavid du Colombier ulong wl2;
2171ce6c95fSDavid du Colombier uint scale; /* how much to right shift window */
2181ce6c95fSDavid du Colombier /* in xmitted packets */
2197dd7cddfSDavid du Colombier /* to implement tahoe and reno TCP */
2207dd7cddfSDavid du Colombier ulong dupacks; /* number of duplicate acks rcvd */
2211ce6c95fSDavid du Colombier ulong partialack;
2227dd7cddfSDavid du Colombier int recovery; /* loss recovery flag */
2231ce6c95fSDavid du Colombier int retransmit; /* retransmit 1 packet @ una flag */
2241ce6c95fSDavid du Colombier int rto;
2257dd7cddfSDavid du Colombier ulong rxt; /* right window marker for recovery */
2261ce6c95fSDavid du Colombier /* "recover" rfc3782 */
2277dd7cddfSDavid du Colombier } snd;
2287dd7cddfSDavid du Colombier struct {
2297dd7cddfSDavid du Colombier ulong nxt; /* Receive pointer to next uchar slot */
2303f695129SDavid du Colombier ulong wnd; /* Receive window incoming */
2311ce6c95fSDavid du Colombier ulong wsnt; /* Last wptr sent. important to */
2321ce6c95fSDavid du Colombier /* track for large bdp */
2331ce6c95fSDavid du Colombier ulong wptr;
2347dd7cddfSDavid du Colombier ulong urg; /* Urgent pointer */
2351ce6c95fSDavid du Colombier ulong ackptr; /* last acked sequence */
2367dd7cddfSDavid du Colombier int blocked;
2371ce6c95fSDavid du Colombier uint scale; /* how much to left shift window in */
2381ce6c95fSDavid du Colombier /* rcv'd packets */
2397dd7cddfSDavid du Colombier } rcv;
2407dd7cddfSDavid du Colombier ulong iss; /* Initial sequence number */
2413f695129SDavid du Colombier ulong cwind; /* Congestion window */
2421ce6c95fSDavid du Colombier ulong abcbytes; /* appropriate byte counting rfc 3465 */
2431ce6c95fSDavid du Colombier uint scale; /* desired snd.scale */
2441ce6c95fSDavid du Colombier ulong ssthresh; /* Slow start threshold */
2457dd7cddfSDavid du Colombier int resent; /* Bytes just resent */
2467dd7cddfSDavid du Colombier int irs; /* Initial received squence */
2477ec5746aSDavid du Colombier ushort mss; /* Maximum segment size */
2487dd7cddfSDavid du Colombier int rerecv; /* Overlap of data rerecevived */
2491ce6c95fSDavid du Colombier ulong window; /* Our receive window (queue) */
2501ce6c95fSDavid du Colombier uint qscale; /* Log2 of our receive window (queue) */
2517dd7cddfSDavid du Colombier uchar backoff; /* Exponential backoff counter */
2527dd7cddfSDavid du Colombier int backedoff; /* ms we've backed off for rexmits */
2537dd7cddfSDavid du Colombier uchar flags; /* State flags */
2547dd7cddfSDavid du Colombier Reseq *reseq; /* Resequencing queue */
2551ce6c95fSDavid du Colombier int nreseq;
2561ce6c95fSDavid du Colombier int reseqlen;
2579a747e4fSDavid du Colombier Tcptimer timer; /* Activity timer */
2589a747e4fSDavid du Colombier Tcptimer acktimer; /* Acknowledge timer */
2599a747e4fSDavid du Colombier Tcptimer rtt_timer; /* Round trip timer */
2609a747e4fSDavid du Colombier Tcptimer katimer; /* keep alive timer */
2617dd7cddfSDavid du Colombier ulong rttseq; /* Round trip sequence */
2621ce6c95fSDavid du Colombier int srtt; /* Smoothed round trip */
2637dd7cddfSDavid du Colombier int mdev; /* Mean deviation of round trip */
2647dd7cddfSDavid du Colombier int kacounter; /* count down for keep alive */
2657dd7cddfSDavid du Colombier uint sndsyntime; /* time syn sent */
2667dd7cddfSDavid du Colombier ulong time; /* time Finwait2 or Syn_received was sent */
2671ce6c95fSDavid du Colombier ulong timeuna; /* snd.una when time was set */
26859cc4ca5SDavid du Colombier int nochecksum; /* non-zero means don't send checksums */
2693ff48bf5SDavid du Colombier int flgcnt; /* number of flags in the sequence (FIN,SEQ) */
2707dd7cddfSDavid du Colombier
2713ff48bf5SDavid du Colombier union {
2723ff48bf5SDavid du Colombier Tcp4hdr tcp4hdr;
2733ff48bf5SDavid du Colombier Tcp6hdr tcp6hdr;
2743ff48bf5SDavid du Colombier } protohdr; /* prototype header */
2753ff48bf5SDavid du Colombier };
2763ff48bf5SDavid du Colombier
2773ff48bf5SDavid du Colombier /*
2783ff48bf5SDavid du Colombier * New calls are put in limbo rather than having a conversation structure
2793ff48bf5SDavid du Colombier * allocated. Thus, a SYN attack results in lots of limbo'd calls but not
2803ff48bf5SDavid du Colombier * any real Conv structures mucking things up. Calls in limbo rexmit their
2813ff48bf5SDavid du Colombier * SYN ACK every SYNACK_RXTIMER ms up to 4 times, i.e., they disappear after 1 second.
2823ff48bf5SDavid du Colombier *
2833ff48bf5SDavid du Colombier * In particular they aren't on a listener's queue so that they don't figure
2843ff48bf5SDavid du Colombier * in the input queue limit.
2853ff48bf5SDavid du Colombier *
2863ff48bf5SDavid du Colombier * If 1/2 of a T3 was attacking SYN packets, we'ld have a permanent queue
2873ff48bf5SDavid du Colombier * of 70000 limbo'd calls. Not great for a linear list but doable. Therefore
2883ff48bf5SDavid du Colombier * there is no hashing of this list.
2893ff48bf5SDavid du Colombier */
2903ff48bf5SDavid du Colombier typedef struct Limbo Limbo;
2913ff48bf5SDavid du Colombier struct Limbo
2923ff48bf5SDavid du Colombier {
2933ff48bf5SDavid du Colombier Limbo *next;
2943ff48bf5SDavid du Colombier
2953ff48bf5SDavid du Colombier uchar laddr[IPaddrlen];
2963ff48bf5SDavid du Colombier uchar raddr[IPaddrlen];
2973ff48bf5SDavid du Colombier ushort lport;
2983ff48bf5SDavid du Colombier ushort rport;
2993ff48bf5SDavid du Colombier ulong irs; /* initial received sequence */
3003ff48bf5SDavid du Colombier ulong iss; /* initial sent sequence */
3013ff48bf5SDavid du Colombier ushort mss; /* mss from the other end */
3023f695129SDavid du Colombier ushort rcvscale; /* how much to scale rcvd windows */
3033f695129SDavid du Colombier ushort sndscale; /* how much to scale sent windows */
3043ff48bf5SDavid du Colombier ulong lastsend; /* last time we sent a synack */
3053ff48bf5SDavid du Colombier uchar version; /* v4 or v6 */
3063ff48bf5SDavid du Colombier uchar rexmits; /* number of retransmissions */
3077dd7cddfSDavid du Colombier };
3087dd7cddfSDavid du Colombier
3097dd7cddfSDavid du Colombier int tcp_irtt = DEF_RTT; /* Initial guess at round trip time */
3107dd7cddfSDavid du Colombier
31159cc4ca5SDavid du Colombier enum {
31259cc4ca5SDavid du Colombier /* MIB stats */
31359cc4ca5SDavid du Colombier MaxConn,
3147ec5746aSDavid du Colombier Mss,
31559cc4ca5SDavid du Colombier ActiveOpens,
31659cc4ca5SDavid du Colombier PassiveOpens,
31759cc4ca5SDavid du Colombier EstabResets,
31859cc4ca5SDavid du Colombier CurrEstab,
31959cc4ca5SDavid du Colombier InSegs,
32059cc4ca5SDavid du Colombier OutSegs,
32159cc4ca5SDavid du Colombier RetransSegs,
3221ce6c95fSDavid du Colombier RetransSegsSent,
32359cc4ca5SDavid du Colombier RetransTimeouts,
32459cc4ca5SDavid du Colombier InErrs,
32559cc4ca5SDavid du Colombier OutRsts,
32659cc4ca5SDavid du Colombier
32759cc4ca5SDavid du Colombier /* non-MIB stats */
32859cc4ca5SDavid du Colombier CsumErrs,
32959cc4ca5SDavid du Colombier HlenErrs,
33059cc4ca5SDavid du Colombier LenErrs,
3311ce6c95fSDavid du Colombier Resequenced,
33259cc4ca5SDavid du Colombier OutOfOrder,
3331ce6c95fSDavid du Colombier ReseqBytelim,
3341ce6c95fSDavid du Colombier ReseqPktlim,
3351ce6c95fSDavid du Colombier Delayack,
3361ce6c95fSDavid du Colombier Wopenack,
3371ce6c95fSDavid du Colombier
3381ce6c95fSDavid du Colombier Recovery,
3391ce6c95fSDavid du Colombier RecoveryDone,
3401ce6c95fSDavid du Colombier RecoveryRTO,
3411ce6c95fSDavid du Colombier RecoveryNoSeq,
3421ce6c95fSDavid du Colombier RecoveryCwind,
3431ce6c95fSDavid du Colombier RecoveryPA,
34459cc4ca5SDavid du Colombier
34559cc4ca5SDavid du Colombier Nstats
34659cc4ca5SDavid du Colombier };
34759cc4ca5SDavid du Colombier
3481ce6c95fSDavid du Colombier static char *statnames[Nstats] =
3497dd7cddfSDavid du Colombier {
35059cc4ca5SDavid du Colombier [MaxConn] "MaxConn",
3517ec5746aSDavid du Colombier [Mss] "MaxSegment",
35259cc4ca5SDavid du Colombier [ActiveOpens] "ActiveOpens",
35359cc4ca5SDavid du Colombier [PassiveOpens] "PassiveOpens",
35459cc4ca5SDavid du Colombier [EstabResets] "EstabResets",
35559cc4ca5SDavid du Colombier [CurrEstab] "CurrEstab",
35659cc4ca5SDavid du Colombier [InSegs] "InSegs",
35759cc4ca5SDavid du Colombier [OutSegs] "OutSegs",
35859cc4ca5SDavid du Colombier [RetransSegs] "RetransSegs",
3591ce6c95fSDavid du Colombier [RetransSegsSent] "RetransSegsSent",
36059cc4ca5SDavid du Colombier [RetransTimeouts] "RetransTimeouts",
36159cc4ca5SDavid du Colombier [InErrs] "InErrs",
36259cc4ca5SDavid du Colombier [OutRsts] "OutRsts",
36359cc4ca5SDavid du Colombier [CsumErrs] "CsumErrs",
36459cc4ca5SDavid du Colombier [HlenErrs] "HlenErrs",
36559cc4ca5SDavid du Colombier [LenErrs] "LenErrs",
36659cc4ca5SDavid du Colombier [OutOfOrder] "OutOfOrder",
3671ce6c95fSDavid du Colombier [Resequenced] "Resequenced",
3681ce6c95fSDavid du Colombier [ReseqBytelim] "ReseqBytelim",
3691ce6c95fSDavid du Colombier [ReseqPktlim] "ReseqPktlim",
3701ce6c95fSDavid du Colombier [Delayack] "Delayack",
3711ce6c95fSDavid du Colombier [Wopenack] "Wopenack",
3721ce6c95fSDavid du Colombier
3731ce6c95fSDavid du Colombier [Recovery] "Recovery",
3741ce6c95fSDavid du Colombier [RecoveryDone] "RecoveryDone",
3751ce6c95fSDavid du Colombier [RecoveryRTO] "RecoveryRTO",
3761ce6c95fSDavid du Colombier
3771ce6c95fSDavid du Colombier [RecoveryNoSeq] "RecoveryNoSeq",
3781ce6c95fSDavid du Colombier [RecoveryCwind] "RecoveryCwind",
3791ce6c95fSDavid du Colombier [RecoveryPA] "RecoveryPA",
3807dd7cddfSDavid du Colombier };
3817dd7cddfSDavid du Colombier
3827dd7cddfSDavid du Colombier typedef struct Tcppriv Tcppriv;
3837dd7cddfSDavid du Colombier struct Tcppriv
3847dd7cddfSDavid du Colombier {
3853ff48bf5SDavid du Colombier /* List of active timers */
3863ff48bf5SDavid du Colombier QLock tl;
3873ff48bf5SDavid du Colombier Tcptimer *timers;
3883ff48bf5SDavid du Colombier
38980ee5cbfSDavid du Colombier /* hash table for matching conversations */
39080ee5cbfSDavid du Colombier Ipht ht;
39180ee5cbfSDavid du Colombier
3923ff48bf5SDavid du Colombier /* calls in limbo waiting for an ACK to our SYN ACK */
3933ff48bf5SDavid du Colombier int nlimbo;
3943ff48bf5SDavid du Colombier Limbo *lht[NLHT];
3957dd7cddfSDavid du Colombier
3967dd7cddfSDavid du Colombier /* for keeping track of tcpackproc */
3977dd7cddfSDavid du Colombier QLock apl;
3983ff48bf5SDavid du Colombier int ackprocstarted;
3993ff48bf5SDavid du Colombier
4005e27dea9SDavid du Colombier uvlong stats[Nstats];
4017dd7cddfSDavid du Colombier };
4027dd7cddfSDavid du Colombier
4033ff48bf5SDavid du Colombier /*
4043ff48bf5SDavid du Colombier * Setting tcpporthogdefense to non-zero enables Dong Lin's
4053ff48bf5SDavid du Colombier * solution to hijacked systems staking out port's as a form
4063ff48bf5SDavid du Colombier * of DoS attack.
4073ff48bf5SDavid du Colombier *
4083ff48bf5SDavid du Colombier * To avoid stateless Conv hogs, we pick a sequence number at random. If
40909911ebdSDavid du Colombier * that number gets acked by the other end, we shut down the connection.
41009911ebdSDavid du Colombier * Look for tcpporthogdefense in the code.
4113ff48bf5SDavid du Colombier */
4123ff48bf5SDavid du Colombier int tcpporthogdefense = 0;
4133ff48bf5SDavid du Colombier
4141ce6c95fSDavid du Colombier static int addreseq(Fs*, Tcpctl*, Tcppriv*, Tcp*, Block*, ushort);
4151ce6c95fSDavid du Colombier static int dumpreseq(Tcpctl*);
4161ce6c95fSDavid du Colombier static void getreseq(Tcpctl*, Tcp*, Block**, ushort*);
4173ff48bf5SDavid du Colombier static void limbo(Conv*, uchar*, uchar*, Tcp*, int);
4181ce6c95fSDavid du Colombier static void limborexmit(Proto*);
4191ce6c95fSDavid du Colombier static void localclose(Conv*, char*);
4201ce6c95fSDavid du Colombier static void procsyn(Conv*, Tcp*);
4211ce6c95fSDavid du Colombier static void tcpacktimer(void*);
4221ce6c95fSDavid du Colombier static void tcpiput(Proto*, Ipifc*, Block*);
4231ce6c95fSDavid du Colombier static void tcpkeepalive(void*);
4241ce6c95fSDavid du Colombier static void tcpoutput(Conv*);
4251ce6c95fSDavid du Colombier static void tcprcvwin(Conv*);
4261ce6c95fSDavid du Colombier static void tcprxmit(Conv*);
4271ce6c95fSDavid du Colombier static void tcpsetkacounter(Tcpctl*);
4281ce6c95fSDavid du Colombier static void tcpsetscale(Conv*, Tcpctl*, ushort, ushort);
4291ce6c95fSDavid du Colombier static void tcpsettimer(Tcpctl*);
4301ce6c95fSDavid du Colombier static void tcpsndsyn(Conv*, Tcpctl*);
4311ce6c95fSDavid du Colombier static void tcpstart(Conv*, int);
4321ce6c95fSDavid du Colombier static void tcpsynackrtt(Conv*);
4331ce6c95fSDavid du Colombier static void tcptimeout(void*);
4341ce6c95fSDavid du Colombier static int tcptrim(Tcpctl*, Tcp*, Block**, ushort*);
4357dd7cddfSDavid du Colombier
4361ce6c95fSDavid du Colombier static void
tcpsetstate(Conv * s,uchar newstate)4377dd7cddfSDavid du Colombier tcpsetstate(Conv *s, uchar newstate)
4387dd7cddfSDavid du Colombier {
4397dd7cddfSDavid du Colombier Tcpctl *tcb;
4407dd7cddfSDavid du Colombier uchar oldstate;
4417dd7cddfSDavid du Colombier Tcppriv *tpriv;
4427dd7cddfSDavid du Colombier
4437dd7cddfSDavid du Colombier tpriv = s->p->priv;
4447dd7cddfSDavid du Colombier
4457dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
4467dd7cddfSDavid du Colombier
4477dd7cddfSDavid du Colombier oldstate = tcb->state;
4487dd7cddfSDavid du Colombier if(oldstate == newstate)
4497dd7cddfSDavid du Colombier return;
4507dd7cddfSDavid du Colombier
4517dd7cddfSDavid du Colombier if(oldstate == Established)
45259cc4ca5SDavid du Colombier tpriv->stats[CurrEstab]--;
4537dd7cddfSDavid du Colombier if(newstate == Established)
45459cc4ca5SDavid du Colombier tpriv->stats[CurrEstab]++;
4557dd7cddfSDavid du Colombier
4567dd7cddfSDavid du Colombier switch(newstate) {
4577dd7cddfSDavid du Colombier case Closed:
4587dd7cddfSDavid du Colombier qclose(s->rq);
4597dd7cddfSDavid du Colombier qclose(s->wq);
4607dd7cddfSDavid du Colombier qclose(s->eq);
4617dd7cddfSDavid du Colombier break;
4627dd7cddfSDavid du Colombier
4637dd7cddfSDavid du Colombier case Close_wait: /* Remote closes */
4647dd7cddfSDavid du Colombier qhangup(s->rq, nil);
4657dd7cddfSDavid du Colombier break;
4667dd7cddfSDavid du Colombier }
4677dd7cddfSDavid du Colombier
4687dd7cddfSDavid du Colombier tcb->state = newstate;
4697dd7cddfSDavid du Colombier
4707dd7cddfSDavid du Colombier if(oldstate == Syn_sent && newstate != Closed)
4717dd7cddfSDavid du Colombier Fsconnected(s, nil);
4727dd7cddfSDavid du Colombier }
4737dd7cddfSDavid du Colombier
4747dd7cddfSDavid du Colombier static char*
tcpconnect(Conv * c,char ** argv,int argc)4757dd7cddfSDavid du Colombier tcpconnect(Conv *c, char **argv, int argc)
4767dd7cddfSDavid du Colombier {
4777dd7cddfSDavid du Colombier char *e;
47884363d68SDavid du Colombier Tcpctl *tcb;
47984363d68SDavid du Colombier
48084363d68SDavid du Colombier tcb = (Tcpctl*)(c->ptcl);
48184363d68SDavid du Colombier if(tcb->state != Closed)
48284363d68SDavid du Colombier return Econinuse;
4837dd7cddfSDavid du Colombier
4847dd7cddfSDavid du Colombier e = Fsstdconnect(c, argv, argc);
4857dd7cddfSDavid du Colombier if(e != nil)
4867dd7cddfSDavid du Colombier return e;
4873f695129SDavid du Colombier tcpstart(c, TCP_CONNECT);
4887dd7cddfSDavid du Colombier
4897dd7cddfSDavid du Colombier return nil;
4907dd7cddfSDavid du Colombier }
4917dd7cddfSDavid du Colombier
4927dd7cddfSDavid du Colombier static int
tcpstate(Conv * c,char * state,int n)4937dd7cddfSDavid du Colombier tcpstate(Conv *c, char *state, int n)
4947dd7cddfSDavid du Colombier {
4957dd7cddfSDavid du Colombier Tcpctl *s;
4967dd7cddfSDavid du Colombier
4977dd7cddfSDavid du Colombier s = (Tcpctl*)(c->ptcl);
4987dd7cddfSDavid du Colombier
4997dd7cddfSDavid du Colombier return snprint(state, n,
5001ce6c95fSDavid du Colombier "%s qin %d qout %d rq %d.%d srtt %d mdev %d sst %lud cwin %lud "
5011ce6c95fSDavid du Colombier "swin %lud>>%d rwin %lud>>%d qscale %d timer.start %d "
5021ce6c95fSDavid du Colombier "timer.count %d rerecv %d katimer.start %d katimer.count %d\n",
50325332fe6SDavid du Colombier tcpstates[s->state],
50425332fe6SDavid du Colombier c->rq ? qlen(c->rq) : 0,
50525332fe6SDavid du Colombier c->wq ? qlen(c->wq) : 0,
5061ce6c95fSDavid du Colombier s->nreseq, s->reseqlen,
5071ce6c95fSDavid du Colombier s->srtt, s->mdev, s->ssthresh,
5083f695129SDavid du Colombier s->cwind, s->snd.wnd, s->rcv.scale, s->rcv.wnd, s->snd.scale,
5091ce6c95fSDavid du Colombier s->qscale,
51040ef9009SDavid du Colombier s->timer.start, s->timer.count, s->rerecv,
51140ef9009SDavid du Colombier s->katimer.start, s->katimer.count);
5127dd7cddfSDavid du Colombier }
5137dd7cddfSDavid du Colombier
5147dd7cddfSDavid du Colombier static int
tcpinuse(Conv * c)5157dd7cddfSDavid du Colombier tcpinuse(Conv *c)
5167dd7cddfSDavid du Colombier {
5177dd7cddfSDavid du Colombier Tcpctl *s;
5187dd7cddfSDavid du Colombier
5197dd7cddfSDavid du Colombier s = (Tcpctl*)(c->ptcl);
5207dd7cddfSDavid du Colombier return s->state != Closed;
5217dd7cddfSDavid du Colombier }
5227dd7cddfSDavid du Colombier
5237dd7cddfSDavid du Colombier static char*
tcpannounce(Conv * c,char ** argv,int argc)5247dd7cddfSDavid du Colombier tcpannounce(Conv *c, char **argv, int argc)
5257dd7cddfSDavid du Colombier {
5267dd7cddfSDavid du Colombier char *e;
52784363d68SDavid du Colombier Tcpctl *tcb;
52884363d68SDavid du Colombier
52984363d68SDavid du Colombier tcb = (Tcpctl*)(c->ptcl);
53084363d68SDavid du Colombier if(tcb->state != Closed)
53184363d68SDavid du Colombier return Econinuse;
5327dd7cddfSDavid du Colombier
5337dd7cddfSDavid du Colombier e = Fsstdannounce(c, argv, argc);
5347dd7cddfSDavid du Colombier if(e != nil)
5357dd7cddfSDavid du Colombier return e;
5363f695129SDavid du Colombier tcpstart(c, TCP_LISTEN);
5377dd7cddfSDavid du Colombier Fsconnected(c, nil);
5387dd7cddfSDavid du Colombier
5397dd7cddfSDavid du Colombier return nil;
5407dd7cddfSDavid du Colombier }
5417dd7cddfSDavid du Colombier
542ddc91d4cSDavid du Colombier static void
tcpclosestate(Conv * c,Tcpctl * tcb,int state)543ddc91d4cSDavid du Colombier tcpclosestate(Conv *c, Tcpctl *tcb, int state)
544ddc91d4cSDavid du Colombier {
545ddc91d4cSDavid du Colombier tcb->flgcnt++;
546ddc91d4cSDavid du Colombier tcb->snd.nxt++;
547ddc91d4cSDavid du Colombier tcpsetstate(c, state);
548ddc91d4cSDavid du Colombier tcpoutput(c);
549ddc91d4cSDavid du Colombier }
550ddc91d4cSDavid du Colombier
551ddc91d4cSDavid du Colombier /* close the output half of a tcp connection */
552ddc91d4cSDavid du Colombier static char *
tcpxmitclose(Conv * c)553ddc91d4cSDavid du Colombier tcpxmitclose(Conv *c)
554ddc91d4cSDavid du Colombier {
555ddc91d4cSDavid du Colombier Tcpctl *tcb;
556ddc91d4cSDavid du Colombier
557ddc91d4cSDavid du Colombier qhangup(c->wq, nil);
558ddc91d4cSDavid du Colombier
559ddc91d4cSDavid du Colombier tcb = (Tcpctl*)c->ptcl;
560ddc91d4cSDavid du Colombier switch(tcb->state) {
561ddc91d4cSDavid du Colombier case Listen:
562ddc91d4cSDavid du Colombier /*
563ddc91d4cSDavid du Colombier * reset any incoming calls to this listener
564ddc91d4cSDavid du Colombier */
565ddc91d4cSDavid du Colombier Fsconnected(c, "Hangup");
566ddc91d4cSDavid du Colombier /* fall through */
567ddc91d4cSDavid du Colombier case Closed:
568ddc91d4cSDavid du Colombier case Syn_sent:
569ddc91d4cSDavid du Colombier localclose(c, nil);
570ddc91d4cSDavid du Colombier break;
571ddc91d4cSDavid du Colombier case Syn_received:
572ddc91d4cSDavid du Colombier case Established:
573ddc91d4cSDavid du Colombier case Close_wait:
574ddc91d4cSDavid du Colombier tcpclosestate(c, tcb, tcb->state);
575ddc91d4cSDavid du Colombier break;
576ddc91d4cSDavid du Colombier }
577ddc91d4cSDavid du Colombier return nil;
578ddc91d4cSDavid du Colombier }
579ddc91d4cSDavid du Colombier
58080ee5cbfSDavid du Colombier /*
58180ee5cbfSDavid du Colombier * tcpclose is always called with the q locked
58280ee5cbfSDavid du Colombier */
5837dd7cddfSDavid du Colombier static void
tcpclose(Conv * c)5847dd7cddfSDavid du Colombier tcpclose(Conv *c)
5857dd7cddfSDavid du Colombier {
5867dd7cddfSDavid du Colombier Tcpctl *tcb;
5877dd7cddfSDavid du Colombier
5887dd7cddfSDavid du Colombier tcb = (Tcpctl*)c->ptcl;
5897dd7cddfSDavid du Colombier
5907dd7cddfSDavid du Colombier qhangup(c->rq, nil);
5917dd7cddfSDavid du Colombier qhangup(c->wq, nil);
5927dd7cddfSDavid du Colombier qhangup(c->eq, nil);
59380ee5cbfSDavid du Colombier qflush(c->rq);
5947dd7cddfSDavid du Colombier
5957dd7cddfSDavid du Colombier switch(tcb->state) {
5967dd7cddfSDavid du Colombier case Listen:
5977dd7cddfSDavid du Colombier /*
5987dd7cddfSDavid du Colombier * reset any incoming calls to this listener
5997dd7cddfSDavid du Colombier */
6007dd7cddfSDavid du Colombier Fsconnected(c, "Hangup");
601ddc91d4cSDavid du Colombier /* fall through */
6027dd7cddfSDavid du Colombier case Closed:
6037dd7cddfSDavid du Colombier case Syn_sent:
6047dd7cddfSDavid du Colombier localclose(c, nil);
6057dd7cddfSDavid du Colombier break;
6067dd7cddfSDavid du Colombier case Syn_received:
6077dd7cddfSDavid du Colombier case Established:
608ddc91d4cSDavid du Colombier tcpclosestate(c, tcb, Finwait1);
6097dd7cddfSDavid du Colombier break;
6107dd7cddfSDavid du Colombier case Close_wait:
611ddc91d4cSDavid du Colombier tcpclosestate(c, tcb, Last_ack);
6127dd7cddfSDavid du Colombier break;
6137dd7cddfSDavid du Colombier }
6147dd7cddfSDavid du Colombier }
6157dd7cddfSDavid du Colombier
6161ce6c95fSDavid du Colombier static void
tcpkick(void * x)6173ff48bf5SDavid du Colombier tcpkick(void *x)
6187dd7cddfSDavid du Colombier {
6193ff48bf5SDavid du Colombier Conv *s = x;
6207dd7cddfSDavid du Colombier Tcpctl *tcb;
6217dd7cddfSDavid du Colombier
6227dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
6237dd7cddfSDavid du Colombier
62480ee5cbfSDavid du Colombier if(waserror()){
62580ee5cbfSDavid du Colombier qunlock(s);
62680ee5cbfSDavid du Colombier nexterror();
62780ee5cbfSDavid du Colombier }
62880ee5cbfSDavid du Colombier qlock(s);
6297dd7cddfSDavid du Colombier
6307dd7cddfSDavid du Colombier switch(tcb->state) {
6317dd7cddfSDavid du Colombier case Syn_sent:
6327dd7cddfSDavid du Colombier case Syn_received:
6337dd7cddfSDavid du Colombier case Established:
6347dd7cddfSDavid du Colombier case Close_wait:
6357dd7cddfSDavid du Colombier /*
6367dd7cddfSDavid du Colombier * Push data
6377dd7cddfSDavid du Colombier */
6387dd7cddfSDavid du Colombier tcpoutput(s);
6397dd7cddfSDavid du Colombier break;
6407dd7cddfSDavid du Colombier default:
6417dd7cddfSDavid du Colombier localclose(s, "Hangup");
64280ee5cbfSDavid du Colombier break;
6437dd7cddfSDavid du Colombier }
64480ee5cbfSDavid du Colombier
64580ee5cbfSDavid du Colombier qunlock(s);
64680ee5cbfSDavid du Colombier poperror();
6477dd7cddfSDavid du Colombier }
6487dd7cddfSDavid du Colombier
6491ce6c95fSDavid du Colombier static int seq_lt(ulong, ulong);
6501ce6c95fSDavid du Colombier
6511ce6c95fSDavid du Colombier static void
tcprcvwin(Conv * s)6527dd7cddfSDavid du Colombier tcprcvwin(Conv *s) /* Call with tcb locked */
6537dd7cddfSDavid du Colombier {
6547dd7cddfSDavid du Colombier int w;
6557dd7cddfSDavid du Colombier Tcpctl *tcb;
6567dd7cddfSDavid du Colombier
6577dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
6583f695129SDavid du Colombier w = tcb->window - qlen(s->rq);
6597dd7cddfSDavid du Colombier if(w < 0)
6607dd7cddfSDavid du Colombier w = 0;
6611ce6c95fSDavid du Colombier /* RFC 1122 § 4.2.2.17 do not move right edge of window left */
6621ce6c95fSDavid du Colombier if(seq_lt(tcb->rcv.nxt + w, tcb->rcv.wptr))
6631ce6c95fSDavid du Colombier w = tcb->rcv.wptr - tcb->rcv.nxt;
6641ce6c95fSDavid du Colombier if(w != tcb->rcv.wnd)
6651ce6c95fSDavid du Colombier if(w>>tcb->rcv.scale == 0 || tcb->window > 4*tcb->mss && w < tcb->mss/4){
6667dd7cddfSDavid du Colombier tcb->rcv.blocked = 1;
6671ce6c95fSDavid du Colombier netlog(s->p->f, Logtcp, "tcprcvwin: window %lud qlen %d ws %ud lport %d\n",
6681ce6c95fSDavid du Colombier tcb->window, qlen(s->rq), tcb->rcv.scale, s->lport);
6691ce6c95fSDavid du Colombier }
6701ce6c95fSDavid du Colombier tcb->rcv.wnd = w;
6711ce6c95fSDavid du Colombier tcb->rcv.wptr = tcb->rcv.nxt + w;
6727dd7cddfSDavid du Colombier }
6737dd7cddfSDavid du Colombier
6741ce6c95fSDavid du Colombier static void
tcpacktimer(void * v)6757dd7cddfSDavid du Colombier tcpacktimer(void *v)
6767dd7cddfSDavid du Colombier {
6777dd7cddfSDavid du Colombier Tcpctl *tcb;
6787dd7cddfSDavid du Colombier Conv *s;
6797dd7cddfSDavid du Colombier
6807dd7cddfSDavid du Colombier s = v;
6817dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
6827dd7cddfSDavid du Colombier
6837dd7cddfSDavid du Colombier if(waserror()){
6847dd7cddfSDavid du Colombier qunlock(s);
6857dd7cddfSDavid du Colombier nexterror();
6867dd7cddfSDavid du Colombier }
6877dd7cddfSDavid du Colombier qlock(s);
6887dd7cddfSDavid du Colombier if(tcb->state != Closed){
6897dd7cddfSDavid du Colombier tcb->flags |= FORCE;
6907dd7cddfSDavid du Colombier tcpoutput(s);
6917dd7cddfSDavid du Colombier }
6927dd7cddfSDavid du Colombier qunlock(s);
6937dd7cddfSDavid du Colombier poperror();
6947dd7cddfSDavid du Colombier }
6957dd7cddfSDavid du Colombier
6967dd7cddfSDavid du Colombier static void
tcpcongestion(Tcpctl * tcb)6971ce6c95fSDavid du Colombier tcpcongestion(Tcpctl *tcb)
6981ce6c95fSDavid du Colombier {
6991ce6c95fSDavid du Colombier ulong inflight;
7001ce6c95fSDavid du Colombier
7011ce6c95fSDavid du Colombier inflight = tcb->snd.nxt - tcb->snd.una;
7021ce6c95fSDavid du Colombier if(inflight > tcb->cwind)
7031ce6c95fSDavid du Colombier inflight = tcb->cwind;
7041ce6c95fSDavid du Colombier tcb->ssthresh = inflight / 2;
7051ce6c95fSDavid du Colombier if(tcb->ssthresh < 2*tcb->mss)
7061ce6c95fSDavid du Colombier tcb->ssthresh = 2*tcb->mss;
7071ce6c95fSDavid du Colombier }
7081ce6c95fSDavid du Colombier
7091ce6c95fSDavid du Colombier enum {
7101ce6c95fSDavid du Colombier L = 2, /* aggressive slow start; legal values ∈ (1.0, 2.0) */
7111ce6c95fSDavid du Colombier };
7121ce6c95fSDavid du Colombier
7131ce6c95fSDavid du Colombier static void
tcpabcincr(Tcpctl * tcb,uint acked)7141ce6c95fSDavid du Colombier tcpabcincr(Tcpctl *tcb, uint acked)
7151ce6c95fSDavid du Colombier {
7161ce6c95fSDavid du Colombier uint limit;
7171ce6c95fSDavid du Colombier
7181ce6c95fSDavid du Colombier tcb->abcbytes += acked;
7191ce6c95fSDavid du Colombier if(tcb->cwind < tcb->ssthresh){
7201ce6c95fSDavid du Colombier /* slow start */
7211ce6c95fSDavid du Colombier if(tcb->snd.rto)
7221ce6c95fSDavid du Colombier limit = tcb->mss;
7231ce6c95fSDavid du Colombier else
7241ce6c95fSDavid du Colombier limit = L*tcb->mss;
7251ce6c95fSDavid du Colombier tcb->cwind += MIN(tcb->abcbytes, limit);
7261ce6c95fSDavid du Colombier tcb->abcbytes = 0;
7271ce6c95fSDavid du Colombier } else {
7281ce6c95fSDavid du Colombier tcb->snd.rto = 0;
7291ce6c95fSDavid du Colombier /* avoidance */
7301ce6c95fSDavid du Colombier if(tcb->abcbytes >= tcb->cwind){
7311ce6c95fSDavid du Colombier tcb->abcbytes -= tcb->cwind;
7321ce6c95fSDavid du Colombier tcb->cwind += tcb->mss;
7331ce6c95fSDavid du Colombier }
7341ce6c95fSDavid du Colombier }
7351ce6c95fSDavid du Colombier }
7361ce6c95fSDavid du Colombier
7371ce6c95fSDavid du Colombier static void
tcpcreate(Conv * c)7387dd7cddfSDavid du Colombier tcpcreate(Conv *c)
7397dd7cddfSDavid du Colombier {
7403ff48bf5SDavid du Colombier c->rq = qopen(QMAX, Qcoalesce, tcpacktimer, c);
7411ce6c95fSDavid du Colombier c->wq = qopen(QMAX, Qkick, tcpkick, c);
7427dd7cddfSDavid du Colombier }
7437dd7cddfSDavid du Colombier
7447dd7cddfSDavid du Colombier static void
timerstate(Tcppriv * priv,Tcptimer * t,int newstate)7459a747e4fSDavid du Colombier timerstate(Tcppriv *priv, Tcptimer *t, int newstate)
7467dd7cddfSDavid du Colombier {
7479a747e4fSDavid du Colombier if(newstate != TcptimerON){
7489a747e4fSDavid du Colombier if(t->state == TcptimerON){
74941dd6b47SDavid du Colombier /* unchain */
7507dd7cddfSDavid du Colombier if(priv->timers == t){
7517dd7cddfSDavid du Colombier priv->timers = t->next;
7527dd7cddfSDavid du Colombier if(t->prev != nil)
7537dd7cddfSDavid du Colombier panic("timerstate1");
7547dd7cddfSDavid du Colombier }
7557dd7cddfSDavid du Colombier if(t->next)
7567dd7cddfSDavid du Colombier t->next->prev = t->prev;
7577dd7cddfSDavid du Colombier if(t->prev)
7587dd7cddfSDavid du Colombier t->prev->next = t->next;
7597dd7cddfSDavid du Colombier t->next = t->prev = nil;
7607dd7cddfSDavid du Colombier }
7617dd7cddfSDavid du Colombier } else {
7629a747e4fSDavid du Colombier if(t->state != TcptimerON){
76341dd6b47SDavid du Colombier /* chain */
7647dd7cddfSDavid du Colombier if(t->prev != nil || t->next != nil)
7657dd7cddfSDavid du Colombier panic("timerstate2");
7667dd7cddfSDavid du Colombier t->prev = nil;
7677dd7cddfSDavid du Colombier t->next = priv->timers;
7687dd7cddfSDavid du Colombier if(t->next)
7697dd7cddfSDavid du Colombier t->next->prev = t;
7707dd7cddfSDavid du Colombier priv->timers = t;
7717dd7cddfSDavid du Colombier }
7727dd7cddfSDavid du Colombier }
7737dd7cddfSDavid du Colombier t->state = newstate;
7747dd7cddfSDavid du Colombier }
7757dd7cddfSDavid du Colombier
7761ce6c95fSDavid du Colombier static void
tcpackproc(void * a)7777dd7cddfSDavid du Colombier tcpackproc(void *a)
7787dd7cddfSDavid du Colombier {
7799a747e4fSDavid du Colombier Tcptimer *t, *tp, *timeo;
7807dd7cddfSDavid du Colombier Proto *tcp;
7817dd7cddfSDavid du Colombier Tcppriv *priv;
7827dd7cddfSDavid du Colombier int loop;
7837dd7cddfSDavid du Colombier
7847dd7cddfSDavid du Colombier tcp = a;
7857dd7cddfSDavid du Colombier priv = tcp->priv;
7867dd7cddfSDavid du Colombier
7877dd7cddfSDavid du Colombier for(;;) {
788dc5a79c1SDavid du Colombier tsleep(&up->sleep, return0, 0, MSPTICK);
7897dd7cddfSDavid du Colombier
7907dd7cddfSDavid du Colombier qlock(&priv->tl);
7917dd7cddfSDavid du Colombier timeo = nil;
7927dd7cddfSDavid du Colombier loop = 0;
7937dd7cddfSDavid du Colombier for(t = priv->timers; t != nil; t = tp) {
7947dd7cddfSDavid du Colombier if(loop++ > 10000)
7957dd7cddfSDavid du Colombier panic("tcpackproc1");
7967dd7cddfSDavid du Colombier tp = t->next;
7979a747e4fSDavid du Colombier if(t->state == TcptimerON) {
7987dd7cddfSDavid du Colombier t->count--;
7997dd7cddfSDavid du Colombier if(t->count == 0) {
8009a747e4fSDavid du Colombier timerstate(priv, t, TcptimerDONE);
8017dd7cddfSDavid du Colombier t->readynext = timeo;
8027dd7cddfSDavid du Colombier timeo = t;
8037dd7cddfSDavid du Colombier }
8047dd7cddfSDavid du Colombier }
8057dd7cddfSDavid du Colombier }
8067dd7cddfSDavid du Colombier qunlock(&priv->tl);
8077dd7cddfSDavid du Colombier
8087dd7cddfSDavid du Colombier loop = 0;
8097dd7cddfSDavid du Colombier for(t = timeo; t != nil; t = t->readynext) {
8107dd7cddfSDavid du Colombier if(loop++ > 10000)
8117dd7cddfSDavid du Colombier panic("tcpackproc2");
8129a747e4fSDavid du Colombier if(t->state == TcptimerDONE && t->func != nil && !waserror()){
8137dd7cddfSDavid du Colombier (*t->func)(t->arg);
8149a747e4fSDavid du Colombier poperror();
8159a747e4fSDavid du Colombier }
8167dd7cddfSDavid du Colombier }
8173ff48bf5SDavid du Colombier
8183ff48bf5SDavid du Colombier limborexmit(tcp);
8197dd7cddfSDavid du Colombier }
8207dd7cddfSDavid du Colombier }
8217dd7cddfSDavid du Colombier
8221ce6c95fSDavid du Colombier static void
tcpgo(Tcppriv * priv,Tcptimer * t)8239a747e4fSDavid du Colombier tcpgo(Tcppriv *priv, Tcptimer *t)
8247dd7cddfSDavid du Colombier {
8257dd7cddfSDavid du Colombier if(t == nil || t->start == 0)
8267dd7cddfSDavid du Colombier return;
8277dd7cddfSDavid du Colombier
8287dd7cddfSDavid du Colombier qlock(&priv->tl);
8297dd7cddfSDavid du Colombier t->count = t->start;
8309a747e4fSDavid du Colombier timerstate(priv, t, TcptimerON);
8317dd7cddfSDavid du Colombier qunlock(&priv->tl);
8327dd7cddfSDavid du Colombier }
8337dd7cddfSDavid du Colombier
8341ce6c95fSDavid du Colombier static void
tcphalt(Tcppriv * priv,Tcptimer * t)8359a747e4fSDavid du Colombier tcphalt(Tcppriv *priv, Tcptimer *t)
8367dd7cddfSDavid du Colombier {
8377dd7cddfSDavid du Colombier if(t == nil)
8387dd7cddfSDavid du Colombier return;
8397dd7cddfSDavid du Colombier
8407dd7cddfSDavid du Colombier qlock(&priv->tl);
8419a747e4fSDavid du Colombier timerstate(priv, t, TcptimerOFF);
8427dd7cddfSDavid du Colombier qunlock(&priv->tl);
8437dd7cddfSDavid du Colombier }
8447dd7cddfSDavid du Colombier
8451ce6c95fSDavid du Colombier static int
backoff(int n)8467dd7cddfSDavid du Colombier backoff(int n)
8477dd7cddfSDavid du Colombier {
8487dd7cddfSDavid du Colombier return 1 << n;
8497dd7cddfSDavid du Colombier }
8507dd7cddfSDavid du Colombier
8511ce6c95fSDavid du Colombier static void
localclose(Conv * s,char * reason)8527dd7cddfSDavid du Colombier localclose(Conv *s, char *reason) /* called with tcb locked */
8537dd7cddfSDavid du Colombier {
8547dd7cddfSDavid du Colombier Tcpctl *tcb;
8557dd7cddfSDavid du Colombier Tcppriv *tpriv;
8567dd7cddfSDavid du Colombier
8577dd7cddfSDavid du Colombier tpriv = s->p->priv;
8587dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
8597dd7cddfSDavid du Colombier
86080ee5cbfSDavid du Colombier iphtrem(&tpriv->ht, s);
86180ee5cbfSDavid du Colombier
8627dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->timer);
8637dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
8647dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
8657dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->katimer);
8667dd7cddfSDavid du Colombier
8677dd7cddfSDavid du Colombier /* Flush reassembly queue; nothing more can arrive */
8681ce6c95fSDavid du Colombier dumpreseq(tcb);
8697dd7cddfSDavid du Colombier
8707dd7cddfSDavid du Colombier if(tcb->state == Syn_sent)
8717dd7cddfSDavid du Colombier Fsconnected(s, reason);
8727dd7cddfSDavid du Colombier if(s->state == Announced)
8737dd7cddfSDavid du Colombier wakeup(&s->listenr);
8747dd7cddfSDavid du Colombier
8757dd7cddfSDavid du Colombier qhangup(s->rq, reason);
8767dd7cddfSDavid du Colombier qhangup(s->wq, reason);
8777dd7cddfSDavid du Colombier
8787dd7cddfSDavid du Colombier tcpsetstate(s, Closed);
8797dd7cddfSDavid du Colombier }
8807dd7cddfSDavid du Colombier
8817dd7cddfSDavid du Colombier /* mtu (- TCP + IP hdr len) of 1st hop */
8821ce6c95fSDavid du Colombier static int
tcpmtu(Proto * tcp,uchar * addr,int version,uint * scale)8831ce6c95fSDavid du Colombier tcpmtu(Proto *tcp, uchar *addr, int version, uint *scale)
8847dd7cddfSDavid du Colombier {
8857dd7cddfSDavid du Colombier Ipifc *ifc;
8867dd7cddfSDavid du Colombier int mtu;
8877dd7cddfSDavid du Colombier
8883ff48bf5SDavid du Colombier ifc = findipifc(tcp->f, addr, 0);
8893ff48bf5SDavid du Colombier switch(version){
8903ff48bf5SDavid du Colombier default:
8913ff48bf5SDavid du Colombier case V4:
8927dd7cddfSDavid du Colombier mtu = DEF_MSS;
8933ff48bf5SDavid du Colombier if(ifc != nil)
894cc057a51SDavid du Colombier mtu = ifc->maxtu - ifc->medium->hsize - (TCP4_PKT + TCP4_HDRSIZE);
8953ff48bf5SDavid du Colombier break;
8963ff48bf5SDavid du Colombier case V6:
8973ff48bf5SDavid du Colombier mtu = DEF_MSS6;
8983ff48bf5SDavid du Colombier if(ifc != nil)
899cc057a51SDavid du Colombier mtu = ifc->maxtu - ifc->medium->hsize - (TCP6_PKT + TCP6_HDRSIZE);
9003ff48bf5SDavid du Colombier break;
9013ff48bf5SDavid du Colombier }
9021ce6c95fSDavid du Colombier /*
9031ce6c95fSDavid du Colombier * set the ws. it doesn't commit us to anything.
9041ce6c95fSDavid du Colombier * ws is the ultimate limit to the bandwidth-delay product.
9051ce6c95fSDavid du Colombier */
9061ce6c95fSDavid du Colombier *scale = Defadvscale;
9073ff48bf5SDavid du Colombier
9083ff48bf5SDavid du Colombier return mtu;
9097dd7cddfSDavid du Colombier }
9107dd7cddfSDavid du Colombier
9111ce6c95fSDavid du Colombier static void
inittcpctl(Conv * s,int mode)91280ee5cbfSDavid du Colombier inittcpctl(Conv *s, int mode)
9137dd7cddfSDavid du Colombier {
9147dd7cddfSDavid du Colombier Tcpctl *tcb;
9153ff48bf5SDavid du Colombier Tcp4hdr* h4;
9163ff48bf5SDavid du Colombier Tcp6hdr* h6;
9177ec5746aSDavid du Colombier Tcppriv *tpriv;
9183f695129SDavid du Colombier int mss;
9197dd7cddfSDavid du Colombier
9207dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
9217dd7cddfSDavid du Colombier
9227dd7cddfSDavid du Colombier memset(tcb, 0, sizeof(Tcpctl));
9237dd7cddfSDavid du Colombier
9241ce6c95fSDavid du Colombier tcb->ssthresh = QMAX; /* reset by tcpsetscale() */
92580ee5cbfSDavid du Colombier tcb->srtt = tcp_irtt<<LOGAGAIN;
92680ee5cbfSDavid du Colombier tcb->mdev = 0;
9277dd7cddfSDavid du Colombier
92880ee5cbfSDavid du Colombier /* setup timers */
9297dd7cddfSDavid du Colombier tcb->timer.start = tcp_irtt / MSPTICK;
9307dd7cddfSDavid du Colombier tcb->timer.func = tcptimeout;
9317dd7cddfSDavid du Colombier tcb->timer.arg = s;
9327dd7cddfSDavid du Colombier tcb->rtt_timer.start = MAX_TIME;
9337dd7cddfSDavid du Colombier tcb->acktimer.start = TCP_ACK / MSPTICK;
9347dd7cddfSDavid du Colombier tcb->acktimer.func = tcpacktimer;
9357dd7cddfSDavid du Colombier tcb->acktimer.arg = s;
9367dd7cddfSDavid du Colombier tcb->katimer.start = DEF_KAT / MSPTICK;
9377dd7cddfSDavid du Colombier tcb->katimer.func = tcpkeepalive;
9387dd7cddfSDavid du Colombier tcb->katimer.arg = s;
9397dd7cddfSDavid du Colombier
9403f695129SDavid du Colombier mss = DEF_MSS;
9413f695129SDavid du Colombier
9427dd7cddfSDavid du Colombier /* create a prototype(pseudo) header */
9433ff48bf5SDavid du Colombier if(mode != TCP_LISTEN){
9447dd7cddfSDavid du Colombier if(ipcmp(s->laddr, IPnoaddr) == 0)
9457dd7cddfSDavid du Colombier findlocalip(s->p->f, s->laddr, s->raddr);
9467dd7cddfSDavid du Colombier
9473ff48bf5SDavid du Colombier switch(s->ipversion){
9483ff48bf5SDavid du Colombier case V4:
9493ff48bf5SDavid du Colombier h4 = &tcb->protohdr.tcp4hdr;
9503ff48bf5SDavid du Colombier memset(h4, 0, sizeof(*h4));
9513ff48bf5SDavid du Colombier h4->proto = IP_TCPPROTO;
9523ff48bf5SDavid du Colombier hnputs(h4->tcpsport, s->lport);
9533ff48bf5SDavid du Colombier hnputs(h4->tcpdport, s->rport);
9543ff48bf5SDavid du Colombier v6tov4(h4->tcpsrc, s->laddr);
9553ff48bf5SDavid du Colombier v6tov4(h4->tcpdst, s->raddr);
9563ff48bf5SDavid du Colombier break;
9573ff48bf5SDavid du Colombier case V6:
9583ff48bf5SDavid du Colombier h6 = &tcb->protohdr.tcp6hdr;
9593ff48bf5SDavid du Colombier memset(h6, 0, sizeof(*h6));
9603ff48bf5SDavid du Colombier h6->proto = IP_TCPPROTO;
9613ff48bf5SDavid du Colombier hnputs(h6->tcpsport, s->lport);
9623ff48bf5SDavid du Colombier hnputs(h6->tcpdport, s->rport);
9633ff48bf5SDavid du Colombier ipmove(h6->tcpsrc, s->laddr);
9643ff48bf5SDavid du Colombier ipmove(h6->tcpdst, s->raddr);
9653f695129SDavid du Colombier mss = DEF_MSS6;
9663ff48bf5SDavid du Colombier break;
9673ff48bf5SDavid du Colombier default:
9683ff48bf5SDavid du Colombier panic("inittcpctl: version %d", s->ipversion);
9693ff48bf5SDavid du Colombier }
9703ff48bf5SDavid du Colombier }
9713ff48bf5SDavid du Colombier
9723f695129SDavid du Colombier tcb->mss = tcb->cwind = mss;
9731ce6c95fSDavid du Colombier tcb->abcbytes = 0;
9747ec5746aSDavid du Colombier tpriv = s->p->priv;
9757ec5746aSDavid du Colombier tpriv->stats[Mss] = tcb->mss;
9763f695129SDavid du Colombier
9773f695129SDavid du Colombier /* default is no window scaling */
9781ce6c95fSDavid du Colombier tcpsetscale(s, tcb, 0, 0);
9797dd7cddfSDavid du Colombier }
9807dd7cddfSDavid du Colombier
98180ee5cbfSDavid du Colombier /*
98280ee5cbfSDavid du Colombier * called with s qlocked
98380ee5cbfSDavid du Colombier */
9841ce6c95fSDavid du Colombier static void
tcpstart(Conv * s,int mode)9853f695129SDavid du Colombier tcpstart(Conv *s, int mode)
9867dd7cddfSDavid du Colombier {
9877dd7cddfSDavid du Colombier Tcpctl *tcb;
9887dd7cddfSDavid du Colombier Tcppriv *tpriv;
9899a747e4fSDavid du Colombier char kpname[KNAMELEN];
9907dd7cddfSDavid du Colombier
9917dd7cddfSDavid du Colombier tpriv = s->p->priv;
9927dd7cddfSDavid du Colombier
9937dd7cddfSDavid du Colombier if(tpriv->ackprocstarted == 0){
9947dd7cddfSDavid du Colombier qlock(&tpriv->apl);
9957dd7cddfSDavid du Colombier if(tpriv->ackprocstarted == 0){
9961ce6c95fSDavid du Colombier snprint(kpname, sizeof kpname, "#I%dtcpack", s->p->f->dev);
9977dd7cddfSDavid du Colombier kproc(kpname, tcpackproc, s->p);
9987dd7cddfSDavid du Colombier tpriv->ackprocstarted = 1;
9997dd7cddfSDavid du Colombier }
10007dd7cddfSDavid du Colombier qunlock(&tpriv->apl);
10017dd7cddfSDavid du Colombier }
10027dd7cddfSDavid du Colombier
10037dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
10047dd7cddfSDavid du Colombier
100580ee5cbfSDavid du Colombier inittcpctl(s, mode);
10067dd7cddfSDavid du Colombier
100780ee5cbfSDavid du Colombier iphtadd(&tpriv->ht, s);
10087dd7cddfSDavid du Colombier switch(mode) {
10097dd7cddfSDavid du Colombier case TCP_LISTEN:
101059cc4ca5SDavid du Colombier tpriv->stats[PassiveOpens]++;
10117dd7cddfSDavid du Colombier tcb->flags |= CLONE;
10127dd7cddfSDavid du Colombier tcpsetstate(s, Listen);
10137dd7cddfSDavid du Colombier break;
10147dd7cddfSDavid du Colombier
10157dd7cddfSDavid du Colombier case TCP_CONNECT:
101659cc4ca5SDavid du Colombier tpriv->stats[ActiveOpens]++;
10177dd7cddfSDavid du Colombier tcb->flags |= ACTIVE;
10183f695129SDavid du Colombier tcpsndsyn(s, tcb);
10197dd7cddfSDavid du Colombier tcpsetstate(s, Syn_sent);
10207dd7cddfSDavid du Colombier tcpoutput(s);
10217dd7cddfSDavid du Colombier break;
10227dd7cddfSDavid du Colombier }
10237dd7cddfSDavid du Colombier }
10247dd7cddfSDavid du Colombier
10257dd7cddfSDavid du Colombier static char*
tcpflag(char * buf,char * e,ushort flag)10261ce6c95fSDavid du Colombier tcpflag(char *buf, char *e, ushort flag)
10277dd7cddfSDavid du Colombier {
10281ce6c95fSDavid du Colombier char *p;
10297dd7cddfSDavid du Colombier
10301ce6c95fSDavid du Colombier p = seprint(buf, e, "%d", flag>>10); /* Head len */
10317dd7cddfSDavid du Colombier if(flag & URG)
10321ce6c95fSDavid du Colombier p = seprint(p, e, " URG");
10337dd7cddfSDavid du Colombier if(flag & ACK)
10341ce6c95fSDavid du Colombier p = seprint(p, e, " ACK");
10357dd7cddfSDavid du Colombier if(flag & PSH)
10361ce6c95fSDavid du Colombier p = seprint(p, e, " PSH");
10377dd7cddfSDavid du Colombier if(flag & RST)
10381ce6c95fSDavid du Colombier p = seprint(p, e, " RST");
10397dd7cddfSDavid du Colombier if(flag & SYN)
10401ce6c95fSDavid du Colombier p = seprint(p, e, " SYN");
10417dd7cddfSDavid du Colombier if(flag & FIN)
10421ce6c95fSDavid du Colombier p = seprint(p, e, " FIN");
10431ce6c95fSDavid du Colombier USED(p);
10447dd7cddfSDavid du Colombier return buf;
10457dd7cddfSDavid du Colombier }
10467dd7cddfSDavid du Colombier
10471ce6c95fSDavid du Colombier static Block*
htontcp6(Tcp * tcph,Block * data,Tcp6hdr * ph,Tcpctl * tcb)10483ff48bf5SDavid du Colombier htontcp6(Tcp *tcph, Block *data, Tcp6hdr *ph, Tcpctl *tcb)
10497dd7cddfSDavid du Colombier {
10507dd7cddfSDavid du Colombier int dlen;
10513ff48bf5SDavid du Colombier Tcp6hdr *h;
10527dd7cddfSDavid du Colombier ushort csum;
10533f695129SDavid du Colombier ushort hdrlen, optpad = 0;
10543f695129SDavid du Colombier uchar *opt;
10557dd7cddfSDavid du Colombier
10563ff48bf5SDavid du Colombier hdrlen = TCP6_HDRSIZE;
10573f695129SDavid du Colombier if(tcph->flags & SYN){
10587dd7cddfSDavid du Colombier if(tcph->mss)
10597dd7cddfSDavid du Colombier hdrlen += MSS_LENGTH;
10603f695129SDavid du Colombier if(tcph->ws)
10613f695129SDavid du Colombier hdrlen += WS_LENGTH;
10623f695129SDavid du Colombier optpad = hdrlen & 3;
10633f695129SDavid du Colombier if(optpad)
10643f695129SDavid du Colombier optpad = 4 - optpad;
10653f695129SDavid du Colombier hdrlen += optpad;
10663f695129SDavid du Colombier }
10677dd7cddfSDavid du Colombier
10687dd7cddfSDavid du Colombier if(data) {
10697dd7cddfSDavid du Colombier dlen = blocklen(data);
10703ff48bf5SDavid du Colombier data = padblock(data, hdrlen + TCP6_PKT);
10717dd7cddfSDavid du Colombier if(data == nil)
10727dd7cddfSDavid du Colombier return nil;
10737dd7cddfSDavid du Colombier }
10747dd7cddfSDavid du Colombier else {
10757dd7cddfSDavid du Colombier dlen = 0;
10763ff48bf5SDavid du Colombier data = allocb(hdrlen + TCP6_PKT + 64); /* the 64 pad is to meet mintu's */
10777dd7cddfSDavid du Colombier if(data == nil)
10787dd7cddfSDavid du Colombier return nil;
10793ff48bf5SDavid du Colombier data->wp += hdrlen + TCP6_PKT;
10807dd7cddfSDavid du Colombier }
10817dd7cddfSDavid du Colombier
10827dd7cddfSDavid du Colombier /* copy in pseudo ip header plus port numbers */
10833ff48bf5SDavid du Colombier h = (Tcp6hdr *)(data->rp);
10843ff48bf5SDavid du Colombier memmove(h, ph, TCP6_TCBPHDRSZ);
10853ff48bf5SDavid du Colombier
10863ff48bf5SDavid du Colombier /* compose pseudo tcp header, do cksum calculation */
10873ff48bf5SDavid du Colombier hnputl(h->vcf, hdrlen + dlen);
10883ff48bf5SDavid du Colombier h->ploadlen[0] = h->ploadlen[1] = h->proto = 0;
10893ff48bf5SDavid du Colombier h->ttl = ph->proto;
10903ff48bf5SDavid du Colombier
10913ff48bf5SDavid du Colombier /* copy in variable bits */
10923ff48bf5SDavid du Colombier hnputl(h->tcpseq, tcph->seq);
10933ff48bf5SDavid du Colombier hnputl(h->tcpack, tcph->ack);
10943ff48bf5SDavid du Colombier hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
10953f695129SDavid du Colombier hnputs(h->tcpwin, tcph->wnd>>(tcb != nil ? tcb->snd.scale : 0));
10963ff48bf5SDavid du Colombier hnputs(h->tcpurg, tcph->urg);
10973ff48bf5SDavid du Colombier
10983f695129SDavid du Colombier if(tcph->flags & SYN){
10993f695129SDavid du Colombier opt = h->tcpopt;
11003ff48bf5SDavid du Colombier if(tcph->mss != 0){
11013f695129SDavid du Colombier *opt++ = MSSOPT;
11023f695129SDavid du Colombier *opt++ = MSS_LENGTH;
11033f695129SDavid du Colombier hnputs(opt, tcph->mss);
11043f695129SDavid du Colombier opt += 2;
11053ff48bf5SDavid du Colombier }
11063f695129SDavid du Colombier if(tcph->ws != 0){
11073f695129SDavid du Colombier *opt++ = WSOPT;
11083f695129SDavid du Colombier *opt++ = WS_LENGTH;
11093f695129SDavid du Colombier *opt++ = tcph->ws;
11103f695129SDavid du Colombier }
11113f695129SDavid du Colombier while(optpad-- > 0)
11123f695129SDavid du Colombier *opt++ = NOOPOPT;
11133f695129SDavid du Colombier }
11143f695129SDavid du Colombier
11153ff48bf5SDavid du Colombier if(tcb != nil && tcb->nochecksum){
11163ff48bf5SDavid du Colombier h->tcpcksum[0] = h->tcpcksum[1] = 0;
11173ff48bf5SDavid du Colombier } else {
11183ff48bf5SDavid du Colombier csum = ptclcsum(data, TCP6_IPLEN, hdrlen+dlen+TCP6_PHDRSIZE);
11193ff48bf5SDavid du Colombier hnputs(h->tcpcksum, csum);
11203ff48bf5SDavid du Colombier }
11213ff48bf5SDavid du Colombier
11223ff48bf5SDavid du Colombier /* move from pseudo header back to normal ip header */
11233ff48bf5SDavid du Colombier memset(h->vcf, 0, 4);
11243ff48bf5SDavid du Colombier h->vcf[0] = IP_VER6;
11253ff48bf5SDavid du Colombier hnputs(h->ploadlen, hdrlen+dlen);
11263ff48bf5SDavid du Colombier h->proto = ph->proto;
11273ff48bf5SDavid du Colombier
11283ff48bf5SDavid du Colombier return data;
11293ff48bf5SDavid du Colombier }
11303ff48bf5SDavid du Colombier
11311ce6c95fSDavid du Colombier static Block*
htontcp4(Tcp * tcph,Block * data,Tcp4hdr * ph,Tcpctl * tcb)11323ff48bf5SDavid du Colombier htontcp4(Tcp *tcph, Block *data, Tcp4hdr *ph, Tcpctl *tcb)
11333ff48bf5SDavid du Colombier {
11343ff48bf5SDavid du Colombier int dlen;
11353ff48bf5SDavid du Colombier Tcp4hdr *h;
11363ff48bf5SDavid du Colombier ushort csum;
11373f695129SDavid du Colombier ushort hdrlen, optpad = 0;
11383f695129SDavid du Colombier uchar *opt;
11393ff48bf5SDavid du Colombier
11403ff48bf5SDavid du Colombier hdrlen = TCP4_HDRSIZE;
11413f695129SDavid du Colombier if(tcph->flags & SYN){
11423ff48bf5SDavid du Colombier if(tcph->mss)
11433ff48bf5SDavid du Colombier hdrlen += MSS_LENGTH;
11441ce6c95fSDavid du Colombier if(1)
11453f695129SDavid du Colombier hdrlen += WS_LENGTH;
11463f695129SDavid du Colombier optpad = hdrlen & 3;
11473f695129SDavid du Colombier if(optpad)
11483f695129SDavid du Colombier optpad = 4 - optpad;
11493f695129SDavid du Colombier hdrlen += optpad;
11503f695129SDavid du Colombier }
11513ff48bf5SDavid du Colombier
11523ff48bf5SDavid du Colombier if(data) {
11533ff48bf5SDavid du Colombier dlen = blocklen(data);
11543ff48bf5SDavid du Colombier data = padblock(data, hdrlen + TCP4_PKT);
11553ff48bf5SDavid du Colombier if(data == nil)
11563ff48bf5SDavid du Colombier return nil;
11573ff48bf5SDavid du Colombier }
11583ff48bf5SDavid du Colombier else {
11593ff48bf5SDavid du Colombier dlen = 0;
11603ff48bf5SDavid du Colombier data = allocb(hdrlen + TCP4_PKT + 64); /* the 64 pad is to meet mintu's */
11613ff48bf5SDavid du Colombier if(data == nil)
11623ff48bf5SDavid du Colombier return nil;
11633ff48bf5SDavid du Colombier data->wp += hdrlen + TCP4_PKT;
11643ff48bf5SDavid du Colombier }
11653ff48bf5SDavid du Colombier
11663ff48bf5SDavid du Colombier /* copy in pseudo ip header plus port numbers */
11673ff48bf5SDavid du Colombier h = (Tcp4hdr *)(data->rp);
11683ff48bf5SDavid du Colombier memmove(h, ph, TCP4_TCBPHDRSZ);
11697dd7cddfSDavid du Colombier
11707dd7cddfSDavid du Colombier /* copy in variable bits */
11717dd7cddfSDavid du Colombier hnputs(h->tcplen, hdrlen + dlen);
11727dd7cddfSDavid du Colombier hnputl(h->tcpseq, tcph->seq);
11737dd7cddfSDavid du Colombier hnputl(h->tcpack, tcph->ack);
11747dd7cddfSDavid du Colombier hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
11753f695129SDavid du Colombier hnputs(h->tcpwin, tcph->wnd>>(tcb != nil ? tcb->snd.scale : 0));
11767dd7cddfSDavid du Colombier hnputs(h->tcpurg, tcph->urg);
11777dd7cddfSDavid du Colombier
11783f695129SDavid du Colombier if(tcph->flags & SYN){
11793f695129SDavid du Colombier opt = h->tcpopt;
11807dd7cddfSDavid du Colombier if(tcph->mss != 0){
11813f695129SDavid du Colombier *opt++ = MSSOPT;
11823f695129SDavid du Colombier *opt++ = MSS_LENGTH;
11833f695129SDavid du Colombier hnputs(opt, tcph->mss);
11843f695129SDavid du Colombier opt += 2;
11857dd7cddfSDavid du Colombier }
11861ce6c95fSDavid du Colombier /* always offer. rfc1323 §2.2 */
11871ce6c95fSDavid du Colombier if(1){
11883f695129SDavid du Colombier *opt++ = WSOPT;
11893f695129SDavid du Colombier *opt++ = WS_LENGTH;
11903f695129SDavid du Colombier *opt++ = tcph->ws;
11913f695129SDavid du Colombier }
11923f695129SDavid du Colombier while(optpad-- > 0)
11933f695129SDavid du Colombier *opt++ = NOOPOPT;
11943f695129SDavid du Colombier }
11953f695129SDavid du Colombier
119659cc4ca5SDavid du Colombier if(tcb != nil && tcb->nochecksum){
119759cc4ca5SDavid du Colombier h->tcpcksum[0] = h->tcpcksum[1] = 0;
119859cc4ca5SDavid du Colombier } else {
11993ff48bf5SDavid du Colombier csum = ptclcsum(data, TCP4_IPLEN, hdrlen+dlen+TCP4_PHDRSIZE);
12007dd7cddfSDavid du Colombier hnputs(h->tcpcksum, csum);
120159cc4ca5SDavid du Colombier }
12027dd7cddfSDavid du Colombier
12037dd7cddfSDavid du Colombier return data;
12047dd7cddfSDavid du Colombier }
12057dd7cddfSDavid du Colombier
12061ce6c95fSDavid du Colombier static int
ntohtcp6(Tcp * tcph,Block ** bpp)12073ff48bf5SDavid du Colombier ntohtcp6(Tcp *tcph, Block **bpp)
12087dd7cddfSDavid du Colombier {
12093ff48bf5SDavid du Colombier Tcp6hdr *h;
12107dd7cddfSDavid du Colombier uchar *optr;
12117dd7cddfSDavid du Colombier ushort hdrlen;
12127dd7cddfSDavid du Colombier ushort optlen;
12137dd7cddfSDavid du Colombier int n;
12147dd7cddfSDavid du Colombier
12153ff48bf5SDavid du Colombier *bpp = pullupblock(*bpp, TCP6_PKT+TCP6_HDRSIZE);
12167dd7cddfSDavid du Colombier if(*bpp == nil)
12177dd7cddfSDavid du Colombier return -1;
12187dd7cddfSDavid du Colombier
12193ff48bf5SDavid du Colombier h = (Tcp6hdr *)((*bpp)->rp);
12207dd7cddfSDavid du Colombier tcph->source = nhgets(h->tcpsport);
12217dd7cddfSDavid du Colombier tcph->dest = nhgets(h->tcpdport);
12227dd7cddfSDavid du Colombier tcph->seq = nhgetl(h->tcpseq);
12237dd7cddfSDavid du Colombier tcph->ack = nhgetl(h->tcpack);
12243f695129SDavid du Colombier hdrlen = (h->tcpflag[0]>>2) & ~3;
12253ff48bf5SDavid du Colombier if(hdrlen < TCP6_HDRSIZE) {
12267dd7cddfSDavid du Colombier freeblist(*bpp);
12277dd7cddfSDavid du Colombier return -1;
12287dd7cddfSDavid du Colombier }
12297dd7cddfSDavid du Colombier
12307dd7cddfSDavid du Colombier tcph->flags = h->tcpflag[1];
12317dd7cddfSDavid du Colombier tcph->wnd = nhgets(h->tcpwin);
12327dd7cddfSDavid du Colombier tcph->urg = nhgets(h->tcpurg);
12337dd7cddfSDavid du Colombier tcph->mss = 0;
12343f695129SDavid du Colombier tcph->ws = 0;
12351ce6c95fSDavid du Colombier tcph->update = 0;
12363ff48bf5SDavid du Colombier tcph->len = nhgets(h->ploadlen) - hdrlen;
12377dd7cddfSDavid du Colombier
12383ff48bf5SDavid du Colombier *bpp = pullupblock(*bpp, hdrlen+TCP6_PKT);
12397dd7cddfSDavid du Colombier if(*bpp == nil)
12407dd7cddfSDavid du Colombier return -1;
12417dd7cddfSDavid du Colombier
12427dd7cddfSDavid du Colombier optr = h->tcpopt;
12433ff48bf5SDavid du Colombier n = hdrlen - TCP6_HDRSIZE;
12447dd7cddfSDavid du Colombier while(n > 0 && *optr != EOLOPT) {
12457dd7cddfSDavid du Colombier if(*optr == NOOPOPT) {
12467dd7cddfSDavid du Colombier n--;
12477dd7cddfSDavid du Colombier optr++;
12487dd7cddfSDavid du Colombier continue;
12497dd7cddfSDavid du Colombier }
12507dd7cddfSDavid du Colombier optlen = optr[1];
12517dd7cddfSDavid du Colombier if(optlen < 2 || optlen > n)
12527dd7cddfSDavid du Colombier break;
12537dd7cddfSDavid du Colombier switch(*optr) {
12547dd7cddfSDavid du Colombier case MSSOPT:
12557dd7cddfSDavid du Colombier if(optlen == MSS_LENGTH)
12567dd7cddfSDavid du Colombier tcph->mss = nhgets(optr+2);
12577dd7cddfSDavid du Colombier break;
12583f695129SDavid du Colombier case WSOPT:
12593f695129SDavid du Colombier if(optlen == WS_LENGTH && *(optr+2) <= 14)
12601ce6c95fSDavid du Colombier tcph->ws = *(optr+2);
12613f695129SDavid du Colombier break;
12627dd7cddfSDavid du Colombier }
12637dd7cddfSDavid du Colombier n -= optlen;
12647dd7cddfSDavid du Colombier optr += optlen;
12657dd7cddfSDavid du Colombier }
12667dd7cddfSDavid du Colombier return hdrlen;
12677dd7cddfSDavid du Colombier }
12687dd7cddfSDavid du Colombier
12691ce6c95fSDavid du Colombier static int
ntohtcp4(Tcp * tcph,Block ** bpp)12703ff48bf5SDavid du Colombier ntohtcp4(Tcp *tcph, Block **bpp)
12713ff48bf5SDavid du Colombier {
12723ff48bf5SDavid du Colombier Tcp4hdr *h;
12733ff48bf5SDavid du Colombier uchar *optr;
12743ff48bf5SDavid du Colombier ushort hdrlen;
12753ff48bf5SDavid du Colombier ushort optlen;
12763ff48bf5SDavid du Colombier int n;
12773ff48bf5SDavid du Colombier
12783ff48bf5SDavid du Colombier *bpp = pullupblock(*bpp, TCP4_PKT+TCP4_HDRSIZE);
12793ff48bf5SDavid du Colombier if(*bpp == nil)
12803ff48bf5SDavid du Colombier return -1;
12813ff48bf5SDavid du Colombier
12823ff48bf5SDavid du Colombier h = (Tcp4hdr *)((*bpp)->rp);
12833ff48bf5SDavid du Colombier tcph->source = nhgets(h->tcpsport);
12843ff48bf5SDavid du Colombier tcph->dest = nhgets(h->tcpdport);
12853ff48bf5SDavid du Colombier tcph->seq = nhgetl(h->tcpseq);
12863ff48bf5SDavid du Colombier tcph->ack = nhgetl(h->tcpack);
12873ff48bf5SDavid du Colombier
12883f695129SDavid du Colombier hdrlen = (h->tcpflag[0]>>2) & ~3;
12893ff48bf5SDavid du Colombier if(hdrlen < TCP4_HDRSIZE) {
12903ff48bf5SDavid du Colombier freeblist(*bpp);
12913ff48bf5SDavid du Colombier return -1;
12923ff48bf5SDavid du Colombier }
12933ff48bf5SDavid du Colombier
12943ff48bf5SDavid du Colombier tcph->flags = h->tcpflag[1];
12953ff48bf5SDavid du Colombier tcph->wnd = nhgets(h->tcpwin);
12963ff48bf5SDavid du Colombier tcph->urg = nhgets(h->tcpurg);
12973ff48bf5SDavid du Colombier tcph->mss = 0;
12983f695129SDavid du Colombier tcph->ws = 0;
12991ce6c95fSDavid du Colombier tcph->update = 0;
13003ff48bf5SDavid du Colombier tcph->len = nhgets(h->length) - (hdrlen + TCP4_PKT);
13013ff48bf5SDavid du Colombier
13023ff48bf5SDavid du Colombier *bpp = pullupblock(*bpp, hdrlen+TCP4_PKT);
13033ff48bf5SDavid du Colombier if(*bpp == nil)
13043ff48bf5SDavid du Colombier return -1;
13053ff48bf5SDavid du Colombier
13063ff48bf5SDavid du Colombier optr = h->tcpopt;
13073ff48bf5SDavid du Colombier n = hdrlen - TCP4_HDRSIZE;
13083ff48bf5SDavid du Colombier while(n > 0 && *optr != EOLOPT) {
13093ff48bf5SDavid du Colombier if(*optr == NOOPOPT) {
13103ff48bf5SDavid du Colombier n--;
13113ff48bf5SDavid du Colombier optr++;
13123ff48bf5SDavid du Colombier continue;
13133ff48bf5SDavid du Colombier }
13143ff48bf5SDavid du Colombier optlen = optr[1];
13153ff48bf5SDavid du Colombier if(optlen < 2 || optlen > n)
13163ff48bf5SDavid du Colombier break;
13173ff48bf5SDavid du Colombier switch(*optr) {
13183ff48bf5SDavid du Colombier case MSSOPT:
13191ce6c95fSDavid du Colombier if(optlen == MSS_LENGTH)
13203ff48bf5SDavid du Colombier tcph->mss = nhgets(optr+2);
13213ff48bf5SDavid du Colombier break;
13223f695129SDavid du Colombier case WSOPT:
13233f695129SDavid du Colombier if(optlen == WS_LENGTH && *(optr+2) <= 14)
13241ce6c95fSDavid du Colombier tcph->ws = *(optr+2);
13253f695129SDavid du Colombier break;
13263ff48bf5SDavid du Colombier }
13273ff48bf5SDavid du Colombier n -= optlen;
13283ff48bf5SDavid du Colombier optr += optlen;
13293ff48bf5SDavid du Colombier }
13303ff48bf5SDavid du Colombier return hdrlen;
13313ff48bf5SDavid du Colombier }
13323ff48bf5SDavid du Colombier
13333ff48bf5SDavid du Colombier /*
13341ce6c95fSDavid du Colombier * For outgoing calls, generate an initial sequence
13353ff48bf5SDavid du Colombier * number and put a SYN on the send queue
13363ff48bf5SDavid du Colombier */
13371ce6c95fSDavid du Colombier static void
tcpsndsyn(Conv * s,Tcpctl * tcb)13383f695129SDavid du Colombier tcpsndsyn(Conv *s, Tcpctl *tcb)
13397dd7cddfSDavid du Colombier {
13407ec5746aSDavid du Colombier Tcppriv *tpriv;
13417ec5746aSDavid du Colombier
13427dd7cddfSDavid du Colombier tcb->iss = (nrand(1<<16)<<16)|nrand(1<<16);
13437dd7cddfSDavid du Colombier tcb->rttseq = tcb->iss;
13447dd7cddfSDavid du Colombier tcb->snd.wl2 = tcb->iss;
13457dd7cddfSDavid du Colombier tcb->snd.una = tcb->iss;
13461ce6c95fSDavid du Colombier tcb->snd.rxt = tcb->iss;
13477dd7cddfSDavid du Colombier tcb->snd.ptr = tcb->rttseq;
13487dd7cddfSDavid du Colombier tcb->snd.nxt = tcb->rttseq;
134980ee5cbfSDavid du Colombier tcb->flgcnt++;
13507dd7cddfSDavid du Colombier tcb->flags |= FORCE;
13513ff48bf5SDavid du Colombier tcb->sndsyntime = NOW;
13523f695129SDavid du Colombier
13533f695129SDavid du Colombier /* set desired mss and scale */
13543f695129SDavid du Colombier tcb->mss = tcpmtu(s->p, s->laddr, s->ipversion, &tcb->scale);
13557ec5746aSDavid du Colombier tpriv = s->p->priv;
13567ec5746aSDavid du Colombier tpriv->stats[Mss] = tcb->mss;
13577dd7cddfSDavid du Colombier }
13587dd7cddfSDavid du Colombier
13597dd7cddfSDavid du Colombier void
sndrst(Proto * tcp,uchar * source,uchar * dest,ushort length,Tcp * seg,uchar version,char * reason)13603ff48bf5SDavid du Colombier sndrst(Proto *tcp, uchar *source, uchar *dest, ushort length, Tcp *seg, uchar version, char *reason)
13617dd7cddfSDavid du Colombier {
13627dd7cddfSDavid du Colombier Block *hbp;
13637dd7cddfSDavid du Colombier uchar rflags;
13647dd7cddfSDavid du Colombier Tcppriv *tpriv;
13653ff48bf5SDavid du Colombier Tcp4hdr ph4;
13663ff48bf5SDavid du Colombier Tcp6hdr ph6;
13673ff48bf5SDavid du Colombier
136884363d68SDavid du Colombier netlog(tcp->f, Logtcp, "sndrst: %s\n", reason);
13697dd7cddfSDavid du Colombier
13707dd7cddfSDavid du Colombier tpriv = tcp->priv;
13717dd7cddfSDavid du Colombier
13727dd7cddfSDavid du Colombier if(seg->flags & RST)
13737dd7cddfSDavid du Colombier return;
13747dd7cddfSDavid du Colombier
13757dd7cddfSDavid du Colombier /* make pseudo header */
13763ff48bf5SDavid du Colombier switch(version) {
13773ff48bf5SDavid du Colombier case V4:
13783ff48bf5SDavid du Colombier memset(&ph4, 0, sizeof(ph4));
13793ff48bf5SDavid du Colombier ph4.vihl = IP_VER4;
13803ff48bf5SDavid du Colombier v6tov4(ph4.tcpsrc, dest);
13813ff48bf5SDavid du Colombier v6tov4(ph4.tcpdst, source);
13823ff48bf5SDavid du Colombier ph4.proto = IP_TCPPROTO;
13833ff48bf5SDavid du Colombier hnputs(ph4.tcplen, TCP4_HDRSIZE);
13843ff48bf5SDavid du Colombier hnputs(ph4.tcpsport, seg->dest);
13853ff48bf5SDavid du Colombier hnputs(ph4.tcpdport, seg->source);
13863ff48bf5SDavid du Colombier break;
13873ff48bf5SDavid du Colombier case V6:
13883ff48bf5SDavid du Colombier memset(&ph6, 0, sizeof(ph6));
13893ff48bf5SDavid du Colombier ph6.vcf[0] = IP_VER6;
13903ff48bf5SDavid du Colombier ipmove(ph6.tcpsrc, dest);
13913ff48bf5SDavid du Colombier ipmove(ph6.tcpdst, source);
13923ff48bf5SDavid du Colombier ph6.proto = IP_TCPPROTO;
13933ff48bf5SDavid du Colombier hnputs(ph6.ploadlen, TCP6_HDRSIZE);
13943ff48bf5SDavid du Colombier hnputs(ph6.tcpsport, seg->dest);
13953ff48bf5SDavid du Colombier hnputs(ph6.tcpdport, seg->source);
13963ff48bf5SDavid du Colombier break;
13973ff48bf5SDavid du Colombier default:
13983ff48bf5SDavid du Colombier panic("sndrst: version %d", version);
13993ff48bf5SDavid du Colombier }
14007dd7cddfSDavid du Colombier
140159cc4ca5SDavid du Colombier tpriv->stats[OutRsts]++;
14027dd7cddfSDavid du Colombier rflags = RST;
14037dd7cddfSDavid du Colombier
14047dd7cddfSDavid du Colombier /* convince the other end that this reset is in band */
14057dd7cddfSDavid du Colombier if(seg->flags & ACK) {
14067dd7cddfSDavid du Colombier seg->seq = seg->ack;
14077dd7cddfSDavid du Colombier seg->ack = 0;
14087dd7cddfSDavid du Colombier }
14097dd7cddfSDavid du Colombier else {
14107dd7cddfSDavid du Colombier rflags |= ACK;
14117dd7cddfSDavid du Colombier seg->ack = seg->seq;
14127dd7cddfSDavid du Colombier seg->seq = 0;
14137dd7cddfSDavid du Colombier if(seg->flags & SYN)
14147dd7cddfSDavid du Colombier seg->ack++;
14157dd7cddfSDavid du Colombier seg->ack += length;
14167dd7cddfSDavid du Colombier if(seg->flags & FIN)
14177dd7cddfSDavid du Colombier seg->ack++;
14187dd7cddfSDavid du Colombier }
14197dd7cddfSDavid du Colombier seg->flags = rflags;
14207dd7cddfSDavid du Colombier seg->wnd = 0;
14217dd7cddfSDavid du Colombier seg->urg = 0;
14227dd7cddfSDavid du Colombier seg->mss = 0;
14233f695129SDavid du Colombier seg->ws = 0;
14243ff48bf5SDavid du Colombier switch(version) {
14253ff48bf5SDavid du Colombier case V4:
14263ff48bf5SDavid du Colombier hbp = htontcp4(seg, nil, &ph4, nil);
14277dd7cddfSDavid du Colombier if(hbp == nil)
14287dd7cddfSDavid du Colombier return;
1429a6a9e072SDavid du Colombier ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
14303ff48bf5SDavid du Colombier break;
14313ff48bf5SDavid du Colombier case V6:
14323ff48bf5SDavid du Colombier hbp = htontcp6(seg, nil, &ph6, nil);
14333ff48bf5SDavid du Colombier if(hbp == nil)
14343ff48bf5SDavid du Colombier return;
1435a6a9e072SDavid du Colombier ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
14363ff48bf5SDavid du Colombier break;
14373ff48bf5SDavid du Colombier default:
14383ff48bf5SDavid du Colombier panic("sndrst2: version %d", version);
14393ff48bf5SDavid du Colombier }
14407dd7cddfSDavid du Colombier }
14417dd7cddfSDavid du Colombier
14427dd7cddfSDavid du Colombier /*
1443*41aa7335SDavid du Colombier * close the conversation
1444*41aa7335SDavid du Colombier */
1445*41aa7335SDavid du Colombier static char*
tcpclose2(Conv * s)1446*41aa7335SDavid du Colombier tcpclose2(Conv *s)
1447*41aa7335SDavid du Colombier {
1448*41aa7335SDavid du Colombier tcpclose(s);
1449*41aa7335SDavid du Colombier return nil;
1450*41aa7335SDavid du Colombier }
1451*41aa7335SDavid du Colombier
1452*41aa7335SDavid du Colombier /*
14537dd7cddfSDavid du Colombier * send a reset to the remote side and close the conversation
145480ee5cbfSDavid du Colombier * called with s qlocked
14557dd7cddfSDavid du Colombier */
14561ce6c95fSDavid du Colombier static char*
tcphangup(Conv * s)14577dd7cddfSDavid du Colombier tcphangup(Conv *s)
14587dd7cddfSDavid du Colombier {
14597dd7cddfSDavid du Colombier Tcp seg;
14607dd7cddfSDavid du Colombier Tcpctl *tcb;
14617dd7cddfSDavid du Colombier Block *hbp;
14627dd7cddfSDavid du Colombier
14637dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
146480ee5cbfSDavid du Colombier if(waserror())
14657dd7cddfSDavid du Colombier return commonerror();
1466c02046c4SDavid du Colombier if(ipcmp(s->raddr, IPnoaddr) != 0) {
14675fab9909SDavid du Colombier if(!waserror()){
146810ae047aSDavid du Colombier memset(&seg, 0, sizeof seg);
14697dd7cddfSDavid du Colombier seg.flags = RST | ACK;
14707dd7cddfSDavid du Colombier seg.ack = tcb->rcv.nxt;
14711ce6c95fSDavid du Colombier tcb->rcv.ackptr = seg.ack;
14727dd7cddfSDavid du Colombier seg.seq = tcb->snd.ptr;
14737dd7cddfSDavid du Colombier seg.wnd = 0;
14747dd7cddfSDavid du Colombier seg.urg = 0;
14757dd7cddfSDavid du Colombier seg.mss = 0;
14763f695129SDavid du Colombier seg.ws = 0;
14773ff48bf5SDavid du Colombier switch(s->ipversion) {
14783ff48bf5SDavid du Colombier case V4:
14793ff48bf5SDavid du Colombier tcb->protohdr.tcp4hdr.vihl = IP_VER4;
14803ff48bf5SDavid du Colombier hbp = htontcp4(&seg, nil, &tcb->protohdr.tcp4hdr, tcb);
1481a6a9e072SDavid du Colombier ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s);
14823ff48bf5SDavid du Colombier break;
14833ff48bf5SDavid du Colombier case V6:
14843ff48bf5SDavid du Colombier tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
14853ff48bf5SDavid du Colombier hbp = htontcp6(&seg, nil, &tcb->protohdr.tcp6hdr, tcb);
1486a6a9e072SDavid du Colombier ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s);
14873ff48bf5SDavid du Colombier break;
14883ff48bf5SDavid du Colombier default:
14893ff48bf5SDavid du Colombier panic("tcphangup: version %d", s->ipversion);
14903ff48bf5SDavid du Colombier }
14915fab9909SDavid du Colombier poperror();
14925fab9909SDavid du Colombier }
14937dd7cddfSDavid du Colombier }
14947dd7cddfSDavid du Colombier localclose(s, nil);
14957dd7cddfSDavid du Colombier poperror();
14967dd7cddfSDavid du Colombier return nil;
14977dd7cddfSDavid du Colombier }
14987dd7cddfSDavid du Colombier
14993ff48bf5SDavid du Colombier /*
15003ff48bf5SDavid du Colombier * (re)send a SYN ACK
15013ff48bf5SDavid du Colombier */
15021ce6c95fSDavid du Colombier static int
sndsynack(Proto * tcp,Limbo * lp)15033ff48bf5SDavid du Colombier sndsynack(Proto *tcp, Limbo *lp)
15043ff48bf5SDavid du Colombier {
15053ff48bf5SDavid du Colombier Block *hbp;
15063ff48bf5SDavid du Colombier Tcp4hdr ph4;
15073ff48bf5SDavid du Colombier Tcp6hdr ph6;
15083ff48bf5SDavid du Colombier Tcp seg;
15091ce6c95fSDavid du Colombier uint scale;
15103ff48bf5SDavid du Colombier
15113ff48bf5SDavid du Colombier /* make pseudo header */
15123ff48bf5SDavid du Colombier switch(lp->version) {
15133ff48bf5SDavid du Colombier case V4:
15143ff48bf5SDavid du Colombier memset(&ph4, 0, sizeof(ph4));
15153ff48bf5SDavid du Colombier ph4.vihl = IP_VER4;
15163ff48bf5SDavid du Colombier v6tov4(ph4.tcpsrc, lp->laddr);
15173ff48bf5SDavid du Colombier v6tov4(ph4.tcpdst, lp->raddr);
15183ff48bf5SDavid du Colombier ph4.proto = IP_TCPPROTO;
15193ff48bf5SDavid du Colombier hnputs(ph4.tcplen, TCP4_HDRSIZE);
15203ff48bf5SDavid du Colombier hnputs(ph4.tcpsport, lp->lport);
15213ff48bf5SDavid du Colombier hnputs(ph4.tcpdport, lp->rport);
15223ff48bf5SDavid du Colombier break;
15233ff48bf5SDavid du Colombier case V6:
15243ff48bf5SDavid du Colombier memset(&ph6, 0, sizeof(ph6));
15253ff48bf5SDavid du Colombier ph6.vcf[0] = IP_VER6;
15263ff48bf5SDavid du Colombier ipmove(ph6.tcpsrc, lp->laddr);
15273ff48bf5SDavid du Colombier ipmove(ph6.tcpdst, lp->raddr);
15283ff48bf5SDavid du Colombier ph6.proto = IP_TCPPROTO;
15293ff48bf5SDavid du Colombier hnputs(ph6.ploadlen, TCP6_HDRSIZE);
15303ff48bf5SDavid du Colombier hnputs(ph6.tcpsport, lp->lport);
15313ff48bf5SDavid du Colombier hnputs(ph6.tcpdport, lp->rport);
15323ff48bf5SDavid du Colombier break;
15333ff48bf5SDavid du Colombier default:
15343ff48bf5SDavid du Colombier panic("sndrst: version %d", lp->version);
15353ff48bf5SDavid du Colombier }
15363ff48bf5SDavid du Colombier
153710ae047aSDavid du Colombier memset(&seg, 0, sizeof seg);
15383ff48bf5SDavid du Colombier seg.seq = lp->iss;
15393ff48bf5SDavid du Colombier seg.ack = lp->irs+1;
15403ff48bf5SDavid du Colombier seg.flags = SYN|ACK;
15413ff48bf5SDavid du Colombier seg.urg = 0;
15423f695129SDavid du Colombier seg.mss = tcpmtu(tcp, lp->laddr, lp->version, &scale);
15433ff48bf5SDavid du Colombier seg.wnd = QMAX;
15443ff48bf5SDavid du Colombier
15453f695129SDavid du Colombier /* if the other side set scale, we should too */
15463f695129SDavid du Colombier if(lp->rcvscale){
15473f695129SDavid du Colombier seg.ws = scale;
15483f695129SDavid du Colombier lp->sndscale = scale;
15493f695129SDavid du Colombier } else {
15503f695129SDavid du Colombier seg.ws = 0;
15513f695129SDavid du Colombier lp->sndscale = 0;
15523f695129SDavid du Colombier }
15533f695129SDavid du Colombier
15543ff48bf5SDavid du Colombier switch(lp->version) {
15553ff48bf5SDavid du Colombier case V4:
15563ff48bf5SDavid du Colombier hbp = htontcp4(&seg, nil, &ph4, nil);
15573ff48bf5SDavid du Colombier if(hbp == nil)
15583ff48bf5SDavid du Colombier return -1;
1559a6a9e072SDavid du Colombier ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
15603ff48bf5SDavid du Colombier break;
15613ff48bf5SDavid du Colombier case V6:
15623ff48bf5SDavid du Colombier hbp = htontcp6(&seg, nil, &ph6, nil);
15633ff48bf5SDavid du Colombier if(hbp == nil)
15643ff48bf5SDavid du Colombier return -1;
1565a6a9e072SDavid du Colombier ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
15663ff48bf5SDavid du Colombier break;
15673ff48bf5SDavid du Colombier default:
15683ff48bf5SDavid du Colombier panic("sndsnack: version %d", lp->version);
15693ff48bf5SDavid du Colombier }
15703ff48bf5SDavid du Colombier lp->lastsend = NOW;
15713ff48bf5SDavid du Colombier return 0;
15723ff48bf5SDavid du Colombier }
15733ff48bf5SDavid du Colombier
15743ff48bf5SDavid du Colombier #define hashipa(a, p) ( ( (a)[IPaddrlen-2] + (a)[IPaddrlen-1] + p )&LHTMASK )
15753ff48bf5SDavid du Colombier
15763ff48bf5SDavid du Colombier /*
15773ff48bf5SDavid du Colombier * put a call into limbo and respond with a SYN ACK
15783ff48bf5SDavid du Colombier *
15793ff48bf5SDavid du Colombier * called with proto locked
15803ff48bf5SDavid du Colombier */
15813ff48bf5SDavid du Colombier static void
limbo(Conv * s,uchar * source,uchar * dest,Tcp * seg,int version)15823ff48bf5SDavid du Colombier limbo(Conv *s, uchar *source, uchar *dest, Tcp *seg, int version)
15833ff48bf5SDavid du Colombier {
15843ff48bf5SDavid du Colombier Limbo *lp, **l;
15853ff48bf5SDavid du Colombier Tcppriv *tpriv;
15863ff48bf5SDavid du Colombier int h;
15873ff48bf5SDavid du Colombier
15883ff48bf5SDavid du Colombier tpriv = s->p->priv;
15893ff48bf5SDavid du Colombier h = hashipa(source, seg->source);
15903ff48bf5SDavid du Colombier
15913ff48bf5SDavid du Colombier for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){
15923ff48bf5SDavid du Colombier lp = *l;
15933ff48bf5SDavid du Colombier if(lp->lport != seg->dest || lp->rport != seg->source || lp->version != version)
15943ff48bf5SDavid du Colombier continue;
15953ff48bf5SDavid du Colombier if(ipcmp(lp->raddr, source) != 0)
15963ff48bf5SDavid du Colombier continue;
15973ff48bf5SDavid du Colombier if(ipcmp(lp->laddr, dest) != 0)
15983ff48bf5SDavid du Colombier continue;
15993ff48bf5SDavid du Colombier
1600ef9eff0bSDavid du Colombier /* each new SYN restarts the retransmits */
16013ff48bf5SDavid du Colombier lp->irs = seg->seq;
16023ff48bf5SDavid du Colombier break;
16033ff48bf5SDavid du Colombier }
16043ff48bf5SDavid du Colombier lp = *l;
16053ff48bf5SDavid du Colombier if(lp == nil){
16063ff48bf5SDavid du Colombier if(tpriv->nlimbo >= Maxlimbo && tpriv->lht[h]){
16073ff48bf5SDavid du Colombier lp = tpriv->lht[h];
16083ff48bf5SDavid du Colombier tpriv->lht[h] = lp->next;
16093ff48bf5SDavid du Colombier lp->next = nil;
16103ff48bf5SDavid du Colombier } else {
16113ff48bf5SDavid du Colombier lp = malloc(sizeof(*lp));
16123ff48bf5SDavid du Colombier if(lp == nil)
16133ff48bf5SDavid du Colombier return;
16143ff48bf5SDavid du Colombier tpriv->nlimbo++;
16153ff48bf5SDavid du Colombier }
16163ff48bf5SDavid du Colombier *l = lp;
16173ff48bf5SDavid du Colombier lp->version = version;
16183ff48bf5SDavid du Colombier ipmove(lp->laddr, dest);
16193ff48bf5SDavid du Colombier ipmove(lp->raddr, source);
16203ff48bf5SDavid du Colombier lp->lport = seg->dest;
16213ff48bf5SDavid du Colombier lp->rport = seg->source;
16223ff48bf5SDavid du Colombier lp->mss = seg->mss;
16233f695129SDavid du Colombier lp->rcvscale = seg->ws;
16243ff48bf5SDavid du Colombier lp->irs = seg->seq;
16253ff48bf5SDavid du Colombier lp->iss = (nrand(1<<16)<<16)|nrand(1<<16);
16263ff48bf5SDavid du Colombier }
16273ff48bf5SDavid du Colombier
16283ff48bf5SDavid du Colombier if(sndsynack(s->p, lp) < 0){
16293ff48bf5SDavid du Colombier *l = lp->next;
16303ff48bf5SDavid du Colombier tpriv->nlimbo--;
16313ff48bf5SDavid du Colombier free(lp);
16323ff48bf5SDavid du Colombier }
16333ff48bf5SDavid du Colombier }
16343ff48bf5SDavid du Colombier
16353ff48bf5SDavid du Colombier /*
16363ff48bf5SDavid du Colombier * resend SYN ACK's once every SYNACK_RXTIMER ms.
16373ff48bf5SDavid du Colombier */
16383ff48bf5SDavid du Colombier static void
limborexmit(Proto * tcp)16393ff48bf5SDavid du Colombier limborexmit(Proto *tcp)
16403ff48bf5SDavid du Colombier {
16413ff48bf5SDavid du Colombier Tcppriv *tpriv;
16423ff48bf5SDavid du Colombier Limbo **l, *lp;
16433ff48bf5SDavid du Colombier int h;
16443ff48bf5SDavid du Colombier int seen;
16453ff48bf5SDavid du Colombier ulong now;
16463ff48bf5SDavid du Colombier
16473ff48bf5SDavid du Colombier tpriv = tcp->priv;
16483ff48bf5SDavid du Colombier
16493ff48bf5SDavid du Colombier if(!canqlock(tcp))
16503ff48bf5SDavid du Colombier return;
16513ff48bf5SDavid du Colombier seen = 0;
16523ff48bf5SDavid du Colombier now = NOW;
16533ff48bf5SDavid du Colombier for(h = 0; h < NLHT && seen < tpriv->nlimbo; h++){
16543ff48bf5SDavid du Colombier for(l = &tpriv->lht[h]; *l != nil && seen < tpriv->nlimbo; ){
16553ff48bf5SDavid du Colombier lp = *l;
16563ff48bf5SDavid du Colombier seen++;
16573ff48bf5SDavid du Colombier if(now - lp->lastsend < (lp->rexmits+1)*SYNACK_RXTIMER)
16583ff48bf5SDavid du Colombier continue;
16593ff48bf5SDavid du Colombier
16603ff48bf5SDavid du Colombier /* time it out after 1 second */
16613ff48bf5SDavid du Colombier if(++(lp->rexmits) > 5){
16623ff48bf5SDavid du Colombier tpriv->nlimbo--;
16633ff48bf5SDavid du Colombier *l = lp->next;
16643ff48bf5SDavid du Colombier free(lp);
16653ff48bf5SDavid du Colombier continue;
16663ff48bf5SDavid du Colombier }
16673ff48bf5SDavid du Colombier
16683ff48bf5SDavid du Colombier /* if we're being attacked, don't bother resending SYN ACK's */
16693ff48bf5SDavid du Colombier if(tpriv->nlimbo > 100)
16703ff48bf5SDavid du Colombier continue;
16713ff48bf5SDavid du Colombier
16723ff48bf5SDavid du Colombier if(sndsynack(tcp, lp) < 0){
16733ff48bf5SDavid du Colombier tpriv->nlimbo--;
16743ff48bf5SDavid du Colombier *l = lp->next;
16753ff48bf5SDavid du Colombier free(lp);
16763ff48bf5SDavid du Colombier continue;
16773ff48bf5SDavid du Colombier }
16783ff48bf5SDavid du Colombier
16793ff48bf5SDavid du Colombier l = &lp->next;
16803ff48bf5SDavid du Colombier }
16813ff48bf5SDavid du Colombier }
16823ff48bf5SDavid du Colombier qunlock(tcp);
16833ff48bf5SDavid du Colombier }
16843ff48bf5SDavid du Colombier
16853ff48bf5SDavid du Colombier /*
16863ff48bf5SDavid du Colombier * lookup call in limbo. if found, throw it out.
16873ff48bf5SDavid du Colombier *
16883ff48bf5SDavid du Colombier * called with proto locked
16893ff48bf5SDavid du Colombier */
16903ff48bf5SDavid du Colombier static void
limborst(Conv * s,Tcp * segp,uchar * src,uchar * dst,uchar version)16913ff48bf5SDavid du Colombier limborst(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
16923ff48bf5SDavid du Colombier {
16933ff48bf5SDavid du Colombier Limbo *lp, **l;
16943ff48bf5SDavid du Colombier int h;
16953ff48bf5SDavid du Colombier Tcppriv *tpriv;
16963ff48bf5SDavid du Colombier
16973ff48bf5SDavid du Colombier tpriv = s->p->priv;
16983ff48bf5SDavid du Colombier
16993ff48bf5SDavid du Colombier /* find a call in limbo */
17003ff48bf5SDavid du Colombier h = hashipa(src, segp->source);
17013ff48bf5SDavid du Colombier for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){
17023ff48bf5SDavid du Colombier lp = *l;
17033ff48bf5SDavid du Colombier if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version)
17043ff48bf5SDavid du Colombier continue;
17053ff48bf5SDavid du Colombier if(ipcmp(lp->laddr, dst) != 0)
17063ff48bf5SDavid du Colombier continue;
17073ff48bf5SDavid du Colombier if(ipcmp(lp->raddr, src) != 0)
17083ff48bf5SDavid du Colombier continue;
17093ff48bf5SDavid du Colombier
17103ff48bf5SDavid du Colombier /* RST can only follow the SYN */
17113ff48bf5SDavid du Colombier if(segp->seq == lp->irs+1){
17123ff48bf5SDavid du Colombier tpriv->nlimbo--;
17133ff48bf5SDavid du Colombier *l = lp->next;
17143ff48bf5SDavid du Colombier free(lp);
17153ff48bf5SDavid du Colombier }
17163ff48bf5SDavid du Colombier break;
17173ff48bf5SDavid du Colombier }
17183ff48bf5SDavid du Colombier }
17193ff48bf5SDavid du Colombier
17201ce6c95fSDavid du Colombier static void
initialwindow(Tcpctl * tcb)17211ce6c95fSDavid du Colombier initialwindow(Tcpctl *tcb)
17221ce6c95fSDavid du Colombier {
17231ce6c95fSDavid du Colombier /* RFC 3390 initial window */
17241ce6c95fSDavid du Colombier if(tcb->mss < 1095)
17251ce6c95fSDavid du Colombier tcb->cwind = 4*tcb->mss;
17261ce6c95fSDavid du Colombier else if(tcb->mss < 2190)
17271ce6c95fSDavid du Colombier tcb->cwind = 2*2190;
17281ce6c95fSDavid du Colombier else
17291ce6c95fSDavid du Colombier tcb->cwind = 2*tcb->mss;
17301ce6c95fSDavid du Colombier }
17311ce6c95fSDavid du Colombier
17323ff48bf5SDavid du Colombier /*
17333f695129SDavid du Colombier * come here when we finally get an ACK to our SYN-ACK.
17343ff48bf5SDavid du Colombier * lookup call in limbo. if found, create a new conversation
17353ff48bf5SDavid du Colombier *
17363ff48bf5SDavid du Colombier * called with proto locked
17373ff48bf5SDavid du Colombier */
17383ff48bf5SDavid du Colombier static Conv*
tcpincoming(Conv * s,Tcp * segp,uchar * src,uchar * dst,uchar version)17393ff48bf5SDavid du Colombier tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
17407dd7cddfSDavid du Colombier {
17417dd7cddfSDavid du Colombier Conv *new;
17427dd7cddfSDavid du Colombier Tcpctl *tcb;
174380ee5cbfSDavid du Colombier Tcppriv *tpriv;
17443ff48bf5SDavid du Colombier Tcp4hdr *h4;
17453ff48bf5SDavid du Colombier Tcp6hdr *h6;
17463ff48bf5SDavid du Colombier Limbo *lp, **l;
17473ff48bf5SDavid du Colombier int h;
17487dd7cddfSDavid du Colombier
17493ff48bf5SDavid du Colombier /* unless it's just an ack, it can't be someone coming out of limbo */
17503ff48bf5SDavid du Colombier if((segp->flags & SYN) || (segp->flags & ACK) == 0)
17513ff48bf5SDavid du Colombier return nil;
17523ff48bf5SDavid du Colombier
17533ff48bf5SDavid du Colombier tpriv = s->p->priv;
17543ff48bf5SDavid du Colombier
17553ff48bf5SDavid du Colombier /* find a call in limbo */
17563ff48bf5SDavid du Colombier h = hashipa(src, segp->source);
17573ff48bf5SDavid du Colombier for(l = &tpriv->lht[h]; (lp = *l) != nil; l = &lp->next){
17587133e0eeSDavid du Colombier netlog(s->p->f, Logtcp, "tcpincoming s %I!%ud/%I!%ud d %I!%ud/%I!%ud v %d/%d\n",
17593ff48bf5SDavid du Colombier src, segp->source, lp->raddr, lp->rport,
17603ff48bf5SDavid du Colombier dst, segp->dest, lp->laddr, lp->lport,
17613ff48bf5SDavid du Colombier version, lp->version
17623ff48bf5SDavid du Colombier );
17633ff48bf5SDavid du Colombier
17643ff48bf5SDavid du Colombier if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version)
17653ff48bf5SDavid du Colombier continue;
17663ff48bf5SDavid du Colombier if(ipcmp(lp->laddr, dst) != 0)
17673ff48bf5SDavid du Colombier continue;
17683ff48bf5SDavid du Colombier if(ipcmp(lp->raddr, src) != 0)
17693ff48bf5SDavid du Colombier continue;
17703ff48bf5SDavid du Colombier
17713ff48bf5SDavid du Colombier /* we're assuming no data with the initial SYN */
17723ff48bf5SDavid du Colombier if(segp->seq != lp->irs+1 || segp->ack != lp->iss+1){
177384363d68SDavid du Colombier netlog(s->p->f, Logtcp, "tcpincoming s %lux/%lux a %lux %lux\n",
17743ff48bf5SDavid du Colombier segp->seq, lp->irs+1, segp->ack, lp->iss+1);
17753ff48bf5SDavid du Colombier lp = nil;
17763ff48bf5SDavid du Colombier } else {
17773ff48bf5SDavid du Colombier tpriv->nlimbo--;
17783ff48bf5SDavid du Colombier *l = lp->next;
17793ff48bf5SDavid du Colombier }
17803ff48bf5SDavid du Colombier break;
17813ff48bf5SDavid du Colombier }
17823ff48bf5SDavid du Colombier if(lp == nil)
17833ff48bf5SDavid du Colombier return nil;
17843ff48bf5SDavid du Colombier
17853ff48bf5SDavid du Colombier new = Fsnewcall(s, src, segp->source, dst, segp->dest, version);
17867dd7cddfSDavid du Colombier if(new == nil)
17877dd7cddfSDavid du Colombier return nil;
17887dd7cddfSDavid du Colombier
17897dd7cddfSDavid du Colombier memmove(new->ptcl, s->ptcl, sizeof(Tcpctl));
17907dd7cddfSDavid du Colombier tcb = (Tcpctl*)new->ptcl;
17917dd7cddfSDavid du Colombier tcb->flags &= ~CLONE;
17927dd7cddfSDavid du Colombier tcb->timer.arg = new;
17939a747e4fSDavid du Colombier tcb->timer.state = TcptimerOFF;
17947dd7cddfSDavid du Colombier tcb->acktimer.arg = new;
17959a747e4fSDavid du Colombier tcb->acktimer.state = TcptimerOFF;
17967dd7cddfSDavid du Colombier tcb->katimer.arg = new;
17979a747e4fSDavid du Colombier tcb->katimer.state = TcptimerOFF;
17987dd7cddfSDavid du Colombier tcb->rtt_timer.arg = new;
17999a747e4fSDavid du Colombier tcb->rtt_timer.state = TcptimerOFF;
18007dd7cddfSDavid du Colombier
18013ff48bf5SDavid du Colombier tcb->irs = lp->irs;
18023ff48bf5SDavid du Colombier tcb->rcv.nxt = tcb->irs+1;
18031ce6c95fSDavid du Colombier tcb->rcv.wptr = tcb->rcv.nxt;
18041ce6c95fSDavid du Colombier tcb->rcv.wsnt = 0;
18053ff48bf5SDavid du Colombier tcb->rcv.urg = tcb->rcv.nxt;
18067dd7cddfSDavid du Colombier
18073ff48bf5SDavid du Colombier tcb->iss = lp->iss;
18083ff48bf5SDavid du Colombier tcb->rttseq = tcb->iss;
18093ff48bf5SDavid du Colombier tcb->snd.wl2 = tcb->iss;
18103ff48bf5SDavid du Colombier tcb->snd.una = tcb->iss+1;
18113ff48bf5SDavid du Colombier tcb->snd.ptr = tcb->iss+1;
18123ff48bf5SDavid du Colombier tcb->snd.nxt = tcb->iss+1;
18131ce6c95fSDavid du Colombier tcb->snd.rxt = tcb->iss+1;
18143ff48bf5SDavid du Colombier tcb->flgcnt = 0;
18153ff48bf5SDavid du Colombier tcb->flags |= SYNACK;
18163ff48bf5SDavid du Colombier
18176d6d0683SDavid du Colombier /* set desired mss and scale */
18186d6d0683SDavid du Colombier tcb->mss = tcpmtu(s->p, dst, s->ipversion, &tcb->scale);
18196d6d0683SDavid du Colombier
18203ff48bf5SDavid du Colombier /* our sending max segment size cannot be bigger than what he asked for */
18216d6d0683SDavid du Colombier if(lp->mss != 0 && lp->mss < tcb->mss)
18223ff48bf5SDavid du Colombier tcb->mss = lp->mss;
18237ec5746aSDavid du Colombier tpriv->stats[Mss] = tcb->mss;
18243ff48bf5SDavid du Colombier
18253f695129SDavid du Colombier /* window scaling */
18263f695129SDavid du Colombier tcpsetscale(new, tcb, lp->rcvscale, lp->sndscale);
18273f695129SDavid du Colombier
18281ce6c95fSDavid du Colombier /* congestion window */
18293ff48bf5SDavid du Colombier tcb->snd.wnd = segp->wnd;
18301ce6c95fSDavid du Colombier initialwindow(tcb);
18313ff48bf5SDavid du Colombier
18323ff48bf5SDavid du Colombier /* set initial round trip time */
18333ff48bf5SDavid du Colombier tcb->sndsyntime = lp->lastsend+lp->rexmits*SYNACK_RXTIMER;
18343ff48bf5SDavid du Colombier tcpsynackrtt(new);
18353ff48bf5SDavid du Colombier
18363ff48bf5SDavid du Colombier free(lp);
18373ff48bf5SDavid du Colombier
18383ff48bf5SDavid du Colombier /* set up proto header */
18393ff48bf5SDavid du Colombier switch(version){
18403ff48bf5SDavid du Colombier case V4:
18413ff48bf5SDavid du Colombier h4 = &tcb->protohdr.tcp4hdr;
18423ff48bf5SDavid du Colombier memset(h4, 0, sizeof(*h4));
18433ff48bf5SDavid du Colombier h4->proto = IP_TCPPROTO;
18443ff48bf5SDavid du Colombier hnputs(h4->tcpsport, new->lport);
18453ff48bf5SDavid du Colombier hnputs(h4->tcpdport, new->rport);
18463ff48bf5SDavid du Colombier v6tov4(h4->tcpsrc, dst);
18473ff48bf5SDavid du Colombier v6tov4(h4->tcpdst, src);
18483ff48bf5SDavid du Colombier break;
18493ff48bf5SDavid du Colombier case V6:
18503ff48bf5SDavid du Colombier h6 = &tcb->protohdr.tcp6hdr;
18513ff48bf5SDavid du Colombier memset(h6, 0, sizeof(*h6));
18523ff48bf5SDavid du Colombier h6->proto = IP_TCPPROTO;
18533ff48bf5SDavid du Colombier hnputs(h6->tcpsport, new->lport);
18543ff48bf5SDavid du Colombier hnputs(h6->tcpdport, new->rport);
18553ff48bf5SDavid du Colombier ipmove(h6->tcpsrc, dst);
18563ff48bf5SDavid du Colombier ipmove(h6->tcpdst, src);
18573ff48bf5SDavid du Colombier break;
18583ff48bf5SDavid du Colombier default:
18593ff48bf5SDavid du Colombier panic("tcpincoming: version %d", new->ipversion);
18603ff48bf5SDavid du Colombier }
18613ff48bf5SDavid du Colombier
18623ff48bf5SDavid du Colombier tcpsetstate(new, Established);
18633ff48bf5SDavid du Colombier
186480ee5cbfSDavid du Colombier iphtadd(&tpriv->ht, new);
186580ee5cbfSDavid du Colombier
18667dd7cddfSDavid du Colombier return new;
18677dd7cddfSDavid du Colombier }
18687dd7cddfSDavid du Colombier
18691ce6c95fSDavid du Colombier static int
seq_within(ulong x,ulong low,ulong high)18707dd7cddfSDavid du Colombier seq_within(ulong x, ulong low, ulong high)
18717dd7cddfSDavid du Colombier {
18727dd7cddfSDavid du Colombier if(low <= high){
18737dd7cddfSDavid du Colombier if(low <= x && x <= high)
18747dd7cddfSDavid du Colombier return 1;
18757dd7cddfSDavid du Colombier }
18767dd7cddfSDavid du Colombier else {
18777dd7cddfSDavid du Colombier if(x >= low || x <= high)
18787dd7cddfSDavid du Colombier return 1;
18797dd7cddfSDavid du Colombier }
18807dd7cddfSDavid du Colombier return 0;
18817dd7cddfSDavid du Colombier }
18827dd7cddfSDavid du Colombier
18831ce6c95fSDavid du Colombier static int
seq_lt(ulong x,ulong y)18847dd7cddfSDavid du Colombier seq_lt(ulong x, ulong y)
18857dd7cddfSDavid du Colombier {
18867dd7cddfSDavid du Colombier return (int)(x-y) < 0;
18877dd7cddfSDavid du Colombier }
18887dd7cddfSDavid du Colombier
18891ce6c95fSDavid du Colombier static int
seq_le(ulong x,ulong y)18907dd7cddfSDavid du Colombier seq_le(ulong x, ulong y)
18917dd7cddfSDavid du Colombier {
18927dd7cddfSDavid du Colombier return (int)(x-y) <= 0;
18937dd7cddfSDavid du Colombier }
18947dd7cddfSDavid du Colombier
18951ce6c95fSDavid du Colombier static int
seq_gt(ulong x,ulong y)18967dd7cddfSDavid du Colombier seq_gt(ulong x, ulong y)
18977dd7cddfSDavid du Colombier {
18987dd7cddfSDavid du Colombier return (int)(x-y) > 0;
18997dd7cddfSDavid du Colombier }
19007dd7cddfSDavid du Colombier
19011ce6c95fSDavid du Colombier static int
seq_ge(ulong x,ulong y)19027dd7cddfSDavid du Colombier seq_ge(ulong x, ulong y)
19037dd7cddfSDavid du Colombier {
19047dd7cddfSDavid du Colombier return (int)(x-y) >= 0;
19057dd7cddfSDavid du Colombier }
19067dd7cddfSDavid du Colombier
19077dd7cddfSDavid du Colombier /*
19087dd7cddfSDavid du Colombier * use the time between the first SYN and it's ack as the
19097dd7cddfSDavid du Colombier * initial round trip time
19107dd7cddfSDavid du Colombier */
19111ce6c95fSDavid du Colombier static void
tcpsynackrtt(Conv * s)19127dd7cddfSDavid du Colombier tcpsynackrtt(Conv *s)
19137dd7cddfSDavid du Colombier {
19147dd7cddfSDavid du Colombier Tcpctl *tcb;
19157dd7cddfSDavid du Colombier int delta;
19167dd7cddfSDavid du Colombier Tcppriv *tpriv;
19177dd7cddfSDavid du Colombier
19187dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
19197dd7cddfSDavid du Colombier tpriv = s->p->priv;
19207dd7cddfSDavid du Colombier
19213ff48bf5SDavid du Colombier delta = NOW - tcb->sndsyntime;
19227dd7cddfSDavid du Colombier tcb->srtt = delta<<LOGAGAIN;
19237dd7cddfSDavid du Colombier tcb->mdev = delta<<LOGDGAIN;
19247dd7cddfSDavid du Colombier
19257dd7cddfSDavid du Colombier /* halt round trip timer */
19267dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
19277dd7cddfSDavid du Colombier }
19287dd7cddfSDavid du Colombier
19291ce6c95fSDavid du Colombier static void
update(Conv * s,Tcp * seg)19307dd7cddfSDavid du Colombier update(Conv *s, Tcp *seg)
19317dd7cddfSDavid du Colombier {
19327dd7cddfSDavid du Colombier int rtt, delta;
19337dd7cddfSDavid du Colombier Tcpctl *tcb;
19343f695129SDavid du Colombier ulong acked;
19357dd7cddfSDavid du Colombier Tcppriv *tpriv;
19367dd7cddfSDavid du Colombier
19371ce6c95fSDavid du Colombier if(seg->update)
19381ce6c95fSDavid du Colombier return;
19391ce6c95fSDavid du Colombier seg->update = 1;
19401ce6c95fSDavid du Colombier
19417dd7cddfSDavid du Colombier tpriv = s->p->priv;
19427dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
19437dd7cddfSDavid du Colombier
19441ce6c95fSDavid du Colombier /* catch zero-window updates, update window & recover */
19451ce6c95fSDavid du Colombier if(tcb->snd.wnd == 0 && seg->wnd > 0 &&
19461ce6c95fSDavid du Colombier seq_lt(seg->ack, tcb->snd.ptr)){
19471ce6c95fSDavid du Colombier netlog(s->p->f, Logtcp, "tcp: zwu ack %lud una %lud ptr %lud win %lud\n",
19481ce6c95fSDavid du Colombier seg->ack, tcb->snd.una, tcb->snd.ptr, seg->wnd);
19491ce6c95fSDavid du Colombier tcb->snd.wnd = seg->wnd;
19501ce6c95fSDavid du Colombier goto recovery;
19517dd7cddfSDavid du Colombier }
19527dd7cddfSDavid du Colombier
19531ce6c95fSDavid du Colombier /* newreno fast retransmit */
19541ce6c95fSDavid du Colombier if(seg->ack == tcb->snd.una && tcb->snd.una != tcb->snd.nxt &&
19551ce6c95fSDavid du Colombier ++tcb->snd.dupacks == 3){ /* was TCPREXMTTHRESH */
19561ce6c95fSDavid du Colombier recovery:
19571ce6c95fSDavid du Colombier if(tcb->snd.recovery){
19581ce6c95fSDavid du Colombier tpriv->stats[RecoveryCwind]++;
19591ce6c95fSDavid du Colombier tcb->cwind += tcb->mss;
19601ce6c95fSDavid du Colombier }else if(seq_le(tcb->snd.rxt, seg->ack)){
19611ce6c95fSDavid du Colombier tpriv->stats[Recovery]++;
19621ce6c95fSDavid du Colombier tcb->abcbytes = 0;
19637dd7cddfSDavid du Colombier tcb->snd.recovery = 1;
19641ce6c95fSDavid du Colombier tcb->snd.partialack = 0;
19657dd7cddfSDavid du Colombier tcb->snd.rxt = tcb->snd.nxt;
19661ce6c95fSDavid du Colombier tcpcongestion(tcb);
19671ce6c95fSDavid du Colombier tcb->cwind = tcb->ssthresh + 3*tcb->mss;
19681ce6c95fSDavid du Colombier netlog(s->p->f, Logtcpwin, "recovery inflate %ld ss %ld @%lud\n",
19691ce6c95fSDavid du Colombier tcb->cwind, tcb->ssthresh, tcb->snd.rxt);
19707dd7cddfSDavid du Colombier tcprxmit(s);
19717dd7cddfSDavid du Colombier }else{
19721ce6c95fSDavid du Colombier tpriv->stats[RecoveryNoSeq]++;
19731ce6c95fSDavid du Colombier netlog(s->p->f, Logtcpwin, "!recov %lud not ≤ %lud %ld\n",
19741ce6c95fSDavid du Colombier tcb->snd.rxt, seg->ack, tcb->snd.rxt - seg->ack);
19751ce6c95fSDavid du Colombier /* don't enter fast retransmit, don't change ssthresh */
19767dd7cddfSDavid du Colombier }
19771ce6c95fSDavid du Colombier }else if(tcb->snd.recovery){
19781ce6c95fSDavid du Colombier tpriv->stats[RecoveryCwind]++;
19791ce6c95fSDavid du Colombier tcb->cwind += tcb->mss;
19807dd7cddfSDavid du Colombier }
19817dd7cddfSDavid du Colombier
198280ee5cbfSDavid du Colombier /*
198380ee5cbfSDavid du Colombier * update window
198480ee5cbfSDavid du Colombier */
198580ee5cbfSDavid du Colombier if(seq_gt(seg->ack, tcb->snd.wl2)
198680ee5cbfSDavid du Colombier || (tcb->snd.wl2 == seg->ack && seg->wnd > tcb->snd.wnd)){
19871ce6c95fSDavid du Colombier /* clear dupack if we advance wl2 */
19881ce6c95fSDavid du Colombier if(tcb->snd.wl2 != seg->ack)
19891ce6c95fSDavid du Colombier tcb->snd.dupacks = 0;
19907dd7cddfSDavid du Colombier tcb->snd.wnd = seg->wnd;
19917dd7cddfSDavid du Colombier tcb->snd.wl2 = seg->ack;
19927dd7cddfSDavid du Colombier }
19937dd7cddfSDavid du Colombier
199480ee5cbfSDavid du Colombier if(!seq_gt(seg->ack, tcb->snd.una)){
199580ee5cbfSDavid du Colombier /*
199680ee5cbfSDavid du Colombier * don't let us hangup if sending into a closed window and
199780ee5cbfSDavid du Colombier * we're still getting acks
199880ee5cbfSDavid du Colombier */
19991ce6c95fSDavid du Colombier if((tcb->flags&RETRAN) && tcb->snd.wnd == 0)
200080ee5cbfSDavid du Colombier tcb->backedoff = MAXBACKMS/4;
20017dd7cddfSDavid du Colombier return;
200280ee5cbfSDavid du Colombier }
20037dd7cddfSDavid du Colombier
20047dd7cddfSDavid du Colombier /* Compute the new send window size */
20057dd7cddfSDavid du Colombier acked = seg->ack - tcb->snd.una;
20067dd7cddfSDavid du Colombier
20077dd7cddfSDavid du Colombier /* avoid slow start and timers for SYN acks */
20087dd7cddfSDavid du Colombier if((tcb->flags & SYNACK) == 0) {
20097dd7cddfSDavid du Colombier tcb->flags |= SYNACK;
20107dd7cddfSDavid du Colombier acked--;
201180ee5cbfSDavid du Colombier tcb->flgcnt--;
20127dd7cddfSDavid du Colombier goto done;
20137dd7cddfSDavid du Colombier }
20147dd7cddfSDavid du Colombier
20151ce6c95fSDavid du Colombier /*
20161ce6c95fSDavid du Colombier * congestion control
20171ce6c95fSDavid du Colombier */
20181ce6c95fSDavid du Colombier if(tcb->snd.recovery){
20191ce6c95fSDavid du Colombier if(seq_ge(seg->ack, tcb->snd.rxt)){
20201ce6c95fSDavid du Colombier /* recovery finished; deflate window */
20211ce6c95fSDavid du Colombier tpriv->stats[RecoveryDone]++;
20221ce6c95fSDavid du Colombier tcb->snd.dupacks = 0;
20231ce6c95fSDavid du Colombier tcb->snd.recovery = 0;
20241ce6c95fSDavid du Colombier tcb->cwind = (tcb->snd.nxt - tcb->snd.una) + tcb->mss;
20251ce6c95fSDavid du Colombier if(tcb->ssthresh < tcb->cwind)
20261ce6c95fSDavid du Colombier tcb->cwind = tcb->ssthresh;
20271ce6c95fSDavid du Colombier netlog(s->p->f, Logtcpwin, "recovery deflate %ld %ld\n",
20281ce6c95fSDavid du Colombier tcb->cwind, tcb->ssthresh);
20291ce6c95fSDavid du Colombier } else {
20301ce6c95fSDavid du Colombier /* partial ack; we lost more than one segment */
20311ce6c95fSDavid du Colombier tpriv->stats[RecoveryPA]++;
20321ce6c95fSDavid du Colombier if(tcb->cwind > acked)
20331ce6c95fSDavid du Colombier tcb->cwind -= acked;
20341ce6c95fSDavid du Colombier else{
20351ce6c95fSDavid du Colombier netlog(s->p->f, Logtcpwin, "partial ack neg\n");
20361ce6c95fSDavid du Colombier tcb->cwind = tcb->mss;
20377dd7cddfSDavid du Colombier }
20381ce6c95fSDavid du Colombier netlog(s->p->f, Logtcpwin, "partial ack %ld left %ld cwind %ld\n",
20391ce6c95fSDavid du Colombier acked, tcb->snd.rxt - seg->ack, tcb->cwind);
20407dd7cddfSDavid du Colombier
20411ce6c95fSDavid du Colombier if(acked >= tcb->mss)
20421ce6c95fSDavid du Colombier tcb->cwind += tcb->mss;
20431ce6c95fSDavid du Colombier tcb->snd.partialack++;
20447dd7cddfSDavid du Colombier }
20451ce6c95fSDavid du Colombier } else
20461ce6c95fSDavid du Colombier tcpabcincr(tcb, acked);
20477dd7cddfSDavid du Colombier
20487dd7cddfSDavid du Colombier /* Adjust the timers according to the round trip time */
20491ce6c95fSDavid du Colombier /* TODO: fix sloppy treatment of overflow cases here. */
20509a747e4fSDavid du Colombier if(tcb->rtt_timer.state == TcptimerON && seq_ge(seg->ack, tcb->rttseq)) {
20517dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
20527dd7cddfSDavid du Colombier if((tcb->flags&RETRAN) == 0) {
20537dd7cddfSDavid du Colombier tcb->backoff = 0;
20547dd7cddfSDavid du Colombier tcb->backedoff = 0;
20557dd7cddfSDavid du Colombier rtt = tcb->rtt_timer.start - tcb->rtt_timer.count;
20567dd7cddfSDavid du Colombier if(rtt == 0)
20571ce6c95fSDavid du Colombier rtt = 1; /* else all close sys's will rexmit in 0 time */
20587dd7cddfSDavid du Colombier rtt *= MSPTICK;
20597dd7cddfSDavid du Colombier if(tcb->srtt == 0) {
20607dd7cddfSDavid du Colombier tcb->srtt = rtt << LOGAGAIN;
20617dd7cddfSDavid du Colombier tcb->mdev = rtt << LOGDGAIN;
20627dd7cddfSDavid du Colombier } else {
20637dd7cddfSDavid du Colombier delta = rtt - (tcb->srtt>>LOGAGAIN);
20647dd7cddfSDavid du Colombier tcb->srtt += delta;
20657dd7cddfSDavid du Colombier if(tcb->srtt <= 0)
20667dd7cddfSDavid du Colombier tcb->srtt = 1;
20677dd7cddfSDavid du Colombier
20687dd7cddfSDavid du Colombier delta = abs(delta) - (tcb->mdev>>LOGDGAIN);
20697dd7cddfSDavid du Colombier tcb->mdev += delta;
20707dd7cddfSDavid du Colombier if(tcb->mdev <= 0)
20717dd7cddfSDavid du Colombier tcb->mdev = 1;
20727dd7cddfSDavid du Colombier }
20739a747e4fSDavid du Colombier tcpsettimer(tcb);
20747dd7cddfSDavid du Colombier }
20757dd7cddfSDavid du Colombier }
20767dd7cddfSDavid du Colombier
20777dd7cddfSDavid du Colombier done:
207880ee5cbfSDavid du Colombier if(qdiscard(s->wq, acked) < acked)
207980ee5cbfSDavid du Colombier tcb->flgcnt--;
20807dd7cddfSDavid du Colombier tcb->snd.una = seg->ack;
20811ce6c95fSDavid du Colombier
20821ce6c95fSDavid du Colombier /* newreno fast recovery */
20831ce6c95fSDavid du Colombier if(tcb->snd.recovery)
20841ce6c95fSDavid du Colombier tcprxmit(s);
20851ce6c95fSDavid du Colombier
20867dd7cddfSDavid du Colombier if(seq_gt(seg->ack, tcb->snd.urg))
20877dd7cddfSDavid du Colombier tcb->snd.urg = seg->ack;
20887dd7cddfSDavid du Colombier
20891ce6c95fSDavid du Colombier if(tcb->snd.una != tcb->snd.nxt){
20901ce6c95fSDavid du Colombier /* `impatient' variant */
20911ce6c95fSDavid du Colombier if(!tcb->snd.recovery || tcb->snd.partialack == 1){
20921ce6c95fSDavid du Colombier tcb->time = NOW;
20931ce6c95fSDavid du Colombier tcb->timeuna = tcb->snd.una;
20947dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->timer);
20951ce6c95fSDavid du Colombier }
20961ce6c95fSDavid du Colombier } else
20973ff48bf5SDavid du Colombier tcphalt(tpriv, &tcb->timer);
20987dd7cddfSDavid du Colombier
20997dd7cddfSDavid du Colombier if(seq_lt(tcb->snd.ptr, tcb->snd.una))
21007dd7cddfSDavid du Colombier tcb->snd.ptr = tcb->snd.una;
21017dd7cddfSDavid du Colombier
21021ce6c95fSDavid du Colombier if(!tcb->snd.recovery)
21037dd7cddfSDavid du Colombier tcb->flags &= ~RETRAN;
21047dd7cddfSDavid du Colombier tcb->backoff = 0;
21057dd7cddfSDavid du Colombier tcb->backedoff = 0;
21067dd7cddfSDavid du Colombier }
21077dd7cddfSDavid du Colombier
21081ce6c95fSDavid du Colombier static void
tcpiput(Proto * tcp,Ipifc *,Block * bp)21099a747e4fSDavid du Colombier tcpiput(Proto *tcp, Ipifc*, Block *bp)
21107dd7cddfSDavid du Colombier {
21117dd7cddfSDavid du Colombier Tcp seg;
21123ff48bf5SDavid du Colombier Tcp4hdr *h4;
21133ff48bf5SDavid du Colombier Tcp6hdr *h6;
21147dd7cddfSDavid du Colombier int hdrlen;
21157dd7cddfSDavid du Colombier Tcpctl *tcb;
211684363d68SDavid du Colombier ushort length, csum;
21177dd7cddfSDavid du Colombier uchar source[IPaddrlen], dest[IPaddrlen];
211880ee5cbfSDavid du Colombier Conv *s;
21197dd7cddfSDavid du Colombier Fs *f;
21207dd7cddfSDavid du Colombier Tcppriv *tpriv;
21213ff48bf5SDavid du Colombier uchar version;
21227dd7cddfSDavid du Colombier
21237dd7cddfSDavid du Colombier f = tcp->f;
21247dd7cddfSDavid du Colombier tpriv = tcp->priv;
21257dd7cddfSDavid du Colombier
212659cc4ca5SDavid du Colombier tpriv->stats[InSegs]++;
21277dd7cddfSDavid du Colombier
21283ff48bf5SDavid du Colombier h4 = (Tcp4hdr*)(bp->rp);
21293ff48bf5SDavid du Colombier h6 = (Tcp6hdr*)(bp->rp);
21307dd7cddfSDavid du Colombier
21313ff48bf5SDavid du Colombier if((h4->vihl&0xF0)==IP_VER4) {
21323ff48bf5SDavid du Colombier version = V4;
21333ff48bf5SDavid du Colombier length = nhgets(h4->length);
21343ff48bf5SDavid du Colombier v4tov6(dest, h4->tcpdst);
21353ff48bf5SDavid du Colombier v4tov6(source, h4->tcpsrc);
21367dd7cddfSDavid du Colombier
21373ff48bf5SDavid du Colombier h4->Unused = 0;
21383ff48bf5SDavid du Colombier hnputs(h4->tcplen, length-TCP4_PKT);
21396b6b9ac8SDavid du Colombier if(!(bp->flag & Btcpck) && (h4->tcpcksum[0] || h4->tcpcksum[1]) &&
21403ff48bf5SDavid du Colombier ptclcsum(bp, TCP4_IPLEN, length-TCP4_IPLEN)) {
214159cc4ca5SDavid du Colombier tpriv->stats[CsumErrs]++;
214259cc4ca5SDavid du Colombier tpriv->stats[InErrs]++;
21437dd7cddfSDavid du Colombier netlog(f, Logtcp, "bad tcp proto cksum\n");
21447dd7cddfSDavid du Colombier freeblist(bp);
21457dd7cddfSDavid du Colombier return;
21467dd7cddfSDavid du Colombier }
21477dd7cddfSDavid du Colombier
21483ff48bf5SDavid du Colombier hdrlen = ntohtcp4(&seg, &bp);
21497dd7cddfSDavid du Colombier if(hdrlen < 0){
215059cc4ca5SDavid du Colombier tpriv->stats[HlenErrs]++;
215159cc4ca5SDavid du Colombier tpriv->stats[InErrs]++;
21527dd7cddfSDavid du Colombier netlog(f, Logtcp, "bad tcp hdr len\n");
21537dd7cddfSDavid du Colombier return;
21547dd7cddfSDavid du Colombier }
21557dd7cddfSDavid du Colombier
21567dd7cddfSDavid du Colombier /* trim the packet to the size claimed by the datagram */
21573ff48bf5SDavid du Colombier length -= hdrlen+TCP4_PKT;
21583ff48bf5SDavid du Colombier bp = trimblock(bp, hdrlen+TCP4_PKT, length);
21597dd7cddfSDavid du Colombier if(bp == nil){
216059cc4ca5SDavid du Colombier tpriv->stats[LenErrs]++;
216159cc4ca5SDavid du Colombier tpriv->stats[InErrs]++;
21627dd7cddfSDavid du Colombier netlog(f, Logtcp, "tcp len < 0 after trim\n");
21637dd7cddfSDavid du Colombier return;
21647dd7cddfSDavid du Colombier }
21653ff48bf5SDavid du Colombier }
21663ff48bf5SDavid du Colombier else {
21673ff48bf5SDavid du Colombier int ttl = h6->ttl;
21683ff48bf5SDavid du Colombier int proto = h6->proto;
21693ff48bf5SDavid du Colombier
21703ff48bf5SDavid du Colombier version = V6;
21713ff48bf5SDavid du Colombier length = nhgets(h6->ploadlen);
21723ff48bf5SDavid du Colombier ipmove(dest, h6->tcpdst);
21733ff48bf5SDavid du Colombier ipmove(source, h6->tcpsrc);
21743ff48bf5SDavid du Colombier
21753ff48bf5SDavid du Colombier h6->ploadlen[0] = h6->ploadlen[1] = h6->proto = 0;
21763ff48bf5SDavid du Colombier h6->ttl = proto;
21773ff48bf5SDavid du Colombier hnputl(h6->vcf, length);
21783ff48bf5SDavid du Colombier if((h6->tcpcksum[0] || h6->tcpcksum[1]) &&
217984363d68SDavid du Colombier (csum = ptclcsum(bp, TCP6_IPLEN, length+TCP6_PHDRSIZE)) != 0) {
21803ff48bf5SDavid du Colombier tpriv->stats[CsumErrs]++;
21813ff48bf5SDavid du Colombier tpriv->stats[InErrs]++;
218284363d68SDavid du Colombier netlog(f, Logtcp,
218384363d68SDavid du Colombier "bad tcpv6 proto cksum: got %#ux, computed %#ux\n",
218484363d68SDavid du Colombier h6->tcpcksum[0]<<8 | h6->tcpcksum[1], csum);
21853ff48bf5SDavid du Colombier freeblist(bp);
21863ff48bf5SDavid du Colombier return;
21873ff48bf5SDavid du Colombier }
21883ff48bf5SDavid du Colombier h6->ttl = ttl;
21893ff48bf5SDavid du Colombier h6->proto = proto;
21903ff48bf5SDavid du Colombier hnputs(h6->ploadlen, length);
21913ff48bf5SDavid du Colombier
21923ff48bf5SDavid du Colombier hdrlen = ntohtcp6(&seg, &bp);
21933ff48bf5SDavid du Colombier if(hdrlen < 0){
21943ff48bf5SDavid du Colombier tpriv->stats[HlenErrs]++;
21953ff48bf5SDavid du Colombier tpriv->stats[InErrs]++;
219684363d68SDavid du Colombier netlog(f, Logtcp, "bad tcpv6 hdr len\n");
21973ff48bf5SDavid du Colombier return;
21983ff48bf5SDavid du Colombier }
21993ff48bf5SDavid du Colombier
22003ff48bf5SDavid du Colombier /* trim the packet to the size claimed by the datagram */
22013ff48bf5SDavid du Colombier length -= hdrlen;
22023ff48bf5SDavid du Colombier bp = trimblock(bp, hdrlen+TCP6_PKT, length);
22033ff48bf5SDavid du Colombier if(bp == nil){
22043ff48bf5SDavid du Colombier tpriv->stats[LenErrs]++;
22053ff48bf5SDavid du Colombier tpriv->stats[InErrs]++;
220684363d68SDavid du Colombier netlog(f, Logtcp, "tcpv6 len < 0 after trim\n");
22073ff48bf5SDavid du Colombier return;
22083ff48bf5SDavid du Colombier }
22093ff48bf5SDavid du Colombier }
22107dd7cddfSDavid du Colombier
22117dd7cddfSDavid du Colombier /* lock protocol while searching for a conversation */
22127dd7cddfSDavid du Colombier qlock(tcp);
22137dd7cddfSDavid du Colombier
221480ee5cbfSDavid du Colombier /* Look for a matching conversation */
221580ee5cbfSDavid du Colombier s = iphtlook(&tpriv->ht, source, seg.source, dest, seg.dest);
221680ee5cbfSDavid du Colombier if(s == nil){
22177133e0eeSDavid du Colombier netlog(f, Logtcp, "iphtlook(src %I!%d, dst %I!%d) failed\n",
22187133e0eeSDavid du Colombier source, seg.source, dest, seg.dest);
221980ee5cbfSDavid du Colombier reset:
22207dd7cddfSDavid du Colombier qunlock(tcp);
22213ff48bf5SDavid du Colombier sndrst(tcp, source, dest, length, &seg, version, "no conversation");
22227dd7cddfSDavid du Colombier freeblist(bp);
22237dd7cddfSDavid du Colombier return;
22247dd7cddfSDavid du Colombier }
222580ee5cbfSDavid du Colombier
222680ee5cbfSDavid du Colombier /* if it's a listener, look for the right flags and get a new conv */
222780ee5cbfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
222880ee5cbfSDavid du Colombier if(tcb->state == Listen){
22297dd7cddfSDavid du Colombier if(seg.flags & RST){
22303ff48bf5SDavid du Colombier limborst(s, &seg, source, dest, version);
22317dd7cddfSDavid du Colombier qunlock(tcp);
22327dd7cddfSDavid du Colombier freeblist(bp);
22337dd7cddfSDavid du Colombier return;
22347dd7cddfSDavid du Colombier }
22357dd7cddfSDavid du Colombier
22363ff48bf5SDavid du Colombier /* if this is a new SYN, put the call into limbo */
22373ff48bf5SDavid du Colombier if((seg.flags & SYN) && (seg.flags & ACK) == 0){
22383ff48bf5SDavid du Colombier limbo(s, source, dest, &seg, version);
22393ff48bf5SDavid du Colombier qunlock(tcp);
22403ff48bf5SDavid du Colombier freeblist(bp);
22413ff48bf5SDavid du Colombier return;
22423ff48bf5SDavid du Colombier }
22433ff48bf5SDavid du Colombier
22443ff48bf5SDavid du Colombier /*
22453ff48bf5SDavid du Colombier * if there's a matching call in limbo, tcpincoming will
22463ff48bf5SDavid du Colombier * return it in state Syn_received
22473ff48bf5SDavid du Colombier */
22483ff48bf5SDavid du Colombier s = tcpincoming(s, &seg, source, dest, version);
224980ee5cbfSDavid du Colombier if(s == nil)
225080ee5cbfSDavid du Colombier goto reset;
22517dd7cddfSDavid du Colombier }
22527dd7cddfSDavid du Colombier
22537dd7cddfSDavid du Colombier /* The rest of the input state machine is run with the control block
22547dd7cddfSDavid du Colombier * locked and implements the state machine directly out of the RFC.
22557dd7cddfSDavid du Colombier * Out-of-band data is ignored - it was always a bad idea.
22567dd7cddfSDavid du Colombier */
22577dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
22587dd7cddfSDavid du Colombier if(waserror()){
22597dd7cddfSDavid du Colombier qunlock(s);
22607dd7cddfSDavid du Colombier nexterror();
22617dd7cddfSDavid du Colombier }
22627dd7cddfSDavid du Colombier qlock(s);
22637dd7cddfSDavid du Colombier qunlock(tcp);
22647dd7cddfSDavid du Colombier
22653f695129SDavid du Colombier /* fix up window */
22663f695129SDavid du Colombier seg.wnd <<= tcb->rcv.scale;
22673f695129SDavid du Colombier
226840ef9009SDavid du Colombier /* every input packet in puts off the keep alive time out */
2269e288d156SDavid du Colombier tcpsetkacounter(tcb);
22707dd7cddfSDavid du Colombier
22717dd7cddfSDavid du Colombier switch(tcb->state) {
22727dd7cddfSDavid du Colombier case Closed:
22733ff48bf5SDavid du Colombier sndrst(tcp, source, dest, length, &seg, version, "sending to Closed");
22747dd7cddfSDavid du Colombier goto raise;
22757dd7cddfSDavid du Colombier case Syn_sent:
22767dd7cddfSDavid du Colombier if(seg.flags & ACK) {
22777dd7cddfSDavid du Colombier if(!seq_within(seg.ack, tcb->iss+1, tcb->snd.nxt)) {
22783ff48bf5SDavid du Colombier sndrst(tcp, source, dest, length, &seg, version,
22793ff48bf5SDavid du Colombier "bad seq in Syn_sent");
22807dd7cddfSDavid du Colombier goto raise;
22817dd7cddfSDavid du Colombier }
22827dd7cddfSDavid du Colombier }
22837dd7cddfSDavid du Colombier if(seg.flags & RST) {
22847dd7cddfSDavid du Colombier if(seg.flags & ACK)
22857dd7cddfSDavid du Colombier localclose(s, Econrefused);
22867dd7cddfSDavid du Colombier goto raise;
22877dd7cddfSDavid du Colombier }
22887dd7cddfSDavid du Colombier
22897dd7cddfSDavid du Colombier if(seg.flags & SYN) {
22907dd7cddfSDavid du Colombier procsyn(s, &seg);
22917dd7cddfSDavid du Colombier if(seg.flags & ACK){
22927dd7cddfSDavid du Colombier update(s, &seg);
22937dd7cddfSDavid du Colombier tcpsynackrtt(s);
22947dd7cddfSDavid du Colombier tcpsetstate(s, Established);
22953f695129SDavid du Colombier tcpsetscale(s, tcb, seg.ws, tcb->scale);
22967dd7cddfSDavid du Colombier }
22977dd7cddfSDavid du Colombier else {
22983ff48bf5SDavid du Colombier tcb->time = NOW;
22993f695129SDavid du Colombier tcpsetstate(s, Syn_received); /* DLP - shouldn't this be a reset? */
23007dd7cddfSDavid du Colombier }
23017dd7cddfSDavid du Colombier
23027dd7cddfSDavid du Colombier if(length != 0 || (seg.flags & FIN))
23037dd7cddfSDavid du Colombier break;
23047dd7cddfSDavid du Colombier
23057dd7cddfSDavid du Colombier freeblist(bp);
23067dd7cddfSDavid du Colombier goto output;
23077dd7cddfSDavid du Colombier }
23087dd7cddfSDavid du Colombier else
23097dd7cddfSDavid du Colombier freeblist(bp);
23107dd7cddfSDavid du Colombier
23117dd7cddfSDavid du Colombier qunlock(s);
23127dd7cddfSDavid du Colombier poperror();
23137dd7cddfSDavid du Colombier return;
23147dd7cddfSDavid du Colombier case Syn_received:
23157dd7cddfSDavid du Colombier /* doesn't matter if it's the correct ack, we're just trying to set timing */
23167dd7cddfSDavid du Colombier if(seg.flags & ACK)
23177dd7cddfSDavid du Colombier tcpsynackrtt(s);
23187dd7cddfSDavid du Colombier break;
23197dd7cddfSDavid du Colombier }
23207dd7cddfSDavid du Colombier
23213ff48bf5SDavid du Colombier /*
23223ff48bf5SDavid du Colombier * One DOS attack is to open connections to us and then forget about them,
23233ff48bf5SDavid du Colombier * thereby tying up a conv at no long term cost to the attacker.
23243ff48bf5SDavid du Colombier * This is an attempt to defeat these stateless DOS attacks. See
23253ff48bf5SDavid du Colombier * corresponding code in tcpsendka().
23263ff48bf5SDavid du Colombier */
23273ff48bf5SDavid du Colombier if(tcb->state != Syn_received && (seg.flags & RST) == 0){
23283ff48bf5SDavid du Colombier if(tcpporthogdefense
23293ff48bf5SDavid du Colombier && seq_within(seg.ack, tcb->snd.una-(1<<31), tcb->snd.una-(1<<29))){
23303ff48bf5SDavid du Colombier print("stateless hog %I.%d->%I.%d f %ux %lux - %lux - %lux\n",
23313ff48bf5SDavid du Colombier source, seg.source, dest, seg.dest, seg.flags,
23323ff48bf5SDavid du Colombier tcb->snd.una-(1<<31), seg.ack, tcb->snd.una-(1<<29));
23333ff48bf5SDavid du Colombier localclose(s, "stateless hog");
23343ff48bf5SDavid du Colombier }
23353ff48bf5SDavid du Colombier }
23363ff48bf5SDavid du Colombier
23377dd7cddfSDavid du Colombier /* Cut the data to fit the receive window */
23381ce6c95fSDavid du Colombier tcprcvwin(s);
23397dd7cddfSDavid du Colombier if(tcptrim(tcb, &seg, &bp, &length) == -1) {
23401ce6c95fSDavid du Colombier if(seg.seq+1 != tcb->rcv.nxt || length != 1)
23411ce6c95fSDavid du Colombier netlog(f, Logtcp, "tcp: trim: !inwind: seq %lud-%lud win "
23421ce6c95fSDavid du Colombier "%lud-%lud l %d from %I\n", seg.seq,
23431ce6c95fSDavid du Colombier seg.seq + length - 1, tcb->rcv.nxt,
23441ce6c95fSDavid du Colombier tcb->rcv.nxt + tcb->rcv.wnd-1, length, s->raddr);
23457dd7cddfSDavid du Colombier update(s, &seg);
234680ee5cbfSDavid du Colombier if(qlen(s->wq)+tcb->flgcnt == 0 && tcb->state == Closing) {
23477dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
23487dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
23497dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->katimer);
23507dd7cddfSDavid du Colombier tcpsetstate(s, Time_wait);
23517dd7cddfSDavid du Colombier tcb->timer.start = MSL2*(1000 / MSPTICK);
23527dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->timer);
23537dd7cddfSDavid du Colombier }
23547dd7cddfSDavid du Colombier if(!(seg.flags & RST)) {
23557dd7cddfSDavid du Colombier tcb->flags |= FORCE;
23567dd7cddfSDavid du Colombier goto output;
23577dd7cddfSDavid du Colombier }
23587dd7cddfSDavid du Colombier qunlock(s);
23597dd7cddfSDavid du Colombier poperror();
23607dd7cddfSDavid du Colombier return;
23617dd7cddfSDavid du Colombier }
23627dd7cddfSDavid du Colombier
23637dd7cddfSDavid du Colombier /* Cannot accept so answer with a rst */
23647dd7cddfSDavid du Colombier if(length && tcb->state == Closed) {
23653ff48bf5SDavid du Colombier sndrst(tcp, source, dest, length, &seg, version, "sending to Closed");
23667dd7cddfSDavid du Colombier goto raise;
23677dd7cddfSDavid du Colombier }
23687dd7cddfSDavid du Colombier
23697dd7cddfSDavid du Colombier /* The segment is beyond the current receive pointer so
23707dd7cddfSDavid du Colombier * queue the data in the resequence queue
23717dd7cddfSDavid du Colombier */
23727dd7cddfSDavid du Colombier if(seg.seq != tcb->rcv.nxt)
23737dd7cddfSDavid du Colombier if(length != 0 || (seg.flags & (SYN|FIN))) {
23747dd7cddfSDavid du Colombier update(s, &seg);
23751ce6c95fSDavid du Colombier if(addreseq(f, tcb, tpriv, &seg, bp, length) < 0)
23761ce6c95fSDavid du Colombier print("reseq %I.%d -> %I.%d\n", s->raddr, s->rport,
23771ce6c95fSDavid du Colombier s->laddr, s->lport);
23781ce6c95fSDavid du Colombier tcb->flags |= FORCE; /* force duplicate ack; RFC 5681 §3.2 */
23797dd7cddfSDavid du Colombier goto output;
23807dd7cddfSDavid du Colombier }
23817dd7cddfSDavid du Colombier
23821ce6c95fSDavid du Colombier if(tcb->nreseq > 0)
23831ce6c95fSDavid du Colombier tcb->flags |= FORCE; /* filled hole in seq. space; RFC 5681 §3.2 */
23841ce6c95fSDavid du Colombier
23857dd7cddfSDavid du Colombier /*
23867dd7cddfSDavid du Colombier * keep looping till we've processed this packet plus any
23877dd7cddfSDavid du Colombier * adjacent packets in the resequence queue
23887dd7cddfSDavid du Colombier */
23897dd7cddfSDavid du Colombier for(;;) {
23907dd7cddfSDavid du Colombier if(seg.flags & RST) {
239151711cb6SDavid du Colombier if(tcb->state == Established) {
239259cc4ca5SDavid du Colombier tpriv->stats[EstabResets]++;
239351711cb6SDavid du Colombier if(tcb->rcv.nxt != seg.seq)
23946b7c5dceSDavid du Colombier netlog(f, Logtcp, "out of order RST "
23956b7c5dceSDavid du Colombier "rcvd: %I.%d -> %I.%d, rcv.nxt "
23966b7c5dceSDavid du Colombier "%lux seq %lux\n",
23971ce6c95fSDavid du Colombier s->raddr, s->rport, s->laddr,
23981ce6c95fSDavid du Colombier s->lport, tcb->rcv.nxt, seg.seq);
239951711cb6SDavid du Colombier }
24007dd7cddfSDavid du Colombier localclose(s, Econrefused);
24017dd7cddfSDavid du Colombier goto raise;
24027dd7cddfSDavid du Colombier }
24037dd7cddfSDavid du Colombier
24047dd7cddfSDavid du Colombier if((seg.flags&ACK) == 0)
24057dd7cddfSDavid du Colombier goto raise;
24067dd7cddfSDavid du Colombier
24077dd7cddfSDavid du Colombier switch(tcb->state) {
24087dd7cddfSDavid du Colombier case Syn_received:
24097dd7cddfSDavid du Colombier if(!seq_within(seg.ack, tcb->snd.una+1, tcb->snd.nxt)){
24103ff48bf5SDavid du Colombier sndrst(tcp, source, dest, length, &seg, version,
24113ff48bf5SDavid du Colombier "bad seq in Syn_received");
24127dd7cddfSDavid du Colombier goto raise;
24137dd7cddfSDavid du Colombier }
24147dd7cddfSDavid du Colombier update(s, &seg);
24157dd7cddfSDavid du Colombier tcpsetstate(s, Established);
24167dd7cddfSDavid du Colombier case Established:
24177dd7cddfSDavid du Colombier case Close_wait:
24187dd7cddfSDavid du Colombier update(s, &seg);
24197dd7cddfSDavid du Colombier break;
24207dd7cddfSDavid du Colombier case Finwait1:
24217dd7cddfSDavid du Colombier update(s, &seg);
242280ee5cbfSDavid du Colombier if(qlen(s->wq)+tcb->flgcnt == 0){
24237dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
24247dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
24257dd7cddfSDavid du Colombier tcpsetkacounter(tcb);
24263ff48bf5SDavid du Colombier tcb->time = NOW;
24277dd7cddfSDavid du Colombier tcpsetstate(s, Finwait2);
24287dd7cddfSDavid du Colombier tcb->katimer.start = MSL2 * (1000 / MSPTICK);
24297dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->katimer);
24307dd7cddfSDavid du Colombier }
24317dd7cddfSDavid du Colombier break;
24327dd7cddfSDavid du Colombier case Finwait2:
24337dd7cddfSDavid du Colombier update(s, &seg);
24347dd7cddfSDavid du Colombier break;
24357dd7cddfSDavid du Colombier case Closing:
24367dd7cddfSDavid du Colombier update(s, &seg);
243780ee5cbfSDavid du Colombier if(qlen(s->wq)+tcb->flgcnt == 0) {
24387dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
24397dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
24407dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->katimer);
24417dd7cddfSDavid du Colombier tcpsetstate(s, Time_wait);
24427dd7cddfSDavid du Colombier tcb->timer.start = MSL2*(1000 / MSPTICK);
24437dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->timer);
24447dd7cddfSDavid du Colombier }
24457dd7cddfSDavid du Colombier break;
24467dd7cddfSDavid du Colombier case Last_ack:
24477dd7cddfSDavid du Colombier update(s, &seg);
244880ee5cbfSDavid du Colombier if(qlen(s->wq)+tcb->flgcnt == 0) {
24497dd7cddfSDavid du Colombier localclose(s, nil);
24507dd7cddfSDavid du Colombier goto raise;
24517dd7cddfSDavid du Colombier }
24527dd7cddfSDavid du Colombier case Time_wait:
24537dd7cddfSDavid du Colombier tcb->flags |= FORCE;
24549a747e4fSDavid du Colombier if(tcb->timer.state != TcptimerON)
24557dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->timer);
24567dd7cddfSDavid du Colombier }
24577dd7cddfSDavid du Colombier
24587dd7cddfSDavid du Colombier if((seg.flags&URG) && seg.urg) {
24597dd7cddfSDavid du Colombier if(seq_gt(seg.urg + seg.seq, tcb->rcv.urg)) {
24607dd7cddfSDavid du Colombier tcb->rcv.urg = seg.urg + seg.seq;
24617dd7cddfSDavid du Colombier pullblock(&bp, seg.urg);
24627dd7cddfSDavid du Colombier }
24637dd7cddfSDavid du Colombier }
24647dd7cddfSDavid du Colombier else
24657dd7cddfSDavid du Colombier if(seq_gt(tcb->rcv.nxt, tcb->rcv.urg))
24667dd7cddfSDavid du Colombier tcb->rcv.urg = tcb->rcv.nxt;
24677dd7cddfSDavid du Colombier
24687dd7cddfSDavid du Colombier if(length == 0) {
24697dd7cddfSDavid du Colombier if(bp != nil)
24707dd7cddfSDavid du Colombier freeblist(bp);
24717dd7cddfSDavid du Colombier }
24727dd7cddfSDavid du Colombier else {
24737dd7cddfSDavid du Colombier switch(tcb->state){
24747dd7cddfSDavid du Colombier default:
24757dd7cddfSDavid du Colombier /* Ignore segment text */
24767dd7cddfSDavid du Colombier if(bp != nil)
24777dd7cddfSDavid du Colombier freeblist(bp);
24787dd7cddfSDavid du Colombier break;
24797dd7cddfSDavid du Colombier
24807dd7cddfSDavid du Colombier case Syn_received:
24817dd7cddfSDavid du Colombier case Established:
24827dd7cddfSDavid du Colombier case Finwait1:
24837dd7cddfSDavid du Colombier /* If we still have some data place on
24847dd7cddfSDavid du Colombier * receive queue
24857dd7cddfSDavid du Colombier */
24867dd7cddfSDavid du Colombier if(bp) {
24877dd7cddfSDavid du Colombier bp = packblock(bp);
24887dd7cddfSDavid du Colombier if(bp == nil)
24897dd7cddfSDavid du Colombier panic("tcp packblock");
24907dd7cddfSDavid du Colombier qpassnolim(s->rq, bp);
24917dd7cddfSDavid du Colombier bp = nil;
24927dd7cddfSDavid du Colombier }
24937dd7cddfSDavid du Colombier tcb->rcv.nxt += length;
249480ee5cbfSDavid du Colombier
249580ee5cbfSDavid du Colombier /*
249680ee5cbfSDavid du Colombier * turn on the acktimer if there's something
249780ee5cbfSDavid du Colombier * to ack
249880ee5cbfSDavid du Colombier */
24999a747e4fSDavid du Colombier if(tcb->acktimer.state != TcptimerON)
25007dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->acktimer);
25017dd7cddfSDavid du Colombier
25027dd7cddfSDavid du Colombier break;
25037dd7cddfSDavid du Colombier case Finwait2:
25047dd7cddfSDavid du Colombier /* no process to read the data, send a reset */
25057dd7cddfSDavid du Colombier if(bp != nil)
25067dd7cddfSDavid du Colombier freeblist(bp);
25073ff48bf5SDavid du Colombier sndrst(tcp, source, dest, length, &seg, version,
25083ff48bf5SDavid du Colombier "send to Finwait2");
25097dd7cddfSDavid du Colombier qunlock(s);
25107dd7cddfSDavid du Colombier poperror();
25117dd7cddfSDavid du Colombier return;
25127dd7cddfSDavid du Colombier }
25137dd7cddfSDavid du Colombier }
25147dd7cddfSDavid du Colombier
25157dd7cddfSDavid du Colombier if(seg.flags & FIN) {
25167dd7cddfSDavid du Colombier tcb->flags |= FORCE;
25177dd7cddfSDavid du Colombier
25187dd7cddfSDavid du Colombier switch(tcb->state) {
25197dd7cddfSDavid du Colombier case Syn_received:
25207dd7cddfSDavid du Colombier case Established:
25217dd7cddfSDavid du Colombier tcb->rcv.nxt++;
25227dd7cddfSDavid du Colombier tcpsetstate(s, Close_wait);
25237dd7cddfSDavid du Colombier break;
25247dd7cddfSDavid du Colombier case Finwait1:
25257dd7cddfSDavid du Colombier tcb->rcv.nxt++;
252680ee5cbfSDavid du Colombier if(qlen(s->wq)+tcb->flgcnt == 0) {
25277dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
25287dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
25297dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->katimer);
25307dd7cddfSDavid du Colombier tcpsetstate(s, Time_wait);
25317dd7cddfSDavid du Colombier tcb->timer.start = MSL2*(1000/MSPTICK);
25327dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->timer);
25337dd7cddfSDavid du Colombier }
25347dd7cddfSDavid du Colombier else
25357dd7cddfSDavid du Colombier tcpsetstate(s, Closing);
25367dd7cddfSDavid du Colombier break;
25377dd7cddfSDavid du Colombier case Finwait2:
25387dd7cddfSDavid du Colombier tcb->rcv.nxt++;
25397dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
25407dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
25417dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->katimer);
25427dd7cddfSDavid du Colombier tcpsetstate(s, Time_wait);
25437dd7cddfSDavid du Colombier tcb->timer.start = MSL2 * (1000/MSPTICK);
25447dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->timer);
25457dd7cddfSDavid du Colombier break;
25467dd7cddfSDavid du Colombier case Close_wait:
25477dd7cddfSDavid du Colombier case Closing:
25487dd7cddfSDavid du Colombier case Last_ack:
25497dd7cddfSDavid du Colombier break;
25507dd7cddfSDavid du Colombier case Time_wait:
25517dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->timer);
25527dd7cddfSDavid du Colombier break;
25537dd7cddfSDavid du Colombier }
25547dd7cddfSDavid du Colombier }
25557dd7cddfSDavid du Colombier
25567dd7cddfSDavid du Colombier /*
25577dd7cddfSDavid du Colombier * get next adjacent segment from the resequence queue.
25587dd7cddfSDavid du Colombier * dump/trim any overlapping segments
25597dd7cddfSDavid du Colombier */
25607dd7cddfSDavid du Colombier for(;;) {
25617dd7cddfSDavid du Colombier if(tcb->reseq == nil)
25627dd7cddfSDavid du Colombier goto output;
25637dd7cddfSDavid du Colombier
25647dd7cddfSDavid du Colombier if(seq_ge(tcb->rcv.nxt, tcb->reseq->seg.seq) == 0)
25657dd7cddfSDavid du Colombier goto output;
25667dd7cddfSDavid du Colombier
25677dd7cddfSDavid du Colombier getreseq(tcb, &seg, &bp, &length);
25687dd7cddfSDavid du Colombier
25691ce6c95fSDavid du Colombier tcprcvwin(s);
25701ce6c95fSDavid du Colombier if(tcptrim(tcb, &seg, &bp, &length) == 0){
25711ce6c95fSDavid du Colombier tcb->flags |= FORCE;
25727dd7cddfSDavid du Colombier break;
25737dd7cddfSDavid du Colombier }
25747dd7cddfSDavid du Colombier }
25751ce6c95fSDavid du Colombier }
25767dd7cddfSDavid du Colombier output:
25777dd7cddfSDavid du Colombier tcpoutput(s);
25787dd7cddfSDavid du Colombier qunlock(s);
25797dd7cddfSDavid du Colombier poperror();
25807dd7cddfSDavid du Colombier return;
25817dd7cddfSDavid du Colombier raise:
25827dd7cddfSDavid du Colombier qunlock(s);
25837dd7cddfSDavid du Colombier poperror();
25847dd7cddfSDavid du Colombier freeblist(bp);
258580ee5cbfSDavid du Colombier tcpkick(s);
25867dd7cddfSDavid du Colombier }
25877dd7cddfSDavid du Colombier
25887dd7cddfSDavid du Colombier /*
258980ee5cbfSDavid du Colombier * always enters and exits with the s locked. We drop
259080ee5cbfSDavid du Colombier * the lock to ipoput the packet so some care has to be
259180ee5cbfSDavid du Colombier * taken by callers.
25927dd7cddfSDavid du Colombier */
25931ce6c95fSDavid du Colombier static void
tcpoutput(Conv * s)25947dd7cddfSDavid du Colombier tcpoutput(Conv *s)
25957dd7cddfSDavid du Colombier {
25967dd7cddfSDavid du Colombier Tcp seg;
25971ce6c95fSDavid du Colombier uint msgs;
25987dd7cddfSDavid du Colombier Tcpctl *tcb;
25997dd7cddfSDavid du Colombier Block *hbp, *bp;
26001ce6c95fSDavid du Colombier int sndcnt;
26011ce6c95fSDavid du Colombier ulong ssize, dsize, sent;
26027dd7cddfSDavid du Colombier Fs *f;
26037dd7cddfSDavid du Colombier Tcppriv *tpriv;
26043ff48bf5SDavid du Colombier uchar version;
26057dd7cddfSDavid du Colombier
26067dd7cddfSDavid du Colombier f = s->p->f;
26077dd7cddfSDavid du Colombier tpriv = s->p->priv;
26083ff48bf5SDavid du Colombier version = s->ipversion;
26097dd7cddfSDavid du Colombier
26107dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
26117dd7cddfSDavid du Colombier
26121ce6c95fSDavid du Colombier /* force ack every 2*mss */
26131ce6c95fSDavid du Colombier if((tcb->flags & FORCE) == 0 &&
26141ce6c95fSDavid du Colombier tcb->rcv.nxt - tcb->rcv.ackptr >= 2*tcb->mss){
26151ce6c95fSDavid du Colombier tpriv->stats[Delayack]++;
26161ce6c95fSDavid du Colombier tcb->flags |= FORCE;
26171ce6c95fSDavid du Colombier }
26181ce6c95fSDavid du Colombier
26191ce6c95fSDavid du Colombier /* force ack if window opening */
26201ce6c95fSDavid du Colombier if((tcb->flags & FORCE) == 0){
26211ce6c95fSDavid du Colombier tcprcvwin(s);
26221ce6c95fSDavid du Colombier if((int)(tcb->rcv.wptr - tcb->rcv.wsnt) >= 2*tcb->mss){
26231ce6c95fSDavid du Colombier tpriv->stats[Wopenack]++;
26241ce6c95fSDavid du Colombier tcb->flags |= FORCE;
26251ce6c95fSDavid du Colombier }
26261ce6c95fSDavid du Colombier }
26271ce6c95fSDavid du Colombier
26281ce6c95fSDavid du Colombier for(msgs = 0; msgs < 100; msgs++) {
26297dd7cddfSDavid du Colombier switch(tcb->state) {
26307dd7cddfSDavid du Colombier case Listen:
26317dd7cddfSDavid du Colombier case Closed:
26327dd7cddfSDavid du Colombier case Finwait2:
2633e6c6b7f8SDavid du Colombier return;
26347dd7cddfSDavid du Colombier }
26357dd7cddfSDavid du Colombier
26361ce6c95fSDavid du Colombier /* Don't send anything else until our SYN has been acked */
26371ce6c95fSDavid du Colombier if(tcb->snd.ptr != tcb->iss && (tcb->flags & SYNACK) == 0)
26381ce6c95fSDavid du Colombier break;
26391ce6c95fSDavid du Colombier
26407dd7cddfSDavid du Colombier /* force an ack when a window has opened up */
26411ce6c95fSDavid du Colombier tcprcvwin(s);
26427dd7cddfSDavid du Colombier if(tcb->rcv.blocked && tcb->rcv.wnd > 0){
26437dd7cddfSDavid du Colombier tcb->rcv.blocked = 0;
26447dd7cddfSDavid du Colombier tcb->flags |= FORCE;
26457dd7cddfSDavid du Colombier }
26467dd7cddfSDavid du Colombier
264780ee5cbfSDavid du Colombier sndcnt = qlen(s->wq)+tcb->flgcnt;
26487dd7cddfSDavid du Colombier sent = tcb->snd.ptr - tcb->snd.una;
26491ce6c95fSDavid du Colombier ssize = sndcnt;
26507dd7cddfSDavid du Colombier if(tcb->snd.wnd == 0){
26511ce6c95fSDavid du Colombier /* zero window probe */
26521ce6c95fSDavid du Colombier if(sent > 0 && !(tcb->flags & FORCE))
26531ce6c95fSDavid du Colombier break; /* already probing, rto re-probes */
26541ce6c95fSDavid du Colombier if(ssize < sent)
26551ce6c95fSDavid du Colombier ssize = 0;
26567dd7cddfSDavid du Colombier else{
26571ce6c95fSDavid du Colombier ssize -= sent;
26581ce6c95fSDavid du Colombier if(ssize > 0)
26591ce6c95fSDavid du Colombier ssize = 1;
26607dd7cddfSDavid du Colombier }
26611ce6c95fSDavid du Colombier } else {
26621ce6c95fSDavid du Colombier /* calculate usable segment size */
26631ce6c95fSDavid du Colombier if(ssize > tcb->cwind)
26641ce6c95fSDavid du Colombier ssize = tcb->cwind;
26651ce6c95fSDavid du Colombier if(ssize > tcb->snd.wnd)
26661ce6c95fSDavid du Colombier ssize = tcb->snd.wnd;
26671ce6c95fSDavid du Colombier
26681ce6c95fSDavid du Colombier if(ssize < sent)
26691ce6c95fSDavid du Colombier ssize = 0;
26701ce6c95fSDavid du Colombier else {
26711ce6c95fSDavid du Colombier ssize -= sent;
26721ce6c95fSDavid du Colombier if(ssize > tcb->mss)
26737dd7cddfSDavid du Colombier ssize = tcb->mss;
26741ce6c95fSDavid du Colombier }
26751ce6c95fSDavid du Colombier }
26761ce6c95fSDavid du Colombier
26777dd7cddfSDavid du Colombier dsize = ssize;
26787dd7cddfSDavid du Colombier seg.urg = 0;
26797dd7cddfSDavid du Colombier
26801ce6c95fSDavid du Colombier if(!(tcb->flags & FORCE))
26811ce6c95fSDavid du Colombier if(ssize == 0 ||
26821ce6c95fSDavid du Colombier ssize < tcb->mss && tcb->snd.nxt == tcb->snd.ptr &&
26831ce6c95fSDavid du Colombier sent > TCPREXMTTHRESH * tcb->mss)
26847dd7cddfSDavid du Colombier break;
26857dd7cddfSDavid du Colombier
26867dd7cddfSDavid du Colombier tcb->flags &= ~FORCE;
26877dd7cddfSDavid du Colombier
26887dd7cddfSDavid du Colombier /* By default we will generate an ack */
26893f695129SDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
26907dd7cddfSDavid du Colombier seg.source = s->lport;
26917dd7cddfSDavid du Colombier seg.dest = s->rport;
26927dd7cddfSDavid du Colombier seg.flags = ACK;
26937dd7cddfSDavid du Colombier seg.mss = 0;
26943f695129SDavid du Colombier seg.ws = 0;
26951ce6c95fSDavid du Colombier seg.update = 0;
26967dd7cddfSDavid du Colombier switch(tcb->state){
26977dd7cddfSDavid du Colombier case Syn_sent:
26987dd7cddfSDavid du Colombier seg.flags = 0;
26997dd7cddfSDavid du Colombier if(tcb->snd.ptr == tcb->iss){
27007dd7cddfSDavid du Colombier seg.flags |= SYN;
27017dd7cddfSDavid du Colombier dsize--;
27023f695129SDavid du Colombier seg.mss = tcb->mss;
27033f695129SDavid du Colombier seg.ws = tcb->scale;
27047dd7cddfSDavid du Colombier }
27057dd7cddfSDavid du Colombier break;
27067dd7cddfSDavid du Colombier case Syn_received:
27077dd7cddfSDavid du Colombier /*
27087dd7cddfSDavid du Colombier * don't send any data with a SYN/ACK packet
27097dd7cddfSDavid du Colombier * because Linux rejects the packet in its
27107dd7cddfSDavid du Colombier * attempt to solve the SYN attack problem
27117dd7cddfSDavid du Colombier */
27127dd7cddfSDavid du Colombier if(tcb->snd.ptr == tcb->iss){
27137dd7cddfSDavid du Colombier seg.flags |= SYN;
27147dd7cddfSDavid du Colombier dsize = 0;
27157dd7cddfSDavid du Colombier ssize = 1;
27163f695129SDavid du Colombier seg.mss = tcb->mss;
27173f695129SDavid du Colombier seg.ws = tcb->scale;
27187dd7cddfSDavid du Colombier }
27197dd7cddfSDavid du Colombier break;
27207dd7cddfSDavid du Colombier }
27217dd7cddfSDavid du Colombier seg.seq = tcb->snd.ptr;
27227dd7cddfSDavid du Colombier seg.ack = tcb->rcv.nxt;
27237dd7cddfSDavid du Colombier seg.wnd = tcb->rcv.wnd;
27247dd7cddfSDavid du Colombier
27257dd7cddfSDavid du Colombier /* Pull out data to send */
27267dd7cddfSDavid du Colombier bp = nil;
27277dd7cddfSDavid du Colombier if(dsize != 0) {
27287dd7cddfSDavid du Colombier bp = qcopy(s->wq, dsize, sent);
27297dd7cddfSDavid du Colombier if(BLEN(bp) != dsize) {
27307dd7cddfSDavid du Colombier seg.flags |= FIN;
27317dd7cddfSDavid du Colombier dsize--;
27327dd7cddfSDavid du Colombier }
27337dd7cddfSDavid du Colombier }
27347dd7cddfSDavid du Colombier
27351ce6c95fSDavid du Colombier if(sent+dsize == sndcnt && dsize)
27367dd7cddfSDavid du Colombier seg.flags |= PSH;
27377dd7cddfSDavid du Colombier
27387dd7cddfSDavid du Colombier tcb->snd.ptr += ssize;
27397dd7cddfSDavid du Colombier
27407dd7cddfSDavid du Colombier /* Pull up the send pointer so we can accept acks
27417dd7cddfSDavid du Colombier * for this window
27427dd7cddfSDavid du Colombier */
27437dd7cddfSDavid du Colombier if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
27447dd7cddfSDavid du Colombier tcb->snd.nxt = tcb->snd.ptr;
27457dd7cddfSDavid du Colombier
27467dd7cddfSDavid du Colombier /* Build header, link data and compute cksum */
27473ff48bf5SDavid du Colombier switch(version){
27483ff48bf5SDavid du Colombier case V4:
27493ff48bf5SDavid du Colombier tcb->protohdr.tcp4hdr.vihl = IP_VER4;
27503ff48bf5SDavid du Colombier hbp = htontcp4(&seg, bp, &tcb->protohdr.tcp4hdr, tcb);
27517dd7cddfSDavid du Colombier if(hbp == nil) {
27527dd7cddfSDavid du Colombier freeblist(bp);
2753e6c6b7f8SDavid du Colombier return;
27547dd7cddfSDavid du Colombier }
27553ff48bf5SDavid du Colombier break;
27563ff48bf5SDavid du Colombier case V6:
27573ff48bf5SDavid du Colombier tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
27583ff48bf5SDavid du Colombier hbp = htontcp6(&seg, bp, &tcb->protohdr.tcp6hdr, tcb);
27593ff48bf5SDavid du Colombier if(hbp == nil) {
27603ff48bf5SDavid du Colombier freeblist(bp);
2761e6c6b7f8SDavid du Colombier return;
27623ff48bf5SDavid du Colombier }
27633ff48bf5SDavid du Colombier break;
27643ff48bf5SDavid du Colombier default:
27653ff48bf5SDavid du Colombier hbp = nil; /* to suppress a warning */
27663ff48bf5SDavid du Colombier panic("tcpoutput: version %d", version);
27673ff48bf5SDavid du Colombier }
27687dd7cddfSDavid du Colombier
27697dd7cddfSDavid du Colombier /* Start the transmission timers if there is new data and we
27707dd7cddfSDavid du Colombier * expect acknowledges
27717dd7cddfSDavid du Colombier */
27727dd7cddfSDavid du Colombier if(ssize != 0){
27731ce6c95fSDavid du Colombier if(tcb->timer.state != TcptimerON){
27741ce6c95fSDavid du Colombier tcb->time = NOW;
27751ce6c95fSDavid du Colombier tcb->timeuna = tcb->snd.una;
27767dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->timer);
27771ce6c95fSDavid du Colombier }
27787dd7cddfSDavid du Colombier
27797dd7cddfSDavid du Colombier /* If round trip timer isn't running, start it.
27807dd7cddfSDavid du Colombier * measure the longest packet only in case the
27817dd7cddfSDavid du Colombier * transmission time dominates RTT
27827dd7cddfSDavid du Colombier */
27831ce6c95fSDavid du Colombier if(tcb->snd.retransmit == 0)
27849a747e4fSDavid du Colombier if(tcb->rtt_timer.state != TcptimerON)
27857dd7cddfSDavid du Colombier if(ssize == tcb->mss) {
27867dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->rtt_timer);
27877dd7cddfSDavid du Colombier tcb->rttseq = tcb->snd.ptr;
27887dd7cddfSDavid du Colombier }
27897dd7cddfSDavid du Colombier }
27907dd7cddfSDavid du Colombier
279159cc4ca5SDavid du Colombier tpriv->stats[OutSegs]++;
27921ce6c95fSDavid du Colombier if(tcb->snd.retransmit)
27931ce6c95fSDavid du Colombier tpriv->stats[RetransSegsSent]++;
27941ce6c95fSDavid du Colombier tcb->rcv.ackptr = seg.ack;
27951ce6c95fSDavid du Colombier tcb->rcv.wsnt = tcb->rcv.wptr;
279640ef9009SDavid du Colombier
279740ef9009SDavid du Colombier /* put off the next keep alive */
27987dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->katimer);
27993ff48bf5SDavid du Colombier
28003ff48bf5SDavid du Colombier switch(version){
28013ff48bf5SDavid du Colombier case V4:
2802a6a9e072SDavid du Colombier if(ipoput4(f, hbp, 0, s->ttl, s->tos, s) < 0){
2803e6c6b7f8SDavid du Colombier /* a negative return means no route */
2804e6c6b7f8SDavid du Colombier localclose(s, "no route");
2805e6c6b7f8SDavid du Colombier }
28063ff48bf5SDavid du Colombier break;
28073ff48bf5SDavid du Colombier case V6:
2808a6a9e072SDavid du Colombier if(ipoput6(f, hbp, 0, s->ttl, s->tos, s) < 0){
2809e6c6b7f8SDavid du Colombier /* a negative return means no route */
2810e6c6b7f8SDavid du Colombier localclose(s, "no route");
2811e6c6b7f8SDavid du Colombier }
28123ff48bf5SDavid du Colombier break;
28133ff48bf5SDavid du Colombier default:
28143ff48bf5SDavid du Colombier panic("tcpoutput2: version %d", version);
281580ee5cbfSDavid du Colombier }
28161ce6c95fSDavid du Colombier if((msgs%4) == 3){
28173ff48bf5SDavid du Colombier qunlock(s);
281880ee5cbfSDavid du Colombier qlock(s);
28193ff48bf5SDavid du Colombier }
28207dd7cddfSDavid du Colombier }
28217dd7cddfSDavid du Colombier }
28227dd7cddfSDavid du Colombier
28237dd7cddfSDavid du Colombier /*
28247dd7cddfSDavid du Colombier * the BSD convention (hack?) for keep alives. resend last uchar acked.
28257dd7cddfSDavid du Colombier */
28261ce6c95fSDavid du Colombier static void
tcpsendka(Conv * s)28277dd7cddfSDavid du Colombier tcpsendka(Conv *s)
28287dd7cddfSDavid du Colombier {
28297dd7cddfSDavid du Colombier Tcp seg;
28307dd7cddfSDavid du Colombier Tcpctl *tcb;
28317dd7cddfSDavid du Colombier Block *hbp,*dbp;
28327dd7cddfSDavid du Colombier
28337dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
28347dd7cddfSDavid du Colombier
28357dd7cddfSDavid du Colombier dbp = nil;
283610ae047aSDavid du Colombier memset(&seg, 0, sizeof seg);
28377dd7cddfSDavid du Colombier seg.urg = 0;
28387dd7cddfSDavid du Colombier seg.source = s->lport;
28397dd7cddfSDavid du Colombier seg.dest = s->rport;
28407dd7cddfSDavid du Colombier seg.flags = ACK|PSH;
28417dd7cddfSDavid du Colombier seg.mss = 0;
28423f695129SDavid du Colombier seg.ws = 0;
28433ff48bf5SDavid du Colombier if(tcpporthogdefense)
28443ff48bf5SDavid du Colombier seg.seq = tcb->snd.una-(1<<30)-nrand(1<<20);
28453ff48bf5SDavid du Colombier else
28467dd7cddfSDavid du Colombier seg.seq = tcb->snd.una-1;
28477dd7cddfSDavid du Colombier seg.ack = tcb->rcv.nxt;
28481ce6c95fSDavid du Colombier tcb->rcv.ackptr = seg.ack;
28491ce6c95fSDavid du Colombier tcprcvwin(s);
28507dd7cddfSDavid du Colombier seg.wnd = tcb->rcv.wnd;
28517dd7cddfSDavid du Colombier if(tcb->state == Finwait2){
28527dd7cddfSDavid du Colombier seg.flags |= FIN;
28537dd7cddfSDavid du Colombier } else {
28547dd7cddfSDavid du Colombier dbp = allocb(1);
28557dd7cddfSDavid du Colombier dbp->wp++;
28567dd7cddfSDavid du Colombier }
28577dd7cddfSDavid du Colombier
28583ff48bf5SDavid du Colombier if(isv4(s->raddr)) {
28597dd7cddfSDavid du Colombier /* Build header, link data and compute cksum */
28603ff48bf5SDavid du Colombier tcb->protohdr.tcp4hdr.vihl = IP_VER4;
28613ff48bf5SDavid du Colombier hbp = htontcp4(&seg, dbp, &tcb->protohdr.tcp4hdr, tcb);
28627dd7cddfSDavid du Colombier if(hbp == nil) {
28637dd7cddfSDavid du Colombier freeblist(dbp);
28647dd7cddfSDavid du Colombier return;
28657dd7cddfSDavid du Colombier }
2866a6a9e072SDavid du Colombier ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s);
28673ff48bf5SDavid du Colombier }
28683ff48bf5SDavid du Colombier else {
28693ff48bf5SDavid du Colombier /* Build header, link data and compute cksum */
28703ff48bf5SDavid du Colombier tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
28713ff48bf5SDavid du Colombier hbp = htontcp6(&seg, dbp, &tcb->protohdr.tcp6hdr, tcb);
28723ff48bf5SDavid du Colombier if(hbp == nil) {
28733ff48bf5SDavid du Colombier freeblist(dbp);
28743ff48bf5SDavid du Colombier return;
28753ff48bf5SDavid du Colombier }
2876a6a9e072SDavid du Colombier ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s);
28773ff48bf5SDavid du Colombier }
28787dd7cddfSDavid du Colombier }
28797dd7cddfSDavid du Colombier
28807dd7cddfSDavid du Colombier /*
288140ef9009SDavid du Colombier * set connection to time out after 12 minutes
28827dd7cddfSDavid du Colombier */
28831ce6c95fSDavid du Colombier static void
tcpsetkacounter(Tcpctl * tcb)28847dd7cddfSDavid du Colombier tcpsetkacounter(Tcpctl *tcb)
28857dd7cddfSDavid du Colombier {
2886e288d156SDavid du Colombier tcb->kacounter = (12 * 60 * 1000) / (tcb->katimer.start*MSPTICK);
28877dd7cddfSDavid du Colombier if(tcb->kacounter < 3)
28887dd7cddfSDavid du Colombier tcb->kacounter = 3;
28897dd7cddfSDavid du Colombier }
289040ef9009SDavid du Colombier
289140ef9009SDavid du Colombier /*
289240ef9009SDavid du Colombier * if we've timed out, close the connection
289340ef9009SDavid du Colombier * otherwise, send a keepalive and restart the timer
289440ef9009SDavid du Colombier */
28951ce6c95fSDavid du Colombier static void
tcpkeepalive(void * v)28967dd7cddfSDavid du Colombier tcpkeepalive(void *v)
28977dd7cddfSDavid du Colombier {
28987dd7cddfSDavid du Colombier Tcpctl *tcb;
28997dd7cddfSDavid du Colombier Conv *s;
29007dd7cddfSDavid du Colombier
29017dd7cddfSDavid du Colombier s = v;
29027dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
29037dd7cddfSDavid du Colombier if(waserror()){
29047dd7cddfSDavid du Colombier qunlock(s);
29057dd7cddfSDavid du Colombier nexterror();
29067dd7cddfSDavid du Colombier }
29077dd7cddfSDavid du Colombier qlock(s);
29087dd7cddfSDavid du Colombier if(tcb->state != Closed){
29097dd7cddfSDavid du Colombier if(--(tcb->kacounter) <= 0) {
29107dd7cddfSDavid du Colombier localclose(s, Etimedout);
29117dd7cddfSDavid du Colombier } else {
29127dd7cddfSDavid du Colombier tcpsendka(s);
29137dd7cddfSDavid du Colombier tcpgo(s->p->priv, &tcb->katimer);
29147dd7cddfSDavid du Colombier }
29157dd7cddfSDavid du Colombier }
29167dd7cddfSDavid du Colombier qunlock(s);
29177dd7cddfSDavid du Colombier poperror();
29187dd7cddfSDavid du Colombier }
29197dd7cddfSDavid du Colombier
29207dd7cddfSDavid du Colombier /*
29217dd7cddfSDavid du Colombier * start keepalive timer
29227dd7cddfSDavid du Colombier */
29231ce6c95fSDavid du Colombier static char*
tcpstartka(Conv * s,char ** f,int n)29247dd7cddfSDavid du Colombier tcpstartka(Conv *s, char **f, int n)
29257dd7cddfSDavid du Colombier {
29267dd7cddfSDavid du Colombier Tcpctl *tcb;
29277dd7cddfSDavid du Colombier int x;
29287dd7cddfSDavid du Colombier
29297dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
293040ef9009SDavid du Colombier if(tcb->state != Established)
293140ef9009SDavid du Colombier return "connection must be in Establised state";
29327dd7cddfSDavid du Colombier if(n > 1){
29337dd7cddfSDavid du Colombier x = atoi(f[1]);
29347dd7cddfSDavid du Colombier if(x >= MSPTICK)
29357dd7cddfSDavid du Colombier tcb->katimer.start = x/MSPTICK;
29367dd7cddfSDavid du Colombier }
29377dd7cddfSDavid du Colombier tcpsetkacounter(tcb);
29387dd7cddfSDavid du Colombier tcpgo(s->p->priv, &tcb->katimer);
29397dd7cddfSDavid du Colombier
29407dd7cddfSDavid du Colombier return nil;
29417dd7cddfSDavid du Colombier }
29427dd7cddfSDavid du Colombier
294359cc4ca5SDavid du Colombier /*
294459cc4ca5SDavid du Colombier * turn checksums on/off
294559cc4ca5SDavid du Colombier */
29461ce6c95fSDavid du Colombier static char*
tcpsetchecksum(Conv * s,char ** f,int)294759cc4ca5SDavid du Colombier tcpsetchecksum(Conv *s, char **f, int)
294859cc4ca5SDavid du Colombier {
294959cc4ca5SDavid du Colombier Tcpctl *tcb;
295059cc4ca5SDavid du Colombier
295159cc4ca5SDavid du Colombier tcb = (Tcpctl*)s->ptcl;
295259cc4ca5SDavid du Colombier tcb->nochecksum = !atoi(f[1]);
295359cc4ca5SDavid du Colombier
295459cc4ca5SDavid du Colombier return nil;
295559cc4ca5SDavid du Colombier }
295659cc4ca5SDavid du Colombier
29571ce6c95fSDavid du Colombier /*
29581ce6c95fSDavid du Colombier * retransmit (at most) one segment at snd.una.
29591ce6c95fSDavid du Colombier * preserve cwind & snd.ptr
29601ce6c95fSDavid du Colombier */
29611ce6c95fSDavid du Colombier static void
tcprxmit(Conv * s)29627dd7cddfSDavid du Colombier tcprxmit(Conv *s)
29637dd7cddfSDavid du Colombier {
29647dd7cddfSDavid du Colombier Tcpctl *tcb;
29651ce6c95fSDavid du Colombier Tcppriv *tpriv;
29661ce6c95fSDavid du Colombier ulong tcwind, tptr;
29677dd7cddfSDavid du Colombier
29687dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
29697dd7cddfSDavid du Colombier tcb->flags |= RETRAN|FORCE;
29701ce6c95fSDavid du Colombier
29711ce6c95fSDavid du Colombier tptr = tcb->snd.ptr;
29721ce6c95fSDavid du Colombier tcwind = tcb->cwind;
29737dd7cddfSDavid du Colombier tcb->snd.ptr = tcb->snd.una;
29747dd7cddfSDavid du Colombier tcb->cwind = tcb->mss;
29751ce6c95fSDavid du Colombier tcb->snd.retransmit = 1;
29767dd7cddfSDavid du Colombier tcpoutput(s);
29771ce6c95fSDavid du Colombier tcb->snd.retransmit = 0;
29781ce6c95fSDavid du Colombier tcb->cwind = tcwind;
29791ce6c95fSDavid du Colombier tcb->snd.ptr = tptr;
29801ce6c95fSDavid du Colombier
29811ce6c95fSDavid du Colombier tpriv = s->p->priv;
29821ce6c95fSDavid du Colombier tpriv->stats[RetransSegs]++;
29837dd7cddfSDavid du Colombier }
29847dd7cddfSDavid du Colombier
29851ce6c95fSDavid du Colombier /*
29861ce6c95fSDavid du Colombier * TODO: RFC 4138 F-RTO
29871ce6c95fSDavid du Colombier */
29881ce6c95fSDavid du Colombier static void
tcptimeout(void * arg)29897dd7cddfSDavid du Colombier tcptimeout(void *arg)
29907dd7cddfSDavid du Colombier {
29917dd7cddfSDavid du Colombier Conv *s;
29927dd7cddfSDavid du Colombier Tcpctl *tcb;
29937dd7cddfSDavid du Colombier int maxback;
29947dd7cddfSDavid du Colombier Tcppriv *tpriv;
29957dd7cddfSDavid du Colombier
29967dd7cddfSDavid du Colombier s = (Conv*)arg;
29977dd7cddfSDavid du Colombier tpriv = s->p->priv;
29987dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
29997dd7cddfSDavid du Colombier
30007dd7cddfSDavid du Colombier if(waserror()){
30017dd7cddfSDavid du Colombier qunlock(s);
30027dd7cddfSDavid du Colombier nexterror();
30037dd7cddfSDavid du Colombier }
30047dd7cddfSDavid du Colombier qlock(s);
30057dd7cddfSDavid du Colombier switch(tcb->state){
30067dd7cddfSDavid du Colombier default:
30077dd7cddfSDavid du Colombier tcb->backoff++;
30087dd7cddfSDavid du Colombier if(tcb->state == Syn_sent)
30097dd7cddfSDavid du Colombier maxback = MAXBACKMS/2;
30107dd7cddfSDavid du Colombier else
30117dd7cddfSDavid du Colombier maxback = MAXBACKMS;
30127dd7cddfSDavid du Colombier tcb->backedoff += tcb->timer.start * MSPTICK;
30137dd7cddfSDavid du Colombier if(tcb->backedoff >= maxback) {
30147dd7cddfSDavid du Colombier localclose(s, Etimedout);
30157dd7cddfSDavid du Colombier break;
30167dd7cddfSDavid du Colombier }
30171ce6c95fSDavid du Colombier netlog(s->p->f, Logtcprxmt, "rxm %d/%d %ldms %lud rto %d %lud %s\n",
30181ce6c95fSDavid du Colombier tcb->srtt, tcb->mdev, NOW - tcb->time,
30191ce6c95fSDavid du Colombier tcb->snd.una - tcb->timeuna, tcb->snd.rto, tcb->snd.ptr,
30201ce6c95fSDavid du Colombier tcpstates[s->state]);
30219a747e4fSDavid du Colombier tcpsettimer(tcb);
30221ce6c95fSDavid du Colombier if(tcb->snd.rto == 0)
30231ce6c95fSDavid du Colombier tcpcongestion(tcb);
30247dd7cddfSDavid du Colombier tcprxmit(s);
30251ce6c95fSDavid du Colombier tcb->snd.ptr = tcb->snd.una;
30261ce6c95fSDavid du Colombier tcb->cwind = tcb->mss;
30271ce6c95fSDavid du Colombier tcb->snd.rto = 1;
302859cc4ca5SDavid du Colombier tpriv->stats[RetransTimeouts]++;
30291ce6c95fSDavid du Colombier
30301ce6c95fSDavid du Colombier if(tcb->snd.recovery){
30311ce6c95fSDavid du Colombier tcb->snd.dupacks = 0; /* reno rto */
30321ce6c95fSDavid du Colombier tcb->snd.recovery = 0;
30331ce6c95fSDavid du Colombier tpriv->stats[RecoveryRTO]++;
30341ce6c95fSDavid du Colombier tcb->snd.rxt = tcb->snd.nxt;
30351ce6c95fSDavid du Colombier netlog(s->p->f, Logtcpwin,
30361ce6c95fSDavid du Colombier "rto recovery rxt @%lud\n", tcb->snd.nxt);
30371ce6c95fSDavid du Colombier }
30381ce6c95fSDavid du Colombier
30391ce6c95fSDavid du Colombier tcb->abcbytes = 0;
30407dd7cddfSDavid du Colombier break;
30417dd7cddfSDavid du Colombier case Time_wait:
30427dd7cddfSDavid du Colombier localclose(s, nil);
30437dd7cddfSDavid du Colombier break;
30447dd7cddfSDavid du Colombier case Closed:
30457dd7cddfSDavid du Colombier break;
30467dd7cddfSDavid du Colombier }
30477dd7cddfSDavid du Colombier qunlock(s);
30487dd7cddfSDavid du Colombier poperror();
30497dd7cddfSDavid du Colombier }
30507dd7cddfSDavid du Colombier
30511ce6c95fSDavid du Colombier static int
inwindow(Tcpctl * tcb,int seq)30527dd7cddfSDavid du Colombier inwindow(Tcpctl *tcb, int seq)
30537dd7cddfSDavid du Colombier {
30547dd7cddfSDavid du Colombier return seq_within(seq, tcb->rcv.nxt, tcb->rcv.nxt+tcb->rcv.wnd-1);
30557dd7cddfSDavid du Colombier }
30567dd7cddfSDavid du Colombier
30573ff48bf5SDavid du Colombier /*
30583ff48bf5SDavid du Colombier * set up state for a received SYN (or SYN ACK) packet
30593ff48bf5SDavid du Colombier */
30601ce6c95fSDavid du Colombier static void
procsyn(Conv * s,Tcp * seg)30617dd7cddfSDavid du Colombier procsyn(Conv *s, Tcp *seg)
30627dd7cddfSDavid du Colombier {
30637dd7cddfSDavid du Colombier Tcpctl *tcb;
30647ec5746aSDavid du Colombier Tcppriv *tpriv;
30657dd7cddfSDavid du Colombier
30667dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
30677dd7cddfSDavid du Colombier tcb->flags |= FORCE;
30687dd7cddfSDavid du Colombier
30697dd7cddfSDavid du Colombier tcb->rcv.nxt = seg->seq + 1;
30701ce6c95fSDavid du Colombier tcb->rcv.wptr = tcb->rcv.nxt;
30711ce6c95fSDavid du Colombier tcb->rcv.wsnt = 0;
30727dd7cddfSDavid du Colombier tcb->rcv.urg = tcb->rcv.nxt;
30737dd7cddfSDavid du Colombier tcb->irs = seg->seq;
30747dd7cddfSDavid du Colombier
30753ff48bf5SDavid du Colombier /* our sending max segment size cannot be bigger than what he asked for */
30767ec5746aSDavid du Colombier if(seg->mss != 0 && seg->mss < tcb->mss) {
30777dd7cddfSDavid du Colombier tcb->mss = seg->mss;
30787ec5746aSDavid du Colombier tpriv = s->p->priv;
30797ec5746aSDavid du Colombier tpriv->stats[Mss] = tcb->mss;
30807ec5746aSDavid du Colombier }
30817dd7cddfSDavid du Colombier
30823ff48bf5SDavid du Colombier tcb->snd.wnd = seg->wnd;
30831ce6c95fSDavid du Colombier initialwindow(tcb);
30847dd7cddfSDavid du Colombier }
30857dd7cddfSDavid du Colombier
30861ce6c95fSDavid du Colombier static int
dumpreseq(Tcpctl * tcb)30871ce6c95fSDavid du Colombier dumpreseq(Tcpctl *tcb)
30887dd7cddfSDavid du Colombier {
30891ce6c95fSDavid du Colombier Reseq *r, *next;
30907dd7cddfSDavid du Colombier
30911ce6c95fSDavid du Colombier for(r = tcb->reseq; r != nil; r = next){
30921ce6c95fSDavid du Colombier next = r->next;
30931ce6c95fSDavid du Colombier freeblist(r->bp);
30941ce6c95fSDavid du Colombier free(r);
30951ce6c95fSDavid du Colombier }
30961ce6c95fSDavid du Colombier tcb->reseq = nil;
30971ce6c95fSDavid du Colombier tcb->nreseq = 0;
30981ce6c95fSDavid du Colombier tcb->reseqlen = 0;
30991ce6c95fSDavid du Colombier return -1;
31001ce6c95fSDavid du Colombier }
31011ce6c95fSDavid du Colombier
31021ce6c95fSDavid du Colombier static void
logreseq(Fs * f,Reseq * r,ulong n)31031ce6c95fSDavid du Colombier logreseq(Fs *f, Reseq *r, ulong n)
31041ce6c95fSDavid du Colombier {
31051ce6c95fSDavid du Colombier char *s;
31061ce6c95fSDavid du Colombier
31071ce6c95fSDavid du Colombier for(; r != nil; r = r->next){
31081ce6c95fSDavid du Colombier s = nil;
31091ce6c95fSDavid du Colombier if(r->next == nil && r->seg.seq != n)
31101ce6c95fSDavid du Colombier s = "hole/end";
31111ce6c95fSDavid du Colombier else if(r->next == nil)
31121ce6c95fSDavid du Colombier s = "end";
31131ce6c95fSDavid du Colombier else if(r->seg.seq != n)
31141ce6c95fSDavid du Colombier s = "hole";
31151ce6c95fSDavid du Colombier if(s != nil)
31161ce6c95fSDavid du Colombier netlog(f, Logtcp, "%s %lud-%lud (%ld) %#ux\n", s,
31171ce6c95fSDavid du Colombier n, r->seg.seq, r->seg.seq - n, r->seg.flags);
31181ce6c95fSDavid du Colombier n = r->seg.seq + r->seg.len;
31191ce6c95fSDavid du Colombier }
31201ce6c95fSDavid du Colombier }
31211ce6c95fSDavid du Colombier
31221ce6c95fSDavid du Colombier static int
addreseq(Fs * f,Tcpctl * tcb,Tcppriv * tpriv,Tcp * seg,Block * bp,ushort length)31231ce6c95fSDavid du Colombier addreseq(Fs *f, Tcpctl *tcb, Tcppriv *tpriv, Tcp *seg, Block *bp, ushort length)
31241ce6c95fSDavid du Colombier {
31251ce6c95fSDavid du Colombier Reseq *rp, **rr;
31261ce6c95fSDavid du Colombier int qmax;
31271ce6c95fSDavid du Colombier
31281ce6c95fSDavid du Colombier rp = malloc(sizeof *rp);
31297dd7cddfSDavid du Colombier if(rp == nil){
31301ce6c95fSDavid du Colombier freeblist(bp); /* bp always consumed by addreseq */
31317dd7cddfSDavid du Colombier return 0;
31327dd7cddfSDavid du Colombier }
31337dd7cddfSDavid du Colombier
31347dd7cddfSDavid du Colombier rp->seg = *seg;
31357dd7cddfSDavid du Colombier rp->bp = bp;
31367dd7cddfSDavid du Colombier rp->length = length;
31377dd7cddfSDavid du Colombier
31381ce6c95fSDavid du Colombier tcb->reseqlen += length;
31391ce6c95fSDavid du Colombier tcb->nreseq++;
31401ce6c95fSDavid du Colombier
31417dd7cddfSDavid du Colombier /* Place on reassembly list sorting by starting seq number */
31421ce6c95fSDavid du Colombier for(rr = &tcb->reseq; ; rr = &(*rr)->next)
31431ce6c95fSDavid du Colombier if(*rr == nil || seq_lt(seg->seq, (*rr)->seg.seq)){
31441ce6c95fSDavid du Colombier rp->next = *rr;
31451ce6c95fSDavid du Colombier *rr = rp;
31461ce6c95fSDavid du Colombier tpriv->stats[Resequenced]++;
31473ff48bf5SDavid du Colombier if(rp->next != nil)
31483ff48bf5SDavid du Colombier tpriv->stats[OutOfOrder]++;
31497dd7cddfSDavid du Colombier break;
31507dd7cddfSDavid du Colombier }
3151d7baab40SDavid du Colombier
31521ce6c95fSDavid du Colombier qmax = tcb->window;
31531ce6c95fSDavid du Colombier if(tcb->reseqlen > qmax){
31541ce6c95fSDavid du Colombier netlog(f, Logtcp, "tcp: reseq: queue > window: %d > %d; %d packets\n",
31551ce6c95fSDavid du Colombier tcb->reseqlen, qmax, tcb->nreseq);
31561ce6c95fSDavid du Colombier logreseq(f, tcb->reseq, tcb->rcv.nxt);
31571ce6c95fSDavid du Colombier tpriv->stats[ReseqBytelim]++;
31581ce6c95fSDavid du Colombier return dumpreseq(tcb);
3159d7baab40SDavid du Colombier }
31601ce6c95fSDavid du Colombier qmax = tcb->window / tcb->mss; /* ~190 for qscale=2, 390 for qscale=3 */
31611ce6c95fSDavid du Colombier if(tcb->nreseq > qmax){
31621ce6c95fSDavid du Colombier netlog(f, Logtcp, "resequence queue > packets: %d %d; %d bytes\n",
31631ce6c95fSDavid du Colombier tcb->nreseq, qmax, tcb->reseqlen);
31641ce6c95fSDavid du Colombier logreseq(f, tcb->reseq, tcb->rcv.nxt);
31651ce6c95fSDavid du Colombier tpriv->stats[ReseqPktlim]++;
31661ce6c95fSDavid du Colombier return dumpreseq(tcb);
31677dd7cddfSDavid du Colombier }
31687dd7cddfSDavid du Colombier return 0;
31697dd7cddfSDavid du Colombier }
31707dd7cddfSDavid du Colombier
31711ce6c95fSDavid du Colombier static void
getreseq(Tcpctl * tcb,Tcp * seg,Block ** bp,ushort * length)31727dd7cddfSDavid du Colombier getreseq(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
31737dd7cddfSDavid du Colombier {
31747dd7cddfSDavid du Colombier Reseq *rp;
31757dd7cddfSDavid du Colombier
31767dd7cddfSDavid du Colombier rp = tcb->reseq;
31777dd7cddfSDavid du Colombier if(rp == nil)
31787dd7cddfSDavid du Colombier return;
31797dd7cddfSDavid du Colombier
31807dd7cddfSDavid du Colombier tcb->reseq = rp->next;
31817dd7cddfSDavid du Colombier
31827dd7cddfSDavid du Colombier *seg = rp->seg;
31837dd7cddfSDavid du Colombier *bp = rp->bp;
31847dd7cddfSDavid du Colombier *length = rp->length;
31857dd7cddfSDavid du Colombier
31861ce6c95fSDavid du Colombier tcb->nreseq--;
31871ce6c95fSDavid du Colombier tcb->reseqlen -= rp->length;
31881ce6c95fSDavid du Colombier
31897dd7cddfSDavid du Colombier free(rp);
31907dd7cddfSDavid du Colombier }
31917dd7cddfSDavid du Colombier
31921ce6c95fSDavid du Colombier static int
tcptrim(Tcpctl * tcb,Tcp * seg,Block ** bp,ushort * length)31937dd7cddfSDavid du Colombier tcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
31947dd7cddfSDavid du Colombier {
31957dd7cddfSDavid du Colombier ushort len;
31967dd7cddfSDavid du Colombier uchar accept;
31977dd7cddfSDavid du Colombier int dupcnt, excess;
31987dd7cddfSDavid du Colombier
31997dd7cddfSDavid du Colombier accept = 0;
32007dd7cddfSDavid du Colombier len = *length;
32017dd7cddfSDavid du Colombier if(seg->flags & SYN)
32027dd7cddfSDavid du Colombier len++;
32037dd7cddfSDavid du Colombier if(seg->flags & FIN)
32047dd7cddfSDavid du Colombier len++;
32057dd7cddfSDavid du Colombier
32067dd7cddfSDavid du Colombier if(tcb->rcv.wnd == 0) {
32077dd7cddfSDavid du Colombier if(len == 0 && seg->seq == tcb->rcv.nxt)
32087dd7cddfSDavid du Colombier return 0;
32097dd7cddfSDavid du Colombier }
32107dd7cddfSDavid du Colombier else {
32117dd7cddfSDavid du Colombier /* Some part of the segment should be in the window */
32127dd7cddfSDavid du Colombier if(inwindow(tcb,seg->seq))
32137dd7cddfSDavid du Colombier accept++;
32147dd7cddfSDavid du Colombier else
32157dd7cddfSDavid du Colombier if(len != 0) {
32167dd7cddfSDavid du Colombier if(inwindow(tcb, seg->seq+len-1) ||
32177dd7cddfSDavid du Colombier seq_within(tcb->rcv.nxt, seg->seq,seg->seq+len-1))
32187dd7cddfSDavid du Colombier accept++;
32197dd7cddfSDavid du Colombier }
32207dd7cddfSDavid du Colombier }
32217dd7cddfSDavid du Colombier if(!accept) {
32227dd7cddfSDavid du Colombier freeblist(*bp);
32237dd7cddfSDavid du Colombier return -1;
32247dd7cddfSDavid du Colombier }
32257dd7cddfSDavid du Colombier dupcnt = tcb->rcv.nxt - seg->seq;
32267dd7cddfSDavid du Colombier if(dupcnt > 0){
32277dd7cddfSDavid du Colombier tcb->rerecv += dupcnt;
32287dd7cddfSDavid du Colombier if(seg->flags & SYN){
32297dd7cddfSDavid du Colombier seg->flags &= ~SYN;
32307dd7cddfSDavid du Colombier seg->seq++;
32317dd7cddfSDavid du Colombier
32327dd7cddfSDavid du Colombier if(seg->urg > 1)
32337dd7cddfSDavid du Colombier seg->urg--;
32347dd7cddfSDavid du Colombier else
32357dd7cddfSDavid du Colombier seg->flags &= ~URG;
32367dd7cddfSDavid du Colombier dupcnt--;
32377dd7cddfSDavid du Colombier }
32387dd7cddfSDavid du Colombier if(dupcnt > 0){
32397dd7cddfSDavid du Colombier pullblock(bp, (ushort)dupcnt);
32407dd7cddfSDavid du Colombier seg->seq += dupcnt;
32417dd7cddfSDavid du Colombier *length -= dupcnt;
32427dd7cddfSDavid du Colombier
32437dd7cddfSDavid du Colombier if(seg->urg > dupcnt)
32447dd7cddfSDavid du Colombier seg->urg -= dupcnt;
32457dd7cddfSDavid du Colombier else {
32467dd7cddfSDavid du Colombier seg->flags &= ~URG;
32477dd7cddfSDavid du Colombier seg->urg = 0;
32487dd7cddfSDavid du Colombier }
32497dd7cddfSDavid du Colombier }
32507dd7cddfSDavid du Colombier }
32517dd7cddfSDavid du Colombier excess = seg->seq + *length - (tcb->rcv.nxt + tcb->rcv.wnd);
32527dd7cddfSDavid du Colombier if(excess > 0) {
32537dd7cddfSDavid du Colombier tcb->rerecv += excess;
32547dd7cddfSDavid du Colombier *length -= excess;
325580ee5cbfSDavid du Colombier *bp = trimblock(*bp, 0, *length);
325680ee5cbfSDavid du Colombier if(*bp == nil)
325780ee5cbfSDavid du Colombier panic("presotto is a boofhead");
32587dd7cddfSDavid du Colombier seg->flags &= ~FIN;
32597dd7cddfSDavid du Colombier }
32607dd7cddfSDavid du Colombier return 0;
32617dd7cddfSDavid du Colombier }
32627dd7cddfSDavid du Colombier
32631ce6c95fSDavid du Colombier static void
tcpadvise(Proto * tcp,Block * bp,char * msg)32647dd7cddfSDavid du Colombier tcpadvise(Proto *tcp, Block *bp, char *msg)
32657dd7cddfSDavid du Colombier {
32663ff48bf5SDavid du Colombier Tcp4hdr *h4;
32673ff48bf5SDavid du Colombier Tcp6hdr *h6;
32687dd7cddfSDavid du Colombier Tcpctl *tcb;
32697dd7cddfSDavid du Colombier uchar source[IPaddrlen];
32707dd7cddfSDavid du Colombier uchar dest[IPaddrlen];
32717dd7cddfSDavid du Colombier ushort psource, pdest;
32727dd7cddfSDavid du Colombier Conv *s, **p;
32737dd7cddfSDavid du Colombier
32743ff48bf5SDavid du Colombier h4 = (Tcp4hdr*)(bp->rp);
32753ff48bf5SDavid du Colombier h6 = (Tcp6hdr*)(bp->rp);
32767dd7cddfSDavid du Colombier
32773ff48bf5SDavid du Colombier if((h4->vihl&0xF0)==IP_VER4) {
32783ff48bf5SDavid du Colombier v4tov6(dest, h4->tcpdst);
32793ff48bf5SDavid du Colombier v4tov6(source, h4->tcpsrc);
32803ff48bf5SDavid du Colombier psource = nhgets(h4->tcpsport);
32813ff48bf5SDavid du Colombier pdest = nhgets(h4->tcpdport);
32823ff48bf5SDavid du Colombier }
32833ff48bf5SDavid du Colombier else {
32843ff48bf5SDavid du Colombier ipmove(dest, h6->tcpdst);
32853ff48bf5SDavid du Colombier ipmove(source, h6->tcpsrc);
32863ff48bf5SDavid du Colombier psource = nhgets(h6->tcpsport);
32873ff48bf5SDavid du Colombier pdest = nhgets(h6->tcpdport);
32883ff48bf5SDavid du Colombier }
32897dd7cddfSDavid du Colombier
32907dd7cddfSDavid du Colombier /* Look for a connection */
32917dd7cddfSDavid du Colombier qlock(tcp);
32927dd7cddfSDavid du Colombier for(p = tcp->conv; *p; p++) {
32937dd7cddfSDavid du Colombier s = *p;
32947dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
32957dd7cddfSDavid du Colombier if(s->rport == pdest)
32967dd7cddfSDavid du Colombier if(s->lport == psource)
32977dd7cddfSDavid du Colombier if(tcb->state != Closed)
32987dd7cddfSDavid du Colombier if(ipcmp(s->raddr, dest) == 0)
32997dd7cddfSDavid du Colombier if(ipcmp(s->laddr, source) == 0){
33007dd7cddfSDavid du Colombier qlock(s);
33017dd7cddfSDavid du Colombier qunlock(tcp);
33027dd7cddfSDavid du Colombier switch(tcb->state){
33037dd7cddfSDavid du Colombier case Syn_sent:
33047dd7cddfSDavid du Colombier localclose(s, msg);
33057dd7cddfSDavid du Colombier break;
33067dd7cddfSDavid du Colombier }
33077dd7cddfSDavid du Colombier qunlock(s);
33087dd7cddfSDavid du Colombier freeblist(bp);
33097dd7cddfSDavid du Colombier return;
33107dd7cddfSDavid du Colombier }
33117dd7cddfSDavid du Colombier }
33127dd7cddfSDavid du Colombier qunlock(tcp);
33137dd7cddfSDavid du Colombier freeblist(bp);
33147dd7cddfSDavid du Colombier }
33157dd7cddfSDavid du Colombier
33163f695129SDavid du Colombier static char*
tcpporthogdefensectl(char * val)33173f695129SDavid du Colombier tcpporthogdefensectl(char *val)
33183f695129SDavid du Colombier {
33193f695129SDavid du Colombier if(strcmp(val, "on") == 0)
33203f695129SDavid du Colombier tcpporthogdefense = 1;
33213f695129SDavid du Colombier else if(strcmp(val, "off") == 0)
33223f695129SDavid du Colombier tcpporthogdefense = 0;
33233f695129SDavid du Colombier else
33243f695129SDavid du Colombier return "unknown value for tcpporthogdefense";
33253f695129SDavid du Colombier return nil;
33263f695129SDavid du Colombier }
33273f695129SDavid du Colombier
332880ee5cbfSDavid du Colombier /* called with c qlocked */
33291ce6c95fSDavid du Colombier static char*
tcpctl(Conv * c,char ** f,int n)33307dd7cddfSDavid du Colombier tcpctl(Conv* c, char** f, int n)
33317dd7cddfSDavid du Colombier {
3332*41aa7335SDavid du Colombier if(n == 1 && strcmp(f[0], "close") == 0)
3333*41aa7335SDavid du Colombier return tcpclose2(c);
33347dd7cddfSDavid du Colombier if(n == 1 && strcmp(f[0], "hangup") == 0)
33357dd7cddfSDavid du Colombier return tcphangup(c);
3336ddc91d4cSDavid du Colombier if(n == 1 && strcmp(f[0], "hangupxmit") == 0)
3337ddc91d4cSDavid du Colombier return tcpxmitclose(c);
33387dd7cddfSDavid du Colombier if(n >= 1 && strcmp(f[0], "keepalive") == 0)
33397dd7cddfSDavid du Colombier return tcpstartka(c, f, n);
334059cc4ca5SDavid du Colombier if(n >= 1 && strcmp(f[0], "checksum") == 0)
334159cc4ca5SDavid du Colombier return tcpsetchecksum(c, f, n);
33423f695129SDavid du Colombier if(n >= 1 && strcmp(f[0], "tcpporthogdefense") == 0)
33433f695129SDavid du Colombier return tcpporthogdefensectl(f[1]);
33447dd7cddfSDavid du Colombier return "unknown control request";
33457dd7cddfSDavid du Colombier }
33467dd7cddfSDavid du Colombier
33471ce6c95fSDavid du Colombier static int
tcpstats(Proto * tcp,char * buf,int len)33487dd7cddfSDavid du Colombier tcpstats(Proto *tcp, char *buf, int len)
33497dd7cddfSDavid du Colombier {
335059cc4ca5SDavid du Colombier Tcppriv *priv;
335159cc4ca5SDavid du Colombier char *p, *e;
335259cc4ca5SDavid du Colombier int i;
33537dd7cddfSDavid du Colombier
335459cc4ca5SDavid du Colombier priv = tcp->priv;
335559cc4ca5SDavid du Colombier p = buf;
335659cc4ca5SDavid du Colombier e = p+len;
335759cc4ca5SDavid du Colombier for(i = 0; i < Nstats; i++)
33585e27dea9SDavid du Colombier p = seprint(p, e, "%s: %llud\n", statnames[i], priv->stats[i]);
335959cc4ca5SDavid du Colombier return p - buf;
33607dd7cddfSDavid du Colombier }
33617dd7cddfSDavid du Colombier
33627dd7cddfSDavid du Colombier /*
33637dd7cddfSDavid du Colombier * garbage collect any stale conversations:
33647dd7cddfSDavid du Colombier * - SYN received but no SYN-ACK after 5 seconds (could be the SYN attack)
33657dd7cddfSDavid du Colombier * - Finwait2 after 5 minutes
33667dd7cddfSDavid du Colombier *
33677dd7cddfSDavid du Colombier * this is called whenever we run out of channels. Both checks are
33687dd7cddfSDavid du Colombier * of questionable validity so we try to use them only when we're
33697dd7cddfSDavid du Colombier * up against the wall.
33707dd7cddfSDavid du Colombier */
33711ce6c95fSDavid du Colombier static int
tcpgc(Proto * tcp)33727dd7cddfSDavid du Colombier tcpgc(Proto *tcp)
33737dd7cddfSDavid du Colombier {
33747dd7cddfSDavid du Colombier Conv *c, **pp, **ep;
33757dd7cddfSDavid du Colombier int n;
33767dd7cddfSDavid du Colombier Tcpctl *tcb;
33777dd7cddfSDavid du Colombier
33787dd7cddfSDavid du Colombier
33797dd7cddfSDavid du Colombier n = 0;
33807dd7cddfSDavid du Colombier ep = &tcp->conv[tcp->nc];
33817dd7cddfSDavid du Colombier for(pp = tcp->conv; pp < ep; pp++) {
33827dd7cddfSDavid du Colombier c = *pp;
33837dd7cddfSDavid du Colombier if(c == nil)
33847dd7cddfSDavid du Colombier break;
33857dd7cddfSDavid du Colombier if(!canqlock(c))
33867dd7cddfSDavid du Colombier continue;
33877dd7cddfSDavid du Colombier tcb = (Tcpctl*)c->ptcl;
33887dd7cddfSDavid du Colombier switch(tcb->state){
33897dd7cddfSDavid du Colombier case Syn_received:
33903ff48bf5SDavid du Colombier if(NOW - tcb->time > 5000){
33911ce6c95fSDavid du Colombier localclose(c, Etimedout);
33927dd7cddfSDavid du Colombier n++;
33937dd7cddfSDavid du Colombier }
33947dd7cddfSDavid du Colombier break;
33957dd7cddfSDavid du Colombier case Finwait2:
33963ff48bf5SDavid du Colombier if(NOW - tcb->time > 5*60*1000){
33971ce6c95fSDavid du Colombier localclose(c, Etimedout);
33987dd7cddfSDavid du Colombier n++;
33997dd7cddfSDavid du Colombier }
34007dd7cddfSDavid du Colombier break;
34017dd7cddfSDavid du Colombier }
34027dd7cddfSDavid du Colombier qunlock(c);
34037dd7cddfSDavid du Colombier }
34047dd7cddfSDavid du Colombier return n;
34057dd7cddfSDavid du Colombier }
34067dd7cddfSDavid du Colombier
34071ce6c95fSDavid du Colombier static void
tcpsettimer(Tcpctl * tcb)34089a747e4fSDavid du Colombier tcpsettimer(Tcpctl *tcb)
34099a747e4fSDavid du Colombier {
34109a747e4fSDavid du Colombier int x;
34119a747e4fSDavid du Colombier
34121b0f96a1SDavid du Colombier /* round trip dependency */
34139a747e4fSDavid du Colombier x = backoff(tcb->backoff) *
34149a747e4fSDavid du Colombier (tcb->mdev + (tcb->srtt>>LOGAGAIN) + MSPTICK) / MSPTICK;
34159a747e4fSDavid du Colombier
34161ce6c95fSDavid du Colombier /* bounded twixt 0.3 and 64 seconds */
34171ce6c95fSDavid du Colombier if(x < 300/MSPTICK)
34181ce6c95fSDavid du Colombier x = 300/MSPTICK;
34191b0f96a1SDavid du Colombier else if(x > (64000/MSPTICK))
34201b0f96a1SDavid du Colombier x = 64000/MSPTICK;
34219a747e4fSDavid du Colombier tcb->timer.start = x;
34229a747e4fSDavid du Colombier }
34239a747e4fSDavid du Colombier
34249a747e4fSDavid du Colombier void
tcpinit(Fs * fs)34257dd7cddfSDavid du Colombier tcpinit(Fs *fs)
34267dd7cddfSDavid du Colombier {
34277dd7cddfSDavid du Colombier Proto *tcp;
34287dd7cddfSDavid du Colombier Tcppriv *tpriv;
34297dd7cddfSDavid du Colombier
34307dd7cddfSDavid du Colombier tcp = smalloc(sizeof(Proto));
34317dd7cddfSDavid du Colombier tpriv = tcp->priv = smalloc(sizeof(Tcppriv));
34327dd7cddfSDavid du Colombier tcp->name = "tcp";
34337dd7cddfSDavid du Colombier tcp->connect = tcpconnect;
34347dd7cddfSDavid du Colombier tcp->announce = tcpannounce;
34357dd7cddfSDavid du Colombier tcp->ctl = tcpctl;
34367dd7cddfSDavid du Colombier tcp->state = tcpstate;
34377dd7cddfSDavid du Colombier tcp->create = tcpcreate;
34387dd7cddfSDavid du Colombier tcp->close = tcpclose;
34397dd7cddfSDavid du Colombier tcp->rcv = tcpiput;
34407dd7cddfSDavid du Colombier tcp->advise = tcpadvise;
34417dd7cddfSDavid du Colombier tcp->stats = tcpstats;
34427dd7cddfSDavid du Colombier tcp->inuse = tcpinuse;
34437dd7cddfSDavid du Colombier tcp->gc = tcpgc;
34447dd7cddfSDavid du Colombier tcp->ipproto = IP_TCPPROTO;
34459a747e4fSDavid du Colombier tcp->nc = scalednconv();
34467dd7cddfSDavid du Colombier tcp->ptclsize = sizeof(Tcpctl);
34479a747e4fSDavid du Colombier tpriv->stats[MaxConn] = tcp->nc;
34487dd7cddfSDavid du Colombier
34497dd7cddfSDavid du Colombier Fsproto(fs, tcp);
34507dd7cddfSDavid du Colombier }
34513f695129SDavid du Colombier
34521ce6c95fSDavid du Colombier static void
tcpsetscale(Conv * s,Tcpctl * tcb,ushort rcvscale,ushort sndscale)34533f695129SDavid du Colombier tcpsetscale(Conv *s, Tcpctl *tcb, ushort rcvscale, ushort sndscale)
34543f695129SDavid du Colombier {
34551ce6c95fSDavid du Colombier /*
34561ce6c95fSDavid du Colombier * guess at reasonable queue sizes. there's no current way
34571ce6c95fSDavid du Colombier * to know how many nic receive buffers we can safely tie up in the
34581ce6c95fSDavid du Colombier * tcp stack, and we don't adjust our queues to maximize throughput
34591ce6c95fSDavid du Colombier * and minimize bufferbloat. n.b. the offer (rcvscale) needs to be
34601ce6c95fSDavid du Colombier * respected, but we still control our own buffer commitment by
34611ce6c95fSDavid du Colombier * keeping a seperate qscale.
34621ce6c95fSDavid du Colombier */
34633f695129SDavid du Colombier tcb->rcv.scale = rcvscale & 0xff;
34643f695129SDavid du Colombier tcb->snd.scale = sndscale & 0xff;
34651ce6c95fSDavid du Colombier tcb->qscale = rcvscale & 0xff;
34661ce6c95fSDavid du Colombier if(rcvscale > Maxqscale)
34671ce6c95fSDavid du Colombier tcb->qscale = Maxqscale;
34681ce6c95fSDavid du Colombier
34691ce6c95fSDavid du Colombier if(rcvscale != tcb->rcv.scale)
34701ce6c95fSDavid du Colombier netlog(s->p->f, Logtcp, "tcpsetscale: window %lud "
34711ce6c95fSDavid du Colombier "qlen %d >> window %ud lport %d\n",
34721ce6c95fSDavid du Colombier tcb->window, qlen(s->rq), QMAX<<tcb->qscale, s->lport);
34731ce6c95fSDavid du Colombier tcb->window = QMAX << tcb->qscale;
34741ce6c95fSDavid du Colombier tcb->ssthresh = tcb->window;
34751ce6c95fSDavid du Colombier
34761ce6c95fSDavid du Colombier /*
34771ce6c95fSDavid du Colombier * it's important to set wq large enough to cover the full
34781ce6c95fSDavid du Colombier * bandwidth-delay product. it's possible to be in loss
34791ce6c95fSDavid du Colombier * recovery with a big window, and we need to keep sending
34801ce6c95fSDavid du Colombier * into the inflated window. the difference can be huge
34811ce6c95fSDavid du Colombier * for even modest (70ms) ping times.
34821ce6c95fSDavid du Colombier */
34833f695129SDavid du Colombier qsetlimit(s->rq, tcb->window);
34841ce6c95fSDavid du Colombier qsetlimit(s->wq, tcb->window);
34851ce6c95fSDavid du Colombier tcprcvwin(s);
34863f695129SDavid du Colombier }
3487