Configuring Linux
Network technologies
3 August 2015

Прошиваем роутер Upvel UR-313N4G на OpenWRT

Tutorial


Как-то раз, еще до скачков курса доллара, мне понадобился очень дешевый SOHO-роутер. Требования — 5 портов, NAT и Wi-Fi, не помешал бы и USB. Выбор пал на Upvel UR-313N4G, который в те времена стоил в Citilink’е смешные 860 рублей. Роутер был куплен и выполнял свои обязанности вполне сносно, стабильно зависая раз в неделю. Кроме того, не работал проброс портов, ну и что, он не больно сильно был нужен. К тому же в Web-интерфейсе был доступ к командной строке, так что в критичных случаях можно было просто написать iptables -A PREROUTING -j DNAT и наслаждаться работающими портами до следующего зависания. Впрочем, большего я от коробочки за такую смешную цену я не ждал. Потом кое-что в моей жизни поменялось и роутер лег на дальнюю полку, а его заменил TP-Link TR-ML3420. Надо сказать, TR-ML3420 отлично работает под OpenWRT и никаких проблем с ним не возникает, а вот роутеры Upvel официально не поддерживаются OpenWRT чуть менее, чем полностью (из всего модельного ряда — только два роутера).
Когда стало ясно, что Upvel UR-313N4G мне уже не жалко, я решил попробовать поставить на него OpenWRT. Основная задача — заставить работать 3G-модем Megafon M21-4, он же Huawei E3531. Надо сказать, что опыт прошивки роутеров на OpenWRT у меня был, как через Web-интерфейс, так и через UART, но он сводился к “выберите файл для прошивки и молитесь” или “наберите эти три команды в консоли и молитесь”. На этот раз мне хотелось разобраться, почему вводятся именно такие команды, ну и вообще получить общее представление об архитектуре MIPS, с которой я до этого не работал.

Тут, конечно, нужно отметить, что читать этот материал отцам, разрабатывающим Gigabit Ethernet карты на FPGA и пишущим видеодрайверы для Mali-400, будет, наверное, смешно — они все это и так давно знают.

Для начала спросим у Гугла о том, что мы будем шить и куда. В архитектуре x86 для загрузки ОС используется специальным образом подготовленный носитель — жесткий диск, SSD или флеш-память, размеченная по стандарту MBR или GPT, с основной загрузочной записью и специально отмеченным загрузочным разделом. Внутренняя структура диска скрыта от ОС его прошивкой, а BIOS предоставляет минимальный фунционал даже тогда, когда ОС не установлена. В роутере с архитектурой MIPS все устроено по-другому. Данные хранятся на MTD-устройстве, которое представляет собой микросхему EEPROM без контроллера, так что когда мы пишем на эту схему, нам нужно стараться не писать в одни и те же сектора слишком часто. Кроме того, у MTD фиксированный и довольно большой размер блоков, которыми допустимо производить стирание (в моем случае — 64Кб). Таблицы разделов на MTD-устройстве нет, но, когда загружено ядро Linux, то оно симулирует наличие таблицы разделов. Настройки этой симуляции задаются либо при сборке ядра, либо с помощью параметра mtdparts при загрузке. В начале MTD расположен загрузчик, который при неудачном стечении обстоятельств можно стереть, и тогда поможет только прошивка EEPROM на программаторе. Загрузчик распаковывает в память прошитый в MTD образ и передает управление ядру Linux.

Теперь настало время экспериментов. Для начала вскроем роутер и посмотрим маркировку основного чипа — Ralink 5350F. Легкое гугление показывает, что этот чип поддерживается OpenWRT, и довольно неплохо, — есть целый каталог ramips с прошивками под серию 5350. Еще на плате видна микросхема RAM на 32Мб и чип EEPROM на 8Мб. Поиск по сайту 4pda показывает, что на чипсете Ralink 5350F построен Upvel UR-322N4G, Hame MPR-A1 и Zyxel Keenetic 4G II. Ну что ж, качаем OpenWRT для Hame MPR-A1 и попробуем прошиться через Web-интерфейс роутера.
Прошивка не шьется, видимо, не проходит проверка каких-то сигнатур. Выяснять с отладчиком наперевес, каких именно, нет желания. Ну и ладно, не больно надо было.

