Как позвонить всем вокруг

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Мы в Postuf большие поклонники франшизы Watch Dogs - приключенческой видеоигры, в которой главный герой в лице Эйдена Пирса, имея доступ к вымышленной системе ctOS, способен проворачивать со своего смартфона разные хакерские трюки. Однажды нам стало интересно, возможно ли в реальной жизни повторить трюк звонка на телефоны находящихся рядом людей.

Оказалось, возможно:

Чтобы заставить все телефоны вокруг звенеть, необходимо всего лишь всем им (желательно, одновременно) отправить команду на звонок. Делов-то! Супер простая цель, которая не так легко достижима на практике. Отправить такую абстрактную команду можно либо непосредственно на каждый телефон, если он вас каким-либо образом "слушает", либо через посредника - сотового оператора, которого каждый девайс "слушает" по умолчанию (в зависимости от своей SIM).

Минусы первого способа - придется разбираться в том как устроен "протокол" сотового звонка, чтобы суметь отправить заветную команду на звонок, а также думать над тем, как в принципе заставить телефоны вокруг "слушать" команды.

Минусы второго способа - слишком большое число операторов и совершенно непонятно, как найти телефоны "вокруг" себя. Здесь можно было бы схитрить, воспользовавшись услугой из даркнета по получению номеров абонентов в конкретном регионе(в соте) - т.н "Зонтик", но на практике оказалось, что в реальном времени такую услугу никто не предоставляет - нужно ждать несколько дней, что делает этот способ сомнительным.

Становимся мобильным оператором

Чтобы можно было звонить мобильным устройствам напрямую, необходимо, чтобы они относились к нам как к полноценному оператору сотовой связи. Неотъемлемая составляющая любой современной сотовой сети - базовая станция (BTS), к которой подключаются мобильные устройства для дальнейшего взаимодействия с сетью. За базовой станцией может и не быть никакой реальной "сети", однако об этом абоненты никогда не узнают.

О поднятии собственных базовых станций уже писали не один десяток раз, в том числе и на Хабре, поэтому лишний раз углубляться в подробности не будем. Отметим лишь, что наиболее удобной будет станция поколения 2G, т.к в этом поколении можно напрочь прибить все механизмы безопасности на уровне самой BTS, на которые полагаются современные устройства.

Существует целый ряд уже готовых реализаций 2G BTS, среди них особенно выделяются Osmocom и YateBTS. Для поднятия своей BTS также понадобится SDR-оборудование. Мы решили остановиться на наиболее доступной связке Osmocom+LimeSDR. Небольшой ресерч с использованием лишь одного гугла позволил в достаточно короткие сроки разобраться с основными концепциями GSM(2G) сети и поднять собственную "коробочную" версию сети.

Собственная мобильная сеть
Собственная мобильная сеть

На этом этапе мы уже могли подключать абонентов к себе вручную через выбор сети в настройках смартфона. Но на ручном подключении далеко не уедешь...

Вышибаем абонентов из родной сети к себе

Как заставить чужие мобильные телефоны подключаться к какой-то левой сотовой сети вместо "родной", да еще и к сети более старого(небезопасного) поколения 2G? Для ответа на этот вопрос понадобилось разобраться с тем, как мобильные устройства вообще сканируют эфир в поисках базовых станций. Кратко алгоритм выглядит так:

  1. "текущее_поколение" = МАКСИМАЛЬНО_ПОДДЕРЖИВАЕМОЕ (5G, 4G, 3G, 2G)

  2. "найденные_станции" = просканируй_эфир("текущее поколение")

  3. если "найденные_станции" != пусто

    1. подключиться(лучшая_базовая_станция("найденные_станции"))

  4. иначе, если "текущее_поколение" > 2G

    1. "текущее_поколение" -= 1

    2. goto 2

И говорит нам этот алгоритм о том, что для получения абонента в свою сеть, необходимо, чтобы вокруг не было базовых станций поколений выше 2G, да еще и наша BTS должна быть самой "лучшей" из всех 2G. Проще говоря, мы должны быть единственными в округе, чтобы мобильным устройствам даже не пришлось выбирать (алгоритм выбора "лучшего" не тривиален и зависит от реализации радиомодуля).

Чтобы стать "единственной" BTS, можно заглушить сигнал от всех базовых станций вокруг. Проблема тут в том, что точечно этого сделать не получится - доступные для покупки средства глушения связи накрывают диапазоны целиком. Где тогда оставить нашу BTS?

Посмотрим, какие вообще диапазоны частот входят в стандарт GSM:

Uplink (передача данных)

Downlink (прием данных)

Европа, Азия

880 — 915 МГц (E-GSM-900)
1710 — 1785 МГц (DCS-1800)

925 — 960 МГц (E-GSM-900)
1805 — 1880 МГц (DCS-1800)

США, Канада, Латинская Америка и Африка

824 — 849 МГц (GSM-850)
1850 — 1910 МГц (PCS-1900)

869 — 894 МГц (GSM-850)
1930 — 1990 МГц (PCS-1900)

Что если притвориться для телефонов вокруг, будто бы они из привычной Европейской среды попали в Американскую? Ведь это вполне реальный кейс - их владельцы вполне себе могли отправиться в путешествие на другую сторону Земного шара.

Большинство доступных в продаже глушилок поддерживают селективное глушение полос мобильной связи. Вот фрагмент ТТХ одной из таких:

Характеристики мультичастотного подавителя
Характеристики мультичастотного подавителя

Приобретаем любой подавитель, способный заглушить связь в радиусе 10-20 метров, включаем диапазоны глушения E-GSM-900, DCS-1800, все диапазоны 4G и 3G, которые в сумме накроют всю мобильную связь в радиусе действия глушилки, за исключением американского GSM-850. А на американской полосе GSM-850 поднимаем нашу BTS:

Одинокая BTS на американской частоте
Одинокая BTS на американской частоте

После этой операции все телефоны магическим образом разделились на 2 категории:

  • те, кто беспрепятственно подключаются к BTS в течение нескольких минут после глушения (в основном Android-девайсы);

  • те, кто долго висят без сети и подключаются спустя 30+ минут после глушения (в основном iOS-девайсы).

Ждать 30 минут, пока телефон перестроится в новый режим, - слишком долго - за это время все вокруг просто разойдутся. Здесь мы пошли на вторую уловку - перенесли BTS в самое начало стандартной европейской полосы E-GSM-900 - на частоту 925.2МГц, подкрутили глушилку так, чтобы она накрывала все базовые станции в диапазоне E-GSM-900 выше BTS:

BTS в самом начале E-GSM-900, дальше все закрыто глушилкой
BTS в самом начале E-GSM-900, дальше все закрыто глушилкой

В этом случае мы смогли собрать 100% мобильных устройств в радиусе подавления на своей BTS!

Массовый звонок

На текущий момент у нас есть 1 базовая станция стандарта GSM, поднятая в самом начале европейской полосы E-GSM-900, выше которой все закрыто глушилкой. Абоненты подключаются, и с ними уже можно взаимодействовать посредством VTY-интерфейса Osmocom. Для удобства мы написали скрипт на Python, который все красиво отображает в терминале:

Список подключенных абонентов после нескольких минут работы BTS совместно с глушилкой
Список подключенных абонентов после нескольких минут работы BTS совместно с глушилкой

Чтобы звонить со стороны BTS на выбранные номера телефонов(msisdn) необходима SIP-телефония, интегрированная со стеком Osmocom. Самое быстрое решение - провести интеграцию с SIP-стеком Asterisk по официальному мануалу от Osmocom.

После этого мы можем совершать звонки абонентам на их локальные номера(msisdn) уже по мануалу Asterisk. Попробуем позвонить 1-2 абонентам с произвольного номера - звонки успешно доходят. Запускаем звонок всем подключившимся абонентам...и натыкаемся на серьезное ограничение GSM по числу параллельных звонков. Так как GSM - крайне древний и простой протокол, в основе разделения абонентов лежат принципы временнОго разделения (TDMA) в сочетании с частотным разделением(FDMA), а конфигурация Osmocom по этой части выглядит следующим образом:

