summaryrefslogtreecommitdiffhomepage
path: root/dhcpv4/bsdp/option_vendor_specific_information.go
blob: 99c72d1ffaf78c5e9cfa8398c9b98b6a234ac9f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package bsdp

import (
	"errors"
	"fmt"
	"strings"

	"github.com/insomniacslk/dhcp/dhcpv4"
)

// OptVendorSpecificInformation encapsulates the BSDP-specific options used for
// the protocol.
type OptVendorSpecificInformation struct {
	Options []dhcpv4.Option
}

// parseOption is similar to dhcpv4.ParseOption, except that it switches based
// on the BSDP specific options.
func parseOption(data []byte) (dhcpv4.Option, error) {
	if len(data) == 0 {
		return nil, dhcpv4.ErrZeroLengthByteStream
	}
	var (
		opt dhcpv4.Option
		err error
	)
	switch dhcpv4.OptionCode(data[0]) {
	case OptionBootImageList:
		opt, err = ParseOptBootImageList(data)
	case OptionDefaultBootImageID:
		opt, err = ParseOptDefaultBootImageID(data)
	case OptionMachineName:
		opt, err = ParseOptMachineName(data)
	case OptionMessageType:
		opt, err = ParseOptMessageType(data)
	case OptionReplyPort:
		opt, err = ParseOptReplyPort(data)
	case OptionSelectedBootImageID:
		opt, err = ParseOptSelectedBootImageID(data)
	case OptionServerIdentifier:
		opt, err = ParseOptServerIdentifier(data)
	case OptionServerPriority:
		opt, err = ParseOptServerPriority(data)
	case OptionVersion:
		opt, err = ParseOptVersion(data)
	default:
		opt, err = ParseOptGeneric(data)
	}
	if err != nil {
		return nil, err
	}
	return opt, nil
}

// ParseOptVendorSpecificInformation constructs an OptVendorSpecificInformation struct from a sequence of
// bytes and returns it, or an error.
func ParseOptVendorSpecificInformation(data []byte) (*OptVendorSpecificInformation, error) {
	// Should at least have code + length
	if len(data) < 2 {
		return nil, dhcpv4.ErrShortByteStream
	}
	code := dhcpv4.OptionCode(data[0])
	if code != dhcpv4.OptionVendorSpecificInformation {
		return nil, fmt.Errorf("expected option %v, got %v instead", dhcpv4.OptionVendorSpecificInformation, code)
	}
	length := int(data[1])
	if len(data) < length+2 {
		return nil, fmt.Errorf("expected length 2, got %d instead", length)
	}

	options := make([]dhcpv4.Option, 0, 10)
	idx := 2
	for {
		if idx == len(data) {
			break
		}
		// This should never happen.
		if idx > len(data) {
			return nil, errors.New("read past the end of options")
		}
		opt, err := parseOption(data[idx:])
		if err != nil {
			return nil, err
		}
		options = append(options, opt)

		// Account for code + length bytes
		idx += 2 + opt.Length()
	}

	return &OptVendorSpecificInformation{options}, nil
}

// Code returns the option code.
func (o *OptVendorSpecificInformation) Code() dhcpv4.OptionCode {
	return dhcpv4.OptionVendorSpecificInformation
}

// ToBytes returns a serialized stream of bytes for this option.
func (o *OptVendorSpecificInformation) ToBytes() []byte {
	bs := []byte{byte(o.Code()), byte(o.Length())}

	// Append data section
	for _, opt := range o.Options {
		bs = append(bs, opt.ToBytes()...)
	}
	return bs
}

// String returns a human-readable string for this option.
func (o *OptVendorSpecificInformation) String() string {
	s := "Vendor Specific Information ->"
	for _, opt := range o.Options {
		optString := opt.String()
		// If this option has sub-structures, offset them accordingly.
		if strings.Contains(optString, "\n") {
			optString = strings.Replace(optString, "\n  ", "\n    ", -1)
		}
		s += "\n  " + optString
	}
	return s
}

// Length returns the length of the data portion of this option. Take into
// account code + data length bytes for each sub option.
func (o *OptVendorSpecificInformation) Length() int {
	var length int
	for _, opt := range o.Options {
		length += 2 + opt.Length()
	}
	return length
}

// GetOptions returns all suboptions that match the given OptionCode code.
func (o *OptVendorSpecificInformation) GetOptions(code dhcpv4.OptionCode) []dhcpv4.Option {
	var opts []dhcpv4.Option
	for _, opt := range o.Options {
		if opt.Code() == code {
			opts = append(opts, opt)
		}
	}
	return opts
}

// GetOption returns the first suboption that matches the OptionCode code.
func (o *OptVendorSpecificInformation) GetOption(code dhcpv4.OptionCode) dhcpv4.Option {
	opts := o.GetOptions(code)
	if len(opts) == 0 {
		return nil
	}
	return opts[0]
}