Udpxy на сервере провайдера

в 16:20, , рубрики: iptv, linux, udpxy, метки:

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

Во-первых, всем клиентам доступны через Веб-браузер команды «status» и «reset».

Во-вторых, примерно при 40 одновременных потоках udpxy начинает притормаживать, хотя процессор и сетевой интерфейс практически не заняты.

Небольшое отступление

Как правило, udpxy отсутствует в виде двоичного пакета под нужный дистрибутив, поэтому его требуется собирать из исходных текстов.

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

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

Компромиссом для разовой сборки является создание временной сборочной среды («песочницы» aka «sandbox») на рабочей системе в отдельном каталоге.

В Debian Squeeze для этого достаточно следующих команд:

apt-get install debootstrap
mkdir /home/builder
debootstrap squeeze /home/builder http://mirror.yandex.ru/debian
chroot /home/builder apt-get update
chroot /home/builder apt-get -y dist-upgrade
chroot /home/builder apt-get -y install gcc make

Для сборки udpxy нужны только gcc и make, для более объёмных программ требуемых пакетов может быть больше.

Сборка и установка

Скачиваем архив с исходными текстами и разворачиваем в песочницу:

ver="1.0.23-0"
wget http://downloads.sourceforge.net/project/udpxy/udpxy/Chipmunk-1.0/udpxy.${ver}-prod.tar.gz'
tar xzf ~/udpxy.${ver}-prod.tar.gz -C /home/builder/home/

Изменяем названия управляющих команд на очень секретные:

cd /home/builder/home/udpxy-${ver}
sed -i.orig -e 's!/restart!/SECRET_restart!' -e 's!/status!/SECRET_status!' statpg.h
sed -i.orig -e 's!"status"!"SECRET_status"!' -e 's!"restart"!"SECRET_restart"!' -e 's!"rtp"!"SECRET_rtp"!' extrn.c

Заходим в песочницу и компилируем:

chroot /home/builder make -C /home/udpxy-${ver}

Создаём псевдопользователя для запуска udpxy и каталог для программы и утилит, переносим в него программу:

useradd --system --shell /bin/true --create-home udpxy
install -o udpxy -g udpxy -m 700 -p /home/builder/home/udpxy-${ver}/udpxy /home/udpxy/

Запуск

Чтобы увеличить рабочий лимит одновременных подключений, будем запускать несколько экземпляров udpxy на разных портах и раскидывать по ним запросы, приходящие на стандартный порт 4022, в зависимости от IP-адреса клиента с помощью iptables=>nat=>PREROUTING=>REDIRECT:

#!/bin/bash
# /home/udpxy/start

MAINPORT="4022"
SUBPORTS="16"
 SUBMASK="40%02d"
  LOGDIR="/var/log/udpxy"
   UDPXY="/home/udpxy/udpxy"
    USER="udpxy"

mkdir -p $LOGDIR || { echo "ERROR: cannot create $LOGDIR, aborted."; exit 1; }

id "$USER" > /dev/null 2>&1 || { echo "ERROR: user $USER does not exists, aborted."; exit 1; }

# Run instances and create redirects...
for ((a = 0; a < "$SUBPORTS"; a++)); do
        p=`printf "$SUBMASK" "$a"`
        iptables -t nat -I PREROUTING -s 0.0.0.0/0.0.0.$a -p tcp -m tcp --dport "$MAINPORT" 
                -j REDIRECT --to-ports "$p"
        while : ; do
                date +"%Y.%m.%d %H:%M:%S -- Started $p"
                sudo -u "$USER" "$UDPXY" -T -p $p -c32
                date +"%Y.%m.%d %H:%M:%S -- Finished $p"
                sleep 30
        done >> "$LOGDIR/$p.log" 2>&1 &
done

Пояснения: во-первых, сценарий требует bash, т.к. sh не понимает цикл со счётчиком. Во-вторых, использовать «iptables -A» вместо "-I" нельзя, т.к. правило с "-s 0.0.0.0/0.0.0.0" окажется первым и заберёт себе все подключения. В-третьих, сценарий продолжает работать в фоновом режиме и автоматически перезапускает завершившиеся (упавшие?) экземпляры udpxy.

