概要
『C for Linux 2』という本に ioctl でネットワークインターフェースの情報を取得するサンプルプログラムがあった。 以下のような実行イメージ。
% ./ifinfo eth0 name=eth0 UP BROADCAST MULTICAST mtu=9001 addr=172.31.7.56 dstaddr=172.31.7.56 broadaddr=172.31.15.255 netmask=255.255.240.0
Rubyでネットワークプログラムを書いているのだが、あまりサンプルプログラムや情報が少ないので、まずは真似て勉強してみようということで、上記プログラムの一部と同じ動作結果を得られるプログラムを書いてみたメモ。
全部やりきるのは労力がかかるので、一部(フラグ: UP BROADCAST MULTICAST
)の出し方だけ調べてみた。あとは、ioctl に渡す引数の仕様等を調べれば同じやり方で実装できるはず。
C言語の実装は上記本のサンプルプログラムに記載があるので、それと突き合わせながら確認すると良いと思う。
メモ
ソースコード
実行結果
100001000011
これだと何だかわからないが、 UP BROADCAST MULTICAST
となっているインターフェースの情報となっている。
linux/if.h
に ifreq.ifr_flags に格納される各フラグのビットの位置が記載されている。
ネットですぐに拾えたものだと以下等(なぜか spotify さん)。
つまり、左から1番目(1bit目)がONになっているのは IFF_UP 0x1
がONになっているためなのでUPだと判断できる。
2bit目以降も突き合わせていけば整合が確認できる。
linux/if.h at master · spotify/linux · GitHub
コードのメモ
3行目の以下は
SIOCGIFFLAGS = 0x8913
ioctl の引数に渡す定数。ioctl はデバイスに対して様々な情報を取得したり設定できるシステムコール。
渡す引数によって実行を制御できるが、 SIOCGIFFLAGS
の場合は、インターフェースのフラグを取得できる、ということらしい。
ちなみに手元のPC(Amazon Linux2(arm64))では /usr/include/bits/ioctls.h
のパスに当該ファイルは存在した。
ネットだと↓に同じようなものが落ちていた。
6行目では、 ioctl に渡す引数の ifreq
構造体を準備する。
C言語では ifreq 構造体のポインタを ioctl に渡すと取得された情報をその構造体に格納してくれる。
Rubyの場合はC言語的なポインタは準備できないので、パックした文字列を渡すと、その文字列を破壊的に変更してくれるらしい(ドキュメント等で確認できておらず、挙動から推定)
今回の場合は、ASCII文字列に変更すると都合がいいようなのでそのように変換しておく。
このへんの動作仕様は
https://gist.github.com/k-sone/8036832
の実装を参考にさせてもらった。
ifreq = ["eth0"].pack("a4")
10行目では結果を取得している。
ifreq 構造体
Man page of NETDEVICE では最初に char ifr_name[IFNAMSIZ]; /* Interface name */
が入っているのでこれを除外して、どうやらその次のデータから取得したデータが入ってくるらしい。このあたりの動きは完全に結果から類推しているので、きちんと仕様として理解したいのだが、パッとドキュメント等には見当たらず、多分実装の深い部分を追っていかないと分からなそうだったので、これ以上深追いはしていない。
で、同じ定数はRubyでも Socket::IFNAMSIZ
で参照できる(手元の環境では 16
だった)。
フラグが入っている ifr_flags
は上記 ifreq の仕様をみると short 型で2Byteなのでその分だけ取ってくる。
result = ifreq[Socket::IFNAMSIZ, 0x02].dup
最後に結果を16進数(文字列)を数値(10進数)に変えて、2進数(文字列)に変えて、フラグのビットを確認する。
puts result[1].ord.to_s(2) + result[0].ord.to_s(2)
感想
いまだに分からないところだらけだが、Rubyで ioctl を触る雰囲気だけつかめてよかった。