summaryrefslogtreecommitdiffhomepage
path: root/dhcpv4/bsdp
diff options
context:
space:
mode:
authorChristopher Koch <chrisko@google.com>2018-12-29 14:48:10 -0800
committerinsomniac <insomniacslk@users.noreply.github.com>2019-01-24 08:05:49 +0000
commitc90ab10024ada840e24bb028a3405961e8e4c26a (patch)
tree9b8af0c1b80ee6efc112921f9a14b92d6c73f8eb /dhcpv4/bsdp
parent2be5cae32d33f01ddecf6f167a9c0e5290e6d58f (diff)
dhcpv4: nicer API for option parsing.
From: r := d.GetOneOption(OptionRouter).(*OptRouter).Routers d.UpdateOption(&OptRouter{Routers: []net.IP{net.IP{192, 168, 0, 1}}}) To: r := GetRouter(d.Options) d.UpdateOption(OptRouter(net.IP{192, 168, 0, 1}, ...))
Diffstat (limited to 'dhcpv4/bsdp')
-rw-r--r--dhcpv4/bsdp/boot_image.go51
-rw-r--r--dhcpv4/bsdp/bsdp.go158
-rw-r--r--dhcpv4/bsdp/bsdp_option_boot_image_list.go66
-rw-r--r--dhcpv4/bsdp/bsdp_option_boot_image_list_test.go19
-rw-r--r--dhcpv4/bsdp/bsdp_option_default_boot_image_id.go42
-rw-r--r--dhcpv4/bsdp/bsdp_option_default_boot_image_id_test.go46
-rw-r--r--dhcpv4/bsdp/bsdp_option_generic.go36
-rw-r--r--dhcpv4/bsdp/bsdp_option_generic_test.go57
-rw-r--r--dhcpv4/bsdp/bsdp_option_machine_name.go34
-rw-r--r--dhcpv4/bsdp/bsdp_option_machine_name_test.go26
-rw-r--r--dhcpv4/bsdp/bsdp_option_message_type.go50
-rw-r--r--dhcpv4/bsdp/bsdp_option_message_type_test.go19
-rw-r--r--dhcpv4/bsdp/bsdp_option_misc.go99
-rw-r--r--dhcpv4/bsdp/bsdp_option_misc_test.go95
-rw-r--r--dhcpv4/bsdp/bsdp_option_reply_port.go42
-rw-r--r--dhcpv4/bsdp/bsdp_option_reply_port_test.go36
-rw-r--r--dhcpv4/bsdp/bsdp_option_selected_boot_image_id.go42
-rw-r--r--dhcpv4/bsdp/bsdp_option_selected_boot_image_id_test.go46
-rw-r--r--dhcpv4/bsdp/bsdp_option_server_identifier.go36
-rw-r--r--dhcpv4/bsdp/bsdp_option_server_identifier_test.go33
-rw-r--r--dhcpv4/bsdp/bsdp_option_server_priority.go37
-rw-r--r--dhcpv4/bsdp/bsdp_option_server_priority_test.go30
-rw-r--r--dhcpv4/bsdp/bsdp_option_version.go41
-rw-r--r--dhcpv4/bsdp/bsdp_option_version_test.go29
-rw-r--r--dhcpv4/bsdp/bsdp_test.go152
-rw-r--r--dhcpv4/bsdp/client.go32
-rw-r--r--dhcpv4/bsdp/option_vendor_specific_information.go136
-rw-r--r--dhcpv4/bsdp/option_vendor_specific_information_test.go218
-rw-r--r--dhcpv4/bsdp/types.go6
29 files changed, 613 insertions, 1101 deletions
diff --git a/dhcpv4/bsdp/boot_image.go b/dhcpv4/bsdp/boot_image.go
index 954dcb6..58b5167 100644
--- a/dhcpv4/bsdp/boot_image.go
+++ b/dhcpv4/bsdp/boot_image.go
@@ -3,6 +3,7 @@ package bsdp
import (
"fmt"
+ "github.com/insomniacslk/dhcp/dhcpv4"
"github.com/u-root/u-root/pkg/uio"
)
@@ -18,9 +19,9 @@ const (
// 4 - 127 are reserved for future use.
)
-// BootImageTypeToString maps the different BootImageTypes to human-readable
+// bootImageTypeToString maps the different BootImageTypes to human-readable
// representations.
-var BootImageTypeToString = map[BootImageType]string{
+var bootImageTypeToString = map[BootImageType]string{
BootImageTypeMacOS9: "macOS 9",
BootImageTypeMacOSX: "macOS",
BootImageTypeMacOSXServer: "macOS Server",
@@ -35,6 +36,16 @@ type BootImageID struct {
Index uint16
}
+// ToBytes implements dhcpv4.OptionValue.
+func (b BootImageID) ToBytes() []byte {
+ return uio.ToBigEndian(b)
+}
+
+// FromBytes reads data into b.
+func (b *BootImageID) FromBytes(data []byte) error {
+ return uio.FromBigEndian(b, data)
+}
+
// Marshal writes the binary representation to buf.
func (b BootImageID) Marshal(buf *uio.Lexer) {
var byte0 byte
@@ -55,7 +66,7 @@ func (b BootImageID) String() string {
} else {
s += " uninstallable"
}
- t, ok := BootImageTypeToString[b.ImageType]
+ t, ok := bootImageTypeToString[b.ImageType]
if !ok {
t = "unknown"
}
@@ -99,3 +110,37 @@ func (b *BootImage) Unmarshal(buf *uio.Lexer) error {
b.Name = string(buf.Consume(int(nameLength)))
return buf.Error()
}
+
+func getBootImageID(code dhcpv4.OptionCode, o dhcpv4.Options) *BootImageID {
+ v := o.Get(code)
+ if v == nil {
+ return nil
+ }
+ var b BootImageID
+ if err := uio.FromBigEndian(&b, v); err != nil {
+ return nil
+ }
+ return &b
+}
+
+// OptDefaultBootImageID returns a new default boot image ID option as per
+// BSDP.
+func OptDefaultBootImageID(b BootImageID) dhcpv4.Option {
+ return dhcpv4.Option{Code: OptionDefaultBootImageID, Value: b}
+}
+
+// GetDefaultBootImageID returns the default boot image ID contained in o.
+func GetDefaultBootImageID(o dhcpv4.Options) *BootImageID {
+ return getBootImageID(OptionDefaultBootImageID, o)
+}
+
+// OptSelectedBootImageID returns a new selected boot image ID option as per
+// BSDP.
+func OptSelectedBootImageID(b BootImageID) dhcpv4.Option {
+ return dhcpv4.Option{Code: OptionSelectedBootImageID, Value: b}
+}
+
+// GetSelectedBootImageID returns the selected boot image ID contained in o.
+func GetSelectedBootImageID(o dhcpv4.Options) *BootImageID {
+ return getBootImageID(OptionSelectedBootImageID, o)
+}
diff --git a/dhcpv4/bsdp/bsdp.go b/dhcpv4/bsdp/bsdp.go
index 3cc87d2..9bcc15d 100644
--- a/dhcpv4/bsdp/bsdp.go
+++ b/dhcpv4/bsdp/bsdp.go
@@ -30,20 +30,12 @@ type ReplyConfig struct {
// ParseBootImageListFromAck parses the list of boot images presented in the
// ACK[LIST] packet and returns them as a list of BootImages.
-func ParseBootImageListFromAck(ack dhcpv4.DHCPv4) ([]BootImage, error) {
- opt := ack.GetOneOption(dhcpv4.OptionVendorSpecificInformation)
- if opt == nil {
+func ParseBootImageListFromAck(ack *dhcpv4.DHCPv4) ([]BootImage, error) {
+ vendorOpts := GetVendorOptions(ack.Options)
+ if vendorOpts == nil {
return nil, errors.New("ParseBootImageListFromAck: could not find vendor-specific option")
}
- vendorOpt, err := ParseOptVendorSpecificInformation(opt.ToBytes())
- if err != nil {
- return nil, err
- }
- bootImageOpts := vendorOpt.GetOneOption(OptionBootImageList)
- if bootImageOpts == nil {
- return nil, fmt.Errorf("boot image option not found")
- }
- return bootImageOpts.(*OptBootImageList).Images, nil
+ return GetBootImageList(vendorOpts.Options), nil
}
func needsReplyPort(replyPort uint16) bool {
@@ -53,28 +45,41 @@ func needsReplyPort(replyPort uint16) bool {
// MessageTypeFromPacket extracts the BSDP message type (LIST, SELECT) from the
// vendor-specific options and returns it. If the message type option cannot be
// found, returns false.
-func MessageTypeFromPacket(packet *dhcpv4.DHCPv4) *MessageType {
- var (
- vendorOpts *OptVendorSpecificInformation
- err error
- )
- opt := packet.GetOneOption(dhcpv4.OptionVendorSpecificInformation)
- if opt == nil {
- return nil
- }
- if vendorOpts, err = ParseOptVendorSpecificInformation(opt.ToBytes()); err == nil {
- if o := vendorOpts.GetOneOption(OptionMessageType); o != nil {
- if optMessageType, ok := o.(*OptMessageType); ok {
- return &optMessageType.Type
- }
- }
- }
- return nil
+func MessageTypeFromPacket(packet *dhcpv4.DHCPv4) MessageType {
+ vendorOpts := GetVendorOptions(packet.Options)
+ if vendorOpts == nil {
+ return MessageTypeNone
+ }
+ return GetMessageType(vendorOpts.Options)
+}
+
+// Packet is a BSDP packet wrapper around a DHCPv4 packet in order to print the
+// correct vendor-specific BSDP information in String().
+type Packet struct {
+ dhcpv4.DHCPv4
+}
+
+// PacketFor returns a wrapped BSDP Packet given a DHCPv4 packet.
+func PacketFor(d *dhcpv4.DHCPv4) *Packet {
+ return &Packet{*d}
+}
+
+func (p Packet) v4() *dhcpv4.DHCPv4 {
+ return &p.DHCPv4
+}
+
+func (p Packet) String() string {
+ return p.DHCPv4.String()
+}
+
+// Summary prints the BSDP packet with the correct vendor-specific options.
+func (p Packet) Summary() string {
+ return p.DHCPv4.SummaryWithVendor(&VendorOptions{})
}
// NewInformListForInterface creates a new INFORM packet for interface ifname
// with configuration options specified by config.
-func NewInformListForInterface(ifname string, replyPort uint16) (*dhcpv4.DHCPv4, error) {
+func NewInformListForInterface(ifname string, replyPort uint16) (*Packet, error) {
iface, err := net.InterfaceByName(ifname)
if err != nil {
return nil, err
@@ -96,7 +101,7 @@ func NewInformListForInterface(ifname string, replyPort uint16) (*dhcpv4.DHCPv4,
// NewInformList creates a new INFORM packet for interface with hardware address
// `hwaddr` and IP `localIP`. Packet will be sent out on port `replyPort`.
-func NewInformList(hwaddr net.HardwareAddr, localIP net.IP, replyPort uint16, modifiers ...dhcpv4.Modifier) (*dhcpv4.DHCPv4, error) {
+func NewInformList(hwaddr net.HardwareAddr, localIP net.IP, replyPort uint16, modifiers ...dhcpv4.Modifier) (*Packet, error) {
// Validate replyPort first
if needsReplyPort(replyPort) && replyPort >= 1024 {
return nil, errors.New("replyPort must be a privileged port")
@@ -109,60 +114,61 @@ func NewInformList(hwaddr net.HardwareAddr, localIP net.IP, replyPort uint16, mo
// These are vendor-specific options used to pass along BSDP information.
vendorOpts := []dhcpv4.Option{
- &OptMessageType{MessageTypeList},
- Version1_1,
+ OptMessageType(MessageTypeList),
+ OptVersion(Version1_1),
}
if needsReplyPort(replyPort) {
- vendorOpts = append(vendorOpts, &OptReplyPort{replyPort})
+ vendorOpts = append(vendorOpts, OptReplyPort(replyPort))
}
- return dhcpv4.NewInform(hwaddr, localIP,
+ d, err := dhcpv4.NewInform(hwaddr, localIP,
dhcpv4.PrependModifiers(modifiers, dhcpv4.WithRequestedOptions(
dhcpv4.OptionVendorSpecificInformation,
dhcpv4.OptionClassIdentifier,
),
- dhcpv4.WithOption(&dhcpv4.OptMaximumDHCPMessageSize{Size: MaxDHCPMessageSize}),
- dhcpv4.WithOption(&dhcpv4.OptClassIdentifier{Identifier: vendorClassID}),
- dhcpv4.WithOption(&OptVendorSpecificInformation{vendorOpts}),
+ dhcpv4.WithOption(dhcpv4.OptMaxMessageSize(MaxDHCPMessageSize)),
+ dhcpv4.WithOption(dhcpv4.OptClassIdentifier(vendorClassID)),
+ dhcpv4.WithOption(OptVendorOptions(vendorOpts...)),
)...)
+ if err != nil {
+ return nil, err
+ }
+ return PacketFor(d), nil
}
// InformSelectForAck constructs an INFORM[SELECT] packet given an ACK to the
// previously-sent INFORM[LIST].
-func InformSelectForAck(ack dhcpv4.DHCPv4, replyPort uint16, selectedImage BootImage) (*dhcpv4.DHCPv4, error) {
+func InformSelectForAck(ack *Packet, replyPort uint16, selectedImage BootImage) (*Packet, error) {
if needsReplyPort(replyPort) && replyPort >= 1024 {
return nil, errors.New("replyPort must be a privileged port")
}
// Data for OptionSelectedBootImageID
vendorOpts := []dhcpv4.Option{
- &OptMessageType{MessageTypeSelect},
- Version1_1,
- &OptSelectedBootImageID{selectedImage.ID},
+ OptMessageType(MessageTypeSelect),
+ OptVersion(Version1_1),
+ OptSelectedBootImageID(selectedImage.ID),
}
// Validate replyPort if requested.
if needsReplyPort(replyPort) {
- vendorOpts = append(vendorOpts, &OptReplyPort{replyPort})
+ vendorOpts = append(vendorOpts, OptReplyPort(replyPort))
}
// Find server IP address
- var serverIP net.IP
- if opt := ack.GetOneOption(dhcpv4.OptionServerIdentifier); opt != nil {
- serverIP = opt.(*dhcpv4.OptServerIdentifier).ServerID
- }
+ serverIP := dhcpv4.GetServerIdentifier(ack.Options)
if serverIP.To4() == nil {
return nil, fmt.Errorf("could not parse server identifier from ACK")
}
- vendorOpts = append(vendorOpts, &OptServerIdentifier{serverIP})
+ vendorOpts = append(vendorOpts, OptServerIdentifier(serverIP))
vendorClassID, err := MakeVendorClassIdentifier()
if err != nil {
return nil, err
}
- return dhcpv4.New(dhcpv4.WithReply(&ack),
- dhcpv4.WithOption(&dhcpv4.OptClassIdentifier{Identifier: vendorClassID}),
+ d, err := dhcpv4.New(dhcpv4.WithReply(ack.v4()),
+ dhcpv4.WithOption(dhcpv4.OptClassIdentifier(vendorClassID)),
dhcpv4.WithRequestedOptions(
dhcpv4.OptionSubnetMask,
dhcpv4.OptionRouter,
@@ -171,20 +177,24 @@ func InformSelectForAck(ack dhcpv4.DHCPv4, replyPort uint16, selectedImage BootI
dhcpv4.OptionClassIdentifier,
),
dhcpv4.WithMessageType(dhcpv4.MessageTypeInform),
- dhcpv4.WithOption(&OptVendorSpecificInformation{vendorOpts}),
+ dhcpv4.WithOption(OptVendorOptions(vendorOpts...)),
)
+ if err != nil {
+ return nil, err
+ }
+ return PacketFor(d), nil
}
// NewReplyForInformList constructs an ACK for the INFORM[LIST] packet `inform`
// with additional options in `config`.
-func NewReplyForInformList(inform *dhcpv4.DHCPv4, config ReplyConfig) (*dhcpv4.DHCPv4, error) {
+func NewReplyForInformList(inform *Packet, config ReplyConfig) (*Packet, error) {
if config.DefaultImage == nil {
return nil, errors.New("NewReplyForInformList: no default boot image ID set")
}
if config.Images == nil || len(config.Images) == 0 {
return nil, errors.New("NewReplyForInformList: no boot images provided")
}
- reply, err := dhcpv4.NewReplyFromRequest(inform)
+ reply, err := dhcpv4.NewReplyFromRequest(&inform.DHCPv4)
if err != nil {
return nil, err
}
@@ -193,34 +203,34 @@ func NewReplyForInformList(inform *dhcpv4.DHCPv4, config ReplyConfig) (*dhcpv4.D
reply.ServerIPAddr = config.ServerIP
reply.ServerHostName = config.ServerHostname
- reply.UpdateOption(&dhcpv4.OptMessageType{MessageType: dhcpv4.MessageTypeAck})
- reply.UpdateOption(&dhcpv4.OptServerIdentifier{ServerID: config.ServerIP})
- reply.UpdateOption(&dhcpv4.OptClassIdentifier{Identifier: AppleVendorID})
+ reply.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
+ reply.UpdateOption(dhcpv4.OptServerIdentifier(config.ServerIP))
+ reply.UpdateOption(dhcpv4.OptClassIdentifier(AppleVendorID))
// BSDP opts.
vendorOpts := []dhcpv4.Option{
- &OptMessageType{Type: MessageTypeList},
- &OptServerPriority{Priority: config.ServerPriority},
- &OptDefaultBootImageID{ID: config.DefaultImage.ID},
- &OptBootImageList{Images: config.Images},
+ OptMessageType(MessageTypeList),
+ OptServerPriority(config.ServerPriority),
+ OptDefaultBootImageID(config.DefaultImage.ID),
+ OptBootImageList(config.Images...),
}
if config.SelectedImage != nil {
- vendorOpts = append(vendorOpts, &OptSelectedBootImageID{ID: config.SelectedImage.ID})
+ vendorOpts = append(vendorOpts, OptSelectedBootImageID(config.SelectedImage.ID))
}
- reply.UpdateOption(&OptVendorSpecificInformation{Options: vendorOpts})
- return reply, nil
+ reply.UpdateOption(OptVendorOptions(vendorOpts...))
+ return PacketFor(reply), nil
}
// NewReplyForInformSelect constructs an ACK for the INFORM[Select] packet
// `inform` with additional options in `config`.
-func NewReplyForInformSelect(inform *dhcpv4.DHCPv4, config ReplyConfig) (*dhcpv4.DHCPv4, error) {
+func NewReplyForInformSelect(inform *Packet, config ReplyConfig) (*Packet, error) {
if config.SelectedImage == nil {
return nil, errors.New("NewReplyForInformSelect: no selected boot image ID set")
}
if config.Images == nil || len(config.Images) == 0 {
return nil, errors.New("NewReplyForInformSelect: no boot images provided")
}
- reply, err := dhcpv4.NewReplyFromRequest(inform)
+ reply, err := dhcpv4.NewReplyFromRequest(&inform.DHCPv4)
if err != nil {
return nil, err
}
@@ -231,16 +241,14 @@ func NewReplyForInformSelect(inform *dhcpv4.DHCPv4, config ReplyConfig) (*dhcpv4
reply.ServerHostName = config.ServerHostname
reply.BootFileName = config.BootFileName
- reply.UpdateOption(&dhcpv4.OptMessageType{MessageType: dhcpv4.MessageTypeAck})
- reply.UpdateOption(&dhcpv4.OptServerIdentifier{ServerID: config.ServerIP})
- reply.UpdateOption(&dhcpv4.OptClassIdentifier{Identifier: AppleVendorID})
+ reply.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
+ reply.UpdateOption(dhcpv4.OptServerIdentifier(config.ServerIP))
+ reply.UpdateOption(dhcpv4.OptClassIdentifier(AppleVendorID))
// BSDP opts.
- reply.UpdateOption(&OptVendorSpecificInformation{
- Options: []dhcpv4.Option{
- &OptMessageType{Type: MessageTypeSelect},
- &OptSelectedBootImageID{ID: config.SelectedImage.ID},
- },
- })
- return reply, nil
+ reply.UpdateOption(OptVendorOptions(
+ OptMessageType(MessageTypeSelect),
+ OptSelectedBootImageID(config.SelectedImage.ID),
+ ))
+ return PacketFor(reply), nil
}
diff --git a/dhcpv4/bsdp/bsdp_option_boot_image_list.go b/dhcpv4/bsdp/bsdp_option_boot_image_list.go
index 3282fa3..ebbbd2d 100644
--- a/dhcpv4/bsdp/bsdp_option_boot_image_list.go
+++ b/dhcpv4/bsdp/bsdp_option_boot_image_list.go
@@ -1,52 +1,66 @@
package bsdp
import (
+ "strings"
+
"github.com/insomniacslk/dhcp/dhcpv4"
"github.com/u-root/u-root/pkg/uio"
)
-// OptBootImageList contains the list of boot images presented by a netboot
-// server.
-type OptBootImageList struct {
- Images []BootImage
-}
+// BootImageList contains a list of boot images presented by a netboot server.
+//
+// Implements the BSDP option listing the boot images.
+type BootImageList []BootImage
-// ParseOptBootImageList constructs an OptBootImageList struct from a sequence
-// of bytes and returns it, or an error.
-func ParseOptBootImageList(data []byte) (*OptBootImageList, error) {
+// FromBytes deserializes data into bil.
+func (bil *BootImageList) FromBytes(data []byte) error {
buf := uio.NewBigEndianBuffer(data)
- var bootImages []BootImage
for buf.Has(5) {
var image BootImage
- if err := (&image).Unmarshal(buf); err != nil {
- return nil, err
+ if err := image.Unmarshal(buf); err != nil {
+ return err
}
- bootImages = append(bootImages, image)
+ *bil = append(*bil, image)
}
-
- return &OptBootImageList{bootImages}, nil
-}
-
-// Code returns the option code.
-func (o *OptBootImageList) Code() dhcpv4.OptionCode {
- return OptionBootImageList
+ return nil
}
// ToBytes returns a serialized stream of bytes for this option.
-func (o *OptBootImageList) ToBytes() []byte {
+func (bil BootImageList) ToBytes() []byte {
buf := uio.NewBigEndianBuffer(nil)
- for _, image := range o.Images {
+ for _, image := range bil {
image.Marshal(buf)
}
return buf.Data()
}
// String returns a human-readable string for this option.
-func (o *OptBootImageList) String() string {
- s := "BSDP Boot Image List ->"
- for _, image := range o.Images {
- s += "\n " + image.String()
+func (bil BootImageList) String() string {
+ s := make([]string, 0, len(bil))
+ for _, image := range bil {
+ s = append(s, image.String())
+ }
+ return strings.Join(s, ", ")
+}
+
+// OptBootImageList returns a new BSDP boot image list.
+func OptBootImageList(b ...BootImage) dhcpv4.Option {
+ return dhcpv4.Option{
+ Code: OptionBootImageList,
+ Value: BootImageList(b),
+ }
+}
+
+// GetBootImageList returns the BSDP boot image list.
+func GetBootImageList(o dhcpv4.Options) BootImageList {
+ v := o.Get(OptionBootImageList)
+ if v == nil {
+ return nil
+ }
+ var bil BootImageList
+ if err := bil.FromBytes(v); err != nil {
+ return nil
}
- return s
+ return bil
}
diff --git a/dhcpv4/bsdp/bsdp_option_boot_image_list_test.go b/dhcpv4/bsdp/bsdp_option_boot_image_list_test.go
index 5d1b77c..6282156 100644
--- a/dhcpv4/bsdp/bsdp_option_boot_image_list_test.go
+++ b/dhcpv4/bsdp/bsdp_option_boot_image_list_test.go
@@ -25,8 +25,8 @@ func TestOptBootImageListInterfaceMethods(t *testing.T) {
Name: "bsdp-2",
},
}
- o := OptBootImageList{bs}
- require.Equal(t, OptionBootImageList, o.Code(), "Code")
+ o := OptBootImageList(bs...)
+ require.Equal(t, OptionBootImageList, o.Code, "Code")
expectedBytes := []byte{
// boot image 1
0x1, 0x0, 0x03, 0xe9, // ID
@@ -37,7 +37,7 @@ func TestOptBootImageListInterfaceMethods(t *testing.T) {
6, // name length
'b', 's', 'd', 'p', '-', '2',
}
- require.Equal(t, expectedBytes, o.ToBytes(), "ToBytes")
+ require.Equal(t, expectedBytes, o.Value.ToBytes(), "ToBytes")
}
func TestParseOptBootImageList(t *testing.T) {
@@ -51,9 +51,10 @@ func TestParseOptBootImageList(t *testing.T) {
6, // name length
'b', 's', 'd', 'p', '-', '2',
}
- o, err := ParseOptBootImageList(data)
+ var o BootImageList
+ err := o.FromBytes(data)
require.NoError(t, err)
- expectedBootImages := []BootImage{
+ expectedBootImages := BootImageList{
BootImage{
ID: BootImageID{
IsInstall: false,
@@ -71,7 +72,7 @@ func TestParseOptBootImageList(t *testing.T) {
Name: "bsdp-2",
},
}
- require.Equal(t, &OptBootImageList{expectedBootImages}, o)
+ require.Equal(t, expectedBootImages, o)
// Error parsing boot image (malformed)
data = []byte{
@@ -84,7 +85,7 @@ func TestParseOptBootImageList(t *testing.T) {
6, // name length
'b', 's', 'd', 'p', '-', '2',
}
- _, err = ParseOptBootImageList(data)
+ err = o.FromBytes(data)
require.Error(t, err, "should get error from bad boot image")
}
@@ -107,7 +108,7 @@ func TestOptBootImageListString(t *testing.T) {
Name: "bsdp-2",
},
}
- o := OptBootImageList{bs}
- expectedString := "BSDP Boot Image List ->\n bsdp-1 [1001] uninstallable macOS image\n bsdp-2 [9009] installable macOS 9 image"
+ o := OptBootImageList(bs...)
+ expectedString := "BSDP Boot Image List: bsdp-1 [1001] uninstallable macOS image, bsdp-2 [9009] installable macOS 9 image"
require.Equal(t, expectedString, o.String())
}
diff --git a/dhcpv4/bsdp/bsdp_option_default_boot_image_id.go b/dhcpv4/bsdp/bsdp_option_default_boot_image_id.go
deleted file mode 100644
index 40ab0be..0000000
--- a/dhcpv4/bsdp/bsdp_option_default_boot_image_id.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package bsdp
-
-import (
- "fmt"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
- "github.com/u-root/u-root/pkg/uio"
-)
-
-// OptDefaultBootImageID contains the selected boot image ID.
-//
-// Implements the BSDP option default boot image ID, which tells the client
-// which image is the default boot image if one is not selected.
-type OptDefaultBootImageID struct {
- ID BootImageID
-}
-
-// ParseOptDefaultBootImageID constructs an OptDefaultBootImageID struct from a sequence of
-// bytes and returns it, or an error.
-func ParseOptDefaultBootImageID(data []byte) (*OptDefaultBootImageID, error) {
- var o OptDefaultBootImageID
- buf := uio.NewBigEndianBuffer(data)
- if err := o.ID.Unmarshal(buf); err != nil {
- return nil, err
- }
- return &o, buf.FinError()
-}
-
-// Code returns the option code.
-func (o *OptDefaultBootImageID) Code() dhcpv4.OptionCode {
- return OptionDefaultBootImageID
-}
-
-// ToBytes returns a serialized stream of bytes for this option.
-func (o *OptDefaultBootImageID) ToBytes() []byte {
- return uio.ToBigEndian(o.ID)
-}
-
-// String returns a human-readable string for this option.
-func (o *OptDefaultBootImageID) String() string {
- return fmt.Sprintf("BSDP Default Boot Image ID -> %s", o.ID.String())
-}
diff --git a/dhcpv4/bsdp/bsdp_option_default_boot_image_id_test.go b/dhcpv4/bsdp/bsdp_option_default_boot_image_id_test.go
deleted file mode 100644
index a5abdaf..0000000
--- a/dhcpv4/bsdp/bsdp_option_default_boot_image_id_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package bsdp
-
-import (
- "testing"
-
- "github.com/stretchr/testify/require"
- "github.com/u-root/u-root/pkg/uio"
-)
-
-func TestOptDefaultBootImageIDInterfaceMethods(t *testing.T) {
- b := BootImageID{IsInstall: true, ImageType: BootImageTypeMacOSX, Index: 1001}
- o := OptDefaultBootImageID{b}
- require.Equal(t, OptionDefaultBootImageID, o.Code(), "Code")
- require.Equal(t, uio.ToBigEndian(b), o.ToBytes(), "ToBytes")
-}
-
-func TestParseOptDefaultBootImageID(t *testing.T) {
- b := BootImageID{IsInstall: true, ImageType: BootImageTypeMacOSX, Index: 1001}
- o, err := ParseOptDefaultBootImageID(uio.ToBigEndian(b))
- require.NoError(t, err)
- require.Equal(t, &OptDefaultBootImageID{b}, o)
-
- // Short byte stream
- data := []byte{}
- _, err = ParseOptDefaultBootImageID(data)
- require.Error(t, err, "should get error from short byte stream")
-
- // Bad length
- data = []byte{1, 0, 0, 0, 0}
- _, err = ParseOptDefaultBootImageID(data)
- require.Error(t, err, "should get error from bad length")
-}
-
-func TestOptDefaultBootImageIDString(t *testing.T) {
- b := BootImageID{IsInstall: true, ImageType: BootImageTypeMacOSX, Index: 1001}
- o := OptDefaultBootImageID{b}
- require.Equal(t, "BSDP Default Boot Image ID -> [1001] installable macOS image", o.String())
-
- b = BootImageID{IsInstall: false, ImageType: BootImageTypeMacOS9, Index: 1001}
- o = OptDefaultBootImageID{b}
- require.Equal(t, "BSDP Default Boot Image ID -> [1001] uninstallable macOS 9 image", o.String())
-
- b = BootImageID{IsInstall: false, ImageType: BootImageType(99), Index: 1001}
- o = OptDefaultBootImageID{b}
- require.Equal(t, "BSDP Default Boot Image ID -> [1001] uninstallable unknown image", o.String())
-}
diff --git a/dhcpv4/bsdp/bsdp_option_generic.go b/dhcpv4/bsdp/bsdp_option_generic.go
deleted file mode 100644
index e9e163f..0000000
--- a/dhcpv4/bsdp/bsdp_option_generic.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package bsdp
-
-import (
- "fmt"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
-)
-
-// OptGeneric is an option that only contains the option code and associated
-// data. Every option that does not have a specific implementation will fall
-// back to this option.
-type OptGeneric struct {
- OptionCode dhcpv4.OptionCode
- Data []byte
-}
-
-// ParseOptGeneric parses a bytestream and creates a new OptGeneric from it,
-// or an error.
-func ParseOptGeneric(code dhcpv4.OptionCode, data []byte) (*OptGeneric, error) {
- return &OptGeneric{OptionCode: code, Data: data}, nil
-}
-
-// Code returns the generic option code.
-func (o OptGeneric) Code() dhcpv4.OptionCode {
- return o.OptionCode
-}
-
-// ToBytes returns a serialized generic option as a slice of bytes.
-func (o OptGeneric) ToBytes() []byte {
- return o.Data
-}
-
-// String returns a human-readable representation of a generic option.
-func (o OptGeneric) String() string {
- return fmt.Sprintf("%s -> %v", o.OptionCode, o.Data)
-}
diff --git a/dhcpv4/bsdp/bsdp_option_generic_test.go b/dhcpv4/bsdp/bsdp_option_generic_test.go
deleted file mode 100644
index a813f95..0000000
--- a/dhcpv4/bsdp/bsdp_option_generic_test.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package bsdp
-
-import (
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestParseOptGeneric(t *testing.T) {
- // Good parse
- o, err := ParseOptGeneric(OptionMessageType, []byte{1})
- require.NoError(t, err)
- require.Equal(t, OptionMessageType, o.Code())
- require.Equal(t, MessageTypeList, MessageType(o.Data[0]))
-}
-
-func TestOptGenericCode(t *testing.T) {
- o := OptGeneric{
- OptionCode: OptionMessageType,
- Data: []byte{byte(MessageTypeList)},
- }
- require.Equal(t, OptionMessageType, o.Code())
-}
-
-func TestOptGenericData(t *testing.T) {
- o := OptGeneric{
- OptionCode: OptionServerIdentifier,
- Data: []byte{192, 168, 0, 1},
- }
- require.Equal(t, []byte{192, 168, 0, 1}, o.Data)
-}
-
-func TestOptGenericToBytes(t *testing.T) {
- o := OptGeneric{
- OptionCode: OptionServerIdentifier,
- Data: []byte{192, 168, 0, 1},
- }
- serialized := o.ToBytes()
- expected := []byte{192, 168, 0, 1}
- require.Equal(t, expected, serialized)
-}
-
-func TestOptGenericString(t *testing.T) {
- o := OptGeneric{
- OptionCode: OptionServerIdentifier,
- Data: []byte{192, 168, 0, 1},
- }
- require.Equal(t, "BSDP Server Identifier -> [192 168 0 1]", o.String())
-}
-
-func TestOptGenericStringUnknown(t *testing.T) {
- o := OptGeneric{
- OptionCode: optionCode(102), // Returned option code.
- Data: []byte{5},
- }
- require.Equal(t, "unknown -> [5]", o.String())
-}
diff --git a/dhcpv4/bsdp/bsdp_option_machine_name.go b/dhcpv4/bsdp/bsdp_option_machine_name.go
deleted file mode 100644
index ced88b0..0000000
--- a/dhcpv4/bsdp/bsdp_option_machine_name.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package bsdp
-
-import (
- "github.com/insomniacslk/dhcp/dhcpv4"
-)
-
-// OptMachineName represents a BSDP message type.
-//
-// Implements the BSDP option machine name, which gives the Netboot server's
-// machine name.
-type OptMachineName struct {
- Name string
-}
-
-// ParseOptMachineName constructs an OptMachineName struct from a sequence of
-// bytes and returns it, or an error.
-func ParseOptMachineName(data []byte) (*OptMachineName, error) {
- return &OptMachineName{Name: string(data)}, nil
-}
-
-// Code returns the option code.
-func (o *OptMachineName) Code() dhcpv4.OptionCode {
- return OptionMachineName
-}
-
-// ToBytes returns a serialized stream of bytes for this option.
-func (o *OptMachineName) ToBytes() []byte {
- return []byte(o.Name)
-}
-
-// String returns a human-readable string for this option.
-func (o *OptMachineName) String() string {
- return "BSDP Machine Name -> " + o.Name
-}
diff --git a/dhcpv4/bsdp/bsdp_option_machine_name_test.go b/dhcpv4/bsdp/bsdp_option_machine_name_test.go
deleted file mode 100644
index abc0d54..0000000
--- a/dhcpv4/bsdp/bsdp_option_machine_name_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package bsdp
-
-import (
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestOptMachineNameInterfaceMethods(t *testing.T) {
- o := OptMachineName{"somebox"}
- require.Equal(t, OptionMachineName, o.Code(), "Code")
- expectedBytes := []byte{'s', 'o', 'm', 'e', 'b', 'o', 'x'}
- require.Equal(t, expectedBytes, o.ToBytes(), "ToBytes")
-}
-
-func TestParseOptMachineName(t *testing.T) {
- data := []byte{'s', 'o', 'm', 'e', 'b', 'o', 'x'}
- o, err := ParseOptMachineName(data)
- require.NoError(t, err)
- require.Equal(t, &OptMachineName{"somebox"}, o)
-}
-
-func TestOptMachineNameString(t *testing.T) {
- o := OptMachineName{"somebox"}
- require.Equal(t, "BSDP Machine Name -> somebox", o.String())
-}
diff --git a/dhcpv4/bsdp/bsdp_option_message_type.go b/dhcpv4/bsdp/bsdp_option_message_type.go
index 5f96f12..cb0c5cf 100644
--- a/dhcpv4/bsdp/bsdp_option_message_type.go
+++ b/dhcpv4/bsdp/bsdp_option_message_type.go
@@ -15,16 +15,23 @@ type MessageType byte
// BSDP Message types - e.g. LIST, SELECT, FAILED
const (
+ MessageTypeNone MessageType = 0
MessageTypeList MessageType = 1
MessageTypeSelect MessageType = 2
MessageTypeFailed MessageType = 3
)
+// ToBytes returns a serialized stream of bytes for this option.
+func (m MessageType) ToBytes() []byte {
+ return []byte{byte(m)}
+}
+
+// String returns a human-friendly representation of MessageType.
func (m MessageType) String() string {
if s, ok := messageTypeToString[m]; ok {
return s
}
- return "Unknown"
+ return fmt.Sprintf("unknown (%d)", m)
}
// messageTypeToString maps each BSDP message type to a human-readable string.
@@ -34,29 +41,30 @@ var messageTypeToString = map[MessageType]string{
MessageTypeFailed: "FAILED",
}
-// OptMessageType represents a BSDP message type.
-type OptMessageType struct {
- Type MessageType
-}
-
-// ParseOptMessageType constructs an OptMessageType struct from a sequence of
-// bytes and returns it, or an error.
-func ParseOptMessageType(data []byte) (*OptMessageType, error) {
+// FromBytes reads data into m.
+func (m *MessageType) FromBytes(data []byte) error {
buf := uio.NewBigEndianBuffer(data)
- return &OptMessageType{Type: MessageType(buf.Read8())}, buf.FinError()
+ *m = MessageType(buf.Read8())
+ return buf.FinError()
}
-// Code returns the option code.
-func (o *OptMessageType) Code() dhcpv4.OptionCode {
- return OptionMessageType
-}
-
-// ToBytes returns a serialized stream of bytes for this option.
-func (o *OptMessageType) ToBytes() []byte {
- return []byte{byte(o.Type)}
+// OptMessageType returns a new BSDP Message Type option.
+func OptMessageType(mt MessageType) dhcpv4.Option {
+ return dhcpv4.Option{
+ Code: OptionMessageType,
+ Value: mt,
+ }
}
-// String returns a human-readable string for this option.
-func (o *OptMessageType) String() string {
- return fmt.Sprintf("BSDP Message Type -> %s", o.Type.String())
+// GetMessageType returns the BSDP Message Type in o.
+func GetMessageType(o dhcpv4.Options) MessageType {
+ v := o.Get(OptionMessageType)
+ if v == nil {
+ return MessageTypeNone
+ }
+ var m MessageType
+ if err := m.FromBytes(v); err != nil {
+ return MessageTypeNone
+ }
+ return m
}
diff --git a/dhcpv4/bsdp/bsdp_option_message_type_test.go b/dhcpv4/bsdp/bsdp_option_message_type_test.go
index a6695cc..6666137 100644
--- a/dhcpv4/bsdp/bsdp_option_message_type_test.go
+++ b/dhcpv4/bsdp/bsdp_option_message_type_test.go
@@ -7,24 +7,25 @@ import (
)
func TestOptMessageTypeInterfaceMethods(t *testing.T) {
- o := OptMessageType{MessageTypeList}
- require.Equal(t, OptionMessageType, o.Code(), "Code")
- require.Equal(t, []byte{1}, o.ToBytes(), "ToBytes")
+ o := OptMessageType(MessageTypeList)
+ require.Equal(t, OptionMessageType, o.Code, "Code")
+ require.Equal(t, []byte{1}, o.Value.ToBytes(), "ToBytes")
}
func TestParseOptMessageType(t *testing.T) {
+ var o MessageType
data := []byte{1} // DISCOVER
- o, err := ParseOptMessageType(data)
+ err := o.FromBytes(data)
require.NoError(t, err)
- require.Equal(t, &OptMessageType{MessageTypeList}, o)
+ require.Equal(t, MessageTypeList, o)
}
func TestOptMessageTypeString(t *testing.T) {
// known
- o := OptMessageType{MessageTypeList}
- require.Equal(t, "BSDP Message Type -> LIST", o.String())
+ o := OptMessageType(MessageTypeList)
+ require.Equal(t, "BSDP Message Type: LIST", o.String())
// unknown
- o = OptMessageType{99}
- require.Equal(t, "BSDP Message Type -> Unknown", o.String())
+ o = OptMessageType(99)
+ require.Equal(t, "BSDP Message Type: unknown (99)", o.String())
}
diff --git a/dhcpv4/bsdp/bsdp_option_misc.go b/dhcpv4/bsdp/bsdp_option_misc.go
new file mode 100644
index 0000000..2d3a7bf
--- /dev/null
+++ b/dhcpv4/bsdp/bsdp_option_misc.go
@@ -0,0 +1,99 @@
+package bsdp
+
+import (
+ "fmt"
+ "net"
+
+ "github.com/insomniacslk/dhcp/dhcpv4"
+ "github.com/u-root/u-root/pkg/uio"
+)
+
+// OptReplyPort returns a new BSDP reply port option.
+//
+// Implements the BSDP option reply port. This is used when BSDP responses
+// should be sent to a reply port other than the DHCP default. The macOS GUI
+// "Startup Disk Select" sends this option since it's operating in an
+// unprivileged context.
+func OptReplyPort(port uint16) dhcpv4.Option {
+ return dhcpv4.Option{Code: OptionReplyPort, Value: dhcpv4.Uint16(port)}
+}
+
+// GetReplyPort returns the BSDP reply port in o, if present.
+func GetReplyPort(o dhcpv4.Options) (uint16, error) {
+ return dhcpv4.GetUint16(OptionReplyPort, o)
+}
+
+// OptServerPriority returns a new BSDP server priority option.
+func OptServerPriority(prio uint16) dhcpv4.Option {
+ return dhcpv4.Option{Code: OptionServerPriority, Value: dhcpv4.Uint16(prio)}
+}
+
+// GetServerPriority returns the BSDP server priority in o if present.
+func GetServerPriority(o dhcpv4.Options) (uint16, error) {
+ return dhcpv4.GetUint16(OptionServerPriority, o)
+}
+
+// OptMachineName returns a BSDP Machine Name option.
+func OptMachineName(name string) dhcpv4.Option {
+ return dhcpv4.Option{Code: OptionMachineName, Value: dhcpv4.String(name)}
+}
+
+// GetMachineName finds and parses the BSDP Machine Name option from o.
+func GetMachineName(o dhcpv4.Options) string {
+ return dhcpv4.GetString(OptionMachineName, o)
+}
+
+// Version is the BSDP protocol version. Can be one of 1.0 or 1.1.
+type Version [2]byte
+
+// Specific versions.
+var (
+ Version1_0 = Version{1, 0}
+ Version1_1 = Version{1, 1}
+)
+
+// ToBytes returns a serialized stream of bytes for this option.
+func (o Version) ToBytes() []byte {
+ return o[:]
+}
+
+// String returns a human-readable string for this option.
+func (o Version) String() string {
+ return fmt.Sprintf("%d.%d", o[0], o[1])
+}
+
+// FromBytes constructs a Version struct from a sequence of
+// bytes and returns it, or an error.
+func (o *Version) FromBytes(data []byte) error {
+ buf := uio.NewBigEndianBuffer(data)
+ buf.ReadBytes(o[:])
+ return buf.FinError()
+}
+
+// OptVersion returns a new BSDP version option.
+func OptVersion(version Version) dhcpv4.Option {
+ return dhcpv4.Option{Code: OptionVersion, Value: version}
+}
+
+// GetVersion returns the BSDP version in o if present.
+func GetVersion(o dhcpv4.Options) (Version, error) {
+ v := o.Get(OptionVersion)
+ if v == nil {
+ return Version{0, 0}, fmt.Errorf("version not found")
+ }
+ var ver Version
+ if err := ver.FromBytes(v); err != nil {
+ return Version{0, 0}, err
+ }
+ return ver, nil
+}
+
+// GetServerIdentifier returns the BSDP Server Identifier value in o.
+func GetServerIdentifier(o dhcpv4.Options) net.IP {
+ return dhcpv4.GetIP(OptionServerIdentifier, o)
+}
+
+// OptServerIdentifier returns a new BSDP Server Identifier option.
+func OptServerIdentifier(ip net.IP) dhcpv4.Option {
+ return dhcpv4.Option{Code: OptionServerIdentifier, Value: dhcpv4.IP(ip)}
+}
diff --git a/dhcpv4/bsdp/bsdp_option_misc_test.go b/dhcpv4/bsdp/bsdp_option_misc_test.go
new file mode 100644
index 0000000..dfa81b5
--- /dev/null
+++ b/dhcpv4/bsdp/bsdp_option_misc_test.go
@@ -0,0 +1,95 @@
+package bsdp
+
+import (
+ "net"
+ "testing"
+
+ "github.com/insomniacslk/dhcp/dhcpv4"
+ "github.com/stretchr/testify/require"
+)
+
+func TestOptReplyPort(t *testing.T) {
+ o := OptReplyPort(1234)
+ require.Equal(t, OptionReplyPort, o.Code, "Code")
+ require.Equal(t, []byte{4, 210}, o.Value.ToBytes(), "ToBytes")
+ require.Equal(t, "BSDP Reply Port: 1234", o.String())
+}
+
+func TestGetReplyPort(t *testing.T) {
+ o := VendorOptions{dhcpv4.OptionsFromList(OptReplyPort(1234))}
+ port, err := GetReplyPort(o.Options)
+ require.NoError(t, err)
+ require.Equal(t, uint16(1234), port)
+
+ port, err = GetReplyPort(dhcpv4.Options{})
+ require.Error(t, err, "no reply port present")
+}
+
+func TestOptServerPriority(t *testing.T) {
+ o := OptServerPriority(1234)
+ require.Equal(t, OptionServerPriority, o.Code, "Code")
+ require.Equal(t, []byte{4, 210}, o.Value.ToBytes(), "ToBytes")
+ require.Equal(t, "BSDP Server Priority: 1234", o.String())
+}
+
+func TestGetServerPriority(t *testing.T) {
+ o := VendorOptions{dhcpv4.OptionsFromList(OptServerPriority(1234))}
+ prio, err := GetServerPriority(o.Options)
+ require.NoError(t, err)
+ require.Equal(t, uint16(1234), prio)
+
+ prio, err = GetServerPriority(dhcpv4.Options{})
+ require.Error(t, err, "no server prio present")
+}
+
+func TestOptMachineName(t *testing.T) {
+ o := OptMachineName("foo")
+ require.Equal(t, OptionMachineName, o.Code, "Code")
+ require.Equal(t, []byte("foo"), o.Value.ToBytes(), "ToBytes")
+ require.Equal(t, "BSDP Machine Name: foo", o.String())
+}
+
+func TestGetMachineName(t *testing.T) {
+ o := VendorOptions{dhcpv4.OptionsFromList(OptMachineName("foo"))}
+ require.Equal(t, "foo", GetMachineName(o.Options))
+ require.Equal(t, "", GetMachineName(dhcpv4.Options{}))
+}
+
+func TestOptVersion(t *testing.T) {
+ o := OptVersion(Version1_1)
+ require.Equal(t, OptionVersion, o.Code, "Code")
+ require.Equal(t, []byte{1, 1}, o.Value.ToBytes(), "ToBytes")
+ require.Equal(t, "BSDP Version: 1.1", o.String())
+}
+
+func TestGetVersion(t *testing.T) {
+ o := VendorOptions{dhcpv4.OptionsFromList(OptVersion(Version1_1))}
+ ver, err := GetVersion(o.Options)
+ require.NoError(t, err)
+ require.Equal(t, ver, Version1_1)
+
+ ver, err = GetVersion(dhcpv4.Options{})
+ require.Error(t, err, "no version present")
+
+ ver, err = GetVersion(dhcpv4.Options{OptionVersion.Code(): []byte{}})
+ require.Error(t, err, "empty version field")
+
+ ver, err = GetVersion(dhcpv4.Options{OptionVersion.Code(): []byte{1}})
+ require.Error(t, err, "version option too short")
+
+ ver, err = GetVersion(dhcpv4.Options{OptionVersion.Code(): []byte{1, 2, 3}})
+ require.Error(t, err, "version option too long")
+}
+
+func TestOptServerIdentifier(t *testing.T) {
+ o := OptServerIdentifier(net.IP{1, 1, 1, 1})
+ require.Equal(t, OptionServerIdentifier, o.Code, "Code")
+ require.Equal(t, []byte{1, 1, 1, 1}, o.Value.ToBytes(), "ToBytes")
+ require.Equal(t, "BSDP Server Identifier: 1.1.1.1", o.String())
+}
+
+func TestGetServerIdentifier(t *testing.T) {
+ o := VendorOptions{dhcpv4.OptionsFromList(OptServerIdentifier(net.IP{1, 1, 1, 1}))}
+ require.Equal(t, net.IP{1, 1, 1, 1}, GetServerIdentifier(o.Options))
+ require.Equal(t, net.IP(nil), GetServerIdentifier(dhcpv4.Options{}))
+}
diff --git a/dhcpv4/bsdp/bsdp_option_reply_port.go b/dhcpv4/bsdp/bsdp_option_reply_port.go
deleted file mode 100644
index 5eea5ee..0000000
--- a/dhcpv4/bsdp/bsdp_option_reply_port.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package bsdp
-
-import (
- "fmt"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
- "github.com/u-root/u-root/pkg/uio"
-)
-
-// OptReplyPort represents a BSDP protocol version.
-//
-// Implements the BSDP option reply port. This is used when BSDP responses
-// should be sent to a reply port other than the DHCP default. The macOS GUI
-// "Startup Disk Select" sends this option since it's operating in an
-// unprivileged context.
-type OptReplyPort struct {
- Port uint16
-}
-
-// ParseOptReplyPort constructs an OptReplyPort struct from a sequence of
-// bytes and returns it, or an error.
-func ParseOptReplyPort(data []byte) (*OptReplyPort, error) {
- buf := uio.NewBigEndianBuffer(data)
- return &OptReplyPort{buf.Read16()}, buf.FinError()
-}
-
-// Code returns the option code.
-func (o *OptReplyPort) Code() dhcpv4.OptionCode {
- return OptionReplyPort
-}
-
-// ToBytes returns a serialized stream of bytes for this option.
-func (o *OptReplyPort) ToBytes() []byte {
- buf := uio.NewBigEndianBuffer(nil)
- buf.Write16(o.Port)
- return buf.Data()
-}
-
-// String returns a human-readable string for this option.
-func (o *OptReplyPort) String() string {
- return fmt.Sprintf("BSDP Reply Port -> %v", o.Port)
-}
diff --git a/dhcpv4/bsdp/bsdp_option_reply_port_test.go b/dhcpv4/bsdp/bsdp_option_reply_port_test.go
deleted file mode 100644
index de94ffb..0000000
--- a/dhcpv4/bsdp/bsdp_option_reply_port_test.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package bsdp
-
-import (
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestOptReplyPortInterfaceMethods(t *testing.T) {
- o := OptReplyPort{1234}
- require.Equal(t, OptionReplyPort, o.Code(), "Code")
- require.Equal(t, []byte{4, 210}, o.ToBytes(), "ToBytes")
-}
-
-func TestParseOptReplyPort(t *testing.T) {
- data := []byte{0, 1}
- o, err := ParseOptReplyPort(data)
- require.NoError(t, err)
- require.Equal(t, &OptReplyPort{1}, o)
-
- // Short byte stream
- data = []byte{}
- _, err = ParseOptReplyPort(data)
- require.Error(t, err, "should get error from short byte stream")
-
- // Bad length
- data = []byte{1}
- _, err = ParseOptReplyPort(data)
- require.Error(t, err, "should get error from bad length")
-}
-
-func TestOptReplyPortString(t *testing.T) {
- // known
- o := OptReplyPort{1234}
- require.Equal(t, "BSDP Reply Port -> 1234", o.String())
-}
diff --git a/dhcpv4/bsdp/bsdp_option_selected_boot_image_id.go b/dhcpv4/bsdp/bsdp_option_selected_boot_image_id.go
deleted file mode 100644
index 67f99a8..0000000
--- a/dhcpv4/bsdp/bsdp_option_selected_boot_image_id.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package bsdp
-
-import (
- "fmt"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
- "github.com/u-root/u-root/pkg/uio"
-)
-
-// OptSelectedBootImageID contains the selected boot image ID.
-//
-// Implements the BSDP option selected boot image ID, which tells the server
-// which boot image has been selected by the client.
-type OptSelectedBootImageID struct {
- ID BootImageID
-}
-
-// ParseOptSelectedBootImageID constructs an OptSelectedBootImageID struct from a sequence of
-// bytes and returns it, or an error.
-func ParseOptSelectedBootImageID(data []byte) (*OptSelectedBootImageID, error) {
- var o OptSelectedBootImageID
- buf := uio.NewBigEndianBuffer(data)
- if err := o.ID.Unmarshal(buf); err != nil {
- return nil, err
- }
- return &o, buf.FinError()
-}
-
-// Code returns the option code.
-func (o *OptSelectedBootImageID) Code() dhcpv4.OptionCode {
- return OptionSelectedBootImageID
-}
-
-// ToBytes returns a serialized stream of bytes for this option.
-func (o *OptSelectedBootImageID) ToBytes() []byte {
- return uio.ToBigEndian(o.ID)
-}
-
-// String returns a human-readable string for this option.
-func (o *OptSelectedBootImageID) String() string {
- return fmt.Sprintf("BSDP Selected Boot Image ID -> %s", o.ID.String())
-}
diff --git a/dhcpv4/bsdp/bsdp_option_selected_boot_image_id_test.go b/dhcpv4/bsdp/bsdp_option_selected_boot_image_id_test.go
deleted file mode 100644
index e187fc7..0000000
--- a/dhcpv4/bsdp/bsdp_option_selected_boot_image_id_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package bsdp
-
-import (
- "testing"
-
- "github.com/stretchr/testify/require"
- "github.com/u-root/u-root/pkg/uio"
-)
-
-func TestOptSelectedBootImageIDInterfaceMethods(t *testing.T) {
- b := BootImageID{IsInstall: true, ImageType: BootImageTypeMacOSX, Index: 1001}
- o := OptSelectedBootImageID{b}
- require.Equal(t, OptionSelectedBootImageID, o.Code(), "Code")
- require.Equal(t, uio.ToBigEndian(b), o.ToBytes(), "ToBytes")
-}
-
-func TestParseOptSelectedBootImageID(t *testing.T) {
- b := BootImageID{IsInstall: true, ImageType: BootImageTypeMacOSX, Index: 1001}
- o, err := ParseOptSelectedBootImageID(uio.ToBigEndian(b))
- require.NoError(t, err)
- require.Equal(t, &OptSelectedBootImageID{b}, o)
-
- // Short byte stream
- data := []byte{}
- _, err = ParseOptSelectedBootImageID(data)
- require.Error(t, err, "should get error from short byte stream")
-
- // Bad length
- data = []byte{1, 0, 0, 0, 0}
- _, err = ParseOptSelectedBootImageID(data)
- require.Error(t, err, "should get error from bad length")
-}
-
-func TestOptSelectedBootImageIDString(t *testing.T) {
- b := BootImageID{IsInstall: true, ImageType: BootImageTypeMacOSX, Index: 1001}
- o := OptSelectedBootImageID{b}
- require.Equal(t, "BSDP Selected Boot Image ID -> [1001] installable macOS image", o.String())
-
- b = BootImageID{IsInstall: false, ImageType: BootImageTypeMacOS9, Index: 1001}
- o = OptSelectedBootImageID{b}
- require.Equal(t, "BSDP Selected Boot Image ID -> [1001] uninstallable macOS 9 image", o.String())
-
- b = BootImageID{IsInstall: false, ImageType: BootImageType(99), Index: 1001}
- o = OptSelectedBootImageID{b}
- require.Equal(t, "BSDP Selected Boot Image ID -> [1001] uninstallable unknown image", o.String())
-}
diff --git a/dhcpv4/bsdp/bsdp_option_server_identifier.go b/dhcpv4/bsdp/bsdp_option_server_identifier.go
deleted file mode 100644
index d1f5b6c..0000000
--- a/dhcpv4/bsdp/bsdp_option_server_identifier.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package bsdp
-
-import (
- "fmt"
- "net"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
- "github.com/u-root/u-root/pkg/uio"
-)
-
-// OptServerIdentifier implements the BSDP server identifier option.
-type OptServerIdentifier struct {
- ServerID net.IP
-}
-
-// ParseOptServerIdentifier returns a new OptServerIdentifier from a byte
-// stream, or error if any.
-func ParseOptServerIdentifier(data []byte) (*OptServerIdentifier, error) {
- buf := uio.NewBigEndianBuffer(data)
- return &OptServerIdentifier{ServerID: net.IP(buf.CopyN(net.IPv4len))}, buf.FinError()
-}
-
-// Code returns the option code.
-func (o *OptServerIdentifier) Code() dhcpv4.OptionCode {
- return OptionServerIdentifier
-}
-
-// ToBytes returns a serialized stream of bytes for this option.
-func (o *OptServerIdentifier) ToBytes() []byte {
- return o.ServerID.To4()
-}
-
-// String returns a human-readable string.
-func (o *OptServerIdentifier) String() string {
- return fmt.Sprintf("BSDP Server Identifier -> %v", o.ServerID.String())
-}
diff --git a/dhcpv4/bsdp/bsdp_option_server_identifier_test.go b/dhcpv4/bsdp/bsdp_option_server_identifier_test.go
deleted file mode 100644
index 5a77644..0000000
--- a/dhcpv4/bsdp/bsdp_option_server_identifier_test.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package bsdp
-
-import (
- "net"
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestOptServerIdentifierInterfaceMethods(t *testing.T) {
- ip := net.IP{192, 168, 0, 1}
- o := OptServerIdentifier{ServerID: ip}
- require.Equal(t, OptionServerIdentifier, o.Code(), "Code")
- expectedBytes := []byte{192, 168, 0, 1}
- require.Equal(t, expectedBytes, o.ToBytes(), "ToBytes")
- require.Equal(t, "BSDP Server Identifier -> 192.168.0.1", o.String(), "String")
-}
-
-func TestParseOptServerIdentifier(t *testing.T) {
- var (
- o *OptServerIdentifier
- err error
- )
- o, err = ParseOptServerIdentifier([]byte{})
- require.Error(t, err, "empty byte stream")
-
- o, err = ParseOptServerIdentifier([]byte{3, 4, 192})
- require.Error(t, err, "wrong IP length")
-
- o, err = ParseOptServerIdentifier([]byte{192, 168, 0, 1})
- require.NoError(t, err)
- require.Equal(t, net.IP{192, 168, 0, 1}, o.ServerID)
-}
diff --git a/dhcpv4/bsdp/bsdp_option_server_priority.go b/dhcpv4/bsdp/bsdp_option_server_priority.go
deleted file mode 100644
index f6fcf57..0000000
--- a/dhcpv4/bsdp/bsdp_option_server_priority.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package bsdp
-
-import (
- "fmt"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
- "github.com/u-root/u-root/pkg/uio"
-)
-
-// OptServerPriority represents an option encapsulating the server priority.
-type OptServerPriority struct {
- Priority uint16
-}
-
-// ParseOptServerPriority returns a new OptServerPriority from a byte stream, or
-// error if any.
-func ParseOptServerPriority(data []byte) (*OptServerPriority, error) {
- buf := uio.NewBigEndianBuffer(data)
- return &OptServerPriority{Priority: buf.Read16()}, buf.FinError()
-}
-
-// Code returns the option code.
-func (o *OptServerPriority) Code() dhcpv4.OptionCode {
- return OptionServerPriority
-}
-
-// ToBytes returns a serialized stream of bytes for this option.
-func (o *OptServerPriority) ToBytes() []byte {
- buf := uio.NewBigEndianBuffer(nil)
- buf.Write16(o.Priority)
- return buf.Data()
-}
-
-// String returns a human-readable string.
-func (o *OptServerPriority) String() string {
- return fmt.Sprintf("BSDP Server Priority -> %v", o.Priority)
-}
diff --git a/dhcpv4/bsdp/bsdp_option_server_priority_test.go b/dhcpv4/bsdp/bsdp_option_server_priority_test.go
deleted file mode 100644
index c4c96de..0000000
--- a/dhcpv4/bsdp/bsdp_option_server_priority_test.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package bsdp
-
-import (
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestOptServerPriorityInterfaceMethods(t *testing.T) {
- o := OptServerPriority{Priority: 100}
- require.Equal(t, OptionServerPriority, o.Code(), "Code")
- require.Equal(t, []byte{0, 100}, o.ToBytes(), "ToBytes")
- require.Equal(t, "BSDP Server Priority -> 100", o.String(), "String")
-}
-
-func TestParseOptServerPriority(t *testing.T) {
- var (
- o *OptServerPriority
- err error
- )
- o, err = ParseOptServerPriority([]byte{})
- require.Error(t, err, "empty byte stream")
-
- o, err = ParseOptServerPriority([]byte{1})
- require.Error(t, err, "short byte stream")
-
- o, err = ParseOptServerPriority([]byte{0, 100})
- require.NoError(t, err)
- require.Equal(t, uint16(100), o.Priority)
-}
diff --git a/dhcpv4/bsdp/bsdp_option_version.go b/dhcpv4/bsdp/bsdp_option_version.go
deleted file mode 100644
index d6b78c8..0000000
--- a/dhcpv4/bsdp/bsdp_option_version.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package bsdp
-
-import (
- "fmt"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
- "github.com/u-root/u-root/pkg/uio"
-)
-
-// Version is the BSDP protocol version. Can be one of 1.0 or 1.1.
-type Version [2]byte
-
-// Specific versions.
-var (
- Version1_0 = Version{1, 0}
- Version1_1 = Version{1, 1}
-)
-
-// ParseOptVersion constructs an OptVersion struct from a sequence of
-// bytes and returns it, or an error.
-func ParseOptVersion(data []byte) (Version, error) {
- buf := uio.NewBigEndianBuffer(data)
- var v Version
- buf.ReadBytes(v[:])
- return v, buf.FinError()
-}
-
-// Code returns the option code.
-func (o Version) Code() dhcpv4.OptionCode {
- return OptionVersion
-}
-
-// ToBytes returns a serialized stream of bytes for this option.
-func (o Version) ToBytes() []byte {
- return o[:]
-}
-
-// String returns a human-readable string for this option.
-func (o Version) String() string {
- return fmt.Sprintf("BSDP Version -> %d.%d", o[0], o[1])
-}
diff --git a/dhcpv4/bsdp/bsdp_option_version_test.go b/dhcpv4/bsdp/bsdp_option_version_test.go
deleted file mode 100644
index 69d4c86..0000000
--- a/dhcpv4/bsdp/bsdp_option_version_test.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package bsdp
-
-import (
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestOptVersionInterfaceMethods(t *testing.T) {
- o := Version1_1
- require.Equal(t, OptionVersion, o.Code(), "Code")
- require.Equal(t, []byte{1, 1}, o.ToBytes(), "ToBytes")
-}
-
-func TestParseOptVersion(t *testing.T) {
- data := []byte{1, 1}
- o, err := ParseOptVersion(data)
- require.NoError(t, err)
- require.Equal(t, Version1_1, o)
-
- // Short byte stream
- data = []byte{2}
- _, err = ParseOptVersion(data)
- require.Error(t, err, "should get error from short byte stream")
-}
-
-func TestOptVersionString(t *testing.T) {
- require.Equal(t, "BSDP Version -> 1.1", Version1_1.String())
-}
diff --git a/dhcpv4/bsdp/bsdp_test.go b/dhcpv4/bsdp/bsdp_test.go
index 638a408..e0378c2 100644
--- a/dhcpv4/bsdp/bsdp_test.go
+++ b/dhcpv4/bsdp/bsdp_test.go
@@ -12,9 +12,9 @@ import (
func RequireHasOption(t *testing.T, opts dhcpv4.Options, opt dhcpv4.Option) {
require.NotNil(t, opts, "must pass list of options")
require.NotNil(t, opt, "must pass option")
- require.True(t, opts.Has(opt.Code()))
- actual := opts.GetOne(opt.Code())
- require.Equal(t, opt, actual)
+ require.True(t, opts.Has(opt.Code))
+ actual := opts.Get(opt.Code)
+ require.Equal(t, opt.Value.ToBytes(), actual)
}
func TestParseBootImageListFromAck(t *testing.T) {
@@ -37,11 +37,11 @@ func TestParseBootImageListFromAck(t *testing.T) {
},
}
ack, _ := dhcpv4.New()
- ack.UpdateOption(&OptVendorSpecificInformation{
- []dhcpv4.Option{&OptBootImageList{expectedBootImages}},
- })
+ ack.UpdateOption(OptVendorOptions(
+ OptBootImageList(expectedBootImages...),
+ ))
- images, err := ParseBootImageListFromAck(*ack)
+ images, err := ParseBootImageListFromAck(ack)
require.NoError(t, err)
require.NotEmpty(t, images, "should get BootImages")
require.Equal(t, expectedBootImages, images, "should get same BootImages")
@@ -49,7 +49,7 @@ func TestParseBootImageListFromAck(t *testing.T) {
func TestParseBootImageListFromAckNoVendorOption(t *testing.T) {
ack, _ := dhcpv4.New()
- images, err := ParseBootImageListFromAck(*ack)
+ images, err := ParseBootImageListFromAck(ack)
require.Error(t, err)
require.Empty(t, images, "no BootImages")
}
@@ -70,14 +70,13 @@ func TestNewInformList_NoReplyPort(t *testing.T) {
require.True(t, m.Options.Has(dhcpv4.OptionParameterRequestList))
require.True(t, m.Options.Has(dhcpv4.OptionMaximumDHCPMessageSize))
- opt := m.GetOneOption(dhcpv4.OptionVendorSpecificInformation)
- require.NotNil(t, opt, "vendor opts not present")
- vendorInfo := opt.(*OptVendorSpecificInformation)
- require.True(t, vendorInfo.Options.Has(OptionMessageType))
- require.True(t, vendorInfo.Options.Has(OptionVersion))
+ vendorOpts := GetVendorOptions(m.Options)
+ require.NotNil(t, vendorOpts, "vendor opts not present")
+ require.True(t, vendorOpts.Has(OptionMessageType))
+ require.True(t, vendorOpts.Has(OptionVersion))
- opt = vendorInfo.GetOneOption(OptionMessageType)
- require.Equal(t, MessageTypeList, opt.(*OptMessageType).Type)
+ mt := GetMessageType(vendorOpts.Options)
+ require.Equal(t, MessageTypeList, mt)
}
func TestNewInformList_ReplyPort(t *testing.T) {
@@ -94,12 +93,12 @@ func TestNewInformList_ReplyPort(t *testing.T) {
m, err := NewInformList(hwAddr, localIP, replyPort)
require.NoError(t, err)
- opt := m.GetOneOption(dhcpv4.OptionVendorSpecificInformation)
- vendorInfo := opt.(*OptVendorSpecificInformation)
- require.True(t, vendorInfo.Options.Has(OptionReplyPort))
+ vendorOpts := GetVendorOptions(m.Options)
+ require.True(t, vendorOpts.Options.Has(OptionReplyPort))
- opt = vendorInfo.GetOneOption(OptionReplyPort)
- require.Equal(t, replyPort, opt.(*OptReplyPort).Port)
+ port, err := GetReplyPort(vendorOpts.Options)
+ require.NoError(t, err)
+ require.Equal(t, replyPort, port)
}
func newAck(hwAddr net.HardwareAddr, transactionID [4]byte) *dhcpv4.DHCPv4 {
@@ -108,7 +107,7 @@ func newAck(hwAddr net.HardwareAddr, transactionID [4]byte) *dhcpv4.DHCPv4 {
ack.TransactionID = transactionID
ack.HWType = iana.HWTypeEthernet
ack.ClientHWAddr = hwAddr
- ack.UpdateOption(&dhcpv4.OptMessageType{MessageType: dhcpv4.MessageTypeAck})
+ ack.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
return ack
}
@@ -126,9 +125,9 @@ func TestInformSelectForAck_Broadcast(t *testing.T) {
}
ack := newAck(hwAddr, tid)
ack.SetBroadcast()
- ack.UpdateOption(&dhcpv4.OptServerIdentifier{ServerID: serverID})
+ ack.UpdateOption(dhcpv4.OptServerIdentifier(serverID))
- m, err := InformSelectForAck(*ack, 0, bootImage)
+ m, err := InformSelectForAck(PacketFor(ack), 0, bootImage)
require.NoError(t, err)
require.Equal(t, dhcpv4.OpcodeBootRequest, m.OpCode)
require.Equal(t, ack.HWType, m.HWType)
@@ -140,17 +139,16 @@ func TestInformSelectForAck_Broadcast(t *testing.T) {
require.True(t, m.Options.Has(dhcpv4.OptionClassIdentifier))
require.True(t, m.Options.Has(dhcpv4.OptionParameterRequestList))
require.True(t, m.Options.Has(dhcpv4.OptionDHCPMessageType))
- opt := m.GetOneOption(dhcpv4.OptionDHCPMessageType)
- require.Equal(t, dhcpv4.MessageTypeInform, opt.(*dhcpv4.OptMessageType).MessageType)
+ mt := dhcpv4.GetMessageType(m.Options)
+ require.Equal(t, dhcpv4.MessageTypeInform, mt)
// Validate vendor opts.
require.True(t, m.Options.Has(dhcpv4.OptionVendorSpecificInformation))
- opt = m.GetOneOption(dhcpv4.OptionVendorSpecificInformation)
- vendorInfo := opt.(*OptVendorSpecificInformation)
- RequireHasOption(t, vendorInfo.Options, &OptMessageType{Type: MessageTypeSelect})
- require.True(t, vendorInfo.Options.Has(OptionVersion))
- RequireHasOption(t, vendorInfo.Options, &OptSelectedBootImageID{ID: bootImage.ID})
- RequireHasOption(t, vendorInfo.Options, &OptServerIdentifier{ServerID: serverID})
+ vendorOpts := GetVendorOptions(m.Options).Options
+ RequireHasOption(t, vendorOpts, OptMessageType(MessageTypeSelect))
+ require.True(t, vendorOpts.Has(OptionVersion))
+ RequireHasOption(t, vendorOpts, OptSelectedBootImageID(bootImage.ID))
+ RequireHasOption(t, vendorOpts, OptServerIdentifier(serverID))
}
func TestInformSelectForAck_NoServerID(t *testing.T) {
@@ -166,7 +164,7 @@ func TestInformSelectForAck_NoServerID(t *testing.T) {
}
ack := newAck(hwAddr, tid)
- _, err := InformSelectForAck(*ack, 0, bootImage)
+ _, err := InformSelectForAck(PacketFor(ack), 0, bootImage)
require.Error(t, err, "expect error for no server identifier option")
}
@@ -184,9 +182,9 @@ func TestInformSelectForAck_BadReplyPort(t *testing.T) {
}
ack := newAck(hwAddr, tid)
ack.SetBroadcast()
- ack.UpdateOption(&dhcpv4.OptServerIdentifier{ServerID: serverID})
+ ack.UpdateOption(dhcpv4.OptServerIdentifier(serverID))
- _, err := InformSelectForAck(*ack, 11223, bootImage)
+ _, err := InformSelectForAck(PacketFor(ack), 11223, bootImage)
require.Error(t, err, "expect error for > 1024 replyPort")
}
@@ -204,16 +202,15 @@ func TestInformSelectForAck_ReplyPort(t *testing.T) {
}
ack := newAck(hwAddr, tid)
ack.SetBroadcast()
- ack.UpdateOption(&dhcpv4.OptServerIdentifier{ServerID: serverID})
+ ack.UpdateOption(dhcpv4.OptServerIdentifier(serverID))
replyPort := uint16(999)
- m, err := InformSelectForAck(*ack, replyPort, bootImage)
+ m, err := InformSelectForAck(PacketFor(ack), replyPort, bootImage)
require.NoError(t, err)
require.True(t, m.Options.Has(dhcpv4.OptionVendorSpecificInformation))
- opt := m.GetOneOption(dhcpv4.OptionVendorSpecificInformation)
- vendorInfo := opt.(*OptVendorSpecificInformation)
- RequireHasOption(t, vendorInfo.Options, &OptReplyPort{Port: replyPort})
+ vendorOpts := GetVendorOptions(m.Options).Options
+ RequireHasOption(t, vendorOpts, OptReplyPort(replyPort))
}
func TestNewReplyForInformList_NoDefaultImage(t *testing.T) {
@@ -274,24 +271,24 @@ func TestNewReplyForInformList(t *testing.T) {
require.Equal(t, "bsdp.foo.com", ack.ServerHostName)
// Validate options.
- RequireHasOption(t, ack.Options, &dhcpv4.OptMessageType{MessageType: dhcpv4.MessageTypeAck})
- RequireHasOption(t, ack.Options, &dhcpv4.OptServerIdentifier{ServerID: net.IP{9, 9, 9, 9}})
- RequireHasOption(t, ack.Options, &dhcpv4.OptClassIdentifier{Identifier: AppleVendorID})
+ RequireHasOption(t, ack.Options, dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
+ RequireHasOption(t, ack.Options, dhcpv4.OptServerIdentifier(net.IP{9, 9, 9, 9}))
+ RequireHasOption(t, ack.Options, dhcpv4.OptClassIdentifier(AppleVendorID))
require.NotNil(t, ack.GetOneOption(dhcpv4.OptionVendorSpecificInformation))
// Vendor-specific options.
- vendorOpts := ack.GetOneOption(dhcpv4.OptionVendorSpecificInformation).(*OptVendorSpecificInformation)
- RequireHasOption(t, vendorOpts.Options, &OptMessageType{Type: MessageTypeList})
- RequireHasOption(t, vendorOpts.Options, &OptDefaultBootImageID{ID: images[0].ID})
- RequireHasOption(t, vendorOpts.Options, &OptServerPriority{Priority: 0x7070})
- RequireHasOption(t, vendorOpts.Options, &OptBootImageList{Images: images})
+ vendorOpts := GetVendorOptions(ack.Options).Options
+ RequireHasOption(t, vendorOpts, OptMessageType(MessageTypeList))
+ RequireHasOption(t, vendorOpts, OptDefaultBootImageID(images[0].ID))
+ RequireHasOption(t, vendorOpts, OptServerPriority(0x7070))
+ RequireHasOption(t, vendorOpts, OptBootImageList(images...))
// Add in selected boot image, ensure it's in the generated ACK.
config.SelectedImage = &images[0]
ack, err = NewReplyForInformList(inform, config)
require.NoError(t, err)
- vendorOpts = ack.GetOneOption(dhcpv4.OptionVendorSpecificInformation).(*OptVendorSpecificInformation)
- RequireHasOption(t, vendorOpts.Options, &OptSelectedBootImageID{ID: images[0].ID})
+ vendorOpts = GetVendorOptions(ack.Options).Options
+ RequireHasOption(t, vendorOpts, OptSelectedBootImageID(images[0].ID))
}
func TestNewReplyForInformSelect_NoSelectedImage(t *testing.T) {
@@ -352,30 +349,22 @@ func TestNewReplyForInformSelect(t *testing.T) {
require.Equal(t, "bsdp.foo.com", ack.ServerHostName)
// Validate options.
- RequireHasOption(t, ack.Options, &dhcpv4.OptMessageType{MessageType: dhcpv4.MessageTypeAck})
- RequireHasOption(t, ack.Options, &dhcpv4.OptServerIdentifier{ServerID: net.IP{9, 9, 9, 9}})
- RequireHasOption(t, ack.Options, &dhcpv4.OptServerIdentifier{ServerID: net.IP{9, 9, 9, 9}})
- RequireHasOption(t, ack.Options, &dhcpv4.OptClassIdentifier{Identifier: AppleVendorID})
+ RequireHasOption(t, ack.Options, dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
+ RequireHasOption(t, ack.Options, dhcpv4.OptServerIdentifier(net.IP{9, 9, 9, 9}))
+ RequireHasOption(t, ack.Options, dhcpv4.OptServerIdentifier(net.IP{9, 9, 9, 9}))
+ RequireHasOption(t, ack.Options, dhcpv4.OptClassIdentifier(AppleVendorID))
require.NotNil(t, ack.GetOneOption(dhcpv4.OptionVendorSpecificInformation))
- vendorOpts := ack.GetOneOption(dhcpv4.OptionVendorSpecificInformation).(*OptVendorSpecificInformation)
- RequireHasOption(t, vendorOpts.Options, &OptMessageType{Type: MessageTypeSelect})
- RequireHasOption(t, vendorOpts.Options, &OptSelectedBootImageID{ID: images[0].ID})
+ vendorOpts := GetVendorOptions(ack.Options)
+ RequireHasOption(t, vendorOpts.Options, OptMessageType(MessageTypeSelect))
+ RequireHasOption(t, vendorOpts.Options, OptSelectedBootImageID(images[0].ID))
}
func TestMessageTypeForPacket(t *testing.T) {
- var (
- pkt *dhcpv4.DHCPv4
- gotMessageType *MessageType
- )
-
- list := new(MessageType)
- *list = MessageTypeList
-
testcases := []struct {
tcName string
opts []dhcpv4.Option
- wantMessageType *MessageType
+ wantMessageType MessageType
}{
{
tcName: "No options",
@@ -384,45 +373,38 @@ func TestMessageTypeForPacket(t *testing.T) {
{
tcName: "Some options, no vendor opts",
opts: []dhcpv4.Option{
- &dhcpv4.OptHostName{HostName: "foobar1234"},
+ dhcpv4.OptHostName("foobar1234"),
},
},
{
tcName: "Vendor opts, no message type",
opts: []dhcpv4.Option{
- &dhcpv4.OptHostName{HostName: "foobar1234"},
- &OptVendorSpecificInformation{
- Options: []dhcpv4.Option{
- Version1_1,
- },
- },
+ dhcpv4.OptHostName("foobar1234"),
+ OptVendorOptions(
+ OptVersion(Version1_1),
+ ),
},
},
{
tcName: "Vendor opts, with message type",
opts: []dhcpv4.Option{
- &dhcpv4.OptHostName{HostName: "foobar1234"},
- &OptVendorSpecificInformation{
- Options: []dhcpv4.Option{
- Version1_1,
- &OptMessageType{Type: MessageTypeList},
- },
- },
+ dhcpv4.OptHostName("foobar1234"),
+ OptVendorOptions(
+ OptVersion(Version1_1),
+ OptMessageType(MessageTypeList),
+ ),
},
- wantMessageType: list,
+ wantMessageType: MessageTypeList,
},
}
for _, tt := range testcases {
t.Run(tt.tcName, func(t *testing.T) {
- pkt, _ = dhcpv4.New()
+ pkt, _ := dhcpv4.New()
for _, opt := range tt.opts {
pkt.UpdateOption(opt)
}
- gotMessageType = MessageTypeFromPacket(pkt)
+ gotMessageType := MessageTypeFromPacket(pkt)
require.Equal(t, tt.wantMessageType, gotMessageType)
- if tt.wantMessageType != nil {
- require.Equal(t, *tt.wantMessageType, *gotMessageType)
- }
})
}
}
diff --git a/dhcpv4/bsdp/client.go b/dhcpv4/bsdp/client.go
index dd4a0a0..e8ca2ca 100644
--- a/dhcpv4/bsdp/client.go
+++ b/dhcpv4/bsdp/client.go
@@ -18,24 +18,10 @@ func NewClient() *Client {
return &Client{Client: dhcpv4.Client{}}
}
-func castVendorOpt(ack *dhcpv4.DHCPv4) {
- opts := ack.Options
- for i := 0; i < len(opts); i++ {
- if opts[i].Code() == dhcpv4.OptionVendorSpecificInformation {
- vendorOpt, err := ParseOptVendorSpecificInformation(opts[i].ToBytes())
- // Oh well, we tried
- if err != nil {
- return
- }
- opts[i] = vendorOpt
- }
- }
-}
-
// Exchange runs a full BSDP exchange (Inform[list], Ack, Inform[select],
// Ack). Returns a list of DHCPv4 structures representing the exchange.
-func (c *Client) Exchange(ifname string) ([]*dhcpv4.DHCPv4, error) {
- conversation := make([]*dhcpv4.DHCPv4, 0)
+func (c *Client) Exchange(ifname string) ([]*Packet, error) {
+ conversation := make([]*Packet, 0)
// Get our file descriptor for the broadcast socket.
sendFd, err := dhcpv4.MakeBroadcastSocket(ifname)
@@ -55,17 +41,16 @@ func (c *Client) Exchange(ifname string) ([]*dhcpv4.DHCPv4, error) {
conversation = append(conversation, informList)
// ACK[LIST]
- ackForList, err := c.Client.SendReceive(sendFd, recvFd, informList, dhcpv4.MessageTypeAck)
+ ackForList, err := c.Client.SendReceive(sendFd, recvFd, informList.v4(), dhcpv4.MessageTypeAck)
if err != nil {
return conversation, err
}
// Rewrite vendor-specific option for pretty printing.
- castVendorOpt(ackForList)
- conversation = append(conversation, ackForList)
+ conversation = append(conversation, PacketFor(ackForList))
// Parse boot images sent back by server
- bootImages, err := ParseBootImageListFromAck(*ackForList)
+ bootImages, err := ParseBootImageListFromAck(ackForList)
if err != nil {
return conversation, err
}
@@ -74,17 +59,16 @@ func (c *Client) Exchange(ifname string) ([]*dhcpv4.DHCPv4, error) {
}
// INFORM[SELECT]
- informSelect, err := InformSelectForAck(*ackForList, dhcpv4.ClientPort, bootImages[0])
+ informSelect, err := InformSelectForAck(PacketFor(ackForList), dhcpv4.ClientPort, bootImages[0])
if err != nil {
return conversation, err
}
conversation = append(conversation, informSelect)
// ACK[SELECT]
- ackForSelect, err := c.Client.SendReceive(sendFd, recvFd, informSelect, dhcpv4.MessageTypeAck)
- castVendorOpt(ackForSelect)
+ ackForSelect, err := c.Client.SendReceive(sendFd, recvFd, informSelect.v4(), dhcpv4.MessageTypeAck)
if err != nil {
return conversation, err
}
- return append(conversation, ackForSelect), nil
+ return append(conversation, PacketFor(ackForSelect)), nil
}
diff --git a/dhcpv4/bsdp/option_vendor_specific_information.go b/dhcpv4/bsdp/option_vendor_specific_information.go
index a87135f..4e107e1 100644
--- a/dhcpv4/bsdp/option_vendor_specific_information.go
+++ b/dhcpv4/bsdp/option_vendor_specific_information.go
@@ -1,93 +1,87 @@
package bsdp
import (
- "strings"
+ "fmt"
"github.com/insomniacslk/dhcp/dhcpv4"
- "github.com/u-root/u-root/pkg/uio"
)
-// OptVendorSpecificInformation encapsulates the BSDP-specific options used for
-// the protocol.
-type OptVendorSpecificInformation struct {
- Options dhcpv4.Options
+// VendorOptions is like dhcpv4.Options, but stringifies using BSDP-specific
+// option codes.
+type VendorOptions struct {
+ dhcpv4.Options
}
-// parseOption is similar to dhcpv4.ParseOption, except that it switches based
-// on the BSDP specific options.
-func parseOption(code dhcpv4.OptionCode, data []byte) (dhcpv4.Option, error) {
- var (
- opt dhcpv4.Option
- err error
- )
- switch code {
- 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(code, data)
- }
- if err != nil {
- return nil, err
- }
- return opt, nil
+// String prints the contained options using BSDP-specific option code parsing.
+func (v VendorOptions) String() string {
+ return v.Options.ToString(bsdpHumanizer)
}
-// codeGetter is a dhcpv4.OptionCodeGetter for BSDP optionCodes.
-func codeGetter(c uint8) dhcpv4.OptionCode {
- return optionCode(c)
+// FromBytes parses vendor options from
+func (v *VendorOptions) FromBytes(data []byte) error {
+ v.Options = make(dhcpv4.Options)
+ return v.Options.FromBytes(data)
}
-// ParseOptVendorSpecificInformation constructs an OptVendorSpecificInformation struct from a sequence of
-// bytes and returns it, or an error.
-func ParseOptVendorSpecificInformation(data []byte) (*OptVendorSpecificInformation, error) {
- options, err := dhcpv4.OptionsFromBytesWithParser(data, codeGetter, parseOption, false /* don't check for OptionEnd tag */)
- if err != nil {
- return nil, err
+// OptVendorOptions returns the BSDP Vendor Specific Info in o.
+func OptVendorOptions(o ...dhcpv4.Option) dhcpv4.Option {
+ return dhcpv4.Option{
+ Code: dhcpv4.OptionVendorSpecificInformation,
+ Value: VendorOptions{dhcpv4.OptionsFromList(o...)},
}
- return &OptVendorSpecificInformation{options}, nil
}
-// Code returns the option code.
-func (o *OptVendorSpecificInformation) Code() dhcpv4.OptionCode {
- return dhcpv4.OptionVendorSpecificInformation
+// GetVendorOptions returns a new BSDP Vendor Specific Info option.
+func GetVendorOptions(o dhcpv4.Options) *VendorOptions {
+ v := o.Get(dhcpv4.OptionVendorSpecificInformation)
+ if v == nil {
+ return nil
+ }
+ var vo VendorOptions
+ if err := vo.FromBytes(v); err != nil {
+ return nil
+ }
+ return &vo
}
-// ToBytes returns a serialized stream of bytes for this option.
-func (o *OptVendorSpecificInformation) ToBytes() []byte {
- return uio.ToBigEndian(o.Options)
+var bsdpHumanizer = dhcpv4.OptionHumanizer{
+ ValueHumanizer: parseOption,
+ CodeHumanizer: func(c uint8) dhcpv4.OptionCode {
+ return optionCode(c)
+ },
}
-// 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
-}
+// parseOption is similar to dhcpv4.parseOption, except that it interprets
+// option codes based on the BSDP-specific options.
+func parseOption(code dhcpv4.OptionCode, data []byte) fmt.Stringer {
+ var d dhcpv4.OptionDecoder
+ switch code {
+ case OptionMachineName:
+ var s dhcpv4.String
+ d = &s
+
+ case OptionServerIdentifier:
+ d = &dhcpv4.IP{}
+
+ case OptionServerPriority, OptionReplyPort:
+ var u dhcpv4.Uint16
+ d = &u
+
+ case OptionBootImageList:
+ d = &BootImageList{}
-// GetOneOption returns the first suboption that matches the OptionCode code.
-func (o *OptVendorSpecificInformation) GetOneOption(code dhcpv4.OptionCode) dhcpv4.Option {
- return o.Options.GetOne(code)
+ case OptionDefaultBootImageID, OptionSelectedBootImageID:
+ d = &BootImageID{}
+
+ case OptionMessageType:
+ var m MessageType
+ d = &m
+
+ case OptionVersion:
+ d = &Version{}
+ }
+ if d != nil && d.FromBytes(data) == nil {
+ return d
+ }
+ return dhcpv4.OptionGeneric{data}
}
diff --git a/dhcpv4/bsdp/option_vendor_specific_information_test.go b/dhcpv4/bsdp/option_vendor_specific_information_test.go
index ede8a0b..a6727f5 100644
--- a/dhcpv4/bsdp/option_vendor_specific_information_test.go
+++ b/dhcpv4/bsdp/option_vendor_specific_information_test.go
@@ -1,6 +1,7 @@
package bsdp
import (
+ "net"
"testing"
"github.com/insomniacslk/dhcp/dhcpv4"
@@ -8,182 +9,71 @@ import (
)
func TestOptVendorSpecificInformationInterfaceMethods(t *testing.T) {
- messageTypeOpt := &OptMessageType{MessageTypeList}
- versionOpt := Version1_1
- o := &OptVendorSpecificInformation{[]dhcpv4.Option{messageTypeOpt, versionOpt}}
- require.Equal(t, dhcpv4.OptionVendorSpecificInformation, o.Code(), "Code")
-
- expectedBytes := []byte{
- 1, 1, 1, // List option
- 2, 2, 1, 1, // Version option
- }
- o = &OptVendorSpecificInformation{
- []dhcpv4.Option{
- &OptMessageType{MessageTypeList},
- Version1_1,
- },
- }
- require.Equal(t, expectedBytes, o.ToBytes(), "ToBytes")
-}
-
-func TestParseOptVendorSpecificInformation(t *testing.T) {
- var (
- o *OptVendorSpecificInformation
- err error
+ o := OptVendorOptions(
+ OptVersion(Version1_1),
+ OptMessageType(MessageTypeList),
)
- o, err = ParseOptVendorSpecificInformation([]byte{1, 2})
- require.Error(t, err, "short byte stream")
-
- // Good byte stream
- data := []byte{
- 1, 1, 1, // List option
- 2, 2, 1, 1, // Version option
- }
- o, err = ParseOptVendorSpecificInformation(data)
- require.NoError(t, err)
- expected := &OptVendorSpecificInformation{
- []dhcpv4.Option{
- &OptMessageType{MessageTypeList},
- Version1_1,
- },
- }
- require.Equal(t, 2, len(o.Options), "number of parsed suboptions")
- typ := o.GetOneOption(OptionMessageType)
- version := o.GetOneOption(OptionVersion)
- require.Equal(t, expected.Options[0].Code(), typ.Code())
- require.Equal(t, expected.Options[1].Code(), version.Code())
-
- // Short byte stream (length and data mismatch)
- data = []byte{
- 1, 1, 1, // List option
- 2, 2, 1, // Version option
- }
- o, err = ParseOptVendorSpecificInformation(data)
- require.Error(t, err)
-
- // Bad option
- data = []byte{
- 1, 1, 1, // List option
- 2, 2, 1, // Version option
- 5, 3, 1, 1, 1, // Reply port option
- }
- o, err = ParseOptVendorSpecificInformation(data)
- require.Error(t, err)
+ require.Equal(t, dhcpv4.OptionVendorSpecificInformation, o.Code, "Code")
- // Boot images + default.
- data = []byte{
+ expectedBytes := []byte{
1, 1, 1, // List option
2, 2, 1, 1, // Version option
- 5, 2, 1, 1, // Reply port option
-
- // Boot image list
- 9, 22,
- 0x1, 0x0, 0x03, 0xe9, // ID
- 6, // name length
- 'b', 's', 'd', 'p', '-', '1',
- 0x80, 0x0, 0x23, 0x31, // ID
- 6, // name length
- 'b', 's', 'd', 'p', '-', '2',
-
- // Default Boot Image ID
- 7, 4, 0x1, 0x0, 0x03, 0xe9,
}
- o, err = ParseOptVendorSpecificInformation(data)
- require.NoError(t, err)
- require.Equal(t, 5, len(o.Options))
- for _, opt := range []dhcpv4.OptionCode{
- OptionMessageType,
- OptionVersion,
- OptionReplyPort,
- OptionBootImageList,
- OptionDefaultBootImageID,
- } {
- require.True(t, o.Options.Has(opt))
- }
- optBootImage := o.GetOneOption(OptionBootImageList).(*OptBootImageList)
- expectedBootImages := []BootImage{
- BootImage{
- ID: BootImageID{
- IsInstall: false,
- ImageType: BootImageTypeMacOSX,
- Index: 1001,
- },
- Name: "bsdp-1",
- },
- BootImage{
- ID: BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOS9,
- Index: 9009,
- },
- Name: "bsdp-2",
- },
- }
- require.Equal(t, expectedBootImages, optBootImage.Images)
+ require.Equal(t, expectedBytes, o.Value.ToBytes(), "ToBytes")
}
func TestOptVendorSpecificInformationString(t *testing.T) {
- o := &OptVendorSpecificInformation{
- []dhcpv4.Option{
- &OptMessageType{MessageTypeList},
- Version1_1,
- },
- }
- expectedString := "Vendor Specific Information ->\n BSDP Message Type -> LIST\n BSDP Version -> 1.1"
+ o := OptVendorOptions(
+ OptMessageType(MessageTypeList),
+ OptVersion(Version1_1),
+ )
+ expectedString := "Vendor Specific Information:\n BSDP Message Type: LIST\n BSDP Version: 1.1\n"
require.Equal(t, expectedString, o.String())
// Test more complicated string - sub options of sub options.
- o = &OptVendorSpecificInformation{
- []dhcpv4.Option{
- &OptMessageType{MessageTypeList},
- &OptBootImageList{
- []BootImage{
- BootImage{
- ID: BootImageID{
- IsInstall: false,
- ImageType: BootImageTypeMacOSX,
- Index: 1001,
- },
- Name: "bsdp-1",
- },
- BootImage{
- ID: BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOS9,
- Index: 9009,
- },
- Name: "bsdp-2",
- },
+ o = OptVendorOptions(
+ OptMessageType(MessageTypeList),
+ OptBootImageList(
+ BootImage{
+ ID: BootImageID{
+ IsInstall: false,
+ ImageType: BootImageTypeMacOSX,
+ Index: 1001,
},
+ Name: "bsdp-1",
},
- },
- }
- expectedString = "Vendor Specific Information ->\n" +
- " BSDP Message Type -> LIST\n" +
- " BSDP Boot Image List ->\n" +
- " bsdp-1 [1001] uninstallable macOS image\n" +
- " bsdp-2 [9009] installable macOS 9 image"
+ BootImage{
+ ID: BootImageID{
+ IsInstall: true,
+ ImageType: BootImageTypeMacOS9,
+ Index: 9009,
+ },
+ Name: "bsdp-2",
+ },
+ ),
+ OptMachineName("foo"),
+ OptServerIdentifier(net.IP{1, 1, 1, 1}),
+ OptServerPriority(1234),
+ OptReplyPort(1235),
+ OptDefaultBootImageID(BootImageID{
+ IsInstall: true,
+ ImageType: BootImageTypeMacOS9,
+ Index: 9009,
+ }),
+ OptSelectedBootImageID(BootImageID{
+ IsInstall: true,
+ ImageType: BootImageTypeMacOS9,
+ Index: 9009,
+ }),
+ )
+ expectedString = "Vendor Specific Information:\n" +
+ " BSDP Message Type: LIST\n" +
+ " BSDP Server Identifier: 1.1.1.1\n" +
+ " BSDP Server Priority: 1234\n" +
+ " BSDP Reply Port: 1235\n" +
+ " BSDP Default Boot Image ID: [9009] installable macOS 9 image\n" +
+ " BSDP Selected Boot Image ID: [9009] installable macOS 9 image\n" +
+ " BSDP Boot Image List: bsdp-1 [1001] uninstallable macOS image, bsdp-2 [9009] installable macOS 9 image\n" +
+ " BSDP Machine Name: foo\n"
require.Equal(t, expectedString, o.String())
}
-
-func TestOptVendorSpecificInformationGetOneOption(t *testing.T) {
- // No option
- o := &OptVendorSpecificInformation{
- []dhcpv4.Option{
- &OptMessageType{MessageTypeList},
- Version1_1,
- },
- }
- foundOpt := o.GetOneOption(OptionBootImageList)
- require.Nil(t, foundOpt, "should not get options")
-
- // One option
- o = &OptVendorSpecificInformation{
- []dhcpv4.Option{
- &OptMessageType{MessageTypeList},
- Version1_1,
- },
- }
- foundOpt = o.GetOneOption(OptionMessageType)
- require.Equal(t, MessageTypeList, foundOpt.(*OptMessageType).Type)
-}
diff --git a/dhcpv4/bsdp/types.go b/dhcpv4/bsdp/types.go
index 4ce840f..4931081 100644
--- a/dhcpv4/bsdp/types.go
+++ b/dhcpv4/bsdp/types.go
@@ -1,5 +1,9 @@
package bsdp
+import (
+ "fmt"
+)
+
// DefaultMacOSVendorClassIdentifier is a default vendor class identifier used
// on non-darwin hosts where the vendor class identifier cannot be determined.
// It should mostly be used for debugging if testing BSDP on a non-darwin
@@ -19,7 +23,7 @@ func (o optionCode) String() string {
if s, ok := optionCodeToString[o]; ok {
return s
}
- return "unknown"
+ return fmt.Sprintf("unknown (%d)", o)
}
// Options (occur as sub-options of DHCP option 43).