diff options
author | David Barr <38654497+davebarrau@users.noreply.github.com> | 2018-10-07 05:09:58 +1100 |
---|---|---|
committer | insomniac <insomniacslk@users.noreply.github.com> | 2018-10-06 11:09:58 -0700 |
commit | 074d0d2f1a02cafabbaeb618ed2efb896349e81d (patch) | |
tree | 01b929922cafbc592e0a3611a7f2fa1f0ed2e469 | |
parent | 896739d57dea7a47d0bc00831e02d715c5f1a5ba (diff) |
Add support for DHCPv6-PD sub-options (#166)
-rw-r--r-- | dhcpv6/option_prefixdelegation.go | 55 | ||||
-rw-r--r-- | dhcpv6/option_prefixdelegation_test.go | 132 |
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()) +} |