Маршрутизатор Banana Pi R64 — Debian, Wireguard, РКН
- суббота, 11 апреля 2020 г. в 00:12:11
Banana Pi 64 — это одноплатный компьютер по типу Raspberry Pi, но с несколькими портами Ethernet, что дает возможность сделать из него маршрутизатор на базе дистрибутива Linuх общего назначения.
Да, уже есть Openwrt, но у него свои заморочки свой GUI и CLI; есть Mikrotik, но у него опять же свой GUI/CLI, да и Wireguard из коробки не работает… В общем хочется маршрутизатор с гибкими настройками, при этом оставшись в рамках стандартного линукса, с которым работаешь каждый день.
В статье под названиями BPI, R64, одноплатник я буду подразумевать одно и тоже — собственно сам одноплатник Banana Pi R64.
Самый первый навык, который нужно приобрести при работе с SBC вообще, и с R64 в частности, это научиться грузить в него операционку и иметь возможность взаимодействовать с ним, ведь у R64 нет порта для монитора (HDMI, например). Когда все отвалилось — перестал работать Wifi, сеть Ethernet, Bluetooth, USB и прочее есть UART, через интерфейс которого всегда можно посмотреть, что пошло не так, а также запустить пару команд из консоли, по необходимости.
Алгоритм подключения к R64 по USB-UART:
sudo minicom
После этого в большинстве случаев появится консоль одноплатника = успех.
Подробнее можно посмотреть здесь.
Далее, проще всего загрузить операционную систему с SD-карты: скачиваем по ссылке образ и заливаем его:
unzip -p 2019-08-23-ubuntu-16.04-lite-preview-bpi-r64-sd-emmc.img.zip | pv | sudo dd of=/dev/mmcblk0 bs=10M status=noxfer
вставляем карту в SD-слот R64, включаем, наблюдаем по подключенной консоли загрузку сначала uboot, затем стандартную загрузку линукса.
Альтернативный вариант загрузки — с помощью уже вшитой в R64 карты размером 8Gb, называемой eMMC. По инструкции в вики переписываем образ на устройство
/dev/mmcblk0 в BPI, перегружаемся, вытаскиваем SD-карту, включаем BPI снова… и не работает. Как туда-сюда Boot select
ни дергай.
Дело в том, что как минимум для BPI необходимо поставить специальный флаг, чтобы иметь возможность грузиться с внутренней флешки:
root@bpi-r64:~# ./mmc extcsd read /dev/mmcblk1 | grep 'PARTITION_CONFIG'
Boot configuration bytes [PARTITION_CONFIG: 0x00]
root@bpi-r64:~# ./mmc bootpart enable 1 1 /dev/mmcblk1
root@bpi-r64:~# ./mmc extcsd read /dev/mmcblk1 | grep 'PARTITION_CONFIG'
Boot configuration bytes [PARTITION_CONFIG: 0x48]
Далее, в специальный boot-вый раздел надо записать preloader
root@bpi-r64:~# echo 0 > /sys/block/mmcblk0boot0/force_ro
root@bpi-r64:~# dd if=preloader_evb7622_64_foremmc.bin of=/dev/mmcblk0boot0
Производитель R64 (Китай) выложил этот бинарь здесь. Что он делает неизвестно (исходников нет), но без него тоже не заработает.
В общем-то после этого образы начинают грузиться и с eMMC. Если же захотеть разобраться и создавать образы с нуля, то для обоих случаев (SD/eMMC) нужно записывать еще несколько файлов (preloader для SD-карты, ATF, u-boot), только чтоб добраться до загрузки ядра. Тема эта до сих пор развивается, но для нас главное, что работает и ладно.
Сейчас я загрузку по eMMC, честно говоря, не использую, SD карты достаточно, но я довольно много времени потратил на то, чтоб оно заработало, поэтому пусть будет в статье.
Первая прикладная задача — запустить VPN, естественно Wireguard. Сразу же обнаружилось, что со стороны ядра он не собран, и заголовков нет. Пересобрал ядро и, по привычке с x86, собрал модуль ядра с помощью DKMS. Однако скорость сборки на arm64 даже небольших утилит меня неприятно удивила. А дальше еще один модуль ядра потребовался, и т.д. В общем, выходит, что все, связанное с ядром, лучше собирать на теплом-ламповом ноутбуке x86, затем простым копированием переносить на R64, перегружаться и тестить.
Другое дело — юзейспейсная часть. В моем случае выбора Debian, все для архитектуры arm64 уже есть на packages.debian.org и ничего пересобирать не приходится.
Чтобы не плодить очередной велосипед, я портировал Armbian на BPI R64.
Вернее так: userspace-ная часть — Armbian, а ядро берется из репозитория Frank-а. Самый свежий образ можно скачать здесь.
Вся активность по разработке софтовой части R64 ведется на форуме. Вообще говоря, сам производитель стремится популяризировать роутер под Openwrt, но благодаря активности разработчика Frank-а из Германии все фичи быстро оказываются в ядре для Debian-а. Удивительно, но Frank активен в каждой ветке форума.
Отдельно хочу рассказать, как во время разработки/тестирования разместить SBC (не только BPI) на столе так, чтоб не вести Ethernet-кабель к нему от источника интернета через всю комнату/офис. Дело в том, что с одной стороны нужно обеспечить железке интернет, а с другой стороны в этой самой железке может все ломаться, и в первую очередь Wifi.
Сначала я решил приобрести дешевый USB-Wifi "свисток", воткнуть его в единственный порт на BPI и забыть о проводах. Для этого приобрел недорогой TP-LINK TL-WN725N USB 2.0, но очень скоро стало понятно, что не взлетит: для работы свистка нужен драйвер ядра, которого там, естественно, не было (позже я собрал нужный драйвер RTL8XXXU, но все равно это непрактично). И Ethernet-кабель портил вид комнаты некоторое время.
В итоге от кабеля мне удалось избавиться с помощь Tenda MW3 (Wifi mesh-система): просто расположил один кубик под столом и метровым Ethernet-кабелем подключил BPI к LAN-порту последнего. Успех.
Одна из хотелок, для чего я использую Banana PI — иметь свободный доступ на сайты, заблокированные РКН, в частности, чтоб работал Telegram и звонки в Slack-е. На эту тему уже были предложены статьи на Хабре: раз, два, три.
Развертывание именно такого решения я реализовал с помощью Ansible: ссылка.
Предполагается, что VPS работает под Ubuntu 18.04. Работоспособность проверил на двух хостерах в Европе: Amazon и Digital Ocean.
Итак, мы установили вышеуказанный Armbian на R64, он доступен по ssh под именем hm-bananapi-1
и имеет доступ в интернет. Развертываем последовательно ansible, скрипты автоматизации и запускаем установку собственно на R64:
# зависимости для Debian-based дистрибутивов
$ sudo apt install --no-install-recommends python3-pip python3-setuptools python3-wheel git
$ which pip3
/usr/bin/pip3
# ansible с pybook, скриптование на Python
$ pip3 install https://github.com/muravjov/ansible/archive/ansible-2.10.0.dev0-pybook2019.tar.gz
$ export PATH=~/.local/bin:$PATH
$ which ansible-playbook
/home/sa/.local/bin/ansible-playbook
$ git clone https://github.com/muravjov/ansible-bpi-r64.git
$ cd ansible-bpi-r64
$ git submodule update --init
# убеждаемся в доступности hm-bananapi-1
$ ssh hm-bananapi-1 which python3
/usr/bin/python3
# собственно установка
$ ansible-playbook ./router.py -l hm-bananapi-1
Далее нужно аналогичным образом задеплоить на VPS наш VPN:
ansible-playbook ./router.py -l current-vpn
Здесь аргумент всегда current-vpn, а собственно имя VPS настраивается в переменной (в данном случае это paris-vpn-aws-t2-micro-1):
$ grep current_vpn group_vars/all
current_vpn: paris-vpn-aws-t2-micro-1
#current_vpn: frankfurt-vpn-d0-starter-1
Ах да, перед всеми этими операциями нужно сгенерировать секреты (в частности ключи Wireguard) в папку ./secrets
, директория должна выглядеть так.
Можно заметить, что вместо формата YAML команды Ansible закодированы в скриптах Python. Для сравнения, как обычным способом включить демон bird:
- name: start bird
systemd:
name: bird
state: started
enabled: yes
и как то же самое через Python:
with mapping:
append("name", "start bird")
with mapping("systemd"):
append("name", "bird")
append("state", "started")
append("enabled", "yes")
Запись команд Ansible кодом на Python позволяет повторно использовать код, да и вообще открыты все возможности языка общего назначения. Например, установка bird на R64 и VPS:
install_bird("router/bird.conf.j2")
install_bird("vpn/bird.conf.j2")
посмотреть код функции install_bird().
Данная фича под названием pybook
реализована здесь. Документации по pybook пока нет, потом исправлю этот недочет.
Что думает upstream по этому поводу.
Итого: телеграмчик работает, linkedin и pornhub тоже, в общем user experience — ок. Но все может ломаться, и китайские железки тоже.
Обновления ядра тоже бывают интересными: например, я захотел обновить ядро 5.4 => 5.6, нуачо, там же Wireguard изкоробки, не надо патчить… Сказано-сделано: кропотливо переносил патчи с 5.4 на 5.6, ядро завелось, туннель до VPS пингуется, но bird не может соединиться с ошибкой "BGP Error"… "В ужасе откатился назад" (с) на 5.4; переезд на 5.6 отложил в TODO.
Поэтому в дополнение к инсталяции роутера и VPS добавил мониторинг (на x86 Ubuntu 18.04), который ставится на отдельный хост со следующими компонентами:
Пример установки мониторинга:
ansible-playbook ./monitoring.py -l monitoring-preprod
Auto Discovery для прометея настроено на папку /etc/prometheus/auto_http, пример добавления хоста в мониторинг (по умолчанию хосты не мониторятся):
bash << 'EOF'
HOSTNAME=hm-bananapi-1
IP_ADDRESS=`ssh -G $HOSTNAME | awk '/^hostname / { print $2 }'`
ssh monitoring-preprod sudo sponge /etc/prometheus/auto_http/$HOSTNAME.json << EOF2
[
{
"targets": ["$IP_ADDRESS:9100"],
"labels": {
"env": "prod",
"hostname": "$HOSTNAME"
}
}
]
EOF2
EOF
В дополнении ко всему запланировал подключиться к двум провайдерам, чтобы интернет продолжил работать, даже если у одного провайдера проблемы с сетью, или забыли заплатить за интернет и т.д., и прочий человеческий фактор.
Наиболее продвинутый user experience на тему multi-wan описан здесь для системы Mwan3 под Openwrt. У данного решения богатый функционал, но настройка и эксплуатация вообще для multi-wan довольно хлопотна. Один только пример: если приходить на некоторые сайты сразу с двух IP-адресов, то им это может не понравится, они перестанут работать => "интернет не работает".
Учитывая данный опыт решил, что multihoming пока не в приоритете, только failover. Хотя, кажется, в последних версиях linux все должно работать одной командой вида:
ip route add default \
nexthop via 192.168.1.1 weight 10 \
nexthop via 192.168.2.1 weight 5
Итак, чтобы не было единой точки отказа, берем 2 BPI, каждый подключаем к одному провайдеру, соединяем их между собой и связь друг c другом сделаем динамической маршрутизацией через bird/OSPF.
Далее, на каждом анонсируем одинаковый IP-адрес в случае, если сервис доступен (интернет, DNS). То есть проставлять маршрут по умолчанию будем не сами, а посредством bird. Решение подсмотрел здесь .
Данный функционал пока не реализовал, коварный коронавирус и тут подгадил (не все приехало c алиэкспресс; еще один интернет-магазин, Layta, обещал доставить за неделю, а уже больше месяца прошло; второй провайдер не успел кабель протянуть до карантина, успел только дырку в стене просверлить для кабеля).
Сама плата в официальном магазине SinoVoip.
Также лучше сразу заказать:
Есть нюанс — цена доставки с какого-то времени стала неадекватно высокой в официальном магазине. Менеджер Judy Huang убеждала меня, что ошибки нет, и можно выбрать ePacket за \$5, но я видел, что для России есть только EMS за >33\$. Неприятно, но не критично. Причем, если выбрать любую другую страну для доставки (перебрал все континенты), доставка будет за ~5\$. Русофобы?.. Но потом я нашел, что для Франции цена доставки тоже ~30\$, и успокоился.
В итоге Judy предложила сделать заказ, но не оплачивать (hint: положить на карту меньше, чтоб автоматом оплата не прошла); написать ей, и она снизит цену доставки до нормальной. Успех.
Не все пока работает идеально.
Медленно выполняются команды Ansible=Python, даже холостые, по 20-30 секунд; на порядок дольше, чем на ноуте x86. Причем сначала выполняются достачно быстро, ~3 секунды, затем резко замедляются. Возможно это происходит из-за нагревающегося CPU (throttling). Код на Go тоже долго работает:
# запрос метрик для прометея из node_exporter на Go
$ time curl -s http://172.30.1.1:9100/metrics > /dev/null
real 0m6,118s
user 0m0,005s
sys 0m0,009s
# однако температура 51 градус, не так и много
sa@bananapir64:~$ cat /sys/devices/virtual/thermal/thermal_zone0/temp
51700
Wifi работает, но на Armbian где-то через сутки перестает, пишет:
sa@bananapir64:~$ dmesg | grep -E 'mt7622_wmac.*timeout'
[470303.802539] mt7622_wmac 18000000.wmac: Message 38 (seq 3) timeout
[470314.042508] mt7622_wmac 18000000.wmac: Message 50 (seq 4) timeout
...
Помогает лишь рестарт. Надо дальше разбираться.
Ethernet работает, но через ~сутки пакеты (DHCP) от R64 перестают приходить.
Помогает рестарт интерфейса:
ifdown br0; sleep 30; ifup br0
Драйвер новый, в ядро еще не приняли, надеюсь китаец Landen Chao допилит.