#!/bin/bash # author : Petr Simandl www.simandl.cz # release date : 03/06/2007 # name : sedlo # description : dynamic side routing tables tool # license : GPL sl_version="0.0.4pre11" PATH=$PATH:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin sl_nmcnf="sedlo.conf" sl_sedlocnf="/etc/$sl_nmcnf" sl_sedlocache="/var/cache/sedlo" mkdir -p /var/cache/sedlo sl_rttab="/etc/iproute2/rt_tables" sl_rtnmin=110 sl_rtnmax=200 #all traffic that is handled as internal (CZF traffic) sl_ipnodef="10.0.0.0/8" #this will specify base priority in rule table sl_priobase=10000 #this will specify base priority in rule table select mask sl_priorulesmask="100.." #no default traffic will have rule at higher priority sl_prionodef=$(($sl_priobase - 1)) slm_unknown="Nezname parametry : " sl_ipcmd=`which ip` sl_trcmd=`which tr` sl_wgetcmd=`which wget` sl_hnmcmd=`which hostname` sl_awkcmd=`which awk` sl_catcmd=`which cat` sl_grepcmd=`which grep` sl_diffcmd=`which diff` if [ -e $sl_sedlocnf ] then sl_nop=1 else echo "$sl_sedlocnf not found" exit 1 fi if [ -e $sl_rttab ] then sl_nop=1 else echo "$sl_rttab not found" exit 1 fi sl_murlcfg=`cat $sl_sedlocnf | grep "^mcnf" | uniq | awk '{print $2" "$3" "$4}'` ###################################################################### #this will delete all rules at sl_prionodef and sl_priorulesmask priorities s_flruall() { if [ $scm_info -gt 0 ]; then echo "Flushing all rules" ; fi sl_rules=`$sl_ipcmd ru ls | $sl_grepcmd "^"$sl_prionodef":" | $sl_trcmd '[:blank:]' '*'` for sl_rule in $sl_rules do sl_ipgws=`echo $sl_rule | $sl_awkcmd -F '*' '{print $2,$3,$4,$5,$6,$7}'` $sl_ipcmd ru del $sl_ipgws #this should make faster applying of new routing tables $sl_ipcmd ro flush cache done sl_rules=`$sl_ipcmd ru ls | $sl_grepcmd "^"$sl_priorulesmask":" | $sl_trcmd '[:blank:]' '*'` for sl_rule in $sl_rules do sl_ipgws=`echo $sl_rule | $sl_awkcmd -F '*' '{print $2,$3,$4,$5,$6,$7}'` $sl_ipcmd ru del $sl_ipgws #this should make faster applying of new routing tables $sl_ipcmd ro flush cache done } # s_flruall ###################################################################### s_checknodefru() { #checking if we have present nodef rule and if not we create it sl_nodefrule=`$sl_ipcmd ru ls | $sl_grepcmd "^"$sl_prionodef":" | $sl_trcmd '[:blank:]' '*'` #echo $sl_nodefrule if [ "$sl_nodefrule x" == " x" ] then if [ $scm_info -gt 0 ]; then echo "Creating rule for nodef route" ; fi $sl_ipcmd ru add from $sl_ipnodef to $sl_ipnodef lookup main prio $sl_prionodef fi } ###################################################################### # here we get each ip and we create a rule to send this ip to a # certain table # this routine can be skipped when the number of ips and ip directions # are still the same = old and new configs are the same s_fillrules() { #this will check no default rule if exists and if not it will be created s_checknodefru if [ $scm_info -gt 0 ]; then echo "Checking rules for ips" ; fi sl_ips=`$sl_catcmd $sl_sedlocache/$sl_nmcnf | $sl_grepcmd "^ip" | $sl_awkcmd '{print $2"*"$4"*"$5"*"$6}'` sl_rules=`$sl_ipcmd ru ls | $sl_grepcmd ^$sl_priorulesmask":" | $sl_trcmd '[:blank:]' '*'` #for all IPs we check and leave, change or create rule for sl_ip in $sl_ips do sl_ipn=`echo $sl_ip | $sl_awkcmd -F '*' '{print $1}'` sl_ipgws=`echo $sl_ip | $sl_awkcmd -F '*' '{print $2,$3,$4}'` sl_ok="no" #for all GWs we check rules for sl_ipgw in $sl_ipgws do sl_tbl=`$sl_ipcmd ro ls ta $sl_ipgw` #check if table exists if [ "$sl_tbl x" != " x" ] && [ "$sl_ok" = "no" ] then #we have IP and GW table #check if the rule alredady exists sl_oldrule=`echo $sl_rules | $sl_trcmd " " "\n" | $sl_grepcmd "\*"$sl_ipn"\*"` sl_exactrule=`echo $sl_oldrule | $sl_trcmd " " "\n" | $sl_grepcmd "\*"$sl_ipgw"\*"` if [ "$sl_exactrule x" == " x" ] then #exact rule doesn't exist so we check if an old rule for this IP is present if [ "$sl_oldrule x" != " x" ] then #some old rule(s) for IP is present so we delete it for sl_rule in $sl_oldrule do sl_ipgws=`echo $sl_rule | $sl_awkcmd -F '*' '{print $2,$3,$4,$5,$6,$7}'` if [ $scm_info -gt 0 ]; then echo "Deleting old rule $sl_ipgws" ; fi $sl_ipcmd ru del $sl_ipgws #this should make faster applying of new routing tables $sl_ipcmd ro flush cache done fi #getting subnet mask if exists sl_subnet=`echo $sl_ip | $sl_awkcmd -F '/' '{print $2}' | $sl_awkcmd -F '*' '{print $1}' ` if [ "$sl_subnet x" == " x" ] then #if subnet was not found we set subnet to 32 sl_subnet=32 else #this is just to be sure to have subnet between 1 and 32 if [ $sl_subnet -gt 32 ]; then sl_subnet=32 ; fi if [ $sl_subnet -lt 1 ]; then sl_subnet=1 ; fi fi #bigger subnets have lower priority sl_priorule=$(($sl_priobase + 32)) sl_priorule=$(($sl_priorule - $sl_subnet)) if [ $scm_info -gt 0 ]; then echo "Creating new rule to send $sl_ipn to table $sl_ipgw" ; fi $sl_ipcmd ru add from $sl_ipn lookup $sl_ipgw prio $sl_priorule else if [ $scm_info -gt 1 ]; then echo "Rule to send $sl_ipn to table $sl_ipgw already exists" ; fi fi sl_ok="yes" else #we have no table if [ "$sl_ok" = "no" ] then if [ $scm_info -gt 1 ]; then echo "For $sl_ipn table $sl_ipgw not used because it is empty" ; fi else if [ $scm_info -gt 1 ]; then echo "For $sl_ipn table $sl_ipgw not used because it has lower priority" ; fi fi fi done done #finally we check all rules and if there is a rule without IP from config we delete it for sl_rule in $sl_rules do sl_iprule=`echo $sl_rule | $sl_awkcmd -F '*' '{print $3}'` sl_ipconf=`echo $sl_ips | $sl_trcmd " " "\n" | $sl_grepcmd "^"$sl_iprule"\*"` if [ "$sl_ipconf x" == " x" ] then #we have a rule without an IP in config so we delete this rule sl_ipgws=`echo $sl_rule | $sl_awkcmd -F '*' '{print $2,$3,$4,$5,$6,$7}'` if [ $scm_info -gt 0 ]; then echo "Deleting non config rule $sl_ipgws" ; fi $sl_ipcmd ru del $sl_ipgws #this should make faster applying of new routing tables $sl_ipcmd ro flush cache fi done } # s_fillrules ###################################################################### # here we look into the main routing table for path to our iGWs # and we fill these tables with two halves default nets that # point to appropriate direction # this routine can be skipped when the routing table is the same s_filltables() { if [ $scm_info -gt 0 ]; then echo "Checking main routing table" ; fi if [ $scm_info -gt 0 ]; then echo "Filling tables" ; fi sl_igws=`$sl_catcmd $sl_sedlocache/$sl_nmcnf | $sl_grepcmd -E "^igw|^myigw" | $sl_awkcmd '{print $3"*"$2"*"$1}'` for sl_igw in $sl_igws do sl_igwn=`echo $sl_igw | $sl_awkcmd -F '*' '{print $1}'` sl_igwip=`echo $sl_igw | $sl_awkcmd -F '*' '{print $2}'` sl_igwtype=`echo $sl_igw | $sl_awkcmd -F '*' '{print $3}'` #oprava falesneho routovani na lokalni iface - pokud jsme lokalni igw tak se nema najit ip #protoze cesta dal neni - jsme totiz uz na lokalnim iface #head je tam proto ze se pro prespolni(a bgp) muze objevit vice rout s ruznou metrikou tak vezmem jen prvni (head) #s nejmensi metrikou (sort) sl_igwgt=`$sl_ipcmd ro ls | $sl_grepcmd -v "proto kernel" | $sl_grepcmd "^$sl_igwip " | sort | $sl_awkcmd '{print $3}' | head -n 1` # equal cost multipath detection - just first IP is taken as way to igw if [ "$sl_igwgt x" = "zebra x" ] then sl_igwgt=`$sl_ipcmd ro ls | $sl_grepcmd -A 1 "^$sl_igwip " | $sl_grepcmd "nexthop" | $sl_awkcmd '{print $3}'` fi #if myigw then fill table for local gateway with single ip from config if [ "$sl_igwtype x" = "myigw x" ] then sl_igwgt=$sl_igwip fi #testing if the igw has not a route in global routing table if [ "$sl_igwgt x" = " x" ] then if [ $scm_info -gt 1 ]; then echo "Route not found for igw $sl_igwn" ; fi sl_myigw=`cat $sl_sedlocnf | $sl_grepcmd "^myigw" | $sl_grepcmd $sl_igwn | $sl_awkcmd '{print $3}'` #testing if the igw without route is in local config #if not we go to flush its table and set flag to redo rules if [ "$sl_myigw x" = " x" ] then #getting num of routes of igw sl_igwnr=`$sl_ipcmd ro ls ta all | $sl_grepcmd -c "table ${sl_igwn} "` if [ "$sl_igwnr x" = "0 x" ] then if [ $scm_info -gt 1 ]; then echo "Table $sl_igwn is already empty - no action taken" ; fi else if [ $scm_info -gt 1 ]; then echo "Table $sl_igwn will be flushed and rules rearranged" ; fi $sl_ipcmd ro fl ta $sl_igwn #because this igw dissapeared we set a flag for rules recreation sl_diffigw=1 fi else if [ $scm_info -gt 1 ]; then echo "Igw $sl_igwn found in local config - leaving table as is" ; fi fi else sl_tbl=`$sl_ipcmd ro ls ta $sl_igwn` #if the table is empty we fill it and we set flag for rules recreation if [ "$sl_tbl x" = " x" ] then sl_diffigw=1 $sl_ipcmd ro add 0.0.0.0/1 via $sl_igwgt ta $sl_igwn $sl_ipcmd ro add 128.0.0.0/1 via $sl_igwgt ta $sl_igwn if [ "$sl_igwtype x" = "myigw x" ] then if [ $scm_info -gt 1 ]; then echo "Table $sl_igwn filled with default myigw $sl_igwgt" ; fi else if [ $scm_info -gt 1 ]; then echo "Table $sl_igwn filled with default gw $sl_igwgt" ; fi fi #the table is not empty so we check if routes are the same else #picking default gateway from the table sl_igwogt=`$sl_ipcmd ro ls ta $sl_igwn | $sl_awkcmd '{print $3}' | uniq` #checking if the old default is same as the new one if [ "$sl_igwogt x" = "$sl_igwgt x" ] then if [ $scm_info -gt 1 ]; then echo "Table $sl_igwn will not be changed and default is $sl_igwgt" ; fi else #the new default is different so we will flush the table, fill new default if [ $scm_info -gt 1 ]; then echo "Table $sl_igwn will be rewritten to default $sl_igwgt" ; fi #flushing old default route in the table $sl_ipcmd ro fl ta $sl_igwn #filling new default $sl_ipcmd ro add 0.0.0.0/1 via $sl_igwgt ta $sl_igwn $sl_ipcmd ro add 128.0.0.0/1 via $sl_igwgt ta $sl_igwn fi fi fi done } # s_filltables ###################################################################### # filling rttab with tables from config # only new tables are created with a new uniq number that is not important because # usually we handle tables just by their names # this routine acts only when a new iGW appears - only adding a table is supported # no deleting is implemented because it seems to be not necessary to delete an old table # because there is space enough and after reboot table will not be created s_mktables() { if [ $scm_info -gt 0 ]; then echo "Checking tables" ; fi sl_igws=`$sl_catcmd $sl_sedlocache/$sl_nmcnf | $sl_grepcmd -E "^igw|^myigw" | $sl_awkcmd '{print $3}'` for sl_igw in $sl_igws do sl_igwrttb=`$sl_catcmd $sl_rttab | $sl_awkcmd '{print $2}' | $sl_grepcmd $sl_igw ` if [ "$sl_igwrttb x" = " x" ] then if [ $scm_info -gt 1 ]; then echo "Creating table for $sl_igw" ; fi sl_cnt="$sl_rtnmax" sl_ok="no" until [ "$sl_cnt" -eq "$sl_rtnmin" ] || [ "$sl_ok" = "yes" ] do #space is used to recognized two and three digit numbers sl_igwrttb=`cat $sl_rttab | awk '{print $1" "}' | grep "$sl_cnt " ` if [ "$sl_igwrttb x" = " x" ] then sl_ok="yes" echo "$sl_cnt $sl_igw" >> $sl_rttab fi sl_cnt=$(($sl_cnt - 1 )) done # a new table was created so we should set a flag for rules creation sl_difftbl=1 else if [ $scm_info -gt 1 ]; then echo "Table found for $sl_igw no action taken" ; fi fi done } # s_mktables ###################################################################### s_getcfg() { if [ $scm_info -gt 0 ]; then echo "Getting config" ; fi if [ $scm_info -gt 1 ]; then echo "Using main config $sl_murlcfg" ; fi if [ $scm_info -gt 1 ]; then echo "Using local config $sl_sedlocnf" ; fi rm -f "$sl_sedlocache/$sl_nmcnf.main.tmp" $sl_wgetcmd -q -t 3 $sl_murlcfg -O "$sl_sedlocache/$sl_nmcnf.main.tmp" if [ -s $sl_sedlocache/$sl_nmcnf.main.tmp ] then date > $sl_sedlocache/last_getcnf.txt cp $sl_sedlocache/$sl_nmcnf.main.tmp $sl_sedlocache/$sl_nmcnf.main if [ $scm_info -gt 1 ]; then echo "Main config downloaded and accepted" ; fi else if [ $scm_info -gt 1 ]; then echo "Main config not downloaded - cached config will be used" ; fi echo -n "Main config not downloaded " > $sl_sedlocache/last_getcnf.txt date >> $sl_sedlocache/last_getcnf.txt fi # before generating a new cached config we store the old one for # comparison with the new one rm -f "$sl_sedlocache/$sl_nmcnf.old" if [ -s $sl_sedlocache/$sl_nmcnf ] then cp $sl_sedlocache/$sl_nmcnf $sl_sedlocache/$sl_nmcnf.old else touch $sl_sedlocache/$sl_nmcnf.old fi # preparing cached config from local and main # the local config should be processed as the first to have # higher priority for rules from local config echo "# generated file" > $sl_sedlocache/$sl_nmcnf for sl_file in `ls $sl_sedlocnf ; ls $sl_sedlocache/$sl_nmcnf.main ` do cat $sl_file | grep "^mcnf" | $sl_trcmd ';' '#' | awk '{print $1"\t"$2}' >> $sl_sedlocache/$sl_nmcnf cat $sl_file | grep "^igw" | $sl_trcmd ';' '#' | awk '{print $1"\t"$2"\t"$3}' >> $sl_sedlocache/$sl_nmcnf cat $sl_file | grep "^ip" | $sl_trcmd ';' '#' | awk '{print $1"\t"$2"\t"$3"\t"$4"\t"$5"\t"$6}' >> $sl_sedlocache/$sl_nmcnf done #local gateways taken from local config cat $sl_sedlocnf | grep "^myigw" | $sl_trcmd ';' '#' | awk '{print $1"\t"$2"\t"$3}' >> $sl_sedlocache/$sl_nmcnf #cat $sl_sedlocache/$sl_nmcnf | sort | uniq > $sl_sedlocache/$sl_nmcnf.uniq #mv $sl_sedlocache/$sl_nmcnf.uniq $sl_sedlocache/$sl_nmcnf sl_diffcfg=`diff $sl_sedlocache/$sl_nmcnf $sl_sedlocache/$sl_nmcnf.old | grep -c .` if [ $sl_diffcfg -gt 0 ] then if [ $scm_info -gt 0 ]; then echo "New config is different than the old one" ; fi else if [ $scm_info -gt 0 ]; then echo "New config is the same as the old one" ; fi fi # showing number of rules in config and system sl_numru=`ip ru ls | grep -c lookup` sl_numip=`grep -c ^ip $sl_sedlocache/$sl_nmcnf` sl_numru=$(($sl_numru - 3 )) if [ $sl_numip -gt $sl_numru ] then if [ $scm_info -gt 0 ]; then echo "We have less rules ($sl_numru) than new config has ips ($sl_numip)" ; fi # sl_diffcfg="1" else if [ $scm_info -gt 0 ]; then echo "We have $sl_numru rules and $sl_numip ips" ; fi fi } ###################################################################### s_version() { echo sedlo $sl_version } # s_version ###################################################################### s_report() { echo Content-type: text/html echo echo "Sedlo na routeru `hostname`" echo "
"
  echo "##### SEDLO #####"
  echo "date         : `date`"
  echo "version      : $sl_version"
  echo "local_config : $sl_sedlocnf"
  echo "main_config  : $sl_murlcfg"
  echo "last update  : `cat $sl_sedlocache/last_getcnf.txt`"
  echo "##### TABLES #####"
  cat $sl_rttab
  echo ; echo "##### DEFAULT ROUTES IN TABLES #####"
  $sl_ipcmd ro ls ta all | $sl_grepcmd table | $sl_grepcmd -v local | $sl_trcmd " " "\t"
  echo ; echo "##### RULES FOR IPS #####"
  $sl_ipcmd ru ls | $sl_trcmd " " "\t"
  echo "
