github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+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 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 }