Есть несколько способов прошить роутер без использования Web-интерфейса. Можно, например, воспользоваться режимом восстановления, в который загрузчик переходит при запуске роутера. если нажата кнопка Reset. Забегая вперед, скажу, что в Upvel UR-313N4G этого режима нет, не повезло, что ж поделаешь.
Можно также подключиться к роутеру через telnet, который доступен в оригинальной прошивке, “расчистить” память роутера, убив ненужные процессы, загрузить в память новую прошивку через tftp и прошить ее командой mtd_write. Способ этот плох тем, что у нас, по сути, есть только одна попытка — если прошивка окажется несовместимой с роутером и не сможет загрузиться, то на выходе мы получим “полукирпич”, который можно восстановить, но только через UART. К тому же в этом случае придется поломать голову над тем, в какой раздел MTD следует записывать прошивку. Сразу скажу — в разметке MTD, которая применяется в оригинальной прошивке, раздела Firmware нет.

Прошивка через UART


Из всего этого следует, что для экспериментов нам нужно подключиться к роутеру через UART, тогда у нас будет доступ к консоли с момента старта роутера. На всякий случай закажем из Китая за 160 рублей программатор. Если мы не убьем загрузчик, программатор нам не понадобится, но, как говориться, “случаи бывают разные”. На плате, если держать ее светодиодами к себе, в верхней правой части заметны 4 нераспаянных отверстия в ряд, подозрительно похожие на UART. Внимательно смотрим на плату — первое слева отверстие сделано в широком слое фольги, кажется, это схемная земля, дорожки от следующих двух отверстий через резисторы уходят куда-то вглубь платы — это Rx и Tx, правда, пока не понятно. в каком порядке. Крайне правое отверстие так же располагается на слое фольги, но поменьше, это, скорее всего, +3,3 В. Берем в руки тестер и измеряем напряжение. На Tx должно быть +3,3 В, на Rx — 0 В. Получаем (слева направо) — 0В; 3,3В, 0В, 3,3В. Значить, второй слева — Tx, а третий — Rx. Крайний справа контакт — 3,3В, мы распаивать не будем — сожгем переходник.
Берем китайский клон шнура Nokia CA-42, разрезаем его, находим в Гугле распиновку и припаиваем (слева направо) — оранжевый, красный, синий.

Ставим Putty (у меня Linux на рабочей станции, с Windows проблем тоже возникнуть не должно) и подбираем скорость порта: выставляем стандартные для COM-порта значения, включаем роутер и смотрим, чтобы на экране были не кракозябы, а осмысленный текст. У меня соединилось на скорости 57600 бод. Теперь у нас есть доступ к загрузчику, а значит, шанс окирпичить роутер уменьшается.

Хорошо, попробуем прошиться через загрузчик. В момент старта системы мы видим приветствие

Please choose the operation: 
   1: Load system code to SDRAM via TFTP. 
   2: Load system code then write to Flash via TFTP. 
   3: Boot system code via Flash (default).
   4: Entr boot command line interface.
   7: Load Boot Loader code then write to Flash via Serial. 
   9: Load Boot Loader code then write to Flash via TFTP. 


Меня в этом меню больше всего привлекает пункт 1 — получение прошивки через TFTP и запуск ее из RAM. Получается, проверить совместимость прошивки с устройством можно даже не прошивая ее в EEPROM. Для меня это стало приятным сюрпризом. Устанавливаем tftp-сервер (я взял atftpd), кладем в рабочий каталог прошивку от Hame MPR-A1 и переименовываем ее поудобнее — в mpr-a1.bin.
Запускаем роутер, нажимаем в консоли клавишу 1, выставляем на сетевом интерфейсе компьютера адрес 10.10.10.3, вводим в загрузчике нужные параметры и загружаем прошивку.

