Εξερευνώντας τη μηχανή VoIP Mediastreamer2. Μέρος 7

Το υλικό του άρθρου είναι παρμένο από το δικό μου κανάλι ζεν.

Εξερευνώντας τη μηχανή VoIP Mediastreamer2. Μέρος 7

Χρήση TShark για ανάλυση πακέτων RTP

Εξερευνώντας τη μηχανή VoIP Mediastreamer2. Μέρος 7

Στο παρελθόν άρθρο Συναρμολογήσαμε ένα κύκλωμα τηλεχειρισμού από μια γεννήτρια τόνου και έναν ανιχνευτή τόνου, η επικοινωνία μεταξύ των οποίων πραγματοποιήθηκε χρησιμοποιώντας μια ροή RTP.

Σε αυτό το άρθρο, συνεχίζουμε να μελετάμε τη μετάδοση σήματος ήχου χρησιμοποιώντας το πρωτόκολλο RTP. Αρχικά, ας χωρίσουμε την εφαρμογή δοκιμής μας σε έναν πομπό και έναν δέκτη και ας μάθουμε πώς να εξετάζουμε τη ροή RTP χρησιμοποιώντας έναν αναλυτή κίνησης δικτύου.

Έτσι, για να μπορούμε να δούμε πιο ξεκάθαρα ποια στοιχεία προγράμματος είναι υπεύθυνα για τη μετάδοση RTP και ποια είναι υπεύθυνα για τη λήψη, χωρίζουμε το αρχείο mstest6.c σε δύο ανεξάρτητα προγράμματα για τον πομπό και τον δέκτη· θα βάλουμε τις κοινές λειτουργίες που χρησιμοποιούν και οι δύο στο τρίτο αρχείο , το οποίο θα ονομάσουμε mstest_common.c, θα συνδεθεί από τον πομπό και τον δέκτη χρησιμοποιώντας την οδηγία περιλαμβάνει:

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

Τώρα το ξεχωριστό αρχείο πομπού:

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

Και τέλος, το αρχείο του δέκτη:

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

Μεταγλωττίζουμε τον πομπό και τον δέκτη και μετά εκκινούμε το καθένα στη δική του κονσόλα. Στη συνέχεια, θα πρέπει να λειτουργεί όπως πριν - μόνο θα πρέπει να εισάγουμε αριθμούς από το 1 έως το 6 στην κονσόλα του πομπού και η απάντηση σε αυτούς θα πρέπει να εμφανίζεται στην κονσόλα του δέκτη. Οι τόνοι πρέπει να ακούγονται στο ηχείο. Εάν όλα είναι έτσι, τότε έχουμε δημιουργήσει μια σύνδεση μεταξύ του δέκτη και του πομπού - υπάρχει μια συνεχής μετάδοση πακέτων RTP από τον πομπό στον δέκτη.

Τώρα είναι η ώρα να εγκαταστήσουμε έναν αναλυτή κίνησης· γι' αυτό θα εγκαταστήσουμε την έκδοση κονσόλας του εξαιρετικού προγράμματος Wireshark - που ονομάζεται TShark. Επέλεξα το TShark για περαιτέρω συζήτηση προκειμένου να διευκολύνω την περιγραφή της διαχείρισης του προγράμματος. Με το Wireshark, θα χρειαζόμουν μια θάλασσα από στιγμιότυπα οθόνης, τα οποία θα μπορούσαν γρήγορα να ξεπεραστούν όταν κυκλοφορήσει μια νέα έκδοση του Wireshark.

Εάν γνωρίζετε πώς να χρησιμοποιείτε το Wireshark, μπορείτε να το χρησιμοποιήσετε για να μελετήσετε τα παραδείγματά μας. Αλλά ακόμα και σε αυτήν την περίπτωση, σας συνιστώ να κατέχετε το TShark, καθώς θα σας βοηθήσει να αυτοματοποιήσετε τη δοκιμή των εφαρμογών VoIP σας, καθώς και να πραγματοποιήσετε απομακρυσμένη λήψη.

Εγκαταστήστε το TShark με την εντολή:

$ sudo apt-get install tshark

Παραδοσιακά, ελέγχουμε το αποτέλεσμα εγκατάστασης ζητώντας την έκδοση του προγράμματος:

$ tshark --version

Εάν ληφθεί επαρκής απάντηση, συνεχίζουμε περαιτέρω.

