diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 28f9c90..6c23ebf 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -8,18 +8,26 @@ "args": [ "${workspaceFolder}/cmd/phpipamsync/" ], + "options": { + "env": { + "GOOS": "windows", + "GOARCH": "amd64" + }, + "cwd": "${workspaceFolder}" + }, "problemMatcher": [ "$go" ], - "group": "build", - // "detail": "cd e:\\docs\\src\\go\\sdwanctl; go build ${workspaceFolder}" + "group": "build" }, { "type": "go", "label": "go: build package for linux", "command": "build", "args": [ - "-o", "cmd/phpipamsync", "${workspaceFolder}/cmd/phpipamsync/" + "-o", + "cmd/phpipamsync", + "${workspaceFolder}/cmd/phpipamsync/" ], "problemMatcher": [ "$go" @@ -30,8 +38,7 @@ "GOARCH": "amd64" } }, - "group": "build", - // "detail": "cd e:\\docs\\src\\go\\sdwanctl; go build -o cmd/sdwanctl-linux ${workspaceFolder}" + "group": "build" }, ] } \ No newline at end of file diff --git a/internal/cli/get.go b/internal/cli/get.go index 3f49307..06c1b3d 100644 --- a/internal/cli/get.go +++ b/internal/cli/get.go @@ -4,8 +4,11 @@ import ( "encoding/json" "fmt" "log" + "net" + "net/netip" "os" "strconv" + "strings" "github.com/ivankuchin/phpipamsync/internal/api_client" "github.com/ivankuchin/phpipamsync/internal/config_reader" @@ -87,6 +90,91 @@ func writeToPiHoleCustom(addresses string, cfg *config_reader.Config) error { return nil } +func getSubnetMaskBySubnetID(subnetID string) (string, error) { + mask := "" + + prefix, err := netip.ParsePrefix(subnetID) + if err != nil { + log.Printf("Error parsing subnet ID: %s", err.Error()) + return mask, err + } + + bits := prefix.Bits() + mask = net.IP(net.CIDRMask(bits, 32)).String() + + return mask, nil +} + +func getGWIPBySubnetID(subnetID string) (string, error) { + ip_addr := "" + + prefix, err := netip.ParsePrefix(subnetID) + if err != nil { + log.Printf("Error parsing subnet ID: %s", err.Error()) + return ip_addr, err + } + + ip_addr = prefix.Addr().Next().String() + + return ip_addr, nil +} + +func convertMacToClientIdentifier(mac string) string { + cleaned_mac := strings.ReplaceAll(mac, ":", "") + client_identifier := "" + + for i := 0; i < len(cleaned_mac); i++ { + if (i-2)%4 == 0 { + client_identifier += "." + } + client_identifier += string(cleaned_mac[i]) + } + + return "01" + client_identifier +} + +func getCiscoDHCPOutput(addresses IPAddresses, cfg *config_reader.Config) (string, error) { + output := "" + + mask, err := getSubnetMaskBySubnetID(cfg.Ipam_subnet) + if err != nil { + return output, err + } + + gw, err := getGWIPBySubnetID(cfg.Ipam_subnet) + if err != nil { + return output, err + } + + for _, address := range addresses.IPAddresses { + switch address.Tag { + case "2": // In Use + if address.Hostname == "" { + log.Printf("WARNING: Skipping %s because hostname is empty", address.IP) + } else if address.IP == "" { + log.Printf("WARNING: Skipping %s because IP is empty", address.Hostname) + } else { + output += "ip dhcp pool " + address.Hostname + "\n" + output += " host " + address.IP + " " + mask + "\n" + switch { + case address.ClientIdentifier != "": + output += " client-identifier " + address.ClientIdentifier + "\n" + case address.Mac != "": + output += " client-identifier " + convertMacToClientIdentifier(address.Mac) + "\n" + } + output += " default-router " + gw + "\n" + output += "!\n" + } + case "3": // Reserved + case "4": // DHCP pool + default: + log.Printf("WARNING: Skipping %s because tag is %s", address.IP, address.Tag) + } + } + + return output, nil +} + var getCmd = &cobra.Command{ Use: "get", Short: "Get information from phpIPAM", @@ -99,24 +187,29 @@ var getCiscoDHCP = &cobra.Command{ Long: "Get DHCP config for Cisco IOS devices", RunE: func(cmd *cobra.Command, args []string) error { - auth := new(api_client.AuthAppCode) + auth := api_client.AuthAppCode{} err := auth.Login(config_reader.Cfg) if err != nil { return err } defer auth.Logout() - subnet_id, err := getSubnetID(auth, config_reader.Cfg) + subnet_id, err := getSubnetID(&auth, config_reader.Cfg) if err != nil { return err } - addresses, err := getIPAddressesBySubnetID(subnet_id, auth, config_reader.Cfg) + addresses, err := getIPAddressesBySubnetID(subnet_id, &auth, config_reader.Cfg) + if err != nil { + return err + } + + output, err := getCiscoDHCPOutput(addresses, config_reader.Cfg) if err != nil { return err } - fmt.Println(addresses) + fmt.Println(output) return nil }, diff --git a/internal/cli/get_types.go b/internal/cli/get_types.go index 52f0190..bfdb8c1 100644 --- a/internal/cli/get_types.go +++ b/internal/cli/get_types.go @@ -34,4 +34,5 @@ type IPAddress struct { FirewallAddressObject string `json:"firewallAddressObject"` EditDate string `json:"editDate"` CustomerID string `json:"customer_id"` + ClientIdentifier string `json:"custom_ClientID"` }