넷링크 응답 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, 두 번째 메시지는 88바이트이고 IFA_UNSPEC, 세 번째 메시지는 1번 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);

의 길이 가 아니라 의 길이 IFA_PAYLOAD인 것 같습니다 . 전체 네트워크 링크 "패킷" 구조는 (잘못 문서화된) 미스터리이며 어쩌면 "고통"일 수도 있습니다.IFA_RTANLMSG_DATA

관련 정보