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 }