サーチ…


C構造体をパック構文に手動で変換する

syscallioctl 、またはfcntl関数を使用してPerlコードからCバイナリAPIを処理する場合は、Cコンパチブルな方法でメモリを構築する方法を知る必要があります。

たとえば、 timespecが必要な関数を扱っていたなら、/ /usr/include/time.h / /usr/include/time.h / /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に署名しました。そして、私は64Bitプロセッサを搭載しています。 =)

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データ型の観点から定義された構造に対処する必要があります。そのようなアプリケーションの1つは、通常のソケットAPIが提供しなければならないものよりも魅力的なことをしたい場合に備えて、生のネットワークパケットを作成することです。これはちょうどpack() (とunpack()ももちろん)のためのものです。

IPヘッダーの必須部分は、 20オクテット(AKA "バイト")です。このリンクの背後にあるように、送信元と宛先IPアドレスはヘッダーの最後の2つの32ビット値を構成します。他のフィールドの中には、16ビットのもの、8ビットのもの、2〜13ビットの小さなチャンクがあります。

ヘッダーに含めるために次の変数があると仮定します。

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

ヘッダーの3つのフィールドがないことに注意してください。

  • バージョンは常に4です(結局IPv4です)
  • オプションフィールドがないので、IHLはこの例では5です。長さは4オクテット単位で指定され、20オクテットは長さ5を与えます。
  • チェックサムは0のままにすることができます。実際には計算する必要がありますが、これを行うコードはここでは関係ありません。

たとえば、最初の32ビットを構築するためにビット操作を試してみることができます:

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

このアプローチは整数のサイズまでしか動作しませんが、通常は64ビットですが、32にもなる可能性があります。さらに、CPUのエンディアンに依存するため、一部のCPUでは動作し、他のプロセッサでは失敗します。 pack()試してみましょう:

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

テンプレートは最初に、 2文字の16進数の文字列 H2最初にハイ・ニブルで指定します。 packに対応する引数は "45" - バージョン4、長さ5です。次のテンプレートはB8です.8ビットのビットストリングで、各バイト内で降順のビット順序です。ビットストリングを使用してニブル(4ビット)より小さいチャンクにレイアウトを制御する必要があるため、 sprintf()を使用して$dscp 6ビットと$ecn 2からそのようなビット列を構築します。最後のものはnで、 ネットワークバイトオーダーの符号なし16ビット値です 。つまり、CPUのネイティブ整数フォーマットに関係なく常にビッグエンディアンで、 $lengthから埋められます。

ヘッダの最初の32ビットです。残りは同様に構築することができます:

テンプレート引数備考
n $id
B16 sprintf("%03b%013b", $flags, $frag_off) DSCP / ECNと同じ
C2 $ttl, $proto 2つの連続する符号なしオクテット
n 0 / $checksum xはnullバイトを挿入するために使用できますが、チェックサムを計算するためにn指定すると引数を指定できます
N2 $src_ip, $dst_ip a4a4を使って2つのgethostbyname()呼び出しの結果をネットワークバイトオーダーにパックしています!

したがって、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