Wireless・のおと
サイレックスの無線LAN 開発者が語る、無線技術についてや製品開発の秘話、技術者向け情報、新しく興味深い話題、サイレックスが提供するサービスや現状などの話題などを配信していきます。
前の記事:「SX-580 WiFi 電気スタンド製作記(1)」へ
SX-580 WiFi 電気スタンド製作記(2)
前回は SX-580 SDK を用いて SX-580-2700DM 用のファームウェアを開発できる環境を整えました。今回は SX-580-2700DM の WiFi を AP モードで動かし、iPhone を接続するまでの手順です。
iPhone と SX-580-2700DM を WiFi で接続するには、(1) 両者とも共通のアクセスポイント(AP)に接続する (2) SX-580-2700DM を簡易 AP として稼動させ、そこに iPhone を接続するという2つの形態が考えられます。どちらも利害得失があり、(1) は既に AP があって稼動している環境(ネットにつないだまま操作できる)、(2) は AP のないスタンドアロン環境向きです。今回は (2) として、SX-580-2700DM を AP モードで稼動させてみることにします。
AP モードを動かしてみる
有線 Ethernet を接続した状態で SX-580 SDK を起動し、"root" でログインします。有線 Ethernet を接続できない事情があるときは、ログイン後に
で WiFi モードをリセットしておいてください(これについては後述します)。
さて、SX-580 SDK のソフトウェアマニュアルには AP モードの設定方法が記されています。Linux 上で AP モードを動かすためには hostapd というサプリカントを用い、hostapd.conf と呼ばれる設定ファイルが必要となります。SX-580-2700DM をデフォルトビルドした状態では /etc/hostapd.conf にサンプルの conf ファイルが入っているので、これを1行変更(有線 LAN とのブリッジ機能は使わないので bridge=br0 をコメントアウト)して使うことにします。
のようにして hostapd を起動します。-B はバックグラウンド実行の指定です。うまく動かないとか SSID が見えないようなときには、-B を付けずに起動するとフォアグラウンド実行で各種のメッセージが表示されます。(-d, -dd を付けると更にメッセージが増えますが、それなりの知識がなければ読み解けません)
SSID が見えた状態。(表示)
あれ、いつまでもくるくる回っているだけで接続完了になりません。
くるくる回りっ放し。(表示)
しかし SX-580 側で「hostapd_cli」コマンドを使って確認すると iPhone の MAC アドレスは見えています。
- DHCP で IP アドレスが与えられること
- DHCP オプションに Default Gateway が含まれること
という条件が必要です。DHCP 応答がなくとも3分くらい放置すれば 169.254.x.x の AutoIP が振られますが、3分も待っていられません(※註)。また iPhone 側の設定で固定アドレスを振ることも可能ですが面倒です。
(※註) もともと AutoIP は「Zeroconf」と呼ばれたプロジェクトの一環で、Zeroconf は世の中の機器を何でも自由に相互接続しようとした規格でした。Apple が実装した Zeroconf プロトコルが Bonjour になります。AutoIP は DHCP サーバの有無に関わらず、いつでもどこでも使えるリンクローカルアドレスを目指したものですが、IETF のエライセンセイ達から散々な批判を受け、リトライ間隔を馬鹿みたいに伸ばした結果、「いつでもどこでも使い物にならない」代物になってしまいました。
static 設定の例。(表示)
つまり「AP モードで iPhone を接続する」機器は単に AP として動作するだけでは不十分で、「DHCP サーバ」の機能が必要になります。
busybox のカスタマイズ
SX-580 は「busybox」を使用しています。busybox というのは1つの巨大実行ファイルのなかにいろんな機能を詰めこんだ「便利箱」で、shell や ls や cat や ps など伝統的な Unix コマンドも個別の実行ファイルとしては実装されておらず、全て busybox の一部として実装されています。
busybox には DHCP サーバの機能も実装されていますが、SX-580 SDK のデフォルトでは ON になっていません。なので busybox の設定を変えて rootfs を更新する必要があります。
VM の上の開発環境から
BusyBox menuconfig の画面。(表示)
udhcpd server を選択 [ * ] に設定。(表示)
httpd が選択されていることを確認。(表示)
さて busybox-config の設定を変更したら <Exit> を選択し "Do you wish to save your new configuration?" で <Yes> を選択、snapshot 下で make を実行すると...あれ?busybox を再コンパイルしている様子がありません。ls -l staging/build/busybox-1.19.4/busybox で見てみると、以前にビルドしたままの日付になっています。しかし ls -l staging/build/busybox-1.19.4/.config で見ると確かに config ファイルの日付は変わっています。
これは snapshot/Makefile の依存関係記述が不十分で、busybox .config ファイルの更新と busybox のリビルドが関連付けられていないためです。少々手間ですが、手作業で stating/target/bin/busybox を削除してから make してください。今度は ">>> busybox 1.19.4 Building" のメッセージが出て busybox がコンパイルされるはずです。
busybox DHCP サーバの挙動は conf ファイルで設定します。conf ファイルの文法については http://git.busybox.net/busybox/tree/examples/udhcp/udhcpd.conf を参照してください。今回は最低限の設定で
というファイルを /etc/udhcpd.conf に作成します。
例によって /etc/hostapd.conf から bridge=br0 の設定を外しておいて
とやって AP を起動、AP のインターフェースに IP アドレス 192.168.99.1 を与え、DHCP サーバを起動します。この状態で iPhone から接続すれば、今度は「くるくる回りっ放し」にはならず「接続完了」を示すチェックマークが表示されるはずです。
WiFi 接続完了。(表示)
かくして SX-580-2700DM を AP モードとして動かして iPhone が接続できるようにしましたが、rootfs を転送してから hostapd.conf を編集したり udhcpd.conf を追加したり、hostapd や udhcpd を手動で起動したりと手間がかかりました。次はそれを自動化するための手順です。
init プロセスと init.d スクリプト
Linux の初期化処理は init と呼ばれるプロセスによって行われます。init が何をどういう順番で初期化するかは Linux のバージョンやディストリビューションによって相違がありますが、SX-580 SDK では /etc/inittab に起動手順が書かれており、そこから呼び出される初期化処理のスクリプトが /etc/init.d/rcS, /etc/init.d/rcW として実装されています。
ブリッジと DHCP サーバ
AP のブリッジモードと DHCP サーバは相性が良くありません。ブリッジの場合、無線 LAN と有線 LAN は透過に接続されます。無線から受信された DHCP リクエストは有線 LAN に中継され、有線 LAN 上に接続されている(筈)の DHCP サーバから IP アドレスが与えられます。AP 上で DHCP サーバを動かすと、有線 LAN 上に DHCP サーバが稼動していた場合、AP 上の DHCP サーバと衝突する可能性があります。
この面倒を割けるため、家庭用の「無線 LAN ルータ」では無線 LAN を独立したネットワークとして扱い、有線 LAN との中継を NAT (Network Address Translation) で行うものが多いです。しかし NAT の設定はそれだけで一章の記事が書けるほどの分量になるため、今回は単純に「ブリッジ機能を切って独立無線 LAN 環境として動作させる」ことにします。
init.d/rcW の改修(部分)
rootfs イメージとその雛型
さて SX-580-2700DM ターゲットの rcW ファイルは当然 /etc/init.d にあるわけですが、開発環境上(VM 上)では違う場所にあります。まかり間違っても /etc/init.d 下のファイルを編集してはいけません!そこには Ubuntu Linux の初期化ファイルが入っています。
開発環境上では snapshot/staging/target 下に「ターゲットに転送されるはず」の rootfs コピーが作られており、このディレクトリの内容が丸ごと変換されて rootfs.jffs2 になっています。しかし snapshot/staging/target の内容は "make rootfs-build" を実行するたび半ば動的に生成されており、init.d のようなファイルの「雛型」は更に別のディレクトリ、snapshot/staging/skeleton 下にあります。(※註)
(※註)rootfs 原型や雛型ディレクトリの配置・命名は Linux 開発環境によってマチマチです。同じ会社の製品でも BSP やバージョンが変わるとゴッソリ違っていることも珍しくありません。SX-580 SDK では rootfs="target", 雛型="skeleton" と呼んでいますが、silex の別製品ではこれが "rootfs" "default" になる場合もあったりします。
- snapshot/staging/skeleton/etc/hostapd.conf の編集
(bridge=br0 のコメントアウト)
- snapshot/staging/skeleton/etc/udhcpd.conf の追加
- snapshot/staging/skeleton/etc/init.d/ipconfig の編集
(IPV4METHOD=static, IPV4ADDR=192.168.99.1, WIFIMODE=AP)
- snapshot/staging/skeleton/etc/init.d/rcW の編集
(ブリッジ初期化のコメントアウト、udhcpd 起動の追加)
の後で snapshot 下で make rootfs-build を実行すれば staging/images/rootfs.jffs2 が更新されます。いつもの手順で update_rootfs を実行してください。再起動すれば自動的に hostapd と udhcpd が起動し、iPhone が接続できる状態になっているはずです。
次回は HTTP サーバーと CGI についての解説です。
AP モードを動かしてみる
有線 Ethernet を接続した状態で SX-580 SDK を起動し、"root" でログインします。有線 Ethernet を接続できない事情があるときは、ログイン後に
# killall wpa_supplicant
# ifconfig ath0 up
で WiFi モードをリセットしておいてください(これについては後述します)。
さて、SX-580 SDK のソフトウェアマニュアルには AP モードの設定方法が記されています。Linux 上で AP モードを動かすためには hostapd というサプリカントを用い、hostapd.conf と呼ばれる設定ファイルが必要となります。SX-580-2700DM をデフォルトビルドした状態では /etc/hostapd.conf にサンプルの conf ファイルが入っているので、これを1行変更(有線 LAN とのブリッジ機能は使わないので bridge=br0 をコメントアウト)して使うことにします。
#hostapd には hostapd.conf のパスを引数として与え、
# This file is a sample configuration file of hostapd.
#
interface=ath0
# bridge=br0
driver=ar6000
ssid=SX-IMAPP-SDK
channel_num=11
ignore_broadcast_ssid=0
auth_algs=1
ctrl_interface=/var/run/hostapd
# hostapd -B /etc/hostapd.conf
のようにして hostapd を起動します。-B はバックグラウンド実行の指定です。うまく動かないとか SSID が見えないようなときには、-B を付けずに起動するとフォアグラウンド実行で各種のメッセージが表示されます。(-d, -dd を付けると更にメッセージが増えますが、それなりの知識がなければ読み解けません)
# hostapd ./hostapd.confiPhone から WiFi 一覧を見てみると、確かに「SX-IMAPP-SDK」という SSID が見えています。これをタップして接続すると...
Configuration file: ./hostapd.conf
Delete Filter 0 = 33:33:00:00:00:01
Delete Filter 1 = 01:00:5e:00:00:01
Delete Filter 2 = 33:33:ff:5b:34:ec
Add Filter 0 = 01:00:5e:00:00:01
ADDRCONF(NETDEV_UP): ath0: link is not ready
channel hint set to 2462
Using interface ath0 with hwaddr 00:80:9ADDRCONF(NETDEV_CHANGE): ath0: link becomes ready
2:5b:34:ec and ssid 'SX-IMAPP-SDK'

