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 }