Главная arrow Администрирование arrow FreeBSD, Linux, ... arrow FreeBSD ipnat: автоматическая поддержка правил NAT в актуальном состоянии Monday, September 25 2023  
ГлавнаяКонтактыАдминистрированиеПрограммированиеСсылки
UK-flag-ico.png English Version
GERMAN-flag-ico.png Die deutsche Version
map.gif карта сайта
нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

Поделиться:

FreeBSD ipnat: автоматическая поддержка правил NAT в актуальном состоянии Версия для печати
Написал microsin   
15.02.2008
Такая необходимость возникает, если провайдер не выдает постоянный IP, либо Вам не хочется переплачивать за эту услугу (как например, было у меня с Корбиной).

В случае NAT, настроенного через ipnat, в файле /etc/ipnat.rules могут быть правила, привязанные к текущему внешнему IP, например (адрес 89.178.134.209 автоматически назначен провайдером на виртуальном внешнем интерфейсе ng0 при соединении по pptp VPN с Корбиной):
bimap ng0 192.168.7.1 -> 89.178.134.209
Это очень неудобно, поскольку при переподключении IP в этой строке должен меняться. Мне удалось избавиться от этой проблемы с помощью скрипта, который запускается каждую минуту, проверяет IP адрес на интерфейсе ng0, и делает все необходимые действия (если IP нет, то пытается переподключиться, если IP поменялся, то корректирует файл /etc/ipnat.rules и перезапускает правила NAT, если все нормально, то никаких действий не производится). Кроме того, если Вы поменяли правила в /etc/ipnat.rules, то новые правила применятся автоматически. Скрипт запускается заданием, добавленным вручную в файл /etc/crontab (системный crontab. Я пробовал его назначить иначе, через crontab -e, но почему-то скрипт работал неправильно - не находилась команда ifconfig, применяемая в скрипте):
*       *       *       *       *       root    /путь_до_скрипта/corbina.sh

Вот текст скрипта corbina.sh:
#!/bin/sh

logname=/var/log/corbina.log
byteIP="([0-9]|[0-9][0-9]|[01][0-9][0-9]|2[0-4][0-9]|25[0-5])"
regexIP="$byteIP(\.$byteIP){3}(\b|/)"
natcfg='/etc/ipnat.rules'
outside=ng0
timeout=60   # timeout in seconds, while $outside interface bring up
tmpIPNATL='/scripts/ipnatl'

log ()
# процедура просто записывает строку в лог .var/log/corbina.log
{
  prefix=`eval date`
  echo $prefix': '$1 >> $logname
}

reloadNAT ()
# процедура вписывает в файл правил NAT новый IP
#  и применяет новые правила
{
  log "correct $natcfg with new $outside IP $WANIP"
  # для корректировки правил применяем скрипт на Perl
  #   replace.pl (см. ниже)
  regex="bimap $outside 192.168.7.1/32 -> $byteIP(\.$byteIP){3}/"
  replaceval="bimap $outside 192.168.7.1/32 -> $WANIP/"
  perl /scripts/replace.pl $natcfg "$regex" "$replaceval"
  ipnatmsg=`eval ipnat -CF -f $natcfg`
  log "$ipnatmsg"
}

getWANIP ()
# если WAN интерфейс имеет IP, то он записывается в переменную $WANIP
# иначе $WANIP будет пустая
{
  WANIP=`eval ifconfig $outside | grep -E -o --regexp="$byteIP(\.$byteIP){3} -->" | grep -E -o --regexp="$byteIP(\.$byteIP){3}"`
}

# тут начинается работа скрипта - получим IP на внешнем интерфейсе
getWANIP

