Configure two network cards in a different subnet on RHEL 6, RHEL 7, CentOS 6 and CentOS 7

When configuring a Linux host running either Red Hat Linux 6, Red Hat Linux 7, CentOS 6 or CentOS7 with two network interface cards (NIC) that each have an IP address in a different network or subnet, you could end up in a situation where one of the IP addresses isn’t reachable outside it’s own network. Both IP’s will be responding to a ping from another host in the same network as those IP addresses but only one is responding to ping from another network. On most other distributions, like Debian, this issue, which is caused by asymmetric routing, doesn’t seem to exist.

When you’re experiencing this issue, it is most probably caused by asymmetric routing. In short this means that the interface that has received a packet doesn’t respond to the source of the packet by itself but via another interface. While this causes one of the interfaces to be unavailable from the outside on Red Hat based distributions, the problems with asymmetric routing also do exist on the other distributions although it seems to work fine.

Asymmetric routing

Asymmetric routing isn’t a big problem but it can cause issues related to NAT on firewalls and it has some impact on network performance.
routing_symmetricThe above image shows asymmetric routing: a packet sent from client A to the server, enters the server via network-interface eth0 but, due to the routing, it’s routed back to client A via interface eth1 of the server. For the client, it looks like everything is working normal. Packets do flow in and out from the server. It’s clear that interface eth1 will have a higher load that eth0 and while you’re thinking that you have two full 1Gbit interfaces, the speed is limited to the speed of eth1.

rp_filter

Since RHEL 6 (and CentOS 6), asymmetric routing doesn’t work anymore out of the box. The cause of the issue is a change in the default value for kernel parameter rp_filter.

Rp_filter stands for reverse path filtering. The reverse path filter will check if the source of a packet that was received on a certain interface is reachable trough the same interface it was received. The purpose is to prevent spoofed packets, with a changed source address, not being processed/routed further. In a router it could also prevent routing packets that have a private IP as source to the internet as they obviously will never find their way back.

Since RHEL 6 and its derivative CentOS 6, rp_filter, which can be controlled by kernel parameters, is set on a default value of 1. This means that the rp_filter is operational in strict mode and does exactly what it is designed for.

Possible value are:

  • 0: No source validation
  • 1: Strict mode (failed packets are discarded), described in RFC3704
  • 2: Loose mode, only discards the packet when it isn’t routable over any of the interfaces on the host.

To test this behavior, I set up a system that is connected to two networks: 192.168.0.0/24 and 192.168.1.0/24. I have a client in both networks and a client which is in another network.

The server:

Ping to the server from a client in the 192.168.0.0/24 network:

Ping to the server from a client in the 192.168.1.0/24 network

Ping to both IP’s from a client in another network:

As you can see, both IP’s are reachable from within the same network but only one, the one that has it’s default gateway in the routing table, is reachable from other networks.

In the output from the server, you can see that rp_filter for both interfaces has a value of 1, strict mode.

The quick and dirty solution

The easiest solution is just to change the value of rp_filter to allow packets to flow out of another interface that the source interface.

Now, let’s change the value of rp_filter to loose mode, value 2 and see what happens:

With a minimum of effort, both IP addresses are reachable from the outside. To make the change to rp_filter permanent, you can do the following:

For RHEL 6 and CentOS 6:

Edit /etc/sysctl.conf and change the value for rp_filter:

For RHEL 7 and CentOS 7:

The best solution

Besides the quick solution, there is also a better solution. While changing the value for rp_filter is getting both interfaces and IP-addresses to respond from other networks, the setup is still asymmetric. The best solution is to get rid of the asymmetric routing and let each interface route it’s own packets to the default gateway.

The goal is to become symmetric routing:

routing_asymmetric

Each interface on the server should have it’s own default gateway, which allows that interface to reply itself to incoming packets from other networks.

A normal routing table can only have one default gateway. This is quite logical since it’s the place where to send packets that do not match anything else in the rest of the table. To be able to have two default gateways, one for each interface, you need to setup policy based routing.

Policy based routing allows you to have multiple routing tables. Which table is used, depends on a set of rules.

To setup policy based routing for our example case, we will use two policy based tables. While it is possible to give a nice name to the tables (in /etc/iproute2/rt_tables), it’s not really when you only plan to have a few. Without a name, the tables are automatically created when you’re adding something to them.

Let’s start with adding a route for the network itself (link) and one for the default gateway for each interface. ens192 (192.168.0.10) will use table 1, ens224 (192.168.1.10) will use table 2.

To define when table 1 or 2 will be used, we’ll add a rule, based on the source of the packet to the policy and refresh the policy based routing:

To check if we did everything correctly, let’s list the tables and the rules:

As you can see in the output from ip rule show, our policy based tables have a higher priority than the main table, which can be viewed with ip route. Nevertheless it’s import to still have a default route in the main table since packets leaving the machine itself can have a source IP of 0.0.0.0 and would not match any of the rules in our policy.

Make the changes permanent

Up to now, the changes would get lost after a reboot or restart of the networking. To make the changes permanent, create a route and rule file for every interface. For the above example, the contents would look like this:

