+ dhcpv6 server; support static leases

This commit is contained in:
Simon Zolin
2020-04-27 10:46:11 +03:00
parent 08b033dd04
commit 8aa30a8e83
6 changed files with 193 additions and 4 deletions

View File

@@ -46,6 +46,7 @@ func (s *Server) dbLoad() {
s.IPpool = make(map[[4]byte]net.HardwareAddr)
dynLeases := []*Lease{}
staticLeases := []*Lease{}
v6StaticLeases := []*Lease{}
data, err := ioutil.ReadFile(s.conf.DBFilePath)
if err != nil {
@@ -66,7 +67,13 @@ func (s *Server) dbLoad() {
for i := range obj {
obj[i].IP = normalizeIP(obj[i].IP)
if !(len(obj[i].IP) == 4 || len(obj[i].IP) == 16) {
log.Info("DHCP: invalid IP: %s", obj[i].IP)
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)
@@ -80,7 +87,10 @@ func (s *Server) dbLoad() {
Expiry: time.Unix(obj[i].Expiry, 0),
}
if obj[i].Expiry == leaseExpireStatic {
if len(obj[i].IP) == 16 {
v6StaticLeases = append(v6StaticLeases, &lease)
} else if obj[i].Expiry == leaseExpireStatic {
staticLeases = append(staticLeases, &lease)
} else {
dynLeases = append(dynLeases, &lease)
@@ -93,7 +103,10 @@ func (s *Server) dbLoad() {
s.reserveIP(lease.IP, lease.HWAddr)
}
log.Info("DHCP: loaded %d (%d) leases from DB", len(s.leases), numLeases)
s.v6Leases = normalizeLeases(v6StaticLeases, []*Lease{})
log.Info("DHCP: loaded leases v4:%d v6:%d total-read:%d from DB",
len(s.leases), len(s.v6Leases), numLeases)
}
// Skip duplicate leases
@@ -140,6 +153,19 @@ func (s *Server) dbStore() {
leases = append(leases, lease)
}
for _, l := range s.v6Leases {
if l.Expiry.Unix() == 0 {
continue
}
lease := leaseJSON{
HWAddr: l.HWAddr,
IP: l.IP,
Hostname: l.Hostname,
Expiry: l.Expiry.Unix(),
}
leases = append(leases, lease)
}
data, err := json.Marshal(leases)
if err != nil {
log.Error("json.Marshal: %v", err)

View File

@@ -246,13 +246,38 @@ func (s *Server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request
return
}
ip, _ := parseIPv4(lj.IP)
ip := net.ParseIP(lj.IP)
if ip != nil && ip.To16() != nil {
mac, err := net.ParseMAC(lj.HWAddr)
if err != nil {
httpError(r, w, http.StatusBadRequest, "invalid MAC")
return
}
lease := Lease{
IP: ip,
HWAddr: mac,
}
err = s.v6AddStaticLease(lease)
if err != nil {
httpError(r, w, http.StatusBadRequest, "%s", err)
return
}
return
}
ip, _ = parseIPv4(lj.IP)
if ip == nil {
httpError(r, w, http.StatusBadRequest, "invalid IP")
return
}
mac, _ := net.ParseMAC(lj.HWAddr)
mac, err := net.ParseMAC(lj.HWAddr)
if err != nil {
httpError(r, w, http.StatusBadRequest, "invalid MAC")
return
}
lease := Lease{
IP: ip,

View File

@@ -47,6 +47,8 @@ type ServerConfig struct {
// 0: disable
ICMPTimeout uint32 `json:"icmp_timeout_msec" yaml:"icmp_timeout_msec"`
EnableV6 bool `yaml:"enable_v6"`
WorkDir string `json:"-" yaml:"-"`
DBFilePath string `json:"-" yaml:"-"` // path to DB file
@@ -89,6 +91,9 @@ type Server struct {
// IP address pool -- if entry is in the pool, then it's attached to a lease
IPpool map[[4]byte]net.HardwareAddr
v6Leases []*Lease
v6LeasesLock sync.RWMutex
conf ServerConfig
// Called when the leases DB is modified
@@ -255,6 +260,13 @@ func (s *Server) Start() error {
s.cond.Signal()
}()
if s.conf.EnableV6 {
err := s.v6Start()
if err != nil {
return err
}
}
return nil
}

119
dhcpd/v6.go Normal file
View File

@@ -0,0 +1,119 @@
package dhcpd
import (
"bytes"
"fmt"
"net"
"time"
"github.com/AdguardTeam/golibs/log"
"github.com/insomniacslk/dhcp/dhcpv6"
"github.com/insomniacslk/dhcp/dhcpv6/server6"
)
const valIAID = "ADGH"
func (s *Server) v6AddStaticLease(l Lease) error {
l.Expiry = time.Unix(leaseExpireStatic, 0)
s.v6LeasesLock.Lock()
s.v6Leases = append(s.v6Leases, &l)
s.dbStore()
s.v6LeasesLock.Unlock()
// s.notify(LeaseChangedAddedStatic)
return nil
}
func (s *Server) v6FindLease(mac net.HardwareAddr) *Lease {
s.v6LeasesLock.Lock()
defer s.v6LeasesLock.Unlock()
for i := range s.v6Leases {
if bytes.Equal(mac, s.v6Leases[i].HWAddr) {
return s.v6Leases[i]
}
}
return nil
}
func (s *Server) v6Process(req dhcpv6.DHCPv6, resp dhcpv6.DHCPv6) {
mac, err := dhcpv6.ExtractMAC(req)
if err != nil {
log.Debug("DHCPv6: dhcpv6.ExtractMAC: %s", err)
return
}
lease := s.v6FindLease(mac)
if lease == nil {
log.Debug("DHCPv6: no lease for: %s", mac)
return
}
oia := &dhcpv6.OptIANA{}
copy(oia.IaId[:], []byte(valIAID))
oia.Options = dhcpv6.IdentityOptions{Options: []dhcpv6.Option{
&dhcpv6.OptIAAddress{
IPv6Addr: lease.IP,
PreferredLifetime: s.leaseTime,
ValidLifetime: s.leaseTime,
},
}}
resp.AddOption(oia)
}
func (s *Server) v6PacketHandler(conn net.PacketConn, peer net.Addr, req dhcpv6.DHCPv6) {
msg, err := req.GetInnerMessage()
if err != nil {
log.Error("DHCPv6: %s", err)
return
}
var resp dhcpv6.DHCPv6
switch msg.Type() {
case dhcpv6.MessageTypeSolicit:
if msg.GetOneOption(dhcpv6.OptionRapidCommit) != nil {
resp, err = dhcpv6.NewReplyFromMessage(msg)
} else {
resp, err = dhcpv6.NewAdvertiseFromSolicit(msg)
}
case dhcpv6.MessageTypeRequest,
dhcpv6.MessageTypeConfirm,
dhcpv6.MessageTypeRenew,
dhcpv6.MessageTypeRebind,
dhcpv6.MessageTypeRelease,
dhcpv6.MessageTypeInformationRequest:
resp, err = dhcpv6.NewReplyFromMessage(msg)
default:
err = fmt.Errorf("message type %d not supported", msg.Type())
}
if err != nil {
log.Error("DHCPv6: %s", err)
return
}
s.v6Process(req, resp)
_, err = conn.WriteTo(resp.ToBytes(), peer)
if err != nil {
log.Error("DHCPv6: conn.Write to %s failed: %s", peer, err)
return
}
}
func (s *Server) v6Start() error {
laddr := &net.UDPAddr{
IP: net.ParseIP("::1"),
Port: dhcpv6.DefaultServerPort,
}
server, err := server6.NewServer("", laddr, s.v6PacketHandler, server6.WithDebugLogger())
if err != nil {
log.Fatal(err)
}
server.Serve()
return nil
}