" } # s_report ###################################################################### s_help() { echo Pouziti: sedlo [param] echo param: echo -V vypise verzi echo -help vypise napovedu echo -v malo upovidany echo -vv hodne upovidany echo -nogetcfg zajisti ze se nedude znovu nacitat konfigurace a pouzije se predchozi z cache echo -report vypise prehled pravidel a tabulek echo -flru odstrani vsechny pravidla echo -force bezpodminecne znovu obnovi vsechny pravidla } # s_help ###################################################################### ###################################################################### sl_unknown="" scm_nogetcfg=0 scm_flru=0 scm_info=0 scm_force=0 sl_diffigw=0 sl_difftbl=0 # parsing input parameters while [ "a$1" != "a" ] do case $1 in -V) s_version exit 0 ;; -h) s_help exit 0 ;; -report) s_report exit 0 ;; -flru) scm_flru=1 shift ;; -force) scm_force=1 shift ;; -help) s_help exit 0 ;; -nogetcfg) scm_nogetcfg=1 shift ;; -v) scm_info=1 shift ;; -vv) scm_info=2 shift ;; *) sl_unknown="$sl_unknown$1 " shift esac done # printing the list of bad parameters (if there are some) if [ "a$sl_unknown" != "a" ] then echo "$slm_unknown $sl_unknown" s_help exit 0 fi if [ $scm_flru -eq 1 ] then s_flruall exit 0 fi if [ $scm_nogetcfg -eq 0 ] then s_getcfg fi s_mktables s_filltables #toto je pro ladici ucely #echo $sl_difftbl #echo $sl_diffcfg #echo $sl_diffigw #echo $scm_force #exit 0 #flushing and filling rules is done only when #new table is created #config is changed #some igw dissapears or appears #-force command line parameter was used if [ $sl_difftbl -gt 0 ] || [ $sl_diffcfg -gt 0 ] || [ $sl_diffigw -gt 0 ] || [ $scm_force -gt 0 ] then s_fillrules fi exit 0