Now your configuration should be persistent.

While this solution is slightly more work than changing the value for rp_filter, it isn’t that hard and has a lot of advantages over the other solution.

28 thoughts on “Configure two network cards in a different subnet on RHEL 6, RHEL 7, CentOS 6 and CentOS 7

      • It’s not working, I still have to enter the commands after reboot.

        And moreover, it messed up 2 of my network connections (1 nic with multiple aliases), I had to reset them from Network Settings in GUI.

        I’m running Centos 7.

        • Sorry but without any knowledge of your exact config it’s hard to tell exactly what you need to do. The commands and config in this post are what I setup and tested. The idea is that you get the principle and you can use it as an example but you’ll have to adjust it to fit for your needs.

          • I think the problem with this is that while you are creating your initial tables with ‘ip route add’ that it’s not persistent in with the reboot. You probably want to manually add these tables to rt_tables prior?

  1. orientalsniper: On centos 7 to get this to stick use: net.ipv4.conf.all.rp_filter = 2 (all instead of default) in /etc/sysctl.conf worked for me.

  2. Thanks for writing this post! We ran into this problem at work, and this helped to identify and properly resolve the issue real quick.

  3. Thank for the post.
    I would like to add for permanent section that ‘NetworkManager doesn’t natively support policy routing but it can support Legacy route-[interface] files via NetworkManager-config-routing-rules package.’ says raatti on page https://www.centos.org/forums/viewtopic.php?f=50&t=50400
    Therefore permanent changes on CentOS 7 requires;
    yum install NetworkManager-config-routing-rules
    systemctl enable NetworkManager-dispatcher.service
    systemctl start NetworkManager-dispatcher.service

  4. Many thanks for this post!! I spent hours to try and figure why two nics in different vlans wasn’t working properly, while in old CentOS 5 it worked fine. Both solutions here solved these problems!

    • Nice catch. And a good question. I would need to look into this but for sure you could generate the rule-files using a script which is filling in the leased ip dynamically.

  5. how does this apply to earlier versions of redhat like version 5.2?

    i am about to come up against this problem,
    I have two different switches that go to my sonicwall firewall,
    switch a is on the 10.0.1.x subnet and switch b is on the 10.0.3.x subnet,
    on switch A is the Redhat 5.2 fileserver 10.0.1.5
    right now it has 2 active network connections –
    IB0 is an infiniband connection to a separate subnet 192.168.100.x this network is local and doesn’t goto the firewall.
    eth0 which is a 10gbe nic card that is connected to the 10gb switch on 10.0.1.x network, 10.0.1.5/255.255.255.0 gateway 10.0.1.1
    I have 2 spare 1gb ports on the server eth1 and eth2
    I need clients on the 10.0.3.x network to have access to the server without going through the firewall, as the speed through the firewall is horribly slow due to packets being analyzed.

    So i was thinking about running a cat 6 cable from eth1 nic to switch 2 – 10.0.3.x network switch and assigning it an ip 10.0.3.5/255.255.255.0 gateway 10.0.3.1

    and then having the clients that happen to be macs connect via nfs to the server using that ip address 10.0.3.5, however I’m worried about causing broadcast storms or other issues.

    whats the best way to tackle this? or will this work without any other problems

  6. Hello I just what to ask if there is any way that Client A and B can ping one another in this scence. Thanks a lot.

    • The ability for client A & B to ping each other is not really relevant for this article. Both clients should have a route (or via the default route) to each others subnet. In case you want routing to be done by the server, you will need to add routes on the clients for that.

  7. Dear sir,
    in my Scenario i have two nic with different network one is global and one is local with your giving setting both networks are accessible in local network but when i come from another global network (internet) i can’t access those both network only one network can accessible some time global or some time local kindly give me smooth and possible solution of my problem
    Thanks.

      • Hi Mohammad,

        What you’re looking for is not really what this post is about. You should search for a guide to configure NAT and port forwarding. This will allow you to reach the local addresses from the public IP and to reach the public network from the local addresses.

  8. What if server have 3 NICs?
    2 the same as in this case and third (i.e. eth2 and IP 10.10.0.1) is connected to Internet.
    On eth2 masquerade is on. Both nets see the internet, but could connect each other. It is necessary that some or all clients from 192.168.0.0/24 could connect with 192.168.1.0/24 and vice versa and at the same time they should have internet connection using eth2

    • You would need to add another entry for the 3rd card. Same for iptables and masquerade, you will need to specify the interface names on your iptables statements.

  9. Please need your help, i am getting this error

    [root@server iproute2]# ip route add 64.x.x.x/24 dev em1 tab 1
    RTNETLINK answers: Invalid argument

  10. Helo,

    Thank you for this post!)

    Everything works well, but I have warning message in the system log:

    NetworkManager[879]: ‘rule-‘ or ‘rule6-‘ file is present; you will need to use a dispatcher script to apply these routes

  11. If it’s not working for you on a CentOS 7
    just make sure you install the following packages first
    yum -y install NetworkManager-config-routing-rules
    systemctl enable NetworkManager-dispatcher.service

Leave a Reply

Your email address will not be published. Required fields are marked *