+ 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
}

2
go.mod
View file

@ -9,6 +9,7 @@ require (
github.com/NYTimes/gziphandler v1.1.1
github.com/fsnotify/fsnotify v1.4.7
github.com/gobuffalo/packr v1.30.1
github.com/insomniacslk/dhcp v0.0.0-20200420235442-ed3125c2efe7
github.com/joomcode/errorx v1.0.1
github.com/kardianos/service v1.0.0
github.com/krolaw/dhcp4 v0.0.0-20180925202202-7cead472c414
@ -16,6 +17,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c
github.com/stretchr/testify v1.5.1
github.com/u-root/u-root v6.0.0+incompatible // indirect
go.etcd.io/bbolt v1.3.4
golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e

5
go.sum
View file

@ -49,6 +49,8 @@ github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIavi
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/insomniacslk/dhcp v0.0.0-20200420235442-ed3125c2efe7 h1:iaCm+9nZdYb8XCSU2TfIb0qYTcAlIv2XzyKR2d2xZ38=
github.com/insomniacslk/dhcp v0.0.0-20200420235442-ed3125c2efe7/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
@ -100,6 +102,7 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -107,6 +110,8 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/u-root/u-root v6.0.0+incompatible h1:YqPGmRoRyYmeg17KIWFRSyVq6LX5T6GSzawyA6wG6EE=
github.com/u-root/u-root v6.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=