LoginSignup
5
6

More than 5 years have passed since last update.

Ruby で Linux の NETLINKを使う

Last updated at Posted at 2017-05-20

Ruby で Linux の NETLINKを使ってみました。

例としてネットワークインターフェースのアドレスを取得するコードを書きました。

NETLINKを使う場合は、地道にバイナリのエンコードとデコードを書く必要がありますね。

socket生成

NETLINKのデータ送受信で使うsocketを生成します。
RubyではAF_NETLINKとNETLINK_ROUTEが定義されていないのでヘッダファイルを見て定義します。

require 'socket'

module Linux
  NETLINK_ROUTE    = 0
end

class Socket
  PF_NETLINK    = 16    unless defined? Socket::PF_NETLINK
  AF_NETLINK    = PF_NETLINK  unless defined? Socket::AF_NETLINK
end

s = Socket.new(Socket::AF_NETLINK, Socket::SOCK_DGRAM, Linux::NETLINK_ROUTE)

リクエスト生成

netlinkでは、指定されたバイナリフォーマットを使ってリクエストを送受信します。
netlinkはstruct nlmsghdrを持ちます。
nlmsg_typeに、リクエストのタイプを指定します。IPアドレスの情報を取得するためにはGETADDRを設定します。
GETADDRの場合はstruct ifaddrmsgを使います。

/usr/include/linux/netlink.h
struct nlmsghdr {
        __u32           nlmsg_len;      /* Length of message including header */
        __u16           nlmsg_type;     /* Message content */
        __u16           nlmsg_flags;    /* Additional flags */
        __u32           nlmsg_seq;      /* Sequence number */
        __u32           nlmsg_pid;      /* Sending process port ID */
};
/usr/include/linux/if_addr.h
struct ifaddrmsg {
        __u8            ifa_family;
        __u8            ifa_prefixlen;  /* The prefix length            */
        __u8            ifa_flags;      /* Flags                        */
        __u8            ifa_scope;      /* Address scope                */
        __u32           ifa_index;      /* Link index                   */
};
  struct {
    struct nlmsghdr nh;
    struct ifaddrmsg ifa;
  } request;

この構造体をpackを使ってバイナリにエンコードします。

module Linux
  RTM_GETADDR    = 22
  NLM_F_REQUEST    = 1
  NLM_F_ROOT    = 0x100
end

hdr = [24, Linux::RTM_GETADDR, Linux::NLM_F_REQUEST | Linux::NLM_F_ROOT, 0, 0].pack("ISSII")
ifa = [Socket::AF_INET, 0, 0, 0, 0].pack("CCCCI")

リクエスト送信、レスポンス受信

先ほど作ったバイナリデータをsendで送信し、カーネルからの応答をrecvで受信します。

s.send(hdr+ifa, 0)
resp = s.recv(4096)

レスポンス解析

レスポンスもバイナリデータです。
GETADDRのレスポンスは、struct nlmsghdr と struct ifaddrmsg と struct rtattr の組み合わせです。

/usr/include/linux/rtnetlink.h
struct rtattr {
        unsigned short  rta_len;
        unsigned short  rta_type;
};

nlmsghdrとifaddrmsgをデコードします。ここまでは固定長です。
残っている部分はrtattrと可変長データなので順次デコードします。
データは4バイトアラインされています。

  resp_len = resp.length
  printf("resp_len:%d\n", resp_len)

  while resp_len > 0

    nlhdr_len, nlhdr_type = resp.slice!(0, 16).unpack("ISSII")
    resp_len -= nlhdr_len
    printf("len:%d type:%d ",nlhdr_len, nlhdr_type)
    ifa = resp.slice!(0, 8).unpack("CCCCI")
    printf("family:%d prefixlen:%d flags:%d scope:%d index:%d\n",ifa[0], ifa[1], ifa[2], ifa[3], ifa[4])

    len = nlhdr_len - 24

    while len > 0
      rta_len, rta_type = resp.slice!(0, 4).unpack("SS")
      rta_align_len = 4 * ((rta_len + 3) / 4)
      data_len = rta_align_len - 4
      if rta_type == 3
        data = resp.slice!(0, data_len)
      else
        data = resp.slice!(0, data_len).unpack("C*")
      end
      printf("  len:%2d type:%d data:%s\n", rta_len, rta_type, data.to_s)
      len -= rta_align_len
    end
  end

実行例

resp_len:220
len:68 type:20 family:2 prefixlen:8 flags:128 scope:254 index:1
  len: 8 type:1 data:[127, 0, 0, 1]
  len: 8 type:2 data:[127, 0, 0, 1]
  len: 7 type:3 data:lo
  len:20 type:6 data:[255, 255, 255, 255, 255, 255, 255, 255, 155, 9, 0, 0, 155, 9, 0, 0]
len:80 type:20 family:2 prefixlen:24 flags:128 scope:0 index:2
  len: 8 type:1 data:[192, 168, 1, 8]
  len: 8 type:2 data:[192, 168, 1, 8]
  len: 8 type:4 data:[192, 168, 1, 255]
  len: 9 type:3 data:eth0
  len:20 type:6 data:[255, 255, 255, 255, 255, 255, 255, 255, 188, 9, 0, 0, 188, 9, 0, 0]
len:72 type:20 family:2 prefixlen:16 flags:128 scope:0 index:3
  len: 8 type:1 data:[172, 17, 42, 1]
  len: 8 type:2 data:[172, 17, 42, 1]
  len:12 type:3 data:docker0
  len:20 type:6 data:[255, 255, 255, 255, 255, 255, 255, 255, 71, 15, 0, 0, 71, 15, 0, 0]

参考

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6