Network: работа с pcap – сбор и анализ (Wireshark/tshark, tcpdump, packetdump, capinfos), редактирование (hex editor, tcprewrite, bittwiste, editcap, mergecap, wireedit), воспроизведение (tcpreplay, bittwist), анонимизация, комплексные продукты (moloch, observer)
как любые дебаг утилиты, захват трафика лучше запускать в режиме записи в файл, а не в режиме вывода на экран т.к. чаще всего именно формирование вывода является наиболее стрессовым по нагрузке на cpu, а не запуск захвата трафика/debug как таковой.
https://wiki.wireshark.org/SampleCaptures
https://packetlife.net/captures/
- Альтернативы *nix tcpdump на оборудовании security вендоров, все они, включая tcpdump представлены на крутом сайте tcpdump101.com, который позволяет создавать фильтры (конструктор фильтров) в GUI:
- fw monitor – CheckPoint
- packet capture (capture-traffic, capture) – cisco
- diag sniffer – fortigate
Go to File -> Export -> File and select a file name and "CSV" for Save as Type. That way you will get all rows exported exactly as displayed in the packet list. You can also give Wireshark ranges if you don't need all of the rows.
- Wireshark по умолчанию кладет весь дамп в оперативку. Дамп 6GB – будет 6GB занято в оперативке.
- НО такие объемные дампы лучше не делать, а использовать функционал по созданию каждые N-пакетов/секунд/байт нового файла в Output настройках Wireshark (настраивается перед стартом захвата).
- Так же в output настройках можно
- задать отключение сбора дампа(ов) после N-пакетов/секунд/байт.
- включить резолвинг IP адресов в DNS имена (как по умолчанию у tcpdump)
tcp.analysis.retransmission
tcp.analysis.duplicate_ack_frame- При анализе задержек в pcap всегда надо учитывать, что задержка со стороны клиента может быть простым “thinking time”, а не фактической задержкой клиентского приложения
- Wireshark показывает Checksum Ethernet только в случае, если его сохранила сетевая карта, а это редко. Пример дампа с наличием CRC/FCS – eth-crc-fcs.pcap. Поэтому, смотря в дамп трафика Wireshark:
- можно думать, что конец фрейма одинаковый у фреймов с одинаковым payload, но разными SRC/DST адресами, но по факту он отличается т.к. checksum фрейма отличается (напр. различие в окончании фрейма ярко видно в пакетном конструкторе Ixia IxExplorer)
- можно некорректно рассчитывать размер фрейма – нужно прибавлять 4 байта к тому, что видим в Wireshark (4 байта чаще всего представляются в виде 8ми hex символов, сгруппированных по два)

Из канала Chris Greer интересное о перехваченном tcpdump или wireshark (о специфике TCP в отдельной статье l4/tcp):
0) по IP TTL полю в перехваченном дампе в общем случае можно понять где он собран с точки зрения сети – на клиенте, сервере или промежуточном устройстве. Смысл в том что зачение поля в IP TTL имеет двоичную разрядность и чаще всего равно 64/128/256 (раньше разные ОС использовали разный IP TTL и даже были нестандартные значения типо 62, сейчас все чаще стеки в целом работают очень похоже и в том числе для IP TTL используют 64). Если пакет имеет подобный ttl – вероятнее всего он перехвачен на клиенте или менее вероятное – сервер в одном бродкаст домене/кто то подменяет поле (напр. Web proxy корпоративный) или пройдено столько хопов, что значение случайно стало «нужное».
1) по полю IP TTL в ответах от сервера (при перехвате на клиенте и наоборот) можем понять в скольких роутинг-хопах от нас находится сервер путев добавления к текущему IP TTL значения, ближайшего до 64/128/256. Возможны исключения (как в примере выше), но маловероятны. А на промежуточном устройстве можно понять в скольких хопах от него сервер/клиент.2) хорошо иметь дамп с двух сторон – клиента и сервера, т.к., например, то, что мы считаем «проблемой» сервера может быть проблема на промежуточном устройстве
WIRESHARK
- Wireshark может расшифровать зашифрованные данные, если дать ему нужную ключевую информацию (например, дав ему пароль от Wi-Fi для WEP/WPA сети, IPsec, SSL/TLS-сессии).
https://habrahabr.ru/company/billing/blog/261301/
http://costiser.ro/2016/07/07/overlay-tunneling-with-openvswitch-gre-vxlan-geneve-greoipsec/
Here is the full packet capture, but of course, as it's IPsec, you will only see the outer IP header (192.168.56.11 and 192.168.56.12), while the payload (GRE/ETH/IP/ICMP) is encrypted and you only see ESP information. But if you use Wireshark, you can provide the keys and it will decrypt it for you.- Найти пароль в протоколах, не использующих шифрование (telnet, snmp, http, SIP/RTP, etc). Причем по follow tcp stream можно посмотреть весь текстовый обмен – красным отправленные данные, синим полученные (дубли для некоторых т.к. сервер подтверждает прием от клиента символов).

- Собрать телефонный VoIP звонок из stream
- Можно посмотреть задержку в stream
- Собрать файл из stream SMB/FTP/HTTP/DICOM/etc – в wireshark можно экспортировать из payload пакеты, в том числе потенциально опасные в виде исполняемых файлов (exe, dll, etc)
- Собрать страницу из HTTP пакетов (follow TCP stream)
- Легко посмотреть задержку между пакетами – delta time.
In Wireshark Preferences / Appearance / Columns you can add a new column of type Delta time. With this new column and wise capture filtering you should be able to do what you want.

- В Wireshark есть выгрузка object из дампов – можно картинки просмотренного сайта выгрузить, страницы html

- Можно искать запросы к определенным URL (можно и в tcpdump, но проще куда в акулке)
- В wireshark можно добавить вывод информации geoip, что позволит сразу в дампе видеть страны/ASN
- В wireshark можно добавить вычисление fingerprint ssl с использованием JA3 (подробнее о ja3 в security)
- Продуманный интерфейс: Сразу можно посмотреть и HEX и ASCII репрезентацию данных, отсортировать пакеты, увидеть проблемы по цвету, отфильтровать вывод и прочпроч
Wireshark не только с точки зрения удобства использования лучше tcpdump, он считается и намного более мощным инструментом в сравнении с ним, особенно когда идет вопрос в анализе уровня приложений (поддержка более 2к протоколов, даже таких как USB/Bluetooth/Zigbee). Так же использует libpcap. Кроме того у Wireshark есть консольная утилита tshark (terminal-shark), которая часто позволяет обойтись без tcpdump вовсе (сори, tcpdump), но при больших нагрузках (гигабит и более фреймами 64 байта) нужно учитывать, что tshark хуже по производительности в сравнении с tcpdump. Например, можно использовать фильтры, аналогичные wireshark в консоли.
tcpdump is a command line utility, while wireshark has a powerful graphical interface. While tcpdump understands some application-layer protocols, wireshark expands on this with a much larger complement of protocols understood.
Фильтры
Фильтры бывают двух видов:
- display filters – применяемые на уже захваченный трафик, чаще всего используются именно они

- capture filters – применяемые при перехвате трафика, используются при больших объемах данных или заведомо зная, что мы ищем в трафике. Синтаксис capture filters отличается от display filters, но соответствуетсинтаксисуtcpdump.

Очень полезная вещь, которая позволяет не знать синтаксис:
- apply as a filter – применяешь нажав ПКМ по информации из полей пакетов или по самому пакету в brief view. Причем
- можно выбрать как подпадания (Selected), так и все записи, которые отличаются от нашей (Not Selected)
- можно дополнять фильтр другими полями, используя между выражениями булевую логику (and/or selected, not/or selected)

- drag and drop (новые версии) – тянешь любое значения из поля любого пакета и тебе просто показывается синтаксис (можешь дотянуть до фильтра или просто узнать, что нужно вводить в нем)!
# GENERAL/MISC
dhcp # user insted bootp nbns # netbios http.request or !(ssdp) # вместо or можно использовать || kerberos.CNameString kerberos.CNameString and !(kerberos.CNameString contains $) # вместо and можно использовать && http.request.uri matches "q=example"
tcp.srcport==8000
# ADDR
ip.addr == 192.168.0.0
ip.src == 192.168.0.0
ip.dst == 192.168.0.0
ipv6.addr ip.addr == 192.168.0.1 or not tcp.port in (80 25)
eth.addr == ff:ff:ff:ff:ff:ff
# NETS
ip.src==192.168.0.0/16 and ip.dst==192.168.0.0/16 # traffic inside LAN (между сетями)
# CONTENT
frame contains "HTTP/1.1 200 OK"
frame contains 06:03:55:1d:0e:04 # CONTENT (данные любого уровня во фрейме)
udp contains 81:60:03 # данные в UDP (поиск делается как в header, так и в payload)
tcp contains 03:00:00
or
tcp.payload == 03:00:00
not (frame contains "!0123456789!") and frame.len == 519 # булева логика (boolean or/and/not)
# IP
ip.flags.mf == 1 or ip.frag_offset gt 0 # фрагментированные пакеты (fragmented ip packets)
# TCP
tcp # оставляем только фреймы, в которых инкапсулирован TCP
tcp.port == 3389
tcp.window_size == 0 && tcp.flags.reset != 1 # TCP buffer full - source is instructing Destination to stop sending data
tcp.window_size_value != 2497
# TCP FLAGS
tcp.flags.syn == 1
tcp.flags.reset == 1
tcp.flags.fin == 1
# TCP EXPERT WINDOW
expert.message == "Out-Of-Order segment"
expert.message == "Duplicate ACK (#1)"
tcp.analysis.retransmission
tcp.analysis.duplicate_ack_frame
# HTTP
Http.time > 1 # смотрим задержки ответа по HTTP
# BGP
bgp.open.identifier # смотрим open сообщения с полем BGP identifier в нем
bgp.update.path_attribute # смотрим update сообщения с полем path_attribute в нем
# OSPF
ospf.area_id = 0.0.0.0
# SSH
not port 22 # полезно для отброса управляющего коннекта
# ICMP
icmp # полезно для просмотра только icmp пакетов (ping)
# WIFI handshake
# в дампе должны быть все четыре сообщения eapol key (Message 1-4 to 4)
# тут есть sh скрипт по извлечению https://miloserdov.org/?p=1047
# напр. wlan.addr==28:28:5D:6C:16:24
wlan.fc.type_subtype == 0x08 || wlan.fc.type_subtype == 0x05 || eapol && wlan.addr == BSSID
# TSHARK example (Y - display filter)
tshark -r dump.pcap -Y "(ip.src == 172.17.0.2)"
tshark -r dump.pcap -nn -e ip.src -e eth.src -Tfields | grep ^172. | sort -u # извлекаем уникальные связки SRC & DST IP с адресацией 172.x.x.xПрофили (profiles)
Можно создать разные профили под разные задачи – default_with_time_diff, Multicast, VOIP, RIP/OSPF/BGP/etc. В профилях будут уникальные настройки интерфейса – zoom, расположение панелей, шрифты (размер, цвета), можно добавить уникальные столбцы (тот же timediff только в отдельном профиле).
RTT
statistics -> tcp stream graph -> round trip time
VoIP
- sngrep это утилита для анализа SIP/VoIP трафика в консоли Linux сервера.
- Wireshark очень полезен для диагностики VoIP.
Заходим в контекст Telephony, выбираем VoIP Calls. Видим звонки успешные и не очень. Так же видим и активные звонки.


