How to use scapy to analyze sip/rtp packet?
I am trying to write a python script using scapy(not pyshark) library to live capture the packet and extract the information. Any idea how to use scapy(jython/python) to analyze sip/rtp packet?
To make it more clearly, I had a sample code to analyse the rtp packet which use Pyshark as following:
cap = pyshark.LiveCapture(interface=’en0′, display_filter=’sip’)
Can this code be transferred to Scapy instead of Pyshark with the same function?
1 Answer 1
Kenneth, To start with you need some packet capture — you can do this in scapy — or using Wireshark etc. I now assume that you have a file in PCAP (or PCAPNG) format. I have also assumed that in Wireshark you are not filtering — but recording everything.
So inside Scapy you will write something like this.
But I can not see a Layer definition for SIP inside Scapy — which means you will need to develop it yourself.
bbc/rd-apmm-python-lib-rtp
This python library provides a means to decode, encode, and interact with RTP packets. It is intended to be used together with other libraries that decode, encode, and interact with the payload bitstreams. This library does not provide any network functionality.
We desire that contributors of pull requests have signed, and submitted via email, a Contributor Licence Agreement (CLA), which is based on the Apache CLA.
The purpose of this agreement is to clearly define the terms under which intellectual property has been contributed to the BBC and thereby allow us to defend the project should there be a legal dispute regarding the software at some future time.
If you haven’t signed and emailed the agreement yet then the project owners will contact you using the contact info with the pull request.
Захват видео с сетевых камер, часть 2
В первой своей статье «измерение расстояния до объекта и его скорости» я рассмотрел захват изображений с веб-камер через Video4Linux2 и через DirectX. В следующей статье «захват видео с сетевых камер, часть 1» я рассмотрел как работать с сетевыми Motion-JPEG камерами. Сейчас я поведаю Вам о захвате изображений с сетевых RTSP камер, в частности поток Motion-JPEG по RTSP.
Задача эта более сложная нежели Motion-JPEG по HTTP, так как необходимо больше действий, больше подключений, но взамен мы получаем большую гибкость, скорость, функциональность и даже некую универсальность. Честно говоря, RTSP для простых задач избыточен, но я не сомневаюсь, что найдутся ситуации, где он будет необходим.
Что такое RTSP
- OPTIONS — возвращает список поддерживаемых методов (OPTIONS, DESCRIBE и т.д.);
- DESCRIBE — запрос описания контента, описывает каждый трек в формате SDP;
- SETUP — запрос установки соединений и транспорта для потоков;
- PLAY — старт вещания;
- TEARDOWN — остановка вещания.
Рабочей лошадкой является другой протокол: RTP — Real-time Transport Protocol — транспортный протокол реального времени. С его помощью и передаются нужные нам данные. Стоит отметить, что с этим протоколом очень даже приятно работать, дело в том, что он облегчает клиентскому ПО восстановление данных после их фрагментации на канальном уровне. А также несёт в себе ещё несколько полезных полей: формат передаваемых данных, временную метку и поле синхронизации (если передаётся, например, одновременно аудио и видео). Хотя этот протокол может работать по TCP, его обычно используют с UDP из-за его ориентированности на скорость. То есть RTP данные это UDP датаграмма с заголовком и полезными данными медиа-контента (payload).
Казалось бы нам больше ничего и не нужно. Подключаемся по RTSP, забираем по RTP. Но не тут-то было, умные дяди придумали третий протокол: RTCP — Real-time Transport Control Protocol — протокол контроля за транспортом в реальном времени. Этот протокол служит для определения качества сервиса, с его помощью клиент и сервер знают как хорошо или плохо идёт передача контента. В соответствии с этими данными сервер, например, может понизить битрейт или вообще перейти на другой кодек.
Принято, что RTP использует чётный номер порта, а RTCP следующий нечётный.
Пример общения по RTSP
У меня только один источник RTSP потока — камера eVidence APIX Box M1, поэтому все примеры относятся к ней.
Ниже лог общения между плеером VLC (он правда мне очень помогает в моих исследованиях) и этой камерой. Первый запрос от VLC на порт 554 камеры. Ответ через пустую строку и начинается с «RTSP/1.0».
Первым делом VLC спрашивает камеру:
— А что я вообще могу с тобой делать? (OPTIONS)
— И тебе привет. А можешь ты меня просить сделать любое из OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY и PAUSE.
— Ладно, тогда скажи мне что у тебя есть по запросу «/jpeg»? (DESCRIBE)
— Тут у меня видео первой дорожкой идёт, M-JPEG, а второй дорожкой идёт аудио простое.
— Интересно глянуть на видео, первую дорожку, отсыпь мне его, пожалуйста в карман номер 41760, а шелуху всякую можешь в карман номер 41761 скидывать. (SETUP track1)
— ОК, по твоей команде…
— И звук тоже хочу послушать, сыпь в 7878, 7879 карманы. (SETUP track2)
— Да без проблем.
— Ну, посыпали. (PLAY)
Через некоторое время:
— Ладно, хватит, насмотрелся. (TEARDOWN)
— Как скажешь.
На этом небольшое лирическое отступление заканчивается. В первом запросе » OPTIONS rtsp://192.168.0.254/jpeg RTSP/1.0 » напоминает » GET /jpeg HTTP/1.1 » в том смысле, что с этого начинается разговор, а так у протокола HTTP тоже есть метод OPTIONS. Здесь 192.168.0.254 — это IP адрес моей камеры. CSeq отражает порядковый номер запроса, ответ от сервера должен содержать тот же самый CSeq .
А ответ от сервера начинается с » RTSP/1.0 200 OK «, это прямо как » HTTP/1.1 200 OK » — знак, что всё хорошо: запрос принят, запрос понятен и не было никаких проблем в его реализации. И прямым текстом следует перечисление всех доступных методов.
Далее мы собираем информацию о том, что нас ждёт по запросу /jpeg, ведь мы именно за ним и пришли по ссылке » rtsp://192.168.0.254/jpeg «. Также указываем, что хотим получить ответ в виде SDP (строка 12).
В ответ нам приходит RTSP заголовок с указанием Content-Type и Content-Length , а после заголовка через пустую строку непосредственно сам контент в формате SDP:
Здесь всё достаточно очевидно. Нужны нам следующие строки:
Если мы хотим получать только видео, то из аудио данных мы игнорируем всё, кроме названия трека. Он нам нужен, чтобы настроить поток, но нас никто не заставляет этот поток принимать, однако камера отказывается работать, если игнорировать аудио полностью (если делать SETUP только для видео трека).
Честно говоря, я не знаю как будут реагировать разные камеры, если пренебрегать номером порта для аудио потока (7878), ведь мы его указываем с командой SETUP .
Далее идут два запроса SETUP , c указанием портов, на которые мы бы хотели принимать видео и аудио потоки. Первое число — порт для RTP, второе — для RTCP. В ответе камеры содержится информация о портах, с ними можно сверяться, чтобы удостовериться, что всё настроено правильно. Ещё нам необходимо запомнить идентификатор Session . Мы должны будем указывать его во всех последующих вызовах.
После команды PLAY начнётся передача видео на порт 41760 и аудио на порт 7878. И по команде TEARDOWN вещание прекращается, соединение разрывается.
MJPEG over RTP
- V (Version): (2) версия протокола. Сейчас номер версии 2.
- P (Padding, Дополнение): (1) используется в случаях, когда RTP-пакет дополняется пустыми байтами в конце, например для алгоритмов шифрования.
- X (Extension, Расширение): (1) указывает на наличие расширенного заголовка, определяется приложением. В нашем случае это не используется.
- CC (CSRC Count): (4) содержит количество CSRC-идентификаторов. Нами тоже не используется.
- M (Marker): (1) используется на уровне приложения, в нашем случае этот бит выставляется в единицу, если RTP пакет содержит окончание JPEG кадра.
- PT (Payload Type): (7) указывает формат полезной нагрузки — передаваемых данных. Для MJPEG это 26.
- Sequence Number: (16) номер RTP пакета, используется для обнаружения потерянных пакетов.
- Timestamp (32): временная метка, в нашем случае 90000 герцовая (90000 = 1 секунда).
- SSRC (Synchronization Source): (32) идентификатор синхронизатора, как смешно бы это не звучало. Определяет источник потока.
- CSRC (Contributing Source): (32) идентификаторы дополнительных источников, используется когда у нас поток идёт с нескольких мест.
- Extension Header ID: (16) идентификатор расширения, если оно у нас есть надо знать что оно из себя представляет. В нашем случае не используется.
- Extension Header Length: (16) длинна этого заголовка в байтах.
- Extension Header (Заголовок Расширения): сам заголовок. Содержимое может быть самым разным, зависит от контекста.
- Payload (Нагрузка): полезные данные — те самые наши JPEG кадры. Фрагментированные, конечно.
Переносимся на один уровень инкапсуляции выше. Теперь стоит задача преобразовать получаемые видео данные в полноценное JPEG изображение. В случае MJPEG по HTTP всё просто — вырезаем кусок потока и работаем с ним сразу как с JPEG изображением. В случае же RTP изображение передаётся не полностью, JPEG заголовок опускается для экономии трафика. Его необходимо восстановить самостоятельно из прилагаемых данных.
- Type-specific (Зависит от типа): (8) смысл поля зависит от реализации, в нашем случае не применяется.
- Fragment Offset (Смещение фрагмента): (24) указывает на положение текущего фрагмента кадра во всём кадре.
- Type (Тип): (8) от типа зависит как восстанавливается изображение.
- Q (Quality): (8) качество изображения.
- Width: (8) ширина кадра.
- Height: (8) и высота.
- Restart Marker header (Заголовок маркеров RST): (32) используется при декодировании JPEG, если применяются RST маркеры. Не знаю используют их камеры или нет, но я этот заголовок игнорирую. Это поле появляется только при Type от 64 до 127.
- Quantization Table Data (Таблицы квантинизации): если они присутствуют, то не нужно их отдельно вычислять. А нужны они для правильного воссоздания картинки из JPEG данных. Если эти таблицы не правильные, то изображение будет с неправильными цветами и контрастами. Таблиц должно быть две: Luma и Chroma для яркости и цветности соответственно.
- MBZ, Precision, Length: (32) параметры таблиц квантинизации, я их игнорирую, Length задаю равным 128 — две таблицы по 64 байт. В ином случае я не знаю как с ними работать.
RTCP пакет содержит в себе некоторое подмножество, он бывает четырёх типов: 201 — отчёт источника, 202 — отчёт получателя, 203 — описание источников и 204 — назначение определяется приложением. Мы должны принимать в первую очередь 201 тип, затем отправлять 202 тип. 203 и 204 необязательны, но я их тоже учитываю. В одном UDP пакете может быть несколько RTCP пакетов.
- Version: (2) версия RTP.
- Padding: (1) то же самое, что и для RTP.
- SC or RC or Subtype: (5) в зависимости от типа может быть количеством источников (Sources Count) или количеством получателей (Receivers Count) включенных в отчёт получателя и источника соответсвенно. Если это APP пакет, то это поле определяет подтип такого пакета.
- Packet Type: (8) тип пакета, 201 — отчёт источника (Sender’s Report SS), 202 — отчёт получателя (Receiver’s Report RR), 203 — описание источников (Source Description SDES) и 204 — назначение определяется приложением (APP).
- Length: (16) размер следующих за заголовком данных, измеряется в 32 битных единицах.
На этом введение заканчивается.
Python MJPEG over RTSP client
Вот мы и добрались до питона. Клиент состоит из нескольких файлов, main.py содержит в себе callback функцию, которая обрабатывает получаемые изображения, также он запускает механизмы сетевого фреймворка Twisted и хранит в себе параметры подключения к камере. Все листинги я привожу укороченными, полную версию можно скачать по ссылке в конце статьи.
main.py
В принципе можно работать и без реализации RTCP протокола и приёма аудио данных. В этом случае камера разрывает соединение через примерно минуту. Приходится всё время переподключаться, это делается автоматически, поэтому проблем не доставляет. Однако для статьи я дописал RTCP часть и сделал заготовку для приёма аудио данных.
Следующим важным файлом является rtsp_client.py . Он самый запутанный, но его цель очевидна — правильно установить соединение, описанное выше.
rtsp_client.py
В случае присутсвия аудио трека, этот модуль также запускает rtp_audio_client.py и соответствующий RTCP клиент.
После успешного соединения за работу принимается rtp_mjpeg_client.py , обрабатывая входящий поток данных.
rtp_mjpeg_client.py
Он прост в понимании. Каждый раз, когда мы принимаем очередную датаграмму, мы парсим её с помощью модуля rtp_datagram.py , а результат скармливаем модулю rfc2435jpeg.py , который создаёт полноценное JPEG изображение. Далее мы ждём появления маркера rtp_dg.Marker и как он появится вызываем callback функцию с восстановленным изображением.
Парсер RTP датаграм выглядит вот так:
rtp_datagram.py
Модуль восстановления JPEG достаточно большой, так как содержит в себе несколько таблиц и довольно длинную функцию генерации заголовка. Поэтому я их здесь опущу, предоставив только функции парсинга полезной нагрузки RTP и создания окончательного JPEG изображения.
rfc2435jpeg.py
Я также реализовал модуль приёма аудио данных rtp_audio_client.py , но не стал их преобразовывать в проигрываемые данные. Если кому-нибудь это будет необходимо я в этом файле сделал набросок как всё должно быть. Нужно только организовать парсинг на подобии rfc2435jpeg.py . С аудио данными легче, так как они не фрагментированны. Каждая посылка несёт в себе достаточно данных для воспроизведения. Приводить этот модуль здесь не буду, так как статья и так уж очень длинная (поскорей бы реализовали хабрафолд).
Для корректной работы нам нужно принимать и отсылать RTCP пакеты, принимаем Sender’s Reports, отсылаем Receiver’s Reports. Для упрощения задачи мы будем отсылать наши RR сразу после приёма SR от камеры и будем в них закладывать идеализированные данные о том, что всё хорошо.
rtcp_client.py
А вот модуль работы непосредственно с RTCP датаграмами. Он получился тоже достаточно большим.
rtcp_datagram.py
Парсинг строго согласно RFC. Использую функцию unpack для конвертирования данных в численные переменные, по массиву данных перемещаюсь с помощью переменной off , которая содержит текущее смещение.
Делать версию листингов с русскими коментариями уже не было сил, так что простите если кому так не удобно.