Фильтрация и упорядочивание UDP RTP пакетов

Обсуждение настройки и работы сервисов, резервирования, сетевых настроек и вопросов безопасности ОС.

Модератор: SLEDopit

Ответить
Аватара пользователя
denel
Сообщения: 497
ОС: Gentoo Linux
Контактная информация:

Фильтрация и упорядочивание UDP RTP пакетов

Сообщение denel »

Привет всем!)

Долго уже мучаюсь с гарантированной доставкой UDP пакетов через openvpn туннели, проходящие через двух разных операторов сотовой связи. Пока всё же остановился на TCP OpenVPN, как крайне рекомендовали. Но когда у одного из провайдеров начинается проблема доставки пакетов, разность во времени доставки пакетов по этим двум туннелям начинает порой доходить до 30 и более секунд. И принимающий VLC упорно не хочет работать по принципу "схватил №12345, далее ждёт именно №12346 и так далее", вместо этого он хватает то пакеты с одного интерфейса, то пакеты со второго интерфейса (для VLC приходит 2 копии трафика, iptables TEE, эти копии то и дело расходятся во времени, а так же некоторые пакеты одной из копий могут вообще не дойти, значительно реже недоходят некоторые пакеты с обоих копий)

Вопрос. Можно ли сделать какое-нибудь приспособление, которое на лету смогло бы упорядочивать пакеты по RTP sequence id, отбрасывая повторяющиеся пакеты? То есть это приспособление должно запоминать пришедшие данные и выпускать их, когда удастся установить порядок следования или станет ясно, что утерян недостающий пакет в обоих копиях трафика? Пока дошёл только до переключения между копиями через iptables DROP:

Код: Выделить всё

Date() { date +%Y-%m-%d_%H-%M-%S; }

tcpdump --immediate-mode -l -tt -T rtp -i any -n -v \
 "udp and host 192.168.1.2 and host 192.168.1.20 and port 1234" | \
 sed -u ':a; /)$/N; s/\n//; ta; s/,//g' | \
 awk -v 'waittime=0.5' -v 'obzor=100' -v 'seqMax=65535' -v "seqMaxPolov=$((65535/2))" \
 -v "conti=0" -v "timeGranica=-0.5" \
'{
print >> "/var/log/radio_tcpdump_source.log"
  time = $1
  eth = $6
  pkgId = $8
  inp = $18
  out = $20
  seqId = $24

  if (ethStat == "") ethStat = eth

  # Вычисляем, сколько пакетов потеряно с предыдущего принятого от этого интерфейса
  inPackE[eth][inPackN[eth]] = seqId - inPack[eth][inPackN[eth]] - 1
  if (inPackE[eth][inPackN[eth]] < 0)
   inPackE[eth][inPackN[eth]] = inPackE[eth][inPackN[eth]] + seqMax + 1

  if (inPackE[eth][inPackN[eth]] > 0)
   print "log " time "\tКоличество пропусков:" inPackE[eth][inPackN[eth]] "\tSeqID:" seqId "\tУ интерфейса:" eth

  inPackN[eth] ++
  if (inPackN[eth] == obzor+1) inPackN[eth] = 1

  inPack[eth][inPackN[eth]] = seqId
  inPackAct[eth] = seqId
  inPackActTime[eth] = time

  # Перебор известных интерфейсов для подсчёта количества пропущенных пакетов
  for (key1 in inPack) {
    seqNOld = -1
    inPackESumm[key1] = 0

    # Перебор обозримых принятых пакетов на выбранном выше интерфейсе
    for (key2 in inPack[key1]) {
      key3 = obzor - inPackN[key1] + key2
      if (key3 > obzor) key3 = key3 - obzor

      # Суммируем ошибки для интерфейса
      inPackESumm[key1] = inPackESumm[key1] + inPackE[key1][key3]

      seqN = inPack[key1][key2]
      seqNOld = seqN
    }
  }

  # Находим альтернативный актуальному интерфейс
  if (ethStatAlt == "") {
    for (key1 in inPack) {
      if (key1 != ethStat) {
        ethStatAlt = key1
        print ethStat " " inp " " out " " ethStatAlt
      }
    }
  }

  # Корректируем разницу seq numnber принятых пакетов по сбросу нумерации после 65535
  inPackActRaznica = inPackAct[ethStat] - inPackAct[ethStatAlt]
  inPackActRaznicaTime = inPackActTime[ethStat] - inPackActTime[ethStatAlt]
  if (inPackActRaznica > seqMaxPolov) inPackActRaznica = seqMax - inPackActRaznica
  if (inPackActRaznica < -seqMaxPolov) inPackActRaznica = -seqMax - inPackActRaznica

  # Если inPackActRaznica имеет положительное значение,
  # значит выбранный интерфейс опережает алтернативный на столько то пакетов, всё хорошо

  if (inPackActRaznicaTime > timeGranica * -0.5 || inPackActRaznicaTime < -timeGranica * -0.5 || inPackESumm[ethStat] != 0)
   print "log " time "\tethStat:" ethStat "\t" \
    inPackAct[ethStat] "(" inPackActTime[ethStat] ")\t" \
    inPackAct[ethStatAlt] "(" inPackActTime[ethStatAlt] \
    ")\tErr:" inPackESumm[ethStat] ":" inPackESumm[ethStatAlt] "\t" \
    inPackActRaznica "\t:\t" inPackActRaznicaTime

  if (inPackActRaznica < 10 && inPackESumm[ethStatAlt] <= inPackESumm[ethStat])
  if ( (inPackActRaznicaTime < timeGranica) ||
       (inPackESumm[ethStat] != 0 && inPackActRaznicaTime > timeGranica*4) )
  {
    tmp = ethStat
    ethStat = ethStatAlt
    ethStatAlt = tmp
    print ethStat " " inp " " out " " ethStatAlt " " inPackActRaznicaTime ":" inPackESumm[ethStatAlt] ":" inPackESumm[ethStat]
  }
  fflush()
}' | while read eth in out downEthArr raznica
do
  switch=0
  numRM=""
  in=${in%.*}
  out=${out%.*}
  if [[ $eth == "log" ]]; then
    echo "$(Date) $eth $in $out $downEthArr Razn: $raznica" >> "$log"
    continue
  fi