あれ、いつまでもくるくる回っているだけで接続完了になりません。

しかし SX-580 側で「hostapd_cli」コマンドを使って確認すると iPhone の MAC アドレスは見えています。
種明かしをすると、iPhone を WiFi に接続するためには# hostapd_cli all_sta
Selected interface 'ath0'
60:fa:cd:**:**:**
- DHCP で IP アドレスが与えられること
- DHCP オプションに Default Gateway が含まれること
という条件が必要です。DHCP 応答がなくとも3分くらい放置すれば 169.254.x.x の AutoIP が振られますが、3分も待っていられません(※註)。また iPhone 側の設定で固定アドレスを振ることも可能ですが面倒です。
(※註) もともと AutoIP は「Zeroconf」と呼ばれたプロジェクトの一環で、Zeroconf は世の中の機器を何でも自由に相互接続しようとした規格でした。Apple が実装した Zeroconf プロトコルが Bonjour になります。AutoIP は DHCP サーバの有無に関わらず、いつでもどこでも使えるリンクローカルアドレスを目指したものですが、IETF のエライセンセイ達から散々な批判を受け、リトライ間隔を馬鹿みたいに伸ばした結果、「いつでもどこでも使い物にならない」代物になってしまいました。

つまり「AP モードで iPhone を接続する」機器は単に AP として動作するだけでは不十分で、「DHCP サーバ」の機能が必要になります。
busybox のカスタマイズ
SX-580 は「busybox」を使用しています。busybox というのは1つの巨大実行ファイルのなかにいろんな機能を詰めこんだ「便利箱」で、shell や ls や cat や ps など伝統的な Unix コマンドも個別の実行ファイルとしては実装されておらず、全て busybox の一部として実装されています。
busybox には DHCP サーバの機能も実装されていますが、SX-580 SDK のデフォルトでは ON になっていません。なので busybox の設定を変えて rootfs を更新する必要があります。
VM の上の開発環境から
sxdevel@silex:~/sx580sdk/snapshot$ make buildroot-busybox-configで busybox の設定を行います。Networking Utilities を開いて "udhcp server (udhcpd)" の設定を ON ([*]) にしてください。後で WEB サーバも使うので "httpd" も ON にする必要がありますが、これは SX-580 SDK ではデフォルトで ON になっています。



