Статистика BIND

Материал из wiki.p3.ru
Версия от 10:58, 23 августа 2012; Eternity (обсуждение | вклад) (Новая страница: «== Своя статистика BIND == === Почему захотелось === Всегда интересовался DNS и, наконец, захотел…»)
(разн.) ← Предыдущая версия | Текущая версия (разн.) | Следующая версия → (разн.)

Своя статистика BIND

Почему захотелось

Всегда интересовался DNS и, наконец, захотелось выяснить кто, когда, как и что спрашивает у DNS серверов о домене. Для этой цели был сделан отдельный VDS здесь, собственно поэтому статья размещается здесь.

Требуется

bind9. Как установить - этого полно в интернете. Например, для debian требуется всего одна команда "apt-get install bind9". Запуск\остановка /etc/init.d/bind9 start\stop.

Настройка

Главное, это собирать информацию о принятых запросах. Файл named.conf.

logging {
    channel query_log {
        file "/var/log/bind/bind.log";
        severity dynamic;
        print-time yes;
    };
    channel main {
        file "/var/log/bind/main.log";
        severity dynamic;
        print-time yes;
        print-category yes;
        print-severity yes;
    };
    category queries { query_log; };
    category xfer-in { main; };
    category xfer-out { main; };
    category security { main; };
    category resolver { main; };
    category client { main; };
    category unmatched { main; };
    category default { main; };
    category database { main; };
};

Выше я написал все, что собирает мой DNS сервер. Достаточно будет:

logging {
    channel query_log {
        file "/var/log/bind/bind.log";
        severity dynamic;
        print-time yes;
    };
    category queries { query_log; };
};

Сбор статистики

Подготовка

Я использую очень простой механизм хранения логов bind. Для этого создан отдельный пользователь (adduser), для удобного доступа по ftp (демон proftpd). Внутри домашней директории (например /home/net) расположены еще две /home/net/bind - сюда складываются логи DNS сервера, /home/net/stat - сюда будет помещатся интересующая меня статистика. Сбор логов происходит с помощью php скрипта, запускаемого с помощью cron. Статистика собирается за месяц.

Конфигурация cron

# m h  dom mon dow   command
0 */1 * * * /usr/bin/php /home/net/bind.php > /dev/null 2>&1
1 */1 * * * /usr/bin/php /home/net/stat1.php > /dev/null 2>&1
2 */1 * * * /usr/bin/php /home/net/stat2.php > /dev/null 2>&1
3 */1 * * * /usr/bin/php /home/net/stat3.php > /dev/null 2>&1
4 */1 * * * /usr/bin/php /home/net/stat4.php > /dev/null 2>&1

Первый скрипт, запускаемый каждый час делает остановку DNS сервера, копирование файла с логом и запуск DNS сервера. Интервал можете изменить по своему усмотрению. Мне все-равно необходимо перезапускать DNS сервер каждый час. Если нужно только обновить зоны, то надо "rndc reload". Остальный скрипты создают интересующую меня статистику. Запускаются последовательно, чтобы не нагружать сервер. Многие скрипты можно написать более оптимально, но я предлагаю только основу.

bind.php

<?php
	$cfg_src1 = "/var/log/bind/bind.log" ;
	$cfg_drop1 = "/home/net/bind/" ;
	$f = date('YmdHis') ;
	$d = date('Y.m') ;
	if ( !file_exists($cfg_drop1.$d) )
		mkdir($cfg_drop1.$d) ;
	exec('/etc/init.d/bind9 stop') ;
	copy( $cfg_src1 , $cfg_drop1.$d.'/'.$f ) ;
	exec('rm '.$cfg_src1) ;
	exec('/etc/init.d/bind9 start') ;
?>

Статистика

1. Кто, что именно и сколько раз спрашивал

Файл с информацией вида IP адрес [tab] количество запросов. Пример:

xx.143.5.39             954
xxx.177.96.1            858
xx.111.180.223          378
xxx.92.168.54           373
xxx.122.226.27          247
xxx.23.19.2             204

Файл с информацией о том, какими записями сколько раз интересовались. Пример:

$$.ru         19137
$$$$.ru         2969
$$$$.ru         1523
$$$$.ru         1224
$$$$$$.ru               1119
$$$$$.ru                1055
$$$.ru          1047

Файл с более детальной информацией, указывающей какими именно типами записей интересовались. Пример:

A       $$.$$.tj                39
AAAA    $$$.$$$$.ru             36
AAAA    $$$.$$$.ru              34
A       $$$$.ru         32
A       $$$.$$$$.ru             31
MX      $$$.$$$$.ru             30
A       $$$.$$$$$$$.com         30
A       $$$$.ru         30
A6      $$$.$$$$.ru             29

PHP-скрипт для создания такой информации:

