TCP/IPのIP, ICMP,UDP, TCP のヘッダにはそれぞれ、チェックサムがある。 TCPのヘッダはまだためしていないので、IP,ICMP,UDPのチェックサムの 計算方法を紹介する。
どのチェックサムも、ネットワークバイトオーダーの16ビットの数値を
読み出して1の補数和の1の補数をチェックサムとする点は共通だ。
チェックサムを計算する領域は、IPヘッダ、ICMPデータはそれぞれ、そのもの
の範囲だが、UDPではUDPの領域に加えて、source, destinationの
IPアドレス、プロトコル番号、データ長からなる擬似ヘッダを含める。
16bitの数値Aの1の補数は、0xffff-Aと定義されるが、bit反転でもある。
では、1の補数和とは1の補数の和のことなのかというと、そうではない。
数学的な背景はよくわからないが、1の補数和は普通の足し算の結果の
MSB側の繰り上がりをLSBに加算することで計算できるらしい。
この足し算は、面白いことに、バイトオーダーに関して特別な配慮を必要しない。
なぜならば、桁あふれが巡回するからだ。
たとえばMSBの桁上がりが、LSBへ加算されることは、逆オーダーの下位8ビットから
上位8ビットへの桁上がりを実行していることになる。
計算のテクニックとして、このMSBの桁上がりはまとめてあとから加算することが できる。そのときに桁あふれが発生したら、それもまた加算する。
最後に1の補数和の結果の1の補数に変換してチェックサムとする。 リトルエンディアンのシステムでは、ここでバイトオーダを変換して、 それぞれのヘッダのチェックサムフィールドの値にする。
では、実際のコードをみてみよう。
unsigned short checksum(unsigned short *buf, int size) { unsigned long sum = 0; while (size > 1) { sum += *buf++; size -= 2; } if (size) sum += *(u_int8_t *)buf; sum = (sum & 0xffff) + (sum >> 16); /* add overflow counts */ sum = (sum & 0xffff) + (sum >> 16); /* once again */ return ~sum; }見てのとおり、とくに特殊なテクニックは使用していない。
ip->ip_sum = 0; ip->ip_sum = checksum((unsigned short *ip)ip, 4 * ip->ip_hl);
UDPのチェックサムは、UDPヘッダとUDPデータに加えて、
source, destinationのIPアドレス、プロトコル番号、データ長からなる
擬似ヘッダをチェックサムの計算に含めなければならない。
offset | +0 | +1 | +2 | +3 |
+0 | src ip address | |||
+4 | dst ip address | |||
+8 | 0:zero | protocol | UDP data length |
unsigned short udpchecksum(struct ip *ip, struct udphdr *udp) { unsigned long sum; u_int16_t *s; int size; u_int32_t addr; sum = 0; addr = ip->ip_src; sum += addr >> 16; sum += addr & 0xffff; addr = ip->ip_dst; sum += addr >> 16; sum += addr & 0xffff; sum += ip->ip_p << 8; /* endian swap */ size = udp->uh_ulen; sum += size; size = ntohs(size); s = (u_int16_t *)udp; while (size > 1) { sum += *s; s++; size -= 2; } if (size) sum += *(u_int8_t *)s; sum = (sum & 0xffff) + (sum >> 16); /* add overflow counts */ sum = (sum & 0xffff) + (sum >> 16); /* once again */ return ~sum; }これは、引数にIPヘッダとUDPヘッダへのポインタを与えて、UDPのチェックサムを 計算する関数だ。
FENIX/ MEMBERS/ thomas's HOME/ memo/ ip/checksum
佐藤益弘 thomas@fenix.ne.jp