github.com/zaolin/u-root@v0.0.0-20200428085104-64aaafd46c6d/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 // NIC defines NIC network configuration such as IP, gateway, DNS. 141 type NIC struct { 142 // Valid NIC. 143 Valid bool 144 145 // Boot indicates this NIC was used to boot from. 146 Boot bool 147 148 // Global indicates a globally reachable IP (as opposed to a link-local IP). 149 Global bool 150 151 // IPNet is the IP and subnet mask for this network interface. 152 IPNet *net.IPNet 153 154 // Origin. Nobody knows what this is. Nobody cares. 155 // 156 // The spec links to a Microsoft.com 404 page. Yay. 157 Origin uint8 158 159 // Gateway is the network gateway. The gateway must be within the IPNet. 160 Gateway net.IP 161 162 // PrimaryDNS is the primary DNS server. 163 PrimaryDNS net.IP 164 165 // SecondaryDNS is a secondary DNS server. 166 SecondaryDNS net.IP 167 168 // DHCPServer is the address for the DHCP server, if one was used. 169 DHCPServer net.IP 170 171 // VLAN is the VLAN for this network interface. 172 VLAN uint16 173 174 // MACAddress is the MAC of the network interface. 175 MACAddress net.HardwareAddr 176 177 // PCIBDF is the Bus/Device/Function identifier of the PCI NIC device. 178 PCIBDF BDF 179 180 // HostName is the host name of the machine. 181 HostName string 182 } 183 184 func (n *NIC) marshal(h *heapTable) { 185 header := ibftStructHeader{ 186 StructureID: ibftNICID, 187 Version: 1, 188 Length: ibftNICLen, 189 Index: 0, 190 Flags: flags(n.Valid, n.Boot, n.Global), 191 } 192 h.Table.WriteData(&header) 193 194 // IP + subnet mask prefix. 195 if n.IPNet == nil { 196 writeIP6(h.Table, net.IPv6zero) 197 h.Table.Write8(0) 198 } else { 199 writeIP6(h.Table, n.IPNet.IP) 200 ones, _ := n.IPNet.Mask.Size() 201 h.Table.Write8(uint8(ones)) 202 } 203 204 h.Table.Write8(n.Origin) 205 206 writeIP6(h.Table, n.Gateway) 207 writeIP6(h.Table, n.PrimaryDNS) 208 writeIP6(h.Table, n.SecondaryDNS) 209 writeIP6(h.Table, n.DHCPServer) 210 211 h.Table.Write16(n.VLAN) 212 copy(h.Table.WriteN(6), n.MACAddress) 213 n.PCIBDF.marshal(h) 214 h.writeHeap([]byte(n.HostName)) 215 } 216 217 // Target carries info about an iSCSI target server. 218 type Target struct { 219 Valid bool 220 Boot bool 221 222 CHAP bool 223 RCHAP bool 224 225 // Target is the server to connect to. 226 Target *net.TCPAddr 227 228 // BootLUN is the LUN to connect to. 229 BootLUN uint64 230 231 CHAPType uint8 232 233 // NICAssociation is the Index of the NIC. 234 NICAssociation uint8 235 236 // TargetName is the name of the iSCSI target. 237 // 238 // The target name must be a valid iSCSI Qualifier Name or EUI. 239 TargetName string 240 241 CHAPName string 242 CHAPSecret string 243 ReverseCHAPName string 244 ReverseCHAPSecret string 245 } 246 247 func (t *Target) marshal(h *heapTable) { 248 header := ibftStructHeader{ 249 StructureID: ibftTargetID, 250 Version: 1, 251 Length: ibftTargetLen, 252 Index: 0, 253 Flags: flags(t.Valid, t.Boot, t.CHAP, t.RCHAP), 254 } 255 h.Table.WriteData(&header) 256 257 if t.Target == nil { 258 writeIP6(h.Table, net.IPv6zero) 259 h.Table.Write16(0) 260 } else { 261 writeIP6(h.Table, t.Target.IP) 262 h.Table.Write16(uint16(t.Target.Port)) 263 } 264 h.Table.Write64(t.BootLUN) 265 h.Table.Write8(t.CHAPType) 266 h.Table.Write8(t.NICAssociation) 267 268 h.writeHeap([]byte(t.TargetName)) 269 h.writeHeap([]byte(t.CHAPName)) 270 h.writeHeap([]byte(t.CHAPSecret)) 271 h.writeHeap([]byte(t.ReverseCHAPName)) 272 h.writeHeap([]byte(t.ReverseCHAPSecret)) 273 } 274 275 // IBFT defines the entire iSCSI boot firmware table. 276 type IBFT struct { 277 SingleLoginMode bool 278 279 // Initiator offset: 0x50 280 Initiator Initiator 281 282 // NIC offset: 0xa0 283 NIC0 NIC 284 285 // Target offset: 0x110 286 Target0 Target 287 } 288 289 // String is a short summary of the iBFT contents. 290 func (i *IBFT) String() string { 291 return fmt.Sprintf("iBFT(iSCSI target=%s, IP=%s)", i.Target0.Target, i.NIC0.IPNet) 292 } 293 294 type heapTable struct { 295 heapOffset uint64 296 297 Table *uio.Lexer 298 Heap *uio.Lexer 299 } 300 301 func (h *heapTable) writeHeap(item []byte) { 302 if len(item) == 0 { 303 // Length. 304 h.Table.Write16(0) 305 306 // Offset. 307 h.Table.Write16(0) 308 } else { 309 offset := h.heapOffset + uint64(h.Heap.Len()) 310 311 // Length. 312 h.Table.Write16(uint16(len(item))) 313 314 // Offset from beginning of iBFT. 315 h.Table.Write16(uint16(offset)) 316 317 // Write a null-terminated array item. 318 // 319 // iBFT Spec, Section 1.3.5: "All array items stored in the 320 // Heap area will be followed by a separate NULL. This 321 // terminating NULL is not counted as part of the array 322 // [item's] length." 323 h.Heap.WriteBytes(item) 324 h.Heap.Write8(0) 325 } 326 } 327 328 type ibftControl struct { 329 SingleLoginMode bool 330 Initiator uint16 331 NIC0 uint16 332 Target0 uint16 333 NIC1 uint16 334 Target1 uint16 335 } 336 337 func (c ibftControl) marshal(h *heapTable) { 338 header := ibftStructHeader{ 339 StructureID: ibftControlID, 340 Version: 1, 341 Length: ibftControlLen, 342 Index: 0, 343 Flags: flags(c.SingleLoginMode), 344 } 345 h.Table.WriteData(&header) 346 347 // Extensions: none. EVER. 348 h.Table.Write16(0) 349 350 h.Table.Write16(c.Initiator) 351 h.Table.Write16(c.NIC0) 352 h.Table.Write16(c.Target0) 353 h.Table.Write16(c.NIC1) 354 h.Table.Write16(c.Target1) 355 } 356 357 func gencsum(b []byte) byte { 358 var csum byte 359 for _, bb := range b { 360 csum += bb 361 } 362 return ^csum + 1 363 } 364 365 const lengthOffset = 4 366 const checksumOffset = 9 367 368 func fixACPIHeader(b []byte) []byte { 369 binary.LittleEndian.PutUint16(b[lengthOffset:], uint16(len(b))) 370 b[checksumOffset] = gencsum(b) 371 return b 372 } 373 374 const ( 375 controlOffset = 0x30 376 initiatorOffset = 0x48 377 nic0Offset = 0x98 378 target0Offset = 0x100 379 heapOffset = 0x138 380 ) 381 382 // Marshal returns a binary representation of the iBFT. 383 // 384 // Pointers within an iBFT is relative, so this can be placed anywhere 385 // necessary. 386 func (i *IBFT) Marshal() []byte { 387 h := &heapTable{ 388 heapOffset: heapOffset, 389 390 Table: uio.NewLittleEndianBuffer(nil), 391 Heap: uio.NewLittleEndianBuffer(nil), 392 } 393 394 header := &acpiHeader{ 395 Signature: signature, 396 Length: 0, 397 Revision: 1, 398 Checksum: 0, 399 OEMID: oemID, 400 OEMTableID: oemTableID, 401 OEMRevision: 0, 402 CreatorID: 0, 403 CreatorRevision: 0, 404 } 405 h.Table.WriteData(header) 406 407 // iBFT spec, Section 1.4.4.4 "Each structure must be aligned on an 8 408 // byte boundary." 409 410 // 0x30 411 h.Table.Align(8) 412 control := ibftControl{ 413 SingleLoginMode: i.SingleLoginMode, 414 Initiator: initiatorOffset, 415 NIC0: nic0Offset, 416 Target0: target0Offset, 417 } 418 control.marshal(h) 419 420 // 0x48 421 h.Table.Align(8) 422 i.Initiator.marshal(h) 423 424 // 0x98 425 h.Table.Align(8) 426 i.NIC0.marshal(h) 427 428 // 0x100 429 h.Table.Align(8) 430 i.Target0.marshal(h) 431 432 // 0x138 433 h.Table.Align(8) 434 h.Table.WriteBytes(h.Heap.Data()) 435 436 return fixACPIHeader(h.Table.Data()) 437 }