Как позвонить всем вокруг
Однажды нам стало интересно, возможно ли в реальной жизни повторить трюк звонка на телефоны находящихся рядом людей. Оказалось, возможно.
Чтобы заставить все телефоны вокруг звенеть, необходимо всего лишь всем им (желательно, одновременно) отправить команду на звонок. Делов-то! Супер простая цель, которая не так легко достижима на практике. Отправить такую абстрактную команду можно либо непосредственно на каждый телефон, если он вас каким-либо образом "слушает", либо через посредника - сотового оператора, которого каждый девайс "слушает" по умолчанию (в зависимости от своей SIM).
Минусы первого способа - придется разбираться в том как устроен "протокол" сотового звонка, чтобы суметь отправить заветную команду на звонок, а также думать над тем, как в принципе заставить телефоны вокруг "слушать" команды.
Минусы второго способа - слишком большое число операторов и совершенно непонятно, как найти телефоны "вокруг" себя. Здесь можно было бы схитрить, воспользовавшись услугой из даркнета по получению номеров абонентов в конкретном регионе(в соте) - т.н "Зонтик", но на практике оказалось, что в реальном времени такую услугу никто не предоставляет - нужно ждать несколько дней, что делает этот способ сомнительным.
Становимся мобильным оператором
Чтобы можно было звонить мобильным устройствам напрямую, необходимо, чтобы они относились к нам как к полноценному оператору сотовой связи. Неотъемлемая составляющая любой современной сотовой сети - базовая станция (BTS), к которой подключаются мобильные устройства для дальнейшего взаимодействия с сетью. За базовой станцией может и не быть никакой реальной "сети", однако об этом абоненты никогда не узнают.
О поднятии собственных базовых станций уже писали не один десяток раз, в том числе и на Хабре, поэтому лишний раз углубляться в подробности не будем. Отметим лишь, что наиболее удобной будет станция поколения 2G, т.к в этом поколении можно напрочь прибить все механизмы безопасности на уровне самой BTS, на которые полагаются современные устройства.
Существует целый ряд уже готовых реализаций 2G BTS, среди них особенно выделяются Osmocom и YateBTS. Для поднятия своей BTS также понадобится SDR-оборудование. Мы решили остановиться на наиболее доступной связке Osmocom+LimeSDR. Небольшой ресерч с использованием лишь одного гугла позволил в достаточно короткие сроки разобраться с основными концепциями GSM(2G) сети и поднять собственную "коробочную" версию сети.
На этом этапе мы уже могли подключать абонентов к себе вручную через выбор сети в настройках смартфона. Но на ручном подключении далеко не уедешь...
Вышибаем абонентов из родной сети к себе
Как заставить чужие мобильные телефоны подключаться к какой-то левой сотовой сети вместо "родной", да еще и к сети более старого(небезопасного) поколения 2G? Для ответа на этот вопрос понадобилось разобраться с тем, как мобильные устройства вообще сканируют эфир в поисках базовых станций. Кратко алгоритм выглядит так:
- "текущее_поколение" = МАКСИМАЛЬНО_ПОДДЕРЖИВАЕМОЕ (5G, 4G, 3G, 2G)
- "найденные_станции" = просканируй_эфир("текущее поколение")
- если "найденные_станции" != пусто
- подключиться(лучшая_базовая_станция("найденные_станции"))
- иначе, если "текущее_поколение" > 2G
- "текущее_поколение" -= 1
- 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:
После этой операции все телефоны магическим образом разделились на 2 категории:
- те, кто беспрепятственно подключаются к BTS в течение нескольких минут после глушения (в основном Android-девайсы);
- те, кто долго висят без сети и подключаются спустя 30+ минут после глушения (в основном iOS-девайсы).
Ждать 30 минут, пока телефон перестроится в новый режим, - слишком долго - за это время все вокруг просто разойдутся. Здесь мы пошли на вторую уловку - перенесли BTS в самое начало стандартной европейской полосы E-GSM-900 - на частоту 925.2МГц, подкрутили глушилку так, чтобы она накрывала все базовые станции в диапазоне E-GSM-900 выше BTS:
В этом случае мы смогли собрать 100% мобильных устройств в радиусе подавления на своей BTS!
Массовый звонок
На текущий момент у нас есть 1 базовая станция стандарта GSM, поднятая в самом начале европейской полосы E-GSM-900, выше которой все закрыто глушилкой. Абоненты подключаются, и с ними уже можно взаимодействовать посредством VTY-интерфейса Osmocom. Для удобства мы написали скрипт на Python, который все красиво отображает в терминале:
Чтобы звонить со стороны 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 остается всего 927.5-925 = 2.5МГц "свободного места", а с тремя активными TRX будет занято 1.8МГц из них (каждый занимает 0.4МГц полосы и требует 0.2МГц свободного пространства после себя) - почти впритык. Однако из-за того, что границы глушилки могут "плавать" на +-1-2МГц из-за температуры окружающей среды, необходимо сместить начало границы глушения немного ниже частоты 927.5МГц - на частоту ~926МГц, чтобы обеспечить надежное закрытие базовой станции Мегафона. В этом случае последний TRX может попадать в зону глушения, и звонки будут либо обрываться, либо вообще не будут даже начинаться:
Здесь стало ясно, что для увеличения числа параллельных звонков придется уходить с полосы E-GSM-900. Но куда в этот раз? С полосы GSM-850 мы уже ушли из-за того, что не все мобильные устройства её сканируют. А что если совместить эти два способа?
Вытягиваем абонентов с помощью станции-"приманки"
В ходе многочисленных экспериментов и наблюдений за чужими базовыми станциями выяснилось, что станции могут сообщать подключенным абонентам своих "соседей" (в рамках одной мобильной сети), а абоненты в свою очередь принимают эту информацию и кэшируют у себя.
Если, например, абонент подключился к GSM-станции на частоте 955МГц, и она сообщила, что рядом с ней есть еще одна GSM-станция на частоте 957МГц, то при внезапном исчезновении активной станции (на частоте 955МГц) абонент в первую очередь сходит в свой кэш и попробует переключиться на станции оттуда, вместо полного сканирования радиоэфира. Для абонента это банально менее энергозатратно!
Если мы поднимем одну BTS в начале европейской полосы E-GSM-900, а вторую - в любом месте американской полосы GSM-850 и заставим "европейскую" BTS сообщать о своем соседе на "американской" полосе, у нас получится провернуть трюк с "отравлением" кэша, чтобы ускорить перевод медлительных абонентов на полосу GSM-850, которую они так долго не хотят начинать сканировать. Потребуется еще один LimeSDR для запуска второй BTS.
Провернуть этот трюк мы решили следующим образом:
- Поднимем первую BTS с 3 TRX на частоте 869.2МГц (американская полоса), которая сообщает пока что несуществующего соседа на частоте 871.2МГц;
- Поднимем вторую BTS с 1 TRX на частоте 925.2МГц (европейская полоса), которая сообщает единственного соседа на частоте 869.2МГц;
- Собираем глушилкой абонентов вокруг - какие-то из абонентов "осядут" на первой BTS, какие-то на второй;
- Выключаем вторую BTS (925.2МГц) и тут же поднимаем её, но уже с 3 TRX на американской частоте 871.2МГц - теперь сосед существует у первой BTS, а перезапущенная BTS сообщает соседа на 869.2МГц;
- Все абоненты успешно "спустились" с 925.2МГц на 869.2МГц/871.2МГц.
Такой трюк позволил нам собрать 100% мобильных устройств в радиусе подавления на своих BTS, и вдобавок в нашем распоряжении оказалось 2*(6+8+8) = 44 таймслота под параллельные звонки. Неплохо, но можно лучше.
Звоним много и быстро
На этом этапе мы поняли, что выжали максимум из "железного уровня", а потому далее нам предстояла долгая и кропотливая работа по оптимизации софтварного уровня с целью еще большего увеличения числа звонков и ускорения их установки.
Начали мы с банального устранения хардкода, которым грешат ребята из Osmocom. После парочки оформленных issue стало ясно, что у ребят и без нас дел по горло, поэтому мы просто форкнули репозиторий и исправляли все находимые баги/фичи/затыки самостоятельно у себя с учетом наших потребностей (а они у нас достаточно агрессивные с точки зрения эксплуатации сетей GSM).
Оптимизация стека Osmocom под массовый звонок в сумме заняла у нас несколько месяцев - при каждом выявляемом затыке приходилось разбираться, какие механизмы GSM лежат в его основе, изучать эти механизмы и придумывать такой способ исправления, который бы не выходил за рамки протокола GSM. Ключевые milestone получились следующие:
- Исправление бага, из-за которого выставлялся низкий уровень усиления для BTS и базовая станция была крайне слабой, спустя пару месяцев этот баг поправили официально.
- Устранение дополнительного хардкода.
- Хардкод портов в сервисе osmo-bts, который не позволял в пределах одной системы запускать более одной BTS [git].
- Хардкод при выборе номера физического канала LimeSDR в сервисе osmo-trx [git].
- Подготовка сбалансированной конфигурации каналов для обеих BTS - такой, чтобы при массовом звонке активных звонков было не просто много(TCH), но еще и не было затыка при их массовой установке(SDCCH) [git].
- Добавлено равномерное распределение абонентов по двум BTS - если будет перевес на какой-то одной из них, потенциал звонка не будет реализован полностью на менее загруженной (там останутся свободные речевые каналы в то время как на соседней BTS будет их недостаток) [git].
- Уменьшен таймаут paging-request (своеобразный ping перед стартом звонка) с 10 до 3 секунд - в процессе сбора абонентов некоторые из них могут "отвалиться". Такие отвалившиеся абоненты занимают каналы (т.к для них тоже запускается paging-request), в результате чего не получается достучаться до всех живых абонентов [git].
- Реализован звонок в 2 "эшелона": сначала пускаем команду звонка тем, у кого наименьший last_seen(отклик на последний пинг), а затем всем остальным [git].
- Исправление бага в libosmocore, который из-за захардкоженного размера буфера не позволял единоразово отправлять команду более чем на 75 звонков. Позже выяснилось, что в проекте libosmocore на репорт также отреагировали и этот баг исправили спустя некоторое время [git].
- Изменен алгоритм распределения абонентов на более "умный". распределение происходит в реальном времени на основе свободных каналов у BTS, а перед звонком сначала все они пингуются с помощью механизма silent-call, чтобы понять, кто жив, а кто уже отвалился [git].
- Произведен "тюнинг" GSM-таймеров [git].
- Добавлена фильтрация двух-симочных телефонов [git].
- Увеличение числа звонков с 44 до 72 за счет снижения битрейта речевого канала в 2 раза - перешли с
TCH/F(full-rate)
наTCH/H(half-rate)
[git]. - Произведен "тюнинг" сетевой подсистемы Linux(sysctl), активно используемой стеком Osmocom для обмена данными между сервисами(IPC) [git].
- Для всех входящих звонков от абонентов вешаем трубку - чтобы они не занимали ресурсы BTS [git].
- Внедрен pool для более оптимальной отправки paging-request(GSM-пинг) с учетом отвалившихся абонентов перед совершением звонка [git].
- Добавлена фильтрация технических 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.❞ ©