Dork's port

Checksum 계산하기[소스 코드] (C++) 본문

Develop

Checksum 계산하기[소스 코드] (C++)

Dork94 2017. 9. 22. 11:59

네트워크에 관련된 프로그래밍을 하다 보면  Checksum을 직접 계산해서 값을 넣어 줘야 하는 경우가 있다.

때문에, 오늘은 그와 관련된 포스팅을 하려 한다.

우선, Checksum을 계산하는 방법에 대해서는 여러 사이트에서 자세히 서술 하고 있으므로 개념적인 내용은 다루지 않을 것이며, 그에 대한 나의 소스코드를 공유하는 것이 목적이다.


#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <iostream>
#include <cstring>

#pragma pack(push,1)

struct Pseudoheader{
    uint32_t srcIP;
    uint32_t destIP;
    uint8_t reserved=0;
    uint8_t protocol;
    uint16_t TCPLen;
};

#pragma pack(pop)
#define CARRY 65536

uint16_t calTCPChecksum(uint8_t *data,int dataLen)
{
    //make Pseudo Header
    struct Pseudoheader pseudoheader; //saved by network byte order

    //init Pseudoheader
    struct iphdr *iph=(struct iphdr*)data;
    struct tcphdr *tcph=(struct tcphdr*)(data+iph->ihl*4);

    memcpy(&pseudoheader.srcIP,&iph->saddr,sizeof(pseudoheader.srcIP));
    memcpy(&pseudoheader.destIP,&iph->daddr,sizeof(pseudoheader.destIP));
    pseudoheader.protocol=iph->protocol;
    pseudoheader.TCPLen=htons(dataLen-(iph->ihl*4));

    //Cal pseudoChecksum
    uint16_t pseudoResult=calculate((uint16_t*)&pseudoheader,sizeof(pseudoheader));

    //Cal TCP Segement Checksum
    tcph->check=0; //set Checksum field 0
    uint16_t tcpHeaderResult=calculate((uint16_t*)tcph,ntohs(pseudoheader.TCPLen));


    uint16_t checksum;
    int tempCheck;

    if((tempCheck=pseudoResult+tcpHeaderResult)>CARRY)
        checksum=(tempCheck-CARRY) +1;
    else
        checksum=tempCheck;


    checksum=ntohs(checksum^0xffff); //xor checksum
    tcph->check=checksum;

    return checksum;
}

uint16_t calICMPChecksum(uint8_t *data,int dataLen)
{

    //init Pseudoheader
    struct iphdr *iph=(struct iphdr*)data;
    struct icmphdr *icmph=(struct icmphdr*)(data+iph->ihl*4);

    //Cal ICMP Segement Checksum
    icmph->checksum=0; //set Checksum field 0
    uint16_t tcpHeaderResult=calculate((uint16_t*)icmph,(dataLen-(iph->ihl*4)));


    uint16_t checksum=tcpHeaderResult;

    checksum=ntohs(checksum^0xffff); //xor checksum
    icmph->checksum=checksum;

    return checksum;
}

uint16_t calUDPChecksum(uint8_t *data, int dataLen)
{
    //make Pseudo Header
    struct Pseudoheader pseudoheader; //save to network byte order

    //init Pseudoheader
    struct iphdr *iph=(struct iphdr*)data;
    struct udphdr *udph=(struct udphdr*)(data+iph->ihl*4);
    memcpy(&pseudoheader.srcIP,&iph->saddr,sizeof(pseudoheader.srcIP));
    memcpy(&pseudoheader.destIP,&iph->daddr,sizeof(pseudoheader.destIP));
    pseudoheader.protocol=iph->protocol;
    pseudoheader.TCPLen=htons(dataLen-(iph->ihl*4));

    //Cal pseudoChecksum
    uint16_t pseudoResult=calculate((uint16_t*)&pseudoheader,sizeof(pseudoheader));

    //Cal TCP Segement Checksum
    udph->check=0; //set Checksum field 0
    uint16_t tcpHeaderResult=calculate((uint16_t*)udph,ntohs(pseudoheader.TCPLen));


    uint16_t checksum;
    int tempCheck;

    if((tempCheck=pseudoResult+tcpHeaderResult)>CARRY)
        checksum=(tempCheck-CARRY) +1;
    else
        checksum=tempCheck;


    checksum=ntohs(checksum^0xffff); //xor checksum
    udph->check=checksum;

    return checksum;
}

uint16_t calculate(uint16_t* data, int dataLen)
{
    uint16_t result;
    int tempChecksum=0;
    int length;
    bool flag=false;
    if((dataLen%2)==0)
        length=dataLen/2;
    else
    {
        length=(dataLen/2)+1;
        flag=true;
    }

    for (int i = 0; i < length; ++i) // cal 2byte unit
    {
       

        if(i==length-1&&flag) //last num is odd num
            tempChecksum+=ntohs(data[i]&0x00ff);
        else
            tempChecksum+=ntohs(data[i]);

        if(tempChecksum>CARRY)
                tempChecksum=(tempChecksum-CARRY)+1;

    }

    result=tempChecksum;
    return result;
}

uint16_t calIPChecksum(uint8_t* data)
{
    struct iphdr* iph=(struct iphdr*)data;
    iph->check=0;//set Checksum field 0

    uint16_t checksum=calculate((uint16_t*)iph,iph->ihl*4);
    iph->check=htons(checksum^0xffff);//xor checksum

    return checksum;
}


위의 코드는 linux기반 코드이며, 관련 구조체를 제외하고는 동작 방식은 동일하다.


다만 내가 작성한 로직이 최선의 로직은 아니므로, 위의 코드를 수정하거나 위의 코드는 이해 및 예제 사용 정도로 추천한다.


위의 파일은 원래 작성한 checksum.h 와 checksum.cpp 파일을 게시를 위해 합친 것으로 원본 소스코드는 아래의 명령어를 통해 다운 받을 수 있다.


# git clone https://github.com/JangHanbin/NetworkLib



만약 제대로 다운로드가 되지 않는 다면 git패키지를 설치해주길 바란다. 내가 사용하는 kali linux의 명령어는 아래와 같다.


# apt-get install git

운영체제마다 명령어가 상이할 수 있으므로, 확인후 설치를 해야한다.


위의 소스코드의 사용 방법은 아래와 같으며 자동으로 계산된 checksum값을 패킷의 checksum field에 삽입해 준다.


calIPChecksum(<ip헤더의 시작 포인터>)

calTCPChecksum(<ip헤더의 시작 포인터>,<ip헤더부터 패킷끝 까지의 길이>)

calICMPChecksum(<ip헤더의 시작 포인터>,<ip헤더부터 패킷끝 까지의 길이>)

calUDPChecksum(<ip헤더의 시작 포인터>,<ip헤더부터 패킷끝 까지의 길이>)


위의 소스코드에서 icmp관련 구조체가 안드로이드 컴파일시 오류가 발생하여 따로 선언해 주었으므로, 관련 구조체를 삭제하고 헤더를 추가하여 사용하여도 무방하다.


Comments