summaryrefslogtreecommitdiffhomepage
path: root/dhcpv6
diff options
context:
space:
mode:
authorChris Koch <chrisko@google.com>2019-12-28 05:55:41 -0800
committerChris K <c@chrisko.ch>2020-03-11 13:52:51 -0700
commiteed709df9494fb0c994e41d7b8360a2f1b137b6e (patch)
treeedccc9faea9f1a18b73167edf39932714f8e2096 /dhcpv6
parent4d26b5e0b42f27fa41d872b15a7d2bc867787975 (diff)
v6: introduce Prefix options
IAPD has PDOptions which allows IAPrefix and StatusCode. IAPrefix has PrefixOptions which allow StatusCode. Signed-off-by: Chris Koch <chrisko@google.com>
Diffstat (limited to 'dhcpv6')
-rw-r--r--dhcpv6/modifiers.go24
-rw-r--r--dhcpv6/modifiers_test.go6
-rw-r--r--dhcpv6/option_iapd.go37
-rw-r--r--dhcpv6/option_iapd_test.go27
-rw-r--r--dhcpv6/option_iaprefix.go86
-rw-r--r--dhcpv6/option_iaprefix_test.go39
6 files changed, 133 insertions, 86 deletions
diff --git a/dhcpv6/modifiers.go b/dhcpv6/modifiers.go
index 95f49d1..96fbf4b 100644
--- a/dhcpv6/modifiers.go
+++ b/dhcpv6/modifiers.go
@@ -33,7 +33,7 @@ func WithNetboot(d DHCPv6) {
func WithFQDN(flags uint8, domainname string) Modifier {
return func(d DHCPv6) {
d.UpdateOption(&OptFQDN{
- Flags: flags,
+ Flags: flags,
DomainName: &rfc1035label.Labels{
Labels: []string{domainname},
},
@@ -136,19 +136,19 @@ func WithDHCP4oDHCP6Server(addrs ...net.IP) Modifier {
// WithIAPD adds or updates an IAPD option with the provided IAID and
// prefix options to a DHCPv6 packet.
-func WithIAPD(iaid [4]byte, prefixes ...OptIAPrefix) Modifier {
+func WithIAPD(iaid [4]byte, prefixes ...*OptIAPrefix) Modifier {
return func(d DHCPv6) {
- opt := d.GetOneOption(OptionIAPD)
- if opt == nil {
- opt = &OptIAForPrefixDelegation{}
- }
- iaPd := opt.(*OptIAForPrefixDelegation)
-
- copy(iaPd.IaId[:], iaid[:])
+ if msg, ok := d.(*Message); ok {
+ opt := msg.Options.OneIAPD()
+ if opt == nil {
+ opt = &OptIAPD{}
+ }
+ copy(opt.IaId[:], iaid[:])
- for _, prefix := range prefixes {
- iaPd.Options.Add(&prefix)
+ for _, prefix := range prefixes {
+ opt.Options.Add(prefix)
+ }
+ d.UpdateOption(opt)
}
- d.UpdateOption(iaPd)
}
}
diff --git a/dhcpv6/modifiers_test.go b/dhcpv6/modifiers_test.go
index 8d06e75..2354bd0 100644
--- a/dhcpv6/modifiers_test.go
+++ b/dhcpv6/modifiers_test.go
@@ -110,12 +110,12 @@ func TestWithDHCP4oDHCP6Server(t *testing.T) {
func TestWithIAPD(t *testing.T) {
var d Message
- prefix := OptIAPrefix{
+ _, pre, _ := net.ParseCIDR("2001:DB8:7689::/48")
+ prefix := &OptIAPrefix{
PreferredLifetime: 3600,
ValidLifetime: 5200,
+ Prefix: pre,
}
- prefix.SetPrefixLength(48)
- prefix.SetIPv6Prefix(net.ParseIP("2001:DB8:7689::"))
WithIAPD([4]byte{1, 2, 3, 4}, prefix)(&d)
opt := d.Options.IAPD()
require.Equal(t, 1, len(opt))
diff --git a/dhcpv6/option_iapd.go b/dhcpv6/option_iapd.go
index 03f3744..496aa81 100644
--- a/dhcpv6/option_iapd.go
+++ b/dhcpv6/option_iapd.go
@@ -7,13 +7,46 @@ import (
"github.com/u-root/u-root/pkg/uio"
)
+// PDOptions are options used with the IAPD (prefix delegation) option.
+//
+// RFC 3633 describes that IA_PD-options may contain the IAPrefix option and
+// the StatusCode option.
+type PDOptions struct {
+ Options
+}
+
+// Prefixes are the prefixes associated with this delegation.
+func (po PDOptions) Prefixes() []*OptIAPrefix {
+ opts := po.Options.Get(OptionIAPrefix)
+ pre := make([]*OptIAPrefix, 0, len(opts))
+ for _, o := range opts {
+ if iap, ok := o.(*OptIAPrefix); ok {
+ pre = append(pre, iap)
+ }
+ }
+ return pre
+}
+
+// Status returns the status code associated with this option.
+func (po PDOptions) Status() *OptStatusCode {
+ opt := po.Options.GetOne(OptionStatusCode)
+ if opt == nil {
+ return nil
+ }
+ sc, ok := opt.(*OptStatusCode)
+ if !ok {
+ return nil
+ }
+ return sc
+}
+
// OptIAPD implements the identity association for prefix
// delegation option defined by RFC 3633, Section 9.
type OptIAPD struct {
IaId [4]byte
T1 time.Duration
T2 time.Duration
- Options Options
+ Options PDOptions
}
// Code returns the option code
@@ -37,7 +70,7 @@ func (op *OptIAPD) ToBytes() []byte {
// String returns a string representation of the OptIAPD data
func (op *OptIAPD) String() string {
- return fmt.Sprintf("OptIAPD{IAID=%v, t1=%v, t2=%v, options=%v}",
+ return fmt.Sprintf("IAPD: {IAID=%v, t1=%v, t2=%v, Options=[%v]}",
op.IaId, op.T1, op.T2, op.Options)
}
diff --git a/dhcpv6/option_iapd_test.go b/dhcpv6/option_iapd_test.go
index babb5a7..b2b9d3f 100644
--- a/dhcpv6/option_iapd_test.go
+++ b/dhcpv6/option_iapd_test.go
@@ -53,17 +53,20 @@ func TestOptIAPDParseOptIAPDInvalidOptions(t *testing.T) {
}
func TestOptIAPDToBytes(t *testing.T) {
- oaddr := OptIAPrefix{}
- oaddr.PreferredLifetime = 0xaabbccdd * time.Second
- oaddr.ValidLifetime = 0xeeff0011 * time.Second
- oaddr.SetPrefixLength(36)
- oaddr.SetIPv6Prefix(net.IPv6loopback)
-
- opt := OptIAPD{}
- opt.IaId = [4]byte{1, 2, 3, 4}
- opt.T1 = 12345 * time.Second
- opt.T2 = 54321 * time.Second
- opt.Options = append(opt.Options, &oaddr)
+ oaddr := OptIAPrefix{
+ PreferredLifetime: 0xaabbccdd * time.Second,
+ ValidLifetime: 0xeeff0011 * time.Second,
+ Prefix: &net.IPNet{
+ Mask: net.CIDRMask(36, 128),
+ IP: net.IPv6loopback,
+ },
+ }
+ opt := OptIAPD{
+ IaId: [4]byte{1, 2, 3, 4},
+ T1: 12345 * time.Second,
+ T2: 54321 * time.Second,
+ Options: PDOptions{[]Option{&oaddr}},
+ }
expected := []byte{
1, 2, 3, 4, // IA ID
@@ -105,7 +108,7 @@ func TestOptIAPDString(t *testing.T) {
)
require.Contains(
t, str,
- "options=[",
+ "Options=[",
"String() should return a list of options",
)
}
diff --git a/dhcpv6/option_iaprefix.go b/dhcpv6/option_iaprefix.go
index bb907b2..750f0cd 100644
--- a/dhcpv6/option_iaprefix.go
+++ b/dhcpv6/option_iaprefix.go
@@ -8,6 +8,30 @@ import (
"github.com/u-root/u-root/pkg/uio"
)
+// PrefixOptions are the options valid for use with IAPrefix option field.
+//
+// RFC 3633 states that it's just the StatusCode option.
+//
+// RFC 8415 Appendix C does not list the Status Code option as valid, but it
+// does say that the previous text in RFC 8415 Section 21.22 supersedes that
+// table. Section 21.22 does mention the Status Code option.
+type PrefixOptions struct {
+ Options
+}
+
+// Status returns the status code associated with this option.
+func (po PrefixOptions) Status() *OptStatusCode {
+ opt := po.Options.GetOne(OptionStatusCode)
+ if opt == nil {
+ return nil
+ }
+ sc, ok := opt.(*OptStatusCode)
+ if !ok {
+ return nil
+ }
+ return sc
+}
+
// OptIAPrefix implements the IAPrefix option.
//
// This module defines the OptIAPrefix structure.
@@ -15,9 +39,8 @@ import (
type OptIAPrefix struct {
PreferredLifetime time.Duration
ValidLifetime time.Duration
- prefixLength byte
- ipv6Prefix net.IP
- Options Options
+ Prefix *net.IPNet
+ Options PrefixOptions
}
func (op *OptIAPrefix) Code() OptionCode {
@@ -33,44 +56,22 @@ func (op *OptIAPrefix) ToBytes() []byte {
t2 := Duration{op.ValidLifetime}
t2.Marshal(buf)
- buf.Write8(op.prefixLength)
- write16(buf, op.ipv6Prefix)
+ if op.Prefix != nil {
+ // Even if Mask is nil, Size will return 0 without panicking.
+ length, _ := op.Prefix.Mask.Size()
+ buf.Write8(uint8(length))
+ write16(buf, op.Prefix.IP)
+ } else {
+ buf.Write8(0)
+ write16(buf, nil)
+ }
buf.WriteBytes(op.Options.ToBytes())
return buf.Data()
}
-func (op *OptIAPrefix) PrefixLength() byte {
- return op.prefixLength
-}
-
-func (op *OptIAPrefix) SetPrefixLength(pl byte) {
- op.prefixLength = pl
-}
-
-// IPv6Prefix returns the ipv6Prefix
-func (op *OptIAPrefix) IPv6Prefix() net.IP {
- return op.ipv6Prefix
-}
-
-// SetIPv6Prefix sets the ipv6Prefix
-func (op *OptIAPrefix) SetIPv6Prefix(p net.IP) {
- op.ipv6Prefix = p
-}
-
func (op *OptIAPrefix) String() string {
- return fmt.Sprintf("OptIAPrefix{preferredlifetime=%v, validlifetime=%v, prefixlength=%v, ipv6prefix=%v, options=%v}",
- op.PreferredLifetime, op.ValidLifetime, op.PrefixLength(), op.IPv6Prefix(), op.Options)
-}
-
-// GetOneOption will get an option of the give type from the Options field, if
-// it is present. It will return `nil` otherwise
-func (op *OptIAPrefix) GetOneOption(code OptionCode) Option {
- return op.Options.GetOne(code)
-}
-
-// DelOption will remove all the options that match a Option code.
-func (op *OptIAPrefix) DelOption(code OptionCode) {
- op.Options.Del(code)
+ return fmt.Sprintf("IAPrefix: {PreferredLifetime=%v, ValidLifetime=%v, Prefix=%s, Options=%v}",
+ op.PreferredLifetime, op.ValidLifetime, op.Prefix, op.Options)
}
// ParseOptIAPrefix an OptIAPrefix structure from a sequence of bytes. The
@@ -85,8 +86,17 @@ func ParseOptIAPrefix(data []byte) (*OptIAPrefix, error) {
opt.PreferredLifetime = t1.Duration
opt.ValidLifetime = t2.Duration
- opt.prefixLength = buf.Read8()
- opt.ipv6Prefix = net.IP(buf.CopyN(net.IPv6len))
+ length := buf.Read8()
+ ip := net.IP(buf.CopyN(net.IPv6len))
+
+ if length == 0 {
+ opt.Prefix = nil
+ } else {
+ opt.Prefix = &net.IPNet{
+ Mask: net.CIDRMask(int(length), 128),
+ IP: ip,
+ }
+ }
if err := opt.Options.FromBytes(buf.ReadAll()); err != nil {
return nil, err
}
diff --git a/dhcpv6/option_iaprefix_test.go b/dhcpv6/option_iaprefix_test.go
index baa4808..27d0c95 100644
--- a/dhcpv6/option_iaprefix_test.go
+++ b/dhcpv6/option_iaprefix_test.go
@@ -3,6 +3,7 @@ package dhcpv6
import (
"bytes"
"net"
+ "reflect"
"testing"
"time"
@@ -15,23 +16,22 @@ func TestOptIAPrefix(t *testing.T) {
0xee, 0xff, 0x00, 0x11, // validLifetime
36, // prefixLength
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // ipv6Prefix
- 0, 8, 0, 2, 0xaa, 0xbb, // options
}
opt, err := ParseOptIAPrefix(buf)
if err != nil {
t.Fatal(err)
}
- if pl := opt.PreferredLifetime; pl != 0xaabbccdd*time.Second {
- t.Fatalf("Invalid Preferred Lifetime. Expected 0xaabbccdd, got %v", pl)
- }
- if vl := opt.ValidLifetime; vl != 0xeeff0011*time.Second {
- t.Fatalf("Invalid Valid Lifetime. Expected 0xeeff0011, got %v", vl)
- }
- if pr := opt.PrefixLength(); pr != 36 {
- t.Fatalf("Invalid Prefix Length. Expected 36, got %v", pr)
+ want := &OptIAPrefix{
+ PreferredLifetime: 0xaabbccdd * time.Second,
+ ValidLifetime: 0xeeff0011 * time.Second,
+ Prefix: &net.IPNet{
+ Mask: net.CIDRMask(36, 128),
+ IP: net.IPv6loopback,
+ },
+ Options: PrefixOptions{[]Option{}},
}
- if ip := opt.IPv6Prefix(); !ip.Equal(net.IPv6loopback) {
- t.Fatalf("Invalid Prefix Length. Expected %v, got %v", net.IPv6loopback, ip)
+ if !reflect.DeepEqual(want, opt) {
+ t.Errorf("parseIAPrefix = %v, want %v", opt, want)
}
}
@@ -46,10 +46,12 @@ func TestOptIAPrefixToBytes(t *testing.T) {
opt := OptIAPrefix{
PreferredLifetime: 0xaabbccdd * time.Second,
ValidLifetime: 0xeeff0011 * time.Second,
- prefixLength: 36,
- ipv6Prefix: net.IPv6zero,
+ Prefix: &net.IPNet{
+ Mask: net.CIDRMask(36, 128),
+ IP: net.IPv6zero,
+ },
+ Options: PrefixOptions{[]Option{OptElapsedTime(10 * time.Millisecond)}},
}
- opt.Options.Add(OptElapsedTime(10 * time.Millisecond))
toBytes := opt.ToBytes()
if !bytes.Equal(toBytes, buf) {
t.Fatalf("Invalid ToBytes result. Expected %v, got %v", buf, toBytes)
@@ -63,8 +65,7 @@ func TestOptIAPrefixToBytesDefault(t *testing.T) {
0, // prefixLength
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ipv6Prefix
}
- opt := OptIAPrefix{
- }
+ opt := OptIAPrefix{}
toBytes := opt.ToBytes()
if !bytes.Equal(toBytes, buf) {
t.Fatalf("Invalid ToBytes result. Expected %v, got %v", buf, toBytes)
@@ -96,17 +97,17 @@ func TestOptIAPrefixString(t *testing.T) {
str := opt.String()
require.Contains(
t, str,
- "ipv6prefix=2001:db8::",
+ "Prefix=2001:db8::/36",
"String() should return the ipv6addr",
)
require.Contains(
t, str,
- "preferredlifetime=1m",
+ "PreferredLifetime=1m",
"String() should return the preferredlifetime",
)
require.Contains(
t, str,
- "validlifetime=50s",
+ "ValidLifetime=50s",
"String() should return the validlifetime",
)
}