Прошивка запустилась, ура! Даже есть доступ к Web-интерфейсу, а значить, большая часть прошивки работоспособна. И это мы еще ничего не шили! Только со светодиодами твориться что-то странное — горит только Wi-Fi (а он отключен и гореть не должен), а вот светодиоды портов Ethernet не горят. Разбираемся дальше. Для настройки коммутаторов в OpenWRT используется утилита swconfig. Выясняем, что может наш коммутатор

>swconfig dev rt2305x help
switch0: rt305x(rt305x-esw), ports: 7 (cpu @ 6), vlans: 4096
     --switch
    Attribute 1 (int): enable_vlan (VLAN mode (1:enabled))
    Attribute 2 (int): alternate_vlan_disable (Use en_vlan instead of doubletag to disable VLAN mode)
    Attribute 3 (int): bc_storm_protect (Global broadcast storm protection (0:Disable, 1:64 blocks, 2:96 blocks, 3:128 blocks))
    Attribute 4 (int): led_frequency (LED Flash frequency (0:30mS, 1:60mS, 2:240mS, 3:480mS))
    Attribute 5 (none): apply (Activate changes in the hardware)
    Attribute 6 (none): reset (Reset the switch)
     --vlan
    Attribute 1 (ports): ports (VLAN port mapping)
     --port
    Attribute 1 (int): disable (Port state (1:disabled))
    Attribute 2 (int): doubletag (Double tagging for incoming vlan packets (1:enabled))
    Attribute 3 (int): untag (Untag (1:strip outgoing vlan tag))
    Attribute 4 (int): led (LED mode (0:link, 1:100m, 2:duplex, 3:activity, 4:collision, 5:linkact, 6:duplcoll, 7:10mact, 8:100mact, 10:blink, 11:off, 12:on))
    Attribute 5 (int): lan (HW port group (0:wan, 1:lan))
    Attribute 6 (int): recv_bad (Receive bad packet counter)
    Attribute 7 (int): recv_good (Receive good packet counter)
    Attribute 8 (int): tr_bad (Transmit bad packet counter. rt5350 only)
    Attribute 9 (int): tr_good (Transmit good packet counter. rt5350 only)
    Attribute 10 (int): pvid (Primary VLAN ID)
    Attribute 11 (string): link (Get port link information)

На первый взгляд — обнадеживающе, мы можем управлять не только VLAN’ами, что жизненно необходимо для разграничения трафика LAN и WAN-подсетей, но и программировать светодиоды.
Делаем

>swconfig dev rt305x port 4 set led 12
>swconfig dev rt305x set apply

и… ничего не происходит. Видимо проблема где-то глубже, в драйверах. Хорошо, что у OpenWRT открытый код. Идем в git-репозитоий, находим там исходники драйвера и видим

