diff options
author | Christopher Koch <c@chrisko.ch> | 2019-01-20 04:05:36 +0000 |
---|---|---|
committer | insomniac <insomniacslk@users.noreply.github.com> | 2019-01-26 23:34:26 +0000 |
commit | 8936b6e4e714e0b682e37fa81fde29606e58e7c2 (patch) | |
tree | 25aa6c19674258c391654e52af3dc7db1ba12f7b | |
parent | 6a9ec900f656439652b2f20e34b2452d561eb0a6 (diff) |
dhcpv6: introduce options type.
-rw-r--r-- | dhcpv6/dhcpv6.go | 41 | ||||
-rw-r--r-- | dhcpv6/dhcpv6message.go | 18 | ||||
-rw-r--r-- | dhcpv6/dhcpv6relay.go | 19 | ||||
-rw-r--r-- | dhcpv6/option_iaaddress.go | 6 | ||||
-rw-r--r-- | dhcpv6/option_iaprefix.go | 10 | ||||
-rw-r--r-- | dhcpv6/option_nontemporaryaddress.go | 12 | ||||
-rw-r--r-- | dhcpv6/option_nontemporaryaddress_test.go | 4 | ||||
-rw-r--r-- | dhcpv6/option_prefixdelegation.go | 10 | ||||
-rw-r--r-- | dhcpv6/option_relaymsg_test.go | 3 | ||||
-rw-r--r-- | dhcpv6/option_vendor_opts.go | 6 | ||||
-rw-r--r-- | dhcpv6/options.go | 79 |
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 } |