trx 0 # FDMA
   rf_locked 0
   arfcn 975 # номер GSM-частоты в ARFCN-нотации (925.2МГц)
   nominal power 23
   timeslot 0 # TDMA
    phys_chan_config CCCH # Common Control Channel
   timeslot 1 # TDMA
    phys_chan_config SDCCH8 # Stand-alone Dedicated Control Channel
   timeslot 2 # TDMA
    phys_chan_config TCH/F # Traffic Channel Full Rate
   timeslot 3 # TDMA
    phys_chan_config TCH/F # Traffic Channel Full Rate
   timeslot 4 # TDMA
    phys_chan_config TCH/F # Traffic Channel Full Rate
   timeslot 5 # TDMA
    phys_chan_config TCH/F # Traffic Channel Full Rate
   timeslot 6 # TDMA
    phys_chan_config TCH/F # Traffic Channel Full Rate
   timeslot 7 # TDMA
    phys_chan_config TCH/F # Traffic Channel Full Rate

Каждый TRX(приемо-передатчик) имеет собственную несущую частоту(ARFCN) и содержит 8 тайм-слотов, каждый из которых может иметь свою конфигурацию каналов. Например:

  • CCCH - канал для непосредственного "вещания" информации о BTS и первичного обнаружения BTS мобильными устройствами;

  • SDCCH - сигнальный канал, необходим для установки звонка и рассылки SMS;

  • TCH - передача речевого трафика.

Подробно о конфигурациях каналов в GSM можно почитать здесь, а узнать их маппинг в более низкоуровневые каналы(channel combinations) в стеке Osmocom - из кода Osmocom.

