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];
131*8e860520SDavid 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];
153*8e860520SDavid 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
54280ee5cbfSDavid du Colombier /*
54380ee5cbfSDavid du Colombier * tcpclose is always called with the q locked
54480ee5cbfSDavid du Colombier */
5457dd7cddfSDavid du Colombier static void
tcpclose(Conv * c)5467dd7cddfSDavid du Colombier tcpclose(Conv *c)
5477dd7cddfSDavid du Colombier {
5487dd7cddfSDavid du Colombier Tcpctl *tcb;
5497dd7cddfSDavid du Colombier
5507dd7cddfSDavid du Colombier tcb = (Tcpctl*)c->ptcl;
5517dd7cddfSDavid du Colombier
5527dd7cddfSDavid du Colombier qhangup(c->rq, nil);
5537dd7cddfSDavid du Colombier qhangup(c->wq, nil);
5547dd7cddfSDavid du Colombier qhangup(c->eq, nil);
55580ee5cbfSDavid du Colombier qflush(c->rq);
5567dd7cddfSDavid du Colombier
5577dd7cddfSDavid du Colombier switch(tcb->state) {
5587dd7cddfSDavid du Colombier case Listen:
5597dd7cddfSDavid du Colombier /*
5607dd7cddfSDavid du Colombier * reset any incoming calls to this listener
5617dd7cddfSDavid du Colombier */
5627dd7cddfSDavid du Colombier Fsconnected(c, "Hangup");
5637dd7cddfSDavid du Colombier
5647dd7cddfSDavid du Colombier localclose(c, nil);
5657dd7cddfSDavid du Colombier break;
5667dd7cddfSDavid du Colombier case Closed:
5677dd7cddfSDavid du Colombier case Syn_sent:
5687dd7cddfSDavid du Colombier localclose(c, nil);
5697dd7cddfSDavid du Colombier break;
5707dd7cddfSDavid du Colombier case Syn_received:
5717dd7cddfSDavid du Colombier case Established:
57280ee5cbfSDavid du Colombier tcb->flgcnt++;
5737dd7cddfSDavid du Colombier tcb->snd.nxt++;
5747dd7cddfSDavid du Colombier tcpsetstate(c, Finwait1);
5757dd7cddfSDavid du Colombier tcpoutput(c);
5767dd7cddfSDavid du Colombier break;
5777dd7cddfSDavid du Colombier case Close_wait:
57880ee5cbfSDavid du Colombier tcb->flgcnt++;
5797dd7cddfSDavid du Colombier tcb->snd.nxt++;
5807dd7cddfSDavid du Colombier tcpsetstate(c, Last_ack);
5817dd7cddfSDavid du Colombier tcpoutput(c);
5827dd7cddfSDavid du Colombier break;
5837dd7cddfSDavid du Colombier }
5847dd7cddfSDavid du Colombier }
5857dd7cddfSDavid du Colombier
5861ce6c95fSDavid du Colombier static void
tcpkick(void * x)5873ff48bf5SDavid du Colombier tcpkick(void *x)
5887dd7cddfSDavid du Colombier {
5893ff48bf5SDavid du Colombier Conv *s = x;
5907dd7cddfSDavid du Colombier Tcpctl *tcb;
5917dd7cddfSDavid du Colombier
5927dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
5937dd7cddfSDavid du Colombier
59480ee5cbfSDavid du Colombier if(waserror()){
59580ee5cbfSDavid du Colombier qunlock(s);
59680ee5cbfSDavid du Colombier nexterror();
59780ee5cbfSDavid du Colombier }
59880ee5cbfSDavid du Colombier qlock(s);
5997dd7cddfSDavid du Colombier
6007dd7cddfSDavid du Colombier switch(tcb->state) {
6017dd7cddfSDavid du Colombier case Syn_sent:
6027dd7cddfSDavid du Colombier case Syn_received:
6037dd7cddfSDavid du Colombier case Established:
6047dd7cddfSDavid du Colombier case Close_wait:
6057dd7cddfSDavid du Colombier /*
6067dd7cddfSDavid du Colombier * Push data
6077dd7cddfSDavid du Colombier */
6087dd7cddfSDavid du Colombier tcpoutput(s);
6097dd7cddfSDavid du Colombier break;
6107dd7cddfSDavid du Colombier default:
6117dd7cddfSDavid du Colombier localclose(s, "Hangup");
61280ee5cbfSDavid du Colombier break;
6137dd7cddfSDavid du Colombier }
61480ee5cbfSDavid du Colombier
61580ee5cbfSDavid du Colombier qunlock(s);
61680ee5cbfSDavid du Colombier poperror();
6177dd7cddfSDavid du Colombier }
6187dd7cddfSDavid du Colombier
6191ce6c95fSDavid du Colombier static int seq_lt(ulong, ulong);
6201ce6c95fSDavid du Colombier
6211ce6c95fSDavid du Colombier static void
tcprcvwin(Conv * s)6227dd7cddfSDavid du Colombier tcprcvwin(Conv *s) /* Call with tcb locked */
6237dd7cddfSDavid du Colombier {
6247dd7cddfSDavid du Colombier int w;
6257dd7cddfSDavid du Colombier Tcpctl *tcb;
6267dd7cddfSDavid du Colombier
6277dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
6283f695129SDavid du Colombier w = tcb->window - qlen(s->rq);
6297dd7cddfSDavid du Colombier if(w < 0)
6307dd7cddfSDavid du Colombier w = 0;
6311ce6c95fSDavid du Colombier /* RFC 1122 § 4.2.2.17 do not move right edge of window left */
6321ce6c95fSDavid du Colombier if(seq_lt(tcb->rcv.nxt + w, tcb->rcv.wptr))
6331ce6c95fSDavid du Colombier w = tcb->rcv.wptr - tcb->rcv.nxt;
6341ce6c95fSDavid du Colombier if(w != tcb->rcv.wnd)
6351ce6c95fSDavid du Colombier if(w>>tcb->rcv.scale == 0 || tcb->window > 4*tcb->mss && w < tcb->mss/4){
6367dd7cddfSDavid du Colombier tcb->rcv.blocked = 1;
6371ce6c95fSDavid du Colombier netlog(s->p->f, Logtcp, "tcprcvwin: window %lud qlen %d ws %ud lport %d\n",
6381ce6c95fSDavid du Colombier tcb->window, qlen(s->rq), tcb->rcv.scale, s->lport);
6391ce6c95fSDavid du Colombier }
6401ce6c95fSDavid du Colombier tcb->rcv.wnd = w;
6411ce6c95fSDavid du Colombier tcb->rcv.wptr = tcb->rcv.nxt + w;
6427dd7cddfSDavid du Colombier }
6437dd7cddfSDavid du Colombier
6441ce6c95fSDavid du Colombier static void
tcpacktimer(void * v)6457dd7cddfSDavid du Colombier tcpacktimer(void *v)
6467dd7cddfSDavid du Colombier {
6477dd7cddfSDavid du Colombier Tcpctl *tcb;
6487dd7cddfSDavid du Colombier Conv *s;
6497dd7cddfSDavid du Colombier
6507dd7cddfSDavid du Colombier s = v;
6517dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
6527dd7cddfSDavid du Colombier
6537dd7cddfSDavid du Colombier if(waserror()){
6547dd7cddfSDavid du Colombier qunlock(s);
6557dd7cddfSDavid du Colombier nexterror();
6567dd7cddfSDavid du Colombier }
6577dd7cddfSDavid du Colombier qlock(s);
6587dd7cddfSDavid du Colombier if(tcb->state != Closed){
6597dd7cddfSDavid du Colombier tcb->flags |= FORCE;
6607dd7cddfSDavid du Colombier tcpoutput(s);
6617dd7cddfSDavid du Colombier }
6627dd7cddfSDavid du Colombier qunlock(s);
6637dd7cddfSDavid du Colombier poperror();
6647dd7cddfSDavid du Colombier }
6657dd7cddfSDavid du Colombier
6667dd7cddfSDavid du Colombier static void
tcpcongestion(Tcpctl * tcb)6671ce6c95fSDavid du Colombier tcpcongestion(Tcpctl *tcb)
6681ce6c95fSDavid du Colombier {
6691ce6c95fSDavid du Colombier ulong inflight;
6701ce6c95fSDavid du Colombier
6711ce6c95fSDavid du Colombier inflight = tcb->snd.nxt - tcb->snd.una;
6721ce6c95fSDavid du Colombier if(inflight > tcb->cwind)
6731ce6c95fSDavid du Colombier inflight = tcb->cwind;
6741ce6c95fSDavid du Colombier tcb->ssthresh = inflight / 2;
6751ce6c95fSDavid du Colombier if(tcb->ssthresh < 2*tcb->mss)
6761ce6c95fSDavid du Colombier tcb->ssthresh = 2*tcb->mss;
6771ce6c95fSDavid du Colombier }
6781ce6c95fSDavid du Colombier
6791ce6c95fSDavid du Colombier enum {
6801ce6c95fSDavid du Colombier L = 2, /* aggressive slow start; legal values ∈ (1.0, 2.0) */
6811ce6c95fSDavid du Colombier };
6821ce6c95fSDavid du Colombier
6831ce6c95fSDavid du Colombier static void
tcpabcincr(Tcpctl * tcb,uint acked)6841ce6c95fSDavid du Colombier tcpabcincr(Tcpctl *tcb, uint acked)
6851ce6c95fSDavid du Colombier {
6861ce6c95fSDavid du Colombier uint limit;
6871ce6c95fSDavid du Colombier
6881ce6c95fSDavid du Colombier tcb->abcbytes += acked;
6891ce6c95fSDavid du Colombier if(tcb->cwind < tcb->ssthresh){
6901ce6c95fSDavid du Colombier /* slow start */
6911ce6c95fSDavid du Colombier if(tcb->snd.rto)
6921ce6c95fSDavid du Colombier limit = tcb->mss;
6931ce6c95fSDavid du Colombier else
6941ce6c95fSDavid du Colombier limit = L*tcb->mss;
6951ce6c95fSDavid du Colombier tcb->cwind += MIN(tcb->abcbytes, limit);
6961ce6c95fSDavid du Colombier tcb->abcbytes = 0;
6971ce6c95fSDavid du Colombier } else {
6981ce6c95fSDavid du Colombier tcb->snd.rto = 0;
6991ce6c95fSDavid du Colombier /* avoidance */
7001ce6c95fSDavid du Colombier if(tcb->abcbytes >= tcb->cwind){
7011ce6c95fSDavid du Colombier tcb->abcbytes -= tcb->cwind;
7021ce6c95fSDavid du Colombier tcb->cwind += tcb->mss;
7031ce6c95fSDavid du Colombier }
7041ce6c95fSDavid du Colombier }
7051ce6c95fSDavid du Colombier }
7061ce6c95fSDavid du Colombier
7071ce6c95fSDavid du Colombier static void
tcpcreate(Conv * c)7087dd7cddfSDavid du Colombier tcpcreate(Conv *c)
7097dd7cddfSDavid du Colombier {
7103ff48bf5SDavid du Colombier c->rq = qopen(QMAX, Qcoalesce, tcpacktimer, c);
7111ce6c95fSDavid du Colombier c->wq = qopen(QMAX, Qkick, tcpkick, c);
7127dd7cddfSDavid du Colombier }
7137dd7cddfSDavid du Colombier
7147dd7cddfSDavid du Colombier static void
timerstate(Tcppriv * priv,Tcptimer * t,int newstate)7159a747e4fSDavid du Colombier timerstate(Tcppriv *priv, Tcptimer *t, int newstate)
7167dd7cddfSDavid du Colombier {
7179a747e4fSDavid du Colombier if(newstate != TcptimerON){
7189a747e4fSDavid du Colombier if(t->state == TcptimerON){
71941dd6b47SDavid du Colombier /* unchain */
7207dd7cddfSDavid du Colombier if(priv->timers == t){
7217dd7cddfSDavid du Colombier priv->timers = t->next;
7227dd7cddfSDavid du Colombier if(t->prev != nil)
7237dd7cddfSDavid du Colombier panic("timerstate1");
7247dd7cddfSDavid du Colombier }
7257dd7cddfSDavid du Colombier if(t->next)
7267dd7cddfSDavid du Colombier t->next->prev = t->prev;
7277dd7cddfSDavid du Colombier if(t->prev)
7287dd7cddfSDavid du Colombier t->prev->next = t->next;
7297dd7cddfSDavid du Colombier t->next = t->prev = nil;
7307dd7cddfSDavid du Colombier }
7317dd7cddfSDavid du Colombier } else {
7329a747e4fSDavid du Colombier if(t->state != TcptimerON){
73341dd6b47SDavid du Colombier /* chain */
7347dd7cddfSDavid du Colombier if(t->prev != nil || t->next != nil)
7357dd7cddfSDavid du Colombier panic("timerstate2");
7367dd7cddfSDavid du Colombier t->prev = nil;
7377dd7cddfSDavid du Colombier t->next = priv->timers;
7387dd7cddfSDavid du Colombier if(t->next)
7397dd7cddfSDavid du Colombier t->next->prev = t;
7407dd7cddfSDavid du Colombier priv->timers = t;
7417dd7cddfSDavid du Colombier }
7427dd7cddfSDavid du Colombier }
7437dd7cddfSDavid du Colombier t->state = newstate;
7447dd7cddfSDavid du Colombier }
7457dd7cddfSDavid du Colombier
7461ce6c95fSDavid du Colombier static void
tcpackproc(void * a)7477dd7cddfSDavid du Colombier tcpackproc(void *a)
7487dd7cddfSDavid du Colombier {
7499a747e4fSDavid du Colombier Tcptimer *t, *tp, *timeo;
7507dd7cddfSDavid du Colombier Proto *tcp;
7517dd7cddfSDavid du Colombier Tcppriv *priv;
7527dd7cddfSDavid du Colombier int loop;
7537dd7cddfSDavid du Colombier
7547dd7cddfSDavid du Colombier tcp = a;
7557dd7cddfSDavid du Colombier priv = tcp->priv;
7567dd7cddfSDavid du Colombier
7577dd7cddfSDavid du Colombier for(;;) {
758dc5a79c1SDavid du Colombier tsleep(&up->sleep, return0, 0, MSPTICK);
7597dd7cddfSDavid du Colombier
7607dd7cddfSDavid du Colombier qlock(&priv->tl);
7617dd7cddfSDavid du Colombier timeo = nil;
7627dd7cddfSDavid du Colombier loop = 0;
7637dd7cddfSDavid du Colombier for(t = priv->timers; t != nil; t = tp) {
7647dd7cddfSDavid du Colombier if(loop++ > 10000)
7657dd7cddfSDavid du Colombier panic("tcpackproc1");
7667dd7cddfSDavid du Colombier tp = t->next;
7679a747e4fSDavid du Colombier if(t->state == TcptimerON) {
7687dd7cddfSDavid du Colombier t->count--;
7697dd7cddfSDavid du Colombier if(t->count == 0) {
7709a747e4fSDavid du Colombier timerstate(priv, t, TcptimerDONE);
7717dd7cddfSDavid du Colombier t->readynext = timeo;
7727dd7cddfSDavid du Colombier timeo = t;
7737dd7cddfSDavid du Colombier }
7747dd7cddfSDavid du Colombier }
7757dd7cddfSDavid du Colombier }
7767dd7cddfSDavid du Colombier qunlock(&priv->tl);
7777dd7cddfSDavid du Colombier
7787dd7cddfSDavid du Colombier loop = 0;
7797dd7cddfSDavid du Colombier for(t = timeo; t != nil; t = t->readynext) {
7807dd7cddfSDavid du Colombier if(loop++ > 10000)
7817dd7cddfSDavid du Colombier panic("tcpackproc2");
7829a747e4fSDavid du Colombier if(t->state == TcptimerDONE && t->func != nil && !waserror()){
7837dd7cddfSDavid du Colombier (*t->func)(t->arg);
7849a747e4fSDavid du Colombier poperror();
7859a747e4fSDavid du Colombier }
7867dd7cddfSDavid du Colombier }
7873ff48bf5SDavid du Colombier
7883ff48bf5SDavid du Colombier limborexmit(tcp);
7897dd7cddfSDavid du Colombier }
7907dd7cddfSDavid du Colombier }
7917dd7cddfSDavid du Colombier
7921ce6c95fSDavid du Colombier static void
tcpgo(Tcppriv * priv,Tcptimer * t)7939a747e4fSDavid du Colombier tcpgo(Tcppriv *priv, Tcptimer *t)
7947dd7cddfSDavid du Colombier {
7957dd7cddfSDavid du Colombier if(t == nil || t->start == 0)
7967dd7cddfSDavid du Colombier return;
7977dd7cddfSDavid du Colombier
7987dd7cddfSDavid du Colombier qlock(&priv->tl);
7997dd7cddfSDavid du Colombier t->count = t->start;
8009a747e4fSDavid du Colombier timerstate(priv, t, TcptimerON);
8017dd7cddfSDavid du Colombier qunlock(&priv->tl);
8027dd7cddfSDavid du Colombier }
8037dd7cddfSDavid du Colombier
8041ce6c95fSDavid du Colombier static void
tcphalt(Tcppriv * priv,Tcptimer * t)8059a747e4fSDavid du Colombier tcphalt(Tcppriv *priv, Tcptimer *t)
8067dd7cddfSDavid du Colombier {
8077dd7cddfSDavid du Colombier if(t == nil)
8087dd7cddfSDavid du Colombier return;
8097dd7cddfSDavid du Colombier
8107dd7cddfSDavid du Colombier qlock(&priv->tl);
8119a747e4fSDavid du Colombier timerstate(priv, t, TcptimerOFF);
8127dd7cddfSDavid du Colombier qunlock(&priv->tl);
8137dd7cddfSDavid du Colombier }
8147dd7cddfSDavid du Colombier
8151ce6c95fSDavid du Colombier static int
backoff(int n)8167dd7cddfSDavid du Colombier backoff(int n)
8177dd7cddfSDavid du Colombier {
8187dd7cddfSDavid du Colombier return 1 << n;
8197dd7cddfSDavid du Colombier }
8207dd7cddfSDavid du Colombier
8211ce6c95fSDavid du Colombier static void
localclose(Conv * s,char * reason)8227dd7cddfSDavid du Colombier localclose(Conv *s, char *reason) /* called with tcb locked */
8237dd7cddfSDavid du Colombier {
8247dd7cddfSDavid du Colombier Tcpctl *tcb;
8257dd7cddfSDavid du Colombier Tcppriv *tpriv;
8267dd7cddfSDavid du Colombier
8277dd7cddfSDavid du Colombier tpriv = s->p->priv;
8287dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
8297dd7cddfSDavid du Colombier
83080ee5cbfSDavid du Colombier iphtrem(&tpriv->ht, s);
83180ee5cbfSDavid du Colombier
8327dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->timer);
8337dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
8347dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
8357dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->katimer);
8367dd7cddfSDavid du Colombier
8377dd7cddfSDavid du Colombier /* Flush reassembly queue; nothing more can arrive */
8381ce6c95fSDavid du Colombier dumpreseq(tcb);
8397dd7cddfSDavid du Colombier
8407dd7cddfSDavid du Colombier if(tcb->state == Syn_sent)
8417dd7cddfSDavid du Colombier Fsconnected(s, reason);
8427dd7cddfSDavid du Colombier if(s->state == Announced)
8437dd7cddfSDavid du Colombier wakeup(&s->listenr);
8447dd7cddfSDavid du Colombier
8457dd7cddfSDavid du Colombier qhangup(s->rq, reason);
8467dd7cddfSDavid du Colombier qhangup(s->wq, reason);
8477dd7cddfSDavid du Colombier
8487dd7cddfSDavid du Colombier tcpsetstate(s, Closed);
8497dd7cddfSDavid du Colombier }
8507dd7cddfSDavid du Colombier
8517dd7cddfSDavid du Colombier /* mtu (- TCP + IP hdr len) of 1st hop */
8521ce6c95fSDavid du Colombier static int
tcpmtu(Proto * tcp,uchar * addr,int version,uint * scale)8531ce6c95fSDavid du Colombier tcpmtu(Proto *tcp, uchar *addr, int version, uint *scale)
8547dd7cddfSDavid du Colombier {
8557dd7cddfSDavid du Colombier Ipifc *ifc;
8567dd7cddfSDavid du Colombier int mtu;
8577dd7cddfSDavid du Colombier
8583ff48bf5SDavid du Colombier ifc = findipifc(tcp->f, addr, 0);
8593ff48bf5SDavid du Colombier switch(version){
8603ff48bf5SDavid du Colombier default:
8613ff48bf5SDavid du Colombier case V4:
8627dd7cddfSDavid du Colombier mtu = DEF_MSS;
8633ff48bf5SDavid du Colombier if(ifc != nil)
8643f695129SDavid du Colombier mtu = ifc->maxtu - ifc->m->hsize - (TCP4_PKT + TCP4_HDRSIZE);
8653ff48bf5SDavid du Colombier break;
8663ff48bf5SDavid du Colombier case V6:
8673ff48bf5SDavid du Colombier mtu = DEF_MSS6;
8683ff48bf5SDavid du Colombier if(ifc != nil)
8693f695129SDavid du Colombier mtu = ifc->maxtu - ifc->m->hsize - (TCP6_PKT + TCP6_HDRSIZE);
8703ff48bf5SDavid du Colombier break;
8713ff48bf5SDavid du Colombier }
8721ce6c95fSDavid du Colombier /*
8731ce6c95fSDavid du Colombier * set the ws. it doesn't commit us to anything.
8741ce6c95fSDavid du Colombier * ws is the ultimate limit to the bandwidth-delay product.
8751ce6c95fSDavid du Colombier */
8761ce6c95fSDavid du Colombier *scale = Defadvscale;
8773ff48bf5SDavid du Colombier
8783ff48bf5SDavid du Colombier return mtu;
8797dd7cddfSDavid du Colombier }
8807dd7cddfSDavid du Colombier
8811ce6c95fSDavid du Colombier static void
inittcpctl(Conv * s,int mode)88280ee5cbfSDavid du Colombier inittcpctl(Conv *s, int mode)
8837dd7cddfSDavid du Colombier {
8847dd7cddfSDavid du Colombier Tcpctl *tcb;
8853ff48bf5SDavid du Colombier Tcp4hdr* h4;
8863ff48bf5SDavid du Colombier Tcp6hdr* h6;
8877ec5746aSDavid du Colombier Tcppriv *tpriv;
8883f695129SDavid du Colombier int mss;
8897dd7cddfSDavid du Colombier
8907dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
8917dd7cddfSDavid du Colombier
8927dd7cddfSDavid du Colombier memset(tcb, 0, sizeof(Tcpctl));
8937dd7cddfSDavid du Colombier
8941ce6c95fSDavid du Colombier tcb->ssthresh = QMAX; /* reset by tcpsetscale() */
89580ee5cbfSDavid du Colombier tcb->srtt = tcp_irtt<<LOGAGAIN;
89680ee5cbfSDavid du Colombier tcb->mdev = 0;
8977dd7cddfSDavid du Colombier
89880ee5cbfSDavid du Colombier /* setup timers */
8997dd7cddfSDavid du Colombier tcb->timer.start = tcp_irtt / MSPTICK;
9007dd7cddfSDavid du Colombier tcb->timer.func = tcptimeout;
9017dd7cddfSDavid du Colombier tcb->timer.arg = s;
9027dd7cddfSDavid du Colombier tcb->rtt_timer.start = MAX_TIME;
9037dd7cddfSDavid du Colombier tcb->acktimer.start = TCP_ACK / MSPTICK;
9047dd7cddfSDavid du Colombier tcb->acktimer.func = tcpacktimer;
9057dd7cddfSDavid du Colombier tcb->acktimer.arg = s;
9067dd7cddfSDavid du Colombier tcb->katimer.start = DEF_KAT / MSPTICK;
9077dd7cddfSDavid du Colombier tcb->katimer.func = tcpkeepalive;
9087dd7cddfSDavid du Colombier tcb->katimer.arg = s;
9097dd7cddfSDavid du Colombier
9103f695129SDavid du Colombier mss = DEF_MSS;
9113f695129SDavid du Colombier
9127dd7cddfSDavid du Colombier /* create a prototype(pseudo) header */
9133ff48bf5SDavid du Colombier if(mode != TCP_LISTEN){
9147dd7cddfSDavid du Colombier if(ipcmp(s->laddr, IPnoaddr) == 0)
9157dd7cddfSDavid du Colombier findlocalip(s->p->f, s->laddr, s->raddr);
9167dd7cddfSDavid du Colombier
9173ff48bf5SDavid du Colombier switch(s->ipversion){
9183ff48bf5SDavid du Colombier case V4:
9193ff48bf5SDavid du Colombier h4 = &tcb->protohdr.tcp4hdr;
9203ff48bf5SDavid du Colombier memset(h4, 0, sizeof(*h4));
9213ff48bf5SDavid du Colombier h4->proto = IP_TCPPROTO;
9223ff48bf5SDavid du Colombier hnputs(h4->tcpsport, s->lport);
9233ff48bf5SDavid du Colombier hnputs(h4->tcpdport, s->rport);
9243ff48bf5SDavid du Colombier v6tov4(h4->tcpsrc, s->laddr);
9253ff48bf5SDavid du Colombier v6tov4(h4->tcpdst, s->raddr);
9263ff48bf5SDavid du Colombier break;
9273ff48bf5SDavid du Colombier case V6:
9283ff48bf5SDavid du Colombier h6 = &tcb->protohdr.tcp6hdr;
9293ff48bf5SDavid du Colombier memset(h6, 0, sizeof(*h6));
9303ff48bf5SDavid du Colombier h6->proto = IP_TCPPROTO;
9313ff48bf5SDavid du Colombier hnputs(h6->tcpsport, s->lport);
9323ff48bf5SDavid du Colombier hnputs(h6->tcpdport, s->rport);
9333ff48bf5SDavid du Colombier ipmove(h6->tcpsrc, s->laddr);
9343ff48bf5SDavid du Colombier ipmove(h6->tcpdst, s->raddr);
9353f695129SDavid du Colombier mss = DEF_MSS6;
9363ff48bf5SDavid du Colombier break;
9373ff48bf5SDavid du Colombier default:
9383ff48bf5SDavid du Colombier panic("inittcpctl: version %d", s->ipversion);
9393ff48bf5SDavid du Colombier }
9403ff48bf5SDavid du Colombier }
9413ff48bf5SDavid du Colombier
9423f695129SDavid du Colombier tcb->mss = tcb->cwind = mss;
9431ce6c95fSDavid du Colombier tcb->abcbytes = 0;
9447ec5746aSDavid du Colombier tpriv = s->p->priv;
9457ec5746aSDavid du Colombier tpriv->stats[Mss] = tcb->mss;
9463f695129SDavid du Colombier
9473f695129SDavid du Colombier /* default is no window scaling */
9481ce6c95fSDavid du Colombier tcpsetscale(s, tcb, 0, 0);
9497dd7cddfSDavid du Colombier }
9507dd7cddfSDavid du Colombier
95180ee5cbfSDavid du Colombier /*
95280ee5cbfSDavid du Colombier * called with s qlocked
95380ee5cbfSDavid du Colombier */
9541ce6c95fSDavid du Colombier static void
tcpstart(Conv * s,int mode)9553f695129SDavid du Colombier tcpstart(Conv *s, int mode)
9567dd7cddfSDavid du Colombier {
9577dd7cddfSDavid du Colombier Tcpctl *tcb;
9587dd7cddfSDavid du Colombier Tcppriv *tpriv;
9599a747e4fSDavid du Colombier char kpname[KNAMELEN];
9607dd7cddfSDavid du Colombier
9617dd7cddfSDavid du Colombier tpriv = s->p->priv;
9627dd7cddfSDavid du Colombier
9637dd7cddfSDavid du Colombier if(tpriv->ackprocstarted == 0){
9647dd7cddfSDavid du Colombier qlock(&tpriv->apl);
9657dd7cddfSDavid du Colombier if(tpriv->ackprocstarted == 0){
9661ce6c95fSDavid du Colombier snprint(kpname, sizeof kpname, "#I%dtcpack", s->p->f->dev);
9677dd7cddfSDavid du Colombier kproc(kpname, tcpackproc, s->p);
9687dd7cddfSDavid du Colombier tpriv->ackprocstarted = 1;
9697dd7cddfSDavid du Colombier }
9707dd7cddfSDavid du Colombier qunlock(&tpriv->apl);
9717dd7cddfSDavid du Colombier }
9727dd7cddfSDavid du Colombier
9737dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
9747dd7cddfSDavid du Colombier
97580ee5cbfSDavid du Colombier inittcpctl(s, mode);
9767dd7cddfSDavid du Colombier
97780ee5cbfSDavid du Colombier iphtadd(&tpriv->ht, s);
9787dd7cddfSDavid du Colombier switch(mode) {
9797dd7cddfSDavid du Colombier case TCP_LISTEN:
98059cc4ca5SDavid du Colombier tpriv->stats[PassiveOpens]++;
9817dd7cddfSDavid du Colombier tcb->flags |= CLONE;
9827dd7cddfSDavid du Colombier tcpsetstate(s, Listen);
9837dd7cddfSDavid du Colombier break;
9847dd7cddfSDavid du Colombier
9857dd7cddfSDavid du Colombier case TCP_CONNECT:
98659cc4ca5SDavid du Colombier tpriv->stats[ActiveOpens]++;
9877dd7cddfSDavid du Colombier tcb->flags |= ACTIVE;
9883f695129SDavid du Colombier tcpsndsyn(s, tcb);
9897dd7cddfSDavid du Colombier tcpsetstate(s, Syn_sent);
9907dd7cddfSDavid du Colombier tcpoutput(s);
9917dd7cddfSDavid du Colombier break;
9927dd7cddfSDavid du Colombier }
9937dd7cddfSDavid du Colombier }
9947dd7cddfSDavid du Colombier
9957dd7cddfSDavid du Colombier static char*
tcpflag(char * buf,char * e,ushort flag)9961ce6c95fSDavid du Colombier tcpflag(char *buf, char *e, ushort flag)
9977dd7cddfSDavid du Colombier {
9981ce6c95fSDavid du Colombier char *p;
9997dd7cddfSDavid du Colombier
10001ce6c95fSDavid du Colombier p = seprint(buf, e, "%d", flag>>10); /* Head len */
10017dd7cddfSDavid du Colombier if(flag & URG)
10021ce6c95fSDavid du Colombier p = seprint(p, e, " URG");
10037dd7cddfSDavid du Colombier if(flag & ACK)
10041ce6c95fSDavid du Colombier p = seprint(p, e, " ACK");
10057dd7cddfSDavid du Colombier if(flag & PSH)
10061ce6c95fSDavid du Colombier p = seprint(p, e, " PSH");
10077dd7cddfSDavid du Colombier if(flag & RST)
10081ce6c95fSDavid du Colombier p = seprint(p, e, " RST");
10097dd7cddfSDavid du Colombier if(flag & SYN)
10101ce6c95fSDavid du Colombier p = seprint(p, e, " SYN");
10117dd7cddfSDavid du Colombier if(flag & FIN)
10121ce6c95fSDavid du Colombier p = seprint(p, e, " FIN");
10131ce6c95fSDavid du Colombier USED(p);
10147dd7cddfSDavid du Colombier return buf;
10157dd7cddfSDavid du Colombier }
10167dd7cddfSDavid du Colombier
10171ce6c95fSDavid du Colombier static Block*
htontcp6(Tcp * tcph,Block * data,Tcp6hdr * ph,Tcpctl * tcb)10183ff48bf5SDavid du Colombier htontcp6(Tcp *tcph, Block *data, Tcp6hdr *ph, Tcpctl *tcb)
10197dd7cddfSDavid du Colombier {
10207dd7cddfSDavid du Colombier int dlen;
10213ff48bf5SDavid du Colombier Tcp6hdr *h;
10227dd7cddfSDavid du Colombier ushort csum;
10233f695129SDavid du Colombier ushort hdrlen, optpad = 0;
10243f695129SDavid du Colombier uchar *opt;
10257dd7cddfSDavid du Colombier
10263ff48bf5SDavid du Colombier hdrlen = TCP6_HDRSIZE;
10273f695129SDavid du Colombier if(tcph->flags & SYN){
10287dd7cddfSDavid du Colombier if(tcph->mss)
10297dd7cddfSDavid du Colombier hdrlen += MSS_LENGTH;
10303f695129SDavid du Colombier if(tcph->ws)
10313f695129SDavid du Colombier hdrlen += WS_LENGTH;
10323f695129SDavid du Colombier optpad = hdrlen & 3;
10333f695129SDavid du Colombier if(optpad)
10343f695129SDavid du Colombier optpad = 4 - optpad;
10353f695129SDavid du Colombier hdrlen += optpad;
10363f695129SDavid du Colombier }
10377dd7cddfSDavid du Colombier
10387dd7cddfSDavid du Colombier if(data) {
10397dd7cddfSDavid du Colombier dlen = blocklen(data);
10403ff48bf5SDavid du Colombier data = padblock(data, hdrlen + TCP6_PKT);
10417dd7cddfSDavid du Colombier if(data == nil)
10427dd7cddfSDavid du Colombier return nil;
10437dd7cddfSDavid du Colombier }
10447dd7cddfSDavid du Colombier else {
10457dd7cddfSDavid du Colombier dlen = 0;
10463ff48bf5SDavid du Colombier data = allocb(hdrlen + TCP6_PKT + 64); /* the 64 pad is to meet mintu's */
10477dd7cddfSDavid du Colombier if(data == nil)
10487dd7cddfSDavid du Colombier return nil;
10493ff48bf5SDavid du Colombier data->wp += hdrlen + TCP6_PKT;
10507dd7cddfSDavid du Colombier }
10517dd7cddfSDavid du Colombier
10527dd7cddfSDavid du Colombier /* copy in pseudo ip header plus port numbers */
10533ff48bf5SDavid du Colombier h = (Tcp6hdr *)(data->rp);
10543ff48bf5SDavid du Colombier memmove(h, ph, TCP6_TCBPHDRSZ);
10553ff48bf5SDavid du Colombier
10563ff48bf5SDavid du Colombier /* compose pseudo tcp header, do cksum calculation */
10573ff48bf5SDavid du Colombier hnputl(h->vcf, hdrlen + dlen);
10583ff48bf5SDavid du Colombier h->ploadlen[0] = h->ploadlen[1] = h->proto = 0;
10593ff48bf5SDavid du Colombier h->ttl = ph->proto;
10603ff48bf5SDavid du Colombier
10613ff48bf5SDavid du Colombier /* copy in variable bits */
10623ff48bf5SDavid du Colombier hnputl(h->tcpseq, tcph->seq);
10633ff48bf5SDavid du Colombier hnputl(h->tcpack, tcph->ack);
10643ff48bf5SDavid du Colombier hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
10653f695129SDavid du Colombier hnputs(h->tcpwin, tcph->wnd>>(tcb != nil ? tcb->snd.scale : 0));
10663ff48bf5SDavid du Colombier hnputs(h->tcpurg, tcph->urg);
10673ff48bf5SDavid du Colombier
10683f695129SDavid du Colombier if(tcph->flags & SYN){
10693f695129SDavid du Colombier opt = h->tcpopt;
10703ff48bf5SDavid du Colombier if(tcph->mss != 0){
10713f695129SDavid du Colombier *opt++ = MSSOPT;
10723f695129SDavid du Colombier *opt++ = MSS_LENGTH;
10733f695129SDavid du Colombier hnputs(opt, tcph->mss);
10743f695129SDavid du Colombier opt += 2;
10753ff48bf5SDavid du Colombier }
10763f695129SDavid du Colombier if(tcph->ws != 0){
10773f695129SDavid du Colombier *opt++ = WSOPT;
10783f695129SDavid du Colombier *opt++ = WS_LENGTH;
10793f695129SDavid du Colombier *opt++ = tcph->ws;
10803f695129SDavid du Colombier }
10813f695129SDavid du Colombier while(optpad-- > 0)
10823f695129SDavid du Colombier *opt++ = NOOPOPT;
10833f695129SDavid du Colombier }
10843f695129SDavid du Colombier
10853ff48bf5SDavid du Colombier if(tcb != nil && tcb->nochecksum){
10863ff48bf5SDavid du Colombier h->tcpcksum[0] = h->tcpcksum[1] = 0;
10873ff48bf5SDavid du Colombier } else {
10883ff48bf5SDavid du Colombier csum = ptclcsum(data, TCP6_IPLEN, hdrlen+dlen+TCP6_PHDRSIZE);
10893ff48bf5SDavid du Colombier hnputs(h->tcpcksum, csum);
10903ff48bf5SDavid du Colombier }
10913ff48bf5SDavid du Colombier
10923ff48bf5SDavid du Colombier /* move from pseudo header back to normal ip header */
10933ff48bf5SDavid du Colombier memset(h->vcf, 0, 4);
10943ff48bf5SDavid du Colombier h->vcf[0] = IP_VER6;
10953ff48bf5SDavid du Colombier hnputs(h->ploadlen, hdrlen+dlen);
10963ff48bf5SDavid du Colombier h->proto = ph->proto;
10973ff48bf5SDavid du Colombier
10983ff48bf5SDavid du Colombier return data;
10993ff48bf5SDavid du Colombier }
11003ff48bf5SDavid du Colombier
11011ce6c95fSDavid du Colombier static Block*
htontcp4(Tcp * tcph,Block * data,Tcp4hdr * ph,Tcpctl * tcb)11023ff48bf5SDavid du Colombier htontcp4(Tcp *tcph, Block *data, Tcp4hdr *ph, Tcpctl *tcb)
11033ff48bf5SDavid du Colombier {
11043ff48bf5SDavid du Colombier int dlen;
11053ff48bf5SDavid du Colombier Tcp4hdr *h;
11063ff48bf5SDavid du Colombier ushort csum;
11073f695129SDavid du Colombier ushort hdrlen, optpad = 0;
11083f695129SDavid du Colombier uchar *opt;
11093ff48bf5SDavid du Colombier
11103ff48bf5SDavid du Colombier hdrlen = TCP4_HDRSIZE;
11113f695129SDavid du Colombier if(tcph->flags & SYN){
11123ff48bf5SDavid du Colombier if(tcph->mss)
11133ff48bf5SDavid du Colombier hdrlen += MSS_LENGTH;
11141ce6c95fSDavid du Colombier if(1)
11153f695129SDavid du Colombier hdrlen += WS_LENGTH;
11163f695129SDavid du Colombier optpad = hdrlen & 3;
11173f695129SDavid du Colombier if(optpad)
11183f695129SDavid du Colombier optpad = 4 - optpad;
11193f695129SDavid du Colombier hdrlen += optpad;
11203f695129SDavid du Colombier }
11213ff48bf5SDavid du Colombier
11223ff48bf5SDavid du Colombier if(data) {
11233ff48bf5SDavid du Colombier dlen = blocklen(data);
11243ff48bf5SDavid du Colombier data = padblock(data, hdrlen + TCP4_PKT);
11253ff48bf5SDavid du Colombier if(data == nil)
11263ff48bf5SDavid du Colombier return nil;
11273ff48bf5SDavid du Colombier }
11283ff48bf5SDavid du Colombier else {
11293ff48bf5SDavid du Colombier dlen = 0;
11303ff48bf5SDavid du Colombier data = allocb(hdrlen + TCP4_PKT + 64); /* the 64 pad is to meet mintu's */
11313ff48bf5SDavid du Colombier if(data == nil)
11323ff48bf5SDavid du Colombier return nil;
11333ff48bf5SDavid du Colombier data->wp += hdrlen + TCP4_PKT;
11343ff48bf5SDavid du Colombier }
11353ff48bf5SDavid du Colombier
11363ff48bf5SDavid du Colombier /* copy in pseudo ip header plus port numbers */
11373ff48bf5SDavid du Colombier h = (Tcp4hdr *)(data->rp);
11383ff48bf5SDavid du Colombier memmove(h, ph, TCP4_TCBPHDRSZ);
11397dd7cddfSDavid du Colombier
11407dd7cddfSDavid du Colombier /* copy in variable bits */
11417dd7cddfSDavid du Colombier hnputs(h->tcplen, hdrlen + dlen);
11427dd7cddfSDavid du Colombier hnputl(h->tcpseq, tcph->seq);
11437dd7cddfSDavid du Colombier hnputl(h->tcpack, tcph->ack);
11447dd7cddfSDavid du Colombier hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
11453f695129SDavid du Colombier hnputs(h->tcpwin, tcph->wnd>>(tcb != nil ? tcb->snd.scale : 0));
11467dd7cddfSDavid du Colombier hnputs(h->tcpurg, tcph->urg);
11477dd7cddfSDavid du Colombier
11483f695129SDavid du Colombier if(tcph->flags & SYN){
11493f695129SDavid du Colombier opt = h->tcpopt;
11507dd7cddfSDavid du Colombier if(tcph->mss != 0){
11513f695129SDavid du Colombier *opt++ = MSSOPT;
11523f695129SDavid du Colombier *opt++ = MSS_LENGTH;
11533f695129SDavid du Colombier hnputs(opt, tcph->mss);
11543f695129SDavid du Colombier opt += 2;
11557dd7cddfSDavid du Colombier }
11561ce6c95fSDavid du Colombier /* always offer. rfc1323 §2.2 */
11571ce6c95fSDavid du Colombier if(1){
11583f695129SDavid du Colombier *opt++ = WSOPT;
11593f695129SDavid du Colombier *opt++ = WS_LENGTH;
11603f695129SDavid du Colombier *opt++ = tcph->ws;
11613f695129SDavid du Colombier }
11623f695129SDavid du Colombier while(optpad-- > 0)
11633f695129SDavid du Colombier *opt++ = NOOPOPT;
11643f695129SDavid du Colombier }
11653f695129SDavid du Colombier
116659cc4ca5SDavid du Colombier if(tcb != nil && tcb->nochecksum){
116759cc4ca5SDavid du Colombier h->tcpcksum[0] = h->tcpcksum[1] = 0;
116859cc4ca5SDavid du Colombier } else {
11693ff48bf5SDavid du Colombier csum = ptclcsum(data, TCP4_IPLEN, hdrlen+dlen+TCP4_PHDRSIZE);
11707dd7cddfSDavid du Colombier hnputs(h->tcpcksum, csum);
117159cc4ca5SDavid du Colombier }
11727dd7cddfSDavid du Colombier
11737dd7cddfSDavid du Colombier return data;
11747dd7cddfSDavid du Colombier }
11757dd7cddfSDavid du Colombier
11761ce6c95fSDavid du Colombier static int
ntohtcp6(Tcp * tcph,Block ** bpp)11773ff48bf5SDavid du Colombier ntohtcp6(Tcp *tcph, Block **bpp)
11787dd7cddfSDavid du Colombier {
11793ff48bf5SDavid du Colombier Tcp6hdr *h;
11807dd7cddfSDavid du Colombier uchar *optr;
11817dd7cddfSDavid du Colombier ushort hdrlen;
11827dd7cddfSDavid du Colombier ushort optlen;
11837dd7cddfSDavid du Colombier int n;
11847dd7cddfSDavid du Colombier
11853ff48bf5SDavid du Colombier *bpp = pullupblock(*bpp, TCP6_PKT+TCP6_HDRSIZE);
11867dd7cddfSDavid du Colombier if(*bpp == nil)
11877dd7cddfSDavid du Colombier return -1;
11887dd7cddfSDavid du Colombier
11893ff48bf5SDavid du Colombier h = (Tcp6hdr *)((*bpp)->rp);
11907dd7cddfSDavid du Colombier tcph->source = nhgets(h->tcpsport);
11917dd7cddfSDavid du Colombier tcph->dest = nhgets(h->tcpdport);
11927dd7cddfSDavid du Colombier tcph->seq = nhgetl(h->tcpseq);
11937dd7cddfSDavid du Colombier tcph->ack = nhgetl(h->tcpack);
11943f695129SDavid du Colombier hdrlen = (h->tcpflag[0]>>2) & ~3;
11953ff48bf5SDavid du Colombier if(hdrlen < TCP6_HDRSIZE) {
11967dd7cddfSDavid du Colombier freeblist(*bpp);
11977dd7cddfSDavid du Colombier return -1;
11987dd7cddfSDavid du Colombier }
11997dd7cddfSDavid du Colombier
12007dd7cddfSDavid du Colombier tcph->flags = h->tcpflag[1];
12017dd7cddfSDavid du Colombier tcph->wnd = nhgets(h->tcpwin);
12027dd7cddfSDavid du Colombier tcph->urg = nhgets(h->tcpurg);
12037dd7cddfSDavid du Colombier tcph->mss = 0;
12043f695129SDavid du Colombier tcph->ws = 0;
12051ce6c95fSDavid du Colombier tcph->update = 0;
12063ff48bf5SDavid du Colombier tcph->len = nhgets(h->ploadlen) - hdrlen;
12077dd7cddfSDavid du Colombier
12083ff48bf5SDavid du Colombier *bpp = pullupblock(*bpp, hdrlen+TCP6_PKT);
12097dd7cddfSDavid du Colombier if(*bpp == nil)
12107dd7cddfSDavid du Colombier return -1;
12117dd7cddfSDavid du Colombier
12127dd7cddfSDavid du Colombier optr = h->tcpopt;
12133ff48bf5SDavid du Colombier n = hdrlen - TCP6_HDRSIZE;
12147dd7cddfSDavid du Colombier while(n > 0 && *optr != EOLOPT) {
12157dd7cddfSDavid du Colombier if(*optr == NOOPOPT) {
12167dd7cddfSDavid du Colombier n--;
12177dd7cddfSDavid du Colombier optr++;
12187dd7cddfSDavid du Colombier continue;
12197dd7cddfSDavid du Colombier }
12207dd7cddfSDavid du Colombier optlen = optr[1];
12217dd7cddfSDavid du Colombier if(optlen < 2 || optlen > n)
12227dd7cddfSDavid du Colombier break;
12237dd7cddfSDavid du Colombier switch(*optr) {
12247dd7cddfSDavid du Colombier case MSSOPT:
12257dd7cddfSDavid du Colombier if(optlen == MSS_LENGTH)
12267dd7cddfSDavid du Colombier tcph->mss = nhgets(optr+2);
12277dd7cddfSDavid du Colombier break;
12283f695129SDavid du Colombier case WSOPT:
12293f695129SDavid du Colombier if(optlen == WS_LENGTH && *(optr+2) <= 14)
12301ce6c95fSDavid du Colombier tcph->ws = *(optr+2);
12313f695129SDavid du Colombier break;
12327dd7cddfSDavid du Colombier }
12337dd7cddfSDavid du Colombier n -= optlen;
12347dd7cddfSDavid du Colombier optr += optlen;
12357dd7cddfSDavid du Colombier }
12367dd7cddfSDavid du Colombier return hdrlen;
12377dd7cddfSDavid du Colombier }
12387dd7cddfSDavid du Colombier
12391ce6c95fSDavid du Colombier static int
ntohtcp4(Tcp * tcph,Block ** bpp)12403ff48bf5SDavid du Colombier ntohtcp4(Tcp *tcph, Block **bpp)
12413ff48bf5SDavid du Colombier {
12423ff48bf5SDavid du Colombier Tcp4hdr *h;
12433ff48bf5SDavid du Colombier uchar *optr;
12443ff48bf5SDavid du Colombier ushort hdrlen;
12453ff48bf5SDavid du Colombier ushort optlen;
12463ff48bf5SDavid du Colombier int n;
12473ff48bf5SDavid du Colombier
12483ff48bf5SDavid du Colombier *bpp = pullupblock(*bpp, TCP4_PKT+TCP4_HDRSIZE);
12493ff48bf5SDavid du Colombier if(*bpp == nil)
12503ff48bf5SDavid du Colombier return -1;
12513ff48bf5SDavid du Colombier
12523ff48bf5SDavid du Colombier h = (Tcp4hdr *)((*bpp)->rp);
12533ff48bf5SDavid du Colombier tcph->source = nhgets(h->tcpsport);
12543ff48bf5SDavid du Colombier tcph->dest = nhgets(h->tcpdport);
12553ff48bf5SDavid du Colombier tcph->seq = nhgetl(h->tcpseq);
12563ff48bf5SDavid du Colombier tcph->ack = nhgetl(h->tcpack);
12573ff48bf5SDavid du Colombier
12583f695129SDavid du Colombier hdrlen = (h->tcpflag[0]>>2) & ~3;
12593ff48bf5SDavid du Colombier if(hdrlen < TCP4_HDRSIZE) {
12603ff48bf5SDavid du Colombier freeblist(*bpp);
12613ff48bf5SDavid du Colombier return -1;
12623ff48bf5SDavid du Colombier }
12633ff48bf5SDavid du Colombier
12643ff48bf5SDavid du Colombier tcph->flags = h->tcpflag[1];
12653ff48bf5SDavid du Colombier tcph->wnd = nhgets(h->tcpwin);
12663ff48bf5SDavid du Colombier tcph->urg = nhgets(h->tcpurg);
12673ff48bf5SDavid du Colombier tcph->mss = 0;
12683f695129SDavid du Colombier tcph->ws = 0;
12691ce6c95fSDavid du Colombier tcph->update = 0;
12703ff48bf5SDavid du Colombier tcph->len = nhgets(h->length) - (hdrlen + TCP4_PKT);
12713ff48bf5SDavid du Colombier
12723ff48bf5SDavid du Colombier *bpp = pullupblock(*bpp, hdrlen+TCP4_PKT);
12733ff48bf5SDavid du Colombier if(*bpp == nil)
12743ff48bf5SDavid du Colombier return -1;
12753ff48bf5SDavid du Colombier
12763ff48bf5SDavid du Colombier optr = h->tcpopt;
12773ff48bf5SDavid du Colombier n = hdrlen - TCP4_HDRSIZE;
12783ff48bf5SDavid du Colombier while(n > 0 && *optr != EOLOPT) {
12793ff48bf5SDavid du Colombier if(*optr == NOOPOPT) {
12803ff48bf5SDavid du Colombier n--;
12813ff48bf5SDavid du Colombier optr++;
12823ff48bf5SDavid du Colombier continue;
12833ff48bf5SDavid du Colombier }
12843ff48bf5SDavid du Colombier optlen = optr[1];
12853ff48bf5SDavid du Colombier if(optlen < 2 || optlen > n)
12863ff48bf5SDavid du Colombier break;
12873ff48bf5SDavid du Colombier switch(*optr) {
12883ff48bf5SDavid du Colombier case MSSOPT:
12891ce6c95fSDavid du Colombier if(optlen == MSS_LENGTH)
12903ff48bf5SDavid du Colombier tcph->mss = nhgets(optr+2);
12913ff48bf5SDavid du Colombier break;
12923f695129SDavid du Colombier case WSOPT:
12933f695129SDavid du Colombier if(optlen == WS_LENGTH && *(optr+2) <= 14)
12941ce6c95fSDavid du Colombier tcph->ws = *(optr+2);
12953f695129SDavid du Colombier break;
12963ff48bf5SDavid du Colombier }
12973ff48bf5SDavid du Colombier n -= optlen;
12983ff48bf5SDavid du Colombier optr += optlen;
12993ff48bf5SDavid du Colombier }
13003ff48bf5SDavid du Colombier return hdrlen;
13013ff48bf5SDavid du Colombier }
13023ff48bf5SDavid du Colombier
13033ff48bf5SDavid du Colombier /*
13041ce6c95fSDavid du Colombier * For outgoing calls, generate an initial sequence
13053ff48bf5SDavid du Colombier * number and put a SYN on the send queue
13063ff48bf5SDavid du Colombier */
13071ce6c95fSDavid du Colombier static void
tcpsndsyn(Conv * s,Tcpctl * tcb)13083f695129SDavid du Colombier tcpsndsyn(Conv *s, Tcpctl *tcb)
13097dd7cddfSDavid du Colombier {
13107ec5746aSDavid du Colombier Tcppriv *tpriv;
13117ec5746aSDavid du Colombier
13127dd7cddfSDavid du Colombier tcb->iss = (nrand(1<<16)<<16)|nrand(1<<16);
13137dd7cddfSDavid du Colombier tcb->rttseq = tcb->iss;
13147dd7cddfSDavid du Colombier tcb->snd.wl2 = tcb->iss;
13157dd7cddfSDavid du Colombier tcb->snd.una = tcb->iss;
13161ce6c95fSDavid du Colombier tcb->snd.rxt = tcb->iss;
13177dd7cddfSDavid du Colombier tcb->snd.ptr = tcb->rttseq;
13187dd7cddfSDavid du Colombier tcb->snd.nxt = tcb->rttseq;
131980ee5cbfSDavid du Colombier tcb->flgcnt++;
13207dd7cddfSDavid du Colombier tcb->flags |= FORCE;
13213ff48bf5SDavid du Colombier tcb->sndsyntime = NOW;
13223f695129SDavid du Colombier
13233f695129SDavid du Colombier /* set desired mss and scale */
13243f695129SDavid du Colombier tcb->mss = tcpmtu(s->p, s->laddr, s->ipversion, &tcb->scale);
13257ec5746aSDavid du Colombier tpriv = s->p->priv;
13267ec5746aSDavid du Colombier tpriv->stats[Mss] = tcb->mss;
13277dd7cddfSDavid du Colombier }
13287dd7cddfSDavid du Colombier
13297dd7cddfSDavid du Colombier void
sndrst(Proto * tcp,uchar * source,uchar * dest,ushort length,Tcp * seg,uchar version,char * reason)13303ff48bf5SDavid du Colombier sndrst(Proto *tcp, uchar *source, uchar *dest, ushort length, Tcp *seg, uchar version, char *reason)
13317dd7cddfSDavid du Colombier {
13327dd7cddfSDavid du Colombier Block *hbp;
13337dd7cddfSDavid du Colombier uchar rflags;
13347dd7cddfSDavid du Colombier Tcppriv *tpriv;
13353ff48bf5SDavid du Colombier Tcp4hdr ph4;
13363ff48bf5SDavid du Colombier Tcp6hdr ph6;
13373ff48bf5SDavid du Colombier
133884363d68SDavid du Colombier netlog(tcp->f, Logtcp, "sndrst: %s\n", reason);
13397dd7cddfSDavid du Colombier
13407dd7cddfSDavid du Colombier tpriv = tcp->priv;
13417dd7cddfSDavid du Colombier
13427dd7cddfSDavid du Colombier if(seg->flags & RST)
13437dd7cddfSDavid du Colombier return;
13447dd7cddfSDavid du Colombier
13457dd7cddfSDavid du Colombier /* make pseudo header */
13463ff48bf5SDavid du Colombier switch(version) {
13473ff48bf5SDavid du Colombier case V4:
13483ff48bf5SDavid du Colombier memset(&ph4, 0, sizeof(ph4));
13493ff48bf5SDavid du Colombier ph4.vihl = IP_VER4;
13503ff48bf5SDavid du Colombier v6tov4(ph4.tcpsrc, dest);
13513ff48bf5SDavid du Colombier v6tov4(ph4.tcpdst, source);
13523ff48bf5SDavid du Colombier ph4.proto = IP_TCPPROTO;
13533ff48bf5SDavid du Colombier hnputs(ph4.tcplen, TCP4_HDRSIZE);
13543ff48bf5SDavid du Colombier hnputs(ph4.tcpsport, seg->dest);
13553ff48bf5SDavid du Colombier hnputs(ph4.tcpdport, seg->source);
13563ff48bf5SDavid du Colombier break;
13573ff48bf5SDavid du Colombier case V6:
13583ff48bf5SDavid du Colombier memset(&ph6, 0, sizeof(ph6));
13593ff48bf5SDavid du Colombier ph6.vcf[0] = IP_VER6;
13603ff48bf5SDavid du Colombier ipmove(ph6.tcpsrc, dest);
13613ff48bf5SDavid du Colombier ipmove(ph6.tcpdst, source);
13623ff48bf5SDavid du Colombier ph6.proto = IP_TCPPROTO;
13633ff48bf5SDavid du Colombier hnputs(ph6.ploadlen, TCP6_HDRSIZE);
13643ff48bf5SDavid du Colombier hnputs(ph6.tcpsport, seg->dest);
13653ff48bf5SDavid du Colombier hnputs(ph6.tcpdport, seg->source);
13663ff48bf5SDavid du Colombier break;
13673ff48bf5SDavid du Colombier default:
13683ff48bf5SDavid du Colombier panic("sndrst: version %d", version);
13693ff48bf5SDavid du Colombier }
13707dd7cddfSDavid du Colombier
137159cc4ca5SDavid du Colombier tpriv->stats[OutRsts]++;
13727dd7cddfSDavid du Colombier rflags = RST;
13737dd7cddfSDavid du Colombier
13747dd7cddfSDavid du Colombier /* convince the other end that this reset is in band */
13757dd7cddfSDavid du Colombier if(seg->flags & ACK) {
13767dd7cddfSDavid du Colombier seg->seq = seg->ack;
13777dd7cddfSDavid du Colombier seg->ack = 0;
13787dd7cddfSDavid du Colombier }
13797dd7cddfSDavid du Colombier else {
13807dd7cddfSDavid du Colombier rflags |= ACK;
13817dd7cddfSDavid du Colombier seg->ack = seg->seq;
13827dd7cddfSDavid du Colombier seg->seq = 0;
13837dd7cddfSDavid du Colombier if(seg->flags & SYN)
13847dd7cddfSDavid du Colombier seg->ack++;
13857dd7cddfSDavid du Colombier seg->ack += length;
13867dd7cddfSDavid du Colombier if(seg->flags & FIN)
13877dd7cddfSDavid du Colombier seg->ack++;
13887dd7cddfSDavid du Colombier }
13897dd7cddfSDavid du Colombier seg->flags = rflags;
13907dd7cddfSDavid du Colombier seg->wnd = 0;
13917dd7cddfSDavid du Colombier seg->urg = 0;
13927dd7cddfSDavid du Colombier seg->mss = 0;
13933f695129SDavid du Colombier seg->ws = 0;
13943ff48bf5SDavid du Colombier switch(version) {
13953ff48bf5SDavid du Colombier case V4:
13963ff48bf5SDavid du Colombier hbp = htontcp4(seg, nil, &ph4, nil);
13977dd7cddfSDavid du Colombier if(hbp == nil)
13987dd7cddfSDavid du Colombier return;
1399a6a9e072SDavid du Colombier ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
14003ff48bf5SDavid du Colombier break;
14013ff48bf5SDavid du Colombier case V6:
14023ff48bf5SDavid du Colombier hbp = htontcp6(seg, nil, &ph6, nil);
14033ff48bf5SDavid du Colombier if(hbp == nil)
14043ff48bf5SDavid du Colombier return;
1405a6a9e072SDavid du Colombier ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
14063ff48bf5SDavid du Colombier break;
14073ff48bf5SDavid du Colombier default:
14083ff48bf5SDavid du Colombier panic("sndrst2: version %d", version);
14093ff48bf5SDavid du Colombier }
14107dd7cddfSDavid du Colombier }
14117dd7cddfSDavid du Colombier
14127dd7cddfSDavid du Colombier /*
14137dd7cddfSDavid du Colombier * send a reset to the remote side and close the conversation
141480ee5cbfSDavid du Colombier * called with s qlocked
14157dd7cddfSDavid du Colombier */
14161ce6c95fSDavid du Colombier static char*
tcphangup(Conv * s)14177dd7cddfSDavid du Colombier tcphangup(Conv *s)
14187dd7cddfSDavid du Colombier {
14197dd7cddfSDavid du Colombier Tcp seg;
14207dd7cddfSDavid du Colombier Tcpctl *tcb;
14217dd7cddfSDavid du Colombier Block *hbp;
14227dd7cddfSDavid du Colombier
14237dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
142480ee5cbfSDavid du Colombier if(waserror())
14257dd7cddfSDavid du Colombier return commonerror();
1426c02046c4SDavid du Colombier if(ipcmp(s->raddr, IPnoaddr) != 0) {
14275fab9909SDavid du Colombier if(!waserror()){
142810ae047aSDavid du Colombier memset(&seg, 0, sizeof seg);
14297dd7cddfSDavid du Colombier seg.flags = RST | ACK;
14307dd7cddfSDavid du Colombier seg.ack = tcb->rcv.nxt;
14311ce6c95fSDavid du Colombier tcb->rcv.ackptr = seg.ack;
14327dd7cddfSDavid du Colombier seg.seq = tcb->snd.ptr;
14337dd7cddfSDavid du Colombier seg.wnd = 0;
14347dd7cddfSDavid du Colombier seg.urg = 0;
14357dd7cddfSDavid du Colombier seg.mss = 0;
14363f695129SDavid du Colombier seg.ws = 0;
14373ff48bf5SDavid du Colombier switch(s->ipversion) {
14383ff48bf5SDavid du Colombier case V4:
14393ff48bf5SDavid du Colombier tcb->protohdr.tcp4hdr.vihl = IP_VER4;
14403ff48bf5SDavid du Colombier hbp = htontcp4(&seg, nil, &tcb->protohdr.tcp4hdr, tcb);
1441a6a9e072SDavid du Colombier ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s);
14423ff48bf5SDavid du Colombier break;
14433ff48bf5SDavid du Colombier case V6:
14443ff48bf5SDavid du Colombier tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
14453ff48bf5SDavid du Colombier hbp = htontcp6(&seg, nil, &tcb->protohdr.tcp6hdr, tcb);
1446a6a9e072SDavid du Colombier ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s);
14473ff48bf5SDavid du Colombier break;
14483ff48bf5SDavid du Colombier default:
14493ff48bf5SDavid du Colombier panic("tcphangup: version %d", s->ipversion);
14503ff48bf5SDavid du Colombier }
14515fab9909SDavid du Colombier poperror();
14525fab9909SDavid du Colombier }
14537dd7cddfSDavid du Colombier }
14547dd7cddfSDavid du Colombier localclose(s, nil);
14557dd7cddfSDavid du Colombier poperror();
14567dd7cddfSDavid du Colombier return nil;
14577dd7cddfSDavid du Colombier }
14587dd7cddfSDavid du Colombier
14593ff48bf5SDavid du Colombier /*
14603ff48bf5SDavid du Colombier * (re)send a SYN ACK
14613ff48bf5SDavid du Colombier */
14621ce6c95fSDavid du Colombier static int
sndsynack(Proto * tcp,Limbo * lp)14633ff48bf5SDavid du Colombier sndsynack(Proto *tcp, Limbo *lp)
14643ff48bf5SDavid du Colombier {
14653ff48bf5SDavid du Colombier Block *hbp;
14663ff48bf5SDavid du Colombier Tcp4hdr ph4;
14673ff48bf5SDavid du Colombier Tcp6hdr ph6;
14683ff48bf5SDavid du Colombier Tcp seg;
14691ce6c95fSDavid du Colombier uint scale;
14703ff48bf5SDavid du Colombier
14713ff48bf5SDavid du Colombier /* make pseudo header */
14723ff48bf5SDavid du Colombier switch(lp->version) {
14733ff48bf5SDavid du Colombier case V4:
14743ff48bf5SDavid du Colombier memset(&ph4, 0, sizeof(ph4));
14753ff48bf5SDavid du Colombier ph4.vihl = IP_VER4;
14763ff48bf5SDavid du Colombier v6tov4(ph4.tcpsrc, lp->laddr);
14773ff48bf5SDavid du Colombier v6tov4(ph4.tcpdst, lp->raddr);
14783ff48bf5SDavid du Colombier ph4.proto = IP_TCPPROTO;
14793ff48bf5SDavid du Colombier hnputs(ph4.tcplen, TCP4_HDRSIZE);
14803ff48bf5SDavid du Colombier hnputs(ph4.tcpsport, lp->lport);
14813ff48bf5SDavid du Colombier hnputs(ph4.tcpdport, lp->rport);
14823ff48bf5SDavid du Colombier break;
14833ff48bf5SDavid du Colombier case V6:
14843ff48bf5SDavid du Colombier memset(&ph6, 0, sizeof(ph6));
14853ff48bf5SDavid du Colombier ph6.vcf[0] = IP_VER6;
14863ff48bf5SDavid du Colombier ipmove(ph6.tcpsrc, lp->laddr);
14873ff48bf5SDavid du Colombier ipmove(ph6.tcpdst, lp->raddr);
14883ff48bf5SDavid du Colombier ph6.proto = IP_TCPPROTO;
14893ff48bf5SDavid du Colombier hnputs(ph6.ploadlen, TCP6_HDRSIZE);
14903ff48bf5SDavid du Colombier hnputs(ph6.tcpsport, lp->lport);
14913ff48bf5SDavid du Colombier hnputs(ph6.tcpdport, lp->rport);
14923ff48bf5SDavid du Colombier break;
14933ff48bf5SDavid du Colombier default:
14943ff48bf5SDavid du Colombier panic("sndrst: version %d", lp->version);
14953ff48bf5SDavid du Colombier }
14963ff48bf5SDavid du Colombier
149710ae047aSDavid du Colombier memset(&seg, 0, sizeof seg);
14983ff48bf5SDavid du Colombier seg.seq = lp->iss;
14993ff48bf5SDavid du Colombier seg.ack = lp->irs+1;
15003ff48bf5SDavid du Colombier seg.flags = SYN|ACK;
15013ff48bf5SDavid du Colombier seg.urg = 0;
15023f695129SDavid du Colombier seg.mss = tcpmtu(tcp, lp->laddr, lp->version, &scale);
15033ff48bf5SDavid du Colombier seg.wnd = QMAX;
15043ff48bf5SDavid du Colombier
15053f695129SDavid du Colombier /* if the other side set scale, we should too */
15063f695129SDavid du Colombier if(lp->rcvscale){
15073f695129SDavid du Colombier seg.ws = scale;
15083f695129SDavid du Colombier lp->sndscale = scale;
15093f695129SDavid du Colombier } else {
15103f695129SDavid du Colombier seg.ws = 0;
15113f695129SDavid du Colombier lp->sndscale = 0;
15123f695129SDavid du Colombier }
15133f695129SDavid du Colombier
15143ff48bf5SDavid du Colombier switch(lp->version) {
15153ff48bf5SDavid du Colombier case V4:
15163ff48bf5SDavid du Colombier hbp = htontcp4(&seg, nil, &ph4, nil);
15173ff48bf5SDavid du Colombier if(hbp == nil)
15183ff48bf5SDavid du Colombier return -1;
1519a6a9e072SDavid du Colombier ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
15203ff48bf5SDavid du Colombier break;
15213ff48bf5SDavid du Colombier case V6:
15223ff48bf5SDavid du Colombier hbp = htontcp6(&seg, nil, &ph6, nil);
15233ff48bf5SDavid du Colombier if(hbp == nil)
15243ff48bf5SDavid du Colombier return -1;
1525a6a9e072SDavid du Colombier ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
15263ff48bf5SDavid du Colombier break;
15273ff48bf5SDavid du Colombier default:
15283ff48bf5SDavid du Colombier panic("sndsnack: version %d", lp->version);
15293ff48bf5SDavid du Colombier }
15303ff48bf5SDavid du Colombier lp->lastsend = NOW;
15313ff48bf5SDavid du Colombier return 0;
15323ff48bf5SDavid du Colombier }
15333ff48bf5SDavid du Colombier
15343ff48bf5SDavid du Colombier #define hashipa(a, p) ( ( (a)[IPaddrlen-2] + (a)[IPaddrlen-1] + p )&LHTMASK )
15353ff48bf5SDavid du Colombier
15363ff48bf5SDavid du Colombier /*
15373ff48bf5SDavid du Colombier * put a call into limbo and respond with a SYN ACK
15383ff48bf5SDavid du Colombier *
15393ff48bf5SDavid du Colombier * called with proto locked
15403ff48bf5SDavid du Colombier */
15413ff48bf5SDavid du Colombier static void
limbo(Conv * s,uchar * source,uchar * dest,Tcp * seg,int version)15423ff48bf5SDavid du Colombier limbo(Conv *s, uchar *source, uchar *dest, Tcp *seg, int version)
15433ff48bf5SDavid du Colombier {
15443ff48bf5SDavid du Colombier Limbo *lp, **l;
15453ff48bf5SDavid du Colombier Tcppriv *tpriv;
15463ff48bf5SDavid du Colombier int h;
15473ff48bf5SDavid du Colombier
15483ff48bf5SDavid du Colombier tpriv = s->p->priv;
15493ff48bf5SDavid du Colombier h = hashipa(source, seg->source);
15503ff48bf5SDavid du Colombier
15513ff48bf5SDavid du Colombier for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){
15523ff48bf5SDavid du Colombier lp = *l;
15533ff48bf5SDavid du Colombier if(lp->lport != seg->dest || lp->rport != seg->source || lp->version != version)
15543ff48bf5SDavid du Colombier continue;
15553ff48bf5SDavid du Colombier if(ipcmp(lp->raddr, source) != 0)
15563ff48bf5SDavid du Colombier continue;
15573ff48bf5SDavid du Colombier if(ipcmp(lp->laddr, dest) != 0)
15583ff48bf5SDavid du Colombier continue;
15593ff48bf5SDavid du Colombier
1560ef9eff0bSDavid du Colombier /* each new SYN restarts the retransmits */
15613ff48bf5SDavid du Colombier lp->irs = seg->seq;
15623ff48bf5SDavid du Colombier break;
15633ff48bf5SDavid du Colombier }
15643ff48bf5SDavid du Colombier lp = *l;
15653ff48bf5SDavid du Colombier if(lp == nil){
15663ff48bf5SDavid du Colombier if(tpriv->nlimbo >= Maxlimbo && tpriv->lht[h]){
15673ff48bf5SDavid du Colombier lp = tpriv->lht[h];
15683ff48bf5SDavid du Colombier tpriv->lht[h] = lp->next;
15693ff48bf5SDavid du Colombier lp->next = nil;
15703ff48bf5SDavid du Colombier } else {
15713ff48bf5SDavid du Colombier lp = malloc(sizeof(*lp));
15723ff48bf5SDavid du Colombier if(lp == nil)
15733ff48bf5SDavid du Colombier return;
15743ff48bf5SDavid du Colombier tpriv->nlimbo++;
15753ff48bf5SDavid du Colombier }
15763ff48bf5SDavid du Colombier *l = lp;
15773ff48bf5SDavid du Colombier lp->version = version;
15783ff48bf5SDavid du Colombier ipmove(lp->laddr, dest);
15793ff48bf5SDavid du Colombier ipmove(lp->raddr, source);
15803ff48bf5SDavid du Colombier lp->lport = seg->dest;
15813ff48bf5SDavid du Colombier lp->rport = seg->source;
15823ff48bf5SDavid du Colombier lp->mss = seg->mss;
15833f695129SDavid du Colombier lp->rcvscale = seg->ws;
15843ff48bf5SDavid du Colombier lp->irs = seg->seq;
15853ff48bf5SDavid du Colombier lp->iss = (nrand(1<<16)<<16)|nrand(1<<16);
15863ff48bf5SDavid du Colombier }
15873ff48bf5SDavid du Colombier
15883ff48bf5SDavid du Colombier if(sndsynack(s->p, lp) < 0){
15893ff48bf5SDavid du Colombier *l = lp->next;
15903ff48bf5SDavid du Colombier tpriv->nlimbo--;
15913ff48bf5SDavid du Colombier free(lp);
15923ff48bf5SDavid du Colombier }
15933ff48bf5SDavid du Colombier }
15943ff48bf5SDavid du Colombier
15953ff48bf5SDavid du Colombier /*
15963ff48bf5SDavid du Colombier * resend SYN ACK's once every SYNACK_RXTIMER ms.
15973ff48bf5SDavid du Colombier */
15983ff48bf5SDavid du Colombier static void
limborexmit(Proto * tcp)15993ff48bf5SDavid du Colombier limborexmit(Proto *tcp)
16003ff48bf5SDavid du Colombier {
16013ff48bf5SDavid du Colombier Tcppriv *tpriv;
16023ff48bf5SDavid du Colombier Limbo **l, *lp;
16033ff48bf5SDavid du Colombier int h;
16043ff48bf5SDavid du Colombier int seen;
16053ff48bf5SDavid du Colombier ulong now;
16063ff48bf5SDavid du Colombier
16073ff48bf5SDavid du Colombier tpriv = tcp->priv;
16083ff48bf5SDavid du Colombier
16093ff48bf5SDavid du Colombier if(!canqlock(tcp))
16103ff48bf5SDavid du Colombier return;
16113ff48bf5SDavid du Colombier seen = 0;
16123ff48bf5SDavid du Colombier now = NOW;
16133ff48bf5SDavid du Colombier for(h = 0; h < NLHT && seen < tpriv->nlimbo; h++){
16143ff48bf5SDavid du Colombier for(l = &tpriv->lht[h]; *l != nil && seen < tpriv->nlimbo; ){
16153ff48bf5SDavid du Colombier lp = *l;
16163ff48bf5SDavid du Colombier seen++;
16173ff48bf5SDavid du Colombier if(now - lp->lastsend < (lp->rexmits+1)*SYNACK_RXTIMER)
16183ff48bf5SDavid du Colombier continue;
16193ff48bf5SDavid du Colombier
16203ff48bf5SDavid du Colombier /* time it out after 1 second */
16213ff48bf5SDavid du Colombier if(++(lp->rexmits) > 5){
16223ff48bf5SDavid du Colombier tpriv->nlimbo--;
16233ff48bf5SDavid du Colombier *l = lp->next;
16243ff48bf5SDavid du Colombier free(lp);
16253ff48bf5SDavid du Colombier continue;
16263ff48bf5SDavid du Colombier }
16273ff48bf5SDavid du Colombier
16283ff48bf5SDavid du Colombier /* if we're being attacked, don't bother resending SYN ACK's */
16293ff48bf5SDavid du Colombier if(tpriv->nlimbo > 100)
16303ff48bf5SDavid du Colombier continue;
16313ff48bf5SDavid du Colombier
16323ff48bf5SDavid du Colombier if(sndsynack(tcp, lp) < 0){
16333ff48bf5SDavid du Colombier tpriv->nlimbo--;
16343ff48bf5SDavid du Colombier *l = lp->next;
16353ff48bf5SDavid du Colombier free(lp);
16363ff48bf5SDavid du Colombier continue;
16373ff48bf5SDavid du Colombier }
16383ff48bf5SDavid du Colombier
16393ff48bf5SDavid du Colombier l = &lp->next;
16403ff48bf5SDavid du Colombier }
16413ff48bf5SDavid du Colombier }
16423ff48bf5SDavid du Colombier qunlock(tcp);
16433ff48bf5SDavid du Colombier }
16443ff48bf5SDavid du Colombier
16453ff48bf5SDavid du Colombier /*
16463ff48bf5SDavid du Colombier * lookup call in limbo. if found, throw it out.
16473ff48bf5SDavid du Colombier *
16483ff48bf5SDavid du Colombier * called with proto locked
16493ff48bf5SDavid du Colombier */
16503ff48bf5SDavid du Colombier static void
limborst(Conv * s,Tcp * segp,uchar * src,uchar * dst,uchar version)16513ff48bf5SDavid du Colombier limborst(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
16523ff48bf5SDavid du Colombier {
16533ff48bf5SDavid du Colombier Limbo *lp, **l;
16543ff48bf5SDavid du Colombier int h;
16553ff48bf5SDavid du Colombier Tcppriv *tpriv;
16563ff48bf5SDavid du Colombier
16573ff48bf5SDavid du Colombier tpriv = s->p->priv;
16583ff48bf5SDavid du Colombier
16593ff48bf5SDavid du Colombier /* find a call in limbo */
16603ff48bf5SDavid du Colombier h = hashipa(src, segp->source);
16613ff48bf5SDavid du Colombier for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){
16623ff48bf5SDavid du Colombier lp = *l;
16633ff48bf5SDavid du Colombier if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version)
16643ff48bf5SDavid du Colombier continue;
16653ff48bf5SDavid du Colombier if(ipcmp(lp->laddr, dst) != 0)
16663ff48bf5SDavid du Colombier continue;
16673ff48bf5SDavid du Colombier if(ipcmp(lp->raddr, src) != 0)
16683ff48bf5SDavid du Colombier continue;
16693ff48bf5SDavid du Colombier
16703ff48bf5SDavid du Colombier /* RST can only follow the SYN */
16713ff48bf5SDavid du Colombier if(segp->seq == lp->irs+1){
16723ff48bf5SDavid du Colombier tpriv->nlimbo--;
16733ff48bf5SDavid du Colombier *l = lp->next;
16743ff48bf5SDavid du Colombier free(lp);
16753ff48bf5SDavid du Colombier }
16763ff48bf5SDavid du Colombier break;
16773ff48bf5SDavid du Colombier }
16783ff48bf5SDavid du Colombier }
16793ff48bf5SDavid du Colombier
16801ce6c95fSDavid du Colombier static void
initialwindow(Tcpctl * tcb)16811ce6c95fSDavid du Colombier initialwindow(Tcpctl *tcb)
16821ce6c95fSDavid du Colombier {
16831ce6c95fSDavid du Colombier /* RFC 3390 initial window */
16841ce6c95fSDavid du Colombier if(tcb->mss < 1095)
16851ce6c95fSDavid du Colombier tcb->cwind = 4*tcb->mss;
16861ce6c95fSDavid du Colombier else if(tcb->mss < 2190)
16871ce6c95fSDavid du Colombier tcb->cwind = 2*2190;
16881ce6c95fSDavid du Colombier else
16891ce6c95fSDavid du Colombier tcb->cwind = 2*tcb->mss;
16901ce6c95fSDavid du Colombier }
16911ce6c95fSDavid du Colombier
16923ff48bf5SDavid du Colombier /*
16933f695129SDavid du Colombier * come here when we finally get an ACK to our SYN-ACK.
16943ff48bf5SDavid du Colombier * lookup call in limbo. if found, create a new conversation
16953ff48bf5SDavid du Colombier *
16963ff48bf5SDavid du Colombier * called with proto locked
16973ff48bf5SDavid du Colombier */
16983ff48bf5SDavid du Colombier static Conv*
tcpincoming(Conv * s,Tcp * segp,uchar * src,uchar * dst,uchar version)16993ff48bf5SDavid du Colombier tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
17007dd7cddfSDavid du Colombier {
17017dd7cddfSDavid du Colombier Conv *new;
17027dd7cddfSDavid du Colombier Tcpctl *tcb;
170380ee5cbfSDavid du Colombier Tcppriv *tpriv;
17043ff48bf5SDavid du Colombier Tcp4hdr *h4;
17053ff48bf5SDavid du Colombier Tcp6hdr *h6;
17063ff48bf5SDavid du Colombier Limbo *lp, **l;
17073ff48bf5SDavid du Colombier int h;
17087dd7cddfSDavid du Colombier
17093ff48bf5SDavid du Colombier /* unless it's just an ack, it can't be someone coming out of limbo */
17103ff48bf5SDavid du Colombier if((segp->flags & SYN) || (segp->flags & ACK) == 0)
17113ff48bf5SDavid du Colombier return nil;
17123ff48bf5SDavid du Colombier
17133ff48bf5SDavid du Colombier tpriv = s->p->priv;
17143ff48bf5SDavid du Colombier
17153ff48bf5SDavid du Colombier /* find a call in limbo */
17163ff48bf5SDavid du Colombier h = hashipa(src, segp->source);
17173ff48bf5SDavid du Colombier for(l = &tpriv->lht[h]; (lp = *l) != nil; l = &lp->next){
17187133e0eeSDavid du Colombier netlog(s->p->f, Logtcp, "tcpincoming s %I!%ud/%I!%ud d %I!%ud/%I!%ud v %d/%d\n",
17193ff48bf5SDavid du Colombier src, segp->source, lp->raddr, lp->rport,
17203ff48bf5SDavid du Colombier dst, segp->dest, lp->laddr, lp->lport,
17213ff48bf5SDavid du Colombier version, lp->version
17223ff48bf5SDavid du Colombier );
17233ff48bf5SDavid du Colombier
17243ff48bf5SDavid du Colombier if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version)
17253ff48bf5SDavid du Colombier continue;
17263ff48bf5SDavid du Colombier if(ipcmp(lp->laddr, dst) != 0)
17273ff48bf5SDavid du Colombier continue;
17283ff48bf5SDavid du Colombier if(ipcmp(lp->raddr, src) != 0)
17293ff48bf5SDavid du Colombier continue;
17303ff48bf5SDavid du Colombier
17313ff48bf5SDavid du Colombier /* we're assuming no data with the initial SYN */
17323ff48bf5SDavid du Colombier if(segp->seq != lp->irs+1 || segp->ack != lp->iss+1){
173384363d68SDavid du Colombier netlog(s->p->f, Logtcp, "tcpincoming s %lux/%lux a %lux %lux\n",
17343ff48bf5SDavid du Colombier segp->seq, lp->irs+1, segp->ack, lp->iss+1);
17353ff48bf5SDavid du Colombier lp = nil;
17363ff48bf5SDavid du Colombier } else {
17373ff48bf5SDavid du Colombier tpriv->nlimbo--;
17383ff48bf5SDavid du Colombier *l = lp->next;
17393ff48bf5SDavid du Colombier }
17403ff48bf5SDavid du Colombier break;
17413ff48bf5SDavid du Colombier }
17423ff48bf5SDavid du Colombier if(lp == nil)
17433ff48bf5SDavid du Colombier return nil;
17443ff48bf5SDavid du Colombier
17453ff48bf5SDavid du Colombier new = Fsnewcall(s, src, segp->source, dst, segp->dest, version);
17467dd7cddfSDavid du Colombier if(new == nil)
17477dd7cddfSDavid du Colombier return nil;
17487dd7cddfSDavid du Colombier
17497dd7cddfSDavid du Colombier memmove(new->ptcl, s->ptcl, sizeof(Tcpctl));
17507dd7cddfSDavid du Colombier tcb = (Tcpctl*)new->ptcl;
17517dd7cddfSDavid du Colombier tcb->flags &= ~CLONE;
17527dd7cddfSDavid du Colombier tcb->timer.arg = new;
17539a747e4fSDavid du Colombier tcb->timer.state = TcptimerOFF;
17547dd7cddfSDavid du Colombier tcb->acktimer.arg = new;
17559a747e4fSDavid du Colombier tcb->acktimer.state = TcptimerOFF;
17567dd7cddfSDavid du Colombier tcb->katimer.arg = new;
17579a747e4fSDavid du Colombier tcb->katimer.state = TcptimerOFF;
17587dd7cddfSDavid du Colombier tcb->rtt_timer.arg = new;
17599a747e4fSDavid du Colombier tcb->rtt_timer.state = TcptimerOFF;
17607dd7cddfSDavid du Colombier
17613ff48bf5SDavid du Colombier tcb->irs = lp->irs;
17623ff48bf5SDavid du Colombier tcb->rcv.nxt = tcb->irs+1;
17631ce6c95fSDavid du Colombier tcb->rcv.wptr = tcb->rcv.nxt;
17641ce6c95fSDavid du Colombier tcb->rcv.wsnt = 0;
17653ff48bf5SDavid du Colombier tcb->rcv.urg = tcb->rcv.nxt;
17667dd7cddfSDavid du Colombier
17673ff48bf5SDavid du Colombier tcb->iss = lp->iss;
17683ff48bf5SDavid du Colombier tcb->rttseq = tcb->iss;
17693ff48bf5SDavid du Colombier tcb->snd.wl2 = tcb->iss;
17703ff48bf5SDavid du Colombier tcb->snd.una = tcb->iss+1;
17713ff48bf5SDavid du Colombier tcb->snd.ptr = tcb->iss+1;
17723ff48bf5SDavid du Colombier tcb->snd.nxt = tcb->iss+1;
17731ce6c95fSDavid du Colombier tcb->snd.rxt = tcb->iss+1;
17743ff48bf5SDavid du Colombier tcb->flgcnt = 0;
17753ff48bf5SDavid du Colombier tcb->flags |= SYNACK;
17763ff48bf5SDavid du Colombier
17773ff48bf5SDavid du Colombier /* our sending max segment size cannot be bigger than what he asked for */
17787ec5746aSDavid du Colombier if(lp->mss != 0 && lp->mss < tcb->mss) {
17793ff48bf5SDavid du Colombier tcb->mss = lp->mss;
17807ec5746aSDavid du Colombier tpriv->stats[Mss] = tcb->mss;
17817ec5746aSDavid du Colombier }
17823ff48bf5SDavid du Colombier
17833f695129SDavid du Colombier /* window scaling */
17843f695129SDavid du Colombier tcpsetscale(new, tcb, lp->rcvscale, lp->sndscale);
17853f695129SDavid du Colombier
17861ce6c95fSDavid du Colombier /* congestion window */
17873ff48bf5SDavid du Colombier tcb->snd.wnd = segp->wnd;
17881ce6c95fSDavid du Colombier initialwindow(tcb);
17893ff48bf5SDavid du Colombier
17903ff48bf5SDavid du Colombier /* set initial round trip time */
17913ff48bf5SDavid du Colombier tcb->sndsyntime = lp->lastsend+lp->rexmits*SYNACK_RXTIMER;
17923ff48bf5SDavid du Colombier tcpsynackrtt(new);
17933ff48bf5SDavid du Colombier
17943ff48bf5SDavid du Colombier free(lp);
17953ff48bf5SDavid du Colombier
17963ff48bf5SDavid du Colombier /* set up proto header */
17973ff48bf5SDavid du Colombier switch(version){
17983ff48bf5SDavid du Colombier case V4:
17993ff48bf5SDavid du Colombier h4 = &tcb->protohdr.tcp4hdr;
18003ff48bf5SDavid du Colombier memset(h4, 0, sizeof(*h4));
18013ff48bf5SDavid du Colombier h4->proto = IP_TCPPROTO;
18023ff48bf5SDavid du Colombier hnputs(h4->tcpsport, new->lport);
18033ff48bf5SDavid du Colombier hnputs(h4->tcpdport, new->rport);
18043ff48bf5SDavid du Colombier v6tov4(h4->tcpsrc, dst);
18053ff48bf5SDavid du Colombier v6tov4(h4->tcpdst, src);
18063ff48bf5SDavid du Colombier break;
18073ff48bf5SDavid du Colombier case V6:
18083ff48bf5SDavid du Colombier h6 = &tcb->protohdr.tcp6hdr;
18093ff48bf5SDavid du Colombier memset(h6, 0, sizeof(*h6));
18103ff48bf5SDavid du Colombier h6->proto = IP_TCPPROTO;
18113ff48bf5SDavid du Colombier hnputs(h6->tcpsport, new->lport);
18123ff48bf5SDavid du Colombier hnputs(h6->tcpdport, new->rport);
18133ff48bf5SDavid du Colombier ipmove(h6->tcpsrc, dst);
18143ff48bf5SDavid du Colombier ipmove(h6->tcpdst, src);
18153ff48bf5SDavid du Colombier break;
18163ff48bf5SDavid du Colombier default:
18173ff48bf5SDavid du Colombier panic("tcpincoming: version %d", new->ipversion);
18183ff48bf5SDavid du Colombier }
18193ff48bf5SDavid du Colombier
18203ff48bf5SDavid du Colombier tcpsetstate(new, Established);
18213ff48bf5SDavid du Colombier
182280ee5cbfSDavid du Colombier iphtadd(&tpriv->ht, new);
182380ee5cbfSDavid du Colombier
18247dd7cddfSDavid du Colombier return new;
18257dd7cddfSDavid du Colombier }
18267dd7cddfSDavid du Colombier
18271ce6c95fSDavid du Colombier static int
seq_within(ulong x,ulong low,ulong high)18287dd7cddfSDavid du Colombier seq_within(ulong x, ulong low, ulong high)
18297dd7cddfSDavid du Colombier {
18307dd7cddfSDavid du Colombier if(low <= high){
18317dd7cddfSDavid du Colombier if(low <= x && x <= high)
18327dd7cddfSDavid du Colombier return 1;
18337dd7cddfSDavid du Colombier }
18347dd7cddfSDavid du Colombier else {
18357dd7cddfSDavid du Colombier if(x >= low || x <= high)
18367dd7cddfSDavid du Colombier return 1;
18377dd7cddfSDavid du Colombier }
18387dd7cddfSDavid du Colombier return 0;
18397dd7cddfSDavid du Colombier }
18407dd7cddfSDavid du Colombier
18411ce6c95fSDavid du Colombier static int
seq_lt(ulong x,ulong y)18427dd7cddfSDavid du Colombier seq_lt(ulong x, ulong y)
18437dd7cddfSDavid du Colombier {
18447dd7cddfSDavid du Colombier return (int)(x-y) < 0;
18457dd7cddfSDavid du Colombier }
18467dd7cddfSDavid du Colombier
18471ce6c95fSDavid du Colombier static int
seq_le(ulong x,ulong y)18487dd7cddfSDavid du Colombier seq_le(ulong x, ulong y)
18497dd7cddfSDavid du Colombier {
18507dd7cddfSDavid du Colombier return (int)(x-y) <= 0;
18517dd7cddfSDavid du Colombier }
18527dd7cddfSDavid du Colombier
18531ce6c95fSDavid du Colombier static int
seq_gt(ulong x,ulong y)18547dd7cddfSDavid du Colombier seq_gt(ulong x, ulong y)
18557dd7cddfSDavid du Colombier {
18567dd7cddfSDavid du Colombier return (int)(x-y) > 0;
18577dd7cddfSDavid du Colombier }
18587dd7cddfSDavid du Colombier
18591ce6c95fSDavid du Colombier static int
seq_ge(ulong x,ulong y)18607dd7cddfSDavid du Colombier seq_ge(ulong x, ulong y)
18617dd7cddfSDavid du Colombier {
18627dd7cddfSDavid du Colombier return (int)(x-y) >= 0;
18637dd7cddfSDavid du Colombier }
18647dd7cddfSDavid du Colombier
18657dd7cddfSDavid du Colombier /*
18667dd7cddfSDavid du Colombier * use the time between the first SYN and it's ack as the
18677dd7cddfSDavid du Colombier * initial round trip time
18687dd7cddfSDavid du Colombier */
18691ce6c95fSDavid du Colombier static void
tcpsynackrtt(Conv * s)18707dd7cddfSDavid du Colombier tcpsynackrtt(Conv *s)
18717dd7cddfSDavid du Colombier {
18727dd7cddfSDavid du Colombier Tcpctl *tcb;
18737dd7cddfSDavid du Colombier int delta;
18747dd7cddfSDavid du Colombier Tcppriv *tpriv;
18757dd7cddfSDavid du Colombier
18767dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
18777dd7cddfSDavid du Colombier tpriv = s->p->priv;
18787dd7cddfSDavid du Colombier
18793ff48bf5SDavid du Colombier delta = NOW - tcb->sndsyntime;
18807dd7cddfSDavid du Colombier tcb->srtt = delta<<LOGAGAIN;
18817dd7cddfSDavid du Colombier tcb->mdev = delta<<LOGDGAIN;
18827dd7cddfSDavid du Colombier
18837dd7cddfSDavid du Colombier /* halt round trip timer */
18847dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
18857dd7cddfSDavid du Colombier }
18867dd7cddfSDavid du Colombier
18871ce6c95fSDavid du Colombier static void
update(Conv * s,Tcp * seg)18887dd7cddfSDavid du Colombier update(Conv *s, Tcp *seg)
18897dd7cddfSDavid du Colombier {
18907dd7cddfSDavid du Colombier int rtt, delta;
18917dd7cddfSDavid du Colombier Tcpctl *tcb;
18923f695129SDavid du Colombier ulong acked;
18937dd7cddfSDavid du Colombier Tcppriv *tpriv;
18947dd7cddfSDavid du Colombier
18951ce6c95fSDavid du Colombier if(seg->update)
18961ce6c95fSDavid du Colombier return;
18971ce6c95fSDavid du Colombier seg->update = 1;
18981ce6c95fSDavid du Colombier
18997dd7cddfSDavid du Colombier tpriv = s->p->priv;
19007dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
19017dd7cddfSDavid du Colombier
19021ce6c95fSDavid du Colombier /* catch zero-window updates, update window & recover */
19031ce6c95fSDavid du Colombier if(tcb->snd.wnd == 0 && seg->wnd > 0 &&
19041ce6c95fSDavid du Colombier seq_lt(seg->ack, tcb->snd.ptr)){
19051ce6c95fSDavid du Colombier netlog(s->p->f, Logtcp, "tcp: zwu ack %lud una %lud ptr %lud win %lud\n",
19061ce6c95fSDavid du Colombier seg->ack, tcb->snd.una, tcb->snd.ptr, seg->wnd);
19071ce6c95fSDavid du Colombier tcb->snd.wnd = seg->wnd;
19081ce6c95fSDavid du Colombier goto recovery;
19097dd7cddfSDavid du Colombier }
19107dd7cddfSDavid du Colombier
19111ce6c95fSDavid du Colombier /* newreno fast retransmit */
19121ce6c95fSDavid du Colombier if(seg->ack == tcb->snd.una && tcb->snd.una != tcb->snd.nxt &&
19131ce6c95fSDavid du Colombier ++tcb->snd.dupacks == 3){ /* was TCPREXMTTHRESH */
19141ce6c95fSDavid du Colombier recovery:
19151ce6c95fSDavid du Colombier if(tcb->snd.recovery){
19161ce6c95fSDavid du Colombier tpriv->stats[RecoveryCwind]++;
19171ce6c95fSDavid du Colombier tcb->cwind += tcb->mss;
19181ce6c95fSDavid du Colombier }else if(seq_le(tcb->snd.rxt, seg->ack)){
19191ce6c95fSDavid du Colombier tpriv->stats[Recovery]++;
19201ce6c95fSDavid du Colombier tcb->abcbytes = 0;
19217dd7cddfSDavid du Colombier tcb->snd.recovery = 1;
19221ce6c95fSDavid du Colombier tcb->snd.partialack = 0;
19237dd7cddfSDavid du Colombier tcb->snd.rxt = tcb->snd.nxt;
19241ce6c95fSDavid du Colombier tcpcongestion(tcb);
19251ce6c95fSDavid du Colombier tcb->cwind = tcb->ssthresh + 3*tcb->mss;
19261ce6c95fSDavid du Colombier netlog(s->p->f, Logtcpwin, "recovery inflate %ld ss %ld @%lud\n",
19271ce6c95fSDavid du Colombier tcb->cwind, tcb->ssthresh, tcb->snd.rxt);
19287dd7cddfSDavid du Colombier tcprxmit(s);
19297dd7cddfSDavid du Colombier }else{
19301ce6c95fSDavid du Colombier tpriv->stats[RecoveryNoSeq]++;
19311ce6c95fSDavid du Colombier netlog(s->p->f, Logtcpwin, "!recov %lud not ≤ %lud %ld\n",
19321ce6c95fSDavid du Colombier tcb->snd.rxt, seg->ack, tcb->snd.rxt - seg->ack);
19331ce6c95fSDavid du Colombier /* don't enter fast retransmit, don't change ssthresh */
19347dd7cddfSDavid du Colombier }
19351ce6c95fSDavid du Colombier }else if(tcb->snd.recovery){
19361ce6c95fSDavid du Colombier tpriv->stats[RecoveryCwind]++;
19371ce6c95fSDavid du Colombier tcb->cwind += tcb->mss;
19387dd7cddfSDavid du Colombier }
19397dd7cddfSDavid du Colombier
194080ee5cbfSDavid du Colombier /*
194180ee5cbfSDavid du Colombier * update window
194280ee5cbfSDavid du Colombier */
194380ee5cbfSDavid du Colombier if(seq_gt(seg->ack, tcb->snd.wl2)
194480ee5cbfSDavid du Colombier || (tcb->snd.wl2 == seg->ack && seg->wnd > tcb->snd.wnd)){
19451ce6c95fSDavid du Colombier /* clear dupack if we advance wl2 */
19461ce6c95fSDavid du Colombier if(tcb->snd.wl2 != seg->ack)
19471ce6c95fSDavid du Colombier tcb->snd.dupacks = 0;
19487dd7cddfSDavid du Colombier tcb->snd.wnd = seg->wnd;
19497dd7cddfSDavid du Colombier tcb->snd.wl2 = seg->ack;
19507dd7cddfSDavid du Colombier }
19517dd7cddfSDavid du Colombier
195280ee5cbfSDavid du Colombier if(!seq_gt(seg->ack, tcb->snd.una)){
195380ee5cbfSDavid du Colombier /*
195480ee5cbfSDavid du Colombier * don't let us hangup if sending into a closed window and
195580ee5cbfSDavid du Colombier * we're still getting acks
195680ee5cbfSDavid du Colombier */
19571ce6c95fSDavid du Colombier if((tcb->flags&RETRAN) && tcb->snd.wnd == 0)
195880ee5cbfSDavid du Colombier tcb->backedoff = MAXBACKMS/4;
19597dd7cddfSDavid du Colombier return;
196080ee5cbfSDavid du Colombier }
19617dd7cddfSDavid du Colombier
19627dd7cddfSDavid du Colombier /* Compute the new send window size */
19637dd7cddfSDavid du Colombier acked = seg->ack - tcb->snd.una;
19647dd7cddfSDavid du Colombier
19657dd7cddfSDavid du Colombier /* avoid slow start and timers for SYN acks */
19667dd7cddfSDavid du Colombier if((tcb->flags & SYNACK) == 0) {
19677dd7cddfSDavid du Colombier tcb->flags |= SYNACK;
19687dd7cddfSDavid du Colombier acked--;
196980ee5cbfSDavid du Colombier tcb->flgcnt--;
19707dd7cddfSDavid du Colombier goto done;
19717dd7cddfSDavid du Colombier }
19727dd7cddfSDavid du Colombier
19731ce6c95fSDavid du Colombier /*
19741ce6c95fSDavid du Colombier * congestion control
19751ce6c95fSDavid du Colombier */
19761ce6c95fSDavid du Colombier if(tcb->snd.recovery){
19771ce6c95fSDavid du Colombier if(seq_ge(seg->ack, tcb->snd.rxt)){
19781ce6c95fSDavid du Colombier /* recovery finished; deflate window */
19791ce6c95fSDavid du Colombier tpriv->stats[RecoveryDone]++;
19801ce6c95fSDavid du Colombier tcb->snd.dupacks = 0;
19811ce6c95fSDavid du Colombier tcb->snd.recovery = 0;
19821ce6c95fSDavid du Colombier tcb->cwind = (tcb->snd.nxt - tcb->snd.una) + tcb->mss;
19831ce6c95fSDavid du Colombier if(tcb->ssthresh < tcb->cwind)
19841ce6c95fSDavid du Colombier tcb->cwind = tcb->ssthresh;
19851ce6c95fSDavid du Colombier netlog(s->p->f, Logtcpwin, "recovery deflate %ld %ld\n",
19861ce6c95fSDavid du Colombier tcb->cwind, tcb->ssthresh);
19871ce6c95fSDavid du Colombier } else {
19881ce6c95fSDavid du Colombier /* partial ack; we lost more than one segment */
19891ce6c95fSDavid du Colombier tpriv->stats[RecoveryPA]++;
19901ce6c95fSDavid du Colombier if(tcb->cwind > acked)
19911ce6c95fSDavid du Colombier tcb->cwind -= acked;
19921ce6c95fSDavid du Colombier else{
19931ce6c95fSDavid du Colombier netlog(s->p->f, Logtcpwin, "partial ack neg\n");
19941ce6c95fSDavid du Colombier tcb->cwind = tcb->mss;
19957dd7cddfSDavid du Colombier }
19961ce6c95fSDavid du Colombier netlog(s->p->f, Logtcpwin, "partial ack %ld left %ld cwind %ld\n",
19971ce6c95fSDavid du Colombier acked, tcb->snd.rxt - seg->ack, tcb->cwind);
19987dd7cddfSDavid du Colombier
19991ce6c95fSDavid du Colombier if(acked >= tcb->mss)
20001ce6c95fSDavid du Colombier tcb->cwind += tcb->mss;
20011ce6c95fSDavid du Colombier tcb->snd.partialack++;
20027dd7cddfSDavid du Colombier }
20031ce6c95fSDavid du Colombier } else
20041ce6c95fSDavid du Colombier tcpabcincr(tcb, acked);
20057dd7cddfSDavid du Colombier
20067dd7cddfSDavid du Colombier /* Adjust the timers according to the round trip time */
20071ce6c95fSDavid du Colombier /* TODO: fix sloppy treatment of overflow cases here. */
20089a747e4fSDavid du Colombier if(tcb->rtt_timer.state == TcptimerON && seq_ge(seg->ack, tcb->rttseq)) {
20097dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
20107dd7cddfSDavid du Colombier if((tcb->flags&RETRAN) == 0) {
20117dd7cddfSDavid du Colombier tcb->backoff = 0;
20127dd7cddfSDavid du Colombier tcb->backedoff = 0;
20137dd7cddfSDavid du Colombier rtt = tcb->rtt_timer.start - tcb->rtt_timer.count;
20147dd7cddfSDavid du Colombier if(rtt == 0)
20151ce6c95fSDavid du Colombier rtt = 1; /* else all close sys's will rexmit in 0 time */
20167dd7cddfSDavid du Colombier rtt *= MSPTICK;
20177dd7cddfSDavid du Colombier if(tcb->srtt == 0) {
20187dd7cddfSDavid du Colombier tcb->srtt = rtt << LOGAGAIN;
20197dd7cddfSDavid du Colombier tcb->mdev = rtt << LOGDGAIN;
20207dd7cddfSDavid du Colombier } else {
20217dd7cddfSDavid du Colombier delta = rtt - (tcb->srtt>>LOGAGAIN);
20227dd7cddfSDavid du Colombier tcb->srtt += delta;
20237dd7cddfSDavid du Colombier if(tcb->srtt <= 0)
20247dd7cddfSDavid du Colombier tcb->srtt = 1;
20257dd7cddfSDavid du Colombier
20267dd7cddfSDavid du Colombier delta = abs(delta) - (tcb->mdev>>LOGDGAIN);
20277dd7cddfSDavid du Colombier tcb->mdev += delta;
20287dd7cddfSDavid du Colombier if(tcb->mdev <= 0)
20297dd7cddfSDavid du Colombier tcb->mdev = 1;
20307dd7cddfSDavid du Colombier }
20319a747e4fSDavid du Colombier tcpsettimer(tcb);
20327dd7cddfSDavid du Colombier }
20337dd7cddfSDavid du Colombier }
20347dd7cddfSDavid du Colombier
20357dd7cddfSDavid du Colombier done:
203680ee5cbfSDavid du Colombier if(qdiscard(s->wq, acked) < acked)
203780ee5cbfSDavid du Colombier tcb->flgcnt--;
20387dd7cddfSDavid du Colombier tcb->snd.una = seg->ack;
20391ce6c95fSDavid du Colombier
20401ce6c95fSDavid du Colombier /* newreno fast recovery */
20411ce6c95fSDavid du Colombier if(tcb->snd.recovery)
20421ce6c95fSDavid du Colombier tcprxmit(s);
20431ce6c95fSDavid du Colombier
20447dd7cddfSDavid du Colombier if(seq_gt(seg->ack, tcb->snd.urg))
20457dd7cddfSDavid du Colombier tcb->snd.urg = seg->ack;
20467dd7cddfSDavid du Colombier
20471ce6c95fSDavid du Colombier if(tcb->snd.una != tcb->snd.nxt){
20481ce6c95fSDavid du Colombier /* `impatient' variant */
20491ce6c95fSDavid du Colombier if(!tcb->snd.recovery || tcb->snd.partialack == 1){
20501ce6c95fSDavid du Colombier tcb->time = NOW;
20511ce6c95fSDavid du Colombier tcb->timeuna = tcb->snd.una;
20527dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->timer);
20531ce6c95fSDavid du Colombier }
20541ce6c95fSDavid du Colombier } else
20553ff48bf5SDavid du Colombier tcphalt(tpriv, &tcb->timer);
20567dd7cddfSDavid du Colombier
20577dd7cddfSDavid du Colombier if(seq_lt(tcb->snd.ptr, tcb->snd.una))
20587dd7cddfSDavid du Colombier tcb->snd.ptr = tcb->snd.una;
20597dd7cddfSDavid du Colombier
20601ce6c95fSDavid du Colombier if(!tcb->snd.recovery)
20617dd7cddfSDavid du Colombier tcb->flags &= ~RETRAN;
20627dd7cddfSDavid du Colombier tcb->backoff = 0;
20637dd7cddfSDavid du Colombier tcb->backedoff = 0;
20647dd7cddfSDavid du Colombier }
20657dd7cddfSDavid du Colombier
20661ce6c95fSDavid du Colombier static void
tcpiput(Proto * tcp,Ipifc *,Block * bp)20679a747e4fSDavid du Colombier tcpiput(Proto *tcp, Ipifc*, Block *bp)
20687dd7cddfSDavid du Colombier {
20697dd7cddfSDavid du Colombier Tcp seg;
20703ff48bf5SDavid du Colombier Tcp4hdr *h4;
20713ff48bf5SDavid du Colombier Tcp6hdr *h6;
20727dd7cddfSDavid du Colombier int hdrlen;
20737dd7cddfSDavid du Colombier Tcpctl *tcb;
207484363d68SDavid du Colombier ushort length, csum;
20757dd7cddfSDavid du Colombier uchar source[IPaddrlen], dest[IPaddrlen];
207680ee5cbfSDavid du Colombier Conv *s;
20777dd7cddfSDavid du Colombier Fs *f;
20787dd7cddfSDavid du Colombier Tcppriv *tpriv;
20793ff48bf5SDavid du Colombier uchar version;
20807dd7cddfSDavid du Colombier
20817dd7cddfSDavid du Colombier f = tcp->f;
20827dd7cddfSDavid du Colombier tpriv = tcp->priv;
20837dd7cddfSDavid du Colombier
208459cc4ca5SDavid du Colombier tpriv->stats[InSegs]++;
20857dd7cddfSDavid du Colombier
20863ff48bf5SDavid du Colombier h4 = (Tcp4hdr*)(bp->rp);
20873ff48bf5SDavid du Colombier h6 = (Tcp6hdr*)(bp->rp);
20887dd7cddfSDavid du Colombier
20893ff48bf5SDavid du Colombier if((h4->vihl&0xF0)==IP_VER4) {
20903ff48bf5SDavid du Colombier version = V4;
20913ff48bf5SDavid du Colombier length = nhgets(h4->length);
20923ff48bf5SDavid du Colombier v4tov6(dest, h4->tcpdst);
20933ff48bf5SDavid du Colombier v4tov6(source, h4->tcpsrc);
20947dd7cddfSDavid du Colombier
20953ff48bf5SDavid du Colombier h4->Unused = 0;
20963ff48bf5SDavid du Colombier hnputs(h4->tcplen, length-TCP4_PKT);
20976b6b9ac8SDavid du Colombier if(!(bp->flag & Btcpck) && (h4->tcpcksum[0] || h4->tcpcksum[1]) &&
20983ff48bf5SDavid du Colombier ptclcsum(bp, TCP4_IPLEN, length-TCP4_IPLEN)) {
209959cc4ca5SDavid du Colombier tpriv->stats[CsumErrs]++;
210059cc4ca5SDavid du Colombier tpriv->stats[InErrs]++;
21017dd7cddfSDavid du Colombier netlog(f, Logtcp, "bad tcp proto cksum\n");
21027dd7cddfSDavid du Colombier freeblist(bp);
21037dd7cddfSDavid du Colombier return;
21047dd7cddfSDavid du Colombier }
21057dd7cddfSDavid du Colombier
21063ff48bf5SDavid du Colombier hdrlen = ntohtcp4(&seg, &bp);
21077dd7cddfSDavid du Colombier if(hdrlen < 0){
210859cc4ca5SDavid du Colombier tpriv->stats[HlenErrs]++;
210959cc4ca5SDavid du Colombier tpriv->stats[InErrs]++;
21107dd7cddfSDavid du Colombier netlog(f, Logtcp, "bad tcp hdr len\n");
21117dd7cddfSDavid du Colombier return;
21127dd7cddfSDavid du Colombier }
21137dd7cddfSDavid du Colombier
21147dd7cddfSDavid du Colombier /* trim the packet to the size claimed by the datagram */
21153ff48bf5SDavid du Colombier length -= hdrlen+TCP4_PKT;
21163ff48bf5SDavid du Colombier bp = trimblock(bp, hdrlen+TCP4_PKT, length);
21177dd7cddfSDavid du Colombier if(bp == nil){
211859cc4ca5SDavid du Colombier tpriv->stats[LenErrs]++;
211959cc4ca5SDavid du Colombier tpriv->stats[InErrs]++;
21207dd7cddfSDavid du Colombier netlog(f, Logtcp, "tcp len < 0 after trim\n");
21217dd7cddfSDavid du Colombier return;
21227dd7cddfSDavid du Colombier }
21233ff48bf5SDavid du Colombier }
21243ff48bf5SDavid du Colombier else {
21253ff48bf5SDavid du Colombier int ttl = h6->ttl;
21263ff48bf5SDavid du Colombier int proto = h6->proto;
21273ff48bf5SDavid du Colombier
21283ff48bf5SDavid du Colombier version = V6;
21293ff48bf5SDavid du Colombier length = nhgets(h6->ploadlen);
21303ff48bf5SDavid du Colombier ipmove(dest, h6->tcpdst);
21313ff48bf5SDavid du Colombier ipmove(source, h6->tcpsrc);
21323ff48bf5SDavid du Colombier
21333ff48bf5SDavid du Colombier h6->ploadlen[0] = h6->ploadlen[1] = h6->proto = 0;
21343ff48bf5SDavid du Colombier h6->ttl = proto;
21353ff48bf5SDavid du Colombier hnputl(h6->vcf, length);
21363ff48bf5SDavid du Colombier if((h6->tcpcksum[0] || h6->tcpcksum[1]) &&
213784363d68SDavid du Colombier (csum = ptclcsum(bp, TCP6_IPLEN, length+TCP6_PHDRSIZE)) != 0) {
21383ff48bf5SDavid du Colombier tpriv->stats[CsumErrs]++;
21393ff48bf5SDavid du Colombier tpriv->stats[InErrs]++;
214084363d68SDavid du Colombier netlog(f, Logtcp,
214184363d68SDavid du Colombier "bad tcpv6 proto cksum: got %#ux, computed %#ux\n",
214284363d68SDavid du Colombier h6->tcpcksum[0]<<8 | h6->tcpcksum[1], csum);
21433ff48bf5SDavid du Colombier freeblist(bp);
21443ff48bf5SDavid du Colombier return;
21453ff48bf5SDavid du Colombier }
21463ff48bf5SDavid du Colombier h6->ttl = ttl;
21473ff48bf5SDavid du Colombier h6->proto = proto;
21483ff48bf5SDavid du Colombier hnputs(h6->ploadlen, length);
21493ff48bf5SDavid du Colombier
21503ff48bf5SDavid du Colombier hdrlen = ntohtcp6(&seg, &bp);
21513ff48bf5SDavid du Colombier if(hdrlen < 0){
21523ff48bf5SDavid du Colombier tpriv->stats[HlenErrs]++;
21533ff48bf5SDavid du Colombier tpriv->stats[InErrs]++;
215484363d68SDavid du Colombier netlog(f, Logtcp, "bad tcpv6 hdr len\n");
21553ff48bf5SDavid du Colombier return;
21563ff48bf5SDavid du Colombier }
21573ff48bf5SDavid du Colombier
21583ff48bf5SDavid du Colombier /* trim the packet to the size claimed by the datagram */
21593ff48bf5SDavid du Colombier length -= hdrlen;
21603ff48bf5SDavid du Colombier bp = trimblock(bp, hdrlen+TCP6_PKT, length);
21613ff48bf5SDavid du Colombier if(bp == nil){
21623ff48bf5SDavid du Colombier tpriv->stats[LenErrs]++;
21633ff48bf5SDavid du Colombier tpriv->stats[InErrs]++;
216484363d68SDavid du Colombier netlog(f, Logtcp, "tcpv6 len < 0 after trim\n");
21653ff48bf5SDavid du Colombier return;
21663ff48bf5SDavid du Colombier }
21673ff48bf5SDavid du Colombier }
21687dd7cddfSDavid du Colombier
21697dd7cddfSDavid du Colombier /* lock protocol while searching for a conversation */
21707dd7cddfSDavid du Colombier qlock(tcp);
21717dd7cddfSDavid du Colombier
217280ee5cbfSDavid du Colombier /* Look for a matching conversation */
217380ee5cbfSDavid du Colombier s = iphtlook(&tpriv->ht, source, seg.source, dest, seg.dest);
217480ee5cbfSDavid du Colombier if(s == nil){
21757133e0eeSDavid du Colombier netlog(f, Logtcp, "iphtlook(src %I!%d, dst %I!%d) failed\n",
21767133e0eeSDavid du Colombier source, seg.source, dest, seg.dest);
217780ee5cbfSDavid du Colombier reset:
21787dd7cddfSDavid du Colombier qunlock(tcp);
21793ff48bf5SDavid du Colombier sndrst(tcp, source, dest, length, &seg, version, "no conversation");
21807dd7cddfSDavid du Colombier freeblist(bp);
21817dd7cddfSDavid du Colombier return;
21827dd7cddfSDavid du Colombier }
218380ee5cbfSDavid du Colombier
218480ee5cbfSDavid du Colombier /* if it's a listener, look for the right flags and get a new conv */
218580ee5cbfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
218680ee5cbfSDavid du Colombier if(tcb->state == Listen){
21877dd7cddfSDavid du Colombier if(seg.flags & RST){
21883ff48bf5SDavid du Colombier limborst(s, &seg, source, dest, version);
21897dd7cddfSDavid du Colombier qunlock(tcp);
21907dd7cddfSDavid du Colombier freeblist(bp);
21917dd7cddfSDavid du Colombier return;
21927dd7cddfSDavid du Colombier }
21937dd7cddfSDavid du Colombier
21943ff48bf5SDavid du Colombier /* if this is a new SYN, put the call into limbo */
21953ff48bf5SDavid du Colombier if((seg.flags & SYN) && (seg.flags & ACK) == 0){
21963ff48bf5SDavid du Colombier limbo(s, source, dest, &seg, version);
21973ff48bf5SDavid du Colombier qunlock(tcp);
21983ff48bf5SDavid du Colombier freeblist(bp);
21993ff48bf5SDavid du Colombier return;
22003ff48bf5SDavid du Colombier }
22013ff48bf5SDavid du Colombier
22023ff48bf5SDavid du Colombier /*
22033ff48bf5SDavid du Colombier * if there's a matching call in limbo, tcpincoming will
22043ff48bf5SDavid du Colombier * return it in state Syn_received
22053ff48bf5SDavid du Colombier */
22063ff48bf5SDavid du Colombier s = tcpincoming(s, &seg, source, dest, version);
220780ee5cbfSDavid du Colombier if(s == nil)
220880ee5cbfSDavid du Colombier goto reset;
22097dd7cddfSDavid du Colombier }
22107dd7cddfSDavid du Colombier
22117dd7cddfSDavid du Colombier /* The rest of the input state machine is run with the control block
22127dd7cddfSDavid du Colombier * locked and implements the state machine directly out of the RFC.
22137dd7cddfSDavid du Colombier * Out-of-band data is ignored - it was always a bad idea.
22147dd7cddfSDavid du Colombier */
22157dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
22167dd7cddfSDavid du Colombier if(waserror()){
22177dd7cddfSDavid du Colombier qunlock(s);
22187dd7cddfSDavid du Colombier nexterror();
22197dd7cddfSDavid du Colombier }
22207dd7cddfSDavid du Colombier qlock(s);
22217dd7cddfSDavid du Colombier qunlock(tcp);
22227dd7cddfSDavid du Colombier
22233f695129SDavid du Colombier /* fix up window */
22243f695129SDavid du Colombier seg.wnd <<= tcb->rcv.scale;
22253f695129SDavid du Colombier
222640ef9009SDavid du Colombier /* every input packet in puts off the keep alive time out */
2227e288d156SDavid du Colombier tcpsetkacounter(tcb);
22287dd7cddfSDavid du Colombier
22297dd7cddfSDavid du Colombier switch(tcb->state) {
22307dd7cddfSDavid du Colombier case Closed:
22313ff48bf5SDavid du Colombier sndrst(tcp, source, dest, length, &seg, version, "sending to Closed");
22327dd7cddfSDavid du Colombier goto raise;
22337dd7cddfSDavid du Colombier case Syn_sent:
22347dd7cddfSDavid du Colombier if(seg.flags & ACK) {
22357dd7cddfSDavid du Colombier if(!seq_within(seg.ack, tcb->iss+1, tcb->snd.nxt)) {
22363ff48bf5SDavid du Colombier sndrst(tcp, source, dest, length, &seg, version,
22373ff48bf5SDavid du Colombier "bad seq in Syn_sent");
22387dd7cddfSDavid du Colombier goto raise;
22397dd7cddfSDavid du Colombier }
22407dd7cddfSDavid du Colombier }
22417dd7cddfSDavid du Colombier if(seg.flags & RST) {
22427dd7cddfSDavid du Colombier if(seg.flags & ACK)
22437dd7cddfSDavid du Colombier localclose(s, Econrefused);
22447dd7cddfSDavid du Colombier goto raise;
22457dd7cddfSDavid du Colombier }
22467dd7cddfSDavid du Colombier
22477dd7cddfSDavid du Colombier if(seg.flags & SYN) {
22487dd7cddfSDavid du Colombier procsyn(s, &seg);
22497dd7cddfSDavid du Colombier if(seg.flags & ACK){
22507dd7cddfSDavid du Colombier update(s, &seg);
22517dd7cddfSDavid du Colombier tcpsynackrtt(s);
22527dd7cddfSDavid du Colombier tcpsetstate(s, Established);
22533f695129SDavid du Colombier tcpsetscale(s, tcb, seg.ws, tcb->scale);
22547dd7cddfSDavid du Colombier }
22557dd7cddfSDavid du Colombier else {
22563ff48bf5SDavid du Colombier tcb->time = NOW;
22573f695129SDavid du Colombier tcpsetstate(s, Syn_received); /* DLP - shouldn't this be a reset? */
22587dd7cddfSDavid du Colombier }
22597dd7cddfSDavid du Colombier
22607dd7cddfSDavid du Colombier if(length != 0 || (seg.flags & FIN))
22617dd7cddfSDavid du Colombier break;
22627dd7cddfSDavid du Colombier
22637dd7cddfSDavid du Colombier freeblist(bp);
22647dd7cddfSDavid du Colombier goto output;
22657dd7cddfSDavid du Colombier }
22667dd7cddfSDavid du Colombier else
22677dd7cddfSDavid du Colombier freeblist(bp);
22687dd7cddfSDavid du Colombier
22697dd7cddfSDavid du Colombier qunlock(s);
22707dd7cddfSDavid du Colombier poperror();
22717dd7cddfSDavid du Colombier return;
22727dd7cddfSDavid du Colombier case Syn_received:
22737dd7cddfSDavid du Colombier /* doesn't matter if it's the correct ack, we're just trying to set timing */
22747dd7cddfSDavid du Colombier if(seg.flags & ACK)
22757dd7cddfSDavid du Colombier tcpsynackrtt(s);
22767dd7cddfSDavid du Colombier break;
22777dd7cddfSDavid du Colombier }
22787dd7cddfSDavid du Colombier
22793ff48bf5SDavid du Colombier /*
22803ff48bf5SDavid du Colombier * One DOS attack is to open connections to us and then forget about them,
22813ff48bf5SDavid du Colombier * thereby tying up a conv at no long term cost to the attacker.
22823ff48bf5SDavid du Colombier * This is an attempt to defeat these stateless DOS attacks. See
22833ff48bf5SDavid du Colombier * corresponding code in tcpsendka().
22843ff48bf5SDavid du Colombier */
22853ff48bf5SDavid du Colombier if(tcb->state != Syn_received && (seg.flags & RST) == 0){
22863ff48bf5SDavid du Colombier if(tcpporthogdefense
22873ff48bf5SDavid du Colombier && seq_within(seg.ack, tcb->snd.una-(1<<31), tcb->snd.una-(1<<29))){
22883ff48bf5SDavid du Colombier print("stateless hog %I.%d->%I.%d f %ux %lux - %lux - %lux\n",
22893ff48bf5SDavid du Colombier source, seg.source, dest, seg.dest, seg.flags,
22903ff48bf5SDavid du Colombier tcb->snd.una-(1<<31), seg.ack, tcb->snd.una-(1<<29));
22913ff48bf5SDavid du Colombier localclose(s, "stateless hog");
22923ff48bf5SDavid du Colombier }
22933ff48bf5SDavid du Colombier }
22943ff48bf5SDavid du Colombier
22957dd7cddfSDavid du Colombier /* Cut the data to fit the receive window */
22961ce6c95fSDavid du Colombier tcprcvwin(s);
22977dd7cddfSDavid du Colombier if(tcptrim(tcb, &seg, &bp, &length) == -1) {
22981ce6c95fSDavid du Colombier if(seg.seq+1 != tcb->rcv.nxt || length != 1)
22991ce6c95fSDavid du Colombier netlog(f, Logtcp, "tcp: trim: !inwind: seq %lud-%lud win "
23001ce6c95fSDavid du Colombier "%lud-%lud l %d from %I\n", seg.seq,
23011ce6c95fSDavid du Colombier seg.seq + length - 1, tcb->rcv.nxt,
23021ce6c95fSDavid du Colombier tcb->rcv.nxt + tcb->rcv.wnd-1, length, s->raddr);
23037dd7cddfSDavid du Colombier update(s, &seg);
230480ee5cbfSDavid du Colombier if(qlen(s->wq)+tcb->flgcnt == 0 && tcb->state == Closing) {
23057dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
23067dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
23077dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->katimer);
23087dd7cddfSDavid du Colombier tcpsetstate(s, Time_wait);
23097dd7cddfSDavid du Colombier tcb->timer.start = MSL2*(1000 / MSPTICK);
23107dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->timer);
23117dd7cddfSDavid du Colombier }
23127dd7cddfSDavid du Colombier if(!(seg.flags & RST)) {
23137dd7cddfSDavid du Colombier tcb->flags |= FORCE;
23147dd7cddfSDavid du Colombier goto output;
23157dd7cddfSDavid du Colombier }
23167dd7cddfSDavid du Colombier qunlock(s);
23177dd7cddfSDavid du Colombier poperror();
23187dd7cddfSDavid du Colombier return;
23197dd7cddfSDavid du Colombier }
23207dd7cddfSDavid du Colombier
23217dd7cddfSDavid du Colombier /* Cannot accept so answer with a rst */
23227dd7cddfSDavid du Colombier if(length && tcb->state == Closed) {
23233ff48bf5SDavid du Colombier sndrst(tcp, source, dest, length, &seg, version, "sending to Closed");
23247dd7cddfSDavid du Colombier goto raise;
23257dd7cddfSDavid du Colombier }
23267dd7cddfSDavid du Colombier
23277dd7cddfSDavid du Colombier /* The segment is beyond the current receive pointer so
23287dd7cddfSDavid du Colombier * queue the data in the resequence queue
23297dd7cddfSDavid du Colombier */
23307dd7cddfSDavid du Colombier if(seg.seq != tcb->rcv.nxt)
23317dd7cddfSDavid du Colombier if(length != 0 || (seg.flags & (SYN|FIN))) {
23327dd7cddfSDavid du Colombier update(s, &seg);
23331ce6c95fSDavid du Colombier if(addreseq(f, tcb, tpriv, &seg, bp, length) < 0)
23341ce6c95fSDavid du Colombier print("reseq %I.%d -> %I.%d\n", s->raddr, s->rport,
23351ce6c95fSDavid du Colombier s->laddr, s->lport);
23361ce6c95fSDavid du Colombier tcb->flags |= FORCE; /* force duplicate ack; RFC 5681 §3.2 */
23377dd7cddfSDavid du Colombier goto output;
23387dd7cddfSDavid du Colombier }
23397dd7cddfSDavid du Colombier
23401ce6c95fSDavid du Colombier if(tcb->nreseq > 0)
23411ce6c95fSDavid du Colombier tcb->flags |= FORCE; /* filled hole in seq. space; RFC 5681 §3.2 */
23421ce6c95fSDavid du Colombier
23437dd7cddfSDavid du Colombier /*
23447dd7cddfSDavid du Colombier * keep looping till we've processed this packet plus any
23457dd7cddfSDavid du Colombier * adjacent packets in the resequence queue
23467dd7cddfSDavid du Colombier */
23477dd7cddfSDavid du Colombier for(;;) {
23487dd7cddfSDavid du Colombier if(seg.flags & RST) {
234951711cb6SDavid du Colombier if(tcb->state == Established) {
235059cc4ca5SDavid du Colombier tpriv->stats[EstabResets]++;
235151711cb6SDavid du Colombier if(tcb->rcv.nxt != seg.seq)
23526b7c5dceSDavid du Colombier netlog(f, Logtcp, "out of order RST "
23536b7c5dceSDavid du Colombier "rcvd: %I.%d -> %I.%d, rcv.nxt "
23546b7c5dceSDavid du Colombier "%lux seq %lux\n",
23551ce6c95fSDavid du Colombier s->raddr, s->rport, s->laddr,
23561ce6c95fSDavid du Colombier s->lport, tcb->rcv.nxt, seg.seq);
235751711cb6SDavid du Colombier }
23587dd7cddfSDavid du Colombier localclose(s, Econrefused);
23597dd7cddfSDavid du Colombier goto raise;
23607dd7cddfSDavid du Colombier }
23617dd7cddfSDavid du Colombier
23627dd7cddfSDavid du Colombier if((seg.flags&ACK) == 0)
23637dd7cddfSDavid du Colombier goto raise;
23647dd7cddfSDavid du Colombier
23657dd7cddfSDavid du Colombier switch(tcb->state) {
23667dd7cddfSDavid du Colombier case Syn_received:
23677dd7cddfSDavid du Colombier if(!seq_within(seg.ack, tcb->snd.una+1, tcb->snd.nxt)){
23683ff48bf5SDavid du Colombier sndrst(tcp, source, dest, length, &seg, version,
23693ff48bf5SDavid du Colombier "bad seq in Syn_received");
23707dd7cddfSDavid du Colombier goto raise;
23717dd7cddfSDavid du Colombier }
23727dd7cddfSDavid du Colombier update(s, &seg);
23737dd7cddfSDavid du Colombier tcpsetstate(s, Established);
23747dd7cddfSDavid du Colombier case Established:
23757dd7cddfSDavid du Colombier case Close_wait:
23767dd7cddfSDavid du Colombier update(s, &seg);
23777dd7cddfSDavid du Colombier break;
23787dd7cddfSDavid du Colombier case Finwait1:
23797dd7cddfSDavid du Colombier update(s, &seg);
238080ee5cbfSDavid du Colombier if(qlen(s->wq)+tcb->flgcnt == 0){
23817dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
23827dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
23837dd7cddfSDavid du Colombier tcpsetkacounter(tcb);
23843ff48bf5SDavid du Colombier tcb->time = NOW;
23857dd7cddfSDavid du Colombier tcpsetstate(s, Finwait2);
23867dd7cddfSDavid du Colombier tcb->katimer.start = MSL2 * (1000 / MSPTICK);
23877dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->katimer);
23887dd7cddfSDavid du Colombier }
23897dd7cddfSDavid du Colombier break;
23907dd7cddfSDavid du Colombier case Finwait2:
23917dd7cddfSDavid du Colombier update(s, &seg);
23927dd7cddfSDavid du Colombier break;
23937dd7cddfSDavid du Colombier case Closing:
23947dd7cddfSDavid du Colombier update(s, &seg);
239580ee5cbfSDavid du Colombier if(qlen(s->wq)+tcb->flgcnt == 0) {
23967dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
23977dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
23987dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->katimer);
23997dd7cddfSDavid du Colombier tcpsetstate(s, Time_wait);
24007dd7cddfSDavid du Colombier tcb->timer.start = MSL2*(1000 / MSPTICK);
24017dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->timer);
24027dd7cddfSDavid du Colombier }
24037dd7cddfSDavid du Colombier break;
24047dd7cddfSDavid du Colombier case Last_ack:
24057dd7cddfSDavid du Colombier update(s, &seg);
240680ee5cbfSDavid du Colombier if(qlen(s->wq)+tcb->flgcnt == 0) {
24077dd7cddfSDavid du Colombier localclose(s, nil);
24087dd7cddfSDavid du Colombier goto raise;
24097dd7cddfSDavid du Colombier }
24107dd7cddfSDavid du Colombier case Time_wait:
24117dd7cddfSDavid du Colombier tcb->flags |= FORCE;
24129a747e4fSDavid du Colombier if(tcb->timer.state != TcptimerON)
24137dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->timer);
24147dd7cddfSDavid du Colombier }
24157dd7cddfSDavid du Colombier
24167dd7cddfSDavid du Colombier if((seg.flags&URG) && seg.urg) {
24177dd7cddfSDavid du Colombier if(seq_gt(seg.urg + seg.seq, tcb->rcv.urg)) {
24187dd7cddfSDavid du Colombier tcb->rcv.urg = seg.urg + seg.seq;
24197dd7cddfSDavid du Colombier pullblock(&bp, seg.urg);
24207dd7cddfSDavid du Colombier }
24217dd7cddfSDavid du Colombier }
24227dd7cddfSDavid du Colombier else
24237dd7cddfSDavid du Colombier if(seq_gt(tcb->rcv.nxt, tcb->rcv.urg))
24247dd7cddfSDavid du Colombier tcb->rcv.urg = tcb->rcv.nxt;
24257dd7cddfSDavid du Colombier
24267dd7cddfSDavid du Colombier if(length == 0) {
24277dd7cddfSDavid du Colombier if(bp != nil)
24287dd7cddfSDavid du Colombier freeblist(bp);
24297dd7cddfSDavid du Colombier }
24307dd7cddfSDavid du Colombier else {
24317dd7cddfSDavid du Colombier switch(tcb->state){
24327dd7cddfSDavid du Colombier default:
24337dd7cddfSDavid du Colombier /* Ignore segment text */
24347dd7cddfSDavid du Colombier if(bp != nil)
24357dd7cddfSDavid du Colombier freeblist(bp);
24367dd7cddfSDavid du Colombier break;
24377dd7cddfSDavid du Colombier
24387dd7cddfSDavid du Colombier case Syn_received:
24397dd7cddfSDavid du Colombier case Established:
24407dd7cddfSDavid du Colombier case Finwait1:
24417dd7cddfSDavid du Colombier /* If we still have some data place on
24427dd7cddfSDavid du Colombier * receive queue
24437dd7cddfSDavid du Colombier */
24447dd7cddfSDavid du Colombier if(bp) {
24457dd7cddfSDavid du Colombier bp = packblock(bp);
24467dd7cddfSDavid du Colombier if(bp == nil)
24477dd7cddfSDavid du Colombier panic("tcp packblock");
24487dd7cddfSDavid du Colombier qpassnolim(s->rq, bp);
24497dd7cddfSDavid du Colombier bp = nil;
24507dd7cddfSDavid du Colombier }
24517dd7cddfSDavid du Colombier tcb->rcv.nxt += length;
245280ee5cbfSDavid du Colombier
245380ee5cbfSDavid du Colombier /*
245480ee5cbfSDavid du Colombier * turn on the acktimer if there's something
245580ee5cbfSDavid du Colombier * to ack
245680ee5cbfSDavid du Colombier */
24579a747e4fSDavid du Colombier if(tcb->acktimer.state != TcptimerON)
24587dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->acktimer);
24597dd7cddfSDavid du Colombier
24607dd7cddfSDavid du Colombier break;
24617dd7cddfSDavid du Colombier case Finwait2:
24627dd7cddfSDavid du Colombier /* no process to read the data, send a reset */
24637dd7cddfSDavid du Colombier if(bp != nil)
24647dd7cddfSDavid du Colombier freeblist(bp);
24653ff48bf5SDavid du Colombier sndrst(tcp, source, dest, length, &seg, version,
24663ff48bf5SDavid du Colombier "send to Finwait2");
24677dd7cddfSDavid du Colombier qunlock(s);
24687dd7cddfSDavid du Colombier poperror();
24697dd7cddfSDavid du Colombier return;
24707dd7cddfSDavid du Colombier }
24717dd7cddfSDavid du Colombier }
24727dd7cddfSDavid du Colombier
24737dd7cddfSDavid du Colombier if(seg.flags & FIN) {
24747dd7cddfSDavid du Colombier tcb->flags |= FORCE;
24757dd7cddfSDavid du Colombier
24767dd7cddfSDavid du Colombier switch(tcb->state) {
24777dd7cddfSDavid du Colombier case Syn_received:
24787dd7cddfSDavid du Colombier case Established:
24797dd7cddfSDavid du Colombier tcb->rcv.nxt++;
24807dd7cddfSDavid du Colombier tcpsetstate(s, Close_wait);
24817dd7cddfSDavid du Colombier break;
24827dd7cddfSDavid du Colombier case Finwait1:
24837dd7cddfSDavid du Colombier tcb->rcv.nxt++;
248480ee5cbfSDavid du Colombier if(qlen(s->wq)+tcb->flgcnt == 0) {
24857dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
24867dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
24877dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->katimer);
24887dd7cddfSDavid du Colombier tcpsetstate(s, Time_wait);
24897dd7cddfSDavid du Colombier tcb->timer.start = MSL2*(1000/MSPTICK);
24907dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->timer);
24917dd7cddfSDavid du Colombier }
24927dd7cddfSDavid du Colombier else
24937dd7cddfSDavid du Colombier tcpsetstate(s, Closing);
24947dd7cddfSDavid du Colombier break;
24957dd7cddfSDavid du Colombier case Finwait2:
24967dd7cddfSDavid du Colombier tcb->rcv.nxt++;
24977dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
24987dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
24997dd7cddfSDavid du Colombier tcphalt(tpriv, &tcb->katimer);
25007dd7cddfSDavid du Colombier tcpsetstate(s, Time_wait);
25017dd7cddfSDavid du Colombier tcb->timer.start = MSL2 * (1000/MSPTICK);
25027dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->timer);
25037dd7cddfSDavid du Colombier break;
25047dd7cddfSDavid du Colombier case Close_wait:
25057dd7cddfSDavid du Colombier case Closing:
25067dd7cddfSDavid du Colombier case Last_ack:
25077dd7cddfSDavid du Colombier break;
25087dd7cddfSDavid du Colombier case Time_wait:
25097dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->timer);
25107dd7cddfSDavid du Colombier break;
25117dd7cddfSDavid du Colombier }
25127dd7cddfSDavid du Colombier }
25137dd7cddfSDavid du Colombier
25147dd7cddfSDavid du Colombier /*
25157dd7cddfSDavid du Colombier * get next adjacent segment from the resequence queue.
25167dd7cddfSDavid du Colombier * dump/trim any overlapping segments
25177dd7cddfSDavid du Colombier */
25187dd7cddfSDavid du Colombier for(;;) {
25197dd7cddfSDavid du Colombier if(tcb->reseq == nil)
25207dd7cddfSDavid du Colombier goto output;
25217dd7cddfSDavid du Colombier
25227dd7cddfSDavid du Colombier if(seq_ge(tcb->rcv.nxt, tcb->reseq->seg.seq) == 0)
25237dd7cddfSDavid du Colombier goto output;
25247dd7cddfSDavid du Colombier
25257dd7cddfSDavid du Colombier getreseq(tcb, &seg, &bp, &length);
25267dd7cddfSDavid du Colombier
25271ce6c95fSDavid du Colombier tcprcvwin(s);
25281ce6c95fSDavid du Colombier if(tcptrim(tcb, &seg, &bp, &length) == 0){
25291ce6c95fSDavid du Colombier tcb->flags |= FORCE;
25307dd7cddfSDavid du Colombier break;
25317dd7cddfSDavid du Colombier }
25327dd7cddfSDavid du Colombier }
25331ce6c95fSDavid du Colombier }
25347dd7cddfSDavid du Colombier output:
25357dd7cddfSDavid du Colombier tcpoutput(s);
25367dd7cddfSDavid du Colombier qunlock(s);
25377dd7cddfSDavid du Colombier poperror();
25387dd7cddfSDavid du Colombier return;
25397dd7cddfSDavid du Colombier raise:
25407dd7cddfSDavid du Colombier qunlock(s);
25417dd7cddfSDavid du Colombier poperror();
25427dd7cddfSDavid du Colombier freeblist(bp);
254380ee5cbfSDavid du Colombier tcpkick(s);
25447dd7cddfSDavid du Colombier }
25457dd7cddfSDavid du Colombier
25467dd7cddfSDavid du Colombier /*
254780ee5cbfSDavid du Colombier * always enters and exits with the s locked. We drop
254880ee5cbfSDavid du Colombier * the lock to ipoput the packet so some care has to be
254980ee5cbfSDavid du Colombier * taken by callers.
25507dd7cddfSDavid du Colombier */
25511ce6c95fSDavid du Colombier static void
tcpoutput(Conv * s)25527dd7cddfSDavid du Colombier tcpoutput(Conv *s)
25537dd7cddfSDavid du Colombier {
25547dd7cddfSDavid du Colombier Tcp seg;
25551ce6c95fSDavid du Colombier uint msgs;
25567dd7cddfSDavid du Colombier Tcpctl *tcb;
25577dd7cddfSDavid du Colombier Block *hbp, *bp;
25581ce6c95fSDavid du Colombier int sndcnt;
25591ce6c95fSDavid du Colombier ulong ssize, dsize, sent;
25607dd7cddfSDavid du Colombier Fs *f;
25617dd7cddfSDavid du Colombier Tcppriv *tpriv;
25623ff48bf5SDavid du Colombier uchar version;
25637dd7cddfSDavid du Colombier
25647dd7cddfSDavid du Colombier f = s->p->f;
25657dd7cddfSDavid du Colombier tpriv = s->p->priv;
25663ff48bf5SDavid du Colombier version = s->ipversion;
25677dd7cddfSDavid du Colombier
25687dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
25697dd7cddfSDavid du Colombier
25701ce6c95fSDavid du Colombier /* force ack every 2*mss */
25711ce6c95fSDavid du Colombier if((tcb->flags & FORCE) == 0 &&
25721ce6c95fSDavid du Colombier tcb->rcv.nxt - tcb->rcv.ackptr >= 2*tcb->mss){
25731ce6c95fSDavid du Colombier tpriv->stats[Delayack]++;
25741ce6c95fSDavid du Colombier tcb->flags |= FORCE;
25751ce6c95fSDavid du Colombier }
25761ce6c95fSDavid du Colombier
25771ce6c95fSDavid du Colombier /* force ack if window opening */
25781ce6c95fSDavid du Colombier if((tcb->flags & FORCE) == 0){
25791ce6c95fSDavid du Colombier tcprcvwin(s);
25801ce6c95fSDavid du Colombier if((int)(tcb->rcv.wptr - tcb->rcv.wsnt) >= 2*tcb->mss){
25811ce6c95fSDavid du Colombier tpriv->stats[Wopenack]++;
25821ce6c95fSDavid du Colombier tcb->flags |= FORCE;
25831ce6c95fSDavid du Colombier }
25841ce6c95fSDavid du Colombier }
25851ce6c95fSDavid du Colombier
25861ce6c95fSDavid du Colombier for(msgs = 0; msgs < 100; msgs++) {
25877dd7cddfSDavid du Colombier switch(tcb->state) {
25887dd7cddfSDavid du Colombier case Listen:
25897dd7cddfSDavid du Colombier case Closed:
25907dd7cddfSDavid du Colombier case Finwait2:
2591e6c6b7f8SDavid du Colombier return;
25927dd7cddfSDavid du Colombier }
25937dd7cddfSDavid du Colombier
25941ce6c95fSDavid du Colombier /* Don't send anything else until our SYN has been acked */
25951ce6c95fSDavid du Colombier if(tcb->snd.ptr != tcb->iss && (tcb->flags & SYNACK) == 0)
25961ce6c95fSDavid du Colombier break;
25971ce6c95fSDavid du Colombier
25987dd7cddfSDavid du Colombier /* force an ack when a window has opened up */
25991ce6c95fSDavid du Colombier tcprcvwin(s);
26007dd7cddfSDavid du Colombier if(tcb->rcv.blocked && tcb->rcv.wnd > 0){
26017dd7cddfSDavid du Colombier tcb->rcv.blocked = 0;
26027dd7cddfSDavid du Colombier tcb->flags |= FORCE;
26037dd7cddfSDavid du Colombier }
26047dd7cddfSDavid du Colombier
260580ee5cbfSDavid du Colombier sndcnt = qlen(s->wq)+tcb->flgcnt;
26067dd7cddfSDavid du Colombier sent = tcb->snd.ptr - tcb->snd.una;
26071ce6c95fSDavid du Colombier ssize = sndcnt;
26087dd7cddfSDavid du Colombier if(tcb->snd.wnd == 0){
26091ce6c95fSDavid du Colombier /* zero window probe */
26101ce6c95fSDavid du Colombier if(sent > 0 && !(tcb->flags & FORCE))
26111ce6c95fSDavid du Colombier break; /* already probing, rto re-probes */
26121ce6c95fSDavid du Colombier if(ssize < sent)
26131ce6c95fSDavid du Colombier ssize = 0;
26147dd7cddfSDavid du Colombier else{
26151ce6c95fSDavid du Colombier ssize -= sent;
26161ce6c95fSDavid du Colombier if(ssize > 0)
26171ce6c95fSDavid du Colombier ssize = 1;
26187dd7cddfSDavid du Colombier }
26191ce6c95fSDavid du Colombier } else {
26201ce6c95fSDavid du Colombier /* calculate usable segment size */
26211ce6c95fSDavid du Colombier if(ssize > tcb->cwind)
26221ce6c95fSDavid du Colombier ssize = tcb->cwind;
26231ce6c95fSDavid du Colombier if(ssize > tcb->snd.wnd)
26241ce6c95fSDavid du Colombier ssize = tcb->snd.wnd;
26251ce6c95fSDavid du Colombier
26261ce6c95fSDavid du Colombier if(ssize < sent)
26271ce6c95fSDavid du Colombier ssize = 0;
26281ce6c95fSDavid du Colombier else {
26291ce6c95fSDavid du Colombier ssize -= sent;
26301ce6c95fSDavid du Colombier if(ssize > tcb->mss)
26317dd7cddfSDavid du Colombier ssize = tcb->mss;
26321ce6c95fSDavid du Colombier }
26331ce6c95fSDavid du Colombier }
26341ce6c95fSDavid du Colombier
26357dd7cddfSDavid du Colombier dsize = ssize;
26367dd7cddfSDavid du Colombier seg.urg = 0;
26377dd7cddfSDavid du Colombier
26381ce6c95fSDavid du Colombier if(!(tcb->flags & FORCE))
26391ce6c95fSDavid du Colombier if(ssize == 0 ||
26401ce6c95fSDavid du Colombier ssize < tcb->mss && tcb->snd.nxt == tcb->snd.ptr &&
26411ce6c95fSDavid du Colombier sent > TCPREXMTTHRESH * tcb->mss)
26427dd7cddfSDavid du Colombier break;
26437dd7cddfSDavid du Colombier
26447dd7cddfSDavid du Colombier tcb->flags &= ~FORCE;
26457dd7cddfSDavid du Colombier
26467dd7cddfSDavid du Colombier /* By default we will generate an ack */
26473f695129SDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
26487dd7cddfSDavid du Colombier seg.source = s->lport;
26497dd7cddfSDavid du Colombier seg.dest = s->rport;
26507dd7cddfSDavid du Colombier seg.flags = ACK;
26517dd7cddfSDavid du Colombier seg.mss = 0;
26523f695129SDavid du Colombier seg.ws = 0;
26531ce6c95fSDavid du Colombier seg.update = 0;
26547dd7cddfSDavid du Colombier switch(tcb->state){
26557dd7cddfSDavid du Colombier case Syn_sent:
26567dd7cddfSDavid du Colombier seg.flags = 0;
26577dd7cddfSDavid du Colombier if(tcb->snd.ptr == tcb->iss){
26587dd7cddfSDavid du Colombier seg.flags |= SYN;
26597dd7cddfSDavid du Colombier dsize--;
26603f695129SDavid du Colombier seg.mss = tcb->mss;
26613f695129SDavid du Colombier seg.ws = tcb->scale;
26627dd7cddfSDavid du Colombier }
26637dd7cddfSDavid du Colombier break;
26647dd7cddfSDavid du Colombier case Syn_received:
26657dd7cddfSDavid du Colombier /*
26667dd7cddfSDavid du Colombier * don't send any data with a SYN/ACK packet
26677dd7cddfSDavid du Colombier * because Linux rejects the packet in its
26687dd7cddfSDavid du Colombier * attempt to solve the SYN attack problem
26697dd7cddfSDavid du Colombier */
26707dd7cddfSDavid du Colombier if(tcb->snd.ptr == tcb->iss){
26717dd7cddfSDavid du Colombier seg.flags |= SYN;
26727dd7cddfSDavid du Colombier dsize = 0;
26737dd7cddfSDavid du Colombier ssize = 1;
26743f695129SDavid du Colombier seg.mss = tcb->mss;
26753f695129SDavid du Colombier seg.ws = tcb->scale;
26767dd7cddfSDavid du Colombier }
26777dd7cddfSDavid du Colombier break;
26787dd7cddfSDavid du Colombier }
26797dd7cddfSDavid du Colombier seg.seq = tcb->snd.ptr;
26807dd7cddfSDavid du Colombier seg.ack = tcb->rcv.nxt;
26817dd7cddfSDavid du Colombier seg.wnd = tcb->rcv.wnd;
26827dd7cddfSDavid du Colombier
26837dd7cddfSDavid du Colombier /* Pull out data to send */
26847dd7cddfSDavid du Colombier bp = nil;
26857dd7cddfSDavid du Colombier if(dsize != 0) {
26867dd7cddfSDavid du Colombier bp = qcopy(s->wq, dsize, sent);
26877dd7cddfSDavid du Colombier if(BLEN(bp) != dsize) {
26887dd7cddfSDavid du Colombier seg.flags |= FIN;
26897dd7cddfSDavid du Colombier dsize--;
26907dd7cddfSDavid du Colombier }
26917dd7cddfSDavid du Colombier }
26927dd7cddfSDavid du Colombier
26931ce6c95fSDavid du Colombier if(sent+dsize == sndcnt && dsize)
26947dd7cddfSDavid du Colombier seg.flags |= PSH;
26957dd7cddfSDavid du Colombier
26967dd7cddfSDavid du Colombier tcb->snd.ptr += ssize;
26977dd7cddfSDavid du Colombier
26987dd7cddfSDavid du Colombier /* Pull up the send pointer so we can accept acks
26997dd7cddfSDavid du Colombier * for this window
27007dd7cddfSDavid du Colombier */
27017dd7cddfSDavid du Colombier if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
27027dd7cddfSDavid du Colombier tcb->snd.nxt = tcb->snd.ptr;
27037dd7cddfSDavid du Colombier
27047dd7cddfSDavid du Colombier /* Build header, link data and compute cksum */
27053ff48bf5SDavid du Colombier switch(version){
27063ff48bf5SDavid du Colombier case V4:
27073ff48bf5SDavid du Colombier tcb->protohdr.tcp4hdr.vihl = IP_VER4;
27083ff48bf5SDavid du Colombier hbp = htontcp4(&seg, bp, &tcb->protohdr.tcp4hdr, tcb);
27097dd7cddfSDavid du Colombier if(hbp == nil) {
27107dd7cddfSDavid du Colombier freeblist(bp);
2711e6c6b7f8SDavid du Colombier return;
27127dd7cddfSDavid du Colombier }
27133ff48bf5SDavid du Colombier break;
27143ff48bf5SDavid du Colombier case V6:
27153ff48bf5SDavid du Colombier tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
27163ff48bf5SDavid du Colombier hbp = htontcp6(&seg, bp, &tcb->protohdr.tcp6hdr, tcb);
27173ff48bf5SDavid du Colombier if(hbp == nil) {
27183ff48bf5SDavid du Colombier freeblist(bp);
2719e6c6b7f8SDavid du Colombier return;
27203ff48bf5SDavid du Colombier }
27213ff48bf5SDavid du Colombier break;
27223ff48bf5SDavid du Colombier default:
27233ff48bf5SDavid du Colombier hbp = nil; /* to suppress a warning */
27243ff48bf5SDavid du Colombier panic("tcpoutput: version %d", version);
27253ff48bf5SDavid du Colombier }
27267dd7cddfSDavid du Colombier
27277dd7cddfSDavid du Colombier /* Start the transmission timers if there is new data and we
27287dd7cddfSDavid du Colombier * expect acknowledges
27297dd7cddfSDavid du Colombier */
27307dd7cddfSDavid du Colombier if(ssize != 0){
27311ce6c95fSDavid du Colombier if(tcb->timer.state != TcptimerON){
27321ce6c95fSDavid du Colombier tcb->time = NOW;
27331ce6c95fSDavid du Colombier tcb->timeuna = tcb->snd.una;
27347dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->timer);
27351ce6c95fSDavid du Colombier }
27367dd7cddfSDavid du Colombier
27377dd7cddfSDavid du Colombier /* If round trip timer isn't running, start it.
27387dd7cddfSDavid du Colombier * measure the longest packet only in case the
27397dd7cddfSDavid du Colombier * transmission time dominates RTT
27407dd7cddfSDavid du Colombier */
27411ce6c95fSDavid du Colombier if(tcb->snd.retransmit == 0)
27429a747e4fSDavid du Colombier if(tcb->rtt_timer.state != TcptimerON)
27437dd7cddfSDavid du Colombier if(ssize == tcb->mss) {
27447dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->rtt_timer);
27457dd7cddfSDavid du Colombier tcb->rttseq = tcb->snd.ptr;
27467dd7cddfSDavid du Colombier }
27477dd7cddfSDavid du Colombier }
27487dd7cddfSDavid du Colombier
274959cc4ca5SDavid du Colombier tpriv->stats[OutSegs]++;
27501ce6c95fSDavid du Colombier if(tcb->snd.retransmit)
27511ce6c95fSDavid du Colombier tpriv->stats[RetransSegsSent]++;
27521ce6c95fSDavid du Colombier tcb->rcv.ackptr = seg.ack;
27531ce6c95fSDavid du Colombier tcb->rcv.wsnt = tcb->rcv.wptr;
275440ef9009SDavid du Colombier
275540ef9009SDavid du Colombier /* put off the next keep alive */
27567dd7cddfSDavid du Colombier tcpgo(tpriv, &tcb->katimer);
27573ff48bf5SDavid du Colombier
27583ff48bf5SDavid du Colombier switch(version){
27593ff48bf5SDavid du Colombier case V4:
2760a6a9e072SDavid du Colombier if(ipoput4(f, hbp, 0, s->ttl, s->tos, s) < 0){
2761e6c6b7f8SDavid du Colombier /* a negative return means no route */
2762e6c6b7f8SDavid du Colombier localclose(s, "no route");
2763e6c6b7f8SDavid du Colombier }
27643ff48bf5SDavid du Colombier break;
27653ff48bf5SDavid du Colombier case V6:
2766a6a9e072SDavid du Colombier if(ipoput6(f, hbp, 0, s->ttl, s->tos, s) < 0){
2767e6c6b7f8SDavid du Colombier /* a negative return means no route */
2768e6c6b7f8SDavid du Colombier localclose(s, "no route");
2769e6c6b7f8SDavid du Colombier }
27703ff48bf5SDavid du Colombier break;
27713ff48bf5SDavid du Colombier default:
27723ff48bf5SDavid du Colombier panic("tcpoutput2: version %d", version);
277380ee5cbfSDavid du Colombier }
27741ce6c95fSDavid du Colombier if((msgs%4) == 3){
27753ff48bf5SDavid du Colombier qunlock(s);
277680ee5cbfSDavid du Colombier qlock(s);
27773ff48bf5SDavid du Colombier }
27787dd7cddfSDavid du Colombier }
27797dd7cddfSDavid du Colombier }
27807dd7cddfSDavid du Colombier
27817dd7cddfSDavid du Colombier /*
27827dd7cddfSDavid du Colombier * the BSD convention (hack?) for keep alives. resend last uchar acked.
27837dd7cddfSDavid du Colombier */
27841ce6c95fSDavid du Colombier static void
tcpsendka(Conv * s)27857dd7cddfSDavid du Colombier tcpsendka(Conv *s)
27867dd7cddfSDavid du Colombier {
27877dd7cddfSDavid du Colombier Tcp seg;
27887dd7cddfSDavid du Colombier Tcpctl *tcb;
27897dd7cddfSDavid du Colombier Block *hbp,*dbp;
27907dd7cddfSDavid du Colombier
27917dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
27927dd7cddfSDavid du Colombier
27937dd7cddfSDavid du Colombier dbp = nil;
279410ae047aSDavid du Colombier memset(&seg, 0, sizeof seg);
27957dd7cddfSDavid du Colombier seg.urg = 0;
27967dd7cddfSDavid du Colombier seg.source = s->lport;
27977dd7cddfSDavid du Colombier seg.dest = s->rport;
27987dd7cddfSDavid du Colombier seg.flags = ACK|PSH;
27997dd7cddfSDavid du Colombier seg.mss = 0;
28003f695129SDavid du Colombier seg.ws = 0;
28013ff48bf5SDavid du Colombier if(tcpporthogdefense)
28023ff48bf5SDavid du Colombier seg.seq = tcb->snd.una-(1<<30)-nrand(1<<20);
28033ff48bf5SDavid du Colombier else
28047dd7cddfSDavid du Colombier seg.seq = tcb->snd.una-1;
28057dd7cddfSDavid du Colombier seg.ack = tcb->rcv.nxt;
28061ce6c95fSDavid du Colombier tcb->rcv.ackptr = seg.ack;
28071ce6c95fSDavid du Colombier tcprcvwin(s);
28087dd7cddfSDavid du Colombier seg.wnd = tcb->rcv.wnd;
28097dd7cddfSDavid du Colombier if(tcb->state == Finwait2){
28107dd7cddfSDavid du Colombier seg.flags |= FIN;
28117dd7cddfSDavid du Colombier } else {
28127dd7cddfSDavid du Colombier dbp = allocb(1);
28137dd7cddfSDavid du Colombier dbp->wp++;
28147dd7cddfSDavid du Colombier }
28157dd7cddfSDavid du Colombier
28163ff48bf5SDavid du Colombier if(isv4(s->raddr)) {
28177dd7cddfSDavid du Colombier /* Build header, link data and compute cksum */
28183ff48bf5SDavid du Colombier tcb->protohdr.tcp4hdr.vihl = IP_VER4;
28193ff48bf5SDavid du Colombier hbp = htontcp4(&seg, dbp, &tcb->protohdr.tcp4hdr, tcb);
28207dd7cddfSDavid du Colombier if(hbp == nil) {
28217dd7cddfSDavid du Colombier freeblist(dbp);
28227dd7cddfSDavid du Colombier return;
28237dd7cddfSDavid du Colombier }
2824a6a9e072SDavid du Colombier ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s);
28253ff48bf5SDavid du Colombier }
28263ff48bf5SDavid du Colombier else {
28273ff48bf5SDavid du Colombier /* Build header, link data and compute cksum */
28283ff48bf5SDavid du Colombier tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
28293ff48bf5SDavid du Colombier hbp = htontcp6(&seg, dbp, &tcb->protohdr.tcp6hdr, tcb);
28303ff48bf5SDavid du Colombier if(hbp == nil) {
28313ff48bf5SDavid du Colombier freeblist(dbp);
28323ff48bf5SDavid du Colombier return;
28333ff48bf5SDavid du Colombier }
2834a6a9e072SDavid du Colombier ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s);
28353ff48bf5SDavid du Colombier }
28367dd7cddfSDavid du Colombier }
28377dd7cddfSDavid du Colombier
28387dd7cddfSDavid du Colombier /*
283940ef9009SDavid du Colombier * set connection to time out after 12 minutes
28407dd7cddfSDavid du Colombier */
28411ce6c95fSDavid du Colombier static void
tcpsetkacounter(Tcpctl * tcb)28427dd7cddfSDavid du Colombier tcpsetkacounter(Tcpctl *tcb)
28437dd7cddfSDavid du Colombier {
2844e288d156SDavid du Colombier tcb->kacounter = (12 * 60 * 1000) / (tcb->katimer.start*MSPTICK);
28457dd7cddfSDavid du Colombier if(tcb->kacounter < 3)
28467dd7cddfSDavid du Colombier tcb->kacounter = 3;
28477dd7cddfSDavid du Colombier }
284840ef9009SDavid du Colombier
284940ef9009SDavid du Colombier /*
285040ef9009SDavid du Colombier * if we've timed out, close the connection
285140ef9009SDavid du Colombier * otherwise, send a keepalive and restart the timer
285240ef9009SDavid du Colombier */
28531ce6c95fSDavid du Colombier static void
tcpkeepalive(void * v)28547dd7cddfSDavid du Colombier tcpkeepalive(void *v)
28557dd7cddfSDavid du Colombier {
28567dd7cddfSDavid du Colombier Tcpctl *tcb;
28577dd7cddfSDavid du Colombier Conv *s;
28587dd7cddfSDavid du Colombier
28597dd7cddfSDavid du Colombier s = v;
28607dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
28617dd7cddfSDavid du Colombier if(waserror()){
28627dd7cddfSDavid du Colombier qunlock(s);
28637dd7cddfSDavid du Colombier nexterror();
28647dd7cddfSDavid du Colombier }
28657dd7cddfSDavid du Colombier qlock(s);
28667dd7cddfSDavid du Colombier if(tcb->state != Closed){
28677dd7cddfSDavid du Colombier if(--(tcb->kacounter) <= 0) {
28687dd7cddfSDavid du Colombier localclose(s, Etimedout);
28697dd7cddfSDavid du Colombier } else {
28707dd7cddfSDavid du Colombier tcpsendka(s);
28717dd7cddfSDavid du Colombier tcpgo(s->p->priv, &tcb->katimer);
28727dd7cddfSDavid du Colombier }
28737dd7cddfSDavid du Colombier }
28747dd7cddfSDavid du Colombier qunlock(s);
28757dd7cddfSDavid du Colombier poperror();
28767dd7cddfSDavid du Colombier }
28777dd7cddfSDavid du Colombier
28787dd7cddfSDavid du Colombier /*
28797dd7cddfSDavid du Colombier * start keepalive timer
28807dd7cddfSDavid du Colombier */
28811ce6c95fSDavid du Colombier static char*
tcpstartka(Conv * s,char ** f,int n)28827dd7cddfSDavid du Colombier tcpstartka(Conv *s, char **f, int n)
28837dd7cddfSDavid du Colombier {
28847dd7cddfSDavid du Colombier Tcpctl *tcb;
28857dd7cddfSDavid du Colombier int x;
28867dd7cddfSDavid du Colombier
28877dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
288840ef9009SDavid du Colombier if(tcb->state != Established)
288940ef9009SDavid du Colombier return "connection must be in Establised state";
28907dd7cddfSDavid du Colombier if(n > 1){
28917dd7cddfSDavid du Colombier x = atoi(f[1]);
28927dd7cddfSDavid du Colombier if(x >= MSPTICK)
28937dd7cddfSDavid du Colombier tcb->katimer.start = x/MSPTICK;
28947dd7cddfSDavid du Colombier }
28957dd7cddfSDavid du Colombier tcpsetkacounter(tcb);
28967dd7cddfSDavid du Colombier tcpgo(s->p->priv, &tcb->katimer);
28977dd7cddfSDavid du Colombier
28987dd7cddfSDavid du Colombier return nil;
28997dd7cddfSDavid du Colombier }
29007dd7cddfSDavid du Colombier
290159cc4ca5SDavid du Colombier /*
290259cc4ca5SDavid du Colombier * turn checksums on/off
290359cc4ca5SDavid du Colombier */
29041ce6c95fSDavid du Colombier static char*
tcpsetchecksum(Conv * s,char ** f,int)290559cc4ca5SDavid du Colombier tcpsetchecksum(Conv *s, char **f, int)
290659cc4ca5SDavid du Colombier {
290759cc4ca5SDavid du Colombier Tcpctl *tcb;
290859cc4ca5SDavid du Colombier
290959cc4ca5SDavid du Colombier tcb = (Tcpctl*)s->ptcl;
291059cc4ca5SDavid du Colombier tcb->nochecksum = !atoi(f[1]);
291159cc4ca5SDavid du Colombier
291259cc4ca5SDavid du Colombier return nil;
291359cc4ca5SDavid du Colombier }
291459cc4ca5SDavid du Colombier
29151ce6c95fSDavid du Colombier /*
29161ce6c95fSDavid du Colombier * retransmit (at most) one segment at snd.una.
29171ce6c95fSDavid du Colombier * preserve cwind & snd.ptr
29181ce6c95fSDavid du Colombier */
29191ce6c95fSDavid du Colombier static void
tcprxmit(Conv * s)29207dd7cddfSDavid du Colombier tcprxmit(Conv *s)
29217dd7cddfSDavid du Colombier {
29227dd7cddfSDavid du Colombier Tcpctl *tcb;
29231ce6c95fSDavid du Colombier Tcppriv *tpriv;
29241ce6c95fSDavid du Colombier ulong tcwind, tptr;
29257dd7cddfSDavid du Colombier
29267dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
29277dd7cddfSDavid du Colombier tcb->flags |= RETRAN|FORCE;
29281ce6c95fSDavid du Colombier
29291ce6c95fSDavid du Colombier tptr = tcb->snd.ptr;
29301ce6c95fSDavid du Colombier tcwind = tcb->cwind;
29317dd7cddfSDavid du Colombier tcb->snd.ptr = tcb->snd.una;
29327dd7cddfSDavid du Colombier tcb->cwind = tcb->mss;
29331ce6c95fSDavid du Colombier tcb->snd.retransmit = 1;
29347dd7cddfSDavid du Colombier tcpoutput(s);
29351ce6c95fSDavid du Colombier tcb->snd.retransmit = 0;
29361ce6c95fSDavid du Colombier tcb->cwind = tcwind;
29371ce6c95fSDavid du Colombier tcb->snd.ptr = tptr;
29381ce6c95fSDavid du Colombier
29391ce6c95fSDavid du Colombier tpriv = s->p->priv;
29401ce6c95fSDavid du Colombier tpriv->stats[RetransSegs]++;
29417dd7cddfSDavid du Colombier }
29427dd7cddfSDavid du Colombier
29431ce6c95fSDavid du Colombier /*
29441ce6c95fSDavid du Colombier * TODO: RFC 4138 F-RTO
29451ce6c95fSDavid du Colombier */
29461ce6c95fSDavid du Colombier static void
tcptimeout(void * arg)29477dd7cddfSDavid du Colombier tcptimeout(void *arg)
29487dd7cddfSDavid du Colombier {
29497dd7cddfSDavid du Colombier Conv *s;
29507dd7cddfSDavid du Colombier Tcpctl *tcb;
29517dd7cddfSDavid du Colombier int maxback;
29527dd7cddfSDavid du Colombier Tcppriv *tpriv;
29537dd7cddfSDavid du Colombier
29547dd7cddfSDavid du Colombier s = (Conv*)arg;
29557dd7cddfSDavid du Colombier tpriv = s->p->priv;
29567dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
29577dd7cddfSDavid du Colombier
29587dd7cddfSDavid du Colombier if(waserror()){
29597dd7cddfSDavid du Colombier qunlock(s);
29607dd7cddfSDavid du Colombier nexterror();
29617dd7cddfSDavid du Colombier }
29627dd7cddfSDavid du Colombier qlock(s);
29637dd7cddfSDavid du Colombier switch(tcb->state){
29647dd7cddfSDavid du Colombier default:
29657dd7cddfSDavid du Colombier tcb->backoff++;
29667dd7cddfSDavid du Colombier if(tcb->state == Syn_sent)
29677dd7cddfSDavid du Colombier maxback = MAXBACKMS/2;
29687dd7cddfSDavid du Colombier else
29697dd7cddfSDavid du Colombier maxback = MAXBACKMS;
29707dd7cddfSDavid du Colombier tcb->backedoff += tcb->timer.start * MSPTICK;
29717dd7cddfSDavid du Colombier if(tcb->backedoff >= maxback) {
29727dd7cddfSDavid du Colombier localclose(s, Etimedout);
29737dd7cddfSDavid du Colombier break;
29747dd7cddfSDavid du Colombier }
29751ce6c95fSDavid du Colombier netlog(s->p->f, Logtcprxmt, "rxm %d/%d %ldms %lud rto %d %lud %s\n",
29761ce6c95fSDavid du Colombier tcb->srtt, tcb->mdev, NOW - tcb->time,
29771ce6c95fSDavid du Colombier tcb->snd.una - tcb->timeuna, tcb->snd.rto, tcb->snd.ptr,
29781ce6c95fSDavid du Colombier tcpstates[s->state]);
29799a747e4fSDavid du Colombier tcpsettimer(tcb);
29801ce6c95fSDavid du Colombier if(tcb->snd.rto == 0)
29811ce6c95fSDavid du Colombier tcpcongestion(tcb);
29827dd7cddfSDavid du Colombier tcprxmit(s);
29831ce6c95fSDavid du Colombier tcb->snd.ptr = tcb->snd.una;
29841ce6c95fSDavid du Colombier tcb->cwind = tcb->mss;
29851ce6c95fSDavid du Colombier tcb->snd.rto = 1;
298659cc4ca5SDavid du Colombier tpriv->stats[RetransTimeouts]++;
29871ce6c95fSDavid du Colombier
29881ce6c95fSDavid du Colombier if(tcb->snd.recovery){
29891ce6c95fSDavid du Colombier tcb->snd.dupacks = 0; /* reno rto */
29901ce6c95fSDavid du Colombier tcb->snd.recovery = 0;
29911ce6c95fSDavid du Colombier tpriv->stats[RecoveryRTO]++;
29921ce6c95fSDavid du Colombier tcb->snd.rxt = tcb->snd.nxt;
29931ce6c95fSDavid du Colombier netlog(s->p->f, Logtcpwin,
29941ce6c95fSDavid du Colombier "rto recovery rxt @%lud\n", tcb->snd.nxt);
29951ce6c95fSDavid du Colombier }
29961ce6c95fSDavid du Colombier
29971ce6c95fSDavid du Colombier tcb->abcbytes = 0;
29987dd7cddfSDavid du Colombier break;
29997dd7cddfSDavid du Colombier case Time_wait:
30007dd7cddfSDavid du Colombier localclose(s, nil);
30017dd7cddfSDavid du Colombier break;
30027dd7cddfSDavid du Colombier case Closed:
30037dd7cddfSDavid du Colombier break;
30047dd7cddfSDavid du Colombier }
30057dd7cddfSDavid du Colombier qunlock(s);
30067dd7cddfSDavid du Colombier poperror();
30077dd7cddfSDavid du Colombier }
30087dd7cddfSDavid du Colombier
30091ce6c95fSDavid du Colombier static int
inwindow(Tcpctl * tcb,int seq)30107dd7cddfSDavid du Colombier inwindow(Tcpctl *tcb, int seq)
30117dd7cddfSDavid du Colombier {
30127dd7cddfSDavid du Colombier return seq_within(seq, tcb->rcv.nxt, tcb->rcv.nxt+tcb->rcv.wnd-1);
30137dd7cddfSDavid du Colombier }
30147dd7cddfSDavid du Colombier
30153ff48bf5SDavid du Colombier /*
30163ff48bf5SDavid du Colombier * set up state for a received SYN (or SYN ACK) packet
30173ff48bf5SDavid du Colombier */
30181ce6c95fSDavid du Colombier static void
procsyn(Conv * s,Tcp * seg)30197dd7cddfSDavid du Colombier procsyn(Conv *s, Tcp *seg)
30207dd7cddfSDavid du Colombier {
30217dd7cddfSDavid du Colombier Tcpctl *tcb;
30227ec5746aSDavid du Colombier Tcppriv *tpriv;
30237dd7cddfSDavid du Colombier
30247dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
30257dd7cddfSDavid du Colombier tcb->flags |= FORCE;
30267dd7cddfSDavid du Colombier
30277dd7cddfSDavid du Colombier tcb->rcv.nxt = seg->seq + 1;
30281ce6c95fSDavid du Colombier tcb->rcv.wptr = tcb->rcv.nxt;
30291ce6c95fSDavid du Colombier tcb->rcv.wsnt = 0;
30307dd7cddfSDavid du Colombier tcb->rcv.urg = tcb->rcv.nxt;
30317dd7cddfSDavid du Colombier tcb->irs = seg->seq;
30327dd7cddfSDavid du Colombier
30333ff48bf5SDavid du Colombier /* our sending max segment size cannot be bigger than what he asked for */
30347ec5746aSDavid du Colombier if(seg->mss != 0 && seg->mss < tcb->mss) {
30357dd7cddfSDavid du Colombier tcb->mss = seg->mss;
30367ec5746aSDavid du Colombier tpriv = s->p->priv;
30377ec5746aSDavid du Colombier tpriv->stats[Mss] = tcb->mss;
30387ec5746aSDavid du Colombier }
30397dd7cddfSDavid du Colombier
30403ff48bf5SDavid du Colombier tcb->snd.wnd = seg->wnd;
30411ce6c95fSDavid du Colombier initialwindow(tcb);
30427dd7cddfSDavid du Colombier }
30437dd7cddfSDavid du Colombier
30441ce6c95fSDavid du Colombier static int
dumpreseq(Tcpctl * tcb)30451ce6c95fSDavid du Colombier dumpreseq(Tcpctl *tcb)
30467dd7cddfSDavid du Colombier {
30471ce6c95fSDavid du Colombier Reseq *r, *next;
30487dd7cddfSDavid du Colombier
30491ce6c95fSDavid du Colombier for(r = tcb->reseq; r != nil; r = next){
30501ce6c95fSDavid du Colombier next = r->next;
30511ce6c95fSDavid du Colombier freeblist(r->bp);
30521ce6c95fSDavid du Colombier free(r);
30531ce6c95fSDavid du Colombier }
30541ce6c95fSDavid du Colombier tcb->reseq = nil;
30551ce6c95fSDavid du Colombier tcb->nreseq = 0;
30561ce6c95fSDavid du Colombier tcb->reseqlen = 0;
30571ce6c95fSDavid du Colombier return -1;
30581ce6c95fSDavid du Colombier }
30591ce6c95fSDavid du Colombier
30601ce6c95fSDavid du Colombier static void
logreseq(Fs * f,Reseq * r,ulong n)30611ce6c95fSDavid du Colombier logreseq(Fs *f, Reseq *r, ulong n)
30621ce6c95fSDavid du Colombier {
30631ce6c95fSDavid du Colombier char *s;
30641ce6c95fSDavid du Colombier
30651ce6c95fSDavid du Colombier for(; r != nil; r = r->next){
30661ce6c95fSDavid du Colombier s = nil;
30671ce6c95fSDavid du Colombier if(r->next == nil && r->seg.seq != n)
30681ce6c95fSDavid du Colombier s = "hole/end";
30691ce6c95fSDavid du Colombier else if(r->next == nil)
30701ce6c95fSDavid du Colombier s = "end";
30711ce6c95fSDavid du Colombier else if(r->seg.seq != n)
30721ce6c95fSDavid du Colombier s = "hole";
30731ce6c95fSDavid du Colombier if(s != nil)
30741ce6c95fSDavid du Colombier netlog(f, Logtcp, "%s %lud-%lud (%ld) %#ux\n", s,
30751ce6c95fSDavid du Colombier n, r->seg.seq, r->seg.seq - n, r->seg.flags);
30761ce6c95fSDavid du Colombier n = r->seg.seq + r->seg.len;
30771ce6c95fSDavid du Colombier }
30781ce6c95fSDavid du Colombier }
30791ce6c95fSDavid du Colombier
30801ce6c95fSDavid du Colombier static int
addreseq(Fs * f,Tcpctl * tcb,Tcppriv * tpriv,Tcp * seg,Block * bp,ushort length)30811ce6c95fSDavid du Colombier addreseq(Fs *f, Tcpctl *tcb, Tcppriv *tpriv, Tcp *seg, Block *bp, ushort length)
30821ce6c95fSDavid du Colombier {
30831ce6c95fSDavid du Colombier Reseq *rp, **rr;
30841ce6c95fSDavid du Colombier int qmax;
30851ce6c95fSDavid du Colombier
30861ce6c95fSDavid du Colombier rp = malloc(sizeof *rp);
30877dd7cddfSDavid du Colombier if(rp == nil){
30881ce6c95fSDavid du Colombier freeblist(bp); /* bp always consumed by addreseq */
30897dd7cddfSDavid du Colombier return 0;
30907dd7cddfSDavid du Colombier }
30917dd7cddfSDavid du Colombier
30927dd7cddfSDavid du Colombier rp->seg = *seg;
30937dd7cddfSDavid du Colombier rp->bp = bp;
30947dd7cddfSDavid du Colombier rp->length = length;
30957dd7cddfSDavid du Colombier
30961ce6c95fSDavid du Colombier tcb->reseqlen += length;
30971ce6c95fSDavid du Colombier tcb->nreseq++;
30981ce6c95fSDavid du Colombier
30997dd7cddfSDavid du Colombier /* Place on reassembly list sorting by starting seq number */
31001ce6c95fSDavid du Colombier for(rr = &tcb->reseq; ; rr = &(*rr)->next)
31011ce6c95fSDavid du Colombier if(*rr == nil || seq_lt(seg->seq, (*rr)->seg.seq)){
31021ce6c95fSDavid du Colombier rp->next = *rr;
31031ce6c95fSDavid du Colombier *rr = rp;
31041ce6c95fSDavid du Colombier tpriv->stats[Resequenced]++;
31053ff48bf5SDavid du Colombier if(rp->next != nil)
31063ff48bf5SDavid du Colombier tpriv->stats[OutOfOrder]++;
31077dd7cddfSDavid du Colombier break;
31087dd7cddfSDavid du Colombier }
3109d7baab40SDavid du Colombier
31101ce6c95fSDavid du Colombier qmax = tcb->window;
31111ce6c95fSDavid du Colombier if(tcb->reseqlen > qmax){
31121ce6c95fSDavid du Colombier netlog(f, Logtcp, "tcp: reseq: queue > window: %d > %d; %d packets\n",
31131ce6c95fSDavid du Colombier tcb->reseqlen, qmax, tcb->nreseq);
31141ce6c95fSDavid du Colombier logreseq(f, tcb->reseq, tcb->rcv.nxt);
31151ce6c95fSDavid du Colombier tpriv->stats[ReseqBytelim]++;
31161ce6c95fSDavid du Colombier return dumpreseq(tcb);
3117d7baab40SDavid du Colombier }
31181ce6c95fSDavid du Colombier qmax = tcb->window / tcb->mss; /* ~190 for qscale=2, 390 for qscale=3 */
31191ce6c95fSDavid du Colombier if(tcb->nreseq > qmax){
31201ce6c95fSDavid du Colombier netlog(f, Logtcp, "resequence queue > packets: %d %d; %d bytes\n",
31211ce6c95fSDavid du Colombier tcb->nreseq, qmax, tcb->reseqlen);
31221ce6c95fSDavid du Colombier logreseq(f, tcb->reseq, tcb->rcv.nxt);
31231ce6c95fSDavid du Colombier tpriv->stats[ReseqPktlim]++;
31241ce6c95fSDavid du Colombier return dumpreseq(tcb);
31257dd7cddfSDavid du Colombier }
31267dd7cddfSDavid du Colombier return 0;
31277dd7cddfSDavid du Colombier }
31287dd7cddfSDavid du Colombier
31291ce6c95fSDavid du Colombier static void
getreseq(Tcpctl * tcb,Tcp * seg,Block ** bp,ushort * length)31307dd7cddfSDavid du Colombier getreseq(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
31317dd7cddfSDavid du Colombier {
31327dd7cddfSDavid du Colombier Reseq *rp;
31337dd7cddfSDavid du Colombier
31347dd7cddfSDavid du Colombier rp = tcb->reseq;
31357dd7cddfSDavid du Colombier if(rp == nil)
31367dd7cddfSDavid du Colombier return;
31377dd7cddfSDavid du Colombier
31387dd7cddfSDavid du Colombier tcb->reseq = rp->next;
31397dd7cddfSDavid du Colombier
31407dd7cddfSDavid du Colombier *seg = rp->seg;
31417dd7cddfSDavid du Colombier *bp = rp->bp;
31427dd7cddfSDavid du Colombier *length = rp->length;
31437dd7cddfSDavid du Colombier
31441ce6c95fSDavid du Colombier tcb->nreseq--;
31451ce6c95fSDavid du Colombier tcb->reseqlen -= rp->length;
31461ce6c95fSDavid du Colombier
31477dd7cddfSDavid du Colombier free(rp);
31487dd7cddfSDavid du Colombier }
31497dd7cddfSDavid du Colombier
31501ce6c95fSDavid du Colombier static int
tcptrim(Tcpctl * tcb,Tcp * seg,Block ** bp,ushort * length)31517dd7cddfSDavid du Colombier tcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
31527dd7cddfSDavid du Colombier {
31537dd7cddfSDavid du Colombier ushort len;
31547dd7cddfSDavid du Colombier uchar accept;
31557dd7cddfSDavid du Colombier int dupcnt, excess;
31567dd7cddfSDavid du Colombier
31577dd7cddfSDavid du Colombier accept = 0;
31587dd7cddfSDavid du Colombier len = *length;
31597dd7cddfSDavid du Colombier if(seg->flags & SYN)
31607dd7cddfSDavid du Colombier len++;
31617dd7cddfSDavid du Colombier if(seg->flags & FIN)
31627dd7cddfSDavid du Colombier len++;
31637dd7cddfSDavid du Colombier
31647dd7cddfSDavid du Colombier if(tcb->rcv.wnd == 0) {
31657dd7cddfSDavid du Colombier if(len == 0 && seg->seq == tcb->rcv.nxt)
31667dd7cddfSDavid du Colombier return 0;
31677dd7cddfSDavid du Colombier }
31687dd7cddfSDavid du Colombier else {
31697dd7cddfSDavid du Colombier /* Some part of the segment should be in the window */
31707dd7cddfSDavid du Colombier if(inwindow(tcb,seg->seq))
31717dd7cddfSDavid du Colombier accept++;
31727dd7cddfSDavid du Colombier else
31737dd7cddfSDavid du Colombier if(len != 0) {
31747dd7cddfSDavid du Colombier if(inwindow(tcb, seg->seq+len-1) ||
31757dd7cddfSDavid du Colombier seq_within(tcb->rcv.nxt, seg->seq,seg->seq+len-1))
31767dd7cddfSDavid du Colombier accept++;
31777dd7cddfSDavid du Colombier }
31787dd7cddfSDavid du Colombier }
31797dd7cddfSDavid du Colombier if(!accept) {
31807dd7cddfSDavid du Colombier freeblist(*bp);
31817dd7cddfSDavid du Colombier return -1;
31827dd7cddfSDavid du Colombier }
31837dd7cddfSDavid du Colombier dupcnt = tcb->rcv.nxt - seg->seq;
31847dd7cddfSDavid du Colombier if(dupcnt > 0){
31857dd7cddfSDavid du Colombier tcb->rerecv += dupcnt;
31867dd7cddfSDavid du Colombier if(seg->flags & SYN){
31877dd7cddfSDavid du Colombier seg->flags &= ~SYN;
31887dd7cddfSDavid du Colombier seg->seq++;
31897dd7cddfSDavid du Colombier
31907dd7cddfSDavid du Colombier if(seg->urg > 1)
31917dd7cddfSDavid du Colombier seg->urg--;
31927dd7cddfSDavid du Colombier else
31937dd7cddfSDavid du Colombier seg->flags &= ~URG;
31947dd7cddfSDavid du Colombier dupcnt--;
31957dd7cddfSDavid du Colombier }
31967dd7cddfSDavid du Colombier if(dupcnt > 0){
31977dd7cddfSDavid du Colombier pullblock(bp, (ushort)dupcnt);
31987dd7cddfSDavid du Colombier seg->seq += dupcnt;
31997dd7cddfSDavid du Colombier *length -= dupcnt;
32007dd7cddfSDavid du Colombier
32017dd7cddfSDavid du Colombier if(seg->urg > dupcnt)
32027dd7cddfSDavid du Colombier seg->urg -= dupcnt;
32037dd7cddfSDavid du Colombier else {
32047dd7cddfSDavid du Colombier seg->flags &= ~URG;
32057dd7cddfSDavid du Colombier seg->urg = 0;
32067dd7cddfSDavid du Colombier }
32077dd7cddfSDavid du Colombier }
32087dd7cddfSDavid du Colombier }
32097dd7cddfSDavid du Colombier excess = seg->seq + *length - (tcb->rcv.nxt + tcb->rcv.wnd);
32107dd7cddfSDavid du Colombier if(excess > 0) {
32117dd7cddfSDavid du Colombier tcb->rerecv += excess;
32127dd7cddfSDavid du Colombier *length -= excess;
321380ee5cbfSDavid du Colombier *bp = trimblock(*bp, 0, *length);
321480ee5cbfSDavid du Colombier if(*bp == nil)
321580ee5cbfSDavid du Colombier panic("presotto is a boofhead");
32167dd7cddfSDavid du Colombier seg->flags &= ~FIN;
32177dd7cddfSDavid du Colombier }
32187dd7cddfSDavid du Colombier return 0;
32197dd7cddfSDavid du Colombier }
32207dd7cddfSDavid du Colombier
32211ce6c95fSDavid du Colombier static void
tcpadvise(Proto * tcp,Block * bp,char * msg)32227dd7cddfSDavid du Colombier tcpadvise(Proto *tcp, Block *bp, char *msg)
32237dd7cddfSDavid du Colombier {
32243ff48bf5SDavid du Colombier Tcp4hdr *h4;
32253ff48bf5SDavid du Colombier Tcp6hdr *h6;
32267dd7cddfSDavid du Colombier Tcpctl *tcb;
32277dd7cddfSDavid du Colombier uchar source[IPaddrlen];
32287dd7cddfSDavid du Colombier uchar dest[IPaddrlen];
32297dd7cddfSDavid du Colombier ushort psource, pdest;
32307dd7cddfSDavid du Colombier Conv *s, **p;
32317dd7cddfSDavid du Colombier
32323ff48bf5SDavid du Colombier h4 = (Tcp4hdr*)(bp->rp);
32333ff48bf5SDavid du Colombier h6 = (Tcp6hdr*)(bp->rp);
32347dd7cddfSDavid du Colombier
32353ff48bf5SDavid du Colombier if((h4->vihl&0xF0)==IP_VER4) {
32363ff48bf5SDavid du Colombier v4tov6(dest, h4->tcpdst);
32373ff48bf5SDavid du Colombier v4tov6(source, h4->tcpsrc);
32383ff48bf5SDavid du Colombier psource = nhgets(h4->tcpsport);
32393ff48bf5SDavid du Colombier pdest = nhgets(h4->tcpdport);
32403ff48bf5SDavid du Colombier }
32413ff48bf5SDavid du Colombier else {
32423ff48bf5SDavid du Colombier ipmove(dest, h6->tcpdst);
32433ff48bf5SDavid du Colombier ipmove(source, h6->tcpsrc);
32443ff48bf5SDavid du Colombier psource = nhgets(h6->tcpsport);
32453ff48bf5SDavid du Colombier pdest = nhgets(h6->tcpdport);
32463ff48bf5SDavid du Colombier }
32477dd7cddfSDavid du Colombier
32487dd7cddfSDavid du Colombier /* Look for a connection */
32497dd7cddfSDavid du Colombier qlock(tcp);
32507dd7cddfSDavid du Colombier for(p = tcp->conv; *p; p++) {
32517dd7cddfSDavid du Colombier s = *p;
32527dd7cddfSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
32537dd7cddfSDavid du Colombier if(s->rport == pdest)
32547dd7cddfSDavid du Colombier if(s->lport == psource)
32557dd7cddfSDavid du Colombier if(tcb->state != Closed)
32567dd7cddfSDavid du Colombier if(ipcmp(s->raddr, dest) == 0)
32577dd7cddfSDavid du Colombier if(ipcmp(s->laddr, source) == 0){
32587dd7cddfSDavid du Colombier qlock(s);
32597dd7cddfSDavid du Colombier qunlock(tcp);
32607dd7cddfSDavid du Colombier switch(tcb->state){
32617dd7cddfSDavid du Colombier case Syn_sent:
32627dd7cddfSDavid du Colombier localclose(s, msg);
32637dd7cddfSDavid du Colombier break;
32647dd7cddfSDavid du Colombier }
32657dd7cddfSDavid du Colombier qunlock(s);
32667dd7cddfSDavid du Colombier freeblist(bp);
32677dd7cddfSDavid du Colombier return;
32687dd7cddfSDavid du Colombier }
32697dd7cddfSDavid du Colombier }
32707dd7cddfSDavid du Colombier qunlock(tcp);
32717dd7cddfSDavid du Colombier freeblist(bp);
32727dd7cddfSDavid du Colombier }
32737dd7cddfSDavid du Colombier
32743f695129SDavid du Colombier static char*
tcpporthogdefensectl(char * val)32753f695129SDavid du Colombier tcpporthogdefensectl(char *val)
32763f695129SDavid du Colombier {
32773f695129SDavid du Colombier if(strcmp(val, "on") == 0)
32783f695129SDavid du Colombier tcpporthogdefense = 1;
32793f695129SDavid du Colombier else if(strcmp(val, "off") == 0)
32803f695129SDavid du Colombier tcpporthogdefense = 0;
32813f695129SDavid du Colombier else
32823f695129SDavid du Colombier return "unknown value for tcpporthogdefense";
32833f695129SDavid du Colombier return nil;
32843f695129SDavid du Colombier }
32853f695129SDavid du Colombier
328680ee5cbfSDavid du Colombier /* called with c qlocked */
32871ce6c95fSDavid du Colombier static char*
tcpctl(Conv * c,char ** f,int n)32887dd7cddfSDavid du Colombier tcpctl(Conv* c, char** f, int n)
32897dd7cddfSDavid du Colombier {
32907dd7cddfSDavid du Colombier if(n == 1 && strcmp(f[0], "hangup") == 0)
32917dd7cddfSDavid du Colombier return tcphangup(c);
32927dd7cddfSDavid du Colombier if(n >= 1 && strcmp(f[0], "keepalive") == 0)
32937dd7cddfSDavid du Colombier return tcpstartka(c, f, n);
329459cc4ca5SDavid du Colombier if(n >= 1 && strcmp(f[0], "checksum") == 0)
329559cc4ca5SDavid du Colombier return tcpsetchecksum(c, f, n);
32963f695129SDavid du Colombier if(n >= 1 && strcmp(f[0], "tcpporthogdefense") == 0)
32973f695129SDavid du Colombier return tcpporthogdefensectl(f[1]);
32987dd7cddfSDavid du Colombier return "unknown control request";
32997dd7cddfSDavid du Colombier }
33007dd7cddfSDavid du Colombier
33011ce6c95fSDavid du Colombier static int
tcpstats(Proto * tcp,char * buf,int len)33027dd7cddfSDavid du Colombier tcpstats(Proto *tcp, char *buf, int len)
33037dd7cddfSDavid du Colombier {
330459cc4ca5SDavid du Colombier Tcppriv *priv;
330559cc4ca5SDavid du Colombier char *p, *e;
330659cc4ca5SDavid du Colombier int i;
33077dd7cddfSDavid du Colombier
330859cc4ca5SDavid du Colombier priv = tcp->priv;
330959cc4ca5SDavid du Colombier p = buf;
331059cc4ca5SDavid du Colombier e = p+len;
331159cc4ca5SDavid du Colombier for(i = 0; i < Nstats; i++)
33125e27dea9SDavid du Colombier p = seprint(p, e, "%s: %llud\n", statnames[i], priv->stats[i]);
331359cc4ca5SDavid du Colombier return p - buf;
33147dd7cddfSDavid du Colombier }
33157dd7cddfSDavid du Colombier
33167dd7cddfSDavid du Colombier /*
33177dd7cddfSDavid du Colombier * garbage collect any stale conversations:
33187dd7cddfSDavid du Colombier * - SYN received but no SYN-ACK after 5 seconds (could be the SYN attack)
33197dd7cddfSDavid du Colombier * - Finwait2 after 5 minutes
33207dd7cddfSDavid du Colombier *
33217dd7cddfSDavid du Colombier * this is called whenever we run out of channels. Both checks are
33227dd7cddfSDavid du Colombier * of questionable validity so we try to use them only when we're
33237dd7cddfSDavid du Colombier * up against the wall.
33247dd7cddfSDavid du Colombier */
33251ce6c95fSDavid du Colombier static int
tcpgc(Proto * tcp)33267dd7cddfSDavid du Colombier tcpgc(Proto *tcp)
33277dd7cddfSDavid du Colombier {
33287dd7cddfSDavid du Colombier Conv *c, **pp, **ep;
33297dd7cddfSDavid du Colombier int n;
33307dd7cddfSDavid du Colombier Tcpctl *tcb;
33317dd7cddfSDavid du Colombier
33327dd7cddfSDavid du Colombier
33337dd7cddfSDavid du Colombier n = 0;
33347dd7cddfSDavid du Colombier ep = &tcp->conv[tcp->nc];
33357dd7cddfSDavid du Colombier for(pp = tcp->conv; pp < ep; pp++) {
33367dd7cddfSDavid du Colombier c = *pp;
33377dd7cddfSDavid du Colombier if(c == nil)
33387dd7cddfSDavid du Colombier break;
33397dd7cddfSDavid du Colombier if(!canqlock(c))
33407dd7cddfSDavid du Colombier continue;
33417dd7cddfSDavid du Colombier tcb = (Tcpctl*)c->ptcl;
33427dd7cddfSDavid du Colombier switch(tcb->state){
33437dd7cddfSDavid du Colombier case Syn_received:
33443ff48bf5SDavid du Colombier if(NOW - tcb->time > 5000){
33451ce6c95fSDavid du Colombier localclose(c, Etimedout);
33467dd7cddfSDavid du Colombier n++;
33477dd7cddfSDavid du Colombier }
33487dd7cddfSDavid du Colombier break;
33497dd7cddfSDavid du Colombier case Finwait2:
33503ff48bf5SDavid du Colombier if(NOW - tcb->time > 5*60*1000){
33511ce6c95fSDavid du Colombier localclose(c, Etimedout);
33527dd7cddfSDavid du Colombier n++;
33537dd7cddfSDavid du Colombier }
33547dd7cddfSDavid du Colombier break;
33557dd7cddfSDavid du Colombier }
33567dd7cddfSDavid du Colombier qunlock(c);
33577dd7cddfSDavid du Colombier }
33587dd7cddfSDavid du Colombier return n;
33597dd7cddfSDavid du Colombier }
33607dd7cddfSDavid du Colombier
33611ce6c95fSDavid du Colombier static void
tcpsettimer(Tcpctl * tcb)33629a747e4fSDavid du Colombier tcpsettimer(Tcpctl *tcb)
33639a747e4fSDavid du Colombier {
33649a747e4fSDavid du Colombier int x;
33659a747e4fSDavid du Colombier
33661b0f96a1SDavid du Colombier /* round trip dependency */
33679a747e4fSDavid du Colombier x = backoff(tcb->backoff) *
33689a747e4fSDavid du Colombier (tcb->mdev + (tcb->srtt>>LOGAGAIN) + MSPTICK) / MSPTICK;
33699a747e4fSDavid du Colombier
33701ce6c95fSDavid du Colombier /* bounded twixt 0.3 and 64 seconds */
33711ce6c95fSDavid du Colombier if(x < 300/MSPTICK)
33721ce6c95fSDavid du Colombier x = 300/MSPTICK;
33731b0f96a1SDavid du Colombier else if(x > (64000/MSPTICK))
33741b0f96a1SDavid du Colombier x = 64000/MSPTICK;
33759a747e4fSDavid du Colombier tcb->timer.start = x;
33769a747e4fSDavid du Colombier }
33779a747e4fSDavid du Colombier
33789a747e4fSDavid du Colombier void
tcpinit(Fs * fs)33797dd7cddfSDavid du Colombier tcpinit(Fs *fs)
33807dd7cddfSDavid du Colombier {
33817dd7cddfSDavid du Colombier Proto *tcp;
33827dd7cddfSDavid du Colombier Tcppriv *tpriv;
33837dd7cddfSDavid du Colombier
33847dd7cddfSDavid du Colombier tcp = smalloc(sizeof(Proto));
33857dd7cddfSDavid du Colombier tpriv = tcp->priv = smalloc(sizeof(Tcppriv));
33867dd7cddfSDavid du Colombier tcp->name = "tcp";
33877dd7cddfSDavid du Colombier tcp->connect = tcpconnect;
33887dd7cddfSDavid du Colombier tcp->announce = tcpannounce;
33897dd7cddfSDavid du Colombier tcp->ctl = tcpctl;
33907dd7cddfSDavid du Colombier tcp->state = tcpstate;
33917dd7cddfSDavid du Colombier tcp->create = tcpcreate;
33927dd7cddfSDavid du Colombier tcp->close = tcpclose;
33937dd7cddfSDavid du Colombier tcp->rcv = tcpiput;
33947dd7cddfSDavid du Colombier tcp->advise = tcpadvise;
33957dd7cddfSDavid du Colombier tcp->stats = tcpstats;
33967dd7cddfSDavid du Colombier tcp->inuse = tcpinuse;
33977dd7cddfSDavid du Colombier tcp->gc = tcpgc;
33987dd7cddfSDavid du Colombier tcp->ipproto = IP_TCPPROTO;
33999a747e4fSDavid du Colombier tcp->nc = scalednconv();
34007dd7cddfSDavid du Colombier tcp->ptclsize = sizeof(Tcpctl);
34019a747e4fSDavid du Colombier tpriv->stats[MaxConn] = tcp->nc;
34027dd7cddfSDavid du Colombier
34037dd7cddfSDavid du Colombier Fsproto(fs, tcp);
34047dd7cddfSDavid du Colombier }
34053f695129SDavid du Colombier
34061ce6c95fSDavid du Colombier static void
tcpsetscale(Conv * s,Tcpctl * tcb,ushort rcvscale,ushort sndscale)34073f695129SDavid du Colombier tcpsetscale(Conv *s, Tcpctl *tcb, ushort rcvscale, ushort sndscale)
34083f695129SDavid du Colombier {
34091ce6c95fSDavid du Colombier /*
34101ce6c95fSDavid du Colombier * guess at reasonable queue sizes. there's no current way
34111ce6c95fSDavid du Colombier * to know how many nic receive buffers we can safely tie up in the
34121ce6c95fSDavid du Colombier * tcp stack, and we don't adjust our queues to maximize throughput
34131ce6c95fSDavid du Colombier * and minimize bufferbloat. n.b. the offer (rcvscale) needs to be
34141ce6c95fSDavid du Colombier * respected, but we still control our own buffer commitment by
34151ce6c95fSDavid du Colombier * keeping a seperate qscale.
34161ce6c95fSDavid du Colombier */
34173f695129SDavid du Colombier tcb->rcv.scale = rcvscale & 0xff;
34183f695129SDavid du Colombier tcb->snd.scale = sndscale & 0xff;
34191ce6c95fSDavid du Colombier tcb->qscale = rcvscale & 0xff;
34201ce6c95fSDavid du Colombier if(rcvscale > Maxqscale)
34211ce6c95fSDavid du Colombier tcb->qscale = Maxqscale;
34221ce6c95fSDavid du Colombier
34231ce6c95fSDavid du Colombier if(rcvscale != tcb->rcv.scale)
34241ce6c95fSDavid du Colombier netlog(s->p->f, Logtcp, "tcpsetscale: window %lud "
34251ce6c95fSDavid du Colombier "qlen %d >> window %ud lport %d\n",
34261ce6c95fSDavid du Colombier tcb->window, qlen(s->rq), QMAX<<tcb->qscale, s->lport);
34271ce6c95fSDavid du Colombier tcb->window = QMAX << tcb->qscale;
34281ce6c95fSDavid du Colombier tcb->ssthresh = tcb->window;
34291ce6c95fSDavid du Colombier
34301ce6c95fSDavid du Colombier /*
34311ce6c95fSDavid du Colombier * it's important to set wq large enough to cover the full
34321ce6c95fSDavid du Colombier * bandwidth-delay product. it's possible to be in loss
34331ce6c95fSDavid du Colombier * recovery with a big window, and we need to keep sending
34341ce6c95fSDavid du Colombier * into the inflated window. the difference can be huge
34351ce6c95fSDavid du Colombier * for even modest (70ms) ping times.
34361ce6c95fSDavid du Colombier */
34373f695129SDavid du Colombier qsetlimit(s->rq, tcb->window);
34381ce6c95fSDavid du Colombier qsetlimit(s->wq, tcb->window);
34391ce6c95fSDavid du Colombier tcprcvwin(s);
34403f695129SDavid du Colombier }
3441