Go net.DialUDP auto-selecting loopback interface - go

I have a need to send some UDP packets from eth0 on a host to itself, also on eth0.
I have some working Go code, but it appears to be using the loopback interface instead of eth0 as expected.
package main
import (
"net"
)
func main() {
ip := net.ParseIP("192.168.1.158")
src := net.UDPAddr{IP: ip, Port: 8888,}
dest := net.UDPAddr{IP: ip, Port: 88}
conn, _ := net.DialUDP("udp", &src, &dest)
conn.Write([]byte("hi"))
}
192.168.1.158 is the primary and only IP address for the interface on my machine.
Running the following command shows me the traffic:
sudo tcpdump udp -v -i lo
However, I would expect the traffic be to/from the system interface with the IP address I've defined.
Am I missing a step here?
I have also tried this slightly different approach, but with the same results:
src, _ := net.ResolveUDPAddr("udp", "192.168.1.158:8888")
dest, _ := net.ResolveUDPAddr("udp", "192.168.1.158:88")

However, I would expect the traffic be to/from the system interface with the IP address I've defined.
This is simply the wrong expectation and unrelated to Go.
Just try ping 192.168.1.158 and check with tcpdump -i lo -n icmp vs. tcpdump -i eth0 -n icmp where the data actually gets transferred. You will see that the packets are transferred on the lo interface. Similar a ip route get 192.168.1.158 will show you that the route to your local address goes through the lo interface.

Related

TCP Listener is not shut down completely

I have a TCP Listener that initialized as next:
myListener := net.Listen("tcp", addr)
Then am able to receive connections and process them. Then I need to close the server in order that I can reuse the same port but this is not happening, this is how am closing the tcp server:
myListener.Close()
In the client side am closing all the existent TCP connections to that server and then from the terminal I see that those connections are being close but the port is still in use by the server and listening (even when is not accepting new connections which is right according to documentation). This is how I check in the terminal:
netstat -an | grep 8080
And after close the client side connections I get this and cannot reuse the port:
tcp46 0 0 *.8080 *.* LISTEN
After doing myListener.Close() I waited some time but in the terminal the port is still in use.
In addition to checking the error from the net.Listener as stated in https://stackoverflow.com/a/65638937/1435495
You will also want to add a defer to your myListener.Close() will help ensure that the close does actually execute even if something would cause the app to exit prematurely.
defer myListener.Close()
The net.Listen function returns two parameters (Listener, error), in your example above you appear to only be capturing the Listener and not the error.
Assuming you're actually capturing it, you should check if the error is empty before you begin using the listener.
package main
import "net"
func main() {
myListener, err := net.Listen("tcp", ":8080")
if err != nil {
panic(err)
}
myListener.Close()
}
Something similar to the snippet above should work. Now if you're not getting an error (I presume you will get an error), the problem you likely have is that something else is already using that port.
Try running your netstat as root so you can see all processes which will give you a better idea of what is holding on to that port.
sudo netstat -apn | grep -i listen | grep 8080

listen tcp 127.0.4.1:2040: bind: can't assign requested address

I am using this golang code:
l, err := net.Listen("tcp", "127.0.4.1:2040")
if err != nil {
log.Fatal("d0b9184a-5248-413e-a5a8-30fea66997f5:", err)
}
log.Fatal(s.Serve(l))
I am getting this error:
listen tcp 127.0.4.1:2040: bind: can't assign requested address
anyone know why that won't work?
You have to add an additional IPv4 address to the loopback interface:
sudo ifconfig lo0 alias 127.0.4.1
Before running the command you can verify the routes using:
netstat -nr
By default (On MacOS Catalina 10.15.4), these routes are present:
127 127.0.0.1 UCS lo0
127.0.0.1 127.0.0.1 UH lo0
After you have added your desired address to the loopback interface (i.e, lo0), netstat -nr would show:
127 127.0.0.1 UCS lo0
127.0.0.1 127.0.0.1 UH lo0
127.0.4.1 127.0.4.1 UH lo0
Now try using your program, it should work.
Also, this is temporary. It won't persist on reboot. For that give this a read!

Why does http.Get("http://[::]:1234") work?

