Updated on 2025-12-30 GMT+08:00

Automatically Configuring IPv4 Policy-based Routes for a Linux ECS with Multiple Network Interfaces (Ubuntu)

Scenarios

This section describes how to use an automation script to configure policy-based routes for a Linux ECS with two network interfaces. The automation script supports Ubuntu 22.04.

For details about the background knowledge and networking of an ECS with two network interfaces, see Overview.

Configuring Policy-based Routes for a Linux ECS with IPv4 Addresses

  1. Collect the information, such as IPv4 addresses, about ECS network interfaces for configuring policy-based routes.

    For details, see Collecting ECS Network Information.

    In this example, the network information of the ECS is shown in Table 1.

    Table 1 Linux ECSs using IPv4 addresses

    Type

    Primary Network Interface

    Extended Network Interface

    Source

    • IPv4 address: 10.0.0.115
    • Subnet IPv4 CIDR block: 10.0.0.0/24
    • Subnet IPv4 gateway: 10.0.0.1
    • IPv4 address: 10.0.1.183
    • Subnet IPv4 CIDR block: 10.0.1.0/24
    • Subnet IPv4 gateway: 10.0.1.1

    Destination

    IPv4 address: 10.0.2.12

    N/A

  2. Log in to the source ECS.

    For details, see How Do I Log In to My ECS?

  3. Check whether the source ECS can use its primary network interface to communicate with the destination ECS using IPv4 addresses:

    Before configuring policy-based routes, ensure that the source ECS can use its primary network interface to communicate with the destination ECS.

    ping -I IPv4-address-of-the-primary-network-interface-on-the-source-ECS IPv4-address-of-the-network-interface-on-the-destination-ECS

    Example command:

    ping -I 10.0.0.115 10.0.2.12

    If information similar to the following is displayed, the source ECS can use its primary network interface to communicate with the destination ECS.
    root@ecs-resource:~# ping -I 10.0.0.115 10.0.2.12
    PING 10.0.2.12 (10.0.2.12) from 10.0.0.115 : 56(84) bytes of data.
    64 bytes from 10.0.2.12: icmp_seq=1 ttl=64 time=0.262 ms
    64 bytes from 10.0.2.12: icmp_seq=2 ttl=64 time=0.193 ms
    64 bytes from 10.0.2.12: icmp_seq=3 ttl=64 time=0.086 ms
    ^C
    --- 10.0.2.12 ping statistics ---
    3 packets transmitted, 3 received, 0% packet loss, time 2045ms
    rtt min/avg/max/mdev = 0.086/0.180/0.262/0.072 ms
  4. Query the network interface names of the source ECS:

    ifconfig

    Search for the network interface names based on IP addresses.
    • The IPv4 address of the primary network interface is 10.0.0.115, and its name is eth0.
    • The IPv4 address of the extended network interface is 10.0.1.183, and its name is eth1.
    root@ecs-resource:~# ifconfig
    eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
            inet 10.0.0.115  netmask 255.255.255.0  broadcast 10.0.0.255
            inet6 fe80::f816:3eff:fe72:72e5  prefixlen 64  scopeid 0x20<link>
            ether fa:16:3e:72:72:e5  txqueuelen 1000  (Ethernet)
            RX packets 139035  bytes 201093027 (201.0 MB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 18669  bytes 1744334 (1.7 MB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    
    eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
            inet 10.0.1.183  netmask 255.255.255.0  broadcast 10.0.1.255
            inet6 fe80::f816:3eff:fe72:7329  prefixlen 64  scopeid 0x20<link>
            ether fa:16:3e:72:73:29  txqueuelen 1000  (Ethernet)
            RX packets 2  bytes 738 (738.0 B)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 35  bytes 2466 (2.4 KB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
  5. Create an automation configuration script to configure IPv4 routes.
    1. Create the automation configuration script file 15-policy-route.sh:

      vi /etc/NetworkManager/dispatcher.d/15-policy-route.sh

    2. Press i to enter the editing mode.
    3. Add the automation configuration script to the file.
      #!/bin/bash
      
      if [ "$2" != "up" ]; then
          exit 0
      fi
      
      interface=$1
      
      check_route_table() {
          local route_table_id=$1
          local ip_version=$2
          local max_attempts=100
          local attempts=0
      
          while [ $attempts -lt $max_attempts ]; do
              output=$(ip -$ip_version route show table $route_table_id &>/dev/null)
              if [ $? -ne 0 ] || [ -z "$output" ]; then
                  break
              else
                  route_table_id=$((route_table_id + 1))
                  attempts=$((attempts + 1))
              fi
          done
      
          echo $route_table_id
      }
      
      add_ipv4_route_table() {
          local DEFAULT_IPV4_ROUTE_TABLE_ID=1000
          v4_route_table_id=$(ip route show table all | grep -F "default via $gateway dev $interface table" | awk '{print $NF}')
          if [ -z $v4_route_table_id ]; then
              interface_number=$(echo $interface | grep -o '[0-9]*$')
              if [ -z "$interface_number" ]; then
                  v4_route_table_id="$DEFAULT_IPV4_ROUTE_TABLE_ID"
              else
                  v4_route_table_id=$((DEFAULT_IPV4_ROUTE_TABLE_ID + interface_number))
              fi
      
              v4_route_table_id=$(check_route_table $v4_route_table_id 4)
      
              echo "add policy route for dev: $interface table: $v4_route_table_id subnet: $subnet gateway: $gateway"
      
              ip route add default via $gateway dev $interface table $v4_route_table_id
              ip route add $subnet dev $interface table $v4_route_table_id
          fi
      }
      
      get_gateway_by_subnet() {
        subnet=$1
        IFS='/' read -r ip mask <<< "$subnet"
        IFS='.' read -r ip1 ip2 ip3 ip4 <<< "$ip"
        gateway="$ip1.$ip2.$ip3.$((ip4+1))"
        echo $gateway
      }
      
      generate_ipv4_policy_route() {
          subnet=$(nmcli device show $interface | grep -F 'IP4.ROUTE' | grep -F 'nh = 0.0.0.0' | cut -d'=' -f2 | cut -d',' -f1 | tr -d ' ' | head -n 1)
          gateway=$(get_gateway_by_subnet $subnet)
      
          add_ipv4_route_table
      
          nmcli device show $interface | grep -F 'IP4.ADDRESS' | while read -r line; do
              IP=$(echo $line | awk '{print $2}' | cut -d'/' -f1)
              if ip rule list | grep -F "$v4_route_table_id" | grep -q "$IP"; then
                  echo "ip rule already exists for $IP with table $v4_route_table_id"
                  continue
              fi
      
              echo "Adding rule for $IP on $interface with table $v4_route_table_id"
              ip rule add from $IP table $v4_route_table_id
          done
      }
      
      generate_ipv4_policy_route
    4. Press ESC to exit and enter :wq! to save the configuration.
    5. Check the permissions on the automatic configuration script file 15-policy-route.sh:

      ll /etc/NetworkManager/dispatcher.d/

      Information similar to the following is displayed. The permission on 15-policy-route.sh is -rw-r--r--, which is different from that on other system files, for example, permission -rwxr-xr-x on the file 01-ifupdown*.
      root@ecs-resource:~# ll /etc/NetworkManager/dispatcher.d/
      total 32
      drwxr-xr-x 5 root root 4096 Nov  7 12:21 ./
      drwxr-xr-x 7 root root 4096 Apr 22  2025 ../
      -rwxr-xr-x 1 root root 2293 Dec 11  2024 01-ifupdown*
      -rw-r--r-- 1 root root 2317 Nov  7 12:21 15-policy-route.sh
      -rwxrwxr-x 1 root root  719 May 10  2019 hook-network-manager*
      drwxr-xr-x 2 root root 4096 Dec 11  2024 no-wait.d/
      drwxr-xr-x 2 root root 4096 Dec 11  2024 pre-down.d/
      drwxr-xr-x 2 root root 4096 Dec 11  2024 pre-up.d/
    6. Grant permissions on the automation configuration script file 15-policy-route.sh:

      sudo chown root:root /etc/NetworkManager/dispatcher.d/15-policy-route.sh

      sudo chmod 755 /etc/NetworkManager/dispatcher.d/15-policy-route.sh

    7. Check the permissions on the automatic configuration script file 15-policy-route.sh again:

      ll /etc/NetworkManager/dispatcher.d/

      Information similar to the following is displayed. The permission on 15-policy-route.sh is -rwxr-xr-x.
      root@ecs-resource:~# ll /etc/NetworkManager/dispatcher.d/
      total 32
      drwxr-xr-x 5 root root 4096 Nov  7 12:21 ./
      drwxr-xr-x 7 root root 4096 Apr 22  2025 ../
      -rwxr-xr-x 1 root root 2293 Dec 11  2024 01-ifupdown*
      -rwxr-xr-x 1 root root 2317 Nov  7 12:21 15-policy-route.sh*
      -rwxrwxr-x 1 root root  719 May 10  2019 hook-network-manager*
      drwxr-xr-x 2 root root 4096 Dec 11  2024 no-wait.d/
      drwxr-xr-x 2 root root 4096 Dec 11  2024 pre-down.d/
      drwxr-xr-x 2 root root 4096 Dec 11  2024 pre-up.d/
    8. Restart the network service to execute the 15-policy-route.sh script:

      systemctl restart NetworkManager

      Before restarting the network service, ensure that services are not affected.

  6. Check whether the policy-based routes are added for the network interfaces with IPv4 addresses.

    ip rule

    ip route show table 1000

    ip route show table 1001

    table 1000 is the route table name of the primary network interface, and table 1001 is the route table name of the extended network interface.

    If information similar to the following is displayed, the policy-based routes have been added for the network interfaces with IPv4 addresses.
    root@ecs-resource:~# ip rule
    0:      from all lookup local
    32764:  from 10.0.1.183 lookup 1001
    32765:  from 10.0.0.115 lookup 1000
    32766:  from all lookup main
    32767:  from all lookup default
    root@ecs-resource:~# ip route show table 1000
    default via 10.0.0.1 dev eth0 
    10.0.0.0/24 dev eth0 scope link 
    root@ecs-resource:~# ip route show table 1001
    default via 10.0.1.1 dev eth1 
    10.0.1.0/24 dev eth1 scope link 
  7. Check whether the source and destination ECSs can communicate with each other using IPv4 addresses.

    ping -I IPv4-address-of-the-primary-network-interface-on-the-source-ECS IPv4-address-of-the-network-interface-on-the-destination-ECS

    ping -I IPv4-address-of-the-extended-network-interface-on-the-source-ECS IPv4-address-of-the-network-interface-on-the-destination-ECS

    Example commands:

    ping -I 10.0.0.115 10.0.2.12

    ping -I 10.0.1.183 10.0.2.12

    If information similar to the following is displayed, both the IPv4 addresses of the source ECS can communicate with the destination ECS, indicating that the IPv4 policy-based routes are successfully configured.
    root@ecs-resource:~# ping -I 10.0.0.115 10.0.2.12
    PING 10.0.2.12 (10.0.2.12) from 10.0.0.115 : 56(84) bytes of data.
    64 bytes from 10.0.2.12: icmp_seq=1 ttl=64 time=0.233 ms
    64 bytes from 10.0.2.12: icmp_seq=2 ttl=64 time=0.177 ms
    64 bytes from 10.0.2.12: icmp_seq=3 ttl=64 time=0.089 ms
    ^C
    --- 10.0.2.12 ping statistics ---
    3 packets transmitted, 3 received, 0% packet loss, time 2029ms
    rtt min/avg/max/mdev = 0.089/0.166/0.233/0.059 ms
    root@ecs-resource:~# ping -I 10.0.1.183 10.0.2.12
    PING 10.0.2.12 (10.0.2.12) from 10.0.1.183 : 56(84) bytes of data.
    64 bytes from 10.0.2.12: icmp_seq=1 ttl=64 time=0.300 ms
    64 bytes from 10.0.2.12: icmp_seq=2 ttl=64 time=0.194 ms
    64 bytes from 10.0.2.12: icmp_seq=3 ttl=64 time=0.100 ms
    ^C
    --- 10.0.2.12 ping statistics ---
    3 packets transmitted, 3 received, 0% packet loss, time 2025ms
    rtt min/avg/max/mdev = 0.100/0.198/0.300/0.081 ms