package main import ( "encoding/binary" "fmt" "os" "path/filepath" "sort" "strings" "sync/atomic" "time" "unsafe" "golang.org/x/sys/unix" ) // The noise cable: a deliberately bad cable twisted around the one under test, // there to radiate into it. Its ports are driven for the interference they // produce, not measured: nothing is ever received from them, and nothing they // count reaches the error columns. // // The wire cannot be quieted by going idle, because without EEE the PHYs // signal at full power whether or not frames flow, and these PHYs live inside // the SFP+ modules where no EEE control reaches them. Closing the port is the // one switch the host actually has, so the cycle is built on it: links up and // carrying frames for a spell, then administratively down for one. Every wake // re-runs 10GBASE-T training, which is as loud as this wire ever gets. const ( noiseDriver = "i40e" noiseFrameLen = 1514 noiseUpSpan = 5 * time.Second noiseDownSpan = 5 * time.Second noiseFrameGap = 10 * time.Millisecond noiseEther uint16 = etherBase + numStreams ) // Kernel names shift with which drivers are built in, since ethN is handed out // in link order rather than by slot. The driver name is the one label a port // keeps across kernel configs, so pairs are found by it rather than named. func driverPair(driver string) (string, string, error) { ents, err := os.ReadDir("/sys/class/net") if err != nil { return "", "", err } var names []string for _, e := range ents { link, err := os.Readlink("/sys/class/net/" + e.Name() + "/device/driver") if err != nil { continue } if filepath.Base(link) == driver { names = append(names, e.Name()) } } if len(names) != 2 { return "", "", fmt.Errorf("want 2 %s interfaces, found %d [%s]", driver, len(names), strings.Join(names, " ")) } sort.Strings(names) return names[0], names[1], nil } type noisePort struct { name string fd int frame []byte } type noiser struct { eps [2]endpoint ports [2]noisePort // Whether the cable is judged present: both carriers seen during an up // phase. Latched across the down phase, where the missing carrier is our // own doing and says nothing about the cable. connected atomic.Bool } func newNoiser() (*noiser, error) { aName, bName, err := driverPair(noiseDriver) if err != nil { return nil, fmt.Errorf("noise: %w", err) } a, err := lookupEndpoint(aName) if err != nil { return nil, fmt.Errorf("noise: %w", err) } b, err := lookupEndpoint(bName) if err != nil { return nil, fmt.Errorf("noise: %w", err) } n := &noiser{eps: [2]endpoint{a, b}} for i, p := range [][2]endpoint{{a, b}, {b, a}} { fd, err := openTxSocket(p[0].idx) if err != nil { return nil, fmt.Errorf("noise tx socket %s: %w", p[0].name, err) } // The payload is left zero: the PCS scrambles everything on the wire, // so no pattern radiates differently from any other. The frame exists // to occupy the link, not to say anything. frame := make([]byte, noiseFrameLen) copy(frame[0:6], p[1].mac[:]) copy(frame[6:12], p[0].mac[:]) binary.BigEndian.PutUint16(frame[12:14], noiseEther) n.ports[i] = noisePort{name: p[0].name, fd: fd, frame: frame} } return n, nil } func (n *noiser) names() []string { return []string{n.eps[0].name, n.eps[1].name} } // Zero while the cable was there at the last verdict, one while it was not: // the shape the error cells already colour by, so absence paints as the fault // it is and presence as the usual green. func (n *noiser) missing() uint64 { if n.connected.Load() { return 0 } return 1 } // The ports were reachable when the noiser was built, so one that stops taking // the ioctl now is the interface going away underneath us, the same fault the // counter reads stop for. func (n *noiser) setLinks(fd int, up bool) { for i := range n.ports { var ifr flagsIfreq copy(ifr.name[:], n.ports[i].name) if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCGIFFLAGS), uintptr(unsafe.Pointer(&ifr))); errno != 0 { panic(fmt.Sprintf("reading %s flags: %v", n.ports[i].name, errno)) } if up { ifr.flags |= unix.IFF_UP } else { ifr.flags &^= unix.IFF_UP } if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFFLAGS), uintptr(unsafe.Pointer(&ifr))); errno != 0 { panic(fmt.Sprintf("setting %s flags: %v", n.ports[i].name, errno)) } } } // Down reads as EINVAL rather than zero, and either way the answer is the // same: no carrier here now. func carrierUp(name string) bool { v, ok := readUint("/sys/class/net/" + name + "/carrier") return ok && v == 1 } func (n *noiser) bothUp() bool { return carrierUp(n.ports[0].name) && carrierUp(n.ports[1].name) } // Send results are deliberately dropped: the cable is bad on purpose, the link // comes and goes under the cycle, and a frame this side declined to send is as // good as one the wire mangled. What matters is only ever what the test cable // counted. func (n *noiser) run(done *atomic.Bool) { fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0) if err != nil { panic(fmt.Sprintf("noise ioctl socket: %v", err)) } defer unix.Close(fd) tick := time.NewTicker(noiseFrameGap) defer tick.Stop() for !done.Load() { n.setLinks(fd, true) linked := false for end := time.Now().Add(noiseUpSpan); time.Now().Before(end) && !done.Load(); { <-tick.C if !n.bothUp() { continue } linked = true n.connected.Store(true) for i := range n.ports { unix.Write(n.ports[i].fd, n.ports[i].frame) } } // A whole up phase with no link is many times the ~1s the wire needs // to train, so by now the silence is the cable's answer. n.connected.Store(linked) n.setLinks(fd, false) for end := time.Now().Add(noiseDownSpan); time.Now().Before(end) && !done.Load(); { <-tick.C } } // Left up rather than wherever the cycle stopped, so a run never strands // the ports down for whoever looks next. n.setLinks(fd, true) } func (n *noiser) close() { for i := range n.ports { unix.Close(n.ports[i].fd) } }