internal/clientinfo: improving mdns discovery

- Prevent duplicated log message.
 - Distinguish in case of create/update hostname.
 - Stop probing if network is unreachable or invalid.
This commit is contained in:
Cuong Manh Le
2023-07-18 19:12:29 +07:00
committed by Cuong Manh Le
parent cacd957594
commit 59a895bfe2
2 changed files with 59 additions and 29 deletions
+4 -2
View File
@@ -48,10 +48,11 @@ type Table struct {
ptr *ptrDiscover ptr *ptrDiscover
mdns *mdns mdns *mdns
cfg *ctrld.Config cfg *ctrld.Config
quitCh chan struct{}
} }
func NewTable(cfg *ctrld.Config) *Table { func NewTable(cfg *ctrld.Config) *Table {
return &Table{cfg: cfg} return &Table{cfg: cfg, quitCh: make(chan struct{})}
} }
func (t *Table) AddLeaseFile(name string, format ctrld.LeaseFileFormat) { func (t *Table) AddLeaseFile(name string, format ctrld.LeaseFileFormat) {
@@ -70,6 +71,7 @@ func (t *Table) RefreshLoop(stopCh chan struct{}) {
_ = r.refresh() _ = r.refresh()
} }
case <-stopCh: case <-stopCh:
close(t.quitCh)
return return
} }
} }
@@ -122,7 +124,7 @@ func (t *Table) Init() {
if t.discoverMDNS() { if t.discoverMDNS() {
t.mdns = &mdns{} t.mdns = &mdns{}
ctrld.ProxyLog.Debug().Msg("start mdns discovery") ctrld.ProxyLog.Debug().Msg("start mdns discovery")
if err := t.mdns.init(); err != nil { if err := t.mdns.init(t.quitCh); err != nil {
ctrld.ProxyLog.Error().Err(err).Msg("could not init mDNS discover") ctrld.ProxyLog.Error().Err(err).Msg("could not init mDNS discover")
} else { } else {
t.hostnameResolvers = append(t.hostnameResolvers, t.mdns) t.hostnameResolvers = append(t.hostnameResolvers, t.mdns)
+55 -27
View File
@@ -4,7 +4,9 @@ import (
"context" "context"
"errors" "errors"
"net" "net"
"os"
"sync" "sync"
"syscall"
"time" "time"
"github.com/miekg/dns" "github.com/miekg/dns"
@@ -41,7 +43,7 @@ func (m *mdns) LookupHostnameByMac(mac string) string {
return "" return ""
} }
func (m *mdns) init() error { func (m *mdns) init(quitCh chan struct{}) error {
ifaces, err := multicastInterfaces() ifaces, err := multicastInterfaces()
if err != nil { if err != nil {
return err return err
@@ -65,20 +67,35 @@ func (m *mdns) init() error {
} }
} }
go func() { go m.probeLoop(v4ConnList, mdnsV4Addr, quitCh)
bo := backoff.NewBackoff("mdns probe", func(format string, args ...any) {}, time.Second*30) go m.probeLoop(v6ConnList, mdnsV6Addr, quitCh)
for {
err := m.probe(v4ConnList, v6ConnList)
if err != nil {
ctrld.ProxyLog.Warn().Err(err).Msg("error while probing mdns")
}
bo.BackOff(context.Background(), errors.New("mdns probe backoff"))
}
}()
return nil return nil
} }
func (m *mdns) probeLoop(conns []*net.UDPConn, remoteAddr net.Addr, quitCh chan struct{}) {
bo := backoff.NewBackoff("mdns probe", func(format string, args ...any) {}, time.Second*30)
for {
err := m.probe(conns, remoteAddr, quitCh)
if isErrNetUnreachableOrInvalid(err) {
ctrld.ProxyLog.Warn().Msgf("stop probing %q: network unreachable or invalid", remoteAddr)
break
}
if err != nil {
ctrld.ProxyLog.Warn().Err(err).Msg("error while probing mdns")
bo.BackOff(context.Background(), errors.New("mdns probe backoff"))
}
select {
case <-quitCh:
break
}
}
<-quitCh
for _, conn := range conns {
_ = conn.Close()
}
}
func (m *mdns) readLoop(conn *net.UDPConn) { func (m *mdns) readLoop(conn *net.UDPConn) {
defer conn.Close() defer conn.Close()
buf := make([]byte, dns.MaxMsgSize) buf := make([]byte, dns.MaxMsgSize)
@@ -87,12 +104,10 @@ func (m *mdns) readLoop(conn *net.UDPConn) {
_ = conn.SetReadDeadline(time.Now().Add(time.Second * 30)) _ = conn.SetReadDeadline(time.Now().Add(time.Second * 30))
n, _, err := conn.ReadFromUDP(buf) n, _, err := conn.ReadFromUDP(buf)
if err != nil { if err != nil {
if err, ok := err.(*net.OpError); ok { if err, ok := err.(*net.OpError); ok && (err.Timeout() || err.Temporary()) {
if err.Timeout() || err.Temporary() { continue
continue
}
ctrld.ProxyLog.Debug().Err(err).Msg("mdns readLoop error")
} }
ctrld.ProxyLog.Debug().Err(err).Msg("mdns readLoop error")
return return
} }
@@ -111,14 +126,22 @@ func (m *mdns) readLoop(conn *net.UDPConn) {
} }
if ip != "" && name != "" { if ip != "" && name != "" {
name = normalizeHostname(name) name = normalizeHostname(name)
ctrld.ProxyLog.Debug().Msgf("Found hostname: %q, ip: %q via mdns", name, ip) if val, loaded := m.name.LoadOrStore(ip, name); !loaded {
m.name.Store(ip, name) ctrld.ProxyLog.Debug().Msgf("found hostname: %q, ip: %q via mdns", name, ip)
} else {
old := val.(string)
if old != name {
ctrld.ProxyLog.Debug().Msgf("update hostname: %q, ip: %q, old: %q via mdns", name, ip, old)
m.name.Store(ip, name)
}
}
ip, name = "", ""
} }
} }
} }
} }
func (m *mdns) probe(v4connList, v6connList []*net.UDPConn) error { func (m *mdns) probe(conns []*net.UDPConn, remoteAddr net.Addr, quitCh chan struct{}) error {
msg := new(dns.Msg) msg := new(dns.Msg)
msg.Question = make([]dns.Question, len(services)) msg.Question = make([]dns.Question, len(services))
for i, service := range services { for i, service := range services {
@@ -133,16 +156,13 @@ func (m *mdns) probe(v4connList, v6connList []*net.UDPConn) error {
if err != nil { if err != nil {
return err return err
} }
do := func(connList []*net.UDPConn, remoteAddr net.Addr) error { for _, conn := range conns {
for _, conn := range connList { _ = conn.SetWriteDeadline(time.Now().Add(time.Second * 30))
_ = conn.SetWriteDeadline(time.Now().Add(time.Second * 30)) if _, werr := conn.WriteTo(buf, remoteAddr); werr != nil {
if _, err := conn.WriteTo(buf, remoteAddr); err != nil { err = werr
return err
}
} }
return nil
} }
return errors.Join(do(v4connList, mdnsV4Addr), do(v6connList, mdnsV6Addr)) return err
} }
func multicastInterfaces() ([]net.Interface, error) { func multicastInterfaces() ([]net.Interface, error) {
@@ -161,3 +181,11 @@ func multicastInterfaces() ([]net.Interface, error) {
} }
return interfaces, nil return interfaces, nil
} }
func isErrNetUnreachableOrInvalid(err error) bool {
var se *os.SyscallError
if errors.As(err, &se) {
return se.Err == syscall.ENETUNREACH || se.Err == syscall.EINVAL
}
return false
}