【後編】ルーターのrootfsを拡張してPythonをビルドする

本記事はルーターハックAdvent Calendar 10日目の記事です。

9日目となる前回は、ルーターに生やした USB ポートに SD カードリーダーを挿し、rootfs として使えるようにしたうえで、sl をビルドするところまでを書いた。ゴールはルーター上での Python のビルドだったのだが、sl まででえらく時間がかかり気分的に記事を分けたくなったので前後編に分けてお送りする。

おことわり

技適 に関する筆者の配慮や考えについてはカレンダー1日目「技適とルーターハック」をご覧ください。本記事で紹介する内容は、法を遵守するための慎重な注意をもって書かれています。

www.zopfco.de

]

やっと Python をビルドする

前回 sl をビルドするためにビルドした ncurses は Python のビルドにおいてもあったほうが嬉しいライブラリ(必須ではないはず?)なので結果オーライ。意を決して Python へ。

落とす

落としてextractするだけで2分かかっててたいへんさきがおもいやられますね(朗らかな笑顔)

root@OpenWrt:~# wget https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz
Downloading 'https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz'
Connecting to 151.101.0.223:443
Writing to 'Python-3.7.1.tgz'
Python-3.7.1.tgz     100% |*******************************| 22267k  0:00:00 ETA
Download completed (22802018 bytes)
root@OpenWrt:~# time tar zxf Python-3.7.1.tgz
real    2m 18.03s
user    0m 19.11s
sys     0m 8.08s

configure

configure。3分47秒経って死。

root@OpenWrt:~/Python-3.7.1# time ./configure
...
checking for grep that handles long lines and -e... configure: error: no acceptable grep could be found in /usr/sbin:/usr/bin:/sbin:/bin:/usr/xpg4/bin
Command exited with non-zero status 1
real    3m 46.97s
user    0m 6.14s
sys     0m 8.21s

フルなgrepを入れねば。ファームをビルドするときに busybox をモリモリにすれば解決しそうだが、それは一旦置いておく。

root@OpenWrt:~/Python-3.7.1# opkg install grep
Installing grep (3.1-1) to root...
Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/packages/grep_3.1-1_mips_24kc.ipk
Configuring grep.

configure。grep, ncurses通過の後成功。

root@OpenWrt:~/Python-3.7.1# time ./configure
...
checking for grep that handles long lines and -e... /usr/bin/grep
...
checking curses.h usability... yes
checking curses.h presence... yes
checking for curses.h... yes
checking ncurses.h usability... yes
checking ncurses.h presence... yes
checking for ncurses.h... yes
...
creating Makefile


If you want a release build with all stable optimizations active (PGO, etc),
please run ./configure --enable-optimizations


real    1h 53m 48s
user    5m 16.06s
sys     4m 26.75s

あー --enable-optimizations 忘れてた。まあいっか。ともかく! 1時間53分で configure が成功を収めただけでも喜ぼう!

make

これから一体どれだけ時間がかかるのやら。思えばdistccでも使えばよかった。まあ使うとしたら後の機会に。

root@OpenWrt:~/Python-3.7.1# time make

……ここから外出し、約12時間後に見てみてもまだビルドが終わってなかった。

おそるおそる top を見る。

Mem: 26508K used, 1332K free, 20K shrd, 316K buff, 6284K cached
CPU:   3% usr  11% sys   0% nic   0% idle  85% io   0% irq   0% sirq
Load average: 2.02 2.04 2.02 1/41 25308
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
25307 25306 root     D    26144  94%  10% {cc1} /usr/lib/gcc/mips-openwrt-linux-
   97     2 root     IW       0   0%   4% [kworker/0:1]
  406     2 root     DW       0   0%   0% [usb-storage]
  121     2 root     SW       0   0%   0% [kswapd0]
25308   532 root     R     1196   4%   0% top
    7     2 root     SW       0   0%   0% [ksoftirqd/0]
25306 24639 root     S     3144  11%   0% gcc -c -Wno-unused-result -Wsign-compa
24639 24638 root     S     2204   8%   0% make
  784     1 root     S     1700   6%   0% /sbin/netifd
    1     0 root     S     1544   6%   0% /sbin/procd
  819     1 root     S     1428   5%   0% /usr/sbin/odhcpd
 1140     1 dnsmasq  S     1324   5%   0% /usr/sbin/dnsmasq -C /var/etc/dnsmasq.
  665     1 root     S     1232   4%   0% /sbin/logd -S 64
  532     1 root     S     1204   4%   0% /bin/ash --login
15256 15255 root     S     1204   4%   0% -ash
 1457     1 root     S<    1196   4%   0% /usr/sbin/ntpd -n -N -S /usr/sbin/ntpd