Выбрав звонок и нажав на Flow Sequence можно посмотреть наглядно весь процесс согласования.

А по Play Streams можно даже прослушать звонок (если он успешен и поддерживается кодек, как тут G.711A), через встроенный в акулу RTP player. Круто!

Разговор в VoIP состоит из двух unicast RTP потоков – один от src до dst, второй от dst до src. По analyse можно посмотреть детальные данные (max jitter, RTP packets, lost, etc) по каждому потоку.


Пример дампа с разговором (из курса GNS3 Wireshark: Packet Analysis and Ethical Hacking: Core Skills). Для прослушивания RTP потока нужно сделать decode as по одному из UDP пакетов разговора и выбрать протокол RTP для интерпретирования.
Время
В настройках просмотра можно выбрать формат времени – например, не на основе старта снятия дампа, а реальное время с датой.

Статистика
В разделе statistics можно узнать кучу разных вещей (только часть):
- общая статистика по файлу, эту статистику можно получить используя консольную утилиту capinfos от Wireshark:
- дата/время первого и последнего пакета
- размер дампа
- количество пакетов
- время съема трафика
- средний pps, packet size, bits/s
Установка:
apt install wireshark-common
capinfos -z /home/user/*.pcap # average packet size
capinfos /home/user/test.pcap | tail -n 1 # dump packet count
# capinfos dump.20130621.cap
File name: dump.20130621.cap
File type: Wireshark/tcpdump/. - libpcap
File encapsulation: Linux cooked-mode capture
Number of packets: 2000
File size: 2065933 bytes
Data size: 2033909 bytes
Capture duration: 43 seconds
Start time: Fri Jun 21 17:45:06 2013
End time: Fri Jun 21 17:45:49 2013
Data byte rate: 46968.49 bytes/sec
Data bit rate: 375747.94 bits/sec
Average packet size: 1016.95 bytes
Average packet rate: 46.19 packets/sec- распределение по протоколам в protocol hierarchy, причем можно посмотреть на эти данные в самом дампе, применив стандартный Apply as a filter в статистике

- по размерам пакетов в packet lengths

- найти top talkers в endpoints (MAC, IP4/6, UDP, TCP)

- проанализировать взаимодействие между хостами по объему трафика в conversations (MAC, IP4/6, UDP, TCP)

- проанализировать взаимодействие между хостами по передаваемым сообщениям (транзакциям), сохраняя их последовательность в flow graph. Очень удобно, что используя стандартный фильтр можно посмотреть конкретное взаимодейтсвие (в статистике прожать “Limit to display filter”) – к примеру ниже отфильтрованное взаимодействие только по DHCP.


tcpdump
- tcpdump по практике является самым производительной утилитой – в сравнительных тестированиях с wireshark, tshark, windump только он смог захватить 64 byte frame на 1G line rate (порядка 1.5 МЛН PPS)
- производительность tcpdump можно повысить правильной конфигурацией
- запись в файл, не на экран
- увеличение размера буфера
- использование фильтров для пакетов – чем меньше захватываем ненужного, тем больше можем захватить нужного
- -p/–no-promiscuous-mode отключение promiscous mode
- использование фильтров захвата (ниже примерыф) напр. захватываем только TCP трафик
# mkdir /tmp/ramdisk
# mount -t tmpfs -o size=1000m tmpfs /tmp/ramdisk/
# nice --adjustment=-10 tcpdump -s 1514 -i eth2 -w /tmp/ramdisk/ecg_sniffer_eth2_20180321.pcap-
- snaplen (чем меньше, тем лучше) – tcpdump поддерживает захват только части пакета, что увеличивает его производительность и уменьшает накладные расходы по памяти для хранения дампов. К примеру, можно захватывать только 96 байт пакета IPv4 или 114 байт пакета IPv6 (snaplen, -s) и таким образом получить захват всех header без большей части payload (в средней реальной сети). Кроме того можно дополнительно обработать перехваченный трафик утилитой TraceWrangler (подробнее и альтернативы в разделе анонимизация ниже), которая удалит payload в том числе из пакетов, которые его содержат.
tcpdump -i eth0 -s 98 -w traffic.pcap # IPV4
tcpdump -i eth0 -s 114 -w traffic.pcap # IPV6
tcpdump -i eth0 -s 50 -w traffic.pcap # SRC+DST+PROTO
Both tcpdump and tshark/dumpcap use the "-s" option to limit the amount of data captured (snap length).
It is better to capture with at least "-s 94" for IPv4 or "-s 114" for IPv6 and then post process the trace file with tracewrangler to remove the data above TCP without damaging the TCP header then to capture with a smaller snaplen.
I was in the same situation and I solved it by adding -s 96. In this example i'm using 96 to be "almost" sure that I would capture 100% of ethernet+ip+(icmp || udp || tcp) header values.
tcpdump -ieth0 -s96 -w traffic.dump 'ip or icmp or tcp or udp'
If you just need Time, Source, Destination, Protocol. It work for me using
tcpdump -i $INTERFACE -n -s 50- tcpdump – по сути консольная версия Wireshark/Tshark. Дампы tcpdump, кстати, читаются легко Wireshark, что делает его очень полезной утилитой
- tcpdump может не запускаться при записи дампа в файл, если использовать расширение отличное от pcap (потенциально проблема из-за SE Linux)
tcpdump -w 5345345.pcap
tcpdump: listening on enp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
tcpdump -w 5345345.cap
tcpdump: 5345345.cap: Permission denied
tcpdump -w 5345345.test
tcpdump: 5345345.test: Permission denied- tcpdump поддерживает BPF фильтры, захватывая копию необходимого трафика из ядра системы
- tcpdump (да и wireshark) может дропать трафик при большом объеме. Главное узкое место – скорость ЖД. Сделал несколько генераций одного трафика. В первом pcap меньше данных и нет всех пакетов если открыть в Wireshark. При этом все пакеты пришли по назначению во все итерации.
-rw-r--r-- 1 root root 830277116 янв 01 10:29 test_1.pcap
-rw-r--r-- 1 root root 830356104 янв 01 10:33 test_2.pcap
-rw-r--r-- 1 root root 830356104 янв 01 10:44 test_3.pcap
-rw-r--r-- 1 root root 830356104 янв 01 10:56 test_4.pcap
-rw-r--r-- 1 root root 830311550 янв 01 10:59 test_5.pcapbash-4.2# tcpdump -n -i eth3 -w test.pcap
tcpdump: listening on eth3, link-type EN10MB (Ethernet), capture size 262144 bytes
258673 packets captured
258673 packets received by filter
0 packets dropped by kernel
А в дампе по факту 0 пакетов.
bash-4.2# tcpdump -r test.pcap
tcpdump: truncated dump file; tried to read 4 file header bytes, only got 0- Tcpdump работает на почти любые типы интерфейсов, которые идентифицируются системой (не DPDK): sub интерфейсы, bridge интерфейсы, туннельные интерфейсы и даже loopback
tcpdump -n -i eth1.855
tcpdump -n -i bridge0
tcpdump -n -i utun0
tcpdump -n -i lo0- Tcpdump может при старте не показывать какое-то время наличие пакетов на интерейсе, особенно если не отключен DNS resolve через -n
tcpdump -i enp2s0 -n
- При передаче файлов с дампами по сети, всегда нужно учитывать, что Wireshark/tcpdump не ужимают дампы, а после сжатия обычным zip/rar файл может весить в десятки раз меньше (gzip c 800MB до 50MB, 7z c 800MB до 20MB).
tcpdump использует open source libpcap библиотеку. Показывает (STDOUT) по умолчанию данные в terminal. Может писать в файл дамп или читать из файла. По умолчанию показывает brief информацию в удобном для человека формате – IP адреса там например через точку или по умолчанию он даже делает reverse lookup (PTR) или номера портов подменит на сервисы, но конечно все равно не так удобно как Wireshark.
Tcpdump is a popular, lightweight command line tool for capturing packets and analyzing network traffic.
tcpdump требует прав админа для запуска. Минимум ему нужно скормить название ip интерфейса через опцию -i (узнать его можно через ifconfig/ip link). По результату будет показана статистика. По умолчанию будет показана базовая инфа – layer 3 protocol, source, and destination addresses and ports, TCP details, like flags, sequence and ack numbers, window size, and options.
Head's up that tcpdump does require root or administrator privileges in order to capture traffic, so every command must begin with sudo. At a minimum, you must specify an interface to listen on with the -i flag. You may want to check what the primary network interface name is using ip link.
~$ tcpdump host 8.8.8.8 tcpdump: no suitable device found ~$ sudo tcpdump host 8.8.8.8 [sudo] password for : tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
Опции (подробнее в man tcpdump):
-U # отключаем буфферизацию дампа в пользу "записывать сразу"
-D # смотрим список интерфейсов на которых возможен захват
-i [if] # название прослушиваемого интерфейса, сразу после интерфейса можно указать еще что-то, например port 80, тогда вывод будет фильтроваться сообщениями только port 80 на этом интерфейсе -e # показать MAC адреса (ethernet адреса) в выводе -n # не резолвим IP в FQDN, номера портов в сервисы -w [filename.pcap] # сохраняем захваченное в binary файл -r [filename.pcap] # читаем файл (можно без прав sudo), причем можно указывать те же опции, что при обычном захвате (-v для большего вывода, -n для фильтрации, etc) -v # более детальная инфа (details of the IP header, like time-to-live, IP ID number, IP options, and IP flags) -x # показываем raw data (payload) -X # ASCII вид данных
-c # выход при получении определенного количества пакетов, особенно полезно когда трафика ожидается большое количество или когда мы переживаем за состояние системы из-за работы tcpdumpЕсть поддержка фильтров, как базовых, так и довольно сложных (напр. L4). Возможна комбинация фильтров (см. usage).
vlan [N] - фильтруем по номеру VLAN (при наличии SVI)
|src|dst| host [ip] - указываем чтобы в содержимом был IP (DST/SRC), можно управлять будет он SRC|DST или оба (без опций)
|src|dst| network [ip/prefix] - указываем чтобы в содержимом был трафик определенной сети, можно управлять будет он SRC|DST или оба (без опций)
port [N] - TCP/UDP port
portrage [N-N] - TCP/UDP portrange
greater [N] - указываем размер пакета
timeout [N] - запуск только на определенное время
tcp[tcpflags] == tcp-syn - пример L4 фильтра
and|or|not - можно комбинировать филтьтры булевой логикой (boolean)# СПИСОК интерфейсов
# sudo tcpdump -D
1.enp7s0 [Up, Running]
2.any (Pseudo-device that captures on all interfaces) [Up, Running]
3.lo [Up, Running, Loopback]
4.enp8s0 [Up]
5.nflog (Linux netfilter log (NFLOG) interface)
6.nfqueue (Linux netfilter queue (NFQUEUE) interface)
7.usbmon1 (USB bus number 1)
8.usbmon2 (USB bus number 2)
9.usbmon3 (USB bus number 3)
10.usbmon4 (USB bus number 4)
# БАЗОВЫЙ ЗАХВАТ $ sudo tcpdump -i eth0 20:44:29.087535 IP linux-instance.c.qwiklabs-gcp-c7fbce528931fd28.internal.ssh > test.net.54895: Flags [P.], seq 419056:419280, ack 4961, win 285, length 224 20:44:29.103033 IP test.net.54895 > linux-instance.c.qwiklabs-gcp-c7fbce528931fd28.internal.ssh: Flags [P.], seq 4961:5025, ack 402400, win 1020, length 64 1951 packets captured 2001 packets received by filter 50 packets dropped by kernel
# ЗАХВАТ только заданное количество секунд
$ sudo timeout 5 -n host 1.1.1.1 and port 80 and greater 1000
# ЗАХВАТ с завершением по получению хоть одного пакета
$ sudo tcpdump -c 1 -n host 1.1.1.1 and port 80 and greater 1000
# ЗАХВАТ только TCP-SYN трафика определенного хоста (src & dst)
$ sudo tcpdump -n host 1.1.1.1 and tcp[tcpflags] == tcp-syn # ЗАХВАТ В PCAP $ sudo tcpdump -i eth0 port 80 -w http.pcap # This starts a capture on our eth0 interface that filters for only HTTP traffic by specifying port 80. The-wflag indicates that we want to write the captured packets to a file namedhttp.pcap$ curl example.com $ ls -lh http.pcap -rw-r--r-- 1 root root 9.3K Feb 19 21:16 http.pcap $ tcpdump -r http.pcap -nv # Note that we don't need to usesudoto read packets from a file. Also note that tcpdump writes full packets to the file, not just the text-based analysis that it prints to the screen when it's operating normally. For example, somewhere in the output you should see the html that was returned as the body of the original query in the other terminal # С ФИЛЬТРАМИ на DNS 8.8.8.8 ~$ sudo tcpdump -i eth0 -vn host 8.8.8.8 and port 53 tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes 21:02:36.484819 IP (tos 0x0, ttl 64, id 32534, offset 0, flags [none], proto UDP (17), length 68) 10.0.0.2.40596 > 8.8.8.8.53: 64482+ [1au] A? example.com. (40) 21:02:36.486077 IP (tos 0x0, ttl 115, id 11509, offset 0, flags [none], proto UDP (17), length 84) 8.8.8.8.53 > 10.0.0.2.40596: 64482$ 1/0/1 example.com. A 93.184.216.34 (56) Host 8.8.8.8 specifies that we only want packets where the source or destination IP address matches what we specify (in this case 8.8.8.8). If we only want traffic in one direction, we could also add a direction qualifier, like dst or src (for the destination and source IP addresses, respectively). However, leaving out the direction qualifier will match traffic in either direction. Next, the port 53 portion means we only want to see packets where the source or destination port matches what we specify (in this case, DNS). These two filter statements are joined together with the logical operator "and". This means that both halves of the filter statement must be true for a packet to be captured by our filter. $ dig @8.8.8.8 A example.com ; > DiG 9.10.3-P4-Debian > @8.8.8.8 A example.com ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER# читаем файл $ tcpdump -r # читаем файл и фильтруем вывод только udp пакетами $ tcpdump -r udppacketdump
packetdump -i eth0 -G 3600 -w foo-%y%m%d-%H%M%S.pcap.lz4
Читаем последние логи для интерфейса:
packetdump collected-info-eth1 dump_text.txt
This is a high-speed (10gig) packet-sniffer that compresses packets as it writes to disk. I wrote it because the packet logging features of tcpdump are slow, but extra special slow when attempting to compress files after they've been written uncompressed to the disk when rotating files.
Python + pyshark
Редактирование
PYTHON + SCAPy
Самый удобный способ когда нужно сделать более чем одно действие по изменению сетевых дампов. Легко можно сделать даже непростые операции, например, заменить все сетевые (MAC, IP, PORT) адреса в дампе на другие, IPv6 header на IPv4. Замена IPv6 header на IPv4 подразумевает извлечение payload, создание IPv4 header, добавление в него payload, пересчет checksum и length (см. в HEX EDITORS).
Подробнее в python поиском по scapy.
HEX READER and WIRESHARK
Извлекаем из записанных byte файлов вывод для сопоставления с Wireshark.
hexdump -C aggregated.bytes # получение вывода like Wireshark Hex Dump
xxd -b aggregated.bytes # в бинарном видеhex editors
Самый хардкорный способ изменения дампов. Подразумевает и пересчет checksum, тут может помочь tcprewrite.
К примеру, c помощью hex editor ipv6 header заменить на ipv4 не так просто (особенно, в сравнении со python + scapy):
1) изменяем hex строку. Много разных способов, хорошо с gif описано тут.
https://hexed.it/
vim
:%!xxd # преобразуем в hex данные и превращаем vim в hex editor2) после изменения нужно использовать pcapfix (причина однозначно непонятна, возможно, разрушается структура при экспорте)
pcapfix -d test.pcap
[+] CORRECTED Packet #1 at position 24 (1566304697 | 904775 | 74 | 74).
[!] This corruption seems to be a result of an ascii-mode transferred pcap file via FTP.
[!] The pcap structure of those files can be repaired, but the data inside might still be corrupted!
[+] SUCCESS: 1 Corruption(s) fixed!3) IPv6 header меняется от пакета к пакету, даже с учетом совпадения адресов SRC/DST – flow label в пакетах дампа зачастую разный, payload length так же разный; поэтому при массовой замене нужно заменять в HEX с помощью regexp
4) IPv4 header должен иметь корректный total length от IPv4 header + payload; т.е. при каждой замене нужно корректировать total length для каждого пакета, иначе пакет будет отбрасываться еще на уровне ядра ОС (Linux, Windows)
15:38:17.904775 IP 7.7.7.7.49474 > 192.168.0.1.8000: Flags [S] [bad hdr length 40 - too long, > 32]
[Malformed Packet: TCP: length of contained item exceeds length of containing item]5) Checksum может не пересчитываться tcprewrite с segmentation fault (помог запуск tcprewrite на другой VM)
# tcprewrite --fixcsum --infile=test.pcap --outfile=test_new.pcap
Ошибка сегментированияИ все это нужно делать для всех пакетов в дампе.
tcprewrite
Хороший, хотя и базовый, редактор дампов.
- Usage
- Ставится совместно с последним tcpreplay (apt-get install tcpreplay)
- Если воспроизвести корректно дамп с icmp request (dst ip) хост, на который прилетит трафик, может ответить icmp reply. Аналогичное справедливо и для любых других серверных приложений.
Change MAC
tcprewrite --enet-dmac=2c:56:dc:72:08:39 --infile=UDP_original.pcap --outfile=UDP_changed_dst.pcap
Change IP
Можно менять по net/mask, если не использовать опцию -fixsum, то чексумма может не пересчитаться.
tcprewrite --srcipmap=127.0.0.1:7.7.7.7 --dstipmap=127.0.0.1:192.168.0.1 --infile=test.pcap --outfile=test_without_loo.pcap
tcprewrite --fixcsum --dstipmap=0.0.0.0/0:172.17.0.2 --infile=UDP_changed_dst.pcap --outfile=UDP_changed_dst_mac_ip.pcap
tcprewrite --fixcsum --srcipmap=0.0.0.0/0:172.0.0.2 --infile=UDP_changed_dst_mac_ip.pcap --outfile=UDP_changed_dst_mac_ip_src_ip.pcapbittwist/bittwiste
Хорошие, но старые утилиты для воспроизведения/генерации трафика (bittwist) и редактирования pcap (bittwiste). Bittwiste не поддерживает IPv6.
This removes (-D) the frame IP-, UDP- and GTP-Header. Result: The encapsulated IP header will be the new frame IP header ;-) Maybe you'll have to adjust the number of stripped bytes for your environment (IP Options).
bittwiste -I gtp-u.pcap -O gtp-stripped.pcap -D 15-54Может помочь удалить лишние инкапсуляции
802.1Q VLANs, MPLS, GRE and PPPoE, L2TP,VXLAN
./stripe -r mpls-frames.cap -w clean-frames.cap
dsniff
старая хакер-утилита, по сути “wireshark” который показывает только payload открытых протоколов в перехваченном трафике и вычленяет оттуда явки-пароли.
WireEdit
Хороший продукт для редактирования pcap. У самого Wireshark была beta с возможностью edit, но видимо дальше беты не зашло.
С помощью WireEdit в пару кликов можно поменять любые поля (в том числе массовые замены) и пересчитать checksum. Требует лицензию (высылают на почту при регистрации, сейчас с этим стало сложнее).

Продукт развивается, пример добавлений в новом релизе:
--------------------------------
11/01/19 WireEdit 2.10.54
--------------------------------
1)* DOCSIS 3.1 support added. Needs more work, usable as is.
2)* VRRP support added.
3)* Mobile Core (SS7, GTP, GSM MAP, DIAMETER, etc.) support added.SPLIT PCAP
Разбиваем крупный pcap на несколько дампов по 100 MB (tcpdump).
sudo tcpdump -r ~/3.pcap -w /tmp/new3.pcap -C 100
Разбиваем крупный pcap на несколько дампов по миллиону пакетов (editcap).
sudo editcap -c 1000000 ch5.pcap out.pcap
editcap + mergecap
Поменять order пакетов можно с помощью editcap + mergecap. Сначала с помощью edit создаем pcap с отдельными пакетами из необходимого дампа, потом merge’им с помощью mergecap.
https://stackoverflow.com/questions/12741079/how-to-edit-wireshark-pcap-to-change-the-order-of-packets
editcap -r in.pcap tmp1 2-3
editcap -r in.pcap tmp2 1
editcap -r in.pcap tmp3 4-6
mergecap -w out.pcap -a tmp1 tmp2 tmp3Воспроизведение
pfsend
pfsend позволяет создавать большую нагрузку в сравнении с tcpreplay без кастомизаций (netmap, preload). Основан не на netmap/DPDK, а на pfring.
pfsend -i zc:eth1 -n 0 -f test.pcap
Tcpreplay
- По практике если нужен рigh performance – TRex может быть более производителен, в том числе на пакетах небольшого размера (в тесте это чуть более 100 байт). В данном случае возможно была проблема с разной версией драйверов для сетевых карт (пример ниже для X710) – TRex мог использовать более актуальные драйвера в сравнении с netmap.
- 1 gbps tcpreplay + netmap
- 5 gbps TRex (ideal dump repeat), 10 gbps (not ideal dump repeat)
Q: Why doesn't Tcpreplay send traffic as fast as I told it to?
Usually this occurs when trying a pcap of small packets.- Про promiscuous режим можно почитать тут. Хотя мне не понадобилось его включение. Чаще всего сетевые утилиты, снимающие трафик с интерфейса, его включают сами.
Tcpdump start/stop
[ 1691.019004] device eth0 entered promiscuous mode
[ 1699.783211] device eth0 left promiscuous mode-
- (tcpdump/bmon-nload/ifconfig) Трафик может не фиксироваться на интерфейсе в счетчиках и соответственно в утилитах bmon/nload, которые строят графики на основе счетчиков. Причина отсутствия счетчиков может быть не отсутствие трафика, а необходимость перевода интерфейса в promisc режим. Проверить можно tcpdump – он это делает автоматически.
# ifconfig [interface] promisc
# ip link set [interface] promisc on
8: enp5s0f0: PROMISC,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether d4:5d:64:bd:89:13 brd ff:ff:ff:ff:ff:ff
inet 172.16.0.1/16 scope global enp5s0f0
alid_lft forever preferred_lft forever# cat fragroute.conf
ip_frag 24 new
order random
dup random 10
print- По практике Tcpreplay при проигрывании pcap в петле (loop) даже в случае ограничения собственной производительности и/или канала (line rate) всегда генерирует ожидаемое количество пакетов – т.е. гарантируется проигрывание всех пакетов из pcap с повторением в соответстии с количеством loop, несмотря на то, что заданная целевая скорость не гарантируется.
- Tcpreplay по умолчанию (без указания скорости проигрывания) стремится повторить pcap с точки зрения задержек между пакетами и уложить все за время снятия дампа!
Wireshark
Measurement
Captured
Packets 1585
Time span, s 130.900
Average pps 12.1
Average bytes/s 5691
TCPREPLAY
tcpreplay -i enp3s0f1 --preload-pcap --netmap test.pcap
Switching network driver for enp3s0f1 to netmap bypass mode. done!
File Cache is enabled
Actual: 1585 packets (744982 bytes) sent in 130.90 seconds
Rated: 5690.9 Bps, 0.045 Mbps, 12.10 pps
Successful packets: 1585tcpreplay - Replay network traffic stored in pcap files
tcpreplay is a tool for replaying network traffic from files saved with tcpdump or other tools which write pcap(3) files.tcpreplay -i eth0 sample.pcap
# performance (так же смотри ниже - объем и taskset)
tcpreplay -i eth0 --topspeed --preload-pcap sample.pcap# можно указать, что работать нужно с максимальной скоростью генерации (по умолчанию не с максимальной, а на той, на которой записан pcap с сохранением тайминга между пакетами) и выгружать pcap в RAM для ускорения (по практике прирост может быть до трех раз или не быть вовсе)
tcpreplay -i eth0 -p 669642 --preload-pcap sample.pcap # можно указать конкретный pps
# loop, duration
tcpreplay -i eth0 --loop=1000 sample.pcap # повтор воспроизведения
tcpreplay -i eth0 --loop=1000 --duration=100 sample.pcap # повтор воспроизведения, но не более 100 секунд
# directory
tcpreplay -i eth0 --topspeed --loop=1000000000000 *cap* # воспроизводим все дампы в директории с максимальной скоростью (без предварительной загрузки в RAM)
# settings
tcpreplay --listnics # команда может не показывать по факту интерфейсы, которые может использовать. В моем случае была бесполезной (как promiscuous режим).
# version (как собрана утилита)
# tcpreplay -V
tcpreplay version: 4.3.1 (build git:v4.3.1) (debug)
Copyright 2013-2018 by Fred Klassen - AppNeta
Copyright 2000-2012 by Aaron Turner
The entire Tcpreplay Suite is licensed under the GPLv3
Cache file supported: 04
Not compiled with libdnet.
Compiled against libpcap: 1.8.1
64 bit packet counters: enabled
Verbose printing via tcpdump: enabled
Packet editing: disabled
Fragroute engine: disabled
Injection method: PF_PACKET send()
Not compiled with netmapperformance tuning results
4.5KB-HTTP.pcap
Производительность с netmap & preload-pcap: 4768.55 Mbps
Производительность без netmap: 2434.93 Mbps
Производительность без netmap & preload-pcap: 2079.60 Mbps
44KB-HTTP.pcap
Производительность с netmap & preload-pcap: 8055.77 Mbps
Производительность без netmap: 4473.61 Mbps
Производительность без netmap & preload-pcap: 3435.65 Mbpsdump size
Не только preload/nemap помогают увеличить создаваемую нагрузку, но даже простое уменьшение дампа. Причем прирост после уменьшения может быть больше чем прирост от использования preload. К примеру – уменьшил дамп с 3GB до 300MB (аналогично и для 100MB) – прирост нагрузки в 5 раз. Добавил preload-pcap – прирост в 6 раз (по сравнению с первоначальной нагрузкой).
Rated: 167814425.8 Bps, 1342.51 Mbps, 118685.07 pps
Rated: 552510175.3 Bps, 4420.08 Mbps, 390921.39 pps
Rated: 750648600.3 Bps, 6005.18 Mbps, 531111.74 ppsобъем памяти
Причем на множество файлов tcpreplay открывает их все сразу – для preload в таком случае нужен большой объем оперативной памяти. Прирост при этом в генерируемой нагрузке не факт, что будет высокий.
tcpreplay -i enp3s0f1 --topspeed --loop=1000000000000 --preload-pcap *pcap
Rated: 291740421.8 Bps, 2333.92 Mbps, 590406.88 pps
tcpreplay -i enp3s0f1 --topspeed --loop=1000000000000 *cap
Rated: 214934733.2 Bps, 1719.47 Mbps, 427457.74 ppsпроцессор и сетевая карта
Производительность генерации может быть так же увеличена за счет использования более производительного процессора и сетевых карт (напр. Intel X710 вместо Intel X520). В реальных тестах прирост до +30, несмотря на то, что обе сетевые карты 10G capable.
Кроме того на практике на производительность очень значительно может влиять драйвер сетевой карты – на старых драйверах X710 (i40e) производительность была до 2 раз ниже в сравнении с актуальными драйверами в совокупности с периодическим зависанием netmap.
разные cpu и количество процессов tcpreplay
Из интересного в контексте performance так же
- Taskset/affinity/pinning двух процессов tcpreplay по разным ядрам CPU зачастую увеличивает суммарную нагрузку т.к. tcpreplay процесс работает только на одном ядре CPU. Так же на практике даже однопоточный tcpreplay может работать более производительно при использовании pinning (до 5 раз!) и медленнее без него. Но бывало и обратное для других дампов, когда pinning приводил к деградации в сравнении с “без pinning”, хотя и негативное влияние не настолько значимо – до -30%, а не нескольких раз.
- tcpreplay netmap - 850 Mbps
- tcpreplay no netmap - 400 Mbps
- tcpreplay no netmap multiple process (one cpu) - 200 Mbps
- tcpreplay no netmap multiple process (multiple cpu) - 550 Mbps
Since tcpreplay is not multi-threaded, SMP or dual-core processors won’t help very much. However, other processes can run on the other CPU(s).
Turn off hyperthreading (HT) if your CPU supports it.
# Можно напрямую указать конкретный CPU (affinity), range или list (man taskset --cpu-list 0-2,6)
taskset --cpu-list 1 /root/redkin/tcpreplay-master/src/tcpreplay -i enp3s0f1 --topspeed --loop=1000000000000 --duration=600 --preload-pcap /root/redkin/test.pcap &
taskset --cpu-list 2 /root/redkin/tcpreplay-master/src/tcpreplay -i enp3s0f1 --topspeed --loop=1000000000000 --duration=600 --preload-pcap /root/redkin/test.pcap &- Параллельный запуск нескольких tcpreplay без контроля ядер (“на удачу”) taskset/affinity на одном интерфейсе так же, соответственно, может увеличивать генерируемую нагрузку – напр. запуск 10 instance приложения может иметь суммарную производительность незначимо выше (в среднем менее 10%) в сравнении с запуском 1 instance netmap (а в тестах выше netmap вообще выигрывал). Запустить же несколько процессов с netmap не получится – запущенный процесс с netmap не позволит другим получить доступ к сетевой карте.
tcpreplay 6 сессий - tcpreplay с netmap (mbps)
3262 2811
2940 2711
2613 2808
10091 9688
10129 9689
10347 9677
10397 9842
10293 9842
10744 9841tcpreplay-edit с fuzzing
- fuzzing так же возможен на базе tcpreplay-edit,scapy, isic
Может падать с segmentation fault (segfault) на какие-то дампы/каких-то сборках, так же как tcprewrite.
Запуск простейший, поддерживаются те же опции, что в “обычном” tcpreplay и дополнительные, например нужные нам fuzz-seed и fuzz-factor.
- fuzz-seed – seed value для рандомизации (random seed/repeatable random по аналогии с isic, TRex, Ixia IxNetwork)
- fuzz-factor – сколько пакетов подпадает под fuzzing (с единицей каждый пакет). При fuzz-factor=1 скорость генерации уменьшается (может в несколько раз!) т.к. утилите приходится менять каждый пакет.
./src/tcpreplay-edit -i eth0 --topspeed --loop=100 --fuzz-seed=1 --fuzz-factor=1 *
tcpreplay с netmap
Для high performance можно использовать tcpreplay с netmap (хотя и при большом кол-ве прорцессов производительность может быть выше netmap). Производительность в таком случае до line rate, что делает инструмент сопоставимым с commercial traffic generators на “обычных” сетевых картах.
--netmap
Write packets directly to netmap enabled network adapter.
This feature will detect netmap capable network drivers on Linux and BSD systems. If detected, the network driver is bypassed for the execution duration, and network buffers will be written to directly. This will allow you to achieve full line rates on commodity network adapters, similar to rates achieved by commercial network traffic generators. Note that bypassing the network driver will disrupt other applications connected through the test interface. See INSTALL for more information.
This feature can also be enabled by specifying an interface as 'netmap:' or 'vale:. For example 'netmap:eth0' specifies netmap over interface eth0.По практике netmap дает вполне ощутимый прирост в генерации на 20-30% на 10G сетевых картах – напр. с 6 GBPS до 8-9. На 1G NIC может быть даже деградация, поэтому зачастую нет смысла на них использовать netmap.
As an aside, usually netmap is not required for GigE, especially with latest enhancements to checksum algorithms in 4.1.1. With some drivers such as e1000e I find it actually slows things down. It comes into play with 10GigE speeds and higher.
Сборка netmap.
https://github.com/luigirizzo/netmap
cd netmap-master
chmod -R 777 *
./configure
# alternate actual Intel X710 Drivers ./configure --select-version=i40e:2.17.4
make
sudo make installСборка tcpreplay с netmap.
Для начала качаем сборку tcpreplay-4.3.1.tar.xz с сайта разработчика т.к. напрямую с git требует autogen + сборка можеты быть не stable. Далее схема аналогична netmap.
cd tcpreplay-master
chmod -R 777 *
./configure --with-netmap=/path_to_netmap_dir/netmap-master
make
sudo make install
src/tcpreplay -VЗапуск tcpreplay с netmap.
Для запуска требуются установленные в ядро драйвера netmap (netmap.ko) и сетевой карты (напр. ixgbe.ko, igb.ko, igb_uio.ko, e1000e), иначе будет ошибка. Загружаем в ядро драйвер с помощью insmod.
Fatal Error: failed to open device eth0: Unable to determine the running netmap version.
sudo insmod netmap.ko
sudo insmod ixgbe.koМожно так же проверить lsmod, ls /dev/netmap. Просмотреть путь до всех модулей ядра/драйверов можно используя awk + xargs.
# lsmod | grep netmap
netmap 221184 14
# ls /dev/netmap
/dev/netmap
# awk '< print $1 >' /proc/modules | xargs modinfo -n | sortВыгрузить драйвер можно с помощью rmmod и modprobe.
rmmod netmap
modprobe -r netmapПолучить информацию о модуле (например, Linux kernel, на которой был собран модуль) можно с помощью modinfo. Модуль сильно зависит от ядра, поэтому по умолчанию не запустится на другом ядре. В теории может помочь функция -f для insmod, но по факту у меня она к успеху не приводила – были ошибки некорректного формата. В таком случае нужно пересобирать модуль для нового ядра.
modinfo netmap.ko
filename: /home/user/netmap.ko
license: Dual BSD/GPL
description: The netmap packet I/O framework
author: http://info.iet.unipi.it/~luigi/netmap/
alias: pci:v00001B36d0000000Dsv*sd*bc*sc*i*
alias: pci:v00001B36d0000000Csv*sd*bc*sc*i*
depends:
retpoline: Y
name: netmap
vermagic: 4.19.0-6-amd64 SMP mod_unload modversions
parm: priv_buf_size:int
parm: priv_buf_num:int./tcpreplay-master/src/tcpreplay -i eth0 --topspeed --loop=100 --preload-pcap --netmap *cap*
Switching network driver for eth0 to netmap bypass mode. done!
File Cache is enabled
Warning: Unable to send packet: User abort
Actual: 88828775 packets (125603887850 bytes) sent in 123.73 seconds
Rated: 1015128566.9 Bps, 8121.02 Mbps, 717912.70 pps
Statistics for network device: eth0
Switching network driver for eth0 to normal mode. done!
./tcpreplay-master/src/tcpreplay -i eth0 --topspeed --loop=100 --preload-pcap *cap*
File Cache is enabled
Actual: 33439919 packets (47284045466 bytes) sent in 66.10 seconds
Rated: 715276154.4 Bps, 5722.20 Mbps, 505853.00 pps
Statistics for network device: eth0Анонимизация/anonymization/sanitization
https://www.tracewrangler.com
TraceWrangler is a network capture file toolkit running on Windows (or on Linux, using WINE) that supports PCAP as well as the new PCAPng file format, which is now the standard file format used by Wireshark. The most prominent use case for TraceWrangler is the easy sanitization and anonymization of PCAP and PCAPng files (sometimes called "trace files", "capture files" or "packet captures"), removing or replacing sensitive data while being easy to use.Комплексные продукты
Moloch – система сбора/анализа/хранения/индексации дампов в формате pcap. Идея – не замена IDS, а максимальная видимость сети (network visibility). Сайт moloch. Использует elasticsearch для поиска в дампах. Может быть использован в Big Data, Forensics. Имеет api. Доступ к дампам на сенсорах может быть через WEB HTTPS или API, есть поддержка шифрования дампов. Проект активно развивается (last release буквально за месяц до написания этого текста). Использует java environment, могут быть проблемы с установкой и работой (удаление файлов).
Leave a Reply Cancel reply
You must be logged in to post a comment.
Как разобрать rtp пакеты python
в выводе команды kubectl get service получаю:
nginx-app-service NodePort 10.108.20.23 80:30438/TCP
Если кто знает, подскажите пожалуйста, почему кубер назначил мне порт 30438 по которому я могу достучатся до nginx из вне и могу ли я как то повлиять и назначить тот порт, который я сам хочу? Например хочу запустить в ноде какой нибудь контейнер и стучаться в него из внешней сети при наличии белого ip.
Прошу не кидаться какашками и не отправлять читать мануалы, но если хочется, то кидайтесь и отправляйте конечно)
ps: Если кто-то посоветует литературу или ресурсы по куберу для тупых, напишите пожалуйста.
Показать полностью
6 месяцев назад
Как создать и настроить свой VPN-сервер
Выбираем хостинг
Для настройки VPN нужен VPS — виртуальный частный сервер. Вы можете выбрать любого хостинг‑провайдера, главное, чтобы выполнялись следующие условия:
- Сервер находится в стране, достаточно близкой к вашему реальному местоположению.
- Оперативной памяти (RAM) должно быть не меньше 512 МБ.
- Скорость сетевого интерфейса — 100 МБ/сек и выше.
- Сетевой трафик — 512 ГБ и выше или неограниченный.
Количество выделенного места на жёстком диске и тип накопителя не имеет значения. Найти подходящее решение можно за 3–4 доллара в месяц. Вот несколько самых популярных VPS‑провайдеров:
При покупке сервера выбирайте KVM. OpenVZ и Xen тоже подойдут, если у них подключён TUN — об этом нужно спросить в технической службе хостинг‑провайдера.
С KVM никаких дополнительных манипуляций производить не придётся, хотя некоторые хостинг‑провайдеры и на нём могут ограничить возможность создания VPN. Уточнить это можно также в службе поддержки.

При настройке сервера в пункте «Имя хоста» можно вписать любое значение: например, test.test. Префиксы NS1 и NS2 тоже не важны: пишем ns1.test и ns2.test.
Операционная система — CentOS 8 64 bit или любой другой дистрибутив, принципиальных отличий в настройке нет. Сетевой трафик оставьте 512 ГБ или выберите дополнительный объём, если боитесь, что имеющегося не хватит. Локация — чем ближе, тем лучше. Нидерланды подойдут.

После оплаты на почту придёт письмо со всеми необходимыми данными для настройки VPN. Вы приобрели место на сервере другой страны, осталось перенаправить на него весь трафик.
Настраиваем VPN
Для подключения к серверу и отправки команд мы будем использовать программу Putty. Мне ссылка на неё пришла в письме с регистрационными данными для хостинга. Скачать программу можно здесь. Putty и её аналоги есть и на macOS, настройки будут идентичными.
Запустите Putty. На вкладке Session в поле Host Name введите IP‑адрес, который пришёл в письме, и нажмите Open.

При появлении окна с предупреждением об опасности нажмите «Да». После этого запустится консоль, через которую вы будете отправлять команды серверу. Для начала нужно залогиниться — данные для авторизации также есть в письме от хостера. Логин будет root, его напечатайте руками. Пароль скопируйте в буфер обмена. Для вставки пароля в консоль щёлкните правой кнопкой и нажмите Enter. Пароль не отобразится в консоли, но если вы авторизовались, то увидите информацию о системе или номер сервера.

Между вводом логина и пароля не должно пройти много времени. Если появилось сообщение об ошибке, перезапустите Putty и попробуйте ещё раз.
Для настройки VPN я использовал готовый скрипт OpenVPN road warrior. Этот способ не гарантирует полную анонимность, так что при совершении противозаконных действий пользователя легко найти. Но для стандартных нужд его достаточно. Если все VPN‑сервисы перестанут работать, это подключение продолжит функционировать, пока я плачу за хостинг.
Чтобы использовать скрипт, вставьте в консоль строчку wget https://git.io/vpn -O openvpn‑ install.sh && bash openvpn‑ install.sh .

После успешного добавления скрипта запустится диалог с мастером настройки. Он самостоятельно находит оптимальные значения, вам останется только согласиться или выбрать подходящий вариант. Все действия подтверждаются нажатием клавиши Enter. Пойдём по порядку:
- IP‑адрес должен совпадать с IP‑адресом, который вы получили в письме от хостера.
- Протокол оставьте по умолчанию UDP.
- Port:1194 — согласитесь.
- Какой DNS использовать — выберите Google. Сотрите 1, напишите 3 и нажмите Enter.
- Client name — укажите имя пользователя. Можно оставить client.
- Press any key — ещё раз нажмите Enter и дождитесь окончания настройки.
После завершения настройки необходимо создать файл, через который вы будете подключаться к VPN. Введите команду cat ~/client.ovpn.
В консоли появится содержимое файла. Прокрутите экран наверх к команде cat ~/client.ovpn и выделите всё, что появилось ниже, кроме последней строчки. Выделение должно закончиться на . Для копирования фрагмента нажмите Ctrl + V.
Запустите «Блокнот», вставьте скопированный фрагмент и сохраните файл на рабочем столе с именем client.ovpn.
Откройте меню «Файл», выберите «Сохранить как», установите тип «Все файлы» и введите имя с расширением — client.ovpn.
Подключаемся к серверу
Для подключения с помощью созданного файла нужен клиент OpenVPN. Версию для компьютера можно скачать здесь. Загрузите и установите программу, но не запускайте. Щёлкните правой кнопкой по файлу client.ovpn и выберите пункт Start OpenVPN.
Появится окно консоли с инициализацией соединения. Если подключение прошло успешно, внизу будет статус Initialization Sequence Completed. В процессе соединения может появиться окно выбора сети, нажмите на общественную сеть.
Чтобы убедиться, что подключение установлено верно, проверьте IP‑адрес. Он должен совпадать с тем, который хостер написал в письме. Чтобы перестать направлять запросы на сервер в другой стране, закройте окно OpenVPN.
Показать полностью 5
6 месяцев назадПомогите с смс-рассылкой со своих мощностей
Извините, что не про сиськи.
Наверняка тут знают всё. Возникла небольшая задача: разослать смс работникам предприятия по списку. Ну, например, «С новым годом» или «Офис захватили марсиане, пожалуйста, не приходите сегодня». Отправка через веб-форму и сервис упиратора были бы идеальным решением, но нет. Нужно, чтобы самостоятельно и на своих мощностях: имеется риск, что у отправителя не будет доступа к интернетам, например.
Идея простая: человек отправляет сообщение на номер сервера, тот получает, смотрит в список разрешенных, и, если номер там есть, распыляет сообщение по списку получателей. Возможно, парсит сообщение — если сообщение начинается с тэга ALL — пересылает сообщение всем вообще, если с тэга HR — только в службу по борьбе с персоналом и тогдалие.
Всякие линуксы, винсерверы, сотовые модемы в хозяйстве имеются. Ноклы 3310 нет, к сожалению (читал про всякие решения на ее основе).
Руки вроде бы тоже не из жопы. Идей, как это все сделать — ни одной, увы.
Заранее большое спасибо.Удаленный доступ к IP камерам, теперь на Python

Удивительно, что в 2021-м все еще можно обсуждать такую избитую тему. Однако, мне пришлось пройти довольно длинный путь от покупки охранных камер до готового решения, покрывающего мои, довольно нехитрые, задачи. Под катом вы найдете скрипт, который показался мне достаточно удачным, чтобы опубликовать его на Хабре, и некоторые пояснения к нему. Надеюсь, кому-то поможет.
Немного предыстории: мне достались IP камеры Rubetek RV-3414 и Hikvision DS-2CD2023. Мне нужно было организовать оперативный доступ к ним с мобильных устройств и непрерывную запись на жесткий диск. В качестве видеорегистратора и медиасервера я использую Intel NUC младшей модели из-за низкого потребления энергии, что сильно увеличивает автономность всей системы. Обе камеры работают по протоколу RTSP — Real Time Streaming Protocol. Теперь обо всем по порядку.
Инструкции производителей.
Первое, что я сделал после установки камер, — подключил их согласно прилагаемым инструкциям. И тут началось приключение, полное сюрпризов и неожиданных поворотов.
Rubetek предложил проприетарный софт для просмотра видеопотока, который работает с оборудованием собственного производства (и почти не глючит). Но при подключении нескольких клиентов к камере это чудо техники почему-то отказывается отдавать поток на основное устройство (у меня это мобильный телефон на Андроиде). Ладно, не больно то и хотелось, все равно держать зоопарк программ под каждую камеру я не собирался. Устанавливаю на телефон VLC — работает.
Hikvision порадовал меня тем, что не требует ничего скачивать, и у камеры есть веб интерфейс для настройки. Инструкция предлагает открыть указанный IP адрес в IE, Chrome или Firefox’е. Открываю в Windows 11:

Требует обновить Edge до IE. Смешно. В 11-й, согласно официальным данным, нет возможности установить IE, зато есть режим IE (на скриншоте). Включаю — не открывается. Chrome, FF — то же самое, Linux — то же самое. Весело. Устанавливаю Win 7 в виртуалку, настраиваю камеру. Без комментариев. Справедливости ради должен сказать, что к работе самой камеры у меня претензий нет.
Удаленный доступ.
Наконец, пришло время настроить удаленный просмотр видеопотоков. Мои камеры подключены к роутеру с «серым» IP адресом, подключится извне не получится. Самый простой выход из этой ситуации (не считая покупки IP) — прокинуть SSH туннель до ближайшего сервера с «белым» IP:
ssh -NT -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes -R :: @
Команда выполняется на сервере в локальной сети. Все запросы на : будут проброшены на :, т. е. на камеру. Но чтобы удаленный сервер слушал сетевой интерфейс, придется разрешить проброс строкой «GatewayPorts yes» в sshd_config. Это не безопасно, делать так не надо. Кроме того, логин/пароль от камеры в этом случае гуляет по интернету в открытом виде и легко может быть скомпрометирован.
К счастью, существуют готовые решения на этот случай, например rtsp-simple-server. Отличный скрипт на Go, покрывает почти все мои потребности. Но хочется больше контроля и больше функционала, а значит, придется разбираться в деталях.
Техзадание.
Опыт, полученный до этого момента, помог мне сформулировать требования к будущему серверу.
- Надежное подключение клиентов в локальной сети. Оперативное подключение к камерам должно работать железобетонно. По моим наблюдениям, камеры глючит при большом числе подключений (большое — это больше одного — двух). Поэтому в идеале нужно организовать одно подключение к каждой камере, независимо от количества клиентов.
- Минимальная задержка подключения клиентов, как минимум, первого локального.
- Низкая нагрузка на процессор. Для меня это важно, потому что иначе мой Intel NUC начнет есть батарею бесперебойника и противно шуршать охлаждающей турбинкой.
- Проксирование потоков с IP камер неограниченному количеству клиентов в локальной сети и ограничение количества веб клиентов.
- Запись на жесткий диск с разбивкой на фрагменты и суточной ротацией.
- Восстановление соединения с камерами и записи на диск после отключения камер. Рано или поздно отключение произойдет обязательно, например, из-за перебоя питания.
- Подключения нужно логировать.
Ну все, ТЗ есть, приступаю к реализации.
Сбор данных.
Для начала нужно взглянуть, как VLC общается с камерами. Включаю Wireshark с фильтром tcp.port == 554 и вижу:
Ask: OPTIONS rtsp://192.168.0.114:554/onvif1 RTSP/1.0
CSeq: 2
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2021.08.24)Reply: RTSP/1.0 200 OK
CSeq: 2
Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER,USER_CMD_SETAsk: DESCRIBE rtsp://192.168.0.114:554/onvif1 RTSP/1.0
CSeq: 3
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2021.08.24)
Accept: application/sdpReply: RTSP/1.0 200 OK
CSeq: 3
Content-Type: application/sdp
Content-Length: 422v=0
o=- 1421069297525233 1 IN IP4 192.168.0.113
s=H.264 Video, RtspServer_0.0.0.2
t=0 0
a=tool:RtspServer_0.0.0.2
a=type:broadcast
a=control:*
a=range:npt=0-
m=video 0 RTP/AVP 96
c=IN IP4 0.0.0.0
b=AS:500
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;profile-level-id=42001F;sprop-parameter-sets=Z0IAH5WoFAFuQA==,aM48gA==
a=control:track1
m=audio 0 RTP/AVP 8
a=control:track2
a=rtpmap:8 PCMA/8000Ask: SETUP rtsp://192.168.0.114:554/onvif1/track1 RTSP/1.0
CSeq: 4
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2021.08.24)
Transport: RTP/AVP;unicast;client_port=45150-45151Reply: RTSP/1.0 200 OK
CSeq: 4
Transport: RTP/AVP;unicast;destination=192.168.0.165;source=192.168.0.113;client_port=45150-45151;server_port=7060-7061
Session: 7c2467db;timeout=60Ask: SETUP rtsp://192.168.0.114:554/onvif1/track2 RTSP/1.0
CSeq: 5
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2021.08.24)
Transport: RTP/AVP;unicast;client_port=35736-35737
Session: 7c2467dbReply: RTSP/1.0 200 OK
CSeq: 5
Transport: RTP/AVP;unicast;destination=192.168.0.165;source=192.168.0.113;client_port=35736-35737;server_port=7062-7063
Session: 7c2467db;timeout=60Ask: PLAY rtsp://192.168.0.114:554/onvif1 RTSP/1.0
CSeq: 6
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2021.08.24)
Session: 7c2467db
Range: npt=0.000-Reply: RTSP/1.0 200 OK
CSeq: 6
Range: npt=0.000-
Session: 7c2467db
RTP-Info: url=rtsp:192.168.0.113:554/onvif1/track1;seq=57651;rtptime=61388916750,url=rtsp:192.168.0.113:554/onvif1/track2;seq=58422;rtptime=5456792600*** Тут камера начинает посылать поток на указанный порт ***
Ask: TEARDOWN rtsp://192.168.0.114:554/onvif1 RTSP/1.0
CSeq: 7
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2021.08.24)
Session: 7c2467dbAsk: OPTIONS rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101 RTSP/1.0
CSeq: 2
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2021.08.24)Reply: RTSP/1.0 200 OK
CSeq: 2
Public: OPTIONS, DESCRIBE, PLAY, PAUSE, SETUP, TEARDOWN, SET_PARAMETER, GET_PARAMETER
Date: Mon, Nov 22 2021 09:57:17 GMTAsk: DESCRIBE rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101 RTSP/1.0
CSeq: 3
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2021.08.24)
Accept: application/sdpReply: RTSP/1.0 401 Unauthorized
CSeq: 3
WWW-Authenticate: Digest realm=»IP Camera(G2669)», nonce=»17215f510ab5085c7aef996a1d42769f», stale=»FALSE»
Date: Mon, Nov 22 2021 09:57:17 GMTAsk: DESCRIBE rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101 RTSP/1.0
CSeq: 4
Authorization: Digest username=»login», realm=»IP Camera(G2669)», nonce=»17215f510ab5085c7aef996a1d42769f», uri=»rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101″, response=»69ce13d857b38e6e68f7f5a4a85cd709″
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2021.08.24)
Accept: application/sdpReply: RTSP/1.0 200 OK
CSeq: 4
Content-Type: application/sdp
Content-Base: rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101/
Content-Length: 587v=0
o=- 1637575037561170 1637575037561170 IN IP4 192.168.0.110
s=Media Presentation
e=NONE
b=AS:5050
t=0 0
a=control:rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101/
m=video 0 RTP/AVP 96
c=IN IP4 0.0.0.0
b=AS:5000
a=recvonly
a=x-dimensions:1920,1080
a=control:rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101/trackID=1
a=rtpmap:96 H265/90000
a=fmtp:96 sprop-sps=QgEBAWAAAAMAsAAAAwAAAwB7oAPAgBDljb5JMvTcBAQEAg==; sprop-pps=RAHA8vA8kAA=
a=Media_header:MEDIAINFO=494D4B48010300000400050000000000000000000000000081000000000000000000000000000000;
a=appversion:1.0Ask: SETUP rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101/trackID=1 RTSP/1.0
CSeq: 5
Authorization: Digest username=»login», realm=»IP Camera(G2669)», nonce=»17215f510ab5085c7aef996a1d42769f», uri=»rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101/», response=»2341d81156d9cee08db0004835486f51″
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2021.08.24)
Transport: RTP/AVP;unicast;client_port=59446-59447Reply: RTSP/1.0 200 OK
CSeq: 5
Session: 695167870;timeout=60
Transport: RTP/AVP;unicast;client_port=59446-59447;server_port=8302-8303;ssrc=568ed713;mode=»play»
Date: Mon, Nov 22 2021 09:57:17 GMTAsk: PLAY rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101/ RTSP/1.0
CSeq: 6
Authorization: Digest username=»login», realm=»IP Camera(G2669)», nonce=»17215f510ab5085c7aef996a1d42769f», uri=»rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101/», response=»a2a71ba4866e2f77d14f7368f368da5f»
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2021.08.24)
Session: 695167870
Range: npt=0.000-Reply: RTSP/1.0 200 OK
CSeq: 6
Session: 695167870
RTP-Info: url=rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101/trackID=1;seq=54784;rtptime=2171307498
Date: Mon, Nov 22 2021 09:57:17 GMTAsk: GET_PARAMETER rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101/ RTSP/1.0
CSeq: 7
Authorization: Digest username=»login», realm=»IP Camera(G2669)», nonce=»17215f510ab5085c7aef996a1d42769f», uri=»rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101/», response=»192d15433a0964eb2782026d8e908ed3″
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2021.08.24)
Session: 695167870Reply: RTSP/1.0 200 OK
CSeq: 7
Date: Mon, Nov 22 2021 09:58:15 GMTAsk: GET_PARAMETER rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101/ RTSP/1.0
CSeq: 9
Authorization: Digest username=»login», realm=»IP Camera(G2669)», nonce=»17215f510ab5085c7aef996a1d42769f», uri=»rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101/», response=»192d15433a0964eb2782026d8e908ed3″
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2021.08.24)
Session: 695167870Reply: RTSP/1.0 200 OK
CSeq: 9
Date: Mon, Nov 22 2021 10:00:11 GMT*** Тут камера начинает посылать поток на указанный порт ***
Ask: TEARDOWN rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101/ RTSP/1.0
CSeq: 16
Authorization: Digest username=»login», realm=»IP Camera(G2669)», nonce=»17215f510ab5085c7aef996a1d42769f», uri=»rtsp://192.168.0.110:554/ISAPI/Streaming/Channels/101/», response=»e6ef5c8c7ab615db158e7e77c8f7b77a»
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2021.08.24)
Session:Reply: RTSP/1.0 200 OK
CSeq: 16
Session: 695167870
Date: Mon, Nov 22 2021 10:06:25 GMTОбщение происходит по протокол RTSP, который достаточно хорошо описан в интернете, поэтому останавливаться на нем я не вижу смысла. А вот о различиях стоит сказать пару слов.
Первая камера не требует авторизации и отдает картинку всем желающим, вторая же требует дайджест-аутентификации (строка WWW-Authenticate: Digest в листинге). Поэтому на первый запрос DESCRIBE она отвечает 401 Unauthorized, но дает ключи шифрования realm и nonce. Имея эти ключи, в следующем запросе нужно указать параметр response, который вычисляется так:
где login:password — логин/пароль от камеры, option — текущий метод.
Еще одно важное отличие состоит в наличии у первой камеры микрофона, поэтому клиент запрашивает SETUP дважды: для видео (track1) и звуковой дорожки (track2).
Для каждой дорожки VLC запрашивает client_port, куда камера будет посылать данные по протоколу UDP.Скрипт.
Теперь, пожалуй, данных достаточно, чтобы написать первую версию сервера. Писать я буду на «чистом» Python 3.7+, без сторонних зависимостей. Поскольку все подключения и потоки должны обрабатываться параллельно, неплохо бы использовать библиотеку asyncio.
Идея скрипта предельно проста: один раз подключиться к каждой камере, запомнить ее параметры и отвечать клиентам ровно то же самое (за исключением мелких деталей). Видеопоток нужно раздавать (проксировать) клиентам по мере их подключения.
Ниже приведен листинг минимальной работающей версии сервера, исключительно для облегчения понимания. Полная версия доступна на Github.
import asyncio from config import Config from camera import Camera from client import Client async def main(): for hash in Config.cameras.keys(): await Camera(hash).connect() await Client.listen() if __name__ == '__main__': asyncio.run(main())Здесь я сразу подключаю все указанные в конфиге камеры и начинаю слушать TCP порт 4554. В реальной жизни делать так не нужно — клиентов ведь может и не быть, а электричество надо экономить:)
import socket class Config: cameras = < 'хеш/любая-URL-совместимая-строка': < 'path': 'относительный путь к хранилищу', 'url': 'rtsp://:@:554/'>, > rtsp_port = 4554 start_udp_port = 5550 local_ip = socket.gethostbyname(socket.gethostname()) # Ограничить число веб-клиентов web_limit = 2 log_file = '/var/log/python-rtsp-server.log'Доступ к камерам я буду предоставлять по адресам вида rtsp://:/, где хеш камеры — любая URL-совместимая строка, включая символы UTF. Хеши и реальные адреса камер нужно указать в словаре cameras.
import asyncio import re import time from hashlib import md5 from shared import Shared from config import Config from log import Log class Camera: def __init__(self, hash): self.hash = hash self.url = self._parse_url(Config.cameras[hash]['url']) async def connect(self): """ Открываем TCP сокет и подключаем камеру """ self.udp_ports = self._get_self_udp_ports() self.cseq = 1 self.realm, self.nonce = None, None try: self.reader, self.writer = await asyncio.open_connection(self.url['host'], self.url['tcp_port']) except Exception as e: print(f"Can't connect to camera []: ") return await self._request('OPTIONS', self.url['url']) reply, code = await self._request( 'DESCRIBE', self.url['url'], 'User-Agent: python-rtsp-server', 'Accept: application/sdp') if code == 401: self.realm, self.nonce = self._get_auth_params(reply) reply, code = await self._request( 'DESCRIBE', self.url['url'], 'Accept: application/sdp') self.description = self._get_description(reply) track_ids = self._get_track_ids(reply) reply, code = await self._request( 'SETUP', f'/', ('Transport: RTP/AVP;unicast;' f'client_port=-')) self.session_id = self._get_session_id(reply) if len(track_ids) > 1: reply, code = await self._request( 'SETUP', f'/', ('Transport: RTP/AVP;unicast;' f'client_port=-'), f'Session: ') reply, code = await self._request( 'PLAY', self.url['url'], f'Session: ', 'Range: npt=0.000-') Shared.data[self.hash] = < 'description': self.description, 'rtp_info': self._get_rtp_info(reply), # 'transports': <>, 'clients': <>> Log.add(f'Camera [] connected') await self._listen() async def _listen(self): """ Открываем UDP сокет и начинаем проксировать фреймы """ await self._start_server('track1') if self.description['audio']: await self._start_server('track2') async def _request(self, option, url, *lines): """ Запрос к камере с заданным OPTION и другими строками. Возвращает декодированный ответ и статус """ command = f' RTSP/1.0\r\n' \ f'CSeq: \r\n' auth_line = self._get_auth_line(option) if auth_line: command += f'\r\n' for row in lines: if row: command += f'\r\n' command += '\r\n' print(f'*** Ask:\n') self.writer.write(command.encode()) reply = (await self.reader.read(4096)).decode() print(f"*** Reply:\n") self.cseq += 1 res = re.match(r'RTSP/1.0 (\d) ([^\r\n]+)', reply) if not res: print('Error: invalid reply\n') return reply, 0 return reply, int(res.group(1)) def _get_auth_params(self, reply): """ Достать параметры realm и nonce для "digest" авторизации """ realm_nonce = re.match(r'.+?\nWWW-Authenticate:.+?realm="(.+?)", ?nonce="(.+?)"', reply, re.DOTALL) if not realm_nonce: raise RuntimeError('Invalid digest auth reply') return realm_nonce.group(1), realm_nonce.group(2) def _get_auth_line(self, option): """ Собрать "response" хеш авторизации """ if not self.realm or not self.nonce: return ha1 = md5(f'::'.encode('utf-8')).hexdigest() ha2 = md5(f':'.encode('utf-8')).hexdigest() response = md5(f'::'.encode('utf-8')).hexdigest() line = f'Authorization: Digest username="", ' \ f'realm="" nonce="", uri="", response=""' return line def _get_description(self, reply): """ Достать SDP (Session Description Protocol) из ответа """ blocks = reply.split('\r\n\r\n', 2) if len(blocks) < 2: raise RuntimeError('Invalid DESCRIBE reply') sdp = blocks[1].strip() details = , 'audio': <>> res = re.match(r'.+?\nm=video (.+?)\r\n', sdp, re.DOTALL) if res: details['video'] = res = re.match(r'.+?\nm=video .+?\nb=([^\r\n]+)', sdp, re.DOTALL) if res: details['video']['bandwidth'] = res.group(1) res = re.match(r'.+?\nm=video .+?\na=rtpmap:([^\r\n]+)', sdp, re.DOTALL) if res: details['video']['rtpmap'] = res.group(1) res = re.match(r'.+?\nm=video .+?\na=fmtp:([^\r\n]+)', sdp, re.DOTALL) if res: details['video']['format'] = res.group(1) res = re.match(r'.+?\nm=audio (.+?)\r\n', sdp, re.DOTALL) if res: details['audio'] = res = re.match(r'.+?\nm=audio .+?\na=rtpmap:([^\r\n]+)', sdp, re.DOTALL) if res: details['audio']['rtpmap'] = res.group(1) return details def _get_rtp_info(self, reply): """ Достать строку "RTP-Info" из ответа """ res = re.match(r'.+?\r\n(RTP-Info: .+?)\r\n', reply, re.DOTALL) if not res: raise RuntimeError('Invalid RTP-Info') rtp_info = res.group(1) seq = re.findall(r';seq=(\d+)', rtp_info) rtptime = re.findall(r';rtptime=(\d+)', rtp_info) if not seq or not rtptime: raise RuntimeError('Invalid RTP-Info') return def _get_track_ids(self, reply): """ Достать ID дорожек из ответа """ track_ids = re.findall(r'\na=control:.*?(track.*?\d)', reply, re.DOTALL) if not track_ids: raise RuntimeError('Invalid track ID in reply') return track_ids def _get_session_id(self, reply): """ Достать ID сессии из ответа """ res = re.match(r'.+?\nSession: *([^;]+)', reply, re.DOTALL) if not res: raise RuntimeError('Invalid session ID') return res.group(1) def _get_self_udp_ports(self): """ Получить свободный динамический UDP порт """ start_port = Config.start_udp_port idx = list(Config.cameras.keys()).index(self.hash) * 4 return < 'track1': [start_port + idx, start_port + idx + 1], 'track2': [start_port + idx + 2, start_port + idx + 3]>def _parse_url(self, url): """ Разобрать url камеры на части """ parsed_url = re.match(r'(rtsps?)://(.+?):([^@]+)@(.+?):(\d+)(.+)', url) if not parsed_url or len(parsed_url.groups()) != 6: raise RuntimeError('Invalid rtsp url') return < 'login': parsed_url.group(2), 'password': parsed_url.group(3), 'host': parsed_url.group(4), 'tcp_port': int(parsed_url.group(5)), 'url': url.replace(f':@', '')> async def _start_server(self, track_id): """ Запустить UDP сервер """ loop = asyncio.get_running_loop() await loop.create_datagram_endpoint( lambda: CameraUdpProtocol(self.hash, track_id), local_addr=('0.0.0.0', self.udp_ports[track_id][0])) class CameraUdpProtocol(asyncio.DatagramProtocol): """ Этот колбэк вызывается при подключении к каждой камере """ def __init__(self, hash, track_id): self.hash = hash self.track_id = track_id def connection_made(self, transport): self.transport = transport def datagram_received(self, data, addr): if not Shared.data[self.hash]['clients']: return for _sid, client in Shared.data[self.hash]['clients'].items(): self.transport.sendto(data, (client['host'], client['ports'][self.track_id][0]))Обслуживает камеры. Единственный публичный метод Camera.connect тривиально реализует подключение по протоколу RTSP, описанное в листингах выше. Для каждой подключенной камеры после команды PLAY запускается сервер asyncio.create_datagram_endpoint(), проксирование входящих потоков происходит в колбэке CameraUdpProtocol.
# https://docs.python.org/3/library/asyncio-protocol.html import asyncio import re import string import time from random import choices, randrange from config import Config from shared import Shared from log import Log class Client: def __init__(self): self.camera_hash = None self.udp_ports = <> @staticmethod async def listen(): """ Слушаем здесь подключения всех клиентов """ host = '0.0.0.0' print(f'*** Start listening : ***\n') loop = asyncio.get_running_loop() server = await loop.create_server( lambda: ClientTcpProtocol(), host, Config.rtsp_port) async with server: await server.serve_forever() def handle_request(self, transport, host, data): """ Общяемся с клиентами по протоколу RTSP """ ask, option = self._request(data) session_id = self._get_session_id(ask) if option == 'OPTIONS': self._response( transport, 'Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY') if option == 'DESCRIBE': sdp = self._get_description() self._response( transport, 'Content-Type: application/sdp', f'Content-Length: ', '', sdp) elif option == 'SETUP': udp_ports = self._get_ports(ask) track_id = 'track1' if not self.udp_ports else 'track2' self.udp_ports[track_id] = udp_ports self._response( transport, f'Transport: RTP/AVP;unicast;client_port=-;server_port=5998-5999', f'Session: ;timeout=60') elif option == 'PLAY': self._response( transport, f'Session: ', self._get_rtp_info()) # if session_id not in Shared.data[self.camera_hash]['clients']: Shared.data[self.camera_hash]['clients'][session_id] = < 'host': host, 'ports': self.udp_ports, 'transport': transport>self._check_web_limit(host) Log.add(f'Play [] [] []') elif option == 'TEARDOWN': self._response(transport, f'Session: ') return self.camera_hash, session_id def _get_rtp_info(self): """ Строим строку "RTP-Info" для клиента, изменив время rtptime для корректной работы счетчика. По хорошему, надо просто запросить камеру, но в режиме TCP (interleaved) им это не нравится. """ rtp_info = Shared.data[self.camera_hash]['rtp_info'] print(rtp_info) delta = time.time() - rtp_info['starttime'] rtptime = int(rtp_info["rtptime"][0]) + int(delta * 90000) # 90000 is clock frequency in SDP a=rtpmap:96 H26*/90000 res = f'RTP-Info: url=rtsp://:/track1;' \ f'seq=;rtptime=' if len(rtp_info['seq']) < 2: return res rtptime = int(rtp_info["rtptime"][1]) + int(delta * 8000) # 90000 is clock frequency in SDP a=rtpmap:8 PCMA/8000 res += f',url=rtsp://:/track2;' \ f'seq=;rtptime=' return res def _request(self, data): """ Разбираем ответ клиента """ try: ask = data.decode() except Exception: raise RuntimeError(f"can't decode this ask:\n") print(f'*** Ask:\n') # res = re.match(r'(.+?) rtsps?://.+?:\d+/(.+?)(/track.*?)? .+?\r\n', ask) res = re.match(r'(.+?) rtsps?://.+?:\d+/?(.*?) .+?\r\n', ask) if not res: raise RuntimeError('invalid ask') self.cseq = self._get_cseq(ask) if not self.camera_hash: hash = res.group(2) if hash not in Config.cameras: raise RuntimeError('invalid camera hash') if hash not in Shared.data: raise RuntimeError('camera is offline') self.camera_hash = hash return ask, res.group(1) def _response(self, transport, *lines): """ Отдаем клиенту данные строки """ reply = 'RTSP/1.0 200 OK\r\n' \ f'CSeq: \r\n' for row in lines: reply += f'\r\n' reply += '\r\n' transport.write(reply.encode()) print(f'*** Reply:\n') def _get_cseq(self, ask): """ Текущий счетчик из запроса клиента """ res = re.match(r'.+?\r\nCSeq: (\d+)', ask, re.DOTALL) if not res: raise RuntimeError('invalid incoming CSeq') return int(res.group(1)) def _get_session_id(self, ask): """ ID сессии из запроса клиента """ res = re.match(r'.+?\nSession: *([^;\r\n]+)', ask, re.DOTALL) if res: return res.group(1).strip() return ''.join(choices(string.ascii_lowercase + string.digits, k=9)) def _get_ports(self, ask): """ Номера портов из запроса клиента """ res = re.match(r'.+?\nTransport:[^\n]+client_port=(\d+)-(\d+)', ask, re.DOTALL) if not res: raise RuntimeError('invalid transport ports') return [int(res.group(1)), int(res.group(2))] def _get_description(self): """ Блок SDP из запроса клиента """ sdp = Shared.data[self.camera_hash]['description'] res = 'v=0\r\n' \ f'o=-IN IP4 \r\n' \ 's=python-rtsp-server\r\n' \ 't=0 0' if not sdp['video']: return res res += f'\r\nm=video \r\n' \ 'c=IN IP4 0.0.0.0\r\n' \ f'b=\r\n' \ f'a=rtpmap:\r\n' \ f'a=fmtp:\r\n' \ 'a=control:track1' if not sdp['audio']: return res res += f'\r\nm=audio \r\n' \ f'a=rtpmap:\r\n' \ 'a=control:track2' return res def _check_web_limit(self, host): """ Ограничим веб подключения. Локальные - без ограничений. """ if not Config.web_limit or self._get_client_type(host) == 'local': return web_sessions = [] for session_id, data in Shared.data[self.camera_hash]['clients'].items(): if self._get_client_type(data['host']) == 'web': web_sessions.append(session_id) if len(web_sessions) > Config.web_limit: ws = web_sessions[:-Config.web_limit] for session_id in ws: print('Web limit exceeded, cloce old connection\n') Shared.data[self.camera_hash]['clients'][session_id]['transport'].close() # Shared.data item will be deleted by ClientTcpProtocol.connection_lost callback def _get_client_type(self, host): """ Хелпер для определения типа подключения. Если IP клиента совпадает с локальным адресом сервера, то это веб клиент за ssh туннелем """ if host == '127.0.0.1' \ or host == 'localhost' \ or (host.startswith('192.168.') and host != Config.local_ip): return 'local' return 'web' class ClientTcpProtocol(asyncio.Protocol): """ Этот колбэк вызывается при подключении к серверу каждого нового клиента """ def __init__(self): self.client = Client() self.camera_hash, self.session_id = None, None # self.event = event def connection_made(self, transport): peername = transport.get_extra_info('peername') self.transport = transport self.host = peername[0] print(f'*** New connection from : ***\n\n') def data_received(self, data): try: self.camera_hash, self.session_id = self.client.handle_request(self.transport, self.host, data) except Exception as e: print(f'Error in clent request handler: \n\n') self.transport.close() def connection_lost(self, exc): if not self.session_id or self.session_id not in Shared.data[self.camera_hash]['clients']: return del(Shared.data[self.camera_hash]['clients'][self.session_id]) Log.add(f'Close [] [] []') Обслуживает клиентов. Metod Client.handle_request делает то же, что и Camera.connect, но на этот раз прикинувшись камерой. За прослушивание порта 4554 отвечает asyncio.create_server (один на всех) и его колбэк ClientTcpProtocol (по экземпляру на каждого клиента).
class Shared: """ Все задачи (tasks) будут общаться через этот объект """ data = <>Объект Sared.data используется для обмена данными между задачами (tasks) и корутинами.
import time from config import Config class Log: @staticmethod def add(info): print(f'*** ***\n\n') try: with open(Config.log_file, 'a') as f: f.write(f'\n') except Exception as e: print(f'Log error: \n\n') pass Отвечает за логирование подключений, а заодно и за вывод отладочной информации.
Тестирование.
Интересно, что «медленный» Python может противопоставить высокоэффективному многопоточному Go? На скриншотах представлены результаты тестирования приведенного выше скрипта и rtsp-simple-server. Условия равные, одна камера и 10 клиентов в каждом тесте. Сервер легко поднимает и большее количество клиентов, но вот мой десктоп начинает дико тормозить при запуске нескольких десятков окон. Выпадений кадров на глаз не заметно в обоих тестах.


Судите сами. На мой вкус, неплохо. Есть смысл продолжить и довести скрипт до логического конца.
Что дальше?
В реальной жизни, к сожалению, все немного сложнее, чем в теории. Получить UDP пакеты через упомянутый выше SSH туннель не получится. Да и вообще, гонять UDP по интернету кажется мне не самой лучшей идеей. Значит, придется реализовывать получение потоков через TCP.
Если верить показаниям моего WireShark’а, различие в общении VLC с камерами в режиме TCP (с ключом —rtsp-tcp) сводится к изменению строки
в запросе SETUP. Как видите, здесь добавляется параметр TCP, а вместо портов клиента указываются каналы чередования interleaved=0-1 (для звуковой дорожки interleaved=2-3). Входящий поток в этом случае будет направлен в тот же TCP порт, фреймы с бинарными данными будут начинаться со знака доллара, за которым следует номер канала. Реализовать такое немного сложнее, так как нужно поиграть в чехарду на единственном сокете, слушающим TCP порт.
Версия на Github содержит простую реализацию чередования, детали можно посмотреть там.
Кроме того, в ТЗ входит видеорегистратор и watchdog к нему — все это находится в файле storage.py и слишком тривиально для обсуждения.
Резюме.
Похоже, полученный результат удовлетворяет всем заявленным требованиям. Во всяком случае, я использую этот скрипт на своих серверах без проблем, правда, в ограниченном окружении.
Обратите внимание, приведенный код не готов к промышленной эксплуатации, но может послужить отправной точкой и сэкономить массу времени тем, кто захочет получить больше контроля над своими камерами видеонаблюдения.
Вопросы, пожелания и конструктивная критика приветствуются!
Базовые принципы IP — телефония

Большинство организаций заинтересованы в улучшении своей бизнес инфраструктуры. Телефония на базе IP – это комплексное масштабируемое решение, которое позволяет компании снизить расходы, повысить лояльность клиентов и улучшить KPI (Key Performance Indicators).
Что же такое «IP — телефония»?
IP — телефония — это набор протоколов, алгоритмов и соглашений об обслуживании (QoS).
Среди протоколов можно выделить протокол сигнализации и протоколы, которые переносят сами медиа данные (голос). Сигнализации в терминологии телефонии, это набор событий и логика их обработки. К таким событиям можно отнести установление телефонного соединения, удержание, трансфер, завершение вызова и т.д. Хочется отметить, что сигнализацией между двумя телефонами всегда управляет телефонная станция. Перенос самих медиа данных осуществляется напрямую между двумя телефонами.
Обратим внимание на рисунок ниже:
В данном примере телефонная станция обозначена как сервер, программное обеспечение которого занимается обработкой телефонной сигнализации. Давайте разберемся с участками сигнализации, обозначенными пунктиром.
На сегодняшний день, широко используется протокол SIP (Session Initiation Protocol), описанный в рекомендации RFC 3261. Это открытый стандарт, поддерживаемый всеми известными АТС. SIP удобен в использовании и обнаружении проблем, т.к имеет структуру, коды и синтаксис во многом схожий с протоколом HTTP. Так же в корпоративном сегменте часто встречается проприетарный SCCP (Skinny Client Control Protocol), разработанный компанией Cisco Systems, и стандарт H.323. Именно эти протоколы полностью контролируют процесс телефонного разговора.
Теперь, когда мы понимаем, что такое телефонная сигнализация, давайте посмотрим на процесс передачи медиа – потока. Эти функции выполняет протокол RTP (Real-time Transport Protocol), описанный в RFC 3550. Специально разработанный для аудио и видео данных, RTP назначает каждому пакету номер и временной тэг. Когда поток данных приходит в узел получатель, пакеты обрабатываются и декодируются в правильной последовательности. Представьте, если бы IP-пакеты приходили к получателю в не правильном порядке? Разговор вряд ли бы состоялся, т.к разобрать речь не представлялось бы возможным.
Картина начинает складываться. Под управлением протоколов сигнализации RTP поток передается от отправителя к получателю. Теперь имеет смысл подумать над тем, как обеспечить интеграцию интернет и телефонного трафика, без взаимного влияния друг на друга. В телефонии, зачастую под цели голосового трафика выделяют отдельные VLAN. Это позволяет разграничить телефонный и интернет трафик на логическом уровне. Но самым важным и надежным механизмом является QoS (Quality of Service). Это алгоритм приоритизации трафика, обработки очередей, выделений необходимой полосы пропускания, задержки, джиттера и др. В треке CCNP Voice (Cisco Certified Network Professional Voice), под QoS выделен целый экзамен. В следующих статьях мы постараемся описать данный механизм более подробно.
Хочется подвести небольшой итог: IP — телефония – это неотъемлемая часть успешного бизнеса. Мы, как команда опытных профессионалов готовы оказать вам помощь в установке, настройке офисной телефонии. Провести консультации, помочь с выбором провайдера и спроектировать оптимальное решение для уже существующего сетевого ландшафта.