# ветка 1: работает, если нет IP адреса на интерфейсе WAN
# В этом случае делается попытка переподключения (с помощью
#   посылки сигналов демону mpd). В случае успеха
#   корректируются правила NAT, а затем они вводятся в работу
if test -z "$WANIP"
then
  log "no IP address at $outside adapter, try reconnect..."
  log "killall -USR2 mpd"
  killall -USR2 mpd
  sleep 1
  log "killall -USR1 mpd"
  killall -USR1 mpd
  # цикл: в течение 60 секунд ожидаем появления IP на интерфейсе $outside.
  #  Это должно означать, что подключение произошло успешно.
  timecnt=0
  while test $timecnt -ne $timeout
  do
     getWANIP
     if test -n "$WANIP"
     then
        # IP назначен успешно (переменная $WANIP не пустая), прерываем цикл
        break
     fi
     # задержка на 1 сек, ждем появления IP
     sleep 1
     # отсчет таймаута
     timecnt=`expr $timecnt + 1`
  done
  if test -n "$WANIP"
  then
     # да, IP действительно назначен, перезагружаем ipnat новыми правилами
     log "in $timecnt sec WAN $outside RST ok."
     reloadNAT
     exit 0
  else
     # подключиться на этот раз не удалось
     log "timeout $timeout sec expired, WAN $outside RST error... Quit."
     exit 1
  fi
fi

# ветка 2: IP назначен, проверяем его на соответсвие правилам NAT
#  в файле /etc/ipnat.rules
ipnatrulesIP=`eval more $natcfg | grep -E --regexp="^bimap" | grep -E -o --regexp="-> $byteIP(\.$byteIP){3}(\b|/)" | grep -E -o --regexp="$byteIP(\.$byteIP){3}"`
if test -n "$ipnatrulesIP"
then
   # в файле правил NAT есть правило с IP, который возможно надо поменять
   if test "$ipnatrulesIP" != "$WANIP"
   then
      # кроме того, его действительно надо поменять
      log "$outside IP $WANIP != IP NAT, reload NAT rules"
      reloadNAT
      exit 0
   fi
fi

# ветка 3: проверяем на соответствие текущих правил
#   (которые работают) и правил из /etc/ipnat.rules
# Запишем во временный файл текущие правила NAT:
ipnat -l > $tmpIPNATL
# Отфильруем все ненужное (оставим в файле только правила NAT.
#    для этого применяем скрипт на Perl cut_block.pl - см. ниже):
perl /scripts/cut_block.pl $tmpIPNATL "List of active MAP/Redirect filters:" "List of active sessions:"
# впишем в переменную $ipnatrules_curr текущие правила NAT
ipnatrules_curr=`more $tmpIPNATL`
# впишем в переменную $ipnatrules_file системные правила NAT
#   (они в файле /etc/ipnat.rules)
ipnatrules_file=`more $natcfg | grep -E --regexp="^[^#]"`
# удаляем временный файл
rm $tmpIPNATL
if test "$ipnatrules_curr" != "$ipnatrules_file"
then
   # правила NAT поменялись, применяем новые правила
   log "ipnat -l != $natcfg, apply $natcfg"
   ipnatmsg=`eval ipnat -CF -f $natcfg`
   log "$ipnatmsg"
fi
exit 0

Немного замечаний по тексту. Переменная regexIP представляет регулярное выражение для поиска IP в тексте. Переменная outside задает имя внешнего интерфейса, на котором постоянно меняется IP (IP вычисляется командой ifconfig $outside). Процедура log() помогает вести отдельный лог для работы скрипта. Процедура getWANIP() получает IP на интерфейсе $outside и записывает его в переменную $WANIP. Процедура reloadNAT() меняет нужные правила в /etc/ipnat.rules и корректирует их новым IP, а потом перезапускает ipnat с новыми правилами. Остальные комментарии достаточно подробно вставлены по месту у текст.

Для корректировки файла /etc/ipnat.rules процедура reloadNAT() запускает скрипт replace.pl, написанный на Perl. Вот его текст:
#----------------------------------------------------------------------------------------------
#       Script replaces in file <pattval> with <replval>. Value <pattval>
#       is interpreted as regular expression.
#
#       Usage:
#       perl replace.pl file pattval repval
#----------------------------------------------------------------------------------------------

sub Usage
{
  die "Usage:\nperl replace.pl file regex_pattern replace_value\n";
}

