Skip to content

Instantly share code, notes, and snippets.

@spali
Last active November 11, 2025 17:07
Show Gist options
  • Save spali/2da4f23e488219504b2ada12ac59a7dc to your computer and use it in GitHub Desktop.
Save spali/2da4f23e488219504b2ada12ac59a7dc to your computer and use it in GitHub Desktop.
Disable WAN Interface on CARP Backup
#!/usr/local/bin/php
<?php
require_once("config.inc");
require_once("interfaces.inc");
require_once("util.inc");
$subsystem = !empty($argv[1]) ? $argv[1] : '';
$type = !empty($argv[2]) ? $argv[2] : '';
if ($type != 'MASTER' && $type != 'BACKUP') {
log_error("Carp '$type' event unknown from source '{$subsystem}'");
exit(1);
}
if (!strstr($subsystem, '@')) {
log_error("Carp '$type' event triggered from wrong source '{$subsystem}'");
exit(1);
}
$ifkey = 'wan';
if ($type === "MASTER") {
log_error("enable interface '$ifkey' due CARP event '$type'");
$config['interfaces'][$ifkey]['enable'] = '1';
write_config("enable interface '$ifkey' due CARP event '$type'", false);
interface_configure(false, $ifkey, false, false);
} else {
log_error("disable interface '$ifkey' due CARP event '$type'");
unset($config['interfaces'][$ifkey]['enable']);
write_config("disable interface '$ifkey' due CARP event '$type'", false);
interface_configure(false, $ifkey, false, false);
}
@oLeDfrEeZe
Copy link

I have not been able to get this working on anything newer than 25.1.12, the syshook script seems to be getting called with the BACKUP parameter and it’s bringing the WAN interface down even when the carp state is master. Can anyone else duplicate this?

i use this script on OPNsense 25.7.2. Unlike the original, it only triggers on CARP events for a specific CARP address (replace "99@vlan099" with your CARP interface to react on). Replace "wan" with your WAN interface and "WAN_GATEWAY" with your WAN gateway name. It properly handles interface, gateway, and DHCP client states to avoid failover issues.

#!/usr/local/bin/php
<?php

require_once("config.inc");
require_once("interfaces.inc");
require_once("util.inc");
require_once("system.inc");


$subsystem = !empty($argv[1]) ? $argv[1] : '';
$type      = !empty($argv[2]) ? $argv[2] : '';

// Only continue for MASTER or BACKUP events
if (!in_array($type, ['MASTER', 'BACKUP', 'INIT'])) {
    log_msg("CARP event '$type' unknown from source '{$subsystem}'");
    exit(1);
}

// Filter on your specific CARP interface, e.g. "99@vlan099"
if ($subsystem !== "99@vlan099") {
    exit(0);
}

$ifkey       = 'wan';                // Your WAN interface key
$real_if     = get_real_interface($ifkey);
$wan_gw_name = 'WAN_GATEWAY';      // Your WAN gateway name

if ($type === "MASTER") {
    log_error("Enable interface '$ifkey' due to CARP event '$type' on '{$subsystem}'");
    // 1. Enable interface
    $config['interfaces'][$ifkey]['enable'] = '1';
    write_config("Enable interface '$ifkey' due to CARP event '$type'", false);
    interface_configure(false, $ifkey, false, false);
    sleep(2);
    shell_exec("/sbin/ifconfig {$real_if} up");
    log_msg("Issuing dhclient command on interface '$real_if' to request DHCP lease");
    sleep(1);
    shell_exec("dhclient {$real_if}");
    // 2. Enable gateway after interface is up
    mwexec("/usr/local/sbin/configctl gateway enable {$wan_gw_name}");

} elseif ($type === "BACKUP") {
    log_error("Disable gateway '$wan_gw_name' and interface '$ifkey' due to CARP event '$type' on '{$subsystem}'");
    // 1. Disable gateway first
    mwexec("/usr/local/sbin/configctl gateway disable {$wan_gw_name}");
    sleep(1);
    // 2. Explicitly kill DHCP client to stop renews
    shell_exec("killall -9 dhclient");
    sleep(1);
    // 3. Disable interface
    unset($config['interfaces'][$ifkey]['enable']);
    write_config("Disable interface '$ifkey' due to CARP event '$type'", false);
    interface_configure(false, $ifkey, false, false);
    shell_exec("/sbin/ifconfig {$real_if} down");
}

?>

@coderph0x
Copy link

coderph0x commented Sep 5, 2025

i use this script on OPNsense 25.7.2. Unlike the original, it only triggers on CARP events for a specific CARP address (replace "99@vlan099" with your CARP interface to react on). Replace "wan" with your WAN interface and "WAN_GATEWAY" with your WAN gateway name. It properly handles interface, gateway, and DHCP client states to avoid failover issues.

Works great, thanks a lot! If your WAN-Interface works with PPPoE, you might want to have a look at https://github.com/Pieshka/minibox-pppoe-annihilator
Switching the gateway costs around three pings even with DHCP, and deactivating the gateway isn't even necessary. Disabling the interface is sufficient.

@USBAkimbo
Copy link

USBAkimbo commented Sep 25, 2025

My WAN DHCP script seems to have broken recently as well

I fed some context and versions to Claude and got this

I'll note that this is specific to my setup and works great, though I imagine others will have a similar setup

  • 2x OPNsense VMs in Proxmox, each on separate physical nodes
  • Each OPNsense VM has a WAN interface on VLAN 2
  • The NIC on each WAN interface have the same MAC address (because only 1 is ever active at once - so from the router in modem mode's perspective, OPNsense just changed switch ports
  • My Virgin ISP router in modem mode is plugged into a port in VLAN 2
  • Virgin run DHCP on WAN, so the IP config is received automatically
#!/usr/local/bin/php

# This files lives in
# /usr/local/etc/rc.syshook.d/carp/50-dhcp

<?php

require_once("config.inc");
require_once("interfaces.inc");
require_once("util.inc");
require_once("plugins.inc");

$subsystem = !empty($argv[1]) ? $argv[1] : '';
$type = !empty($argv[2]) ? $argv[2] : '';

if ($type != 'MASTER' && $type != 'BACKUP') {
    log_error("Carp '$type' event unknown from source '{$subsystem}'");
    exit(1);
}

if (!strstr($subsystem, '@')) {
    log_error("Carp '$type' event triggered from wrong source '{$subsystem}'");
    exit(1);
}

$ifkey = 'wan';
$realif = get_real_interface($ifkey);

if (empty($realif)) {
    log_error("Could not determine real interface for '$ifkey'");
    exit(1);
}

if ($type === "MASTER") {
    log_error("CARP MASTER event: Enabling WAN interface '$ifkey' ($realif)");
    
    // Bring the interface up at OS level
    mwexec("/sbin/ifconfig {$realif} up");
    
    // Small delay to ensure interface is ready
    sleep(2);
    
    // Reconfigure the interface (this will trigger DHCP client)
    interface_configure(false, $ifkey, true, false);
    
    // Explicitly request DHCP renewal if interface uses DHCP
    if (!empty($config['interfaces'][$ifkey]['ipaddr']) && 
        $config['interfaces'][$ifkey]['ipaddr'] == 'dhcp') {
        
        log_error("Requesting DHCP renewal on '$ifkey' ($realif)");
        
        // Kill any existing dhclient process for this interface
        mwexec("/bin/pkill -f 'dhclient: {$realif}'");
        
        // Small delay after killing dhclient
        sleep(1);
        
        // Restart DHCP client
        interface_dhcp_configure($ifkey);
        
        // Alternative method if the above doesn't work:
        // mwexec("/usr/local/sbin/configctl interface reconfigure {$ifkey}");
    }
    
} else {
    log_error("CARP BACKUP event: Disabling WAN interface '$ifkey' ($realif)");
    
    // Release DHCP lease if applicable
    if (!empty($config['interfaces'][$ifkey]['ipaddr']) && 
        $config['interfaces'][$ifkey]['ipaddr'] == 'dhcp') {
        
        log_error("Releasing DHCP lease on '$ifkey' ($realif)");
        
        // Kill dhclient to release the lease
        mwexec("/bin/pkill -f 'dhclient: {$realif}'");
        
        // Explicitly release DHCP lease
        mwexec("/sbin/dhclient -r {$realif}");
    }
    
    // Bring the interface down at OS level
    mwexec("/sbin/ifconfig {$realif} down");
    
    // Clear any remaining IP configuration
    mwexec("/sbin/ifconfig {$realif} inet 0.0.0.0 delete 2>/dev/null");
    mwexec("/sbin/ifconfig {$realif} inet6 ::1 delete 2>/dev/null");
}

// Reload filter rules to accommodate interface state change
filter_configure();

// Signal any plugins about the interface change
plugins_configure('interface', false, array($ifkey));

log_error("CARP WAN failover script completed for '$type' state");

@coderph0x
Copy link

The script doesn't break, it gets reset to default if you update to the next version. I haven't checked what Claude made differentl, however the latest versions posted above still work well.

@USBAkimbo
Copy link

@coderph0x sorry I mean I had a different, older script - not one from this thread

@kronenpj
Copy link

kronenpj commented Sep 26, 2025

I used my script at https://gist.github.com/kronenpj/e90258f12f7a40c4f38a23b609b3288b many times last week while diagnosing a problem. It works very well for me on Opnsense 25.7.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment