Pull to refresh

Делаем Hyperboria hotspot. Пособие по ковырянию роутера

Reading time 13 min
Views 31K
В этой статье я попытаюсь рассказать, как прошить на роутер OpenWrt и установить туда cjdns. По правде говоря, изучение тонкостей этого дела отняло у меня много времени и нервов. В основном потому, что многое в OpenWrt недодокументировано, и приходилось многое искать по форумам и додумывать. Думаю, это как раз тот случай, когда лучшей документацией являются исходники. Так что эту статью я попытаюсь написать такой, какую я хотел бы прочитать в самом начале этого пути.


Итак, первое, что следует сделать, это пойти на сайт OpenWrt и посмотреть список поддерживаемых устройств: wiki.openwrt.org/toh/start
Если не нашли вашу железку, не растраивайтесь. Мы ведь всегда можем портировать OpenWrt на новое утройство, следуя этой инструкции: kamikaze.openwrt.org/docs/openwrt.html#x1-540002.3 Хотя, сначала нужно как-то вытянуть исходники ядра у производителя.

Теперь немного теории. В OpenWrt для хранения rootfs используется две файловые системы: SquashFS и JJFS2. Так же вожможно подключение других, но об этом позже.
SquashFS — сжатая статическая файловая система. Она — то, что собственно и прошивается. За счет её статичности сжатие в ней лучше.
JJFS2 — сжатая динамическая файловая система, в которую записываются изменения файлов. Она монтируется как-бы поверх SquashFS при помощи фичи ядра Linux Overlayfs. Появилась она начиная с версии ядра 3.11.

Среду для сборки OpenWRT можно скачать в 4-х вариантах.
  • OpenWrt Buildroot — эдакий монстр Франкенштейна, написаный на make, который сам качает исходники всего, что нужно, сам же патчит все это, сам собирает кросскомпилятор, собирает пакеты и образы для прошивки. Форк Buildroot Насчет формата этих makefil-ов. Тут достаточно процитировать документацию:
    «Глядя на Makefile из какого либо пакета, вы вряд ли узнаете в нем обычный Makefile. Это может быть охарактеризовано как вопиющее пренебрежение и жестокое обращение с традиционным форматом make, однако, преобразование Makefile пакета в объектно-ориентированный шаблон позволило упростить весь процесс портирования приложений.»
  • OpenWrt Image Builder — то же самое, но грузит уже откомпиленые пакеты и собирает из них образ.
  • OpenWrt SDK — то же самое, но набор програм для кросскомпиляции уже собран и собирать образы оно уже не может. Предназначено для сборки отдельных пакетов.
  • OpenWrt based Toolchain — уже откомпиленый набор програм для кросскомпиляции, выдраный из Openwrt Buildroot.

Последние три могут быть собраны из OpenWrt Buildroot.

Как уже было сказано выше, в SquashFS сжатие лучше, так что у нас есть больше шансов уместить все, что нужно, собрав прошивку самостоятельно, чем скачав готовый образ и доставив нужный пакет. Для тех, кто собирается ставить готовую минимальную прошивку, вот сборки от разработчиков. Они рассортированы по архитектурам (колонка target в списке поддерживаемого оборудования) и по типу памяти (nand и все остальные), названия файлов включают в себя название роутера. Прошивки, содержащие в названии «sysupgrade» предназначены для перепрошивки средствами уже установленной OpenWrt. Содержащие «factory» можно скормить вебформе заводской прошивки. Не перепутайте.

Приступим к сборке.



Внимание! Сборка не должна проводится от суперпользователя или в fakeroot.

Для начала загрузим buildroot. Здесь есть список веток и команд для их загрузки. Мы же будем собирать Attitude Adjustment stable:
git clone git://git.openwrt.org/12.09/openwrt.git

Репозиториев с beta, beta2, rc1 и rc2, видимо, нету, а жаль, потому что пакет, собраный для stable на beta не стал, только ругнулся, что git commit id ядра не тот, который нужен (хотя верси совпадали).

Далее, следуя инструкции, добавляем в список мест, откуда будут грузится исходники «репозиторий» с cjdns.
cd ./openwrt
cp feeds.conf.default feeds.conf

Добавляем в файл feeds.conf строку
src-git cjdns git://github.com/cjdelisle/cjdns-openwrt.git

Далее, грузим исходники всех пакетов:
./scripts/feeds update -a
./scripts/feeds install -a

Теперь запускаем
make menuconfig

и настраиваем все, что нужно.


Выбираем «Target System» (ту, которая указана в списке поддерживаемых устройств возле названия вашего роутера), Subtarget (если в роутере постоянная память не NAND, смело выбираем Generic), «Target Profile» (ищем название роутера, под который будем собирать). В «Target Images» обычно ничего менять не надо. Выбираем пробелом.

На данном этапе все нужные драйверы уже отмечены (благодаря «Target Profile»). Далее ходим по остальным веткам и выбираем нужные нам пекеты (Именно нам. По идее, и так работать будет). Поиска здесь не предусмотрено, так что искать будем так:
find ./feeds/ -name nano

Благо, большинство пакетов рассортировано по разделам. Хотя они могут находится и в ./package и тогда вы их таким образом не найдете. В выводе видим ./feeds/packages/utils/nano, что значит, что nano нам нужно искать в разделе «Utilities». Для полной уверенности можно почитать makefile пакта. Для nano это ./feeds/packages/utils/nano/Makefile. В нем мы видим такой фрагмент:
define Package/nano
  SUBMENU:=Editors
  SECTION:=utils
  CATEGORY:=Utilities
  TITLE:=An enhanced clone of the Pico text editor
  URL:=http://www.nano-editor.org/
  MAINTAINER:=Bernhard Loos <bernhardloos@googlemail.com>
  DEPENDS:=+libncurses
endef

«CATEGORY» — название раздела, «SUBMENU» — название подраздела. Кстати, такая запись может быть не одна. Дело в том, что из одного набора исходников может собиратся несколько разных пакетов.

UPD: Хабрапользователь klirichek подсказал, что поиск здесь таки есть. Чтобы войти в него, нужно в menuconfig нажать "/".



Получаем списочек объектов, название которых включает в себя введенный запрос, с полным описанием и расположением в меню.

Итак, пакеты, которые нам могут понадобится.
  • nano — чтобы не пришлось править конфиги с помощью vi или cat/echo
  • htop — просмотрщик процесов
  • iftop — програмка для отслеживания сетевой активности (кто, с кем, на какой порт, с какой скоростью)
  • tcpdump — ну куда ж без него?
  • kmod-usb-storage — модуль ядра для подключения внешних носителей памяти по usb
  • kmod-usb-storage-extras — модуль ядра для подключения некоторых экзотических внешних носителей памяти по usb (например, кардридера)
  • block-mount — набор скриптов для автоматического монтирования по /etc/fstab. Для автомонтирования файловых систем по конфигам в формате uci (об этом позже) хватит и mount из busybox, но swap автоматически без них не монтируется.
  • swap-utils — необходим для монтирования swap
  • Всевозможные драйверы файловых систем. Найти можно в «Kernel modules»->«Filesystems»
  • shadow-groopadd, shadow-groupdel, shadow-groupmod, shadow-groups, shadow-passwd, shadow-su, shadow-useradd, shadow-userdel, shadow-usermod — набор утилит для управления пользователями и группами. Обычно можно обойтись и без них.
  • ip,iw,iptables,ip6tables — для ручной настройки сети
  • bind-dig — dns-клиент

Следует отметить, что все может и не поместится. В таком случае образ для роутера собран не будет, но пакеты соберутся. Пакет можно отметить и так, чтобы он собрался, но в образ не включался. Для этого нужно нажать клавишу m.

А теперь расскажу об одном баге, который мешает установке cjdns. В зависимостях cjdns есть пакет с чудным названием nacl. Он используется при сборке других пакетов, но при этом бинарный пакет из него не собирается. В то же время, он появляется и в зависимостях бинарного пакета cjdns. В результате, чтобы установить уже собранный пакет с cjdns, нужно перед этим установить не существующий в собранном виде пакет nacl.

