package ipsec import ( "strings" "testing" "vpn-platform/backend/internal/service" ) func TestValidateDefaults(t *testing.T) { cfg, err := New().Validate(map[string]interface{}{ "remote_gateway": "203.0.113.1", "local_subnets": "192.168.1.0/24", "remote_subnets": "192.168.2.0/24", }) if err != nil { t.Fatal(err) } defaults := []struct { key string expected string }{ {"ike_version", "ikev2"}, {"ike_proposal", "aes256-sha256-modp2048"}, {"esp_proposal", "aes256-sha256"}, {"dpd_action", "restart"}, {"local_id", "@vpn-local"}, {"remote_id", "@vpn-remote"}, {"pfs_group", "modp2048"}, } for _, d := range defaults { if got := service.Str(cfg, d.key); got != d.expected { t.Errorf("%s: got %q, want %q", d.key, got, d.expected) } } if service.Num(cfg, "ike_lifetime", 0) != 86400 { t.Errorf("ike_lifetime: got %v, want 86400", cfg["ike_lifetime"]) } } func TestValidateRejectsInvalid(t *testing.T) { s := New() cases := []struct { name string cfg map[string]interface{} }{ {"missing remote_gateway", map[string]interface{}{ "local_subnets": "10.0.0.0/24", "remote_subnets": "10.1.0.0/24", }}, {"missing local_subnets", map[string]interface{}{ "remote_gateway": "1.2.3.4", "remote_subnets": "10.1.0.0/24", }}, {"missing remote_subnets", map[string]interface{}{ "remote_gateway": "1.2.3.4", "local_subnets": "10.0.0.0/24", }}, {"invalid gateway", map[string]interface{}{ "remote_gateway": "not a gateway{}", "local_subnets": "10.0.0.0/24", "remote_subnets": "10.1.0.0/24", }}, {"invalid local_subnets", map[string]interface{}{ "remote_gateway": "1.2.3.4", "local_subnets": "bad-cidr", "remote_subnets": "10.1.0.0/24", }}, {"invalid remote_subnets", map[string]interface{}{ "remote_gateway": "1.2.3.4", "local_subnets": "10.0.0.0/24", "remote_subnets": "nope", }}, {"overlapping subnets", map[string]interface{}{ "remote_gateway": "1.2.3.4", "local_subnets": "10.0.0.0/24", "remote_subnets": "10.0.0.0/24", }}, } for _, tc := range cases { if _, err := s.Validate(tc.cfg); err == nil { t.Errorf("%s: expected error, got nil", tc.name) } } } func TestVendorTemplates(t *testing.T) { templates := Templates() if len(templates) == 0 { t.Fatal("Templates() returned empty slice") } for _, tmpl := range templates { if tmpl.Key == "" || tmpl.Name == "" { t.Errorf("template missing key/name: %#v", tmpl) } // Validate with vendor_template should fill in template values cfg, err := New().Validate(map[string]interface{}{ "remote_gateway": "1.2.3.4", "local_subnets": "10.0.0.0/24", "remote_subnets": "10.1.0.0/24", "vendor_template": tmpl.Key, }) if err != nil { t.Errorf("template %q: Validate failed: %v", tmpl.Key, err) continue } if service.Str(cfg, "ike_version") != tmpl.IKEVersion { t.Errorf("template %q: ike_version %q != %q", tmpl.Key, service.Str(cfg, "ike_version"), tmpl.IKEVersion) } if service.Str(cfg, "ike_proposal") != tmpl.IKEProposal { t.Errorf("template %q: ike_proposal %q != %q", tmpl.Key, service.Str(cfg, "ike_proposal"), tmpl.IKEProposal) } if service.Str(cfg, "esp_proposal") != tmpl.ESPProposal { t.Errorf("template %q: esp_proposal %q != %q", tmpl.Key, service.Str(cfg, "esp_proposal"), tmpl.ESPProposal) } if service.Str(cfg, "dpd_action") != tmpl.DPDAction { t.Errorf("template %q: dpd_action %q != %q", tmpl.Key, service.Str(cfg, "dpd_action"), tmpl.DPDAction) } } } func TestSwanctlConf(t *testing.T) { s := New() cfg := map[string]interface{}{ "remote_gateway": "203.0.113.1", "local_subnets": "192.168.1.0/24", "remote_subnets": "192.168.2.0/24", "ike_version": "ikev2", "ike_proposal": "aes256-sha256-modp2048", "esp_proposal": "aes256-sha256", "dpd_action": "restart", "local_id": "@vpn-local", "remote_id": "@vpn-remote", } conf := s.swanctlConf(42, cfg, "my-secret-psk") expected := []string{ "tunnel-42", "remote_addrs = 203.0.113.1", "version = 2", "proposals = aes256-sha256-modp2048", "id = @vpn-local", "id = @vpn-remote", "site-42", "local_ts = 192.168.1.0/24", "remote_ts = 192.168.2.0/24", "esp_proposals = aes256-sha256", "dpd_action = restart", "start_action = start", "ike-tunnel-42", "my-secret-psk", } for _, e := range expected { if !strings.Contains(conf, e) { t.Errorf("swanctlConf missing %q:\n%s", e, conf) } } } func TestSwanctlConfVIP(t *testing.T) { s := New() cfg := map[string]interface{}{ "remote_gateway": "1.2.3.4", "local_subnets": "10.0.0.0/24", "remote_subnets": "10.1.0.0/24", "local_id": "@local", "remote_id": "@remote", "local_ts_vip": "10.255.0.1", } conf := s.swanctlConf(1, cfg, "psk") if !strings.Contains(conf, "vips = 10.255.0.1") { t.Errorf("swanctlConf should include vips when local_ts_vip is set:\n%s", conf) } } func TestPeerReference(t *testing.T) { s := New() cfg := map[string]interface{}{ "remote_gateway": "203.0.113.1", "ike_version": "ikev2", "ike_proposal": "aes256-sha256-modp2048", "esp_proposal": "aes256-sha256", "local_id": "@vpn-local", "remote_id": "@vpn-remote", "local_subnets": "192.168.1.0/24", "remote_subnets": "192.168.2.0/24", "pfs_group": "modp2048", "dpd_action": "restart", "public_endpoint": "198.51.100.1", } ref := s.PeerReference(cfg) for _, expected := range []string{ "198.51.100.1", "203.0.113.1", "ikev2", "aes256-sha256-modp2048", "@vpn-local", "@vpn-remote", "192.168.1.0/24", "192.168.2.0/24", "modp2048", "restart", } { if !strings.Contains(ref, expected) { t.Errorf("PeerReference missing %q:\n%s", expected, ref) } } } func TestComparePeerParams(t *testing.T) { local := map[string]interface{}{ "ike_version": "ikev2", "ike_proposal": "aes256-sha256-modp2048", "esp_proposal": "aes256-sha256", "pfs_group": "modp2048", "local_subnets": "192.168.1.0/24", "remote_subnets": "192.168.2.0/24", } // Matching peer — no diffs expected peer := map[string]interface{}{ "ike_version": "ikev2", "ike_proposal": "aes256-sha256-modp2048", "esp_proposal": "aes256-sha256", "pfs_group": "modp2048", "local_subnets": "192.168.2.0/24", } diffs := ComparePeerParams(local, peer) if len(diffs) != 0 { t.Errorf("expected no diffs, got %v", diffs) } // Mismatching peer — should detect diffs peerBad := map[string]interface{}{ "ike_version": "ikev1", "ike_proposal": "aes128-sha1-modp1024", "esp_proposal": "aes128-sha1", "pfs_group": "modp1024", "local_subnets": "10.0.0.0/8", } diffs = ComparePeerParams(local, peerBad) if len(diffs) < 3 { t.Errorf("expected multiple diffs, got %d: %v", len(diffs), diffs) } // Verify cross-subnet mismatch is detected found := false for _, d := range diffs { if strings.Contains(d, "网段交叉不匹配") || strings.Contains(d, "cross") { found = true } } if !found { t.Errorf("expected subnet cross-mismatch in diffs: %v", diffs) } }