github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/checker/interfaces.go (about)

     1  // Copyright 2017-2019 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 checker
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"net"
    11  	"time"
    12  
    13  	"github.com/insomniacslk/dhcp/netboot"
    14  	"github.com/safchain/ethtool"
    15  )
    16  
    17  // InterfaceExists returns a Checker that verifies if an interface is present on
    18  // the system
    19  func InterfaceExists(ifname string) Checker {
    20  	return func() error {
    21  		_, err := net.InterfaceByName(ifname)
    22  		return err
    23  	}
    24  }
    25  
    26  // LinkSpeed checks the link speed, and complains if smaller than `min`
    27  // megabit/s.
    28  func LinkSpeed(ifname string, minSpeed uint32) Checker {
    29  	return func() error {
    30  		ec := ethtool.EthtoolCmd{}
    31  		speed, err := ec.CmdGet(ifname)
    32  		if err != nil {
    33  			return err
    34  		}
    35  		if speed < minSpeed {
    36  			return fmt.Errorf("link speed %d < %d", speed, minSpeed)
    37  		}
    38  		return nil
    39  	}
    40  }
    41  
    42  // LinkAutoneg checks if the link auto-negotiation state, and return an error if
    43  // it's not the expected state.
    44  func LinkAutoneg(ifname string, expected bool) Checker {
    45  	return func() error {
    46  		ec := ethtool.EthtoolCmd{}
    47  		_, err := ec.CmdGet(ifname)
    48  		if err != nil {
    49  			return err
    50  		}
    51  		var want uint8
    52  		if expected {
    53  			want = 1
    54  		}
    55  		if ec.Autoneg != want {
    56  			return fmt.Errorf("link autoneg %d; want %d", ec.Autoneg, want)
    57  		}
    58  		return nil
    59  	}
    60  }
    61  
    62  func addresses(ifname string) ([]net.IP, error) {
    63  	iface, err := net.InterfaceByName(ifname)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  	addrs, err := iface.Addrs()
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	iplist := make([]net.IP, 0)
    72  	for _, addr := range addrs {
    73  		ipnet, ok := addr.(*net.IPNet)
    74  		if !ok {
    75  			return nil, errors.New("not a net.IPNet")
    76  		}
    77  		iplist = append(iplist, ipnet.IP)
    78  	}
    79  	return iplist, nil
    80  }
    81  
    82  // InterfaceHasLinkLocalAddress returns a Checker that verifies if an interface
    83  // has a configured link-local address.
    84  func InterfaceHasLinkLocalAddress(ifname string) Checker {
    85  	return func() error {
    86  		addrs, err := addresses(ifname)
    87  		if err != nil {
    88  			return err
    89  		}
    90  		for _, addr := range addrs {
    91  			if addr.IsLinkLocalUnicast() {
    92  				return nil
    93  			}
    94  		}
    95  		return fmt.Errorf("no link local addresses for interface %s", ifname)
    96  	}
    97  }
    98  
    99  // InterfaceHasGlobalAddresses returns a Checker that verifies if an interface has
   100  // at least one global address.
   101  func InterfaceHasGlobalAddresses(ifname string) Checker {
   102  	return func() error {
   103  		addrs, err := addresses(ifname)
   104  		if err != nil {
   105  			return err
   106  		}
   107  		for _, addr := range addrs {
   108  			if addr.IsGlobalUnicast() {
   109  				return nil
   110  			}
   111  		}
   112  		return fmt.Errorf("no unicast global addresses for interface %s", ifname)
   113  	}
   114  }
   115  
   116  // InterfaceRemediate returns a Remediator that tries to fix a missing
   117  // interface issue.
   118  func InterfaceRemediate(ifname string) Remediator {
   119  	return func() error {
   120  		// TODO implement driver loading logic
   121  		dmesg, err := getDmesg()
   122  		if err != nil {
   123  			return fmt.Errorf("cannot read dmesg to look for NIC driver information: %v", err)
   124  		}
   125  		lines := grep(dmesg, ifname)
   126  		if len(lines) == 0 {
   127  			return fmt.Errorf("no trace of %s in dmesg", ifname)
   128  		}
   129  		// TODO should this be returned as a string to the caller?
   130  		fmt.Printf("  found %d references to %s in dmesg\n", len(lines), ifname)
   131  		return nil
   132  	}
   133  }
   134  
   135  // InterfaceCanDoDHCPv6 checks whether DHCPv6 succeeds on an interface, and if
   136  // it has a valid netboot URL.
   137  func InterfaceCanDoDHCPv6(ifname string) Checker {
   138  	return func() error {
   139  		conv, err := netboot.RequestNetbootv6(ifname, 10*time.Second, 2)
   140  		if err != nil {
   141  			return err
   142  		}
   143  		_, err = netboot.ConversationToNetconf(conv)
   144  		return err
   145  	}
   146  }