Hi Juan,
i wrote some shell scripts to support rudimentary failover for ippools:
Basically there are two identically configured radius-servers
radius1 and radius2
each time one of them added/removed an ip to/from pool the
script is executed and updates the "other" server
the first script "radpoolsync"
-------------------------------------------------------------
#! /bin/sh
# Copyright (c) 2004 dataport Germany.
#
# Author: Arne Spetzler
#
# /usr/sbin/radpoolsync
#
# to use as exec module from freeradius
# syncs used ippool addreses to backup server
#
set -u # force initializing of vars
set -o noclobber # for semaphore handling
LOGFILE=/var/log/radius/radpoolsync.log
RELAY_ACCT=/var/log/radius/radacct/detail-combined
#
# This script is customized for mutual relay between radius1 and radius2!!!!
#
MASTER=radius1
SLAVE=radius2
if [[ $HOSTNAME == $MASTER ]];then
DESTINATION=10.3.99.11
elif [[ $HOSTNAME == $SLAVE ]];then
DESTINATION=10.3.99.10
else
echo "no matching hostname found" >> $LOGFILE
exit -1
fi
DB_DIR=/etc/raddb
RADIUSD_BIN=/usr/sbin/radiusd
BACK_DIR=$DB_DIR/last_running_config
POOL_DIR=$DB_DIR/ippool
POOL_LIST=$DB_DIR/aktive_ippools.list
## semaphores
#
# when radcontrol is running do not start stop the server!!
#
RADCONTROL_SEM=0
SEF[$RADCONTROL_SEM]=/tmp/radcontrol.lock.pid # semaphore file
SES[$RADCONTROL_SEM]=0 # semaphore status
#
# protect transfer and db access
#
SEMAPHORE_DB=1
SEF[$SEMAPHORE_DB]=/tmp/radsync.lock.pid.db
SES[$SEMAPHORE_DB]=0
#
SEMAPHORE_TRANS=2
SEF[$SEMAPHORE_TRANS]=/tmp/radsync.lock.pid.trans
SES[$SEMAPHORE_TRANS]=0
#
SEMAPHORE_RACTION=3
SEF[$SEMAPHORE_RACTION]=/tmp/remote.action.lock.pid
SES[$SEMAPHORE_RACTION]=0
#
## base name for cache file
#
DBTRANS=/tmp/radsync.transdb
#
## name for remote action script
#
RACTION=/tmp/radsync.action
#
### semaphore handling
#
function lock
{
if echo "$$" > ${SEF[$1]};then
SES[$1]=1
return 0
else
SES[$1]=0
return -1
fi
}
function unlock
{
if [[ ${SES[$1]} == 1 ]];then
rm ${SEF[$1]}
SES[$1]=0
fi
}
function unlockall
{
# echo "EXIT trapped" >> $LOGFILE
unlock $SEMAPHORE_DB
unlock $SEMAPHORE_TRANS
}
function exit_on_sig
{
echo "***USR1 trapped" >> $LOGFILE
exit
}
function dbg_sig
{
echo "***OTHER trapped" >> $LOGFILE
}
## because the cache is deleted after a process
# commited to transfer it we check if someone else
# has catched it while we are sleeping
#
function checkdb
{
if ! [[ -s $DBTRANS ]]; then
#echo "no db" >> $LOGFILE
exit
fi
}
###################################################################################
#
# *** main ***
#
## this is executed on the 'remote' server!!
#
if [[ $1 == 'TRANS' ]]; then
while ! lock $SEMAPHORE_RACTION; do
echo " `date` ${SEF[$SEMAPHORE_RACTION]} locked" >> $LOGFILE
sleep 0.5
done
#
# do not stop/start the server if not allready running!!
#
if /usr/sbin/rcradiusd status; then
if [[ $HOSTNAME == $MASTER ]];then
echo "WARNING: `date` MASTER running while Backup active" >> $LOGFILE
fi
/usr/sbin/rcradiusd stop
. $2
/usr/sbin/rcradiusd start
else
. $2
fi
rm $RACTION
unlock $SEMAPHORE_RACTION
else
#
## this is only executed on the local server!!
#
# assure deletion of semaphore on EXIT
trap unlockall EXIT
# otherwise ignore USR1 (from radcontrol)
# the other signals (SIGTERM, SIGHUP are blocked, when started from radiusd)
# who knows why ???
trap '' SIGUSR1
trap dbg_sig SIGINT SIGTERM SIGHUP
if [[ $1 == 'ASTOP' ]]; then
if [[ $ACCT_STATUS_TYPE == 'Stop' ]]; then
# put unused ip into queue
POOLS=`grep -v '#' $POOL_LIST`
while ! lock $SEMAPHORE_DB; do
sleep 0.1
done
for POOL_NAME in $POOLS; do
echo "/usr/bin/rlm_ippool_tool -r $POOL_DIR/$POOL_NAME.pool
$POOL_DIR/$POOL_NAME.index $FRAMED_IP_ADDRESS" >> $DBTRANS
done
unlock $SEMAPHORE_DB
else
exit
fi
elif [[ $1 == 'GOTIP' ]]; then
sleep 0.6 # give foregoing accounting stop some time
# put new ip into queue
while ! lock $SEMAPHORE_DB; do
sleep 0.1
done
echo "/usr/bin/rlm_ippool_tool -n $POOL_DIR/$POOL_NAME.pool
$POOL_DIR/$POOL_NAME.index $2 $3 $4" >> $DBTRANS
unlock $SEMAPHORE_DB
else
echo "Error: unknown action $1"
exit -1
fi
# transfer the queue to remote machine
while ! lock $SEMAPHORE_TRANS; do
sleep 0.5 # smaller intervall results in much cpu load
checkdb # exit if DB empty
done
checkdb # exit if DB empty
while ! lock $SEMAPHORE_DB; do
sleep 0.2
checkdb # exit if DB empty
done
checkdb # exit if DB empty
mv $DBTRANS $DBTRANS.$$ >> $LOGFILE 2>&1
unlock $SEMAPHORE_DB
if scp $DBTRANS.$$ $DESTINATION:$RACTION >> $LOGFILE
then
#wc -l $DBTRANS.$$ >> $LOGFILE
rm $DBTRANS.$$;
if ! ssh $DESTINATION "/usr/sbin/radpoolsync 'TRANS' $RACTION" # >> $LOGFILE
then
echo "sshfail: $1 $2 $3 $POOL_NAME" >> $LOGFILE
fi
else
echo "scpfail: $1 $2 $3 $POOL_NAME" >> $LOGFILE
fi
trap exit_on_sig SIGUSR1
unlock $SEMAPHORE_TRANS
fi
--------------------------------------------------
is called from
--------------------------------------------------
exec ippoolsync_on_Access_accept {
wait = no
program = "/usr/sbin/radpoolsync GOTIP %{reply:Framed-Ip-Address}
%{NAS-IP-Address} %{NAS-Port}"
input_pairs = config
packet_type = Access-Accept
}
exec ippoolsync_on_Accounting_stop {
wait = no
program = "/usr/sbin/radpoolsync ASTOP"
input_pairs = request
packet_type = Accounting-Request
}
...
accounting {
...
# Tell backup about freed address _before_ really free it
ippoolsync_on_Accounting_stop
# Return an address to the IP Pool when we see a stop record.
$INCLUDE ${confdir}/aktive_ippools.list
...
post-auth {
# Get an address from the IP Pool.
$INCLUDE ${confdir}/aktive_ippools.list
ippoolsync_on_Access_accept
...
-------------------------------------------------------------
in radiusd.conf
There is another script which controls the two servers:
radcontrol:
--------------------------------------------------------------
#! /bin/sh
# Copyright (c) 2004 dataport Germany.
#
# Author: Arne Spetzler
#
# /usr/sbin/radcontrol
#
#
# This script is customized for mutual relay between radius1 and radius2!!!!
#
set -u # force initializing of vars
set -o noclobber # allow semaphore operation
MASTER=radius1
SLAVE=radius2
if [[ $HOSTNAME == $MASTER ]];then
LOCAL=10.3.99.10
REMOTE=10.3.99.11
SELF="MASTER"
OTHER="SLAVE"
elif [[ $HOSTNAME == $SLAVE ]];then
LOCAL=10.3.99.11
REMOTE=10.3.99.10
SELF="SLAVE"
OTHER="MASTER"
else
echo "no matching hostname found" 1>&2
exit -1
fi
## semaphores
#
# when radcontrol is running do not start stop the server!!
#
RADCONTROL_SEM=0
SEF[$RADCONTROL_SEM]=/tmp/radcontrol.lock.pid # semaphore file
SES[$RADCONTROL_SEM]=0 # semaphore status
#
# protect transfer and db access
#
SEMAPHORE_DB=1
SEF[$SEMAPHORE_DB]=/tmp/radsync.lock.pid.db
SES[$SEMAPHORE_DB]=0
#
SEMAPHORE_TRANS=2
SEF[$SEMAPHORE_TRANS]=/tmp/radsync.lock.pid.trans
SES[$SEMAPHORE_TRANS]=0
#
SEMAPHORE_RACTION=3
SEF[$SEMAPHORE_RACTION]=/tmp/remote.action.lock.pid
SES[$SEMAPHORE_RACTION]=0
# assure deletion of semaphore
trap unlockall EXIT SIGTERM
rc_done="\033[71G\033[32mdone\033[m"
rc_failed="\033[71G\033[31m\033[1mfailed\033[m"
DB_DIR=/etc/raddb
RADIUSD_BIN=/usr/sbin/radiusd
BACK_DIR=$DB_DIR/last_running_config
CHCK_DIR=$DB_DIR/last_checked_config
POOL_DIR=$DB_DIR/ippool
TEMP_DIR=/tmp/
DB_PREFIX=radsync.transdb
DB_TRANS=/tmp/radcontrol.trans
LOG=radcontrol.log
########################################################################
# HELPER START
########################################################################
function lock
{
if echo "$$" > ${SEF[$1]} ;then
SES[$1]=1
return 0
else
SES[$1]=0
return -1
fi
}
function unlock
{
if [[ ${SES[$1]} == 1 ]];then
rm ${SEF[$1]}
SES[$1]=0
fi
}
function check_sem
{
[[ -f ${SEF[$1]} ]]
}
function remove_sem
{
rm ${SEF[$1]}
}
function unlockall
{
unlock $SEMAPHORE_RACTION
unlock $RADCONTROL_SEM
}
function save
{
DIR=$1
if ! [ -d $DIR ]; then
mkdir $DIR
fi
if ! rm $DIR/*; then
echo "WARNING: Konnte alte Sicherung nicht l�schen"
fi
for i in `ls -A $DB_DIR`; do
if [ -f $DB_DIR/$i ]; then
if ! cp -p $DB_DIR/$i $DIR; then
echo "\nFehler beim Anlegen von $DIR/$i";
return 1;
fi
fi
done
rm $DIR/save.date
date > $DIR/save.date
return 0
}
function restore
{
DIR=$1
if [ -f $DIR/save.date ]; then
echo -n "old config from `cat $DIR/save.date`"
else
echo -n "WARNING: save.date nicht gefunden in $DIR auf $HOSTNAME"
fi
for i in `ls -A $DIR`; do
if [ -f $DIR/$i ]; then
if ! cp -p $DIR/$i $DB_DIR; then
echo "Fehler beim Anlegen von $DB_DIR/$i";
return -1;
fi
fi
done
return 0
}
function sync_files
{
DIR=$1
DEST=$2
if ! files=`ssh $DEST find $DIR -type f -maxdepth 1`;then
echo -n "Could not fetch file list from $DEST"
echo -e $rc_failed;
return -1;
else
echo -n "delete surplus files: "
anzahl=0
for i in $files; do
if ! [[ -e $i ]] ;then
if ! `ssh $DEST rm $i`;then
echo " - could not delete file: $i"
echo -e $rc_failed;
return -1;
fi
let anzahl=$anzahl+1
fi
done
echo -n $anzahl
echo -e $rc_done;
fi
echo -n "copy files: "
anzahl=0
files=''
for i in `ls $DIR`; do
if [ -f $DIR/$i ]; then
files="$files $DIR/$i" # collect files to transfer
let anzahl=$anzahl+1
fi
done
if ! scp $files $DEST:/$DIR > /dev/null; then
echo -e $rc_failed;
echo "-- error when copying $files!";
return -1;
fi
echo -n $anzahl
echo -e $rc_done;
}
function transfer_tools
{
files=''
files="$files /usr/sbin/radpoolsync"
files="$files /usr/sbin/radcontrol"
if ! scp $files $REMOTE:/usr/sbin > /dev/null; then
echo -e $rc_failed;
echo "-- error when copying $files!";
return -1
fi
files="/etc/logrotate.d/radiusd"
if ! scp $files $REMOTE:/etc/logrotate.d > /dev/null; then
echo -e $rc_failed;
echo "-- error when copying $files!";
return -1
fi
files="/usr/share/freeradius/dictionary.cisco.vpn3000"
if ! scp $files $REMOTE:/usr/share/freeradius > /dev/null; then
echo -e $rc_failed;
echo "-- error when copying $files!";
return -1
fi
return 0
}
function reload_server
{
if checkproc $RADIUSD_BIN; then
echo -n "sending reload signal (HUP) to $HOSTNAME"
if ! killproc -HUP $RADIUSD_BIN; then
echo -e $rc_failed
return -1
fi
else
echo -n "Server $HOSTNAME not running - starting"
if ! startproc $RADIUSD_BIN >/dev/null; then
echo -e $rc_failed;
echo "-- Server konnte nicht gestartet werden!!!";
return -1
fi
fi
if sleep 1;! checkproc $RADIUSD_BIN; then
echo -e $rc_failed
echo "-- bad configuration file! Server stopped"
else
echo -e $rc_done
return 0
fi
echo "reloading old config from $BACK_DIR"
if ! restore $BACK_DIR; then
echo -e $rc_failed
return -1
elif ! startproc $RADIUSD_BIN >/dev/null; then
echo -e $rc_failed
echo "-- could not start server!!!"
return -1
elif sleep 1;! checkproc $RADIUSD_BIN; then
echo -e $rc_failed;
echo "-- FATAL: last running config is bad! Server stopped."
return -1
else
echo -e " *** activated *** $rc_done";
return -1;
fi
}
########################################################################
# HELPER END
########################################################################
function rad_status
{
echo "*** Checking for freeradius daemons... ***"
echo "$SELF ($LOCAL):"
rcradiusd status
#rcradrelay status
echo "$OTHER ($REMOTE):"
ssh -tq $REMOTE "PATH=$PATH":/sbin";/usr/sbin/rcradiusd status"
#ssh -tq $REMOTE "PATH=$PATH":/sbin";/usr/sbin/rcradrelay status"
}
function rad_iptrans
{
if ! [[ $1 -ge 1 ]] ;then
echo "<age> not specified"
usage
exit -1
fi
# maybe there are some ippool entrys to transfer to slave
TO_TRANS_DB=`find $TEMP_DIR -amin -$1 -name "$DB_PREFIX*";`
echo "files found: $TO_TRANS_DB"
for db in $TO_TRANS_DB;do
ls -l $db;
cat $db >> $DB_TRANS.$$
rm $db
done
find $TEMP_DIR -name "$DB_PREFIX*" -exec rm {} \;
if [[ -f $DB_TRANS.$$ ]];then
echo -n "address files to proceed: `cat $DB_TRANS.$$ | wc -l`"
if scp $DB_TRANS.$$ $REMOTE:$DB_TRANS >> $LOG;then
if ! ssh $REMOTE "/usr/sbin/radpoolsync 'TRANS' $DB_TRANS" >> $LOG;then
echo "sshfail: could not add ip_addresses to pool at $REMOTE" >> $LOG
echo -e $rc_failed
exit -1
else
rm $DB_TRANS.$$
fi
else
echo "scpfail: could not transfer ip_addresses to $REMOTE" >> $LOG
echo -e $rc_failed
exit -1
fi
fi
}
function rad_stop
{
while ! lock $SEMAPHORE_RACTION; do
echo "${SEF[$SEMAPHORE_RACTION]} locked"
sleep 0.5
done
result=0
echo -n "*** attempt to STOP radius $SELF: $HOSTNAME ***"
if checkproc $RADIUSD_BIN ;then
if ! killproc $RADIUSD_BIN; then
echo -e $rc_failed
exit -1
fi
echo -e $rc_done
else
echo " server already down!"
fi
echo -n "*** cease all instances of radpoolsync ***"
timer=0
while check_sem $SEMAPHORE_TRANS ;do
killproc -USR1 /usr/sbin/radpoolsync
echo -n "."
sleep 0.5
let timer=$timer+1
if [[ $timer -gt 60 ]];then
echo -e "\n***** WARNING: removing SEMAPHORE ${SEF[$SEMAPHORE_TRANS]}"
echo " maybe ippools out of sync - contact support!"
remove_sem $SEMAPHORE_TRANS
result=-1
fi
done
if check_sem $SEMAPHORE_TRANS;then
echo "***** WARNING: ${SEF[$SEMAPHORE_DB]} found - deleting"
echo " maybe ippools out of sync - contact support!"
remove_sem $SEMAPHORE_DB
result=-1
fi
unlock $SEMAPHORE_RACTION
if [[ $result == 0 ]];then
echo -e $rc_done
else
echo -e $rc_failed
exit $result
fi
}
function rad_start
{
while ! lock $SEMAPHORE_RACTION; do
echo "${SEF[$SEMAPHORE_RACTION]} locked"
sleep 0.5
done
echo -n "*** attempt to START radius $SELF: $HOSTNAME ***"
if ! startproc $RADIUSD_BIN >/dev/null; then
echo -e $rc_failed;
echo "-- Server konnte nicht gestartet werden!!!";
exit -1
fi
if sleep 1;! checkproc $RADIUSD_BIN; then
echo -e $rc_failed;
echo "-- Konfiguration fehlerhaft! Server gestoppt."
exit -1
fi
echo -e $rc_done;
unlock $SEMAPHORE_RACTION
}
function rad_reload
{
while ! lock $SEMAPHORE_RACTION; do
echo "${SEF[$SEMAPHORE_RACTION]} locked"
sleep 0.5
done
echo "*** attempt to RELOAD radius system from $SELF: $HOSTNAME $LOCAL ***"
if save $CHCK_DIR; then
echo "changes saved to $CHCK_DIR"
if ! reload_server;then
echo "restore changes from $CHCK_DIR"
if ! restore $CHCK_DIR;then
echo -e $rc_failed
echo "WARNING: could not restore changes from $CHCK_DIR"
else
echo -e " *** restored *** $rc_done"
echo "Changes:"
diff -q $CHCK_DIR $BACK_DIR | grep -v save\.date
fi
exit -1
fi
else
echo "WARNING: could not save changes to $CHCK_DIR"
if ! reload_server;then
exit -1
fi
fi
echo -e "START update $OTHER $SLAVE $REMOTE START"
if ! sync_files $DB_DIR $REMOTE;then
exit -1
fi
if ! transfer_tools;then
exit -1
fi
if ! ssh $REMOTE "PATH=$PATH":/sbin";/usr/sbin/radcontrol local_reload"; then
echo -e $rc_failed;
echo "-- Fehler bei der Remoteausf�hrung!";
exit 1;
fi
echo -n "STOP update $OTHER $SLAVE $REMOTE STOP"
echo -e $rc_done
echo -n "Save new config to $HOSTNAME:$BACK_DIR"
if ! save $BACK_DIR; then
echo -e $rc_failed;
exit -1;
else
echo -e $rc_done;
fi
unlock $SEMAPHORE_RACTION;
}
function rad_local_reload
{
while ! lock $SEMAPHORE_RACTION; do
echo "${SEF[$SEMAPHORE_RACTION]} locked"
sleep 0.5
done
echo "*** attempt to RELOAD $SELF: $HOSTNAME ***"
reload_server # exits if fail!
echo -n "Save new config to $HOSTNAME:$BACK_DIR"
if ! save $BACK_DIR; then
echo -e $rc_failed
exit -1
else
echo -e $rc_done
fi
unlock $SEMAPHORE_RACTION;
}
function rad_clearpool
{
POOL=$POOL_DIR/$1
if ! [[ -e $POOL.pool ]] || ! [[ -e $POOL.index ]];then
echo -n "error: pool '$1' not found"
echo -e $rc_failed
exit -1
fi
# stop remoteserver
if ! ssh $REMOTE "PATH=$PATH":/sbin";/usr/sbin/radcontrol stop"; then
echo -e $rc_failed;
echo "-- Fehler bei der Remoteausf�hrung!";
exit -1;
fi
# stop localserver
rad_stop
# save pool-state
mv $POOL.pool $POOL.pool.back
mv $POOL.index $POOL.index.back
# test config
if ! reload_server;then
rad_stop
mv $POOL.pool.back $POOL.pool
mv $POOL.index.back $POOL.index
else
rad_stop
files="$POOL.pool $POOL.index"
if ! scp $files $REMOTE:$POOL_DIR > /dev/null; then
echo -e $rc_failed;
echo "-- error when copying $files!";
fi
sync_files $DB_DIR $REMOTE
fi
rad_start
if ! ssh $REMOTE "PATH=$PATH":/sbin";/usr/sbin/radcontrol start"; then
echo -e $rc_failed;
echo "-- Fehler bei der Remoteausf�hrung!";
exit -1;
fi
rad_status
}
function rad_syncpools
{
# stop remoteserver
if ! ssh $REMOTE "PATH=$PATH":/sbin";/usr/sbin/radcontrol stop"; then
echo -e $rc_failed;
echo "-- Fehler bei der Remoteausf�hrung!";
exit -1;
fi
rad_stop
echo "sync ippools from $POOL_DIR to $REMOTE"
sync_files $POOL_DIR $REMOTE
rad_start
if ! ssh $REMOTE "PATH=$PATH":/sbin";/usr/sbin/radcontrol start"; then
echo -e $rc_failed;
echo "-- Fehler bei der Remoteausf�hrung!";
exit -1;
fi
}
################################################################################
function rad_help
{
cat<<"EOF"
usage: radcontrol <option>
options are:
help - give short description of the program
stop - stop local server
start - start local server
reload [<pool>] - reload both servers with updated config files
use [<pool>] argument if ippool <pool> definition changed !!
if <pool> is given, it is interpreted as ippool name
name prefix. *** pool DB is removed on both servers ***
local_reload - reload only local server with updated config files
*** do not use if existing ippool definition changed !!
syncpools - stop/start servers and syncronise ippools
this is needed if stale ip-adresses at backup or
if master is to restart after crash
status - show status of all proccesses on both machines
iptrans <age> - transfer ipaddresses from stuck transferfiles
only transfer addressses from files not older than
<age> minutes - delete older files
EOF
}
function usage
{
cat<<"EOF"
usage: radcontrol <option>
options are:
help - give short description of the program
stop - stop local server
start - start local server
reload [<pool>] - reload both servers - optional remove ippool
local_reload - reload local server
syncpools - stop/start servers and syncronise ippools
status - show status of all proccesses on both machines
iptrans <age> - transfer stuck ipaddresses
EOF
}
##################################################################
if ! lock $RADCONTROL_SEM;then
echo -e "another instance running... $rc_failed"
exit;
fi
if [[ $# == 0 ]];then usage;exit;fi
if [[ $1 == status ]];then rad_$1
elif [[ $1 == stop ]];then rad_$1
elif [[ $1 == start ]];then rad_$1
elif [[ $1 == local_reload ]];then rad_$1
elif [[ $1 == reload ]] && [[ $# == 1 ]] ;then rad_$1
elif [[ $1 == reload ]] && [[ $# == 2 ]] ;then rad_clearpool $2
elif [[ $1 == syncpools ]];then rad_$1
elif [[ $1 == help ]];then rad_$1
elif [[ $1 == iptrans ]] && [[ $# == 2 ]] ;then rad_$1 $2
else
usage 1>&2
fi
----------------------------------------
The crux with this system is that - because of race conditions -
under rare circumstances some ip addresses could get lost.
But i think without modifikations to the freeradius server, there
is no chance to change that.
regards arne
PS.: Feel free to tell me about any bug in these scripts :)
I would also appriciate any enhancements :)
> Message: 1
> Date: Thu, 27 May 2004 18:28:14 +0200
> To: [EMAIL PROTECTED]
> From: "Juan" <[EMAIL PROTECTED]>
> Subject: Fail-Over
> Reply-To: [EMAIL PROTECTED]
>
> Hello,
>
> i have read configurable_failover for three times
> but i
> can not do that freeradius failover with ippool. I
> have
> two pools that i want to use then for all my
> users. I
> need that freradius start to asign IPs from the
> second
> Pool whe the first is full. I do not known what i
> must
> read to do it.
>
> Can somebody help me?
>
> Thank you.
>
-
List info/subscribe/unsubscribe? See http://www.freeradius.org/list/users.html