खोज…


सिंटैक्स को पैक करने के लिए मैन्युअल रूप से सी संरचनाओं को परिवर्तित करना

यदि आप कभी भी सी बाइनरी एपीआई के पर्ल कोड से, syscall , ioctl , या fcntl फ़ंक्शन के माध्यम से काम कर रहे हैं, तो आपको यह जानना होगा कि C संगत तरीके से मेमोरी कैसे fcntl

उदाहरण के लिए, यदि आप कभी किसी ऐसे कार्य से निपट रहे हैं, जो एक 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 बाइट्स लगते हैं। इसलिए 64 बिट पर हस्ताक्षर किए। और मैं 64 बिट प्रोसेसर पर हूं। =)

Perldoc pack कहता है

            q  A signed quad (64-bit) value.

तो एक टाइमस्पेस पैक करने के लिए:

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

और एक टाइमपैक अनपैक करने के लिए:

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 हेडर का निर्माण

कभी-कभी आपको पर्ल से सी डेटा प्रकारों के संदर्भ में परिभाषित संरचनाओं से निपटना पड़ता है। ऐसा ही एक आवेदन कच्चे नेटवर्क पैकेट का निर्माण है, यदि आप नियमित सॉकेट एपीआई की पेशकश की तुलना में कुछ कट्टरपंथी करना चाहते हैं। यह सिर्फ क्या pack() और unpack() के लिए है।

IP हेडर का अनिवार्य हिस्सा 20 ऑक्टेट (AKA "बाइट्स") लंबा है। जैसा कि आप इस लिंक के पीछे देख सकते हैं, स्रोत और गंतव्य आईपी पता हेडर में अंतिम दो 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 के रूप में कम हो सकता है। इससे भी बदतर, यह सीपीयू की समाप्ति पर निर्भर करता है, इसलिए यह कुछ सीपीयू पर काम करेगा और दूसरों पर विफल होगा। चलो pack() की कोशिश करो pack() :

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

टेम्पलेट पहले H2 , 2-वर्ण हेक्स स्ट्रिंग, उच्च nybble को पहले निर्दिष्ट करता है। पैक करने के लिए संबंधित तर्क "45" है- 4 फैलाव, लंबाई 5. अगला टेम्पलेट B8 , एक 8-बिट बिट स्ट्रिंग है, प्रत्येक बाइट के अंदर अवरोही क्रम । हमें नीबबल (4 बिट्स) की तुलना में छोटे हिस्से को नीचे की ओर ले जाने के लिए बिट स्ट्रिंग्स का उपयोग करने की आवश्यकता है, इसलिए sprintf() का उपयोग $dscp से 6 बिट्स और $ecn से 2 से ऐसे बिट स्ट्रिंग के निर्माण के लिए किया जाता है। पिछले एक n , नेटवर्क बाइट ऑर्डर में एक अहस्ताक्षरित 16-बिट मान , यानी हमेशा बड़ा-एंडियन कोई फर्क नहीं पड़ता कि आपके सीपीयू का मूल पूर्णांक प्रारूप क्या है, और यह $length से भरा है।

यह हेडर के पहले 32 बिट्स हैं। बाकी समान बनाया जा सकता है:

खाका बहस टिप्पणियों
n $id
B16 sprintf("%03b%013b", $flags, $frag_off) DSCP / ECN के रूप में भी
C2 $ttl, $proto दो लगातार अहस्ताक्षरित ओकटेट
n 0 / $checksum x का उपयोग शून्य बाइट डालने के लिए किया जा सकता है, लेकिन n हमें एक तर्क निर्दिष्ट करना चाहिए जिससे हमें एक चेकसम की गणना करने के लिए चुनना चाहिए
N2 $src_ip, $dst_ip a4a4 का उपयोग दो 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