dhcp4d: offer requested address if available
This commit is contained in:
parent
0152ef3601
commit
3561ec3708
@ -2,6 +2,7 @@
|
|||||||
package dhcp4d
|
package dhcp4d
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
@ -62,6 +63,7 @@ func NewHandler(dir string) (*Handler, error) {
|
|||||||
func (h *Handler) findLease() int {
|
func (h *Handler) findLease() int {
|
||||||
if len(h.leasesIP) < h.leaseRange {
|
if len(h.leasesIP) < h.leaseRange {
|
||||||
// Hand out a free lease
|
// Hand out a free lease
|
||||||
|
// TODO: hash the hwaddr like dnsmasq
|
||||||
i := rand.Intn(h.leaseRange)
|
i := rand.Intn(h.leaseRange)
|
||||||
if _, ok := h.leasesIP[i]; !ok {
|
if _, ok := h.leasesIP[i]; !ok {
|
||||||
return i
|
return i
|
||||||
@ -72,13 +74,35 @@ func (h *Handler) findLease() int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Re-use the oldest lease
|
// TODO: expire the oldest lease
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) canLease(reqIP net.IP, hwaddr string) int {
|
||||||
|
if len(reqIP) != 4 || reqIP.Equal(net.IPv4zero) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
leaseNum := dhcp4.IPRange(h.start, reqIP) - 1
|
||||||
|
if leaseNum < 0 || leaseNum >= h.leaseRange {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if l, exists := h.leasesIP[leaseNum]; exists && l.HardwareAddr != hwaddr {
|
||||||
|
return -1 // lease already in use
|
||||||
|
}
|
||||||
|
|
||||||
|
return leaseNum
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: is ServeDHCP always run from the same goroutine, or do we need locking?
|
// TODO: is ServeDHCP always run from the same goroutine, or do we need locking?
|
||||||
func (h *Handler) ServeDHCP(p dhcp4.Packet, msgType dhcp4.MessageType, options dhcp4.Options) dhcp4.Packet {
|
func (h *Handler) ServeDHCP(p dhcp4.Packet, msgType dhcp4.MessageType, options dhcp4.Options) dhcp4.Packet {
|
||||||
log.Printf("got DHCP packet: %+v, msgType: %v, options: %v", p, msgType, options)
|
log.Printf("got DHCP packet: %+v, msgType: %v, options: %v", p, msgType, options)
|
||||||
|
reqIP := net.IP(options[dhcp4.OptionRequestedIPAddress])
|
||||||
|
if reqIP == nil {
|
||||||
|
reqIP = net.IP(p.CIAddr())
|
||||||
|
}
|
||||||
|
|
||||||
switch msgType {
|
switch msgType {
|
||||||
case dhcp4.Discover:
|
case dhcp4.Discover:
|
||||||
// Find previous lease for this HardwareAddr, if any
|
// Find previous lease for this HardwareAddr, if any
|
||||||
@ -86,11 +110,20 @@ func (h *Handler) ServeDHCP(p dhcp4.Packet, msgType dhcp4.MessageType, options d
|
|||||||
// if lease, ok := h.leases[hwAddr]; ok {
|
// if lease, ok := h.leases[hwAddr]; ok {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
free := h.findLease()
|
|
||||||
|
free := -1
|
||||||
|
if !bytes.Equal(reqIP.To4(), net.IPv4zero) {
|
||||||
|
free = h.canLease(reqIP, p.CHAddr().String())
|
||||||
|
log.Printf("canLease: %v", free)
|
||||||
|
}
|
||||||
|
if free == -1 {
|
||||||
|
free = h.findLease()
|
||||||
|
}
|
||||||
if free == -1 {
|
if free == -1 {
|
||||||
log.Printf("Cannot reply with DHCPOFFER: no more leases available")
|
log.Printf("Cannot reply with DHCPOFFER: no more leases available")
|
||||||
return nil // no free leases
|
return nil // no free leases
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("start = %v, free = %v", h.start, free)
|
log.Printf("start = %v, free = %v", h.start, free)
|
||||||
return dhcp4.ReplyPacket(p,
|
return dhcp4.ReplyPacket(p,
|
||||||
dhcp4.Offer,
|
dhcp4.Offer,
|
||||||
@ -104,33 +137,16 @@ func (h *Handler) ServeDHCP(p dhcp4.Packet, msgType dhcp4.MessageType, options d
|
|||||||
return nil // message not for this dhcp server
|
return nil // message not for this dhcp server
|
||||||
}
|
}
|
||||||
nak := dhcp4.ReplyPacket(p, dhcp4.NAK, h.serverIP, nil, 0, nil)
|
nak := dhcp4.ReplyPacket(p, dhcp4.NAK, h.serverIP, nil, 0, nil)
|
||||||
reqIP := net.IP(options[dhcp4.OptionRequestedIPAddress])
|
leaseNum := h.canLease(reqIP, p.CHAddr().String())
|
||||||
if reqIP == nil {
|
if leaseNum == -1 {
|
||||||
reqIP = net.IP(p.CIAddr())
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(reqIP) != 4 || reqIP.Equal(net.IPv4zero) {
|
|
||||||
return nak
|
return nak
|
||||||
}
|
}
|
||||||
leaseNum := dhcp4.IPRange(h.start, reqIP) - 1
|
|
||||||
if leaseNum < 0 || leaseNum >= h.leaseRange {
|
|
||||||
return nak
|
|
||||||
}
|
|
||||||
|
|
||||||
if l, exists := h.leasesIP[leaseNum]; exists && l.HardwareAddr != p.CHAddr().String() {
|
|
||||||
return nak // lease already in use
|
|
||||||
}
|
|
||||||
|
|
||||||
var hostname string
|
|
||||||
if b, ok := options[dhcp4.OptionHostName]; ok {
|
|
||||||
hostname = string(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
lease := &Lease{
|
lease := &Lease{
|
||||||
Addr: reqIP,
|
Addr: reqIP,
|
||||||
HardwareAddr: p.CHAddr().String(),
|
HardwareAddr: p.CHAddr().String(),
|
||||||
Expiry: time.Now().Add(h.leasePeriod),
|
Expiry: time.Now().Add(h.leasePeriod),
|
||||||
Hostname: hostname,
|
Hostname: string(options[dhcp4.OptionHostName]),
|
||||||
}
|
}
|
||||||
h.leasesIP[leaseNum] = lease
|
h.leasesIP[leaseNum] = lease
|
||||||
h.leasesHW[lease.HardwareAddr] = lease
|
h.leasesHW[lease.HardwareAddr] = lease
|
||||||
|
@ -80,3 +80,87 @@ func TestLease(t *testing.T) {
|
|||||||
t.Fatalf("leased callback not called")
|
t.Fatalf("leased callback not called")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPreferredAddress(t *testing.T) {
|
||||||
|
tmpdir, err := ioutil.TempDir("", "dhcp4dtest")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
if err := ioutil.WriteFile(filepath.Join(tmpdir, "interfaces.json"), []byte(goldenInterfaces), 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
addr = net.IP{192, 168, 42, 23}
|
||||||
|
hardwareAddr = net.HardwareAddr{0x11, 0x22, 0x33, 0x44, 0x55, 0x66}
|
||||||
|
hostname = "xps"
|
||||||
|
)
|
||||||
|
handler, err := NewHandler(tmpdir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("no requested IP", func(t *testing.T) {
|
||||||
|
p := dhcp4.RequestPacket(
|
||||||
|
dhcp4.Discover,
|
||||||
|
hardwareAddr, // MAC address
|
||||||
|
net.IPv4zero, // requested IP address
|
||||||
|
[]byte{0xaa, 0xbb, 0xcc, 0xdd}, // transaction ID
|
||||||
|
false, // broadcast,
|
||||||
|
[]dhcp4.Option{
|
||||||
|
{
|
||||||
|
Code: dhcp4.OptionHostName,
|
||||||
|
Value: []byte(hostname),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
resp := handler.ServeDHCP(p, dhcp4.Discover, p.ParseOptions())
|
||||||
|
if got, want := resp.YIAddr().To4(), addr.To4(); bytes.Equal(got, want) {
|
||||||
|
t.Errorf("DHCPOFFER for wrong IP: got %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("requested CIAddr", func(t *testing.T) {
|
||||||
|
p := dhcp4.RequestPacket(
|
||||||
|
dhcp4.Discover,
|
||||||
|
hardwareAddr, // MAC address
|
||||||
|
addr, // requested IP address
|
||||||
|
[]byte{0xaa, 0xbb, 0xcc, 0xdd}, // transaction ID
|
||||||
|
false, // broadcast,
|
||||||
|
[]dhcp4.Option{
|
||||||
|
{
|
||||||
|
Code: dhcp4.OptionHostName,
|
||||||
|
Value: []byte(hostname),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
resp := handler.ServeDHCP(p, dhcp4.Discover, p.ParseOptions())
|
||||||
|
if got, want := resp.YIAddr().To4(), addr.To4(); !bytes.Equal(got, want) {
|
||||||
|
t.Errorf("DHCPOFFER for wrong IP: got %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("requested option", func(t *testing.T) {
|
||||||
|
p := dhcp4.RequestPacket(
|
||||||
|
dhcp4.Discover,
|
||||||
|
hardwareAddr, // MAC address
|
||||||
|
net.IPv4zero, // requested IP address
|
||||||
|
[]byte{0xaa, 0xbb, 0xcc, 0xdd}, // transaction ID
|
||||||
|
false, // broadcast,
|
||||||
|
[]dhcp4.Option{
|
||||||
|
{
|
||||||
|
Code: dhcp4.OptionHostName,
|
||||||
|
Value: []byte(hostname),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Code: dhcp4.OptionRequestedIPAddress,
|
||||||
|
Value: addr,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
resp := handler.ServeDHCP(p, dhcp4.Discover, p.ParseOptions())
|
||||||
|
if got, want := resp.YIAddr().To4(), addr.To4(); !bytes.Equal(got, want) {
|
||||||
|
t.Errorf("DHCPOFFER for wrong IP: got %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user