github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/boot/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  	"context"
    18  	"net"
    19  	"net/url"
    20  	"path"
    21  
    22  	"github.com/u-root/u-root/pkg/boot"
    23  	"github.com/u-root/u-root/pkg/boot/netboot/ipxe"
    24  	"github.com/u-root/u-root/pkg/boot/netboot/pxe"
    25  	"github.com/u-root/u-root/pkg/curl"
    26  	"github.com/u-root/u-root/pkg/dhclient"
    27  	"github.com/u-root/u-root/pkg/ulog"
    28  )
    29  
    30  // BootImages figure out a ranked order of images 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.0 and
    37  //   try 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 BootImages(ctx context.Context, l ulog.Logger, s curl.Schemes, lease dhclient.Lease) ([]boot.OSImage, error) {
    42  	uri, err := lease.Boot()
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	l.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 getBootImages(ctx, l, s, uri, lease.Link().Attrs().HardwareAddr, ip), nil
    55  }
    56  
    57  // getBootImages 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 getBootImages(ctx context.Context, l ulog.Logger, schemes curl.Schemes, uri *url.URL, mac net.HardwareAddr, ip net.IP) []boot.OSImage {
    61  	var images []boot.OSImage
    62  
    63  	// Attempt to read the given boot path as an ipxe config file.
    64  	ipc, err := ipxe.ParseConfig(ctx, l, uri, schemes)
    65  	if err != nil {
    66  		l.Printf("Parsing boot files as iPXE failed, trying other formats...: %v", err)
    67  	}
    68  	if ipc != nil {
    69  		images = append(images, ipc)
    70  	}
    71  
    72  	// Fallback to pxe boot.
    73  	wd := &url.URL{
    74  		Scheme: uri.Scheme,
    75  		Host:   uri.Host,
    76  		Path:   path.Dir(uri.Path),
    77  	}
    78  	pxeImages, err := pxe.ParseConfig(ctx, wd, mac, ip, schemes)
    79  	if err != nil {
    80  		l.Printf("Failed to try parsing pxelinux config: %v", err)
    81  	}
    82  	return append(images, pxeImages...)
    83  }