CARP (Common Address Redundancy Protocol) is an alternative protocol to VRRP (Virtual Router Redundancy Protocol) and the Cisco proprietary HSRP (Hot Standby Router Protocol).

BSD systems extensively use CARP while Linux systems prefer VRRP.

The objective is to make two different gateways respond to the same IP. This is done by creating a Virtual IP, different from the IPs used by both routers, to decide which will be active and which will be on standby.

In a network where two routers 10.0.0.2 and 10.0.0.3 communicate over CARP, they define which one responds to the Virtual IP 10.0.0.1. When the active router becomes inoperative, the standby takes over.


ON PFSENSE WEB-UI

On the Master, go to Firewall > Virtual IPs:

  • Click “+ Add“,
  • Set “Type” to “CARP“,
  • Select the LAN “Interface“,
  • Define the Virtual IP address in “Address(es)“, for example 10.0.0.1 with mask /24,
  • Define the “Virtual IP Password“,
  • Leave the VHID as ‘1‘ for the first entry; if you already have a virtual IP, choose a different number,
  • The “Description” can be ‘LAN Default Gateway‘, for example,
  • Click “Save“, then “Apply Changes“.

If you configured HV Sync [Read It], you do not need to repeat this on the Slave.

If the Virtual IP was created for the WAN interface, go to Firewall > NAT > Outbound:

  • Set “Outbound NAT Mode” to “Hybrid“,
  • Click “Save“, then “Apply Changes“,
  • Click “+ Add“,
  • In the “Edit Advanced Outbound NAT Entry” table, set “Source” to the LAN network address, for example 10.0.0.0/24,
  • Under “Translation” > “Address“, select the “WAN Virtual IP“,
  • Enter a “Description“, such as ‘WAN Virtual IP for NAT‘,
  • Click “Save” and “Apply Changes“.

This prevents packet loss during the transition to the Slave.


ON LINUX

On Linux, VRRP can be implemented using KeepAliveD [link].

Install it on all redundant instances in the cluster.

sudo apt update
sudo apt install keepalived -y
sudo nano /etc/keepalived/keepalived.conf

Configure all redundant instances as follows, assuming the interface name is eth0.

Primary

vrrp_instance VirtualIP01 {
    state MASTER
    interface eth0
    virtual_router_id 999
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 7bd3a2aa-f49b-11ef-8b04-ff74caed956a
    }
    virtual_ipaddress {
        10.0.0.1
    }
}

Backup

vrrp_instance VirtualIP01 {
    state BACKUP
    interface eth0
    virtual_router_id 999
    priority 90
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 7bd3a2aa-f49b-11ef-8b04-ff74caed956a
    }
    virtual_ipaddress {
        10.0.0.1
    }
}

Configuration

  • VirtualIP01 is an arbitrary name for this setup.
    • It helps identify the purpose of the configuration block in the file.
  • MASTER/BACKUP defines their roles at the initial state.
    • It determines which instance has authority over the active role.
  • eth0 is the interface name on each instance.
    • Interface names are tied to hardware and are specific to each instance.
  • 999 is a shared ID that tells instances which virtual address(es) a relationship refers to.
    • Servers can have multiple relationships for different address(es).
  • 90/100 is the priority assigned to each instance to determine which has preference to be active.
    • Priority numbers are more meaningful when there are multiple backups.

Enable the service on all instances.

sudo systemctl enable keepalived --now
sudo systemctl status keepalived
ip -4 -brief a

For more refined prioritization based on NGINX health, configure the master as follows.

vrrp_script Check_NGINX {
    script "/usr/bin/pgrep nginx"
    interval 2
    weight -30
}

vrrp_instance VirtualIP01 {
    state MASTER
    interface eth0
    virtual_router_id 999
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 7bd3a2aa-f49b-11ef-8b04-ff74caed956a
    }
    virtual_ipaddress {
        10.0.0.1
    }
    track_script {
        Check_NGINX
    }
}

Note: the script Check_NGINX checks if NGINX is running every 2 seconds. If it fails, the priority of the MASTER instance drops by 30 (from 100 to 70), allowing the BACKUP instance, with a priority of 90, to take over the virtual IP. It can also be configured to check interface status (down/up), monitor a running process, or manage routes based on status (using virtual_routes).

Reflections

Keep in mind that while the second server is the backup for the first in one or more IPs, the first server can also be the backup for the second in other VRRP relationships. Multiple servers can relate to each other in many ways simultaneously.

VRRP uses multicast by default, which raises security concerns in a shared broadcast domain (not recommended). Using unicast peer(s) does not eliminate the issue but provides more control over the communication. Replace X with the IP of the peer instance.

global_defs {
    enable_script_security
}
vrrp_instance VirtualIP01 {
    (...)
    unicast_src_ip 10.0.0.Y
    unicast_peer {
        10.0.0.X
    }
    (...)
}