Linux – Route Specific Source IP to Destination IP with iproute or iptables

iprouteiptableslinuxnetworking

Problem

I've already created different IP aliases, (each with a different virtual Mac Address too) this way on my shell:

ip link add link eth0 address 00:11:11:11:11:11 eth0.1 type macvlan

ifconfig eth0.1 172.17.1.15/21 up

I have used eth0.1 instead of eth0:1 because otherwise, it does not work the macvlan. I know that the IP alias are written as: eth0:1, though.

It is set a speed throttling to 10Mbps per IP (or Mac Addr, I do not know yet) and I am studying its bypassing for a well intentioned project.

What I want now is to set a specific destination for each IP alias in order to study the router's speed throttling. It should now have 10Mbps per IP/connection.

For instance:

Router: 172.17.0.1/21

eth0> 172.17.1.14/21

eth0.1> 172.17.1.15/21

eth0.2> 172.17.1.16/21

I need now the eth0.1 to be the source to access: URL(speedtest1Web).

And the eth0.2 the source to access: URL(speedtest2Web).

I have tried:

iptables -t nat -A POSTROUTING -p tcp -s 172.17.1.15 -o eth0.1 -j SNAT --to-source xxx.xxx.xxx.xxx

or

iptables -t nat -I POSTROUTING -o eth0 -d xxx.xxx.xxx.xxx/32 -s 10.255.0.127 -j SNAT --to-source 172.17.1.15

where xxx.xxx.xxx.xxx = speedtest1Web

Is it correct the "-o eth0"? Or should I write "-o eth0.1"?

I have been trying many similar commands with no success or at least I continue having 10Mbps for all the connections instead of 10Mbps per connection (what it is supposed now with the IP aliasing).

Best Answer

You don't need usage of macvlan in your case.

  • Assign addresses on single interface. Aliases are deprecated way to have multiple addresses on same interface. You need macvlan only if you want have various mac addresses for various ip addresses, but it complicates the configuration of routing.
 l1:~# ip link set up dev eth0
 l1:~# ip address add 172.17.1.14/21 dev eth0
 l1:~# ip address add 172.17.1.15/21 dev eth0
 l1:~# ip address add 172.17.1.16/21 dev eth0
  • Verify list of addresses on the eth0 interface:
 l1:~# ip -4 a ls dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 172.17.0.14/21 scope global eth0
       valid_lft forever preferred_lft forever
    inet 172.17.0.15/21 scope global secondary eth0
       valid_lft forever preferred_lft forever
    inet 172.17.0.16/21 scope global secondary eth0
       valid_lft forever preferred_lft forever
  • Add the default route and the default source address, then verify configuration:
l1:~# ip route add 0/0 via 172.17.0.1 src 172.17.0.14
l1:~# ip route list
default via 172.17.0.1 dev eth0 src 172.17.0.14 
172.17.0.0/21 dev eth0 proto kernel scope link src 172.17.0.14 
  • You have two ways to use the secondary addresses for specific destination. You can add the route to a particular destination with specifying src attribute:

Use 172.17.0.15 as source address for 192.168.10.2 destination

l1:~# ip route add 192.168.10.2 via 172.17.0.1 src 172.17.0.15

Use 172.17.0.16 as source address for 192.168.11.2 destination

l1:~# ip r add 192.168.11.2 via 172.17.0.1 src 172.17.0.16

Verify the routing table:

l1:~# ip r ls
default via 172.17.0.1 dev eth0 src 172.17.0.14 
172.17.0.0/21 dev eth0 proto kernel scope link src 172.17.0.14 
192.168.10.2 via 172.17.0.1 dev eth0 src 172.17.0.15 
192.168.11.2 via 172.17.0.1 dev eth0 src 172.17.0.16 

Also you can check the actual routes for a specific destination with ip route get command:

l1:~# ip route get 192.168.10.2
192.168.10.2 via 172.17.0.1 dev eth0 src 172.17.0.15 uid 0 
    cache 

There is a short version of same command:

l1:~# ip r g 192.168.11.2
192.168.11.2 via 172.17.0.1 dev eth0 src 172.17.0.16 uid 0 
    cache 

Check the results with ping and tcpdump. Ping the hosts and check the output of tcpdump in an other console. You can use the wireshark instead tcpdump.:

l1:~# tcpdump -ni eth0 'icmp'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
10:21:07.972535 IP 172.17.0.14 > 172.17.0.1: ICMP echo request, id 23048, seq 0, length 64
10:21:07.974416 IP 172.17.0.1 > 172.17.0.14: ICMP echo reply, id 23048, seq 0, length 64
10:21:15.391709 IP 172.17.0.15 > 192.168.10.2: ICMP echo request, id 23304, seq 0, length 64
10:21:15.393515 IP 192.168.10.2 > 172.17.0.15: ICMP echo reply, id 23304, seq 0, length 64
10:21:18.207461 IP 172.17.0.16 > 192.168.11.2: ICMP echo request, id 23560, seq 0, length 64
10:21:18.209391 IP 192.168.11.2 > 172.17.0.16: ICMP echo reply, id 23560, seq 0, length 64
  • Other way is usage the SNAT target in the iptables. But if you have the hundreds of similar rules, it may impact the performance.
l1:~# iptables -t nat -A POSTROUTING \
               -o eth0 --dst 192.168.10.2 \
          -j SNAT --to-source 172.17.0.15
l1:~# iptables -t nat -A POSTROUTING \
               -o eth0 --dst 192.168.11.2 \
          -j SNAT --to-source 172.17.0.16

Better use the iptables-save and iptables-apply to safe configure the iptables. Verification is the same as in the route case - with ping and tcpdump. Also, you can check the rule counters to sure those rules work.

l1:~# iptables-save -c -t nat
# Generated by iptables-save v1.6.2 on Wed May 15 10:31:26 2019
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [4:336]
:POSTROUTING ACCEPT [0:0]
[2:168] -A POSTROUTING -d 192.168.10.2/32 -o eth0 -j SNAT --to-source 172.17.0.15
[2:168] -A POSTROUTING -d 192.168.11.2/32 -o eth0 -j SNAT --to-source 172.17.0.16
COMMIT
# Completed on Wed May 15 10:31:26 2019
  • There is the third way to use the specific source. In some applications you can specify the source in a configuration file or with command line arguments. For ping this is -I option:
l1:~# ping -c 2 -I 172.17.0.15 192.168.11.2
PING 192.168.11.2 (192.168.11.2) from 172.17.0.15: 56 data bytes
64 bytes from 192.168.11.2: seq=0 ttl=63 time=2.348 ms
64 bytes from 192.168.11.2: seq=1 ttl=63 time=1.270 ms

--- 192.168.11.2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 1.270/1.809/2.348 ms
l1:~# tcpdump -ni eth0 'icmp'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
10:37:08.724723 IP 172.17.0.15 > 192.168.11.2: ICMP echo request, id 37384, seq 0, length 64
10:37:08.726805 IP 192.168.11.2 > 172.17.0.15: ICMP echo reply, id 37384, seq 0, length 64
10:37:09.724985 IP 172.17.0.15 > 192.168.11.2: ICMP echo request, id 37384, seq 1, length 64
10:37:09.726084 IP 192.168.11.2 > 172.17.0.15: ICMP echo reply, id 37384, seq 1, length 64
  • Also, you should know, what neither ip route or iptables know nothing about domain-names and can use only ip address of destination. Iptables can resolve domain-name at rule creation, but ip address in it won't being updated magically. There are some tricks to avoid this limitation:
    • Fix the ip address associated with domain-name. You can use the /etc/hosts file to do it. This way suitable for quick tests and DNS records with long TTL.
    • Most flexible way: usage of dnsmasq, ipset and iptables. In this case dnsmasq resolves the domain-names and stores its ip addresses into ipset lists. You can use these lists in the iptables SNAT rules.
Related Topic