Как себя чувствует RADIUS?

от автора

Сегодняшний день начался с неожиданного. Один из наших Заказчиков сообщил, что он считает, что RADIUS сервер не справляется с работой, поскольку NAS отсылает большое количество запросов повторно. Так как это достаточно серьезное обвинение, его следовало немедленно проверить. Описанию того как это сделать, посвящена эта статья (материал ориентирован на начинающих разработчиков и системных администраторов).

Первый шаг довольно очевиден. Необходимо получить дамп сетевой активности во время максимальной нагрузки на RADIUS-сервер. Данных 10-15 минутного интервала вполне достаточно для анализа. Для Unix-like систем получить эти данные можно при помощи tcpdump, но поскольку, в нашем случае, RADIUS-сервер работал на Windows-системе, пришлось прибегнуть к помощи Wireshark. Выполнив захват пакетов, Wireshark показал эту замечательную картинку:

image

Хотя этот интерфейс очень удобен для интерактивной работы, для получения статистики, требуется перевести данные в форму более удобную для автоматизированной обработки. К счастью, Wireshark умеет сохранять данные в текстовый формат:

image

Результирующий файл выглядит следующим образом:

+---------+---------------+----------+ 10:03:59,092,544   ETHER |0   |ff|ff|ff|ff|ff|ff|00|0c|29|6b|5a|a3|08|00|45|00|00|e5|b6|15|00|00|80|11|04|d4|c0|a8|7d|ce|c0|a8|7f|ff|00|8a|00|8a|00|d1|ee|a9|11|0e|d1|fb|c0|a8|7d|ce|00|8a|00|bb|00|00|20|46|44|46|41|44|44|43|41|43|41|43|41|43|41|43|41|43|41|43|41|43|41|43|41|43|41|43|41|43|41|43|41|00|20|45|4a|45|4f|46|45|45|46|46|43|43|41|43|41|43|41|43|41|43|41|43|41|43|41|43|41|43|41|43|41|42|4e|00|ff|53|4d|42|25|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|11|00|00|21|00|00|00|00|00|00|00|00|00|e8|03|00|00|00|00|00|00|00|00|21|00|56|00|03|00|01|00|00|00|02|00|32|00|5c|4d|41|49|4c|53|4c|4f|54|5c|42|52|4f|57|53|45|00|01|00|80|fc|0a|00|53|50|33|00|25|03|00|00|00|00|fc|5e|20|03|02|00|05|01|03|12|01|00|0f|01|55|aa|00|  +---------+---------------+----------+ 10:03:59,184,049   ETHER |0   |ff|ff|ff|ff|ff|ff|00|1d|7d|3a|f9|3a|08|06|00|01|08|00|06|04|00|01|00|1d|7d|3a|f9|3a|0a|15|52|44|00|00|00|00|00|00|0a|15|52|41|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00| ... 

С этим уже можно работать. Из спецификаций RFC 768, 2865, 2866 нам известны смещения интересующих нас полей (смещения представлены в десятеричной системе счисления, значения — в шестнадцатеричной):

+23 = 11 (UDP) +36 = 07 14 (RADIUS Destination Port = 1812)     +42 - (01 = Access Request)     +43 - ID +34 = 07 14 (RADIUS Source Port = 1812)     +42 - (02 = Access Accept; 03 = Access Reject)     +43 - ID +36 = 07 15 (RADIUS Destination Port = 1813)     +42 - (04 = Accounting Request)     +43 - ID +34 = 07 14 (RADIUS Source Port = 1812)     +42 - (05 = Accounting Response)     +43 - ID 

Обработать данные нам поможет Perl:

Скрипт

