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バイトです。したがって、パーサーは現在のメッセージが終了した後に何らかの方法で解析し、次のメッセージをタイプとして検出しますIFA_UNSPEC
。
具体的には、解析された最初のメッセージは3回88バイトIFA_UNSPEC
、2番目のメッセージは88バイト、3番目のIFA_UNSPEC
メッセージは1回88バイト、最後のメッセージIFA_UNSPEC
はなしで終わります。IFA_UNSPEC
。
IFA_RTA() を呼び出す前に NLMSG_DATA() の結果を IFA_PAYLOAD() に切り捨てると、そのIFA_UNSPEC
属性は魔法のように消えます。
同意します。すべてが非常に抽象的に聞こえ、質問に欠けている多くのコードを提供するわけではありません。
応答を解析するデフォルトのPerlコードは次のとおりです(生のC構造は対応する__U_
*ルーチンによって解凍されたバイナリデータとして存在します。部分がバグを導入した可能性があります)。 :
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);
の長さではなくの長さIFA_PAYLOAD
のようです。ネットワークリンク全体の「パケット」構造は(誤って文書化された)謎であり、おそらく「痛み」である可能性があります。IFA_RTA
NLMSG_DATA