さて busybox-config の設定を変更したら <Exit> を選択し "Do you wish to save your new configuration?" で <Yes> を選択、snapshot 下で make を実行すると...あれ?busybox を再コンパイルしている様子がありません。ls -l staging/build/busybox-1.19.4/busybox で見てみると、以前にビルドしたままの日付になっています。しかし ls -l staging/build/busybox-1.19.4/.config で見ると確かに config ファイルの日付は変わっています。
sxdevel@silex:~/sx580sdk/snapshot$ ls -l staging/build/busybox-1.19.4/busybox
-rwxr-xr-x 1 sxdevel sxdevel 814936 2014-08-05 19:16 staging/build/busybox-1.19.4/busybox
sxdevel@silex:~/sx580sdk/snapshot$ ls -l staging/build/busybox-1.19.4/.config
-rw-r--r-- 1 sxdevel sxdevel 27125 2014-08-13 10:31 staging/build/busybox-1.19.4/.config
これは snapshot/Makefile の依存関係記述が不十分で、busybox .config ファイルの更新と busybox のリビルドが関連付けられていないためです。少々手間ですが、手作業で stating/target/bin/busybox を削除してから make してください。今度は ">>> busybox 1.19.4 Building" のメッセージが出て busybox がコンパイルされるはずです。
sxdevel@silex:~/sx580sdk/snapshot$ rm staging/target/bin/busyboxビルドが終わったら staging/images/rootfs.jffs2 を /tftp にコピーして、U-Boot から rootfs を更新してください。今回はカーネルの更新は要りません。
sxdevel@silex:~/sx580sdk/snapshot$ make
busybox DHCP サーバの挙動は conf ファイルで設定します。conf ファイルの文法については http://git.busybox.net/busybox/tree/examples/udhcp/udhcpd.conf を参照してください。今回は最低限の設定で
start 192.168.99.100
end 192.168.99.199
interface ath0
opt router 192.168.99.1
というファイルを /etc/udhcpd.conf に作成します。
例によって /etc/hostapd.conf から bridge=br0 の設定を外しておいて
# hostapd -B /etc/hostapd.conf
# ifconfig ath0 192.168.99.1
# udhcpd /etc/udhcpd.conf
とやって AP を起動、AP のインターフェースに IP アドレス 192.168.99.1 を与え、DHCP サーバを起動します。この状態で iPhone から接続すれば、今度は「くるくる回りっ放し」にはならず「接続完了」を示すチェックマークが表示されるはずです。

かくして SX-580-2700DM を AP モードとして動かして iPhone が接続できるようにしましたが、rootfs を転送してから hostapd.conf を編集したり udhcpd.conf を追加したり、hostapd や udhcpd を手動で起動したりと手間がかかりました。次はそれを自動化するための手順です。
init プロセスと init.d スクリプト
Linux の初期化処理は init と呼ばれるプロセスによって行われます。init が何をどういう順番で初期化するかは Linux のバージョンやディストリビューションによって相違がありますが、SX-580 SDK では /etc/inittab に起動手順が書かれており、そこから呼び出される初期化処理のスクリプトが /etc/init.d/rcS, /etc/init.d/rcW として実装されています。
# Startup the systemrcS はネットワークに関係ない初期化処理、rcW はネットワークの初期化処理を行っています。rcW は /etc/init.d/ipconfig から設定を拾って動くようになっています。デフォルトの ipconfig は次のようになっています。
null::sysinit:/etc/init.d/rcS
null::sysinit:/etc/init.d/rcW
# Put a getty on the serial port
::respawn:/sbin/getty -L ttyAM0 115200 vt100 # GENERIC_SERIAL
# Stuff to do before rebooting
null::shutdown:/etc/init.d/rcD
null::shutdown:/bin/umount -r /
#IPV4METHOD=static (小文字)、WIFIMODE=AP (大文字) に設定すれば AP モードとして起動するようになります。但しデフォルトの rcW ではイーサネット eth0 が自動的にブリッジモードとして初期化されてしまいますし、udhcpd の自動起動も行われません。
HOSTNAME=
IPV4METHOD=auto
IPTRIES=3
IPV4ADDR=169.254.111.111
IPV4MASK=255.255.0.0
IPV4ROUTER=0.0.0.0
IPV4DNS1=0.0.0.0
IPV4DNS2=0.0.0.0
#
USETFTP=1
USETELNET=1
#
WIFIMODE=AD-HOC
APCOUNTRY=JP
REGCODE=0x406A
ブリッジと DHCP サーバ
AP のブリッジモードと DHCP サーバは相性が良くありません。ブリッジの場合、無線 LAN と有線 LAN は透過に接続されます。無線から受信された DHCP リクエストは有線 LAN に中継され、有線 LAN 上に接続されている(筈)の DHCP サーバから IP アドレスが与えられます。AP 上で DHCP サーバを動かすと、有線 LAN 上に DHCP サーバが稼動していた場合、AP 上の DHCP サーバと衝突する可能性があります。
この面倒を割けるため、家庭用の「無線 LAN ルータ」では無線 LAN を独立したネットワークとして扱い、有線 LAN との中継を NAT (Network Address Translation) で行うものが多いです。しかし NAT の設定はそれだけで一章の記事が書けるほどの分量になるため、今回は単純に「ブリッジ機能を切って独立無線 LAN 環境として動作させる」ことにします。
init.d/rcW の改修(部分)
#actif=ath0 に設定しているのは、startip.sh で無線 AP のインターフェースである ath0 に IP アドレスを設定させるためです。DHCP サーバは IP アドレス設定後でないと起動してくれないので、udhcpd の起動は startip.sh の呼び出し後になります。
# AP Mode or Station Mode
#
if [ ${WIFIMODE} = "ap" ] ; then
#
# Add bridge interface
#
# brctl addbr br0
# brctl addif br0 eth0
# brctl setfd br0 2
# ifconfig br0 up
# actif=br0
actif=ath0
if [ -e /sys/class/net/ath0 ] ; then
#
# Start up the hostapd
#
if [ -x /etc/init.d/wireless.sh ]; then
/etc/init.d/wireless.sh start ath0 ${WIFIMODE} ${APCOUNTRY}
fi
# brctl addif br0 ath0
fi
.
.
.
/etc/init.d/startip.sh ${actif}
# Start DHCP server
if [ -e /etc/udhcpd.conf ] ; then
udhcpd /etc/udhcpd.conf
fi
rootfs イメージとその雛型
さて SX-580-2700DM ターゲットの rcW ファイルは当然 /etc/init.d にあるわけですが、開発環境上(VM 上)では違う場所にあります。まかり間違っても /etc/init.d 下のファイルを編集してはいけません!そこには Ubuntu Linux の初期化ファイルが入っています。
開発環境上では snapshot/staging/target 下に「ターゲットに転送されるはず」の rootfs コピーが作られており、このディレクトリの内容が丸ごと変換されて rootfs.jffs2 になっています。しかし snapshot/staging/target の内容は "make rootfs-build" を実行するたび半ば動的に生成されており、init.d のようなファイルの「雛型」は更に別のディレクトリ、snapshot/staging/skeleton 下にあります。(※註)
(※註)rootfs 原型や雛型ディレクトリの配置・命名は Linux 開発環境によってマチマチです。同じ会社の製品でも BSP やバージョンが変わるとゴッソリ違っていることも珍しくありません。SX-580 SDK では rootfs="target", 雛型="skeleton" と呼んでいますが、silex の別製品ではこれが "rootfs" "default" になる場合もあったりします。
- snapshot/staging/skeleton/etc/hostapd.conf の編集
(bridge=br0 のコメントアウト)
- snapshot/staging/skeleton/etc/udhcpd.conf の追加
- snapshot/staging/skeleton/etc/init.d/ipconfig の編集
(IPV4METHOD=static, IPV4ADDR=192.168.99.1, WIFIMODE=AP)
- snapshot/staging/skeleton/etc/init.d/rcW の編集
(ブリッジ初期化のコメントアウト、udhcpd 起動の追加)
の後で snapshot 下で make rootfs-build を実行すれば staging/images/rootfs.jffs2 が更新されます。いつもの手順で update_rootfs を実行してください。再起動すれば自動的に hostapd と udhcpd が起動し、iPhone が接続できる状態になっているはずです。
次回は HTTP サーバーと CGI についての解説です。