use Time::Local;  my $tm; my $ln;  my %auth; my %acco;  my %discarded; my %accepted;  # Обрабатываем все поступающие строки while (<>) {   # Убираем перевод строки   chomp;   s/\|0\s+\|//;   # Регулярным выражением извлекаем время   if (/^(\d\d):(\d\d):(\d\d),(\d\d\d),\d\d\d/) {       # Приводим значение времени к количеству миллисекунд с начала эпохи       # Поскольку дата нас не интересует, берем 01/01/1970       $tm = timelocal($3, $2, $1, 01, 01, 1970) * 1000 + $4;   }   if (/\|/) {       $ln .= $_;   } else {       if ($ln) {           # Разбиваем строку на байты           my @packet = split /\|/, $ln;           # Нас интересуют только UDP-пакеты           if ($packet[23] eq '11') {               my $code = $packet[42];               my $id   = $packet[43];               # RADIUS Request               if ($packet[36] eq '07' and                   ($packet[37] eq '14' or $packet[37] eq '15')) {                   # Аккаутинг                   if ($code eq '04') {                       # Повтор ID запроса означает потерю соответствующего ответа                       if (exists($acco{%id})) {                           $discarded{$code}++;                       }                       # Сохраняем время получения запроса                       $acco{%id} = $tm;                   # Авторизация                   } else {                       if (exists($auth{%id})) {                           $discarded{$code}++;                       }                       $auth{%id} = $tm;                   }               }               # RADIUS Response               if ($packet[34] eq '07' and                   ($packet[35] eq '14' or $packet[35] eq '15')) {                   # Аккаутинг                   if ($code eq '05') {                       if (exists($acco{%id})) {                           if ($tm - $acco{%id} > 1000) {                               # Если прошло более 1 секунды, считаем пакет отброшенным                               $discarded{$code}++;                           } else {                               # Иначе увеличиваем количество успешно обработанных пакетов                               $accepted{$code}++;                           }                           delete($acco{%id});                       }                   # Авторизация                   } else {                       if (exists($auth{%id})) {                           if ($tm - $auth{%id} > 1000) {                               $discarded{$code}++;                           } else {                               $accepted{$code}++;                           }                           delete($auth{%id});                       }                   }               }           }                                 $ln = '';       }   } }  # На всякий случай, учитываем ID, для которых не сформирован ответ while (($id, $tm) = each(%auth)) {   $discarded{'01'}++; }  while (($id, $tm) = each(%acco)) {   $discarded{'04'}++; }  #Выводим результаты обработки print "Auth  Accepted: $accepted{'02'}\n"; print "Auth  Rejected: $accepted{'03'}\n"; print "Acco Responsed: $accepted{'05'}\n\n";  print "Auth Request  Discarded: $discarded{'01'}\n"; print "Auth Accept   Discarded: $discarded{'02'}\n"; print "Auth Reject   Discarded: $discarded{'03'}\n"; print "Acco Request  Discarded: $discarded{'04'}\n"; print "Acco Response Discarded: $discarded{'05'}\n"; 

Поскольку мы все еще работаем в Windows, bang-комментарий в исходном коде не обязателен (кстати, Perl для Windows можно взять здесь). Для работы со временем, не забываем подключить Time::Local. Скрипт запускаем на выполнение следующей командой:

perl stat.pl dump.txt > dump 

Скрипт работает довольно долго (в основном из-за split-а большого объема данных), но один раз можно и потерпеть. В конечном итоге, формируется следующий результат:

Auth  Accepted: 195 Auth  Rejected: 907 Acco Responsed: 6058  Auth Request  Discarded: 13 Auth Accept   Discarded:  Auth Reject   Discarded:  Acco Request  Discarded: 1476 Acco Response Discarded:  

Здесь мы видим, что за 10 минут было успешно обработано 6058 пакетов аккаунтинга и 1102 пакетов авторизации (из них на 907 запросов был сформирован Reject). Все запросы были обработаны сервером не более чем за 1 секунду. По непонятной причине, NAS отослал 13 повторных запросов авторизации и 1476 запросов аккаунтинга. Возможно это связано с потерями UDP-пакетов при передаче их по сети.

Приведенный выше скрипт можно улучшить, для получения более детальной статистики по времени отклика, но в целом задачу можно считать выполненной. RADIUS-сервер работает нормально.

ссылка на оригинал статьи http://habrahabr.ru/post/191720/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *