Using ifstated to watch an egress link

· 3min · Dan F.

While developing my own OpenBSD router, I stumbled across a built-in service called ifstated. Previously, I was using a cronjob to run a script every five minutes to check the status of pppoe0. However, ifstated is able to do everything that my script could, in a more powerful way.

The inspiration for this configuration file originated heavily from calomel's tutorial. I did modify a handful of items though, to better tailor it to my own router's design.

The egress_check.sh script is used to run extra steps, such as email the admin if the egress IP has changed.

/etc/ifstated.conf

init-state start 

# Check if external link is up. 
link_up = '( "ifconfig egress | grep -q \"status: active\"" every 10 )'

# Check to see if egress is working
ping_check = '( "ifconfig egress | grep -q \"inet *.*.*.* netmask\" && ping -c 3 -w 5 8.8.8.8 >/dev/null" every 20 )'

# Check if this router is using pppoe to connect
pppoe_check = '( "ifconfig | grep -q pppoe" every 5 )'

state start {
    init {
        run "logger \"Starting ifstated\""
        run "touch /tmp/.ifstated.start"
    }
    if $link_up {
        if $ping_check {
            set-state online
        }
        if ! $ping_check {
            set-state offline
        }
    }
    if ! $link_up {
        set-state egress_down
    }
}

state online {
    # This will look at the IP currently on egress, and notify admin via email if it has been changed
    init {
        run "test ! -e /tmp/.ifstated.start && /home/admin/bin/egress_check.sh || rm /tmp/.ifstated.start"
    }
    if ! $ping_check {
        set-state offline
    }
}

state offline {
    init {
        run "logger \"Egress is active, but we cannot ping 8.8.8.8\""
        run "echo -e \"Router egress is online, but cannot ping out\"\n $(ifconfig egress) | mail -s \"$(hostname -s) is offline\" $myemail@email.com"
        if $pppoe_check {
            run "ifconfig pppoe0 up"
        }
        if ! $pppoe_check {
            run "ifconfig em0 up"
        }
    }
    if ! $link_up {
        set-state egress_down
    }
    if $ping_check {
        set-state online
    }
}

state egress_down {
    init {
        run "logger \"Egress is disconnected\""
        run "echo \"$(date; ifconfig egress)\" | mail -s \"$(hostname -s) Egress is disconnected\" $myemail@email.com"
    }
    if $link_up {
        if $ping_check {
            set-state online
        }
        if ! $ping_check {
            set-state offline
        }
    }
}

This the the egress script that gets called when the router/server changes into the online state. I run a pppoe connection at home, and would like to be notified when the link goes down, or if my external IP is changed.

egress_check.sh

#!/usr/bin/env bash

egress_file=/tmp/ifstated_egress
egress_ip=$(ifconfig egress | grep -m1 inet | awk '{print $2}')
admin_email=$myemail@mail.com

#
# If the egress file exists, read it. If not, create a new one with the current egress ip.
# Since OpenBSD clears /tmp on reboot, assume this is a new boot
if [[ ! -e $egress_file ]]; then
    echo $egress_ip > $egress_file
    logger "Egress log absent, creating"
    exit 0
fi

#
# Compare the egress log to the current egress ip. If they differ, email admin
egress_old=$(cat $egress_file)
if [[ $egress_ip != $egress_old ]]; then
    logger "IP has changed to $egress_ip" 
    echo $egress_ip > $egress_file
    if [[ -n $admin_email ]]; then
        echo "IP has changed $(uptime) $(ifconfig egress)" | mail -s "$(hostname -s) IP has changed" $admin_email
    fi
    exit 0
fi

#
# We only got this far if there was an egress log, and the IP had not changed. Therefore, the link must have dropped
if [[ -n $admin_email ]]; then
    echo "$(uptime) $(ifconfig egress)" | mail -s "$(hostname -s) is online" $admin_email
fi