Serge Bazanski | 6b649f8 | 2021-05-24 15:09:25 +0200 | [diff] [blame] | 1 | package kubenat |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "flag" |
| 6 | "io/ioutil" |
| 7 | "net" |
| 8 | "os" |
| 9 | "testing" |
| 10 | |
| 11 | "github.com/go-test/deep" |
| 12 | ) |
| 13 | |
| 14 | // testConntrack is the anonymized content of a production host. |
| 15 | // The first entry is an appservice-irc connection from a pod to an IRC server. |
| 16 | // The second connection is an UDP connection between two pods. |
| 17 | // The third to last entry is not a NAT entry, but an incoming external |
| 18 | // connection. |
| 19 | // The fourth connection has a mangled/incomplete entry. |
| 20 | const testConntrack = ` |
| 21 | 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 |
| 22 | 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 |
| 23 | 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 |
| 24 | ipv4 2 tcp 6 2 |
| 25 | ` |
| 26 | |
| 27 | // TestConntrackParse exercises the conntrack parser for all entries in testConntrack. |
| 28 | func TestConntrackParse(t *testing.T) { |
| 29 | // Last line is truncated and should be ignored. |
| 30 | got, err := conntrackParse([]byte(testConntrack)) |
| 31 | if err != nil { |
| 32 | t.Fatalf("conntrackParse: %v", err) |
| 33 | } |
| 34 | want := []conntrackEntry{ |
| 35 | { |
| 36 | "ipv4", "tcp", 86384, "ESTABLISHED", |
| 37 | map[string]string{ |
| 38 | "src": "10.10.26.23", "dst": "192.0.2.180", "sport": "57640", "dport": "6697", |
| 39 | "mark": "0", "zone": "0", "use": "2", |
| 40 | }, |
| 41 | map[string]string{ |
| 42 | "src": "192.0.2.180", "dst": "185.236.240.36", "sport": "6697", "dport": "28706", |
| 43 | }, |
| 44 | map[string]bool{ |
| 45 | "ASSURED": true, |
| 46 | }, |
| 47 | }, |
| 48 | { |
| 49 | "ipv4", "udp", 35, "", |
| 50 | map[string]string{ |
| 51 | "src": "10.10.24.162", "dst": "10.10.26.108", "sport": "49347", "dport": "53", |
| 52 | "mark": "0", "zone": "0", "use": "2", |
| 53 | }, |
| 54 | map[string]string{ |
| 55 | "src": "10.10.26.108", "dst": "10.10.24.162", "sport": "53", "dport": "49347", |
| 56 | }, |
| 57 | map[string]bool{ |
| 58 | "ASSURED": true, |
| 59 | }, |
| 60 | }, |
| 61 | { |
| 62 | "ipv4", "tcp", 2, "SYN_SENT", |
| 63 | map[string]string{ |
| 64 | "src": "198.51.100.67", "dst": "185.236.240.56", "sport": "51053", "dport": "3359", |
| 65 | "mark": "0", "zone": "0", "use": "2", |
| 66 | }, |
| 67 | map[string]string{ |
| 68 | "src": "185.236.240.56", "dst": "198.51.100.67", "sport": "3359", "dport": "51053", |
| 69 | }, |
| 70 | map[string]bool{ |
| 71 | "UNREPLIED": true, |
| 72 | }, |
| 73 | }, |
| 74 | } |
| 75 | if diff := deep.Equal(want, got); diff != nil { |
| 76 | t.Error(diff) |
| 77 | } |
| 78 | |
| 79 | ix := buildIndex(got) |
| 80 | if want, got := 0, len(ix.getByRequest("src", "1.2.3.4")); want != got { |
| 81 | t.Errorf("by request, src, 1.2.3.4 should have returned %d result, wanted %d", want, got) |
| 82 | } |
| 83 | if want, got := 1, len(ix.getByRequest("src", "10.10.26.23")); want != got { |
| 84 | t.Errorf("by request, src, 1.2.3.4 should have returned %d result, wanted %d", want, got) |
| 85 | } |
| 86 | if want, got := "10.10.26.23", ix.getByRequest("src", "10.10.26.23")[0].request["src"]; want != got { |
| 87 | t.Errorf("by request, wanted src %q, got %q", want, got) |
| 88 | } |
| 89 | if want, got := 3, len(ix.getByRequest("mark", "0")); want != got { |
| 90 | t.Errorf("by request, mark, 0 should have returned %d result, wanted %d", want, got) |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | // TestTranslationWorker exercises a translation worker with a |
| 95 | // testConntrack-backed conntrack file. |
| 96 | func TestTranslationWorker(t *testing.T) { |
| 97 | flag.Set("logtostderr", "true") |
| 98 | tmpfile, err := ioutil.TempFile("", "conntack") |
| 99 | if err != nil { |
| 100 | t.Fatal(err) |
| 101 | } |
| 102 | defer os.Remove(tmpfile.Name()) |
| 103 | if _, err := tmpfile.Write([]byte(testConntrack)); err != nil { |
| 104 | t.Fatal(err) |
| 105 | } |
| 106 | r := &Resolver{ |
| 107 | conntrackPath: tmpfile.Name(), |
| 108 | translationC: make(chan *translationReq), |
| 109 | } |
| 110 | ctx, ctxC := context.WithCancel(context.Background()) |
| 111 | defer ctxC() |
| 112 | |
| 113 | go r.runTranslationWorker(ctx) |
| 114 | |
| 115 | res, err := r.translate(ctx, &Tuple4{ |
| 116 | RemoteIP: net.ParseIP("192.0.2.180"), |
| 117 | RemotePort: 6697, |
| 118 | LocalIP: net.ParseIP("185.236.240.36"), |
| 119 | LocalPort: 28706, |
| 120 | }) |
| 121 | if err != nil { |
| 122 | t.Fatalf("translate: %v", err) |
| 123 | } |
| 124 | if want, got := net.ParseIP("10.10.26.23"), res.localIP; !want.Equal(got) { |
| 125 | t.Errorf("local ip: wanted %v, got %v", want, got) |
| 126 | } |
| 127 | if want, got := uint16(51336), res.localPort; want != got { |
| 128 | t.Errorf("local port: wanted %d, got %d", want, got) |
| 129 | } |
| 130 | } |