![](https://habrastorage.org/files/6ec/214/7a0/6ec2147a08bd49f2aec758f0e15f84e3.jpg)
Недоступный простым смертным мифический ресурс pcet.cisco.com
Читатель, сталкивавшийся с необходимостью заниматься захватом данных PCM на маршрутизаторах Cisco ISR, легко находит исчерпывающее пошаговое руководство по данной теме, но непременно спотыкается о пункт «полученный файл следует отправить в TAC для дальнейшего извлечения звука». Можно ли обойтись без этого?
Предыстория
Телефонную станцию заказчика требовалось подключить как к одному известному SIP-оператору (публичный IP-адрес), так и к SIP-серверу провайдера, предоставлявшего местную связь (частный IP-адрес). К сожалению, станции этой модели не могут одновременно подключаться к SIP-серверам за NAT'ом и к таковым в частной сети, поскольку обход NAT подменой адресов в них включается для всех SIP-транков без исключения. В качестве маршрутизатора клиент использовал Cisco 2811. Поскольку у заказчика в АТС имелась свободная плата потока ISDN PRI, задачу решили приобретением в указанный маршрутизатор платы потока E1 и голосового процессора. Были выполнены соответствующие настройки (везде использовался кодек G.711 A-law) — и все заработало.
Проблема
Ситуация стандартная — все работало замечательно, но в один прекрасный день перестало. Сотрудники заказчика начали обращаться с жалобами на некий сигнал во время разговора, «как будто кто-то кнопку на телефоне нажал». Данный сигнал был довольно быстро идентифицирован как DTMF-тон, который мог прозвучать в начале, середине или конце разговора, а то и несколько раз в продолжение такового. Оставалось выяснить, кто в этом виноват (или что виновато) и как ситуацию исправить.
Следует сказать, что состав используемого оборудования предоставляет впечатляющий набор инструментов для тестирования и отладки. Кроме всего спектра отладочных возможностей Cisco есть весьма полезный инструмент трассировки ISDN в реальном времени, входящий в состав консоли управления используемой АТС. К сожалению, в этом случае ничего не помогало, «поймать» данный сигнал, то есть сопоставить его с отладочной информацией, никак не получалось в силу крайней нерегулярности его появления. Изучение всей информации по данному вопросу привело к ряду документов по захвату PCM на оборудовании Cisco.
Сообщается, что маршрутизаторы серий 2900, 3900, 3900e и шлюзы VG350 позволяют это делать легко и просто, причем в результате получается файл, содержащий голосовые данные, пригодные для непосредственного импорта в аудиоредакторы.
К огромному сожалению, маршрутизатор заказчика серии 2800 не имел чести принадлежать к перечисленным благородным семействам и таковыми возможностями не обладал. В IOS с версиями ниже 15.2(2)T1 (для маршрутизатора заказчика такая версия ПО никогда не будет выпущена, ибо End of Support) для захвата PCM требовалось выполнить ряд команд. Если событие ожидаемое, то можно дождаться его и произвести запись:
voice hpi capture buffer 51200000
voice hpi capture destination flash:pcm.dat
test voice port x/x/x pcm-dump caplog fff duration xxx
Или же можно записывать вообще все подряд до тех пор, пока не произойдет интересующее событие (осторожно, возможно переполнение памяти flash!):
dial-peer voice x voip/pots
pcm-dump caplog fff duration xxx
При этом создается файл с голосовой и отладочной информацией, который может быть извлечен из памяти flash и скопирован на TFTP-сервер.
Еще одна проблема, в каком-то смысле похуже первой
Вот тут началось самое интересное. Многочисленные граждане, озадачившиеся захватом PCM, изливали на форумах технической поддержки свое негодование — мол, я сделал дамп, импортировал его в аудиоредактор и так и эдак, все равно получается какая-то фигня! Что я делаю неправильно? На это другие граждане им отвечали, что ситуация совершенно нормальная, извлечь из оного файла вожделенный звук могут только могущественные специалисты TAC при помощи мифического инструмента PCM Capture Extraction Tool, доступного по ссылке pcet.cisco.com. Думаю, не я один, посмотрев замечательный обучающий видеоролик на Youtube, пытался перейти по данной ссылке и был жестоко унижен, ибо это невозможно для простых смертных. Якобы необходимость вовлечения TAC связана не с техническими, а с правовыми аспектами (интересно, чем в правовом аспекте ISR G2 отличается от ISR?) — дескать, такое декодирование с какой-то точки зрения можно считать незаконным прослушиванием, и вообще декодер меняется слишком часто, версию используемого DSP определить трудно, etc. Удалось найти и мнение, высказанное предположительно от лица нерусскоязычного инженера TAC — мол, мы о механизме не задумываемся, а файл просто загружаем в систему через форму на страничке. Есть ряд ограничений на имя и размер, но в результате получаем .WAV-Файлы, а также некоторую отладочную информацию. В виде распространяемого дистрибутива данный инструмент не существует, и вообще основная трудность — заполучить сами .DAT-Файлы, так что вы, уважаемые заказчики, сии файлы заполучите и перешлите нам, а уж мы извлечем из них все, что надо и не надо.
Однако вовлечь в решение вопроса инженеров TAC в данном случае не получилось — помните, что оборудование в статусе End of Support? Соответственно, никаких сервисных контрактов и никакой поддержки. Правда, инженеры TAC порадовали меня, сообщив, что мое желание самостоятельно решить данную задачу, то есть разобраться в структуре файла и извлечь хранимую там информацию, нарушением чего бы то ни было не является и в случае успеха меня за это ничего пожизненно не лишат. И на том, как говорится, спасибо.
Начал с того, что загрузил один из полученных файлов в шестнадцатеричный редактор. Увиденное не слишком порадовало, хотя там и были знакомые слова («C2800NM-ADVENTERPRISEK9-M, 15.1(4)M10, IP|SLA|IPv6|IS-IS|FIREWALL|VOICE|PLUS|QoS|HA|NAT|MPLS|VPN|LE» и «28.3.14», последнее представляет собой значение DSPWARE VERSION из вывода команды show voice dsp detail). Кроме того, была заметна, так сказать, структура и периодичность. На этом этапе я решил вернуться к рассмотрению команды test voice port:
C2811#test voice port 0/3/0:15.1 pcm-dump ?
caplog Print to caplog, please enable banjo logger
console Print to console, possible flood console
disable Disable the message dump
Во всех найденных описаниях внимание уделяется исключительно параметру caplog. Почему же заслуженную консоль так незаслуженно обошли вниманием? Это следует исправить:
C2811#test voice port 0/3/0:15.1 pcm-dump console ?
<1-7> PCM stream index. Bit0:R_in=0x01 Bit1:S_in=0x02 Bit2:S_out=0x04
Тут все более или менее понятно. Следующий параметр кодирует номера потоков PCM, которые требуется захватить. Поток R_in содержит аудиоданные, передающиеся из VoIP в PSTN. Поток S_in содержит аудиоданные, передающиеся из PSTN в VoIP ДО обработки DSP. Поток S_out содержит аудиоданные, передающиеся из PSTN в VoIP ПОСЛЕ обработки DSP. Номера потоков можно складывать, получая любые желаемые комбинации — можно захватывать любой один, любые два или же сразу все три потока одновременно. Впрочем, не вижу причин, по которым имеет смысл захватывать не все три потока данных сразу – разве что с целью разобраться в структуре поступающей в консоль информации. При этом можно запустить бессрочный захват (не забываем о том, что на карте flash должно быть достаточно свободного места) с последующей ручной остановкой:
test voice port 0/3/0:15.1 pcm-dump console 1
[разговор]
test voice port 0/3/0:15.1 pcm-dump disable
Можно сразу указать требуемую продолжительность в секундах:
C2811#test voice port 0/3/0:15.1 pcm-dump console 1 duration ?
<0-255> capturing time in sec
Захват трех каналов в консоли выглядит примерно так:
047581: Jan 19 11:52:15.491: len=172, ch_id=1, pak_id=143, proc_id=0, <== Payload: 00 07 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
047582: Jan 19 11:52:15.495: len=172, ch_id=1, pak_id=143, proc_id=0, <== Payload: 00 07 00 00 00 07 00 09 00 0E 00 0E 00 0D 00 0D 00 03 FF FC 00 00 00 04 00 02 00 00 FF F9 00 03 00 0B 00 04 00 0B 00 0C 00 01 00 00 00 01 FF FE 00 06 00 02 00 03 00 07 00 06 00 00 00 05 FF FD 00 02 00 07 00 03 00 04 00 08 FF FF 00 02 00 04 FF FF FF F8 FF F5 FF F3 FF FA FF F8 FF F1 FF F1 FF F2 FF F4 FF EF FF EF FF EE FF F0 FF F8 FF FB FF F4 FF F3 FF FA FF F4 FF F2 FF F8 FF FF 00 04 FF FB FF F5 FF F1 FF FC FF FD FF FE FF F6 FF F3 FF F2 FF ED FF EA FF EB FF F7 FF F5 FF F2 FF F2 FF F6 FF FC
047583: Jan 19 11:52:15.499: len=172, ch_id=1, pak_id=143, proc_id=0, <== Payload: 00 07 00 01 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08
[и еще очень много похожих сообщений]
Захват одного канала:
027761: Jan 15 00:59:53.549: len=172, ch_id=1, pak_id=143, proc_id=0, <== Payload: 00 07 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00
[и еще очень много похожих сообщений]
Выполним следующие команды:
test voice port 0/3/0:15.1 pcm-dump con 1 duration 10
test voice port 0/3/0:15.1 pcm-dump dis
test voice port 0/3/0:15.1 pcm-dump con 2 duration 10
test voice port 0/3/0:15.1 pcm-dump dis
test voice port 0/3/0:15.1 pcm-dump con 4 duration 10
test voice port 0/3/0:15.1 pcm-dump dis
test voice port 0/3/0:15.1 pcm-dump con 7 duration 10
test voice port 0/3/0:15.1 pcm-dump dis
Вернемся к рассмотрению структуры полученных текстовых сообщений. Файл для R_in:
047648: Jan 19 12:10:48.525: len=172, ch_id=1, pak_id=143, proc_id=0, <== Payload: 00 07 00 00 00 18 00 28 00 28 00 18 00 28 00 28 00 18 00 28 00 18 00 18 00 18 00 18 00 08 00 18 00 18 00 08 00 18 00 08 FF F8 00 08 FF F8 FF E8 FF E8 FF E8 FF E8 FF E8 FF E8 FF E8 FF E8 FF D8 FF D8 FF E8 FF E8 FF E8 FF E8 FF D8 FF E8 FF E8 FF E8 FF E8 FF E8 FF F8 00 08 00 08 FF F8 FF F8 FF F8 FF F8 FF F8 FF F8 FF F8 FF F8 00 08 00 08 FF F8 00 08 00 08 00 08 00 08 00 18 00 08 00 18 00 18 00 18 00 18 00 18 00 18 00 08 00 18 00 18 00 18 00 18 00 08 00 08 00 08 00 08 00 18 00 18 FF F8 00 08
[...]
048647: Jan 19 12:10:58.517: len=172, ch_id=1, pak_id=143, proc_id=0, <== Payload: 00 07 00 00 00 08 00 08 FF E8 FF F8 FF E8 FF F8 FF F8 FF E8 FF E8 FF E8 FF E8 FF F8 FF E8 00 08 FF F8 FF F8 FF F8 FF F8 00 18 FF F8 00 08 00 08 FF F8 FF F8 FF E8 FF F8 FF E8 FF F8 FF E8 FF F8 FF F8 FF F8 FF F8 FF E8 FF D8 FF F8 FF E8 FF E8 FF E8 FF E8 FF F8 FF F8 FF D8 FF E8 FF E8 FF F8 FF E8 FF F8 FF F8 FF E8 FF F8 FF F8 FF E8 FF E8 FF F8 FF E8 FF E8 00 08 FF F8 FF F8 FF F8 FF F8 FF F8 FF E8 FF E8 FF F8 FF E8 00 08 FF F8 00 18 00 08 00 08 FF E8 00 08 00 08 00 08 00 08 00 18 00 08 00 08
Шестизначное число перед датой представляет собой уникальный идентификатор, всего у нас присутствует ровно одна тысяча сообщений с номерами от 047648 до 048647 включительно.
Файл для S_in:
050234: Jan 19 12:12:58.636: len=172, ch_id=1, pak_id=143, proc_id=0, <== Payload: 00 07 00 01 FF E8 FD 90 FE F8 FF B8 02 B0 01 C8 FF 48 03 D0 FE 98 08 40 FD F0 FD 70 FF D8 FD D0 00 C8 FC D0 04 E0 FA E0 02 10 04 A0 F9 E0 02 90 FD F0 FE A8 00 B8 02 50 02 70 FE E8 02 90 FD 90 00 F8 04 60 FA 60 00 A8 FC D0 02 30 01 E8 FD D0 03 50 FA 20 FF D8 01 78 FF 28 03 90 01 08 FB E0 01 78 FF 98 FE C8 FF A8 FF 38 06 A0 FA A0 00 A8 04 A0 FD 10 00 E8 FE 08 00 B8 FE D8 FD B0 01 98 01 78 01 D8 00 38 FF C8 FF 08 00 28 01 58 FE B8 01 D8 FE E8 FE 08 01 C8 FF 68 FF A8 00 08 00 38 FF 28 FE 48
[...]
051233: Jan 19 12:13:08.624: len=172, ch_id=1, pak_id=143, proc_id=0, <== Payload: 00 07 00 01 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 18 00 08 00 08 00 08 00 18 00 18 00 18 00 18 00 18 00 18 00 08 00 08 FF F8 FF F8 FF F8 FF E8 FF E8 FF E8 FF D8 FF E8 FF D8 FF D8 FF D8 FF E8 FF E8 FF E8 FF F8 FF F8 00 08 00 18 00 08 00 08 00 18 00 18 00 18 00 18 00 18 00 08 00 08 00 08 00 08 00 08 FF F8 00 08 FF F8 00 08 00 08 00 08 FF F8 FF F8 FF F8 FF F8 00 08 00 08 00 08 00 08 FF F8 00 08 00 08 00 08 00 18 00 08 00 18 00 18 FF F8 FF F8 FF F8 FF F8 FF F8 FF E8 FF E8
Тоже одна тысяча сообщений ровно.
Файл для S_out:
051234: Jan 19 12:14:00.396: len=172, ch_id=1, pak_id=143, proc_id=0, <== Payload: 00 07 00 02 00 06 FF F6 00 76 00 F6 00 36 FF C6 00 68 FF F8 FF 88 00 78 FF A8 FE B8 00 18 00 98 FF 98 FF C8 01 38 FF 98 FF 18 01 58 00 58 FE F6 FF A8 01 A8 00 E8 00 38 00 18 FF 08 FE 56 FF 06 00 A6 00 86 00 E6 FF 86 FF 58 FF D8 00 18 00 86 00 26 00 66 FF F6 FF E6 FF 56 00 56 00 76 FE F6 FF E6 00 16 FF C6 FF B6 FF C6 00 86 FF B6 FF 76 FF F6 00 A6 00 36 FF E6 00 88 00 08 00 38 FF E8 FF F6 00 18 00 3A 00 8A 00 2A FF BA FF 5A FF D8 00 18 00 58 00 5A FF 7A FF AA FF E8 00 18 00 18 FF C8 FF F8
[...]
052233: Jan 19 12:14:10.388: len=172, ch_id=1, pak_id=143, proc_id=0, <== Payload: 00 07 00 02 00 C0 01 8C FD 52 01 00 01 C2 FE E8 FE BE 01 3E 01 60 FF 4E FE 56 FE 44 02 0E 02 14 FF E8 FF 46 FF B4 FE F6 01 64 00 C2 FC D2 03 84 00 84 FC BC FD E8 01 5E 04 66 FD B6 FE 62 00 60 FF C6 01 6E 00 50 FE AA FE AC 01 3C 02 2C FC E8 00 16 01 3E 00 38 00 EC FF 26 FF 40 00 26 01 30 00 22 00 20 FF 74 FE 9C 01 A8 01 16 FF A8 FD EE FF 84 01 E0 FE 9C 00 64 FF A4 00 7A 01 96 FE BC FF 44 00 86 FF E0 FE F8 01 8A FF 22 FF 00 02 12 00 5A FE 70 00 18 00 B0 FE D6 01 B6 FE 1A FF E6 02 6E FE 22
Одна тысяча сообщений ровно.
Файл для всех потоков:
052234: Jan 19 12:15:02.576: len=172, ch_id=1, pak_id=143, proc_id=0, <== Payload: 00 07 00 00 00 08 00 38 00 78 00 B8 00 18 00 18 FF F8 FF C8 FF F8 FF E8 FF B8 FF 58 00 08 FF 98 FF A8 00 58 00 48 00 08 FF 88 FF C8 00 28 00 48 00 68 00 98 00 68 00 A8 00 B8 00 18 FF 98 FF 68 FF C8 FF A8 FF 88 FF 78 FF 58 00 38 FF C8 FF 78 00 48 00 48 00 38 00 88 01 78 00 78 FF D8 00 38 FF A8 FF 78 FF E8 00 28 FF A8 FF 98 00 88 00 A8 00 18 FF 88 FF E8 00 78 FF E8 FF B8 00 58 00 58 FF 48 FF C8 00 48 00 08 FF C8 00 38 00 78 FF D8 00 28 00 68 00 18 FF B8 FF 78 FF C8 00 68 00 38 FF D8 FF A8
[...]
055211: Jan 19 12:15:12.516: len=172, ch_id=1, pak_id=143, proc_id=0, <== Payload: 00 07 00 01 FF A8 01 28 FE 28 00 18 FF 78 FE E8 01 A8 00 08 02 10 00 48 FE C8 00 28 00 98 00 38 FF 68 FF 18 FE C8 00 A8 FF 68 01 98 00 08 FF C8 01 18 00 B8 FF E8 FF 08 FF F8 FE 18 00 28 FF 68 FE F8 00 E8 00 78 00 F8 00 78 FE E8 00 78 FE D8 00 48 01 A8 00 E8 00 B8 FF F8 FF C8 FF 58 FF 08 00 C8 FF B8 FF 58 01 D8 FE 78 00 08 FF 38 FE 28 FF E8 00 C8 FF 88 01 48 00 D8 00 68 00 08 FF 68 FF D8 00 68 FF B8 FF 58 00 78 00 08 00 68 FF A8 01 18 FE 58 00 18 00 28 FF E8 00 F8 00 28 01 08 00 B8 FE E8
055212: Jan 19 12:15:12.520: len=172, ch_id=1, pak_id=143, proc_id=0, <== Payload: 00 07 00 02 FF AC 01
055234: Jan 19 12:15:32.851: %ISDN-6-DISCONNECT: Interface Serial0/3/0:0 disconnected from 2606699 , call lasted 372 seconds
Тут картина несколько отличается. Очевидно, что для трех потоков сообщений будет в три раза больше, чем для одного. Однако во взятом для примера логе полных сообщений всего-навсего 2978 штук, плюс одно обрезанное в самом конце. Некоторые источники указывают на то, что в случаях, когда диагностические сообщения поступают в буфер быстрее, чем маршрутизатор может выводить их в лог, некоторые сообщения могу теряться целиком или частично.
Можно обратить внимание, что в каждом сообщении данные начинаются одной и той же последовательностью байтов (R_in: 00 07 00 00, S_in: 00 07 00 01, S_out: 00 07 00 02; файл для всех потоков содержит сообщения со всеми указанными последовательностями).
Очевидно, эти четыре байта представляют собой некий служебный заголовок, который при рассмотрении можно смело не принимать в расчет, поскольку он просто указывает на поток, которому принадлежит сообщение. Данное предположение неплохо согласуется с тем, что для десятисекундного интервала в лог попадает тысяча сообщений со 160 байтами полезной информации каждое, то есть 160000 байтов полезной информации. При частоте дискретизации 8000 Герц, временном интервале 10 секунд и при глубине дискретизации 16 бит для одного звукового канала (моно) получается именно такой объем данных.
Первая грубая обработка
При помощи текстового редактора, поддерживающего регулярные выржения, и регулярного выражения (......:).*(<== Payload: 00 07 00 01 ) удаляю строки (050234: Jan 19 12:12:58.636: len=172, ch_id=1, pak_id=143, proc_id=0, <== Payload: 00 07 00 01 ) из соответствующего файла. Происходит ровно 1000 замен. После преобразования ASCII данных в двоичные в шестнадцатеричном редакторе (вставка в файл буфера ASCII-HEX) получаем файл размером ровно 160000 байтов.
Необходимо отметить, что при этом происходит следующее. В лог маршрутизатора данные попадают в том же порядке, в котором они хранятся в его памяти (порядок big-endian). То есть, например, значение 0x1234 хранится в двух последовательных ячейках: 12 34. В таком виде мы и копируем текст в буфер, в том же порядке байты попадают в двоичный файл.
Посмею напомнить читателю, что в памяти персональных компьютеров с x86-процессорами принят порядок записи от младшего байта к старшему (англ. little-endian, дословно: «остроконечный»), в связи с чем иногда его называют интеловский порядок (по названию фирмы-создателя архитектуры x86).
В то же время стандартным для протоколов TCP/IP является байтовый порядок от старшего к младшему (англ. big-endian, дословно: «тупоконечный»). Он используется в заголовках пакетов данных и во многих протоколах более высокого уровня, разработанных для использования поверх TCP/IP. Поэтому порядок байтов от старшего к младшему часто называют сетевым порядком байтов (англ. network byte order). Этот порядок байтов используется не только процессорами IBM 360/370/390, Motorola 68000, SPARC (отсюда третье название — порядок байтов Motorola, Motorola byte order), но и процессорами некоторых маршрутизаторов Cisco.
Далее производится импорт полученного RAW-файл в аудиоредактор. В итоге удается получить корректный звуковой файл при указании следующих параметров: Signed 16 bit, Big-endian, 1 канал (моно), частота 8000 Гц.
Все слышно!
Это, безусловно, первый маленький успех. Его следует развить — попробовать разобраться со структурой .DAT файла, после чего всяк желающий сможет написать на своем любимом языке программирования собственную программу, которая будет извлекать из данного файла звук.
При ближайшем рассмотрении файла обнаружил, что в файле также чередуются последовательности 00 07 00 0X, смещенные на F4 байтов друг относительно друга. Двухбайтовое слово со значением 00 F4 (big-endian, не забываем это) присутствует во всех случаях со смещением -0x0E относительно последовательности 00 07 00 0X. Можно предположить, что оно относится к заголовку пакета данных и указывает размер текущего пакета (смещение заголовка следующего пакета по отношению к заголовку текущего пакета).
Рассмотрев последний голосовой пакет в файле, можно заметить, что за F4 байтов от конца файла присутствует последовательность 00 00 00 00. Предположим, что она обозначает начало пакета. Таким образом, слово со значением 00 F4 находится по смещению 0x42 от начала голосового пакета. В то же время для каждого голосового пакета в файле между последовательностями 00 07 00 0X и 00 00 00 00 помещается ровно 0xA0, то есть 160 байтов информации, что неплохо согласуется с зависимостями, установленными в ходе анализа текстового лога. Можно предположить, что это 160 байт полезной нагрузки — голосовых данных. На иллюстрациях я выделяю данные байты пурпурным цветом, дабы указать на их важность.
Проанализируем заголовки:
![](https://habrastorage.org/files/548/78a/782/54878a7820a1435985401253969275c6.jpg)
Голубым выделено предполагаемое положение идентификаторов len, ch_id, pak_id, proc_id (Вспомним текстовый лог: 792308: Jan 22 16:30:42.458: len=172, ch_id=1, pak_id=143, proc_id=0, <== Payload:), оранжевое значение в проанализированных пакетах совпадает с ch_id.
Значение 172=0xAC, расположенное по смещению 0x48, соответствует размеру порции данных начиная от этого смещения и до конца пакета.
Однако у нас остается не идентифицированный массив байтов, содержащих служебную информацию, в самом начале файла. Необходимо выявить закономерности, иначе решение нельзя считать приемлемым.
![](https://habrastorage.org/files/71a/44f/52e/71a44f52ecda427a81971fa710cb7018.jpg)
Обнаруживаются две завершающиеся нулем строки, содержащие текстовую информацию о маршрутизаторе (на рисунке выделены желто-зеленым цветом):
1) текст 28.3.14 (соответствует DSPWARE VERSION из вывода команды show voice dsp detail) находится со смещением 0x5С от начала файла;
2) текст C2800NM-ADVENTERPRISEK9-M, 15.1(4)M10, IP|SLA|IPv6|IS-IS|FIREWALL|VOICE|PLUS |QoS|HA|NAT|MPLS|VPN|LE) находится со смещением 0xC0 от начала файла.
Можно также попытаться отыскать в данном файле какие-то частные значения, относящиеся к конкретному маршрутизатору (типа MAC'ов и серийных номеров) или пакету (CRC). Пока же не следует углубляться в изыскания, поскольку эти данные для нас особого интереса не представляют.
По смещению 0x42 в файле содержится слово 01 24, а по смещению 0x0124 начинается первый голосовой пакет. Это могло бы быть совпадением, однако проверка на ряде файлов позволяет сделать вывод, что закономерность подтверждается для всех файлов и пакетов в них. Это соблюдается и для неизвестного назначения и содержания пакетов, явно не содержащих голос.
Итак, структура голосового пакета:
packet_sign [0x00] 0x04 байта всегда 00 00 00 00
UNKNOWN [0x04] 0x3E байтов
packet_size [0x42] 0x02 байта
UNKNOWN [0x44] 0x04 байта всегда 00 00 00 00
len [0x48] 0x02 байта
ch_id [0x4A] 0x02 байта
pak_id [0x4C] 0x02 байта
proc_id [0x4E] 0x02 байта
UNKNOWN [0x50] 0x02 байта всегда 00 07
stream_id [0x52] 0x02 байта R_in 00 00
S_in 00 01
S_out 00 02
raw_data [0x54] 0xA0 байтов
Вооружившись изложенным, всяк желающий может самостоятельно на своем любимом языке программирования написать программу, формирующую из DAT-файла файлы, содержащие голос — хоть RAW для последующего импорта в аудиоредактор и обработки, хоть сразу WAV.
Итог
Разумеется, это лишь частное решение, не претендующее на полноту. Например, при использовании кодеков, отличных от G.711, идентифицировать и распаковать звуковые данные будет существенно сложнее. Наверняка проявятся и другие побочные эффекты. По этим причинам я не публикую код, при помощи которого достигалась автоматизация обработки входных данных.
Данные предположения позволили без проблем разделить по каналам файл с данными PCM, полученный на диалпире и содержащий несколько одновременных разговоров с различными ch_id. Благодаря этому удалось отследить и идентифицировать проблемный DTMF, который был всему причиной.
К тому же маршрутизатору, как выяснилось, был подключен по SIP китайский GSM-шлюз. Абоненты АТС слышали тоны только при звонках через данный шлюз. Удалось выявить, что шлюз, на котором включен RFC2833, передает странные (не вполне понятно, откуда они берутся — то ли генерируются самим шлюзом, то ли приходят из сети оператора, но точно не от удаленного абонента) пакеты, которые маршрутизатор (на котором, в свою очередь, тоже включен RFC2833) воспринимает как RTP NTE, после чего отправляет в поток полноценный тон, который слышат абоненты АТС и для обнаружения которого были проделаны все те манипуляции, которые легли в основу данного материала.
Поскольку шлюз в силу своей предельной дешевизны не позволяет выполнять сбор отладочной информации и установить степень его вины не представлялось возможным, было на всякий случай произведено обновление прошивки шлюза до актуальной версии. Это не помогло, сигнал продолжал появляться. Далее было произведено переключение шлюза в режим SIP INFO (на маршрутизаторах Cisco ISR данный режим всегда включен). Пока жалобы отсутствуют.
Поделиться с друзьями
Комментарии (6)
lostpassword
28.02.2017 19:57В голове не укладывается, как же получилось распознать структуру и выдернуть звук… Это очень круто, по-моему!)
Anton1114
06.03.2017 12:38+1На самом деле, публикуя заметку, опасался получить комментарий вроде: «уже давно существуют инструменты, позволяющие распознать внутреннюю структуру произвольного файла, зачем было изобретать велосипед?»
Мне о таких инструментах ничего не известно, но кто знает, что там уже успели изобрести? ;)
Debug_all
Подход, конечно, интересный, но не логичнее было бы сначала использовать встроенный в ISR еще с IOS 12.X monitor capture на SIP call-leg'е?
Если на dial-peer статически задать dtmf-relay rtp-nte без дополнительных вариантов, то не так уж и всегда.На выходе получили бы pcap файс с дампом трафика, Wireshark отлично декодит RTP из него во всех основных кодеках.
Да и само появление DTMF-тонов можно было бы отследить через debug voice rtp session named-event и debug voice ccapi inout, не прибегая сразу к такому глубокому анализу.
Anton1114
Здравствуйте! Хочу поблагодарить за высказанное мнение. Перечисленные вами инструменты мне известны и в ходе работ по данному инциденту применялись. Wireshark действительно может извлечь звук из дампа, вот только тон мы там не услышим. По крайней мере лично я до сих пор так и не научился отличать RTP-NTE и SIP INFO на слух.
появление DTMF-тонов можно было бы отследить через debug voice rtp session named-event и debug voice ccapi inout
Да, появление тонов так можно отследить. Но в отрыве от контекста разговора мы не сможем сказать, правомерно ли появился данный тон (например, целенаправленный донабор добавочного номера или случайное нажатие клавиши удаленным абонентом) или же нет. Возможно, в заметке я уделил недостаточно внимания использованию данных инструментов, но я и не стремился сфокусироваться на них. О них и так в доступе масса информации, чего не скажешь об экстракции звука из дампов PCM.
Если на dial-peer статически задать dtmf-relay rtp-nte без дополнительных вариантов, то не так уж и всегда.
Здесь я лишь озвучил точку зрения производителя:
The sip-info method is available only on SIP dial peers. This is an out-of-band DTMF relay mechanism that registers the DTMF signals using SIP-Subscribe messages and transports the DTMF signals using SIP-Info messages. The body of the SIP message consists of signaling information and uses the content-type application/dtmf-relay. The method is always enabled for SIP dial peers, and is invoked when a SIP INFO message is received with DTMF relay content.
Debug_all
Вам также спасибо за развернутый ответ.
Зависит от конкретного случая, оборудования и настроек. RFC2833 описывает возможность как оставлять оригинальный тон в аудио-потоке, наряду с генерацией NTE-пайлода, так и удалять его (в Cisco реализовано оба варианта: dtmf-relay rtp-nte [digit-strip]). Из RFC:Так что тон мог быть в записанном голосе (или даже только в нем как in-band DTMF, без relay вовсе), и подобная проблема сменой DTMF-relay механизма уже не решалась бы.
В Wireshark они были бы видны как пакеты с полной структурой сообщения внутри.
Декодированный RTP-NTE, к примеру (нажата цифра 8):
Очевидно, анализ имеет смысл производить только на вызове с воспроизведенной проблемой и уточненными данными со стороны абонентов (в какое время тестового звонка возник тон, не нажимались ли клавиши и т.д.).
Ничуть не умаляю проделанной работы, за анализ и статью спасибо, было интересно почитать. :)
Вопрос только зачем было это делать в контексте возникшей проблемы?
В схеме [Абонент1]---[АТС]--E1--[Cisco GW]--SIP--(Провайдер)--(ТфОП)--[Абонент2] начинать сбор основной информации на участке АТС-Cisco, как мне показалось, немного нелогично. Дебаги и стандартные средства показали бы, что в тестовом вызове DTMF приходил на call-leg'е провайдера, и дальнейший источник проблемы, и ее фикс стоит искать в той стороне. К чему Вы, собственно, и пришли:
Anton1114
Вопрос только зачем было это делать в контексте возникшей проблемы?
Проверялись все участки прохождения вызова, в том числе и поток. Натолкнувшись в процессе на дамп PCM, самостоятельный анализ которого заявлялся категорически невозможным, я просто не смог пройти мимо. Первоначальную проблему можно было решить и без этого, согласен, но тогда одним нераскрытым секретом было бы больше. ;) Видимо, мне следовало иначе скомпоновать текст, оторвать предмет описания от контекста, в котором я начал разбирать формат дампа. Тогда бы у Вас не возник вопрос, который таки возник.