summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Barr <38654497+davebarrau@users.noreply.github.com>2018-10-07 05:09:58 +1100
committerinsomniac <insomniacslk@users.noreply.github.com>2018-10-06 11:09:58 -0700
commit074d0d2f1a02cafabbaeb618ed2efb896349e81d (patch)
tree01b929922cafbc592e0a3611a7f2fa1f0ed2e469
parent896739d57dea7a47d0bc00831e02d715c5f1a5ba (diff)
Add support for DHCPv6-PD sub-options (#166)
-rw-r--r--dhcpv6/option_prefixdelegation.go55
-rw-r--r--dhcpv6/option_prefixdelegation_test.go132
2 files changed, 180 insertions, 7 deletions
diff --git a/dhcpv6/option_prefixdelegation.go b/dhcpv6/option_prefixdelegation.go
index ecc7990..6cc22fd 100644
--- a/dhcpv6/option_prefixdelegation.go
+++ b/dhcpv6/option_prefixdelegation.go
@@ -6,19 +6,22 @@ package dhcpv6
import (
"encoding/binary"
"fmt"
+ "log"
)
type OptIAForPrefixDelegation struct {
iaId [4]byte
t1 uint32
t2 uint32
- options []byte
+ options []Option
}
+// Code returns the option code
func (op *OptIAForPrefixDelegation) Code() OptionCode {
return OptionIAPD
}
+// ToBytes serializes the option and returns it as a sequence of bytes
func (op *OptIAForPrefixDelegation) ToBytes() []byte {
buf := make([]byte, 16)
binary.BigEndian.PutUint16(buf[0:2], uint16(OptionIAPD))
@@ -26,54 +29,89 @@ func (op *OptIAForPrefixDelegation) ToBytes() []byte {
copy(buf[4:8], op.iaId[:])
binary.BigEndian.PutUint32(buf[8:12], op.t1)
binary.BigEndian.PutUint32(buf[12:16], op.t2)
- buf = append(buf, op.options...)
+ for _, opt := range op.options {
+ buf = append(buf, opt.ToBytes()...)
+ }
return buf
}
+// IAID returns the identity association identifier for this option
func (op *OptIAForPrefixDelegation) IAID() []byte {
return op.iaId[:]
}
+// SetIAID sets the identity association identifier for this option
func (op *OptIAForPrefixDelegation) SetIAID(iaId [4]byte) {
op.iaId = iaId
}
+// T1 returns the T1 timer for this option
func (op *OptIAForPrefixDelegation) T1() uint32 {
return op.t1
}
+// SetT1 sets the T1 timer for this option
func (op *OptIAForPrefixDelegation) SetT1(t1 uint32) {
op.t1 = t1
}
+// T2 returns the T2 timer for this option
func (op *OptIAForPrefixDelegation) T2() uint32 {
return op.t2
}
+// SetT2 sets the T2 timer for this option
func (op *OptIAForPrefixDelegation) SetT2(t2 uint32) {
op.t2 = t2
}
+// Options serializes the options and returns them as a sequence of bytes
func (op *OptIAForPrefixDelegation) Options() []byte {
- return op.options
+ log.Printf("Warning: OptIAForPrefixDelegation.Options() is deprecated and will be changed to a public field")
+ buf := op.ToBytes()
+ return buf[16:]
}
-func (op *OptIAForPrefixDelegation) SetOptions(options []byte) {
- op.options = options
+// SetOptions sets the options as a sequence of bytes
+func (op *OptIAForPrefixDelegation) SetOptions(options []byte) error {
+ var err error
+ op.options, err = OptionsFromBytes(options)
+ if err != nil {
+ return err
+ }
+ return nil
}
+// Length returns the option length
func (op *OptIAForPrefixDelegation) Length() int {
- return 12 + len(op.options)
+ l := 12
+ for _, opt := range op.options {
+ l += 4 + opt.Length()
+ }
+ return l
}
+// String returns a string representation of the OptIAForPrefixDelegation data
func (op *OptIAForPrefixDelegation) String() string {
return fmt.Sprintf("OptIAForPrefixDelegation{IAID=%v, t1=%v, t2=%v, options=%v}",
op.iaId, op.t1, op.t2, 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 *OptIAForPrefixDelegation) GetOneOption(code OptionCode) Option {
+ return getOption(op.options, code)
+}
+
+// DelOption will remove all the options that match a Option code.
+func (op *OptIAForPrefixDelegation) DelOption(code OptionCode) {
+ op.options = delOption(op.options, code)
+}
+
// build an OptIAForPrefixDelegation structure from a sequence of bytes.
// The input data does not include option code and length bytes.
func ParseOptIAForPrefixDelegation(data []byte) (*OptIAForPrefixDelegation, error) {
+ var err error
opt := OptIAForPrefixDelegation{}
if len(data) < 12 {
return nil, fmt.Errorf("Invalid IA for Prefix Delegation data length. Expected at least 12 bytes, got %v", len(data))
@@ -81,6 +119,9 @@ func ParseOptIAForPrefixDelegation(data []byte) (*OptIAForPrefixDelegation, erro
copy(opt.iaId[:], data[:4])
opt.t1 = binary.BigEndian.Uint32(data[4:8])
opt.t2 = binary.BigEndian.Uint32(data[8:12])
- opt.options = append(opt.options, data[12:]...)
+ opt.options, err = OptionsFromBytes(data[12:])
+ if err != nil {
+ return nil, err
+ }
return &opt, nil
}
diff --git a/dhcpv6/option_prefixdelegation_test.go b/dhcpv6/option_prefixdelegation_test.go
new file mode 100644
index 0000000..caedd40
--- /dev/null
+++ b/dhcpv6/option_prefixdelegation_test.go
@@ -0,0 +1,132 @@
+package dhcpv6
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestOptIAForPrefixDelegationParseOptIAForPrefixDelegation(t *testing.T) {
+ data := []byte{
+ 1, 0, 0, 0, // IAID
+ 0, 0, 0, 1, // T1
+ 0, 0, 0, 2, // T2
+ 0, 26, 0, 25, // 26 = IAPrefix Option, 25 = length
+ 0xaa, 0xbb, 0xcc, 0xdd, // IAPrefix preferredLifetime
+ 0xee, 0xff, 0x00, 0x11, // IAPrefix validLifetime
+ 36, // IAPrefix prefixLength
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // IAPrefix ipv6Prefix
+ }
+ opt, err := ParseOptIAForPrefixDelegation(data)
+ require.NoError(t, err)
+ require.Equal(t, len(data), opt.Length())
+}
+
+func TestOptIAForPrefixDelegationParseOptIAForPrefixDelegationInvalidLength(t *testing.T) {
+ data := []byte{
+ 1, 0, 0, 0, // IAID
+ 0, 0, 0, 1, // T1
+ // truncated from here
+ }
+ _, err := ParseOptIAForPrefixDelegation(data)
+ require.Error(t, err)
+}
+
+func TestOptIAForPrefixDelegationParseOptIAForPrefixDelegationInvalidOptions(t *testing.T) {
+ data := []byte{
+ 1, 0, 0, 0, // IAID
+ 0, 0, 0, 1, // T1
+ 0, 0, 0, 2, // T2
+ 0, 26, 0, 25, // 26 = IAPrefix Option, 25 = length
+ 0xaa, 0xbb, 0xcc, 0xdd, // IAPrefix preferredLifetime
+ 0xee, 0xff, 0x00, 0x11, // IAPrefix validLifetime
+ 36, // IAPrefix prefixLength
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // IAPrefix ipv6Prefix missing last byte
+ }
+ _, err := ParseOptIAForPrefixDelegation(data)
+ require.Error(t, err)
+}
+
+func TestOptIAForPrefixDelegationGetOneOption(t *testing.T) {
+ buf := []byte{
+ 0xaa, 0xbb, 0xcc, 0xdd, // preferredLifetime
+ 0xee, 0xff, 0x00, 0x11, // validLifetime
+ 36, // prefixLength
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // ipv6Prefix
+ }
+ oaddr, err := ParseOptIAPrefix(buf)
+ if err != nil {
+ t.Fatal(err)
+ }
+ opt := OptIAForPrefixDelegation{}
+ opt.SetOptions(oaddr.ToBytes())
+ require.Equal(t, oaddr, opt.GetOneOption(OptionIAPrefix))
+}
+
+func TestOptIAForPrefixDelegationGetOneOptionMissingOpt(t *testing.T) {
+ buf := []byte{
+ 0xaa, 0xbb, 0xcc, 0xdd, // preferredLifetime
+ 0xee, 0xff, 0x00, 0x11, // validLifetime
+ 36, // prefixLength
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // ipv6Prefix
+ }
+ oaddr, err := ParseOptIAPrefix(buf)
+ if err != nil {
+ t.Fatal(err)
+ }
+ opt := OptIAForPrefixDelegation{}
+ opt.SetOptions(oaddr.ToBytes())
+ require.Equal(t, nil, opt.GetOneOption(OptionDNSRecursiveNameServer))
+}
+
+func TestOptIAForPrefixDelegationDelOption(t *testing.T) {
+ optiana1 := OptIAForPrefixDelegation{}
+ optiana2 := OptIAForPrefixDelegation{}
+ optiaaddr := OptIAPrefix{}
+ optsc := OptStatusCode{}
+
+ var buf = make([]byte, 0)
+
+ buf = append(buf, optsc.ToBytes()...)
+ buf = append(buf, optiaaddr.ToBytes()...)
+ buf = append(buf, optiaaddr.ToBytes()...)
+ optiana1.SetOptions(buf)
+ optiana1.DelOption(OptionIAPrefix)
+ require.Equal(t, optiana1.Options(), optsc.ToBytes())
+
+ buf = make([]byte, 0)
+ buf = append(buf, optiaaddr.ToBytes()...)
+ buf = append(buf, optsc.ToBytes()...)
+ buf = append(buf, optiaaddr.ToBytes()...)
+ optiana2.SetOptions(buf)
+ optiana2.DelOption(OptionIAPrefix)
+ require.Equal(t, optiana2.Options(), optsc.ToBytes())
+}
+
+func TestOptIAForPrefixDelegationToBytes(t *testing.T) {
+ oaddr := OptIAPrefix{}
+ oaddr.SetPreferredLifetime(0xaabbccdd)
+ oaddr.SetValidLifetime(0xeeff0011)
+ oaddr.SetPrefixLength(36)
+ oaddr.SetIPv6Prefix([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})
+
+ opt := OptIAForPrefixDelegation{
+ iaId: [4]byte{1, 2, 3, 4},
+ t1: 12345,
+ t2: 54321,
+ }
+ opt.SetOptions(oaddr.ToBytes())
+ expected := []byte{
+ 0, 25, // OptionIAPD
+ 0, 41, // length
+ 1, 2, 3, 4, // IA ID
+ 0, 0, 0x30, 0x39, // T1 = 12345
+ 0, 0, 0xd4, 0x31, // T2 = 54321
+ 0, 26, 0, 25, // 26 = IAPrefix Option, 25 = length
+ 0xaa, 0xbb, 0xcc, 0xdd, // IAPrefix preferredLifetime
+ 0xee, 0xff, 0x00, 0x11, // IAPrefix validLifetime
+ 36, // IAPrefix prefixLength
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // IAPrefix ipv6Prefix
+ }
+ require.Equal(t, expected, opt.ToBytes())
+}