diff --git a/pkg/tcpip/stack/conntrack.go b/pkg/tcpip/stack/conntrack.go index 74830cc51..e3498cfa2 100644 --- a/pkg/tcpip/stack/conntrack.go +++ b/pkg/tcpip/stack/conntrack.go @@ -193,14 +193,14 @@ func (cn *conn) update(pkt *PacketBuffer, reply bool) { // client. However, we only need to know whether the connection is // established or not, so the client/server distinction isn't important. if cn.tcb.IsEmpty() { - cn.tcb.Init(tcpHeader) + cn.tcb.Init(tcpHeader, pkt.Data().Size()) return } if reply { - cn.tcb.UpdateStateReply(tcpHeader) + cn.tcb.UpdateStateReply(tcpHeader, pkt.Data().Size()) } else { - cn.tcb.UpdateStateOriginal(tcpHeader) + cn.tcb.UpdateStateOriginal(tcpHeader, pkt.Data().Size()) } } diff --git a/pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go b/pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go index a3ea9e197..f3a962a7d 100644 --- a/pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go +++ b/pkg/tcpip/transport/tcpconntrack/tcp_conntrack.go @@ -55,9 +55,10 @@ type TCB struct { reply stream original stream - // State handlers. - handlerReply func(*TCB, header.TCP) Result - handlerOriginal func(*TCB, header.TCP) Result + // State handlers. hdr is not guaranteed to contain bytes beyond the TCP + // header itself, i.e. it may not contain the payload. + handlerReply func(tcb *TCB, hdr header.TCP, dataLen int) Result + handlerOriginal func(tcb *TCB, hdr header.TCP, dataLen int) Result // firstFin holds a pointer to the first stream to send a FIN. firstFin *stream @@ -67,13 +68,13 @@ type TCB struct { } // Init initializes the state of the TCB according to the initial SYN. -func (t *TCB) Init(initialSyn header.TCP) Result { +func (t *TCB) Init(initialSyn header.TCP, dataLen int) Result { t.handlerReply = synSentStateReply t.handlerOriginal = synSentStateOriginal iss := seqnum.Value(initialSyn.SequenceNumber()) t.original.una = iss - t.original.nxt = iss.Add(logicalLen(initialSyn)) + t.original.nxt = iss.Add(logicalLen(initialSyn, dataLen)) t.original.end = t.original.nxt // Even though "end" is a sequence number, we don't know the initial @@ -88,8 +89,8 @@ func (t *TCB) Init(initialSyn header.TCP) Result { // UpdateStateReply updates the state of the TCB based on the supplied reply // segment. -func (t *TCB) UpdateStateReply(tcp header.TCP) Result { - st := t.handlerReply(t, tcp) +func (t *TCB) UpdateStateReply(tcp header.TCP, dataLen int) Result { + st := t.handlerReply(t, tcp, dataLen) if st != ResultDrop { t.state = st } @@ -98,8 +99,8 @@ func (t *TCB) UpdateStateReply(tcp header.TCP) Result { // UpdateStateOriginal updates the state of the TCB based on the supplied // original segment. -func (t *TCB) UpdateStateOriginal(tcp header.TCP) Result { - st := t.handlerOriginal(t, tcp) +func (t *TCB) UpdateStateOriginal(tcp header.TCP, dataLen int) Result { + st := t.handlerOriginal(t, tcp, dataLen) if st != ResultDrop { t.state = st } @@ -148,7 +149,7 @@ func (t *TCB) adaptResult(r Result) Result { // synSentStateReply is the state handler for reply segments when the // connection is in SYN-SENT state. -func synSentStateReply(t *TCB, tcp header.TCP) Result { +func synSentStateReply(t *TCB, tcp header.TCP, dataLen int) Result { flags := tcp.Flags() ackPresent := flags&header.TCPFlagAck != 0 ack := seqnum.Value(tcp.AckNumber()) @@ -177,7 +178,7 @@ func synSentStateReply(t *TCB, tcp header.TCP) Result { // Update state informed by this SYN. irs := seqnum.Value(tcp.SequenceNumber()) t.reply.una = irs - t.reply.nxt = irs.Add(logicalLen(tcp)) + t.reply.nxt = irs.Add(logicalLen(tcp, dataLen)) t.reply.end += irs t.original.end = t.original.una.Add(seqnum.Size(tcp.WindowSize())) @@ -204,7 +205,7 @@ func synSentStateReply(t *TCB, tcp header.TCP) Result { // synSentStateOriginal is the state handler for original segments when the // connection is in SYN-SENT state. -func synSentStateOriginal(t *TCB, tcp header.TCP) Result { +func synSentStateOriginal(t *TCB, tcp header.TCP, _ int) Result { // Drop original segments that aren't retransmits of the original one. if tcp.Flags() != header.TCPFlagSyn || tcp.SequenceNumber() != uint32(t.original.una) { @@ -222,10 +223,10 @@ func synSentStateOriginal(t *TCB, tcp header.TCP) Result { // update updates the state of reply and original streams, given the supplied // reply segment. For original segments, this same function can be called with // swapped reply/original streams. -func update(tcp header.TCP, reply, original *stream, firstFin **stream) Result { +func update(tcp header.TCP, reply, original *stream, firstFin **stream, dataLen int) Result { // Ignore segments out of the window. s := seqnum.Value(tcp.SequenceNumber()) - if !reply.acceptable(s, dataLen(tcp)) { + if !reply.acceptable(s, seqnum.Size(dataLen)) { return ResultAlive } @@ -257,7 +258,7 @@ func update(tcp header.TCP, reply, original *stream, firstFin **stream) Result { } // Advance the "nxt" index of the reply stream. - end := s.Add(logicalLen(tcp)) + end := s.Add(logicalLen(tcp, dataLen)) if reply.nxt.LessThan(end) { reply.nxt = end } @@ -278,14 +279,14 @@ func update(tcp header.TCP, reply, original *stream, firstFin **stream) Result { // allOtherReply is the state handler for reply segments in all states // except SYN-SENT. -func allOtherReply(t *TCB, tcp header.TCP) Result { - return t.adaptResult(update(tcp, &t.reply, &t.original, &t.firstFin)) +func allOtherReply(t *TCB, tcp header.TCP, dataLen int) Result { + return t.adaptResult(update(tcp, &t.reply, &t.original, &t.firstFin, dataLen)) } // allOtherOriginal is the state handler for original segments in all states // except SYN-SENT. -func allOtherOriginal(t *TCB, tcp header.TCP) Result { - return t.adaptResult(update(tcp, &t.original, &t.reply, &t.firstFin)) +func allOtherOriginal(t *TCB, tcp header.TCP, dataLen int) Result { + return t.adaptResult(update(tcp, &t.original, &t.reply, &t.firstFin, dataLen)) } // streams holds the state of a TCP unidirectional stream. @@ -326,22 +327,16 @@ func (s *stream) closed() bool { return s.finSeen && s.fin.LessThan(s.una) } -// dataLen returns the length of the TCP segment payload. -func dataLen(tcp header.TCP) seqnum.Size { - return seqnum.Size(len(tcp) - int(tcp.DataOffset())) -} - // logicalLen calculates the logical length of the TCP segment. -func logicalLen(tcp header.TCP) seqnum.Size { - l := dataLen(tcp) +func logicalLen(tcp header.TCP, dataLen int) seqnum.Size { flags := tcp.Flags() if flags&header.TCPFlagSyn != 0 { - l++ + dataLen++ } if flags&header.TCPFlagFin != 0 { - l++ + dataLen++ } - return l + return seqnum.Size(dataLen) } // IsEmpty returns true if tcb is not initialized. diff --git a/pkg/tcpip/transport/tcpconntrack/tcp_conntrack_test.go b/pkg/tcpip/transport/tcpconntrack/tcp_conntrack_test.go index 30732b73d..1ec84aafe 100644 --- a/pkg/tcpip/transport/tcpconntrack/tcp_conntrack_test.go +++ b/pkg/tcpip/transport/tcpconntrack/tcp_conntrack_test.go @@ -35,7 +35,7 @@ func connected(t *testing.T, iss, irs uint32, isw, irw uint16) *tcpconntrack.TCB }) tcb := tcpconntrack.TCB{} - tcb.Init(tcp) + tcb.Init(tcp, dataLen(tcp)) // Receive SYN-ACK. tcp.Encode(&header.TCPFields{ @@ -46,7 +46,7 @@ func connected(t *testing.T, iss, irs uint32, isw, irw uint16) *tcpconntrack.TCB WindowSize: isw, }) - if r := tcb.UpdateStateReply(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateReply(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } @@ -59,7 +59,7 @@ func connected(t *testing.T, iss, irs uint32, isw, irw uint16) *tcpconntrack.TCB WindowSize: irw, }) - if r := tcb.UpdateStateOriginal(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateOriginal(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } @@ -78,7 +78,7 @@ func TestConnectionRefused(t *testing.T) { }) tcb := tcpconntrack.TCB{} - tcb.Init(tcp) + tcb.Init(tcp, dataLen(tcp)) // Receive RST. tcp.Encode(&header.TCPFields{ @@ -89,7 +89,7 @@ func TestConnectionRefused(t *testing.T) { WindowSize: 50000, }) - if r := tcb.UpdateStateReply(tcp); r != tcpconntrack.ResultReset { + if r := tcb.UpdateStateReply(tcp, dataLen(tcp)); r != tcpconntrack.ResultReset { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultReset) } } @@ -106,7 +106,7 @@ func TestConnectionRefusedInSynRcvd(t *testing.T) { }) tcb := tcpconntrack.TCB{} - tcb.Init(tcp) + tcb.Init(tcp, dataLen(tcp)) // Receive SYN. tcp.Encode(&header.TCPFields{ @@ -117,7 +117,7 @@ func TestConnectionRefusedInSynRcvd(t *testing.T) { WindowSize: 50000, }) - if r := tcb.UpdateStateReply(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateReply(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } @@ -130,7 +130,7 @@ func TestConnectionRefusedInSynRcvd(t *testing.T) { WindowSize: 50000, }) - if r := tcb.UpdateStateReply(tcp); r != tcpconntrack.ResultReset { + if r := tcb.UpdateStateReply(tcp, dataLen(tcp)); r != tcpconntrack.ResultReset { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultReset) } } @@ -147,7 +147,7 @@ func TestConnectionResetInSynRcvd(t *testing.T) { }) tcb := tcpconntrack.TCB{} - tcb.Init(tcp) + tcb.Init(tcp, dataLen(tcp)) // Receive SYN. tcp.Encode(&header.TCPFields{ @@ -158,7 +158,7 @@ func TestConnectionResetInSynRcvd(t *testing.T) { WindowSize: 50000, }) - if r := tcb.UpdateStateReply(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateReply(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } @@ -170,7 +170,7 @@ func TestConnectionResetInSynRcvd(t *testing.T) { Flags: header.TCPFlagRst, }) - if r := tcb.UpdateStateOriginal(tcp); r != tcpconntrack.ResultReset { + if r := tcb.UpdateStateOriginal(tcp, dataLen(tcp)); r != tcpconntrack.ResultReset { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultReset) } } @@ -187,10 +187,10 @@ func TestRetransmitOnSynSent(t *testing.T) { }) tcb := tcpconntrack.TCB{} - tcb.Init(tcp) + tcb.Init(tcp, dataLen(tcp)) // Retransmit the same SYN. - if r := tcb.UpdateStateOriginal(tcp); r != tcpconntrack.ResultConnecting { + if r := tcb.UpdateStateOriginal(tcp, dataLen(tcp)); r != tcpconntrack.ResultConnecting { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultConnecting) } } @@ -207,7 +207,7 @@ func TestRetransmitOnSynRcvd(t *testing.T) { }) tcb := tcpconntrack.TCB{} - tcb.Init(tcp) + tcb.Init(tcp, dataLen(tcp)) // Receive SYN. This will cause the state to go to SYN-RCVD. tcp.Encode(&header.TCPFields{ @@ -218,7 +218,7 @@ func TestRetransmitOnSynRcvd(t *testing.T) { WindowSize: 50000, }) - if r := tcb.UpdateStateReply(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateReply(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } @@ -231,7 +231,7 @@ func TestRetransmitOnSynRcvd(t *testing.T) { WindowSize: 30000, }) - if r := tcb.UpdateStateOriginal(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateOriginal(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } @@ -244,7 +244,7 @@ func TestRetransmitOnSynRcvd(t *testing.T) { WindowSize: 30000, }) - if r := tcb.UpdateStateOriginal(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateOriginal(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } } @@ -262,7 +262,7 @@ func TestClosedByOriginator(t *testing.T) { WindowSize: 30000, }) - if r := tcb.UpdateStateOriginal(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateOriginal(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } @@ -275,7 +275,7 @@ func TestClosedByOriginator(t *testing.T) { WindowSize: 50000, }) - if r := tcb.UpdateStateReply(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateReply(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } @@ -288,7 +288,7 @@ func TestClosedByOriginator(t *testing.T) { WindowSize: 30000, }) - if r := tcb.UpdateStateOriginal(tcp); r != tcpconntrack.ResultClosedByOriginator { + if r := tcb.UpdateStateOriginal(tcp, dataLen(tcp)); r != tcpconntrack.ResultClosedByOriginator { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultClosedByOriginator) } } @@ -306,7 +306,7 @@ func TestClosedByResponder(t *testing.T) { WindowSize: 50000, }) - if r := tcb.UpdateStateReply(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateReply(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } @@ -319,7 +319,7 @@ func TestClosedByResponder(t *testing.T) { WindowSize: 30000, }) - if r := tcb.UpdateStateOriginal(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateOriginal(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } @@ -332,7 +332,7 @@ func TestClosedByResponder(t *testing.T) { WindowSize: 50000, }) - if r := tcb.UpdateStateReply(tcp); r != tcpconntrack.ResultClosedByResponder { + if r := tcb.UpdateStateReply(tcp, dataLen(tcp)); r != tcpconntrack.ResultClosedByResponder { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultClosedByResponder) } } @@ -356,9 +356,9 @@ func TestSendAndReceiveDataClosedByOriginator(t *testing.T) { Flags: header.TCPFlagAck, WindowSize: 30000, }) - sseq += uint32(len(tcp)) - header.TCPMinimumSize + sseq += uint32(dataLen(tcp)) - header.TCPMinimumSize - if r := tcb.UpdateStateOriginal(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateOriginal(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } @@ -371,7 +371,7 @@ func TestSendAndReceiveDataClosedByOriginator(t *testing.T) { WindowSize: 50000, }) - if r := tcb.UpdateStateReply(tcp[:header.TCPMinimumSize]); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateReply(tcp[:header.TCPMinimumSize], dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } } @@ -385,9 +385,9 @@ func TestSendAndReceiveDataClosedByOriginator(t *testing.T) { Flags: header.TCPFlagAck, WindowSize: 50000, }) - rseq += uint32(len(tcp)) - header.TCPMinimumSize + rseq += uint32(dataLen(tcp)) - if r := tcb.UpdateStateReply(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateReply(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } @@ -400,7 +400,7 @@ func TestSendAndReceiveDataClosedByOriginator(t *testing.T) { WindowSize: 30000, }) - if r := tcb.UpdateStateOriginal(tcp[:header.TCPMinimumSize]); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateOriginal(tcp[:header.TCPMinimumSize], dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } } @@ -416,7 +416,7 @@ func TestSendAndReceiveDataClosedByOriginator(t *testing.T) { }) sseq++ - if r := tcb.UpdateStateOriginal(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateOriginal(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } @@ -430,7 +430,7 @@ func TestSendAndReceiveDataClosedByOriginator(t *testing.T) { }) rseq++ - if r := tcb.UpdateStateReply(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateReply(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } @@ -443,7 +443,7 @@ func TestSendAndReceiveDataClosedByOriginator(t *testing.T) { WindowSize: 30000, }) - if r := tcb.UpdateStateOriginal(tcp); r != tcpconntrack.ResultClosedByOriginator { + if r := tcb.UpdateStateOriginal(tcp, dataLen(tcp)); r != tcpconntrack.ResultClosedByOriginator { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultClosedByOriginator) } } @@ -460,7 +460,7 @@ func TestIgnoreBadResetOnSynSent(t *testing.T) { }) tcb := tcpconntrack.TCB{} - tcb.Init(tcp) + tcb.Init(tcp, dataLen(tcp)) // Receive a RST with a bad ACK, it should not cause the connection to // be reset. @@ -476,7 +476,7 @@ func TestIgnoreBadResetOnSynSent(t *testing.T) { WindowSize: 50000, }) - if r := tcb.UpdateStateReply(tcp); r != tcpconntrack.ResultConnecting { + if r := tcb.UpdateStateReply(tcp, dataLen(tcp)); r != tcpconntrack.ResultConnecting { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } } @@ -492,7 +492,7 @@ func TestIgnoreBadResetOnSynSent(t *testing.T) { WindowSize: 50000, }) - if r := tcb.UpdateStateReply(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateReply(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } @@ -505,7 +505,13 @@ func TestIgnoreBadResetOnSynSent(t *testing.T) { WindowSize: 30000, }) - if r := tcb.UpdateStateOriginal(tcp); r != tcpconntrack.ResultAlive { + if r := tcb.UpdateStateOriginal(tcp, dataLen(tcp)); r != tcpconntrack.ResultAlive { t.Fatalf("Bad result: got %v, want %v", r, tcpconntrack.ResultAlive) } } + +// dataLen returns the length of the TCP payload assuming that both the header +// and payload are in tcp. +func dataLen(tcp header.TCP) int { + return len(tcp) - int(tcp.DataOffset()) +}