mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2025-03-31 22:43:31 +03:00
wip
This commit is contained in:
parent
f60d6f973d
commit
6280a1ad02
7 changed files with 440 additions and 738 deletions
|
@ -25,12 +25,12 @@ func CheckIfOtherDHCPServersPresent(ifaceName string) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get ipv4 address of an interface
|
// get ipv4 address of an interface
|
||||||
ifaceIPNet := getIfaceIPv4(iface)
|
ifaceIPNet := getIfaceIPv4(*iface)
|
||||||
if ifaceIPNet == nil {
|
if len(ifaceIPNet) == 0 {
|
||||||
return false, fmt.Errorf("Couldn't find IPv4 address of interface %s %+v", ifaceName, iface)
|
return false, fmt.Errorf("Couldn't find IPv4 address of interface %s %+v", ifaceName, iface)
|
||||||
}
|
}
|
||||||
|
|
||||||
srcIP := ifaceIPNet.IP
|
srcIP := ifaceIPNet[0]
|
||||||
src := net.JoinHostPort(srcIP.String(), "68")
|
src := net.JoinHostPort(srcIP.String(), "68")
|
||||||
dst := "255.255.255.255:67"
|
dst := "255.255.255.255:67"
|
||||||
|
|
||||||
|
|
60
dhcpd/db.go
60
dhcpd/db.go
|
@ -11,7 +11,6 @@ import (
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/file"
|
"github.com/AdguardTeam/golibs/file"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
"github.com/krolaw/dhcp4"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const dbFilename = "leases.db"
|
const dbFilename = "leases.db"
|
||||||
|
@ -31,22 +30,12 @@ func normalizeIP(ip net.IP) net.IP {
|
||||||
return ip
|
return ip
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe version of dhcp4.IPInRange()
|
|
||||||
func ipInRange(start, stop, ip net.IP) bool {
|
|
||||||
if len(start) != len(stop) ||
|
|
||||||
len(start) != len(ip) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return dhcp4.IPInRange(start, stop, ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load lease table from DB
|
// Load lease table from DB
|
||||||
func (s *Server) dbLoad() {
|
func (s *Server) dbLoad() {
|
||||||
s.leases = nil
|
|
||||||
s.IPpool = make(map[[4]byte]net.HardwareAddr)
|
|
||||||
dynLeases := []*Lease{}
|
dynLeases := []*Lease{}
|
||||||
staticLeases := []*Lease{}
|
staticLeases := []*Lease{}
|
||||||
v6StaticLeases := []*Lease{}
|
v6StaticLeases := []*Lease{}
|
||||||
|
v6DynLeases := []*Lease{}
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(s.conf.DBFilePath)
|
data, err := ioutil.ReadFile(s.conf.DBFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -72,14 +61,6 @@ func (s *Server) dbLoad() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if obj[i].Expiry != leaseExpireStatic &&
|
|
||||||
len(obj[i].IP) == 4 &&
|
|
||||||
!ipInRange(s.leaseStart, s.leaseStop, obj[i].IP) {
|
|
||||||
|
|
||||||
log.Tracef("Skipping a lease with IP %v: not within current IP range", obj[i].IP)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
lease := Lease{
|
lease := Lease{
|
||||||
HWAddr: obj[i].HWAddr,
|
HWAddr: obj[i].HWAddr,
|
||||||
IP: obj[i].IP,
|
IP: obj[i].IP,
|
||||||
|
@ -88,28 +69,31 @@ func (s *Server) dbLoad() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(obj[i].IP) == 16 {
|
if len(obj[i].IP) == 16 {
|
||||||
v6StaticLeases = append(v6StaticLeases, &lease)
|
if obj[i].Expiry == leaseExpireStatic {
|
||||||
|
v6StaticLeases = append(v6StaticLeases, &lease)
|
||||||
|
} else {
|
||||||
|
v6DynLeases = append(v6DynLeases, &lease)
|
||||||
|
}
|
||||||
|
|
||||||
} else if obj[i].Expiry == leaseExpireStatic {
|
|
||||||
staticLeases = append(staticLeases, &lease)
|
|
||||||
} else {
|
} else {
|
||||||
dynLeases = append(dynLeases, &lease)
|
if obj[i].Expiry == leaseExpireStatic {
|
||||||
|
staticLeases = append(staticLeases, &lease)
|
||||||
|
} else {
|
||||||
|
dynLeases = append(dynLeases, &lease)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.leases = normalizeLeases(staticLeases, dynLeases)
|
leases4 := normalizeLeases(staticLeases, dynLeases)
|
||||||
|
s.srv4.ResetLeases(leases4)
|
||||||
|
|
||||||
for _, lease := range s.leases {
|
leases6 := normalizeLeases(v6StaticLeases, v6DynLeases)
|
||||||
s.reserveIP(lease.IP, lease.HWAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
v6StaticLeases = normalizeLeases(v6StaticLeases, []*Lease{})
|
|
||||||
if s.srv6 != nil {
|
if s.srv6 != nil {
|
||||||
s.srv6.leases = v6StaticLeases
|
s.srv6.ResetLeases(leases6)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("DHCP: loaded leases v4:%d v6:%d total-read:%d from DB",
|
log.Info("DHCP: loaded leases v4:%d v6:%d total-read:%d from DB",
|
||||||
len(s.leases), len(v6StaticLeases), numLeases)
|
len(leases4), len(leases6), numLeases)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip duplicate leases
|
// Skip duplicate leases
|
||||||
|
@ -143,15 +127,15 @@ func normalizeLeases(staticLeases, dynLeases []*Lease) []*Lease {
|
||||||
func (s *Server) dbStore() {
|
func (s *Server) dbStore() {
|
||||||
var leases []leaseJSON
|
var leases []leaseJSON
|
||||||
|
|
||||||
for i := range s.leases {
|
for _, l := range s.srv4.leases {
|
||||||
if s.leases[i].Expiry.Unix() == 0 {
|
if l.Expiry.Unix() == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
lease := leaseJSON{
|
lease := leaseJSON{
|
||||||
HWAddr: s.leases[i].HWAddr,
|
HWAddr: l.HWAddr,
|
||||||
IP: s.leases[i].IP,
|
IP: l.IP,
|
||||||
Hostname: s.leases[i].Hostname,
|
Hostname: l.Hostname,
|
||||||
Expiry: s.leases[i].Expiry.Unix(),
|
Expiry: l.Expiry.Unix(),
|
||||||
}
|
}
|
||||||
leases = append(leases, lease)
|
leases = append(leases, lease)
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ func (s *Server) handleDHCPStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
leases := convertLeases(s.Leases(LeasesDynamic), true)
|
leases := convertLeases(s.Leases(LeasesDynamic), true)
|
||||||
staticLeases := convertLeases(s.Leases(LeasesStatic), false)
|
staticLeases := convertLeases(s.Leases(LeasesStatic), false)
|
||||||
status := map[string]interface{}{
|
status := map[string]interface{}{
|
||||||
"config": s.conf,
|
"config": s.conf.Conf4,
|
||||||
"config_v6": v6ServerConfToJSON(s.conf.Conf6),
|
"config_v6": v6ServerConfToJSON(s.conf.Conf6),
|
||||||
"leases": leases,
|
"leases": leases,
|
||||||
"static_leases": staticLeases,
|
"static_leases": staticLeases,
|
||||||
|
@ -87,7 +87,7 @@ type staticLeaseJSON struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type dhcpServerConfigJSON struct {
|
type dhcpServerConfigJSON struct {
|
||||||
ServerConfig `json:",inline"`
|
V4ServerConf `json:",inline"`
|
||||||
V6 v6ServerConfJSON `json:"v6"`
|
V6 v6ServerConfJSON `json:"v6"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,18 +99,20 @@ func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.CheckConfig(newconfig.ServerConfig)
|
// err = s.CheckConfig(newconfig.ServerConfig)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
httpError(r, w, http.StatusBadRequest, "Invalid DHCP configuration: %s", err)
|
// httpError(r, w, http.StatusBadRequest, "Invalid DHCP configuration: %s", err)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
err = s.Stop()
|
err = s.Stop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to stop the DHCP server: %s", err)
|
log.Error("failed to stop the DHCP server: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.Init(newconfig.ServerConfig)
|
v4conf := newconfig.V4ServerConf
|
||||||
|
v4conf.notify = s.conf.Conf4.notify
|
||||||
|
s4, err := v4Create(v4conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(r, w, http.StatusBadRequest, "Invalid DHCP configuration: %s", err)
|
httpError(r, w, http.StatusBadRequest, "Invalid DHCP configuration: %s", err)
|
||||||
return
|
return
|
||||||
|
@ -123,6 +125,8 @@ func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
httpError(r, w, http.StatusBadRequest, "Invalid DHCPv6 configuration: %s", err)
|
httpError(r, w, http.StatusBadRequest, "Invalid DHCPv6 configuration: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.srv4 = s4
|
||||||
s.srv6 = s6
|
s.srv6 = s6
|
||||||
|
|
||||||
s.conf.ConfigModified()
|
s.conf.ConfigModified()
|
||||||
|
@ -317,7 +321,7 @@ func (s *Server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request
|
||||||
HWAddr: mac,
|
HWAddr: mac,
|
||||||
Hostname: lj.Hostname,
|
Hostname: lj.Hostname,
|
||||||
}
|
}
|
||||||
err = s.AddStaticLease(lease)
|
err = s.srv4.AddStaticLease(lease)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(r, w, http.StatusBadRequest, "%s", err)
|
httpError(r, w, http.StatusBadRequest, "%s", err)
|
||||||
return
|
return
|
||||||
|
@ -367,7 +371,7 @@ func (s *Server) handleDHCPRemoveStaticLease(w http.ResponseWriter, r *http.Requ
|
||||||
HWAddr: mac,
|
HWAddr: mac,
|
||||||
Hostname: lj.Hostname,
|
Hostname: lj.Hostname,
|
||||||
}
|
}
|
||||||
err = s.RemoveStaticLease(lease)
|
err = s.srv4.RemoveStaticLease(lease)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(r, w, http.StatusBadRequest, "%s", err)
|
httpError(r, w, http.StatusBadRequest, "%s", err)
|
||||||
return
|
return
|
||||||
|
@ -387,12 +391,12 @@ func (s *Server) handleReset(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
oldconf := s.conf
|
oldconf := s.conf
|
||||||
s.conf = ServerConfig{}
|
s.conf = ServerConfig{}
|
||||||
s.conf.LeaseDuration = 86400
|
|
||||||
s.conf.ICMPTimeout = 1000
|
|
||||||
s.conf.WorkDir = oldconf.WorkDir
|
s.conf.WorkDir = oldconf.WorkDir
|
||||||
s.conf.HTTPRegister = oldconf.HTTPRegister
|
s.conf.HTTPRegister = oldconf.HTTPRegister
|
||||||
s.conf.ConfigModified = oldconf.ConfigModified
|
s.conf.ConfigModified = oldconf.ConfigModified
|
||||||
s.conf.DBFilePath = oldconf.DBFilePath
|
s.conf.DBFilePath = oldconf.DBFilePath
|
||||||
|
|
||||||
s.conf.ConfigModified()
|
s.conf.ConfigModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
647
dhcpd/dhcpd.go
647
dhcpd/dhcpd.go
|
@ -1,18 +1,14 @@
|
||||||
package dhcpd
|
package dhcpd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
"github.com/krolaw/dhcp4"
|
|
||||||
ping "github.com/sparrc/go-ping"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultDiscoverTime = time.Second * 3
|
const defaultDiscoverTime = time.Second * 3
|
||||||
|
@ -35,18 +31,6 @@ type Lease struct {
|
||||||
// ServerConfig - DHCP server configuration
|
// ServerConfig - DHCP server configuration
|
||||||
// field ordering is important -- yaml fields will mirror ordering from here
|
// field ordering is important -- yaml fields will mirror ordering from here
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
Enabled bool `json:"enabled" yaml:"enabled"`
|
|
||||||
InterfaceName string `json:"interface_name" yaml:"interface_name"` // eth0, en0 and so on
|
|
||||||
GatewayIP string `json:"gateway_ip" yaml:"gateway_ip"`
|
|
||||||
SubnetMask string `json:"subnet_mask" yaml:"subnet_mask"`
|
|
||||||
RangeStart string `json:"range_start" yaml:"range_start"`
|
|
||||||
RangeEnd string `json:"range_end" yaml:"range_end"`
|
|
||||||
LeaseDuration uint32 `json:"lease_duration" yaml:"lease_duration"` // in seconds
|
|
||||||
|
|
||||||
// IP conflict detector: time (ms) to wait for ICMP reply.
|
|
||||||
// 0: disable
|
|
||||||
ICMPTimeout uint32 `json:"icmp_timeout_msec" yaml:"icmp_timeout_msec"`
|
|
||||||
|
|
||||||
Conf4 V4ServerConf `json:"-" yaml:"dhcpv4"`
|
Conf4 V4ServerConf `json:"-" yaml:"dhcpv4"`
|
||||||
Conf6 V6ServerConf `json:"-" yaml:"dhcpv6"`
|
Conf6 V6ServerConf `json:"-" yaml:"dhcpv6"`
|
||||||
|
|
||||||
|
@ -68,26 +52,23 @@ const (
|
||||||
LeaseChangedAddedStatic
|
LeaseChangedAddedStatic
|
||||||
LeaseChangedRemovedStatic
|
LeaseChangedRemovedStatic
|
||||||
LeaseChangedBlacklisted
|
LeaseChangedBlacklisted
|
||||||
|
|
||||||
|
LeaseChangedDBStore
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server - the current state of the DHCP server
|
// Server - the current state of the DHCP server
|
||||||
type Server struct {
|
type Server struct {
|
||||||
conn *filterConn // listening UDP socket
|
// conn *filterConn // listening UDP socket
|
||||||
|
|
||||||
ipnet *net.IPNet // if interface name changes, this needs to be reset
|
// ipnet *net.IPNet // if interface name changes, this needs to be reset
|
||||||
|
|
||||||
cond *sync.Cond // Synchronize worker thread with main thread
|
// cond *sync.Cond // Synchronize worker thread with main thread
|
||||||
mutex sync.Mutex // Mutex for 'cond'
|
// mutex sync.Mutex // Mutex for 'cond'
|
||||||
running bool // Set if the worker thread is running
|
// running bool // Set if the worker thread is running
|
||||||
stopping bool // Set if the worker thread should be stopped
|
// stopping bool // Set if the worker thread should be stopped
|
||||||
|
|
||||||
// leases
|
// leases
|
||||||
leases []*Lease
|
// leaseOptions dhcp4.Options // parsed from config GatewayIP and SubnetMask
|
||||||
leasesLock sync.RWMutex
|
|
||||||
leaseStart net.IP // parsed from config RangeStart
|
|
||||||
leaseStop net.IP // parsed from config RangeEnd
|
|
||||||
leaseTime time.Duration // parsed from config LeaseDuration
|
|
||||||
leaseOptions dhcp4.Options // parsed from config GatewayIP and SubnetMask
|
|
||||||
|
|
||||||
srv4 *V4Server
|
srv4 *V4Server
|
||||||
srv6 *V6Server
|
srv6 *V6Server
|
||||||
|
@ -110,23 +91,15 @@ func printInterfaces() {
|
||||||
|
|
||||||
// CheckConfig checks the configuration
|
// CheckConfig checks the configuration
|
||||||
func (s *Server) CheckConfig(config ServerConfig) error {
|
func (s *Server) CheckConfig(config ServerConfig) error {
|
||||||
tmpServer := Server{}
|
return nil
|
||||||
return tmpServer.setConfig(config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create - create object
|
// Create - create object
|
||||||
func Create(config ServerConfig) *Server {
|
func Create(config ServerConfig) *Server {
|
||||||
s := Server{}
|
s := Server{}
|
||||||
s.conf = config
|
s.conf.Conf4.notify = s.onNotify
|
||||||
s.conf.Conf6.notify = s.notify6
|
s.conf.Conf6.notify = s.onNotify
|
||||||
s.conf.DBFilePath = filepath.Join(config.WorkDir, dbFilename)
|
s.conf.DBFilePath = filepath.Join(config.WorkDir, dbFilename)
|
||||||
if s.conf.Enabled {
|
|
||||||
err := s.setConfig(config)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("DHCP: %s", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !webHandlersRegistered && s.conf.HTTPRegister != nil {
|
if !webHandlersRegistered && s.conf.HTTPRegister != nil {
|
||||||
webHandlersRegistered = true
|
webHandlersRegistered = true
|
||||||
|
@ -152,17 +125,18 @@ func Create(config ServerConfig) *Server {
|
||||||
return &s
|
return &s
|
||||||
}
|
}
|
||||||
|
|
||||||
// v6 server calls this function after DB is updated
|
// server calls this function after DB is updated
|
||||||
func (s *Server) notify6(flags uint32) {
|
func (s *Server) onNotify(flags uint32) {
|
||||||
s.dbStore()
|
if flags == LeaseChangedDBStore {
|
||||||
|
s.dbStore()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.notify(int(flags))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init checks the configuration and initializes the server
|
// Init checks the configuration and initializes the server
|
||||||
func (s *Server) Init(config ServerConfig) error {
|
func (s *Server) Init(config ServerConfig) error {
|
||||||
err := s.setConfig(config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,84 +154,18 @@ func (s *Server) notify(flags int) {
|
||||||
|
|
||||||
// WriteDiskConfig - write configuration
|
// WriteDiskConfig - write configuration
|
||||||
func (s *Server) WriteDiskConfig(c *ServerConfig) {
|
func (s *Server) WriteDiskConfig(c *ServerConfig) {
|
||||||
*c = s.conf
|
s.srv4.WriteDiskConfig(&c.Conf4)
|
||||||
s.srv6.WriteDiskConfig(&c.Conf6)
|
s.srv6.WriteDiskConfig(&c.Conf6)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) setConfig(config ServerConfig) error {
|
|
||||||
iface, err := net.InterfaceByName(config.InterfaceName)
|
|
||||||
if err != nil {
|
|
||||||
printInterfaces()
|
|
||||||
return wrapErrPrint(err, "Couldn't find interface by name %s", config.InterfaceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// get ipv4 address of an interface
|
|
||||||
s.ipnet = getIfaceIPv4(iface)
|
|
||||||
if s.ipnet == nil {
|
|
||||||
return wrapErrPrint(err, "Couldn't find IPv4 address of interface %s %+v", config.InterfaceName, iface)
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.LeaseDuration == 0 {
|
|
||||||
s.leaseTime = time.Hour * 2
|
|
||||||
} else {
|
|
||||||
s.leaseTime = time.Second * time.Duration(config.LeaseDuration)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.leaseStart, err = parseIPv4(config.RangeStart)
|
|
||||||
if err != nil {
|
|
||||||
return wrapErrPrint(err, "Failed to parse range start address %s", config.RangeStart)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.leaseStop, err = parseIPv4(config.RangeEnd)
|
|
||||||
if err != nil {
|
|
||||||
return wrapErrPrint(err, "Failed to parse range end address %s", config.RangeEnd)
|
|
||||||
}
|
|
||||||
if dhcp4.IPRange(s.leaseStart, s.leaseStop) <= 0 {
|
|
||||||
return wrapErrPrint(err, "DHCP: Incorrect range_start/range_end values")
|
|
||||||
}
|
|
||||||
|
|
||||||
subnet, err := parseIPv4(config.SubnetMask)
|
|
||||||
if err != nil || !isValidSubnetMask(subnet) {
|
|
||||||
return wrapErrPrint(err, "Failed to parse subnet mask %s", config.SubnetMask)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if !bytes.Equal(subnet, s.ipnet.Mask) {
|
|
||||||
// return wrapErrPrint(err, "specified subnet mask %s does not meatch interface %s subnet mask %s", s.SubnetMask, s.InterfaceName, s.ipnet.Mask)
|
|
||||||
// }
|
|
||||||
|
|
||||||
router, err := parseIPv4(config.GatewayIP)
|
|
||||||
if err != nil {
|
|
||||||
return wrapErrPrint(err, "Failed to parse gateway IP %s", config.GatewayIP)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.leaseOptions = dhcp4.Options{
|
|
||||||
dhcp4.OptionSubnetMask: subnet,
|
|
||||||
dhcp4.OptionRouter: router,
|
|
||||||
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 = oldconf.DBFilePath
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start will listen on port 67 and serve DHCP requests.
|
// Start will listen on port 67 and serve DHCP requests.
|
||||||
func (s *Server) Start() error {
|
func (s *Server) Start() error {
|
||||||
iface, err := net.InterfaceByName(s.conf.InterfaceName)
|
err := s.srv4.Start()
|
||||||
if err != nil {
|
|
||||||
return wrapErrPrint(err, "Couldn't find interface by name %s", s.conf.InterfaceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.srv4.Start(*iface)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.srv6.Start(*iface)
|
err = s.srv6.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -269,461 +177,6 @@ func (s *Server) Start() error {
|
||||||
func (s *Server) Stop() error {
|
func (s *Server) Stop() error {
|
||||||
s.srv4.Stop()
|
s.srv4.Stop()
|
||||||
s.srv6.Stop()
|
s.srv6.Stop()
|
||||||
|
|
||||||
/* if s.conn == nil {
|
|
||||||
// nothing to do, return silently
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
s.stopping = true
|
|
||||||
|
|
||||||
err := s.closeConn()
|
|
||||||
if err != nil {
|
|
||||||
return wrapErrPrint(err, "Couldn't close UDP listening socket")
|
|
||||||
}
|
|
||||||
|
|
||||||
// We've just closed the listening socket.
|
|
||||||
// Worker thread should exit right after it tries to read from the socket.
|
|
||||||
s.mutex.Lock()
|
|
||||||
for s.running {
|
|
||||||
s.cond.Wait()
|
|
||||||
}
|
|
||||||
s.mutex.Unlock() */
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// closeConn will close the connection and set it to zero
|
|
||||||
func (s *Server) closeConn() error {
|
|
||||||
if s.conn == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := s.conn.Close()
|
|
||||||
s.conn = nil
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reserve a lease for the client
|
|
||||||
func (s *Server) reserveLease(p dhcp4.Packet) (*Lease, error) {
|
|
||||||
// WARNING: do not remove copy()
|
|
||||||
// the given hwaddr by p.CHAddr() in the packet survives only during ServeDHCP() call
|
|
||||||
// since we need to retain it we need to make our own copy
|
|
||||||
hwaddrCOW := p.CHAddr()
|
|
||||||
hwaddr := make(net.HardwareAddr, len(hwaddrCOW))
|
|
||||||
copy(hwaddr, hwaddrCOW)
|
|
||||||
// not assigned a lease, create new one, find IP from LRU
|
|
||||||
hostname := p.ParseOptions()[dhcp4.OptionHostName]
|
|
||||||
lease := &Lease{HWAddr: hwaddr, Hostname: string(hostname)}
|
|
||||||
|
|
||||||
log.Tracef("Lease not found for %s: creating new one", hwaddr)
|
|
||||||
|
|
||||||
s.leasesLock.Lock()
|
|
||||||
defer s.leasesLock.Unlock()
|
|
||||||
|
|
||||||
ip, err := s.findFreeIP(hwaddr)
|
|
||||||
if err != nil {
|
|
||||||
i := s.findExpiredLease()
|
|
||||||
if i < 0 {
|
|
||||||
return nil, wrapErrPrint(err, "Couldn't find free IP for the lease %s", hwaddr.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Tracef("Assigning IP address %s to %s (lease for %s expired at %s)",
|
|
||||||
s.leases[i].IP, hwaddr, s.leases[i].HWAddr, s.leases[i].Expiry)
|
|
||||||
lease.IP = s.leases[i].IP
|
|
||||||
s.leases[i] = lease
|
|
||||||
|
|
||||||
s.reserveIP(lease.IP, hwaddr)
|
|
||||||
return lease, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Tracef("Assigning to %s IP address %s", hwaddr, ip.String())
|
|
||||||
lease.IP = ip
|
|
||||||
s.leases = append(s.leases, lease)
|
|
||||||
return lease, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find a lease for the client
|
|
||||||
func (s *Server) findLease(p dhcp4.Packet) *Lease {
|
|
||||||
hwaddr := p.CHAddr()
|
|
||||||
for i := range s.leases {
|
|
||||||
if bytes.Equal([]byte(hwaddr), []byte(s.leases[i].HWAddr)) {
|
|
||||||
// log.Tracef("bytes.Equal(%s, %s) returned true", hwaddr, s.leases[i].hwaddr)
|
|
||||||
return s.leases[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find an expired lease and return its index or -1
|
|
||||||
func (s *Server) findExpiredLease() int {
|
|
||||||
now := time.Now().Unix()
|
|
||||||
for i, lease := range s.leases {
|
|
||||||
if lease.Expiry.Unix() <= now && lease.Expiry.Unix() != leaseExpireStatic {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) findFreeIP(hwaddr net.HardwareAddr) (net.IP, error) {
|
|
||||||
// go from start to end, find unreserved IP
|
|
||||||
var foundIP net.IP
|
|
||||||
for i := 0; i < dhcp4.IPRange(s.leaseStart, s.leaseStop); i++ {
|
|
||||||
newIP := dhcp4.IPAdd(s.leaseStart, i)
|
|
||||||
foundHWaddr := s.findReservedHWaddr(newIP)
|
|
||||||
log.Tracef("tried IP %v, got hwaddr %v", newIP, foundHWaddr)
|
|
||||||
if foundHWaddr != nil && len(foundHWaddr) != 0 {
|
|
||||||
// if !bytes.Equal(foundHWaddr, hwaddr) {
|
|
||||||
// log.Tracef("SHOULD NOT HAPPEN: hwaddr in IP pool %s is not equal to hwaddr in lease %s", foundHWaddr, hwaddr)
|
|
||||||
// }
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
foundIP = newIP
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if foundIP == nil {
|
|
||||||
// TODO: LRU
|
|
||||||
return nil, fmt.Errorf("couldn't find free entry in IP pool")
|
|
||||||
}
|
|
||||||
|
|
||||||
s.reserveIP(foundIP, hwaddr)
|
|
||||||
|
|
||||||
return foundIP, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) findReservedHWaddr(ip net.IP) net.HardwareAddr {
|
|
||||||
rawIP := []byte(ip)
|
|
||||||
IP4 := [4]byte{rawIP[0], rawIP[1], rawIP[2], rawIP[3]}
|
|
||||||
return s.IPpool[IP4]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) reserveIP(ip net.IP, hwaddr net.HardwareAddr) {
|
|
||||||
rawIP := []byte(ip)
|
|
||||||
IP4 := [4]byte{rawIP[0], rawIP[1], rawIP[2], rawIP[3]}
|
|
||||||
s.IPpool[IP4] = hwaddr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) unreserveIP(ip net.IP) {
|
|
||||||
rawIP := []byte(ip)
|
|
||||||
IP4 := [4]byte{rawIP[0], rawIP[1], rawIP[2], rawIP[3]}
|
|
||||||
delete(s.IPpool, IP4)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeDHCP handles an incoming DHCP request
|
|
||||||
func (s *Server) ServeDHCP(p dhcp4.Packet, msgType dhcp4.MessageType, options dhcp4.Options) dhcp4.Packet {
|
|
||||||
s.printLeases()
|
|
||||||
|
|
||||||
switch msgType {
|
|
||||||
case dhcp4.Discover: // Broadcast Packet From Client - Can I have an IP?
|
|
||||||
return s.handleDiscover(p, options)
|
|
||||||
|
|
||||||
case dhcp4.Request: // Broadcast From Client - I'll take that IP (Also start for renewals)
|
|
||||||
// start/renew a lease -- update lease time
|
|
||||||
// some clients (OSX) just go right ahead and do Request first from previously known IP, if they get NAK, they restart full cycle with Discover then Request
|
|
||||||
return s.handleDHCP4Request(p, options)
|
|
||||||
|
|
||||||
case dhcp4.Decline: // Broadcast From Client - Sorry I can't use that IP
|
|
||||||
return s.handleDecline(p, options)
|
|
||||||
|
|
||||||
case dhcp4.Release: // From Client, I don't need that IP anymore
|
|
||||||
return s.handleRelease(p, options)
|
|
||||||
|
|
||||||
case dhcp4.Inform: // From Client, I have this IP and there's nothing you can do about it
|
|
||||||
return s.handleInform(p, options)
|
|
||||||
|
|
||||||
// from server -- ignore those but enumerate just in case
|
|
||||||
case dhcp4.Offer: // Broadcast From Server - Here's an IP
|
|
||||||
log.Printf("DHCP: received message from %s: Offer", p.CHAddr())
|
|
||||||
|
|
||||||
case dhcp4.ACK: // From Server, Yes you can have that IP
|
|
||||||
log.Printf("DHCP: received message from %s: ACK", p.CHAddr())
|
|
||||||
|
|
||||||
case dhcp4.NAK: // From Server, No you cannot have that IP
|
|
||||||
log.Printf("DHCP: received message from %s: NAK", p.CHAddr())
|
|
||||||
|
|
||||||
default:
|
|
||||||
log.Printf("DHCP: unknown packet %v from %s", msgType, p.CHAddr())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send ICMP to the specified machine
|
|
||||||
// Return TRUE if it doesn't reply, which probably means that the IP is available
|
|
||||||
func (s *Server) addrAvailable(target net.IP) bool {
|
|
||||||
|
|
||||||
if s.conf.ICMPTimeout == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
pinger, err := ping.NewPinger(target.String())
|
|
||||||
if err != nil {
|
|
||||||
log.Error("ping.NewPinger(): %v", err)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
pinger.SetPrivileged(true)
|
|
||||||
pinger.Timeout = time.Duration(s.conf.ICMPTimeout) * time.Millisecond
|
|
||||||
pinger.Count = 1
|
|
||||||
reply := false
|
|
||||||
pinger.OnRecv = func(pkt *ping.Packet) {
|
|
||||||
// log.Tracef("Received ICMP Reply from %v", target)
|
|
||||||
reply = true
|
|
||||||
}
|
|
||||||
log.Tracef("Sending ICMP Echo to %v", target)
|
|
||||||
pinger.Run()
|
|
||||||
|
|
||||||
if reply {
|
|
||||||
log.Info("DHCP: IP conflict: %v is already used by another device", target)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Tracef("ICMP procedure is complete: %v", target)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the specified IP to the black list for a time period
|
|
||||||
func (s *Server) blacklistLease(lease *Lease) {
|
|
||||||
hw := make(net.HardwareAddr, 6)
|
|
||||||
s.leasesLock.Lock()
|
|
||||||
s.reserveIP(lease.IP, hw)
|
|
||||||
lease.HWAddr = hw
|
|
||||||
lease.Hostname = ""
|
|
||||||
lease.Expiry = time.Now().Add(s.leaseTime)
|
|
||||||
s.dbStore()
|
|
||||||
s.leasesLock.Unlock()
|
|
||||||
s.notify(LeaseChangedBlacklisted)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return TRUE if DHCP packet is correct
|
|
||||||
func isValidPacket(p dhcp4.Packet) bool {
|
|
||||||
hw := p.CHAddr()
|
|
||||||
zeroes := make([]byte, len(hw))
|
|
||||||
if bytes.Equal(hw, zeroes) {
|
|
||||||
log.Tracef("Packet has empty CHAddr")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) handleDiscover(p dhcp4.Packet, options dhcp4.Options) dhcp4.Packet {
|
|
||||||
// find a lease, but don't update lease time
|
|
||||||
var lease *Lease
|
|
||||||
var err error
|
|
||||||
|
|
||||||
reqIP := net.IP(options[dhcp4.OptionRequestedIPAddress])
|
|
||||||
hostname := p.ParseOptions()[dhcp4.OptionHostName]
|
|
||||||
log.Tracef("Message from client: Discover. ReqIP: %s HW: %s Hostname: %s",
|
|
||||||
reqIP, p.CHAddr(), hostname)
|
|
||||||
|
|
||||||
if !isValidPacket(p) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
lease = s.findLease(p)
|
|
||||||
for lease == nil {
|
|
||||||
lease, err = s.reserveLease(p)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Couldn't find free lease: %s", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !s.addrAvailable(lease.IP) {
|
|
||||||
s.blacklistLease(lease)
|
|
||||||
lease = nil
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
opt := s.leaseOptions.SelectOrderOrAll(options[dhcp4.OptionParameterRequestList])
|
|
||||||
reply := dhcp4.ReplyPacket(p, dhcp4.Offer, s.ipnet.IP, lease.IP, s.leaseTime, opt)
|
|
||||||
log.Tracef("Replying with offer: offered IP %v for %v with options %+v", lease.IP, s.leaseTime, reply.ParseOptions())
|
|
||||||
return reply
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) handleDHCP4Request(p dhcp4.Packet, options dhcp4.Options) dhcp4.Packet {
|
|
||||||
var lease *Lease
|
|
||||||
|
|
||||||
reqIP := net.IP(options[dhcp4.OptionRequestedIPAddress])
|
|
||||||
log.Tracef("Message from client: Request. IP: %s ReqIP: %s HW: %s",
|
|
||||||
p.CIAddr(), reqIP, p.CHAddr())
|
|
||||||
|
|
||||||
if !isValidPacket(p) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
server := options[dhcp4.OptionServerIdentifier]
|
|
||||||
if server != nil && !net.IP(server).Equal(s.ipnet.IP) {
|
|
||||||
log.Tracef("Request message not for this DHCP server (%v vs %v)", server, s.ipnet.IP)
|
|
||||||
return nil // Message not for this dhcp server
|
|
||||||
}
|
|
||||||
|
|
||||||
if reqIP == nil {
|
|
||||||
reqIP = p.CIAddr()
|
|
||||||
|
|
||||||
} else if reqIP == nil || reqIP.To4() == nil {
|
|
||||||
log.Tracef("Requested IP isn't a valid IPv4: %s", reqIP)
|
|
||||||
return dhcp4.ReplyPacket(p, dhcp4.NAK, s.ipnet.IP, nil, 0, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
lease = s.findLease(p)
|
|
||||||
if lease == nil {
|
|
||||||
log.Tracef("Lease for %s isn't found", p.CHAddr())
|
|
||||||
return dhcp4.ReplyPacket(p, dhcp4.NAK, s.ipnet.IP, nil, 0, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !lease.IP.Equal(reqIP) {
|
|
||||||
log.Tracef("Lease for %s doesn't match requested/client IP: %s vs %s",
|
|
||||||
lease.HWAddr, lease.IP, reqIP)
|
|
||||||
return dhcp4.ReplyPacket(p, dhcp4.NAK, s.ipnet.IP, nil, 0, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
if lease.Expiry.Unix() != leaseExpireStatic {
|
|
||||||
lease.Expiry = time.Now().Add(s.leaseTime)
|
|
||||||
s.leasesLock.Lock()
|
|
||||||
s.dbStore()
|
|
||||||
s.leasesLock.Unlock()
|
|
||||||
s.notify(LeaseChangedAdded) // Note: maybe we shouldn't call this function if only expiration time is updated
|
|
||||||
}
|
|
||||||
log.Tracef("Replying with ACK. IP: %s HW: %s Expire: %s",
|
|
||||||
lease.IP, lease.HWAddr, lease.Expiry)
|
|
||||||
opt := s.leaseOptions.SelectOrderOrAll(options[dhcp4.OptionParameterRequestList])
|
|
||||||
return dhcp4.ReplyPacket(p, dhcp4.ACK, s.ipnet.IP, lease.IP, s.leaseTime, opt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) handleInform(p dhcp4.Packet, options dhcp4.Options) dhcp4.Packet {
|
|
||||||
log.Tracef("Message from client: Inform. IP: %s HW: %s",
|
|
||||||
p.CIAddr(), p.CHAddr())
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) handleRelease(p dhcp4.Packet, options dhcp4.Options) dhcp4.Packet {
|
|
||||||
log.Tracef("Message from client: Release. IP: %s HW: %s",
|
|
||||||
p.CIAddr(), p.CHAddr())
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) handleDecline(p dhcp4.Packet, options dhcp4.Options) dhcp4.Packet {
|
|
||||||
reqIP := net.IP(options[dhcp4.OptionRequestedIPAddress])
|
|
||||||
log.Tracef("Message from client: Decline. IP: %s HW: %s",
|
|
||||||
reqIP, p.CHAddr())
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddStaticLease adds a static lease (thread-safe)
|
|
||||||
func (s *Server) AddStaticLease(l Lease) error {
|
|
||||||
if len(l.IP) != 4 {
|
|
||||||
return fmt.Errorf("invalid IP")
|
|
||||||
}
|
|
||||||
if len(l.HWAddr) != 6 {
|
|
||||||
return fmt.Errorf("invalid MAC")
|
|
||||||
}
|
|
||||||
l.Expiry = time.Unix(leaseExpireStatic, 0)
|
|
||||||
|
|
||||||
s.leasesLock.Lock()
|
|
||||||
|
|
||||||
if s.findReservedHWaddr(l.IP) != nil {
|
|
||||||
err := s.rmDynamicLeaseWithIP(l.IP)
|
|
||||||
if err != nil {
|
|
||||||
s.leasesLock.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err := s.rmDynamicLeaseWithMAC(l.HWAddr)
|
|
||||||
if err != nil {
|
|
||||||
s.leasesLock.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.leases = append(s.leases, &l)
|
|
||||||
s.reserveIP(l.IP, l.HWAddr)
|
|
||||||
s.dbStore()
|
|
||||||
s.leasesLock.Unlock()
|
|
||||||
s.notify(LeaseChangedAddedStatic)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove a dynamic lease by IP address
|
|
||||||
func (s *Server) rmDynamicLeaseWithIP(ip net.IP) error {
|
|
||||||
var newLeases []*Lease
|
|
||||||
for _, lease := range s.leases {
|
|
||||||
if net.IP.Equal(lease.IP.To4(), ip) {
|
|
||||||
if lease.Expiry.Unix() == leaseExpireStatic {
|
|
||||||
return fmt.Errorf("static lease with the same IP already exists")
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newLeases = append(newLeases, lease)
|
|
||||||
}
|
|
||||||
s.leases = newLeases
|
|
||||||
s.unreserveIP(ip)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove a dynamic lease by IP address
|
|
||||||
func (s *Server) rmDynamicLeaseWithMAC(mac net.HardwareAddr) error {
|
|
||||||
var newLeases []*Lease
|
|
||||||
for _, lease := range s.leases {
|
|
||||||
if bytes.Equal(lease.HWAddr, mac) {
|
|
||||||
if lease.Expiry.Unix() == leaseExpireStatic {
|
|
||||||
return fmt.Errorf("static lease with the same IP already exists")
|
|
||||||
}
|
|
||||||
s.unreserveIP(lease.IP)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newLeases = append(newLeases, lease)
|
|
||||||
}
|
|
||||||
s.leases = newLeases
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove a lease
|
|
||||||
func (s *Server) rmLease(l Lease) error {
|
|
||||||
var newLeases []*Lease
|
|
||||||
for _, lease := range s.leases {
|
|
||||||
if net.IP.Equal(lease.IP.To4(), l.IP) {
|
|
||||||
if !bytes.Equal(lease.HWAddr, l.HWAddr) ||
|
|
||||||
lease.Hostname != l.Hostname {
|
|
||||||
return fmt.Errorf("Lease not found")
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newLeases = append(newLeases, lease)
|
|
||||||
}
|
|
||||||
s.leases = newLeases
|
|
||||||
s.unreserveIP(l.IP)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveStaticLease removes a static lease (thread-safe)
|
|
||||||
func (s *Server) RemoveStaticLease(l Lease) error {
|
|
||||||
if len(l.IP) != 4 {
|
|
||||||
return fmt.Errorf("invalid IP")
|
|
||||||
}
|
|
||||||
if len(l.HWAddr) != 6 {
|
|
||||||
return fmt.Errorf("invalid MAC")
|
|
||||||
}
|
|
||||||
|
|
||||||
s.leasesLock.Lock()
|
|
||||||
|
|
||||||
if s.findReservedHWaddr(l.IP) == nil {
|
|
||||||
s.leasesLock.Unlock()
|
|
||||||
return fmt.Errorf("lease not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := s.rmLease(l)
|
|
||||||
if err != nil {
|
|
||||||
s.leasesLock.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s.dbStore()
|
|
||||||
s.leasesLock.Unlock()
|
|
||||||
s.notify(LeaseChangedRemovedStatic)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -736,16 +189,7 @@ const (
|
||||||
|
|
||||||
// Leases returns the list of current DHCP leases (thread-safe)
|
// Leases returns the list of current DHCP leases (thread-safe)
|
||||||
func (s *Server) Leases(flags int) []Lease {
|
func (s *Server) Leases(flags int) []Lease {
|
||||||
var result []Lease
|
result := s.srv4.GetLeases(flags)
|
||||||
now := time.Now().Unix()
|
|
||||||
s.leasesLock.RLock()
|
|
||||||
for _, lease := range s.leases {
|
|
||||||
if ((flags&LeasesDynamic) != 0 && lease.Expiry.Unix() > now) ||
|
|
||||||
((flags&LeasesStatic) != 0 && lease.Expiry.Unix() == leaseExpireStatic) {
|
|
||||||
result = append(result, *lease)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.leasesLock.RUnlock()
|
|
||||||
|
|
||||||
if s.srv6 != nil {
|
if s.srv6 != nil {
|
||||||
v6leases := s.srv6.GetLeases(flags)
|
v6leases := s.srv6.GetLeases(flags)
|
||||||
|
@ -755,49 +199,12 @@ func (s *Server) Leases(flags int) []Lease {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print information about the current leases
|
|
||||||
func (s *Server) printLeases() {
|
|
||||||
log.Tracef("Leases:")
|
|
||||||
for i, lease := range s.leases {
|
|
||||||
log.Tracef("Lease #%d: hwaddr %s, ip %s, expiry %s",
|
|
||||||
i, lease.HWAddr, lease.IP, lease.Expiry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindIPbyMAC finds an IP address by MAC address in the currently active DHCP leases
|
|
||||||
func (s *Server) FindIPbyMAC(mac net.HardwareAddr) net.IP {
|
|
||||||
now := time.Now().Unix()
|
|
||||||
s.leasesLock.RLock()
|
|
||||||
defer s.leasesLock.RUnlock()
|
|
||||||
for _, l := range s.leases {
|
|
||||||
if l.Expiry.Unix() > now && bytes.Equal(mac, l.HWAddr) {
|
|
||||||
return l.IP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindMACbyIP - find a MAC address by IP address in the currently active DHCP leases
|
// FindMACbyIP - find a MAC address by IP address in the currently active DHCP leases
|
||||||
func (s *Server) FindMACbyIP(ip net.IP) net.HardwareAddr {
|
func (s *Server) FindMACbyIP(ip net.IP) net.HardwareAddr {
|
||||||
now := time.Now().Unix()
|
if ip.To4() != nil {
|
||||||
|
return s.srv4.FindMACbyIP4(ip)
|
||||||
s.leasesLock.RLock()
|
|
||||||
defer s.leasesLock.RUnlock()
|
|
||||||
|
|
||||||
ip4 := ip.To4()
|
|
||||||
if ip4 == nil {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
return s.srv6.FindMACbyIP6(ip)
|
||||||
for _, l := range s.leases {
|
|
||||||
if l.IP.Equal(ip4) {
|
|
||||||
unix := l.Expiry.Unix()
|
|
||||||
if unix > now || unix == leaseExpireStatic {
|
|
||||||
return l.HWAddr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset internal state
|
// Reset internal state
|
||||||
|
|
|
@ -17,34 +17,6 @@ func isTimeout(err error) bool {
|
||||||
return operr.Timeout()
|
return operr.Timeout()
|
||||||
}
|
}
|
||||||
|
|
||||||
// return first IPv4 address of an interface, if there is any
|
|
||||||
func getIfaceIPv4(iface *net.Interface) *net.IPNet {
|
|
||||||
ifaceAddrs, err := iface.Addrs()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, addr := range ifaceAddrs {
|
|
||||||
ipnet, ok := addr.(*net.IPNet)
|
|
||||||
if !ok {
|
|
||||||
// not an IPNet, should not happen
|
|
||||||
log.Fatalf("SHOULD NOT HAPPEN: got iface.Addrs() element %s that is not net.IPNet", addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ipnet.IP.To4() == nil {
|
|
||||||
log.Tracef("Got IP that is not IPv4: %v", ipnet.IP)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Tracef("Got IP that is IPv4: %v", ipnet.IP)
|
|
||||||
return &net.IPNet{
|
|
||||||
IP: ipnet.IP.To4(),
|
|
||||||
Mask: ipnet.Mask,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func wrapErrPrint(err error, message string, args ...interface{}) error {
|
func wrapErrPrint(err error, message string, args ...interface{}) error {
|
||||||
var errx error
|
var errx error
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
354
dhcpd/v4.go
354
dhcpd/v4.go
|
@ -1,13 +1,16 @@
|
||||||
package dhcpd
|
package dhcpd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
|
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||||
"github.com/insomniacslk/dhcp/dhcpv4/server4"
|
"github.com/insomniacslk/dhcp/dhcpv4/server4"
|
||||||
"github.com/krolaw/dhcp4"
|
"github.com/sparrc/go-ping"
|
||||||
)
|
)
|
||||||
|
|
||||||
// V4Server - DHCPv4 server
|
// V4Server - DHCPv4 server
|
||||||
|
@ -23,44 +26,326 @@ type V4Server struct {
|
||||||
|
|
||||||
// V4ServerConf - server configuration
|
// V4ServerConf - server configuration
|
||||||
type V4ServerConf struct {
|
type V4ServerConf struct {
|
||||||
Enabled bool `yaml:"enabled"`
|
Enabled bool `json:"enabled" yaml:"enabled"`
|
||||||
// RangeStart string `yaml:"range_start"`
|
InterfaceName string `json:"interface_name" yaml:"interface_name"` // eth0, en0 and so on
|
||||||
LeaseDuration uint32 `yaml:"lease_duration"` // in seconds
|
GatewayIP string `json:"gateway_ip" yaml:"gateway_ip"`
|
||||||
|
SubnetMask string `json:"subnet_mask" yaml:"subnet_mask"`
|
||||||
|
RangeStart string `json:"range_start" yaml:"range_start"`
|
||||||
|
RangeEnd string `json:"range_end" yaml:"range_end"`
|
||||||
|
LeaseDuration uint32 `json:"lease_duration" yaml:"lease_duration"` // in seconds
|
||||||
|
|
||||||
// ipStart net.IP
|
// IP conflict detector: time (ms) to wait for ICMP reply.
|
||||||
leaseTime time.Duration
|
// 0: disable
|
||||||
// dnsIPAddrs []net.IP // IPv6 addresses to return to DHCP clients as DNS server addresses
|
ICMPTimeout uint32 `json:"icmp_timeout_msec" yaml:"icmp_timeout_msec"`
|
||||||
// sid dhcpv6.Duid
|
|
||||||
|
|
||||||
// notify func(uint32)
|
ipStart net.IP
|
||||||
|
ipStop net.IP
|
||||||
|
leaseTime time.Duration
|
||||||
|
dnsIPAddrs []net.IP // IPv4 addresses to return to DHCP clients as DNS server addresses
|
||||||
|
|
||||||
|
notify func(uint32)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteDiskConfig - write configuration
|
||||||
|
func (s *V4Server) WriteDiskConfig(c *V4ServerConf) {
|
||||||
|
*c = s.conf
|
||||||
|
}
|
||||||
|
|
||||||
|
func ipInRange(start, stop, ip net.IP) bool {
|
||||||
|
if len(start) != len(stop) ||
|
||||||
|
len(start) != len(ip) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// return dhcp4.IPInRange(start, stop, ip)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetLeases - reset leases
|
||||||
|
func (s *V4Server) ResetLeases(ll []*Lease) {
|
||||||
|
s.leases = nil
|
||||||
|
s.IPpool = make(map[[4]byte]net.HardwareAddr)
|
||||||
|
for _, l := range ll {
|
||||||
|
|
||||||
|
if l.Expiry.Unix() != leaseExpireStatic &&
|
||||||
|
!ipInRange(s.conf.ipStart, s.conf.ipStop, l.IP) {
|
||||||
|
|
||||||
|
log.Tracef("DHCPv4: skipping a lease with IP %v: not within current IP range", l.IP)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
s.leases = append(s.leases, l)
|
||||||
|
s.reserveIP(l.IP, l.HWAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLeases returns the list of current DHCP leases (thread-safe)
|
||||||
|
func (s *V4Server) GetLeases(flags int) []Lease {
|
||||||
|
var result []Lease
|
||||||
|
now := time.Now().Unix()
|
||||||
|
s.leasesLock.Lock()
|
||||||
|
for _, lease := range s.leases {
|
||||||
|
if ((flags&LeasesDynamic) != 0 && lease.Expiry.Unix() > now) ||
|
||||||
|
((flags&LeasesStatic) != 0 && lease.Expiry.Unix() == leaseExpireStatic) {
|
||||||
|
result = append(result, *lease)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.leasesLock.Unlock()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindMACbyIP4 - find a MAC address by IP address in the currently active DHCP leases
|
||||||
|
func (s *V4Server) FindMACbyIP4(ip net.IP) net.HardwareAddr {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
|
||||||
|
s.leasesLock.Lock()
|
||||||
|
defer s.leasesLock.Unlock()
|
||||||
|
|
||||||
|
ip4 := ip.To4()
|
||||||
|
if ip4 == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, l := range s.leases {
|
||||||
|
if l.IP.Equal(ip4) {
|
||||||
|
unix := l.Expiry.Unix()
|
||||||
|
if unix > now || unix == leaseExpireStatic {
|
||||||
|
return l.HWAddr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *V4Server) reserveIP(ip net.IP, hwaddr net.HardwareAddr) {
|
||||||
|
rawIP := []byte(ip)
|
||||||
|
IP4 := [4]byte{rawIP[0], rawIP[1], rawIP[2], rawIP[3]}
|
||||||
|
s.IPpool[IP4] = hwaddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *V4Server) unreserveIP(ip net.IP) {
|
||||||
|
rawIP := []byte(ip)
|
||||||
|
IP4 := [4]byte{rawIP[0], rawIP[1], rawIP[2], rawIP[3]}
|
||||||
|
delete(s.IPpool, IP4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *V4Server) findReservedHWaddr(ip net.IP) net.HardwareAddr {
|
||||||
|
rawIP := []byte(ip)
|
||||||
|
IP4 := [4]byte{rawIP[0], rawIP[1], rawIP[2], rawIP[3]}
|
||||||
|
return s.IPpool[IP4]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the specified IP to the black list for a time period
|
||||||
|
func (s *V4Server) blacklistLease(lease *Lease) {
|
||||||
|
hw := make(net.HardwareAddr, 6)
|
||||||
|
s.leasesLock.Lock()
|
||||||
|
s.reserveIP(lease.IP, hw)
|
||||||
|
lease.HWAddr = hw
|
||||||
|
lease.Hostname = ""
|
||||||
|
lease.Expiry = time.Now().Add(s.conf.leaseTime)
|
||||||
|
s.conf.notify(LeaseChangedDBStore)
|
||||||
|
s.leasesLock.Unlock()
|
||||||
|
s.conf.notify(LeaseChangedBlacklisted)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a dynamic lease by IP address
|
||||||
|
func (s *V4Server) rmDynamicLeaseWithIP(ip net.IP) error {
|
||||||
|
var newLeases []*Lease
|
||||||
|
for _, lease := range s.leases {
|
||||||
|
if net.IP.Equal(lease.IP.To4(), ip) {
|
||||||
|
if lease.Expiry.Unix() == leaseExpireStatic {
|
||||||
|
return fmt.Errorf("static lease with the same IP already exists")
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newLeases = append(newLeases, lease)
|
||||||
|
}
|
||||||
|
s.leases = newLeases
|
||||||
|
s.unreserveIP(ip)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a dynamic lease by IP address
|
||||||
|
func (s *V4Server) rmDynamicLeaseWithMAC(mac net.HardwareAddr) error {
|
||||||
|
var newLeases []*Lease
|
||||||
|
for _, lease := range s.leases {
|
||||||
|
if bytes.Equal(lease.HWAddr, mac) {
|
||||||
|
if lease.Expiry.Unix() == leaseExpireStatic {
|
||||||
|
return fmt.Errorf("static lease with the same IP already exists")
|
||||||
|
}
|
||||||
|
s.unreserveIP(lease.IP)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newLeases = append(newLeases, lease)
|
||||||
|
}
|
||||||
|
s.leases = newLeases
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a lease
|
||||||
|
func (s *V4Server) rmLease(l Lease) error {
|
||||||
|
var newLeases []*Lease
|
||||||
|
for _, lease := range s.leases {
|
||||||
|
if net.IP.Equal(lease.IP.To4(), l.IP) {
|
||||||
|
if !bytes.Equal(lease.HWAddr, l.HWAddr) ||
|
||||||
|
lease.Hostname != l.Hostname {
|
||||||
|
return fmt.Errorf("Lease not found")
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newLeases = append(newLeases, lease)
|
||||||
|
}
|
||||||
|
s.leases = newLeases
|
||||||
|
s.unreserveIP(l.IP)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddStaticLease adds a static lease (thread-safe)
|
||||||
|
func (s *V4Server) AddStaticLease(l Lease) error {
|
||||||
|
if len(l.IP) != 4 {
|
||||||
|
return fmt.Errorf("invalid IP")
|
||||||
|
}
|
||||||
|
if len(l.HWAddr) != 6 {
|
||||||
|
return fmt.Errorf("invalid MAC")
|
||||||
|
}
|
||||||
|
l.Expiry = time.Unix(leaseExpireStatic, 0)
|
||||||
|
|
||||||
|
s.leasesLock.Lock()
|
||||||
|
|
||||||
|
if s.findReservedHWaddr(l.IP) != nil {
|
||||||
|
err := s.rmDynamicLeaseWithIP(l.IP)
|
||||||
|
if err != nil {
|
||||||
|
s.leasesLock.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := s.rmDynamicLeaseWithMAC(l.HWAddr)
|
||||||
|
if err != nil {
|
||||||
|
s.leasesLock.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.leases = append(s.leases, &l)
|
||||||
|
s.reserveIP(l.IP, l.HWAddr)
|
||||||
|
s.conf.notify(LeaseChangedDBStore)
|
||||||
|
s.leasesLock.Unlock()
|
||||||
|
s.conf.notify(LeaseChangedAddedStatic)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveStaticLease removes a static lease (thread-safe)
|
||||||
|
func (s *V4Server) RemoveStaticLease(l Lease) error {
|
||||||
|
if len(l.IP) != 4 {
|
||||||
|
return fmt.Errorf("invalid IP")
|
||||||
|
}
|
||||||
|
if len(l.HWAddr) != 6 {
|
||||||
|
return fmt.Errorf("invalid MAC")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.leasesLock.Lock()
|
||||||
|
|
||||||
|
if s.findReservedHWaddr(l.IP) == nil {
|
||||||
|
s.leasesLock.Unlock()
|
||||||
|
return fmt.Errorf("lease not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.rmLease(l)
|
||||||
|
if err != nil {
|
||||||
|
s.leasesLock.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.conf.notify(LeaseChangedDBStore)
|
||||||
|
s.leasesLock.Unlock()
|
||||||
|
s.conf.notify(LeaseChangedRemovedStatic)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send ICMP to the specified machine
|
||||||
|
// Return TRUE if it doesn't reply, which probably means that the IP is available
|
||||||
|
func (s *V4Server) addrAvailable(target net.IP) bool {
|
||||||
|
|
||||||
|
if s.conf.ICMPTimeout == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
pinger, err := ping.NewPinger(target.String())
|
||||||
|
if err != nil {
|
||||||
|
log.Error("ping.NewPinger(): %v", err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
pinger.SetPrivileged(true)
|
||||||
|
pinger.Timeout = time.Duration(s.conf.ICMPTimeout) * time.Millisecond
|
||||||
|
pinger.Count = 1
|
||||||
|
reply := false
|
||||||
|
pinger.OnRecv = func(pkt *ping.Packet) {
|
||||||
|
// log.Tracef("Received ICMP Reply from %v", target)
|
||||||
|
reply = true
|
||||||
|
}
|
||||||
|
log.Tracef("Sending ICMP Echo to %v", target)
|
||||||
|
pinger.Run()
|
||||||
|
|
||||||
|
if reply {
|
||||||
|
log.Info("DHCP: IP conflict: %v is already used by another device", target)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Tracef("ICMP procedure is complete: %v", target)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *V4Server) packetHandler(conn net.PacketConn, peer net.Addr, m *dhcpv4.DHCPv4) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get IPv4 address list
|
||||||
|
func getIfaceIPv4(iface net.Interface) []net.IP {
|
||||||
|
addrs, err := iface.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []net.IP
|
||||||
|
for _, a := range addrs {
|
||||||
|
ipnet, ok := a.(*net.IPNet)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ipnet.IP.To4() != nil {
|
||||||
|
res = append(res, ipnet.IP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start - start server
|
// Start - start server
|
||||||
func (s *V4Server) Start(iface net.Interface) error {
|
func (s *V4Server) Start() error {
|
||||||
if s.conn != nil {
|
iface, err := net.InterfaceByName(s.conf.InterfaceName)
|
||||||
_ = s.closeConn()
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := newFilterConn(iface, ":67") // it has to be bound to 0.0.0.0:67, otherwise it won't see DHCP discover/request packets
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wrapErrPrint(err, "Couldn't start listening socket on 0.0.0.0:67")
|
return wrapErrPrint(err, "DHCPv4: Couldn't find interface by name %s", s.conf.InterfaceName)
|
||||||
}
|
}
|
||||||
log.Info("DHCP: listening on 0.0.0.0:67")
|
|
||||||
|
|
||||||
s.conn = c
|
log.Debug("DHCPv4: starting...")
|
||||||
s.cond = sync.NewCond(&s.mutex)
|
s.conf.dnsIPAddrs = getIfaceIPv4(*iface)
|
||||||
|
if len(s.conf.dnsIPAddrs) == 0 {
|
||||||
|
return fmt.Errorf("DHCPv4: no IPv4 address for interface %s", iface.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
laddr := &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("0.0.0.0"),
|
||||||
|
Port: dhcpv4.ServerPort,
|
||||||
|
}
|
||||||
|
|
||||||
|
server, err := server4.NewServer(iface.Name, laddr, s.packetHandler, server4.WithDebugLogger())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("DHCPv4: listening")
|
||||||
|
|
||||||
s.running = true
|
|
||||||
go func() {
|
go func() {
|
||||||
// operate on c instead of c.conn because c.conn can change over time
|
err = server.Serve()
|
||||||
err := dhcp4.Serve(c, s)
|
log.Error("DHCPv4: %s", err)
|
||||||
if err != nil && !s.stopping {
|
|
||||||
log.Printf("dhcp4.Serve() returned with error: %s", err)
|
|
||||||
}
|
|
||||||
_ = c.Close() // in case Serve() exits for other reason than listening socket closure
|
|
||||||
s.running = false
|
|
||||||
s.cond.Signal()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +359,15 @@ func (s *V4Server) Reset() {
|
||||||
|
|
||||||
// Stop - stop server
|
// Stop - stop server
|
||||||
func (s *V4Server) Stop() {
|
func (s *V4Server) Stop() {
|
||||||
|
if s.srv == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.srv.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("DHCPv4: srv.Close: %s", err)
|
||||||
|
}
|
||||||
|
// now server.Serve() will return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create DHCPv6 server
|
// Create DHCPv6 server
|
||||||
|
@ -90,8 +384,10 @@ func v4Create(conf V4ServerConf) (*V4Server, error) {
|
||||||
// return nil, fmt.Errorf("DHCPv6: invalid range-start IP: %s", conf.RangeStart)
|
// return nil, fmt.Errorf("DHCPv6: invalid range-start IP: %s", conf.RangeStart)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// s.conf.ICMPTimeout = 1000
|
||||||
|
|
||||||
if conf.LeaseDuration == 0 {
|
if conf.LeaseDuration == 0 {
|
||||||
s.conf.leaseTime = time.Hour * 2
|
s.conf.leaseTime = time.Hour * 24
|
||||||
s.conf.LeaseDuration = uint32(s.conf.leaseTime.Seconds())
|
s.conf.LeaseDuration = uint32(s.conf.leaseTime.Seconds())
|
||||||
} else {
|
} else {
|
||||||
s.conf.leaseTime = time.Second * time.Duration(conf.LeaseDuration)
|
s.conf.leaseTime = time.Second * time.Duration(conf.LeaseDuration)
|
||||||
|
|
55
dhcpd/v6.go
55
dhcpd/v6.go
|
@ -27,6 +27,7 @@ type V6Server struct {
|
||||||
// V6ServerConf - server configuration
|
// V6ServerConf - server configuration
|
||||||
type V6ServerConf struct {
|
type V6ServerConf struct {
|
||||||
Enabled bool `yaml:"enabled"`
|
Enabled bool `yaml:"enabled"`
|
||||||
|
InterfaceName string `yaml:"interface_name"`
|
||||||
RangeStart string `yaml:"range_start"`
|
RangeStart string `yaml:"range_start"`
|
||||||
LeaseDuration uint32 `yaml:"lease_duration"` // in seconds
|
LeaseDuration uint32 `yaml:"lease_duration"` // in seconds
|
||||||
|
|
||||||
|
@ -43,6 +44,15 @@ func (s *V6Server) WriteDiskConfig(c *V6ServerConf) {
|
||||||
*c = s.conf
|
*c = s.conf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResetLeases - reset leases
|
||||||
|
func (s *V6Server) ResetLeases(ll []*Lease) {
|
||||||
|
s.leases = nil
|
||||||
|
for _, l := range ll {
|
||||||
|
// TODO
|
||||||
|
s.leases = append(s.leases, l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GetLeases - get current leases
|
// GetLeases - get current leases
|
||||||
func (s *V6Server) GetLeases(flags int) []Lease {
|
func (s *V6Server) GetLeases(flags int) []Lease {
|
||||||
var result []Lease
|
var result []Lease
|
||||||
|
@ -64,6 +74,29 @@ func (s *V6Server) GetLeases(flags int) []Lease {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindMACbyIP6 - find a MAC address by IP address in the currently active DHCP leases
|
||||||
|
func (s *V6Server) FindMACbyIP6(ip net.IP) net.HardwareAddr {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
|
||||||
|
s.leasesLock.Lock()
|
||||||
|
defer s.leasesLock.Unlock()
|
||||||
|
|
||||||
|
ip4 := ip.To4()
|
||||||
|
if ip4 == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, l := range s.leases {
|
||||||
|
if l.IP.Equal(ip4) {
|
||||||
|
unix := l.Expiry.Unix()
|
||||||
|
if unix > now || unix == leaseExpireStatic {
|
||||||
|
return l.HWAddr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// AddStaticLease - add a static lease
|
// AddStaticLease - add a static lease
|
||||||
func (s *V6Server) AddStaticLease(l Lease) error {
|
func (s *V6Server) AddStaticLease(l Lease) error {
|
||||||
if len(l.IP) != 16 {
|
if len(l.IP) != 16 {
|
||||||
|
@ -81,9 +114,9 @@ func (s *V6Server) AddStaticLease(l Lease) error {
|
||||||
s.leasesLock.Unlock()
|
s.leasesLock.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.conf.notify(LeaseChangedAddedStatic)
|
s.conf.notify(LeaseChangedDBStore)
|
||||||
s.leasesLock.Unlock()
|
s.leasesLock.Unlock()
|
||||||
// s.notify(LeaseChangedAddedStatic)
|
s.conf.notify(LeaseChangedAddedStatic)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,9 +135,9 @@ func (s *V6Server) RemoveStaticLease(l Lease) error {
|
||||||
s.leasesLock.Unlock()
|
s.leasesLock.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.conf.notify(LeaseChangedRemovedStatic)
|
s.conf.notify(LeaseChangedDBStore)
|
||||||
s.leasesLock.Unlock()
|
s.leasesLock.Unlock()
|
||||||
// s.notify(LeaseChangedRemovedStatic)
|
s.conf.notify(LeaseChangedRemovedStatic)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,8 +298,9 @@ func (s *V6Server) commitLease(msg *dhcpv6.Message, lease *Lease) time.Duration
|
||||||
lease.Expiry = time.Now().Add(s.conf.leaseTime)
|
lease.Expiry = time.Now().Add(s.conf.leaseTime)
|
||||||
|
|
||||||
s.leasesLock.Lock()
|
s.leasesLock.Lock()
|
||||||
s.conf.notify(LeaseChangedAdded)
|
s.conf.notify(LeaseChangedDBStore)
|
||||||
s.leasesLock.Unlock()
|
s.leasesLock.Unlock()
|
||||||
|
s.conf.notify(LeaseChangedAdded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lifetime
|
return lifetime
|
||||||
|
@ -430,13 +464,18 @@ func getIfaceIPv6(iface net.Interface) []net.IP {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start - start server
|
// Start - start server
|
||||||
func (s *V6Server) Start(iface net.Interface) error {
|
func (s *V6Server) Start() error {
|
||||||
|
iface, err := net.InterfaceByName(s.conf.InterfaceName)
|
||||||
|
if err != nil {
|
||||||
|
return wrapErrPrint(err, "Couldn't find interface by name %s", s.conf.InterfaceName)
|
||||||
|
}
|
||||||
|
|
||||||
if !s.conf.Enabled {
|
if !s.conf.Enabled {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("DHCPv6: starting...")
|
log.Debug("DHCPv6: starting...")
|
||||||
s.conf.dnsIPAddrs = getIfaceIPv6(iface)
|
s.conf.dnsIPAddrs = getIfaceIPv6(*iface)
|
||||||
if len(s.conf.dnsIPAddrs) == 0 {
|
if len(s.conf.dnsIPAddrs) == 0 {
|
||||||
return fmt.Errorf("DHCPv6: no IPv6 address for interface %s", iface.Name)
|
return fmt.Errorf("DHCPv6: no IPv6 address for interface %s", iface.Name)
|
||||||
}
|
}
|
||||||
|
@ -501,7 +540,7 @@ func v6Create(conf V6ServerConf) (*V6Server, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.LeaseDuration == 0 {
|
if conf.LeaseDuration == 0 {
|
||||||
s.conf.leaseTime = time.Hour * 2
|
s.conf.leaseTime = time.Hour * 24
|
||||||
s.conf.LeaseDuration = uint32(s.conf.leaseTime.Seconds())
|
s.conf.LeaseDuration = uint32(s.conf.leaseTime.Seconds())
|
||||||
} else {
|
} else {
|
||||||
s.conf.leaseTime = time.Second * time.Duration(conf.LeaseDuration)
|
s.conf.leaseTime = time.Second * time.Duration(conf.LeaseDuration)
|
||||||
|
|
Loading…
Add table
Reference in a new issue