package routing import ( "os" "os/exec" "path/filepath" "strings" "testing" ) func TestIsolatedPolicyActionsAndExactDeletion(t *testing.T) { if os.Getenv("DEBTIK_TEST_ISOLATED_NETNS") != "1" { t.Skip("requires a dedicated network namespace") } useTempRouteStores(t) ip := func(args ...string) string { t.Helper() out, err := exec.Command("ip", args...).CombinedOutput() if err != nil { t.Fatalf("ip %v: %s %v", args, out, err) } return string(out) } ip("rule", "add", "pref", "11200", "from", "203.0.113.0/24", "lookup", "main") m := &RoutingManager{} for _, tc := range []struct { priority int action string }{{11200, "blackhole"}, {11201, "unreachable"}, {11202, "lookup"}} { p := PolicyRuleItem{ID: tc.action, Priority: tc.priority, Action: tc.action, SrcAddress: "198.51.100.0/24", Table: "main"} if err := m.SavePolicyRule(p); err != nil { t.Fatalf("save %s: %v", tc.action, err) } } listed := m.ListPolicyRules() seen := map[string]bool{} unmanaged := false for _, rule := range listed { if rule.ID == "blackhole" || rule.ID == "unreachable" || rule.ID == "lookup" { if rule.Action != rule.ID || rule.IsSystem { t.Fatalf("incorrect rule: %+v", rule) } seen[rule.ID] = true } if rule.Priority == 11200 && rule.SrcAddress == "203.0.113.0/24" { unmanaged = rule.IsSystem } } if len(seen) != 3 || !unmanaged { t.Fatalf("incorrect mapping: %v unmanaged=%v listed=%+v", seen, unmanaged, listed) } if err := m.DeletePolicyRule("blackhole"); err != nil { t.Fatal(err) } show := ip("rule", "show") if !strings.Contains(show, "from 203.0.113.0/24 lookup main") || strings.Contains(show, "from 198.51.100.0/24 blackhole") { t.Fatalf("wrong rule deleted: %s", show) } if err := m.DeletePolicyRule("rule-11200-254"); err == nil { t.Fatal("unmanaged rule deletion accepted") } for _, id := range []string{"unreachable", "lookup"} { if err := m.DeletePolicyRule(id); err != nil { t.Fatal(err) } } } func TestIsolatedPolicySaveFailureRemovesKernelRule(t *testing.T) { if os.Getenv("DEBTIK_TEST_ISOLATED_NETNS") != "1" { t.Skip("requires a dedicated network namespace") } useTempRouteStores(t) bad := filepath.Join(t.TempDir(), "dir") if err := os.Mkdir(bad, 0700); err != nil { t.Fatal(err) } StaticRoutesStorePath = bad m := &RoutingManager{} err := m.SavePolicyRule(PolicyRuleItem{ID: "r", Priority: 11300, Action: "blackhole", SrcAddress: "198.51.100.0/24"}) if err == nil { t.Fatal("save should fail") } out, e := exec.Command("ip", "rule", "show").CombinedOutput() if e != nil { t.Fatal(e) } if strings.Contains(string(out), "11300:") { t.Fatalf("kernel rule not rolled back: %s", out) } if len(m.policyRules) != 0 { t.Fatal("memory not restored") } } func TestTableRegistryFailureRestoresMemory(t *testing.T) { useTempRouteStores(t) old := RTTableFilePath t.Cleanup(func() { RTTableFilePath = old }) RTTableFilePath = filepath.Join(t.TempDir(), "missing", "rt_tables") m := &RoutingManager{} if err := m.SaveTable(RoutingTableItem{Name: "test", TableID: 501}); err == nil { t.Fatal("registry failure ignored") } if len(m.customTables) != 0 { t.Fatalf("table not rolled back: %+v", m.customTables) } } func TestTableRejectsReservedAndInvalidNames(t *testing.T) { useTempRouteStores(t) old := RTTableFilePath t.Cleanup(func() { RTTableFilePath = old }) RTTableFilePath = filepath.Join(t.TempDir(), "rt_tables") m := &RoutingManager{} for _, table := range []RoutingTableItem{{Name: "main", TableID: 501}, {Name: "bad name", TableID: 502}, {Name: "bad\nentry", TableID: 503}, {Name: "test", TableID: 254}} { if err := m.SaveTable(table); err == nil { t.Fatalf("accepted table %+v", table) } } if len(m.customTables) != 0 { t.Fatal("invalid tables saved") } }