crappy AI tests

This commit is contained in:
JackDoan 2026-02-18 15:07:57 -06:00
parent 92ee45ed13
commit 25610225bb
3 changed files with 1880 additions and 0 deletions

400
e2e/snat_test.go Normal file
View file

@ -0,0 +1,400 @@
//go:build e2e_testing
// +build e2e_testing
package e2e
import (
"encoding/binary"
"net/netip"
"testing"
"time"
"github.com/slackhq/nebula/cert"
"github.com/slackhq/nebula/cert_test"
"github.com/slackhq/nebula/e2e/router"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// parseIPv4UDPPacket extracts source/dest IPs, ports, and payload from an IPv4 UDP packet.
func parseIPv4UDPPacket(t testing.TB, pkt []byte) (srcIP, dstIP netip.Addr, srcPort, dstPort uint16, payload []byte) {
t.Helper()
require.True(t, len(pkt) >= 28, "packet too short for IPv4+UDP header")
require.Equal(t, byte(0x45), pkt[0]&0xF0|pkt[0]&0x0F, "not a simple IPv4 packet (IHL!=5)")
srcIP, _ = netip.AddrFromSlice(pkt[12:16])
dstIP, _ = netip.AddrFromSlice(pkt[16:20])
ihl := int(pkt[0]&0x0F) * 4
require.True(t, len(pkt) >= ihl+8, "packet too short for UDP header")
srcPort = binary.BigEndian.Uint16(pkt[ihl : ihl+2])
dstPort = binary.BigEndian.Uint16(pkt[ihl+2 : ihl+4])
udpLen := binary.BigEndian.Uint16(pkt[ihl+4 : ihl+6])
payload = pkt[ihl+8 : ihl+int(udpLen)]
return
}
func TestSNAT_IPv6OnlyPeer_IPv4UnsafeTraffic(t *testing.T) {
// Scenario: Two IPv6-only VPN nodes. The "router" node has unsafe networks
// (192.168.0.0/16) in its cert and a configured SNAT address. The "sender"
// node has an unsafe route for 192.168.0.0/16 via the router.
//
// When sender injects an IPv4 packet destined for the unsafe network, it
// gets tunneled to the router. The router's firewall detects this is IPv4
// from an IPv6-only peer and applies SNAT, rewriting the source IP to the
// SNAT address before delivering it to TUN.
//
// When a reply comes back from TUN addressed to the SNAT address, the
// router un-SNATs it (restoring the original destination) and tunnels it
// back to the sender.
ca, _, caKey, _ := cert_test.NewTestCaCert(cert.Version2, cert.Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{})
unsafePrefix := "192.168.0.0/16"
snatAddr := netip.MustParseAddr("169.254.42.42")
// Router: IPv6-only with unsafe networks and a manual SNAT address.
// Override inbound firewall with local_cidr: "any" so both IPv4 (unsafe)
// and IPv6 (VPN) traffic is accepted.
routerControl, routerVpnIpNet, routerUdpAddr, _ := newSimpleServerWithUdpAndUnsafeNetworks(
cert.Version2, ca, caKey, "router", "ff::1/64",
netip.MustParseAddrPort("[beef::1]:4242"),
unsafePrefix,
m{
"firewall": m{
"inbound": []m{{
"proto": "any",
"port": "any",
"host": "any",
"local_cidr": "any",
}},
},
"tun": m{
"snat_address_for_4over6": snatAddr.String(),
},
},
)
// Sender: IPv6-only with an unsafe route via the router
senderControl, _, _, _ := newSimpleServerWithUdp(
cert.Version2, ca, caKey, "sender", "ff::2/64",
netip.MustParseAddrPort("[beef::2]:4242"),
m{
"tun": m{
"unsafe_routes": []m{
{"route": unsafePrefix, "via": routerVpnIpNet[0].Addr().String()},
},
},
},
)
// Tell sender where the router lives
senderControl.InjectLightHouseAddr(routerVpnIpNet[0].Addr(), routerUdpAddr)
// Build the router and start both nodes
r := router.NewR(t, routerControl, senderControl)
defer r.RenderFlow()
routerControl.Start()
senderControl.Start()
// --- Outbound: sender -> IPv4 unsafe dest (via router with SNAT) ---
origSrcIP := netip.MustParseAddr("10.0.0.1")
unsafeDest := netip.MustParseAddr("192.168.1.1")
var origSrcPort uint16 = 12345
var dstPort uint16 = 80
t.Log("Sender injects an IPv4 packet to the unsafe network")
senderControl.InjectTunUDPPacket(unsafeDest, dstPort, origSrcIP, origSrcPort, []byte("snat me"))
t.Log("Route packets (handshake + data) until the router gets the packet on TUN")
snatPkt := r.RouteForAllUntilTxTun(routerControl)
t.Log("Verify the packet was SNATted")
gotSrcIP, gotDstIP, gotSrcPort, gotDstPort, gotPayload := parseIPv4UDPPacket(t, snatPkt)
assert.Equal(t, snatAddr, gotSrcIP, "source IP should be rewritten to the SNAT address")
assert.Equal(t, unsafeDest, gotDstIP, "destination IP should be unchanged")
assert.Equal(t, dstPort, gotDstPort, "destination port should be unchanged")
assert.Equal(t, []byte("snat me"), gotPayload, "payload should be unchanged")
// Capture the SNAT port (may differ from original if port was remapped)
snatPort := gotSrcPort
t.Logf("SNAT port: %d (original: %d)", snatPort, origSrcPort)
// --- Return: reply from unsafe dest -> un-SNATted back to sender ---
t.Log("Router injects a reply packet from the unsafe dest to the SNAT address")
routerControl.InjectTunUDPPacket(snatAddr, snatPort, unsafeDest, dstPort, []byte("reply from unsafe"))
t.Log("Route until sender gets the reply on TUN")
replyPkt := r.RouteForAllUntilTxTun(senderControl)
t.Log("Verify the reply was un-SNATted")
replySrcIP, replyDstIP, replySrcPort, replyDstPort, replyPayload := parseIPv4UDPPacket(t, replyPkt)
assert.Equal(t, unsafeDest, replySrcIP, "reply source should be the unsafe dest")
assert.Equal(t, origSrcIP, replyDstIP, "reply dest should be the original source IP (un-SNATted)")
assert.Equal(t, dstPort, replySrcPort, "reply source port should be the unsafe dest port")
assert.Equal(t, origSrcPort, replyDstPort, "reply dest port should be the original source port (un-SNATted)")
assert.Equal(t, []byte("reply from unsafe"), replyPayload, "payload should be unchanged")
r.RenderHostmaps("Final hostmaps", routerControl, senderControl)
// Also verify normal IPv6 VPN traffic still works between the nodes
t.Log("Verify normal IPv6 VPN tunnel still works")
assertTunnel(t, routerVpnIpNet[0].Addr(), senderControl.GetVpnAddrs()[0], routerControl, senderControl, r)
routerControl.Stop()
senderControl.Stop()
}
func TestSNAT_MultipleFlows(t *testing.T) {
// Test that multiple distinct IPv4 flows from the same IPv6-only peer
// are tracked independently through SNAT.
ca, _, caKey, _ := cert_test.NewTestCaCert(cert.Version2, cert.Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{})
unsafePrefix := "192.168.0.0/16"
snatAddr := netip.MustParseAddr("169.254.42.42")
routerControl, routerVpnIpNet, routerUdpAddr, _ := newSimpleServerWithUdpAndUnsafeNetworks(
cert.Version2, ca, caKey, "router", "ff::1/64",
netip.MustParseAddrPort("[beef::1]:4242"),
unsafePrefix,
m{
"firewall": m{
"inbound": []m{{
"proto": "any",
"port": "any",
"host": "any",
"local_cidr": "any",
}},
},
"tun": m{
"snat_address_for_4over6": snatAddr.String(),
},
},
)
senderControl, _, _, _ := newSimpleServerWithUdp(
cert.Version2, ca, caKey, "sender", "ff::2/64",
netip.MustParseAddrPort("[beef::2]:4242"),
m{
"tun": m{
"unsafe_routes": []m{
{"route": unsafePrefix, "via": routerVpnIpNet[0].Addr().String()},
},
},
},
)
senderControl.InjectLightHouseAddr(routerVpnIpNet[0].Addr(), routerUdpAddr)
r := router.NewR(t, routerControl, senderControl)
defer r.RenderFlow()
r.CancelFlowLogs()
routerControl.Start()
senderControl.Start()
unsafeDest := netip.MustParseAddr("192.168.1.1")
// Send first flow
senderControl.InjectTunUDPPacket(unsafeDest, 80, netip.MustParseAddr("10.0.0.1"), 1111, []byte("flow1"))
pkt1 := r.RouteForAllUntilTxTun(routerControl)
srcIP1, _, srcPort1, _, payload1 := parseIPv4UDPPacket(t, pkt1)
assert.Equal(t, snatAddr, srcIP1)
assert.Equal(t, []byte("flow1"), payload1)
// Send second flow (different source port)
senderControl.InjectTunUDPPacket(unsafeDest, 80, netip.MustParseAddr("10.0.0.1"), 2222, []byte("flow2"))
pkt2 := r.RouteForAllUntilTxTun(routerControl)
srcIP2, _, srcPort2, _, payload2 := parseIPv4UDPPacket(t, pkt2)
assert.Equal(t, snatAddr, srcIP2)
assert.Equal(t, []byte("flow2"), payload2)
// The two flows should have different SNAT ports (since they're different conntracks)
t.Logf("Flow 1 SNAT port: %d, Flow 2 SNAT port: %d", srcPort1, srcPort2)
// Reply to flow 2 first (out of order)
routerControl.InjectTunUDPPacket(snatAddr, srcPort2, unsafeDest, 80, []byte("reply2"))
reply2 := r.RouteForAllUntilTxTun(senderControl)
_, replyDst2, _, replyDstPort2, replyPayload2 := parseIPv4UDPPacket(t, reply2)
assert.Equal(t, netip.MustParseAddr("10.0.0.1"), replyDst2)
assert.Equal(t, uint16(2222), replyDstPort2, "reply to flow 2 should restore original port 2222")
assert.Equal(t, []byte("reply2"), replyPayload2)
// Reply to flow 1
routerControl.InjectTunUDPPacket(snatAddr, srcPort1, unsafeDest, 80, []byte("reply1"))
reply1 := r.RouteForAllUntilTxTun(senderControl)
_, replyDst1, _, replyDstPort1, replyPayload1 := parseIPv4UDPPacket(t, reply1)
assert.Equal(t, netip.MustParseAddr("10.0.0.1"), replyDst1)
assert.Equal(t, uint16(1111), replyDstPort1, "reply to flow 1 should restore original port 1111")
assert.Equal(t, []byte("reply1"), replyPayload1)
routerControl.Stop()
senderControl.Stop()
}
// --- Adversarial SNAT E2E Tests ---
func TestSNAT_UnsolicitedReplyDropped(t *testing.T) {
// Without any outbound SNAT traffic, inject a packet from the router's TUN
// addressed to the SNAT address. The sender must never receive it because
// there's no conntrack entry to un-SNAT through.
ca, _, caKey, _ := cert_test.NewTestCaCert(cert.Version2, cert.Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{})
unsafePrefix := "192.168.0.0/16"
snatAddr := netip.MustParseAddr("169.254.42.42")
routerControl, routerVpnIpNet, routerUdpAddr, _ := newSimpleServerWithUdpAndUnsafeNetworks(
cert.Version2, ca, caKey, "router", "ff::1/64",
netip.MustParseAddrPort("[beef::1]:4242"),
unsafePrefix,
m{
"firewall": m{
"inbound": []m{{
"proto": "any",
"port": "any",
"host": "any",
"local_cidr": "any",
}},
},
"tun": m{
"snat_address_for_4over6": snatAddr.String(),
},
},
)
senderControl, _, _, _ := newSimpleServerWithUdp(
cert.Version2, ca, caKey, "sender", "ff::2/64",
netip.MustParseAddrPort("[beef::2]:4242"),
m{
"tun": m{
"unsafe_routes": []m{
{"route": unsafePrefix, "via": routerVpnIpNet[0].Addr().String()},
},
},
},
)
senderControl.InjectLightHouseAddr(routerVpnIpNet[0].Addr(), routerUdpAddr)
r := router.NewR(t, routerControl, senderControl)
defer r.RenderFlow()
r.CancelFlowLogs()
routerControl.Start()
senderControl.Start()
// First establish the tunnel with normal IPv6 traffic so handshake completes
assertTunnel(t, routerVpnIpNet[0].Addr(), senderControl.GetVpnAddrs()[0], routerControl, senderControl, r)
// Inject the unsolicited reply from router's TUN to the SNAT address.
// There is NO prior outbound SNAT flow, so no conntrack entry exists.
// The router should silently drop this because unSnat finds no matching conntrack.
routerControl.InjectTunUDPPacket(snatAddr, 55555, netip.MustParseAddr("192.168.1.1"), 80, []byte("unsolicited"))
// Send a canary IPv6 VPN packet after the bad one. Since the router processes
// TUN packets sequentially, the canary arriving proves the bad packet was processed first.
senderVpnAddr := senderControl.GetVpnAddrs()[0]
routerControl.InjectTunUDPPacket(senderVpnAddr, 90, routerVpnIpNet[0].Addr(), 80, []byte("canary"))
canaryPkt := r.RouteForAllUntilTxTun(senderControl)
assertUdpPacket(t, []byte("canary"), canaryPkt, routerVpnIpNet[0].Addr(), senderVpnAddr, 80, 90)
// The unsolicited packet should have been dropped — nothing else on sender's TUN
got := senderControl.GetFromTun(false)
assert.Nil(t, got, "sender should not receive unsolicited packet to SNAT address with no conntrack entry")
routerControl.Stop()
senderControl.Stop()
}
func TestSNAT_NonUnsafeDestDropped(t *testing.T) {
// An IPv6-only sender sends IPv4 traffic to a destination outside the router's
// unsafe networks (172.16.0.1 when unsafe is 192.168.0.0/16). The router should
// reject this because the local address is not routable. This verifies that
// willingToHandleLocalAddr enforces boundaries on what SNAT traffic can reach.
ca, _, caKey, _ := cert_test.NewTestCaCert(cert.Version2, cert.Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{})
unsafePrefix := "192.168.0.0/16"
snatAddr := netip.MustParseAddr("169.254.42.42")
routerControl, routerVpnIpNet, routerUdpAddr, _ := newSimpleServerWithUdpAndUnsafeNetworks(
cert.Version2, ca, caKey, "router", "ff::1/64",
netip.MustParseAddrPort("[beef::1]:4242"),
unsafePrefix,
m{
"firewall": m{
"inbound": []m{{
"proto": "any",
"port": "any",
"host": "any",
"local_cidr": "any",
}},
},
"tun": m{
"snat_address_for_4over6": snatAddr.String(),
},
},
)
// Sender has unsafe routes for BOTH 192.168.0.0/16 AND 172.16.0.0/12 via router.
// This means the sender will route 172.16.0.1 through the tunnel to the router.
// But the router should reject it because 172.16.0.0/12 is NOT in its unsafe networks.
senderControl, _, _, _ := newSimpleServerWithUdp(
cert.Version2, ca, caKey, "sender", "ff::2/64",
netip.MustParseAddrPort("[beef::2]:4242"),
m{
"tun": m{
"unsafe_routes": []m{
{"route": unsafePrefix, "via": routerVpnIpNet[0].Addr().String()},
{"route": "172.16.0.0/12", "via": routerVpnIpNet[0].Addr().String()},
},
},
},
)
senderControl.InjectLightHouseAddr(routerVpnIpNet[0].Addr(), routerUdpAddr)
r := router.NewR(t, routerControl, senderControl)
defer r.RenderFlow()
r.CancelFlowLogs()
routerControl.Start()
senderControl.Start()
// Establish the tunnel first
assertTunnel(t, routerVpnIpNet[0].Addr(), senderControl.GetVpnAddrs()[0], routerControl, senderControl, r)
// Send to 172.16.0.1 (NOT in router's unsafe networks 192.168.0.0/16).
// The router should reject this at willingToHandleLocalAddr.
senderControl.InjectTunUDPPacket(
netip.MustParseAddr("172.16.0.1"), 80,
netip.MustParseAddr("10.0.0.1"), 12345,
[]byte("wrong dest"),
)
// Send a canary to a valid unsafe destination to prove the bad packet was processed
senderControl.InjectTunUDPPacket(
netip.MustParseAddr("192.168.1.1"), 80,
netip.MustParseAddr("10.0.0.1"), 33333,
[]byte("canary"),
)
// Route until the canary arrives — the 172.16.0.1 packet should have been
// processed and dropped before the canary gets through
canaryPkt := r.RouteForAllUntilTxTun(routerControl)
_, canaryDst, _, _, canaryPayload := parseIPv4UDPPacket(t, canaryPkt)
assert.Equal(t, netip.MustParseAddr("192.168.1.1"), canaryDst, "canary should arrive at the valid unsafe dest")
assert.Equal(t, []byte("canary"), canaryPayload)
// No more packets — the 172.16.0.1 packet was dropped
got := routerControl.GetFromTun(false)
assert.Nil(t, got, "packet to non-unsafe destination 172.16.0.1 should be dropped by the router")
routerControl.Stop()
senderControl.Stop()
}

171
overlay/tun_snat_test.go Normal file
View file

@ -0,0 +1,171 @@
package overlay
import (
"io"
"net/netip"
"testing"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/config"
"github.com/slackhq/nebula/routing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// mockDevice is a minimal Device implementation for testing prepareSnatAddr.
type mockDevice struct {
networks []netip.Prefix
unsafeNetworks []netip.Prefix
snatAddr netip.Prefix
}
func (d *mockDevice) Read([]byte) (int, error) { return 0, nil }
func (d *mockDevice) Write([]byte) (int, error) { return 0, nil }
func (d *mockDevice) Close() error { return nil }
func (d *mockDevice) Activate() error { return nil }
func (d *mockDevice) Networks() []netip.Prefix { return d.networks }
func (d *mockDevice) UnsafeNetworks() []netip.Prefix { return d.unsafeNetworks }
func (d *mockDevice) SNATAddress() netip.Prefix { return d.snatAddr }
func (d *mockDevice) Name() string { return "mock" }
func (d *mockDevice) RoutesFor(netip.Addr) routing.Gateways { return routing.Gateways{} }
func (d *mockDevice) SupportsMultiqueue() bool { return false }
func (d *mockDevice) NewMultiQueueReader() (io.ReadWriteCloser, error) { return nil, nil }
func TestPrepareSnatAddr_V4Primary_NoSnat(t *testing.T) {
l := logrus.New()
l.SetLevel(logrus.PanicLevel)
c := config.NewC(l)
// If the device has an IPv4 primary address, no SNAT needed
d := &mockDevice{
networks: []netip.Prefix{netip.MustParsePrefix("10.0.0.1/24")},
}
result := prepareSnatAddr(d, l, c, nil)
assert.Equal(t, netip.Prefix{}, result, "should not assign SNAT addr when device has IPv4 primary")
}
func TestPrepareSnatAddr_V6Primary_NoUnsafeOrRoutes(t *testing.T) {
l := logrus.New()
l.SetLevel(logrus.PanicLevel)
c := config.NewC(l)
// IPv6 primary but no unsafe networks or IPv4 routes
d := &mockDevice{
networks: []netip.Prefix{netip.MustParsePrefix("fd00::1/128")},
}
result := prepareSnatAddr(d, l, c, nil)
assert.Equal(t, netip.Prefix{}, result, "should not assign SNAT addr without IPv4 unsafe networks or routes")
}
func TestPrepareSnatAddr_V6Primary_WithV4Unsafe(t *testing.T) {
l := logrus.New()
l.SetLevel(logrus.PanicLevel)
c := config.NewC(l)
// IPv6 primary with IPv4 unsafe network -> should get SNAT addr
d := &mockDevice{
networks: []netip.Prefix{netip.MustParsePrefix("fd00::1/128")},
unsafeNetworks: []netip.Prefix{netip.MustParsePrefix("192.168.0.0/16")},
}
result := prepareSnatAddr(d, l, c, nil)
require.True(t, result.IsValid(), "should assign SNAT addr")
assert.True(t, result.Addr().Is4(), "SNAT addr should be IPv4")
assert.True(t, result.Addr().IsLinkLocalUnicast(), "SNAT addr should be link-local")
assert.Equal(t, 32, result.Bits(), "SNAT addr should be /32")
}
func TestPrepareSnatAddr_V6Primary_WithV4Route(t *testing.T) {
l := logrus.New()
l.SetLevel(logrus.PanicLevel)
c := config.NewC(l)
// IPv6 primary with IPv4 route -> should get SNAT addr
d := &mockDevice{
networks: []netip.Prefix{netip.MustParsePrefix("fd00::1/128")},
}
routes := []Route{
{Cidr: netip.MustParsePrefix("10.0.0.0/8")},
}
result := prepareSnatAddr(d, l, c, routes)
require.True(t, result.IsValid(), "should assign SNAT addr when IPv4 route exists")
assert.True(t, result.Addr().Is4())
assert.True(t, result.Addr().IsLinkLocalUnicast())
}
func TestPrepareSnatAddr_V6Primary_V6UnsafeOnly(t *testing.T) {
l := logrus.New()
l.SetLevel(logrus.PanicLevel)
c := config.NewC(l)
// IPv6 primary with only IPv6 unsafe network -> no SNAT needed
d := &mockDevice{
networks: []netip.Prefix{netip.MustParsePrefix("fd00::1/128")},
unsafeNetworks: []netip.Prefix{netip.MustParsePrefix("fd01::/64")},
}
result := prepareSnatAddr(d, l, c, nil)
assert.Equal(t, netip.Prefix{}, result, "should not assign SNAT addr for IPv6-only unsafe networks")
}
func TestPrepareSnatAddr_ManualAddress(t *testing.T) {
l := logrus.New()
l.SetLevel(logrus.PanicLevel)
c := config.NewC(l)
c.Settings["tun"] = map[string]any{
"snat_address_for_4over6": "169.254.42.42",
}
d := &mockDevice{
networks: []netip.Prefix{netip.MustParsePrefix("fd00::1/128")},
unsafeNetworks: []netip.Prefix{netip.MustParsePrefix("192.168.0.0/16")},
}
result := prepareSnatAddr(d, l, c, nil)
require.True(t, result.IsValid())
assert.Equal(t, netip.MustParseAddr("169.254.42.42"), result.Addr())
assert.Equal(t, 32, result.Bits())
}
func TestPrepareSnatAddr_InvalidManualAddress_Fallback(t *testing.T) {
l := logrus.New()
l.SetLevel(logrus.PanicLevel)
c := config.NewC(l)
c.Settings["tun"] = map[string]any{
"snat_address_for_4over6": "not-an-ip",
}
d := &mockDevice{
networks: []netip.Prefix{netip.MustParsePrefix("fd00::1/128")},
unsafeNetworks: []netip.Prefix{netip.MustParsePrefix("192.168.0.0/16")},
}
result := prepareSnatAddr(d, l, c, nil)
// Should fall back to auto-assignment
require.True(t, result.IsValid(), "should fall back to auto-assigned address")
assert.True(t, result.Addr().Is4())
assert.True(t, result.Addr().IsLinkLocalUnicast())
}
func TestPrepareSnatAddr_AutoGenerated_Range(t *testing.T) {
l := logrus.New()
l.SetLevel(logrus.PanicLevel)
c := config.NewC(l)
d := &mockDevice{
networks: []netip.Prefix{netip.MustParsePrefix("fd00::1/128")},
unsafeNetworks: []netip.Prefix{netip.MustParsePrefix("192.168.0.0/16")},
}
// Generate several addresses and verify they're all in the expected range
for i := 0; i < 100; i++ {
result := prepareSnatAddr(d, l, c, nil)
require.True(t, result.IsValid())
addr := result.Addr()
octets := addr.As4()
assert.Equal(t, byte(169), octets[0], "first octet should be 169")
assert.Equal(t, byte(254), octets[1], "second octet should be 254")
// Should not have .0 in the last octet
assert.NotEqual(t, byte(0), octets[3], "last octet should not be 0")
// Should not be 169.254.255.255 (broadcast)
if octets[2] == 255 {
assert.NotEqual(t, byte(255), octets[3], "should not be broadcast address")
}
}
}

1309
snat_test.go Normal file

File diff suppressed because it is too large Load diff