Studium silnika VoIP Mediastreamer2. Część 7

Materiał artykułu pochodzi z mojego kanału Zen.

Studium silnika VoIP Mediastreamer2. Część 7

Używamy TShark do analizy pakietów RTP.

Studium silnika VoIP Mediastreamer2. Część 7

W poprzedniej artykuł Zebraliśmy schemat zdalnego sterowania z generatora i detektora sygnałów tonowych, połączenie między którymi odbywało się za pomocą strumienia RTP.

W artykule tym kontynuujemy analizę przesyłania sygnału dźwiękowego przy użyciu protokołu RTP. Najpierw podzielimy naszą aplikację testową na nadajnik i odbiornik, a następnie nauczymy się badać strumień RTP przy użyciu analizatora ruchu sieciowego.

Aby wyraźnie zobaczyć, które elementy programu odpowiadają za przesyłanie RTP, a które za odbieranie, dzielimy nasz plik mstest6.c na dwa niezależne programy: nadajnik i odbiornik. Wspólne funkcje, które są używane przez oba, umieścimy w trzecim pliku, który nazwaliśmy mstest_common.c, który będzie dołączany przez nadajnik i odbiornik za pomocą dyrektywy include:

/* Файл mstest_common.c Общие функции для передатчика и приемника. */
#include <mediastreamer2/msfilter.h>
#include <mediastreamer2/msticker.h>
#include <mediastreamer2/msrtp.h>
#include <ortp/rtpsession.h>
#include <ortp/payloadtype.h>

define PCMU 0

/*---------------------------------------------------------*/
/* Функция регистрации типов полезных нагрузок. */
void register_payloads(void)
{  
 /* Регистрируем типы нагрузок в таблице профилей. Позднее, по индексу    взятому 
     из заголовка RTP-пакета из этой таблицы будут извлекаться    параметры 
     нагрузки, необходимые для декодирования данных пакета. */
  rtp_profile_set_payload (&av_profile, PCMU, &payload_type_pcm8000);
}

/*---------------------------------------------------------*/
/* Эта функция создана из функции create_duplex_rtpsession() в audiostream.c   медиастримера2. */
 static RtpSession *create_rtpsession (int loc_rtp_port, int loc_rtcp_port,  bool_t ipv6, RtpSessionMode mode)
{  
  RtpSession *rtpr;  rtpr = rtp_session_new ((int) mode);  
  rtp_session_set_scheduling_mode (rtpr, 0);  
  rtp_session_set_blocking_mode (rtpr, 0);
  rtp_session_enable_adaptive_jitter_compensation (rtpr, TRUE);
  rtp_session_set_symmetric_rtp (rtpr, TRUE); 
  rtp_session_set_local_addr (rtpr, ipv6 ? "::" : "0.0.0.0", loc_rtp_port,  loc_rtcp_port); 
  rtp_session_signal_connect (rtpr, "timestamp_jump",  (RtpCallback) rtp_session_resync, 0);
  rtp_session_signal_connect (rtpr, "ssrc_changed",  (RtpCallback) rtp_session_resync, 0);
  rtp_session_set_ssrc_changed_threshold (rtpr, 0);
  rtp_session_set_send_payload_type(rtpr, PCMU);

  /* По умолчанию выключаем RTCP-сессию, так как наш пульт не будет использовать 
  её. */  
 rtp_session_enable_rtcp (rtpr, FALSE);
 return rtpr;
}

Teraz plik samodzielnego nadajnika:

/* Файл mstest6.c Имитатор пульта управления (передатчика). */
#include <mediastreamer2/dtmfgen.h>
#include <mediastreamer2/msrtp.h>
#include "mstest_common.c"

/*----------------------------------------------------------*/
int main()
{ 
  ms_init();

/* Создаем экземпляры фильтров. */
  MSFilter *voidsource = ms_filter_new(MS_VOID_SOURCE_ID); 
  MSFilter *dtmfgen = ms_filter_new(MS_DTMF_GEN_ID);

/* Создаем фильтр кодера. */
  MSFilter *encoder = ms_filter_create_encoder("PCMU");

/* Регистрируем типы нагрузки. */
  register_payloads();

/* Создаем RTP-сессию передатчика. */
  RtpSession *tx_rtp_session = create_rtpsession (8010, 8011, FALSE, RTP_SESSION_SENDONLY);  
 rtp_session_set_remote_addr_and_port(tx_rtp_session,"127.0.0.1", 7010, 7011); 
 rtp_session_set_send_payload_type(tx_rtp_session, PCMU);  
 MSFilter *rtpsend = ms_filter_new(MS_RTP_SEND_ID); 
 ms_filter_call_method(rtpsend, MS_RTP_SEND_SET_SESSION, tx_rtp_session);

/* Создаем источник тактов - тикер. */ 
 MSTicker *ticker_tx = ms_ticker_new();

/* Соединяем фильтры передатчика. */ 
 ms_filter_link(voidsource, 0, dtmfgen, 0);  
 ms_filter_link(dtmfgen, 0, encoder, 0);
 ms_filter_link(encoder, 0, rtpsend, 0);

/* Подключаем источник тактов. */
  ms_ticker_attach(ticker_tx, voidsource);

/* Настраиваем структуру, управляющую выходным сигналом генератора. */ 
 MSDtmfGenCustomTone dtmf_cfg; 
 dtmf_cfg.tone_name[0] = 0; 
 dtmf_cfg.duration = 1000; 
 dtmf_cfg.frequencies[0] = 440;

/* Будем генерировать один тон, частоту второго тона установим в 0. */  
 dtmf_cfg.frequencies[1] = 0; 
 dtmf_cfg.amplitude = 1.0; 
 dtmf_cfg.interval = 0.;  
 dtmf_cfg.repeat_count = 0.;

/* Организуем цикл сканирования нажатых клавиш. Ввод нуля завершает
* цикл и работу программы. */  
 char key='9'; 
 printf("Нажмите клавишу команды, затем ввод.n"  
"Для завершения программы введите 0.n");  
while(key != '0')  
{
 key = getchar();   
 if ((key >= 49) && (key <= 54)) 
   {
      printf("Отправлена команда: %cn", key);
      /* Устанавливаем частоту генератора в соответствии с
       * кодом нажатой клавиши. */
      dtmf_cfg.frequencies[0] = 440 + 100*(key-49);

      /* Включаем звуковой генератор c обновленной частотой. */
      ms_filter_call_method(dtmfgen, MS_DTMF_GEN_PLAY_CUSTOM,      (void*)&dtmf_cfg); 
   }
   /* Укладываем тред в спячку на 20мс, чтобы другие треды 
   * приложения получили время на работу. */ 
  ms_usleep(20000);
  }
}

I w końcu plik odbiornika:

/* Файл mstest7.c Имитатор приемника. */
include <mediastreamer2/mssndcard.h>
include <mediastreamer2/mstonedetector.h>
include <mediastreamer2/msrtp.h>

/* Подключаем заголовочный файл с функциями управления событиями  медиастримера.*/
include <mediastreamer2/mseventqueue.h>
/* Подключаем файл общих функций. */
include "mstest_common.c"

/* Функция обратного вызова, она будет вызвана фильтром, как только он   обнаружит совпадение характеристик входного сигнала с заданными. */
static void tone_detected_cb(void *data, MSFilter *f, unsigned int event_id,MSToneDetectorEvent *ev)
{ 
 printf("Принята команда: %sn", ev->tone_name);
}

/*----------------------------------------------------------*/
int main()
{ 
 ms_init();

/* Создаем экземпляры фильтров. */  
 MSSndCard *card_playback =  ms_snd_card_manager_get_default_card(ms_snd_card_manager_get()); 
 MSFilter *snd_card_write = ms_snd_card_create_writer(card_playback); 
 MSFilter *detector = ms_filter_new(MS_TONE_DETECTOR_ID);

/* Очищаем массив находящийся внутри детектора тонов, он описывает
* особые приметы разыскиваемых сигналов.*/
  ms_filter_call_method(detector, MS_TONE_DETECTOR_CLEAR_SCANS, 0);

/* Подключаем к фильтру функцию обратного вызова. */  
ms_filter_set_notify_callback(detector,  (MSFilterNotifyFunc)tone_detected_cb, NULL);

/* Создаем массив, каждый элемент которого описывает характеристику
* одного из тонов, который требуется обнаруживать:
Текстовое имя
* данного элемента, частота в герцах, длительность в миллисекундах,
* минимальный уровень относительно 0,775В. */
  MSToneDetectorDef scan[6]= 
 {   
    {"V+",440, 100, 0.1}, /* Команда "Увеличить громкость". */
    {"V-",540, 100, 0.1}, /* Команда "Уменьшить громкость". */
    {"C+",640, 100, 0.1}, /* Команда "Увеличить номер канала". */
    {"C-",740, 100, 0.1}, /* Команда "Уменьшить номер канала". */
    {"ON",840, 100, 0.1}, /* Команда "Включить телевизор". */
    {"OFF", 940, 100, 0.1}/* Команда "Выключить телевизор". */
  };

/* Передаем "приметы" сигналов детектор тонов. */
  int i; 
 for (i = 0; i < 6; i++) 
 { 
   ms_filter_call_method(detector, MS_TONE_DETECTOR_ADD_SCAN,    &scan[i]); 
 }

/* Создаем фильтр декодера */
  MSFilter *decoder=ms_filter_create_decoder("PCMU");

/* Регистрируем типы нагрузки. */
  register_payloads();

/* Создаем RTP-сессию приемника. */
  MSFilter *rtprecv = ms_filter_new(MS_RTP_RECV_ID);
  RtpSession *rx_rtp_session = create_rtpsession (7010, 7011, FALSE, RTP_SESSION_RECVONLY);
  ms_filter_call_method(rtprecv, MS_RTP_RECV_SET_SESSION, rx_rtp_session);

/* Создаем источник тактов - тикер. */ 
 MSTicker *ticker_rx = ms_ticker_new();

/* Соединяем фильтры приёмника. */
  ms_filter_link(rtprecv, 0, decoder, 0);
  ms_filter_link(decoder, 0, detector, 0);
  ms_filter_link(detector, 0, snd_card_write, 0);

/* Подключаем источник тактов. */
  ms_ticker_attach(ticker_rx, rtprecv);
  char key='9';
  printf( "Для завершения программы введите 0.n");
  while(key != '0') 
 {
    key = getchar();
   /* Укладываем тред в спячку на 20мс, чтобы другие треды    * приложения получили время на работу. */
   ms_usleep(20000); 
 }
}

Kompilujemy nadajnik i odbiornik, a następnie uruchamiamy każdy w swojej konsoli. Potem powinno działać jak wcześniej — wprowadzenie liczb od 1 do 6 powinno odbywać się w konsoli nadajnika, a odpowiedzi powinny pojawiać się w konsoli odbiornika. W dynamicznej scenie powinny być słyszalne tony. Jeśli wszystko jest w porządku, między odbiornikiem a nadajnikiem ustanowione jest połączenie — następuje ciągłe przesyłanie pakietów RTP od nadajnika do odbiornika.

Teraz nadszedł czas, aby zainstalować analizator ruchu. Zainstalujemy wersję konsolową znakomitego programu Wireshark — nazywa się TShark. Wybrałem TShark do dalszej prezentacji, aby ułatwić opis zarządzania programem. Z Wireshark potrzebowałbym mnóstwa zrzutów ekranu, które mogą szybko stracić swoją aktualność wraz z wydaniem nowej wersji Wireshark.

Jeśli umiesz korzystać z Wireshark, możesz go używać do analizy naszych przykładów. Jednak w tym przypadku zalecam również opanowanie TShark, ponieważ pomoże to zautomatyzować testowanie twoich aplikacji VoIP oraz umożliwi zdalny przechwyt.

Instalujemy TShark poleceniem:

$ sudo apt-get install tshark

Tradycyjnie sprawdzamy wynik instalacji, pytając o wersję programu:

$ tshark --version

Jeśli otrzymaliście odpowiedni wynik, przechodzimy dalej.

Ponieważ nasze pakiety krążą tylko wewnątrz komputera, możemy powiedzieć, że tshark pokazuje tylko takie pakiety. W tym celu należy wybrać przechwytywanie pakietów z interfejsu loopback (pętla zwrotna), przekazując TShark opcję -i lo:

$ sudo tshark -i lo

Na konsoli natychmiast zaczną pojawiać się komunikaty o pakietach wysyłanych przez nasz nadajnik (ciągle, niezależnie od tego, czy naciskaliśmy przycisk na pilocie, czy nie). Może na Twoim komputerze są programy, które również wysyłają pakiety po lokalnej pętli, w tym przypadku uzyskamy mieszankę naszych i obcych pakietów. Aby upewnić się, że na liście widzimy tylko pakiety wysyłane przez nasz pilot, dodamy filtr po numerze portu. Naciskając Ctrl-C zatrzymujemy analizator i wprowadzamy filtr na numer portu, który nasz pilot używa jako port docelowy do swojej transmisji (8010): -f "udp port 8010". Teraz nasza linia poleceń będzie wyglądać tak:

$ sudo tshark -i lo -f "udp port 8010"

