github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/boot/ibft/ibft.go (about)

     1  // Copyright 2019 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package ibft defines the iSCSI Boot Firmware Table.
     6  //
     7  // An iBFT is placed in memory by a bootloader to tell an OS which iSCSI target
     8  // it was booted from and what additional iSCSI targets it shall connect to.
     9  //
    10  // An iBFT is typically placed in one of two places:
    11  //
    12  //  (1) an ACPI table named "iBFT", or
    13  //
    14  //  (2) in the 512K-1M physical memory range identified by its first 4 bytes.
    15  //
    16  // However, this package doesn't concern itself with the placement, just the
    17  // marshaling of the table's bytes.
    18  package ibft
    19  
    20  import (
    21  	"encoding/binary"
    22  	"fmt"
    23  	"net"
    24  
    25  	"github.com/u-root/u-root/pkg/uio"
    26  )
    27  
    28  var (
    29  	signature  = [4]byte{'i', 'B', 'F', 'T'}
    30  	oemID      = [6]byte{'G', 'o', 'o', 'g', 'l', 'e'}
    31  	oemTableID = [8]byte{'G', 'o', 'o', 'g', 'I', 'B', 'F', 'T'}
    32  )
    33  
    34  type structureID uint8
    35  
    36  const (
    37  	reserved structureID = iota
    38  	ibftControlID
    39  	ibftInitiatorID
    40  	ibftNICID
    41  	ibftTargetID
    42  	ibftExtensionsID
    43  )
    44  
    45  const (
    46  	ibftControlLen   uint16 = 18
    47  	ibftInitiatorLen uint16 = 74
    48  	ibftNICLen       uint16 = 102
    49  	ibftTargetLen    uint16 = 54
    50  )
    51  
    52  type acpiHeader struct {
    53  	Signature       [4]byte
    54  	Length          uint32
    55  	Revision        uint8
    56  	Checksum        uint8
    57  	OEMID           [6]byte
    58  	OEMTableID      [8]byte
    59  	OEMRevision     uint64
    60  	CreatorID       uint64
    61  	CreatorRevision uint64
    62  }
    63  
    64  func flags(s ...bool) uint8 {
    65  	var i uint8
    66  	for bit, f := range s {
    67  		if f {
    68  			i |= 1 << uint8(bit)
    69  		} else {
    70  			i &= ^(1 << uint8(bit))
    71  		}
    72  	}
    73  	return i
    74  }
    75  
    76  func writeIP6(l *uio.Lexer, ip net.IP) {
    77  	if ip == nil {
    78  		l.WriteBytes(net.IPv6zero)
    79  	} else {
    80  		l.WriteBytes(ip.To16())
    81  	}
    82  }
    83  
    84  // ibftStructHeader is the header of each iBFT structure, as defined by iBFT Spec 1.4.2.
    85  type ibftStructHeader struct {
    86  	StructureID structureID
    87  	Version     uint8
    88  	Length      uint16
    89  	Index       uint8
    90  	Flags       uint8
    91  }
    92  
    93  // Initiator defines an iSCSI initiator.
    94  type Initiator struct {
    95  	// Name is the name of the initiator.
    96  	//
    97  	// Some servers apparently use initiator names for permissions.
    98  	Name string
    99  
   100  	Valid bool
   101  
   102  	// Boot indicates that this initiator was used to boot.
   103  	Boot bool
   104  
   105  	// I have no clue what this stuff is.
   106  	SNSServer             net.IP
   107  	SLPServer             net.IP
   108  	PrimaryRadiusServer   net.IP
   109  	SecondaryRadiusServer net.IP
   110  }
   111  
   112  func (i *Initiator) marshal(h *heapTable) {
   113  	header := ibftStructHeader{
   114  		StructureID: ibftInitiatorID,
   115  		Version:     1,
   116  		Length:      ibftInitiatorLen,
   117  		Index:       0,
   118  		Flags:       flags(i.Valid, i.Boot),
   119  	}
   120  	h.Table.WriteData(&header)
   121  
   122  	writeIP6(h.Table, i.SNSServer)
   123  	writeIP6(h.Table, i.SLPServer)
   124  	writeIP6(h.Table, i.PrimaryRadiusServer)
   125  	writeIP6(h.Table, i.SecondaryRadiusServer)
   126  	h.writeHeap([]byte(i.Name))
   127  }
   128  
   129  // BDF is a Bus/Device/Function Identifier of a PCI device.
   130  type BDF struct {
   131  	Bus      uint8
   132  	Device   uint8
   133  	Function uint8
   134  }
   135  
   136  func (b BDF) marshal(h *heapTable) {
   137  	h.Table.Write16(uint16(uint16(b.Bus)<<8 | uint16(b.Device)<<3 | uint16(b.Function)))
   138  }
   139  
   140  // Origin is the source of network configuration; for example, DHCP or static
   141  // configuration.
   142  //
   143  // The spec links to a Microsoft.com 404 page. We got this info from
   144  // iPXE code, but most likely it refers to the NL_PREFIX_ORIGIN enumeration in
   145  // nldef.h, which can currently be found here
   146  // https://docs.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_prefix_origin
   147  type Origin uint8
   148  
   149  // These values and descriptions were taken from the NL_PREFIX_ORIGIN
   150  // enumeration documentation for Windows.
   151  //
   152  // They can also be found in iPXE source at src/include/ipxe/ibft.h.
   153  const (
   154  	// OriginOther means the IP prefix was provided by a source other than
   155  	// those in this enumeration.
   156  	OriginOther Origin = 0
   157  
   158  	// OriginManual means the IP address prefix was manually specified.
   159  	OriginManual Origin = 1
   160  
   161  	// OriginWellKnown means the IP address prefix is from a well-known
   162  	// source.
   163  	OriginWellKnown Origin = 2
   164  
   165  	// OriginDHCP means the IP addrss prefix was provided by DHCP settings.
   166  	OriginDHCP Origin = 3
   167  
   168  	// OriginRA means the IP address prefix was obtained through a router
   169  	// advertisement (RA).
   170  	OriginRA Origin = 4
   171  
   172  	// OriginUnchanged menas the IP address prefix should be unchanged.
   173  	// This value is used when setting the properties for a unicast IP
   174  	// interface when the value for the IP prefix origin should be left
   175  	// unchanged.
   176  	OriginUnchanged Origin = 0xf
   177  )
   178  
   179  // NIC defines NIC network configuration such as IP, gateway, DNS.
   180  type NIC struct {
   181  	// Valid NIC.
   182  	Valid bool
   183  
   184  	// Boot indicates this NIC was used to boot from.
   185  	Boot bool
   186  
   187  	// Global indicates a globally reachable IP (as opposed to a link-local IP).
   188  	Global bool
   189  
   190  	// IPNet is the IP and subnet mask for this network interface.
   191  	IPNet *net.IPNet
   192  
   193  	// Origin is the source of the IP address prefix.
   194  	//
   195  	// It's hard to say what loaded operating systems do with this field.
   196  	//
   197  	// The Windows docs for NL_PREFIX_ORIGIN consider it the origin of only
   198  	// the IP address prefix. iPXE only ever sets the Manual and DHCP
   199  	// constants, even in all IPv6 cases where it came from RAs.
   200  	Origin Origin
   201  
   202  	// Gateway is the network gateway. The gateway must be within the IPNet.
   203  	Gateway net.IP
   204  
   205  	// PrimaryDNS is the primary DNS server.
   206  	PrimaryDNS net.IP
   207  
   208  	// SecondaryDNS is a secondary DNS server.
   209  	SecondaryDNS net.IP
   210  
   211  	// DHCPServer is the address for the DHCP server, if one was used.
   212  	DHCPServer net.IP
   213  
   214  	// VLAN is the VLAN for this network interface.
   215  	VLAN uint16
   216  
   217  	// MACAddress is the MAC of the network interface.
   218  	MACAddress net.HardwareAddr
   219  
   220  	// PCIBDF is the Bus/Device/Function identifier of the PCI NIC device.
   221  	PCIBDF BDF
   222  
   223  	// HostName is the host name of the machine.
   224  	HostName string
   225  }
   226  
   227  func (n *NIC) marshal(h *heapTable) {
   228  	header := ibftStructHeader{
   229  		StructureID: ibftNICID,
   230  		Version:     1,
   231  		Length:      ibftNICLen,
   232  		Index:       0,
   233  		Flags:       flags(n.Valid, n.Boot, n.Global),
   234  	}
   235  	h.Table.WriteData(&header)
   236  
   237  	// IP + subnet mask prefix.
   238  	if n.IPNet == nil {
   239  		writeIP6(h.Table, net.IPv6zero)
   240  		h.Table.Write8(0)
   241  	} else {
   242  		writeIP6(h.Table, n.IPNet.IP)
   243  		ones, _ := n.IPNet.Mask.Size()
   244  		h.Table.Write8(uint8(ones))
   245  	}
   246  
   247  	h.Table.Write8(uint8(n.Origin))
   248  
   249  	writeIP6(h.Table, n.Gateway)
   250  	writeIP6(h.Table, n.PrimaryDNS)
   251  	writeIP6(h.Table, n.SecondaryDNS)
   252  	writeIP6(h.Table, n.DHCPServer)
   253  
   254  	h.Table.Write16(n.VLAN)
   255  	copy(h.Table.WriteN(6), n.MACAddress)
   256  	n.PCIBDF.marshal(h)
   257  	h.writeHeap([]byte(n.HostName))
   258  }
   259  
   260  // Target carries info about an iSCSI target server.
   261  type Target struct {
   262  	Valid bool
   263  	Boot  bool
   264  
   265  	CHAP  bool
   266  	RCHAP bool
   267  
   268  	// Target is the server to connect to.
   269  	Target *net.TCPAddr
   270  
   271  	// BootLUN is the LUN to connect to.
   272  	BootLUN uint64
   273  
   274  	CHAPType uint8
   275  
   276  	// NICAssociation is the Index of the NIC.
   277  	NICAssociation uint8
   278  
   279  	// TargetName is the name of the iSCSI target.
   280  	//
   281  	// The target name must be a valid iSCSI Qualifier Name or EUI.
   282  	TargetName string
   283  
   284  	CHAPName          string
   285  	CHAPSecret        string
   286  	ReverseCHAPName   string
   287  	ReverseCHAPSecret string
   288  }
   289  
   290  func (t *Target) marshal(h *heapTable) {
   291  	header := ibftStructHeader{
   292  		StructureID: ibftTargetID,
   293  		Version:     1,
   294  		Length:      ibftTargetLen,
   295  		Index:       0,
   296  		Flags:       flags(t.Valid, t.Boot, t.CHAP, t.RCHAP),
   297  	}
   298  	h.Table.WriteData(&header)
   299  
   300  	if t.Target == nil {
   301  		writeIP6(h.Table, net.IPv6zero)
   302  		h.Table.Write16(0)
   303  	} else {
   304  		writeIP6(h.Table, t.Target.IP)
   305  		h.Table.Write16(uint16(t.Target.Port))
   306  	}
   307  	h.Table.Write64(t.BootLUN)
   308  	h.Table.Write8(t.CHAPType)
   309  	h.Table.Write8(t.NICAssociation)
   310  
   311  	h.writeHeap([]byte(t.TargetName))
   312  	h.writeHeap([]byte(t.CHAPName))
   313  	h.writeHeap([]byte(t.CHAPSecret))
   314  	h.writeHeap([]byte(t.ReverseCHAPName))
   315  	h.writeHeap([]byte(t.ReverseCHAPSecret))
   316  }
   317  
   318  // IBFT defines the entire iSCSI boot firmware table.
   319  type IBFT struct {
   320  	SingleLoginMode bool
   321  
   322  	// Initiator offset: 0x50
   323  	Initiator Initiator
   324  
   325  	// NIC offset: 0xa0
   326  	NIC0 NIC
   327  
   328  	// Target offset: 0x110
   329  	Target0 Target
   330  }
   331  
   332  // String is a short summary of the iBFT contents.
   333  func (i *IBFT) String() string {
   334  	return fmt.Sprintf("iBFT(iSCSI target=%s, IP=%s)", i.Target0.Target, i.NIC0.IPNet)
   335  }
   336  
   337  type heapTable struct {
   338  	heapOffset uint64
   339  
   340  	Table *uio.Lexer
   341  	Heap  *uio.Lexer
   342  }
   343  
   344  func (h *heapTable) writeHeap(item []byte) {
   345  	if len(item) == 0 {
   346  		// Length.
   347  		h.Table.Write16(0)
   348  
   349  		// Offset.
   350  		h.Table.Write16(0)
   351  	} else {
   352  		offset := h.heapOffset + uint64(h.Heap.Len())
   353  
   354  		// Length.
   355  		h.Table.Write16(uint16(len(item)))
   356  
   357  		// Offset from beginning of iBFT.
   358  		h.Table.Write16(uint16(offset))
   359  
   360  		// Write a null-terminated array item.
   361  		//
   362  		// iBFT Spec, Section 1.3.5: "All array items stored in the
   363  		// Heap area will be followed by a separate NULL. This
   364  		// terminating NULL is not counted as part of the array
   365  		// [item's] length."
   366  		h.Heap.WriteBytes(item)
   367  		h.Heap.Write8(0)
   368  	}
   369  }
   370  
   371  type ibftControl struct {
   372  	SingleLoginMode bool
   373  	Initiator       uint16
   374  	NIC0            uint16
   375  	Target0         uint16
   376  	NIC1            uint16
   377  	Target1         uint16
   378  }
   379  
   380  func (c ibftControl) marshal(h *heapTable) {
   381  	header := ibftStructHeader{
   382  		StructureID: ibftControlID,
   383  		Version:     1,
   384  		Length:      ibftControlLen,
   385  		Index:       0,
   386  		Flags:       flags(c.SingleLoginMode),
   387  	}
   388  	h.Table.WriteData(&header)
   389  
   390  	// Extensions: none. EVER.
   391  	h.Table.Write16(0)
   392  
   393  	h.Table.Write16(c.Initiator)
   394  	h.Table.Write16(c.NIC0)
   395  	h.Table.Write16(c.Target0)
   396  	h.Table.Write16(c.NIC1)
   397  	h.Table.Write16(c.Target1)
   398  }
   399  
   400  func gencsum(b []byte) byte {
   401  	var csum byte
   402  	for _, bb := range b {
   403  		csum += bb
   404  	}
   405  	return ^csum + 1
   406  }
   407  
   408  const lengthOffset = 4
   409  const checksumOffset = 9
   410  
   411  func fixACPIHeader(b []byte) []byte {
   412  	binary.LittleEndian.PutUint16(b[lengthOffset:], uint16(len(b)))
   413  	b[checksumOffset] = gencsum(b)
   414  	return b
   415  }
   416  
   417  const (
   418  	controlOffset   = 0x30
   419  	initiatorOffset = 0x48
   420  	nic0Offset      = 0x98
   421  	target0Offset   = 0x100
   422  	heapOffset      = 0x138
   423  )
   424  
   425  // Marshal returns a binary representation of the iBFT.
   426  //
   427  // Pointers within an iBFT is relative, so this can be placed anywhere
   428  // necessary.
   429  func (i *IBFT) Marshal() []byte {
   430  	h := &heapTable{
   431  		heapOffset: heapOffset,
   432  
   433  		Table: uio.NewLittleEndianBuffer(nil),
   434  		Heap:  uio.NewLittleEndianBuffer(nil),
   435  	}
   436  
   437  	header := &acpiHeader{
   438  		Signature:       signature,
   439  		Length:          0,
   440  		Revision:        1,
   441  		Checksum:        0,
   442  		OEMID:           oemID,
   443  		OEMTableID:      oemTableID,
   444  		OEMRevision:     0,
   445  		CreatorID:       0,
   446  		CreatorRevision: 0,
   447  	}
   448  	h.Table.WriteData(header)
   449  
   450  	// iBFT spec, Section 1.4.4.4 "Each structure must be aligned on an 8
   451  	// byte boundary."
   452  
   453  	// 0x30
   454  	h.Table.Align(8)
   455  	control := ibftControl{
   456  		SingleLoginMode: i.SingleLoginMode,
   457  		Initiator:       initiatorOffset,
   458  		NIC0:            nic0Offset,
   459  		Target0:         target0Offset,
   460  	}
   461  	control.marshal(h)
   462  
   463  	// 0x48
   464  	h.Table.Align(8)
   465  	i.Initiator.marshal(h)
   466  
   467  	// 0x98
   468  	h.Table.Align(8)
   469  	i.NIC0.marshal(h)
   470  
   471  	// 0x100
   472  	h.Table.Align(8)
   473  	i.Target0.marshal(h)
   474  
   475  	// 0x138
   476  	h.Table.Align(8)
   477  	h.Table.WriteBytes(h.Heap.Data())
   478  
   479  	return fixACPIHeader(h.Table.Data())
   480  }