446 static void esw_hw_init(struct rt305x_esw *esw)
447 {
448         int i;
449         u8 port_disable = 0;
450         u8 port_map = RT305X_ESW_PMAP_LLLLLL;
451
452         /* vodoo from original driver */
453         esw_w32(esw, 0xC8A07850, RT305X_ESW_REG_FCT0);
454         esw_w32(esw, 0x00000000, RT305X_ESW_REG_SGC2);

тут, кажется, используется магия вуду. И еще — по коду драйвера сразу понятно, что без детальной документации на регистры коммутатора нам в нем не разобраться. Но видно, что параметры драйвера задаются не через аргументы команды modprobe, а, судя по всему, через вкомпилированные в ядро строки:

1431    reg_init = of_get_property(np, "ralink,led_polarity", NULL);

Ладно, отложим исходники и попробуем зайти с другой стороны. Все три роутера — Upvel UR-322N4G, Hame MPR-A1 и Zyxel Keenetic 4G II — двухпортовые. Наверняка среди тучи поддерживаемых моделей на Ralink 5350 есть и пятипортовые модели. Идем на WikiDev и находим, например, D-Link 320 NRU B1. Пятипортовый роутер, один USB порт — то, что надо. Качаем прошивку, загружаем в память роутера, запускаем. Светодиоды работают как положено. Очень интересно. Будем прошиваться на нее, а пока попробуем найти разницу между OpenWRT для Hame MPR-A1 и D-Link 320 NRU B1. Разницу нужно искать в профилях сборки — DTS. Вот, например, параметр led_polarity для Hame не указан, а для D-Link равен 0x17. А я то думал, что полярность бывает только прямая и обратная! Опять лезем в исходники драйвера, находим там константу

#define RT5350_EWS_REG_LED_POLARITY     0x168

находим способ задания полярности
565                 /* set the led polarity */
566                 esw_w32(esw, esw->reg_led_polarity & 0x1F, RT5350_EWS_REG_LED_POLARITY);

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

Мы выяснили, что для нашего роутера лучше всего подходит прошивка от D-Link DIR-320 NRU B1. Теперь нужно прошиваться. Быть может вы заметили, что для тестирования работы прошивок я выбирал файлы со словом uImage в имени. Такой файл содержит только образ ядра и файловую систему, загружаемую в RAM и доступную только для чтения. Такой образ можно запускать прямо из памяти, а вот для прошивки он не очень подходит — настройки в таком образе хранить просто негде. Поэтому для прошивки мы выберем образ с расширением sysupgrade — в конце такого образа прицеплен раздел JFFS2, в котором будут храниться изменения, внесенные нами в файловую систему роутера. Поэтому образ sysupgrade стартовать из памяти без прошивки на флешку не может.
Очень хорошо, что у Upvel UR-313N4G флешка на 8Мб, после установки у нас останется еще чуть больше 4Мб для дополнительных пакетов. А вот в TP-Link TR-ML3420 флешка на 4Мб и свободного места там — кот наплакал.
Перезагружаем роутер, нажимаем 2, указываем имя прошивки и шьемся. Итак, вроде все нормально: интерфейсы поднялись, светодиоды моргают, в общем — жизнь бурлит. Вы еще не забыли, что нашей основной целью было заставить работать роутер с модем Megafon M21-4? Втыкаем модем в USB-порт, смотрим в dmesg и… ничего. То есть, совсем ничего. Тут есть два варианта — либо ядро не увидело какую-нибудь адскую шину, на которой внутри чипа висит USB-порт (тут уж мы ничего не поделаем), либо в прошивку забыли добавить драйверы для этого порта. В D-Link 320 NRU B1 USB-порт есть, так что более вероятен второй вариант. Гуглим “OpenWRT usb support”, настраиваем на роутере WAN-порт, чтобы прошивка получила доступ в Интернет и ставим пакеты.

>opkg update
>opkg install kmod-usb2
>opkg install kmod-usb-ohci

Именно в таком порядке, потому что модуль kmod-usb2 должен быть загружен раньше, чем kmod-usb-ohci

В консоли тут же появляются строки:

[  121.570000] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[  121.600000] ehci-platform: EHCI generic platform driver
[  121.810000] phy phy-usbphy.0: remote usb device wakeup disabled
[  121.830000] phy phy-usbphy.0: UTMI 16bit 30MHz
[  121.840000] ehci-platform 101c0000.ehci: EHCI Host Controller
[  121.850000] ehci-platform 101c0000.ehci: new USB bus registered, assigned bus number 1
[  121.860000] ehci-platform 101c0000.ehci: irq 26, io mem 0x101c0000
[  121.900000] ehci-platform 101c0000.ehci: USB 2.0 started, EHCI 1.00
[  121.910000] hub 1-0:1.0: USB hub found
[  121.920000] hub 1-0:1.0: 1 port detected
[  122.300000] usb 1-1: new high-speed USB device number 2 using ehci-platform

[  151.680000] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
[  151.710000] ohci-platform: OHCI generic platform driver
[  151.720000] ohci-platform 101c1000.ohci: Generic Platform OHCI controller
[  151.730000] ohci-platform 101c1000.ohci: new USB bus registered, assigned bus number 2
[  151.750000] ohci-platform 101c1000.ohci: irq 26, io mem 0x101c1000
[  151.820000] hub 2-0:1.0: USB hub found
[  151.830000] hub 2-0:1.0: 1 port detected

Ого, система увидела устройство на шине USB, значит, продолжаем.

Теперь ставим пакет, который при подключении модема пошлет на него “магическую последовательность”, переключающую модем из режима CD-ROM/TF Card в режим CD-ROM/TF Card+3 COM-порта.

>opkg install usb-modeswitch

В консоли видим следующее:

[  278.230000] usb 1-1: USB disconnect, device number 2
[  278.990000] usb 1-1: new high-speed USB device number 3 using ehci-platform

Значит, usb-modeswitch сработал — отправил команду модему, а модем после этого исчез с шины USB и появился там уже как другое устройство.

Поставим два пакета с драйверами, которые увидят COM-порты и сделают их доступными системе
>opkg install kmod-usb-serial kmod-usb-serial-options

[  326.530000] usbcore: registered new interface driver usbserial
[  326.540000] usbcore: registered new interface driver usbserial_generic
[  326.550000] usbserial: USB Serial support registered for generic
[  326.820000] usbcore: registered new interface driver option
[  326.830000] usbserial: USB Serial support registered for GSM modem (1-port)
[  326.850000] option 1-1:1.0: GSM modem (1-port) converter detected
[  326.860000] usb 1-1: GSM modem (1-port) converter now attached to ttyUSB0
[  326.870000] option 1-1:1.2: GSM modem (1-port) converter detected
[  326.890000] usb 1-1: GSM modem (1-port) converter now attached to ttyUSB1
[  326.900000] option 1-1:1.3: GSM modem (1-port) converter detected
[  326.910000] usb 1-1: GSM modem (1-port) converter now attached to ttyUSB2

Видно, что модем определился правильно и в системе появилось 3 COM-порта

Ставим пакет, необходимый для создания отправки команд 3G-модемам и расширение для Web-интерфейса:
>opkg install comgt luci-proto-3g

Заходим в Web-интерфейс, настраиваем 3G, в качестве порта указываем /dev/ttyUSB0, все работает.

Теперь возьмемся за светодиоды. Сначала установим пакет kmod-ledtrig-usbdev — он позволит нам управлять светодиодами в зависимости от наличия устройств на шине USB. Зайдем в Web-интерфейс на вкладку System->LED Configuration и определим там два светодиода:

Name: usb
LED Name: d-link:green:usb
Trigger: usbdev
USB Device: 1-1 (HUAWEI - HUAWEI Mobile)

Name: wifi
LED Name: rt2800:phy0:radio
Trigger: netdev
Default state: on
Device: wlan0
Trigger Mode: Link On, Transmit, Receive

и нажмем кнопку Save/Apply
На этом настройка роутера закончена.
Стоить добавить, что светодиод “usb” ссылается на конкретный адрес устройства на шине USB (1-1), а поэтому, если адрес изменится, то светодиод работать не будет.

Прошивка через Telnet


А вот теперь пришло время выяснить, можно ли прошить роутер без доступа к UART. Для начала попробуем режим восстановления — обычно он включается, если при зажатой кнопке сброса подать питание. Зажимаем, подаем и смотрим в консоли: ничего. Режим не доступен. Откатимся через загрузчик на оригинальную прошивку и посмотрим в лог загрузки:

: System Boot system code via Flash.
## Booting image at bc050000 ...
raspi_read: from:50000 len:40 
.   Image Name:   Linux Kernel Image
   Created:      2014-06-11   7:38:15 UTC
   Image Type:   MIPS Linux Kernel Image (lzma compressed)
   Data Size:    3435300 Bytes =  3.3 MB
   Load Address: 80000000
   Entry Point:  80420000
raspi_read: from:50040 len:346b24 
.....................................................   Verifying Checksum ... OK
   Uncompressing Kernel Image ... OK
No initrd
## Transferring control to Linux (at address 80420000) ...
## Giving linux memsize in MB, 32

Видно, что u-boot читает 64 байта, начиная с адреса 0x00050000 (отцы, объясните, почему в логе bc050000?). Начиная с 0x00050000 на MTD прошит образ uImage и в первых 64-х его байтах находится заголовок. Из заголовка берется размер образа, его будущий адрес в оперативной памяти и точка входа, после чего образ распаковывается в RAM и на точку входа передается управление.
Подождем, пока оригинальная прошивка загрузится и посмотрим в лог:

mtd .name = raspi, .size = 0x00800000 (8M) .erasesize = 0x00010000 (64K) .numeraseregions = 0
Creating 6 MTD partitions on "raspi":
0x00000000-0x00800000 : "ALL"
0x00000000-0x00030000 : "Bootloader"
0x00030000-0x00040000 : "Config"
0x00040000-0x00050000 : "Factory"
0x00050000-0x007d0000 : "Kernel"
0x007d0000-0x00800000 : "RW_FS"

Начиная с адреса 0x00050000 начинается раздел Kernel, и этот адрес подозрительно совпадает с адресом из лога u-boot. Значит, именно здесь храниться прошивка роутера, сюда мы и запишем OpenWRT. Посмотрим, на какие устройства отобразились разделы:

cat /proc/mtd
dev:    size   erasesize  name
mtd0: 00800000 00010000 "ALL"
mtd1: 00030000 00010000 "Bootloader"
mtd2: 00010000 00010000 "Config"
mtd3: 00010000 00010000 "Factory"
mtd4: 00780000 00010000 "Kernel"
mtd5: 00030000 00010000 "RW_FS"

Размер раздела Kernel — 7680 Кб, а образ OpenWRT занимает примерно 3,5 Мб, следовательно, можно прошиваться без опасения, что новая прошивка не поместится в старый раздел.

Посмотрим, сколько у нас свободной памяти:

>free
              total         used         free       shared      buffers
  Mem:        28300        19952         8348            0            0
 Swap:            0            0            0
Total:        28300        19952         8348

Нам нужно примерно 3,5 Мб и они у нас есть.

Запустим на рабочей станции atftpd (адрес 192.168.10.100) и скопируем прошивку в RAM роутера:

>tftp -l dir-320.bin -r dir-320.bin -g 192.168.10.100

Сначала сотрем раздел RW_FS. Может, этого и не надо делать, но на всякий случай перестрахуемся:

> mtd_write erase /dev/mtd5
Unlocking /dev/mtd5 ...
Erasing /dev/mtd5 ...
Erase char is 255


Теперь запишем прошивку:

>mtd_write -r write dir-320.bin /dev/mdt4
Unlocking /dev/mtd4 ...
Writing from dir-320.cc.sys.bin to /dev/mtd4 ...

Ключ -r значит, что после выполнения команды роутер перезагрузится. Корректировать таблицу разделов не нужно, потому что она существует только внутри ядра Linux.

После перезагрузки заходим в Web-интерфейс, ставим пакеты kmod-usb2, kmod-usb-ohci, kmod-usb-serial, kmod-usb-serial-option, kmod-ledtrig-usbdev, usb-modeswitch, comgt, luci-proto-3g, настраиваем интерфейсы и светодиоды.

Роутер прошит, можно открывать печеньки и наливать чай.

+21
40.2k 134
Comments 40