blob: 353d291c9c46c8350a8384cbfb343669679febac [file] [log] [blame]
package kubenat
import (
"context"
"flag"
"io/ioutil"
"net"
"os"
"testing"
"github.com/go-test/deep"
)
// testConntrack is the anonymized content of a production host.
// The first entry is an appservice-irc connection from a pod to an IRC server.
// The second connection is an UDP connection between two pods.
// The third to last entry is not a NAT entry, but an incoming external
// connection.
// The fourth connection has a mangled/incomplete entry.
const testConntrack = `
ipv4 2 tcp 6 86384 ESTABLISHED src=10.10.26.23 dst=192.0.2.180 sport=51336 dport=6697 src=192.0.2.180 dst=185.236.240.36 sport=6697 dport=28706 [ASSURED] mark=0 zone=0 use=2
ipv4 2 udp 17 35 src=10.10.24.162 dst=10.10.26.108 sport=49347 dport=53 src=10.10.26.108 dst=10.10.24.162 sport=53 dport=49347 [ASSURED] mark=0 zone=0 use=2
ipv4 2 tcp 6 2 SYN_SENT src=198.51.100.67 dst=185.236.240.56 sport=51053 dport=3359 [UNREPLIED] src=185.236.240.56 dst=198.51.100.67 sport=3359 dport=51053 mark=0 zone=0 use=2
ipv4 2 tcp 6 2
`
// TestConntrackParse exercises the conntrack parser for all entries in testConntrack.
func TestConntrackParse(t *testing.T) {
// Last line is truncated and should be ignored.
got, err := conntrackParse([]byte(testConntrack))
if err != nil {
t.Fatalf("conntrackParse: %v", err)
}
want := []conntrackEntry{
{
"ipv4", "tcp", 86384, "ESTABLISHED",
map[string]string{
"src": "10.10.26.23", "dst": "192.0.2.180", "sport": "57640", "dport": "6697",
"mark": "0", "zone": "0", "use": "2",
},
map[string]string{
"src": "192.0.2.180", "dst": "185.236.240.36", "sport": "6697", "dport": "28706",
},
map[string]bool{
"ASSURED": true,
},
},
{
"ipv4", "udp", 35, "",
map[string]string{
"src": "10.10.24.162", "dst": "10.10.26.108", "sport": "49347", "dport": "53",
"mark": "0", "zone": "0", "use": "2",
},
map[string]string{
"src": "10.10.26.108", "dst": "10.10.24.162", "sport": "53", "dport": "49347",
},
map[string]bool{
"ASSURED": true,
},
},
{
"ipv4", "tcp", 2, "SYN_SENT",
map[string]string{
"src": "198.51.100.67", "dst": "185.236.240.56", "sport": "51053", "dport": "3359",
"mark": "0", "zone": "0", "use": "2",
},
map[string]string{
"src": "185.236.240.56", "dst": "198.51.100.67", "sport": "3359", "dport": "51053",
},
map[string]bool{
"UNREPLIED": true,
},
},
}
if diff := deep.Equal(want, got); diff != nil {
t.Error(diff)
}
ix := buildIndex(got)
if want, got := 0, len(ix.getByRequest("src", "1.2.3.4")); want != got {
t.Errorf("by request, src, 1.2.3.4 should have returned %d result, wanted %d", want, got)
}
if want, got := 1, len(ix.getByRequest("src", "10.10.26.23")); want != got {
t.Errorf("by request, src, 1.2.3.4 should have returned %d result, wanted %d", want, got)
}
if want, got := "10.10.26.23", ix.getByRequest("src", "10.10.26.23")[0].request["src"]; want != got {
t.Errorf("by request, wanted src %q, got %q", want, got)
}
if want, got := 3, len(ix.getByRequest("mark", "0")); want != got {
t.Errorf("by request, mark, 0 should have returned %d result, wanted %d", want, got)
}
}
// TestTranslationWorker exercises a translation worker with a
// testConntrack-backed conntrack file.
func TestTranslationWorker(t *testing.T) {
flag.Set("logtostderr", "true")
tmpfile, err := ioutil.TempFile("", "conntack")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpfile.Name())
if _, err := tmpfile.Write([]byte(testConntrack)); err != nil {
t.Fatal(err)
}
r := &Resolver{
conntrackPath: tmpfile.Name(),
translationC: make(chan *translationReq),
}
ctx, ctxC := context.WithCancel(context.Background())
defer ctxC()
go r.runTranslationWorker(ctx)
res, err := r.translate(ctx, &Tuple4{
RemoteIP: net.ParseIP("192.0.2.180"),
RemotePort: 6697,
LocalIP: net.ParseIP("185.236.240.36"),
LocalPort: 28706,
})
if err != nil {
t.Fatalf("translate: %v", err)
}
if want, got := net.ParseIP("10.10.26.23"), res.localIP; !want.Equal(got) {
t.Errorf("local ip: wanted %v, got %v", want, got)
}
if want, got := uint16(51336), res.localPort; want != got {
t.Errorf("local port: wanted %d, got %d", want, got)
}
}