Hello Gophers,
I have some trouble receiving broadcast UDP datagrams with Go on an linux/amd64 machine. I'm not sure if what I see is a Go issue or some intrinsic differences between Linux and Windows. I hope somebody here might be able to shed some light on this. I will start with a lengthy description of a system setup and then simply state that what I want works on Windows but not on Linux.
Image two machines, Machine A and Machine B, both in the same Class-C subnet (
192.168.1.0/24). Machine A is dual boot amd64 and alternately runs Windows 7 and Linux. Machine B is a black box for me, but it's providing a service that can be emulated by a tiny Go program listening for UDP datagrams on all network devices port <portB> and sending something back via UDP to
255.255.255.255:<portA>:
func main() {
portA := 56789
portB := portA + 1
fmt.Println("I am machine B")
ln, _ := net.ListenUDP("udp4", &net.UDPAddr{Port: portB})
raddr := &net.UDPAddr{IP: []byte{255, 255, 255, 255}, Port: portA}
b := make([]byte, 128)
for {
n, _ := ln.Read(b)
fmt.Println("Echoing", string(b[:n]))
ln.WriteToUDP(b[:n], raddr)
}
}
Machine B is doing a tremendous job. Machine B is not the problem (except for that I have no control over how it's doing things). The above code is only for reproducing the issue.
Let's hop over to Machine A, shall we. Machine A is a client for the : It sends UDP datagrams to
255.255.255.255:<portB> and listens for UDP datagrams on <portA>. The only difference is that on Machine A, the call to ListenUDP is restricting the listening to the device of Machine A that I will reach Machine B on (that device has the IP 192.168.1.41). Here is the program that is running on Machine A:
func main() {
ipA := net.ParseIP("192.168.1.41")
portA := 10980
portB := portA + 1
fmt.Println("I am machine A, and I will send over", ipA)
laddr := &net.UDPAddr{IP: ipA, Port: portA}
raddr := &net.UDPAddr{IP: []byte{255, 255, 255, 255}, Port: portB}
d, _ := net.ListenUDP("udp4", laddr)
rb := make([]byte, 128)
for {
s := fmt.Sprint(rand.Int())
fmt.Println("Sending", s)
b := []byte(s)
d.WriteToUDP(b, raddr)
d.SetReadDeadline(time.Now().Add(time.Second))
n, err := d.Read(rb)
if e, ok := err.(net.Error); ok && e.Timeout() {
fmt.Println("Timeout")
} else if err != nil {
fmt.Println("Error: err")
} else {
fmt.Println("Received", string(rb[:n]))
time.Sleep(1 * time.Second)
}
}
}
As you can see, I send some data as a broadcast, as I do not know the IP of B, but I know that it will react on broadcast datagrams. I know from sniffing the network that Machine B is answering with a broadcast packet as well. The reason why I bind the ListenUDP to 192.168.1.41 is that there are more network interfaces on Machine A and the default route if over one of these other interfaces. I need to make sure the initial broadcast from A is send on the correct interface.
And finally, here's my issue: Machine A is receiving the answer only if Machine is running Windows. When I run that same code on Linux, that answer datagram is not received.
Is this something that is to be expected for Linux and Windows or am I running into a bug with Go?
Thanks,
Fabian