15311   784 root     S     1196   4%   0% udhcpc -p /var/run/udhcpc-eth1.pid -s
24638 15256 root     S     1196   4%   0% time make
  531     1 root     S     1192   4%   0% /sbin/ubusd
15255   964 root     S     1128   4%   0% /usr/sbin/dropbear -F -P /var/run/drop

VSZ 94%

これはまだマシで、ひどいときは 104% まで達していた。

比較として PC で Python をビルドすると、あともう5倍ぐらいの道のりは残されてそうだったので別のルーターに切り替えることにした。WHR-HP-GN よすまない、無理な仕事をさせてしまったな。

もっと性能の良いルーターで再チャレンジ

やはり敗因は、32 MiB と少なすぎる物理メモリと USB 1.1 という前時代の物理層しか搭載しないことだった。メモリがスワップした瞬間、Fallout シリーズの端末みたいなスピードでしか動作できなくなるし。

次なるルーターは 多摩電子工業 (Axing) W06 だ。

f:id:puhitaku:20180218001123j:plain

秋葉原はジャンク通りに面するショップインバースなどで見かけた人も多いだろう。一時期1000円を余裕で下回る価格で売られていた、持ち運びできるルーターだ。

f:id:puhitaku:20180218002405j:plain

  • CPU: MediaTek MT7688AN@575MHz
  • RAM: Winbond W9751G6KB@DDR2, 64MB
  • Flash: 16MB
  • Ether: FastEther (100Mbps) x1
  • USB 2.0 PHY 搭載・物理ポート付き

