Szukaj…


Ręczne przekształcanie struktur C w składnię paczek

Jeśli kiedykolwiek masz do czynienia z C Binary API z Perla Code, za pomocą funkcji syscall , ioctl lub fcntl , musisz wiedzieć, jak konstruować pamięć w sposób zgodny z C.

Na przykład, jeśli kiedykolwiek miałeś do czynienia z jakąś funkcją, która oczekiwała określonego timespec , zajrzyj do /usr/include/time.h i znajdź:

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

Wykonujesz taniec z cpp aby dowiedzieć się, co to naprawdę oznacza:

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

Więc to jest (podpisany) 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

I zajmuje 8 bajtów. Więc 64bit podpisał int. A ja korzystam z procesora 64-bitowego. =)

Mówi pack Perldoc

            q  A signed quad (64-bit) value.

Aby spakować przedział czasu:

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

I rozpakować przedział czasu:

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

Teraz możesz po prostu użyć tych funkcji.

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 ));

Konstruowanie nagłówka IPv4

Czasami masz do czynienia ze strukturami zdefiniowanymi pod względem typów danych C z Perla. Jedną z takich aplikacji jest tworzenie surowych pakietów sieciowych, na wypadek, gdybyś chciał zrobić coś bardziej wyszukanego niż to, co oferuje zwykłe API gniazd. Właśnie po to jest pack() (i oczywiście unpack() ).

Obowiązkowa część nagłówka IP ma długość 20 oktetów („bajtów” AKA). Jak widać za tym linkiem, źródłowy i docelowy adres IP tworzą dwie ostatnie 32-bitowe wartości w nagłówku. Wśród innych pól są niektóre z 16 bitami, niektóre z 8 bitami i kilka mniejszych fragmentów od 2 do 13 bitów.

Zakładając, że w nagłówku mamy następujące zmienne:

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

Zauważ, że brakuje trzech pól z nagłówka:

  • Wersja ma zawsze 4 (w końcu to IPv4)
  • W naszym przykładzie IHL wynosi 5, ponieważ nie mamy pola opcji ; długość jest określona w jednostkach 4 oktetów, więc 20 oktetów daje długość 5.
  • Suma kontrolna może być pozostawiona na 0. Właściwie musielibyśmy ją obliczyć, ale kod do zrobienia tego nie dotyczy nas tutaj.

Możemy spróbować użyć operacji bitowych do skonstruowania np. Pierwszych 32 bitów:

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

Takie podejście działa tylko do wielkości liczby całkowitej, która zwykle wynosi 64 bity, ale może być tak mała jak 32. Gorzej, zależy to od endianizmu procesora, więc będzie działać na niektórych procesorach, a na innych nie. Spróbujmy pack() :

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

Szablon najpierw określa H2 , 2-znakowy ciąg szesnastkowy, najpierw wysoki węzeł . Odpowiednim argumentem do spakowania jest „45” - wersja 4, długość 5. Następny szablon to B8 , 8-bitowy ciąg bitów, malejący porządek bitów w każdym bajcie . Musimy użyć ciągów bitów do sterowania układem do kawałków mniejszych niż nybble (4 bity), więc sprintf() jest używany do konstruowania takiego łańcucha bitów z 6 bitów z $dscp i 2 z $ecn . Ostatnim jest n , 16-bitowa wartość bez znaku w sieciowej kolejności bajtów , tj. Zawsze big-endian, bez względu na format macierzystej liczby całkowitej twojego procesora, i jest wypełniony od $length .

To pierwsze 32 bity nagłówka. Resztę można zbudować podobnie:

Szablon Argument Uwagi
n $id
B16 sprintf("%03b%013b", $flags, $frag_off) Taki sam jak DSCP / ECN
C2 $ttl, $proto Dwa kolejne nieokreślone oktety
n $checksum 0 / $checksum x może być użyte do wstawienia bajtu zerowego, ale n pozwala nam określić argument, jeśli zdecydujemy się obliczyć sumę kontrolną
N2 $src_ip, $dst_ip użyj a4a4 aby spakować wynik dwóch wywołań gethostbyname() tak jak jest już w Network Byte Order!

Zatem pełne wywołanie spakowania nagłówka IPv4 byłoby:

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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow