github.com/hugelgupf/u-root@v0.0.0-20191023214958-4807c632154c/pkg/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  	"net"
    23  
    24  	"github.com/u-root/u-root/pkg/uio"
    25  )
    26  
    27  var (
    28  	signature  = [4]byte{'i', 'B', 'F', 'T'}
    29  	oemID      = [6]byte{'G', 'o', 'o', 'g', 'l', 'e'}
    30  	oemTableID = [8]byte{'G', 'o', 'o', 'g', 'I', 'B', 'F', 'T'}
    31  )
    32  
    33  type structureID uint8
    34  
    35  const (
    36  	reserved structureID = iota
    37  	ibftControlID
    38  	ibftInitiatorID
    39  	ibftNICID
    40  	ibftTargetID
    41  	ibftExtensionsID
    42  )
    43  
    44  const (
    45  	ibftControlLen   uint16 = 18
    46  	ibftInitiatorLen uint16 = 74
    47  	ibftNICLen       uint16 = 102
    48  	ibftTargetLen    uint16 = 54
    49  )
    50  
    51  type acpiHeader struct {
    52  	Signature       [4]byte
    53  	Length          uint32
    54  	Revision        uint8
    55  	Checksum        uint8
    56  	OEMID           [6]byte
    57  	OEMTableID      [8]byte
    58  	OEMRevision     uint64
    59  	CreatorID       uint64
    60  	CreatorRevision uint64
    61  }
    62  
    63  func flags(s ...bool) uint8 {
    64  	var i uint8
    65  	for bit, f := range s {
    66  		if f {
    67  			i |= 1 << uint8(bit)
    68  		} else {
    69  			i &= ^(1 << uint8(bit))
    70  		}
    71  	}
    72  	return i
    73  }
    74  
    75  func writeIP6(l *uio.Lexer, ip net.IP) {
    76  	if ip == nil {
    77  		l.WriteBytes(net.IPv6zero)
    78  	} else {
    79  		l.WriteBytes(ip.To16())
    80  	}
    81  }
    82  
    83  // ibftStructHeader is the header of each iBFT structure, as defined by iBFT Spec 1.4.2.
    84  type ibftStructHeader struct {
    85  	StructureID structureID
    86  	Version     uint8
    87  	Length      uint16
    88  	Index       uint8
    89  	Flags       uint8
    90  }
    91  
    92  // Initiator defines an iSCSI initiator.
    93  type Initiator struct {
    94  	// Name is the name of the initiator.
    95  	//
    96  	// Some servers apparently use initiator names for permissions.
    97  	Name string
    98  
    99  	Valid bool
   100  
   101  	// Boot indicates that this initiator was used to boot.
   102  	Boot bool
   103  
   104  	// I have no clue what this stuff is.
   105  	SNSServer             net.IP
   106  	SLPServer             net.IP
   107  	PrimaryRadiusServer   net.IP
   108  	SecondaryRadiusServer net.IP
   109  }
   110  
   111  func (i *Initiator) marshal(h *heapTable) {
   112  	header := ibftStructHeader{
   113  		StructureID: ibftInitiatorID,
   114  		Version:     1,
   115  		Length:      ibftInitiatorLen,
   116  		Index:       0,
   117  		Flags:       flags(i.Valid, i.Boot),
   118  	}
   119  	h.Table.WriteData(&header)
   120  
   121  	writeIP6(h.Table, i.SNSServer)
   122  	writeIP6(h.Table, i.SLPServer)
   123  	writeIP6(h.Table, i.PrimaryRadiusServer)
   124  	writeIP6(h.Table, i.SecondaryRadiusServer)
   125  	h.writeHeap([]byte(i.Name))
   126  }
   127  
   128  // BDF is a Bus/Device/Function Identifier of a PCI device.
   129  type BDF struct {
   130  	Bus      uint8
   131  	Device   uint8
   132  	Function uint8
   133  }
   134  
   135  func (b BDF) marshal(h *heapTable) {
   136  	h.Table.Write16(uint16(uint16(b.Bus)<<8 | uint16(b.Device)<<3 | uint16(b.Function)))
   137  }
   138  
   139  // NIC defines NIC network configuration such as IP, gateway, DNS.
   140  type NIC struct {
   141  	// Valid NIC.
   142  	Valid bool
   143  
   144  	// Boot indicates this NIC was used to boot from.
   145  	Boot bool
   146  
   147  	// Global indicates a globally reachable IP (as opposed to a link-local IP).
   148  	Global bool
   149  
   150  	// IPNet is the IP and subnet mask for this network interface.
   151  	IPNet *net.IPNet
   152  
   153  	// Origin. Nobody knows what this is. Nobody cares.
   154  	//
   155  	// The spec links to a Microsoft.com 404 page. Yay.
   156  	Origin uint8
   157  
   158  	// Gateway is the network gateway. The gateway must be within the IPNet.
   159  	Gateway net.IP
   160  
   161  	// PrimaryDNS is the primary DNS server.
   162  	PrimaryDNS net.IP
   163  
   164  	// SecondaryDNS is a secondary DNS server.
   165  	SecondaryDNS net.IP
   166  
   167  	// DHCPServer is the address for the DHCP server, if one was used.
   168  	DHCPServer net.IP
   169  
   170  	// VLAN is the VLAN for this network interface.
   171  	VLAN uint16
   172  
   173  	// MACAddress is the MAC of the network interface.
   174  	MACAddress net.HardwareAddr
   175  
   176  	// PCIBDF is the Bus/Device/Function identifier of the PCI NIC device.
   177  	PCIBDF BDF
   178  
   179  	// HostName is the host name of the machine.
   180  	HostName string
   181  }
   182  
   183  func (n *NIC) marshal(h *heapTable) {
   184  	header := ibftStructHeader{
   185  		StructureID: ibftNICID,
   186  		Version:     1,
   187  		Length:      ibftNICLen,
   188  		Index:       0,
   189  		Flags:       flags(n.Valid, n.Boot, n.Global),
   190  	}
   191  	h.Table.WriteData(&header)
   192  
   193  	// IP + subnet mask prefix.
   194  	if n.IPNet == nil {
   195  		writeIP6(h.Table, net.IPv6zero)
   196  		h.Table.Write8(0)
   197  	} else {
   198  		writeIP6(h.Table, n.IPNet.IP)
   199  		ones, _ := n.IPNet.Mask.Size()
   200  		h.Table.Write8(uint8(ones))
   201  	}
   202  
   203  	h.Table.Write8(n.Origin)
   204  
   205  	writeIP6(h.Table, n.Gateway)
   206  	writeIP6(h.Table, n.PrimaryDNS)
   207  	writeIP6(h.Table, n.SecondaryDNS)
   208  	writeIP6(h.Table, n.DHCPServer)
   209  
   210  	h.Table.Write16(n.VLAN)
   211  	copy(h.Table.WriteN(6), n.MACAddress)
   212  	n.PCIBDF.marshal(h)
   213  	h.writeHeap([]byte(n.HostName))
   214  }
   215  
   216  // Target carries info about an iSCSI target server.
   217  type Target struct {
   218  	Valid bool
   219  	Boot  bool
   220  
   221  	CHAP  bool
   222  	RCHAP bool
   223  
   224  	// Target is the server to connect to.
   225  	Target *net.TCPAddr
   226  
   227  	// BootLUN is the LUN to connect to.
   228  	BootLUN uint64
   229  
   230  	CHAPType uint8
   231  
   232  	// NICAssociation is the Index of the NIC.
   233  	NICAssociation uint8
   234  
   235  	// TargetName is the name of the iSCSI target.
   236  	//
   237  	// The target name must be a valid iSCSI Qualifier Name or EUI.
   238  	TargetName string
   239  
   240  	CHAPName          string
   241  	CHAPSecret        string
   242  	ReverseCHAPName   string
   243  	ReverseCHAPSecret string
   244  }
   245  
   246  func (t *Target) marshal(h *heapTable) {
   247  	header := ibftStructHeader{
   248  		StructureID: ibftTargetID,
   249  		Version:     1,
   250  		Length:      ibftTargetLen,
   251  		Index:       0,
   252  		Flags:       flags(t.Valid, t.Boot, t.CHAP, t.RCHAP),
   253  	}
   254  	h.Table.WriteData(&header)
   255  
   256  	if t.Target == nil {
   257  		writeIP6(h.Table, net.IPv6zero)
   258  		h.Table.Write16(0)
   259  	} else {
   260  		writeIP6(h.Table, t.Target.IP)
   261  		h.Table.Write16(uint16(t.Target.Port))
   262  	}
   263  	h.Table.Write64(t.BootLUN)
   264  	h.Table.Write8(t.CHAPType)
   265  	h.Table.Write8(t.NICAssociation)
   266  
   267  	h.writeHeap([]byte(t.TargetName))
   268  	h.writeHeap([]byte(t.CHAPName))
   269  	h.writeHeap([]byte(t.CHAPSecret))
   270  	h.writeHeap([]byte(t.ReverseCHAPName))
   271  	h.writeHeap([]byte(t.ReverseCHAPSecret))
   272  }
   273  
   274  // IBFT defines the entire iSCSI boot firmware table.
   275  type IBFT struct {
   276  	SingleLoginMode bool
   277  
   278  	// Initiator offset: 0x50
   279  	Initiator Initiator
   280  
   281  	// NIC offset: 0xa0
   282  	NIC0 NIC
   283  
   284  	// Target offset: 0x110
   285  	Target0 Target
   286  }
   287  
   288  type heapTable struct {
   289  	heapOffset uint64
   290  
   291  	Table *uio.Lexer
   292  	Heap  *uio.Lexer
   293  }
   294  
   295  func (h *heapTable) writeHeap(item []byte) {
   296  	if len(item) == 0 {
   297  		// Length.
   298  		h.Table.Write16(0)
   299  
   300  		// Offset.
   301  		h.Table.Write16(0)
   302  	} else {
   303  		offset := h.heapOffset + uint64(h.Heap.Len())
   304  
   305  		// Length.
   306  		h.Table.Write16(uint16(len(item)))
   307  
   308  		// Offset from beginning of iBFT.
   309  		h.Table.Write16(uint16(offset))
   310  
   311  		// Write a null-terminated array item.
   312  		//
   313  		// iBFT Spec, Section 1.3.5: "All array items stored in the
   314  		// Heap area will be followed by a separate NULL. This
   315  		// terminating NULL is not counted as part of the array
   316  		// [item's] length."
   317  		h.Heap.WriteBytes(item)
   318  		h.Heap.Write8(0)
   319  	}
   320  }
   321  
   322  type ibftControl struct {
   323  	SingleLoginMode bool
   324  	Initiator       uint16
   325  	NIC0            uint16
   326  	Target0         uint16
   327  	NIC1            uint16
   328  	Target1         uint16
   329  }
   330  
   331  func (c ibftControl) marshal(h *heapTable) {
   332  	header := ibftStructHeader{
   333  		StructureID: ibftControlID,
   334  		Version:     1,
   335  		Length:      ibftControlLen,
   336  		Index:       0,
   337  		Flags:       flags(c.SingleLoginMode),
   338  	}
   339  	h.Table.WriteData(&header)
   340  
   341  	// Extensions: none. EVER.
   342  	h.Table.Write16(0)
   343  
   344  	h.Table.Write16(c.Initiator)
   345  	h.Table.Write16(c.NIC0)
   346  	h.Table.Write16(c.Target0)
   347  	h.Table.Write16(c.NIC1)
   348  	h.Table.Write16(c.Target1)
   349  }
   350  
   351  func gencsum(b []byte) byte {
   352  	var csum byte
   353  	for _, bb := range b {
   354  		csum += bb
   355  	}
   356  	return ^csum + 1
   357  }
   358  
   359  const lengthOffset = 4
   360  const checksumOffset = 9
   361  
   362  func fixACPIHeader(b []byte) []byte {
   363  	binary.LittleEndian.PutUint16(b[lengthOffset:], uint16(len(b)))
   364  	b[checksumOffset] = gencsum(b)
   365  	return b
   366  }
   367  
   368  const (
   369  	controlOffset   = 0x30
   370  	initiatorOffset = 0x48
   371  	nic0Offset      = 0x98
   372  	target0Offset   = 0x100
   373  	heapOffset      = 0x138
   374  )
   375  
   376  // Marshal returns a binary representation of the iBFT.
   377  //
   378  // Pointers within an iBFT is relative, so this can be placed anywhere
   379  // necessary.
   380  func (i *IBFT) Marshal() []byte {
   381  	h := &heapTable{
   382  		heapOffset: heapOffset,
   383  
   384  		Table: uio.NewLittleEndianBuffer(nil),
   385  		Heap:  uio.NewLittleEndianBuffer(nil),
   386  	}
   387  
   388  	header := &acpiHeader{
   389  		Signature:       signature,
   390  		Length:          0,
   391  		Revision:        1,
   392  		Checksum:        0,
   393  		OEMID:           oemID,
   394  		OEMTableID:      oemTableID,
   395  		OEMRevision:     0,
   396  		CreatorID:       0,
   397  		CreatorRevision: 0,
   398  	}
   399  	h.Table.WriteData(header)
   400  
   401  	// iBFT spec, Section 1.4.4.4 "Each structure must be aligned on an 8
   402  	// byte boundary."
   403  
   404  	// 0x30
   405  	h.Table.Align(8)
   406  	control := ibftControl{
   407  		SingleLoginMode: i.SingleLoginMode,
   408  		Initiator:       initiatorOffset,
   409  		NIC0:            nic0Offset,
   410  		Target0:         target0Offset,
   411  	}
   412  	control.marshal(h)
   413  
   414  	// 0x48
   415  	h.Table.Align(8)
   416  	i.Initiator.marshal(h)
   417  
   418  	// 0x98
   419  	h.Table.Align(8)
   420  	i.NIC0.marshal(h)
   421  
   422  	// 0x100
   423  	h.Table.Align(8)
   424  	i.Target0.marshal(h)
   425  
   426  	// 0x138
   427  	h.Table.Align(8)
   428  	h.Table.WriteBytes(h.Heap.Data())
   429  
   430  	return fixACPIHeader(h.Table.Data())
   431  }