netlink 响应 RTM_GETADDR 中的 IFA_UNSPEC 负载是什么?

netlink 响应 RTM_GETADDR 中的 IFA_UNSPEC 负载是什么?

RTM_GETADDR我编写了一个程序来使用for family查询 Linux rtnetlink(7) 接口AF_INET。解析响应时我意识到响应的最大部分似乎是IFA_UNSPEC长度为 88 字节的类型记录(其他部分通常小于 8 字节长)。

调试示例输出(仅针对一个接口):

  DB<3> r
index 1, family 2, prefixlen 8
flags permanent
host
# len 8, type 1
  address 127.0.0.1
# len 8, type 2
  local 127.0.0.1
# len 7, type 3
  label lo
# len 8, type 8
  flags permanent
# len 20, type 6
  cacheinfo: prefered forever, valid forever, cstamp 2.31, tstamp 2.31
# len 88, type 0
RT_Netlink::handle_response(lib/RT_Netlink.pm:361):

“len 88,type 0”是IFA_UNSPEC响应中的一个块。

所以我想知道:那些未指定的块是什么,为什么在响应中发送它们?

手册页是这样说的:

                        Attributes
rta_type        value type             description
─────────────────────────────────────────────────────────────
IFA_UNSPEC      -                      unspecified.
IFA_ADDRESS     raw protocol address   interface address
IFA_LOCAL       raw protocol address   local address
IFA_LABEL       asciiz string          name of the interface
IFA_BROADCAST   raw protocol address   broadcast address.
IFA_ANYCAST     raw protocol address   anycast address
IFA_CACHEINFO   struct ifa_cacheinfo   Address information.

答案1

我想我发现了问题:

问题出在响应的解析器中(以 C 源代码为模型)https://github.com/Yourens/rtnetlinkexample/blob/master/if_show.c正如最初的手册页一样非常不太了解事情是如何运作的):

我收到的回复消息的长度如下:76,88,88,88

解析器处理的第一条消息是 324 字节,但消息长度实际上只有 52。因此解析器以某种方式在当前消息结束后进行解析,将下一个消息检测为 type IFA_UNSPEC

具体来说,解析的第一条消息有三次 88 字节IFA_UNSPEC,第二条消息有两次 88 字节IFA_UNSPEC,第三条消息有一次 88 字节IFA_UNSPEC,最后一条消息最终没有。IFA_UNSPEC

当我在调用 IFA_RTA() 之前将 NLMSG_DATA() 的结果截断为 IFA_PAYLOAD() 时,那些IFA_UNSPEC属性神奇地消失了。

我同意,一切听起来都很抽象,没有提供问题中缺少的大量代码。

解析响应的基本 Perl 代码是(原始 C 结构以二进制数据形式存在,由相应的__U_* 例程解包。可能该部分引入了错误,因为示例 C 代码只是前进了一个指针,而在 Perl 中我使用了子字符串):

    while (NLMSG_OK($response, length($response))) {
        my ($len, $h_type, $h_flags, $seq, $pid) = __U_NL_MSG_HDR($response);
        my $ifaddrmsg = NLMSG_DATA($response);
        my $ifaddrmsg_len = IFA_PAYLOAD($response);
        my $rt_attr = IFA_RTA($ifaddrmsg);
        my ($family, $prefixlen, $flags, $scope, $index) =
            __U_IF_ADDR_MSG($ifaddrmsg);

        #...
        while (RTA_OK($rt_attr, length($rt_attr))) {
            my ($rta_len, $rta_type) = U_RT_ATTR($rt_attr);
            my $payload = substr(RTA_DATA($rt_attr), 0,
                                 RTA_PAYLOAD($rt_attr));

            if ($rta_type == IFA_UNSPEC) {
                print "  unspec\n";
                #...
            } else {
                print "  unknown $rta_type/$rta_len ($flags)\n";
            }
            $rt_attr = RTA_NEXT($rt_attr, length($rt_attr));
        }
        $response = NLMSG_NEXT($response, length($response));
    }

最终的修复是将相应的行替换为:

my $rt_attr = IFA_RTA(substr($ifaddrmsg, 0, $ifaddrmsg_len));

最后,这里是一些调试输出的摘要,显示修复前后的差异:

### before fix
  DB<1> r
len 76, h_type 20, h_flags 2, seq 1, pid 7113
index 1, family 2, prefixlen 8
host
# len 8, type 1
# len 8, type 2
# len 7, type 3
# len 8, type 8
# len 20, type 6
# len 88, type 0
# len 88, type 0
# len 88, type 0
len 88, h_type 20, h_flags 2, seq 1, pid 7113
index 2, family 2, prefixlen 23
# len 8, type 1
# len 8, type 2
# len 8, type 4
# len 9, type 3
# len 8, type 8
# len 20, type 6
# len 88, type 0
# len 88, type 0
len 88, h_type 20, h_flags 2, seq 1, pid 7113
index 3, family 2, prefixlen 23
# len 8, type 1
# len 8, type 2
# len 8, type 4
# len 9, type 3
# len 8, type 8
# len 20, type 6
# len 88, type 0
len 88, h_type 20, h_flags 2, seq 1, pid 7113
index 4, family 2, prefixlen 23
# len 8, type 1
# len 8, type 2
# len 8, type 4
# len 9, type 3
# len 8, type 8
# len 20, type 6
  DB<1>

  DB<2> x $ifaddrmsg_len
0  52
  DB<3> x length ($ifaddrmsg)
0  324
  DB<4>

### After fix
  DB<1> r
len 76, h_type 20, h_flags 2, seq 1, pid 7534
index 1, family 2, prefixlen 8
# len 8, type 1
# len 8, type 2
# len 7, type 3
# len 8, type 8
len 88, h_type 20, h_flags 2, seq 1, pid 7534
index 2, family 2, prefixlen 23
# len 8, type 1
# len 8, type 2
# len 8, type 4
# len 9, type 3
# len 8, type 8
len 88, h_type 20, h_flags 2, seq 1, pid 7534
index 3, family 2, prefixlen 23
# len 8, type 1
# len 8, type 2
# len 8, type 4
# len 9, type 3
# len 8, type 8
len 88, h_type 20, h_flags 2, seq 1, pid 7534
index 4, family 2, prefixlen 23
# len 8, type 1
# len 8, type 2
# len 8, type 4
# len 9, type 3
# len 8, type 8

不幸的是,修复似乎吞没了类型 6 (cacheinfo) 数据;也许还有更多的错误。

更新:

我找到了解决方案(我认为),改变:

my $rt_attr = IFA_RTA(substr($ifaddrmsg, 0, $ifaddrmsg_len));

my $rt_attr = substr(IFA_RTA($ifaddrmsg), 0, $ifaddrmsg_len);

asIFA_PAYLOAD似乎是 的长度IFA_RTA,而不是 的长度NLMSG_DATA。整个网络链接“数据包”结构是一个(记录很少的)谜团,甚至可能是“痛苦”。

相关内容