diff --git a/dhcpd/dhcp_http.go b/dhcpd/dhcp_http.go
index 35f6a7ad..6031b9a8 100644
--- a/dhcpd/dhcp_http.go
+++ b/dhcpd/dhcp_http.go
@@ -93,10 +93,9 @@ func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
 	s.conf.ConfigModified()
 
 	if newconfig.Enabled {
-
-		staticIP, err := hasStaticIP(newconfig.InterfaceName)
+		staticIP, err := HasStaticIP(newconfig.InterfaceName)
 		if !staticIP && err == nil {
-			err = setStaticIP(newconfig.InterfaceName)
+			err = SetStaticIP(newconfig.InterfaceName)
 			if err != nil {
 				httpError(r, w, http.StatusInternalServerError, "Failed to configure static IP: %s", err)
 				return
@@ -122,7 +121,7 @@ type netInterfaceJSON struct {
 func (s *Server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
 	response := map[string]interface{}{}
 
-	ifaces, err := getValidNetInterfaces()
+	ifaces, err := GetValidNetInterfaces()
 	if err != nil {
 		httpError(r, w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err)
 		return
@@ -213,14 +212,14 @@ func (s *Server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Reque
 	othSrv["found"] = foundVal
 
 	staticIP := map[string]interface{}{}
-	isStaticIP, err := hasStaticIP(interfaceName)
+	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["ip"] = GetFullIP(interfaceName)
 	}
 	staticIP["static"] = staticIPStatus
 
diff --git a/dhcpd/network_utils.go b/dhcpd/network_utils.go
index 85fc261d..204105ae 100644
--- a/dhcpd/network_utils.go
+++ b/dhcpd/network_utils.go
@@ -10,12 +10,13 @@ import (
 	"strings"
 
 	"github.com/AdguardTeam/golibs/file"
+
 	"github.com/AdguardTeam/golibs/log"
 )
 
-// getValidNetInterfaces returns interfaces that are eligible for DNS and/or DHCP
+// 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) {
+func GetValidNetInterfaces() ([]net.Interface, error) {
 	ifaces, err := net.Interfaces()
 	if err != nil {
 		return nil, fmt.Errorf("Couldn't get list of interfaces: %s", err)
@@ -38,22 +39,76 @@ func getValidNetInterfaces() ([]net.Interface, error) {
 
 // Check if network interface has a static IP configured
 // Supports: Raspbian.
-func hasStaticIP(ifaceName string) (bool, error) {
-	if runtime.GOOS == "windows" {
-		return false, errors.New("Can't detect static IP: not supported on Windows")
+func HasStaticIP(ifaceName string) (bool, error) {
+	if runtime.GOOS == "linux" {
+		body, err := ioutil.ReadFile("/etc/dhcpcd.conf")
+		if err != nil {
+			return false, err
+		}
+
+		return hasStaticIPDhcpcdConf(string(body), ifaceName), nil
 	}
 
+	if runtime.GOOS == "darwin" {
+		return hasStaticIPDarwin(ifaceName)
+	}
+
+	return false, fmt.Errorf("Cannot check if IP is static: not supported on %s", runtime.GOOS)
+}
+
+// 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]
+}
+
+// Set a static IP for network interface
+// Supports: Raspbian.
+func SetStaticIP(ifaceName string) error {
+	ip := GetFullIP(ifaceName)
+	if len(ip) == 0 {
+		return errors.New("Can't get IP address")
+	}
+
+	ip4, _, err := net.ParseCIDR(ip)
+	if err != nil {
+		return err
+	}
+	gatewayIP := getGatewayIP(ifaceName)
+	add := setStaticIPDhcpcdConf(ifaceName, ip, gatewayIP, ip4.String())
+
 	body, err := ioutil.ReadFile("/etc/dhcpcd.conf")
 	if err != nil {
-		return false, err
+		return err
 	}
 
-	return hasStaticIPDhcpcdConf(string(body), ifaceName), nil
+	body = append(body, []byte(add)...)
+	err = file.SafeWrite("/etc/dhcpcd.conf", body)
+	if err != nil {
+		return err
+	}
+
+	return nil
 }
 
 // for dhcpcd.conf
-func hasStaticIPDhcpcdConf(data, ifaceName string) bool {
-	lines := strings.Split(data, "\n")
+func hasStaticIPDhcpcdConf(dhcpConf, ifaceName string) bool {
+	lines := strings.Split(dhcpConf, "\n")
 	nameLine := fmt.Sprintf("interface %s", ifaceName)
 	withinInterfaceCtx := false
 
@@ -90,27 +145,6 @@ func hasStaticIPDhcpcdConf(data, ifaceName string) bool {
 	return false
 }
 
-// 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)
@@ -133,35 +167,6 @@ func getGatewayIP(ifaceName string) string {
 	return fields[2]
 }
 
-// Set a static IP for network interface
-// Supports: Raspbian.
-func setStaticIP(ifaceName string) error {
-	ip := getFullIP(ifaceName)
-	if len(ip) == 0 {
-		return errors.New("Can't get IP address")
-	}
-
-	ip4, _, err := net.ParseCIDR(ip)
-	if err != nil {
-		return err
-	}
-	gatewayIP := getGatewayIP(ifaceName)
-	add := setStaticIPDhcpcdConf(ifaceName, ip, gatewayIP, ip4.String())
-
-	body, err := ioutil.ReadFile("/etc/dhcpcd.conf")
-	if err != nil {
-		return err
-	}
-
-	body = append(body, []byte(add)...)
-	err = file.SafeWrite("/etc/dhcpcd.conf", body)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
 // for dhcpcd.conf
 func setStaticIPDhcpcdConf(ifaceName, ip, gatewayIP, dnsIP string) string {
 	var body []byte
diff --git a/dhcpd/network_utils_darwin.go b/dhcpd/network_utils_darwin.go
new file mode 100644
index 00000000..9110b0b0
--- /dev/null
+++ b/dhcpd/network_utils_darwin.go
@@ -0,0 +1,8 @@
+package dhcpd
+
+// Check if network interface has a static IP configured
+// Supports: Raspbian.
+func hasStaticIPDarwin(ifaceName string) (bool, error) {
+
+	return false, nil
+}
diff --git a/home/network_utils_test.go b/dhcpd/network_utils_test.go
similarity index 98%
rename from home/network_utils_test.go
rename to dhcpd/network_utils_test.go
index 6312e535..3948a224 100644
--- a/home/network_utils_test.go
+++ b/dhcpd/network_utils_test.go
@@ -1,4 +1,4 @@
-package home
+package dhcpd
 
 import (
 	"testing"
diff --git a/home/control_install.go b/home/control_install.go
index 45cf0c7c..196b792b 100644
--- a/home/control_install.go
+++ b/home/control_install.go
@@ -13,6 +13,8 @@ import (
 	"runtime"
 	"strconv"
 
+	"github.com/AdguardTeam/AdGuardHome/dhcpd"
+
 	"github.com/AdguardTeam/golibs/log"
 )
 
@@ -141,19 +143,19 @@ func handleInstallCheckConfig(w http.ResponseWriter, r *http.Request) {
 				respData.StaticIP.Error = fmt.Sprintf("Couldn't find network interface by IP %s", reqData.DNS.IP)
 
 			} else if reqData.DNS.SetStaticIP {
-				err = setStaticIP(interfaceName)
+				err = dhcpd.SetStaticIP(interfaceName)
 				staticIPStatus = "error"
 				respData.StaticIP.Error = err.Error()
 
 			} else {
 				// check if we have a static IP
-				isStaticIP, err := hasStaticIP(interfaceName)
+				isStaticIP, err := dhcpd.HasStaticIP(interfaceName)
 				if err != nil {
 					staticIPStatus = "error"
 					respData.StaticIP.Error = err.Error()
 				} else if !isStaticIP {
 					staticIPStatus = "no"
-					respData.StaticIP.IP = getFullIP(interfaceName)
+					respData.StaticIP.IP = dhcpd.GetFullIP(interfaceName)
 				}
 			}
 			respData.StaticIP.Static = staticIPStatus
diff --git a/home/network_utils.go b/home/network_utils.go
index 04112001..4277c1c2 100644
--- a/home/network_utils.go
+++ b/home/network_utils.go
@@ -3,18 +3,15 @@ package home
 import (
 	"errors"
 	"fmt"
-	"io/ioutil"
 	"net"
 	"os"
-	"os/exec"
 	"runtime"
 	"strconv"
-	"strings"
 	"syscall"
 	"time"
 
-	"github.com/AdguardTeam/golibs/file"
-	"github.com/AdguardTeam/golibs/log"
+	"github.com/AdguardTeam/AdGuardHome/dhcpd"
+
 	"github.com/joomcode/errorx"
 )
 
@@ -26,33 +23,10 @@ type netInterface struct {
 	Flags        string
 }
 
-// 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
-}
-
 // getValidNetInterfacesMap returns interfaces that are eligible for DNS and WEB only
 // we do not return link-local addresses here
 func getValidNetInterfacesForWeb() ([]netInterface, error) {
-	ifaces, err := getValidNetInterfaces()
+	ifaces, err := dhcpd.GetValidNetInterfaces()
 	if err != nil {
 		return nil, errorx.Decorate(err, "Couldn't get interfaces")
 	}
@@ -99,81 +73,6 @@ func getValidNetInterfacesForWeb() ([]netInterface, error) {
 	return netInterfaces, nil
 }
 
-// Check if network interface has a static IP configured
-// Supports: Raspbian.
-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
-	}
-
-	return hasStaticIPDhcpcdConf(string(body), ifaceName), nil
-}
-
-// for dhcpcd.conf
-func hasStaticIPDhcpcdConf(data, ifaceName string) bool {
-	lines := strings.Split(data, "\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
-			}
-		}
-	}
-	return false
-}
-
-// 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 interface name by its IP address.
 func getInterfaceByIP(ip string) string {
 	ifaces, err := getValidNetInterfacesForWeb()
@@ -192,85 +91,13 @@ func getInterfaceByIP(ip string) string {
 	return ""
 }
 
-// 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
-// Supports: Raspbian.
-func setStaticIP(ifaceName string) error {
-	ip := getFullIP(ifaceName)
-	if len(ip) == 0 {
-		return errors.New("Can't get IP address")
-	}
-
-	ip4, _, err := net.ParseCIDR(ip)
-	if err != nil {
-		return err
-	}
-	gatewayIP := getGatewayIP(ifaceName)
-	add := setStaticIPDhcpcdConf(ifaceName, ip, gatewayIP, ip4.String())
-
-	body, err := ioutil.ReadFile("/etc/dhcpcd.conf")
-	if err != nil {
-		return err
-	}
-
-	body = append(body, []byte(add)...)
-	err = file.SafeWrite("/etc/dhcpcd.conf", body)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// for dhcpcd.conf
-func setStaticIPDhcpcdConf(ifaceName, ip, gatewayIP, dnsIP string) string {
-	var body []byte
-
-	add := fmt.Sprintf("\ninterface %s\nstatic ip_address=%s\n",
-		ifaceName, ip)
-	body = append(body, []byte(add)...)
-
-	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",
-		dnsIP)
-	body = append(body, []byte(add)...)
-
-	return string(body)
-}
-
 // checkPortAvailable is not a cheap test to see if the port is bindable, because it's actually doing the bind momentarily
 func checkPortAvailable(host string, port int) error {
 	ln, err := net.Listen("tcp", net.JoinHostPort(host, strconv.Itoa(port)))
 	if err != nil {
 		return err
 	}
-	ln.Close()
+	_ = ln.Close()
 
 	// It seems that net.Listener.Close() doesn't close file descriptors right away.
 	// We wait for some time and hope that this fd will be closed.
@@ -283,7 +110,7 @@ func checkPacketPortAvailable(host string, port int) error {
 	if err != nil {
 		return err
 	}
-	ln.Close()
+	_ = ln.Close()
 
 	// It seems that net.Listener.Close() doesn't close file descriptors right away.
 	// We wait for some time and hope that this fd will be closed.