summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChristopher Koch <c@chrisko.ch>2019-01-20 04:05:36 +0000
committerinsomniac <insomniacslk@users.noreply.github.com>2019-01-26 23:34:26 +0000
commit8936b6e4e714e0b682e37fa81fde29606e58e7c2 (patch)
tree25aa6c19674258c391654e52af3dc7db1ba12f7b
parent6a9ec900f656439652b2f20e34b2452d561eb0a6 (diff)
dhcpv6: introduce options type.
-rw-r--r--dhcpv6/dhcpv6.go41
-rw-r--r--dhcpv6/dhcpv6message.go18
-rw-r--r--dhcpv6/dhcpv6relay.go19
-rw-r--r--dhcpv6/option_iaaddress.go6
-rw-r--r--dhcpv6/option_iaprefix.go10
-rw-r--r--dhcpv6/option_nontemporaryaddress.go12
-rw-r--r--dhcpv6/option_nontemporaryaddress_test.go4
-rw-r--r--dhcpv6/option_prefixdelegation.go10
-rw-r--r--dhcpv6/option_relaymsg_test.go3
-rw-r--r--dhcpv6/option_vendor_opts.go6
-rw-r--r--dhcpv6/options.go79
11 files changed, 102 insertions, 106 deletions
diff --git a/dhcpv6/dhcpv6.go b/dhcpv6/dhcpv6.go
index 9382334..c9f8c05 100644
--- a/dhcpv6/dhcpv6.go
+++ b/dhcpv6/dhcpv6.go
@@ -57,12 +57,10 @@ func FromBytes(data []byte) (DHCPv6, error) {
d.linkAddr = linkAddr
peerAddr = append(peerAddr, data[18:34]...)
d.peerAddr = peerAddr
- options, err := OptionsFromBytes(data[34:])
- if err != nil {
+ // TODO fail if no OptRelayMessage is present
+ if err := d.options.FromBytes(data[34:]); err != nil {
return nil, err
}
- // TODO fail if no OptRelayMessage is present
- d.options = options
return &d, nil
} else {
tid, err := BytesToTransactionID(data[1:4])
@@ -73,11 +71,9 @@ func FromBytes(data []byte) (DHCPv6, error) {
messageType: messageType,
transactionID: *tid,
}
- options, err := OptionsFromBytes(data[4:])
- if err != nil {
+ if err := d.options.FromBytes(data[4:]); err != nil {
return nil, err
}
- d.options = options
return &d, nil
}
}
@@ -100,37 +96,6 @@ func NewMessage(modifiers ...Modifier) (DHCPv6, error) {
return d, nil
}
-func getOptions(options []Option, code OptionCode, onlyFirst bool) []Option {
- var ret []Option
- for _, opt := range options {
- if opt.Code() == code {
- ret = append(ret, opt)
- if onlyFirst {
- break
- }
- }
- }
- return ret
-}
-
-func getOption(options []Option, code OptionCode) Option {
- opts := getOptions(options, code, true)
- if opts == nil {
- return nil
- }
- return opts[0]
-}
-
-func delOption(options []Option, code OptionCode) []Option {
- newOpts := make([]Option, 0, len(options))
- for _, opt := range options {
- if opt.Code() != code {
- newOpts = append(newOpts, opt)
- }
- }
- return newOpts
-}
-
// DecapsulateRelay extracts the content of a relay message. It does not recurse
// if there are nested relay messages. Returns the original packet if is not not
// a relay message
diff --git a/dhcpv6/dhcpv6message.go b/dhcpv6/dhcpv6message.go
index a95d9ce..abccf43 100644
--- a/dhcpv6/dhcpv6message.go
+++ b/dhcpv6/dhcpv6message.go
@@ -17,7 +17,7 @@ const MessageHeaderSize = 4
type DHCPv6Message struct {
messageType MessageType
transactionID uint32 // only 24 bits are used though
- options []Option
+ options Options
}
func BytesToTransactionID(data []byte) (*uint32, error) {
@@ -277,21 +277,13 @@ func (d *DHCPv6Message) SetOptions(options []Option) {
}
func (d *DHCPv6Message) AddOption(option Option) {
- d.options = append(d.options, option)
+ d.options.Add(option)
}
// UpdateOption updates the existing options with the passed option, adding it
// at the end if not present already
func (d *DHCPv6Message) UpdateOption(option Option) {
- for idx, opt := range d.options {
- if opt.Code() == option.Code() {
- d.options[idx] = option
- // don't look further
- return
- }
- }
- // if not found, add it
- d.AddOption(option)
+ d.options.Update(option)
}
// IsNetboot returns true if the machine is trying to netboot. It checks if
@@ -373,11 +365,11 @@ func (d *DHCPv6Message) Options() []Option {
}
func (d *DHCPv6Message) GetOption(code OptionCode) []Option {
- return getOptions(d.options, code, false)
+ return d.options.Get(code)
}
func (d *DHCPv6Message) GetOneOption(code OptionCode) Option {
- return getOption(d.options, code)
+ return d.options.GetOne(code)
}
func (d *DHCPv6Message) IsRelay() bool {
diff --git a/dhcpv6/dhcpv6relay.go b/dhcpv6/dhcpv6relay.go
index 220fd3d..b51c139 100644
--- a/dhcpv6/dhcpv6relay.go
+++ b/dhcpv6/dhcpv6relay.go
@@ -13,7 +13,7 @@ type DHCPv6Relay struct {
hopCount uint8
linkAddr net.IP
peerAddr net.IP
- options []Option
+ options Options
}
func (r *DHCPv6Relay) Type() MessageType {
@@ -102,12 +102,13 @@ func (r *DHCPv6Relay) Length() int {
func (r *DHCPv6Relay) Options() []Option {
return r.options
}
+
func (r *DHCPv6Relay) GetOption(code OptionCode) []Option {
- return getOptions(r.options, code, false)
+ return r.options.Get(code)
}
func (r *DHCPv6Relay) GetOneOption(code OptionCode) Option {
- return getOption(r.options, code)
+ return r.options.GetOne(code)
}
func (r *DHCPv6Relay) SetOptions(options []Option) {
@@ -115,20 +116,12 @@ func (r *DHCPv6Relay) SetOptions(options []Option) {
}
func (r *DHCPv6Relay) AddOption(option Option) {
- r.options = append(r.options, option)
+ r.options.Add(option)
}
// UpdateOption replaces the first option of the same type as the specified one.
func (r *DHCPv6Relay) UpdateOption(option Option) {
- for idx, opt := range r.options {
- if opt.Code() == option.Code() {
- r.options[idx] = option
- // don't look further
- return
- }
- }
- // if not found, add it
- r.AddOption(option)
+ r.options.Update(option)
}
func (r *DHCPv6Relay) IsRelay() bool {
diff --git a/dhcpv6/option_iaaddress.go b/dhcpv6/option_iaaddress.go
index 6f430db..811cbbe 100644
--- a/dhcpv6/option_iaaddress.go
+++ b/dhcpv6/option_iaaddress.go
@@ -14,7 +14,7 @@ type OptIAAddress struct {
IPv6Addr net.IP
PreferredLifetime uint32
ValidLifetime uint32
- Options []Option
+ Options Options
}
// Code returns the option's code
@@ -54,7 +54,6 @@ func (op *OptIAAddress) String() string {
// of bytes. The input data does not include option code and length
// bytes.
func ParseOptIAAddress(data []byte) (*OptIAAddress, error) {
- var err error
opt := OptIAAddress{}
if len(data) < 24 {
return nil, fmt.Errorf("Invalid IA Address data length. Expected at least 24 bytes, got %v", len(data))
@@ -62,8 +61,7 @@ func ParseOptIAAddress(data []byte) (*OptIAAddress, error) {
opt.IPv6Addr = net.IP(data[:16])
opt.PreferredLifetime = binary.BigEndian.Uint32(data[16:20])
opt.ValidLifetime = binary.BigEndian.Uint32(data[20:24])
- opt.Options, err = OptionsFromBytes(data[24:])
- if err != nil {
+ if err := opt.Options.FromBytes(data[24:]); err != nil {
return nil, err
}
return &opt, nil
diff --git a/dhcpv6/option_iaprefix.go b/dhcpv6/option_iaprefix.go
index 8a39531..2f71fb0 100644
--- a/dhcpv6/option_iaprefix.go
+++ b/dhcpv6/option_iaprefix.go
@@ -14,7 +14,7 @@ type OptIAPrefix struct {
ValidLifetime uint32
prefixLength byte
ipv6Prefix net.IP
- Options []Option
+ Options Options
}
func (op *OptIAPrefix) Code() OptionCode {
@@ -70,18 +70,17 @@ func (op *OptIAPrefix) String() string {
// 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 getOption(op.Options, code)
+ return op.Options.GetOne(code)
}
// DelOption will remove all the options that match a Option code.
func (op *OptIAPrefix) DelOption(code OptionCode) {
- op.Options = delOption(op.Options, code)
+ op.Options.Del(code)
}
// build an OptIAPrefix structure from a sequence of bytes.
// The input data does not include option code and length bytes.
func ParseOptIAPrefix(data []byte) (*OptIAPrefix, error) {
- var err error
opt := OptIAPrefix{}
if len(data) < 25 {
return nil, fmt.Errorf("Invalid IA for Prefix Delegation data length. Expected at least 25 bytes, got %v", len(data))
@@ -90,8 +89,7 @@ func ParseOptIAPrefix(data []byte) (*OptIAPrefix, error) {
opt.ValidLifetime = binary.BigEndian.Uint32(data[4:8])
opt.prefixLength = data[8]
opt.ipv6Prefix = net.IP(data[9:25])
- opt.Options, err = OptionsFromBytes(data[25:])
- if err != nil {
+ if err := opt.Options.FromBytes(data[25:]); err != nil {
return nil, err
}
return &opt, nil
diff --git a/dhcpv6/option_nontemporaryaddress.go b/dhcpv6/option_nontemporaryaddress.go
index ec09937..94f79c0 100644
--- a/dhcpv6/option_nontemporaryaddress.go
+++ b/dhcpv6/option_nontemporaryaddress.go
@@ -12,7 +12,7 @@ type OptIANA struct {
IaId [4]byte
T1 uint32
T2 uint32
- Options []Option
+ Options Options
}
func (op *OptIANA) Code() OptionCode {
@@ -47,24 +47,23 @@ func (op *OptIANA) String() string {
// AddOption adds an option at the end of the IA_NA options
func (op *OptIANA) AddOption(opt Option) {
- op.Options = append(op.Options, opt)
+ op.Options.Add(opt)
}
// GetOneOption will get an option of the give type from the Options field, if
// it is present. It will return `nil` otherwise
func (op *OptIANA) GetOneOption(code OptionCode) Option {
- return getOption(op.Options, code)
+ return op.Options.GetOne(code)
}
// DelOption will remove all the options that match a Option code.
func (op *OptIANA) DelOption(code OptionCode) {
- op.Options = delOption(op.Options, code)
+ op.Options.Del(code)
}
// build an OptIANA structure from a sequence of bytes.
// The input data does not include option code and length bytes.
func ParseOptIANA(data []byte) (*OptIANA, error) {
- var err error
opt := OptIANA{}
if len(data) < 12 {
return nil, fmt.Errorf("Invalid IA for Non-temporary Addresses data length. Expected at least 12 bytes, got %v", len(data))
@@ -72,8 +71,7 @@ func ParseOptIANA(data []byte) (*OptIANA, error) {
copy(opt.IaId[:], data[:4])
opt.T1 = binary.BigEndian.Uint32(data[4:8])
opt.T2 = binary.BigEndian.Uint32(data[8:12])
- opt.Options, err = OptionsFromBytes(data[12:])
- if err != nil {
+ if err := opt.Options.FromBytes(data[12:]); err != nil {
return nil, err
}
return &opt, nil
diff --git a/dhcpv6/option_nontemporaryaddress_test.go b/dhcpv6/option_nontemporaryaddress_test.go
index a03e0c9..a42601d 100644
--- a/dhcpv6/option_nontemporaryaddress_test.go
+++ b/dhcpv6/option_nontemporaryaddress_test.go
@@ -78,13 +78,13 @@ func TestOptIANADelOption(t *testing.T) {
optiana1.Options = append(optiana1.Options, &optiaaddr)
optiana1.Options = append(optiana1.Options, &optiaaddr)
optiana1.DelOption(OptionIAAddr)
- require.Equal(t, optiana1.Options, []Option{&optsc})
+ require.Equal(t, optiana1.Options, Options{&optsc})
optiana2.Options = append(optiana2.Options, &optiaaddr)
optiana2.Options = append(optiana2.Options, &optsc)
optiana2.Options = append(optiana2.Options, &optiaaddr)
optiana2.DelOption(OptionIAAddr)
- require.Equal(t, optiana2.Options, []Option{&optsc})
+ require.Equal(t, optiana2.Options, Options{&optsc})
}
func TestOptIANAToBytes(t *testing.T) {
diff --git a/dhcpv6/option_prefixdelegation.go b/dhcpv6/option_prefixdelegation.go
index 87c027e..da5dff6 100644
--- a/dhcpv6/option_prefixdelegation.go
+++ b/dhcpv6/option_prefixdelegation.go
@@ -12,7 +12,7 @@ type OptIAForPrefixDelegation struct {
IaId [4]byte
T1 uint32
T2 uint32
- Options []Option
+ Options Options
}
// Code returns the option code
@@ -52,18 +52,17 @@ func (op *OptIAForPrefixDelegation) String() string {
// 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)
+ return op.Options.GetOne(code)
}
// DelOption will remove all the options that match a Option code.
func (op *OptIAForPrefixDelegation) DelOption(code OptionCode) {
- op.Options = delOption(op.Options, code)
+ op.Options.Del(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))
@@ -71,8 +70,7 @@ 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, err = OptionsFromBytes(data[12:])
- if err != nil {
+ if err := opt.Options.FromBytes(data[12:]); err != nil {
return nil, err
}
return &opt, nil
diff --git a/dhcpv6/option_relaymsg_test.go b/dhcpv6/option_relaymsg_test.go
index 15d869a..3e0deaa 100644
--- a/dhcpv6/option_relaymsg_test.go
+++ b/dhcpv6/option_relaymsg_test.go
@@ -26,7 +26,8 @@ func TestRelayMsgParseOptRelayMsg(t *testing.T) {
}
func TestRelayMsgOptionsFromBytes(t *testing.T) {
- opts, err := OptionsFromBytes([]byte{
+ var opts Options
+ err := opts.FromBytes([]byte{
0, 9, // option: relay message
0, 10, // relayed message length
1, // MessageTypeSolicit
diff --git a/dhcpv6/option_vendor_opts.go b/dhcpv6/option_vendor_opts.go
index de35141..f791ddf 100644
--- a/dhcpv6/option_vendor_opts.go
+++ b/dhcpv6/option_vendor_opts.go
@@ -36,7 +36,7 @@ import (
// OptVendorOpts represents a DHCPv6 Status Code option
type OptVendorOpts struct {
EnterpriseNumber uint32
- VendorOpts []Option
+ VendorOpts Options
}
// Code returns the option code
@@ -81,9 +81,7 @@ func ParseOptVendorOpts(data []byte) (*OptVendorOpts, error) {
}
opt.EnterpriseNumber = binary.BigEndian.Uint32(data[:4])
- var err error
- opt.VendorOpts, err = OptionsFromBytesWithParser(data[4:], vendParseOption)
- if err != nil {
+ if err := opt.VendorOpts.FromBytesWithParser(data[4:], vendParseOption); err != nil {
return nil, err
}
return &opt, nil
diff --git a/dhcpv6/options.go b/dhcpv6/options.go
index 8508b08..33f0d20 100644
--- a/dhcpv6/options.go
+++ b/dhcpv6/options.go
@@ -121,26 +121,81 @@ func ParseOption(dataStart []byte) (Option, error) {
return opt, nil
}
-func OptionsFromBytes(data []byte) ([]Option, error) {
- return OptionsFromBytesWithParser(data, ParseOption)
+// Options is a collection of options.
+type Options []Option
+
+// Get returns all options matching the option code.
+func (o Options) Get(code OptionCode) []Option {
+ var ret []Option
+ for _, opt := range o {
+ if opt.Code() == code {
+ ret = append(ret, opt)
+ }
+ }
+ return ret
+}
+
+// GetOne returns the first option matching the option code.
+func (o Options) GetOne(code OptionCode) Option {
+ for _, opt := range o {
+ if opt.Code() == code {
+ return opt
+ }
+ }
+ return nil
+}
+
+// Add appends one option.
+func (o *Options) Add(option Option) {
+ *o = append(*o, option)
+}
+
+// Del deletes all options matching the option code.
+func (o *Options) Del(code OptionCode) {
+ newOpts := make(Options, 0, len(*o))
+ for _, opt := range *o {
+ if opt.Code() != code {
+ newOpts = append(newOpts, opt)
+ }
+ }
+ *o = newOpts
+}
+
+// Update replaces the first option of the same type as the specified one.
+func (o *Options) Update(option Option) {
+ for idx, opt := range *o {
+ if opt.Code() == option.Code() {
+ (*o)[idx] = option
+ // don't look further
+ return
+ }
+ }
+ // if not found, add it
+ o.Add(option)
+}
+
+// FromBytes reads data into o and returns an error if the options are not a
+// valid serialized representation of DHCPv6 options per RFC 3315.
+func (o *Options) FromBytes(data []byte) error {
+ return o.FromBytesWithParser(data, ParseOption)
}
// OptionParser is a function signature for option parsing
type OptionParser func(data []byte) (Option, error)
-// OptionsFromBytesWithParser parses Options from byte sequences using the
-// parsing function that is passed in as a paremeter
-func OptionsFromBytesWithParser(data []byte, parser OptionParser) ([]Option, error) {
+// FromBytesWithParser parses Options from byte sequences using the parsing
+// function that is passed in as a paremeter
+func (o *Options) FromBytesWithParser(data []byte, parser OptionParser) error {
// Parse a sequence of bytes until the end and build a list of options from
// it. Returns an error if any invalid option or length is found.
- options := make([]Option, 0, 10)
+ *o = make(Options, 0, 10)
if len(data) == 0 {
// no options, no party
- return options, nil
+ return nil
}
if len(data) < 4 {
// cannot be shorter than option code (2 bytes) + length (2 bytes)
- return nil, fmt.Errorf("Invalid options: shorter than 4 bytes")
+ return fmt.Errorf("Invalid options: shorter than 4 bytes")
}
idx := 0
for {
@@ -149,14 +204,14 @@ func OptionsFromBytesWithParser(data []byte, parser OptionParser) ([]Option, err
}
if idx > len(data) {
// this should never happen
- return nil, fmt.Errorf("Error: reading past the end of options")
+ return fmt.Errorf("Error: reading past the end of options")
}
opt, err := parser(data[idx:])
if err != nil {
- return nil, err
+ return err
}
- options = append(options, opt)
+ *o = append(*o, opt)
idx += opt.Length() + 4 // 4 bytes for type + length
}
- return options, nil
+ return nil
}