Теперь, как его обойти. Открываем ./openwrt/feeds/cjdns/cjdns/Makefile в текстовом редакторе, находим этот фрагмент:
define Package/cjdns
  SECTION:=net
  CATEGORY:=Network
  SUBMENU:=Routing and Redirection
  TITLE:=Experimental self configuring routing protocol.
  DEPENDS:=+kmod-tun +kmod-ipv6 +libnl +nacl +libpthread
  MAINTAINER:=cjd -- #cjdns on irc.efnet.org
endef

и убераем из «DEPENDS» "+nacl". Должно получится так:
define Package/cjdns
  SECTION:=net
  CATEGORY:=Network
  SUBMENU:=Routing and Redirection
  TITLE:=Experimental self configuring routing protocol.
  DEPENDS:=+kmod-tun +kmod-ipv6 +libnl +libpthread
  MAINTAINER:=cjd -- #cjdns on irc.efnet.org
endef

В menuconfig находим nacl (в разделе «Libraries») и устанавливаем в
.

Далее собираем nacl отдельно:
make package/nacl/compile

Советую при сборке использовать не какой-то mate-terminal, а, к примеру tmux. Будет не очень приятно, если за время сборки, к примеру, упадут иксы.

Еще один нюанс. Чтобы после прошивки поднялась сеть, в прошивке должны быть конфиги настройки сети. Однако, для некоторых роутеров при сборке прошивки соответствующий конфиг не генерируется. В результате, роутер вроде-бы работает, но сеть не поднялась и мы получаем прекрасно работающий кирпич. Я, когда в первый раз собирал прошивку под свой tl-mr3220, столкнулся с этим и потом довольно долго искал нужный кабель и паял так называемый serial hack adapter, чтобы достучатся до железки.
Теперь о формате конфигов. В OpenWrt используется самопальный собственный формат конфигов UCI, общий для всего. Насколько я понял, они потом преобразуются в нормальные. Он, как мне кажется, достаточно хорошо документирован, так что останавливатся на нем не буду. Скажу только, что хранятся они в папке /etc/config/.
Чтобы добавить какие-то свои файлы, в том числе и конфиги, в прошивку, в папке с Buildroot нужно создать папку files и положить в нее нужные файлы. Например, тот же конфиг сети я положил в файл ./files/etc/config/network . Вот и сам конфиг для tl-mr3220 v1.2:
config interface 'loopback'
        option ifname 'lo'
        option proto 'static'
        option ipaddr '127.0.0.1'
        option netmask '255.0.0.0'

config interface 'lan'
        option type 'bridge'
        option proto 'static'
        option netmask '255.255.255.0'
        option _orig_ifname 'eth0 radio0.network1'
        option _orig_bridge 'true'
        option ifname 'eth0'
        option ipaddr '192.168.5.1'
        option gateway '192.168.5.2'
        option broadcast '192.168.5.255'
        option dns '192.168.5.2'

config switch
        option name 'eth0'
        option reset '1'
        option enable_vlan '1'

config switch_vlan
        option device 'eth0'
        option vlan '1'
        option ports '0 1 2 3 4'


Его я извлек из прошивки, взятой с сайта OpenWrt и немножко подправил. Что значат поля _orig_ifname и _orig_bridge не спрашивайте, сам не знаю. Отмечу, что этот конфиг предусматривает, что роутер торчит в одну и ту же сеть всеми своими портами, а не, например, каким-то одним в интернет.

Теперь вернемся к cjdns. Чтобы пакет таки собрался, нужно его отметить в menuconfig: "Network"->"Routing and Redirection"->"cjdns".

Итак, настал торжественный момент:
make


