I have a small embedded DHCP server based on your example. While it works fine with dhclient
- it fails on systemd-networkd
, e.g - the lease can't be obtained. After some tinkering/debugging I realized that the server ID is not set properly. Systemd reports:
It looks like the option bounds are not set properly in the response.
package main
import (
"fmt"
"net"
"reflect"
"testing"
"time"
dhcp "github.com/krolaw/dhcp4"
)
var (
incomingData = []byte{1, 1, 6, 0, 244, 101, 245, 221, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 111, 115, 116, 80, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 130, 83, 99, 53, 1, 1, 61, 19, 255, 254, 220, 71, 216, 0, 2, 0, 0, 171, 17, 138, 52, 26, 55, 116, 247, 151, 249, 55, 8, 1, 3, 12, 15, 6, 33, 121, 42, 57, 2, 2, 64, 12, 6, 100, 101, 118, 98, 111, 120, 255}
outgoingData = []byte{2, 1, 6, 0, 244, 101, 245, 221, 0, 0, 0, 0, 0, 0, 0, 0, 10, 101, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 72, 111, 115, 116, 80, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 130, 83, 99, 53, 1, 2, 54, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 10, 101, 1, 1, 51, 4, 0, 0, 28, 32, 1, 4, 255, 255, 255, 0, 255}
)
func TestOptionValues(t *testing.T) {
test := []struct {
opt dhcp.OptionCode
expected []byte
}{
{
opt: dhcp.OptionSubnetMask,
expected: []byte{255, 255, 255, 0},
},
{
opt: dhcp.OptionServerIdentifier,
expected: []byte{10, 11, 12, 13},
},
}
p := dhcp.NewPacket(2)
for _, opt := range test {
p.AddOption(opt.opt, opt.expected)
}
p.PadToMinSize()
newOpts := p.ParseOptions()
for _, opt := range test {
if target := newOpts[opt.opt]; !reflect.DeepEqual(target, opt.expected) {
t.Fatalf("Option %d mismatch: expected %+v, actual %+v", opt.opt, opt.expected, target)
} else {
fmt.Printf("%+v\n", target)
}
}
}
func TestOutgoingPacketParsing(t *testing.T) {
p := dhcp.Packet(incomingData)
expected := []byte{10, 101, 1, 1}
rteOpts := dhcp.Options{
dhcp.OptionSubnetMask: []byte{255, 255, 255, 0},
}
opts := rteOpts.SelectOrderOrAll(p.ParseOptions()[dhcp.OptionParameterRequestList])
newP := dhcp.ReplyPacket(p, dhcp.Offer, net.IPv4(10, 101, 1, 1), net.IPv4(10, 101, 1, 2), 2*time.Hour, opts)
if !reflect.DeepEqual([]byte(newP), outgoingData) {
t.Fatalf("Packet data mismatch: \n%+v\n%+v\n", newP, outgoingData)
}
serverID := newP.ParseOptions()[dhcp.OptionServerIdentifier]
if !reflect.DeepEqual(serverID, expected) {
t.Fatalf("Expected server IP %+v, actual IP %+v", expected, serverID)
}
}