بطور کلی فرآیند چک سام اینطوری انجام ميشه: در بخشی که میخوان چک سام رو حساب کنن؛ تمام داده ها به ترتیب خاصی با هم جمع میشن و در نهایت نتیجه این جمع با یک روال تعریف شده، در محل چک سام نوشته میشه.
برای تعریف چک سام اینترنتی، در اسناد اینطور گفته شده:
The checksum field is the 16 bit one’s complement of the one’s complement sum of all 16 bit words.
یعنی “چک سام نتیجه 16 بیتی و به فرم مکمل یک (one’s complement) جمع همه داده ها ( یی که قراره در محاسبات چک سام وجود داشته باشند) به صورت 16 بیتی”
توضیح ساده ترش اینه: بایت هایی که باید چک سامشون محاسبه بشه رو به صورت 16 بیتی (دو بایتی) جدا می کنیم، این دو بایتی ها رو به فرم مکمل یک جمع میکنیم؛ نتیجه جمع رو مکمل یک مي گیریم و در فیلد checksum می نویسیم. به عنوان مثال اگر نتیجه جمع شد +11 ؛ در بخش مربوطه -11 مینویسم (اعداد به فرم مکمل یک خواهند بود).
حالا چند تا نکته :
1 2 3 4 5 6 7 | 0111 (+7) + 1010 (-5) //+5==0101 => -5== 1010 ------------- 0001 |
همونطور که میبینیم اینجا عدد 1 رو به عنوان رقم نقلي داریم؛ لذا با نتیجه اصلي که “0001” هست جمع میشه که میشه 0010 یا 2 که اين، نتیجه ی درست محاسبه هست.
1 | 00,01,f2,03,f4,f5,f6,f7 |
| بایت به بایت | دو بایتی با ترتیب نرمال | دو بایتی با ترتیب معکوس | |
Byte 0,1 | 00 | 01 | 0001 | 0100 |
Byte 2,3 | F2 | 03 | F203 | 03F2 |
Byte 4,5 | F4 | F5 | F4F5 | F5F4 |
Byte 6,7 | F6 | F7 | F6F7 | F7F6 |
جمع اول | 2DC | 1F0 | 2DDF0 | 1F2DC |
نقلی | 1 | 2 | 2 | 1 |
جمع دوم | DD | F2 | DDF2 | F2DD |
جابجایی نهایی | DDF2 | DDF2 | DDF2 |
حالت اول اینه که بایت های فرد با هم و بایت های زوج با هم جمع میشن. حالت دوم اینه که بایت ها به فرم Big Endian با هم جمع شدن(حالت نرمال شبکه) و حالت سوم اینه که ابتدا بايت هاي درون داده 16 بیتی، جابجا (swap) شدن (Little Endian).
اگر محاسبات چک سام در نرم افزار (مثلا در نرم افزار میکروکنترلر) پیاده سازی بشه،؛ سرعت انجام اون بسیار اهمیت داره. در سند RFC 1071 و آپدیت هاي اون، بعضی الگوریتم های محاسبه چک سام با توجه به خصوصیات محاسبه به فرم مکمل یک پرداخته و بررسی شده.
يادآوري كنيم كه چک سام در پروتکل IP، تنها روی بایت های واقع در هدر انجام میشه.
قبل از اینکه برگردیم سراغ پروتکل IP، نیاز هست که یه تابع بنویسیم برای محاسبه چک سام. از اونجایی که داده هایی که توسط لایه دوم دریافت شده ن، درون یک آرایه به فرمت uint8_t ذخیره شدن، تابعی که مینویسیم دو آرگومان داره؛ اولی یک اشاره گر به ابتدای داده ها و دومی، طول داده ها رو میگیره و یک نتیجه 16 بیتی كه همون چك سام هست، بما برمی گردونه.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | uint16_t IP_CalcCheckSum( uint8_t* data, uint16_t len) { uint32_t res = 0; uint16_t * ptr = ( uint16_t* ) data; while ( len > 1 ) { res += *ptr; ptr ++ ; len -= 2 ; } if ( len > 0 ) { res += * ( uint8_t* ) ptr; } while ( res > 0xffff ) { res = ( res >> 16 ) + ( res & 0xFFFF ) ; } return ~ (( uint16_t ) res ) ; // or XOR with 0xFFFF } |
چون در عملیات جمع، ممکنه رقم نقلي (carry) داشته باشه؛ برای ذخیره نتیجه، یک متغیر 32 بیتی در نظر گرفته شده. در ابتداي تابع، بایت ها رو دوتا دوتا با هم جمع می کنیم، اگر تعداد بايت ها فرد باشه؛ در نهایت یک بایت اضافه ميماند، اون رو هم جمع میکنیم. از اونجاییکه عملیات جمع ممكنه رقم نقلي داشته باشه و نتیجه بیشتر از 16 بیت بشه، مقدار نقلی رو با نتیجه اصلی، جمع میکنیم ( برای فهم ساده تر الگوریتم؛ کد به صورت بالا نوشته شده). در نهایت دو بایت نتیجه جمع، به صورت بیتی NOT شده ن (تا مكمل يك جواب جمع بدست بياد) و اينو به عنوان نتیجه نهایی چك سام برمیگردونیم.
در سمت گیرنده، طبق الزام استاندارد، بایستي چک سام بررسي بشه (فرستنده هم ملزم به ارسال اون هست) در حاليكه در بسياري از كدهاي داخل نت، چك سام محاسبه يا بررسي نشده! در اینجا (یعنی سمت گیرنده) دو کار میتونیم انجام بدیم، راه ساده تر اینه که اینجا هم چک سام رو حساب کنیم و ببینیم که آیا یکی هستند یا نه!
راه دوم که بنظر سریعتر هست؛ اینه که در سمت گیرنده فیلد چک سام رو صفر نکنیم و محاسبات رو انجام بدیم، در نتیجه جواب نهایی، همواره باید صفر باشه. چرا؟ یادمونه که ما در آخرین مرحله از محاسبه چک سام؛ مکمل یک چک سام رو ثبت می کردیم؛ مثلا اگر چک سام میشد +6 ؛ ما مقدار -6 رو ذخیره می کردیم ، در نتیجه در سمت گیرنده، اگر بدون صفر کردن چک سام، محاسبات رو در سمت گیرنده انجام بدیم، باید به نتیجه صفر برسیم.
نویسنده شو !
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.