Explorons le moteur VoIP Mediastreamer2. Partie 7

Le contenu de cet article est tiré de ma chaîne Zen.

Explorons le moteur VoIP Mediastreamer2. Partie 7

Nous utilisons TShark pour analyser les paquets RTP

Explorons le moteur VoIP Mediastreamer2. Partie 7

Dans le passé, article Nous avons assemblé un schéma de contrôle à distance à partir d'un générateur et d'un détecteur de signaux tonals, la communication entre les deux étant réalisée via un flux RTP.

Dans cet article, nous continuons à explorer la transmission du signal audio à l'aide du protocole RTP. Tout d'abord, nous allons diviser notre application de test en émetteur et récepteur et apprendre à explorer le flux RTP à l'aide d'un analyseur de trafic réseau.

Ainsi, pour mieux comprendre quels éléments du programme sont responsables de la transmission RTP et lesquels sont responsables de la réception, nous allons diviser notre fichier mstest6.c en deux programmes autonomes, l'émetteur et le récepteur, et les fonctions communes que les deux programmes utilisent seront regroupées dans un troisième fichier que nous appellerons mstest_common.c, qui sera inclus par l'émetteur et le récepteur à l'aide de la directive 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;
}

Voici maintenant le fichier de l'émetteur isolé :

/* Файл 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);
  }
}

Et enfin, le fichier du récepteur :

/* Файл 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); 
 }
}

Nous compilons l'émetteur et le récepteur, puis exécutons chacun dans sa propre console. Ensuite, cela devrait fonctionner comme auparavant — seuls les nombres de 1 à 6 doivent être entrés dans la console de l'émetteur, et la réponse devrait apparaître dans la console du récepteur. Les tons devraient être entendus dans la dynamique. Si tout est en ordre, alors une connexion est établie entre le récepteur et l'émetteur — il y a une transmission continue de paquets RTP de l'émetteur au récepteur.

Il est maintenant temps d'installer l'analyseur de trafic, pour cela nous allons installer la version console d'un excellent programme Wireshark — il s'appelle TShark. J'ai choisi TShark pour simplifier la description de la gestion du programme. Avec Wireshark, j'aurais eu besoin de beaucoup de captures d'écran qui pourraient rapidement devenir obsolètes avec la sortie de nouvelles versions de Wireshark.

Si vous savez utiliser Wireshark, vous pouvez l'utiliser pour étudier nos exemples. Mais même dans ce cas, je vous recommande d'apprendre à utiliser TShark, car cela vous aidera à automatiser les tests de vos applications VoIP et à réaliser des captures à distance.

Nous installons TShark avec la commande :

$ sudo apt-get install tshark

Traditionnellement, nous vérifions le résultat de l'installation en demandant la version du programme :

$ tshark --version

Si nous obtenons une réponse adéquate, nous continuons.

Puisque nos paquets circulent uniquement à l'intérieur de l'ordinateur, nous pouvons dire à tshark de n'afficher que ces paquets. Pour cela, nous devons choisir de capturer les paquets depuis l'interface. boucle locale (loopback), en passant l'option TShark -i lo:

$ sudo tshark -i lo

La console commencera immédiatement à afficher des messages sur les paquets envoyés par notre émetteur (en continu, peu importe si nous avons pressé le bouton de la télécommande ou non). Il est possible que sur votre ordinateur, il y ait des programmes qui envoient aussi des paquets sur la boucle locale, dans ce cas nous obtiendrons un mélange de nos paquets et de paquets d'autres sources. Pour être sûrs que nous ne voyons que les paquets envoyés par notre télécommande, nous allons ajouter un filtre par numéro de port. En appuyant sur Ctrl-C, nous arrêtons l'analyseur et nous entrons le filtre sur le numéro de port que la télécommande utilise comme port de destination pour son envoi (8010) : -f "udp port 8010". Maintenant, notre ligne de commande ressemblera à ceci :

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

La console affichera une sortie du type suivant (les 10 premières lignes) :

 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

Pour l'instant, ce ne sont pas encore des paquets, mais une liste numérotée d'événements où chaque ligne est un message concernant un paquet qui a été détecté sur l'interface. Puisque nous avons déjà pris soin de filtrer les paquets, nous voyons dans la liste uniquement des messages concernant les paquets de notre émetteur. Décortiquons ensuite ce tableau par numéro de colonne :

Numéro de l'événement.
Heure de son apparition.
Adresse IP source du paquet et adresse IP du récepteur du paquet.
Protocole du paquet, affiché comme UDP, car les paquets RTP sont transmis comme charge utile à l'intérieur des paquets UDP.
Taille du paquet en octets.
Numéro de port source du paquet et numéro de port de destination du paquet.
Taille de la charge utile du paquet, d'où l'on peut conclure que notre émetteur forme des paquets RTP de 172 octets, qui, comme un canard dans un coffre, se trouve à l'intérieur d'un paquet UDP de 214 octets.
Il est maintenant temps d'explorer l'intérieur des paquets UDP, pour cela nous allons lancer TShark avec un ensemble de clés complémentaire :

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

En conséquence, la sortie du programme sera enrichie — chaque événement ajoutera une décryptage du contenu interne du paquet qui l'a provoqué. Pour mieux examiner les données de sortie, vous pouvez soit arrêter TShark en appuyant sur Ctrl-C, soit dupliquer sa sortie dans un fichier en ajoutant un pipeline à la commande de lancement avec le programme tee en spécifiant le nom de fichier, tee <nom_du_fichier> :

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

Voyons maintenant ce que nous avons obtenu dans le fichier, voici le premier paquet de celui-ci :

1 0.000000000    127.0.0.1 → 127.0.0.1    RTP 214 PT=ITU-T G.711 PCMU, SSRC=0x6B8B4567, Seq=58366, Time=355368720
Frame 1: 214 bytes on wire (1712 bits), 214 bytes captured (1712 bits) on interface 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)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1User Datagram Protocol, Src Port: 8010, Dst Port: 7010
Real-Time Transport Protocol    [Stream setup by HEUR RT (frame 1)]
        [Setup frame: 1] 
       [Setup Method: HEUR RT]
    10.. .... = Version: RFC 1889 Version (2)
    ..0. .... = Padding: False
    ...0 .... = Extension: False
    .... 0000 = Contributing source identifiers count: 0   
   0... .... = Marker: False
    Payload type: ITU-T G.711 PCMU (0)
    Sequence number: 58366    [Extended sequence number: 58366]
    Timestamp: 355368720
    Synchronization Source identifier: 0x6b8b4567 (1804289383)
    Payload: 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                                  ......

Nous dédierons le prochain article à l'analyse des informations contenues dans cette liste et nous parlerons inévitablement de la structure interne du paquet RTP.

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster