package trafficmode import ( "fmt" "strconv" "strings" ) type RunCommandSimpleFunc func(name string, args ...string) (stdout string, stderr string, code int, err error) type OverrideConfig struct { RoutesTableName string RulePerKindLimit int PrefManagedMin int PrefManagedMax int PrefDirectSubnetBase int PrefDirectUIDBase int PrefVPNSubnetBase int PrefVPNUIDBase int } type EffectiveOverrides struct { VPNSubnets []string VPNUIDs []string DirectSubnets []string DirectUIDs []string } func RemoveRulesForTable(cfg OverrideConfig, run RunCommandSimpleFunc) { if run == nil { return } out, _, _, _ := run("ip", "rule", "show") for _, line := range strings.Split(out, "\n") { line = strings.TrimSpace(line) if line == "" { continue } fields := strings.Fields(line) if len(fields) == 0 { continue } pref := strings.TrimSuffix(fields[0], ":") if pref == "" { continue } prefNum, _ := strconv.Atoi(pref) low := strings.ToLower(line) managed := prefNum >= cfg.PrefManagedMin && prefNum <= cfg.PrefManagedMax legacy := strings.Contains(low, "lookup "+cfg.RoutesTableName) if !managed && !legacy { continue } _, _, _, _ = run("ip", "rule", "del", "pref", pref) } } func ApplyRule(pref int, run RunCommandSimpleFunc, args ...string) error { if run == nil { return fmt.Errorf("run command func is nil") } if pref <= 0 { return fmt.Errorf("invalid pref: %d", pref) } cmd := []string{"rule", "add"} cmd = append(cmd, args...) cmd = append(cmd, "pref", PrefStr(pref)) _, _, code, err := run("ip", cmd...) if err != nil || code != 0 { if err == nil { err = fmt.Errorf("ip %s exited with %d", strings.Join(cmd, " "), code) } return err } return nil } func ApplyOverrides(cfg OverrideConfig, e EffectiveOverrides, applyRule func(pref int, args ...string) error) (int, error) { applied := 0 if applyRule == nil { return 0, fmt.Errorf("applyRule callback is nil") } if len(e.DirectSubnets) > cfg.RulePerKindLimit || len(e.DirectUIDs) > cfg.RulePerKindLimit || len(e.VPNSubnets) > cfg.RulePerKindLimit || len(e.VPNUIDs) > cfg.RulePerKindLimit { return 0, fmt.Errorf("override list too large (max %d entries per kind)", cfg.RulePerKindLimit) } for i, cidr := range e.DirectSubnets { if err := applyRule(cfg.PrefDirectSubnetBase+i, "from", cidr, "lookup", "main"); err != nil { return applied, err } applied++ } for i, uidr := range e.DirectUIDs { if err := applyRule(cfg.PrefDirectUIDBase+i, "uidrange", uidr, "lookup", "main"); err != nil { return applied, err } applied++ } for i, cidr := range e.VPNSubnets { if err := applyRule(cfg.PrefVPNSubnetBase+i, "from", cidr, "lookup", cfg.RoutesTableName); err != nil { return applied, err } applied++ } for i, uidr := range e.VPNUIDs { if err := applyRule(cfg.PrefVPNUIDBase+i, "uidrange", uidr, "lookup", cfg.RoutesTableName); err != nil { return applied, err } applied++ } return applied, nil }