github.com/system-transparency/u-root@v6.0.1-0.20190919065413-ed07a650de4c+incompatible/cmds/boot/pxeboot/pxeboot.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 // Command pxeboot implements PXE-based booting. 6 // 7 // pxeboot combines a DHCP client with a TFTP/HTTP client to download files as 8 // well as pxelinux and iPXE configuration file parsing. 9 // 10 // PXE-based booting requests a DHCP lease, and looks at the BootFileName and 11 // ServerName options (which may be embedded in the original BOOTP message, or 12 // as option codes) to find something to boot. 13 // 14 // This BootFileName may point to 15 // 16 // - an iPXE script beginning with #!ipxe 17 // 18 // - a pxelinux.0, in which case we will ignore the pxelinux and try to parse 19 // pxelinux.cfg/<files> 20 package main 21 22 import ( 23 "context" 24 "flag" 25 "fmt" 26 "log" 27 "time" 28 29 "github.com/u-root/u-root/pkg/boot" 30 "github.com/u-root/u-root/pkg/dhclient" 31 "github.com/u-root/u-root/pkg/netboot" 32 "github.com/u-root/u-root/pkg/urlfetch" 33 ) 34 35 var ( 36 noLoad = flag.Bool("no-load", false, "get DHCP response, but don't load the kernel") 37 dryRun = flag.Bool("dry-run", false, "download kernel, but don't kexec it") 38 verbose = flag.Bool("v", false, "Verbose output") 39 ) 40 41 const ( 42 dhcpTimeout = 5 * time.Second 43 dhcpTries = 3 44 ) 45 46 // Netboot boots all interfaces matched by the regex in ifaceNames. 47 func Netboot(ifaceNames string) error { 48 filteredIfs, err := dhclient.Interfaces(ifaceNames) 49 if err != nil { 50 return err 51 } 52 53 ctx, cancel := context.WithTimeout(context.Background(), (1<<dhcpTries)*dhcpTimeout) 54 defer cancel() 55 56 c := dhclient.Config{ 57 Timeout: dhcpTimeout, 58 Retries: dhcpTries, 59 } 60 if *verbose { 61 c.LogLevel = dhclient.LogSummary 62 } 63 r := dhclient.SendRequests(ctx, filteredIfs, true, true, c) 64 65 for { 66 select { 67 case <-ctx.Done(): 68 return ctx.Err() 69 70 case result, ok := <-r: 71 if !ok { 72 log.Printf("Configured all interfaces.") 73 return fmt.Errorf("nothing bootable found") 74 } 75 if result.Err != nil { 76 continue 77 } 78 79 if err := result.Lease.Configure(); err != nil { 80 log.Printf("Failed to configure lease %s: %v", result.Lease, err) 81 continue 82 } 83 img, err := netboot.BootImage(urlfetch.DefaultSchemes, result.Lease) 84 if err != nil { 85 log.Printf("Failed to boot lease %v: %v", result.Lease, err) 86 continue 87 } 88 89 // Cancel other DHCP requests in flight. 90 cancel() 91 log.Printf("Got configuration: %s", img) 92 93 if *noLoad { 94 return nil 95 } 96 if err := img.Load(*dryRun); err != nil { 97 return fmt.Errorf("kexec load of %v failed: %v", img, err) 98 } 99 if *dryRun { 100 return nil 101 } 102 if err := boot.Execute(); err != nil { 103 return fmt.Errorf("kexec of %v failed: %v", img, err) 104 } 105 106 // Kexec should either return an error or not return. 107 panic("unreachable") 108 } 109 } 110 } 111 112 func main() { 113 flag.Parse() 114 115 if err := Netboot("eth0"); err != nil { 116 log.Fatal(err) 117 } 118 }