После сборки файлы должны оказатся в ./bin, рассортированые по target-ам (пока он у нас один). К примеру, у меня собраные образы лежат в ./bin/ar71xx, а пакеты в ./bin/ar71xx/packages/. Теперь открываем архив ./bin/ar71xx/openwrt-ar71xx-generic-rootfs.tar.gz (или аналогичный для вашего target) и проверяем, действительно ли там есть конфиг сети.

Теперь можно прошивать.
Как уже говорилось выше, openwrt-ar71xx-generic-tl-mr3220-v1-squashfs-factory.bin можно скормить вебморде заводской прошивки. Еще раз повторю уже стандартное предупреждение, встречаемое чуть ли не везде, где пишут о процесе прошивки: ни в коем случае не прошивайте по wi-fi!

Прошивка при помощи уже установленого OpenWrt.


Заходим по ssh, проверяем, сколько оперативной памяти свободно:
free

Файл с новой прошивкой должен будет лежать в tmpfs.
Далее правим файл /etc/sysupgrade.conf. В нем должны лежать имена файлов и папок, которые сохранятся при перепрошивке (путем занесения в jffs2). Комментарии выделяются символом #. При сохранении конфигов таким образом возможны разные казусы, так как их формат может поменятся.
Копируем в /tmp на роутере (туда примонтирован tmpfs):
scp ./bin/ar71xx/openwrt-ar71xx-generic-tl-mr3220-v1-squashfs-sysupgrade.bin root@192.168.5.1:/tmp 

Проверяем целостность образа. На роутере считаем хеш файла образа:
cd /tmp
md5sum openwrt-ar71xx-generic-tl-mr3220-v1-squashfs-sysupgrade.bin

и сверяем с хешем файла на компьютере.

Скрещиваем пальцы и прошиваем:
sysupgrade -v /tmp/openwrt-ar71xx-generic-tl-mr3220-v1-squashfs-sysupgrade.bin


Если все прошло хорошо, роутер должен перезапустится. Заходим по ssh и с помощью passwd устанавливаем пароль root.

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

Пакетный менеджер.


В OpenWrt в качестве пакетного менеджера используется opkg. Опции:
  • update - Загрузить список пакетов в репозитории. Загружается он в /tmp/opkg-lists/snapshots, а в /tmp примонтирована tmpfs. Так что, после прерзагрузки для каких-либо действий с пакетами прийдется опять грузить список пакетов.
  • upgrade - Начнем с того, что эта опция вам, скорей всего, не понадобится. Пакеты в репозиториях разработчиков меняются только в trunc. Если прописать другой репозиторий от другой версии, возможны казусы. Крайне не рекомендуется обновлять таким образом модули ядра (названия их пакетов начинаются на kmod-). Действия "обновить все под ряд" opkg не умеет вообще.
  • install принимает название пакета, имя файла или url для скачивания:
    opkg install hiawatha
    opkg install http://downloads.openwrt.org/snapshots/trunk/ar71xx/packages/hiawatha_7.7-2_ar71xx.ipk
    opkg install /tmp/hiawatha_7.7-2_ar71xx.ipk
  • configure - Не совсем понятно, что делает. По видимому, еще раз выполняет конфигурационные скрипты пакета.
  • remove - Удалить пакет.
  • list - список пакетов.
  • list-installed - список установленніх пакетов.

Полный список опций здесь: wiki.openwrt.org/doc/techref/opkg
Чтобы добавить репозиторий, в файл /etc/opkg.conf нужно добавить строку вида
src/gz <название репозитория> <url репозитория>

Пример:
src/gz attitude_adjustment http://downloads.openwrt.org/attitude_adjustment/12.09/ar71xx/generic/packages

Чтобы добавить репозиторий, находящийся в папке, нужно дописать
src/gz local file:////путь/к/папке

Отмечу, что подписывание пакетов, как в apt-get, не предусмотрено, как и забирание их по https. Насчет https, конечно, можно извернутся, но все упирается в неправильно подписаный сертификат на downloads.openwrt.org. Хотя, он не самоподписный, а значит, что, извернувшись вдвойне, может что-то получится.

Монтируем дополнителную память с флешки (если нужно).


На флешке должны быть соответствующие разделы. Для монтирования файловой системы лучше всего подойдет ext4. Итак, у нас есть размеченая флешка, которую мы втыкаем в порт на роутере. Проверяем, появились ли в /dev файлы раделов флешки. Если нет, доставляем нужные модули.
Далее, пытаемся монтировать:
mount /dev/sda1 /mnt

Если не получилось, опять же, ставим нужные пакеты. Далее
rm -rf /mnt/*
tar -C /overlay -cvf - . | tar -C /mnt -xf - # копируем содержимое overlay

Почему именно tar, а не по-нормальному? Должно быть, чтобы сохранить атрибуты файлов. Именно этот способ предлагается в wiki.openwrt.org/doc/howto/extroot#installation1 .
Далее, отмонтирываем и правим /etc/config/fstab. В него дописываем или меняем соответствующую секцию на это:
config mount
        option target   /overlay
        option device   /dev/sda1
#       option uuid     <uuid>
        option fstype   ext4
        option options  rw,sync
        option enabled  1
        option enabled_fsck 0

Чтобы изменения вступили в силу, делаем
uci commit fstab

Можно так-же монтировать по uuid, чтобы не было казусов при подключении нескольких устройств по usb. Для этого нужно убрать параметр "device" и добавить "uuid" с, собственно, uuid раздела.
Подробнее о параметрах здесь: wiki.openwrt.org/doc/uci/fstab#mounting.filesystem

Монтируем swap.


Для этого нужно, чтобы в конфиге было нечто вроде этого:
config swap
        option device   /dev/sda2
#       option uuid    <uuid>
        option enabled  1

Здесь все аналогично. По поводу выбора размера swap. Для linux на десктопах часто советуют делать swap немного больше, чем размер оперативной памяти, чтобы при переходе в "спящий режим" (или как там оно называется?) все, что есть в оперативной памяти, поместилось в swap. Согласитесь, для роутера "спящий режим" - это бред. Так что для них действует другое правило - выделяйте побольше. 512 MiB, думаю, хватит всегда и всем!
Просмотреть обьем оперативной памяти и swap можно с помощью команды free.
И не забываем про uci commit.

Cтавим cjdns (если он не поместился в squashfs).


Копируем пакет с помощью scp в /tmp на роутере и устанавливаем с помощью opkg. Далее создаем конфиг cjdns. На некоторых роутерах это может длится долго. Далее добавляем в конфиг ноды, раскомментирываем раздел ETHInterface и прописываем интерфейс br-lan вместо eth0.

Настраиваем wi-fi в режиме ad-hoc.


Открываем /etc/config/wireless и добамляем секцию вроде этой:
config wifi-iface
        option device radio0
        option mode adhoc
        option ssid hype
        option bssid 66:77:55:88:44:aa
        option disabled 0

Опция "device" должна содержать название "устройства", обозначеного в секции "wifi-device". У меня она выглядит так:
config wifi-device 'radio0'
        option type 'mac80211'
        option macaddr 'ab:cd:ef:ab:cd:ef'
        option hwmode '11ng'
        option htmode 'HT40+'
        list ht_capab 'SHORT-GI-40'
        list ht_capab 'TX-STBC'
        list ht_capab 'RX-STBC1'
        list ht_capab 'DSSS_CCK-40'
        option channel 'auto'
        option txpower '18'
        option country 'RU'

Тут есть один нюанс. Не все роутеры умеют поднимать несколько беспроводных сетей с одного физического устройства. Так что если после uci commit wireless ; /etc/init.d/wireless restart одна из сетей не поднялась, то прийдется обойтись одной. Чтобы отключить одну из сетей, достаточно установить опцию disabled в 1.
Теперь проверяем, не попала ли новосозданная сеть в bridge с нашей локальной сетью. Сначала определяем, какому интерфейсу какая сеть соответствует:
iw dev
</code>
А потом проверяем мосты:
<source>
brctl show

Если наш ad-hoc таки слинковался с локальной сетью, идем править /etc/config/network. Ищем секцию interface с опцией "type" установленой в "bridge" вроде этой:
config interface 'lan'
        option type 'bridge'
        option proto 'static'
        option netmask '255.255.255.0'
        option _orig_ifname 'eth0, radio0.network1'
        option _orig_bridge 'true'
        option ifname 'eth0 wlan0'
        option ipaddr '192.168.5.1'
        option gateway '192.168.5.2'
        option broadcast '192.168.5.255'
        option dns '192.168.5.2'

Для этого случая нужно убрать из "_orig_ifname" "radio0.network1", а из "ifname" - "wlan0"

Теперь прописываем нужный интерфейс в конфиге cjdns. Секция ETHInterface, кстати, может выглядеть и так:
        "ETHInterface":
        [
            {
            "bind": "wlan0",
            "beacon": 2,
            },
            {
            "bind":"br-lan",
            "beacon": 2
            }
        ]

На десктопе такое работает, но на роутере, почему-то, с таким конфигом cjdroute падает. Так что, если у вас тоже это не работает, то прописываем что-то одно.

Создаем скрипт автозапуска.


Скрипты запуска должны лежать в папке /etc/init.d/ . Создаем файл /etc/init.d/cjdns :
#!/bin/sh /etc/rc.common

START=93
STOP=51

start() {
        cjdroute < /etc/cjdroute.conf
        }
stop()  {
        killall cjdroute
        }

$START и $STOP определяют последовательность запуска скриптов при старте и выключении роутера. "start()" - набор команд, выполняемый для запуска чего-либо. "stop()" - для остановки. Честно говоря, я слабо понимаю, чем может помочь функционал запуска скриптов при выключении, если роутеры принято выключать, просто выдергивая из розетки, а соответствующей кнопки обычно не предусмотрено. Вообще-то, в последних версиях cjdns предусмотрена опция конфига "pidFile", но это изменение пока не в ветке "master", из которой он собирается под OpenWrt.
Теперь осталось сделать
chmod u+rwx /etc/init.d/cjdns
/etc/init.d/cjdns enable

Подробнее о стартовых скриптах в OpenWrt можно почитать здесь: wiki.openwrt.org/doc/techref/initscripts

Фух, вроде, все. Удачной прошивки, и да не прийдется вам после нее работать паяльником!

UPD: Разработчики cjdns в последнем коммите в сборочные скрипты потерли все файлы и в README.md оставили сообщение, что отныне надо пользоваться сборочными скриптами от сиэтловцев.

UPD2: Сиэтлский вариант пакета у меня не заработал.
Какие возникли проблемы, или ''фатальные недостатки''
  1. Они решили сделать конфиг в формате uci. Это, конечно, хорошо, но ихний конвертер конфигов json-uci, написанный на LUA, не находит всех нужных библиотек, при том, что они-то как раз имелись, но немного в другом месте. Их копирование и создание линков не помогло - теперь ругается на ошибки в библиотеках.
  2. Запустить cjdroute вручную тоже не удалось. Как потом оказалось, он собирался из ветки crashey.
  3. В ихнем init-скрипте вовсю используются фичи, которые пока есть только в trunc.
  4. Все это вместе занимает много памяти. Впрочем, уменьшение занимаемого объема памяти есть в их todo.


Объем проделанной работы меня впечатлил, и реализация, вроде, не плохая. Так что пожелаю им удачи в их начинаниях.
Но собирать как-то все же надо, так что я решил почистить Makefile и сделать свой init-скрипт. Так же я убрал оттуда все, кроме В общем, получилась облегченная версия. Вот она: github.com/DinoAsm/cjdns-openwrt-tiny
Чтобы собрать, добавляем в feeds.conf эту строчку:
src-git cjdns_tiny https://github.com/DinoAsm/cjdns-openwrt-tiny.git

А дальше все так же, как было описано выше.
Tags:
Hubs:
+25
Comments 16
Comments Comments 16

Articles

Information

Website
cjdroute.net
Registered
Founded
Employees
Unknown
Location
Россия