Na konsoli pojawi się wyjście w następującym formacie (pierwsze 10 wierszy):

 1 0.000000000    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172 
 2 0.020059705    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172
 3 0.040044409    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172 
 4 0.060057104    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172
 5 0.080082311    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172  
 6 0.100597153    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172 
 7 0.120122668    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172
 8 0.140204789    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172
 9 0.160719008    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172
10 0.180673685    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172

Na razie to jeszcze nie są pakiety, a ponumerowana lista zdarzeń, gdzie każdy wiersz to komunikat o kolejnym pakiecie, który został zauważony na interfejsie. Ponieważ zadbaliśmy już o filtrowanie pakietów, widzimy w liście tylko komunikaty o pakietach naszego nadajnika. Następnie rozszyfrujemy tę tabelę według numerów kolumn:

Numer zdarzenia.
Czas jego wystąpienia.
Adres IP źródła pakietu i adres IP odbiorcy pakietu.
Protokół pakietu, pokazany jako UDP, ponieważ pakiety RTP są przesyłane jako ładunek w pakietach UDP.
Rozmiar pakietu w bajtach.
Numer portu źródła pakietu i numer portu odbiorcy pakietu.
Rozmiar ładunku pakietu, z czego można wywnioskować, że nasz nadajnik tworzy pakiety RTP o rozmiarze 172 bajtów, które, jak kaczka w skrzyni, znajdują się w pakiecie UDP o rozmiarze 214 bajtów.
Teraz nadszedł czas, aby zajrzeć do pakietów UDP, w tym celu uruchomimy TShark z rozszerzonym zestawem kluczy:

sudo tshark -i lo -f "udp port 8010"  -P -V -O rtp -o rtp.heuristic_rtp:TRUE -x

W rezultacie, wyjście programu zostanie wzbogacone — do każdego zdarzenia dodany zostanie dekryptaż wewnętrznej zawartości pakietu, który je wywołał. Aby lepiej przyjrzeć się danym w wyjściu, możesz zatrzymać TShark, naciskając Ctrl-C, lub powielić jego wyjście do pliku, dodając do komendy uruchomienia potok z programem tee i wskazując nazwę pliku, tee :

$ sudo tshark -i lo -f "udp port 8010"  -P -V -O rtp -o rtp.heuristic_rtp:TRUE -x | tee log.txt

Teraz przyjrzymy się temu, co otrzymaliśmy w pliku, oto pierwszy pakiet z niego:

1 0.000000000    127.0.0.1 → 127.0.0.1    RTP 214 PT=ITU-T G.711 PCMU, SSRC=0x6B8B4567, Seq=58366, Czas=355368720
Frame 1: 214 bajtów w sieci (1712 bitów), 214 bajtów zarejestrowanych (1712 bitów) na interfejsie 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Protokół Internetowy w wersji 4, Src: 127.0.0.1, Dst: 127.0.0.1Protokół Datagramów Użytkownika, Port Src: 8010, Port Dst: 7010
Protokół Transportu Czasu Rzeczywistego    [Strumień ustawiony przez HEUR RT (ramka 1)]
        [Ustaw ramkę: 1] 
       [Metoda ustawienia: HEUR RT]
    10.. .... = Wersja: Wersja RFC 1889 (2)
    ..0. .... = Padding: Fałsz
    ...0 .... = Rozszerzenie: Fałsz
    .... 0000 = Liczba identyfikatorów źródła współdzielonego: 0   
   0... .... = Znacznik: Fałsz
    Typ ładunku: ITU-T G.711 PCMU (0)
    Numer sekwencji: 58366    [Rozszerzony numer sekwencji: 58366]
    Znacznik czasu: 355368720
    Identifikator źródła synchronizacyjnego: 0x6b8b4567 (1804289383)
    Ładunek: ffffffffffffffffffffffffffffffffffffffffffffffff...

0000  00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00   ..............E.
0010  00 c8 3c 69 40 00 40 11 ff b9 7f 00 00 01 7f 00   ..<i@.@.........
0020  00 01 1f 4a 1b 62 00 b4 fe c7 80 00 e3 fe 15 2e   ...J.b..........
0030  7f 10 6b 8b 45 67 ff ff ff ff ff ff ff ff ff ff   ..k.Eg..........
0040  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
0050  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
0060  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
0070  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
0080  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
0090  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
00a0  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
00b0  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
00c0  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
00d0  ff ff ff ff ff ff                                  ......

Następny artykuł poświęcimy analizie informacji zawartej w tym zrzucie i nieuchronnie porozmawiamy o wewnętrznej strukturze pakietu RTP.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster