github.com/hugelgupf/u-root@v0.0.0-20191023214958-4807c632154c/pkg/netboot/netboot.go (about)

     1  // Copyright 2017-2018 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 netboot provides a one-stop shop for netboot parsing needs.
     6  //
     7  // netboot can take a URL from a DHCP lease and try to detect iPXE scripts and
     8  // PXE scripts.
     9  //
    10  // TODO: detect multiboot and Linux kernels without configuration (URL points
    11  // to a single kernel file).
    12  //
    13  // TODO: detect iSCSI root paths.
    14  package netboot
    15  
    16  import (
    17  	"fmt"
    18  	"log"
    19  	"net"
    20  	"net/url"
    21  	"path"
    22  
    23  	"github.com/u-root/u-root/pkg/boot"
    24  	"github.com/u-root/u-root/pkg/dhclient"
    25  	"github.com/u-root/u-root/pkg/netboot/ipxe"
    26  	"github.com/u-root/u-root/pkg/netboot/pxe"
    27  	"github.com/u-root/u-root/pkg/urlfetch"
    28  )
    29  
    30  // BootImage figures out the image to boot from the given DHCP lease.
    31  //
    32  // Tries, in order:
    33  //
    34  // - to detect an iPXE script beginning with #!ipxe,
    35  //
    36  // - to detect a pxelinux.0, in which case we will ignore the pxelinux and try
    37  //   to parse pxelinux.cfg/<files>.
    38  //
    39  // TODO: detect straight up multiboot and bzImage Linux kernel files rather
    40  // than just configuration scripts.
    41  func BootImage(s urlfetch.Schemes, lease dhclient.Lease) (*boot.LinuxImage, error) {
    42  	uri, err := lease.Boot()
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	log.Printf("Boot URI: %s", uri)
    47  
    48  	// IP only makes sense for v4 anyway, because the PXE probing of files
    49  	// uses a MAC address and an IPv4 address to look at files.
    50  	var ip net.IP
    51  	if p4, ok := lease.(*dhclient.Packet4); ok {
    52  		ip = p4.Lease().IP
    53  	}
    54  	return getBootImage(s, uri, lease.Link().Attrs().HardwareAddr, ip)
    55  }
    56  
    57  // getBootImage attempts to parse the file at uri as an ipxe config and returns
    58  // the ipxe boot image. Otherwise falls back to pxe and uses the uri directory,
    59  // ip, and mac address to search for pxe configs.
    60  func getBootImage(schemes urlfetch.Schemes, uri *url.URL, mac net.HardwareAddr, ip net.IP) (*boot.LinuxImage, error) {
    61  	// Attempt to read the given boot path as an ipxe config file.
    62  	ipc, err := ipxe.ParseConfigWithSchemes(uri, schemes)
    63  	if err == nil {
    64  		return ipc, nil
    65  	}
    66  	log.Printf("Falling back to pxe boot: %v", err)
    67  
    68  	// Fallback to pxe boot.
    69  	wd := &url.URL{
    70  		Scheme: uri.Scheme,
    71  		Host:   uri.Host,
    72  		Path:   path.Dir(uri.Path),
    73  	}
    74  	pc, err := pxe.ParseConfigWithSchemes(wd, mac, ip, schemes)
    75  	if err != nil {
    76  		return nil, fmt.Errorf("failed to parse pxelinux config: %v", err)
    77  	}
    78  
    79  	label := pc.Entries[pc.DefaultEntry]
    80  	return label, nil
    81  }