github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/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 iSCSI root paths.
    11  package netboot
    12  
    13  import (
    14  	"context"
    15  	"net"
    16  	"net/url"
    17  	"path"
    18  
    19  	"github.com/mvdan/u-root-coreutils/pkg/boot"
    20  	"github.com/mvdan/u-root-coreutils/pkg/boot/netboot/ipxe"
    21  	"github.com/mvdan/u-root-coreutils/pkg/boot/netboot/pxe"
    22  	"github.com/mvdan/u-root-coreutils/pkg/boot/netboot/simple"
    23  	"github.com/mvdan/u-root-coreutils/pkg/curl"
    24  	"github.com/mvdan/u-root-coreutils/pkg/dhclient"
    25  	"github.com/mvdan/u-root-coreutils/pkg/ulog"
    26  )
    27  
    28  // BootImages figure out a ranked order of images to boot from the given DHCP lease.
    29  //
    30  // Tries, in order:
    31  //
    32  // - to detect an iPXE script beginning with #!ipxe,
    33  //
    34  //   - to detect a pxelinux.0, in which case we will ignore the pxelinux.0 and
    35  //     try to parse pxelinux.cfg/<files>.
    36  func BootImages(ctx context.Context, l ulog.Logger, s curl.Schemes, lease dhclient.Lease) ([]boot.OSImage, error) {
    37  	uri, err := lease.Boot()
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  	l.Printf("Boot URI: %s", uri)
    42  
    43  	// IP only makes sense for v4 anyway, because the PXE probing of files
    44  	// uses a MAC address and an IPv4 address to look at files.
    45  	var ip net.IP
    46  	if p4, ok := lease.(*dhclient.Packet4); ok {
    47  		ip = p4.Lease().IP
    48  	}
    49  	return getBootImages(ctx, l, s, uri, lease.Link().Attrs().HardwareAddr, ip), nil
    50  }
    51  
    52  // getBootImages attempts to parse the file at uri as an ipxe config and returns
    53  // the ipxe boot image. Otherwise falls back to pxe and uses the uri directory,
    54  // ip, and mac address to search for pxe configs.
    55  func getBootImages(ctx context.Context, l ulog.Logger, schemes curl.Schemes, uri *url.URL, mac net.HardwareAddr, ip net.IP) []boot.OSImage {
    56  	var images []boot.OSImage
    57  
    58  	// 1: Attempt to download the given url as is.
    59  	//
    60  	// 1.1: Try ipxe config file.
    61  	ipc, err := ipxe.ParseConfig(ctx, l, uri, schemes)
    62  	if err != nil {
    63  		l.Printf("Parsing boot files as iPXE failed, trying other formats...: %v", err)
    64  	}
    65  	if ipc != nil {
    66  		images = append(images, ipc)
    67  	}
    68  
    69  	// 1.2: Check if target is a simple file instead of config script
    70  	if ipc == nil {
    71  		l.Printf("Trying to parse file as a non config Image...")
    72  		sImages, err := simple.FetchAndProbe(ctx, uri, schemes)
    73  		if err != nil {
    74  			l.Printf("failed to parse boot file as simple file: %v", err)
    75  		}
    76  		if sImages != nil {
    77  			images = append(images, sImages...)
    78  		}
    79  	}
    80  
    81  	// 2: Fallback to pxe boot.
    82  	//
    83  	// Look for pxelinux.cfg from parent directory of given url path.
    84  	wd := &url.URL{
    85  		Scheme: uri.Scheme,
    86  		Host:   uri.Host,
    87  		Path:   path.Dir(uri.Path),
    88  	}
    89  	pxeImages, err := pxe.ParseConfig(ctx, wd, mac, ip, schemes)
    90  	if err != nil {
    91  		l.Printf("Failed to try parsing pxelinux config: %v", err)
    92  	}
    93  
    94  	return append(images, pxeImages...)
    95  }