|
| 1 | +//go:build e2e_testing |
| 2 | +// +build e2e_testing |
| 3 | + |
| 4 | +package e2e |
| 5 | + |
| 6 | +import ( |
| 7 | + "testing" |
| 8 | + "time" |
| 9 | + |
| 10 | + "github.com/slackhq/nebula" |
| 11 | + "github.com/slackhq/nebula/cert" |
| 12 | + "github.com/slackhq/nebula/cert_test" |
| 13 | + "github.com/slackhq/nebula/e2e/router" |
| 14 | + "github.com/slackhq/nebula/header" |
| 15 | + "github.com/slackhq/nebula/udp" |
| 16 | + "github.com/stretchr/testify/assert" |
| 17 | + "github.com/stretchr/testify/require" |
| 18 | +) |
| 19 | + |
| 20 | +func assertTestRequestEchoed(t *testing.T, cipher string) { |
| 21 | + ca, _, caKey, _ := cert_test.NewTestCaCert(cert.Version1, cert.Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{}) |
| 22 | + over := m{"cipher": cipher} |
| 23 | + a, aNet, aUdp, _ := newSimpleServer(cert.Version1, ca, caKey, "a", "10.128.0.1/24", over) |
| 24 | + b, bNet, bUdp, _ := newSimpleServer(cert.Version1, ca, caKey, "b", "10.128.0.2/24", over) |
| 25 | + |
| 26 | + a.InjectLightHouseAddr(bNet[0].Addr(), bUdp) |
| 27 | + b.InjectLightHouseAddr(aNet[0].Addr(), aUdp) |
| 28 | + a.Start() |
| 29 | + b.Start() |
| 30 | + t.Cleanup(func() { a.Stop(); b.Stop() }) |
| 31 | + r := router.NewR(t, a, b) |
| 32 | + defer r.RenderFlow() |
| 33 | + |
| 34 | + assertTunnel(t, aNet[0].Addr(), bNet[0].Addr(), a, b, r) |
| 35 | + drainUDPTx(a) |
| 36 | + drainUDPTx(b) |
| 37 | + |
| 38 | + payload := []byte("a test payload well over sixteen bytes long, wow it's so very long long long!") |
| 39 | + require.Greater(t, len(payload), header.Len) |
| 40 | + a.GetF().SendMessageToVpnAddr(header.Test, header.TestRequest, bNet[0].Addr(), payload, make([]byte, 12, 12), make([]byte, udp.MTU)) |
| 41 | + |
| 42 | + // Deliver A's request to B; B must echo a reply back |
| 43 | + b.InjectUDPPacket(a.GetFromUDP(true)) |
| 44 | + reply := nextUDPTxOfType(t, b, header.Test, header.TestReply, 2*time.Second) |
| 45 | + |
| 46 | + assert.Equal(t, aUdp, reply.To, "the reply must go back to the requester") |
| 47 | + // header + echoed payload + 16-byte AEAD tag: proves the whole payload |
| 48 | + // round-tripped rather than being dropped or truncated. |
| 49 | + assert.Equal(t, header.Len+len(payload)+16, len(reply.Data), "the full payload must be echoed back") |
| 50 | +} |
| 51 | + |
| 52 | +func TestTestRequestEchoesLongPayloadAES(t *testing.T) { |
| 53 | + assertTestRequestEchoed(t, "aes") |
| 54 | +} |
| 55 | + |
| 56 | +func TestTestRequestEchoesLongPayloadChaChaPoly(t *testing.T) { |
| 57 | + assertTestRequestEchoed(t, "chachapoly") |
| 58 | +} |
| 59 | + |
| 60 | +// drainUDPTx empties a control's UDP tx queue without blocking. |
| 61 | +func drainUDPTx(c *nebula.Control) { |
| 62 | + for c.GetFromUDP(false) != nil { |
| 63 | + } |
| 64 | +} |
| 65 | + |
| 66 | +// nextUDPTxOfType returns the next packet a control transmits whose nebula |
| 67 | +// header matches (wantType, wantSub), skipping unrelated packets. |
| 68 | +// It fails the test if none arrives within the timeout. |
| 69 | +func nextUDPTxOfType(t *testing.T, c *nebula.Control, wantType header.MessageType, wantSub header.MessageSubType, within time.Duration) *udp.Packet { |
| 70 | + t.Helper() |
| 71 | + ch := c.GetUDPTxChan() |
| 72 | + timeout := time.After(within) |
| 73 | + for { |
| 74 | + select { |
| 75 | + case p := <-ch: |
| 76 | + var h header.H |
| 77 | + if err := h.Parse(p.Data); err == nil && h.Type == wantType && h.Subtype == wantSub { |
| 78 | + return p |
| 79 | + } |
| 80 | + case <-timeout: |
| 81 | + t.Fatalf("timed out waiting for a %v/%v packet on the udp tx queue", wantType, wantSub) |
| 82 | + return nil |
| 83 | + } |
| 84 | + } |
| 85 | +} |
0 commit comments