Perl Language
パックして解凍する
サーチ…
C構造体をパック構文に手動で変換する
syscall
、 ioctl
、または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
);