Search code examples
batch-filefailover

Detecting batch IP conflict


How would you detect an IP conflict?

I am trying to implement a fail-over of two systems. Let us assume they take the IPs X.X.X.1 and X.X.X.2 (A and B for convenience), with A as the primary server and B as the back-up.

Both A and B will continuously ping X.X.X.1. Should A ever go down, B will detect "request timed out", and convert itself to X.X.X.1 using the following command:

netsh int ipv4 set address name="Local Area Connection" source=static address=X.X.X.1 mask=255.255.255.0 gateway=none

When A reconnects itself, I want fail-over to happen smoothly and automatically. Now, since there are two machines with X.X.X.1, there will be an IP conflict. B retains X.X.X.1, while A, being the "later" computer, will receive this conflict. When A tries to ping X.X.X.1 again, A instead receives:

PING: General failure.

A will then detect this somehow and convert itself to X.X.X.2. Now both machines are running fine, just that they are mirror-imaged.

Or that's the logic anyway. Currently, I am having trouble detecting the PING: General failure. How would one go about doing this?

Or if there's a better way to do fail-over, what would it be?


Solution

  • I think you need to redirect the ping command's stderr to stdout with 2>&1 before you can test for errors.

    :loop
    ping x.x.x.1 2>&1 | find /i "general failure" && (
            netsh int ipv4 set address name="Local Area Connection" source=static address=X.X.X.2 mask=255.255.255.0 gateway=none
    )
    goto loop
    

    It might be better to check for success rather than failure. Here's something a little more robust that should switch from .2 to .1 if .1 dies; and from .1 to .2 if conflict.

    @echo off
    setlocal
    
    :: Host to ping
    set primary=x.x.x.1
    :: Ping with options (1 ping sent per loop, wait 500 ms for timeout)
    set ping_options=-n 1 -w 500
    :: Fail over after x ping failed responses
    set fail_limit=5
    
    :loop
    
    :: Ping x.x.x.1.  Test for "reply from".  If success, set failures=0; otherwise, increment failures
    ( ping %ping_options% %primary% 2>&1 | find /i "reply from" >NUL && set failures=0 ) || set /a "failures+=1"
    
    :: If failures >= limit, switch IP
    if failures GEQ %fail_limit% call :switch
    
    :: Pause for a second and begin again.
    ping -n 2 0.0.0.0 >NUL
    goto loop
    
    
    :: Switch subroutine
    :switch
    
    :: Get current IPv4 address
    for /f "tokens=2 delims={}," %%I in ('wmic nicconfig where ipenabled="TRUE" get ipaddress /format:list') do set IP=%%~I
    
    :: If the last character if the current IP is 1, switch to 2 (or vice versa)
    if %IP:~-1%==1 ( set other=%IP:0,-1%2 ) else set other=%IP:0,-1%1
    
    :: Perform the switch
    netsh int ipv4 set address name="Local Area Connection" source=static address=%other% mask=255.255.255.0 gateway=none