diff --git a/test/images/regression-issue-74839/BUILD b/test/images/regression-issue-74839/BUILD index 06e30ea34c4..70d3cab01e3 100644 --- a/test/images/regression-issue-74839/BUILD +++ b/test/images/regression-issue-74839/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") go_library( name = "go_default_library", @@ -29,3 +29,9 @@ filegroup( tags = ["automanaged"], visibility = ["//visibility:public"], ) + +go_test( + name = "go_default_test", + srcs = ["tcp_test.go"], + embed = [":go_default_library"], +) diff --git a/test/images/regression-issue-74839/OWNERS b/test/images/regression-issue-74839/OWNERS index 4d3d0381ea4..dce824c5c50 100644 --- a/test/images/regression-issue-74839/OWNERS +++ b/test/images/regression-issue-74839/OWNERS @@ -1,2 +1,3 @@ approvers: - anfernee + - aojea diff --git a/test/images/regression-issue-74839/VERSION b/test/images/regression-issue-74839/VERSION index 9459d4ba2a0..5625e59da88 100644 --- a/test/images/regression-issue-74839/VERSION +++ b/test/images/regression-issue-74839/VERSION @@ -1 +1 @@ -1.1 +1.2 diff --git a/test/images/regression-issue-74839/main.go b/test/images/regression-issue-74839/main.go index 2d46da95493..19065e98056 100644 --- a/test/images/regression-issue-74839/main.go +++ b/test/images/regression-issue-74839/main.go @@ -17,20 +17,33 @@ limitations under the License. package main import ( + "fmt" "log" "net" + "os" + "strings" "time" ) +// TCP port to listen +const port = 9000 + func main() { - ip := getIP().String() - log.Printf("external ip: %v", ip) + ips := getIPs() + if len(ips) == 0 { + panic("No valid IP found") + } - go probe(ip) + // listen TCP packets to inject the out of order TCP packets + for _, ip := range ips { + log.Printf("external ip: %v", ip.String()) + go probe(ip.String()) + } - log.Printf("listen on %v:9000", "0.0.0.0") + log.Printf("listen on %v:%d", "0.0.0.0", port) - listener, err := net.Listen("tcp", "0.0.0.0:9000") + // open a server listening to establish the TCP connections + listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port)) if err != nil { panic(err) } @@ -42,6 +55,7 @@ func main() { } go func(conn net.Conn) { + // Close the connection after 10 secs time.Sleep(10 * time.Second) conn.Close() }(conn) @@ -51,12 +65,12 @@ func main() { func probe(ip string) { log.Printf("probing %v", ip) - ipAddr, err := net.ResolveIPAddr("ip4:tcp", ip) + ipAddr, err := net.ResolveIPAddr("ip:tcp", ip) if err != nil { panic(err) } - conn, err := net.ListenIP("ip4:tcp", ipAddr) + conn, err := net.ListenIP("ip:tcp", ipAddr) if err != nil { panic(err) } @@ -89,7 +103,7 @@ func probe(ip string) { continue } if pkt.Flags&RST != 0 { - panic("RST received") + log.Println("ERROR: RST received") } if pkt.Flags&ACK != 0 { if seq, ok := pending[addr.String()]; ok { @@ -111,20 +125,26 @@ func probe(ip string) { _, err := conn.WriteTo(badPkt.encode(localIP, remoteIP, data[:]), addr) if err != nil { log.Printf("conn.WriteTo() error: %v", err) + } else { + log.Println("boom packet injected") } } } } } -func getIP() net.IP { - conn, err := net.Dial("udp", "8.8.8.8:53") - if err != nil { - log.Fatal(err) +// getIPs gets the IPs from the downward API +// we don't have to validate the IPs because +// they are validated previously by kubernetes/CNI +func getIPs() []net.IP { + var ips []net.IP + podIP, podIPs := os.Getenv("POD_IP"), os.Getenv("POD_IPS") + if podIPs != "" { + for _, ip := range strings.Split(podIPs, ",") { + ips = append(ips, net.ParseIP(ip)) + } + } else if podIP != "" { + ips = append(ips, net.ParseIP(podIP)) } - defer conn.Close() - - localAddr := conn.LocalAddr().(*net.UDPAddr) - - return localAddr.IP + return ips } diff --git a/test/images/regression-issue-74839/tcp.go b/test/images/regression-issue-74839/tcp.go index c0ca067a47e..36af0d0c53d 100644 --- a/test/images/regression-issue-74839/tcp.go +++ b/test/images/regression-issue-74839/tcp.go @@ -31,6 +31,10 @@ const ( ) const ( + // Control Bits. 6 bits. + // 00 01 02 03 04 05 + // U A P R S F + // FIN is a TCP flag FIN uint16 = 1 << iota // SYN is a TCP flag @@ -52,11 +56,16 @@ const ( ) type tcpPacket struct { - SrcPort uint16 // 0 - DestPort uint16 // 2 - Seq uint32 // 4 - Ack uint32 // 8 - Flags uint16 // 13 + SrcPort uint16 // 0 + DestPort uint16 // 2 + Seq uint32 // 4 + Ack uint32 // 8 + // Flags bytes includes + // Data Offset. 4 bits. + // reserved. 3 bits. (must be zero) + // ECN, Explicit Congestion Notification. 3 bits. + // Control Bits (Real flags). 6 bits. + Flags uint16 // 12 WindowSize uint16 // 14 Checksum uint16 // 16 UrgentPtr uint16 // 18 @@ -133,16 +142,29 @@ func (t *tcpPacket) encode(src, dest net.IP, data []byte) []byte { func checksumTCP(src, dest net.IP, tcpHeader, data []byte) uint16 { log.Printf("calling checksumTCP: %v %v %v %v", src, dest, tcpHeader, data) - chk := &tcpChecksumer{} + chk := &tcpChecksummer{} // Encode pseudoheader. - chk.add(src.To4()) - chk.add(dest.To4()) + if src.To4() != nil { + // IPv4 [ src (4) | dst (4) | rsv (1) | proto (1) | tcp length (2) ] ... | tcp header | data + chk.add(src.To4()) + chk.add(dest.To4()) + pseudoHeader := make([]byte, 4) + pseudoHeader[1] = tcpProtoNum + binary.BigEndian.PutUint16(pseudoHeader[2:], uint16(len(data)+len(tcpHeader))) + chk.add(pseudoHeader) + + } else { + // https://tools.ietf.org/html/rfc2460 IPv6 + // IPv6 [ src (16) | dst (16) | payload length (4) | Zero (3) | NH/proto (1) ] ... | tcp header | data + chk.add(src.To16()) + chk.add(dest.To16()) + pseudoHeader := make([]byte, 8) + binary.BigEndian.PutUint32(pseudoHeader, uint32(len(data)+len(tcpHeader))) + pseudoHeader[7] = tcpProtoNum + chk.add(pseudoHeader) + } - var pseudoHeader [4]byte - pseudoHeader[1] = tcpProtoNum - binary.BigEndian.PutUint16(pseudoHeader[2:], uint16(len(data)+len(tcpHeader))) - chk.add(pseudoHeader[:]) chk.add(tcpHeader) chk.add(data) @@ -151,13 +173,13 @@ func checksumTCP(src, dest net.IP, tcpHeader, data []byte) uint16 { return chk.finalize() } -type tcpChecksumer struct { +type tcpChecksummer struct { sum uint32 oddByte byte length int } -func (c *tcpChecksumer) finalize() uint16 { +func (c *tcpChecksummer) finalize() uint16 { ret := c.sum if c.length%2 > 0 { ret += uint32(c.oddByte) @@ -171,7 +193,7 @@ func (c *tcpChecksumer) finalize() uint16 { return ^uint16(ret) } -func (c *tcpChecksumer) add(data []byte) { +func (c *tcpChecksummer) add(data []byte) { if len(data) == 0 { return } diff --git a/test/images/regression-issue-74839/tcp_test.go b/test/images/regression-issue-74839/tcp_test.go new file mode 100644 index 00000000000..63c7ccec4df --- /dev/null +++ b/test/images/regression-issue-74839/tcp_test.go @@ -0,0 +1,91 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Partially copied from https://github.com/bowei/lighthouse/blob/master/pkg/probe/tcp_test.go + +package main + +import ( + "testing" +) + +func TestTCPChecksummer(t *testing.T) { + t.Parallel() + + for _, tc := range []struct { + desc string + data [][]byte + want uint16 + }{ + { + desc: "empty", + data: [][]byte{}, + want: 0xffff, + }, + { + desc: "1 byte", + data: [][]byte{{0x55}}, + want: 0xffaa, + }, + { + desc: "2 bytes", + data: [][]byte{{0x55, 0x88}}, + want: 0x77aa, + }, + { + desc: "3 bytes", + data: [][]byte{{0x55, 0x88, 0x99}}, + want: 0x7711, + }, + { + desc: "3 bytes / 1 at a time", + data: [][]byte{{0x55}, {0x88}, {0x99}}, + want: 0x7711, + }, + { + desc: "3 bytes / 2 1", + data: [][]byte{{0x55, 0x88}, {0x99}}, + want: 0x7711, + }, + { + desc: "simple packet", + data: [][]byte{ + { + 0x7f, 0x00, 0x00, 0x01, // 127.0.0.1 + 0x7f, 0x00, 0x00, 0x01, // 127.0.0.1 + 0x00, 0x06, // TCP proto 6 + 0x00, 0x14, // Size = 20 bytes + 0x00, 0x50, 0x1f, 0x90, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x50, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + }, + }, + want: 0xff91, + }, + } { + t.Run(tc.desc, func(t *testing.T) { + c := &tcpChecksummer{} + for _, b := range tc.data { + c.add(b) + } + got := c.finalize() + if got != tc.want { + t.Errorf("c.finalize() = %x, want %x; bytes: %v", got, tc.want, tc.data) + } + }) + } +}