From a8a12cafc977e79b044772ded82a849e6bb96cdf Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 28 Sep 2022 22:39:20 +0200 Subject: [PATCH] diagd: remove ping4/ping6 to external targets in favor of tcp4/tcp6 External ICMP does not necessarily work. It typically does, but not always. Last week, for a day or two, ICMP traffic was dropped by Google. So now we use ICMP only for network equipment targets (default gateway), and instead use TCP for external connectivity checks. fixes #77 --- cmd/diagd/diagd.go | 8 +++----- internal/diag/tcp.go | 42 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/cmd/diagd/diagd.go b/cmd/diagd/diagd.go index 6c1e129..d14f412 100644 --- a/cmd/diagd/diagd.go +++ b/cmd/diagd/diagd.go @@ -94,14 +94,12 @@ func logic() error { m := diag.NewMonitor(diag.Link(uplink). Then(diag.DHCPv4(). Then(diag.Ping4Gateway(). - Then(diag.Ping4("google.ch"). - Then(diag.TCP4("www.google.ch:80"))))). + Then(diag.TCP4("www.google.ch:80")))). Then(diag.DHCPv6(). - Then(diag.Ping6("lan0", "google.ch"))). + Then(diag.TCP6("lan0", "www.google.ch:80"))). Then(diag.RouterAdvertisments(uplink). Then(diag.Ping6Gateway(). - Then(diag.Ping6(uplink, "google.ch"). - Then(diag.TCP6("www.google.ch:80"))))). + Then(diag.TCP6(uplink, "www.google.ch:80")))). Then(diag.Ping6("", ip6allrouters+"%"+uplink))) var mu sync.Mutex http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { diff --git a/internal/diag/tcp.go b/internal/diag/tcp.go index bad741e..8bd8f55 100644 --- a/internal/diag/tcp.go +++ b/internal/diag/tcp.go @@ -53,6 +53,7 @@ func TCP4(addr string) Node { type tcp6 struct { children []Node + ifname string addr string } @@ -70,7 +71,39 @@ func (d *tcp6) Children() []Node { } func (d *tcp6) Evaluate() (string, error) { - conn, err := net.Dial("tcp6", d.addr) + var dialer net.Dialer + + if d.ifname != "" { + iface, err := net.InterfaceByName(d.ifname) + if err != nil { + return "", err + } + addrs, err := iface.Addrs() + if err != nil { + return "", err + } + for _, addr := range addrs { + ipnet, ok := addr.(*net.IPNet) + if !ok { + continue + } + + if ipnet.IP.To4() != nil { + continue // skip IPv4 addresses + } + + if !global.Contains(ipnet.IP) { + continue // skip local IPv6 addresses + } + + dialer.LocalAddr = &net.TCPAddr{ + IP: ipnet.IP, + } + break + } + } + + conn, err := dialer.Dial("tcp6", d.addr) if err != nil { return "", err } @@ -80,6 +113,9 @@ func (d *tcp6) Evaluate() (string, error) { // TCP6 returns a Node which succeeds when the specified address accepts a TCPv6 // connection. -func TCP6(addr string) Node { - return &tcp6{addr: addr} +func TCP6(ifname, addr string) Node { + return &tcp6{ + ifname: ifname, + addr: addr, + } }