ISP`s IT Аутсорсинг
Быстрый переход: Главная блога Главная сайта Форум
Если Вы чего то недопоняли или не нашли - задайте
вопрос на нашем форуме и мы попробуем Вам помочь.
Subnets.ru Регистрация IP и Автономных систем mega-net.ru

Архив за Апрель, 2009

Добро пожаловать в блог! Надеемся, что Вы еще вернетесь.

Сегодня столкнулся с проблемой на quagga и процессом bgpd.

Перезапуск bgpd нифига не помог, т.к. процесс поднимается и сразу падает…

В логах:

2009/04/30 10:58:09 BGP: Assertion `len < str_size' failed in file bgp_aspath.c, line 619, function aspath_make_str_count
2009/04/30 10:58:09 BGP: No backtrace available on this platform.
2009/04/30 10:59:02 BGP: BGPd 0.99.11 starting: vty@2605, bgp@<all>:179
2009/04/30 10:59:08 BGP: %ADJCHANGE: neighbor ХХХ.ХХХ.ХХХ.150 Up
2009/04/30 10:59:21 BGP: Assertion `len < str_size' failed in file bgp_aspath.c, line 619, function aspath_make_str_count
2009/04/30 10:59:21 BGP: No backtrace available on this platform.

Все до сегодняшнего дня работало на quagga-0.99.11_1

Полазав по инету решил пересобрать quagga, т.к. в портах появилась: quagga-0.99.11_2

Обновился до quagga-0.99.11_2 и проблема исчезла.

Ничего не понялТак себе...Не плохоДовольно интересноОтлично ! То что нужно ! (голосов: 1, среднее: 5,00 из 5)
Загрузка...
Отправить на почту Отправить на почту

ng_ipacct — Netgraph IP accounting

Для работы этого примера необходимо иметь подгруженные модули:

  • ng_ipacct.ko
  • ng_ipfw.ko
  • ng_ksocket.ko

Убедиться в их наличии можно командой:

/sbin/kldstat

Id Refs Address Size Name
1 11 0xc0400000 35e5f8 kernel
...
3 1 0xc4ceb000 3000 ng_ipacct.ko
4 1 0xc4cee000 4000 ng_ksocket.ko
5 1 0xc4d6d000 2000 ng_ipfw.ko

Если модули не загружены, то можно пересобрать ядро добавив следующее:

options NETGRAPH
options NETGRAPH_ETHER
options NETGRAPH_SOCKET
options NETGRAPH_TEE

options IPFIREWALL
options IPFIREWALL_DEFAULT_TO_ACCEPT
options IPFIREWALL_FORWARD
options IPFIREWALL_VERBOSE
options IPFIREWALL_VERBOSE_LIMIT=1000

либо загрузить это модулями:

/sbin/kldload /boot/kernel/ng_ipfw.ko

/sbin/kldload /boot/kernel/ng_ipacct.ko

/sbin/kldload /boot/kernel/ng_ksocket.ko

Устанавливаем порт /usr/ports/net-mgmt/ng_ipacct:

cd /usr/ports/net-mgmt/ng_ipacct

make install clean
В /etc/rc.conf добавляем:

ng_ipacct_enable="YES"
ng_ipacct_modules_load="YES"


Для примера будем собирать трафик для подсети 172.16.5.0/24 с интерфейса bge1

Итак сам скрипт на PERL, пусть он находится в папке /scripts и называется ipacctd_ng.pl

#! /usr/bin/perl

$rul=64000;                                                         #номер начального правила в файере
$iface="bge1";                                                     #интерфейс
$threshold=100000;                                              #максимальное количество записей
$verbose="1";                                                      #расширенная статистика
$savetime="1";                                                     #метка юникс тайма в файл статистики
$nodename=sprintf("ipacct_%s",$iface);                   #имя ноды
$hookprefix=$iface;                                               #крючочки (хуки) в нетграф ;-)

if($ARGV[0] ne "start" and $ARGV[0] ne "show" and $ARGV[0] ne "stop"  and $ARGV[0] ne "restart"){
            error();
}else{
          if($ARGV[0] eq "stop"){                          #stop
                   stop_ng_ipacctd($nodename);
          }elsif($ARGV[0] eq "show"){                    #дампим трафик в файл
                   #проверяем загружен ли ipacctd
                   if(check_ipacctd($iface,$rul)==0){
                              stop_ng_ipacctd($nodename);
                              `/bin/sleep 3`;
                              start_ng_ipacctd($nodename,$hookprefix,$rul,$iface,$verbose,$threshold,$savetime);
                   }else{
                              $cmd=sprintf("/usr/local/sbin/ipacctctl %s:%s checkpoint",$nodename,$hookprefix);
                              `$cmd`;
                              $cmd=sprintf("/usr/local/sbin/ipacctctl %s:%s show",$nodename,$hookprefix);
                              `$cmd`;
                              $cmd=sprintf("/usr/local/sbin/ipacctctl %s:%s clear",$nodename,$hookprefix);
                              `$cmd`;
                   }
          }elsif($ARGV[0] eq "start"){
                  start_ng_ipacctd($dbh,$nodename,$hookprefix,$rul,$iface,$verbose,$threshold,$savetime);
          }elsif($ARGV[0] eq "restart"){
                 stop_ng_ipacctd($nodename);
                 `/bin/sleep 3`;
                start_ng_ipacctd($dbh,$nodename,$hookprefix,$rul,$iface,$verbose,$threshold,$savetime);
          }
}

###функция выводящая аргументы запуска==============================
sub error{
      printf "\nUsage: (start|stop|restart|show)";
      exit;
}

###функция start==================================================
sub start_ng_ipacctd{
         $net="172.16.5.0/24";
         #проверяем загруженность модулей
         @modules=("ng_ipacct.ko","ng_ipfw.ko","ng_ksocket.ko");
         foreach(@modules){
             if(chk_mdl($_)==0){
                `/sbin/kldload $_`;
             }
         }
         #формирование ноды нетграфа
         $cmd_ng=sprintf("/usr/sbin/ngctl -f- <<-SEQ
         mkpeer ipacct ctl ctl
         name .:ctl %s
         mkpeer %s: ksocket %s_in inet/raw/divert
         name %s:%s_in %s_in
         msg %s_in: bind inet/0.0.0.0:3021
         mkpeer %s: ksocket %s_out inet/raw/divert
         name %s:%s_out %s_out
         msg %s_out: bind inet/0.0.0.0:3022
         rmhook .:ctl",$nodename,$nodename,$hookprefix,$nodename,$hookprefix,$nodename,$nodename,$nodename,$hookprefix,$nodename,$hookprefix,$nodename,$nodename);

        `$cmd_ng`;
         $cmd=sprintf("/usr/local/sbin/ipacctctl %s:%s dlt RAW",$nodename,$hookprefix);
         `$cmd`;
         $cmd=sprintf("/usr/local/sbin/ipacctctl %s:%s verbose %s",$nodename,$hookprefix,$verbose);
         `$cmd`;
         $cmd=sprintf("/usr/local/sbin/ipacctctl %s:%s threshold %s",$nodename,$hookprefix,$threshold);
         `$cmd`;
          $cmd=sprintf("/usr/local/sbin/ipacctctl %s:%s savetime %s",$nodename,$hookprefix,$savetime);
         `$cmd`;

          #формируем правила в фаирволе IPFW для перенаправления трафика в ноду нетграфа
          $cmd=sprintf("/sbin/ipfw add %s divert 3021 ip from %s to any",$rul,$net);
          `$cmd`;
          $cmd=sprintf("/sbin/ipfw add %s divert 3022 ip from any to %s",$rul,$net);
          `$cmd`;
           print "ipacct has started..\n";
}

###функция stop==================================================
sub stop_ng_ipacctd{
        `sh /etc/rc.firewall`;
         $cmd=sprintf("/usr/sbin/ngctl shutdown %s:",$nodename);
         `$cmd`;
         print "ipacct has stoped..\n";
}

###функция проверки загрузки модулей================================
sub chk_mdl{
       $chk=`/sbin/kldstat | /usr/bin/awk '/$_/ {print 1}'`;
       if(!$chk){
           return 0;
        }else{
          return 1;
        }
}

###функция проверки ipacctd========================================
sub check_ipacctd{
        $ch1=$ch2=$ch3=0;
        $cmd=sprintf("/usr/sbin/ngctl list | grep %s_in",$iface); #проверяем входящий хук нетграфа
        $check=`$cmd`;
        if($check){
              $ch1=1;
        }

        $cmd=sprintf("/usr/sbin/ngctl list | grep %s_out",$iface); #проверяем исходящий хук нетграфа
        $check=`$cmd`;
        if($check){
              $ch2=1;
        }

        $cmd=sprintf("/sbin/ipfw show | grep divert"); #проверяем наличие правил в файерволе
        $check=`$cmd`;
        if($check){
             $ch3=1;
        }

         $all_ch=$ch1+$ch2+$ch3;
         if ($all_ch < 3){
                return 0; #чего-то явно не хватает
         }else{
               return 1; #все ОК!
         }
}

После старта мы можем посмотреть правила в фаирволе:
/sbin/ipfw show 64000
64000 1551459105 504714065010 divert 3021 ip from 172.16.5.0/24 to any
64000 2103320289 2367292643626 divert 3022 ip from any to 172.16.5.0/24

По команде:

/usr/sbin/ngctl list

можем посмотреть ноды и хуки нетграфа:

There are 7 total nodes:
Name: ngctl12701 Type: socket ID: 000c36ff Num hooks: 0
Name: ipacct_bge1_out Type: ksocket ID: 00097d52 Num hooks: 1
Name: ipacct_bge1_in Type: ksocket ID: 00097d51 Num hooks: 1
Name: ipacct_bge1 Type: ipacct ID: 00097d50 Num hooks: 2
Name: ipfw Type: ipfw ID: 0005f4a3 Num hooks: 0
Name: bge1 Type: ether ID: 00000002 Num hooks: 0
Name: bge0 Type: ether ID: 00000001 Num hooks: 0

Сделаем еще один маленький скриптик, который будет нам складывать статистику в файл.

Назовем его trafgen.pl

#!/usr/bin/perl

$tmp="/usr/local/var/trafd/tmp";    #папка для логов
opendir DIR,$tmp or mkdir $tmp;
closedir DIR;
$log=sprintf("%s/ipacctd.log",$tmp);
system("/scripts/ipacctd_ng.pl show >> $log");  #вызываем наш предыдущий скрипт с аргументом show

в /etc/crontab добавляем строчку:
*/5 * * * * root /scripts/trafgen.pl >/dev/null

Формат данны файла статистики при включенных опциях verbose=1 и savetime=1:
src_IP src_Port dst_IP dst_Port Proto packets bytes unixtime

Пример данных в фале:

172.16.5.211 57349 95.132.7.128 19904 6 1069 822400 1239961920
172.16.5.102 2208 217.118.24.17 7777 6 321 14958 1239961920
172.16.5.91 64835 92.100.50.249 59039 6 426 596900 1239961920
172.16.5.209 64829 77.41.95.94 43768 17 1 58 1239961923
172.16.5.76 1425 195.218.181.123 80 6 4 976 1239961924
172.16.5.188 61651 195.50.197.187 63375 6 1 48 1239961925
172.16.5.220 28988 94.179.59.61 20473 6 5 747 1239961929
....
80.252.240.202 11417 172.16.5.108 60682 17 1 47 1239961922
78.37.156.14 1451 172.16.5.220 28988 6 6 685 1239961922
97.84.143.186 17834 172.16.5.209 64829 17 2 134 1239961922
77.234.8.71 36523 172.16.5.220 28988 17 1 95 1239961924
195.189.47.2 2987 172.16.5.220 28988 17 1 131 1239961925
195.230.112.239 63403 172.16.5.220 28988 6 6 801 1239961930
60.220.156.222 27808 172.16.5.157 34613 17 4 504 1239961932
93.186.239.96 80 172.16.5.101 2720 6 3 358 1239961933

Обратите внимание, что сначала идет трафик исходящий и лишь затем входящий!!!
Все, дальше зависит только от полета Вашей фантазии (или извращенности 🙂 )

Вы можете обрабатывать файлы и складировать их в БД (напрмер MySQL) и т.п.

Плюсы от использования ng_ipacct:

  • держит большие объемы трафика
  • меньшая загрузка cpu по сравнению с обычным ipacct (/usr/ports/net-mgmt/ipacctd)

P.S. После пересборки ядра обязательно пересобрать порт /usr/ports/net-mgmt/ng_ipacct (если он был установлен до этого)

Чтиво:

  • man 4 ipacct
  • man 8 ipfw
  • man 4 netgraph
  • man 4 ng_ipfw

З.Ы. При копировании статьи ссылка на источник ОБЯЗАТЕЛЬНА !

Автор: Folio
Ничего не понялТак себе...Не плохоДовольно интересноОтлично ! То что нужно ! (голосов: 4, среднее: 3,75 из 5)
Загрузка...
Отправить на почту Отправить на почту

Заметка

итак, OpenBSD 4.4

user level ppp (т.к. kernel level ppp мне не удалось подружить с chap-ом)

для «автозапуска рррое» достаточно создать файл
/etc/hostname.tun0, в котором написать:

!/usr/sbin/ppp -ddial pppoe

все настройки относительно самого pppoe находятся в файле:
/etc/ppp/ppp.conf

пример содержимого:

default:
Set log Phase Chat LCP IPCP CCP tun command
Disable ipv6cppppoe:
set device "!/usr/sbin/pppoe -i xl0"
set mtu max 1492
set mru max 1492
set speed sync
disable acfcomp protocomp
deny acfcomp
set authname my-pppoe-login
set authkey my-pppoe-password
add default HISADDR

xl0 — «имя» реальной сетевой карточки, через которую цепляемся к pppoe-серверу
логин-пароль необходимо указывать без кавычек

к сожалению, правильно бы было указать пару «логин-пароль» не в конфиге ррр, а в специальном файле /etc/ppp/chap-secrets (а в конфиге оставить только «логин»)
но — при удалении пароля из ppp.conf соединение не поднимается

ну и самое главное — как рестартовать такое pppoe-соединение

# kill -9 `cat /var/run/tun0.pid`
# ifconfig tun0 destroy
# sh /etc/netstart tun0

пара ньюансов:

  1. увы, не факт, что в tun0.pid окажется «правильный» process-id
  2. мне не нравится «sh /etc/netstart tun0«, так что «когда будет время» — расковыряю netstart и сделаю «как надо»
  3. не забываем про sysctl:

net.inet.gre.allow=1
net.inet.ip.forwarding=1

З.Ы. При копировании статьи ссылка на источник ОБЯЗАТЕЛЬНА !

Автор: GrayCat
Ничего не понялТак себе...Не плохоДовольно интересноОтлично ! То что нужно ! (голосов: 2, среднее: 3,50 из 5)
Загрузка...
Отправить на почту Отправить на почту

Заметка

Столкнулся с задачей мониторинга серверов, без изобретения велосипедов и рисования графиков самостоятельно, а чтобы было всё в одном месте, удобство управления, разграничение прав пользоватеей, реагирование на те или иные события.

Для этого замечательно подходит Zabbix, он умеет собирать данные как по SNMP так и через установку собственного агента на сервера. Мы будем рассматривать вариант сборки данных с FreeBSD через SNMP v3 с авторизацией.

Коротко о главном

Сначала собираем net-snmp, на том хосте (хостах) с которого хотим получать данные, из портов c опциями:

cd /usr/ports/net-mgmt/net-snmp
make install WITHOUT_IPV6=yes NET_SNMP_SYS_CONTACT="root@mydomain.ru" NET_SNMP_SYS_LOCATION="server location"

для запуска snmp сервиса добавляем в /etc/rc.conf:

snmpd_enable="YES"
snmpd_conffile="/etc/snmpd.conf"

в /etc/snmpd.conf пишем единственную строку с именем пользователя, который будет «читать» нас через SNMP:

rouser  пользователь

А теперь самое интересное — добавляем пользователя в SNMP.

Пароль должен быть минумум 8 символов, а процесс snmpd должен быть еще выключен.

net-snmp-config --create-snmpv3-user -ro -A ПАРОЛЬ -X ПАРОЛЬ имя_пользователя

запускаем, проверяем.

/usr/local/etc/rc.d/snmpd start

главное указать уровень доступа — authNoPriv, иначе будет ошибка — authorizationError

snmpwalk -v 3 -u пользователь -A пароль -l authNoPriv хост

Установка Zabbix‘а (/usr/ports/net-mgmt/zabbix) остаеться как домашнее задание

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

—with-net-snmp

как получить данные с хоста по SNMP v3 сейчас я раскажу 🙂

Сначала надо добавить хост:

Configuration — Hosts -> Create host

Указываем имя для заббикса, адрес по которому с ним соединяться — ip/fqdn.
Теперь нужно собрать данные с хоста для мониторинга. Будем смотреть на загрузку процессора:

Configuration — Items — выбираем созданный хост -> Create Item

заполняем поля:

Type - SNMPv3 Agent
SNMP OID - host.hrDevice.hrProcessorTable.hrProcessorEntry.hrProcessorLoad.768
SNMPv3 security name - пользователя что добавляли в snmp ранее
SNMPv3 security level - AuthNoPriv
SNMPv3 auth passphrase и SNMPv3 priv passphrase - пароль пользователя
Key - system.cpu.util[ <,type> <,mode>]

Далее нам надо смотреть как то собранную информацию.. построим по ней график:

Configuration — Items — Graphs — выбираем хост -> Create Graph

Добавляем параметр для отрисовки — жмем Add у параметра Items, там нужно выбрать хост, и источник данных.
Ура! остается сохранить и наслаждаться.

Графики можно посмотреть в Monitoring — Graphs.

З.Ы. При копировании статьи ссылка на источник ОБЯЗАТЕЛЬНА !

Автор: Jekel
Ничего не понялТак себе...Не плохоДовольно интересноОтлично ! То что нужно ! (голосов: 6, среднее: 4,00 из 5)
Загрузка...
Отправить на почту Отправить на почту

Наткнулся на статью, т.к. сам частенько занимаюсь клонированием винтов, решил сохранить сей труд.

Итак сама статья:

Любой из нас хорошенько задумывается над тем, как правильно разбить HDD
при установке FreeBSD. Действительно, потом будет весьма проблематично
изменить размер патриции при необходимости. Проблема заключается в том,
что на жестком диске находятся, так называемые, слайсы, а уже в них
инкапсулированы партиции. Это не всегда так, потому что есть еще и
второй метод разметки HDD без слайсов, но в данной статье он не
рассматривается. Популярные программы для работы с разделами HDD, такие
как Partition Magic, Acronis могут удалить слайс,
скопировать/переместить его посекторно, но никак не заглянуть внутрь и
изменить размер той или иной партиции.

Итак, есть система (далее, система — это установленная ОС FreeBSD,
включающая в себя MBR, корневой раздел, SWAP, дополнительные разделы
/tmp, /var и т.д.) на жестком диске объемом 80 ГБ.

Задача: перенести систему на другой жесткий диск объемом 250 ГБ, увеличивая размеры
партиций пропорционально увеличению объема HDD. В нашем случае это
250-80=170 ГБ.

Вторая часть задачи: повторять эту процедуру автоматически, с определенным интервалом

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

Для выполнения задачи воспользуемся утилиткой CloneHDD. Взять ее можно
здесь: http://sourceforge.net/projects/clonehdd Качаем дистрибутив последней версии и выполняем:

tar -zxvf clonehdd-2.0.3.tar.gz -C /home/user/

Можно сделать проще — установить из портов:

make install -C /usr/ports/sysutils/clonehdd

После установки мы видим грозное предупреждение о том, что запуская
clonehdd нужно семь раз отмерить а потом уже отрезать =)) Но все не так
страшно, как кажется: программа изначально рассчитана так, что данные на
исходном винте никогда и ни при каких условиях не подвергнутся записи.
Это сообщение больше относится к нашему новому, чистому винту. Поэтому,
если вы купили чистый винт и хотите на него переехать, то вряд ли
clonehdd чем то сможет навредить.

Работа с программой предельно проста. Для осуществления задуманного нам
нужно всего один раз запустить утилиту с нужными параметрами. Вот о
параметрах теперь поподробнее.

-src=[device] — Это исходный девайс на котором находится наша система.
Чтобы узнать имя текущего винта, нужно выполнить команду:

/sbin/mount

Мы увидим таблицу, в первой колонке которой указаны имена примонтированных
устройств. Вот, например:

/dev/ad0s1f

/dev/» это папка, в которой находятся все устройства, «ad0» это нужное нам имя самого винта,

«s1» слайс под номером 1, «f» имя партиции на слайсе.

Вывод: значение этому параметру присваиваем «ad0»

-dst=[device] Это девайс назначения. Именно сюда переедет ваша система.
Узнать его можно, например, командой:

cat /var/run/dmesg.boot

На экране появится список сообщений ядра при загрузке. Обычно, сообщения о
найденных винтах находятся в самом конце. Ни одна партиция не должна
быть примонтирована с этого винта, иначе CloneHDD выдасть ошибку.

-swap=[size in MB] Размер будущего раздела SWAP.

-freespace=[Required free space on dst partition] Если клонирование
производится на винт меньшего размера, на DST винте после клонирования
должен остаться небольшой кусочек свободного места, для того чтобы винт
небыл на 100% заполнен после клонирования. По умолчанию — это 100МБ.
Этим параметром его можно изменить.

-safe [Required safe mode for `dump`] Включает режим безопасного
копирования. В этом режиме сначала создается образ каждого раздела в
папке .snap, а потом делается перенос. В «небезопасном» режиме,
копирование производится на лету. Вся проблема в том что размер этого
образа равен размеру данных на разделе. А т.к. образ будет записан на
тот же раздел с которого производится копирование, на разделе долно быть
минимум 50% свободного места. Вернемся к параметру -safe. Если параметр
отсутствует, клонирование будет произведено в безопасном режиме если
достаточно свободного места или в небезопасном режиме если этого места
недостаточно. Если параметр задан, небезопасного копирования не будет, и
разделы с недостатком свободного места склонированы не будут.

-fstab=[Device name to write in fstab] После клонирования будет
сгенерирован файл /etc/fstab на полученном винте. Параметр задает Имя
девайса, который будет записан в этот файл. По умолчанию: девайс,
заданный параметром -src.

-force [Do not ask any questions] При выполнении второй части задачи,
утилита будет выполняться из cron’а и нам не нужны лишние вопросы, в
ходе выполнения программы. Данный параметр отключает интерактивный режим
и вопрос «Are you sure?» задаваться не будет.

Вернемся к нашей задаче. Мой исходный винт: ad0, новый винт: ad1.

srv# clonehdd -src=ad0 -dst=ad1 -swap=1024
Clone parameters:
Source partition: /dev/ad0
Dest partition: /dev/ad1
Swap size: 1024 MB
Safe dumping: Disabled
Free space on DST: 100 MB
Fstab device name: ad0

[OK] Found devices for clone procedure
[OK] DST partition is not in use

Source partition
/usr size: 64489MB, used: 10563MB
/var size: 4958MB, used: 144MB
/ size: 1483MB, used: 82MB
/tmp size: 1483MB, used: MB
Total: 72415 MB, used: 10790 MB

[OK] Device ad1 has enough free space
Wait 10 seconds before start: 10 9 8 7 6 5 4 3 2 1
[OK] Device /dev/ad1 made clean
[OK] New slice created

Destination device partitions:
SWAP size: 1024 MB
/ size 1588 MB
/tmp size 1588 MB
/var size 5306 MB
/usr size 69026 MB

[INF] Last partition were increased for 3 blocks
[OK] Partitions were created successfully

[OK] Partition /tmp was formatted successfully
Starting dump/restore procedure… [OK]

[OK] Partition /var was formatted successfully
Starting dump/restore procedure… [OK]

[OK] Partition /usr was formatted successfully
Starting dump/restore procedure… [OK]

[OK] Partition / was formatted successfully
Starting dump/restore procedure… [OK]

[OK] file /etc/fstab generated successfully

Видим сообщения об успешном клонировании. Теперь сделаем выполнение
задачи по расписанию: выполнять каждый день в 2 часа ночи клонирование
рабочего винта на запасной. Добавляем в файл /etc/crontab строку

0 2 * * * root clonehdd -src=ad0 -dst=ad1 -swap=1024 -force >/dev/null

Обратите внимание, что >/dev/null убивает не все сообщения. Только
обычные сообщения будут опущены. Все сообщения об ошибках отсылаются на
выход STDERR, не попадают на /dev/null и будут отправлены cron’ом по
почте системному администратору.

Автор: Bart Tapolsky, Оригинал тут

Ничего не понялТак себе...Не плохоДовольно интересноОтлично ! То что нужно ! (голосов: 5, среднее: 4,40 из 5)
Загрузка...
Отправить на почту Отправить на почту