ruby – 指向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 ifconf”(与SIOCGIFCONF一起使用)的签名需要传递缓冲区.

这是签名:

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

我如何从ruby调用SIOCGIFCONF ioctl命令,缓冲区为4096字节?

最佳答案 使用
Array#pack的“P”说明符存储指向a的指针

缓冲:

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的字符串:

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 bufffer中的数据字节数.我们
通过解压缩数组中的元素可以看到:

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

将存储在ifreqs中的数据解码为一个练习
读者.

点赞