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
。整个网络链接“数据包”结构是一个(记录很少的)谜团,甚至可能是“痛苦”。