В 2017 году мы в НТЦ ПРОТЕЙ начали разработку пакетного ядра частных 4G-сетей для корпоративных заказчиков. О главных задачах, которые нам пришлось решать, и основных сложностях, с которыми мы столкнулись при работе над полностью отечественным продуктом, рассказано в этой статье.

Корпоративные задачи по передаче мультимедиа, в том числе в разношерстных сетях датчиков IoT, вполне можно решать с помощью стандартов для сетей общего пользования — 4G и 5G, если адаптировать их к частным сетям. Эта идея подтолкнула развитие сегмента Private LTE, и сейчас в мире есть много новых игроков с комплексными решениями. С той же идеи в 2017 году началась наша разработка полностью отечественного ядра сети 4G для частных сетей.
Почему мы взялись за проект?
Более 15 лет мы разрабатываем различные решения для операторов связи, как проводных, так и мобильных, занимаемся собственными исследованиями в этой области.
В начале 2000-х нашими первыми заказчиками были региональные предприятия связи, позже объединенные в рамках ПАО «Ростелеком». Кроме того, долгое время в Мегафоне работал наш центр обработки SMS-трафика. А тарификацию голосовых вызовов в режиме онлайн в ряде крупных регионов обеспечивают наши CAMEL-шлюзы.
На старте мы разрабатывали только отдельные компоненты ядер сетей GSM/UMTS и LTE. Но по мере развития пришли к пониманию, что можем построить всю логическую часть сети (не трогая радиоподсистему), поскольку основные компоненты — HLR/HSS, GMSC, GGSN/PGW, DPI — уже были в нашем портфолио. Теперь на нашем ПО работают все российские виртуальные операторы общедоступной мобильной связи LTE (MVNO, Mobile Virtual Network Operator).
Частные мобильные сети связи — PMR (Professional или Private Mobile Radio) — начали развиваться даже раньше, чем операторы связи общего пользования. Но до сих пор большинство корпоративных сетей отдают приоритет голосу, базируясь на цифровых стандартах второго поколения: TETRA, APCO 25, DMR. В корпоративном сегменте эти задачи частично решаются при помощи Wi-Fi-сетей. Но сама идеология этого семейства стандартов не обеспечивает необходимой надежности передачи данных. Кроме того, несмотря на постепенную адаптацию следующих версий Wi-Fi к сетям с высокой плотностью абонентских терминалов, проблем добавляет общий частотный ресурс.

Особняком стоят специализированные узкополосные сети передачи данных для сбора показаний с датчиков. В основном они строятся на проприетарных технологиях и радиомодемах и совершенно не предназначены для передачи больших объемов данных, например, оцифрованного звука или видео.
Наш Private LTE
Пакетное ядро LTE — это оборудование и программное обеспечение, осуществляющее обработку и маршрутизацию трафика внутри частной мобильной сети 4G. По сути, это центр управления сетью, через который взаимодействуют все базовые станции. Ядро связано с радиоподсистемой посредством стандартных интерфейсов, поэтому может рассматриваться как самостоятельный элемент сети и самостоятельный продукт. Но, естественно, при этом оно должно поддерживать рекомендации 3GPP.
В основу ядра частной сети 4G легли наши наработки для общедоступных сетей.
Схема построения LTE-сети разработки НТЦ ПРОТЕЙ
Как и продукты для общедоступных операторов, ядро частной 4G мы разрабатывали на C++. С нашей точки зрения, это оптимальный выбор.
Отдельной большой частью проекта стала оптимизация кода. Это естественный процесс развития продуктов: ПО просто не может стоять на месте. Внешние факторы — от потребностей абонентов до прошивок железа — меняются, так что даже при неизменном наборе функций продукт должен развиваться. Исходя из этих соображений, мы оптимизировали все компоненты ядра. С переходом на новые стандарты связи нагрузка на железо растет, а пропорционально наращивать аппаратные мощности в некоторых случаях не просто дорого, а невозможно. Так что у нас не было выхода.
Ядро сети 4G должно обрабатывать миллионы пакетов и сотни тысяч транзакций в секунду. В этих условиях любое обращение к памяти, любой дополнительный вызов виртуальной функции вносит свой вклад в общую производительность. Так что нам пришлось провести глубокий рефакторинг кода. Мы ушли от синхронизации кэшей L1-L2 ядер, которая влияла на скорость доступа к оперативной памяти через выравнивание структур данных. Мы не стали раздувать структуры, занимавшие около 80 байт, до 128 (чтобы не терять впустую треть памяти). Вспомнили про выравнивание полей внутри структур, за счет чего сократили размер до 64 байт, кратных размеру кэша.
Использование каждой новой возможности C++ добавляло еще по 3–5% к производительности, так что в итоге мы получили почти двукратный рост.
Выжав разумный максимум из высокоуровневой оптимизации, мы перешли к низкоуровневым. Анализ показал, что процессоры ждут данных: L1 missed, показатель Cycles per Instruction высокий (4–20 тактов на инструкцию). Это вынудило искать решения по превентивной загрузке данных из памяти в кэш процессора.
Для этого мы использовали prefetch.
GCC поддерживает __builtin_prefetch. Мы используем:
<source>
static inline void prefetch(const volatile void *p) {
        asm volatile ("prefetcht0 %[p]" : : [p] "m" (*(const volatile char *)p));
}
</source>
Prefetch обеспечил нам рост производительности еще примерно на 5%. В абсолютных величинах это обработка дополнительно примерно 10–20 тыс. пакетов в секунду без какого-либо изменения логики их обработки.
В процессе работы над ядром 4G нам пришлось учитывать, что каждое предприятие имеет собственные специфические требования и запросы. В частности, многим нужна гладкая миграция от используемых аналоговых и цифровых стандартов голосовой связи к частной сети нового поколения. Поэтому нам пришлось поработать над взаимодействием разрабатываемого ядра с сетями предыдущих поколений. В отдельных сегментах бизнеса существуют собственные требования регулятора, а также корпоративные правила, касающиеся внутренней связи. Это также пришлось иметь в виду.
Вариант построения ведомственной сети LTE
В целом такие частные требования и интеграции серьезно усложняют жизнь нашим разработчикам. Наш любимый пример на эту тему — история с общедоступным мобильным оператором из далекой африканской страны. Хотя это немного другой рынок, история отлично иллюстрирует идею, не раскрывая ничьих корпоративных секретов. В той сети мы внедрили наш PGW (Packet data network Gateway). Он справлялся со своими функциями, пока один из абонентов не начал жаловаться на отсутствие мобильного интернета. Анализ логов показал, что от абонента приходило целых три стандартных запроса Create Session Request с разницей в несколько миллисекунд. Наша система обрабатывала эти запросы в соответствии с 3GPP, который, естественно, не предусматривает возможности такой отправки запросов. В итоге все сессии абонента закрывались — мобильный интернет отключался. Хотя формально баг был не на нашей стороне, пришлось усложнить код TDF, чтобы устранить проблему.
Еще один интересный пример — про TDF (Traffic Detection Function), но уже корпоративного уровня. Установив решение у заказчика из другой страны, мы получили негативную обратную связь с пометкой: CEO IS NOT HAPPY. Однако все тесты показывали, что работа подсистемы в норме. После прояснения ситуации оказалось, что CEO привык к более гладкому графику двухнедельной статистики, поскольку предыдущее решение другого производителя снимало контрольные точки раз в 15 минут. А наш TDF работает слишком точно — берет контрольные точки раз в 10 секунд, чем «портит» гладкий график. В этом случае пришлось ограничиться разъяснительной работой, поскольку на собираемые данные была завязана вся структура нашего пакетного ядра, т. е. нельзя было просто сменить форму отображения графика (красивый график не стоил потерь в остальном продукте).
На сегодняшний день разработка закончена. Ядро позволяет организовать беспроводную связь в компаниях любой численности и любой территориальной локализации, так как по сравнению с сетями общего пользования, с которыми мы обычно работаем, размерность таких сетей невелика, что, однако, компенсируется большей сложностью системы сервисов, требующих гарантированной доставки мультимедиа-данных. В России такого рода системы строятся только крупными организациями, так что и сети заказчиков сразу приобретут значительную размерность уже после опытной эксплуатации.
Уже сейчас мы начали развивать наше ядро в направлении 5G. В логике работы стандартов есть некоторые отличия, кроме того, нам необходимо еще больше оптимизировать ПО для работы с новыми объемами трафика. Выпуск 5G-решений запланирован на 3–4-й квартал 2020 года.
В 2017 году мы в НТЦ ПРОТЕЙ начали разработку пакетного ядра частных 4G-сетей для корпоративных заказчиков. О главных задачах, которые нам пришлось решать, и основных сложностях, с которыми мы столкнулись при работе над полностью отечественным продуктом, рассказано в этой статье.