(出典: 鉄PCブログ

このルーターは価格に対してスペックが良い。CPU 575MHz。DDR2。USBポート付き。しかもUSB 2.0。WHR-HP-GN から四まわりほどアップグレードした感じだ。

こいつのファームをビルドし、ここまでとほぼ同じ作業をもう一度繰り返した。差分は以下。

  • SDカードの rootfs パーティションを ext4 ではなく btrfs にした
    • Kernel Modules -> Filesystem -> kmod-fs-btrfs

どうあがいても USB の帯域幅は DRAM の帯域幅より狭い。btrfs は透過的に圧縮をかけることができるので、CPUが十分に高速であれば(いやそんなに速くないとは思うけど) USB の帯域を少しでも節約できるのではないかと考え選択した。kmod-fs-btrfs を選択すれば、圧縮アルゴリズム系のモジュールも自動で選択してくれるようだ。

  • これまでに不足していた ca-certificates や grep などを追加した

そもそも WHR-HP-GN のような Flash が 8 MiB のシステムでも ppp や Wi-Fi 関連の config を消せばいろいろ入った。こちらはFlash が 16 MiB もあるので余裕で追加できる。

準備

ファームをビルドして実機に転送して SD をフォーマットして extroot を仕込んで ncurses をビルドして Python を落としてくる。

blkid してみると。

root@OpenWrt:~# blkid
/dev/mtdblock5: TYPE="squashfs"
/dev/sda1: UUID="e98d0f04-9cf6-447c-bee3-0c3d5ccb0aef" UUID_SUB="e8a7a945-1721-4fcf-99b4-669e49bea786" TYPE="btrfs" PARTUUID="3859753d-01"
/dev/sda2: UUID="765a8ea4-ef55-48fe-95c7-8c0fee8e9c0e" TYPE="swap" PARTUUID="3859753d-02"

おー、ちっこいルーターなのに btrfs やら swap やら盛りだくさん!ウキウキな瞬間だ。

mount。

root@OpenWrt:~# mount -o ssd,noatime,discard,compress=lzo /dev/sda1 /mnt
root@OpenWrt:~# dmesg | tail -n 11
[  136.750060] BTRFS: device fsid e98d0f04-9cf6-447c-bee3-0c3d5ccb0aef devid 1 transid 5 /dev/sda1
[  136.771595] BTRFS info (device sda1): disk space caching is enabled
[  136.784117] BTRFS info (device sda1): has skinny extents
[  136.794659] BTRFS info (device sda1): flagging fs with big metadata feature
[  136.826883] BTRFS info (device sda1): checking UUID tree
[  168.921098] BTRFS info (device sda1): enabling ssd optimizations
[  168.933092] BTRFS info (device sda1): turning on discard
[  168.943652] BTRFS info (device sda1): setting 8 feature flag
[  168.954885] BTRFS info (device sda1): use lzo compression
[  168.965599] BTRFS info (device sda1): disk space caching is enabled
[  168.978028] BTRFS info (device sda1): has skinny extents

FOOO!!! さて、extrootしよう。マウントオプションの追加も忘れずに。

root@OpenWrt:~# cp -r /overlay/* /mnt/
root@OpenWrt:~# block detect > /etc/config/fstab
root@OpenWrt:~# vi /etc/config/fstab
root@OpenWrt:~# cat /etc/config/fstab
config 'global'
        option  anon_swap       '1'
        option  anon_mount      '0'
        option  auto_swap       '1'
        option  auto_mount      '1'
        option  delay_root      '5'
        option  check_fs        '0'

config 'mount'
        option  target  '/overlay'
        option  options 'ssd,noatime,discard,compress=lzo'
        option  uuid    'e98d0f04-9cf6-447c-bee3-0c3d5ccb0aef'
        option  enabled '1'

config 'swap'
        option  uuid    '765a8ea4-ef55-48fe-95c7-8c0fee8e9c0e'
        option  enabled '1'

root@OpenWrt:~# reboot

...

root@OpenWrt:~# df -h
Filesystem                Size      Used Available Use% Mounted on
/dev/root                 9.0M      9.0M         0 100% /rom
tmpfs                    29.3M     52.0K     29.3M   0% /tmp
/dev/sda1                13.7G     16.6M     11.7G   0% /overlay
overlayfs:/overlay       13.7G     16.6M     11.7G   0% /
tmpfs                   512.0K         0    512.0K   0% /dev

成功!

opkg が相変わらず空気を読まない

GCC だけはファームに内蔵できないので opkg で入れる。

root@OpenWrt:~/ncurses-6.1# opkg update

...

root@OpenWrt:~/ncurses-6.1# opkg install gcc

うーん時間がかかる。kworker が食ってるのはなんだ。

Mem: 51644K used, 8384K free, 0K shrd, 1240K buff, 2948K cached
CPU:   1% usr  98% sys   0% nic   0% idle   0% io   0% irq   0% sirq
Load average: 3.56 1.75 0.71 4/67 2100
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
  119     2 root     IW       0   0%  51% [kworker/0:1]
 2100  1911 root     R     4368   7%  20% wget -q -O /tmp/opkg-olfGfA/binutils_2.27-1_mipsel_24kc.ipk http://downloads.openwrt.org/snapshots/packages/mipsel_24kc/base/binu

どうやら相変わらず opkg が tmpfs にパッケージを置こうとするせいでスラッシングしているらしい。tmp-dirを変える。

root@OpenWrt:~# mkdir tmp
root@OpenWrt:~# opkg update
...
root@OpenWrt:~# opkg install --tmp-dir /root/tmp gcc

うん、目に見えて速くなった。

ncurses リベンジ

落として configure 。

root@OpenWrt:~# wget https://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.1.tar.gz
Downloading 'https://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.1.tar.gz'
Connecting to 208.118.235.20:443
Writing to 'ncurses-6.1.tar.gz'
ncurses-6.1.tar.gz   100% |*******************************|  3286k  0:00:00 ETA
Download completed (3365395 bytes)
root@OpenWrt:~# tar zxf ncurses-6.1.tar.gz
root@OpenWrt:~# cd ncurses-6.1/
root@OpenWrt:~# time ./configure

...

real    2m 13.33s
user    1m 26.98s
sys     0m 19.78s

かーなりマシになった。前は configure だけで40分かかっていたことを考えれば上出来だ。特にスラッシングも起きていないようだ。

make。

root@OpenWrt:~/ncurses-6.1# time make
...
make[1]: Leaving directory '/root/ncurses-6.1/c++'
real    30m 6.52s
user    23m 35.15s
sys     1m 32.85s

うーん速い!WHR-HP-GN が ncurses で configure するよりも速い。

Python リベンジ

落として configure。

root@OpenWrt:~# wget https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz
...
root@OpenWrt:~# tar zxf Python-3.7.1.tgz
root@OpenWrt:~# cd Python-3.7.1
root@OpenWrt:~# time ./configure
...
real    4m 21.46s
user    3m 29.45s
sys     0m 42.52s

いい感じ。お次は makeなのだが、IO が発生するたびに kworker が相変わらず重い… gcc と CPU 取り合ってる…ということで btrfs の醍醐味 compress=lzo はお蔵入りに。残念。

root@OpenWrt:~# time make

kworker は幾分か軽くなったのだが、大きいオブジェクトファイルをコンパイルする時はスワップが発生して途端に遅くなるようだ。とりあえず、開始2〜3時間で WHR-HP-GNが止まっていた部分は追い抜いた。

ここで寝落ちし朝を迎えた時点で typeobject.c をコンパイルしていた。そのまま出勤。家に帰ると…

gcc -c -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall    -std=c99 -Wextra -Wno-unused-result -Wno-unused-parameter -Wno-missing-field-initializers -Werror=implicit-function-declaration   -I. -I./Include    -DPy_BUILD_CORE -o Objects/tupleobject.o Objects/tupleobject.c
gcc -c -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall    -std=c99 -Wextra -Wno-unused-result -Wno-unused-parameter -Wno-missing-field-initializers -Werror=implicit-function-declaration   -I. -I./Include    -DPy_BUILD_CORE -o Objects/typeobject.o Objects/typeobject.c

いやおめー全然進んどらんが!

no-unused-parameter -Wno-missing-field-initializers -Werror=implicit-function-declaration   -I. -I./Include    -DPy_BUILD_CORE -o Objects/typeobject.o Objects/typeobject.c

^C^C^Cmake: *** [Makefile:1623: Objects/typeobject.o] Interrupt
Command terminated by signal 2
real    17h 51m 36s
user    11m 55.93s
sys     5m 6.26s

途中まで進んで結局完全にスラッシングしちゃったのでした。フルな Python のビルドは無理でした。完。

Python 0.9.1 をコンパイルしてお茶を濁す

Python の開発の歴史は1980年代の末まで遡る。(突然始まる Python の歴史)もともとは Guido van Rossum のクリスマスの趣味プログラミングとして始まった Python は、1991年にバージョン 0.9.1 として世界に公開された。

実は、このバージョン 0.9.1 は今でもダウンロードし、コンパイルすることができる。

www.python.org

フルの Python はコンパイルできないので、こいつをコンパイルしてお茶を濁すことにする。

落としてコンパイルする。

root@OpenWrt:~# wget https://www.python.org/ftp/python/src/Python-0.9.1.tar.gz
Downloading 'https://www.python.org/ftp/python/src/Python-0.9.1.tar.gz'
Connecting to 151.101.0.223:443
Writing to 'Python-0.9.1.tar.gz'
Python-0.9.1.tar.gz  100% |*******************************|   378k  0:00:00 ETA
Download completed (387150 bytes)
root@OpenWrt:~# tar zxf Python-0.9.1.tar.gz
root@OpenWrt:~# cd python-0.9.1/src/
root@OpenWrt:~/python-0.9.1/src# CC=gcc time make

...

real    0m 37.69s
user    0m 33.26s
sys     0m 2.67s

あっはっは、37秒ちょっとで終わってしまった。やはり時代の古いプログラムはコンパイルも軽い。最初からこうすればよかった感すらある。

Python 0.9.1 をちょっと触る

※現代の Python との主な違いについては、同梱されている README.reconstructed にも書いてある。

print はある。

>>> print 'hello world!'
hello world!

dir() の使用できる範囲が限られている。現在では任意の object の attribute を一覧にできるが、0.9.1 では module の中の関数を一覧化する程度の機能しかないようだ。

>>> i = 1
>>> dir(i)
Unhandled exception: type error: dir() argument, must be module or absent
Stack backtrace (innermost last):
  File "<stdin>", line 1
>>>
>>> import math
>>> dir(math)
['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'e', 'exp', 'fabs', 'floor', 'log', 'log10', 'pi', 'pow', 'sin', 'sinh', 'sqrt', 'tan', 'tanh']

class の定義ではクラス名の次に必ずカッコを付ける必要がある。

>>> class Foo:
Parsing error: file <stdin>, line 1:
class Foo:
          ^
Unhandled exception: run-time error: syntax error

__init__ という概念がない。標準ライブラリではコンストラクタとしてクラスと同名の関数を作り、手で呼んでいたようだ。

実際、__init__ を作っても呼ばれないことがわかる。

>>> class Foo():
...     def __init__(self):
...         print 'hello!'
...
>>> f = Foo()
>>>

ダブルクォートが文字列リテラルとして解釈されない。

>>> print 'Hello World!'
Hello World!
>>> print "Hello World!"
Parsing error: file <stdin>, line 1:
print "Hello World!"
       ^
Unhandled exception: run-time error: syntax error

次回

次回は、ルーターに備わっているペリフェラルにいろいろつないでみる企画『ルーターにつなぐ』の第1弾とする。まずは I2C にする予定だ。乞うご期待。

【前編】ルーターのrootfsを拡張してPythonをビルドする

本記事はルーターハックAdvent Calendar 9日目の記事です。

これは、遅れを取り戻す物語。(コピー風)(このリズム感どっかで聞いたことあると思ったらFGOでした。FGO1ミリもしらんけど。)

8日目の記事では、USB ポートのないルーターに USB を生やして使えるようにするまでを書いた。こいつを使えば、USB メモリでルーターの rootfs(Windows でいう C ドライブ)の容量を拡張して好き勝手やることができる。今回は、GCCをインストールして Python をビルドしてみよう。

なぜ遅れを取り戻さなければならないのに時間がかかるネタをやろうとするのかは筆者自身でも不明である。

おことわり

技適 に関する筆者の配慮や考えについてはカレンダー1日目「技適とルーターハック」をご覧ください。本記事で紹介する内容は、法を遵守するための慎重な注意をもって書かれています。

www.zopfco.de

SDカードを準備

やろうと思えば opkg を使ってルーターに fdisk や mkfs.ext4 も入れられるのだが、面倒なのでPC側でフォーマットしてしまった。

f:id:puhitaku:20181216011450p:plain

前半 7000 MiB を ext4 でフォーマットし、残る容量を swap とした。WHR-HP-GN のメインメモリは32 MiB と大変少ないので、swap がなければまず GCC の動作は無理だろう。

f:id:puhitaku:20181216125056j:plain
接続!

SD カードを rootfs の拡張領域にする

OpenWrt は何もせずとも overlayfs を用いてメインストレージ(多くの場合 SPI Flash)の固定された rootfs と書き換え可能な領域を分割して提供する。これを pivot_root (2) で置き換える形で、外部ストレージもまた rootfs の overlay として使うことができる。

これからの作業は公式のマニュアルにも記載されている。ぜひ参照されたい。

まず必要な道具として以下を make で追加しビルド。

  • Utilities -> Filesystem -> swap-utils
  • Global build settings -> Kernel build options -> Support for paging of anonymous memory (swap)
  • Base system -> block-mount
  • Base system -> busybox -> Linux System Utilities -> swapon swapoff
    • Customize busybox options にチェックを入れると出現

生成されたら ssh で転送して sysupgrade しよう。

ここで SD カードリーダーを挿入し、 block detect を実行すると以下のようにファイルシステムが認識される。 これは /etc/config/fstab として置く設定ファイルを自動生成するコマンドだ(実行するだけでは書き換えられない)。

root@OpenWrt:~# block detect
config 'global'
        option  anon_swap       '0'
        option  anon_mount      '0'
        option  auto_swap       '1'
        option  auto_mount      '1'
        option  delay_root      '5'
        option  check_fs        '0'

config 'mount'
        option  target  '/mnt/sda1'
        option  uuid    '31407de4-3283-4dfd-b391-375cc78c9a04'
        option  enabled '0'

config 'swap'
        option  uuid    '49c9c539-29d9-4f56-b775-691b1fcdff86'
        option  enabled '0'

認識していることが確認できたら、現在の overlayfs に置いてあるファイルを SD カードにコピーする。公式ドキュメントでは tar を使ってちょっとテクいことをしているが、普通にコピーすれば OK だ。

root@OpenWrt:~# mount /dev/sda1 /mnt
[ 3669.913854] EXT4-fs (sda1): mounted filesystem with ordered data mode. Opts: (null)
root@OpenWrt:~# cp -r /overlay/
.fs_state  upper/     work/
root@OpenWrt:~# cp -r /overlay/* /mnt/

最後に /etc/config/fstab を生成し、全エントリを有効にする。ext4 なパーティションの mount point は /overlay に変更する。

root@OpenWrt:~# block detect > /etc/config/fstab
root@OpenWrt:~# vi /etc/config/fstab
...
root@OpenWrt:~# cat /etc/config/fstab
config 'global'
        option  anon_swap       '0'
        option  anon_mount      '0'
        option  auto_swap       '1'
        option  auto_mount      '1'
        option  delay_root      '5'
        option  check_fs        '0'

config 'mount'
        option  target  '/mnt/sda1'
        option  uuid    '31407de4-3283-4dfd-b391-375cc78c9a04'
        option  enabled '1'

config 'swap'
        option  uuid    '49c9c539-29d9-4f56-b775-691b1fcdff86'
        option  enabled '1'

完了したらリブート!

リブート後…

以下のようなログが出て、df を見て容量が半端ないことになっていれば成功だ。

[   12.135475] block: attempting to load /tmp/jffs_cfg/upper/etc/config/fstab
[   12.276824] EXT4-fs (sda1): mounted filesystem with ordered data mode. Opts:
[   12.452555] mount_root: switched to extroot
root@OpenWrt:/# df -h
Filesystem                Size      Used Available Use% Mounted on
/dev/root                 2.0M      2.0M         0 100% /rom
tmpfs                    13.6M     64.0K     13.5M   0% /tmp
/dev/sda1                 6.7G     16.1M      6.3G   0% /overlay
overlayfs:/overlay        6.7G     16.1M      6.3G   0% /
tmpfs                   512.0K         0    512.0K   0% /dev

あれ?swap が有効になってない。anon_swap を1にしたりしても効かないので、今回はとりあえず手動swaponすることにした。

root@OpenWrt:/# swapon /dev/sda2
[   65.211586] Adding 698364k swap on /dev/sda2.  Priority:-2 extents:1 across:698364k
root@OpenWrt:/# free
             total       used       free     shared    buffers     cached
Mem:         27840      18440       9400         60       1804       4920
-/+ buffers/cache:      11716      16124
Swap:       698364          0     698364

うん、OK。

ビルド環境を整え動作確認

ここからは楽しい冒険の始まりだ。

GCC を入れる

opkg でちゃちゃっと。と思いきや。

root@OpenWrt:/# opkg install gcc
Installing gcc (5.4.0-3) to root...
Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/packages/gcc_5.4.0-3_mips_24kc.ipk
Collected errors:
 * opkg_download: Failed to download http://downloads.openwrt.org/snapshots/packages/mips_24kc/packages/gcc_5.4.
 * opkg_download: Check your network settings and connectivity.

 * opkg_install_pkg: Failed to download gcc. Perhaps you need to run 'opkg update'?
 * opkg_install_cmd: Cannot install package gcc.

んんん???ダウンロードに失敗??PCで試すとダウンロードできるのに。

と、ここで opkg の tmpdir は /tmp なのを思い出す。そうか… gcc のパッケージがメモリに入り切らないんだな!

tmpdir を普通のディレクトリに切り替えて回避。USB 2.0 にも対応してないのでめちゃくそ遅いけど。

root@OpenWrt:~# mkdir cache
root@OpenWrt:~# opkg --tmp-dir /root/cache install gcc
Installing gcc (5.4.0-3) to root...
Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/packages/gcc_5.4.0-3_mips_24kc.ipk
Installing zlib (1.2.11-2) to root...
Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/base/zlib_1.2.11-2_mips_24kc.ipk
Installing libbfd (2.27-1) to root...
Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/base/libbfd_2.27-1_mips_24kc.ipk
Installing libopcodes (2.27-1) to root...
Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/base/libopcodes_2.27-1_mips_24kc.ipk
Installing objdump (2.27-1) to root...
Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/base/objdump_2.27-1_mips_24kc.ipk
Installing ar (2.27-1) to root...
Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/base/ar_2.27-1_mips_24kc.ipk
Installing binutils (2.27-1) to root...
Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/base/binutils_2.27-1_mips_24kc.ipk
Installing libstdcpp (7.3.0-1) to root...
Downloading http://downloads.openwrt.org/snapshots/targets/ar71xx/tiny/packages/libstdcpp_7.3.0-1_mips_24kc.ipk
Configuring zlib.
Configuring libbfd.
Configuring libopcodes.
Configuring objdump.
Configuring ar.
Configuring binutils.
Configuring libstdcpp.
Configuring gcc.

よし入った!

make を入れる

実は make はパッケージで入れなくてもソースコードから bootstrap build ができる。以前はそこまでやっていたが、今回はそれほど M なプレイをしたいわけではないので普通に入れる。

root@OpenWrt:~# opkg install make
Installing make (4.2.1-2) to root...
Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/packages/make_4.2.1-2_mips_24kc.ipk
Configuring make.

sl を落としてビルドしてみる

ここで、ほんとに make できるのか往年の sl で試してみる。GitHub から clone するために git-http をインストールする。

root@OpenWrt:~# opkg install git-http
Installing git-http (2.20.0-1) to root...
Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/packages/git-http_2.20.0-1_mips_24kc.ipk
Installing libopenssl (1.0.2p-1) to root...
Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/base/libopenssl_1.0.2p-1_mips_24kc.ipk
Installing git (2.20.0-1) to root...
Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/packages/git_2.20.0-1_mips_24kc.ipk
Installing libmbedtls (2.13.0-1) to root...
Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/base/libmbedtls_2.13.0-1_mips_24kc.ipk
Installing ca-bundle (20180409-3) to root...
Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/base/ca-bundle_20180409-3_all.ipk
Installing libcurl (7.62.0-1) to root...
Downloading http://downloads.openwrt.org/snapshots/packages/mips_24kc/base/libcurl_7.62.0-1_mips_24kc.ipk
Configuring libmbedtls.
Configuring ca-bundle.
Configuring libcurl.
Configuring libopenssl.
Configuring git.
Configuring git-http.

clone そして make。

github.com

root@OpenWrt:~# git clone https://github.com/mtoyoda/sl.git
Cloning into 'sl'...
remote: Enumerating objects: 97, done.
remote: Total 97 (delta 0), reused 0 (delta 0), pack-reused 97
Unpacking objects: 100% (97/97), done.
root@OpenWrt:~# cd sl
root@OpenWrt:~/sl# make
gcc -O -Wall -o sl sl.c -lncurses
sl.c:41:20: fatal error: curses.h: No such file or directory
compilation terminated.
make: *** [Makefile:15: sl] Error 1

んんっ。ncurses がない。Python でも ncurses はあったほうがいいので入れておくか。

ncurses がないので入れる

パッケージでは ncurses は用意されていない。GNU から落としてくる。

Index of /pub/gnu/ncurses

root@OpenWrt:~# wget https://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.1.tar.gz
wget: SSL support not available, please install one of the libustream-.*[ssl|tls] packages as well as

んんんっw ヤクの毛を刈る感じがしてきたぞw

httpsに必要なあれこれを入れる。

root@OpenWrt:~# opkg install libustream-openssl ca-certificates 
...
root@OpenWrt:~# wget https://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.1.tar.gz
Downloading 'https://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.1.tar.gz'
Connecting to 208.118.235.20:443
Writing to 'ncurses-6.1.tar.gz'
ncurses-6.1.tar.gz   100% |*******************************|  3286k  0:00:00 ETA
Download completed (3365395 bytes)

よし、ビルドだ!

root@OpenWrt:~# tar zxf ncurses-6.1.tar.gz
root@OpenWrt:~# cd ncurses-6.1
root@OpenWrt:~/ncurses-6.1# ./configure

もう configure だけで意味わからんぐらい遅くて最高w USB 1.1 は厳しいな…。

configure だけで40分かかった。このあと走らせた make は実に6時間ぐらいかかってしまった。かなり絶望的なスピードである。

気を取り直して sl

「sl のビルドなんかやろうと思わなきゃよかった」と若干の後悔の念はありつつ、気を取り直してビルド。

root@OpenWrt:~/sl# make
gcc -O -Wall -o sl sl.c -lncurses
root@OpenWrt:~/sl#

おお?

f:id:puhitaku:20181216102958p:plain

おおお!!!やったー!

一旦切ります

…さて。ここまでで結構な時間を使ったためちょっと疲れてしまった。この記事は前編として、後編にて Python のビルドに入りたいと思う。

ルーターに USB を生やせ!

本記事はルーターハックAdvent Calendar 8日目の記事です。

わやくそ遅れていて申し訳ない。

さて。今日は もともと USB ポートのないルーターに USB を生やす 回である。SoC に USB PHY があるのに使わないというのはもったいない。ハードオフの青いかごで眠っていた324円のルーターと一緒に、次なる高みを目指そうじゃないか。

おことわり

技適 に関する筆者の配慮や考えについてはカレンダー1日目「技適とルーターハック」をご覧ください。本記事で紹介する内容は、法を遵守するための慎重な注意をもって書かれています。

www.zopfco.de

ひとまずはんだ付け

先日からハックの犠牲になってもらっている Buffalo WHR-G301N を改造する予定だったのだが、ミスってリアルに犠牲になってしまったので(苦笑)、スペックの近い代用品として改造済み WHR-HP-GN を利用する。利用できる遺産は利用していこう。

WHR-G301N にも WHR-HP-GN にも USB ポートはないが、SoC には USB PHY が搭載されている。

f:id:puhitaku:20181210231711p:plain
データシート。

USB は差動通信するので DP と逆相の DN がある。この2本を接続すれば原理的には通信できそうだ。というわけで接続したのが以下。

f:id:puhitaku:20181216001514j:plain

ファーム側でUSB有効化…のはずが

WHR-HP-GN や WHR-G301N を含む Atheros のチップを搭載した機種(アーキテクチャ名 ar71xx)は、OpenWrt において大きな転換点を迎えている。というのも、もともとはハードウェアの初期化のために機種ごとに異なるCのコードが用意されていたのだが、新しいアーキテクチャ名 ath79 として Device Tree による実装に置き換えられつつあるのだ。(情報をくださったmusashino_205さんに感謝

この流れに乗ろうと WHR-HP-GN も DTS 化を目指した。幸運にもハード的には WHR-HP-G300N とほぼ同じなので、実質的にそれの DTS を使えば WHR-HP-GN も初期化できる。もともとの初期化コードにも WHR-HP-G300N の隣に WHR-HP-GN の記述がなされていることが確認できる

各種記述を追加して早速やってみたのだが、USB を有効化してもprobeがうまくいかない。正確には probe というか、物理層が何かしらうまく行っていない感じだ。

[  431.775895] usb 1-1: new full-speed USB device number 2 using ohci-platform
[  432.065887] ohci-platform 1b000000.usb: frame counter not updating; disabled
[  432.072990] ohci-platform 1b000000.usb: HC died; cleaning up
[  436.836023] usb usb1-port1: attempt power cycle

USB は何かと闇が深いスタックで本業でも泣かされてきたので、今回はひとまず深堀りはやめて DTS ベースの初期化は諦めた。実を言うと ar71xx ベースでの USB 追加は以前やって発表したものと全く同じになるので避けたかったのだが。

初期化コードを加工する

openwrt/target/linux/ar71xx/files/arch/mips/ath79/mach-whr-hp-g300n.c に以下のような加工を加える。見た目通りって感じ。

diff --git a/target/linux/ar71xx/files/arch/mips/ath79/mach-whr-hp-g300n.c b/target/linux/ar71xx/files/arch/mips/ath79/mach-whr-hp-g300n.c
index 48f49ad0f7..84ee3c8a4c 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/mach-whr-hp-g300n.c
+++ b/target/linux/ar71xx/files/arch/mips/ath79/mach-whr-hp-g300n.c
@@ -21,6 +21,7 @@
 #include "dev-gpio-buttons.h"
 #include "dev-leds-gpio.h"
 #include "dev-m25p80.h"
+#include "dev-usb.h"
 #include "machtypes.h"

 #define WHRHPG300N_GPIO_LED_SECURITY           0
@@ -123,6 +124,8 @@ static void __init whrhpg300n_setup(void)
                                    AR724X_GPIO_FUNC_ETH_SWITCH_LED3_EN |
                                    AR724X_GPIO_FUNC_ETH_SWITCH_LED4_EN);

+       ath79_register_usb();
+
        ath79_register_leds_gpio(-1, ARRAY_SIZE(whrhpg300n_leds_gpio),
                                 whrhpg300n_leds_gpio);

カーネルモジュールを追加する

USB という物理層自体のドライバに加え、それ以上のレイヤーのドライバ、例えば USB storage を扱うドライバなどを追加しなければデバイスは利用できない。以前紹介した make menuconfig で追加していく。USB メモリを使うとして、主に必要なのは

  • Target System -> Atheros AR7xxx/AR9xxx
  • Kernel Modules -> USB Support -> kmod-usb-core
  • Kernel Modules -> USB Support -> kmod-usb-ohci
  • Kernel Modules -> USB Support -> kmod-usb-storage
  • Kernel Modules -> Filesystems -> kmod-fs-ext4
  • Kernel Modules -> Native Language Support -> kmod-nls-iso8859-1

といったところ。

挿してみる

手元にあった適当なUSBメモリを挿してみる。

[   86.223642] usb 1-1: new full-speed USB device number 3 using ohci-platform
[   87.638134] usb-storage 1-1:1.0: USB Mass Storage device detected
[   87.647383] scsi host0: usb-storage 1-1:1.0
[   88.680846] scsi 0:0:0:0: Direct-Access     UFD 3.0  Silicon-Power8G  PMAP PQ: 0 ANSI: 6
[   89.588758] sd 0:0:0:0: [sda] 15306752 512-byte logical blocks: (7.84 GB/7.30 GiB)
[   89.601729] sd 0:0:0:0: [sda] Write Protect is off
[   89.611736] sd 0:0:0:0: [sda] No Caching mode page found
[   89.617179] sd 0:0:0:0: [sda] Assuming drive cache: write through
[   89.680778] random: crng init done
[   89.690053]  sda: sda1
[   89.723805] sd 0:0:0:0: [sda] Attached SCSI removable disk

うん、いい感じ。見慣れたログが出る。マウントはどうか?中身のファイルシステムが何かも忘れてるけど。

root@OpenWrt:~# cd
root@OpenWrt:~# pwd
/root
root@OpenWrt:~# mkdir mnt
root@OpenWrt:~# mount /dev/sda1 mnt/
[  170.459769] FAT-fs (sda1): Volume was not properly unmounted. Some data may be corrupt. Please run fsck.
root@OpenWrt:~# ls mnt/
System Volume Information  efi
boot                       keymap.pro
bootmgr                    reagent.xml
bootmgr.efi                sources

マウント成功。どうやら Windows のリカバリ USB のようだ。関係ないが keymap.pro は NiZ Plum のキーマップファイルだ。

USBは無限大

USBが挿さるということは、健康なギーク男子が想像するようなあんなものやこんなものが挿さってしまうということだ。さりとて、USB Storage こそ USB 機器の基本ともいえる。実際こいつが使えると rootfs の拡張がいとも簡単に成し遂げられてしまうし、ハックの幅を広げる第一歩ともいえるだろう。

次回は、USB メモリを実際に rootfs を広げる道具として使い、GCC を入れたりルーターの上で make をビルドしたりしてみたいと思う。なんだかLFSチックな話で胸が踊る。