AdGuardHome/dhcpd/dhcpd.go
Simon Zolin 2a9b87c672 wip
2020-05-20 16:48:23 +03:00

209 lines
4.7 KiB
Go

package dhcpd
import (
"fmt"
"net"
"net/http"
"path/filepath"
"strings"
"time"
"github.com/AdguardTeam/golibs/log"
)
const defaultDiscoverTime = time.Second * 3
const leaseExpireStatic = 1
var webHandlersRegistered = false
// Lease contains the necessary information about a DHCP lease
// field ordering is important -- yaml fields will mirror ordering from here
type Lease struct {
HWAddr net.HardwareAddr `json:"mac" yaml:"hwaddr"`
IP net.IP `json:"ip"`
Hostname string `json:"hostname"`
// Lease expiration time
// 1: static lease
Expiry time.Time `json:"expires"`
}
// ServerConfig - DHCP server configuration
// field ordering is important -- yaml fields will mirror ordering from here
type ServerConfig struct {
Conf4 V4ServerConf `json:"-" yaml:"dhcpv4"`
Conf6 V6ServerConf `json:"-" yaml:"dhcpv6"`
WorkDir string `json:"-" yaml:"-"`
DBFilePath string `json:"-" yaml:"-"` // path to DB file
// 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:"-"`
}
type onLeaseChangedT func(flags int)
// flags for onLeaseChanged()
const (
LeaseChangedAdded = iota
LeaseChangedAddedStatic
LeaseChangedRemovedStatic
LeaseChangedBlacklisted
LeaseChangedDBStore
)
// Server - the current state of the DHCP server
type Server struct {
// conn *filterConn // listening UDP socket
// ipnet *net.IPNet // if interface name changes, this needs to be reset
// cond *sync.Cond // Synchronize worker thread with main thread
// mutex sync.Mutex // Mutex for 'cond'
// running bool // Set if the worker thread is running
// stopping bool // Set if the worker thread should be stopped
// leases
// leaseOptions dhcp4.Options // parsed from config GatewayIP and SubnetMask
srv4 *V4Server
srv6 *V6Server
conf ServerConfig
// Called when the leases DB is modified
onLeaseChanged onLeaseChangedT
}
// Print information about the available network interfaces
func printInterfaces() {
ifaces, _ := net.Interfaces()
var buf strings.Builder
for i := range ifaces {
buf.WriteString(fmt.Sprintf("\"%s\", ", ifaces[i].Name))
}
log.Info("Available network interfaces: %s", buf.String())
}
// CheckConfig checks the configuration
func (s *Server) CheckConfig(config ServerConfig) error {
return nil
}
// Create - create object
func Create(config ServerConfig) *Server {
s := Server{}
config.Conf4.notify = s.onNotify
config.Conf6.notify = s.onNotify
s.conf.DBFilePath = filepath.Join(config.WorkDir, dbFilename)
if !webHandlersRegistered && s.conf.HTTPRegister != nil {
webHandlersRegistered = true
s.registerHandlers()
}
var err error
s.srv4, err = v4Create(config.Conf4)
if s.srv4 == nil {
log.Error("%s", err)
return nil
}
s.srv6, err = v6Create(config.Conf6)
if s.srv6 == nil {
log.Error("%s", err)
return nil
}
// we can't delay database loading until DHCP server is started,
// because we need static leases functionality available beforehand
s.dbLoad()
return &s
}
// server calls this function after DB is updated
func (s *Server) onNotify(flags uint32) {
if flags == LeaseChangedDBStore {
s.dbStore()
return
}
s.notify(int(flags))
}
// SetOnLeaseChanged - set callback
func (s *Server) SetOnLeaseChanged(onLeaseChanged onLeaseChangedT) {
s.onLeaseChanged = onLeaseChanged
}
func (s *Server) notify(flags int) {
if s.onLeaseChanged == nil {
return
}
s.onLeaseChanged(flags)
}
// WriteDiskConfig - write configuration
func (s *Server) WriteDiskConfig(c *ServerConfig) {
s.srv4.WriteDiskConfig(&c.Conf4)
s.srv6.WriteDiskConfig(&c.Conf6)
}
// Start will listen on port 67 and serve DHCP requests.
func (s *Server) Start() error {
err := s.srv4.Start()
if err != nil {
return err
}
err = s.srv6.Start()
if err != nil {
return err
}
return nil
}
// Stop closes the listening UDP socket
func (s *Server) Stop() error {
s.srv4.Stop()
s.srv6.Stop()
return nil
}
// flags for Leases() function
const (
LeasesDynamic = 1
LeasesStatic = 2
LeasesAll = LeasesDynamic | LeasesStatic
)
// Leases returns the list of current DHCP leases (thread-safe)
func (s *Server) Leases(flags int) []Lease {
result := s.srv4.GetLeases(flags)
if s.srv6 != nil {
v6leases := s.srv6.GetLeases(flags)
result = append(result, v6leases...)
}
return result
}
// FindMACbyIP - find a MAC address by IP address in the currently active DHCP leases
func (s *Server) FindMACbyIP(ip net.IP) net.HardwareAddr {
if ip.To4() != nil {
return s.srv4.FindMACbyIP4(ip)
}
return s.srv6.FindMACbyIP6(ip)
}
// Reset internal state
func (s *Server) reset() {
s.srv4.Reset()
s.srv6.Reset()
}