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