package nat import ( "encoding/json" "fmt" "os" "os/exec" "path/filepath" ) // Config holds NAT masquerade settings per interface. type Config struct { Interfaces []string `json:"interfaces"` } func configPath() string { exe, err := os.Executable() if err != nil { return filepath.Join("configs", "nat.json") } return filepath.Join(filepath.Dir(exe), "configs", "nat.json") } // IsInstalled reports whether the nft (nftables) binary is available. func IsInstalled() bool { _, err := exec.LookPath("nft") return err == nil } // Load reads the NAT config from disk. func Load() (*Config, error) { data, err := os.ReadFile(configPath()) if err != nil { if os.IsNotExist(err) { return &Config{Interfaces: []string{}}, nil } return nil, fmt.Errorf("read nat config: %w", err) } var cfg Config if err := json.Unmarshal(data, &cfg); err != nil { return nil, fmt.Errorf("parse nat config: %w", err) } if cfg.Interfaces == nil { cfg.Interfaces = []string{} } return &cfg, nil } // Save writes the NAT config to the configs/ directory. func Save(cfg *Config) error { p := configPath() if err := os.MkdirAll(filepath.Dir(p), 0755); err != nil { return fmt.Errorf("mkdir configs: %w", err) } data, err := json.MarshalIndent(cfg, "", " ") if err != nil { return err } return os.WriteFile(p, data, 0644) }