Khám phá công cụ VoIP Mediastreamer2. Phần 7

Tài liệu của bài viết được lấy từ tài liệu của tôi kênh thiền.

Khám phá công cụ VoIP Mediastreamer2. Phần 7

Sử dụng TShark để phân tích gói RTP

Khám phá công cụ VoIP Mediastreamer2. Phần 7

Cuối cùng Bài viết Chúng tôi đã lắp ráp một mạch điều khiển từ xa từ bộ tạo âm và bộ dò âm, việc liên lạc giữa chúng được thực hiện bằng luồng RTP.

Trong bài viết này chúng ta tiếp tục nghiên cứu việc truyền tín hiệu âm thanh bằng giao thức RTP. Trước tiên, hãy chia ứng dụng thử nghiệm của chúng ta thành bộ phát và bộ thu, đồng thời tìm hiểu cách kiểm tra luồng RTP bằng bộ phân tích lưu lượng mạng.

Vì vậy, để có thể thấy rõ hơn phần tử chương trình nào chịu trách nhiệm truyền RTP và phần tử nào chịu trách nhiệm nhận, chúng tôi chia tệp mstest6.c của mình thành hai chương trình độc lập cho máy phát và máy thu, chúng tôi sẽ đặt các chức năng chung mà cả hai đều sử dụng trong tệp thứ ba mà chúng ta sẽ gọi mstest_common.c, nó sẽ được kết nối bởi bộ phát và bộ thu bằng lệnh 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;
}

Bây giờ là tệp truyền riêng biệt:

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

Và cuối cùng là file nhận:

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

Chúng tôi biên dịch bộ phát và bộ thu, sau đó khởi chạy từng bộ phát trong bảng điều khiển riêng. Sau đó, nó sẽ hoạt động như trước - chỉ chúng ta nên nhập các số từ 1 đến 6 vào bảng điều khiển máy phát và phản hồi cho chúng sẽ xuất hiện trong bảng điều khiển máy thu. Âm thanh phải được nghe thấy trong loa. Nếu mọi thứ đều như vậy thì chúng ta đã thiết lập được kết nối giữa máy thu và máy phát - có sự truyền liên tục các gói RTP từ máy phát đến máy thu.

Bây giờ là lúc cài đặt bộ phân tích lưu lượng truy cập, để làm được điều này, chúng tôi sẽ cài đặt phiên bản console của chương trình Wireshark tuyệt vời - nó được gọi là TShark. Tôi chọn TShark để thảo luận thêm nhằm tạo điều kiện thuận lợi cho việc mô tả quản lý chương trình. Với Wireshark, tôi sẽ cần rất nhiều ảnh chụp màn hình, những ảnh chụp màn hình này có thể nhanh chóng trở nên lỗi thời khi phiên bản Wireshark mới được phát hành.

Nếu bạn biết cách sử dụng Wireshark, bạn có thể sử dụng nó để nghiên cứu các ví dụ của chúng tôi. Nhưng ngay cả trong trường hợp này, tôi khuyên bạn nên thành thạo TShark, vì nó sẽ giúp bạn tự động kiểm tra các ứng dụng VoIP cũng như thực hiện chụp từ xa.

Cài đặt TShark bằng lệnh:

$ sudo apt-get install tshark

Theo truyền thống, chúng tôi kiểm tra kết quả cài đặt bằng cách yêu cầu phiên bản chương trình:

$ tshark --version

Nếu nhận được câu trả lời thỏa đáng, chúng tôi sẽ tiếp tục.

Vì hiện tại các gói của chúng tôi chỉ đi vào bên trong máy tính nên chúng tôi có thể yêu cầu tshark chỉ hiển thị các gói như vậy. Để thực hiện việc này, bạn cần chọn gói chụp từ giao diện loopback (loopback) bằng cách chuyển cho TShark tùy chọn -ilo:

$ sudo tshark -i lo

Thông báo về các gói được gửi bởi bộ phát của chúng tôi sẽ ngay lập tức bắt đầu đổ vào bảng điều khiển (liên tục, bất kể chúng tôi có nhấn nút trên điều khiển từ xa hay không). Có lẽ có những chương trình trên máy tính của bạn cũng gửi các gói thông qua vòng lặp cục bộ, trong trường hợp đó chúng tôi sẽ nhận được hỗn hợp các gói của chúng tôi và của người khác. Để đảm bảo rằng trong danh sách, chúng tôi chỉ thấy các gói được gửi bởi điều khiển từ xa, chúng tôi sẽ thêm bộ lọc theo số cổng. Bằng cách nhấn Ctrl-C, chúng tôi dừng máy phân tích và nhập bộ lọc cho số cổng mà điều khiển từ xa sử dụng làm cổng đích để truyền (8010): -f "cổng udp 8010". Bây giờ dòng lệnh của chúng ta sẽ trông như thế này:

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

Đầu ra sau sẽ xuất hiện trong bảng điều khiển (10 dòng đầu tiên):

 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

Hiện tại, đây không phải là các gói mà là một danh sách các sự kiện được đánh số, trong đó mỗi dòng là một thông báo về gói tiếp theo được nhận thấy trên giao diện. Vì chúng tôi đã xử lý việc lọc gói nên chúng tôi chỉ thấy trong danh sách các thông báo về các gói từ bộ phát của chúng tôi. Tiếp theo, hãy giải mã bảng này theo số cột:

Số sự kiện.
Thời điểm xuất hiện của nó.
Địa chỉ IP nguồn của gói và địa chỉ IP đích của gói.
Giao thức của gói được hiển thị dưới dạng UDP vì các gói RTP được gửi dưới dạng tải trọng bên trong các gói UDP.
Kích thước gói tính bằng byte.
Số cổng nguồn của gói và số cổng đích của gói.
Kích thước của tải trọng gói, từ đây chúng ta có thể kết luận rằng bộ phát của chúng ta tạo ra các gói RTP có kích thước 172 byte, giống như một con vịt trong rương, nằm bên trong gói UDP có kích thước 214 byte.
Bây giờ là lúc xem bên trong các gói UDP, để làm được điều này, chúng tôi sẽ khởi chạy TShark với một bộ khóa mở rộng:

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

Do đó, đầu ra của chương trình sẽ được phong phú hơn - việc giải mã nội dung bên trong của gói gây ra gói đó sẽ được thêm vào mỗi sự kiện. Để nhìn rõ hơn kết quả đầu ra, bạn có thể dừng TShark bằng cách nhấn Ctrl-C hoặc sao chép đầu ra của nó vào một tệp bằng cách thêm một đường dẫn vào chương trình tee vào lệnh chạy, chỉ định tên tệp, tee <filename>:

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

Bây giờ hãy xem những gì chúng ta có trong tệp, đây là gói đầu tiên từ nó:

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

Chúng tôi sẽ dành bài viết tiếp theo để phân tích thông tin có trong danh sách này và chắc chắn sẽ nói về cấu trúc bên trong của gói RTP.

Nguồn: www.habr.com

Thêm một lời nhận xét