Корпоративные задачи по передаче мультимедиа, в том числе в разношерстных сетях датчиков IoT, вполне можно решать с помощью стандартов для сетей общего пользования — 4G и 5G, если адаптировать их к частным сетям. Эта идея подтолкнула развитие сегмента Private LTE, и сейчас в мире есть много новых игроков с комплексными решениями. С той же идеи в 2017 году началась наша разработка полностью отечественного ядра сети 4G для частных сетей.
Почему мы взялись за проект?
Более 15 лет мы разрабатываем различные решения для операторов связи, как проводных, так и мобильных, занимаемся собственными исследованиями в этой области.
В начале 2000-х нашими первыми заказчиками были региональные предприятия связи, позже объединенные в рамках ПАО «Ростелеком». Кроме того, долгое время в Мегафоне работал наш центр обработки SMS-трафика. А тарификацию голосовых вызовов в режиме онлайн в ряде крупных регионов обеспечивают наши CAMEL-шлюзы.
На старте мы разрабатывали только отдельные компоненты ядер сетей GSM/UMTS и LTE. Но по мере развития пришли к пониманию, что можем построить всю логическую часть сети (не трогая радиоподсистему), поскольку основные компоненты — HLR/HSS, GMSC, GGSN/PGW, DPI — уже были в нашем портфолио. Теперь на нашем ПО работают все российские виртуальные операторы общедоступной мобильной связи LTE (MVNO, Mobile Virtual Network Operator).
Частные мобильные сети связи — PMR (Professional или Private Mobile Radio) — начали развиваться даже раньше, чем операторы связи общего пользования. Но до сих пор большинство корпоративных сетей отдают приоритет голосу, базируясь на цифровых стандартах второго поколения: TETRA, APCO 25, DMR. В корпоративном сегменте эти задачи частично решаются при помощи Wi-Fi-сетей. Но сама идеология этого семейства стандартов не обеспечивает необходимой надежности передачи данных. Кроме того, несмотря на постепенную адаптацию следующих версий Wi-Fi к сетям с высокой плотностью абонентских терминалов, проблем добавляет общий частотный ресурс.

