Skip to content

Commit

Permalink
Add prefix delegation handling for onmetal plugin
Browse files Browse the repository at this point in the history
Deliver prefix delegation (`/80`) if the client asked for IAPD
  • Loading branch information
damyan committed Sep 13, 2024
1 parent 10f3caf commit 62edaf6
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 3 deletions.
1 change: 1 addition & 0 deletions plugins/httpboot/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func Init6(bootURL string) {
}

/* parametrization */

func TestWrongNumberArgs(t *testing.T) {
_, _, err := parseArgs("foo", "bar")
if err == nil {
Expand Down
38 changes: 36 additions & 2 deletions plugins/onmetal/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ var Plugin = plugins.Plugin{
Setup6: setup6,
}

var mask80 = net.CIDRMask(prefixLength, 128)

const (
preferredLifeTime = 24 * time.Hour
validLifeTime = 24 * time.Hour
prefixLength = 80
)

func setup6(args ...string) (handler.Handler6, error) {
Expand Down Expand Up @@ -60,7 +63,7 @@ func handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
return resp, false
}

resp.AddOption(&dhcpv6.OptIANA{
iana := &dhcpv6.OptIANA{
IaId: m.Options.OneIANA().IaId,
Options: dhcpv6.IdentityOptions{Options: []dhcpv6.Option{
&dhcpv6.OptIAAddress{
Expand All @@ -69,7 +72,38 @@ func handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
ValidLifetime: validLifeTime,
},
}},
})
}
resp.AddOption(iana)
log.Infof("Added option IA prefix %s", iana.String())

optIAPD := m.Options.OneIAPD()
T1 := preferredLifeTime
T2 := validLifeTime

if optIAPD != nil {
if optIAPD.T1 != 0 {
T1 = optIAPD.T1
}
if optIAPD.T2 != 0 {
T2 = optIAPD.T2
}
iapd := &dhcpv6.OptIAPD{
IaId: optIAPD.IaId,
T1: T1,
T2: T2,
Options: dhcpv6.PDOptions{Options: dhcpv6.Options{&dhcpv6.OptIAPrefix{
PreferredLifetime: preferredLifeTime,
ValidLifetime: validLifeTime,
Prefix: &net.IPNet{
Mask: mask80,
IP: ipaddr.Mask(mask80),
},
Options: dhcpv6.PrefixOptions{Options: dhcpv6.Options{}},
}}},
}
resp.UpdateOption(iapd)
log.Infof("Added option IA prefix %s", iapd.String())
}

log.Debugf("Sent DHCPv6 response: %s", resp.Summary())

Expand Down
101 changes: 100 additions & 1 deletion plugins/onmetal/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
var (
expectedIPAddress6 = net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}
expectedIAID = [4]byte{1, 2, 3, 4}
expectedPrefix = "2001:db8:1111:2222:3333::/80"
)

func Init6() {
Expand All @@ -28,7 +29,6 @@ func Init6() {
}

/* parametrization */

func TestWrongNumberArgs(t *testing.T) {
_, err := setup6("not-needed-arg")
if err == nil {
Expand Down Expand Up @@ -156,3 +156,102 @@ func TestNoRelayIPAddressRequested6(t *testing.T) {
}

}
func TestPrefixDelegationRequested6(t *testing.T) {
Init6()

req, err := dhcpv6.NewMessage()
if err != nil {
t.Fatal(err)
}
req.MessageType = dhcpv6.MessageTypeRequest
req.AddOption(&dhcpv6.OptIANA{
IaId: expectedIAID,
})
req.AddOption(&dhcpv6.OptIAPD{
IaId: expectedIAID,
T1: preferredLifeTime,
T2: validLifeTime,
Options: dhcpv6.PDOptions{},
})

relayedRequest, err := dhcpv6.EncapsulateRelay(req, dhcpv6.MessageTypeRelayForward, net.ParseIP("2001:db8:1111:2222:3333:4444:5555:6666"), net.IPv6loopback)
if err != nil {
t.Fatal(err)
}

stub, err := dhcpv6.NewMessage()
if err != nil {
t.Fatal(err)
}
stub.MessageType = dhcpv6.MessageTypeReply

resp, stop := handler6(relayedRequest, stub)
if resp == nil {
t.Fatal("plugin did not return a message")
}
if stop {
t.Error("plugin interrupted processing, but it shouldn't have")
}

opts := resp.GetOption(dhcpv6.OptionIAPD)
if len(opts) != optionEnabled {
t.Fatalf("Expected %d IAPD option, got %d: %v", optionEnabled, len(opts), opts)
}

iapd := resp.(*dhcpv6.Message).Options.OneIAPD()
t1 := iapd.T1
t2 := iapd.T2
pref := iapd.Options.Options[0].(*dhcpv6.OptIAPrefix).Prefix

if t1 != preferredLifeTime {
t.Errorf("Expected T1 %d, got %d", preferredLifeTime, t1)
}

if t2 != validLifeTime {
t.Errorf("Expected T2 %d, got %d", validLifeTime, t2)
}

if iapd.IaId != expectedIAID {
t.Errorf("expected IAID %d, got %d", expectedIAID, iapd.IaId)
}

if pref.String() != expectedPrefix {
t.Errorf("expected prefix %v, got %v", expectedPrefix, pref)
}
}
func TestPrefixDelegationNotRequested6(t *testing.T) {
Init6()

req, err := dhcpv6.NewMessage()
if err != nil {
t.Fatal(err)
}
req.MessageType = dhcpv6.MessageTypeRequest
req.AddOption(&dhcpv6.OptIANA{
IaId: expectedIAID,
})

relayedRequest, err := dhcpv6.EncapsulateRelay(req, dhcpv6.MessageTypeRelayForward, net.IPv6loopback, net.IPv6loopback)
if err != nil {
t.Fatal(err)
}

stub, err := dhcpv6.NewMessage()
if err != nil {
t.Fatal(err)
}
stub.MessageType = dhcpv6.MessageTypeReply

resp, stop := handler6(relayedRequest, stub)
if resp == nil {
t.Fatal("plugin did not return a message")
}
if stop {
t.Error("plugin interrupted processing, but it shouldn't have")
}

opts := resp.GetOption(dhcpv6.OptionIAPD)
if len(opts) != optionDisabled {
t.Fatalf("Expected %d IAPD option, got %d: %v", optionDisabled, len(opts), opts)
}
}

0 comments on commit 62edaf6

Please sign in to comment.