В текущей конфигурации мы сможем совершать не более 6 параллельных звонков :( Даже не 8, потому что основному каналу CCCH нужен целый тайм-слот, а также еще 1 слот для минимум одного SDCCH, который нужен для установки звонка.

Увеличить число тайм-слотов возможности нет на уровне протокола GSM, попробуем тогда расшириться в плоскости FDMA - увеличить количество TRX. Благо, Osmocom дает возможность довести число TRX при использовании LimeSDR до 6 (6*8 = 48 тайм-слотов), правда после активации четырех и более TRX начинались проблемы с перегрузкой LimeSDR. Стабильная работа BTS на одном LimeSDR сохранялась при использовании трех TRX, что в сумме давало 6+8+8=22 параллельных звонка (на каждом TRX не нужно иметь служебные каналы, поэтому TRX №2 и №3 можно забить чисто речевыми каналами - TCH). Это уже было лучше, но все же недостаточно для полноценного массового звонка.

Попутно всплыла еще одна проблема, сокращающая число потенциальных звонков и связанная с глушением чужих базовых станций на полосе E-GSM-900. Дело в том, что начало границы глушения на полосе E-GSM-900 должно быть максимально выше нашей BTS и минимально ниже первой чужой станции. А согласно распределению частот мобильных операторов в России, первая "чужая" базовая станция присутствует спустя буквально пару МГц - на частоте 927.5Мгц - 3G станция UMTS, принадлежащая Мегафону.

Первая "чужая" базовая станция на полосе E-GSM-900
Первая "чужая" базовая станция на полосе E-GSM-900

Таким образом в начале полосы E-GSM-900 остается всего 927.5-925 = 2.5МГц "свободного места", а с тремя активными TRX будет занято 1.8МГц из них (каждый занимает 0.4МГц полосы и требует 0.2МГц свободного пространства после себя) - почти впритык. Однако из-за того, что границы глушилки могут "плавать" на +-1-2МГц из-за температуры окружающей среды, необходимо сместить начало границы глушения немного ниже частоты 927.5МГц - на частоту ~926МГц, чтобы обеспечить надежное закрытие базовой станции Мегафона. В этом случае последний TRX может попадать в зону глушения, и звонки будут либо обрываться, либо вообще не будут даже начинаться:

BTS с тремя TRX без глушилки
BTS с тремя TRX без глушилки
BTS с тремя TRX с глушилкой (глушилка их задевает)
BTS с тремя TRX с глушилкой (глушилка их задевает)

Здесь стало ясно, что для увеличения числа параллельных звонков придется уходить с полосы E-GSM-900. Но куда в этот раз? С полосы GSM-850 мы уже ушли из-за того, что не все мобильные устройства её сканируют. А что если совместить эти два способа?

Вытягиваем абонентов с помощью станции-"приманки"

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

BTS сообщает о своём соседе с ARFCN=128 (869.2МГц)
BTS сообщает о своём соседе с ARFCN=128 (869.2МГц)

Если, например, абонент подключился к GSM-станции на частоте 955МГц, и она сообщила, что рядом с ней есть еще одна GSM-станция на частоте 957МГц, то при внезапном исчезновении активной станции (на частоте 955МГц) абонент в первую очередь сходит в свой кэш и попробует переключиться на станции оттуда, вместо полного сканирования радиоэфира. Для абонента это банально менее энергозатратно!

Если мы поднимем одну BTS в начале европейской полосы E-GSM-900, а вторую - в любом месте американской полосы GSM-850 и заставим "европейскую" BTS сообщать о своем соседе на "американской" полосе, у нас получится провернуть трюк с "отравлением" кэша, чтобы ускорить перевод медлительных абонентов на полосу GSM-850, которую они так долго не хотят начинать сканировать. Потребуется еще один LimeSDR для запуска второй BTS.

Провернуть этот трюк мы решили следующим образом:

  1. Поднимем первую BTS с 3 TRX на частоте 869.2МГц (американская полоса), которая сообщает пока что несуществующего соседа на частоте 871.2МГц;

  2. Поднимем вторую BTS с 1 TRX на частоте 925.2МГц (европейская полоса), которая сообщает единственного соседа на частоте 869.2МГц;

  3. Собираем глушилкой абонентов вокруг - какие-то из абонентов "осядут" на первой BTS, какие-то на второй;

  4. Выключаем вторую BTS (925.2МГц) и тут же поднимаем её, но уже с 3 TRX на американской частоте 871.2МГц - теперь сосед существует у первой BTS, а перезапущенная BTS сообщает соседа на 869.2МГц;

  5. Все абоненты успешно "спустились" с 925.2МГц на 869.2МГц/871.2МГц.

Такой трюк позволил нам собрать 100% мобильных устройств в радиусе подавления на своих BTS, и вдобавок в нашем распоряжении оказалось 2*(6+8+8) = 44 таймслота под параллельные звонки. Неплохо, но можно лучше.

Звоним много и быстро

На этом этапе мы поняли, что выжали максимум из "железного уровня", а потому далее нам предстояла долгая и кропотливая работа по оптимизации софтварного уровня с целью еще большего увеличения числа звонков и ускорения их установки.

Начали мы с банального устранения хардкода, которым грешат ребята из Osmocom. После парочки оформленных issue стало ясно, что у ребят и без нас дел по горло, поэтому мы просто форкнули репозиторий и исправляли все находимые баги/фичи/затыки самостоятельно у себя с учетом наших потребностей (а они у нас достаточно агрессивные с точки зрения эксплуатации сетей GSM).

Оптимизация стека Osmocom под массовый звонок в сумме заняла у нас несколько месяцев - при каждом выявляемом затыке приходилось разбираться, какие механизмы GSM лежат в его основе, изучать эти механизмы и придумывать такой способ исправления, который бы не выходил за рамки протокола GSM. Ключевые milestone получились следующие:

  1. Исправление бага, из-за которого выставлялся низкий уровень усиления для BTS и базовая станция была крайне слабой, спустя пару месяцев этот баг поправили официально.

  2. Устранение дополнительного хардкода.

    1. Хардкод портов в сервисе osmo-bts, который не позволял в пределах одной системы запускать более одной BTS [git].

    2. Хардкод при выборе номера физического канала LimeSDR в сервисе osmo-trx [git].

  3. Подготовка сбалансированной конфигурации каналов для обеих BTS - такой, чтобы при массовом звонке активных звонков было не просто много(TCH), но еще и не было затыка при их массовой установке(SDCCH) [git].

  4. Добавлено равномерное распределение абонентов по двум BTS - если будет перевес на какой-то одной из них, потенциал звонка не будет реализован полностью на менее загруженной (там останутся свободные речевые каналы в то время как на соседней BTS будет их недостаток) [git].

  5. Уменьшен таймаут paging-request (своеобразный ping перед стартом звонка) с 10 до 3 секунд - в процессе сбора абонентов некоторые из них могут "отвалиться". Такие отвалившиеся абоненты занимают каналы (т.к для них тоже запускается paging-request), в результате чего не получается достучаться до всех живых абонентов [git].

  6. Реализован звонок в 2 "эшелона": сначала пускаем команду звонка тем, у кого наименьший last_seen(отклик на последний пинг), а затем всем остальным [git].

  7. Исправление бага в libosmocore, который из-за захардкоженного размера буфера не позволял единоразово отправлять команду более чем на 75 звонков. Позже выяснилось, что в проекте libosmocore на репорт также отреагировали и этот баг исправили спустя некоторое время [git].

  8. Изменен алгоритм распределения абонентов на более "умный". распределение происходит в реальном времени на основе свободных каналов у BTS, а перед звонком сначала все они пингуются с помощью механизма silent-call, чтобы понять, кто жив, а кто уже отвалился [git].

  9. Произведен "тюнинг" GSM-таймеров [git].

  10. Добавлена фильтрация двух-симочных телефонов [git].

  11. Увеличение числа звонков с 44 до 72 за счет снижения битрейта речевого канала в 2 раза - перешли с TCH/F(full-rate) на TCH/H(half-rate)[git].

  12. Произведен "тюнинг" сетевой подсистемы Linux(sysctl), активно используемой стеком Osmocom для обмена данными между сервисами(IPC) [git].

  13. Для всех входящих звонков от абонентов вешаем трубку - чтобы они не занимали ресурсы BTS [git].

  14. Внедрен pool для более оптимальной отправки paging-request(GSM-пинг) с учетом отвалившихся абонентов перед совершением звонка [git].

  15. Добавлена фильтрация технических GSM-устройств по IMEI (модемы, PoS-терминалы, IoT-устройства и т.д) перед звонком [git].

После всех этих оптимизаций мы смогли звонить всем вокруг одновременно в количестве 72 штук. В качестве бонуса, мы также внедрили рассылку SMS всем вокруг в количестве 384 штук.

По SMS самый интересный затык был в обращениях к SQLite-базе, используемой Osmocom для хранения данных об SMS - они вычитывались из базы по одному. Для нормальной GSM-сети это в общем-то приемлемо до тех пор, пока абоненты не начнут отправлять SMS чаще, чем несколько раз в секунду. Мы перетрясли все запущенные сервисы Osmocom путем отслеживания их сетевого взаимодействия с целью выяснить причину промедления. Очень "приятно" было в итоге обнаружить, как базовые принципы, применяемые при разработке веб-приложений нарушаются в чуть более хардкорных проектах - ну оно и простительно, наверное, - там разработчики все же сосредоточены немного на других вещах - например, как освоить несколько сотен страниц спецификации GSM и не оступиться в реализации.

Итоговая сборка нашего варианта BTS на базе 2 LimeSDR выглядит так и умещается в 24-литровый рюкзак:

Весь исходный код доступен в публичном репозитории Postuf на GitHub, так что при наличии желания, двух LimeSDR, глушилки подходящей мощности и чистой Ubuntu 20.04 на 8-ядерном железе вы сможете повторить описанное у себя. А для удобства мы подготовили CLI-скрипт, который предоставляет простой интерфейс ко всем подкапотным операциям.

❝ We has given you the truth. Do what you will.❞ ©

Источник: https://habr.com/ru/company/postuf/blog/646083/


Интересные статьи

Интересные статьи

Всем привет! С вами Настя, Влад и Даша из команды маркетинга компании Prequel. В этой статье мы расскажем о том, какие фильтры выбирают пользователи в разных странах, и поразмышляем почему.
Рано или поздно, каждый пэхапешник, пишущий на битриксе, начинает задумываться о том, как бы его улучшить, чтобы и всякие стандарты можно было соблюдать, и современные инструменты разработки использов...
В удалении от крупных населенных пунктов, скрытая от посторонних глаз туманом и холмами, многие десятки лет работала крупнейшая в мире обсерватория Аресибо. Это было чудо инженерии. Зер...
Те, кто собираются открывать интернет-магазин, предварительно начитавшись в интернете о важности уникального контента, о фильтрах, накладываемых поисковиками за копирование материалов с других ресурсо...
В Челябинске проходят митапы системных администраторов Sysadminka, и на последнем из них я делал доклад о нашем решении для работы приложений на 1С-Битрикс в Kubernetes. Битрикс, Kubernetes, Сep...