Επειδή τα πακέτα μας μπαίνουν μόνο μέσα στον υπολογιστή προς το παρόν, μπορούμε να πούμε στον tshark να εμφανίζει μόνο τέτοια πακέτα. Για να το κάνετε αυτό, πρέπει να επιλέξετε τη λήψη πακέτων από τη διεπαφή Loopback (loopback) περνώντας την επιλογή TShark -ilo:

$ sudo tshark -i lo

Μηνύματα σχετικά με πακέτα που αποστέλλονται από τον πομπό μας θα αρχίσουν να εισρέουν αμέσως στην κονσόλα (συνεχώς, ανεξάρτητα από το αν πατήσαμε το κουμπί στο τηλεχειριστήριο ή όχι). Ίσως υπάρχουν προγράμματα στον υπολογιστή σας που στέλνουν πακέτα μέσω τοπικού βρόχου, οπότε θα λάβουμε ένα μείγμα από δικά μας και πακέτα άλλων ατόμων. Για να είμαστε σίγουροι ότι στη λίστα βλέπουμε μόνο πακέτα που αποστέλλονται από το τηλεχειριστήριό μας, θα προσθέσουμε ένα φίλτρο ανά αριθμό θύρας. Πατώντας Ctrl-C σταματάμε τον αναλυτή και εισάγουμε ένα φίλτρο για τον αριθμό θύρας που χρησιμοποιεί το τηλεχειριστήριο ως θύρα προορισμού για τη μετάδοσή του (8010): -f "udp port 8010". Τώρα η γραμμή εντολών μας θα μοιάζει με αυτό:

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

Η ακόλουθη έξοδος θα εμφανιστεί στην κονσόλα (πρώτες 10 γραμμές):

 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

Προς το παρόν, αυτά δεν είναι πακέτα, αλλά μια αριθμημένη λίστα συμβάντων, όπου κάθε γραμμή είναι ένα μήνυμα για το επόμενο πακέτο που παρατηρήθηκε στη διεπαφή. Εφόσον έχουμε ήδη φροντίσει για το φιλτράρισμα πακέτων, βλέπουμε στη λίστα μόνο μηνύματα για πακέτα από τον πομπό μας. Στη συνέχεια, ας αποκρυπτογραφήσουμε αυτόν τον πίνακα με αριθμούς στηλών:

Αριθμός εκδήλωσης.
Ο χρόνος εμφάνισής του.
Η διεύθυνση IP προέλευσης του πακέτου και η διεύθυνση IP προορισμού του πακέτου.
Το πρωτόκολλο του πακέτου εμφανίζεται ως UDP επειδή τα πακέτα RTP αποστέλλονται ως ωφέλιμο φορτίο μέσα στα πακέτα UDP.
Μέγεθος πακέτου σε byte.
Ο αριθμός θύρας προέλευσης του πακέτου και ο αριθμός θύρας προορισμού του πακέτου.
Το μέγεθος του ωφέλιμου φορτίου του πακέτου, από εδώ μπορούμε να συμπεράνουμε ότι ο πομπός μας δημιουργεί πακέτα RTP μεγέθους 172 byte, τα οποία, όπως μια πάπια στο στήθος, βρίσκονται μέσα σε ένα πακέτο UDP μεγέθους 214 bytes.
Τώρα ήρθε η ώρα να κοιτάξουμε μέσα στα πακέτα UDP, για αυτό θα εκκινήσουμε το TShark με ένα διευρυμένο σύνολο κλειδιών:

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

Ως αποτέλεσμα, η έξοδος του προγράμματος θα εμπλουτιστεί - μια αποκρυπτογράφηση των εσωτερικών περιεχομένων του πακέτου που το προκάλεσε θα προστεθεί σε κάθε συμβάν. Για να δείτε καλύτερα την έξοδο, μπορείτε είτε να σταματήσετε το TShark πατώντας Ctrl-C ή να αντιγράψετε την έξοδο του σε ένα αρχείο προσθέτοντας μια διοχέτευση στο πρόγραμμα tee στην εντολή εκτέλεσης, καθορίζοντας το όνομα αρχείου, tee :

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

Τώρα ας δούμε τι έχουμε στο αρχείο, εδώ είναι το πρώτο πακέτο από αυτό:

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                                  ......

Θα αφιερώσουμε το επόμενο άρθρο στην ανάλυση των πληροφοριών που περιέχονται σε αυτήν την καταχώριση και αναπόφευκτα θα μιλήσουμε για την εσωτερική δομή του πακέτου RTP.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο