github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/boot/stboot/network.go (about)

     1  // Copyright 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 main
     6  
     7  import (
     8  	"context"
     9  	"crypto/tls"
    10  	"crypto/x509"
    11  	"encoding/pem"
    12  	"errors"
    13  	"fmt"
    14  	"io"
    15  	"io/ioutil"
    16  	"log"
    17  	"net"
    18  	"net/http"
    19  	"os"
    20  	"strconv"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/u-root/u-root/pkg/boot/stboot"
    25  	"github.com/u-root/u-root/pkg/dhclient"
    26  	"github.com/vishvananda/netlink"
    27  )
    28  
    29  func configureStaticNetwork(vars stboot.HostVars) error {
    30  	log.Printf("Setup network configuration with IP: " + vars.HostIP)
    31  	addr, err := netlink.ParseAddr(vars.HostIP)
    32  	if err != nil {
    33  		return fmt.Errorf("Error parsing HostIP string to CIDR format address: %v", err)
    34  	}
    35  
    36  	link, err := findNetworkInterface()
    37  	if err != nil {
    38  		return err
    39  	}
    40  
    41  	if err = netlink.AddrAdd(link, addr); err != nil {
    42  		return fmt.Errorf("Error adding address: %v", err)
    43  	}
    44  
    45  	if err = netlink.LinkSetUp(link); err != nil {
    46  		return fmt.Errorf("Error bringing up interface: %v", err)
    47  	}
    48  
    49  	gateway, err := netlink.ParseAddr(vars.DefaultGateway)
    50  	if err != nil {
    51  		return fmt.Errorf("Error parsing GatewayIP string to CIDR format address: %v", err)
    52  	}
    53  
    54  	r := &netlink.Route{LinkIndex: link.Attrs().Index, Gw: gateway.IPNet.IP}
    55  	if err = netlink.RouteAdd(r); err != nil {
    56  		return fmt.Errorf("Error setting default gateway: %v", err)
    57  	}
    58  
    59  	return nil
    60  }
    61  
    62  func configureDHCPNetwork() error {
    63  	log.Printf("Trying to configure network configuration dynamically...")
    64  
    65  	link, err := findNetworkInterface()
    66  	if err != nil {
    67  		return err
    68  	}
    69  
    70  	var links []netlink.Link
    71  	links = append(links, link)
    72  
    73  	var level dhclient.LogLevel
    74  	if *doDebug {
    75  		level = 1
    76  	} else {
    77  		level = 0
    78  	}
    79  	config := dhclient.Config{
    80  		Timeout:  interfaceUpTimeout,
    81  		Retries:  4,
    82  		LogLevel: level,
    83  	}
    84  
    85  	r := dhclient.SendRequests(context.TODO(), links, true, false, config, 30*time.Second)
    86  	for result := range r {
    87  		if result.Err == nil {
    88  			return result.Lease.Configure()
    89  		} else if *doDebug {
    90  			log.Printf("dhcp response error: %v", result.Err)
    91  		}
    92  	}
    93  	return errors.New("no valid DHCP configuration recieved")
    94  }
    95  
    96  func findNetworkInterface() (netlink.Link, error) {
    97  	ifaces, err := net.Interfaces()
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	if len(ifaces) == 0 {
   103  		return nil, errors.New("No network interface found")
   104  	}
   105  
   106  	var ifnames []string
   107  	for _, iface := range ifaces {
   108  		if *doDebug {
   109  			log.Printf("Found interface %s", iface.Name)
   110  			log.Printf("    MTU: %d Hardware Addr: %s", iface.MTU, iface.HardwareAddr.String())
   111  			log.Printf("    Flags: %v", iface.Flags)
   112  		}
   113  		ifnames = append(ifnames, iface.Name)
   114  		// skip loopback
   115  		if iface.Flags&net.FlagLoopback != 0 || iface.HardwareAddr.String() == "" {
   116  			continue
   117  		}
   118  		log.Printf("Try using %s", iface.Name)
   119  		link, err := netlink.LinkByName(iface.Name)
   120  		if err == nil {
   121  			return link, nil
   122  		}
   123  		log.Print(err)
   124  	}
   125  
   126  	return nil, fmt.Errorf("Could not find a non-loopback network interface with hardware address in any of %v", ifnames)
   127  }
   128  
   129  func downloadFromHTTPS(url string, destination string) error {
   130  	roots := x509.NewCertPool()
   131  	if err := loadHTTPSCertificate(roots); err != nil {
   132  		return fmt.Errorf("Failed to load root certificate: %v", err)
   133  	}
   134  
   135  	// setup https client
   136  	client := http.Client{
   137  		Transport: (&http.Transport{
   138  			Proxy: http.ProxyFromEnvironment,
   139  			DialContext: (&net.Dialer{
   140  				Timeout:   30 * time.Second,
   141  				KeepAlive: 30 * time.Second,
   142  				DualStack: true,
   143  			}).DialContext,
   144  			MaxIdleConns:          100,
   145  			IdleConnTimeout:       90 * time.Second,
   146  			TLSHandshakeTimeout:   10 * time.Second,
   147  			ExpectContinueTimeout: 1 * time.Second,
   148  			TLSClientConfig: (&tls.Config{
   149  				RootCAs: roots,
   150  			}),
   151  		}),
   152  	}
   153  
   154  	// check available kernel entropy
   155  	e, err := ioutil.ReadFile(entropyAvail)
   156  	if err != nil {
   157  		return fmt.Errorf("Cannot evaluate entropy, %v", err)
   158  	}
   159  	es := strings.TrimSpace(string(e))
   160  	entr, err := strconv.Atoi(es)
   161  	if err != nil {
   162  		return fmt.Errorf("Cannot evaluate entropy, %v", err)
   163  	}
   164  	log.Printf("Available kernel entropy: %d", entr)
   165  	if entr < 128 {
   166  		log.Print("WARNING: low entropy!")
   167  		log.Printf("%s : %d", entropyAvail, entr)
   168  	}
   169  	// get remote boot bundle
   170  	log.Print("Downloading bootball ...")
   171  	resp, err := client.Get(url)
   172  	if err != nil {
   173  		return err
   174  	}
   175  	defer resp.Body.Close()
   176  	if resp.StatusCode != 200 {
   177  		return fmt.Errorf("non-200 HTTP status: %d", resp.StatusCode)
   178  	}
   179  	f, err := os.Create(destination)
   180  	if err != nil {
   181  		return fmt.Errorf("failed create boot config file: %v", err)
   182  	}
   183  	defer f.Close()
   184  
   185  	_, err = io.Copy(f, resp.Body)
   186  	if err != nil {
   187  		return fmt.Errorf("failed to save bootball: %v", err)
   188  	}
   189  
   190  	return nil
   191  }
   192  
   193  // loadHTTPSCertificate loads the certificate needed
   194  // for HTTPS and verifies it.
   195  func loadHTTPSCertificate(roots *x509.CertPool) error {
   196  	log.Printf("Load %s as CA certificate", rootCACertPath)
   197  	rootCertBytes, err := ioutil.ReadFile(rootCACertPath)
   198  	if err != nil {
   199  		return err
   200  	}
   201  	rootCertPem, _ := pem.Decode(rootCertBytes)
   202  	if rootCertPem.Type != "CERTIFICATE" {
   203  		return fmt.Errorf("Failed decoding certificate: Certificate is of the wrong type. PEM Type is: %s", rootCertPem.Type)
   204  	}
   205  	ok := roots.AppendCertsFromPEM([]byte(rootCertBytes))
   206  	if !ok {
   207  		return fmt.Errorf("Error parsing CA root certificate")
   208  	}
   209  	return nil
   210  }