1 // Package multicast provides a UDP multicast connection with an in-process multicast interface for sending and
2 // receiving.
3 //
4 // In order to allow processes on the same machine (windows) to receive the messages this code enables multicast
5 // loopback. It is up to the consuming library to discard messages it sends. This is only necessary because the net
6 // standard library disables loopback by default though on windows this takes effect whereas on unix platforms it does
7 // not.
8 //
9 // This code was derived from the information found here:
10 // https://stackoverflow.com/questions/43109552/how-to-set-ip-multicast-loop-on-multicast-udpconn-in-golang
11 12 package multicast
13 14 import (
15 "net"
16 17 "golang.org/x/net/ipv4"
18 19 "github.com/p9c/p9/pkg/util/routeable"
20 )
21 22 func Conn(port int) (conn *net.UDPConn, e error) {
23 var ipv4Addr = &net.UDPAddr{IP: net.IPv4(224, 0, 0, 1), Port: port}
24 if conn, e = net.ListenUDP("udp4", ipv4Addr); E.Chk(e) {
25 return
26 }
27 D.Ln("listening on", conn.LocalAddr(), "to", conn.RemoteAddr())
28 pc := ipv4.NewPacketConn(conn)
29 // var ifaces []net.Interface
30 var iface *net.Interface
31 // if ifaces, e = net.Interfaces(); E.Chk(e) {
32 // }
33 // // This grabs the first physical interface with multicast that is up. Note that this should filter out
34 // // VPN connections which would normally be selected first but don't actually have a multicast connection
35 // // to the local area network.
36 // for i := range ifaces {
37 // if ifaces[i].Flags&net.FlagMulticast != 0 &&
38 // ifaces[i].Flags&net.FlagUp != 0 &&
39 // ifaces[i].HardwareAddr != nil {
40 // iface = ifaces[i]
41 // break
42 // }
43 // }
44 ifcs, _ := routeable.GetAllInterfacesAndAddresses()
45 for _, ifc := range ifcs {
46 iface = ifc
47 if e = pc.JoinGroup(iface, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 1)}); E.Chk(e) {
48 return
49 }
50 // test
51 var loop bool
52 if loop, e = pc.MulticastLoopback(); e == nil {
53 D.F("MulticastLoopback status:%v\n", loop)
54 if !loop {
55 if e = pc.SetMulticastLoopback(true); e != nil {
56 E.F("SetMulticastLoopback error:%v\n", e)
57 }
58 }
59 D.Ln("Multicast Loopback enabled on", ifc.Name)
60 }
61 }
62 return
63 }
64