diff --git a/dhcpd/dhcp_http.go b/dhcpd/dhcp_http.go new file mode 100644 index 00000000..d194d055 --- /dev/null +++ b/dhcpd/dhcp_http.go @@ -0,0 +1,461 @@ +package dhcpd + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net" + "net/http" + "os/exec" + "runtime" + "strings" + "time" + + "github.com/AdguardTeam/golibs/file" + "github.com/AdguardTeam/golibs/log" +) + +func httpError(r *http.Request, w http.ResponseWriter, code int, format string, args ...interface{}) { + text := fmt.Sprintf(format, args...) + log.Info("DHCP: %s %s: %s", r.Method, r.URL, text) + http.Error(w, text, code) +} + +// []Lease -> JSON +func convertLeases(inputLeases []Lease, includeExpires bool) []map[string]string { + leases := []map[string]string{} + for _, l := range inputLeases { + lease := map[string]string{ + "mac": l.HWAddr.String(), + "ip": l.IP.String(), + "hostname": l.Hostname, + } + + if includeExpires { + lease["expires"] = l.Expiry.Format(time.RFC3339) + } + + leases = append(leases, lease) + } + return leases +} + +func (s *Server) handleDHCPStatus(w http.ResponseWriter, r *http.Request) { + leases := convertLeases(s.Leases(), true) + staticLeases := convertLeases(s.StaticLeases(), false) + status := map[string]interface{}{ + "config": s.conf, + "leases": leases, + "static_leases": staticLeases, + } + + w.Header().Set("Content-Type", "application/json") + err := json.NewEncoder(w).Encode(status) + if err != nil { + httpError(r, w, http.StatusInternalServerError, "Unable to marshal DHCP status json: %s", err) + return + } +} + +type staticLeaseJSON struct { + HWAddr string `json:"mac"` + IP string `json:"ip"` + Hostname string `json:"hostname"` +} + +type dhcpServerConfigJSON struct { + ServerConfig `json:",inline"` + StaticLeases []staticLeaseJSON `json:"static_leases"` +} + +func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) { + newconfig := dhcpServerConfigJSON{} + err := json.NewDecoder(r.Body).Decode(&newconfig) + if err != nil { + httpError(r, w, http.StatusBadRequest, "Failed to parse new DHCP config json: %s", err) + return + } + + err = s.CheckConfig(newconfig.ServerConfig) + if err != nil { + httpError(r, w, http.StatusBadRequest, "Invalid DHCP configuration: %s", err) + return + } + + err = s.Stop() + if err != nil { + log.Error("failed to stop the DHCP server: %s", err) + } + + err = s.Init(newconfig.ServerConfig) + if err != nil { + httpError(r, w, http.StatusBadRequest, "Invalid DHCP configuration: %s", err) + return + } + s.conf.ConfigModified() + + if newconfig.Enabled { + + staticIP, err := hasStaticIP(newconfig.InterfaceName) + if !staticIP && err == nil { + err = setStaticIP(newconfig.InterfaceName) + if err != nil { + httpError(r, w, http.StatusInternalServerError, "Failed to configure static IP: %s", err) + return + } + } + + err = s.Start() + if err != nil { + httpError(r, w, http.StatusBadRequest, "Failed to start DHCP server: %s", err) + return + } + } +} + +type netInterface struct { + Name string `json:"name"` + MTU int `json:"mtu"` + HardwareAddr string `json:"hardware_address"` + Addresses []string `json:"ip_addresses"` + Flags string `json:"flags"` +} + +// getValidNetInterfaces returns interfaces that are eligible for DNS and/or DHCP +// invalid interface is a ppp interface or the one that doesn't allow broadcasts +func getValidNetInterfaces() ([]net.Interface, error) { + ifaces, err := net.Interfaces() + if err != nil { + return nil, fmt.Errorf("Couldn't get list of interfaces: %s", err) + } + + netIfaces := []net.Interface{} + + for i := range ifaces { + if ifaces[i].Flags&net.FlagPointToPoint != 0 { + // this interface is ppp, we're not interested in this one + continue + } + + iface := ifaces[i] + netIfaces = append(netIfaces, iface) + } + + return netIfaces, nil +} + +func (s *Server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) { + response := map[string]interface{}{} + + ifaces, err := getValidNetInterfaces() + if err != nil { + httpError(r, w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err) + return + } + + for _, iface := range ifaces { + if iface.Flags&net.FlagLoopback != 0 { + // it's a loopback, skip it + continue + } + if iface.Flags&net.FlagBroadcast == 0 { + // this interface doesn't support broadcast, skip it + continue + } + addrs, err := iface.Addrs() + if err != nil { + httpError(r, w, http.StatusInternalServerError, "Failed to get addresses for interface %s: %s", iface.Name, err) + return + } + + jsonIface := netInterface{ + Name: iface.Name, + MTU: iface.MTU, + HardwareAddr: iface.HardwareAddr.String(), + } + + if iface.Flags != 0 { + jsonIface.Flags = iface.Flags.String() + } + // we don't want link-local addresses in json, so skip them + for _, addr := range addrs { + ipnet, ok := addr.(*net.IPNet) + if !ok { + // not an IPNet, should not happen + httpError(r, w, http.StatusInternalServerError, "SHOULD NOT HAPPEN: got iface.Addrs() element %s that is not net.IPNet, it is %T", addr, addr) + return + } + // ignore link-local + if ipnet.IP.IsLinkLocalUnicast() { + continue + } + jsonIface.Addresses = append(jsonIface.Addresses, ipnet.IP.String()) + } + if len(jsonIface.Addresses) != 0 { + response[iface.Name] = jsonIface + } + + } + + err = json.NewEncoder(w).Encode(response) + if err != nil { + httpError(r, w, http.StatusInternalServerError, "Failed to marshal json with available interfaces: %s", err) + return + } +} + +// Perform the following tasks: +// . Search for another DHCP server running +// . Check if a static IP is configured for the network interface +// Respond with results +func (s *Server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + errorText := fmt.Sprintf("failed to read request body: %s", err) + log.Error(errorText) + http.Error(w, errorText, http.StatusBadRequest) + return + } + + interfaceName := strings.TrimSpace(string(body)) + if interfaceName == "" { + errorText := fmt.Sprintf("empty interface name specified") + log.Error(errorText) + http.Error(w, errorText, http.StatusBadRequest) + return + } + + found, err := CheckIfOtherDHCPServersPresent(interfaceName) + + othSrv := map[string]interface{}{} + foundVal := "no" + if found { + foundVal = "yes" + } else if err != nil { + foundVal = "error" + othSrv["error"] = err.Error() + } + othSrv["found"] = foundVal + + staticIP := map[string]interface{}{} + isStaticIP, err := hasStaticIP(interfaceName) + staticIPStatus := "yes" + if err != nil { + staticIPStatus = "error" + staticIP["error"] = err.Error() + } else if !isStaticIP { + staticIPStatus = "no" + staticIP["ip"] = getFullIP(interfaceName) + } + staticIP["static"] = staticIPStatus + + result := map[string]interface{}{} + result["other_server"] = othSrv + result["static_ip"] = staticIP + + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(result) + if err != nil { + httpError(r, w, http.StatusInternalServerError, "Failed to marshal DHCP found json: %s", err) + return + } +} + +// Check if network interface has a static IP configured +func hasStaticIP(ifaceName string) (bool, error) { + if runtime.GOOS == "windows" { + return false, errors.New("Can't detect static IP: not supported on Windows") + } + + body, err := ioutil.ReadFile("/etc/dhcpcd.conf") + if err != nil { + return false, err + } + lines := strings.Split(string(body), "\n") + nameLine := fmt.Sprintf("interface %s", ifaceName) + withinInterfaceCtx := false + + for _, line := range lines { + line = strings.TrimSpace(line) + + if withinInterfaceCtx && len(line) == 0 { + // an empty line resets our state + withinInterfaceCtx = false + } + + if len(line) == 0 || line[0] == '#' { + continue + } + line = strings.TrimSpace(line) + + if !withinInterfaceCtx { + if line == nameLine { + // we found our interface + withinInterfaceCtx = true + } + + } else { + if strings.HasPrefix(line, "interface ") { + // we found another interface - reset our state + withinInterfaceCtx = false + continue + } + if strings.HasPrefix(line, "static ip_address=") { + return true, nil + } + } + } + + return false, nil +} + +// Get IP address with netmask +func getFullIP(ifaceName string) string { + cmd := exec.Command("ip", "-oneline", "-family", "inet", "address", "show", ifaceName) + log.Tracef("executing %s %v", cmd.Path, cmd.Args) + d, err := cmd.Output() + if err != nil || cmd.ProcessState.ExitCode() != 0 { + return "" + } + + fields := strings.Fields(string(d)) + if len(fields) < 4 { + return "" + } + _, _, err = net.ParseCIDR(fields[3]) + if err != nil { + return "" + } + + return fields[3] +} + +// Get gateway IP address +func getGatewayIP(ifaceName string) string { + cmd := exec.Command("ip", "route", "show", "dev", ifaceName) + log.Tracef("executing %s %v", cmd.Path, cmd.Args) + d, err := cmd.Output() + if err != nil || cmd.ProcessState.ExitCode() != 0 { + return "" + } + + fields := strings.Fields(string(d)) + if len(fields) < 3 || fields[0] != "default" { + return "" + } + + ip := net.ParseIP(fields[2]) + if ip == nil { + return "" + } + + return fields[2] +} + +// Set a static IP for network interface +func setStaticIP(ifaceName string) error { + ip := getFullIP(ifaceName) + if len(ip) == 0 { + return errors.New("Can't get IP address") + } + + body, err := ioutil.ReadFile("/etc/dhcpcd.conf") + if err != nil { + return err + } + + ip4, _, err := net.ParseCIDR(ip) + if err != nil { + return err + } + + add := fmt.Sprintf("\ninterface %s\nstatic ip_address=%s\n", + ifaceName, ip) + body = append(body, []byte(add)...) + + gatewayIP := getGatewayIP(ifaceName) + if len(gatewayIP) != 0 { + add = fmt.Sprintf("static routers=%s\n", + gatewayIP) + body = append(body, []byte(add)...) + } + + add = fmt.Sprintf("static domain_name_servers=%s\n\n", + ip4) + body = append(body, []byte(add)...) + + err = file.SafeWrite("/etc/dhcpcd.conf", body) + if err != nil { + return err + } + + return nil +} + +func (s *Server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request) { + + lj := staticLeaseJSON{} + err := json.NewDecoder(r.Body).Decode(&lj) + if err != nil { + httpError(r, w, http.StatusBadRequest, "json.Decode: %s", err) + return + } + + ip, _ := parseIPv4(lj.IP) + if ip == nil { + httpError(r, w, http.StatusBadRequest, "invalid IP") + return + } + + mac, _ := net.ParseMAC(lj.HWAddr) + + lease := Lease{ + IP: ip, + HWAddr: mac, + Hostname: lj.Hostname, + } + err = s.AddStaticLease(lease) + if err != nil { + httpError(r, w, http.StatusBadRequest, "%s", err) + return + } +} + +func (s *Server) handleDHCPRemoveStaticLease(w http.ResponseWriter, r *http.Request) { + + lj := staticLeaseJSON{} + err := json.NewDecoder(r.Body).Decode(&lj) + if err != nil { + httpError(r, w, http.StatusBadRequest, "json.Decode: %s", err) + return + } + + ip, _ := parseIPv4(lj.IP) + if ip == nil { + httpError(r, w, http.StatusBadRequest, "invalid IP") + return + } + + mac, _ := net.ParseMAC(lj.HWAddr) + + lease := Lease{ + IP: ip, + HWAddr: mac, + Hostname: lj.Hostname, + } + err = s.RemoveStaticLease(lease) + if err != nil { + httpError(r, w, http.StatusBadRequest, "%s", err) + return + } +} + +func (s *Server) registerHandlers() { + s.conf.HTTPRegister("GET", "/control/dhcp/status", s.handleDHCPStatus) + s.conf.HTTPRegister("GET", "/control/dhcp/interfaces", s.handleDHCPInterfaces) + s.conf.HTTPRegister("POST", "/control/dhcp/set_config", s.handleDHCPSetConfig) + s.conf.HTTPRegister("POST", "/control/dhcp/find_active_dhcp", s.handleDHCPFindActiveServer) + s.conf.HTTPRegister("POST", "/control/dhcp/add_static_lease", s.handleDHCPAddStaticLease) + s.conf.HTTPRegister("POST", "/control/dhcp/remove_static_lease", s.handleDHCPRemoveStaticLease) +} diff --git a/dhcpd/dhcpd.go b/dhcpd/dhcpd.go index 74bc45d4..676447b4 100644 --- a/dhcpd/dhcpd.go +++ b/dhcpd/dhcpd.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "net" + "net/http" "path/filepath" "strings" "sync" @@ -45,6 +46,12 @@ type ServerConfig struct { // IP conflict detector: time (ms) to wait for ICMP reply. // 0: disable ICMPTimeout uint `json:"icmp_timeout_msec" yaml:"icmp_timeout_msec"` + + // Called when the configuration is changed by HTTP request + ConfigModified func() `json:"-" yaml:"-"` + + // Register an HTTP handler + HTTPRegister func(string, string, func(http.ResponseWriter, *http.Request)) `json:"-" yaml:"-"` } // Server - the current state of the DHCP server @@ -92,6 +99,9 @@ func (s *Server) CheckConfig(config ServerConfig) error { func Create(config ServerConfig) *Server { s := Server{} s.conf = config + if s.conf.HTTPRegister != nil { + s.registerHandlers() + } return &s } @@ -111,9 +121,6 @@ func (s *Server) WriteDiskConfig(c *ServerConfig) { } func (s *Server) setConfig(config ServerConfig) error { - s.conf = config - s.conf.DBFilePath = filepath.Join(config.WorkDir, dbFilename) - iface, err := net.InterfaceByName(config.InterfaceName) if err != nil { printInterfaces() @@ -165,6 +172,12 @@ func (s *Server) setConfig(config ServerConfig) error { dhcp4.OptionDomainNameServer: s.ipnet.IP, } + oldconf := s.conf + s.conf = config + s.conf.WorkDir = oldconf.WorkDir + s.conf.HTTPRegister = oldconf.HTTPRegister + s.conf.ConfigModified = oldconf.ConfigModified + s.conf.DBFilePath = filepath.Join(config.WorkDir, dbFilename) return nil } diff --git a/home/control.go b/home/control.go index 143f73fc..6aa9cc5d 100644 --- a/home/control.go +++ b/home/control.go @@ -424,12 +424,6 @@ func registerControlHandlers() { httpRegister(http.MethodGet, "/control/i18n/current_language", handleI18nCurrentLanguage) http.HandleFunc("/control/version.json", postInstall(optionalAuth(handleGetVersionJSON))) httpRegister(http.MethodPost, "/control/update", handleUpdate) - httpRegister(http.MethodGet, "/control/dhcp/status", handleDHCPStatus) - httpRegister(http.MethodGet, "/control/dhcp/interfaces", handleDHCPInterfaces) - httpRegister(http.MethodPost, "/control/dhcp/set_config", handleDHCPSetConfig) - httpRegister(http.MethodPost, "/control/dhcp/find_active_dhcp", handleDHCPFindActiveServer) - httpRegister(http.MethodPost, "/control/dhcp/add_static_lease", handleDHCPAddStaticLease) - httpRegister(http.MethodPost, "/control/dhcp/remove_static_lease", handleDHCPRemoveStaticLease) httpRegister(http.MethodGet, "/control/access/list", handleAccessList) httpRegister(http.MethodPost, "/control/access/set", handleAccessSet) diff --git a/home/dhcp.go b/home/dhcp.go index c184b661..dcb7a28b 100644 --- a/home/dhcp.go +++ b/home/dhcp.go @@ -1,426 +1,9 @@ package home import ( - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "net" - "net/http" - "os/exec" - "runtime" - "strings" - "time" - - "github.com/AdguardTeam/AdGuardHome/dhcpd" - "github.com/AdguardTeam/golibs/file" - "github.com/AdguardTeam/golibs/log" "github.com/joomcode/errorx" ) -// []dhcpd.Lease -> JSON -func convertLeases(inputLeases []dhcpd.Lease, includeExpires bool) []map[string]string { - leases := []map[string]string{} - for _, l := range inputLeases { - lease := map[string]string{ - "mac": l.HWAddr.String(), - "ip": l.IP.String(), - "hostname": l.Hostname, - } - - if includeExpires { - lease["expires"] = l.Expiry.Format(time.RFC3339) - } - - leases = append(leases, lease) - } - return leases -} - -func handleDHCPStatus(w http.ResponseWriter, r *http.Request) { - leases := convertLeases(config.dhcpServer.Leases(), true) - staticLeases := convertLeases(config.dhcpServer.StaticLeases(), false) - status := map[string]interface{}{ - "config": config.DHCP, - "leases": leases, - "static_leases": staticLeases, - } - - w.Header().Set("Content-Type", "application/json") - err := json.NewEncoder(w).Encode(status) - if err != nil { - httpError(w, http.StatusInternalServerError, "Unable to marshal DHCP status json: %s", err) - return - } -} - -type leaseJSON struct { - HWAddr string `json:"mac"` - IP string `json:"ip"` - Hostname string `json:"hostname"` -} - -type dhcpServerConfigJSON struct { - dhcpd.ServerConfig `json:",inline"` - StaticLeases []leaseJSON `json:"static_leases"` -} - -func handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) { - newconfig := dhcpServerConfigJSON{} - err := json.NewDecoder(r.Body).Decode(&newconfig) - if err != nil { - httpError(w, http.StatusBadRequest, "Failed to parse new DHCP config json: %s", err) - return - } - - newconfig.ServerConfig.WorkDir = config.ourWorkingDir - err = config.dhcpServer.CheckConfig(newconfig.ServerConfig) - if err != nil { - httpError(w, http.StatusBadRequest, "Invalid DHCP configuration: %s", err) - return - } - - err = config.dhcpServer.Stop() - if err != nil { - log.Error("failed to stop the DHCP server: %s", err) - } - - err = config.dhcpServer.Init(newconfig.ServerConfig) - if err != nil { - httpError(w, http.StatusBadRequest, "Invalid DHCP configuration: %s", err) - return - } - - if newconfig.Enabled { - - staticIP, err := hasStaticIP(newconfig.InterfaceName) - if !staticIP && err == nil { - err = setStaticIP(newconfig.InterfaceName) - if err != nil { - httpError(w, http.StatusInternalServerError, "Failed to configure static IP: %s", err) - return - } - } - - err = config.dhcpServer.Start() - if err != nil { - httpError(w, http.StatusBadRequest, "Failed to start DHCP server: %s", err) - return - } - } - - config.DHCP = newconfig.ServerConfig - httpUpdateConfigReloadDNSReturnOK(w, r) -} - -func handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) { - response := map[string]interface{}{} - - ifaces, err := getValidNetInterfaces() - if err != nil { - httpError(w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err) - return - } - - for _, iface := range ifaces { - if iface.Flags&net.FlagLoopback != 0 { - // it's a loopback, skip it - continue - } - if iface.Flags&net.FlagBroadcast == 0 { - // this interface doesn't support broadcast, skip it - continue - } - addrs, err := iface.Addrs() - if err != nil { - httpError(w, http.StatusInternalServerError, "Failed to get addresses for interface %s: %s", iface.Name, err) - return - } - - jsonIface := netInterface{ - Name: iface.Name, - MTU: iface.MTU, - HardwareAddr: iface.HardwareAddr.String(), - } - - if iface.Flags != 0 { - jsonIface.Flags = iface.Flags.String() - } - // we don't want link-local addresses in json, so skip them - for _, addr := range addrs { - ipnet, ok := addr.(*net.IPNet) - if !ok { - // not an IPNet, should not happen - httpError(w, http.StatusInternalServerError, "SHOULD NOT HAPPEN: got iface.Addrs() element %s that is not net.IPNet, it is %T", addr, addr) - return - } - // ignore link-local - if ipnet.IP.IsLinkLocalUnicast() { - continue - } - jsonIface.Addresses = append(jsonIface.Addresses, ipnet.IP.String()) - } - if len(jsonIface.Addresses) != 0 { - response[iface.Name] = jsonIface - } - - } - - err = json.NewEncoder(w).Encode(response) - if err != nil { - httpError(w, http.StatusInternalServerError, "Failed to marshal json with available interfaces: %s", err) - return - } -} - -// Perform the following tasks: -// . Search for another DHCP server running -// . Check if a static IP is configured for the network interface -// Respond with results -func handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Request) { - body, err := ioutil.ReadAll(r.Body) - if err != nil { - errorText := fmt.Sprintf("failed to read request body: %s", err) - log.Error(errorText) - http.Error(w, errorText, http.StatusBadRequest) - return - } - - interfaceName := strings.TrimSpace(string(body)) - if interfaceName == "" { - errorText := fmt.Sprintf("empty interface name specified") - log.Error(errorText) - http.Error(w, errorText, http.StatusBadRequest) - return - } - - found, err := dhcpd.CheckIfOtherDHCPServersPresent(interfaceName) - - othSrv := map[string]interface{}{} - foundVal := "no" - if found { - foundVal = "yes" - } else if err != nil { - foundVal = "error" - othSrv["error"] = err.Error() - } - othSrv["found"] = foundVal - - staticIP := map[string]interface{}{} - isStaticIP, err := hasStaticIP(interfaceName) - staticIPStatus := "yes" - if err != nil { - staticIPStatus = "error" - staticIP["error"] = err.Error() - } else if !isStaticIP { - staticIPStatus = "no" - staticIP["ip"] = getFullIP(interfaceName) - } - staticIP["static"] = staticIPStatus - - result := map[string]interface{}{} - result["other_server"] = othSrv - result["static_ip"] = staticIP - - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(result) - if err != nil { - httpError(w, http.StatusInternalServerError, "Failed to marshal DHCP found json: %s", err) - return - } -} - -// Check if network interface has a static IP configured -func hasStaticIP(ifaceName string) (bool, error) { - if runtime.GOOS == "windows" { - return false, errors.New("Can't detect static IP: not supported on Windows") - } - - body, err := ioutil.ReadFile("/etc/dhcpcd.conf") - if err != nil { - return false, err - } - lines := strings.Split(string(body), "\n") - nameLine := fmt.Sprintf("interface %s", ifaceName) - withinInterfaceCtx := false - - for _, line := range lines { - line = strings.TrimSpace(line) - - if withinInterfaceCtx && len(line) == 0 { - // an empty line resets our state - withinInterfaceCtx = false - } - - if len(line) == 0 || line[0] == '#' { - continue - } - line = strings.TrimSpace(line) - - if !withinInterfaceCtx { - if line == nameLine { - // we found our interface - withinInterfaceCtx = true - } - - } else { - if strings.HasPrefix(line, "interface ") { - // we found another interface - reset our state - withinInterfaceCtx = false - continue - } - if strings.HasPrefix(line, "static ip_address=") { - return true, nil - } - } - } - - return false, nil -} - -// Get IP address with netmask -func getFullIP(ifaceName string) string { - cmd := exec.Command("ip", "-oneline", "-family", "inet", "address", "show", ifaceName) - log.Tracef("executing %s %v", cmd.Path, cmd.Args) - d, err := cmd.Output() - if err != nil || cmd.ProcessState.ExitCode() != 0 { - return "" - } - - fields := strings.Fields(string(d)) - if len(fields) < 4 { - return "" - } - _, _, err = net.ParseCIDR(fields[3]) - if err != nil { - return "" - } - - return fields[3] -} - -// Get gateway IP address -func getGatewayIP(ifaceName string) string { - cmd := exec.Command("ip", "route", "show", "dev", ifaceName) - log.Tracef("executing %s %v", cmd.Path, cmd.Args) - d, err := cmd.Output() - if err != nil || cmd.ProcessState.ExitCode() != 0 { - return "" - } - - fields := strings.Fields(string(d)) - if len(fields) < 3 || fields[0] != "default" { - return "" - } - - ip := net.ParseIP(fields[2]) - if ip == nil { - return "" - } - - return fields[2] -} - -// Set a static IP for network interface -func setStaticIP(ifaceName string) error { - ip := getFullIP(ifaceName) - if len(ip) == 0 { - return errors.New("Can't get IP address") - } - - body, err := ioutil.ReadFile("/etc/dhcpcd.conf") - if err != nil { - return err - } - - ip4, _, err := net.ParseCIDR(ip) - if err != nil { - return err - } - - add := fmt.Sprintf("\ninterface %s\nstatic ip_address=%s\n", - ifaceName, ip) - body = append(body, []byte(add)...) - - gatewayIP := getGatewayIP(ifaceName) - if len(gatewayIP) != 0 { - add = fmt.Sprintf("static routers=%s\n", - gatewayIP) - body = append(body, []byte(add)...) - } - - add = fmt.Sprintf("static domain_name_servers=%s\n\n", - ip4) - body = append(body, []byte(add)...) - - err = file.SafeWrite("/etc/dhcpcd.conf", body) - if err != nil { - return err - } - - return nil -} - -func handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request) { - - lj := leaseJSON{} - err := json.NewDecoder(r.Body).Decode(&lj) - if err != nil { - httpError(w, http.StatusBadRequest, "json.Decode: %s", err) - return - } - - ip := parseIPv4(lj.IP) - if ip == nil { - httpError(w, http.StatusBadRequest, "invalid IP") - return - } - - mac, _ := net.ParseMAC(lj.HWAddr) - - lease := dhcpd.Lease{ - IP: ip, - HWAddr: mac, - Hostname: lj.Hostname, - } - err = config.dhcpServer.AddStaticLease(lease) - if err != nil { - httpError(w, http.StatusBadRequest, "%s", err) - return - } - returnOK(w) -} - -func handleDHCPRemoveStaticLease(w http.ResponseWriter, r *http.Request) { - - lj := leaseJSON{} - err := json.NewDecoder(r.Body).Decode(&lj) - if err != nil { - httpError(w, http.StatusBadRequest, "json.Decode: %s", err) - return - } - - ip := parseIPv4(lj.IP) - if ip == nil { - httpError(w, http.StatusBadRequest, "invalid IP") - return - } - - mac, _ := net.ParseMAC(lj.HWAddr) - - lease := dhcpd.Lease{ - IP: ip, - HWAddr: mac, - Hostname: lj.Hostname, - } - err = config.dhcpServer.RemoveStaticLease(lease) - if err != nil { - httpError(w, http.StatusBadRequest, "%s", err) - return - } - returnOK(w) -} - func startDHCPServer() error { if !config.DHCP.Enabled { // not enabled, don't do anything diff --git a/home/helpers.go b/home/helpers.go index 6b0f01ed..f4be0b38 100644 --- a/home/helpers.go +++ b/home/helpers.go @@ -1,7 +1,6 @@ package home import ( - "bytes" "context" "errors" "fmt" @@ -338,21 +337,6 @@ func _Func() string { return path.Base(f.Name()) } -// Parse input string and return IPv4 address -func parseIPv4(s string) net.IP { - ip := net.ParseIP(s) - if ip == nil { - return nil - } - - v4InV6Prefix := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff} - if !bytes.Equal(ip[0:12], v4InV6Prefix) { - return nil - } - - return ip.To4() -} - // SplitNext - split string by a byte and return the first chunk // Whitespace is trimmed func SplitNext(str *string, splitBy byte) string { diff --git a/home/home.go b/home/home.go index 9a059cd5..1ff0f128 100644 --- a/home/home.go +++ b/home/home.go @@ -119,6 +119,9 @@ func run(args options) { } } + config.DHCP.WorkDir = config.ourWorkingDir + config.DHCP.HTTPRegister = httpRegister + config.DHCP.ConfigModified = onConfigModified config.dhcpServer = dhcpd.Create(config.DHCP) config.clients.Init(config.Clients, config.dhcpServer) config.Clients = nil