<?php
	$cfg_drop = "/home/net/bind/" ;
	$cfg_stat = "/home/net/stat/" ;

	//$cfg_drop = "D:/Web/bind/bind/" ;
	//$cfg_stat = "D:/Web/bind/stat/" ;
	
	$s_ip = array() ; // IP=>count
	$s_host = array() ; //host=>count
	$s_query = array() ; //host:RR=>count
	
	$yy = date('Y') ;
	$mm = date('m') ;
	$dd = date('d') ;
	$cdir = date('Y.m') ;
	
	if ( !file_exists($cfg_drop.$cdir) )
		exit ;
	$R = scandir($cfg_drop.$cdir) ;
	
	foreach ( $R as $k=>$v )
	{
		if ( strlen($v) < 7 ) continue ;
		echo 'Eval: '.$v."\r\n" ;
		$F = file($cfg_drop.$cdir.'/'.$v) ;
		foreach ( $F as $a=>$b )
		{
			$ps = explode(' ',$b) ;
			$tmp = explode('#',$ps[3]) ;
			$ip = $tmp[0] ;
			$host = strtolower($ps[5]) ;
			$query = strtoupper($ps[7])."\t".$host ;
			
			if ( isset($s_ip[$ip]) ) $s_ip[$ip]++ ; else $s_ip[$ip] = 1 ;
			if ( isset($s_host[$host]) ) $s_host[$host]++ ; else $s_host[$host] = 1 ;
			if ( isset($s_query[$query]) ) $s_query[$query]++ ; else $s_query[$query] = 1 ;
		}
	}
	
	arsort( $s_ip ) ;
	arsort( $s_host ) ;
	arsort( $s_query ) ;
	
	$F = @fopen($cfg_stat.$cdir.'.x01.log','w+') ;
	if ( $F !== false )
	{
		foreach ( $s_ip as $a=>$b )
			fwrite( $F , $a."\t\t".$b."\n") ;
		fclose( $F ) ;
	}
	$F = @fopen($cfg_stat.$cdir.'.x02.log','w+') ;
	if ( $F !== false )
	{
		foreach ( $s_host as $a=>$b )
			fwrite( $F , $a."\t\t".$b."\n") ;
		fclose( $F ) ;
	}
	$F = @fopen($cfg_stat.$cdir.'.x03.log','w+') ;
	if ( $F !== false )
	{
		foreach ( $s_query as $a=>$b )
			fwrite( $F , $a."\t\t".$b."\n") ;
		fclose( $F ) ;
	}
?>

--mef 16:16, 26 декабря 2008 (MSK)

2. Распределение запросов по времени

Статистика по часам, такого вида:

00      689     3.25%
01      673     3.17%
02      660     3.11%
...
21      878     4.14%
22      816     3.85%
23      836     3.94%

По минутам (почти все тоже самое) Таблица распределения типов запросов от времени и их общее количество.

H\RR|  A   |  MX  | AAAA | SRV  | ANY  | SOA  |  A6  | TXT  | AXFR | IXFR |CNAME |  NS  |TYPE99| PTR  |
----|------|------|------|------|------|------|------|------|------|------|------|------|------|------|
 00 |   276|   333|    40|    21|     5|    11|     2|     1|     0|     0|     0|     0|     0|     0|
 01 |   246|   338|    44|    20|     4|    10|     4|     1|     0|     0|     0|     5|     1|     0|
 02 |   278|   293|    46|    20|    10|    11|     2|     0|     0|     0|     0|     0|     0|     0|
 03 |   274|   243|    23|    20|    22|    13|     0|     2|     0|     0|     0|     0|     0|     0|
...
 23 |   310|   363|    54|    21|    68|    11|     0|     9|     0|     0|     0|     0|     0|     0|
----|------|------|------|------|------|------|------|------|------|------|------|------|------|------|
Sum |  7853| 10664|  1124|   528|   559|   297|    71|    38|    17|    25|    10|    18|     1|    10|
----|------|------|------|------|------|------|------|------|------|------|------|------|------|------|

Скрипт создания такой информации

