4

我正在使用 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 ifconf”的签名(与 SIOCGIFCONF 一起使用)需要传递一个缓冲区。

这是签名:

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

如何从 ruby​​ 调用具有 4096 字节缓冲区的 SIOCGIFCONF ioctl 命令?

4

1 回答 1

2

使用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 调用。缓冲区将是一个表示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")

格式说明符分为:

  • “我!” - 平台原生大小的有符号长整数。这编码ifreqs.size.

  • "P" - 指向固定长度缓冲区的指针。这对存储在ifreqs字符串中的缓冲区的地址进行编码。

现在我们可以打电话了:

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

SIOCGIFCONF 调用修改 ifconf 缓冲区,更新ifc_len为实际存储在缓冲区中的数据字节数ifreqs。我们可以通过解包数组的那个元素来看到:

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

将存储到ifreqs中的数据解码留给读者作为练习。

于 2015-08-10T00:35:06.017 回答