When you tell an application to use a specific IP address, the application is using the IP address, not the interface. Some applications do let you use a specific interface, but this is a separate behavior (SO_BINDTODEVICE).
Since the application is binding to an IP address, and not an interface, the kernel is free to use whatever interface it wants. To determine which interface to use, it uses the routing tables (yes, there are multiple).
If you just want a quick way to determine what interface/route the traffic will take, you can use ip route get 1.2.3.99 from 1.2.3.4
, which will output something like:
# ip route get 1.2.3.99 from 1.2.3.4
local 1.2.3.99 from 1.2.3.4 dev lo
cache <local>
This shows that the kernel is going to send the traffic over the lo
interface.
To walk through why, lets start with the command ip rule
:
# ip rule
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
This shows all the routing tables the kernel is going to use to find the route for traffic. It starts with the top, and stops on the first match. The from all
means that the rule matches any source address. So it's going to consult table local
first. We can then look inside this table:
# 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 1.2.3.0 dev eth0 proto kernel scope link src 1.2.3.4
local 1.2.3.4 dev eth0 proto kernel scope host src 1.2.3.4
broadcast 1.2.3.255 dev eth0 proto kernel scope link src 1.2.3.4
local 1.2.3.99 dev eth1 proto kernel scope host src 1.2.3.99
(yours will likely look different)
From this we then look to see if any of the routes match the destination address (1.2.3.99) by looking to see if the destination matches the second field. In the output above, the very last one matches. In this line, the first field is local
, which according to man ip-route
means:
local - the destinations are assigned to this host. The packets are looped back and delivered locally.
This means that the traffic is going to flow over the lo
interface.
As for how to make it use the A
/B
interface, you have 2 options:
1) The application would need to provide you with an argument where you can specify the interface. There are a dozen flavors of netcat, but the version on my systems does not have such an option. socat
does though (I personally recommend socat
over netcat because of the inconsistency & portability nightmare that is netcat. It's also way more powerful).
2) Create a non-local
route that matches before the local
one:
ip route add local 1.2.3.99 dev B table main
ip route del local 1.2.3.99 dev B table local
ip route add 1.2.3.99 dev B table local
In these rules, the first 2 rules move the local
route into the main
table. Adding the route to the main
table has to come first as the host has to have a local
route somewhere for the kernel to accept traffic for that address. Having the route in 2 tables is OK. After that we then add a new route to the local
table which does not have the local
designation, which will result in traffic not going over the lo
interface.
/24
, you're likely going to be frequently running into problems. Having 2 interfaces on the same subnet can be done, but it presents numerous difficulties to get working properly.