По умолчанию udpxy выводит в log-файлы /var/log/udpxy/*.log минимум информации. Если нужно сделать вывод более подробным, добавьте в строку запуска «sudo -u ...» ключи "-v" и "-S".

Дополнительные ручки, за которые имеет смысл покрутить: "-B 1Mb" для увеличения входного буфера (в байтах), "-R 10" — одновременного количества сообщений (в штуках) и "-H 5" — максимального срока буферизации данных (в секундах).

Добавьте вызов /home/udpxy/start в /etc/rc.local (написание сценариев для /etc/init.d отложим на другой раз).

Сценарий завершения должен выглядеть примерно так:

#!/bin/bash
# /home/udpxy/stop

# Remove firewall rules...
iptables-save 
| grep -- "-p tcp -m tcp --dport 4022 -j REDIRECT --to-ports" 
| sed 's/^-A/iptables -t nat -D/' 
| sh -

# Kill program instances...
pkill -f "/home/udpxy/udpxy"
sleep 5

# Kill wrapper scripts...
pkill -f "/home/udpxy/start"

Просмотр состояния

Смотреть общий список подключений ко всем экземплярам udpxy можно следующим образом:

#!/usr/bin/perl
#  /var/www/html/SECRET-udpxy-status.cgi

use strict;
use warnings;

my $port0 = 4000;
my $ports = 16;
my $status_cmd = 'SECRET_status';

my $title = "Udpxy summary status";
my $hostname;

($hostname = $ENV{HTTP_HOST}) =~ s/:d+$//;

print "Content-type: text/htmlnn" if $ENV{REMOTE_ADDR};  # ..cgi-bin mode?

print << "__HEAD__";
<html>
<head>
<title>$title</title>
</head>
<body>
<div id='bodyCon'>
<h1>$title</h1>
<div id='pgCont'>
<table border='1'>
<tr><th>Port</th><th>Process ID</th><th>Source</th><th>Destination</th><th>Throughput</th></tr>
__HEAD__

my %cnt;
my $total = 0;
my $style_copied;
my $style_passed;

for (my $p = $port0; $p < $port0 + $ports; $p++) {
        open F, "wget -q -O - http://localhost:$p/$status_cmd |";
        while(<F>) {
                if ($style_copied and /</style>/) {
                        $style_copied = 0;                  # ..style finished
                        $style_passed = 1;
                        print;
                } elsif (/<style/ and not $style_passed) {
                        $style_copied = 1;                  # ..style started
                        print;
                } elsif ($style_copied) {                   # ..style copy in progress
                        print;
                } elsif (/<td>d+</td><td>d+.d+.d+.d+:d+</td>/) {
                        s/<tr([^>]*)>/<tr$1><td>$p</td>/;  # ..insert first column with port number
                        ($cnt{$p} ||= 0)++;
                        $total++;
                        print;
                }
        }
        close F;
}

print "</table><h3>Clients count</h3><table border='1'><tr><th>Port</th>n";

print "<td><a href='http://$hostname:$_/$status_cmd'>$_</a></td>n"
        foreach $port0 .. ($port0 + $ports - 1);

print "<th>Total</th></tr><tr><th>Count</th>n";

printf "<td>%s</td>n", $cnt{$_} || ''
        foreach $port0 .. ($port0 + $ports - 1);

print "<th>$total</th></tr></table></div></div></body></html>n";

Возможно, есть более простой способ, но этот оказался самым быстрым.

Запускать его можно из-под любого Веб-сервера с поддержкой CGI-BIN. В Debian для этого отлично подходит mini-httpd:

apt-get install mini-httpd

echo 'START=1' >> /etc/default/mini-httpd

echo '
port=4020
user=nobody
nochroot
dir=/var/www
data_dir=/var/www/html
cgipat=**.cgi
logfile=/var/log/mini-httpd.log
pidfile=/var/run/mini-httpd.pid
charset=utf-8
' > /etc/mini-httpd.conf

/etc/init.d/mini-httpd restart

SNMP

Чтобы смотреть по SNMP в Cacti/Zabbix/MRTG текущее количество подключенных клиентов (максимальное на один процесс + общее), добавьте в /etc/snmp/snmpd.conf:

exec udpxy_connections /etc/snmp/udpxy_connections.sh

Собственно сценарий /etc/snmp/udpxy_connections.sh:

#!/bin/sh

A() { netstat -nt |grep ESTABLISHED |grep -v 127.0.0.1: |awk '{print $4;}' |egrep ':40[01][0-9]$'; }

A | wc -l
A | awk -F : '{print $2;}' | sort | uniq -c | sort -nr | head -1 | awk '{print $1;}'

Проверка:

snmpwalk -On -c SecretCommunity -v2c 10.20.30.40 NET-SNMP-EXTEND-MIB::nsExtendObjects

Заключение

С сожалению, модернизация не всякой старой сети (замена тупняков на управляйки, замена гирлянд на прямые волокна и т.д.), требуемая для нормальной работы IP-телевидения по мультикасту, может быть произведена быстро. Временный вариант с udpxy позволяет запустить услугу IPTV, не дожидаясь, пока сеть будет доведена до современного уровня.

Автор: IlyaEvseev

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js