수색…


수동으로 C 구조체를 팩 구문으로 변환

syscall , ioctl 또는 fcntl 함수를 통해 Perl 코드에서 C 바이너리 API를 처리하는 경우 C 호환 방식으로 메모리를 생성하는 방법을 알아야합니다.

예를 들어, timespec 을 예상 한 일부 기능을 다루는 경우 /usr/include/time.h 를보고 다음을 찾으십시오.

struct timespec
{
    __time_t tv_sec;            /* Seconds.  */
    __syscall_slong_t tv_nsec;  /* Nanoseconds.  */
};

당신은 cpp 와 춤을 추면서 그것이 정말로 의미하는 것을 발견합니다 :

cpp -E /usr/include/time.h -o /dev/stdout | grep __time_t
# typedef long int __time_t;
cpp -E /usr/include/time.h -o /dev/stdout | grep __syscall_slong_t
# typedef long int __syscall_slong_t

그래서 그것의 (서명 된) int

echo 'void main(){ printf("%#lx\n", sizeof(__syscall_slong_t)); }' | 
  gcc -x c -include stdio.h -include time.h - -o /tmp/a.out && /tmp/a.out
# 0x8

그리고 8 바이트 걸립니다. 그래서 64bit int에 서명했습니다. 그리고 저는 64 비트 프로세서를 사용하고 있습니다. =)

Perldoc pack 은 말한다

            q  A signed quad (64-bit) value.

그래서 timespec을 포장하려면 :

sub packtime {
    my ( $config ) = @_; 
    return pack 'qq', @{$config}{qw( tv_sec tv_nsec )};
}

timespec의 압축을 풀려면 다음을 수행하십시오.

sub unpacktime {
   my ( $buf ) = @_;
   my $out = {};
   @{$out}{qw( tv_sec tv_nsec )} = unpack 'qq', $buf;
   return $out;
}

이제 그 기능을 대신 사용할 수 있습니다.

my $timespec = packtime({ tv_sec => 0, tv_nsec => 0 });
syscall(  ..., $timespec ); # some syscall that reads timespec

later ...
syscall( ..., $timespec ); # some syscall that writes timespec
print Dumper( unpacktime( $timespec ));

IPv4 헤더 생성

때로는 Perl의 C 데이터 유형에 따라 정의 된 구조를 다루어야합니다. 그러한 응용 프로그램 중 하나는 일반 소켓 API가 제공해야하는 것보다 더 멋진 기능을 수행하려는 경우를 대비하여 원시 네트워크 패킷을 만드는 것입니다. 이것은 pack() (그리고 unpack() 물론)이있는 곳입니다.

IP 헤더의 필수 부분 은 20 옥텟 (AKA "bytes")입니다. 이 링크의 뒷부분에서 볼 수 있듯이 원본 및 대상 IP 주소가 머리글의 마지막 두 32 비트 값을 구성합니다. 다른 필드 중에는 16 비트, 8 비트 및 2 비트와 13 비트 사이의 작은 덩어리가 있습니다.

헤더에 포함 할 다음 변수가 있다고 가정합니다.

my ($dscp, $ecn, $length,
    $id, $flags, $frag_off,
    $ttl, $proto,
    $src_ip,
    $dst_ip);

헤더의 세 필드가 누락되었습니다.

  • 버전은 항상 4입니다 (결국 IPv4입니다).
  • IHL은 옵션 필드가 없으므로 예제에서는 5입니다. 길이는 4 옥텟 단위로 지정되므로 20 옥텟은 길이가 5입니다.
  • 체크섬은 0으로 남겨 둘 수 있습니다. 실제로는 계산해야하지만 코드는 여기에 관련되지 않습니다.

우리는 처음 32 비트를 구성하기 위해 비트 연산을 시도하고 사용할 수 있습니다.

my $hdr = 4 << 28 | 5 << 24 | $dscp << 18 | $ecn << 16 | $length;

이 접근 방식은 정수의 크기까지만 작동합니다. 일반적으로 64 비트이지만 32로 낮을 수 있습니다. 더 나쁜 것은 CPU의 엔디안 에 따라 다르므로 일부 CPU에서 작동하고 다른 CPU에서는 작동하지 않습니다. pack() 해보자.

my $hdr = pack('H2B8n', '45', sprintf("%06b%02b", $dscp, $ecn), $length);

템플릿은 먼저 2 문자 16 진수 문자열 인 H2 먼저 지정합니다. pack에 해당하는 인수는 "45"- 버전 4, 길이 5입니다. 다음 템플리트는 B8 , 8 비트 비트 문자열이며 각 바이트 내에 내림차순으로 비트 순서가 있습니다 . 우리는 nybble (4 비트)보다 작은 청크로 레이아웃을 제어하기 위해 비트 문자열을 사용해야하므로 sprintf()$dscp 6 비트와 $ecn 2로부터 비트 문자열을 구성하는 데 사용됩니다. 마지막 것은 n 네트워크 바이트 순서부호없는 16 비트 값)입니다 . 즉, CPU의 기본 정수 형식이 무엇이든 관계없이 항상 big-endian이며 $length 부터 채워 $length .

헤더의 처음 32 비트입니다. 나머지도 비슷하게 만들 수 있습니다.

주형 논의 비고
n $id
B16 sprintf("%03b%013b", $flags, $frag_off) DSCP / ECN과 동일
C2 $ttl, $proto 두 개의 연속 된 부호없는 옥텟
n 0 / $checksum x 는 null 바이트를 삽입하는 데 사용될 수 있지만 n 은 체크섬을 계산하도록 선택하면 인수를 지정할 수 있습니다.
N2 $src_ip, $dst_ip a4a4 를 사용하여 두 개의 gethostbyname() 호출 결과를 네트워크 바이트 순서대로 a4a4 합니다!

따라서 IPv4 헤더를 압축하는 완전한 호출은 다음과 같습니다.

my $hdr = pack('H2B8n2B16C2nN2',
    '45', sprintf("%06b%02b", $dscp, $ecn), $length,
    $id, sprintf("%03b%013b", $flags, $frag_off),
    $ttl, $proto, 0,
    $src_ip, $dst_ip
);


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow