«Ржавая» IP-камера: прошивка на Rust

    До появления ботнета Mirai только особо интересующиеся знали о том, что находится внутри обычных IP камер. В большинстве случаев там стоит обычный линукс, причем частенько с дефолтным рутовым паролем, а то и вообще без него: у нас в офисе стоит такая камера, с прошивкой от декабря 2016 года и беспарольным рутовым телнетом.

    Но что же дальше, какой софт запущен на этом линуксе? Есть несколько классных статей datacompboy про поиск бага которого нет, есть ещё разрозненная информация, но в целом ситуация такая: на IP-камере стоит специально пропатченное ядро, которое дает доступ программе через специальную библиотеку к железу, выдающему сжатые видеокадры.

    Грустная реальность в том, что очень часто этот софт написан далеко не лучшим образом. Достаточно сказать, что большинство камер, которые висят на улице очень страдают из-за большого расстояния до сервера, потому что авторы их прошивки освоили мастерство потерь данных по TCP.

    Мы решили исправить эту ситуацию своей прошивкой, причем сделав ставку на Rust.

    Условия работы


    Нужно сделать пару пустяков: разобраться с SDK, написать код, который настраивает железо, принимает H264 кадры и отправляет их в сеть. Пара пустяков, особенно учитывая насколько легко и просто деплоиться на IP камеры и отлаживать это всё. Ну и оставшая мелочь: мы решили писать этот код на Rust.

    Rust в качестве эксперимента был выбран за его удивительное свойство: compile time гарантию целостности памяти вместе с отсутствием рантайма. Это означает, что мы можем ожидать возможность контроля за выделением памяти, что очень важно, учитывая стесненность по ресурсам.

    Почему не подойдет Go, Erlang или какая-нибудь Java/C#? Потому что на IP камере флешка на 8 мегабайт и 128 мегабайт памяти из которых половина отнята у ядра под нужды видео. Понятно, что камеры есть разные, но всегда стараются делать по минимуму, чтобы не поднимать стоимость без нужды. На одной камере мы видели флешку на 64 мегабайта, там конечно можно развернуться, но хватает и совсем крохотных флешек.

    Так, обычная картина на дешевой камере за 3000 рублей мы видим:

    # free
                 total       used       free     shared    buffers     cached
    Mem:         60128      17376      42752          0       2708       4416
    -/+ buffers/cache:      10252      49876
    Swap:            0          0          0
    # cat /proc/cpuinfo 
    Processor	: ARM926EJ-S rev 5 (v5l)
    BogoMIPS	: 218.72
    Features	: swp half thumb fastmult edsp java 
    CPU implementer	: 0x41
    CPU architecture: 5TEJ
    CPU variant	: 0x0
    CPU part	: 0x926
    CPU revision	: 5
    
    Hardware	: hi3518
    Revision	: 0000
    Serial		: 0000000000000000
    

    В таких условиях паршиво написанный софт начинает очень сильно страдать уже от 3-4 подключений. Золотое правило при работе с IP камерами: вообще стараться больше одного подключения (или два, по одному на каждое качество) не делать и это связано не только с узким каналом до камеры, но ещё с тем, что четвертый клиент к IP камере зачастую делает невозможным просмотр у первых трех. Забегая вперед, скажу что у нас и на 50 клиентах проблем не возникло.

    Как устроена камера


    Прежде чем идти дальше, расскажу немножко о том устройстве камеры, с которым мы работаем на текущем этапе.

    На камеру напаяна SPI флешка. Это такая же флешка, как та, на которую прошивает себя в биос какой-нибудь локер. Содержимое этой SPI флешки можно прочитать, подцепившись клещами, можно записать (если повезет), с неё же считывает данные в память процессор и выполняет их. Бывает, что флешка не SPI, а NAND, тогда всё сложнее: просто так клещами не подцепишься — приходится быть ответственнее.

    В самом начале флешки находится uboot. Это загрузчик, используемый практически во всех встраиваемых устройствах: не только камеры, а роутеры и телефоны. Т.е. скорее всего можно утверждать, что экземпляров убута в мире побольше, чем экземпляров виндовса.

    У uboot открытые исходники, но в нём хранятся данные, специфичные для конкретной железки. Если скопировать флешку с камеры, сделанной фирмой XM на камеру, сделанную фирмой Hikvision, то есть большой шанс, что даже uboot не загрузится.

    Т.е. уже на этом этапе возникает увлекательный процесс ведения реестра известных камер, их учета, который очень здорово облегчается восхитительным умением наших соседей присылать ровно то, что ты заказал. В качестве хорошего примера можно привести свежую историю у наших клиентов (самый крупный национальный оператор страны), которые подписали контракт на 3 года о поставке камер конкретной модели и характеристик, после чего через неделю приехали камеры уже другой модели и с совершенно другими характеристиками.

    Но ничего страшного, всё это решаемый вопрос, двигаемся дальше.

    А дальше находится ядро линукса. Было бы слишком просто, если бы можно было одно ядро собрать для всех возможных камер и потом просто модули перетыкать. Нет, так нельзя, поэтому для разных версий чипсета нужны разные ядра: где-то 2.x.y, где-то 3.x.y. Почему так? Потому что к ядру идут закрытые модули. Где-то можно исхитриться, но всё равно унифицировать всё не получится.

    После этого идет обычный хозбытовой buildroot. Здесь всё как у людей.

    Дальше надо запустить хитрые скрипты, настраивающие железо через i2c (и возможно как-то ещё), загрузить правильные модули и стартовать специально написанный софт.

    Захват видео


    В захвате видео очень много подготовки железа. Если почитать спецификацию onvif и мануал по SDK IP камеры, то можно увидеть много общего — софтверный интерфейс отражает общую структуру большинства железяк и она следующая: видео снимается с сенсора, немножко обрабатывается, потом засовывается в энкодеры (аппаратные конечно) и потом в софтине можно забрать из определенного места в памяти готовые H264 NAL юниты. Для базового сценария осталось только приделать управление пользователями, настройки и какой-нибудь сетевой протокол. Для полноценной камеры ещё нужна поддержка всяких механизмов массовой настройки (discovery, onvif, psia, etc..) и аналитика.

    А чего там про Rust


    Вот как раз стример у нас ржавый. Целая пачка unsafe кода, автогенеренного из сишного кода SDK с помощью bindgen, подпатченый биндинг к libc (постараемся залить патч в апстрим) и дальше реализация RTSP на tokio. Даже уже есть возможность посмотреть видео с камеры в обычном браузере — это недостижимая роскошь для китайских камер, которые поголовно требуют установку ActiveX.

    Структура очень непривычна после эрланга: ведь тут нет процессов и сообщений, есть каналы, а с ними всё становится немножко по-другому. Как я уже выше писал, современно написанный код с правильной организацией дает возможность раздавать видео не 2-3 клиентам, а более 50 без какой-либо просадки производительности.

    Важный момент: за время разработки пока не случилось ни единого сегфолта. Пока есть стойкое ощущение, что Rust заставляет писать так, как в принципе пишут хорошие поседевшие сишники, повидавшие всякого нехорошего. Так что пока всё нравится.

    В течение августа есть планы закончить работу по базовому сценарию, так что есть вопрос к аудитории, который идет в опросе. Ну и задавайте вопросы, которые возникли.
    про что интереснее почитать дальше

    Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

    Метки:
    Эрливидео 324,18
    Современный видеостриминговый сервер
    Поделиться публикацией
    Похожие публикации
    Комментарии 45
    • +1
      Молодцы!
      • +6
        ща, статью откорректирую и на ICO =)
        • –5
          image
          К сожалению качеством лучше не нашел, но всё же!
      • +4
        Интересно было бы почитать о проблемах, возникающих при программировании на расте
        • +5
          хорошо, постараюсь написать. Они, конечно, есть.
        • +4

          Мяса, мяса давай! А то сплошные байки из склепа. Так-то все понятно, но со стороны — информации ноль.

        • +1

          Сделать опенсорц как хик не хотите ли?

          • 0
            а где у них опенсорсная прошивка?
            • 0

              Ух, обещали но так и нет.
              Тогда как sigrand :)

              • 0
                да и у сигранда негусто https://github.com/sigrand
                • 0
                  Сигранд тут живёт: git://sigrand.ru/sigticam.git
                  • +1
                    ты прям озадачил. Надо подумать, не будет ли здесь проблем от третьих сторон.
          • +1

            Было бы интересно про опыт использования tokio в более подробном изложении.

            • +1
              хорошо. Там есть некоторые ньюансы, связанные с tokio_io::codec и его обходом.
            • +2
              Rust очень хорош, вот прямо очень. ЛУчший язык придуманный за последние 10 лет.
              • +2
                Не могу назвать его лучшим в принципе, но в нише «плати только за то, что используешь» — таки да. Всё постельные приличные языки, изобретённые за последние 10 лет, требуют «тяжёлого» рантайма — независимо от того, какие фишки этого рантайма ты используешь, какие — нет.
                • –1
                  Назовите тогда лучший.
                  • +4

                    Ну не бывает же чего-то абсолютно лучшего, без контекста. Только в своих нишах, только при таких-то требованиях.

                    • –1
                      Это как то мешает делать выбор и сравнивать? Есть куча параметров независящих от контекста и требований.
                      • +5

                        "Лучший" — это же значит что я в любой ситуации этот язык предпочту всем остальным, так?


                        Лично мне сильно мешает — я на вопрос "назови лучший язык" вообще ничего не могу ответить. Для меня в любом случае контекстно зависимые факторы будут весомей абсолютных. Как бы я не любил ржавчину, все равно на практике я для разных типов проектов могу посчитать на данный момент более подходящими и си, и плюсы, и хаскель, и питон и еще что.

                        • –8
                          Люди когда придумывают язык, они знают о конкретных ваших ситуациях? Как же они без этого всего решения выбирают?
                          P.S. так как этот пидорский сайт не дает оставлять комментарии тогда, когдя я хочу, то дискуссию я пожалуй продолжать не буду.
              • +10
                Кому интересен Rust на маленьких микроконтроллерах (типа STM32), есть неплохой блог (на английском): http://blog.japaric.io
                • 0
                  это bare metal, без линукса?
                • 0

                  Блог — очень плохой способ структурировать редкую информацию, вроде описанной… Хорошо для апдейтов, плохо для впитывания.

                  • +2
                    Согласен. Но для тех, кто в такие темы погружается набегами, в качестве хобби, любая информация полезна. Я просто недавно искал замену C++ в для своего маленького проекта на STM32 и без этого блога я бы самостоятельно Rust на голом STM32 не осилил бы.
                    • +2

                      У Jorge Aparicio есть ещё пара вполне структурированных мини-книжек на тему bare metal: https://japaric.github.io/discovery/ для совсем начинающих и https://japaric.github.io/copper/ для продолжающих.

                  • –1
                    Очень интересно. Но очень мало :)

                    Единственный момент который меня озадачил
                    В большинстве случаев там стоит обычный линукс, причем частенько с дефолтным рутовым паролем,

                    Впервые слышу про дефолтовый рутовый пароль у линукса.
                    • +1
                      > дефолтовый рутовый пароль

                      у данного типа устройства он дефолтный, один на всех.

                      Ну и про «обычный линукс» это сильное преувеличение.
                      • +1
                        не, реально это достаточно обычный билдрутовый линукс.

                        Просто в отличие от достаточно сильно развитого openwrt в котором есть пакеты, тут всё сильно попроще, потому что почти вся система readonly.

                      • +1
                        если перед вами ssh (чаще telnet) к IP камере, то в 80-95% случаев вам поможет один из примерно 40 известных паролей.

                        Очень сложно организационно сменить пароль на камере при её установке.
                      • +2
                        Есть ли шанс заиметь вашу ржавчину чтоб поставить самому? Уж очень печальная там родная прошивка. Приходиться танцы с бубном устраивать чтоб получить вменяемый rtsp/h264
                        • +4
                          есть такая масса способов превратить вашу камеру в кирпич, что вы должны быть твердо уверены в себе для этого на текущем этапе.

                          Начните с фотографии процессора на камере. Если там что-то типа hi3518 или 3516, то шанс есть, но нужны все буквы. Так например 3516c и 3516a несовместимы вплоть до загрузчика.
                          • 0
                            наверное лучше даже по-другому: у вас есть оригинальная прошивка от вашей камеры?
                            • 0
                              Должна где-то быть, у меня две камеры, одна сейчас лежит без дела. А так же, насколько я помню, у меня hi3518e. Ваш cpuinfo очень похож на то, что я видел у себя но надо будет перепроверить
                              • 0
                                если найдете — попробуем помочь
                                • 0
                                  Сравнил cpuinfo — один в один.
                                  прошивка текущая — V4.02.R12.00006510.10010.140700.00000 / HI3518E_50H10L_S39
                                  Вот такое нашел когда-то на просторах интернета. + есть бинари с прошивкой за июнь 2016 и сентябрь 2014
                                  Только что-то я перемудрил в прошлый раз с их CMS и теперь не могу по телнету на камеру попасть.

                                  И для интересующихся вот много ссылок на всякое разное по этим китайцам.

                        • 0

                          Какого размера в тоге получились исполняемые бинарники? По умолчанию же они выходят достаточно жирные.

                        • 0
                          Три года назад работал в компании, предоставляющей набор охранной системы для частников — это набор беспроводных датчиков и базовой станции, которая общается с «центром» через GSM/3G и имеет бекапную батарейку (грабители обычно обесточивают дом, обрезают телефонную линию, разбивают панель ввода пинкода, думая, что он посылает сигнал тревоги). Я разрабатывал для них IP камеру (прошивку на основе hi3518 и Ambarella A5s/S2Lm) и серверную часть. SDK обычно предоставляет высокоуровневый RTSP/RTP стриминг, но я брал с уровня NAL h264 фреймов и запихивал в пропраетный протокол. Общение камеры с клаудом по типу dropcam, основан на TLS соединении (каждая камера имеет свой сертификат с UUID, подписанный единым CA, что авторизует каждую камеру, и сертификат сервера может быть проверен тоже с помощью CA паблика вшитого в камеру), и протокол на основе protobuf. Серверная часть C++ Boost::ASIO позволяет держать десятки тысяч одновременно подключенных камер на один сервер (стриминг происходит по активации со стороны приложения или по срабатыванию датчиков в доме).

                          Интересная тема с просмотром live и записанном видео в браузере и в мобильных клиентах. На Android и iOS мы можем просто скармливать NAL фреймы фреймворку и сама OS будет проигрывать видео, т.е. транспорт полностью может быть пропраетным. С браузерами не всё так просто, либо flash, либо html video tag. Хотя есть вариант плагинов, типа VLC для браузеров, чтобы проигрывать RTSP/RTP потоки, или какой-нибудь ActiveX — но это уже сильно устарело.

                          HTML5 video отлично проигрывает HLS и MPEG-DASH во всех современных браузерах, но live video запаздывает на величину 2х-3х сегментов, которые скажем по 2 сек, значит на 4-6 секунд. Мы же хотим видеть live именно live с минимальной задержкой (как в RTSP/RTP). К сожалению, я выбрал вариант Flash и с сервера лил файлы FLV просто потоком, который сразу же проигрывался. FLV формат супер прост, пихаем h264 NAL и AAC фреймы и всё. На мобильных клиентах мы написали простой FLV парсер и скармливали NAL и AAC фреймы системе.
                          • 0
                            С браузерами не всё так просто, либо flash, либо html video tag.
                            Вы смотрели в сторону Media Source Extensions? Мне кажется, он отлично подходит для вашего случая.
                            На википедии такое примечание даже:
                            Unreal HTML5 player uses MSE for low latency (sub-second) live playback of streams sent via WebSockets by Unreal Media Server
                            • 0
                              Нет, тогда MSE прошло мимо нашего радара. Да и сам html5 video tag был немного в диковинку, точнее список видео/аудио кодеков и контейнеров, которые поддерживаются тем или иным браузером. Как минимум мобильные браузеры не могли тогда играть html5 video, не знаю как сейчас. Кстати, AAC аудио кодек тоже немного не для нас, мы всё делали в Speex (потом стал opus). Speex хорошо жмёт голос, а не музыку, предоставляя лучший битрейт. FLV может содержать Speex. Ничего такого из html5 video не могло содержать speex.
                              P.S. Сейчас я мало знаю про техническую сторону того проекта, уже почти два года в другой компании.
                              • 0
                                Теперь уже OPUS в браузерах поддерживается почти повсеместно, как и webm с VP8/9. YouTube по умолчанию отдает webm VP9 + OPUS разными файлами, объединяя их через Media Source Extensions.

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                          Самое читаемое