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 }