diff options
Diffstat (limited to 'dhcpv4/bsdp.go')
-rw-r--r-- | dhcpv4/bsdp.go | 349 |
1 files changed, 0 insertions, 349 deletions
diff --git a/dhcpv4/bsdp.go b/dhcpv4/bsdp.go deleted file mode 100644 index 4096ce6..0000000 --- a/dhcpv4/bsdp.go +++ /dev/null @@ -1,349 +0,0 @@ -// +build darwin - -package dhcpv4 - -// Implements Apple's netboot protocol BSDP (Boot Service Discovery Protocol). -// Canonical implementation is defined here: -// http://opensource.apple.com/source/bootp/bootp-198.1/Documentation/BSDP.doc - -import ( - "encoding/binary" - "fmt" - "syscall" -) - -// Options (occur as sub-options of DHCP option 43). -const ( - BSDPOptionMessageType OptionCode = iota + 1 - BSDPOptionVersion - BSDPOptionServerIdentifier - BSDPOptionServerPriority - BSDPOptionReplyPort - BSDPOptionBootImageListPath // Not used - BSDPOptionDefaultBootImageID - BSDPOptionSelectedBootImageID - BSDPOptionBootImageList - BSDPOptionNetboot1_0Firmware - BSDPOptionBootImageAttributesFilterList - BSDPOptionShadowMountPath OptionCode = 128 - BSDPOptionShadowFilePath OptionCode = 129 - BSDPOptionMachineName OptionCode = 130 -) - -// Versions -var ( - BSDPVersion1_0 = []byte{1, 0} - BSDPVersion1_1 = []byte{1, 1} -) - -// BSDP message types -const ( - BSDPMessageTypeList byte = iota + 1 - BSDPMessageTypeSelect - BSDPMessageTypeFailed -) - -// Boot image kinds -const ( - BSDPBootImageMacOS9 byte = iota - BSDPBootImageMacOSX - BSDPBootImageMacOSXServer - BSDPBootImageHardwareDiagnostics - // 0x4 - 0x7f are reserved for future use. -) - -// BootImageID describes a boot image ID - whether it's an install image and -// what kind of boot image (e.g. OS 9, macOS, hardware diagnostics) -type BootImageID struct { - isInstall bool - imageKind byte - index uint16 -} - -// toBytes serializes a BootImageID to network-order bytes. -func (b BootImageID) toBytes() (bytes []byte) { - bytes = make([]byte, 4) - // Attributes. - if b.isInstall { - bytes[0] |= 0x80 - } - bytes[0] |= b.imageKind - - // Index - binary.BigEndian.PutUint16(bytes[2:], b.index) - return -} - -// BootImageIDFromBytes deserializes a collection of 4 bytes to a BootImageID. -func bootImageIDFromBytes(bytes []byte) BootImageID { - return BootImageID{ - isInstall: bytes[0]&0x80 != 0, - imageKind: bytes[0] & 0x7f, - index: binary.BigEndian.Uint16(bytes[2:]), - } -} - -// BootImage describes a boot image - contains the boot image ID and the name. -type BootImage struct { - ID BootImageID - // This is a utf-8 string. - Name string -} - -// toBytes converts a BootImage to a slice of bytes. -func (b *BootImage) toBytes() (bytes []byte) { - idBytes := b.ID.toBytes() - bytes = append(bytes, idBytes[:]...) - bytes = append(bytes, byte(len(b.Name))) - bytes = append(bytes, []byte(b.Name)...) - return -} - -// BootImageFromBytes returns a deserialized BootImage struct from bytes as well -// as the number of bytes read from the slice. -func bootImageFromBytes(bytes []byte) (*BootImage, int, error) { - // If less than length of boot image ID and count, it's probably invalid. - if len(bytes) < 5 { - return nil, 0, fmt.Errorf("not enough bytes for BootImage") - } - imageID := bootImageIDFromBytes(bytes[:4]) - nameLength := int(bytes[4]) - if 5+nameLength > len(bytes) { - return nil, 0, fmt.Errorf("not enough bytes for BootImage") - } - name := string(bytes[5 : 5+nameLength]) - return &BootImage{ID: imageID, Name: name}, 5 + nameLength, nil -} - -// makeVendorClassIdentifier calls the sysctl syscall on macOS to get the -// platform model. -func makeVendorClassIdentifier() (string, error) { - // Fetch hardware model for class ID. - hwModel, err := syscall.Sysctl("hw.model") - if err != nil { - return "", err - } - vendorClassID := fmt.Sprintf("AAPLBSDPC/i386/%s", hwModel) - return vendorClassID, nil -} - -// parseBootImagesFromBSDPOption parses data from the BSDPOptionBootImageList -// option and returns a list of BootImages. -func parseBootImagesFromBSDPOption(data []byte) ([]BootImage, error) { - // Should at least have the # bytes of boot images. - if len(data) < 4 { - return nil, fmt.Errorf("invalid length boot image list") - } - - readByteCount := 0 - start := data - var bootImages []BootImage - for { - bootImage, readBytes, err := bootImageFromBytes(start) - if err != nil { - return nil, err - } - bootImages = append(bootImages, *bootImage) - readByteCount += readBytes - if readByteCount+1 >= len(data) { - break - } - start = start[readByteCount:] - } - - return bootImages, nil -} - -// parseVendorOptionsFromOptions extracts the sub-options list of the vendor- -// specific options from the larger DHCP options list. -func parseVendorOptionsFromOptions(options []Option) []Option { - var vendorOpts []Option - var err error - for _, opt := range options { - if opt.Code == OptionVendorSpecificInformation { - vendorOpts, err = OptionsFromBytes(opt.Data) - if err != nil { - return []Option{} - } - break - } - } - return vendorOpts -} - -// 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) ([]BootImage, error) { - var bootImages []BootImage - vendorOpts := parseVendorOptionsFromOptions(ack.options) - for _, opt := range vendorOpts { - if opt.Code == BSDPOptionBootImageList { - images, err := parseBootImagesFromBSDPOption(opt.Data) - if err != nil { - return nil, err - } - bootImages = append(bootImages, images...) - } - } - - return bootImages, nil -} - -// NewInformListForInterface creates a new INFORM packet for interface ifname -// with configuration options specified by config. -func NewInformListForInterface(iface string, replyPort uint16) (*DHCPv4, error) { - d, err := NewInformForInterface(iface /* needsBroadcast */, false) - if err != nil { - return nil, err - } - - // These are vendor-specific options used to pass along BSDP information. - vendorOpts := []Option{ - Option{ - Code: BSDPOptionMessageType, - Data: []byte{BSDPMessageTypeList}, - }, - Option{ - Code: BSDPOptionVersion, - Data: BSDPVersion1_1, - }, - } - - // If specified, replyPort MUST be a priviledged port. - if replyPort != 0 && replyPort != ClientPort { - if replyPort >= 1024 { - return nil, fmt.Errorf("replyPort must be a priviledged port (< 1024)") - } - bytes := make([]byte, 3) - bytes[0] = 2 - binary.BigEndian.PutUint16(bytes[1:], replyPort) - d.AddOption(Option{ - Code: BSDPOptionReplyPort, - Data: bytes, - }) - } - d.AddOption(Option{ - Code: OptionVendorSpecificInformation, - Data: OptionsToBytes(vendorOpts), - }) - - d.AddOption(Option{ - Code: OptionParameterRequestList, - Data: []byte{OptionVendorSpecificInformation, OptionClassIdentifier}, - }) - - u16 := make([]byte, 2) - binary.BigEndian.PutUint16(u16, 1500) - d.AddOption(Option{ - Code: OptionMaximumDHCPMessageSize, - Data: u16, - }) - - vendorClassID, err := makeVendorClassIdentifier() - if err != nil { - return nil, err - } - d.AddOption(Option{ - Code: OptionClassIdentifier, - Data: []byte(vendorClassID), - }) - - d.AddOption(Option{Code: OptionEnd}) - return d, nil -} - -// InformSelectForAck constructs an INFORM[SELECT] packet given an ACK to the -// previously-sent INFORM[LIST] with BSDPConfig config. -func InformSelectForAck(ack DHCPv4, replyPort uint16, selectedImage BootImage) (*DHCPv4, error) { - d, err := New() - if err != nil { - return nil, err - } - d.SetOpcode(OpcodeBootRequest) - d.SetHwType(ack.HwType()) - d.SetHwAddrLen(ack.HwAddrLen()) - clientHwAddr := ack.ClientHwAddr() - d.SetClientHwAddr(clientHwAddr[:]) - d.SetTransactionID(ack.TransactionID()) - if ack.IsBroadcast() { - d.SetBroadcast() - } else { - d.SetUnicast() - } - - // Data for BSDPOptionSelectedBootImageID - vendorOpts := []Option{ - Option{ - Code: BSDPOptionMessageType, - Data: []byte{BSDPMessageTypeSelect}, - }, - Option{ - Code: BSDPOptionVersion, - Data: BSDPVersion1_1, - }, - Option{ - Code: BSDPOptionSelectedBootImageID, - Data: selectedImage.ID.toBytes(), - }, - } - - // Find server IP address - var serverIP []byte - for _, opt := range ack.options { - if opt.Code == OptionServerIdentifier { - serverIP = make([]byte, 4) - copy(serverIP, opt.Data) - } - } - if len(serverIP) == 0 { - return nil, fmt.Errorf("could not parse server identifier from ACK") - } - vendorOpts = append(vendorOpts, Option{ - Code: BSDPOptionServerIdentifier, - Data: serverIP, - }) - - // Validate replyPort if requested. - if replyPort != 0 && replyPort != ClientPort { - // replyPort MUST be a priviledged port. - if replyPort >= 1024 { - return nil, fmt.Errorf("replyPort must be a priviledged port") - } - bytes := make([]byte, 3) - bytes[0] = 2 - binary.BigEndian.PutUint16(bytes[1:], replyPort) - vendorOpts = append(vendorOpts, Option{ - Code: BSDPOptionReplyPort, - Data: bytes, - }) - } - - vendorClassID, err := makeVendorClassIdentifier() - if err != nil { - return nil, err - } - d.AddOption(Option{ - Code: OptionClassIdentifier, - Data: []byte(vendorClassID), - }) - d.AddOption(Option{ - Code: OptionParameterRequestList, - Data: []byte{ - OptionSubnetMask, - OptionRouter, - OptionBootfileName, - OptionVendorSpecificInformation, - OptionClassIdentifier, - }, - }) - d.AddOption(Option{ - Code: OptionDHCPMessageType, - Data: []byte{MessageTypeInform}, - }) - d.AddOption(Option{ - Code: OptionVendorSpecificInformation, - Data: OptionsToBytes(vendorOpts), - }) - d.AddOption(Option{Code: OptionEnd}) - return d, nil -} |