I was writing a test where I wanted an HTTP server to listen on a random port and then connect to this port. I wrote:
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Println("foo")
})
listener, err := net.Listen("tcp", ":0")
fmt.Println(err)
httpServer := &http.Server{Handler: mux}
go httpServer.Serve(listener)
fmt.Println("http://" + listener.Addr().String())
r, err := http.Get("http://" + listener.Addr().String())
fmt.Println(r)
fmt.Println(err)
I meant to write net.Listen("tcp", "127.0.0.1:0") but accidentally wrote net.Listen("tcp", ":0").
For "http://" + listener.Addr().String() it prints:
http://[::]:1709
where as far as I understand the "bracket colon colon bracket" means "all interfaces".
To my surprise, the http.Get("http://[::]:1709") works, it connects to the webserver and "foo" is printed.
How is "http://[::]:1709" a valid address?
At least on a Linux system, this results in a connection being made to localhost.
The address :: is IN6ADDR_ANY, typically used when listening to listen for connections to any IPv6 address on the system. It's analogous to INADDR_ANY, also known as 0.0.0.0 in IPv4.
Occasionally someone attempts to use one of these addresses as the destination address for an outgoing connection. When this happens:
When making an outgoing connection to 0.0.0.0, Linux actually connects from 127.0.0.1 to 127.0.0.1.
Similarly, when making an outgoing connection to ::, Linux actually connects from ::1 to ::1. Here is an example, taken from one of my websites (which happens to be an IP address lookup tool):
[error#murloc ~]$ curl -k -H "Host: myip.addr.space" https://[::]:8443/
::1
For completeness, here is the IPv4 version:
[error#murloc ~]$ curl -k -H "Host: myip.addr.space" https://0.0.0.0:8443/
127.0.0.1
Note that this is OS-specific. You would have received an error on Windows.

go-ping library for unprivileged ICMP ping in golang

I have been using go-ping library for the unprivileged ping and calculate various statistics of network in golang.
code snippet is as->
func (p *Ping) doPing() (latency, jitter, packetLoss float64, err error) {
timeout := time.Second*1000
interval := time.Second
count := 5
host := p.ipAddr
pinger, cmdErr := ping.NewPinger(host)
if cmdErr != nil {
glog.Error("Failed to ping " + p.ipAddr)
err = cmdErr
return
}
pinger.Count = count
pinger.Interval = interval
pinger.Timeout = timeout
pinger.SetPrivileged(false)
pinger.Run()
stats := pinger.Statistics()
latency = float64(stats.AvgRtt)
jitter = float64(stats.StdDevRtt)
packetLoss = stats.PacketLoss
return
}
It was working fine but now it has started throwing :-
"Error listening for ICMP packets: socket: permission denied" error.
Anyone knows the reason behind this? Go version I am using is go1.7.4.
This is in the README.md of the library you're using :
This library attempts to send an "unprivileged" ping via UDP. On linux, this must be enabled by setting
sudo sysctl -w net.ipv4.ping_group_range="0 2147483647"
If you do not wish to do this, you can set pinger.SetPrivileged(true) and use setcap to allow your binary using go-ping to bind to raw sockets (or just run as super-user):
setcap cap_net_raw=+ep /bin/goping-binary
See this blog and the Go icmp library for more details.
Hope it helps !
Make sure your setting haven't changed in any way. Using ping from the package still works for me on a 32-bit Ubuntu 16.04 with Go 1.7.4 (linux/386) if I previousely set the net.ipv4.ping_group_range according to the instructions on Github.
Note on Linux Support:
This library attempts to send an "unprivileged" ping via UDP. On linux, this must be enabled by setting
sudo sysctl -w net.ipv4.ping_group_range="0 2147483647"
If you do not wish to do this, you can set pinger.SetPrivileged(true) and use setcap to allow your binary
using go-ping to bind to raw sockets (or just run as super-user):
setcap cap_net_raw=+ep /bin/goping-binary
See this blog
and the Go icmp library for
more details.

amazon ec2, cannot ping internal host

In amazon ec2, I have 2 instances in a placement group. First node is 172.31.12.76/20, second, 172.31.12.77/20 I can ssh both nodes from my pc. They share the same security group that has got these 2 rules:
Inbound rules:
Type Protocol Port Range Source
SSH TCP 22 0.0.0.0/0
All IMCP All N/A 0.0.0.0/0
(no outbound rules)
Both nodes see to each other in L2:
root#ip-172-31-12-76:~# arp
[...]
ip-172-31-12-77.eu-west ether 0a:ad:5e:e4:12:de C eth0
[...]
root#ip-172-31-12-77:~# arp
[...]
ip-172-31-12-76.eu-west ether 0a:34:a1:17:57:28 C eth0
[...]
iptables are empty on both nodes.
But ping does not work between each other
I have already checked a previous post:
EC2 instances not responding to internal ping
but it does not address the issue. It looks like there are no other similar posts.
Any idea? Thank you very much!
I got the answer; I need to also allow outbound icmp on each host in order to be able to ping both external and internal IPs.

Resources