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