Есть домашний компьютер (ноутбук). Есть интернет. Есть выделенная линия. Не всегда все работает хорошо. Хочется, чтобы это не сильно влияло на удобство пользования. Хочется немного сэкономить на трафике. Хочется убедиться, что нас не обманывают и счета выставляют правильно. Какие есть утилиты и как ими воспользоваться? На примере (K)Ubuntu 8.04.
На нашем компьютере могут быть запущены сервисы, доступные из сети. Нужно закрыть к ним доступ, чтобы быть уверенными, что никто извне не сможет получить доступ к этим сервисам и совершить что-нибудь нехорошее. Сделать это можно с помощью штатного фильтра пакетов Linux - iptables.
Создадим (с правами rootа) файл /etc/init.d/firewall
следующего содержания:
#!/bin/sh IPTABLES=/sbin/iptables # очищаем все цепочки, # чтобы этот скрипт можно было бы просто запускать, без перезагрузки :) $IPTABLES -F INPUT $IPTABLES -F OUTPUT $IPTABLES -F FORWARD # по умолчанию все входящие пакеты - уничтожаем $IPTABLES -P INPUT DROP # пакеты с локального интерфейса - принимаем # (локальные службы могут обмениваться данными) $IPTABLES -A INPUT -i lo -j ACCEPT # принимаем пакеты установленных соединений # (входящий трафик уже установленных нами соединений нужно принять) $IPTABLES -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # возможно, здесь понадобится разрешить входящие соединения для специальных случаев # например, входящие GRE пакеты с определенного адреса для pptp VPN подключений #$IPTABLES -A INPUT -p gre -s peer.vnp.net -j ACCEPT # или для работы UPnP на локальном маршрутизаторе #$IPTABLES -A INPUT -s 192.168.0.1 -p udp --sport 1900 -j ACCEPT # про остальные пакеты (перед уничтожением) помещаем запись в журнал $IPTABLES -A INPUT -m limit --limit 1/minute -j LOG --log-prefix "IPT INPUT dropped " # все исходящие пакеты - разрешаем $IPTABLES -P OUTPUT ACCEPT # эти пакеты порождаются процессами на нашем компьютере # можно разрешить здесь пакеты только на известные порты или только # от определенного пользователя, но будем считать, # что зловредных программ (и пользователей) у нас нет # все пакеты, пересылаемые с интерфейса на интерфейс, - уничтожаем # пока наш компьютер не работает маршрутизатором, разрешать нет смысла $IPTABLES -P FORWARD DROP # перед уничтожением помещаем запись в журнал $IPTABLES -A FORWARD -m limit --limit 1/minute -j LOG --log-prefix "IPT FORWARD dropped "
Чтобы правила нашего файервола создавались при старте системы, создадим символическую ссылку:
sudo ln -s /etc/init.d/firewall /etc/rc2.d/S99firewall
Подробности смотрите в man iptables
.
Нам нужен кэширующий прокси, чтобы немного сэкономить на трафике и ускорить просмотр страниц. Конечно, браузеры тоже кэшируют страницы и картинки, но хотелось бы иметь один общий кэш для всех имеющихся браузеров.
Squid слишком тяжел... Воспользуемся Polipo - кеширующим прокси для персонального использования.
Устанавливаем пакет polipo
.
Конфигурация по умолчанию вполне работоспособна.
Я лишь добавил в файл
/etc/polipo/config
одну строчку:
tunnelAllowedPorts = 22, 80, 443, 2096Чтобы разрешить HTTPS через прокси на эти порты (2096 - ну совершенно нестандартный).
Документация по Polipo доступна в нем самом:
зайдите браузером на http://localhost:8123/
.
Теперь надо настроить браузеры для работы с прокси. Для большей гибкости я создал скрипт автоматической конфигурации прокси. Это Javascript файл, который выполняется браузером для того, чтобы определить, какой прокси необходимо использовать (и надо ли использовать вообще) для обращения к данному URL.
Расположение скрипта указывается в настройках браузера. А сам скрипт выглядит так:
//(C) Oskar Pearson and the Internet Solution (http://www.is.co.za) // Эта функция и вызывается браузером. // Она принимает два параметра - полный URL ресурса и имя хоста. // Множественные вспомогательные функции, используемые внутри, // определены где-то "внутри" браузера, работают :) // Функция возвращает значения вида: // DIRECT - использовать прямое подключение, без прокси // PROXY hostname:port - адрес и порт прокси // разделенные точкой с запятой. // Таким образом, можно использовать несколько прокси, последующие // будут использоваться при недоступности предыдущих. function FindProxyForURL(url, host) { // Если имя хоста не содержит доменной части, не используем прокси if (isPlainHostName(host)) return "DIRECT"; // Эти правила задают прямое подключение к машинам // в локальной сети и локальной машине. Имя хоста // совпадает с соответствующей маской. if (shExpMatch(host, "intranet*") || shExpMatch(host, "internal*") || shExpMatch(host, "localhost*") || shExpMatch(host, "*localdomain") || shExpMatch(host, "192.168.*") || shExpMatch(host, "10.*")) return "DIRECT"; // Если доменное имя не резолвится, тоже пробуем без прокси. if (!isResolvable(host)) return "DIRECT"; // Различные большие файлы (которые все равно не HTML страницы) // тоже получаем напрямую. if (shExpMatch(url, "*.(rar|zip|mp3|ogg|avi|tar|tar.gz)")) return "DIRECT"; // Через прокси пойдет только HTTP и HTTPS трафик. if (url.substring(0, 5) == "http:" || url.substring(0, 6) == "https:" //url.substring(0, 4) == "ftp:" || //url.substring(0, 7) == "gopher:" ) // Вот и адрес прокси. // Если прокси недоступен, идем напрямую. return "PROXY localhost:8123; DIRECT"; return "DIRECT"; }В Википедии можно почитать про автоконфигурирование прокси (на английском).
В случае неустойчивого соединения с интернетом бывает так, что качать и серфить более менее можно, но все стопорится из-за DNS. При неустойчивой связи разрешение доменных имен может происходить довольно долго и не всегда с первого раза. Помогает локальный кэширующий DNS сервер. К тому же это позволит чуть-чуть сэкономить на трафике :)
Proxy DNS Server (пакет pdnsd) как раз и создан для наших целей. Он кэширует запросы и хранит кэш на диске.
По умолчанию файл конфигурации /etc/pdnsd.conf
игнорируется. Вместо него используется один из файлов
из каталога /usr/share/pdnsd/
, что задается
параметром AUTO_MODE
в файле
/etc/default/pdnsd
. Для ручной настройки этот параметр
нужно закомментировать.
Далее правим /etc/pdnsd.conf
. Я лишь добавил
секцию с адресами DNS серверов моего провайдера:
server { // метка для группы серверов label="dom.ru"; // IP адреса серверов ip="91.144.168.1"; ip="91.144.170.1"; // доступность серверов проверяется посылкой пустого DNS запроса uptest=query; // таймаут для ответов сервера, 30 секунд timeout=30; // доступность серверов проверяется каждые 60 секунд interval=60; }
В /etc/resolv.conf
я оставил лишь одну строчку:
nameserver 127.0.0.1
Практика показала, что не стоит сюда также вписывать адреса
DNS-серверов провайдера. Если сервера недоступны,
pdnsd будет возвращать либо закэшированный ответ,
либо - немедленно - ошибку.
В случае ошибки приложения будут затем запрашивать
сервера провайдера и ждать положенный таймаут,
задержка не уменьшится.
Если же приложения обращаются
только к pdnsd, ответ, как было сказано выше,
будет получен немедленно. И таймаут можно уменьшить, настроив
соответствующий параметр.
И доступность серверов будет проверять pdnsd, а не каждое
приложение.
Проконтролировать работу и поуправлять нашим сервером можно
с помощью команды pdnsd-ctl
, например,
sudo pdnsd-ctl status
или
sudo pdnsd-ctl server dom.ru retest
На анлимитных тарифах доступа к интернету, ширина канала, как правило, ограничена. Возникает такая проблема. Какое-то приложение занимается скачиванием данных и занимает всю полосу канала. Но одновременно хочется просто ходить по страничкам браузером. А канал уже занят. В результате страницы открываются неоправданно медленно, что очень некомфортно.
Необходимо каким-то образом повысить приоритет трафика, создаваемого браузером, по сравнению с трафиком "качалок". В этом случае при открытии страницы браузер временно займет всю ширину канала, а скачивание замедлится. Но задержки при открытии страниц не будет. Будет гораздо приятнее. Не придется ждать, пока докачается очередной файл, прежде чем пойти что-нибудь почитать или поискать.
Исследование (чтение man tc
,
man tc-pfifo_fast
и man tc-prio
)
показало, что Linux с настройками по умолчанию вполне
умеет приоритизировать трафик по значению поля
TOS
IP пакетов.
Имеется три очереди для пакетов с разным приоритетом, пакеты
из очереди с меньшим приоритетом начинают обрабатываться
только если в очереди с большим приоритетом нет пакетов для
обработки.
С настройками по умолчанию приоритеты разпределяются так
(не вдаваясь сильно в подробности):
Поле же TOS можно установить, на основе разнообразных критериев, с помощью тех же iptables. В результате файервол дополняется следующими строками:
# очищаем правила $IPTABLES -t mangle -F INPUT $IPTABLES -t mangle -F OUTPUT # модуль connbytes позволяет учитывать объем трафика, # переданного через соединение # в данном случае учитывается трафик в байтах, переданный в обе стороны # первые 50 Кбайт (новые соединения) имеют максимальный приоритет $IPTABLES -t mangle -A INPUT \ -m connbytes --connbytes 0:51200 --connbytes-dir both --connbytes-mode bytes \ -j TOS --set-tos Minimize-Delay $IPTABLES -t mangle -A OUTPUT \ -m connbytes --connbytes 0:51200 --connbytes-dir both --connbytes-mode bytes \ -j TOS --set-tos Minimize-Delay # если передано более 200 Кбайт (старые соединения), - минимальный приоритет $IPTABLES -t mangle -A INPUT \ -m connbytes --connbytes 204800: --connbytes-dir both --connbytes-mode bytes \ -j TOS --set-tos Maximize-Throughput $IPTABLES -t mangle -A OUTPUT \ -m connbytes --connbytes 204800: --connbytes-dir both --connbytes-mode bytes \ -j TOS --set-tos Maximize-Throughput # отдельные правила для Jabber - максимальный приоритет $IPTABLES -t mangle -A INPUT \ -p tcp -m multiport --source-ports 5222:5223 \ -j TOS --set-tos Minimize-Delay $IPTABLES -t mangle -A OUTPUT \ -p tcp -m multiport --destination-ports 5222:5223 \ -j TOS --set-tos Minimize-Delay
Все это работает для "классических" соединений, но не работает для торрентов (и, наверняка, для других p2p протоколов), т.к. в этом случае открывается множество короткоживущих соединений, через каждое из которых передается лишь небольшое количество данных. Данная схема отнесет все эти соединения в новым и назначит им наивысший приоритет, что неприемлемо.
Можно поиграть с настройками торрент клиента. Ktorrent позволяет, во-первых, устанавливать поле TOS своих пакетов (точнее, поле DSCP, что, по сути, лишь более новый стандарт интерпретации того же байта IP пакета), а во-вторых, задавать ширину используемого программой канала (в том числе и менять ее по расписанию). Первая настройка, к сожалению, не поможет с входящим трафиком, тут надо, чтобы ваши "напарники" устанавливали тип трафика. Вторая настройка дает не совсем то, что нам нужно. Нужно, чтобы торрент использовал весь канал, и лишь ненадолго уступал его браузеру, а не то, чтобы торрент использовать лишь часть канала, а остальная часть всегда бы принадлежала браузеру.
Я применил довольно грубое, но более-менее работающее, решение. Любому трафику с непривилегированных портов (которые больше 1024) на непривилегированные порты я назначаю наименьший приоритет. В эту категорию не попадает обычный веб-трафик (на 80 или 443 порты), что нам и нужно. Правила надо вставить до специфических правил для отдельных портов (до Jabberа):
$IPTABLES -t mangle -A INPUT \ -p tcp --sport 1024:65535 --dport 1024:65535 \ -j TOS --set-tos Maximize-Throughput $IPTABLES -t mangle -A OUTPUT \ -p tcp --dport 1024:65535 --sport 1024:65535 \ -j TOS --set-tos Maximize-Throughput
Предлагаю еще один, более хитрый и правильный, способ
определения торрент трафика. Только в качестве экспериментального
и непроверенного. Во-первых, необходимо определить пакеты,
создаваемые определенным приложением - торрент-клиентом.
Тут поможет модуль owner
. К сожалению,
определение PID и названия процесса не работает на
многопроцессорных машинах и может быть отключено в вашем ядре.
Во-вторых, надо отделить входящие пакеты для этого приложения.
Тут работает модуль connmark
.
Вот что может получиться (не проверялось!):
$IPTABLES -t mangle -A OUTPUT \ -m owner --cmd-owner ktorrent \ -j CONNMARK --set-mark 1 $IPTABLES -t mangle -A INPUT \ -m connmark --mark 1 \ -j TOS --set-tos Maximize-Throughput $IPTABLES -t mangle -A OUTPUT \ -m connmark --mark 1 \ -j TOS --set-tos Maximize-Throughput
Читайте man iptables
.
Если захочется более серьезно поиграть с управлением
трафиком, смотрите tcng
. Это язык и компилятор
для более дружелюбного, чем позволяет стандартная утилита
tc
, создания правил управления трафиком.
Для контроля собственного трафика достаточно наблюдать трафик на интерфейсе. Это не будет полноценным биллингом, нельзя будет выяснить, куда ушел трафик. Но сверить сумму с данными провайдера - можно.
Очень хорош collectd
.
Это легкий демон, который собирает множество параметров
вашего компьютера. Включая и показания счетчиков интерфейсов.
В новой версии (4.3.0) демон научился снимать показания
счетчиков интерфейсов различных устройств по
SNMP.
Теперь
MRTG
не нужен :)
collectd
хранит данные в файлах
RRD.
Этот формат специально предназначен для хранения
числовых данных за периоды времени с автоматическим
расчетом максимальных, минимальных и средних значений.
Также по данным легко стоятся довольно сложные графики.
Настраиваем демон. Правим файл
/etc/collectd/collectd.conf
:
# Конфигурационный файл для collectd(1) # # Некоторые плугины требуют дополнительной конфигурации и поэтому # по умолчанию отключены. Подробности смотрите в collectd.conf(5). # # Также следует прочитать /usr/share/doc/collectd/README.Debian.plugins # прежде чем включать какой-либо плугин. # интервал по умолчанию - 10 секунд # я увеличил его до 30 секунд, т.к. мне не удалось установить интервал SNMP опроса Interval 30 LoadPlugin battery LoadPlugin cpu LoadPlugin df LoadPlugin disk LoadPlugin entropy LoadPlugin interface LoadPlugin irq LoadPlugin load LoadPlugin memory LoadPlugin processes LoadPlugin rrdtool # этот плугин был выключен LoadPlugin snmp LoadPlugin swap LoadPlugin syslog LoadPlugin tcpconns LoadPlugin users <Plugin rrdtool> DataDir "/var/lib/collectd/rrd" </Plugin> # См. примеры более сложной конфигурации # в /usr/share/doc/collectd/examples/snmp-data.conf.gz <Plugin snmp> # какие параметры будем получать по SNMP <Data "std_traffic"> Type "if_octets" Table true Instance "IF-MIB::ifDescr" Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets" </Data> # адрес хоста и параметры доступа <Host "dlink"> Address "10.200.200.1" Version 1 Community "public" Collect "std_traffic" #Inverval 120 </Host> </Plugin> <Plugin syslog> LogLevel info </Plugin> Include "/etc/collectd/thresholds.conf"
Для некоторых плугинов необходимо установить дополнительные
пакеты, указанные в зависимостях качестве рекомендуемых для
collectd
.
Для плугина ping
нужен пакет liboping0
(этот плугин в этой версии collectd
у меня не заработал).
Для snmp
- libsnmp15
.
Статистика по локальным интерфейсам помещается в файл вида:
/var/lib/collectd/rrd/asus/interface/if_octets-eth1.rrd
.
Здесь asus
- имя локального хоста;
eth1
- название интерфейса.
Статистика, полученная через SNMP помещается в файл вида:
/var/lib/collectd/rrd/dlink/snmp/if_octets-Ethernet-WAN.rrd
.
Здесь dlink
- имя хоста, указанное в конфигурации;
Ethernet-WAN
- имя (описание) интерфейса, полученное
через SNMP.
Теперь необходимо как-то извлечь собранные данные.
Самый простой способ - воспользоваться CGI скриптом
/usr/share/doc/collectd/examples/collection.cgi.gz
.
Распакуйте его куда-нибудь в cgi-bin
имеющегося
под рукой веб-сервера, сделайте исполняемым,
и все, вы можете получить красивые графики
всех доступных параметров.
Скрипт написан на Perl, вам понадобится сам Perl плюс еще
небольшая кучка модулей...
Собственно для манипуляции с RRD файлами предназначена
утилита rrdtool
, с кучей параметров
и страниц мануалов.
Нас пока интересует рисование графиков. Для этого предназначена
команда rrdtool graph
, которая создает картинки
с графиками. Но все же удобнее
нарисовать веб-страницу и поместить на нее графики. Для этого
есть rrdcgi
. CGI скрипт получается примерно такой:
#!/usr/bin/rrdcgi <html> <head><title>Интернет</title></head> <body> <h1>Загрузка интерфейса за сутки</h1> <p> <h2>eth1</h2> <RRD::GRAPH traffic-eth1.png --lazy --width 800 -v "b/s" --title "eth1 traffic" DEF:incoming=/var/lib/collectd/rrd/asus/interface/if_octets-eth1.rrd:rx:AVERAGE DEF:outgoing=/var/lib/collectd/rrd/asus/interface/if_octets-eth1.rrd:tx:AVERAGE CDEF:in=incoming,8,* CDEF:out=outgoing,8,* AREA:in#00a000:"Incoming" LINE1:out#0000a0:"Outgoing"> </p> <p> <h2>wan</h2> <RRD::GRAPH traffic-wan.png --lazy --width 800 -v b/s --title "wan traffic" DEF:incoming=/var/lib/collectd/rrd/dlink/snmp/if_octets-Ethernet-WAN.rrd:rx:AVERAGE DEF:outgoing=/var/lib/collectd/rrd/dlink/snmp/if_octets-Ethernet-WAN.rrd:tx:AVERAGE CDEF:in=incoming,100000,GE,UNKN,incoming,8,*,IF CDEF:out=outgoing,100000,GE,UNKN,outgoing,8,*,IF AREA:in#00a000:"Incoming" LINE1:out#0000a0:"Outgoing"> </p> <h1>Загрузка интерфейса за неделю</h1> <p> <h2>eth1</h2> <RRD::GRAPH traffic-eth1-week.png --lazy --width 800 --start -1w --end now -v "b/s" --title "eth1 traffic" DEF:incoming=/var/lib/collectd/rrd/asus/interface/if_octets-eth1.rrd:rx:AVERAGE DEF:outgoing=/var/lib/collectd/rrd/asus/interface/if_octets-eth1.rrd:tx:AVERAGE CDEF:in=incoming,8,* CDEF:out=outgoing,8,* AREA:in#00a000:"Incoming" LINE1:out#0000a0:"Outgoing"> </p> </body> </html>
Кратко о параметрах (которые аналогичны таковым у
rrdtool graph
):
traffic-eth1-week.png
--lazy
--width 800
--start -1w --end now
-1year
,
-1month
), окончание графика - сейчас.--title "eth1 traffic"
DEF:incoming=/var/lib/collectd/rrd/asus/interface/if_octets-eth1.rrd:rx:AVERAGE
incoming
,
данные берутся из указанного файла из источника данных
под названием rx
, берутся средние значения.CDEF:in=incoming,8,*
in
,
значениями которой являются значения incoming
,
умноженные на 8. Используется обратная польская запись.
Умножать на 8 нужно, чтобы перевести байты (октеты)
в секунду в биты в секунду.CDEF:in=incoming,10000,GE,UNKN,incoming,8,*,IF
AREA:in#00a000:"Incoming"
in
рисуем "заливкой"
зеленым цветом и даем метку "Incoming".LINE1:out#0000a0:"Outgoing"
out
рисуем тонкой линией
синего цвета и даем метку "Outgoing".
Чтобы получить числовые значения трафика, нужно немного больше
программирования. Я воспользовался Python, нужен модуль
rrdtool
из пакета python-rrd
.
Скрипт, который показывает входящий и исходящий трафик за день,
выглядит так:
#!/usr/bin/python import rrdtool # файл статистики интерфейса ETH1 = "/var/lib/collectd/rrd/asus/interface/if_octets-eth1.rrd" # разрешение файла, здесь - 30 секунд # значение должно равняться интервалу опроса collectd RESOLUTION = 30 # выбираем отчеты traffic = rrdtool.fetch( ETH1, "AVERAGE", "-r", str(RESOLUTION), "-s", "-1d") # начиная день тому назад incoming = 0 outgoing = 0 # cуммируем for row in traffic[2]: if row[0]: incoming += row[0] if row[1]: outgoing += row[1] # умножаем на интервал, переводим в мегабайты и выводим print "Incoming:\t%g Mb" % (float(incoming)*RESOLUTION/1024/1024) print "Outgoing:\t%g Mb" % (float(outgoing)*RESOLUTION/1024/1024)К сожалению, этот простой способ неприемлем для подсчета трафика за большие периоды, т.к. тут разрешение, т.е. промежуток между отчетами будет переменным. Необходимо извлекать из RRD допольнительные данные. Есть, куда развивать метод...
Я совместил расчет трафика и генерацию графиков в одном CGI скрипте, написанном на Python. Это не сложно. В результате получается красивая страница со всеми нужными данными.
Читаем страницы мануалов:
collectd
collectd.conf
rrdtool
rrdgraph
rrdgraph_rpn
rrdcgi
Denis Nelubin, May 2008