summaryrefslogtreecommitdiffhomepage
path: root/dhcpv4/bsdp.go
diff options
context:
space:
mode:
Diffstat (limited to 'dhcpv4/bsdp.go')
-rw-r--r--dhcpv4/bsdp.go349
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
-}