Techioz Blog

IOCTL呼び出しのバッファへのポインタ

概要

Ruby と bit-struct を使用して、テスト セットアップでネットワーク インターフェイスを構成しています。これはほとんどの IOCTL 呼び出しでは正常に機能しますが、SIOCGIFCONF を呼び出す方法がわかりません。

以下に例を示します。

インターフェイスの MAC アドレスを取得したい場合は、次のように書きます。

class LinuxIfreqMacAddr < BitStruct
  char       :name,    128
  unsigned   :type,     16, :endian => :native
  hex_octets :macaddr,  48
  pad        :padding,  64
end

ifr = LinuxIfreqMacAddr.new
ifr.name = "eth0"
s.ioctl(SIOCGIFHWADDR, ifr) # s is a socket
puts ifr.macaddr

これは正常に動作し、eth0 の MAC アドレスが出力されます。ただし、「struct ifconfig」の署名 (SIOCGIFCONF で使用) はバッファーに渡す必要がありました。

署名は次のとおりです。

struct ifconf  {
    int     ifc_len;
    char __user *ifcu_buf;
};

Ruby から 4096 バイトのバッファを使用して SIOCGIFCONF ioctl コマンドを呼び出すにはどうすればよいですか?

解決策

Array#pack の「P」指定子を使用して、 バッファ:

require 'socket'
sock = UDPSocket.new
ifreqs = ' ' * 4096
ifconf = [ifreqs.size, ifreqs].pack("l!P")
SIOCGIFCONF = 0x8912
sock.ioctl(SIOCGIFCONF, ifconf)
data_size = ifconf.unpack('l!').first
p data_size # => 120

ioctl がインターフェイス情報の配列を保存するバッファを作成します。

ifreqs = ' ' * 4096

次に、ioctl 呼び出しに渡す 2 番目のバッファを作成します。バッファは ifconf 構造体を表す文字列になります。

struct ifconf {
    int                 ifc_len; /* size of buffer */
    union {
        char           *ifc_buf; /* buffer address */
        struct ifreq   *ifc_req; /* array of structures */
    };
};

これを行うには Array#pack を使用します。

ifconf = [ifreqs.size, ifreqs].pack("l!P")

フォーマット指定子は次のように分類されます。

これで、電話をかけることができます。

SIOCGIFCONF = 0x8912
sock.ioctl(SIOCGIFCONF, ifconf)

SIOCGIFCONF 呼び出しは ifconf バッファを変更し、ifc_len を更新します。 ifreqs バッファに実際に格納されているデータのバイト数。私たちは 配列のその要素だけを解凍すると、それがわかります。

data_size = ifconf.unpack('l!').first
p data_size # => 120

ifreqs に保存されたデータのデコードは、演習として残されています。 読者。