mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +00:00
Add test image for issue-74839
This commit is contained in:
parent
aa52140928
commit
ccf36c235b
@ -30,6 +30,7 @@ filegroup(
|
||||
"//test/images/pets/peer-finder:all-srcs",
|
||||
"//test/images/port-forward-tester:all-srcs",
|
||||
"//test/images/porter:all-srcs",
|
||||
"//test/images/regression-issue-74839:all-srcs",
|
||||
"//test/images/resource-consumer:all-srcs",
|
||||
"//test/images/sample-apiserver:all-srcs",
|
||||
"//test/images/sample-device-plugin:all-srcs",
|
||||
|
1
test/images/regression-issue-74839/.gitignore
vendored
Normal file
1
test/images/regression-issue-74839/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/regression-issue-74839
|
31
test/images/regression-issue-74839/BUILD
Normal file
31
test/images/regression-issue-74839/BUILD
Normal file
@ -0,0 +1,31 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"main.go",
|
||||
"tcp.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/test/images/regression-issue-74839",
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
go_binary(
|
||||
name = "regression-issue-74839",
|
||||
embed = [":go_default_library"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
20
test/images/regression-issue-74839/Dockerfile
Normal file
20
test/images/regression-issue-74839/Dockerfile
Normal file
@ -0,0 +1,20 @@
|
||||
# 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.
|
||||
|
||||
FROM gcr.io/distroless/base
|
||||
|
||||
ADD regression-issue-74839 /regression-issue-74839
|
||||
|
||||
ENTRYPOINT ["/regression-issue-74839"]
|
||||
|
27
test/images/regression-issue-74839/Makefile
Normal file
27
test/images/regression-issue-74839/Makefile
Normal file
@ -0,0 +1,27 @@
|
||||
# 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.
|
||||
|
||||
SRCS=regression-issue-74839
|
||||
ARCH ?= amd64
|
||||
TARGET ?= $(CURDIR)
|
||||
GOARM = 7
|
||||
GOLANG_VERSION ?= latest
|
||||
SRC_DIR = $(notdir $(shell pwd))
|
||||
|
||||
export
|
||||
|
||||
.PHONY: bin
|
||||
bin:
|
||||
../image-util.sh bin $(SRCS)
|
||||
|
14
test/images/regression-issue-74839/README.md
Normal file
14
test/images/regression-issue-74839/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
# Reproduction of k8s issue #74839
|
||||
|
||||
Network services with heavy load will cause "connection reset" from time to
|
||||
time. Especially those with big payloads. When packets with sequence number
|
||||
out-of-window arrived k8s node, conntrack marked them as INVALID. kube-proxy
|
||||
will ignore them, without rewriting DNAT. The packet goes back the the original
|
||||
pod, who doesn't recognize the packet because of the wrong source ip, end up
|
||||
RSTing the connection.
|
||||
|
||||
## Reference
|
||||
|
||||
https://github.com/kubernetes/kubernetes/issues/74839
|
||||
|
||||
|
1
test/images/regression-issue-74839/VERSION
Normal file
1
test/images/regression-issue-74839/VERSION
Normal file
@ -0,0 +1 @@
|
||||
1.0
|
130
test/images/regression-issue-74839/main.go
Normal file
130
test/images/regression-issue-74839/main.go
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ip := getIP().String()
|
||||
log.Printf("external ip: %v", ip)
|
||||
|
||||
go probe(ip)
|
||||
|
||||
log.Printf("listen on %v:9000", "0.0.0.0")
|
||||
|
||||
listener, err := net.Listen("tcp", "0.0.0.0:9000")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go func(conn net.Conn) {
|
||||
time.Sleep(10 * time.Second)
|
||||
conn.Close()
|
||||
}(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func probe(ip string) {
|
||||
log.Printf("probing %v", ip)
|
||||
|
||||
ipAddr, err := net.ResolveIPAddr("ip4:tcp", ip)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
conn, err := net.ListenIP("ip4:tcp", ipAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pending := make(map[string]uint32)
|
||||
|
||||
var buffer [4096]byte
|
||||
for {
|
||||
n, addr, err := conn.ReadFrom(buffer[:])
|
||||
if err != nil {
|
||||
log.Printf("conn.ReadFrom() error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
pkt := &tcpPacket{}
|
||||
data, err := pkt.decode(buffer[:n])
|
||||
if err != nil {
|
||||
log.Printf("tcp packet parse error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if pkt.DestPort != 9000 {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("tcp packet: %+v, flag: %v, data: %v, addr: %v", pkt, pkt.FlagString(), data, addr)
|
||||
|
||||
if pkt.Flags&SYN != 0 {
|
||||
pending[addr.String()] = pkt.Seq + 1
|
||||
continue
|
||||
}
|
||||
if pkt.Flags&RST != 0 {
|
||||
panic("RST received")
|
||||
}
|
||||
if pkt.Flags&ACK != 0 {
|
||||
if seq, ok := pending[addr.String()]; ok {
|
||||
log.Println("connection established")
|
||||
delete(pending, addr.String())
|
||||
|
||||
badPkt := &tcpPacket{
|
||||
SrcPort: pkt.DestPort,
|
||||
DestPort: pkt.SrcPort,
|
||||
Ack: seq,
|
||||
Seq: pkt.Ack - 100000, // Bad: seq out-of-window
|
||||
Flags: (5 << 12) | PSH | ACK, // Offset and Flags oooo000F FFFFFFFF (o:offset, F:flags)
|
||||
WindowSize: pkt.WindowSize,
|
||||
}
|
||||
|
||||
data := []byte("boom!!!")
|
||||
remoteIP := net.ParseIP(addr.String())
|
||||
localIP := net.ParseIP(conn.LocalAddr().String())
|
||||
_, err := conn.WriteTo(badPkt.encode(localIP, remoteIP, data[:]), addr)
|
||||
if err != nil {
|
||||
log.Printf("conn.WriteTo() error: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getIP() net.IP {
|
||||
conn, err := net.Dial("udp", "8.8.8.8:53")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
||||
|
||||
return localAddr.IP
|
||||
}
|
189
test/images/regression-issue-74839/tcp.go
Normal file
189
test/images/regression-issue-74839/tcp.go
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
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.go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
tcpHeaderSize = 20
|
||||
tcpProtoNum = 6
|
||||
)
|
||||
|
||||
const (
|
||||
// FIN is a TCP flag
|
||||
FIN uint16 = 1 << iota
|
||||
// SYN is a TCP flag
|
||||
SYN
|
||||
// RST is a TCP flag
|
||||
RST
|
||||
// PSH is a TCP flag
|
||||
PSH
|
||||
// ACK is a TCP flag
|
||||
ACK
|
||||
// URG is a TCP flag
|
||||
URG
|
||||
// ECE is a TCP flag
|
||||
ECE
|
||||
// CWR is a TCP flag
|
||||
CWR
|
||||
// NS is a TCP flag
|
||||
NS
|
||||
)
|
||||
|
||||
type tcpPacket struct {
|
||||
SrcPort uint16 // 0
|
||||
DestPort uint16 // 2
|
||||
Seq uint32 // 4
|
||||
Ack uint32 // 8
|
||||
Flags uint16 // 13
|
||||
WindowSize uint16 // 14
|
||||
Checksum uint16 // 16
|
||||
UrgentPtr uint16 // 18
|
||||
// 20
|
||||
}
|
||||
|
||||
func (t *tcpPacket) decode(pkt []byte) ([]byte, error) {
|
||||
err := binary.Read(bytes.NewReader(pkt), binary.BigEndian, t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pkt[t.DataOffset():], nil
|
||||
}
|
||||
|
||||
func (t *tcpPacket) DataOffset() int {
|
||||
return int((t.Flags >> 12) * 4)
|
||||
}
|
||||
|
||||
func (t *tcpPacket) FlagString() string {
|
||||
out := ""
|
||||
|
||||
if t.Flags&FIN != 0 {
|
||||
out += "FIN "
|
||||
}
|
||||
if t.Flags&SYN != 0 {
|
||||
out += "SYN "
|
||||
}
|
||||
if t.Flags&RST != 0 {
|
||||
out += "RST "
|
||||
}
|
||||
if t.Flags&PSH != 0 {
|
||||
out += "PSH "
|
||||
}
|
||||
if t.Flags&ACK != 0 {
|
||||
out += "ACK "
|
||||
}
|
||||
if t.Flags&URG != 0 {
|
||||
out += "URG "
|
||||
}
|
||||
if t.Flags&ECE != 0 {
|
||||
out += "ECE "
|
||||
}
|
||||
if t.Flags&CWR != 0 {
|
||||
out += "CWR "
|
||||
}
|
||||
if t.Flags&NS != 0 {
|
||||
out += "NS "
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (t *tcpPacket) encode(src, dest net.IP, data []byte) []byte {
|
||||
pkt := make([]byte, 20, 20+len(data))
|
||||
|
||||
encoder := binary.BigEndian
|
||||
encoder.PutUint16(pkt, t.SrcPort)
|
||||
encoder.PutUint16(pkt[2:], t.DestPort)
|
||||
encoder.PutUint32(pkt[4:], t.Seq)
|
||||
encoder.PutUint32(pkt[8:], t.Ack)
|
||||
encoder.PutUint16(pkt[12:], t.Flags)
|
||||
encoder.PutUint16(pkt[14:], t.WindowSize)
|
||||
encoder.PutUint16(pkt[18:], t.UrgentPtr)
|
||||
|
||||
checksum := checksumTCP(src, dest, pkt[:tcpHeaderSize], data)
|
||||
pkt[16] = uint8(checksum & 0xff)
|
||||
pkt[17] = uint8(checksum >> 8)
|
||||
|
||||
pkt = append(pkt, data...)
|
||||
|
||||
return pkt
|
||||
}
|
||||
|
||||
func checksumTCP(src, dest net.IP, tcpHeader, data []byte) uint16 {
|
||||
log.Printf("calling checksumTCP: %v %v %v %v", src, dest, tcpHeader, data)
|
||||
chk := &tcpChecksumer{}
|
||||
|
||||
// Encode pseudoheader.
|
||||
chk.add(src.To4())
|
||||
chk.add(dest.To4())
|
||||
|
||||
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)
|
||||
|
||||
log.Printf("checksumer: %+v", chk)
|
||||
|
||||
return chk.finalize()
|
||||
}
|
||||
|
||||
type tcpChecksumer struct {
|
||||
sum uint32
|
||||
oddByte byte
|
||||
length int
|
||||
}
|
||||
|
||||
func (c *tcpChecksumer) finalize() uint16 {
|
||||
ret := c.sum
|
||||
if c.length%2 > 0 {
|
||||
ret += uint32(c.oddByte)
|
||||
}
|
||||
log.Println("ret: ", ret)
|
||||
for ret>>16 > 0 {
|
||||
ret = ret&0xffff + ret>>16
|
||||
log.Println("ret: ", ret)
|
||||
}
|
||||
log.Println("ret: ", ret)
|
||||
return ^uint16(ret)
|
||||
}
|
||||
|
||||
func (c *tcpChecksumer) add(data []byte) {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
haveOddByte := c.length%2 > 0
|
||||
c.length += len(data)
|
||||
if haveOddByte {
|
||||
data = append([]byte{c.oddByte}, data...)
|
||||
}
|
||||
for i := 0; i < len(data)-1; i += 2 {
|
||||
c.sum += uint32(data[i]) + uint32(data[i+1])<<8
|
||||
}
|
||||
if c.length%2 > 0 {
|
||||
c.oddByte = data[len(data)-1]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user