From 221f43e4bd44ee480722941db2eebc7ee9944d90 Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Thu, 2 Apr 2015 21:45:52 +0200 Subject: [PATCH] Use a socket per interface for v6 multicast (fixes #1563) --- internal/beacon/broadcast.go | 2 +- internal/beacon/multicast.go | 39 +++++++--------- internal/discover/discover.go | 85 +++++++++++++++++++++++------------ 3 files changed, 73 insertions(+), 53 deletions(-) diff --git a/internal/beacon/broadcast.go b/internal/beacon/broadcast.go index 647bbc1ee..e3497b32f 100644 --- a/internal/beacon/broadcast.go +++ b/internal/beacon/broadcast.go @@ -16,7 +16,7 @@ type Broadcast struct { } func NewBroadcast(port int) (*Broadcast, error) { - conn, err := net.ListenUDP("udp", &net.UDPAddr{Port: port}) + conn, err := net.ListenUDP("udp4", &net.UDPAddr{Port: port}) if err != nil { return nil, err } diff --git a/internal/beacon/multicast.go b/internal/beacon/multicast.go index 557c76579..02a937017 100644 --- a/internal/beacon/multicast.go +++ b/internal/beacon/multicast.go @@ -11,22 +11,28 @@ import "net" type Multicast struct { conn *net.UDPConn addr *net.UDPAddr + intf *net.Interface inbox chan []byte outbox chan recv } -func NewMulticast(addr string) (*Multicast, error) { - gaddr, err := net.ResolveUDPAddr("udp", addr) +func NewMulticast(addr, ifname string) (*Multicast, error) { + gaddr, err := net.ResolveUDPAddr("udp6", addr) if err != nil { return nil, err } - conn, err := net.ListenMulticastUDP("udp", nil, gaddr) + intf, err := net.InterfaceByName(ifname) + if err != nil { + return nil, err + } + conn, err := net.ListenMulticastUDP("udp6", intf, gaddr) if err != nil { return nil, err } b := &Multicast{ conn: conn, addr: gaddr, + intf: intf, inbox: make(chan []byte), outbox: make(chan recv, 16), } @@ -47,27 +53,14 @@ func (b *Multicast) Recv() ([]byte, net.Addr) { } func (b *Multicast) writer() { + addr := *b.addr + addr.Zone = b.intf.Name for bs := range b.inbox { - intfs, err := net.Interfaces() - if err != nil { - if debug { - l.Debugln("multicast interfaces:", err) - } - continue - } - for _, intf := range intfs { - if intf.Flags&net.FlagUp != 0 && intf.Flags&net.FlagMulticast != 0 { - addr := *b.addr - addr.Zone = intf.Name - _, err = b.conn.WriteTo(bs, &addr) - if err != nil { - if debug { - l.Debugln(err, "on write to", addr) - } - } else if debug { - l.Debugf("sent %d bytes to %s", len(bs), addr.String()) - } - } + _, err := b.conn.WriteTo(bs, &addr) + if err != nil && debug { + l.Debugln(err, "on write to", addr) + } else if debug { + l.Debugf("sent %d bytes to %s", len(bs), addr.String()) } } } diff --git a/internal/discover/discover.go b/internal/discover/discover.go index 54baa3827..9c7e7b989 100644 --- a/internal/discover/discover.go +++ b/internal/discover/discover.go @@ -28,8 +28,7 @@ type Discoverer struct { localBcastStart time.Time cacheLifetime time.Duration negCacheCutoff time.Duration - broadcastBeacon beacon.Interface - multicastBeacon beacon.Interface + beacons []beacon.Interface extPort uint16 localBcastTick <-chan time.Time forcedBcastTick chan time.Time @@ -65,38 +64,69 @@ func NewDiscoverer(id protocol.DeviceID, addresses []string) *Discoverer { func (d *Discoverer) StartLocal(localPort int, localMCAddr string) { if localPort > 0 { - bb, err := beacon.NewBroadcast(localPort) - if err != nil { - if debug { - l.Debugln("discover: Start local v4:", err) - } - l.Infoln("Local discovery over IPv4 unavailable") - } else { - d.broadcastBeacon = bb - go d.recvAnnouncements(bb) - } + d.startLocalIPv4Broadcasts(localPort) } if len(localMCAddr) > 0 { - mb, err := beacon.NewMulticast(localMCAddr) + d.startLocalIPv6Multicasts(localMCAddr) + } + + if len(d.beacons) == 0 { + l.Warnln("Local discovery unavailable") + return + } + + d.localBcastTick = time.Tick(d.localBcastIntv) + d.forcedBcastTick = make(chan time.Time) + d.localBcastStart = time.Now() + go d.sendLocalAnnouncements() +} + +func (d *Discoverer) startLocalIPv4Broadcasts(localPort int) { + bb, err := beacon.NewBroadcast(localPort) + if err != nil { + if debug { + l.Debugln("discover: Start local v4:", err) + } + l.Infoln("Local discovery over IPv4 unavailable") + return + } + + d.beacons = append(d.beacons, bb) + go d.recvAnnouncements(bb) +} + +func (d *Discoverer) startLocalIPv6Multicasts(localMCAddr string) { + intfs, err := net.Interfaces() + if err != nil { + if debug { + l.Debugln("discover: interfaces:", err) + } + l.Infoln("Local discovery over IPv6 unavailable") + return + } + + v6Intfs := 0 + for _, intf := range intfs { + if intf.Flags&net.FlagUp == 0 || intf.Flags&net.FlagMulticast == 0 { + continue + } + + mb, err := beacon.NewMulticast(localMCAddr, intf.Name) if err != nil { if debug { l.Debugln("discover: Start local v6:", err) } - l.Infoln("Local discovery over IPv6 unavailable") - } else { - d.multicastBeacon = mb - go d.recvAnnouncements(mb) + continue } + + d.beacons = append(d.beacons, mb) + go d.recvAnnouncements(mb) + v6Intfs++ } - if d.broadcastBeacon == nil && d.multicastBeacon == nil { - l.Warnln("Local discovery unavailable") - } else { - d.localBcastTick = time.Tick(d.localBcastIntv) - d.forcedBcastTick = make(chan time.Time) - d.localBcastStart = time.Now() - go d.sendLocalAnnouncements() + if v6Intfs == 0 { + l.Infoln("Local discovery over IPv6 unavailable") } } @@ -288,11 +318,8 @@ func (d *Discoverer) sendLocalAnnouncements() { msg := pkt.MustMarshalXDR() for { - if d.multicastBeacon != nil { - d.multicastBeacon.Send(msg) - } - if d.broadcastBeacon != nil { - d.broadcastBeacon.Send(msg) + for _, b := range d.beacons { + b.Send(msg) } select {