Особняком стоят специализированные узкополосные сети передачи данных для сбора показаний с датчиков. В основном они строятся на проприетарных технологиях и радиомодемах и совершенно не предназначены для передачи больших объемов данных, например, оцифрованного звука или видео.
Наш Private LTE
Пакетное ядро LTE — это оборудование и программное обеспечение, осуществляющее обработку и маршрутизацию трафика внутри частной мобильной сети 4G. По сути, это центр управления сетью, через который взаимодействуют все базовые станции. Ядро связано с радиоподсистемой посредством стандартных интерфейсов, поэтому может рассматриваться как самостоятельный элемент сети и самостоятельный продукт. Но, естественно, при этом оно должно поддерживать рекомендации 3GPP.
В основу ядра частной сети 4G легли наши наработки для общедоступных сетей.
Схема построения LTE-сети разработки НТЦ ПРОТЕЙ
Как и продукты для общедоступных операторов, ядро частной 4G мы разрабатывали на C++. С нашей точки зрения, это оптимальный выбор.
Отдельной большой частью проекта стала оптимизация кода. Это естественный процесс развития продуктов: ПО просто не может стоять на месте. Внешние факторы — от потребностей абонентов до прошивок железа — меняются, так что даже при неизменном наборе функций продукт должен развиваться. Исходя из этих соображений, мы оптимизировали все компоненты ядра. С переходом на новые стандарты связи нагрузка на железо растет, а пропорционально наращивать аппаратные мощности в некоторых случаях не просто дорого, а невозможно. Так что у нас не было выхода.
Ядро сети 4G должно обрабатывать миллионы пакетов и сотни тысяч транзакций в секунду. В этих условиях любое обращение к памяти, любой дополнительный вызов виртуальной функции вносит свой вклад в общую производительность. Так что нам пришлось провести глубокий рефакторинг кода. Мы ушли от синхронизации кэшей L1-L2 ядер, которая влияла на скорость доступа к оперативной памяти через выравнивание структур данных. Мы не стали раздувать структуры, занимавшие около 80 байт, до 128 (чтобы не терять впустую треть памяти). Вспомнили про выравнивание полей внутри структур, за счет чего сократили размер до 64 байт, кратных размеру кэша.
Использование каждой новой возможности C++ добавляло еще по 3–5% к производительности, так что в итоге мы получили почти двукратный рост.
Выжав разумный максимум из высокоуровневой оптимизации, мы перешли к низкоуровневым. Анализ показал, что процессоры ждут данных: L1 missed, показатель Cycles per Instruction высокий (4–20 тактов на инструкцию). Это вынудило искать решения по превентивной загрузке данных из памяти в кэш процессора.
Для этого мы использовали prefetch.
GCC поддерживает __builtin_prefetch. Мы используем:
<source>
static inline void prefetch(const volatile void *p) {
        asm volatile ("prefetcht0 %[p]" : : [p] "m" (*(const volatile char *)p));
}
</source>
Prefetch обеспечил нам рост производительности еще примерно на 5%. В абсолютных величинах это обработка дополнительно примерно 10–20 тыс. пакетов в секунду без какого-либо изменения логики их обработки.
В процессе работы над ядром 4G нам пришлось учитывать, что каждое предприятие имеет собственные специфические требования и запросы. В частности, многим нужна гладкая миграция от используемых аналоговых и цифровых стандартов голосовой связи к частной сети нового поколения. Поэтому нам пришлось поработать над взаимодействием разрабатываемого ядра с сетями предыдущих поколений. В отдельных сегментах бизнеса существуют собственные требования регулятора, а также корпоративные правила, касающиеся внутренней связи. Это также пришлось иметь в виду.
Вариант построения ведомственной сети LTE
В целом такие частные требования и интеграции серьезно усложняют жизнь нашим разработчикам. Наш любимый пример на эту тему — история с общедоступным мобильным оператором из далекой африканской страны. Хотя это немного другой рынок, история отлично иллюстрирует идею, не раскрывая ничьих корпоративных секретов. В той сети мы внедрили наш PGW (Packet data network Gateway). Он справлялся со своими функциями, пока один из абонентов не начал жаловаться на отсутствие мобильного интернета. Анализ логов показал, что от абонента приходило целых три стандартных запроса Create Session Request с разницей в несколько миллисекунд. Наша система обрабатывала эти запросы в соответствии с 3GPP, который, естественно, не предусматривает возможности такой отправки запросов. В итоге все сессии абонента закрывались — мобильный интернет отключался. Хотя формально баг был не на нашей стороне, пришлось усложнить код TDF, чтобы устранить проблему.
Еще один интересный пример — про TDF (Traffic Detection Function), но уже корпоративного уровня. Установив решение у заказчика из другой страны, мы получили негативную обратную связь с пометкой: CEO IS NOT HAPPY. Однако все тесты показывали, что работа подсистемы в норме. После прояснения ситуации оказалось, что CEO привык к более гладкому графику двухнедельной статистики, поскольку предыдущее решение другого производителя снимало контрольные точки раз в 15 минут. А наш TDF работает слишком точно — берет контрольные точки раз в 10 секунд, чем «портит» гладкий график. В этом случае пришлось ограничиться разъяснительной работой, поскольку на собираемые данные была завязана вся структура нашего пакетного ядра, т. е. нельзя было просто сменить форму отображения графика (красивый график не стоил потерь в остальном продукте).
На сегодняшний день разработка закончена. Ядро позволяет организовать беспроводную связь в компаниях любой численности и любой территориальной локализации, так как по сравнению с сетями общего пользования, с которыми мы обычно работаем, размерность таких сетей невелика, что, однако, компенсируется большей сложностью системы сервисов, требующих гарантированной доставки мультимедиа-данных. В России такого рода системы строятся только крупными организациями, так что и сети заказчиков сразу приобретут значительную размерность уже после опытной эксплуатации.
Уже сейчас мы начали развивать наше ядро в направлении 5G. В логике работы стандартов есть некоторые отличия, кроме того, нам необходимо еще больше оптимизировать ПО для работы с новыми объемами трафика. Выпуск 5G-решений запланирован на 3–4-й квартал 2020 года.

Комментарии (21)


  1. Taxopr
    12.12.2019 12:56
    +5

    Хотелось бы побольше информации.


    • Bare-metal, виртуализация, или может-быть контейнеры?
    • SGW совмещен c MME? Почему не с PGW/GGSN? В чем преимущества такого решения?
    • Не раскрыта разница между частными и публичными сетями. Как с точки зрения необходимого функционала (и трафик модели), так и с точки зрения интеграции.
    • Какие сетевые функции из показанных на иллюстрации реализованы вами?
      Там, кстати, странное. Если Gn, то рядом S5, если S8 — то Gp. От PGW в PCRF идет Gx, а между PCRF и OCS не Gy, а Sy. Ну и где-то идентификатор интерфейса вместе с протоколом, где-то без, а где-то только протокол — не аккуратно.


    1. Nadezhda_PIN
      13.12.2019 21:10

      Благодарим за комментарий, иллюстрацию заменили, теперь везде идентификаторы интерфейса.


    1. MaxShakhmetov
      27.12.2019 18:54

      1. Мы запускаем как на bare-metal, так и виртуализированных окружениях — это в продакшне. В разработке и на тестах используем и контейнеры. Если говорить о выскопроизводительных системах, то мы рекомендуем bare-metal, причем предъявляем требования к сетевым картам, памяти, процессору.

      2. S-GW отдельный. Это ошибка на схеме. А вообще совмещать eNodeB + MME + S-GW + P-GW + IMS имеет в случае для NIB (Network-in-a-Box). Как пример, спец службы на выезде могут взять такое решение для обеспечения служебной связью персонал.

      3. Публичная сеть — любой желающий может подключиться, частая сеть — только с разрешения владельца. Интеграция с чем?

      4. Нами реализованы MME, P-GW, S-GW, HSS.
      Так же у нас есть ядро IMS (S-CSCF, P-CSCF, SBC), PCRF, DPI (TDF)


      1. Taxopr
        27.12.2019 21:01
        +1

        В публичную сеть можно попасть тоже только с разрешения владельца сети, т.е. оператора. Я не об этом.
        Интересен ваш опыт, вернее отличия между опытом реализации частных и публичных сетей.
        Тут можно представить все что угодно… кто клиенты, зачем им частная мобильная сеть, почему выбрали мобильную связь, а не LAN/WiFi/еще-какое-радио. Нужен ли им биллинг или DPI/PCEF, и зачем? UE статичны или мобильны, размер сетей. Есть ли различные сервисы, в чем они заключаются и как они разделяются. Управление UE и подписками. Мониторинг и эксплуатация. Да и вообще, само строительство, одно дело строить для оператора, у которого уже есть опыт с мобильной связью, а другое дело для компании, которая имела дело с этим только на слайдах.
        Тема, как по мне, очень вкусная и интересная, особенно если есть реальный опыт.


  1. linch08
    13.12.2019 11:02
    +4

    Интересно. А какова доля Open-Source кода в вашем решении?


  1. Vaddos
    13.12.2019 17:28

    ~ 30% (dpdk, boost и т.д.)


  1. gosha-z
    14.12.2019 18:26

    А какой инструментарий использовался для conformance testing и подтверждения соответствия стандартам 3GPP?


    1. MaxShakhmetov
      20.12.2019 10:29

      Добрый день.

      Для тестирования использовались несколько продуктов.
      1. openSGSN — для тестирования GTPv1-C & GTPv1-U. Это был первый тул для тестов. 2 сценария проверить можно было + кодирование/декодирование сообщений.
      2. Developing Solutions dsTest — коммерческая система для тестирования EPC. Честно говоря, мы разочаровались в этом продукте — было много ошибок в самом тестере с учетом его стоимости.
      3. MTS (Multi-protocol Test Tool opensourced by Ericsson) — из коробки не работало, требовало усилий для запуска. Зато можно настроить сценарии и IE как вздумается. Большой плюс — open source.
      4. Конечно не обошлось без «фатального недостатка» — сами написали систему для нагрузочных тестов. А со временем добавляем в нашу систему хитрые сценарии, с которыми сталкиваемся в продакшне.



  1. alarmko
    15.12.2019 23:06

    Подскажите, почему вам пришлось код править в TDF, хотя интеграция была между SGW и PGW и именно первый присылал запросы на создание IP-CAN сессии не по рекомендациям 3GPP в сторону последнего? Полагаю, что ваш DPI это и есть PCEF + TDF, но тогда непонятно, зачем пришлось править в нем код, когда, опять же, с SGW он не интегрируется по вашей же схеме, а если TDF встроен в PGW, то это не его функция, терминировать на себя S5/S8 и отвечать SGW, мне кажется, у вас ошибка.

    И, пожалуй, последний вопрос — зачем вам на сети Gy от PGW и от DPI? У вас две точки тарификации? Для каких целей необходимо использовать данный подход?


    1. MaxShakhmetov
      28.12.2019 00:01

      Про TDF упомянули как пример с какими вопросами сталкиваемся и как решаем.
      Вы правы. Конечно запросы на создание IP-CAN сессии не по рекомендациям 3GPP в сторону PGW присылал SGW Это история случилась у нашего клиента — оператора MVNO. В этом инстале был наш PGW, а SGW — оператора MNO.
      Абсолютно верно — наш DPI это и есть PCEF + TDF. Но и PGW так же имеет функционал PCEF. Правила в одном случае работают с bearer/PDP context, APN, UE, в другом случае на уровне сервисов.
      Gy от PGW и от DPI — на сети используется или Gy PGW или Gy DPI.


  1. shifttstas
    21.12.2019 06:22

    А зачем нужно ваше решение если есть github.com/srsLTE/srsLTE?


    1. MaxShakhmetov
      28.12.2019 00:19
      +1

      Наша история про ядро сети, а srsLTE — это больше история про eNodeB & UE.
      В srsLTE в реализации ядра нет IMS, нет резервирования, так же наше решение способно обрабатывать 20 Gbps на 1 CPU Xeon®. srsLTE HSS совместим не совсеми SIM картами операторов.
      srsENB используем у себя на тестах — работает нестабильно. К сожалению…



  1. shifttstas
    21.12.2019 06:27

    И на мой взгляд стоит использовать стандартные названия DPI --> PCEF, в 3GPP нет понятий «криптомаршрутизатор» и «ТСОП»


    1. MaxShakhmetov
      27.12.2019 18:57

      DPI так же можт быть TDF.
      Не нашел в тексте «криптомаршрутизатор» и «ТСОП»


    1. MaxShakhmetov
      27.12.2019 23:45

      Увидел криптомаршрутизатор )
      Это имеется ввиду VPN — так назвали на картинке )
      ТСОП — Телефонная Сеть Общего Пользования (она же ТФОП, PSTN).
      3GPP есть много упоминаний о PSTN — это больше об IMS голосовом домене.
      Согласен, что названия на схеме странные — это из презентации для спец заказчиков.
      DPI наш имеет функционал как PCEF, так и TDF.


  1. axilirator
    04.01.2020 03:32

    Как и продукты для общедоступных операторов, ядро частной 4G мы разрабатывали на C++. С нашей точки зрения, это оптимальный выбор.

    Could you please tell more, why C++? Why not Erlang?


    1. 9a75sd
      08.01.2020 17:54

      В Erlang можно обращаться к низкоуровневым функциям и инструкциям?


  1. suhoy95
    09.01.2020 10:47

    Круто что есть и отечественные реализации.


    1) А есть ли возможность предоставления системы в исследовательсо-образовательных целях?


    В прошлом году Я закончил магистратуру SNE в Иннополисе, где на одном из курсов мы на личном примере с SDR проверяли GSM на безопасность (с определенными успехами). (А недавно мне скинули, что GSM фундаментально умер — https://youtu.be/YlqJCPk2OvQ).


    Было бы здорово иметь возможность поучиться на специально отведённой для этого системе (И не только на GSM).


    2) Вам нужны разработчики?


    Пока после университета, я практикуюсь на миниатюрных диктофонах (low-power/embedded). Но если я себя не переоцениваю, то через 1-2 года я закончу. И стать разработчиком Сотовой связи — звучит интересно.


    А то, пытался вникнуть в Wi-Fi/Mesh, но судя по рынку (https://www.electronicsweekly.com/news/business/arms-2020-predictions-2019-12/), он либо незаметно осядет в ниши, либо каким-то неимоверным образом сольется с Сотовой инфраструктурой, где больше денег и как следствие мотивации.


  1. Nadezhda_PIN
    03.03.2020 18:40

    Благодарим за оценку.
    Разработчики, конечно же, нужны!