Введение

Привет, Хабр! Являюсь давним пользователем маршрутизаторов Keenetic/Netcraze. В свое время подкупили надежность устройств, широкие возможности программного обеспечения, оперативная техническая поддержка, большое сообщество пользователей, возможность установки дополнительных пакетов через среду Entware. В относительно недавнем прошлом производитель строил свои устройства на базе mips/mipsel чипов, таким образом, несколько таких устройств оказались у меня в руках.

Дополнительные пакеты в репозитории среды Entware позволяют решать массу задач. Я в свою очередь решил использовать ее для решения проблем деградации сети Интернет в текущем месте проживания.

Пример деградации сети. Особенно досадно, что файлы от которых зависит корректная работоспособность веб-сайта, приходят выборочно
Пример деградации сети. Особенно досадно, что файлы от которых зависит корректная работоспособность веб-сайта, приходят выборочно

В своих поисках остановился на программе Xray. Помимо исходного кода, авторы любезно предоставляют свой продукт собранным в виде консольной программы под различные платформы и архитектуры, включая mips на linux. Ничего не имею против консоли, но необходимость редактирования достаточно больших json-файлов в поиске работоспособной конфигурации, при появлении новых деградаций, немного удручает. Особенно когда знаешь, что графические приложения, основанные на исходном коде Xray, умеют конфигурироваться с помощью url-ов или qr-кодов. Поэтому решил найти решение с графическим веб-интерфейсом в репозиториях, на которые ссылаются авторы Xray. Подавляющее их большинство написано на языке Go и в бинарном виде поставляются только для архитектур x86_64, arm, s390. Что вполне достаточно для типового VPS/VDS, но не для нашего встраиваемого устройства. Исправим эту оплошность :-)

Идеальный вариант

Конечно было бы удобнее получить инструмент, который можно было бы интегрировать в веб-интерфейс Keenetic OS (например, в виде плагина, как для OpenWRT или для AsusWRT). Но я так понимаю таких возможностей интеграции со стороны ОС нашего устройства нет.

Первая попытка

Среди многообразия вышеупомянутых приложений, было выбрано tx-ui из-за минимальных зависимостей от внешних СУБД и скриптов по настройке, которые по-умолчанию рассчитаны на использование systemd.

Компилятор Go предоставляет возможности по кросс-компиляции из коробки для большого количества архитектур через выставление переменных окружения GOARCH и GOOS. На момент написания статьи есть проблемы с компиляцией под MIPS на версии 1.26, поэтому пользоваться будем версией 1.25.8. Устанавливаем, воспользовавшись инструкцией с официального сайта.

upd: В соответствии с информацией отсюда, выбор архитектуры зависит от времени выхода и наличия adsl-модема в устройстве (новые -- arm, со встроенным adsl-модемом -- mips big endian, все остальные -- mips little endian[mipsel]).

Скачиваем архив с исходным кодом со страницы релизов в github, распаковываем, переходим в директорию:

mkdir habr_mips
cd habr_mips/
wget https://github.com/AghayeCoder/tx-ui/archive/refs/tags/v0.6.9.tar.gz
tar -xzf v0.6.9.tar.gz
cd tx-ui-0.6.9/

Выставляем переменные окружения, собираем:

GOOS=linux GOARCH=mipsle go build 'main.go'

Проверяем формат собранного файла:

file main
main: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), statically linked, BuildID[sha1]=e539635e58a2b8b87f24e4590d1109ed8e0eb99c, with debug_info, not stripped

32-bit LSB executable, MIPS, MIPS32 – сообщает о том что ELF-файл собран для little endian 32-разрядной архитектуры MIPS.

Переносим файл любым доступным способом на маршрутизатор, выполняем его и натыкаемся на ошибку:

./main 2026/03/15 12:11:00 Starting x-ui 0.6.9 2026/03/15 12:11:00 Error initializing database: Binary was compiled with 'CGO_ENABLED=0', go-sqlite3 requires cgo to work. This is a stub

Поверхностный поиск проблемы сообщает о том, что мы должны выставить переменную окружения CGO_ENABLED=1 и убедиться в том, что компилятор gcc установлен. Наверное самым простым способом будет установить обертку над gcc для сборки mips приложений на x86. Но на текущий момент нет ясности в том, что нам не понадобиться собирать зависимости с динамическим подключением библиотек. Этот момент важен, так как система Entware установлена по пути /opt, что не даст нам найти /opt/bin/ld по пути /bin/ld для поиска библиотек из Entware.

Вторая попытка

Подготовительный этап

Для исключения проблем, которые могут гипотетически возникнуть при необходимости подключения зависимостей динамически, перейдем к более консервативному варианту получения обертки над компилятором gcc. Для этого воспользуемся инструкцией по сборке OpenWRT-пакетов из исходных кодов из репозитория Entware, а также ссылками на построение SDK с сайта OpenWRT: 1, 2.

Для сборки я использовал LXC-контейнер с окружением Debian 11, так как вероятно Entware базируется на OpenWRT 19.07 или ниже и ему для сборки требуется Python 2.7.

Сборка toolchain

Устанавливаем следующие зависимости для сборки:

sudo apt install build-essential ccache ecj fastjar file g++ gawk \
gettext git java-propose-classpath libelf-dev libncurses5-dev \
libncursesw5-dev libssl-dev python python2.7-dev python3 unzip wget \
python3-distutils python3-setuptools python3-dev rsync subversion \
swig time xsltproc zlib1g-dev g++-multilib p7zip-full

Скачиваем репозиторий и открываем его директорию:

git clone https://github.com/Entware/Entware.git && cd Entware

Обновляем скрипты сборки:

make package/symlinks

Копируем конфигурационный файл для сборки под архитектуру mipsel:

cp -iv configs/mipsel-3.4.config .config

Собираем оставшиеся инструменты для хост-системы:

make -j$(nproc) tools/install

Тулчейн:

make -j$(nproc) toolchain/install

Системные пакеты для целевой системы:

make -j$(nproc) target/compile

Если на каком-то из предыдущих 3 этапов возникает ошибка, следует перезапустить команду с параметром -j1 вместо -j$(nproc) и дополнительным параметром V=sc, например:

make -j1 target/compile V=sc

Если такая замена не помагает, то придется разбираться с проблемой с помощью поисковых систем.

При успешном выполнении команд, в директории staging_dir/toolchain-mipsel_mips32r2_gcc-8.4.0_glibc-2.27/bin будут располагаться исполняемые файлы с префиксом mipsel-openwrt-linux*.

Список исполняемых файлов

~/Entware/staging_dir/toolchain-mipsel_mips32r2_gcc-8.4.0_glibc-2.27/bin$ ls | grep mips
mipsel-openwrt-linux-addr2line
mipsel-openwrt-linux-ar
mipsel-openwrt-linux-as
mipsel-openwrt-linux-c++
mipsel-openwrt-linux-c++filt
mipsel-openwrt-linux-cpp
mipsel-openwrt-linux-elfedit
mipsel-openwrt-linux-g++
mipsel-openwrt-linux-gcc
mipsel-openwrt-linux-gcc-8.4.0
mipsel-openwrt-linux-gcc-ar
mipsel-openwrt-linux-gcc-nm
mipsel-openwrt-linux-gcc-ranlib
mipsel-openwrt-linux-gcov
mipsel-openwrt-linux-gcov-dump
mipsel-openwrt-linux-gcov-tool
mipsel-openwrt-linux-gnu-addr2line
mipsel-openwrt-linux-gnu-ar
mipsel-openwrt-linux-gnu-as
mipsel-openwrt-linux-gnu-c++
mipsel-openwrt-linux-gnu-c++filt
mipsel-openwrt-linux-gnu-cpp
mipsel-openwrt-linux-gnu-elfedit
mipsel-openwrt-linux-gnu-g++
mipsel-openwrt-linux-gnu-gcc
mipsel-openwrt-linux-gnu-gcc-8.4.0
mipsel-openwrt-linux-gnu-gcc-ar
mipsel-openwrt-linux-gnu-gcc-nm
mipsel-openwrt-linux-gnu-gcc-ranlib
mipsel-openwrt-linux-gnu-gcov
mipsel-openwrt-linux-gnu-gcov-dump
mipsel-openwrt-linux-gnu-gcov-tool
mipsel-openwrt-linux-gnu-gprof
mipsel-openwrt-linux-gnu-ld
mipsel-openwrt-linux-gnu-ld.bfd
mipsel-openwrt-linux-gnu-nm
mipsel-openwrt-linux-gnu-objcopy
mipsel-openwrt-linux-gnu-objdump
mipsel-openwrt-linux-gnu-ranlib
mipsel-openwrt-linux-gnu-readelf
mipsel-openwrt-linux-gnu-size
mipsel-openwrt-linux-gnu-strings
mipsel-openwrt-linux-gnu-strip
mipsel-openwrt-linux-gprof
mipsel-openwrt-linux-ld
mipsel-openwrt-linux-ld.bfd
mipsel-openwrt-linux-nm
mipsel-openwrt-linux-objcopy
mipsel-openwrt-linux-objdump
mipsel-openwrt-linux-ranlib
mipsel-openwrt-linux-readelf
mipsel-openwrt-linux-size
mipsel-openwrt-linux-strings
mipsel-openwrt-linux-strip

