【第 1 回】 OpenWrt開発入門 イントロダクション
当ブログ最初の記事として、OpenWrtについて取り上げてみたいと思います。
OpenWrtとは、主に無線LANルーター向けに開発されたLinuxディストリビューションの1つです。
OpenWrtは、組み込み機器にインストールされることを想定しているため、汎用PC上で使用されるUbuntuとは異なり、 ネットワーク・Wi-Fi制御に必要のない機能はデータサイズ削減のために極力排除し、最適化をしています。 この最適化はライブラリやソフトウェアから成るシステムアーキテクチャまで及び、フラッシュ容量の少ない組み込み機器でも利用できるように工夫されています。
ここではイントロダクションとして、そもそも「Linuxディストリビューションとは何か」から始め、 OpenWrtと他Linuxディストリビューションとの違いやその特徴について説明していきます。
最後に、この連載記事の流れについて簡単な紹介をしますのでよろしくお願いします。
この記事は初学者を含むエンジニアを対象としており、OpenWrt上でのソフトウェア開発を紹介するものです。 そのため、一般ユーザーとしてOpenWrtを使用したいというニーズには合致する内容とは限りませんのでご注意ください。
目次
Linuxディストリビューションとは?
Linuxディストリビューションとは、Linux(カーネル)を使いやすくするためのサービスソフト(デーモン)や クライアントソフトを用途別に応じて組み合わせ、ユーザーに配布しているものです。
あくまでコアとなるカーネルは同じもの(※)で、その上で実行されるアプリケーションの組み合わせが異なります。 上物のアプリケーションの組み合わせを換え、カーネルの仕組みを知らない一般ユーザーでも使えるようにし たものがLinuxディストリビューション(配布版)です。※カーネルがカスタマイズされている場合もあります。
Linuxディストリビューションは目的とする用途に応じて様々なものがあります
代表的なLinuxディストリビューションとして、サーバー用途で広く使用されるCentOS、汎用用途のdebian、Ubuntuなどがあります。
今回取り上げるOpenWrtはその中でもAP・ルーター用途を目的としたLinuxディストリビューションです。
RF(Radio Frequency)チップを搭載する組み込み機器で使用されることを想定しています。そのため、ネットワークやWi-Fi制御
用のソフトウェアが充実している代わりに、Ubuntuなどで見られるようなGUIソフトウェアを持ちません。
ディスプレイ画面上に表示されるようなグラフィカルなユーザーインターフェースを直接持たないということです。
OpenWrtでは代わりに、LuCIと呼ばれる専用WebUIが提供されます。OpenWrtのWebサーバーuhttpdとLuCI が連携し、HTTPリクエストを契機にユーザーPCのブラウザにネットワーク・Wi-Fi設定用のUIページを提供します。
このように、Linuxディストリビューション別でシステムアーキテクチャ(ライブラリとソフトウェアの構成)が違いますので、インストールする際には 目的を果たしてくれる機能があるか常に確認する必要があります。
OpenWrtであれば、その機器をアクセスポイント(AP)やルーターとして使用したいというニーズには答えられますが、デスクトップパソコンとして使いたい、ゲームをやりたいという目的は叶えてくれません。
OpenWrtと他Linuxディストリビューションとの違い
ディストリビューション別でのシステムアーキテクチャの違いを、OpenWrtとUbuntuを比較することで 見ていきます。下図に、それぞれのアーキテクチャ要素を並べたものを掲載します。
この図はなるべく、ベースとなるコア要素を土台に上位要素を並べたものです。
はじめに、コンピューターとしての主要制御(メモリ・リソース・演算制御など)を司るカーネルを底に、
上位層としてシステムコールラッパーなどを定義する標準ライブラリ・ユーティリティライブラリを乗せています。標準ライブラリ・ユーティリティライブラリは
OSの中でも重要なソフトウェアの実装で使用されていますので、そのソフトウェアをライブラリブロック
の上にさらに乗せて表示している形になります。
これを見ると、OpenWrtとUbuntuは共に、カーネルとしてLinuxを採用しています。しかし、その上で使用している 標準ライブラリは、OpenWrtがuClibc/muslなのに対してUbuntuではglibcとなっています。また、これらの標準ライブラリを使用して 実装された初期化プロセスやシステム管理ソフト(OpenWrt:procd、Ubuntu:systemd)も異なるものとなっています。
初学者の方はこれだけを見ると、「2つのLinuxディストリビューションは全く異なるもので作られていて、動作もかなり違うのかな?」と感じるかもしれません。 しかし、実際にはそう感じるというだけで、2つのディストリビューション間で全く異なる動作はそう多くはありません。 基本的には、対応するそれぞれの要素はディストリビューション間で等価の存在です。
では、何故こんなにも構成要素が異なるかというと、OpenWrtが組込み機器で動作することを想定しているのに対し、Ubuntuは 汎用PC上で動作することを想定しているために付きます。
つまり、組込み機器の少ないデータ容量や非力なCPUスペックなどを考慮した場合、Ubuntuが使用しているソフトウェアをそのまま流用しようとすると、 データが逼迫したり、CPUに無駄な負荷が掛かる懸念があります。そこで、OpenWrtでは組込み機器用にこれらのライブラリやソフトウェアを再定義・設計したものを採用しています。
OpenWrtの目的は、機器をAPやルーターとして動作させることなので、主要サービスであるネットワーク・Wi-Fi制御を考えた場合、 Ubuntuなどで広く使用されているソフトウェアは高機能である分、冗長で複雑で大容量過ぎです。
そのため、OpenWrtでは組込み機器向けの既存・独自ライブラリを使用して、Ubuntuなどのシステムアーキテクチャ 要素を参考に、小容量で簡略化したソフトウェアとユーティリティを開発・採用しています。
以上から、システムアーキテクチャの各要素はOpenWrtとUbuntuで名前こそ異なりますが、それぞれは同じ目的のために存在するものと 思って良く、使い方も非常に似ています(もしくは全く同じです)。
次節では、OpenWrtの主要構成要素に対する説明をしますので、その内容も含めて理解に繋げて頂けたら幸いです。
※掲載した図はOpenWrt公式サイトのものを参考にしています。他ディストリビューションとの違いも知りたいという方は
こちらをご確認ください。
OpenWrtの特徴(システムアーキテクチャ)
ここでは、OpenWrtシステムアーキテクチャの重要要素について私、うたカモの理解で紹介します。
少々長いので、早くOpenWrtを触りたいという方は読み飛ばして進んでください。
- uClibc/musl
- 組み込み機器向けに作られたC言語標準ライブラリです。なので、OpenWrt専用というわけではありません。 C言語を少しでも触ったことがある方はprintf関数やscanf関数などのC言語標準関数を使用したことがあると思います。
- 一般的なLinuxディストリビューションを使用している場合、これらの関数はglibcと呼ばれるライブラリの中で定義されています。 uClibcやmuslはこれらの関数群を組み込み機器向けにデータが小さくなるように再定義したものです。
- 加えて、ライブラリ本体が小容量になるために類似関数は削除されていたりもします。例えば、printf関数のファミリである、 printlnやputsなどが存在しない場合があります。このように冗長な機能の除去なども通して、ライブラリデータが 小容量になるように最適化が図られたものがuClibcやmuslなどの組み込み機器向けC言語標準ライブラリです。 (もちろん、組み込み用CPUに対する最適化もしています)
- libubox
- こちらも先に紹介したuClibc/muslと同じく、組み込み機器向けに作られたライブラリ(とは言え、OpenWrt専用のライブラリ) です。多くのLinuxディストリビューションで使用されているユーティリティライブラリのGLibが元になっています。 そのため、プロセス間通信処理の一部であるUnixソケットや動的メモリ割り当てなどを収録しています。
- OpenWrtでは、専用アプリケーションのubusやnetifdがこのライブラリをプログラム上で利用しています。 このライブラリは、各処理が個々のヘッダファイルでまとめられており、utils.h、usock.h、uloop.h、blob.h、blobmsg.h、 list.h、safe-list.h、kvlist.hなどがあります。各ヘッダファイルに関する詳しい説明は OpenWrt公式サイトから確認できます。
- このように、OpenWrtでは一般的なLinuxディストリビューションとは利用しているライブラリが異なります。 しかし、それらの大半は組込み機器にインストールすることを想定したデータサイズや計算処理の最適化が目的で作られていますので、 関数名などはGlibやglibcのものと(ほぼ)同じものです。そのため、C言語のシステム開発やプログラム学習をしていた方に とっては非常に馴染みやすいものだと言えます。
- libubus
- OpenWrt独自アーキテクチャ要素であるUBUSのコマンドユーティリティや対応アプリケーション用の実装について記述されたライブラリです。
- 前述のlibuboxを依存ライブラリとして、UBUSのプロセス間通信に関する実装が記述されています。
- これを利用することで、UBUSを介して他のアプリケーションに自身の機能を公開したり、イベント通知をするアプリケーションが 作成出来るようになります。
- このライブラリの詳しい説明は以下を参照してみてください。
- libuci
- OpenWrt独自アーキテクチャ要素であるUnified Configuration Interface System(UCI System)のコマンドユーティリティ実装が 記述されたライブラリです。
- これを利用することで、UCIコマンドのsetやgetなどの機能を自作アプリケーションに実装することができます。
-
このライブラリの詳しい説明は以下を参照してみてください。
OpenWrt サンプルプログラム掲載! UCIコマンド用C言語ライブラリ - libuci - procd ※記述内容の加筆予定(近日)
- OpenWrtのシステム管理デーモンです。Ubuntuなどで採用されているinitdを参考に開発されています。 procdはシステム起動時にPID=1で起動し、/etc/rc.dに存在する初期化スクリプト(シンボリックリンク)を順次実行します。 ※初期化スクリプトのシンボリックリンクはS1、S2などの優先順位を示すプレフィックスを名前の先頭に付ける決まりがあり、 これらのシンボリックリンクはprocdによって昇順(S1から順)に実行されます。逆に、シャットダウン時は先頭にKと付く シンボリックリンクを実行します。
- LuCI
- OpenWrtのネットワーク・Wi-Fi設定をするためのユーザー用WebUIです。 先に説明したように、OpenWrtはGUIを持ちません。そのため、通常はLuCIによるWebUIを通して ユーザーはネットワークやWi-Fiの設定をすることになります。
- 基本的に、このWebUI上のネットワーク・Wi-Fi設定はUCIコンフィグレーションファイル(下で紹介)と呼ばれる OpenWrt専用の環境変数と対応しています。
- そのため、UI上で設定した項目は対応するUCIコンフィグレーションファイルに書き込まれた上で適用操作が行われます。 ※ただし、メーカーによるカスタムが入っている場合はUCIコンフィグレーションファイルを積極的には使用していない場合も考えられます。
- この記事の連載では、OpenWrtを泥臭く触ってもらい理解を深めてもらうことを目的としていますので、 LuCIについてはあまり取り上げません(多分)。
- 連載記事ではLuCIがやってくれるUCIコンフィグレーションファイル操作を手動で実施します。 これを通して、OpenWrt独自の環境変数であるUCIコンフィグレーションファイルとそれを使用したアプリケーションの関係 性を感じて頂けたら幸いです。
- Unified Configuration Interface System(UCI System)
- OpenWrt専用アプリケーションの環境変数であるUCIコンフィグレーションファイルを操作するインタフェースシステムです。 通常、/etc/config内で定義されるUCIコンフィグレーションファイルのパラメーターを追加・削除・変更・取得する機能を持ちます。 これらはUCIシステムが提供するユーティリティコマンド(UCIコマンド[/usr/bin/uci])やUCI操作プロセス(/sbin/rpcd)により操作可能です。
- ※下記はUCIコマンド(/usr/bin/uci)の一覧を表示した結果です。これだけの操作があります。 UCIシステムの特徴は、UCIコンフィグレーションファイルのパラメーター変更差分をステージング領域に一時保存することです。 つまり、ステージング領域を持つことでUCIコンフィグレーションファイルのパラメーター変更を仮決定し、変更が確定したらステージング領域 からFLASH領域(/etc/config)にその変更差分をコミットすることができます。 ステージング領域は通常RAM領域です。rebootやpoweroffによる電源断で、変更差分が消去されます。
- ※UCIコマンド(/usr/bin/uci)を使用する場合、ステージング領域は/tmp/.uciです。対してUCI操作プロセス(/sbin/rpcd)を 使用する場合、ステージング領域はネットワーク通信のセッションに紐いたメモリ領域(/tmp/run/rpcd/uci-<ubus_rpc_session>)です。
- この仕組みにより、仮決定したパラメーター変更をお試し適用してみたり、不適切な変更であればパラメーター変更をなかった ことに(退行)することができます。※既にOpenWrtを使っている方は、LuCIを通してUCIシステムの動作を実感できているのではないでしょうか。
- そのため、UCIシステムは常にステージング領域とFLASH領域(/etc/config)のマージ結果を認識しています。 内部的にはこれら領域の内容をuci_contextと呼ばれるデータ構造の中に落とし込み、それを読み取ることで認識しています。 ※下の図はUCIコマンド(/usr/bin/uci)を使用した際のシステム内部の動作イメージです。
- C言語で開発されたOpenWrt専用アプリケーションはUCIコンフィグレーションファイルを環境変数として使用するために、UCIライブラリ(libuci)を インクルードし、プログラムコードレベルで上記で説明した機能を実装しています。これにより、自由にUCIコンフィグレーションファイルを取り込める ようになっています。この仕組みとUBUSによるプロセス間通信を組み合わせることで、実行プロセスに対してUCIコンフィグレーションファイルをリロード (再読み込み)することも可能です。※UBUS連携が必要な場合、UCI操作プロセス(/sbin/rpcd)を使用するのが一般的です。
-
※さらに詳細を知りたい方はこちら
- UBUS
- OpenWrt専用のプロセス間通信(IPC)システムです。プロセス間通信とは「少なくとも2つ以上の実行プログラム(プロセス)間で、 お互いが知っているメッセージルールを使用することで、処理要求とその応答をやり取りできる仕組み」のことを指します。
- ubusはプロセス間でメッセージを送受信する仕組みをUnixドメインソケットで実現しています。 Unixドメインソケットとはインターネットを介したデータ送受信の仕組みの1つであるソケット通信を同一ホスト内のプロセス間のやり取りに適用したものです。
- インターネット上のソケット通信は、送信先と送信元PC(ホスト)のネットワーク住所を示すIPアドレスと、それらのPCの中にある 最終的なデータの届け先であるプロセスの住所をポート番号として指定しています。これにより、離れたPC上のプロセス間でデータ を送りあうことが可能です。これを実現するために、サーバー・クライアントプロセスによる処理シーケンス(サーバー[socket、bind、listen、accept] / クライアント[socket、connect]、write/read)の仕組みがあります。
- ubusに使用されているUnixドメインソケットでは、この処理シーケンスの仕組みを同一ホスト内にあるプロセス間で適用して通信しています。 同一ホスト内での通信のため使用されるプロトコルは通常、UDP(User Datagram Protocol)です。※一連の処理は先ほど紹介したlibuboxを使用して実現しています。
- ubusのUnixドメインソケット上では、サーバー・クライアントプロセス間でJSON形式のデータがやり取りされます。 クライアントによるサーバーへの処理要求とその応答は全てJSONデータで送受信されるということです。
- 通常、ubusを使用するアプリケーションはOpenWrtの中でもコアになるソフトウェアであり、代表的なものとして 初期化プロセス用のprocd、ネットワーク制御用のnetifdなどがあります。 クライアント側はこれらのOpenWrtコアソフトウェアに問い合わせをしたい場合、「メッセージルール(プロトコル)」に則った データを処理要求として送信する必要があります。
- このプロトコルに則ったデータによる通信手続きを一般的にRPC(Remote Procedure Call)と呼びます。 ubusを介して特定処理を公開するコアソフトウェアなどは、公開する処理を「オブジェクトパス␣メソッド␣引数(JSONデータ)」の形式 で呼び出せる仕組みを持っています。この形式がubusの通信プロトコルであり、それに則ってサーバー・クライアントプロセス間で要求と応答をやり取りする 一連の手続きがRPCです。
-
次は、netifdが公開するnetwork.deviceオブジェクトのstatusメソッドを引数{"name": "eth0"}で呼び出す例になります。
この例はnetifdに対して、機器のNIC(eth0)ステータスを問い合わせるものです。
root@OpenWrt:~# ubus call network.device status '{"name": "eth0"}'
- 応答結果はJSONデータとして返されます。以下は応答結果の一部抜粋です。
-
このように、ubusが定めるプロトコルに準拠したメッセージをクライアント側から送信すれば、サーバー側が適切に処理要求を受け付け、応答結果を
クライアント側に返すことができます。
※もちろん、処理要求によってはサーバー側からクライアントへ応答がない場合もあります。 - 多くのプロセス間通信システムで採用されるRPCは通常、オブジェクト・メソッド・引数の関係を持ちます。 これは、エンジニアに対する親しみやすさを考えてのことです。つまり、プログラムの中で使用する関数呼び出しに似せてルール付け(プロトコル化)をすることで、 エンジニアにとってRPCを馴染みあるものにしています。
- ※RPCは本来、ネットワークを跨いだホストプロセス間の通信規則やその実装を指す言葉ですが、同一プロセス間でも使用される場合もあるため、ここでもRPCという言葉を使用しています。
-
長くなりましたがこんな感じで、通信機能からデータのメッセージルールまでを取り決め、実装したものがプロセス間通信システムです。
その中でもOpenWrt専用に作られたものがubusということになります。
※ちなみに、ubusは他Linuxディストリビューションで使用されているD-Bus(超複雑な作りです)を参考に作られています。 - ubusについては以下の記事を参照してみてください。
- busybox
- busyboxは組み込みLinuxで良く使用されているユーティリティツールです。 特徴は、Linux上で良く使用されるmv、mkdir、cd、cpなどの各種ユーティリティコマンド機能を内包していることです。 各コマンドを単独の実行ファイルで構成するよりも、それらを1つのソフトウェアで実現した方がデータの総容量が小さくなるため、 busyboxが作られました。
- 通常、busyboxが持つユーティリティコマンドを使用するときに、busybox自体を意識することはありません。 次のように、各コマンド名別にbusyboxのシンボリックリンクが作られているため、「busybox mv ~」ではなく、 いつも通りに「mv ~」のようにしてコマンド名のみで実行することができます。
-
上図を見ると、大半のコマンドがbusyboxで実現されていることが分かります。
※busyboxのバージョンや構成によっては「busybox --list」で、サポートされているコマンド一覧が表示できます。気が向いたら確認してみてください。
連載記事の流れ
今回の記事を皮切りに、OpenWrt開発入門と題して関連する記事を投稿していきます。
ファームウェアイメージのビルドとインストールから始まり、UCIコマンドやLuCI(WebUI)によるネットワークとWi-Fi設定について紹介します。
そして、最後に自作ソフトウェアの開発とインストールパッケージの作成方法を取り上げます。これらの連載記事を通して、OpenWrtの全体的な理解をして頂きたいと思います。
第2回の記事では、OpenWrtファームウェアイメージの作成とインストールについて取り上げます。
Raspberry PiをターゲットにVanillla OpenWrtをビルドして、インストールしてみます。
※ソフトウェア関連用語のVanillaとは「カスタムされてない、純粋な、素の」という意味で使われます。
第3回の記事では、第2回でRaspberry PiにインストールしたOpenWrtの ネットワーク・Wi-Fi設定について取り上げます。Unified Configuration Interface(UCI)コマンドによるAP・ルーターそれぞれのネットワーク・Wi-Fi設定を 紹介します。これにより、OpenWrt専用アプリケーションの環境変数であるUCIコンフィグレーションファイルを操作するためのUCIコマンドとUCIシステムについても一定の理解をして頂ければと思います。
第4回の記事では、前回記事でUCIコマンドによって実施したネットワーク・Wi-Fi設定をWebUIのLuCIを利用して設定します。 これにより、UCIコマンドやUCIシステムの複雑性がLuCIによって抽象化(または隠ぺい)されること、UI設計をする上でターゲットとなるユーザー層を想定することの重要性を理解して頂ければと思います。
第5回と 第6回 の記事では、OpenWrt専用アプリケーション開発としてゼロからソフトウェアを開発し、インストールパッケージを作成します。 開発するソフトウェアは、パケットキャプチャソフトとして有名なTCPDUMPのコアライブラリ(libpcap)を利用した自己流パケットキャプチャソフトです。 EthernetフレームからIPパケットまで図解を入れて説明し、プログラムに落とし込んでいきます。※第6回の最後では、自作ソフトウェアの動作確認として、後述のリポジトリ設定なしでパッケージをダウンロード・インストールします。
そして最後の 第7回 記事では、第6回で作成したパッケージをリポジトリに管理させ、それをOpenWrt専用パッケージマネージャーソフト(opkg)に認識させる方法について紹介します。 開発用PCに簡易Webサーバーを立て、そのドキュメントルートをローカルリポジトリとしてopkgに設定します。これにより、作成したパッケージ本体だけでなく、 その付帯情報(履歴)をopkgに認識させることが出来ます。ローカルリポジトリを通して、パッケージのインストールだけでなく、アップグレードも可能になります。
その他の追加記事について
本来予定していなかった第8回、第9回の記事を追加しました。
第8回では、第1~7回の記事で取り上げなかったTipsについてまとめています。
第9回では、OpenWrt開発の選択支の1つとしてシリアルコンバーターを 利用したUART接続について紹介しています。UART接続でコンソールを操作すれば、ネットワーク設定を弄りまわしてもOpenWrtとのUART通信は保障されます。 私も普段はUART接続で様々なOpenWrtデバイスを操作しています。
おわりに
今回はイントロダクションとして、OpenWrtとは何なのかについてざっくりと説明しました。
広く知られている他のLinuxディストリビューションと比較すると、OpenWrtはライブラリとソフトウェア構成は異なりますが、システムの内部動作が大きく異なるわけではありません。
OpenWrtはターゲットデバイスとなる組込み機器で難なく動作し、アクセスポイントやルーターとしてネットワーク・Wi-Fi制御をすることを目的としたLinuxディストリビューションです。
次回記事からはOpenWrtの導入方法(ファームウェアイメージの作成とインストール)について紹介します。
- 第1回 OpenWrt開発入門 イントロダクション
- 第2回 OpenWrt開発入門 ファームウェアイメージの作成とインストール
- 第3回 OpenWrt開発入門 UCIコマンドによるネットワーク・Wi-Fi設定
- 第4回 OpenWrt開発入門 LuCI(WebUI)によるネットワーク・Wi-Fi設定
- 第5回 OpenWrt開発入門 作ってみようパケットキャプチャソフト
- 第6回 OpenWrt開発入門 自作アプリのパッケージ作成とインストール
- 第7回 OpenWrt開発入門 パッケージマネージャーソフト(opkg)の使い方とリポジトリ設定
- 第8回 OpenWrt開発入門 Coffee Break
- 第9回 OpenWrt開発入門 UART接続によるコンソールログイン方法の紹介
参考文献
- [OpenWrt Wiki] 起動プロセス https://openwrt.org/ja/docs/techref/process.boot
- [OpenWrt Wiki] OpenWrt–operating system architecture: https://openwrt.org/docs/techref/architecture
- [OpenWrt Wiki] ubus (OpenWrt micro bus architecture): https://openwrt.org/docs/techref/ubus
- [OpenWrt Wiki] The UCI system: https://openwrt.org/docs/guide-user/base-system/uci
- 「Linux」と「ディストリビューション」の関係: https://linuc.org/study/knowledge/337/
- musl - wikipedia: https://ja.wikipedia.org/wiki/Musl
- musl libc: https://wiki.musl-libc.org/
- uclibc - wikipedia: https://wiki.musl-libc.org/
- uClibc: https://uclibc.org/