echo >> "$log"
echo $(Date) $eth ${in} ${out} $downEthArr Raznica $raznica >> "$log"

  # Разблокируем валидный поток, если заблокирован
  numRM=( $(iptables -t raw -L PREROUTING -v -n --line | \
   grep "^[ ]*[0-9][0-9]* .* tun_stream$eth .* $in .* $out .* dpt:1234") )
  if [[ -n ${numRM[0]} ]]; then
    iptables -t raw -D PREROUTING ${numRM[0]}
    echo "$(Date) iptables -t raw -D PREROUTING ${numRM[0]}...${numRM[@]}"
    switch=1
  fi

  # Блокируем невалидный поток
  for downEth in $downEthArr; do
    if ! iptables -t raw -L PREROUTING -v -n --line | \
       grep -q "^[ ]*[0-9][0-9]* .* tun_stream$downEth .* $in .* $out .* dpt:1234"
    then
      iptables -t raw -L PREROUTING -v -n --line | \
       grep -q "^[ ]*[0-9][0-9]* .* tun_stream$downEth .* $in .* $out .* dpt:1234" || \
      iptables -t raw -A PREROUTING -p udp -s $in -d $out --dport 1234 -i tun_stream$downEth -j DROP
      echo "$(Date) iptables -t raw -A PREROUTING -p udp -s $in -d $out --dport 1234 -i tun_stream$downEth -j DROP"
      switch=1
    fi
  done

  # Отправка сообщения, если произошло переключение
  [[ $switch == 1 ]] && \
   send "Звуковой поток переключил на tun_stream$eth" \
    "denel@..." \
    "Успешно переключил звуковой поток на tun_stream$eth
$(Date)"
iptables -t raw -L PREROUTING --line -n -v >> "$log"
done
Да, потоки различаю по TTL, который задаю особенный на передающей стороне.

Начинал же это "путишествие" здесь
Спасибо сказали:
Аватара пользователя
chitatel
Сообщения: 2063

Re: Фильтрация и упорядочивание UDP RTP пакетов

Сообщение chitatel »

Ээээ... А UDP можно доставлять гарантированно? Это ж протокол без гарантированной доставки пакетов, если я правильно ошибаюсь.

P.S.
Ыыы.. Оказывается, можно.

Вот тут описан Reliable Udp, правда, для .Net: https://habr.com/ru/post/250227/
Спасибо сказали:
Аватара пользователя
denel
Сообщения: 497
ОС: Gentoo Linux
Контактная информация:

Re: Фильтрация и упорядочивание UDP RTP пакетов

Сообщение denel »

chitatel писал:
12.05.2020 17:57
Ээээ... А UDP можно доставлять гарантированно? Это ж протокол без гарантированной доставки пакетов, если я правильно ошибаюсь.

P.S.
Ыыы.. Оказывается, можно.

Вот тут описан Reliable Udp, правда, для .Net: https://habr.com/ru/post/250227/
В моём случае он доставляется внутри tcp openvpn туннеля. Потери происходят тогда, когда openvpn tcp уже не в состоянии это сделать. И такие моменты меня в частности и интересуют. Но когда поток переключил, например обнаружив потерю 617 пакета (615, 616, 618...), тогда с другого потока те пакеты уже прошли и программе грубо говоря после 618 уже сразу даётся какой-нибудь 693, к примеру, а всё, что было до него - как восстановить?
Спасибо сказали:
Аватара пользователя
Hephaestus
Сообщения: 3729
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2
Контактная информация:

Re: Фильтрация и упорядочивание UDP RTP пакетов

Сообщение Hephaestus »

chitatel писал:
12.05.2020 17:57
Это ж протокол без гарантированной доставки пакетов
И мы это даже уже обсуждали.
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 20792
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: Фильтрация и упорядочивание UDP RTP пакетов

Сообщение Bizdelnick »

Всё работает ровно так, как должно. Протоколы выше транспортного уровня не при делах, оставьте их в покое.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
Аватара пользователя
denel
Сообщения: 497
ОС: Gentoo Linux
Контактная информация:

Re: Фильтрация и упорядочивание UDP RTP пакетов

Сообщение denel »

Да я уже начал думать, может связку tcpdump + awk подчинить этим целям. Только пока не понял, как чередовать информацию о пакете с самим содержанием пакета, вводя в awk, чтобы из awk в соответствующей последовательности уже выводить и... Попробовать скармливать в vlc через stdin. Как сделать, чтоб tcpdump в своём выводе чередовал данные о пакете с содержанием самого пакета?... Соответственно awk придётся выступать неким кешем пакетов на протияжении до поломинуты х 2 потока. То есть наверно это будет храниться в переменных. Хз выдержит ли, в том числе бинарный тип данных

Тем временем попытался выгонять содержимое пакетов из tcpdump опцией -X. Через awk далее пишу в файл через xxd -r -p, получается полная идентичность того, что вышло из tcpdump, если снова сконвертировать hexdump -C. Но VLC это принимать не спешит, данные не в порядке. Пытался скормить через файл, пытался скормить через netcat udp://, нормально не берёт. Походу проще будет начать осваивать программирование и лезть в код VLC))) Конечно это был бы самый прямой способ
Спасибо сказали:
Ответить