0

Here is how to reproduce this problem:

  1. Start a listen service on port 80: nc -l 80 -k
  2. Access this service using curl: curl 127.0.0.1 --interface eth0
  3. Capture the packet using tcpdump both on eth0 and lo: tcpdump -i eth0 port 80 -nn -v, tcpdump -i lo port 80 -nn -v

The tcpdump output shows that the SYN packet was sent from eth0 and didn't get any reply, the tcp client just retransmit SYN until reach the retransmission limits.

However when I changed the destination ip to 192.168.16.4 (also a local ip which is bounded to eth0), accessing succeeded, and the tcpdump output shows that the SYN packet was sent from lo.

Here is the ifconfig output:

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.16.4  netmask 255.255.240.0  broadcast 192.168.31.255
        inet6 fe80::f820:20ff:fe16:588c  prefixlen 64  scopeid 0x20<link>
        ether fa:20:20:16:58:8c  txqueuelen 1000  (Ethernet)
        RX packets 16248748  bytes 2161348902 (2.0 GiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 15203841  bytes 4648786129 (4.3 GiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 4832071  bytes 2872871764 (2.6 GiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 4832071  bytes 2872871764 (2.6 GiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

And the route table content:

$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.16.1    0.0.0.0         UG    100    0        0 eth0
169.254.169.254 192.168.16.2    255.255.255.255 UGH   100    0        0 eth0
192.168.16.0    0.0.0.0         255.255.240.0   U     100    0        0 eth0

$ ip route show table local
broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1 
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1 
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1 
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1 
broadcast 192.168.16.0 dev eth0 proto kernel scope link src 192.168.16.4 
local 192.168.16.4 dev eth0 proto kernel scope host src 192.168.16.4 
broadcast 192.168.31.255 dev eth0 proto kernel scope link src 192.168.16.4

I use strace to record the systemcall log when curl, it shows that --interface eth0 option means: setsockopt(3, SOL_SOCKET, SO_BINDTODEVICE, "eth0\0", 5)

I have seen the discussion in https://github.com/iputils/iputils/issues/198 (the ping -I does in the same way) and https://stackoverflow.com/questions/46036667/route-between-network-interfaces-ubuntu

I'm wondering:

  1. Why curl 127.0.0.1 --interface eth0 can't access the service, what happened to the SYN packet and when it's dropped?
  2. Why curl 192.168.16.4 --interface eth0 is using lo interface to communicate but curl 127.0.0.1 --interface eth0 using eth0
  3. What I should do if I want curl 127.0.0.1 --interface eth0 can work.

Update:

The Linux Kernel version I'm using is 4.18.0. And for curl 192.168.16.4 --interface eth0, the tcpdump -i lo output is:

$ tcpdump -i lo port 80 -nn
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
09:15:38.915880 IP 192.168.16.4.40850 > 192.168.16.4.80: Flags [S], seq 1020241664, win 43690, options [mss 65495,sackOK,TS val 2673572844 ecr 0,nop,wscale 7], length 0
09:15:38.915891 IP 192.168.16.4.80 > 192.168.16.4.40850: Flags [S.], seq 3808229193, ack 1020241665, win 43690, options [mss 65495,sackOK,TS val 2673572844 ecr 2673572844,nop,wscale 7], length 0
09:15:38.915900 IP 192.168.16.4.40850 > 192.168.16.4.80: Flags [.], ack 1, win 342, options [nop,nop,TS val 2673572844 ecr 2673572844], length 0
09:15:38.915928 IP 192.168.16.4.40850 > 192.168.16.4.80: Flags [P.], seq 1:77, ack 1, win 342, options [nop,nop,TS val 2673572844 ecr 2673572844], length 76: HTTP: GET / HTTP/1.1
09:15:38.915931 IP 192.168.16.4.80 > 192.168.16.4.40850: Flags [.], ack 77, win 342, options [nop,nop,TS val 2673572844 ecr 2673572844], length 0
2
  • 1
    I don't know how to answer this other than "127.0.0.1 is only bound to lo and no other interface". That's simply what the loopback interface is designed to do.
    – Panki
    Jul 11, 2022 at 12:40
  • @Panki So you mean it's impossible for lo to communicate with eth0?
    – Jack Yang
    Jul 11, 2022 at 15:58

1 Answer 1

2

It looks very much like an XY-problem, but:

There are two interfaces on your system:

  • eth0, with an IP address of 192.168.16.4
  • lo, with an IP address of 127.0.0.1

Of course, all directly connected IP addresses to that interface will be accessible via that interface. For eth0 that would be anything in 192.168.16.0/20 and for lo, anything in 127.0.0.1.

For anything not in the directly coupled range, the data will first be sent to the gateway, which will then route it further on to the destination. So far, so good.

However, 127.0.0.0/8 is reserved for the loopback network. The router will not route that further. So even if you send it to the gateway, it will not be routed further-on.

Even if the router would route it further, there is no access to your lo interface. For eth0, the router has access via that cable that you plugged in. There is no such access to your lo interface; it is accessible only on your local system.

So, to your questions:

  • Why curl 127.0.0.1 --interface eth0 can't access the service, what happened to the SYN packet and when it's dropped?

Where it is dropped depends a bit on your OS. But it will not get any further than the gateway.

  • Why curl 192.168.16.4 --interface eth0 is using lo interface to communicate but curl 127.0.0.1 --interface eth0 using eth0

This simply does not happen. curl 192.168.16.4 --interface eth0 does not use lo.

  • What I should do if I want curl 127.0.0.1 --interface eth0 can work.

Kernel patches, if you have systemd running, patches there too. curl should be OK. You will also need a custom software on the router. You will need routing enabled on your system (with the aforementioned kernel patches applied of course and a modified version of zebra). My advice: don't even think about it. Seriously re-evaluate why you would even want to try this.

As addition, as elaborate response to

But the curl 192.168.16.4 --interface eth0 did use lo interface

What you might see on device with systemd is (using curl dullaart.website):

07:41:06.008577 IP localhost.domain > localhost.45840: 6127 1/0/0 PTR localhost. (64)
07:41:12.433744 IP localhost.52411 > localhost.domain: 39930+ A? dullaart.website. (34)
07:41:12.433795 IP localhost.52411 > localhost.domain: 49165+ AAAA? dullaart.website. (34)
07:41:12.550329 IP localhost.domain > localhost.52411: 39930 1/0/0 A 212.1.212.17 (50)
07:41:12.582833 IP localhost.domain > localhost.52411: 49165 0/0/0 (34)

This is name resolving that is done on the server that is specified in /etc/resolve.conf, which is

nameserver 127.0.0.53

That is on the loopback network.

Some systems (but not all) may use lo as a short-cut if you go to your own system (as you did in the curl output in your question). This can be done, because the kernel knows that the same system is behind both interfaces. So curl may use lo if, and only if, your target is your own system.

1
  • Thanks for your answer. Yeah this requirement is strange, I just want to understand how these stuffs work. But the curl 192.168.16.4 --interface eth0 did use lo interface, I was using tcpdump -i lo to watch, you can also try.
    – Jack Yang
    Jul 12, 2022 at 1:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .