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
, 두 번째 메시지는 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_RTA
NLMSG_DATA