# просто проверка наличия входных параментров, без затей:
if (@ARGV[0] eq "")
{
   Usage();
}
if (@ARGV[1] eq "")
{
   Usage();
}
if (@ARGV[2] eq "")
{
   Usage();
}

$file=@ARGV[0];               # имя файла, в котором ищем
$tempfile=$file.".tmp";       # имя временного файла, который потом заменит исходный
$regex=@ARGV[1];              # регулярное выражение для поиска
$replval=@ARGV[2];            # текст, который заменит значение, попавшее в регулярное выражение

#Открываем рабочие файлы в двоичном режиме.
# Открыть входной файл только на чтение, если не получится, то выход:
open SRCFILE, '<', $file      or die "Cannot open file ".$file.": $!\n";       
# Открыть выходной файл только на запись, если не получится, то выход:
open DSTFILE, '>', $tempfile  or die "Cannot open file ".$tempfile.": $!\n";    #open only write

# Цикл: читаем построчно входной файл, корректируем, если надо, строку,
#  и результат пишем в выходной файл.
while (!eof(SRCFILE))
{
  $line = readline SRCFILE;
  # /g задает менять все "срабатывания" $regex в строке.
  #  Если /g убрать, то произойдет только первая замена.
  $line =~ s/$regex/$replval/g;
  print DSTFILE $line;
}
# закрываем файлы
close SRCFILE;
close DSTFILE;
# старый файл удаляем и заменяем его на новый, скорректированный
unlink $file;
rename $tempfile, $file;

Еще один скрипт cut_block.pl, тоже на Perl, оставляет во временном файле только правила NAT, остальное все удаляется. Правила NAT выделяются по текстовым маркерам начала и конца блока текста. Скрипт, конечно, простейший, и вся система будет работать только в том случае, если у Вас в правилах NAT /etc/ipnat.rules записано все именно так, как должна вывести команда ipnat -l. Вот содержимое этого скрипта:
#----------------------------------------------------------------------------------------------
# Script get all strings block from file beetween beg_mark and end_mark
#      and placed these strings to file (another strings are removed).
#       Marks and empty strings excluded from block.
#
# Usage:
#       perl cut_block.pl file beg_mark end_mark
#----------------------------------------------------------------------------------------------

sub Usage
{
  die "Usage:\nperl cut_block.pl FILE BEGmark ENDmark\n";
}

if (@ARGV[0] eq "")
{
   Usage();
}
if (@ARGV[1] eq "")
{
   Usage();
}
if (@ARGV[2] eq "")
{
   Usage();
}

$file=@ARGV[0];
$tempfile=$file.".tmp";
$markBEG=@ARGV[1];
$markEND=@ARGV[2];
$BeginDetected=0;

open SRCFILE, '<', $file      or die "Cannot open file ".$file.": $!\n";        #open only read
open DSTFILE, '>', $tempfile  or die "Cannot open file ".$tempfile.": $!\n";    #open only write

sub trim
{
  my($string)=@_;
  for ($string)
  {
    s/^\s+//; #trim begin
    s/\s+$//; #trim eng
  }
  return $string;
}

while (!eof(SRCFILE))
{
  $line = readline SRCFILE;
  $line = &trim($line);
  if ($BeginDetected)
  {
    if (length($line))
    {
      $idx = index ($line, $markEND);
      if (-1!=$idx)
      {
        last;
      }
      print DSTFILE $line."\n";
    }
  }
  else
  {
    if (length($line))
    {
   
      $idx = index ($line, $markBEG);
      if (-1!=$idx)
      {
        $BeginDetected=1;
      }
    }
  }
}
close SRCFILE;
close DSTFILE;

unlink $file;
rename $tempfile, $file;

Последнее обновление ( 23.02.2008 )
 

Добавить комментарий

:D:lol::-);-)8):-|:-*:oops::sad::cry::o:-?:-x:eek::zzz:P:roll::sigh:

Защитный код
Обновить

< Пред.   След. >

Top of Page
 
microsin © 2023