<?php
	$cfg_drop = "/home/net/bind/" ;
	$cfg_stat = "/home/net/stat/" ;
	
	//$cfg_drop = "D:/Web/bind/bind/" ;
	//$cfg_stat = "D:/Web/bind/stat/" ;

	function i2 ( $n )
	{
		$n = (int)$n ;
		if ( $n > 9 ) return (string)$n ; else return '0'.$n ;
	}
	
	$s_hour = array() ; //hour->count
	for ( $i = 0 ; $i < 24 ; $i++ )
		$s_hour[i2($i)] = 0 ;
	$s_min = array() ; //HH:MM->count
	for ( $h = 0 ; $h < 24 ; $h++ )
		for ( $i = 0 ; $i < 60 ; $i++ )
			$s_min[i2($h).':'.i2($i)] = 0 ;
	
	$s_rrs = array() ; //Only RR
	$s_hrr = array() ; //(HOUR:RR)->count ;
	
	$yy = date('Y') ;
	$mm = date('m') ;
	$dd = date('d') ;
	$cdir = date('Y.m') ;
	
	if ( !file_exists($cfg_drop.$cdir) )
		exit ;
	$R = scandir($cfg_drop.$cdir) ;
	
	foreach ( $R as $k=>$v )
	{
		if ( strlen($v) < 7 ) continue ;
		echo 'Eval: '.$v."\r\n" ;
		$F = file($cfg_drop.$cdir.'/'.$v) ;
		foreach ( $F as $a=>$b )
		{
			$ps = explode(' ',$b) ;
			$A1 = substr($ps[1],0,2) ;
			$A2 = substr($ps[1],0,5) ;
			$s_hour[$A1]++ ;
			$s_min[$A2]++ ;
			
			$RR = strtoupper( $ps[7] ) ;
			if ( !isset($s_rrs[$RR]) ) $s_rrs[$RR]=1; else $s_rrs[$RR]++ ;
			if ( !isset($s_hrr[$A1.$RR])) $s_hrr[$A1.$RR]=1; else $s_hrr[$A1.$RR]++ ;
		}
	}
	
	$ALL = 0 ;
	for ( $i = 0 ; $i < 24 ; $i++ )
		$ALL += $s_hour[i2($i)] ;
	if ( $ALL == 0 )
		exit ;
	
	$F = @fopen($cfg_stat.$cdir.'.z01.log','w+') ;
	if ( $F !== false )
	{
		foreach ( $s_hour as $a=>$b )
			fwrite( $F , $a."\t".$b."\t".sprintf("%2.2f",100*$b/$ALL)."%\n") ;
		fclose( $F ) ;
	}
	arsort( $s_min ) ;
	$F = @fopen($cfg_stat.$cdir.'.z02.log','w+') ;
	if ( $F !== false )
	{
		foreach ( $s_min as $a=>$b )
			fwrite( $F , $a."\t".$b."\t".sprintf("%2.4f",100*$b/$ALL)."%\n") ;
		fclose( $F ) ;
	}
	$F = @fopen($cfg_stat.$cdir.'.z03.log','w+') ;
	if ( $F !== false )
	{
		fwrite( $F , "H\RR|") ;
		foreach ( $s_rrs as $a=>$b )
			fwrite( $F , str_pad($a,6," ",STR_PAD_BOTH)."|" ) ;
		fwrite( $F , "\n" ) ;
		fwrite( $F , "----|") ;
		foreach ( $s_rrs as $a=>$b )
			fwrite( $F , "------|" ) ;
		fwrite( $F , "\n" ) ;
		for ( $i = 0 ; $i < 24 ; $i++ )
		{
			fwrite( $F , ' '.i2($i).' |' ) ;
			foreach ( $s_rrs as $a=>$b )
			{
				if ( isset($s_hrr[i2($i).$a])) $k = $s_hrr[i2($i).$a] ; else $k = 0 ;
				fwrite( $F , str_pad($k,6," ",STR_PAD_LEFT)."|" ) ;
			}
			fwrite( $F , "\n" ) ;
		}
		fwrite( $F , "----|") ;
		foreach ( $s_rrs as $a=>$b )
			fwrite( $F , "------|" ) ;
		fwrite( $F , "\n" ) ;
		fwrite( $F , "Sum |") ;
		foreach ( $s_rrs as $a=>$b )
			fwrite( $F , str_pad($b,6," ",STR_PAD_LEFT)."|" ) ;
		fwrite( $F , "\n" ) ;
		fwrite( $F , "----|") ;
		foreach ( $s_rrs as $a=>$b )
			fwrite( $F , "------|" ) ;
		fwrite( $F , "\n" ) ;
		fclose( $F ) ;
	}
?>

3. Детально, по домена второго уровня

Такую же табличку распределения запросов хочется сделать для каждого домена, обслуживаемого DNS сервером. Очевидно, что для многих она будет "скромнее", чем общая. Пример:

# $$$$.ru
----|------|------|------|------|------|
H\RR|     A|   SRV|   ANY|    MX|  AAAA|
----|------|------|------|------|------|
 00 |    44|    21|      |    13|      |
 01 |    25|    20|      |     7|     1|
 02 |    54|    20|      |    35|      |
...
 20 |    36|    23|      |    31|      |
 21 |    29|    20|     1|    54|      |
 22 |    35|    20|     2|    24|      |
 23 |    29|    21|     8|    18|      |
----|------|------|------|------|------|
    |     A|   SRV|   ANY|    MX|  AAAA|
Sum |   895|   528|    27|   783|     3|
----|------|------|------|------|------|

Скрипт:

<?php
	$cfg_drop = "/home/net/bind/" ;
	$cfg_stat = "/home/net/stat/" ;
	
	//$cfg_drop = "D:/Web/bind/bind/" ;
	//$cfg_stat = "D:/Web/bind/stat/" ;

	function i2 ( $n )
	{
		$n = (int)$n ;
		if ( $n > 9 ) return (string)$n ; else return '0'.$n ;
	}
	
	$s_2L  = array() ; // Only 2L domains
	$s_hrr = array() ; // [2L]->HOUR:RR->count ;
	$s_rrs = array() ; // [2L]Only RR
	
	$yy = date('Y') ;
	$mm = date('m') ;
	$dd = date('d') ;
	$cdir = date('Y.m') ;
	
	if ( !file_exists($cfg_drop.$cdir) )
		exit ;
	$R = scandir($cfg_drop.$cdir) ;
	
	foreach ( $R as $k=>$v )
	{
		if ( strlen($v) < 7 ) continue ;
		echo 'Eval: '.$v."\r\n" ;
		$F = file($cfg_drop.$cdir.'/'.$v) ;
		foreach ( $F as $a=>$b )
		{
			$ps = explode(' ',$b) ;
			$A1 = substr($ps[1],0,2) ;
			$DOM = explode( '.' , $ps[5] ) ;
			$DOMk = count ( $DOM ) ;
			if ( $DOMk < 2 )
				continue ;
			$DOM = strtolower($DOM[$DOMk-2].'.'.$DOM[$DOMk-1]) ;
			if ( !isset($s_2L[$DOM] ) )
			{
				$s_2L[$DOM] = 1 ;
				$s_hrr[$DOM] = array() ;
				$s_rrs[$DOM] = array() ;
			}
			else 
				$s_2L[$DOM]++ ;
			$RR = strtoupper( $ps[7] ) ;
			if ( !isset($s_rrs[$DOM][$RR]) ) $s_rrs[$DOM][$RR]=1; else $s_rrs[$DOM][$RR]++ ;
			if ( !isset($s_hrr[$DOM][$A1.$RR])) $s_hrr[$DOM][$A1.$RR]=1; else $s_hrr[$DOM][$A1.$RR]++ ;
		}
	}
	
	if ( !file_exists($cfg_stat.$cdir ) )
		@mkdir( $cfg_stat.$cdir ) ;
	if ( !file_exists($cfg_stat.$cdir ) )
		exit ;
	
	foreach ( $s_2L as $aaa=>$bbb )
	{
		$F = @fopen($cfg_stat.$cdir.'/'.$aaa.'.log','w+') ;
		if ( $F !== false )
		{
			fwrite( $F , "# $aaa\n" ) ;
			fwrite( $F , "----|") ;
			foreach ( $s_rrs[$aaa] as $a=>$b )
				fwrite( $F , "------|" ) ;
			fwrite( $F , "\n" ) ;			
			fwrite( $F , "H\RR|") ;
			foreach ( $s_rrs[$aaa] as $a=>$b )
				fwrite( $F , str_pad($a,6," ",STR_PAD_LEFT)."|" ) ;
			fwrite( $F , "\n" ) ;
			fwrite( $F , "----|") ;
			foreach ( $s_rrs[$aaa] as $a=>$b )
				fwrite( $F , "------|" ) ;
			fwrite( $F , "\n" ) ;
			for ( $i = 0 ; $i < 24 ; $i++ )
			{
				fwrite( $F , ' '.i2($i).' |' ) ;
				foreach ( $s_rrs[$aaa] as $a=>$b )
				{
					if ( isset($s_hrr[$aaa][i2($i).$a])) $k = $s_hrr[$aaa][i2($i).$a] ; else $k =  ;
					fwrite( $F , str_pad($k,6," ",STR_PAD_LEFT)."|" ) ;
				}
				fwrite( $F , "\n" ) ;
			}
			fwrite( $F , "----|") ;
			foreach ( $s_rrs[$aaa] as $a=>$b )
				fwrite( $F , "------|" ) ;
			fwrite( $F , "\n" ) ;
			fwrite( $F , "    |") ;
			foreach ( $s_rrs[$aaa] as $a=>$b )
				fwrite( $F , str_pad($a,6," ",STR_PAD_LEFT)."|" ) ;
			fwrite( $F , "\n" ) ;
			fwrite( $F , "Sum |") ;
			foreach ( $s_rrs[$aaa] as $a=>$b )
			fwrite( $F , str_pad($b,6," ",STR_PAD_LEFT)."|" ) ;
			fwrite( $F , "\n" ) ;
			fwrite( $F , "----|") ;
			foreach ( $s_rrs[$aaa] as $a=>$b )
				fwrite( $F , "------|" ) ;
			fwrite( $F , "\n" ) ;
			fclose( $F ) ;
		}
	}
?>