Теперь когда необходимые компиляторы получены, вернемся к сборке Go приложения.

Сборка

Модифицируем команду которую использовали выше для сборки Go приложения, добавив в нее следующие параметры:

GOMIPS=softfloat – добавляем программную эмуляцию вычислений с плавающей точкой

CGO_ENABLED=1 – переменная окружения, которая включает поддержку cgo в Go, позволяя коду Go взаимодействовать с библиотеками C.

CC=/home/user/Entware/staging_dir/toolchain-mipsel_mips32r2_gcc-8.4.0_glibc-2.27/bin/mipsel-openwrt-linux-gnu-gcc – переменная окружения, которая задает путь до компилятора gcc (нужен для работы CGO_ENABLED)

CXX=/home/user/Entware/staging_dir/toolchain-mipsel_mips32r2_gcc-8.4.0_glibc-2.27/bin/mipsel-openwrt-linux-gnu-g++ – переменная окружения, которая задает путь до компилятора g++ (нужен для работы CGO_ENABLED)

-ldflags=‘-s -w’ – флаги компиляторам: отключение таблицы символов и генерации отладочной информации в исполняемом файле.

GOOS=linux GOARCH=mipsle GOMIPS=softfloat CGO_ENABLED=1 CC=/home/user/Entware/staging_dir/toolchain-mipsel_mips32r2_gcc-8.4.0_glibc-2.27/bin/mipsel-openwrt-linux-gnu-gcc CXX=/home/user/Entware/staging_dir/toolchain-mipsel_mips32r2_gcc-8.4.0_glibc-2.27/bin/mipsel-openwrt-linux-gnu-g++ go build -ldflags='-s -w' 'main.go'

На выходе получаем файл main:

file main
main: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /opt/lib/ld.so.1, BuildID[sha1]=2e0163cb60d026a4edd6d5a8dfe227001a2675cb, for GNU/Linux 3.2.0, stripped

Конфигурация приложения на устройстве

Переносим собранный файл любым удобным способом на конечное устройство (или можно использовать эмулятор – про него возможно стоит статью написать) и переименовываем его в оригинальное название:

mv main x-ui

Первоначальный запуск выполняем для уточнения доступа к веб-панели

./x-ui setting -show
current panel settings as follows:
Warning: Panel is not secure with SSL
hasDefaultCredential: true
port: 2053
webBasePath: /

По информации из github, defaultCredential следующие:

Имя пользователя: admin
Пароль: admin

Запуск приложения производим вместе с выставлением переменной окружения XUI_DB_FOLDER=/opt/etc/x-ui, так как путь к директории с файлом БД по умолчанию /etc/x-ui ведет в read-only для Entware раздел.

XUI_DB_FOLDER=/opt/etc/x-ui ./x-ui

Далее в открываем браузер, вводим туда адрес устройства, порт и webBasePath (если задан) и видим следующую страницу

Окно входа
Окно входа
Окно статуса системы
Окно статуса системы

Считаем, что приложение работоспособно. Конфигурирование приложения не буду затрагивать, так как оно происходит аналогично 3x-ui или схожих программ (примеры можно найти на различных сетевых ресурсах, включая Хабр).

Добавляем скрипт автозапуска в /opt/etc/init.d. Файл должен называться SXXимя, где XX -- приоритет запуска службы (чем больше номер -- тем позже запуск).
#!/bin/sh

ENABLED=yes
PROCS=x-ui
ARGS="run"
PREARGS="XUI_DB_FOLDER=/opt/etc/x-ui"
DESC=$PROCS
PATH=/opt/sbin:/opt/bin:/opt/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/opt/tx-ui

#. /opt/etc/init.d/rc.func

#OPID=$(pgrep x-ui)
#echo $OPID

daemon_status ()
{
        [ -n "`pidof x-ui`" ]
}

start() {
	echo "starting tx-ui..."
	cd /opt/opt/tx-ui
	export $PREARGS
	$PROCS $ARGS &
	}

stop() {
	echo "stopping tx-ui..."
	PID=$(pidof x-ui)
	kill $PID
	}

status() {
	if daemon_status; then
		echo "PID of tx-ui is $(pidof x-ui)"
	else
		echo "tx-ui is not running"
	fi
	}

case "$1" in
        start)
                start
                ;;
        stop)
                stop
                ;;
        restart)
                stop
                sleep 3
                start
                ;;
        status) status
                ;;
        *)
                echo "Usage: $0 (start|stop|restart|status)"
                exit 1
                ;;

esac

exit 0

Таким образом мы собрали и развернули Go приложение для архитектуры mips.