ネットリンク応答RTM_GETADDRのIFA_UNSPECペイロードは何ですか?

ネットリンク応答RTM_GETADDRのIFA_UNSPECペイロードは何ですか?

RTM_GETADDRfor familyを使用してLinux rtnetlink(7)インターフェースを照会するプログラムを作成しましたAF_INET。応答の解析中に、応答の最大部分がIFA_UNSPEC88バイト長の型レコードであるように見えました(他の部分は通常長さが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_RTANLMSG_DATA

関連情報