19ef1f84bSDavid du Colombier #include "u.h"
29ef1f84bSDavid du Colombier #include "../port/lib.h"
39ef1f84bSDavid du Colombier #include "mem.h"
49ef1f84bSDavid du Colombier #include "dat.h"
59ef1f84bSDavid du Colombier #include "fns.h"
69ef1f84bSDavid du Colombier #include "../port/error.h"
79ef1f84bSDavid du Colombier
89ef1f84bSDavid du Colombier #include "ip.h"
99ef1f84bSDavid du Colombier
109ef1f84bSDavid du Colombier enum
119ef1f84bSDavid du Colombier {
129ef1f84bSDavid du Colombier QMAX = 64*1024-1,
139ef1f84bSDavid du Colombier IP_TCPPROTO = 6,
149ef1f84bSDavid du Colombier
159ef1f84bSDavid du Colombier TCP4_IPLEN = 8,
169ef1f84bSDavid du Colombier TCP4_PHDRSIZE = 12,
179ef1f84bSDavid du Colombier TCP4_HDRSIZE = 20,
189ef1f84bSDavid du Colombier TCP4_TCBPHDRSZ = 40,
199ef1f84bSDavid du Colombier TCP4_PKT = TCP4_IPLEN+TCP4_PHDRSIZE,
209ef1f84bSDavid du Colombier
219ef1f84bSDavid du Colombier TCP6_IPLEN = 0,
229ef1f84bSDavid du Colombier TCP6_PHDRSIZE = 40,
239ef1f84bSDavid du Colombier TCP6_HDRSIZE = 20,
249ef1f84bSDavid du Colombier TCP6_TCBPHDRSZ = 60,
259ef1f84bSDavid du Colombier TCP6_PKT = TCP6_IPLEN+TCP6_PHDRSIZE,
269ef1f84bSDavid du Colombier
279ef1f84bSDavid du Colombier TcptimerOFF = 0,
289ef1f84bSDavid du Colombier TcptimerON = 1,
299ef1f84bSDavid du Colombier TcptimerDONE = 2,
309ef1f84bSDavid du Colombier MAX_TIME = (1<<20), /* Forever */
319ef1f84bSDavid du Colombier TCP_ACK = 50, /* Timed ack sequence in ms */
329ef1f84bSDavid du Colombier MAXBACKMS = 9*60*1000, /* longest backoff time (ms) before hangup */
339ef1f84bSDavid du Colombier
349ef1f84bSDavid du Colombier URG = 0x20, /* Data marked urgent */
359ef1f84bSDavid du Colombier ACK = 0x10, /* Acknowledge is valid */
369ef1f84bSDavid du Colombier PSH = 0x08, /* Whole data pipe is pushed */
379ef1f84bSDavid du Colombier RST = 0x04, /* Reset connection */
389ef1f84bSDavid du Colombier SYN = 0x02, /* Pkt. is synchronise */
399ef1f84bSDavid du Colombier FIN = 0x01, /* Start close down */
409ef1f84bSDavid du Colombier
419ef1f84bSDavid du Colombier EOLOPT = 0,
429ef1f84bSDavid du Colombier NOOPOPT = 1,
439ef1f84bSDavid du Colombier MSSOPT = 2,
449ef1f84bSDavid du Colombier MSS_LENGTH = 4, /* Maximum segment size */
459ef1f84bSDavid du Colombier WSOPT = 3,
469ef1f84bSDavid du Colombier WS_LENGTH = 3, /* Bits to scale window size by */
479ef1f84bSDavid du Colombier MSL2 = 10,
489ef1f84bSDavid du Colombier MSPTICK = 50, /* Milliseconds per timer tick */
499ef1f84bSDavid du Colombier DEF_MSS = 1460, /* Default maximum segment */
509ef1f84bSDavid du Colombier DEF_MSS6 = 1280, /* Default maximum segment (min) for v6 */
519ef1f84bSDavid du Colombier DEF_RTT = 500, /* Default round trip */
529ef1f84bSDavid du Colombier DEF_KAT = 120000, /* Default time (ms) between keep alives */
539ef1f84bSDavid du Colombier TCP_LISTEN = 0, /* Listen connection */
549ef1f84bSDavid du Colombier TCP_CONNECT = 1, /* Outgoing connection */
559ef1f84bSDavid du Colombier SYNACK_RXTIMER = 250, /* ms between SYNACK retransmits */
569ef1f84bSDavid du Colombier
579ef1f84bSDavid du Colombier TCPREXMTTHRESH = 3, /* dupack threshhold for rxt */
589ef1f84bSDavid du Colombier
599ef1f84bSDavid du Colombier FORCE = 1,
609ef1f84bSDavid du Colombier CLONE = 2,
619ef1f84bSDavid du Colombier RETRAN = 4,
629ef1f84bSDavid du Colombier ACTIVE = 8,
639ef1f84bSDavid du Colombier SYNACK = 16,
649ef1f84bSDavid du Colombier
659ef1f84bSDavid du Colombier LOGAGAIN = 3,
669ef1f84bSDavid du Colombier LOGDGAIN = 2,
679ef1f84bSDavid du Colombier
689ef1f84bSDavid du Colombier Closed = 0, /* Connection states */
699ef1f84bSDavid du Colombier Listen,
709ef1f84bSDavid du Colombier Syn_sent,
719ef1f84bSDavid du Colombier Syn_received,
729ef1f84bSDavid du Colombier Established,
739ef1f84bSDavid du Colombier Finwait1,
749ef1f84bSDavid du Colombier Finwait2,
759ef1f84bSDavid du Colombier Close_wait,
769ef1f84bSDavid du Colombier Closing,
779ef1f84bSDavid du Colombier Last_ack,
789ef1f84bSDavid du Colombier Time_wait,
799ef1f84bSDavid du Colombier
809ef1f84bSDavid du Colombier Maxlimbo = 1000, /* maximum procs waiting for response to SYN ACK */
819ef1f84bSDavid du Colombier NLHT = 256, /* hash table size, must be a power of 2 */
829ef1f84bSDavid du Colombier LHTMASK = NLHT-1,
839ef1f84bSDavid du Colombier
849ef1f84bSDavid du Colombier /*
859ef1f84bSDavid du Colombier * window is 64kb * 2ⁿ
869ef1f84bSDavid du Colombier * these factors determine the ultimate bandwidth-delay product.
879ef1f84bSDavid du Colombier * 64kb * 2⁵ = 2mb, or 2× overkill for 100mbps * 70ms.
889ef1f84bSDavid du Colombier */
899ef1f84bSDavid du Colombier Maxqscale = 4, /* maximum queuing scale */
909ef1f84bSDavid du Colombier Defadvscale = 4, /* default advertisement */
919ef1f84bSDavid du Colombier };
929ef1f84bSDavid du Colombier
939ef1f84bSDavid du Colombier /* Must correspond to the enumeration above */
949ef1f84bSDavid du Colombier char *tcpstates[] =
959ef1f84bSDavid du Colombier {
969ef1f84bSDavid du Colombier "Closed", "Listen", "Syn_sent", "Syn_received",
979ef1f84bSDavid du Colombier "Established", "Finwait1", "Finwait2", "Close_wait",
989ef1f84bSDavid du Colombier "Closing", "Last_ack", "Time_wait"
999ef1f84bSDavid du Colombier };
1009ef1f84bSDavid du Colombier
1019ef1f84bSDavid du Colombier typedef struct Tcptimer Tcptimer;
1029ef1f84bSDavid du Colombier struct Tcptimer
1039ef1f84bSDavid du Colombier {
1049ef1f84bSDavid du Colombier Tcptimer *next;
1059ef1f84bSDavid du Colombier Tcptimer *prev;
1069ef1f84bSDavid du Colombier Tcptimer *readynext;
1079ef1f84bSDavid du Colombier int state;
1089ef1f84bSDavid du Colombier int start;
1099ef1f84bSDavid du Colombier int count;
1109ef1f84bSDavid du Colombier void (*func)(void*);
1119ef1f84bSDavid du Colombier void *arg;
1129ef1f84bSDavid du Colombier };
1139ef1f84bSDavid du Colombier
1149ef1f84bSDavid du Colombier /*
1159ef1f84bSDavid du Colombier * v4 and v6 pseudo headers used for
1169ef1f84bSDavid du Colombier * checksuming tcp
1179ef1f84bSDavid du Colombier */
1189ef1f84bSDavid du Colombier typedef struct Tcp4hdr Tcp4hdr;
1199ef1f84bSDavid du Colombier struct Tcp4hdr
1209ef1f84bSDavid du Colombier {
1219ef1f84bSDavid du Colombier uchar vihl; /* Version and header length */
1229ef1f84bSDavid du Colombier uchar tos; /* Type of service */
1239ef1f84bSDavid du Colombier uchar length[2]; /* packet length */
1249ef1f84bSDavid du Colombier uchar id[2]; /* Identification */
1259ef1f84bSDavid du Colombier uchar frag[2]; /* Fragment information */
1269ef1f84bSDavid du Colombier uchar Unused;
1279ef1f84bSDavid du Colombier uchar proto;
1289ef1f84bSDavid du Colombier uchar tcplen[2];
1299ef1f84bSDavid du Colombier uchar tcpsrc[4];
1309ef1f84bSDavid du Colombier uchar tcpdst[4];
1319ef1f84bSDavid du Colombier uchar tcpsport[2];
1329ef1f84bSDavid du Colombier uchar tcpdport[2];
1339ef1f84bSDavid du Colombier uchar tcpseq[4];
1349ef1f84bSDavid du Colombier uchar tcpack[4];
1359ef1f84bSDavid du Colombier uchar tcpflag[2];
1369ef1f84bSDavid du Colombier uchar tcpwin[2];
1379ef1f84bSDavid du Colombier uchar tcpcksum[2];
1389ef1f84bSDavid du Colombier uchar tcpurg[2];
1399ef1f84bSDavid du Colombier /* Options segment */
1409ef1f84bSDavid du Colombier uchar tcpopt[1];
1419ef1f84bSDavid du Colombier };
1429ef1f84bSDavid du Colombier
1439ef1f84bSDavid du Colombier typedef struct Tcp6hdr Tcp6hdr;
1449ef1f84bSDavid du Colombier struct Tcp6hdr
1459ef1f84bSDavid du Colombier {
1469ef1f84bSDavid du Colombier uchar vcf[4];
1479ef1f84bSDavid du Colombier uchar ploadlen[2];
1489ef1f84bSDavid du Colombier uchar proto;
1499ef1f84bSDavid du Colombier uchar ttl;
1509ef1f84bSDavid du Colombier uchar tcpsrc[IPaddrlen];
1519ef1f84bSDavid du Colombier uchar tcpdst[IPaddrlen];
1529ef1f84bSDavid du Colombier uchar tcpsport[2];
1539ef1f84bSDavid du Colombier uchar tcpdport[2];
1549ef1f84bSDavid du Colombier uchar tcpseq[4];
1559ef1f84bSDavid du Colombier uchar tcpack[4];
1569ef1f84bSDavid du Colombier uchar tcpflag[2];
1579ef1f84bSDavid du Colombier uchar tcpwin[2];
1589ef1f84bSDavid du Colombier uchar tcpcksum[2];
1599ef1f84bSDavid du Colombier uchar tcpurg[2];
1609ef1f84bSDavid du Colombier /* Options segment */
1619ef1f84bSDavid du Colombier uchar tcpopt[1];
1629ef1f84bSDavid du Colombier };
1639ef1f84bSDavid du Colombier
1649ef1f84bSDavid du Colombier /*
1659ef1f84bSDavid du Colombier * this represents the control info
1669ef1f84bSDavid du Colombier * for a single packet. It is derived from
1679ef1f84bSDavid du Colombier * a packet in ntohtcp{4,6}() and stuck into
1689ef1f84bSDavid du Colombier * a packet in htontcp{4,6}().
1699ef1f84bSDavid du Colombier */
1709ef1f84bSDavid du Colombier typedef struct Tcp Tcp;
1719ef1f84bSDavid du Colombier struct Tcp
1729ef1f84bSDavid du Colombier {
1739ef1f84bSDavid du Colombier ushort source;
1749ef1f84bSDavid du Colombier ushort dest;
1759ef1f84bSDavid du Colombier ulong seq;
1769ef1f84bSDavid du Colombier ulong ack;
1779ef1f84bSDavid du Colombier uchar flags;
1789ef1f84bSDavid du Colombier uchar update;
1799ef1f84bSDavid du Colombier ushort ws; /* window scale option */
1809ef1f84bSDavid du Colombier ulong wnd; /* prescaled window*/
1819ef1f84bSDavid du Colombier ushort urg;
1829ef1f84bSDavid du Colombier ushort mss; /* max segment size option (if not zero) */
1839ef1f84bSDavid du Colombier ushort len; /* size of data */
1849ef1f84bSDavid du Colombier };
1859ef1f84bSDavid du Colombier
1869ef1f84bSDavid du Colombier /*
1879ef1f84bSDavid du Colombier * this header is malloc'd to thread together fragments
1889ef1f84bSDavid du Colombier * waiting to be coalesced
1899ef1f84bSDavid du Colombier */
1909ef1f84bSDavid du Colombier typedef struct Reseq Reseq;
1919ef1f84bSDavid du Colombier struct Reseq
1929ef1f84bSDavid du Colombier {
1939ef1f84bSDavid du Colombier Reseq *next;
1949ef1f84bSDavid du Colombier Tcp seg;
1959ef1f84bSDavid du Colombier Block *bp;
1969ef1f84bSDavid du Colombier ushort length;
1979ef1f84bSDavid du Colombier };
1989ef1f84bSDavid du Colombier
1999ef1f84bSDavid du Colombier /*
2009ef1f84bSDavid du Colombier * the qlock in the Conv locks this structure
2019ef1f84bSDavid du Colombier */
2029ef1f84bSDavid du Colombier typedef struct Tcpctl Tcpctl;
2039ef1f84bSDavid du Colombier struct Tcpctl
2049ef1f84bSDavid du Colombier {
2059ef1f84bSDavid du Colombier uchar state; /* Connection state */
2069ef1f84bSDavid du Colombier uchar type; /* Listening or active connection */
2079ef1f84bSDavid du Colombier uchar code; /* Icmp code */
2089ef1f84bSDavid du Colombier struct {
2099ef1f84bSDavid du Colombier ulong una; /* Unacked data pointer */
2109ef1f84bSDavid du Colombier ulong nxt; /* Next sequence expected */
2119ef1f84bSDavid du Colombier ulong ptr; /* Data pointer */
2129ef1f84bSDavid du Colombier ulong wnd; /* Tcp send window */
2139ef1f84bSDavid du Colombier ulong urg; /* Urgent data pointer */
2149ef1f84bSDavid du Colombier ulong wl2;
2159ef1f84bSDavid du Colombier uint scale; /* how much to right shift window */
2169ef1f84bSDavid du Colombier /* in xmitted packets */
2179ef1f84bSDavid du Colombier /* to implement tahoe and reno TCP */
2189ef1f84bSDavid du Colombier ulong dupacks; /* number of duplicate acks rcvd */
2199ef1f84bSDavid du Colombier ulong partialack;
2209ef1f84bSDavid du Colombier int recovery; /* loss recovery flag */
2219ef1f84bSDavid du Colombier int retransmit; /* retransmit 1 packet @ una flag */
2229ef1f84bSDavid du Colombier int rto;
2239ef1f84bSDavid du Colombier ulong rxt; /* right window marker for recovery */
2249ef1f84bSDavid du Colombier /* "recover" rfc3782 */
2259ef1f84bSDavid du Colombier } snd;
2269ef1f84bSDavid du Colombier struct {
2279ef1f84bSDavid du Colombier ulong nxt; /* Receive pointer to next uchar slot */
2289ef1f84bSDavid du Colombier ulong wnd; /* Receive window incoming */
2299ef1f84bSDavid du Colombier ulong wsnt; /* Last wptr sent. important to */
2309ef1f84bSDavid du Colombier /* track for large bdp */
2319ef1f84bSDavid du Colombier ulong wptr;
2329ef1f84bSDavid du Colombier ulong urg; /* Urgent pointer */
2339ef1f84bSDavid du Colombier ulong ackptr; /* last acked sequence */
2349ef1f84bSDavid du Colombier int blocked;
2359ef1f84bSDavid du Colombier uint scale; /* how much to left shift window in */
2369ef1f84bSDavid du Colombier /* rcv'd packets */
2379ef1f84bSDavid du Colombier } rcv;
2389ef1f84bSDavid du Colombier ulong iss; /* Initial sequence number */
2399ef1f84bSDavid du Colombier ulong cwind; /* Congestion window */
2409ef1f84bSDavid du Colombier ulong abcbytes; /* appropriate byte counting rfc 3465 */
2419ef1f84bSDavid du Colombier uint scale; /* desired snd.scale */
2429ef1f84bSDavid du Colombier ulong ssthresh; /* Slow start threshold */
2439ef1f84bSDavid du Colombier int resent; /* Bytes just resent */
2449ef1f84bSDavid du Colombier int irs; /* Initial received squence */
2459ef1f84bSDavid du Colombier ushort mss; /* Maximum segment size */
2469ef1f84bSDavid du Colombier int rerecv; /* Overlap of data rerecevived */
2479ef1f84bSDavid du Colombier ulong window; /* Our receive window (queue) */
2489ef1f84bSDavid du Colombier uint qscale; /* Log2 of our receive window (queue) */
2499ef1f84bSDavid du Colombier uchar backoff; /* Exponential backoff counter */
2509ef1f84bSDavid du Colombier int backedoff; /* ms we've backed off for rexmits */
2519ef1f84bSDavid du Colombier uchar flags; /* State flags */
2529ef1f84bSDavid du Colombier Reseq *reseq; /* Resequencing queue */
2539ef1f84bSDavid du Colombier int nreseq;
2549ef1f84bSDavid du Colombier int reseqlen;
2559ef1f84bSDavid du Colombier Tcptimer timer; /* Activity timer */
2569ef1f84bSDavid du Colombier Tcptimer acktimer; /* Acknowledge timer */
2579ef1f84bSDavid du Colombier Tcptimer rtt_timer; /* Round trip timer */
2589ef1f84bSDavid du Colombier Tcptimer katimer; /* keep alive timer */
2599ef1f84bSDavid du Colombier ulong rttseq; /* Round trip sequence */
2609ef1f84bSDavid du Colombier int srtt; /* Smoothed round trip */
2619ef1f84bSDavid du Colombier int mdev; /* Mean deviation of round trip */
2629ef1f84bSDavid du Colombier int kacounter; /* count down for keep alive */
2639ef1f84bSDavid du Colombier uint sndsyntime; /* time syn sent */
2649ef1f84bSDavid du Colombier ulong time; /* time Finwait2 or Syn_received was sent */
2659ef1f84bSDavid du Colombier ulong timeuna; /* snd.una when time was set */
2669ef1f84bSDavid du Colombier int nochecksum; /* non-zero means don't send checksums */
2679ef1f84bSDavid du Colombier int flgcnt; /* number of flags in the sequence (FIN,SEQ) */
2689ef1f84bSDavid du Colombier
2699ef1f84bSDavid du Colombier union {
2709ef1f84bSDavid du Colombier Tcp4hdr tcp4hdr;
2719ef1f84bSDavid du Colombier Tcp6hdr tcp6hdr;
2729ef1f84bSDavid du Colombier } protohdr; /* prototype header */
2739ef1f84bSDavid du Colombier };
2749ef1f84bSDavid du Colombier
2759ef1f84bSDavid du Colombier /*
2769ef1f84bSDavid du Colombier * New calls are put in limbo rather than having a conversation structure
2779ef1f84bSDavid du Colombier * allocated. Thus, a SYN attack results in lots of limbo'd calls but not
2789ef1f84bSDavid du Colombier * any real Conv structures mucking things up. Calls in limbo rexmit their
2799ef1f84bSDavid du Colombier * SYN ACK every SYNACK_RXTIMER ms up to 4 times, i.e., they disappear after 1 second.
2809ef1f84bSDavid du Colombier *
2819ef1f84bSDavid du Colombier * In particular they aren't on a listener's queue so that they don't figure
2829ef1f84bSDavid du Colombier * in the input queue limit.
2839ef1f84bSDavid du Colombier *
2849ef1f84bSDavid du Colombier * If 1/2 of a T3 was attacking SYN packets, we'ld have a permanent queue
2859ef1f84bSDavid du Colombier * of 70000 limbo'd calls. Not great for a linear list but doable. Therefore
2869ef1f84bSDavid du Colombier * there is no hashing of this list.
2879ef1f84bSDavid du Colombier */
2889ef1f84bSDavid du Colombier typedef struct Limbo Limbo;
2899ef1f84bSDavid du Colombier struct Limbo
2909ef1f84bSDavid du Colombier {
2919ef1f84bSDavid du Colombier Limbo *next;
2929ef1f84bSDavid du Colombier
2939ef1f84bSDavid du Colombier uchar laddr[IPaddrlen];
2949ef1f84bSDavid du Colombier uchar raddr[IPaddrlen];
2959ef1f84bSDavid du Colombier ushort lport;
2969ef1f84bSDavid du Colombier ushort rport;
2979ef1f84bSDavid du Colombier ulong irs; /* initial received sequence */
2989ef1f84bSDavid du Colombier ulong iss; /* initial sent sequence */
2999ef1f84bSDavid du Colombier ushort mss; /* mss from the other end */
3009ef1f84bSDavid du Colombier ushort rcvscale; /* how much to scale rcvd windows */
3019ef1f84bSDavid du Colombier ushort sndscale; /* how much to scale sent windows */
3029ef1f84bSDavid du Colombier ulong lastsend; /* last time we sent a synack */
3039ef1f84bSDavid du Colombier uchar version; /* v4 or v6 */
3049ef1f84bSDavid du Colombier uchar rexmits; /* number of retransmissions */
3059ef1f84bSDavid du Colombier };
3069ef1f84bSDavid du Colombier
3079ef1f84bSDavid du Colombier int tcp_irtt = DEF_RTT; /* Initial guess at round trip time */
3089ef1f84bSDavid du Colombier
3099ef1f84bSDavid du Colombier enum {
3109ef1f84bSDavid du Colombier /* MIB stats */
3119ef1f84bSDavid du Colombier MaxConn,
3129ef1f84bSDavid du Colombier Mss,
3139ef1f84bSDavid du Colombier ActiveOpens,
3149ef1f84bSDavid du Colombier PassiveOpens,
3159ef1f84bSDavid du Colombier EstabResets,
3169ef1f84bSDavid du Colombier CurrEstab,
3179ef1f84bSDavid du Colombier InSegs,
3189ef1f84bSDavid du Colombier OutSegs,
3199ef1f84bSDavid du Colombier RetransSegs,
3209ef1f84bSDavid du Colombier RetransSegsSent,
3219ef1f84bSDavid du Colombier RetransTimeouts,
3229ef1f84bSDavid du Colombier InErrs,
3239ef1f84bSDavid du Colombier OutRsts,
3249ef1f84bSDavid du Colombier
3259ef1f84bSDavid du Colombier /* non-MIB stats */
3269ef1f84bSDavid du Colombier CsumErrs,
3279ef1f84bSDavid du Colombier HlenErrs,
3289ef1f84bSDavid du Colombier LenErrs,
3299ef1f84bSDavid du Colombier Resequenced,
3309ef1f84bSDavid du Colombier OutOfOrder,
3319ef1f84bSDavid du Colombier ReseqBytelim,
3329ef1f84bSDavid du Colombier ReseqPktlim,
3339ef1f84bSDavid du Colombier Delayack,
3349ef1f84bSDavid du Colombier Wopenack,
3359ef1f84bSDavid du Colombier
3369ef1f84bSDavid du Colombier Recovery,
3379ef1f84bSDavid du Colombier RecoveryDone,
3389ef1f84bSDavid du Colombier RecoveryRTO,
3399ef1f84bSDavid du Colombier RecoveryNoSeq,
3409ef1f84bSDavid du Colombier RecoveryCwind,
3419ef1f84bSDavid du Colombier RecoveryPA,
3429ef1f84bSDavid du Colombier
3439ef1f84bSDavid du Colombier Nstats
3449ef1f84bSDavid du Colombier };
3459ef1f84bSDavid du Colombier
3469ef1f84bSDavid du Colombier static char *statnames[Nstats] =
3479ef1f84bSDavid du Colombier {
3489ef1f84bSDavid du Colombier [MaxConn] "MaxConn",
3499ef1f84bSDavid du Colombier [Mss] "MaxSegment",
3509ef1f84bSDavid du Colombier [ActiveOpens] "ActiveOpens",
3519ef1f84bSDavid du Colombier [PassiveOpens] "PassiveOpens",
3529ef1f84bSDavid du Colombier [EstabResets] "EstabResets",
3539ef1f84bSDavid du Colombier [CurrEstab] "CurrEstab",
3549ef1f84bSDavid du Colombier [InSegs] "InSegs",
3559ef1f84bSDavid du Colombier [OutSegs] "OutSegs",
3569ef1f84bSDavid du Colombier [RetransSegs] "RetransSegs",
3579ef1f84bSDavid du Colombier [RetransSegsSent] "RetransSegsSent",
3589ef1f84bSDavid du Colombier [RetransTimeouts] "RetransTimeouts",
3599ef1f84bSDavid du Colombier [InErrs] "InErrs",
3609ef1f84bSDavid du Colombier [OutRsts] "OutRsts",
3619ef1f84bSDavid du Colombier [CsumErrs] "CsumErrs",
3629ef1f84bSDavid du Colombier [HlenErrs] "HlenErrs",
3639ef1f84bSDavid du Colombier [LenErrs] "LenErrs",
3649ef1f84bSDavid du Colombier [OutOfOrder] "OutOfOrder",
3659ef1f84bSDavid du Colombier [Resequenced] "Resequenced",
3669ef1f84bSDavid du Colombier [ReseqBytelim] "ReseqBytelim",
3679ef1f84bSDavid du Colombier [ReseqPktlim] "ReseqPktlim",
3689ef1f84bSDavid du Colombier [Delayack] "Delayack",
3699ef1f84bSDavid du Colombier [Wopenack] "Wopenack",
3709ef1f84bSDavid du Colombier
3719ef1f84bSDavid du Colombier [Recovery] "Recovery",
3729ef1f84bSDavid du Colombier [RecoveryDone] "RecoveryDone",
3739ef1f84bSDavid du Colombier [RecoveryRTO] "RecoveryRTO",
3749ef1f84bSDavid du Colombier
3759ef1f84bSDavid du Colombier [RecoveryNoSeq] "RecoveryNoSeq",
3769ef1f84bSDavid du Colombier [RecoveryCwind] "RecoveryCwind",
3779ef1f84bSDavid du Colombier [RecoveryPA] "RecoveryPA",
3789ef1f84bSDavid du Colombier };
3799ef1f84bSDavid du Colombier
3809ef1f84bSDavid du Colombier typedef struct Tcppriv Tcppriv;
3819ef1f84bSDavid du Colombier struct Tcppriv
3829ef1f84bSDavid du Colombier {
3839ef1f84bSDavid du Colombier /* List of active timers */
3849ef1f84bSDavid du Colombier QLock tl;
3859ef1f84bSDavid du Colombier Tcptimer *timers;
3869ef1f84bSDavid du Colombier
3879ef1f84bSDavid du Colombier /* hash table for matching conversations */
3889ef1f84bSDavid du Colombier Ipht ht;
3899ef1f84bSDavid du Colombier
3909ef1f84bSDavid du Colombier /* calls in limbo waiting for an ACK to our SYN ACK */
3919ef1f84bSDavid du Colombier int nlimbo;
3929ef1f84bSDavid du Colombier Limbo *lht[NLHT];
3939ef1f84bSDavid du Colombier
3949ef1f84bSDavid du Colombier /* for keeping track of tcpackproc */
3959ef1f84bSDavid du Colombier QLock apl;
3969ef1f84bSDavid du Colombier int ackprocstarted;
3979ef1f84bSDavid du Colombier
3989ef1f84bSDavid du Colombier uvlong stats[Nstats];
3999ef1f84bSDavid du Colombier };
4009ef1f84bSDavid du Colombier
4019ef1f84bSDavid du Colombier /*
4029ef1f84bSDavid du Colombier * Setting tcpporthogdefense to non-zero enables Dong Lin's
4039ef1f84bSDavid du Colombier * solution to hijacked systems staking out port's as a form
4049ef1f84bSDavid du Colombier * of DoS attack.
4059ef1f84bSDavid du Colombier *
4069ef1f84bSDavid du Colombier * To avoid stateless Conv hogs, we pick a sequence number at random. If
4079ef1f84bSDavid du Colombier * that number gets acked by the other end, we shut down the connection.
4089ef1f84bSDavid du Colombier * Look for tcpporthogdefense in the code.
4099ef1f84bSDavid du Colombier */
4109ef1f84bSDavid du Colombier int tcpporthogdefense = 0;
4119ef1f84bSDavid du Colombier
4129ef1f84bSDavid du Colombier static int addreseq(Fs*, Tcpctl*, Tcppriv*, Tcp*, Block*, ushort);
4139ef1f84bSDavid du Colombier static int dumpreseq(Tcpctl*);
4149ef1f84bSDavid du Colombier static void getreseq(Tcpctl*, Tcp*, Block**, ushort*);
4159ef1f84bSDavid du Colombier static void limbo(Conv*, uchar*, uchar*, Tcp*, int);
4169ef1f84bSDavid du Colombier static void limborexmit(Proto*);
4179ef1f84bSDavid du Colombier static void localclose(Conv*, char*);
4189ef1f84bSDavid du Colombier static void procsyn(Conv*, Tcp*);
4199ef1f84bSDavid du Colombier static void tcpacktimer(void*);
4209ef1f84bSDavid du Colombier static void tcpiput(Proto*, Ipifc*, Block*);
4219ef1f84bSDavid du Colombier static void tcpkeepalive(void*);
4229ef1f84bSDavid du Colombier static void tcpoutput(Conv*);
4239ef1f84bSDavid du Colombier static void tcprcvwin(Conv*);
4249ef1f84bSDavid du Colombier static void tcprxmit(Conv*);
4259ef1f84bSDavid du Colombier static void tcpsetkacounter(Tcpctl*);
4269ef1f84bSDavid du Colombier static void tcpsetscale(Conv*, Tcpctl*, ushort, ushort);
4279ef1f84bSDavid du Colombier static void tcpsettimer(Tcpctl*);
4289ef1f84bSDavid du Colombier static void tcpsndsyn(Conv*, Tcpctl*);
4299ef1f84bSDavid du Colombier static void tcpstart(Conv*, int);
4309ef1f84bSDavid du Colombier static void tcpsynackrtt(Conv*);
4319ef1f84bSDavid du Colombier static void tcptimeout(void*);
4329ef1f84bSDavid du Colombier static int tcptrim(Tcpctl*, Tcp*, Block**, ushort*);
4339ef1f84bSDavid du Colombier
4349ef1f84bSDavid du Colombier static void
tcpsetstate(Conv * s,uchar newstate)4359ef1f84bSDavid du Colombier tcpsetstate(Conv *s, uchar newstate)
4369ef1f84bSDavid du Colombier {
4379ef1f84bSDavid du Colombier Tcpctl *tcb;
4389ef1f84bSDavid du Colombier uchar oldstate;
4399ef1f84bSDavid du Colombier Tcppriv *tpriv;
4409ef1f84bSDavid du Colombier
4419ef1f84bSDavid du Colombier tpriv = s->p->priv;
4429ef1f84bSDavid du Colombier
4439ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
4449ef1f84bSDavid du Colombier
4459ef1f84bSDavid du Colombier oldstate = tcb->state;
4469ef1f84bSDavid du Colombier if(oldstate == newstate)
4479ef1f84bSDavid du Colombier return;
4489ef1f84bSDavid du Colombier
4499ef1f84bSDavid du Colombier if(oldstate == Established)
4509ef1f84bSDavid du Colombier tpriv->stats[CurrEstab]--;
4519ef1f84bSDavid du Colombier if(newstate == Established)
4529ef1f84bSDavid du Colombier tpriv->stats[CurrEstab]++;
4539ef1f84bSDavid du Colombier
4549ef1f84bSDavid du Colombier switch(newstate) {
4559ef1f84bSDavid du Colombier case Closed:
4569ef1f84bSDavid du Colombier qclose(s->rq);
4579ef1f84bSDavid du Colombier qclose(s->wq);
4589ef1f84bSDavid du Colombier qclose(s->eq);
4599ef1f84bSDavid du Colombier break;
4609ef1f84bSDavid du Colombier
4619ef1f84bSDavid du Colombier case Close_wait: /* Remote closes */
4629ef1f84bSDavid du Colombier qhangup(s->rq, nil);
4639ef1f84bSDavid du Colombier break;
4649ef1f84bSDavid du Colombier }
4659ef1f84bSDavid du Colombier
4669ef1f84bSDavid du Colombier tcb->state = newstate;
4679ef1f84bSDavid du Colombier
4689ef1f84bSDavid du Colombier if(oldstate == Syn_sent && newstate != Closed)
4699ef1f84bSDavid du Colombier Fsconnected(s, nil);
4709ef1f84bSDavid du Colombier }
4719ef1f84bSDavid du Colombier
4729ef1f84bSDavid du Colombier static char*
tcpconnect(Conv * c,char ** argv,int argc)4739ef1f84bSDavid du Colombier tcpconnect(Conv *c, char **argv, int argc)
4749ef1f84bSDavid du Colombier {
4759ef1f84bSDavid du Colombier char *e;
4769ef1f84bSDavid du Colombier Tcpctl *tcb;
4779ef1f84bSDavid du Colombier
4789ef1f84bSDavid du Colombier tcb = (Tcpctl*)(c->ptcl);
4799ef1f84bSDavid du Colombier if(tcb->state != Closed)
4809ef1f84bSDavid du Colombier return Econinuse;
4819ef1f84bSDavid du Colombier
4829ef1f84bSDavid du Colombier e = Fsstdconnect(c, argv, argc);
4839ef1f84bSDavid du Colombier if(e != nil)
4849ef1f84bSDavid du Colombier return e;
4859ef1f84bSDavid du Colombier tcpstart(c, TCP_CONNECT);
4869ef1f84bSDavid du Colombier
4879ef1f84bSDavid du Colombier return nil;
4889ef1f84bSDavid du Colombier }
4899ef1f84bSDavid du Colombier
4909ef1f84bSDavid du Colombier static int
tcpstate(Conv * c,char * state,int n)4919ef1f84bSDavid du Colombier tcpstate(Conv *c, char *state, int n)
4929ef1f84bSDavid du Colombier {
4939ef1f84bSDavid du Colombier Tcpctl *s;
4949ef1f84bSDavid du Colombier
4959ef1f84bSDavid du Colombier s = (Tcpctl*)(c->ptcl);
4969ef1f84bSDavid du Colombier
4979ef1f84bSDavid du Colombier return snprint(state, n,
4989ef1f84bSDavid du Colombier "%s qin %d qout %d rq %d.%d srtt %d mdev %d sst %lud cwin %lud "
4999ef1f84bSDavid du Colombier "swin %lud>>%d rwin %lud>>%d qscale %d timer.start %d "
5009ef1f84bSDavid du Colombier "timer.count %d rerecv %d katimer.start %d katimer.count %d\n",
5019ef1f84bSDavid du Colombier tcpstates[s->state],
5029ef1f84bSDavid du Colombier c->rq ? qlen(c->rq) : 0,
5039ef1f84bSDavid du Colombier c->wq ? qlen(c->wq) : 0,
5049ef1f84bSDavid du Colombier s->nreseq, s->reseqlen,
5059ef1f84bSDavid du Colombier s->srtt, s->mdev, s->ssthresh,
5069ef1f84bSDavid du Colombier s->cwind, s->snd.wnd, s->rcv.scale, s->rcv.wnd, s->snd.scale,
5079ef1f84bSDavid du Colombier s->qscale,
5089ef1f84bSDavid du Colombier s->timer.start, s->timer.count, s->rerecv,
5099ef1f84bSDavid du Colombier s->katimer.start, s->katimer.count);
5109ef1f84bSDavid du Colombier }
5119ef1f84bSDavid du Colombier
5129ef1f84bSDavid du Colombier static int
tcpinuse(Conv * c)5139ef1f84bSDavid du Colombier tcpinuse(Conv *c)
5149ef1f84bSDavid du Colombier {
5159ef1f84bSDavid du Colombier Tcpctl *s;
5169ef1f84bSDavid du Colombier
5179ef1f84bSDavid du Colombier s = (Tcpctl*)(c->ptcl);
5189ef1f84bSDavid du Colombier return s->state != Closed;
5199ef1f84bSDavid du Colombier }
5209ef1f84bSDavid du Colombier
5219ef1f84bSDavid du Colombier static char*
tcpannounce(Conv * c,char ** argv,int argc)5229ef1f84bSDavid du Colombier tcpannounce(Conv *c, char **argv, int argc)
5239ef1f84bSDavid du Colombier {
5249ef1f84bSDavid du Colombier char *e;
5259ef1f84bSDavid du Colombier Tcpctl *tcb;
5269ef1f84bSDavid du Colombier
5279ef1f84bSDavid du Colombier tcb = (Tcpctl*)(c->ptcl);
5289ef1f84bSDavid du Colombier if(tcb->state != Closed)
5299ef1f84bSDavid du Colombier return Econinuse;
5309ef1f84bSDavid du Colombier
5319ef1f84bSDavid du Colombier e = Fsstdannounce(c, argv, argc);
5329ef1f84bSDavid du Colombier if(e != nil)
5339ef1f84bSDavid du Colombier return e;
5349ef1f84bSDavid du Colombier tcpstart(c, TCP_LISTEN);
5359ef1f84bSDavid du Colombier Fsconnected(c, nil);
5369ef1f84bSDavid du Colombier
5379ef1f84bSDavid du Colombier return nil;
5389ef1f84bSDavid du Colombier }
5399ef1f84bSDavid du Colombier
54040398a43SDavid du Colombier static void
tcpclosestate(Conv * c,Tcpctl * tcb,int state)54140398a43SDavid du Colombier tcpclosestate(Conv *c, Tcpctl *tcb, int state)
54240398a43SDavid du Colombier {
54340398a43SDavid du Colombier tcb->flgcnt++;
54440398a43SDavid du Colombier tcb->snd.nxt++;
54540398a43SDavid du Colombier tcpsetstate(c, state);
54640398a43SDavid du Colombier tcpoutput(c);
54740398a43SDavid du Colombier }
54840398a43SDavid du Colombier
54940398a43SDavid du Colombier /* close the output half of a tcp connection */
55040398a43SDavid du Colombier static char *
tcpxmitclose(Conv * c)55140398a43SDavid du Colombier tcpxmitclose(Conv *c)
55240398a43SDavid du Colombier {
55340398a43SDavid du Colombier Tcpctl *tcb;
55440398a43SDavid du Colombier
55540398a43SDavid du Colombier qhangup(c->wq, nil);
55640398a43SDavid du Colombier
55740398a43SDavid du Colombier tcb = (Tcpctl*)c->ptcl;
55840398a43SDavid du Colombier switch(tcb->state) {
55940398a43SDavid du Colombier case Listen:
56040398a43SDavid du Colombier /*
56140398a43SDavid du Colombier * reset any incoming calls to this listener
56240398a43SDavid du Colombier */
56340398a43SDavid du Colombier Fsconnected(c, "Hangup");
56440398a43SDavid du Colombier /* fall through */
56540398a43SDavid du Colombier case Closed:
56640398a43SDavid du Colombier case Syn_sent:
56740398a43SDavid du Colombier localclose(c, nil);
56840398a43SDavid du Colombier break;
56940398a43SDavid du Colombier case Syn_received:
57040398a43SDavid du Colombier case Established:
57140398a43SDavid du Colombier case Close_wait:
57240398a43SDavid du Colombier tcpclosestate(c, tcb, tcb->state);
57340398a43SDavid du Colombier break;
57440398a43SDavid du Colombier }
57540398a43SDavid du Colombier return nil;
57640398a43SDavid du Colombier }
57740398a43SDavid du Colombier
5789ef1f84bSDavid du Colombier /*
5799ef1f84bSDavid du Colombier * tcpclose is always called with the q locked
5809ef1f84bSDavid du Colombier */
5819ef1f84bSDavid du Colombier static void
tcpclose(Conv * c)5829ef1f84bSDavid du Colombier tcpclose(Conv *c)
5839ef1f84bSDavid du Colombier {
5849ef1f84bSDavid du Colombier Tcpctl *tcb;
5859ef1f84bSDavid du Colombier
5869ef1f84bSDavid du Colombier tcb = (Tcpctl*)c->ptcl;
5879ef1f84bSDavid du Colombier
5889ef1f84bSDavid du Colombier qhangup(c->rq, nil);
5899ef1f84bSDavid du Colombier qhangup(c->wq, nil);
5909ef1f84bSDavid du Colombier qhangup(c->eq, nil);
5919ef1f84bSDavid du Colombier qflush(c->rq);
5929ef1f84bSDavid du Colombier
5939ef1f84bSDavid du Colombier switch(tcb->state) {
5949ef1f84bSDavid du Colombier case Listen:
5959ef1f84bSDavid du Colombier /*
5969ef1f84bSDavid du Colombier * reset any incoming calls to this listener
5979ef1f84bSDavid du Colombier */
5989ef1f84bSDavid du Colombier Fsconnected(c, "Hangup");
59940398a43SDavid du Colombier /* fall through */
6009ef1f84bSDavid du Colombier case Closed:
6019ef1f84bSDavid du Colombier case Syn_sent:
6029ef1f84bSDavid du Colombier localclose(c, nil);
6039ef1f84bSDavid du Colombier break;
6049ef1f84bSDavid du Colombier case Syn_received:
6059ef1f84bSDavid du Colombier case Established:
60640398a43SDavid du Colombier tcpclosestate(c, tcb, Finwait1);
6079ef1f84bSDavid du Colombier break;
6089ef1f84bSDavid du Colombier case Close_wait:
60940398a43SDavid du Colombier tcpclosestate(c, tcb, Last_ack);
6109ef1f84bSDavid du Colombier break;
6119ef1f84bSDavid du Colombier }
6129ef1f84bSDavid du Colombier }
6139ef1f84bSDavid du Colombier
6149ef1f84bSDavid du Colombier static void
tcpkick(void * x)6159ef1f84bSDavid du Colombier tcpkick(void *x)
6169ef1f84bSDavid du Colombier {
6179ef1f84bSDavid du Colombier Conv *s = x;
6189ef1f84bSDavid du Colombier Tcpctl *tcb;
6199ef1f84bSDavid du Colombier
6209ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
6219ef1f84bSDavid du Colombier
6229ef1f84bSDavid du Colombier if(waserror()){
6239ef1f84bSDavid du Colombier qunlock(s);
6249ef1f84bSDavid du Colombier nexterror();
6259ef1f84bSDavid du Colombier }
6269ef1f84bSDavid du Colombier qlock(s);
6279ef1f84bSDavid du Colombier
6289ef1f84bSDavid du Colombier switch(tcb->state) {
6299ef1f84bSDavid du Colombier case Syn_sent:
6309ef1f84bSDavid du Colombier case Syn_received:
6319ef1f84bSDavid du Colombier case Established:
6329ef1f84bSDavid du Colombier case Close_wait:
6339ef1f84bSDavid du Colombier /*
6349ef1f84bSDavid du Colombier * Push data
6359ef1f84bSDavid du Colombier */
6369ef1f84bSDavid du Colombier tcpoutput(s);
6379ef1f84bSDavid du Colombier break;
6389ef1f84bSDavid du Colombier default:
6399ef1f84bSDavid du Colombier localclose(s, "Hangup");
6409ef1f84bSDavid du Colombier break;
6419ef1f84bSDavid du Colombier }
6429ef1f84bSDavid du Colombier
6439ef1f84bSDavid du Colombier qunlock(s);
6449ef1f84bSDavid du Colombier poperror();
6459ef1f84bSDavid du Colombier }
6469ef1f84bSDavid du Colombier
6479ef1f84bSDavid du Colombier static int seq_lt(ulong, ulong);
6489ef1f84bSDavid du Colombier
6499ef1f84bSDavid du Colombier static void
tcprcvwin(Conv * s)6509ef1f84bSDavid du Colombier tcprcvwin(Conv *s) /* Call with tcb locked */
6519ef1f84bSDavid du Colombier {
6529ef1f84bSDavid du Colombier int w;
6539ef1f84bSDavid du Colombier Tcpctl *tcb;
6549ef1f84bSDavid du Colombier
6559ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
6569ef1f84bSDavid du Colombier w = tcb->window - qlen(s->rq);
6579ef1f84bSDavid du Colombier if(w < 0)
6589ef1f84bSDavid du Colombier w = 0;
6599ef1f84bSDavid du Colombier /* RFC 1122 § 4.2.2.17 do not move right edge of window left */
6609ef1f84bSDavid du Colombier if(seq_lt(tcb->rcv.nxt + w, tcb->rcv.wptr))
6619ef1f84bSDavid du Colombier w = tcb->rcv.wptr - tcb->rcv.nxt;
6629ef1f84bSDavid du Colombier if(w != tcb->rcv.wnd)
6639ef1f84bSDavid du Colombier if(w>>tcb->rcv.scale == 0 || tcb->window > 4*tcb->mss && w < tcb->mss/4){
6649ef1f84bSDavid du Colombier tcb->rcv.blocked = 1;
6659ef1f84bSDavid du Colombier netlog(s->p->f, Logtcp, "tcprcvwin: window %lud qlen %d ws %ud lport %d\n",
6669ef1f84bSDavid du Colombier tcb->window, qlen(s->rq), tcb->rcv.scale, s->lport);
6679ef1f84bSDavid du Colombier }
6689ef1f84bSDavid du Colombier tcb->rcv.wnd = w;
6699ef1f84bSDavid du Colombier tcb->rcv.wptr = tcb->rcv.nxt + w;
6709ef1f84bSDavid du Colombier }
6719ef1f84bSDavid du Colombier
6729ef1f84bSDavid du Colombier static void
tcpacktimer(void * v)6739ef1f84bSDavid du Colombier tcpacktimer(void *v)
6749ef1f84bSDavid du Colombier {
6759ef1f84bSDavid du Colombier Tcpctl *tcb;
6769ef1f84bSDavid du Colombier Conv *s;
6779ef1f84bSDavid du Colombier
6789ef1f84bSDavid du Colombier s = v;
6799ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
6809ef1f84bSDavid du Colombier
6819ef1f84bSDavid du Colombier if(waserror()){
6829ef1f84bSDavid du Colombier qunlock(s);
6839ef1f84bSDavid du Colombier nexterror();
6849ef1f84bSDavid du Colombier }
6859ef1f84bSDavid du Colombier qlock(s);
6869ef1f84bSDavid du Colombier if(tcb->state != Closed){
6879ef1f84bSDavid du Colombier tcb->flags |= FORCE;
6889ef1f84bSDavid du Colombier tcpoutput(s);
6899ef1f84bSDavid du Colombier }
6909ef1f84bSDavid du Colombier qunlock(s);
6919ef1f84bSDavid du Colombier poperror();
6929ef1f84bSDavid du Colombier }
6939ef1f84bSDavid du Colombier
6949ef1f84bSDavid du Colombier static void
tcpcongestion(Tcpctl * tcb)6959ef1f84bSDavid du Colombier tcpcongestion(Tcpctl *tcb)
6969ef1f84bSDavid du Colombier {
6979ef1f84bSDavid du Colombier ulong inflight;
6989ef1f84bSDavid du Colombier
6999ef1f84bSDavid du Colombier inflight = tcb->snd.nxt - tcb->snd.una;
7009ef1f84bSDavid du Colombier if(inflight > tcb->cwind)
7019ef1f84bSDavid du Colombier inflight = tcb->cwind;
7029ef1f84bSDavid du Colombier tcb->ssthresh = inflight / 2;
7039ef1f84bSDavid du Colombier if(tcb->ssthresh < 2*tcb->mss)
7049ef1f84bSDavid du Colombier tcb->ssthresh = 2*tcb->mss;
7059ef1f84bSDavid du Colombier }
7069ef1f84bSDavid du Colombier
7079ef1f84bSDavid du Colombier enum {
7089ef1f84bSDavid du Colombier L = 2, /* aggressive slow start; legal values ∈ (1.0, 2.0) */
7099ef1f84bSDavid du Colombier };
7109ef1f84bSDavid du Colombier
7119ef1f84bSDavid du Colombier static void
tcpabcincr(Tcpctl * tcb,uint acked)7129ef1f84bSDavid du Colombier tcpabcincr(Tcpctl *tcb, uint acked)
7139ef1f84bSDavid du Colombier {
7149ef1f84bSDavid du Colombier uint limit;
7159ef1f84bSDavid du Colombier
7169ef1f84bSDavid du Colombier tcb->abcbytes += acked;
7179ef1f84bSDavid du Colombier if(tcb->cwind < tcb->ssthresh){
7189ef1f84bSDavid du Colombier /* slow start */
7199ef1f84bSDavid du Colombier if(tcb->snd.rto)
7209ef1f84bSDavid du Colombier limit = tcb->mss;
7219ef1f84bSDavid du Colombier else
7229ef1f84bSDavid du Colombier limit = L*tcb->mss;
7239ef1f84bSDavid du Colombier tcb->cwind += MIN(tcb->abcbytes, limit);
7249ef1f84bSDavid du Colombier tcb->abcbytes = 0;
7259ef1f84bSDavid du Colombier } else {
7269ef1f84bSDavid du Colombier tcb->snd.rto = 0;
7279ef1f84bSDavid du Colombier /* avoidance */
7289ef1f84bSDavid du Colombier if(tcb->abcbytes >= tcb->cwind){
7299ef1f84bSDavid du Colombier tcb->abcbytes -= tcb->cwind;
7309ef1f84bSDavid du Colombier tcb->cwind += tcb->mss;
7319ef1f84bSDavid du Colombier }
7329ef1f84bSDavid du Colombier }
7339ef1f84bSDavid du Colombier }
7349ef1f84bSDavid du Colombier
7359ef1f84bSDavid du Colombier static void
tcpcreate(Conv * c)7369ef1f84bSDavid du Colombier tcpcreate(Conv *c)
7379ef1f84bSDavid du Colombier {
7389ef1f84bSDavid du Colombier c->rq = qopen(QMAX, Qcoalesce, tcpacktimer, c);
7399ef1f84bSDavid du Colombier c->wq = qopen(QMAX, Qkick, tcpkick, c);
7409ef1f84bSDavid du Colombier }
7419ef1f84bSDavid du Colombier
7429ef1f84bSDavid du Colombier static void
timerstate(Tcppriv * priv,Tcptimer * t,int newstate)7439ef1f84bSDavid du Colombier timerstate(Tcppriv *priv, Tcptimer *t, int newstate)
7449ef1f84bSDavid du Colombier {
7459ef1f84bSDavid du Colombier if(newstate != TcptimerON){
7469ef1f84bSDavid du Colombier if(t->state == TcptimerON){
7479ef1f84bSDavid du Colombier /* unchain */
7489ef1f84bSDavid du Colombier if(priv->timers == t){
7499ef1f84bSDavid du Colombier priv->timers = t->next;
7509ef1f84bSDavid du Colombier if(t->prev != nil)
7519ef1f84bSDavid du Colombier panic("timerstate1");
7529ef1f84bSDavid du Colombier }
7539ef1f84bSDavid du Colombier if(t->next)
7549ef1f84bSDavid du Colombier t->next->prev = t->prev;
7559ef1f84bSDavid du Colombier if(t->prev)
7569ef1f84bSDavid du Colombier t->prev->next = t->next;
7579ef1f84bSDavid du Colombier t->next = t->prev = nil;
7589ef1f84bSDavid du Colombier }
7599ef1f84bSDavid du Colombier } else {
7609ef1f84bSDavid du Colombier if(t->state != TcptimerON){
7619ef1f84bSDavid du Colombier /* chain */
7629ef1f84bSDavid du Colombier if(t->prev != nil || t->next != nil)
7639ef1f84bSDavid du Colombier panic("timerstate2");
7649ef1f84bSDavid du Colombier t->prev = nil;
7659ef1f84bSDavid du Colombier t->next = priv->timers;
7669ef1f84bSDavid du Colombier if(t->next)
7679ef1f84bSDavid du Colombier t->next->prev = t;
7689ef1f84bSDavid du Colombier priv->timers = t;
7699ef1f84bSDavid du Colombier }
7709ef1f84bSDavid du Colombier }
7719ef1f84bSDavid du Colombier t->state = newstate;
7729ef1f84bSDavid du Colombier }
7739ef1f84bSDavid du Colombier
7749ef1f84bSDavid du Colombier static void
tcpackproc(void * a)7759ef1f84bSDavid du Colombier tcpackproc(void *a)
7769ef1f84bSDavid du Colombier {
7779ef1f84bSDavid du Colombier Tcptimer *t, *tp, *timeo;
7789ef1f84bSDavid du Colombier Proto *tcp;
7799ef1f84bSDavid du Colombier Tcppriv *priv;
7809ef1f84bSDavid du Colombier int loop;
7819ef1f84bSDavid du Colombier
7829ef1f84bSDavid du Colombier tcp = a;
7839ef1f84bSDavid du Colombier priv = tcp->priv;
7849ef1f84bSDavid du Colombier
7859ef1f84bSDavid du Colombier for(;;) {
7869ef1f84bSDavid du Colombier tsleep(&up->sleep, return0, 0, MSPTICK);
7879ef1f84bSDavid du Colombier
7889ef1f84bSDavid du Colombier qlock(&priv->tl);
7899ef1f84bSDavid du Colombier timeo = nil;
7909ef1f84bSDavid du Colombier loop = 0;
7919ef1f84bSDavid du Colombier for(t = priv->timers; t != nil; t = tp) {
7929ef1f84bSDavid du Colombier if(loop++ > 10000)
7939ef1f84bSDavid du Colombier panic("tcpackproc1");
7949ef1f84bSDavid du Colombier tp = t->next;
7959ef1f84bSDavid du Colombier if(t->state == TcptimerON) {
7969ef1f84bSDavid du Colombier t->count--;
7979ef1f84bSDavid du Colombier if(t->count == 0) {
7989ef1f84bSDavid du Colombier timerstate(priv, t, TcptimerDONE);
7999ef1f84bSDavid du Colombier t->readynext = timeo;
8009ef1f84bSDavid du Colombier timeo = t;
8019ef1f84bSDavid du Colombier }
8029ef1f84bSDavid du Colombier }
8039ef1f84bSDavid du Colombier }
8049ef1f84bSDavid du Colombier qunlock(&priv->tl);
8059ef1f84bSDavid du Colombier
8069ef1f84bSDavid du Colombier loop = 0;
8079ef1f84bSDavid du Colombier for(t = timeo; t != nil; t = t->readynext) {
8089ef1f84bSDavid du Colombier if(loop++ > 10000)
8099ef1f84bSDavid du Colombier panic("tcpackproc2");
8109ef1f84bSDavid du Colombier if(t->state == TcptimerDONE && t->func != nil && !waserror()){
8119ef1f84bSDavid du Colombier (*t->func)(t->arg);
8129ef1f84bSDavid du Colombier poperror();
8139ef1f84bSDavid du Colombier }
8149ef1f84bSDavid du Colombier }
8159ef1f84bSDavid du Colombier
8169ef1f84bSDavid du Colombier limborexmit(tcp);
8179ef1f84bSDavid du Colombier }
8189ef1f84bSDavid du Colombier }
8199ef1f84bSDavid du Colombier
8209ef1f84bSDavid du Colombier static void
tcpgo(Tcppriv * priv,Tcptimer * t)8219ef1f84bSDavid du Colombier tcpgo(Tcppriv *priv, Tcptimer *t)
8229ef1f84bSDavid du Colombier {
8239ef1f84bSDavid du Colombier if(t == nil || t->start == 0)
8249ef1f84bSDavid du Colombier return;
8259ef1f84bSDavid du Colombier
8269ef1f84bSDavid du Colombier qlock(&priv->tl);
8279ef1f84bSDavid du Colombier t->count = t->start;
8289ef1f84bSDavid du Colombier timerstate(priv, t, TcptimerON);
8299ef1f84bSDavid du Colombier qunlock(&priv->tl);
8309ef1f84bSDavid du Colombier }
8319ef1f84bSDavid du Colombier
8329ef1f84bSDavid du Colombier static void
tcphalt(Tcppriv * priv,Tcptimer * t)8339ef1f84bSDavid du Colombier tcphalt(Tcppriv *priv, Tcptimer *t)
8349ef1f84bSDavid du Colombier {
8359ef1f84bSDavid du Colombier if(t == nil)
8369ef1f84bSDavid du Colombier return;
8379ef1f84bSDavid du Colombier
8389ef1f84bSDavid du Colombier qlock(&priv->tl);
8399ef1f84bSDavid du Colombier timerstate(priv, t, TcptimerOFF);
8409ef1f84bSDavid du Colombier qunlock(&priv->tl);
8419ef1f84bSDavid du Colombier }
8429ef1f84bSDavid du Colombier
8439ef1f84bSDavid du Colombier static int
backoff(int n)8449ef1f84bSDavid du Colombier backoff(int n)
8459ef1f84bSDavid du Colombier {
8469ef1f84bSDavid du Colombier return 1 << n;
8479ef1f84bSDavid du Colombier }
8489ef1f84bSDavid du Colombier
8499ef1f84bSDavid du Colombier static void
localclose(Conv * s,char * reason)8509ef1f84bSDavid du Colombier localclose(Conv *s, char *reason) /* called with tcb locked */
8519ef1f84bSDavid du Colombier {
8529ef1f84bSDavid du Colombier Tcpctl *tcb;
8539ef1f84bSDavid du Colombier Tcppriv *tpriv;
8549ef1f84bSDavid du Colombier
8559ef1f84bSDavid du Colombier tpriv = s->p->priv;
8569ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
8579ef1f84bSDavid du Colombier
8589ef1f84bSDavid du Colombier iphtrem(&tpriv->ht, s);
8599ef1f84bSDavid du Colombier
8609ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->timer);
8619ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
8629ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
8639ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->katimer);
8649ef1f84bSDavid du Colombier
8659ef1f84bSDavid du Colombier /* Flush reassembly queue; nothing more can arrive */
8669ef1f84bSDavid du Colombier dumpreseq(tcb);
8679ef1f84bSDavid du Colombier
8689ef1f84bSDavid du Colombier if(tcb->state == Syn_sent)
8699ef1f84bSDavid du Colombier Fsconnected(s, reason);
8709ef1f84bSDavid du Colombier if(s->state == Announced)
8719ef1f84bSDavid du Colombier wakeup(&s->listenr);
8729ef1f84bSDavid du Colombier
8739ef1f84bSDavid du Colombier qhangup(s->rq, reason);
8749ef1f84bSDavid du Colombier qhangup(s->wq, reason);
8759ef1f84bSDavid du Colombier
8769ef1f84bSDavid du Colombier tcpsetstate(s, Closed);
8779ef1f84bSDavid du Colombier }
8789ef1f84bSDavid du Colombier
8799ef1f84bSDavid du Colombier /* mtu (- TCP + IP hdr len) of 1st hop */
8809ef1f84bSDavid du Colombier static int
tcpmtu(Proto * tcp,uchar * addr,int version,uint * scale)8819ef1f84bSDavid du Colombier tcpmtu(Proto *tcp, uchar *addr, int version, uint *scale)
8829ef1f84bSDavid du Colombier {
8839ef1f84bSDavid du Colombier Ipifc *ifc;
8849ef1f84bSDavid du Colombier int mtu;
8859ef1f84bSDavid du Colombier
8869ef1f84bSDavid du Colombier ifc = findipifc(tcp->f, addr, 0);
8879ef1f84bSDavid du Colombier switch(version){
8889ef1f84bSDavid du Colombier default:
8899ef1f84bSDavid du Colombier case V4:
8909ef1f84bSDavid du Colombier mtu = DEF_MSS;
8919ef1f84bSDavid du Colombier if(ifc != nil)
8929ef1f84bSDavid du Colombier mtu = ifc->maxtu - ifc->medium->hsize - (TCP4_PKT + TCP4_HDRSIZE);
8939ef1f84bSDavid du Colombier break;
8949ef1f84bSDavid du Colombier case V6:
8959ef1f84bSDavid du Colombier mtu = DEF_MSS6;
8969ef1f84bSDavid du Colombier if(ifc != nil)
8979ef1f84bSDavid du Colombier mtu = ifc->maxtu - ifc->medium->hsize - (TCP6_PKT + TCP6_HDRSIZE);
8989ef1f84bSDavid du Colombier break;
8999ef1f84bSDavid du Colombier }
9009ef1f84bSDavid du Colombier /*
9019ef1f84bSDavid du Colombier * set the ws. it doesn't commit us to anything.
9029ef1f84bSDavid du Colombier * ws is the ultimate limit to the bandwidth-delay product.
9039ef1f84bSDavid du Colombier */
9049ef1f84bSDavid du Colombier *scale = Defadvscale;
9059ef1f84bSDavid du Colombier
9069ef1f84bSDavid du Colombier return mtu;
9079ef1f84bSDavid du Colombier }
9089ef1f84bSDavid du Colombier
9099ef1f84bSDavid du Colombier static void
inittcpctl(Conv * s,int mode)9109ef1f84bSDavid du Colombier inittcpctl(Conv *s, int mode)
9119ef1f84bSDavid du Colombier {
9129ef1f84bSDavid du Colombier Tcpctl *tcb;
9139ef1f84bSDavid du Colombier Tcp4hdr* h4;
9149ef1f84bSDavid du Colombier Tcp6hdr* h6;
9159ef1f84bSDavid du Colombier Tcppriv *tpriv;
9169ef1f84bSDavid du Colombier int mss;
9179ef1f84bSDavid du Colombier
9189ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
9199ef1f84bSDavid du Colombier
9209ef1f84bSDavid du Colombier memset(tcb, 0, sizeof(Tcpctl));
9219ef1f84bSDavid du Colombier
9229ef1f84bSDavid du Colombier tcb->ssthresh = QMAX; /* reset by tcpsetscale() */
9239ef1f84bSDavid du Colombier tcb->srtt = tcp_irtt<<LOGAGAIN;
9249ef1f84bSDavid du Colombier tcb->mdev = 0;
9259ef1f84bSDavid du Colombier
9269ef1f84bSDavid du Colombier /* setup timers */
9279ef1f84bSDavid du Colombier tcb->timer.start = tcp_irtt / MSPTICK;
9289ef1f84bSDavid du Colombier tcb->timer.func = tcptimeout;
9299ef1f84bSDavid du Colombier tcb->timer.arg = s;
9309ef1f84bSDavid du Colombier tcb->rtt_timer.start = MAX_TIME;
9319ef1f84bSDavid du Colombier tcb->acktimer.start = TCP_ACK / MSPTICK;
9329ef1f84bSDavid du Colombier tcb->acktimer.func = tcpacktimer;
9339ef1f84bSDavid du Colombier tcb->acktimer.arg = s;
9349ef1f84bSDavid du Colombier tcb->katimer.start = DEF_KAT / MSPTICK;
9359ef1f84bSDavid du Colombier tcb->katimer.func = tcpkeepalive;
9369ef1f84bSDavid du Colombier tcb->katimer.arg = s;
9379ef1f84bSDavid du Colombier
9389ef1f84bSDavid du Colombier mss = DEF_MSS;
9399ef1f84bSDavid du Colombier
9409ef1f84bSDavid du Colombier /* create a prototype(pseudo) header */
9419ef1f84bSDavid du Colombier if(mode != TCP_LISTEN){
9429ef1f84bSDavid du Colombier if(ipcmp(s->laddr, IPnoaddr) == 0)
9439ef1f84bSDavid du Colombier findlocalip(s->p->f, s->laddr, s->raddr);
9449ef1f84bSDavid du Colombier
9459ef1f84bSDavid du Colombier switch(s->ipversion){
9469ef1f84bSDavid du Colombier case V4:
9479ef1f84bSDavid du Colombier h4 = &tcb->protohdr.tcp4hdr;
9489ef1f84bSDavid du Colombier memset(h4, 0, sizeof(*h4));
9499ef1f84bSDavid du Colombier h4->proto = IP_TCPPROTO;
9509ef1f84bSDavid du Colombier hnputs(h4->tcpsport, s->lport);
9519ef1f84bSDavid du Colombier hnputs(h4->tcpdport, s->rport);
9529ef1f84bSDavid du Colombier v6tov4(h4->tcpsrc, s->laddr);
9539ef1f84bSDavid du Colombier v6tov4(h4->tcpdst, s->raddr);
9549ef1f84bSDavid du Colombier break;
9559ef1f84bSDavid du Colombier case V6:
9569ef1f84bSDavid du Colombier h6 = &tcb->protohdr.tcp6hdr;
9579ef1f84bSDavid du Colombier memset(h6, 0, sizeof(*h6));
9589ef1f84bSDavid du Colombier h6->proto = IP_TCPPROTO;
9599ef1f84bSDavid du Colombier hnputs(h6->tcpsport, s->lport);
9609ef1f84bSDavid du Colombier hnputs(h6->tcpdport, s->rport);
9619ef1f84bSDavid du Colombier ipmove(h6->tcpsrc, s->laddr);
9629ef1f84bSDavid du Colombier ipmove(h6->tcpdst, s->raddr);
9639ef1f84bSDavid du Colombier mss = DEF_MSS6;
9649ef1f84bSDavid du Colombier break;
9659ef1f84bSDavid du Colombier default:
9669ef1f84bSDavid du Colombier panic("inittcpctl: version %d", s->ipversion);
9679ef1f84bSDavid du Colombier }
9689ef1f84bSDavid du Colombier }
9699ef1f84bSDavid du Colombier
9709ef1f84bSDavid du Colombier tcb->mss = tcb->cwind = mss;
9719ef1f84bSDavid du Colombier tcb->abcbytes = 0;
9729ef1f84bSDavid du Colombier tpriv = s->p->priv;
9739ef1f84bSDavid du Colombier tpriv->stats[Mss] = tcb->mss;
9749ef1f84bSDavid du Colombier
9759ef1f84bSDavid du Colombier /* default is no window scaling */
9769ef1f84bSDavid du Colombier tcpsetscale(s, tcb, 0, 0);
9779ef1f84bSDavid du Colombier }
9789ef1f84bSDavid du Colombier
9799ef1f84bSDavid du Colombier /*
9809ef1f84bSDavid du Colombier * called with s qlocked
9819ef1f84bSDavid du Colombier */
9829ef1f84bSDavid du Colombier static void
tcpstart(Conv * s,int mode)9839ef1f84bSDavid du Colombier tcpstart(Conv *s, int mode)
9849ef1f84bSDavid du Colombier {
9859ef1f84bSDavid du Colombier Tcpctl *tcb;
9869ef1f84bSDavid du Colombier Tcppriv *tpriv;
9879ef1f84bSDavid du Colombier char kpname[KNAMELEN];
9889ef1f84bSDavid du Colombier
9899ef1f84bSDavid du Colombier tpriv = s->p->priv;
9909ef1f84bSDavid du Colombier
9919ef1f84bSDavid du Colombier if(tpriv->ackprocstarted == 0){
9929ef1f84bSDavid du Colombier qlock(&tpriv->apl);
9939ef1f84bSDavid du Colombier if(tpriv->ackprocstarted == 0){
9949ef1f84bSDavid du Colombier snprint(kpname, sizeof kpname, "#I%dtcpack", s->p->f->dev);
9959ef1f84bSDavid du Colombier kproc(kpname, tcpackproc, s->p);
9969ef1f84bSDavid du Colombier tpriv->ackprocstarted = 1;
9979ef1f84bSDavid du Colombier }
9989ef1f84bSDavid du Colombier qunlock(&tpriv->apl);
9999ef1f84bSDavid du Colombier }
10009ef1f84bSDavid du Colombier
10019ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
10029ef1f84bSDavid du Colombier
10039ef1f84bSDavid du Colombier inittcpctl(s, mode);
10049ef1f84bSDavid du Colombier
10059ef1f84bSDavid du Colombier iphtadd(&tpriv->ht, s);
10069ef1f84bSDavid du Colombier switch(mode) {
10079ef1f84bSDavid du Colombier case TCP_LISTEN:
10089ef1f84bSDavid du Colombier tpriv->stats[PassiveOpens]++;
10099ef1f84bSDavid du Colombier tcb->flags |= CLONE;
10109ef1f84bSDavid du Colombier tcpsetstate(s, Listen);
10119ef1f84bSDavid du Colombier break;
10129ef1f84bSDavid du Colombier
10139ef1f84bSDavid du Colombier case TCP_CONNECT:
10149ef1f84bSDavid du Colombier tpriv->stats[ActiveOpens]++;
10159ef1f84bSDavid du Colombier tcb->flags |= ACTIVE;
10169ef1f84bSDavid du Colombier tcpsndsyn(s, tcb);
10179ef1f84bSDavid du Colombier tcpsetstate(s, Syn_sent);
10189ef1f84bSDavid du Colombier tcpoutput(s);
10199ef1f84bSDavid du Colombier break;
10209ef1f84bSDavid du Colombier }
10219ef1f84bSDavid du Colombier }
10229ef1f84bSDavid du Colombier
10239ef1f84bSDavid du Colombier static char*
tcpflag(char * buf,char * e,ushort flag)10249ef1f84bSDavid du Colombier tcpflag(char *buf, char *e, ushort flag)
10259ef1f84bSDavid du Colombier {
10269ef1f84bSDavid du Colombier char *p;
10279ef1f84bSDavid du Colombier
10289ef1f84bSDavid du Colombier p = seprint(buf, e, "%d", flag>>10); /* Head len */
10299ef1f84bSDavid du Colombier if(flag & URG)
10309ef1f84bSDavid du Colombier p = seprint(p, e, " URG");
10319ef1f84bSDavid du Colombier if(flag & ACK)
10329ef1f84bSDavid du Colombier p = seprint(p, e, " ACK");
10339ef1f84bSDavid du Colombier if(flag & PSH)
10349ef1f84bSDavid du Colombier p = seprint(p, e, " PSH");
10359ef1f84bSDavid du Colombier if(flag & RST)
10369ef1f84bSDavid du Colombier p = seprint(p, e, " RST");
10379ef1f84bSDavid du Colombier if(flag & SYN)
10389ef1f84bSDavid du Colombier p = seprint(p, e, " SYN");
10399ef1f84bSDavid du Colombier if(flag & FIN)
10409ef1f84bSDavid du Colombier p = seprint(p, e, " FIN");
10419ef1f84bSDavid du Colombier USED(p);
10429ef1f84bSDavid du Colombier return buf;
10439ef1f84bSDavid du Colombier }
10449ef1f84bSDavid du Colombier
10459ef1f84bSDavid du Colombier static Block*
htontcp6(Tcp * tcph,Block * data,Tcp6hdr * ph,Tcpctl * tcb)10469ef1f84bSDavid du Colombier htontcp6(Tcp *tcph, Block *data, Tcp6hdr *ph, Tcpctl *tcb)
10479ef1f84bSDavid du Colombier {
10489ef1f84bSDavid du Colombier int dlen;
10499ef1f84bSDavid du Colombier Tcp6hdr *h;
10509ef1f84bSDavid du Colombier ushort csum;
10519ef1f84bSDavid du Colombier ushort hdrlen, optpad = 0;
10529ef1f84bSDavid du Colombier uchar *opt;
10539ef1f84bSDavid du Colombier
10549ef1f84bSDavid du Colombier hdrlen = TCP6_HDRSIZE;
10559ef1f84bSDavid du Colombier if(tcph->flags & SYN){
10569ef1f84bSDavid du Colombier if(tcph->mss)
10579ef1f84bSDavid du Colombier hdrlen += MSS_LENGTH;
10589ef1f84bSDavid du Colombier if(tcph->ws)
10599ef1f84bSDavid du Colombier hdrlen += WS_LENGTH;
10609ef1f84bSDavid du Colombier optpad = hdrlen & 3;
10619ef1f84bSDavid du Colombier if(optpad)
10629ef1f84bSDavid du Colombier optpad = 4 - optpad;
10639ef1f84bSDavid du Colombier hdrlen += optpad;
10649ef1f84bSDavid du Colombier }
10659ef1f84bSDavid du Colombier
10669ef1f84bSDavid du Colombier if(data) {
10679ef1f84bSDavid du Colombier dlen = blocklen(data);
10689ef1f84bSDavid du Colombier data = padblock(data, hdrlen + TCP6_PKT);
10699ef1f84bSDavid du Colombier if(data == nil)
10709ef1f84bSDavid du Colombier return nil;
10719ef1f84bSDavid du Colombier }
10729ef1f84bSDavid du Colombier else {
10739ef1f84bSDavid du Colombier dlen = 0;
10749ef1f84bSDavid du Colombier data = allocb(hdrlen + TCP6_PKT + 64); /* the 64 pad is to meet mintu's */
10759ef1f84bSDavid du Colombier if(data == nil)
10769ef1f84bSDavid du Colombier return nil;
10779ef1f84bSDavid du Colombier data->wp += hdrlen + TCP6_PKT;
10789ef1f84bSDavid du Colombier }
10799ef1f84bSDavid du Colombier
10809ef1f84bSDavid du Colombier /* copy in pseudo ip header plus port numbers */
10819ef1f84bSDavid du Colombier h = (Tcp6hdr *)(data->rp);
10829ef1f84bSDavid du Colombier memmove(h, ph, TCP6_TCBPHDRSZ);
10839ef1f84bSDavid du Colombier
10849ef1f84bSDavid du Colombier /* compose pseudo tcp header, do cksum calculation */
10859ef1f84bSDavid du Colombier hnputl(h->vcf, hdrlen + dlen);
10869ef1f84bSDavid du Colombier h->ploadlen[0] = h->ploadlen[1] = h->proto = 0;
10879ef1f84bSDavid du Colombier h->ttl = ph->proto;
10889ef1f84bSDavid du Colombier
10899ef1f84bSDavid du Colombier /* copy in variable bits */
10909ef1f84bSDavid du Colombier hnputl(h->tcpseq, tcph->seq);
10919ef1f84bSDavid du Colombier hnputl(h->tcpack, tcph->ack);
10929ef1f84bSDavid du Colombier hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
10939ef1f84bSDavid du Colombier hnputs(h->tcpwin, tcph->wnd>>(tcb != nil ? tcb->snd.scale : 0));
10949ef1f84bSDavid du Colombier hnputs(h->tcpurg, tcph->urg);
10959ef1f84bSDavid du Colombier
10969ef1f84bSDavid du Colombier if(tcph->flags & SYN){
10979ef1f84bSDavid du Colombier opt = h->tcpopt;
10989ef1f84bSDavid du Colombier if(tcph->mss != 0){
10999ef1f84bSDavid du Colombier *opt++ = MSSOPT;
11009ef1f84bSDavid du Colombier *opt++ = MSS_LENGTH;
11019ef1f84bSDavid du Colombier hnputs(opt, tcph->mss);
11029ef1f84bSDavid du Colombier opt += 2;
11039ef1f84bSDavid du Colombier }
11049ef1f84bSDavid du Colombier if(tcph->ws != 0){
11059ef1f84bSDavid du Colombier *opt++ = WSOPT;
11069ef1f84bSDavid du Colombier *opt++ = WS_LENGTH;
11079ef1f84bSDavid du Colombier *opt++ = tcph->ws;
11089ef1f84bSDavid du Colombier }
11099ef1f84bSDavid du Colombier while(optpad-- > 0)
11109ef1f84bSDavid du Colombier *opt++ = NOOPOPT;
11119ef1f84bSDavid du Colombier }
11129ef1f84bSDavid du Colombier
11139ef1f84bSDavid du Colombier if(tcb != nil && tcb->nochecksum){
11149ef1f84bSDavid du Colombier h->tcpcksum[0] = h->tcpcksum[1] = 0;
11159ef1f84bSDavid du Colombier } else {
11169ef1f84bSDavid du Colombier csum = ptclcsum(data, TCP6_IPLEN, hdrlen+dlen+TCP6_PHDRSIZE);
11179ef1f84bSDavid du Colombier hnputs(h->tcpcksum, csum);
11189ef1f84bSDavid du Colombier }
11199ef1f84bSDavid du Colombier
11209ef1f84bSDavid du Colombier /* move from pseudo header back to normal ip header */
11219ef1f84bSDavid du Colombier memset(h->vcf, 0, 4);
11229ef1f84bSDavid du Colombier h->vcf[0] = IP_VER6;
11239ef1f84bSDavid du Colombier hnputs(h->ploadlen, hdrlen+dlen);
11249ef1f84bSDavid du Colombier h->proto = ph->proto;
11259ef1f84bSDavid du Colombier
11269ef1f84bSDavid du Colombier return data;
11279ef1f84bSDavid du Colombier }
11289ef1f84bSDavid du Colombier
11299ef1f84bSDavid du Colombier static Block*
htontcp4(Tcp * tcph,Block * data,Tcp4hdr * ph,Tcpctl * tcb)11309ef1f84bSDavid du Colombier htontcp4(Tcp *tcph, Block *data, Tcp4hdr *ph, Tcpctl *tcb)
11319ef1f84bSDavid du Colombier {
11329ef1f84bSDavid du Colombier int dlen;
11339ef1f84bSDavid du Colombier Tcp4hdr *h;
11349ef1f84bSDavid du Colombier ushort csum;
11359ef1f84bSDavid du Colombier ushort hdrlen, optpad = 0;
11369ef1f84bSDavid du Colombier uchar *opt;
11379ef1f84bSDavid du Colombier
11389ef1f84bSDavid du Colombier hdrlen = TCP4_HDRSIZE;
11399ef1f84bSDavid du Colombier if(tcph->flags & SYN){
11409ef1f84bSDavid du Colombier if(tcph->mss)
11419ef1f84bSDavid du Colombier hdrlen += MSS_LENGTH;
11429ef1f84bSDavid du Colombier if(1)
11439ef1f84bSDavid du Colombier hdrlen += WS_LENGTH;
11449ef1f84bSDavid du Colombier optpad = hdrlen & 3;
11459ef1f84bSDavid du Colombier if(optpad)
11469ef1f84bSDavid du Colombier optpad = 4 - optpad;
11479ef1f84bSDavid du Colombier hdrlen += optpad;
11489ef1f84bSDavid du Colombier }
11499ef1f84bSDavid du Colombier
11509ef1f84bSDavid du Colombier if(data) {
11519ef1f84bSDavid du Colombier dlen = blocklen(data);
11529ef1f84bSDavid du Colombier data = padblock(data, hdrlen + TCP4_PKT);
11539ef1f84bSDavid du Colombier if(data == nil)
11549ef1f84bSDavid du Colombier return nil;
11559ef1f84bSDavid du Colombier }
11569ef1f84bSDavid du Colombier else {
11579ef1f84bSDavid du Colombier dlen = 0;
11589ef1f84bSDavid du Colombier data = allocb(hdrlen + TCP4_PKT + 64); /* the 64 pad is to meet mintu's */
11599ef1f84bSDavid du Colombier if(data == nil)
11609ef1f84bSDavid du Colombier return nil;
11619ef1f84bSDavid du Colombier data->wp += hdrlen + TCP4_PKT;
11629ef1f84bSDavid du Colombier }
11639ef1f84bSDavid du Colombier
11649ef1f84bSDavid du Colombier /* copy in pseudo ip header plus port numbers */
11659ef1f84bSDavid du Colombier h = (Tcp4hdr *)(data->rp);
11669ef1f84bSDavid du Colombier memmove(h, ph, TCP4_TCBPHDRSZ);
11679ef1f84bSDavid du Colombier
11689ef1f84bSDavid du Colombier /* copy in variable bits */
11699ef1f84bSDavid du Colombier hnputs(h->tcplen, hdrlen + dlen);
11709ef1f84bSDavid du Colombier hnputl(h->tcpseq, tcph->seq);
11719ef1f84bSDavid du Colombier hnputl(h->tcpack, tcph->ack);
11729ef1f84bSDavid du Colombier hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
11739ef1f84bSDavid du Colombier hnputs(h->tcpwin, tcph->wnd>>(tcb != nil ? tcb->snd.scale : 0));
11749ef1f84bSDavid du Colombier hnputs(h->tcpurg, tcph->urg);
11759ef1f84bSDavid du Colombier
11769ef1f84bSDavid du Colombier if(tcph->flags & SYN){
11779ef1f84bSDavid du Colombier opt = h->tcpopt;
11789ef1f84bSDavid du Colombier if(tcph->mss != 0){
11799ef1f84bSDavid du Colombier *opt++ = MSSOPT;
11809ef1f84bSDavid du Colombier *opt++ = MSS_LENGTH;
11819ef1f84bSDavid du Colombier hnputs(opt, tcph->mss);
11829ef1f84bSDavid du Colombier opt += 2;
11839ef1f84bSDavid du Colombier }
11849ef1f84bSDavid du Colombier /* always offer. rfc1323 §2.2 */
11859ef1f84bSDavid du Colombier if(1){
11869ef1f84bSDavid du Colombier *opt++ = WSOPT;
11879ef1f84bSDavid du Colombier *opt++ = WS_LENGTH;
11889ef1f84bSDavid du Colombier *opt++ = tcph->ws;
11899ef1f84bSDavid du Colombier }
11909ef1f84bSDavid du Colombier while(optpad-- > 0)
11919ef1f84bSDavid du Colombier *opt++ = NOOPOPT;
11929ef1f84bSDavid du Colombier }
11939ef1f84bSDavid du Colombier
11949ef1f84bSDavid du Colombier if(tcb != nil && tcb->nochecksum){
11959ef1f84bSDavid du Colombier h->tcpcksum[0] = h->tcpcksum[1] = 0;
11969ef1f84bSDavid du Colombier } else {
11979ef1f84bSDavid du Colombier csum = ptclcsum(data, TCP4_IPLEN, hdrlen+dlen+TCP4_PHDRSIZE);
11989ef1f84bSDavid du Colombier hnputs(h->tcpcksum, csum);
11999ef1f84bSDavid du Colombier }
12009ef1f84bSDavid du Colombier
12019ef1f84bSDavid du Colombier return data;
12029ef1f84bSDavid du Colombier }
12039ef1f84bSDavid du Colombier
12049ef1f84bSDavid du Colombier static int
ntohtcp6(Tcp * tcph,Block ** bpp)12059ef1f84bSDavid du Colombier ntohtcp6(Tcp *tcph, Block **bpp)
12069ef1f84bSDavid du Colombier {
12079ef1f84bSDavid du Colombier Tcp6hdr *h;
12089ef1f84bSDavid du Colombier uchar *optr;
12099ef1f84bSDavid du Colombier ushort hdrlen;
12109ef1f84bSDavid du Colombier ushort optlen;
12119ef1f84bSDavid du Colombier int n;
12129ef1f84bSDavid du Colombier
12139ef1f84bSDavid du Colombier *bpp = pullupblock(*bpp, TCP6_PKT+TCP6_HDRSIZE);
12149ef1f84bSDavid du Colombier if(*bpp == nil)
12159ef1f84bSDavid du Colombier return -1;
12169ef1f84bSDavid du Colombier
12179ef1f84bSDavid du Colombier h = (Tcp6hdr *)((*bpp)->rp);
12189ef1f84bSDavid du Colombier tcph->source = nhgets(h->tcpsport);
12199ef1f84bSDavid du Colombier tcph->dest = nhgets(h->tcpdport);
12209ef1f84bSDavid du Colombier tcph->seq = nhgetl(h->tcpseq);
12219ef1f84bSDavid du Colombier tcph->ack = nhgetl(h->tcpack);
12229ef1f84bSDavid du Colombier hdrlen = (h->tcpflag[0]>>2) & ~3;
12239ef1f84bSDavid du Colombier if(hdrlen < TCP6_HDRSIZE) {
12249ef1f84bSDavid du Colombier freeblist(*bpp);
12259ef1f84bSDavid du Colombier return -1;
12269ef1f84bSDavid du Colombier }
12279ef1f84bSDavid du Colombier
12289ef1f84bSDavid du Colombier tcph->flags = h->tcpflag[1];
12299ef1f84bSDavid du Colombier tcph->wnd = nhgets(h->tcpwin);
12309ef1f84bSDavid du Colombier tcph->urg = nhgets(h->tcpurg);
12319ef1f84bSDavid du Colombier tcph->mss = 0;
12329ef1f84bSDavid du Colombier tcph->ws = 0;
12339ef1f84bSDavid du Colombier tcph->update = 0;
12349ef1f84bSDavid du Colombier tcph->len = nhgets(h->ploadlen) - hdrlen;
12359ef1f84bSDavid du Colombier
12369ef1f84bSDavid du Colombier *bpp = pullupblock(*bpp, hdrlen+TCP6_PKT);
12379ef1f84bSDavid du Colombier if(*bpp == nil)
12389ef1f84bSDavid du Colombier return -1;
12399ef1f84bSDavid du Colombier
12409ef1f84bSDavid du Colombier optr = h->tcpopt;
12419ef1f84bSDavid du Colombier n = hdrlen - TCP6_HDRSIZE;
12429ef1f84bSDavid du Colombier while(n > 0 && *optr != EOLOPT) {
12439ef1f84bSDavid du Colombier if(*optr == NOOPOPT) {
12449ef1f84bSDavid du Colombier n--;
12459ef1f84bSDavid du Colombier optr++;
12469ef1f84bSDavid du Colombier continue;
12479ef1f84bSDavid du Colombier }
12489ef1f84bSDavid du Colombier optlen = optr[1];
12499ef1f84bSDavid du Colombier if(optlen < 2 || optlen > n)
12509ef1f84bSDavid du Colombier break;
12519ef1f84bSDavid du Colombier switch(*optr) {
12529ef1f84bSDavid du Colombier case MSSOPT:
12539ef1f84bSDavid du Colombier if(optlen == MSS_LENGTH)
12549ef1f84bSDavid du Colombier tcph->mss = nhgets(optr+2);
12559ef1f84bSDavid du Colombier break;
12569ef1f84bSDavid du Colombier case WSOPT:
12579ef1f84bSDavid du Colombier if(optlen == WS_LENGTH && *(optr+2) <= 14)
12589ef1f84bSDavid du Colombier tcph->ws = *(optr+2);
12599ef1f84bSDavid du Colombier break;
12609ef1f84bSDavid du Colombier }
12619ef1f84bSDavid du Colombier n -= optlen;
12629ef1f84bSDavid du Colombier optr += optlen;
12639ef1f84bSDavid du Colombier }
12649ef1f84bSDavid du Colombier return hdrlen;
12659ef1f84bSDavid du Colombier }
12669ef1f84bSDavid du Colombier
12679ef1f84bSDavid du Colombier static int
ntohtcp4(Tcp * tcph,Block ** bpp)12689ef1f84bSDavid du Colombier ntohtcp4(Tcp *tcph, Block **bpp)
12699ef1f84bSDavid du Colombier {
12709ef1f84bSDavid du Colombier Tcp4hdr *h;
12719ef1f84bSDavid du Colombier uchar *optr;
12729ef1f84bSDavid du Colombier ushort hdrlen;
12739ef1f84bSDavid du Colombier ushort optlen;
12749ef1f84bSDavid du Colombier int n;
12759ef1f84bSDavid du Colombier
12769ef1f84bSDavid du Colombier *bpp = pullupblock(*bpp, TCP4_PKT+TCP4_HDRSIZE);
12779ef1f84bSDavid du Colombier if(*bpp == nil)
12789ef1f84bSDavid du Colombier return -1;
12799ef1f84bSDavid du Colombier
12809ef1f84bSDavid du Colombier h = (Tcp4hdr *)((*bpp)->rp);
12819ef1f84bSDavid du Colombier tcph->source = nhgets(h->tcpsport);
12829ef1f84bSDavid du Colombier tcph->dest = nhgets(h->tcpdport);
12839ef1f84bSDavid du Colombier tcph->seq = nhgetl(h->tcpseq);
12849ef1f84bSDavid du Colombier tcph->ack = nhgetl(h->tcpack);
12859ef1f84bSDavid du Colombier
12869ef1f84bSDavid du Colombier hdrlen = (h->tcpflag[0]>>2) & ~3;
12879ef1f84bSDavid du Colombier if(hdrlen < TCP4_HDRSIZE) {
12889ef1f84bSDavid du Colombier freeblist(*bpp);
12899ef1f84bSDavid du Colombier return -1;
12909ef1f84bSDavid du Colombier }
12919ef1f84bSDavid du Colombier
12929ef1f84bSDavid du Colombier tcph->flags = h->tcpflag[1];
12939ef1f84bSDavid du Colombier tcph->wnd = nhgets(h->tcpwin);
12949ef1f84bSDavid du Colombier tcph->urg = nhgets(h->tcpurg);
12959ef1f84bSDavid du Colombier tcph->mss = 0;
12969ef1f84bSDavid du Colombier tcph->ws = 0;
12979ef1f84bSDavid du Colombier tcph->update = 0;
12989ef1f84bSDavid du Colombier tcph->len = nhgets(h->length) - (hdrlen + TCP4_PKT);
12999ef1f84bSDavid du Colombier
13009ef1f84bSDavid du Colombier *bpp = pullupblock(*bpp, hdrlen+TCP4_PKT);
13019ef1f84bSDavid du Colombier if(*bpp == nil)
13029ef1f84bSDavid du Colombier return -1;
13039ef1f84bSDavid du Colombier
13049ef1f84bSDavid du Colombier optr = h->tcpopt;
13059ef1f84bSDavid du Colombier n = hdrlen - TCP4_HDRSIZE;
13069ef1f84bSDavid du Colombier while(n > 0 && *optr != EOLOPT) {
13079ef1f84bSDavid du Colombier if(*optr == NOOPOPT) {
13089ef1f84bSDavid du Colombier n--;
13099ef1f84bSDavid du Colombier optr++;
13109ef1f84bSDavid du Colombier continue;
13119ef1f84bSDavid du Colombier }
13129ef1f84bSDavid du Colombier optlen = optr[1];
13139ef1f84bSDavid du Colombier if(optlen < 2 || optlen > n)
13149ef1f84bSDavid du Colombier break;
13159ef1f84bSDavid du Colombier switch(*optr) {
13169ef1f84bSDavid du Colombier case MSSOPT:
13179ef1f84bSDavid du Colombier if(optlen == MSS_LENGTH)
13189ef1f84bSDavid du Colombier tcph->mss = nhgets(optr+2);
13199ef1f84bSDavid du Colombier break;
13209ef1f84bSDavid du Colombier case WSOPT:
13219ef1f84bSDavid du Colombier if(optlen == WS_LENGTH && *(optr+2) <= 14)
13229ef1f84bSDavid du Colombier tcph->ws = *(optr+2);
13239ef1f84bSDavid du Colombier break;
13249ef1f84bSDavid du Colombier }
13259ef1f84bSDavid du Colombier n -= optlen;
13269ef1f84bSDavid du Colombier optr += optlen;
13279ef1f84bSDavid du Colombier }
13289ef1f84bSDavid du Colombier return hdrlen;
13299ef1f84bSDavid du Colombier }
13309ef1f84bSDavid du Colombier
13319ef1f84bSDavid du Colombier /*
13329ef1f84bSDavid du Colombier * For outgoing calls, generate an initial sequence
13339ef1f84bSDavid du Colombier * number and put a SYN on the send queue
13349ef1f84bSDavid du Colombier */
13359ef1f84bSDavid du Colombier static void
tcpsndsyn(Conv * s,Tcpctl * tcb)13369ef1f84bSDavid du Colombier tcpsndsyn(Conv *s, Tcpctl *tcb)
13379ef1f84bSDavid du Colombier {
13389ef1f84bSDavid du Colombier Tcppriv *tpriv;
13399ef1f84bSDavid du Colombier
13409ef1f84bSDavid du Colombier tcb->iss = (nrand(1<<16)<<16)|nrand(1<<16);
13419ef1f84bSDavid du Colombier tcb->rttseq = tcb->iss;
13429ef1f84bSDavid du Colombier tcb->snd.wl2 = tcb->iss;
13439ef1f84bSDavid du Colombier tcb->snd.una = tcb->iss;
13449ef1f84bSDavid du Colombier tcb->snd.rxt = tcb->iss;
13459ef1f84bSDavid du Colombier tcb->snd.ptr = tcb->rttseq;
13469ef1f84bSDavid du Colombier tcb->snd.nxt = tcb->rttseq;
13479ef1f84bSDavid du Colombier tcb->flgcnt++;
13489ef1f84bSDavid du Colombier tcb->flags |= FORCE;
13499ef1f84bSDavid du Colombier tcb->sndsyntime = NOW;
13509ef1f84bSDavid du Colombier
13519ef1f84bSDavid du Colombier /* set desired mss and scale */
13529ef1f84bSDavid du Colombier tcb->mss = tcpmtu(s->p, s->laddr, s->ipversion, &tcb->scale);
13539ef1f84bSDavid du Colombier tpriv = s->p->priv;
13549ef1f84bSDavid du Colombier tpriv->stats[Mss] = tcb->mss;
13559ef1f84bSDavid du Colombier }
13569ef1f84bSDavid du Colombier
13579ef1f84bSDavid du Colombier void
sndrst(Proto * tcp,uchar * source,uchar * dest,ushort length,Tcp * seg,uchar version,char * reason)13589ef1f84bSDavid du Colombier sndrst(Proto *tcp, uchar *source, uchar *dest, ushort length, Tcp *seg, uchar version, char *reason)
13599ef1f84bSDavid du Colombier {
13609ef1f84bSDavid du Colombier Block *hbp;
13619ef1f84bSDavid du Colombier uchar rflags;
13629ef1f84bSDavid du Colombier Tcppriv *tpriv;
13639ef1f84bSDavid du Colombier Tcp4hdr ph4;
13649ef1f84bSDavid du Colombier Tcp6hdr ph6;
13659ef1f84bSDavid du Colombier
13669ef1f84bSDavid du Colombier netlog(tcp->f, Logtcp, "sndrst: %s\n", reason);
13679ef1f84bSDavid du Colombier
13689ef1f84bSDavid du Colombier tpriv = tcp->priv;
13699ef1f84bSDavid du Colombier
13709ef1f84bSDavid du Colombier if(seg->flags & RST)
13719ef1f84bSDavid du Colombier return;
13729ef1f84bSDavid du Colombier
13739ef1f84bSDavid du Colombier /* make pseudo header */
13749ef1f84bSDavid du Colombier switch(version) {
13759ef1f84bSDavid du Colombier case V4:
13769ef1f84bSDavid du Colombier memset(&ph4, 0, sizeof(ph4));
13779ef1f84bSDavid du Colombier ph4.vihl = IP_VER4;
13789ef1f84bSDavid du Colombier v6tov4(ph4.tcpsrc, dest);
13799ef1f84bSDavid du Colombier v6tov4(ph4.tcpdst, source);
13809ef1f84bSDavid du Colombier ph4.proto = IP_TCPPROTO;
13819ef1f84bSDavid du Colombier hnputs(ph4.tcplen, TCP4_HDRSIZE);
13829ef1f84bSDavid du Colombier hnputs(ph4.tcpsport, seg->dest);
13839ef1f84bSDavid du Colombier hnputs(ph4.tcpdport, seg->source);
13849ef1f84bSDavid du Colombier break;
13859ef1f84bSDavid du Colombier case V6:
13869ef1f84bSDavid du Colombier memset(&ph6, 0, sizeof(ph6));
13879ef1f84bSDavid du Colombier ph6.vcf[0] = IP_VER6;
13889ef1f84bSDavid du Colombier ipmove(ph6.tcpsrc, dest);
13899ef1f84bSDavid du Colombier ipmove(ph6.tcpdst, source);
13909ef1f84bSDavid du Colombier ph6.proto = IP_TCPPROTO;
13919ef1f84bSDavid du Colombier hnputs(ph6.ploadlen, TCP6_HDRSIZE);
13929ef1f84bSDavid du Colombier hnputs(ph6.tcpsport, seg->dest);
13939ef1f84bSDavid du Colombier hnputs(ph6.tcpdport, seg->source);
13949ef1f84bSDavid du Colombier break;
13959ef1f84bSDavid du Colombier default:
13969ef1f84bSDavid du Colombier panic("sndrst: version %d", version);
13979ef1f84bSDavid du Colombier }
13989ef1f84bSDavid du Colombier
13999ef1f84bSDavid du Colombier tpriv->stats[OutRsts]++;
14009ef1f84bSDavid du Colombier rflags = RST;
14019ef1f84bSDavid du Colombier
14029ef1f84bSDavid du Colombier /* convince the other end that this reset is in band */
14039ef1f84bSDavid du Colombier if(seg->flags & ACK) {
14049ef1f84bSDavid du Colombier seg->seq = seg->ack;
14059ef1f84bSDavid du Colombier seg->ack = 0;
14069ef1f84bSDavid du Colombier }
14079ef1f84bSDavid du Colombier else {
14089ef1f84bSDavid du Colombier rflags |= ACK;
14099ef1f84bSDavid du Colombier seg->ack = seg->seq;
14109ef1f84bSDavid du Colombier seg->seq = 0;
14119ef1f84bSDavid du Colombier if(seg->flags & SYN)
14129ef1f84bSDavid du Colombier seg->ack++;
14139ef1f84bSDavid du Colombier seg->ack += length;
14149ef1f84bSDavid du Colombier if(seg->flags & FIN)
14159ef1f84bSDavid du Colombier seg->ack++;
14169ef1f84bSDavid du Colombier }
14179ef1f84bSDavid du Colombier seg->flags = rflags;
14189ef1f84bSDavid du Colombier seg->wnd = 0;
14199ef1f84bSDavid du Colombier seg->urg = 0;
14209ef1f84bSDavid du Colombier seg->mss = 0;
14219ef1f84bSDavid du Colombier seg->ws = 0;
14229ef1f84bSDavid du Colombier switch(version) {
14239ef1f84bSDavid du Colombier case V4:
14249ef1f84bSDavid du Colombier hbp = htontcp4(seg, nil, &ph4, nil);
14259ef1f84bSDavid du Colombier if(hbp == nil)
14269ef1f84bSDavid du Colombier return;
14279ef1f84bSDavid du Colombier ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
14289ef1f84bSDavid du Colombier break;
14299ef1f84bSDavid du Colombier case V6:
14309ef1f84bSDavid du Colombier hbp = htontcp6(seg, nil, &ph6, nil);
14319ef1f84bSDavid du Colombier if(hbp == nil)
14329ef1f84bSDavid du Colombier return;
14339ef1f84bSDavid du Colombier ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
14349ef1f84bSDavid du Colombier break;
14359ef1f84bSDavid du Colombier default:
14369ef1f84bSDavid du Colombier panic("sndrst2: version %d", version);
14379ef1f84bSDavid du Colombier }
14389ef1f84bSDavid du Colombier }
14399ef1f84bSDavid du Colombier
14409ef1f84bSDavid du Colombier /*
1441*41165bbaSDavid du Colombier * close the conversation
1442*41165bbaSDavid du Colombier */
1443*41165bbaSDavid du Colombier static char*
tcpclose2(Conv * s)1444*41165bbaSDavid du Colombier tcpclose2(Conv *s)
1445*41165bbaSDavid du Colombier {
1446*41165bbaSDavid du Colombier tcpclose(s);
1447*41165bbaSDavid du Colombier return nil;
1448*41165bbaSDavid du Colombier }
1449*41165bbaSDavid du Colombier
1450*41165bbaSDavid du Colombier /*
14519ef1f84bSDavid du Colombier * send a reset to the remote side and close the conversation
14529ef1f84bSDavid du Colombier * called with s qlocked
14539ef1f84bSDavid du Colombier */
14549ef1f84bSDavid du Colombier static char*
tcphangup(Conv * s)14559ef1f84bSDavid du Colombier tcphangup(Conv *s)
14569ef1f84bSDavid du Colombier {
14579ef1f84bSDavid du Colombier Tcp seg;
14589ef1f84bSDavid du Colombier Tcpctl *tcb;
14599ef1f84bSDavid du Colombier Block *hbp;
14609ef1f84bSDavid du Colombier
14619ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
14629ef1f84bSDavid du Colombier if(waserror())
14639ef1f84bSDavid du Colombier return up->errstr;
14649ef1f84bSDavid du Colombier if(ipcmp(s->raddr, IPnoaddr) != 0) {
14659ef1f84bSDavid du Colombier if(!waserror()){
14669ef1f84bSDavid du Colombier memset(&seg, 0, sizeof seg);
14679ef1f84bSDavid du Colombier seg.flags = RST | ACK;
14689ef1f84bSDavid du Colombier seg.ack = tcb->rcv.nxt;
14699ef1f84bSDavid du Colombier tcb->rcv.ackptr = seg.ack;
14709ef1f84bSDavid du Colombier seg.seq = tcb->snd.ptr;
14719ef1f84bSDavid du Colombier seg.wnd = 0;
14729ef1f84bSDavid du Colombier seg.urg = 0;
14739ef1f84bSDavid du Colombier seg.mss = 0;
14749ef1f84bSDavid du Colombier seg.ws = 0;
14759ef1f84bSDavid du Colombier switch(s->ipversion) {
14769ef1f84bSDavid du Colombier case V4:
14779ef1f84bSDavid du Colombier tcb->protohdr.tcp4hdr.vihl = IP_VER4;
14789ef1f84bSDavid du Colombier hbp = htontcp4(&seg, nil, &tcb->protohdr.tcp4hdr, tcb);
14799ef1f84bSDavid du Colombier ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s);
14809ef1f84bSDavid du Colombier break;
14819ef1f84bSDavid du Colombier case V6:
14829ef1f84bSDavid du Colombier tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
14839ef1f84bSDavid du Colombier hbp = htontcp6(&seg, nil, &tcb->protohdr.tcp6hdr, tcb);
14849ef1f84bSDavid du Colombier ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s);
14859ef1f84bSDavid du Colombier break;
14869ef1f84bSDavid du Colombier default:
14879ef1f84bSDavid du Colombier panic("tcphangup: version %d", s->ipversion);
14889ef1f84bSDavid du Colombier }
14899ef1f84bSDavid du Colombier poperror();
14909ef1f84bSDavid du Colombier }
14919ef1f84bSDavid du Colombier }
14929ef1f84bSDavid du Colombier localclose(s, nil);
14939ef1f84bSDavid du Colombier poperror();
14949ef1f84bSDavid du Colombier return nil;
14959ef1f84bSDavid du Colombier }
14969ef1f84bSDavid du Colombier
14979ef1f84bSDavid du Colombier /*
14989ef1f84bSDavid du Colombier * (re)send a SYN ACK
14999ef1f84bSDavid du Colombier */
15009ef1f84bSDavid du Colombier static int
sndsynack(Proto * tcp,Limbo * lp)15019ef1f84bSDavid du Colombier sndsynack(Proto *tcp, Limbo *lp)
15029ef1f84bSDavid du Colombier {
15039ef1f84bSDavid du Colombier Block *hbp;
15049ef1f84bSDavid du Colombier Tcp4hdr ph4;
15059ef1f84bSDavid du Colombier Tcp6hdr ph6;
15069ef1f84bSDavid du Colombier Tcp seg;
15079ef1f84bSDavid du Colombier uint scale;
15089ef1f84bSDavid du Colombier
15099ef1f84bSDavid du Colombier /* make pseudo header */
15109ef1f84bSDavid du Colombier switch(lp->version) {
15119ef1f84bSDavid du Colombier case V4:
15129ef1f84bSDavid du Colombier memset(&ph4, 0, sizeof(ph4));
15139ef1f84bSDavid du Colombier ph4.vihl = IP_VER4;
15149ef1f84bSDavid du Colombier v6tov4(ph4.tcpsrc, lp->laddr);
15159ef1f84bSDavid du Colombier v6tov4(ph4.tcpdst, lp->raddr);
15169ef1f84bSDavid du Colombier ph4.proto = IP_TCPPROTO;
15179ef1f84bSDavid du Colombier hnputs(ph4.tcplen, TCP4_HDRSIZE);
15189ef1f84bSDavid du Colombier hnputs(ph4.tcpsport, lp->lport);
15199ef1f84bSDavid du Colombier hnputs(ph4.tcpdport, lp->rport);
15209ef1f84bSDavid du Colombier break;
15219ef1f84bSDavid du Colombier case V6:
15229ef1f84bSDavid du Colombier memset(&ph6, 0, sizeof(ph6));
15239ef1f84bSDavid du Colombier ph6.vcf[0] = IP_VER6;
15249ef1f84bSDavid du Colombier ipmove(ph6.tcpsrc, lp->laddr);
15259ef1f84bSDavid du Colombier ipmove(ph6.tcpdst, lp->raddr);
15269ef1f84bSDavid du Colombier ph6.proto = IP_TCPPROTO;
15279ef1f84bSDavid du Colombier hnputs(ph6.ploadlen, TCP6_HDRSIZE);
15289ef1f84bSDavid du Colombier hnputs(ph6.tcpsport, lp->lport);
15299ef1f84bSDavid du Colombier hnputs(ph6.tcpdport, lp->rport);
15309ef1f84bSDavid du Colombier break;
15319ef1f84bSDavid du Colombier default:
15329ef1f84bSDavid du Colombier panic("sndrst: version %d", lp->version);
15339ef1f84bSDavid du Colombier }
15349ef1f84bSDavid du Colombier
15359ef1f84bSDavid du Colombier memset(&seg, 0, sizeof seg);
15369ef1f84bSDavid du Colombier seg.seq = lp->iss;
15379ef1f84bSDavid du Colombier seg.ack = lp->irs+1;
15389ef1f84bSDavid du Colombier seg.flags = SYN|ACK;
15399ef1f84bSDavid du Colombier seg.urg = 0;
15409ef1f84bSDavid du Colombier seg.mss = tcpmtu(tcp, lp->laddr, lp->version, &scale);
15419ef1f84bSDavid du Colombier seg.wnd = QMAX;
15429ef1f84bSDavid du Colombier
15439ef1f84bSDavid du Colombier /* if the other side set scale, we should too */
15449ef1f84bSDavid du Colombier if(lp->rcvscale){
15459ef1f84bSDavid du Colombier seg.ws = scale;
15469ef1f84bSDavid du Colombier lp->sndscale = scale;
15479ef1f84bSDavid du Colombier } else {
15489ef1f84bSDavid du Colombier seg.ws = 0;
15499ef1f84bSDavid du Colombier lp->sndscale = 0;
15509ef1f84bSDavid du Colombier }
15519ef1f84bSDavid du Colombier
15529ef1f84bSDavid du Colombier switch(lp->version) {
15539ef1f84bSDavid du Colombier case V4:
15549ef1f84bSDavid du Colombier hbp = htontcp4(&seg, nil, &ph4, nil);
15559ef1f84bSDavid du Colombier if(hbp == nil)
15569ef1f84bSDavid du Colombier return -1;
15579ef1f84bSDavid du Colombier ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
15589ef1f84bSDavid du Colombier break;
15599ef1f84bSDavid du Colombier case V6:
15609ef1f84bSDavid du Colombier hbp = htontcp6(&seg, nil, &ph6, nil);
15619ef1f84bSDavid du Colombier if(hbp == nil)
15629ef1f84bSDavid du Colombier return -1;
15639ef1f84bSDavid du Colombier ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
15649ef1f84bSDavid du Colombier break;
15659ef1f84bSDavid du Colombier default:
15669ef1f84bSDavid du Colombier panic("sndsnack: version %d", lp->version);
15679ef1f84bSDavid du Colombier }
15689ef1f84bSDavid du Colombier lp->lastsend = NOW;
15699ef1f84bSDavid du Colombier return 0;
15709ef1f84bSDavid du Colombier }
15719ef1f84bSDavid du Colombier
15729ef1f84bSDavid du Colombier #define hashipa(a, p) ( ( (a)[IPaddrlen-2] + (a)[IPaddrlen-1] + p )&LHTMASK )
15739ef1f84bSDavid du Colombier
15749ef1f84bSDavid du Colombier /*
15759ef1f84bSDavid du Colombier * put a call into limbo and respond with a SYN ACK
15769ef1f84bSDavid du Colombier *
15779ef1f84bSDavid du Colombier * called with proto locked
15789ef1f84bSDavid du Colombier */
15799ef1f84bSDavid du Colombier static void
limbo(Conv * s,uchar * source,uchar * dest,Tcp * seg,int version)15809ef1f84bSDavid du Colombier limbo(Conv *s, uchar *source, uchar *dest, Tcp *seg, int version)
15819ef1f84bSDavid du Colombier {
15829ef1f84bSDavid du Colombier Limbo *lp, **l;
15839ef1f84bSDavid du Colombier Tcppriv *tpriv;
15849ef1f84bSDavid du Colombier int h;
15859ef1f84bSDavid du Colombier
15869ef1f84bSDavid du Colombier tpriv = s->p->priv;
15879ef1f84bSDavid du Colombier h = hashipa(source, seg->source);
15889ef1f84bSDavid du Colombier
15899ef1f84bSDavid du Colombier for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){
15909ef1f84bSDavid du Colombier lp = *l;
15919ef1f84bSDavid du Colombier if(lp->lport != seg->dest || lp->rport != seg->source || lp->version != version)
15929ef1f84bSDavid du Colombier continue;
15939ef1f84bSDavid du Colombier if(ipcmp(lp->raddr, source) != 0)
15949ef1f84bSDavid du Colombier continue;
15959ef1f84bSDavid du Colombier if(ipcmp(lp->laddr, dest) != 0)
15969ef1f84bSDavid du Colombier continue;
15979ef1f84bSDavid du Colombier
15989ef1f84bSDavid du Colombier /* each new SYN restarts the retransmits */
15999ef1f84bSDavid du Colombier lp->irs = seg->seq;
16009ef1f84bSDavid du Colombier break;
16019ef1f84bSDavid du Colombier }
16029ef1f84bSDavid du Colombier lp = *l;
16039ef1f84bSDavid du Colombier if(lp == nil){
16049ef1f84bSDavid du Colombier if(tpriv->nlimbo >= Maxlimbo && tpriv->lht[h]){
16059ef1f84bSDavid du Colombier lp = tpriv->lht[h];
16069ef1f84bSDavid du Colombier tpriv->lht[h] = lp->next;
16079ef1f84bSDavid du Colombier lp->next = nil;
16089ef1f84bSDavid du Colombier } else {
16099ef1f84bSDavid du Colombier lp = malloc(sizeof(*lp));
16109ef1f84bSDavid du Colombier if(lp == nil)
16119ef1f84bSDavid du Colombier return;
16129ef1f84bSDavid du Colombier tpriv->nlimbo++;
16139ef1f84bSDavid du Colombier }
16149ef1f84bSDavid du Colombier *l = lp;
16159ef1f84bSDavid du Colombier lp->version = version;
16169ef1f84bSDavid du Colombier ipmove(lp->laddr, dest);
16179ef1f84bSDavid du Colombier ipmove(lp->raddr, source);
16189ef1f84bSDavid du Colombier lp->lport = seg->dest;
16199ef1f84bSDavid du Colombier lp->rport = seg->source;
16209ef1f84bSDavid du Colombier lp->mss = seg->mss;
16219ef1f84bSDavid du Colombier lp->rcvscale = seg->ws;
16229ef1f84bSDavid du Colombier lp->irs = seg->seq;
16239ef1f84bSDavid du Colombier lp->iss = (nrand(1<<16)<<16)|nrand(1<<16);
16249ef1f84bSDavid du Colombier }
16259ef1f84bSDavid du Colombier
16269ef1f84bSDavid du Colombier if(sndsynack(s->p, lp) < 0){
16279ef1f84bSDavid du Colombier *l = lp->next;
16289ef1f84bSDavid du Colombier tpriv->nlimbo--;
16299ef1f84bSDavid du Colombier free(lp);
16309ef1f84bSDavid du Colombier }
16319ef1f84bSDavid du Colombier }
16329ef1f84bSDavid du Colombier
16339ef1f84bSDavid du Colombier /*
16349ef1f84bSDavid du Colombier * resend SYN ACK's once every SYNACK_RXTIMER ms.
16359ef1f84bSDavid du Colombier */
16369ef1f84bSDavid du Colombier static void
limborexmit(Proto * tcp)16379ef1f84bSDavid du Colombier limborexmit(Proto *tcp)
16389ef1f84bSDavid du Colombier {
16399ef1f84bSDavid du Colombier Tcppriv *tpriv;
16409ef1f84bSDavid du Colombier Limbo **l, *lp;
16419ef1f84bSDavid du Colombier int h;
16429ef1f84bSDavid du Colombier int seen;
16439ef1f84bSDavid du Colombier ulong now;
16449ef1f84bSDavid du Colombier
16459ef1f84bSDavid du Colombier tpriv = tcp->priv;
16469ef1f84bSDavid du Colombier
16479ef1f84bSDavid du Colombier if(!canqlock(tcp))
16489ef1f84bSDavid du Colombier return;
16499ef1f84bSDavid du Colombier seen = 0;
16509ef1f84bSDavid du Colombier now = NOW;
16519ef1f84bSDavid du Colombier for(h = 0; h < NLHT && seen < tpriv->nlimbo; h++){
16529ef1f84bSDavid du Colombier for(l = &tpriv->lht[h]; *l != nil && seen < tpriv->nlimbo; ){
16539ef1f84bSDavid du Colombier lp = *l;
16549ef1f84bSDavid du Colombier seen++;
16559ef1f84bSDavid du Colombier if(now - lp->lastsend < (lp->rexmits+1)*SYNACK_RXTIMER)
16569ef1f84bSDavid du Colombier continue;
16579ef1f84bSDavid du Colombier
16589ef1f84bSDavid du Colombier /* time it out after 1 second */
16599ef1f84bSDavid du Colombier if(++(lp->rexmits) > 5){
16609ef1f84bSDavid du Colombier tpriv->nlimbo--;
16619ef1f84bSDavid du Colombier *l = lp->next;
16629ef1f84bSDavid du Colombier free(lp);
16639ef1f84bSDavid du Colombier continue;
16649ef1f84bSDavid du Colombier }
16659ef1f84bSDavid du Colombier
16669ef1f84bSDavid du Colombier /* if we're being attacked, don't bother resending SYN ACK's */
16679ef1f84bSDavid du Colombier if(tpriv->nlimbo > 100)
16689ef1f84bSDavid du Colombier continue;
16699ef1f84bSDavid du Colombier
16709ef1f84bSDavid du Colombier if(sndsynack(tcp, lp) < 0){
16719ef1f84bSDavid du Colombier tpriv->nlimbo--;
16729ef1f84bSDavid du Colombier *l = lp->next;
16739ef1f84bSDavid du Colombier free(lp);
16749ef1f84bSDavid du Colombier continue;
16759ef1f84bSDavid du Colombier }
16769ef1f84bSDavid du Colombier
16779ef1f84bSDavid du Colombier l = &lp->next;
16789ef1f84bSDavid du Colombier }
16799ef1f84bSDavid du Colombier }
16809ef1f84bSDavid du Colombier qunlock(tcp);
16819ef1f84bSDavid du Colombier }
16829ef1f84bSDavid du Colombier
16839ef1f84bSDavid du Colombier /*
16849ef1f84bSDavid du Colombier * lookup call in limbo. if found, throw it out.
16859ef1f84bSDavid du Colombier *
16869ef1f84bSDavid du Colombier * called with proto locked
16879ef1f84bSDavid du Colombier */
16889ef1f84bSDavid du Colombier static void
limborst(Conv * s,Tcp * segp,uchar * src,uchar * dst,uchar version)16899ef1f84bSDavid du Colombier limborst(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
16909ef1f84bSDavid du Colombier {
16919ef1f84bSDavid du Colombier Limbo *lp, **l;
16929ef1f84bSDavid du Colombier int h;
16939ef1f84bSDavid du Colombier Tcppriv *tpriv;
16949ef1f84bSDavid du Colombier
16959ef1f84bSDavid du Colombier tpriv = s->p->priv;
16969ef1f84bSDavid du Colombier
16979ef1f84bSDavid du Colombier /* find a call in limbo */
16989ef1f84bSDavid du Colombier h = hashipa(src, segp->source);
16999ef1f84bSDavid du Colombier for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){
17009ef1f84bSDavid du Colombier lp = *l;
17019ef1f84bSDavid du Colombier if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version)
17029ef1f84bSDavid du Colombier continue;
17039ef1f84bSDavid du Colombier if(ipcmp(lp->laddr, dst) != 0)
17049ef1f84bSDavid du Colombier continue;
17059ef1f84bSDavid du Colombier if(ipcmp(lp->raddr, src) != 0)
17069ef1f84bSDavid du Colombier continue;
17079ef1f84bSDavid du Colombier
17089ef1f84bSDavid du Colombier /* RST can only follow the SYN */
17099ef1f84bSDavid du Colombier if(segp->seq == lp->irs+1){
17109ef1f84bSDavid du Colombier tpriv->nlimbo--;
17119ef1f84bSDavid du Colombier *l = lp->next;
17129ef1f84bSDavid du Colombier free(lp);
17139ef1f84bSDavid du Colombier }
17149ef1f84bSDavid du Colombier break;
17159ef1f84bSDavid du Colombier }
17169ef1f84bSDavid du Colombier }
17179ef1f84bSDavid du Colombier
17189ef1f84bSDavid du Colombier static void
initialwindow(Tcpctl * tcb)17199ef1f84bSDavid du Colombier initialwindow(Tcpctl *tcb)
17209ef1f84bSDavid du Colombier {
17219ef1f84bSDavid du Colombier /* RFC 3390 initial window */
17229ef1f84bSDavid du Colombier if(tcb->mss < 1095)
17239ef1f84bSDavid du Colombier tcb->cwind = 4*tcb->mss;
17249ef1f84bSDavid du Colombier else if(tcb->mss < 2190)
17259ef1f84bSDavid du Colombier tcb->cwind = 2*2190;
17269ef1f84bSDavid du Colombier else
17279ef1f84bSDavid du Colombier tcb->cwind = 2*tcb->mss;
17289ef1f84bSDavid du Colombier }
17299ef1f84bSDavid du Colombier
17309ef1f84bSDavid du Colombier /*
17319ef1f84bSDavid du Colombier * come here when we finally get an ACK to our SYN-ACK.
17329ef1f84bSDavid du Colombier * lookup call in limbo. if found, create a new conversation
17339ef1f84bSDavid du Colombier *
17349ef1f84bSDavid du Colombier * called with proto locked
17359ef1f84bSDavid du Colombier */
17369ef1f84bSDavid du Colombier static Conv*
tcpincoming(Conv * s,Tcp * segp,uchar * src,uchar * dst,uchar version)17379ef1f84bSDavid du Colombier tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
17389ef1f84bSDavid du Colombier {
17399ef1f84bSDavid du Colombier Conv *new;
17409ef1f84bSDavid du Colombier Tcpctl *tcb;
17419ef1f84bSDavid du Colombier Tcppriv *tpriv;
17429ef1f84bSDavid du Colombier Tcp4hdr *h4;
17439ef1f84bSDavid du Colombier Tcp6hdr *h6;
17449ef1f84bSDavid du Colombier Limbo *lp, **l;
17459ef1f84bSDavid du Colombier int h;
17469ef1f84bSDavid du Colombier
17479ef1f84bSDavid du Colombier /* unless it's just an ack, it can't be someone coming out of limbo */
17489ef1f84bSDavid du Colombier if((segp->flags & SYN) || (segp->flags & ACK) == 0)
17499ef1f84bSDavid du Colombier return nil;
17509ef1f84bSDavid du Colombier
17519ef1f84bSDavid du Colombier tpriv = s->p->priv;
17529ef1f84bSDavid du Colombier
17539ef1f84bSDavid du Colombier /* find a call in limbo */
17549ef1f84bSDavid du Colombier h = hashipa(src, segp->source);
17559ef1f84bSDavid du Colombier for(l = &tpriv->lht[h]; (lp = *l) != nil; l = &lp->next){
17569ef1f84bSDavid du Colombier netlog(s->p->f, Logtcp, "tcpincoming s %I!%ud/%I!%ud d %I!%ud/%I!%ud v %d/%d\n",
17579ef1f84bSDavid du Colombier src, segp->source, lp->raddr, lp->rport,
17589ef1f84bSDavid du Colombier dst, segp->dest, lp->laddr, lp->lport,
17599ef1f84bSDavid du Colombier version, lp->version
17609ef1f84bSDavid du Colombier );
17619ef1f84bSDavid du Colombier
17629ef1f84bSDavid du Colombier if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version)
17639ef1f84bSDavid du Colombier continue;
17649ef1f84bSDavid du Colombier if(ipcmp(lp->laddr, dst) != 0)
17659ef1f84bSDavid du Colombier continue;
17669ef1f84bSDavid du Colombier if(ipcmp(lp->raddr, src) != 0)
17679ef1f84bSDavid du Colombier continue;
17689ef1f84bSDavid du Colombier
17699ef1f84bSDavid du Colombier /* we're assuming no data with the initial SYN */
17709ef1f84bSDavid du Colombier if(segp->seq != lp->irs+1 || segp->ack != lp->iss+1){
17719ef1f84bSDavid du Colombier netlog(s->p->f, Logtcp, "tcpincoming s %lux/%lux a %lux %lux\n",
17729ef1f84bSDavid du Colombier segp->seq, lp->irs+1, segp->ack, lp->iss+1);
17739ef1f84bSDavid du Colombier lp = nil;
17749ef1f84bSDavid du Colombier } else {
17759ef1f84bSDavid du Colombier tpriv->nlimbo--;
17769ef1f84bSDavid du Colombier *l = lp->next;
17779ef1f84bSDavid du Colombier }
17789ef1f84bSDavid du Colombier break;
17799ef1f84bSDavid du Colombier }
17809ef1f84bSDavid du Colombier if(lp == nil)
17819ef1f84bSDavid du Colombier return nil;
17829ef1f84bSDavid du Colombier
17839ef1f84bSDavid du Colombier new = Fsnewcall(s, src, segp->source, dst, segp->dest, version);
17849ef1f84bSDavid du Colombier if(new == nil)
17859ef1f84bSDavid du Colombier return nil;
17869ef1f84bSDavid du Colombier
17879ef1f84bSDavid du Colombier memmove(new->ptcl, s->ptcl, sizeof(Tcpctl));
17889ef1f84bSDavid du Colombier tcb = (Tcpctl*)new->ptcl;
17899ef1f84bSDavid du Colombier tcb->flags &= ~CLONE;
17909ef1f84bSDavid du Colombier tcb->timer.arg = new;
17919ef1f84bSDavid du Colombier tcb->timer.state = TcptimerOFF;
17929ef1f84bSDavid du Colombier tcb->acktimer.arg = new;
17939ef1f84bSDavid du Colombier tcb->acktimer.state = TcptimerOFF;
17949ef1f84bSDavid du Colombier tcb->katimer.arg = new;
17959ef1f84bSDavid du Colombier tcb->katimer.state = TcptimerOFF;
17969ef1f84bSDavid du Colombier tcb->rtt_timer.arg = new;
17979ef1f84bSDavid du Colombier tcb->rtt_timer.state = TcptimerOFF;
17989ef1f84bSDavid du Colombier
17999ef1f84bSDavid du Colombier tcb->irs = lp->irs;
18009ef1f84bSDavid du Colombier tcb->rcv.nxt = tcb->irs+1;
18019ef1f84bSDavid du Colombier tcb->rcv.wptr = tcb->rcv.nxt;
18029ef1f84bSDavid du Colombier tcb->rcv.wsnt = 0;
18039ef1f84bSDavid du Colombier tcb->rcv.urg = tcb->rcv.nxt;
18049ef1f84bSDavid du Colombier
18059ef1f84bSDavid du Colombier tcb->iss = lp->iss;
18069ef1f84bSDavid du Colombier tcb->rttseq = tcb->iss;
18079ef1f84bSDavid du Colombier tcb->snd.wl2 = tcb->iss;
18089ef1f84bSDavid du Colombier tcb->snd.una = tcb->iss+1;
18099ef1f84bSDavid du Colombier tcb->snd.ptr = tcb->iss+1;
18109ef1f84bSDavid du Colombier tcb->snd.nxt = tcb->iss+1;
18119ef1f84bSDavid du Colombier tcb->snd.rxt = tcb->iss+1;
18129ef1f84bSDavid du Colombier tcb->flgcnt = 0;
18139ef1f84bSDavid du Colombier tcb->flags |= SYNACK;
18149ef1f84bSDavid du Colombier
181588bae168SDavid du Colombier /* set desired mss and scale */
181688bae168SDavid du Colombier tcb->mss = tcpmtu(s->p, dst, s->ipversion, &tcb->scale);
181788bae168SDavid du Colombier
18189ef1f84bSDavid du Colombier /* our sending max segment size cannot be bigger than what he asked for */
181988bae168SDavid du Colombier if(lp->mss != 0 && lp->mss < tcb->mss)
18209ef1f84bSDavid du Colombier tcb->mss = lp->mss;
18219ef1f84bSDavid du Colombier tpriv->stats[Mss] = tcb->mss;
18229ef1f84bSDavid du Colombier
18239ef1f84bSDavid du Colombier /* window scaling */
18249ef1f84bSDavid du Colombier tcpsetscale(new, tcb, lp->rcvscale, lp->sndscale);
18259ef1f84bSDavid du Colombier
18269ef1f84bSDavid du Colombier /* congestion window */
18279ef1f84bSDavid du Colombier tcb->snd.wnd = segp->wnd;
18289ef1f84bSDavid du Colombier initialwindow(tcb);
18299ef1f84bSDavid du Colombier
18309ef1f84bSDavid du Colombier /* set initial round trip time */
18319ef1f84bSDavid du Colombier tcb->sndsyntime = lp->lastsend+lp->rexmits*SYNACK_RXTIMER;
18329ef1f84bSDavid du Colombier tcpsynackrtt(new);
18339ef1f84bSDavid du Colombier
18349ef1f84bSDavid du Colombier free(lp);
18359ef1f84bSDavid du Colombier
18369ef1f84bSDavid du Colombier /* set up proto header */
18379ef1f84bSDavid du Colombier switch(version){
18389ef1f84bSDavid du Colombier case V4:
18399ef1f84bSDavid du Colombier h4 = &tcb->protohdr.tcp4hdr;
18409ef1f84bSDavid du Colombier memset(h4, 0, sizeof(*h4));
18419ef1f84bSDavid du Colombier h4->proto = IP_TCPPROTO;
18429ef1f84bSDavid du Colombier hnputs(h4->tcpsport, new->lport);
18439ef1f84bSDavid du Colombier hnputs(h4->tcpdport, new->rport);
18449ef1f84bSDavid du Colombier v6tov4(h4->tcpsrc, dst);
18459ef1f84bSDavid du Colombier v6tov4(h4->tcpdst, src);
18469ef1f84bSDavid du Colombier break;
18479ef1f84bSDavid du Colombier case V6:
18489ef1f84bSDavid du Colombier h6 = &tcb->protohdr.tcp6hdr;
18499ef1f84bSDavid du Colombier memset(h6, 0, sizeof(*h6));
18509ef1f84bSDavid du Colombier h6->proto = IP_TCPPROTO;
18519ef1f84bSDavid du Colombier hnputs(h6->tcpsport, new->lport);
18529ef1f84bSDavid du Colombier hnputs(h6->tcpdport, new->rport);
18539ef1f84bSDavid du Colombier ipmove(h6->tcpsrc, dst);
18549ef1f84bSDavid du Colombier ipmove(h6->tcpdst, src);
18559ef1f84bSDavid du Colombier break;
18569ef1f84bSDavid du Colombier default:
18579ef1f84bSDavid du Colombier panic("tcpincoming: version %d", new->ipversion);
18589ef1f84bSDavid du Colombier }
18599ef1f84bSDavid du Colombier
18609ef1f84bSDavid du Colombier tcpsetstate(new, Established);
18619ef1f84bSDavid du Colombier
18629ef1f84bSDavid du Colombier iphtadd(&tpriv->ht, new);
18639ef1f84bSDavid du Colombier
18649ef1f84bSDavid du Colombier return new;
18659ef1f84bSDavid du Colombier }
18669ef1f84bSDavid du Colombier
18679ef1f84bSDavid du Colombier static int
seq_within(ulong x,ulong low,ulong high)18689ef1f84bSDavid du Colombier seq_within(ulong x, ulong low, ulong high)
18699ef1f84bSDavid du Colombier {
18709ef1f84bSDavid du Colombier if(low <= high){
18719ef1f84bSDavid du Colombier if(low <= x && x <= high)
18729ef1f84bSDavid du Colombier return 1;
18739ef1f84bSDavid du Colombier }
18749ef1f84bSDavid du Colombier else {
18759ef1f84bSDavid du Colombier if(x >= low || x <= high)
18769ef1f84bSDavid du Colombier return 1;
18779ef1f84bSDavid du Colombier }
18789ef1f84bSDavid du Colombier return 0;
18799ef1f84bSDavid du Colombier }
18809ef1f84bSDavid du Colombier
18819ef1f84bSDavid du Colombier static int
seq_lt(ulong x,ulong y)18829ef1f84bSDavid du Colombier seq_lt(ulong x, ulong y)
18839ef1f84bSDavid du Colombier {
18849ef1f84bSDavid du Colombier return (int)(x-y) < 0;
18859ef1f84bSDavid du Colombier }
18869ef1f84bSDavid du Colombier
18879ef1f84bSDavid du Colombier static int
seq_le(ulong x,ulong y)18889ef1f84bSDavid du Colombier seq_le(ulong x, ulong y)
18899ef1f84bSDavid du Colombier {
18909ef1f84bSDavid du Colombier return (int)(x-y) <= 0;
18919ef1f84bSDavid du Colombier }
18929ef1f84bSDavid du Colombier
18939ef1f84bSDavid du Colombier static int
seq_gt(ulong x,ulong y)18949ef1f84bSDavid du Colombier seq_gt(ulong x, ulong y)
18959ef1f84bSDavid du Colombier {
18969ef1f84bSDavid du Colombier return (int)(x-y) > 0;
18979ef1f84bSDavid du Colombier }
18989ef1f84bSDavid du Colombier
18999ef1f84bSDavid du Colombier static int
seq_ge(ulong x,ulong y)19009ef1f84bSDavid du Colombier seq_ge(ulong x, ulong y)
19019ef1f84bSDavid du Colombier {
19029ef1f84bSDavid du Colombier return (int)(x-y) >= 0;
19039ef1f84bSDavid du Colombier }
19049ef1f84bSDavid du Colombier
19059ef1f84bSDavid du Colombier /*
19069ef1f84bSDavid du Colombier * use the time between the first SYN and it's ack as the
19079ef1f84bSDavid du Colombier * initial round trip time
19089ef1f84bSDavid du Colombier */
19099ef1f84bSDavid du Colombier static void
tcpsynackrtt(Conv * s)19109ef1f84bSDavid du Colombier tcpsynackrtt(Conv *s)
19119ef1f84bSDavid du Colombier {
19129ef1f84bSDavid du Colombier Tcpctl *tcb;
19139ef1f84bSDavid du Colombier int delta;
19149ef1f84bSDavid du Colombier Tcppriv *tpriv;
19159ef1f84bSDavid du Colombier
19169ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
19179ef1f84bSDavid du Colombier tpriv = s->p->priv;
19189ef1f84bSDavid du Colombier
19199ef1f84bSDavid du Colombier delta = NOW - tcb->sndsyntime;
19209ef1f84bSDavid du Colombier tcb->srtt = delta<<LOGAGAIN;
19219ef1f84bSDavid du Colombier tcb->mdev = delta<<LOGDGAIN;
19229ef1f84bSDavid du Colombier
19239ef1f84bSDavid du Colombier /* halt round trip timer */
19249ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
19259ef1f84bSDavid du Colombier }
19269ef1f84bSDavid du Colombier
19279ef1f84bSDavid du Colombier static void
update(Conv * s,Tcp * seg)19289ef1f84bSDavid du Colombier update(Conv *s, Tcp *seg)
19299ef1f84bSDavid du Colombier {
19309ef1f84bSDavid du Colombier int rtt, delta;
19319ef1f84bSDavid du Colombier Tcpctl *tcb;
19329ef1f84bSDavid du Colombier ulong acked;
19339ef1f84bSDavid du Colombier Tcppriv *tpriv;
19349ef1f84bSDavid du Colombier
19359ef1f84bSDavid du Colombier if(seg->update)
19369ef1f84bSDavid du Colombier return;
19379ef1f84bSDavid du Colombier seg->update = 1;
19389ef1f84bSDavid du Colombier
19399ef1f84bSDavid du Colombier tpriv = s->p->priv;
19409ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
19419ef1f84bSDavid du Colombier
19429ef1f84bSDavid du Colombier /* catch zero-window updates, update window & recover */
19439ef1f84bSDavid du Colombier if(tcb->snd.wnd == 0 && seg->wnd > 0 &&
19449ef1f84bSDavid du Colombier seq_lt(seg->ack, tcb->snd.ptr)){
19459ef1f84bSDavid du Colombier netlog(s->p->f, Logtcp, "tcp: zwu ack %lud una %lud ptr %lud win %lud\n",
19469ef1f84bSDavid du Colombier seg->ack, tcb->snd.una, tcb->snd.ptr, seg->wnd);
19479ef1f84bSDavid du Colombier tcb->snd.wnd = seg->wnd;
19489ef1f84bSDavid du Colombier goto recovery;
19499ef1f84bSDavid du Colombier }
19509ef1f84bSDavid du Colombier
19519ef1f84bSDavid du Colombier /* newreno fast retransmit */
19529ef1f84bSDavid du Colombier if(seg->ack == tcb->snd.una && tcb->snd.una != tcb->snd.nxt &&
19539ef1f84bSDavid du Colombier ++tcb->snd.dupacks == 3){ /* was TCPREXMTTHRESH */
19549ef1f84bSDavid du Colombier recovery:
19559ef1f84bSDavid du Colombier if(tcb->snd.recovery){
19569ef1f84bSDavid du Colombier tpriv->stats[RecoveryCwind]++;
19579ef1f84bSDavid du Colombier tcb->cwind += tcb->mss;
19589ef1f84bSDavid du Colombier }else if(seq_le(tcb->snd.rxt, seg->ack)){
19599ef1f84bSDavid du Colombier tpriv->stats[Recovery]++;
19609ef1f84bSDavid du Colombier tcb->abcbytes = 0;
19619ef1f84bSDavid du Colombier tcb->snd.recovery = 1;
19629ef1f84bSDavid du Colombier tcb->snd.partialack = 0;
19639ef1f84bSDavid du Colombier tcb->snd.rxt = tcb->snd.nxt;
19649ef1f84bSDavid du Colombier tcpcongestion(tcb);
19659ef1f84bSDavid du Colombier tcb->cwind = tcb->ssthresh + 3*tcb->mss;
19669ef1f84bSDavid du Colombier netlog(s->p->f, Logtcpwin, "recovery inflate %ld ss %ld @%lud\n",
19679ef1f84bSDavid du Colombier tcb->cwind, tcb->ssthresh, tcb->snd.rxt);
19689ef1f84bSDavid du Colombier tcprxmit(s);
19699ef1f84bSDavid du Colombier }else{
19709ef1f84bSDavid du Colombier tpriv->stats[RecoveryNoSeq]++;
19719ef1f84bSDavid du Colombier netlog(s->p->f, Logtcpwin, "!recov %lud not ≤ %lud %ld\n",
19729ef1f84bSDavid du Colombier tcb->snd.rxt, seg->ack, tcb->snd.rxt - seg->ack);
19739ef1f84bSDavid du Colombier /* don't enter fast retransmit, don't change ssthresh */
19749ef1f84bSDavid du Colombier }
19759ef1f84bSDavid du Colombier }else if(tcb->snd.recovery){
19769ef1f84bSDavid du Colombier tpriv->stats[RecoveryCwind]++;
19779ef1f84bSDavid du Colombier tcb->cwind += tcb->mss;
19789ef1f84bSDavid du Colombier }
19799ef1f84bSDavid du Colombier
19809ef1f84bSDavid du Colombier /*
19819ef1f84bSDavid du Colombier * update window
19829ef1f84bSDavid du Colombier */
19839ef1f84bSDavid du Colombier if(seq_gt(seg->ack, tcb->snd.wl2)
19849ef1f84bSDavid du Colombier || (tcb->snd.wl2 == seg->ack && seg->wnd > tcb->snd.wnd)){
19859ef1f84bSDavid du Colombier /* clear dupack if we advance wl2 */
19869ef1f84bSDavid du Colombier if(tcb->snd.wl2 != seg->ack)
19879ef1f84bSDavid du Colombier tcb->snd.dupacks = 0;
19889ef1f84bSDavid du Colombier tcb->snd.wnd = seg->wnd;
19899ef1f84bSDavid du Colombier tcb->snd.wl2 = seg->ack;
19909ef1f84bSDavid du Colombier }
19919ef1f84bSDavid du Colombier
19929ef1f84bSDavid du Colombier if(!seq_gt(seg->ack, tcb->snd.una)){
19939ef1f84bSDavid du Colombier /*
19949ef1f84bSDavid du Colombier * don't let us hangup if sending into a closed window and
19959ef1f84bSDavid du Colombier * we're still getting acks
19969ef1f84bSDavid du Colombier */
19979ef1f84bSDavid du Colombier if((tcb->flags&RETRAN) && tcb->snd.wnd == 0)
19989ef1f84bSDavid du Colombier tcb->backedoff = MAXBACKMS/4;
19999ef1f84bSDavid du Colombier return;
20009ef1f84bSDavid du Colombier }
20019ef1f84bSDavid du Colombier
20029ef1f84bSDavid du Colombier /* Compute the new send window size */
20039ef1f84bSDavid du Colombier acked = seg->ack - tcb->snd.una;
20049ef1f84bSDavid du Colombier
20059ef1f84bSDavid du Colombier /* avoid slow start and timers for SYN acks */
20069ef1f84bSDavid du Colombier if((tcb->flags & SYNACK) == 0) {
20079ef1f84bSDavid du Colombier tcb->flags |= SYNACK;
20089ef1f84bSDavid du Colombier acked--;
20099ef1f84bSDavid du Colombier tcb->flgcnt--;
20109ef1f84bSDavid du Colombier goto done;
20119ef1f84bSDavid du Colombier }
20129ef1f84bSDavid du Colombier
20139ef1f84bSDavid du Colombier /*
20149ef1f84bSDavid du Colombier * congestion control
20159ef1f84bSDavid du Colombier */
20169ef1f84bSDavid du Colombier if(tcb->snd.recovery){
20179ef1f84bSDavid du Colombier if(seq_ge(seg->ack, tcb->snd.rxt)){
20189ef1f84bSDavid du Colombier /* recovery finished; deflate window */
20199ef1f84bSDavid du Colombier tpriv->stats[RecoveryDone]++;
20209ef1f84bSDavid du Colombier tcb->snd.dupacks = 0;
20219ef1f84bSDavid du Colombier tcb->snd.recovery = 0;
20229ef1f84bSDavid du Colombier tcb->cwind = (tcb->snd.nxt - tcb->snd.una) + tcb->mss;
20239ef1f84bSDavid du Colombier if(tcb->ssthresh < tcb->cwind)
20249ef1f84bSDavid du Colombier tcb->cwind = tcb->ssthresh;
20259ef1f84bSDavid du Colombier netlog(s->p->f, Logtcpwin, "recovery deflate %ld %ld\n",
20269ef1f84bSDavid du Colombier tcb->cwind, tcb->ssthresh);
20279ef1f84bSDavid du Colombier } else {
20289ef1f84bSDavid du Colombier /* partial ack; we lost more than one segment */
20299ef1f84bSDavid du Colombier tpriv->stats[RecoveryPA]++;
20309ef1f84bSDavid du Colombier if(tcb->cwind > acked)
20319ef1f84bSDavid du Colombier tcb->cwind -= acked;
20329ef1f84bSDavid du Colombier else{
20339ef1f84bSDavid du Colombier netlog(s->p->f, Logtcpwin, "partial ack neg\n");
20349ef1f84bSDavid du Colombier tcb->cwind = tcb->mss;
20359ef1f84bSDavid du Colombier }
20369ef1f84bSDavid du Colombier netlog(s->p->f, Logtcpwin, "partial ack %ld left %ld cwind %ld\n",
20379ef1f84bSDavid du Colombier acked, tcb->snd.rxt - seg->ack, tcb->cwind);
20389ef1f84bSDavid du Colombier
20399ef1f84bSDavid du Colombier if(acked >= tcb->mss)
20409ef1f84bSDavid du Colombier tcb->cwind += tcb->mss;
20419ef1f84bSDavid du Colombier tcb->snd.partialack++;
20429ef1f84bSDavid du Colombier }
20439ef1f84bSDavid du Colombier } else
20449ef1f84bSDavid du Colombier tcpabcincr(tcb, acked);
20459ef1f84bSDavid du Colombier
20469ef1f84bSDavid du Colombier /* Adjust the timers according to the round trip time */
20479ef1f84bSDavid du Colombier /* TODO: fix sloppy treatment of overflow cases here. */
20489ef1f84bSDavid du Colombier if(tcb->rtt_timer.state == TcptimerON && seq_ge(seg->ack, tcb->rttseq)) {
20499ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
20509ef1f84bSDavid du Colombier if((tcb->flags&RETRAN) == 0) {
20519ef1f84bSDavid du Colombier tcb->backoff = 0;
20529ef1f84bSDavid du Colombier tcb->backedoff = 0;
20539ef1f84bSDavid du Colombier rtt = tcb->rtt_timer.start - tcb->rtt_timer.count;
20549ef1f84bSDavid du Colombier if(rtt == 0)
20559ef1f84bSDavid du Colombier rtt = 1; /* else all close sys's will rexmit in 0 time */
20569ef1f84bSDavid du Colombier rtt *= MSPTICK;
20579ef1f84bSDavid du Colombier if(tcb->srtt == 0) {
20589ef1f84bSDavid du Colombier tcb->srtt = rtt << LOGAGAIN;
20599ef1f84bSDavid du Colombier tcb->mdev = rtt << LOGDGAIN;
20609ef1f84bSDavid du Colombier } else {
20619ef1f84bSDavid du Colombier delta = rtt - (tcb->srtt>>LOGAGAIN);
20629ef1f84bSDavid du Colombier tcb->srtt += delta;
20639ef1f84bSDavid du Colombier if(tcb->srtt <= 0)
20649ef1f84bSDavid du Colombier tcb->srtt = 1;
20659ef1f84bSDavid du Colombier
20669ef1f84bSDavid du Colombier delta = abs(delta) - (tcb->mdev>>LOGDGAIN);
20679ef1f84bSDavid du Colombier tcb->mdev += delta;
20689ef1f84bSDavid du Colombier if(tcb->mdev <= 0)
20699ef1f84bSDavid du Colombier tcb->mdev = 1;
20709ef1f84bSDavid du Colombier }
20719ef1f84bSDavid du Colombier tcpsettimer(tcb);
20729ef1f84bSDavid du Colombier }
20739ef1f84bSDavid du Colombier }
20749ef1f84bSDavid du Colombier
20759ef1f84bSDavid du Colombier done:
20769ef1f84bSDavid du Colombier if(qdiscard(s->wq, acked) < acked)
20779ef1f84bSDavid du Colombier tcb->flgcnt--;
20789ef1f84bSDavid du Colombier tcb->snd.una = seg->ack;
20799ef1f84bSDavid du Colombier
20809ef1f84bSDavid du Colombier /* newreno fast recovery */
20819ef1f84bSDavid du Colombier if(tcb->snd.recovery)
20829ef1f84bSDavid du Colombier tcprxmit(s);
20839ef1f84bSDavid du Colombier
20849ef1f84bSDavid du Colombier if(seq_gt(seg->ack, tcb->snd.urg))
20859ef1f84bSDavid du Colombier tcb->snd.urg = seg->ack;
20869ef1f84bSDavid du Colombier
20879ef1f84bSDavid du Colombier if(tcb->snd.una != tcb->snd.nxt){
20889ef1f84bSDavid du Colombier /* `impatient' variant */
20899ef1f84bSDavid du Colombier if(!tcb->snd.recovery || tcb->snd.partialack == 1){
20909ef1f84bSDavid du Colombier tcb->time = NOW;
20919ef1f84bSDavid du Colombier tcb->timeuna = tcb->snd.una;
20929ef1f84bSDavid du Colombier tcpgo(tpriv, &tcb->timer);
20939ef1f84bSDavid du Colombier }
20949ef1f84bSDavid du Colombier } else
20959ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->timer);
20969ef1f84bSDavid du Colombier
20979ef1f84bSDavid du Colombier if(seq_lt(tcb->snd.ptr, tcb->snd.una))
20989ef1f84bSDavid du Colombier tcb->snd.ptr = tcb->snd.una;
20999ef1f84bSDavid du Colombier
21009ef1f84bSDavid du Colombier if(!tcb->snd.recovery)
21019ef1f84bSDavid du Colombier tcb->flags &= ~RETRAN;
21029ef1f84bSDavid du Colombier tcb->backoff = 0;
21039ef1f84bSDavid du Colombier tcb->backedoff = 0;
21049ef1f84bSDavid du Colombier }
21059ef1f84bSDavid du Colombier
21069ef1f84bSDavid du Colombier static void
tcpiput(Proto * tcp,Ipifc *,Block * bp)21079ef1f84bSDavid du Colombier tcpiput(Proto *tcp, Ipifc*, Block *bp)
21089ef1f84bSDavid du Colombier {
21099ef1f84bSDavid du Colombier Tcp seg;
21109ef1f84bSDavid du Colombier Tcp4hdr *h4;
21119ef1f84bSDavid du Colombier Tcp6hdr *h6;
21129ef1f84bSDavid du Colombier int hdrlen;
21139ef1f84bSDavid du Colombier Tcpctl *tcb;
21149ef1f84bSDavid du Colombier ushort length, csum;
21159ef1f84bSDavid du Colombier uchar source[IPaddrlen], dest[IPaddrlen];
21169ef1f84bSDavid du Colombier Conv *s;
21179ef1f84bSDavid du Colombier Fs *f;
21189ef1f84bSDavid du Colombier Tcppriv *tpriv;
21199ef1f84bSDavid du Colombier uchar version;
21209ef1f84bSDavid du Colombier
21219ef1f84bSDavid du Colombier f = tcp->f;
21229ef1f84bSDavid du Colombier tpriv = tcp->priv;
21239ef1f84bSDavid du Colombier
21249ef1f84bSDavid du Colombier tpriv->stats[InSegs]++;
21259ef1f84bSDavid du Colombier
21269ef1f84bSDavid du Colombier h4 = (Tcp4hdr*)(bp->rp);
21279ef1f84bSDavid du Colombier h6 = (Tcp6hdr*)(bp->rp);
21289ef1f84bSDavid du Colombier
21299ef1f84bSDavid du Colombier if((h4->vihl&0xF0)==IP_VER4) {
21309ef1f84bSDavid du Colombier version = V4;
21319ef1f84bSDavid du Colombier length = nhgets(h4->length);
21329ef1f84bSDavid du Colombier v4tov6(dest, h4->tcpdst);
21339ef1f84bSDavid du Colombier v4tov6(source, h4->tcpsrc);
21349ef1f84bSDavid du Colombier
21359ef1f84bSDavid du Colombier h4->Unused = 0;
21369ef1f84bSDavid du Colombier hnputs(h4->tcplen, length-TCP4_PKT);
21379ef1f84bSDavid du Colombier if(!(bp->flag & Btcpck) && (h4->tcpcksum[0] || h4->tcpcksum[1]) &&
21389ef1f84bSDavid du Colombier ptclcsum(bp, TCP4_IPLEN, length-TCP4_IPLEN)) {
21399ef1f84bSDavid du Colombier tpriv->stats[CsumErrs]++;
21409ef1f84bSDavid du Colombier tpriv->stats[InErrs]++;
21419ef1f84bSDavid du Colombier netlog(f, Logtcp, "bad tcp proto cksum\n");
21429ef1f84bSDavid du Colombier freeblist(bp);
21439ef1f84bSDavid du Colombier return;
21449ef1f84bSDavid du Colombier }
21459ef1f84bSDavid du Colombier
21469ef1f84bSDavid du Colombier hdrlen = ntohtcp4(&seg, &bp);
21479ef1f84bSDavid du Colombier if(hdrlen < 0){
21489ef1f84bSDavid du Colombier tpriv->stats[HlenErrs]++;
21499ef1f84bSDavid du Colombier tpriv->stats[InErrs]++;
21509ef1f84bSDavid du Colombier netlog(f, Logtcp, "bad tcp hdr len\n");
21519ef1f84bSDavid du Colombier return;
21529ef1f84bSDavid du Colombier }
21539ef1f84bSDavid du Colombier
21549ef1f84bSDavid du Colombier /* trim the packet to the size claimed by the datagram */
21559ef1f84bSDavid du Colombier length -= hdrlen+TCP4_PKT;
21569ef1f84bSDavid du Colombier bp = trimblock(bp, hdrlen+TCP4_PKT, length);
21579ef1f84bSDavid du Colombier if(bp == nil){
21589ef1f84bSDavid du Colombier tpriv->stats[LenErrs]++;
21599ef1f84bSDavid du Colombier tpriv->stats[InErrs]++;
21609ef1f84bSDavid du Colombier netlog(f, Logtcp, "tcp len < 0 after trim\n");
21619ef1f84bSDavid du Colombier return;
21629ef1f84bSDavid du Colombier }
21639ef1f84bSDavid du Colombier }
21649ef1f84bSDavid du Colombier else {
21659ef1f84bSDavid du Colombier int ttl = h6->ttl;
21669ef1f84bSDavid du Colombier int proto = h6->proto;
21679ef1f84bSDavid du Colombier
21689ef1f84bSDavid du Colombier version = V6;
21699ef1f84bSDavid du Colombier length = nhgets(h6->ploadlen);
21709ef1f84bSDavid du Colombier ipmove(dest, h6->tcpdst);
21719ef1f84bSDavid du Colombier ipmove(source, h6->tcpsrc);
21729ef1f84bSDavid du Colombier
21739ef1f84bSDavid du Colombier h6->ploadlen[0] = h6->ploadlen[1] = h6->proto = 0;
21749ef1f84bSDavid du Colombier h6->ttl = proto;
21759ef1f84bSDavid du Colombier hnputl(h6->vcf, length);
21769ef1f84bSDavid du Colombier if((h6->tcpcksum[0] || h6->tcpcksum[1]) &&
21779ef1f84bSDavid du Colombier (csum = ptclcsum(bp, TCP6_IPLEN, length+TCP6_PHDRSIZE)) != 0) {
21789ef1f84bSDavid du Colombier tpriv->stats[CsumErrs]++;
21799ef1f84bSDavid du Colombier tpriv->stats[InErrs]++;
21809ef1f84bSDavid du Colombier netlog(f, Logtcp,
21819ef1f84bSDavid du Colombier "bad tcpv6 proto cksum: got %#ux, computed %#ux\n",
21829ef1f84bSDavid du Colombier h6->tcpcksum[0]<<8 | h6->tcpcksum[1], csum);
21839ef1f84bSDavid du Colombier freeblist(bp);
21849ef1f84bSDavid du Colombier return;
21859ef1f84bSDavid du Colombier }
21869ef1f84bSDavid du Colombier h6->ttl = ttl;
21879ef1f84bSDavid du Colombier h6->proto = proto;
21889ef1f84bSDavid du Colombier hnputs(h6->ploadlen, length);
21899ef1f84bSDavid du Colombier
21909ef1f84bSDavid du Colombier hdrlen = ntohtcp6(&seg, &bp);
21919ef1f84bSDavid du Colombier if(hdrlen < 0){
21929ef1f84bSDavid du Colombier tpriv->stats[HlenErrs]++;
21939ef1f84bSDavid du Colombier tpriv->stats[InErrs]++;
21949ef1f84bSDavid du Colombier netlog(f, Logtcp, "bad tcpv6 hdr len\n");
21959ef1f84bSDavid du Colombier return;
21969ef1f84bSDavid du Colombier }
21979ef1f84bSDavid du Colombier
21989ef1f84bSDavid du Colombier /* trim the packet to the size claimed by the datagram */
21999ef1f84bSDavid du Colombier length -= hdrlen;
22009ef1f84bSDavid du Colombier bp = trimblock(bp, hdrlen+TCP6_PKT, length);
22019ef1f84bSDavid du Colombier if(bp == nil){
22029ef1f84bSDavid du Colombier tpriv->stats[LenErrs]++;
22039ef1f84bSDavid du Colombier tpriv->stats[InErrs]++;
22049ef1f84bSDavid du Colombier netlog(f, Logtcp, "tcpv6 len < 0 after trim\n");
22059ef1f84bSDavid du Colombier return;
22069ef1f84bSDavid du Colombier }
22079ef1f84bSDavid du Colombier }
22089ef1f84bSDavid du Colombier
22099ef1f84bSDavid du Colombier /* lock protocol while searching for a conversation */
22109ef1f84bSDavid du Colombier qlock(tcp);
22119ef1f84bSDavid du Colombier
22129ef1f84bSDavid du Colombier /* Look for a matching conversation */
22139ef1f84bSDavid du Colombier s = iphtlook(&tpriv->ht, source, seg.source, dest, seg.dest);
22149ef1f84bSDavid du Colombier if(s == nil){
22159ef1f84bSDavid du Colombier netlog(f, Logtcp, "iphtlook(src %I!%d, dst %I!%d) failed\n",
22169ef1f84bSDavid du Colombier source, seg.source, dest, seg.dest);
22179ef1f84bSDavid du Colombier reset:
22189ef1f84bSDavid du Colombier qunlock(tcp);
22199ef1f84bSDavid du Colombier sndrst(tcp, source, dest, length, &seg, version, "no conversation");
22209ef1f84bSDavid du Colombier freeblist(bp);
22219ef1f84bSDavid du Colombier return;
22229ef1f84bSDavid du Colombier }
22239ef1f84bSDavid du Colombier
22249ef1f84bSDavid du Colombier /* if it's a listener, look for the right flags and get a new conv */
22259ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
22269ef1f84bSDavid du Colombier if(tcb->state == Listen){
22279ef1f84bSDavid du Colombier if(seg.flags & RST){
22289ef1f84bSDavid du Colombier limborst(s, &seg, source, dest, version);
22299ef1f84bSDavid du Colombier qunlock(tcp);
22309ef1f84bSDavid du Colombier freeblist(bp);
22319ef1f84bSDavid du Colombier return;
22329ef1f84bSDavid du Colombier }
22339ef1f84bSDavid du Colombier
22349ef1f84bSDavid du Colombier /* if this is a new SYN, put the call into limbo */
22359ef1f84bSDavid du Colombier if((seg.flags & SYN) && (seg.flags & ACK) == 0){
22369ef1f84bSDavid du Colombier limbo(s, source, dest, &seg, version);
22379ef1f84bSDavid du Colombier qunlock(tcp);
22389ef1f84bSDavid du Colombier freeblist(bp);
22399ef1f84bSDavid du Colombier return;
22409ef1f84bSDavid du Colombier }
22419ef1f84bSDavid du Colombier
22429ef1f84bSDavid du Colombier /*
22439ef1f84bSDavid du Colombier * if there's a matching call in limbo, tcpincoming will
22449ef1f84bSDavid du Colombier * return it in state Syn_received
22459ef1f84bSDavid du Colombier */
22469ef1f84bSDavid du Colombier s = tcpincoming(s, &seg, source, dest, version);
22479ef1f84bSDavid du Colombier if(s == nil)
22489ef1f84bSDavid du Colombier goto reset;
22499ef1f84bSDavid du Colombier }
22509ef1f84bSDavid du Colombier
22519ef1f84bSDavid du Colombier /* The rest of the input state machine is run with the control block
22529ef1f84bSDavid du Colombier * locked and implements the state machine directly out of the RFC.
22539ef1f84bSDavid du Colombier * Out-of-band data is ignored - it was always a bad idea.
22549ef1f84bSDavid du Colombier */
22559ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
22569ef1f84bSDavid du Colombier if(waserror()){
22579ef1f84bSDavid du Colombier qunlock(s);
22589ef1f84bSDavid du Colombier nexterror();
22599ef1f84bSDavid du Colombier }
22609ef1f84bSDavid du Colombier qlock(s);
22619ef1f84bSDavid du Colombier qunlock(tcp);
22629ef1f84bSDavid du Colombier
22639ef1f84bSDavid du Colombier /* fix up window */
22649ef1f84bSDavid du Colombier seg.wnd <<= tcb->rcv.scale;
22659ef1f84bSDavid du Colombier
22669ef1f84bSDavid du Colombier /* every input packet in puts off the keep alive time out */
22679ef1f84bSDavid du Colombier tcpsetkacounter(tcb);
22689ef1f84bSDavid du Colombier
22699ef1f84bSDavid du Colombier switch(tcb->state) {
22709ef1f84bSDavid du Colombier case Closed:
22719ef1f84bSDavid du Colombier sndrst(tcp, source, dest, length, &seg, version, "sending to Closed");
22729ef1f84bSDavid du Colombier goto raise;
22739ef1f84bSDavid du Colombier case Syn_sent:
22749ef1f84bSDavid du Colombier if(seg.flags & ACK) {
22759ef1f84bSDavid du Colombier if(!seq_within(seg.ack, tcb->iss+1, tcb->snd.nxt)) {
22769ef1f84bSDavid du Colombier sndrst(tcp, source, dest, length, &seg, version,
22779ef1f84bSDavid du Colombier "bad seq in Syn_sent");
22789ef1f84bSDavid du Colombier goto raise;
22799ef1f84bSDavid du Colombier }
22809ef1f84bSDavid du Colombier }
22819ef1f84bSDavid du Colombier if(seg.flags & RST) {
22829ef1f84bSDavid du Colombier if(seg.flags & ACK)
22839ef1f84bSDavid du Colombier localclose(s, Econrefused);
22849ef1f84bSDavid du Colombier goto raise;
22859ef1f84bSDavid du Colombier }
22869ef1f84bSDavid du Colombier
22879ef1f84bSDavid du Colombier if(seg.flags & SYN) {
22889ef1f84bSDavid du Colombier procsyn(s, &seg);
22899ef1f84bSDavid du Colombier if(seg.flags & ACK){
22909ef1f84bSDavid du Colombier update(s, &seg);
22919ef1f84bSDavid du Colombier tcpsynackrtt(s);
22929ef1f84bSDavid du Colombier tcpsetstate(s, Established);
22939ef1f84bSDavid du Colombier tcpsetscale(s, tcb, seg.ws, tcb->scale);
22949ef1f84bSDavid du Colombier }
22959ef1f84bSDavid du Colombier else {
22969ef1f84bSDavid du Colombier tcb->time = NOW;
22979ef1f84bSDavid du Colombier tcpsetstate(s, Syn_received); /* DLP - shouldn't this be a reset? */
22989ef1f84bSDavid du Colombier }
22999ef1f84bSDavid du Colombier
23009ef1f84bSDavid du Colombier if(length != 0 || (seg.flags & FIN))
23019ef1f84bSDavid du Colombier break;
23029ef1f84bSDavid du Colombier
23039ef1f84bSDavid du Colombier freeblist(bp);
23049ef1f84bSDavid du Colombier goto output;
23059ef1f84bSDavid du Colombier }
23069ef1f84bSDavid du Colombier else
23079ef1f84bSDavid du Colombier freeblist(bp);
23089ef1f84bSDavid du Colombier
23099ef1f84bSDavid du Colombier qunlock(s);
23109ef1f84bSDavid du Colombier poperror();
23119ef1f84bSDavid du Colombier return;
23129ef1f84bSDavid du Colombier case Syn_received:
23139ef1f84bSDavid du Colombier /* doesn't matter if it's the correct ack, we're just trying to set timing */
23149ef1f84bSDavid du Colombier if(seg.flags & ACK)
23159ef1f84bSDavid du Colombier tcpsynackrtt(s);
23169ef1f84bSDavid du Colombier break;
23179ef1f84bSDavid du Colombier }
23189ef1f84bSDavid du Colombier
23199ef1f84bSDavid du Colombier /*
23209ef1f84bSDavid du Colombier * One DOS attack is to open connections to us and then forget about them,
23219ef1f84bSDavid du Colombier * thereby tying up a conv at no long term cost to the attacker.
23229ef1f84bSDavid du Colombier * This is an attempt to defeat these stateless DOS attacks. See
23239ef1f84bSDavid du Colombier * corresponding code in tcpsendka().
23249ef1f84bSDavid du Colombier */
23259ef1f84bSDavid du Colombier if(tcb->state != Syn_received && (seg.flags & RST) == 0){
23269ef1f84bSDavid du Colombier if(tcpporthogdefense
23279ef1f84bSDavid du Colombier && seq_within(seg.ack, tcb->snd.una-(1<<31), tcb->snd.una-(1<<29))){
23289ef1f84bSDavid du Colombier print("stateless hog %I.%d->%I.%d f %ux %lux - %lux - %lux\n",
23299ef1f84bSDavid du Colombier source, seg.source, dest, seg.dest, seg.flags,
23309ef1f84bSDavid du Colombier tcb->snd.una-(1<<31), seg.ack, tcb->snd.una-(1<<29));
23319ef1f84bSDavid du Colombier localclose(s, "stateless hog");
23329ef1f84bSDavid du Colombier }
23339ef1f84bSDavid du Colombier }
23349ef1f84bSDavid du Colombier
23359ef1f84bSDavid du Colombier /* Cut the data to fit the receive window */
23369ef1f84bSDavid du Colombier tcprcvwin(s);
23379ef1f84bSDavid du Colombier if(tcptrim(tcb, &seg, &bp, &length) == -1) {
23389ef1f84bSDavid du Colombier if(seg.seq+1 != tcb->rcv.nxt || length != 1)
23399ef1f84bSDavid du Colombier netlog(f, Logtcp, "tcp: trim: !inwind: seq %lud-%lud win "
23409ef1f84bSDavid du Colombier "%lud-%lud l %d from %I\n", seg.seq,
23419ef1f84bSDavid du Colombier seg.seq + length - 1, tcb->rcv.nxt,
23429ef1f84bSDavid du Colombier tcb->rcv.nxt + tcb->rcv.wnd-1, length, s->raddr);
23439ef1f84bSDavid du Colombier update(s, &seg);
23449ef1f84bSDavid du Colombier if(qlen(s->wq)+tcb->flgcnt == 0 && tcb->state == Closing) {
23459ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
23469ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
23479ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->katimer);
23489ef1f84bSDavid du Colombier tcpsetstate(s, Time_wait);
23499ef1f84bSDavid du Colombier tcb->timer.start = MSL2*(1000 / MSPTICK);
23509ef1f84bSDavid du Colombier tcpgo(tpriv, &tcb->timer);
23519ef1f84bSDavid du Colombier }
23529ef1f84bSDavid du Colombier if(!(seg.flags & RST)) {
23539ef1f84bSDavid du Colombier tcb->flags |= FORCE;
23549ef1f84bSDavid du Colombier goto output;
23559ef1f84bSDavid du Colombier }
23569ef1f84bSDavid du Colombier qunlock(s);
23579ef1f84bSDavid du Colombier poperror();
23589ef1f84bSDavid du Colombier return;
23599ef1f84bSDavid du Colombier }
23609ef1f84bSDavid du Colombier
23619ef1f84bSDavid du Colombier /* Cannot accept so answer with a rst */
23629ef1f84bSDavid du Colombier if(length && tcb->state == Closed) {
23639ef1f84bSDavid du Colombier sndrst(tcp, source, dest, length, &seg, version, "sending to Closed");
23649ef1f84bSDavid du Colombier goto raise;
23659ef1f84bSDavid du Colombier }
23669ef1f84bSDavid du Colombier
23679ef1f84bSDavid du Colombier /* The segment is beyond the current receive pointer so
23689ef1f84bSDavid du Colombier * queue the data in the resequence queue
23699ef1f84bSDavid du Colombier */
23709ef1f84bSDavid du Colombier if(seg.seq != tcb->rcv.nxt)
23719ef1f84bSDavid du Colombier if(length != 0 || (seg.flags & (SYN|FIN))) {
23729ef1f84bSDavid du Colombier update(s, &seg);
23739ef1f84bSDavid du Colombier if(addreseq(f, tcb, tpriv, &seg, bp, length) < 0)
23749ef1f84bSDavid du Colombier print("reseq %I.%d -> %I.%d\n", s->raddr, s->rport,
23759ef1f84bSDavid du Colombier s->laddr, s->lport);
23769ef1f84bSDavid du Colombier tcb->flags |= FORCE; /* force duplicate ack; RFC 5681 §3.2 */
23779ef1f84bSDavid du Colombier goto output;
23789ef1f84bSDavid du Colombier }
23799ef1f84bSDavid du Colombier
23809ef1f84bSDavid du Colombier if(tcb->nreseq > 0)
23819ef1f84bSDavid du Colombier tcb->flags |= FORCE; /* filled hole in seq. space; RFC 5681 §3.2 */
23829ef1f84bSDavid du Colombier
23839ef1f84bSDavid du Colombier /*
23849ef1f84bSDavid du Colombier * keep looping till we've processed this packet plus any
23859ef1f84bSDavid du Colombier * adjacent packets in the resequence queue
23869ef1f84bSDavid du Colombier */
23879ef1f84bSDavid du Colombier for(;;) {
23889ef1f84bSDavid du Colombier if(seg.flags & RST) {
23899ef1f84bSDavid du Colombier if(tcb->state == Established) {
23909ef1f84bSDavid du Colombier tpriv->stats[EstabResets]++;
23919ef1f84bSDavid du Colombier if(tcb->rcv.nxt != seg.seq)
23929ef1f84bSDavid du Colombier print("out of order RST rcvd: %I.%d -> "
23939ef1f84bSDavid du Colombier "%I.%d, rcv.nxt %lux seq %lux\n",
23949ef1f84bSDavid du Colombier s->raddr, s->rport, s->laddr,
23959ef1f84bSDavid du Colombier s->lport, tcb->rcv.nxt, seg.seq);
23969ef1f84bSDavid du Colombier }
23979ef1f84bSDavid du Colombier localclose(s, Econrefused);
23989ef1f84bSDavid du Colombier goto raise;
23999ef1f84bSDavid du Colombier }
24009ef1f84bSDavid du Colombier
24019ef1f84bSDavid du Colombier if((seg.flags&ACK) == 0)
24029ef1f84bSDavid du Colombier goto raise;
24039ef1f84bSDavid du Colombier
24049ef1f84bSDavid du Colombier switch(tcb->state) {
24059ef1f84bSDavid du Colombier case Syn_received:
24069ef1f84bSDavid du Colombier if(!seq_within(seg.ack, tcb->snd.una+1, tcb->snd.nxt)){
24079ef1f84bSDavid du Colombier sndrst(tcp, source, dest, length, &seg, version,
24089ef1f84bSDavid du Colombier "bad seq in Syn_received");
24099ef1f84bSDavid du Colombier goto raise;
24109ef1f84bSDavid du Colombier }
24119ef1f84bSDavid du Colombier update(s, &seg);
24129ef1f84bSDavid du Colombier tcpsetstate(s, Established);
24139ef1f84bSDavid du Colombier case Established:
24149ef1f84bSDavid du Colombier case Close_wait:
24159ef1f84bSDavid du Colombier update(s, &seg);
24169ef1f84bSDavid du Colombier break;
24179ef1f84bSDavid du Colombier case Finwait1:
24189ef1f84bSDavid du Colombier update(s, &seg);
24199ef1f84bSDavid du Colombier if(qlen(s->wq)+tcb->flgcnt == 0){
24209ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
24219ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
24229ef1f84bSDavid du Colombier tcpsetkacounter(tcb);
24239ef1f84bSDavid du Colombier tcb->time = NOW;
24249ef1f84bSDavid du Colombier tcpsetstate(s, Finwait2);
24259ef1f84bSDavid du Colombier tcb->katimer.start = MSL2 * (1000 / MSPTICK);
24269ef1f84bSDavid du Colombier tcpgo(tpriv, &tcb->katimer);
24279ef1f84bSDavid du Colombier }
24289ef1f84bSDavid du Colombier break;
24299ef1f84bSDavid du Colombier case Finwait2:
24309ef1f84bSDavid du Colombier update(s, &seg);
24319ef1f84bSDavid du Colombier break;
24329ef1f84bSDavid du Colombier case Closing:
24339ef1f84bSDavid du Colombier update(s, &seg);
24349ef1f84bSDavid du Colombier if(qlen(s->wq)+tcb->flgcnt == 0) {
24359ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
24369ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
24379ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->katimer);
24389ef1f84bSDavid du Colombier tcpsetstate(s, Time_wait);
24399ef1f84bSDavid du Colombier tcb->timer.start = MSL2*(1000 / MSPTICK);
24409ef1f84bSDavid du Colombier tcpgo(tpriv, &tcb->timer);
24419ef1f84bSDavid du Colombier }
24429ef1f84bSDavid du Colombier break;
24439ef1f84bSDavid du Colombier case Last_ack:
24449ef1f84bSDavid du Colombier update(s, &seg);
24459ef1f84bSDavid du Colombier if(qlen(s->wq)+tcb->flgcnt == 0) {
24469ef1f84bSDavid du Colombier localclose(s, nil);
24479ef1f84bSDavid du Colombier goto raise;
24489ef1f84bSDavid du Colombier }
24499ef1f84bSDavid du Colombier case Time_wait:
24509ef1f84bSDavid du Colombier tcb->flags |= FORCE;
24519ef1f84bSDavid du Colombier if(tcb->timer.state != TcptimerON)
24529ef1f84bSDavid du Colombier tcpgo(tpriv, &tcb->timer);
24539ef1f84bSDavid du Colombier }
24549ef1f84bSDavid du Colombier
24559ef1f84bSDavid du Colombier if((seg.flags&URG) && seg.urg) {
24569ef1f84bSDavid du Colombier if(seq_gt(seg.urg + seg.seq, tcb->rcv.urg)) {
24579ef1f84bSDavid du Colombier tcb->rcv.urg = seg.urg + seg.seq;
24589ef1f84bSDavid du Colombier pullblock(&bp, seg.urg);
24599ef1f84bSDavid du Colombier }
24609ef1f84bSDavid du Colombier }
24619ef1f84bSDavid du Colombier else
24629ef1f84bSDavid du Colombier if(seq_gt(tcb->rcv.nxt, tcb->rcv.urg))
24639ef1f84bSDavid du Colombier tcb->rcv.urg = tcb->rcv.nxt;
24649ef1f84bSDavid du Colombier
24659ef1f84bSDavid du Colombier if(length == 0) {
24669ef1f84bSDavid du Colombier if(bp != nil)
24679ef1f84bSDavid du Colombier freeblist(bp);
24689ef1f84bSDavid du Colombier }
24699ef1f84bSDavid du Colombier else {
24709ef1f84bSDavid du Colombier switch(tcb->state){
24719ef1f84bSDavid du Colombier default:
24729ef1f84bSDavid du Colombier /* Ignore segment text */
24739ef1f84bSDavid du Colombier if(bp != nil)
24749ef1f84bSDavid du Colombier freeblist(bp);
24759ef1f84bSDavid du Colombier break;
24769ef1f84bSDavid du Colombier
24779ef1f84bSDavid du Colombier case Syn_received:
24789ef1f84bSDavid du Colombier case Established:
24799ef1f84bSDavid du Colombier case Finwait1:
24809ef1f84bSDavid du Colombier /* If we still have some data place on
24819ef1f84bSDavid du Colombier * receive queue
24829ef1f84bSDavid du Colombier */
24839ef1f84bSDavid du Colombier if(bp) {
24849ef1f84bSDavid du Colombier bp = packblock(bp);
24859ef1f84bSDavid du Colombier if(bp == nil)
24869ef1f84bSDavid du Colombier panic("tcp packblock");
24879ef1f84bSDavid du Colombier qpassnolim(s->rq, bp);
24889ef1f84bSDavid du Colombier bp = nil;
24899ef1f84bSDavid du Colombier }
24909ef1f84bSDavid du Colombier tcb->rcv.nxt += length;
24919ef1f84bSDavid du Colombier
24929ef1f84bSDavid du Colombier /*
24939ef1f84bSDavid du Colombier * turn on the acktimer if there's something
24949ef1f84bSDavid du Colombier * to ack
24959ef1f84bSDavid du Colombier */
24969ef1f84bSDavid du Colombier if(tcb->acktimer.state != TcptimerON)
24979ef1f84bSDavid du Colombier tcpgo(tpriv, &tcb->acktimer);
24989ef1f84bSDavid du Colombier
24999ef1f84bSDavid du Colombier break;
25009ef1f84bSDavid du Colombier case Finwait2:
25019ef1f84bSDavid du Colombier /* no process to read the data, send a reset */
25029ef1f84bSDavid du Colombier if(bp != nil)
25039ef1f84bSDavid du Colombier freeblist(bp);
25049ef1f84bSDavid du Colombier sndrst(tcp, source, dest, length, &seg, version,
25059ef1f84bSDavid du Colombier "send to Finwait2");
25069ef1f84bSDavid du Colombier qunlock(s);
25079ef1f84bSDavid du Colombier poperror();
25089ef1f84bSDavid du Colombier return;
25099ef1f84bSDavid du Colombier }
25109ef1f84bSDavid du Colombier }
25119ef1f84bSDavid du Colombier
25129ef1f84bSDavid du Colombier if(seg.flags & FIN) {
25139ef1f84bSDavid du Colombier tcb->flags |= FORCE;
25149ef1f84bSDavid du Colombier
25159ef1f84bSDavid du Colombier switch(tcb->state) {
25169ef1f84bSDavid du Colombier case Syn_received:
25179ef1f84bSDavid du Colombier case Established:
25189ef1f84bSDavid du Colombier tcb->rcv.nxt++;
25199ef1f84bSDavid du Colombier tcpsetstate(s, Close_wait);
25209ef1f84bSDavid du Colombier break;
25219ef1f84bSDavid du Colombier case Finwait1:
25229ef1f84bSDavid du Colombier tcb->rcv.nxt++;
25239ef1f84bSDavid du Colombier if(qlen(s->wq)+tcb->flgcnt == 0) {
25249ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
25259ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
25269ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->katimer);
25279ef1f84bSDavid du Colombier tcpsetstate(s, Time_wait);
25289ef1f84bSDavid du Colombier tcb->timer.start = MSL2*(1000/MSPTICK);
25299ef1f84bSDavid du Colombier tcpgo(tpriv, &tcb->timer);
25309ef1f84bSDavid du Colombier }
25319ef1f84bSDavid du Colombier else
25329ef1f84bSDavid du Colombier tcpsetstate(s, Closing);
25339ef1f84bSDavid du Colombier break;
25349ef1f84bSDavid du Colombier case Finwait2:
25359ef1f84bSDavid du Colombier tcb->rcv.nxt++;
25369ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->rtt_timer);
25379ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
25389ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->katimer);
25399ef1f84bSDavid du Colombier tcpsetstate(s, Time_wait);
25409ef1f84bSDavid du Colombier tcb->timer.start = MSL2 * (1000/MSPTICK);
25419ef1f84bSDavid du Colombier tcpgo(tpriv, &tcb->timer);
25429ef1f84bSDavid du Colombier break;
25439ef1f84bSDavid du Colombier case Close_wait:
25449ef1f84bSDavid du Colombier case Closing:
25459ef1f84bSDavid du Colombier case Last_ack:
25469ef1f84bSDavid du Colombier break;
25479ef1f84bSDavid du Colombier case Time_wait:
25489ef1f84bSDavid du Colombier tcpgo(tpriv, &tcb->timer);
25499ef1f84bSDavid du Colombier break;
25509ef1f84bSDavid du Colombier }
25519ef1f84bSDavid du Colombier }
25529ef1f84bSDavid du Colombier
25539ef1f84bSDavid du Colombier /*
25549ef1f84bSDavid du Colombier * get next adjacent segment from the resequence queue.
25559ef1f84bSDavid du Colombier * dump/trim any overlapping segments
25569ef1f84bSDavid du Colombier */
25579ef1f84bSDavid du Colombier for(;;) {
25589ef1f84bSDavid du Colombier if(tcb->reseq == nil)
25599ef1f84bSDavid du Colombier goto output;
25609ef1f84bSDavid du Colombier
25619ef1f84bSDavid du Colombier if(seq_ge(tcb->rcv.nxt, tcb->reseq->seg.seq) == 0)
25629ef1f84bSDavid du Colombier goto output;
25639ef1f84bSDavid du Colombier
25649ef1f84bSDavid du Colombier getreseq(tcb, &seg, &bp, &length);
25659ef1f84bSDavid du Colombier
25669ef1f84bSDavid du Colombier tcprcvwin(s);
25679ef1f84bSDavid du Colombier if(tcptrim(tcb, &seg, &bp, &length) == 0){
25689ef1f84bSDavid du Colombier tcb->flags |= FORCE;
25699ef1f84bSDavid du Colombier break;
25709ef1f84bSDavid du Colombier }
25719ef1f84bSDavid du Colombier }
25729ef1f84bSDavid du Colombier }
25739ef1f84bSDavid du Colombier output:
25749ef1f84bSDavid du Colombier tcpoutput(s);
25759ef1f84bSDavid du Colombier qunlock(s);
25769ef1f84bSDavid du Colombier poperror();
25779ef1f84bSDavid du Colombier return;
25789ef1f84bSDavid du Colombier raise:
25799ef1f84bSDavid du Colombier qunlock(s);
25809ef1f84bSDavid du Colombier poperror();
25819ef1f84bSDavid du Colombier freeblist(bp);
25829ef1f84bSDavid du Colombier tcpkick(s);
25839ef1f84bSDavid du Colombier }
25849ef1f84bSDavid du Colombier
25859ef1f84bSDavid du Colombier /*
25869ef1f84bSDavid du Colombier * always enters and exits with the s locked. We drop
25879ef1f84bSDavid du Colombier * the lock to ipoput the packet so some care has to be
25889ef1f84bSDavid du Colombier * taken by callers.
25899ef1f84bSDavid du Colombier */
25909ef1f84bSDavid du Colombier static void
tcpoutput(Conv * s)25919ef1f84bSDavid du Colombier tcpoutput(Conv *s)
25929ef1f84bSDavid du Colombier {
25939ef1f84bSDavid du Colombier Tcp seg;
25949ef1f84bSDavid du Colombier uint msgs;
25959ef1f84bSDavid du Colombier Tcpctl *tcb;
25969ef1f84bSDavid du Colombier Block *hbp, *bp;
25979ef1f84bSDavid du Colombier int sndcnt;
25989ef1f84bSDavid du Colombier ulong ssize, dsize, sent;
25999ef1f84bSDavid du Colombier Fs *f;
26009ef1f84bSDavid du Colombier Tcppriv *tpriv;
26019ef1f84bSDavid du Colombier uchar version;
26029ef1f84bSDavid du Colombier
26039ef1f84bSDavid du Colombier f = s->p->f;
26049ef1f84bSDavid du Colombier tpriv = s->p->priv;
26059ef1f84bSDavid du Colombier version = s->ipversion;
26069ef1f84bSDavid du Colombier
26079ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
26089ef1f84bSDavid du Colombier
26099ef1f84bSDavid du Colombier /* force ack every 2*mss */
26109ef1f84bSDavid du Colombier if((tcb->flags & FORCE) == 0 &&
26119ef1f84bSDavid du Colombier tcb->rcv.nxt - tcb->rcv.ackptr >= 2*tcb->mss){
26129ef1f84bSDavid du Colombier tpriv->stats[Delayack]++;
26139ef1f84bSDavid du Colombier tcb->flags |= FORCE;
26149ef1f84bSDavid du Colombier }
26159ef1f84bSDavid du Colombier
26169ef1f84bSDavid du Colombier /* force ack if window opening */
26179ef1f84bSDavid du Colombier if((tcb->flags & FORCE) == 0){
26189ef1f84bSDavid du Colombier tcprcvwin(s);
26199ef1f84bSDavid du Colombier if((int)(tcb->rcv.wptr - tcb->rcv.wsnt) >= 2*tcb->mss){
26209ef1f84bSDavid du Colombier tpriv->stats[Wopenack]++;
26219ef1f84bSDavid du Colombier tcb->flags |= FORCE;
26229ef1f84bSDavid du Colombier }
26239ef1f84bSDavid du Colombier }
26249ef1f84bSDavid du Colombier
26259ef1f84bSDavid du Colombier for(msgs = 0; msgs < 100; msgs++) {
26269ef1f84bSDavid du Colombier switch(tcb->state) {
26279ef1f84bSDavid du Colombier case Listen:
26289ef1f84bSDavid du Colombier case Closed:
26299ef1f84bSDavid du Colombier case Finwait2:
26309ef1f84bSDavid du Colombier return;
26319ef1f84bSDavid du Colombier }
26329ef1f84bSDavid du Colombier
26339ef1f84bSDavid du Colombier /* Don't send anything else until our SYN has been acked */
26349ef1f84bSDavid du Colombier if(tcb->snd.ptr != tcb->iss && (tcb->flags & SYNACK) == 0)
26359ef1f84bSDavid du Colombier break;
26369ef1f84bSDavid du Colombier
26379ef1f84bSDavid du Colombier /* force an ack when a window has opened up */
26389ef1f84bSDavid du Colombier tcprcvwin(s);
26399ef1f84bSDavid du Colombier if(tcb->rcv.blocked && tcb->rcv.wnd > 0){
26409ef1f84bSDavid du Colombier tcb->rcv.blocked = 0;
26419ef1f84bSDavid du Colombier tcb->flags |= FORCE;
26429ef1f84bSDavid du Colombier }
26439ef1f84bSDavid du Colombier
26449ef1f84bSDavid du Colombier sndcnt = qlen(s->wq)+tcb->flgcnt;
26459ef1f84bSDavid du Colombier sent = tcb->snd.ptr - tcb->snd.una;
26469ef1f84bSDavid du Colombier ssize = sndcnt;
26479ef1f84bSDavid du Colombier if(tcb->snd.wnd == 0){
26489ef1f84bSDavid du Colombier /* zero window probe */
26499ef1f84bSDavid du Colombier if(sent > 0 && !(tcb->flags & FORCE))
26509ef1f84bSDavid du Colombier break; /* already probing, rto re-probes */
26519ef1f84bSDavid du Colombier if(ssize < sent)
26529ef1f84bSDavid du Colombier ssize = 0;
26539ef1f84bSDavid du Colombier else{
26549ef1f84bSDavid du Colombier ssize -= sent;
26559ef1f84bSDavid du Colombier if(ssize > 0)
26569ef1f84bSDavid du Colombier ssize = 1;
26579ef1f84bSDavid du Colombier }
26589ef1f84bSDavid du Colombier } else {
26599ef1f84bSDavid du Colombier /* calculate usable segment size */
26609ef1f84bSDavid du Colombier if(ssize > tcb->cwind)
26619ef1f84bSDavid du Colombier ssize = tcb->cwind;
26629ef1f84bSDavid du Colombier if(ssize > tcb->snd.wnd)
26639ef1f84bSDavid du Colombier ssize = tcb->snd.wnd;
26649ef1f84bSDavid du Colombier
26659ef1f84bSDavid du Colombier if(ssize < sent)
26669ef1f84bSDavid du Colombier ssize = 0;
26679ef1f84bSDavid du Colombier else {
26689ef1f84bSDavid du Colombier ssize -= sent;
26699ef1f84bSDavid du Colombier if(ssize > tcb->mss)
26709ef1f84bSDavid du Colombier ssize = tcb->mss;
26719ef1f84bSDavid du Colombier }
26729ef1f84bSDavid du Colombier }
26739ef1f84bSDavid du Colombier
26749ef1f84bSDavid du Colombier dsize = ssize;
26759ef1f84bSDavid du Colombier seg.urg = 0;
26769ef1f84bSDavid du Colombier
26779ef1f84bSDavid du Colombier if(!(tcb->flags & FORCE))
26789ef1f84bSDavid du Colombier if(ssize == 0 ||
26799ef1f84bSDavid du Colombier ssize < tcb->mss && tcb->snd.nxt == tcb->snd.ptr &&
26809ef1f84bSDavid du Colombier sent > TCPREXMTTHRESH * tcb->mss)
26819ef1f84bSDavid du Colombier break;
26829ef1f84bSDavid du Colombier
26839ef1f84bSDavid du Colombier tcb->flags &= ~FORCE;
26849ef1f84bSDavid du Colombier
26859ef1f84bSDavid du Colombier /* By default we will generate an ack */
26869ef1f84bSDavid du Colombier tcphalt(tpriv, &tcb->acktimer);
26879ef1f84bSDavid du Colombier seg.source = s->lport;
26889ef1f84bSDavid du Colombier seg.dest = s->rport;
26899ef1f84bSDavid du Colombier seg.flags = ACK;
26909ef1f84bSDavid du Colombier seg.mss = 0;
26919ef1f84bSDavid du Colombier seg.ws = 0;
26929ef1f84bSDavid du Colombier seg.update = 0;
26939ef1f84bSDavid du Colombier switch(tcb->state){
26949ef1f84bSDavid du Colombier case Syn_sent:
26959ef1f84bSDavid du Colombier seg.flags = 0;
26969ef1f84bSDavid du Colombier if(tcb->snd.ptr == tcb->iss){
26979ef1f84bSDavid du Colombier seg.flags |= SYN;
26989ef1f84bSDavid du Colombier dsize--;
26999ef1f84bSDavid du Colombier seg.mss = tcb->mss;
27009ef1f84bSDavid du Colombier seg.ws = tcb->scale;
27019ef1f84bSDavid du Colombier }
27029ef1f84bSDavid du Colombier break;
27039ef1f84bSDavid du Colombier case Syn_received:
27049ef1f84bSDavid du Colombier /*
27059ef1f84bSDavid du Colombier * don't send any data with a SYN/ACK packet
27069ef1f84bSDavid du Colombier * because Linux rejects the packet in its
27079ef1f84bSDavid du Colombier * attempt to solve the SYN attack problem
27089ef1f84bSDavid du Colombier */
27099ef1f84bSDavid du Colombier if(tcb->snd.ptr == tcb->iss){
27109ef1f84bSDavid du Colombier seg.flags |= SYN;
27119ef1f84bSDavid du Colombier dsize = 0;
27129ef1f84bSDavid du Colombier ssize = 1;
27139ef1f84bSDavid du Colombier seg.mss = tcb->mss;
27149ef1f84bSDavid du Colombier seg.ws = tcb->scale;
27159ef1f84bSDavid du Colombier }
27169ef1f84bSDavid du Colombier break;
27179ef1f84bSDavid du Colombier }
27189ef1f84bSDavid du Colombier seg.seq = tcb->snd.ptr;
27199ef1f84bSDavid du Colombier seg.ack = tcb->rcv.nxt;
27209ef1f84bSDavid du Colombier seg.wnd = tcb->rcv.wnd;
27219ef1f84bSDavid du Colombier
27229ef1f84bSDavid du Colombier /* Pull out data to send */
27239ef1f84bSDavid du Colombier bp = nil;
27249ef1f84bSDavid du Colombier if(dsize != 0) {
27259ef1f84bSDavid du Colombier bp = qcopy(s->wq, dsize, sent);
27269ef1f84bSDavid du Colombier if(BLEN(bp) != dsize) {
27279ef1f84bSDavid du Colombier seg.flags |= FIN;
27289ef1f84bSDavid du Colombier dsize--;
27299ef1f84bSDavid du Colombier }
27309ef1f84bSDavid du Colombier }
27319ef1f84bSDavid du Colombier
27329ef1f84bSDavid du Colombier if(sent+dsize == sndcnt && dsize)
27339ef1f84bSDavid du Colombier seg.flags |= PSH;
27349ef1f84bSDavid du Colombier
27359ef1f84bSDavid du Colombier tcb->snd.ptr += ssize;
27369ef1f84bSDavid du Colombier
27379ef1f84bSDavid du Colombier /* Pull up the send pointer so we can accept acks
27389ef1f84bSDavid du Colombier * for this window
27399ef1f84bSDavid du Colombier */
27409ef1f84bSDavid du Colombier if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
27419ef1f84bSDavid du Colombier tcb->snd.nxt = tcb->snd.ptr;
27429ef1f84bSDavid du Colombier
27439ef1f84bSDavid du Colombier /* Build header, link data and compute cksum */
27449ef1f84bSDavid du Colombier switch(version){
27459ef1f84bSDavid du Colombier case V4:
27469ef1f84bSDavid du Colombier tcb->protohdr.tcp4hdr.vihl = IP_VER4;
27479ef1f84bSDavid du Colombier hbp = htontcp4(&seg, bp, &tcb->protohdr.tcp4hdr, tcb);
27489ef1f84bSDavid du Colombier if(hbp == nil) {
27499ef1f84bSDavid du Colombier freeblist(bp);
27509ef1f84bSDavid du Colombier return;
27519ef1f84bSDavid du Colombier }
27529ef1f84bSDavid du Colombier break;
27539ef1f84bSDavid du Colombier case V6:
27549ef1f84bSDavid du Colombier tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
27559ef1f84bSDavid du Colombier hbp = htontcp6(&seg, bp, &tcb->protohdr.tcp6hdr, tcb);
27569ef1f84bSDavid du Colombier if(hbp == nil) {
27579ef1f84bSDavid du Colombier freeblist(bp);
27589ef1f84bSDavid du Colombier return;
27599ef1f84bSDavid du Colombier }
27609ef1f84bSDavid du Colombier break;
27619ef1f84bSDavid du Colombier default:
27629ef1f84bSDavid du Colombier hbp = nil; /* to suppress a warning */
27639ef1f84bSDavid du Colombier panic("tcpoutput: version %d", version);
27649ef1f84bSDavid du Colombier }
27659ef1f84bSDavid du Colombier
27669ef1f84bSDavid du Colombier /* Start the transmission timers if there is new data and we
27679ef1f84bSDavid du Colombier * expect acknowledges
27689ef1f84bSDavid du Colombier */
27699ef1f84bSDavid du Colombier if(ssize != 0){
27709ef1f84bSDavid du Colombier if(tcb->timer.state != TcptimerON){
27719ef1f84bSDavid du Colombier tcb->time = NOW;
27729ef1f84bSDavid du Colombier tcb->timeuna = tcb->snd.una;
27739ef1f84bSDavid du Colombier tcpgo(tpriv, &tcb->timer);
27749ef1f84bSDavid du Colombier }
27759ef1f84bSDavid du Colombier
27769ef1f84bSDavid du Colombier /* If round trip timer isn't running, start it.
27779ef1f84bSDavid du Colombier * measure the longest packet only in case the
27789ef1f84bSDavid du Colombier * transmission time dominates RTT
27799ef1f84bSDavid du Colombier */
27809ef1f84bSDavid du Colombier if(tcb->snd.retransmit == 0)
27819ef1f84bSDavid du Colombier if(tcb->rtt_timer.state != TcptimerON)
27829ef1f84bSDavid du Colombier if(ssize == tcb->mss) {
27839ef1f84bSDavid du Colombier tcpgo(tpriv, &tcb->rtt_timer);
27849ef1f84bSDavid du Colombier tcb->rttseq = tcb->snd.ptr;
27859ef1f84bSDavid du Colombier }
27869ef1f84bSDavid du Colombier }
27879ef1f84bSDavid du Colombier
27889ef1f84bSDavid du Colombier tpriv->stats[OutSegs]++;
27899ef1f84bSDavid du Colombier if(tcb->snd.retransmit)
27909ef1f84bSDavid du Colombier tpriv->stats[RetransSegsSent]++;
27919ef1f84bSDavid du Colombier tcb->rcv.ackptr = seg.ack;
27929ef1f84bSDavid du Colombier tcb->rcv.wsnt = tcb->rcv.wptr;
27939ef1f84bSDavid du Colombier
27949ef1f84bSDavid du Colombier /* put off the next keep alive */
27959ef1f84bSDavid du Colombier tcpgo(tpriv, &tcb->katimer);
27969ef1f84bSDavid du Colombier
27979ef1f84bSDavid du Colombier switch(version){
27989ef1f84bSDavid du Colombier case V4:
27999ef1f84bSDavid du Colombier if(ipoput4(f, hbp, 0, s->ttl, s->tos, s) < 0){
28009ef1f84bSDavid du Colombier /* a negative return means no route */
28019ef1f84bSDavid du Colombier localclose(s, "no route");
28029ef1f84bSDavid du Colombier }
28039ef1f84bSDavid du Colombier break;
28049ef1f84bSDavid du Colombier case V6:
28059ef1f84bSDavid du Colombier if(ipoput6(f, hbp, 0, s->ttl, s->tos, s) < 0){
28069ef1f84bSDavid du Colombier /* a negative return means no route */
28079ef1f84bSDavid du Colombier localclose(s, "no route");
28089ef1f84bSDavid du Colombier }
28099ef1f84bSDavid du Colombier break;
28109ef1f84bSDavid du Colombier default:
28119ef1f84bSDavid du Colombier panic("tcpoutput2: version %d", version);
28129ef1f84bSDavid du Colombier }
28139ef1f84bSDavid du Colombier if((msgs%4) == 3){
28149ef1f84bSDavid du Colombier qunlock(s);
28159ef1f84bSDavid du Colombier qlock(s);
28169ef1f84bSDavid du Colombier }
28179ef1f84bSDavid du Colombier }
28189ef1f84bSDavid du Colombier }
28199ef1f84bSDavid du Colombier
28209ef1f84bSDavid du Colombier /*
28219ef1f84bSDavid du Colombier * the BSD convention (hack?) for keep alives. resend last uchar acked.
28229ef1f84bSDavid du Colombier */
28239ef1f84bSDavid du Colombier static void
tcpsendka(Conv * s)28249ef1f84bSDavid du Colombier tcpsendka(Conv *s)
28259ef1f84bSDavid du Colombier {
28269ef1f84bSDavid du Colombier Tcp seg;
28279ef1f84bSDavid du Colombier Tcpctl *tcb;
28289ef1f84bSDavid du Colombier Block *hbp,*dbp;
28299ef1f84bSDavid du Colombier
28309ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
28319ef1f84bSDavid du Colombier
28329ef1f84bSDavid du Colombier dbp = nil;
28339ef1f84bSDavid du Colombier memset(&seg, 0, sizeof seg);
28349ef1f84bSDavid du Colombier seg.urg = 0;
28359ef1f84bSDavid du Colombier seg.source = s->lport;
28369ef1f84bSDavid du Colombier seg.dest = s->rport;
28379ef1f84bSDavid du Colombier seg.flags = ACK|PSH;
28389ef1f84bSDavid du Colombier seg.mss = 0;
28399ef1f84bSDavid du Colombier seg.ws = 0;
28409ef1f84bSDavid du Colombier if(tcpporthogdefense)
28419ef1f84bSDavid du Colombier seg.seq = tcb->snd.una-(1<<30)-nrand(1<<20);
28429ef1f84bSDavid du Colombier else
28439ef1f84bSDavid du Colombier seg.seq = tcb->snd.una-1;
28449ef1f84bSDavid du Colombier seg.ack = tcb->rcv.nxt;
28459ef1f84bSDavid du Colombier tcb->rcv.ackptr = seg.ack;
28469ef1f84bSDavid du Colombier tcprcvwin(s);
28479ef1f84bSDavid du Colombier seg.wnd = tcb->rcv.wnd;
28489ef1f84bSDavid du Colombier if(tcb->state == Finwait2){
28499ef1f84bSDavid du Colombier seg.flags |= FIN;
28509ef1f84bSDavid du Colombier } else {
28519ef1f84bSDavid du Colombier dbp = allocb(1);
28529ef1f84bSDavid du Colombier dbp->wp++;
28539ef1f84bSDavid du Colombier }
28549ef1f84bSDavid du Colombier
28559ef1f84bSDavid du Colombier if(isv4(s->raddr)) {
28569ef1f84bSDavid du Colombier /* Build header, link data and compute cksum */
28579ef1f84bSDavid du Colombier tcb->protohdr.tcp4hdr.vihl = IP_VER4;
28589ef1f84bSDavid du Colombier hbp = htontcp4(&seg, dbp, &tcb->protohdr.tcp4hdr, tcb);
28599ef1f84bSDavid du Colombier if(hbp == nil) {
28609ef1f84bSDavid du Colombier freeblist(dbp);
28619ef1f84bSDavid du Colombier return;
28629ef1f84bSDavid du Colombier }
28639ef1f84bSDavid du Colombier ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s);
28649ef1f84bSDavid du Colombier }
28659ef1f84bSDavid du Colombier else {
28669ef1f84bSDavid du Colombier /* Build header, link data and compute cksum */
28679ef1f84bSDavid du Colombier tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
28689ef1f84bSDavid du Colombier hbp = htontcp6(&seg, dbp, &tcb->protohdr.tcp6hdr, tcb);
28699ef1f84bSDavid du Colombier if(hbp == nil) {
28709ef1f84bSDavid du Colombier freeblist(dbp);
28719ef1f84bSDavid du Colombier return;
28729ef1f84bSDavid du Colombier }
28739ef1f84bSDavid du Colombier ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s);
28749ef1f84bSDavid du Colombier }
28759ef1f84bSDavid du Colombier }
28769ef1f84bSDavid du Colombier
28779ef1f84bSDavid du Colombier /*
28789ef1f84bSDavid du Colombier * set connection to time out after 12 minutes
28799ef1f84bSDavid du Colombier */
28809ef1f84bSDavid du Colombier static void
tcpsetkacounter(Tcpctl * tcb)28819ef1f84bSDavid du Colombier tcpsetkacounter(Tcpctl *tcb)
28829ef1f84bSDavid du Colombier {
28839ef1f84bSDavid du Colombier tcb->kacounter = (12 * 60 * 1000) / (tcb->katimer.start*MSPTICK);
28849ef1f84bSDavid du Colombier if(tcb->kacounter < 3)
28859ef1f84bSDavid du Colombier tcb->kacounter = 3;
28869ef1f84bSDavid du Colombier }
28879ef1f84bSDavid du Colombier
28889ef1f84bSDavid du Colombier /*
28899ef1f84bSDavid du Colombier * if we've timed out, close the connection
28909ef1f84bSDavid du Colombier * otherwise, send a keepalive and restart the timer
28919ef1f84bSDavid du Colombier */
28929ef1f84bSDavid du Colombier static void
tcpkeepalive(void * v)28939ef1f84bSDavid du Colombier tcpkeepalive(void *v)
28949ef1f84bSDavid du Colombier {
28959ef1f84bSDavid du Colombier Tcpctl *tcb;
28969ef1f84bSDavid du Colombier Conv *s;
28979ef1f84bSDavid du Colombier
28989ef1f84bSDavid du Colombier s = v;
28999ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
29009ef1f84bSDavid du Colombier if(waserror()){
29019ef1f84bSDavid du Colombier qunlock(s);
29029ef1f84bSDavid du Colombier nexterror();
29039ef1f84bSDavid du Colombier }
29049ef1f84bSDavid du Colombier qlock(s);
29059ef1f84bSDavid du Colombier if(tcb->state != Closed){
29069ef1f84bSDavid du Colombier if(--(tcb->kacounter) <= 0) {
29079ef1f84bSDavid du Colombier localclose(s, Etimedout);
29089ef1f84bSDavid du Colombier } else {
29099ef1f84bSDavid du Colombier tcpsendka(s);
29109ef1f84bSDavid du Colombier tcpgo(s->p->priv, &tcb->katimer);
29119ef1f84bSDavid du Colombier }
29129ef1f84bSDavid du Colombier }
29139ef1f84bSDavid du Colombier qunlock(s);
29149ef1f84bSDavid du Colombier poperror();
29159ef1f84bSDavid du Colombier }
29169ef1f84bSDavid du Colombier
29179ef1f84bSDavid du Colombier /*
29189ef1f84bSDavid du Colombier * start keepalive timer
29199ef1f84bSDavid du Colombier */
29209ef1f84bSDavid du Colombier static char*
tcpstartka(Conv * s,char ** f,int n)29219ef1f84bSDavid du Colombier tcpstartka(Conv *s, char **f, int n)
29229ef1f84bSDavid du Colombier {
29239ef1f84bSDavid du Colombier Tcpctl *tcb;
29249ef1f84bSDavid du Colombier int x;
29259ef1f84bSDavid du Colombier
29269ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
29279ef1f84bSDavid du Colombier if(tcb->state != Established)
29289ef1f84bSDavid du Colombier return "connection must be in Establised state";
29299ef1f84bSDavid du Colombier if(n > 1){
29309ef1f84bSDavid du Colombier x = atoi(f[1]);
29319ef1f84bSDavid du Colombier if(x >= MSPTICK)
29329ef1f84bSDavid du Colombier tcb->katimer.start = x/MSPTICK;
29339ef1f84bSDavid du Colombier }
29349ef1f84bSDavid du Colombier tcpsetkacounter(tcb);
29359ef1f84bSDavid du Colombier tcpgo(s->p->priv, &tcb->katimer);
29369ef1f84bSDavid du Colombier
29379ef1f84bSDavid du Colombier return nil;
29389ef1f84bSDavid du Colombier }
29399ef1f84bSDavid du Colombier
29409ef1f84bSDavid du Colombier /*
29419ef1f84bSDavid du Colombier * turn checksums on/off
29429ef1f84bSDavid du Colombier */
29439ef1f84bSDavid du Colombier static char*
tcpsetchecksum(Conv * s,char ** f,int)29449ef1f84bSDavid du Colombier tcpsetchecksum(Conv *s, char **f, int)
29459ef1f84bSDavid du Colombier {
29469ef1f84bSDavid du Colombier Tcpctl *tcb;
29479ef1f84bSDavid du Colombier
29489ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
29499ef1f84bSDavid du Colombier tcb->nochecksum = !atoi(f[1]);
29509ef1f84bSDavid du Colombier
29519ef1f84bSDavid du Colombier return nil;
29529ef1f84bSDavid du Colombier }
29539ef1f84bSDavid du Colombier
29549ef1f84bSDavid du Colombier /*
29559ef1f84bSDavid du Colombier * retransmit (at most) one segment at snd.una.
29569ef1f84bSDavid du Colombier * preserve cwind & snd.ptr
29579ef1f84bSDavid du Colombier */
29589ef1f84bSDavid du Colombier static void
tcprxmit(Conv * s)29599ef1f84bSDavid du Colombier tcprxmit(Conv *s)
29609ef1f84bSDavid du Colombier {
29619ef1f84bSDavid du Colombier Tcpctl *tcb;
29629ef1f84bSDavid du Colombier Tcppriv *tpriv;
29639ef1f84bSDavid du Colombier ulong tcwind, tptr;
29649ef1f84bSDavid du Colombier
29659ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
29669ef1f84bSDavid du Colombier tcb->flags |= RETRAN|FORCE;
29679ef1f84bSDavid du Colombier
29689ef1f84bSDavid du Colombier tptr = tcb->snd.ptr;
29699ef1f84bSDavid du Colombier tcwind = tcb->cwind;
29709ef1f84bSDavid du Colombier tcb->snd.ptr = tcb->snd.una;
29719ef1f84bSDavid du Colombier tcb->cwind = tcb->mss;
29729ef1f84bSDavid du Colombier tcb->snd.retransmit = 1;
29739ef1f84bSDavid du Colombier tcpoutput(s);
29749ef1f84bSDavid du Colombier tcb->snd.retransmit = 0;
29759ef1f84bSDavid du Colombier tcb->cwind = tcwind;
29769ef1f84bSDavid du Colombier tcb->snd.ptr = tptr;
29779ef1f84bSDavid du Colombier
29789ef1f84bSDavid du Colombier tpriv = s->p->priv;
29799ef1f84bSDavid du Colombier tpriv->stats[RetransSegs]++;
29809ef1f84bSDavid du Colombier }
29819ef1f84bSDavid du Colombier
29829ef1f84bSDavid du Colombier /*
29839ef1f84bSDavid du Colombier * TODO: RFC 4138 F-RTO
29849ef1f84bSDavid du Colombier */
29859ef1f84bSDavid du Colombier static void
tcptimeout(void * arg)29869ef1f84bSDavid du Colombier tcptimeout(void *arg)
29879ef1f84bSDavid du Colombier {
29889ef1f84bSDavid du Colombier Conv *s;
29899ef1f84bSDavid du Colombier Tcpctl *tcb;
29909ef1f84bSDavid du Colombier int maxback;
29919ef1f84bSDavid du Colombier Tcppriv *tpriv;
29929ef1f84bSDavid du Colombier
29939ef1f84bSDavid du Colombier s = (Conv*)arg;
29949ef1f84bSDavid du Colombier tpriv = s->p->priv;
29959ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
29969ef1f84bSDavid du Colombier
29979ef1f84bSDavid du Colombier if(waserror()){
29989ef1f84bSDavid du Colombier qunlock(s);
29999ef1f84bSDavid du Colombier nexterror();
30009ef1f84bSDavid du Colombier }
30019ef1f84bSDavid du Colombier qlock(s);
30029ef1f84bSDavid du Colombier switch(tcb->state){
30039ef1f84bSDavid du Colombier default:
30049ef1f84bSDavid du Colombier tcb->backoff++;
30059ef1f84bSDavid du Colombier if(tcb->state == Syn_sent)
30069ef1f84bSDavid du Colombier maxback = MAXBACKMS/2;
30079ef1f84bSDavid du Colombier else
30089ef1f84bSDavid du Colombier maxback = MAXBACKMS;
30099ef1f84bSDavid du Colombier tcb->backedoff += tcb->timer.start * MSPTICK;
30109ef1f84bSDavid du Colombier if(tcb->backedoff >= maxback) {
30119ef1f84bSDavid du Colombier localclose(s, Etimedout);
30129ef1f84bSDavid du Colombier break;
30139ef1f84bSDavid du Colombier }
30149ef1f84bSDavid du Colombier netlog(s->p->f, Logtcprxmt, "rxm %d/%d %ldms %lud rto %d %lud %s\n",
30159ef1f84bSDavid du Colombier tcb->srtt, tcb->mdev, NOW - tcb->time,
30169ef1f84bSDavid du Colombier tcb->snd.una - tcb->timeuna, tcb->snd.rto, tcb->snd.ptr,
30179ef1f84bSDavid du Colombier tcpstates[s->state]);
30189ef1f84bSDavid du Colombier tcpsettimer(tcb);
30199ef1f84bSDavid du Colombier if(tcb->snd.rto == 0)
30209ef1f84bSDavid du Colombier tcpcongestion(tcb);
30219ef1f84bSDavid du Colombier tcprxmit(s);
30229ef1f84bSDavid du Colombier tcb->snd.ptr = tcb->snd.una;
30239ef1f84bSDavid du Colombier tcb->cwind = tcb->mss;
30249ef1f84bSDavid du Colombier tcb->snd.rto = 1;
30259ef1f84bSDavid du Colombier tpriv->stats[RetransTimeouts]++;
30269ef1f84bSDavid du Colombier
30279ef1f84bSDavid du Colombier if(tcb->snd.recovery){
30289ef1f84bSDavid du Colombier tcb->snd.dupacks = 0; /* reno rto */
30299ef1f84bSDavid du Colombier tcb->snd.recovery = 0;
30309ef1f84bSDavid du Colombier tpriv->stats[RecoveryRTO]++;
30319ef1f84bSDavid du Colombier tcb->snd.rxt = tcb->snd.nxt;
30329ef1f84bSDavid du Colombier netlog(s->p->f, Logtcpwin,
30339ef1f84bSDavid du Colombier "rto recovery rxt @%lud\n", tcb->snd.nxt);
30349ef1f84bSDavid du Colombier }
30359ef1f84bSDavid du Colombier
30369ef1f84bSDavid du Colombier tcb->abcbytes = 0;
30379ef1f84bSDavid du Colombier break;
30389ef1f84bSDavid du Colombier case Time_wait:
30399ef1f84bSDavid du Colombier localclose(s, nil);
30409ef1f84bSDavid du Colombier break;
30419ef1f84bSDavid du Colombier case Closed:
30429ef1f84bSDavid du Colombier break;
30439ef1f84bSDavid du Colombier }
30449ef1f84bSDavid du Colombier qunlock(s);
30459ef1f84bSDavid du Colombier poperror();
30469ef1f84bSDavid du Colombier }
30479ef1f84bSDavid du Colombier
30489ef1f84bSDavid du Colombier static int
inwindow(Tcpctl * tcb,int seq)30499ef1f84bSDavid du Colombier inwindow(Tcpctl *tcb, int seq)
30509ef1f84bSDavid du Colombier {
30519ef1f84bSDavid du Colombier return seq_within(seq, tcb->rcv.nxt, tcb->rcv.nxt+tcb->rcv.wnd-1);
30529ef1f84bSDavid du Colombier }
30539ef1f84bSDavid du Colombier
30549ef1f84bSDavid du Colombier /*
30559ef1f84bSDavid du Colombier * set up state for a received SYN (or SYN ACK) packet
30569ef1f84bSDavid du Colombier */
30579ef1f84bSDavid du Colombier static void
procsyn(Conv * s,Tcp * seg)30589ef1f84bSDavid du Colombier procsyn(Conv *s, Tcp *seg)
30599ef1f84bSDavid du Colombier {
30609ef1f84bSDavid du Colombier Tcpctl *tcb;
30619ef1f84bSDavid du Colombier Tcppriv *tpriv;
30629ef1f84bSDavid du Colombier
30639ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
30649ef1f84bSDavid du Colombier tcb->flags |= FORCE;
30659ef1f84bSDavid du Colombier
30669ef1f84bSDavid du Colombier tcb->rcv.nxt = seg->seq + 1;
30679ef1f84bSDavid du Colombier tcb->rcv.wptr = tcb->rcv.nxt;
30689ef1f84bSDavid du Colombier tcb->rcv.wsnt = 0;
30699ef1f84bSDavid du Colombier tcb->rcv.urg = tcb->rcv.nxt;
30709ef1f84bSDavid du Colombier tcb->irs = seg->seq;
30719ef1f84bSDavid du Colombier
30729ef1f84bSDavid du Colombier /* our sending max segment size cannot be bigger than what he asked for */
30739ef1f84bSDavid du Colombier if(seg->mss != 0 && seg->mss < tcb->mss) {
30749ef1f84bSDavid du Colombier tcb->mss = seg->mss;
30759ef1f84bSDavid du Colombier tpriv = s->p->priv;
30769ef1f84bSDavid du Colombier tpriv->stats[Mss] = tcb->mss;
30779ef1f84bSDavid du Colombier }
30789ef1f84bSDavid du Colombier
30799ef1f84bSDavid du Colombier tcb->snd.wnd = seg->wnd;
30809ef1f84bSDavid du Colombier initialwindow(tcb);
30819ef1f84bSDavid du Colombier }
30829ef1f84bSDavid du Colombier
30839ef1f84bSDavid du Colombier static int
dumpreseq(Tcpctl * tcb)30849ef1f84bSDavid du Colombier dumpreseq(Tcpctl *tcb)
30859ef1f84bSDavid du Colombier {
30869ef1f84bSDavid du Colombier Reseq *r, *next;
30879ef1f84bSDavid du Colombier
30889ef1f84bSDavid du Colombier for(r = tcb->reseq; r != nil; r = next){
30899ef1f84bSDavid du Colombier next = r->next;
30909ef1f84bSDavid du Colombier freeblist(r->bp);
30919ef1f84bSDavid du Colombier free(r);
30929ef1f84bSDavid du Colombier }
30939ef1f84bSDavid du Colombier tcb->reseq = nil;
30949ef1f84bSDavid du Colombier tcb->nreseq = 0;
30959ef1f84bSDavid du Colombier tcb->reseqlen = 0;
30969ef1f84bSDavid du Colombier return -1;
30979ef1f84bSDavid du Colombier }
30989ef1f84bSDavid du Colombier
30999ef1f84bSDavid du Colombier static void
logreseq(Fs * f,Reseq * r,ulong n)31009ef1f84bSDavid du Colombier logreseq(Fs *f, Reseq *r, ulong n)
31019ef1f84bSDavid du Colombier {
31029ef1f84bSDavid du Colombier char *s;
31039ef1f84bSDavid du Colombier
31049ef1f84bSDavid du Colombier for(; r != nil; r = r->next){
31059ef1f84bSDavid du Colombier s = nil;
31069ef1f84bSDavid du Colombier if(r->next == nil && r->seg.seq != n)
31079ef1f84bSDavid du Colombier s = "hole/end";
31089ef1f84bSDavid du Colombier else if(r->next == nil)
31099ef1f84bSDavid du Colombier s = "end";
31109ef1f84bSDavid du Colombier else if(r->seg.seq != n)
31119ef1f84bSDavid du Colombier s = "hole";
31129ef1f84bSDavid du Colombier if(s != nil)
31139ef1f84bSDavid du Colombier netlog(f, Logtcp, "%s %lud-%lud (%ld) %#ux\n", s,
31149ef1f84bSDavid du Colombier n, r->seg.seq, r->seg.seq - n, r->seg.flags);
31159ef1f84bSDavid du Colombier n = r->seg.seq + r->seg.len;
31169ef1f84bSDavid du Colombier }
31179ef1f84bSDavid du Colombier }
31189ef1f84bSDavid du Colombier
31199ef1f84bSDavid du Colombier static int
addreseq(Fs * f,Tcpctl * tcb,Tcppriv * tpriv,Tcp * seg,Block * bp,ushort length)31209ef1f84bSDavid du Colombier addreseq(Fs *f, Tcpctl *tcb, Tcppriv *tpriv, Tcp *seg, Block *bp, ushort length)
31219ef1f84bSDavid du Colombier {
31229ef1f84bSDavid du Colombier Reseq *rp, **rr;
31239ef1f84bSDavid du Colombier int qmax;
31249ef1f84bSDavid du Colombier
31259ef1f84bSDavid du Colombier rp = malloc(sizeof *rp);
31269ef1f84bSDavid du Colombier if(rp == nil){
31279ef1f84bSDavid du Colombier freeblist(bp); /* bp always consumed by addreseq */
31289ef1f84bSDavid du Colombier return 0;
31299ef1f84bSDavid du Colombier }
31309ef1f84bSDavid du Colombier
31319ef1f84bSDavid du Colombier rp->seg = *seg;
31329ef1f84bSDavid du Colombier rp->bp = bp;
31339ef1f84bSDavid du Colombier rp->length = length;
31349ef1f84bSDavid du Colombier
31359ef1f84bSDavid du Colombier tcb->reseqlen += length;
31369ef1f84bSDavid du Colombier tcb->nreseq++;
31379ef1f84bSDavid du Colombier
31389ef1f84bSDavid du Colombier /* Place on reassembly list sorting by starting seq number */
31399ef1f84bSDavid du Colombier for(rr = &tcb->reseq; ; rr = &(*rr)->next)
31409ef1f84bSDavid du Colombier if(*rr == nil || seq_lt(seg->seq, (*rr)->seg.seq)){
31419ef1f84bSDavid du Colombier rp->next = *rr;
31429ef1f84bSDavid du Colombier *rr = rp;
31439ef1f84bSDavid du Colombier tpriv->stats[Resequenced]++;
31449ef1f84bSDavid du Colombier if(rp->next != nil)
31459ef1f84bSDavid du Colombier tpriv->stats[OutOfOrder]++;
31469ef1f84bSDavid du Colombier break;
31479ef1f84bSDavid du Colombier }
31489ef1f84bSDavid du Colombier
31499ef1f84bSDavid du Colombier qmax = tcb->window;
31509ef1f84bSDavid du Colombier if(tcb->reseqlen > qmax){
31519ef1f84bSDavid du Colombier netlog(f, Logtcp, "tcp: reseq: queue > window: %d > %d; %d packets\n",
31529ef1f84bSDavid du Colombier tcb->reseqlen, qmax, tcb->nreseq);
31539ef1f84bSDavid du Colombier logreseq(f, tcb->reseq, tcb->rcv.nxt);
31549ef1f84bSDavid du Colombier tpriv->stats[ReseqBytelim]++;
31559ef1f84bSDavid du Colombier return dumpreseq(tcb);
31569ef1f84bSDavid du Colombier }
31579ef1f84bSDavid du Colombier qmax = tcb->window / tcb->mss; /* ~190 for qscale=2, 390 for qscale=3 */
31589ef1f84bSDavid du Colombier if(tcb->nreseq > qmax){
31599ef1f84bSDavid du Colombier netlog(f, Logtcp, "resequence queue > packets: %d %d; %d bytes\n",
31609ef1f84bSDavid du Colombier tcb->nreseq, qmax, tcb->reseqlen);
31619ef1f84bSDavid du Colombier logreseq(f, tcb->reseq, tcb->rcv.nxt);
31629ef1f84bSDavid du Colombier tpriv->stats[ReseqPktlim]++;
31639ef1f84bSDavid du Colombier return dumpreseq(tcb);
31649ef1f84bSDavid du Colombier }
31659ef1f84bSDavid du Colombier return 0;
31669ef1f84bSDavid du Colombier }
31679ef1f84bSDavid du Colombier
31689ef1f84bSDavid du Colombier static void
getreseq(Tcpctl * tcb,Tcp * seg,Block ** bp,ushort * length)31699ef1f84bSDavid du Colombier getreseq(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
31709ef1f84bSDavid du Colombier {
31719ef1f84bSDavid du Colombier Reseq *rp;
31729ef1f84bSDavid du Colombier
31739ef1f84bSDavid du Colombier rp = tcb->reseq;
31749ef1f84bSDavid du Colombier if(rp == nil)
31759ef1f84bSDavid du Colombier return;
31769ef1f84bSDavid du Colombier
31779ef1f84bSDavid du Colombier tcb->reseq = rp->next;
31789ef1f84bSDavid du Colombier
31799ef1f84bSDavid du Colombier *seg = rp->seg;
31809ef1f84bSDavid du Colombier *bp = rp->bp;
31819ef1f84bSDavid du Colombier *length = rp->length;
31829ef1f84bSDavid du Colombier
31839ef1f84bSDavid du Colombier tcb->nreseq--;
31849ef1f84bSDavid du Colombier tcb->reseqlen -= rp->length;
31859ef1f84bSDavid du Colombier
31869ef1f84bSDavid du Colombier free(rp);
31879ef1f84bSDavid du Colombier }
31889ef1f84bSDavid du Colombier
31899ef1f84bSDavid du Colombier static int
tcptrim(Tcpctl * tcb,Tcp * seg,Block ** bp,ushort * length)31909ef1f84bSDavid du Colombier tcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
31919ef1f84bSDavid du Colombier {
31929ef1f84bSDavid du Colombier ushort len;
31939ef1f84bSDavid du Colombier uchar accept;
31949ef1f84bSDavid du Colombier int dupcnt, excess;
31959ef1f84bSDavid du Colombier
31969ef1f84bSDavid du Colombier accept = 0;
31979ef1f84bSDavid du Colombier len = *length;
31989ef1f84bSDavid du Colombier if(seg->flags & SYN)
31999ef1f84bSDavid du Colombier len++;
32009ef1f84bSDavid du Colombier if(seg->flags & FIN)
32019ef1f84bSDavid du Colombier len++;
32029ef1f84bSDavid du Colombier
32039ef1f84bSDavid du Colombier if(tcb->rcv.wnd == 0) {
32049ef1f84bSDavid du Colombier if(len == 0 && seg->seq == tcb->rcv.nxt)
32059ef1f84bSDavid du Colombier return 0;
32069ef1f84bSDavid du Colombier }
32079ef1f84bSDavid du Colombier else {
32089ef1f84bSDavid du Colombier /* Some part of the segment should be in the window */
32099ef1f84bSDavid du Colombier if(inwindow(tcb,seg->seq))
32109ef1f84bSDavid du Colombier accept++;
32119ef1f84bSDavid du Colombier else
32129ef1f84bSDavid du Colombier if(len != 0) {
32139ef1f84bSDavid du Colombier if(inwindow(tcb, seg->seq+len-1) ||
32149ef1f84bSDavid du Colombier seq_within(tcb->rcv.nxt, seg->seq,seg->seq+len-1))
32159ef1f84bSDavid du Colombier accept++;
32169ef1f84bSDavid du Colombier }
32179ef1f84bSDavid du Colombier }
32189ef1f84bSDavid du Colombier if(!accept) {
32199ef1f84bSDavid du Colombier freeblist(*bp);
32209ef1f84bSDavid du Colombier return -1;
32219ef1f84bSDavid du Colombier }
32229ef1f84bSDavid du Colombier dupcnt = tcb->rcv.nxt - seg->seq;
32239ef1f84bSDavid du Colombier if(dupcnt > 0){
32249ef1f84bSDavid du Colombier tcb->rerecv += dupcnt;
32259ef1f84bSDavid du Colombier if(seg->flags & SYN){
32269ef1f84bSDavid du Colombier seg->flags &= ~SYN;
32279ef1f84bSDavid du Colombier seg->seq++;
32289ef1f84bSDavid du Colombier
32299ef1f84bSDavid du Colombier if(seg->urg > 1)
32309ef1f84bSDavid du Colombier seg->urg--;
32319ef1f84bSDavid du Colombier else
32329ef1f84bSDavid du Colombier seg->flags &= ~URG;
32339ef1f84bSDavid du Colombier dupcnt--;
32349ef1f84bSDavid du Colombier }
32359ef1f84bSDavid du Colombier if(dupcnt > 0){
32369ef1f84bSDavid du Colombier pullblock(bp, (ushort)dupcnt);
32379ef1f84bSDavid du Colombier seg->seq += dupcnt;
32389ef1f84bSDavid du Colombier *length -= dupcnt;
32399ef1f84bSDavid du Colombier
32409ef1f84bSDavid du Colombier if(seg->urg > dupcnt)
32419ef1f84bSDavid du Colombier seg->urg -= dupcnt;
32429ef1f84bSDavid du Colombier else {
32439ef1f84bSDavid du Colombier seg->flags &= ~URG;
32449ef1f84bSDavid du Colombier seg->urg = 0;
32459ef1f84bSDavid du Colombier }
32469ef1f84bSDavid du Colombier }
32479ef1f84bSDavid du Colombier }
32489ef1f84bSDavid du Colombier excess = seg->seq + *length - (tcb->rcv.nxt + tcb->rcv.wnd);
32499ef1f84bSDavid du Colombier if(excess > 0) {
32509ef1f84bSDavid du Colombier tcb->rerecv += excess;
32519ef1f84bSDavid du Colombier *length -= excess;
32529ef1f84bSDavid du Colombier *bp = trimblock(*bp, 0, *length);
32539ef1f84bSDavid du Colombier if(*bp == nil)
32549ef1f84bSDavid du Colombier panic("presotto is a boofhead");
32559ef1f84bSDavid du Colombier seg->flags &= ~FIN;
32569ef1f84bSDavid du Colombier }
32579ef1f84bSDavid du Colombier return 0;
32589ef1f84bSDavid du Colombier }
32599ef1f84bSDavid du Colombier
32609ef1f84bSDavid du Colombier static void
tcpadvise(Proto * tcp,Block * bp,char * msg)32619ef1f84bSDavid du Colombier tcpadvise(Proto *tcp, Block *bp, char *msg)
32629ef1f84bSDavid du Colombier {
32639ef1f84bSDavid du Colombier Tcp4hdr *h4;
32649ef1f84bSDavid du Colombier Tcp6hdr *h6;
32659ef1f84bSDavid du Colombier Tcpctl *tcb;
32669ef1f84bSDavid du Colombier uchar source[IPaddrlen];
32679ef1f84bSDavid du Colombier uchar dest[IPaddrlen];
32689ef1f84bSDavid du Colombier ushort psource, pdest;
32699ef1f84bSDavid du Colombier Conv *s, **p;
32709ef1f84bSDavid du Colombier
32719ef1f84bSDavid du Colombier h4 = (Tcp4hdr*)(bp->rp);
32729ef1f84bSDavid du Colombier h6 = (Tcp6hdr*)(bp->rp);
32739ef1f84bSDavid du Colombier
32749ef1f84bSDavid du Colombier if((h4->vihl&0xF0)==IP_VER4) {
32759ef1f84bSDavid du Colombier v4tov6(dest, h4->tcpdst);
32769ef1f84bSDavid du Colombier v4tov6(source, h4->tcpsrc);
32779ef1f84bSDavid du Colombier psource = nhgets(h4->tcpsport);
32789ef1f84bSDavid du Colombier pdest = nhgets(h4->tcpdport);
32799ef1f84bSDavid du Colombier }
32809ef1f84bSDavid du Colombier else {
32819ef1f84bSDavid du Colombier ipmove(dest, h6->tcpdst);
32829ef1f84bSDavid du Colombier ipmove(source, h6->tcpsrc);
32839ef1f84bSDavid du Colombier psource = nhgets(h6->tcpsport);
32849ef1f84bSDavid du Colombier pdest = nhgets(h6->tcpdport);
32859ef1f84bSDavid du Colombier }
32869ef1f84bSDavid du Colombier
32879ef1f84bSDavid du Colombier /* Look for a connection */
32889ef1f84bSDavid du Colombier qlock(tcp);
32899ef1f84bSDavid du Colombier for(p = tcp->conv; *p; p++) {
32909ef1f84bSDavid du Colombier s = *p;
32919ef1f84bSDavid du Colombier tcb = (Tcpctl*)s->ptcl;
32929ef1f84bSDavid du Colombier if(s->rport == pdest)
32939ef1f84bSDavid du Colombier if(s->lport == psource)
32949ef1f84bSDavid du Colombier if(tcb->state != Closed)
32959ef1f84bSDavid du Colombier if(ipcmp(s->raddr, dest) == 0)
32969ef1f84bSDavid du Colombier if(ipcmp(s->laddr, source) == 0){
32979ef1f84bSDavid du Colombier qlock(s);
32989ef1f84bSDavid du Colombier qunlock(tcp);
32999ef1f84bSDavid du Colombier switch(tcb->state){
33009ef1f84bSDavid du Colombier case Syn_sent:
33019ef1f84bSDavid du Colombier localclose(s, msg);
33029ef1f84bSDavid du Colombier break;
33039ef1f84bSDavid du Colombier }
33049ef1f84bSDavid du Colombier qunlock(s);
33059ef1f84bSDavid du Colombier freeblist(bp);
33069ef1f84bSDavid du Colombier return;
33079ef1f84bSDavid du Colombier }
33089ef1f84bSDavid du Colombier }
33099ef1f84bSDavid du Colombier qunlock(tcp);
33109ef1f84bSDavid du Colombier freeblist(bp);
33119ef1f84bSDavid du Colombier }
33129ef1f84bSDavid du Colombier
33139ef1f84bSDavid du Colombier static char*
tcpporthogdefensectl(char * val)33149ef1f84bSDavid du Colombier tcpporthogdefensectl(char *val)
33159ef1f84bSDavid du Colombier {
33169ef1f84bSDavid du Colombier if(strcmp(val, "on") == 0)
33179ef1f84bSDavid du Colombier tcpporthogdefense = 1;
33189ef1f84bSDavid du Colombier else if(strcmp(val, "off") == 0)
33199ef1f84bSDavid du Colombier tcpporthogdefense = 0;
33209ef1f84bSDavid du Colombier else
33219ef1f84bSDavid du Colombier return "unknown value for tcpporthogdefense";
33229ef1f84bSDavid du Colombier return nil;
33239ef1f84bSDavid du Colombier }
33249ef1f84bSDavid du Colombier
33259ef1f84bSDavid du Colombier /* called with c qlocked */
33269ef1f84bSDavid du Colombier static char*
tcpctl(Conv * c,char ** f,int n)33279ef1f84bSDavid du Colombier tcpctl(Conv* c, char** f, int n)
33289ef1f84bSDavid du Colombier {
3329*41165bbaSDavid du Colombier if(n == 1 && strcmp(f[0], "close") == 0)
3330*41165bbaSDavid du Colombier return tcpclose2(c);
33319ef1f84bSDavid du Colombier if(n == 1 && strcmp(f[0], "hangup") == 0)
33329ef1f84bSDavid du Colombier return tcphangup(c);
333340398a43SDavid du Colombier if(n == 1 && strcmp(f[0], "hangupxmit") == 0)
333440398a43SDavid du Colombier return tcpxmitclose(c);
33359ef1f84bSDavid du Colombier if(n >= 1 && strcmp(f[0], "keepalive") == 0)
33369ef1f84bSDavid du Colombier return tcpstartka(c, f, n);
33379ef1f84bSDavid du Colombier if(n >= 1 && strcmp(f[0], "checksum") == 0)
33389ef1f84bSDavid du Colombier return tcpsetchecksum(c, f, n);
33399ef1f84bSDavid du Colombier if(n >= 1 && strcmp(f[0], "tcpporthogdefense") == 0)
33409ef1f84bSDavid du Colombier return tcpporthogdefensectl(f[1]);
33419ef1f84bSDavid du Colombier return "unknown control request";
33429ef1f84bSDavid du Colombier }
33439ef1f84bSDavid du Colombier
33449ef1f84bSDavid du Colombier static int
tcpstats(Proto * tcp,char * buf,int len)33459ef1f84bSDavid du Colombier tcpstats(Proto *tcp, char *buf, int len)
33469ef1f84bSDavid du Colombier {
33479ef1f84bSDavid du Colombier Tcppriv *priv;
33489ef1f84bSDavid du Colombier char *p, *e;
33499ef1f84bSDavid du Colombier int i;
33509ef1f84bSDavid du Colombier
33519ef1f84bSDavid du Colombier priv = tcp->priv;
33529ef1f84bSDavid du Colombier p = buf;
33539ef1f84bSDavid du Colombier e = p+len;
33549ef1f84bSDavid du Colombier for(i = 0; i < Nstats; i++)
33559ef1f84bSDavid du Colombier p = seprint(p, e, "%s: %llud\n", statnames[i], priv->stats[i]);
33569ef1f84bSDavid du Colombier return p - buf;
33579ef1f84bSDavid du Colombier }
33589ef1f84bSDavid du Colombier
33599ef1f84bSDavid du Colombier /*
33609ef1f84bSDavid du Colombier * garbage collect any stale conversations:
33619ef1f84bSDavid du Colombier * - SYN received but no SYN-ACK after 5 seconds (could be the SYN attack)
33629ef1f84bSDavid du Colombier * - Finwait2 after 5 minutes
33639ef1f84bSDavid du Colombier *
33649ef1f84bSDavid du Colombier * this is called whenever we run out of channels. Both checks are
33659ef1f84bSDavid du Colombier * of questionable validity so we try to use them only when we're
33669ef1f84bSDavid du Colombier * up against the wall.
33679ef1f84bSDavid du Colombier */
33689ef1f84bSDavid du Colombier static int
tcpgc(Proto * tcp)33699ef1f84bSDavid du Colombier tcpgc(Proto *tcp)
33709ef1f84bSDavid du Colombier {
33719ef1f84bSDavid du Colombier Conv *c, **pp, **ep;
33729ef1f84bSDavid du Colombier int n;
33739ef1f84bSDavid du Colombier Tcpctl *tcb;
33749ef1f84bSDavid du Colombier
33759ef1f84bSDavid du Colombier
33769ef1f84bSDavid du Colombier n = 0;
33779ef1f84bSDavid du Colombier ep = &tcp->conv[tcp->nc];
33789ef1f84bSDavid du Colombier for(pp = tcp->conv; pp < ep; pp++) {
33799ef1f84bSDavid du Colombier c = *pp;
33809ef1f84bSDavid du Colombier if(c == nil)
33819ef1f84bSDavid du Colombier break;
33829ef1f84bSDavid du Colombier if(!canqlock(c))
33839ef1f84bSDavid du Colombier continue;
33849ef1f84bSDavid du Colombier tcb = (Tcpctl*)c->ptcl;
33859ef1f84bSDavid du Colombier switch(tcb->state){
33869ef1f84bSDavid du Colombier case Syn_received:
33879ef1f84bSDavid du Colombier if(NOW - tcb->time > 5000){
33889ef1f84bSDavid du Colombier localclose(c, Etimedout);
33899ef1f84bSDavid du Colombier n++;
33909ef1f84bSDavid du Colombier }
33919ef1f84bSDavid du Colombier break;
33929ef1f84bSDavid du Colombier case Finwait2:
33939ef1f84bSDavid du Colombier if(NOW - tcb->time > 5*60*1000){
33949ef1f84bSDavid du Colombier localclose(c, Etimedout);
33959ef1f84bSDavid du Colombier n++;
33969ef1f84bSDavid du Colombier }
33979ef1f84bSDavid du Colombier break;
33989ef1f84bSDavid du Colombier }
33999ef1f84bSDavid du Colombier qunlock(c);
34009ef1f84bSDavid du Colombier }
34019ef1f84bSDavid du Colombier return n;
34029ef1f84bSDavid du Colombier }
34039ef1f84bSDavid du Colombier
34049ef1f84bSDavid du Colombier static void
tcpsettimer(Tcpctl * tcb)34059ef1f84bSDavid du Colombier tcpsettimer(Tcpctl *tcb)
34069ef1f84bSDavid du Colombier {
34079ef1f84bSDavid du Colombier int x;
34089ef1f84bSDavid du Colombier
34099ef1f84bSDavid du Colombier /* round trip dependency */
34109ef1f84bSDavid du Colombier x = backoff(tcb->backoff) *
34119ef1f84bSDavid du Colombier (tcb->mdev + (tcb->srtt>>LOGAGAIN) + MSPTICK) / MSPTICK;
34129ef1f84bSDavid du Colombier
34139ef1f84bSDavid du Colombier /* bounded twixt 0.3 and 64 seconds */
34149ef1f84bSDavid du Colombier if(x < 300/MSPTICK)
34159ef1f84bSDavid du Colombier x = 300/MSPTICK;
34169ef1f84bSDavid du Colombier else if(x > (64000/MSPTICK))
34179ef1f84bSDavid du Colombier x = 64000/MSPTICK;
34189ef1f84bSDavid du Colombier tcb->timer.start = x;
34199ef1f84bSDavid du Colombier }
34209ef1f84bSDavid du Colombier
34219ef1f84bSDavid du Colombier void
tcpinit(Fs * fs)34229ef1f84bSDavid du Colombier tcpinit(Fs *fs)
34239ef1f84bSDavid du Colombier {
34249ef1f84bSDavid du Colombier Proto *tcp;
34259ef1f84bSDavid du Colombier Tcppriv *tpriv;
34269ef1f84bSDavid du Colombier
34279ef1f84bSDavid du Colombier tcp = smalloc(sizeof(Proto));
34289ef1f84bSDavid du Colombier tpriv = tcp->priv = smalloc(sizeof(Tcppriv));
34299ef1f84bSDavid du Colombier tcp->name = "tcp";
34309ef1f84bSDavid du Colombier tcp->connect = tcpconnect;
34319ef1f84bSDavid du Colombier tcp->announce = tcpannounce;
34329ef1f84bSDavid du Colombier tcp->ctl = tcpctl;
34339ef1f84bSDavid du Colombier tcp->state = tcpstate;
34349ef1f84bSDavid du Colombier tcp->create = tcpcreate;
34359ef1f84bSDavid du Colombier tcp->close = tcpclose;
34369ef1f84bSDavid du Colombier tcp->rcv = tcpiput;
34379ef1f84bSDavid du Colombier tcp->advise = tcpadvise;
34389ef1f84bSDavid du Colombier tcp->stats = tcpstats;
34399ef1f84bSDavid du Colombier tcp->inuse = tcpinuse;
34409ef1f84bSDavid du Colombier tcp->gc = tcpgc;
34419ef1f84bSDavid du Colombier tcp->ipproto = IP_TCPPROTO;
34429ef1f84bSDavid du Colombier tcp->nc = scalednconv();
34439ef1f84bSDavid du Colombier tcp->ptclsize = sizeof(Tcpctl);
34449ef1f84bSDavid du Colombier tpriv->stats[MaxConn] = tcp->nc;
34459ef1f84bSDavid du Colombier
34469ef1f84bSDavid du Colombier Fsproto(fs, tcp);
34479ef1f84bSDavid du Colombier }
34489ef1f84bSDavid du Colombier
34499ef1f84bSDavid du Colombier static void
tcpsetscale(Conv * s,Tcpctl * tcb,ushort rcvscale,ushort sndscale)34509ef1f84bSDavid du Colombier tcpsetscale(Conv *s, Tcpctl *tcb, ushort rcvscale, ushort sndscale)
34519ef1f84bSDavid du Colombier {
34529ef1f84bSDavid du Colombier /*
34539ef1f84bSDavid du Colombier * guess at reasonable queue sizes. there's no current way
34549ef1f84bSDavid du Colombier * to know how many nic receive buffers we can safely tie up in the
34559ef1f84bSDavid du Colombier * tcp stack, and we don't adjust our queues to maximize throughput
34569ef1f84bSDavid du Colombier * and minimize bufferbloat. n.b. the offer (rcvscale) needs to be
34579ef1f84bSDavid du Colombier * respected, but we still control our own buffer commitment by
34589ef1f84bSDavid du Colombier * keeping a seperate qscale.
34599ef1f84bSDavid du Colombier */
34609ef1f84bSDavid du Colombier tcb->rcv.scale = rcvscale & 0xff;
34619ef1f84bSDavid du Colombier tcb->snd.scale = sndscale & 0xff;
34629ef1f84bSDavid du Colombier tcb->qscale = rcvscale & 0xff;
34639ef1f84bSDavid du Colombier if(rcvscale > Maxqscale)
34649ef1f84bSDavid du Colombier tcb->qscale = Maxqscale;
34659ef1f84bSDavid du Colombier
34669ef1f84bSDavid du Colombier if(rcvscale != tcb->rcv.scale)
34679ef1f84bSDavid du Colombier netlog(s->p->f, Logtcp, "tcpsetscale: window %lud "
34689ef1f84bSDavid du Colombier "qlen %d >> window %ud lport %d\n",
34699ef1f84bSDavid du Colombier tcb->window, qlen(s->rq), QMAX<<tcb->qscale, s->lport);
34709ef1f84bSDavid du Colombier tcb->window = QMAX << tcb->qscale;
34719ef1f84bSDavid du Colombier tcb->ssthresh = tcb->window;
34729ef1f84bSDavid du Colombier
34739ef1f84bSDavid du Colombier /*
34749ef1f84bSDavid du Colombier * it's important to set wq large enough to cover the full
34759ef1f84bSDavid du Colombier * bandwidth-delay product. it's possible to be in loss
34769ef1f84bSDavid du Colombier * recovery with a big window, and we need to keep sending
34779ef1f84bSDavid du Colombier * into the inflated window. the difference can be huge
34789ef1f84bSDavid du Colombier * for even modest (70ms) ping times.
34799ef1f84bSDavid du Colombier */
34809ef1f84bSDavid du Colombier qsetlimit(s->rq, tcb->window);
34819ef1f84bSDavid du Colombier qsetlimit(s->wq, tcb->window);
34829ef1f84bSDavid du Colombier tcprcvwin(s);
34839ef1f84bSDavid du Colombier }
3484