更新分段数据包中的UDP校验和

时间:2017-04-25 01:50:13

标签: networking udp checksum nat ip-fragmentation

我正在构建一个网络设备。我需要支持NAT和IP数据包碎片。当我更改UDP数据包的源或目标地址时,我必须更正UDP校验和(以及IP校验和,但这是微不足道的)。当数据包碎片化时,我必须收集所有碎片以重新计算校验和。我知道旧地址和新地址。我想:

  1. 取消否定校验和
  2. 减去旧地址
  3. 添加新地址
  4. 重新减少总和并否定
  5. 此过程并不总是有效。有没有办法更新校验和而不必从头开始重新计算?

    我试过了:

    long CalcCheckSumAdd(unsigned char *pbHeader, int iSize, long lInitial){
    
        long lSum = lInitial;
    
        while (iSize > 1){
    
            lSum += *((unsigned short*)pbHeader);
    
            pbHeader += 2;
    
            iSize -= 2;
    
        }
    
        if (iSize > 0) lSum += *pbHeader;
    
        return lSum;
    
    }
    
    long CalcCheckSumSubract(unsigned char *pbHeader, int iSize, long lInitial){
    
        long lSum = lInitial;
    
        while (iSize > 1){
    
            lSum -= *((unsigned short*)pbHeader);
    
            pbHeader += 2;
    
            iSize -= 2;
    
        }
    
        if (iSize > 0) lSum -= *pbHeader;
    
        return lSum;
    
    }
    
    unsigned short CalcCheckSumFinish(long lSum){
    
        while (lSum >> 16){
    
            lSum = (lSum & 0xFFFF) + (lSum >> 16);
    
        }
    
        return (unsigned short)(~lSum);
    
    }
    
    long CalcCheckSumUnfinish(unsigned short usSum){
    
        // Can't totally undo lossy finish logic
    
        return ~usSum;
    
    }
    
    unsigned short CalcCheckSumUpdateAddress(unsigned short usOldSum, unsigned long ulOldAddress, unsigned long ulNewAddress){
    
        long lSumFixed = CalcCheckSumUnfinish(usOldSum);
    
        lSumFixed = CalcCheckSumSubract((unsigned char*)&ulOldAddress,sizeof(ulOldAddress),lSumFixed);
    
        lSumFixed = CalcCheckSumAdd((unsigned char*)&ulNewAddress,sizeof(ulNewAddress),lSumFixed);
    
        return CalcCheckSumFinish(lSumFixed);
    
    }
    

    谢谢!

    编辑:在

    下添加了单元测试代码
    #include <time.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    long CalcCheckSumAdd(unsigned char *pbHeader, int iSize, long lInitial){
    
        long lSum = lInitial;
    
        while (iSize > 1){
    
            lSum += *((unsigned short*)pbHeader);
    
            pbHeader += 2;
    
            iSize -= 2;
    
        }
    
        if (iSize > 0) lSum += *pbHeader;
    
        return lSum;
    
    }
    
    unsigned short CalcCheckSumFinish(long lSum){
    
        while (lSum >> 16){
    
            lSum = (lSum & 0xFFFF) + (lSum >> 16);
    
        }
    
        return (unsigned short)(~lSum);
    
    }
    
    void Randomize(unsigned char *pucPacket, unsigned long ulSize){
    
        for (unsigned long ulByte = 0; ulByte < ulSize; ulByte++){
    
            pucPacket[ulByte] = (unsigned char)(255 * rand() / RAND_MAX);
    
        }
    
    }
    
    unsigned short Calc(unsigned char *pucPacket, unsigned long ulSize){
    
        long lSum = CalcCheckSumAdd(pucPacket,ulSize,0);
    
        return CalcCheckSumFinish(lSum);
    
    }
    
    unsigned short Fix(unsigned short usOrig, unsigned int uiOld, unsigned int uiNew){
    
        // TODO: Replace this with something that makes main never fail
        usOrig -= uiOld & 0xffff;
        usOrig -= uiOld >> 16 & 0xffff;
        usOrig += uiNew & 0xffff;
        usOrig += uiNew >>16 & 0xffff;
    
        return usOrig;
    
    }
    
    void Break(unsigned char *pucPacket, unsigned int *puiOld, unsigned int *puiNew){
    
        unsigned int *puiChange = (unsigned int*)pucPacket;
    
        *puiOld = *puiChange;
    
        Randomize((unsigned char*)puiNew,sizeof(unsigned int));
    
        *puiChange = *puiNew;
    
    }
    
    void PrintBuffer(const char *szName, unsigned char *pucBuff, unsigned int uiSize){
    
        printf("%s: ",szName);
    
        for (unsigned int uiByte = 0; uiByte < uiSize; uiByte++){
    
            printf("%02X",(unsigned int)pucBuff[uiByte]);
    
        }
    
        printf("\n");
    
    }
    
    void PrintTestCase(unsigned char *pucOrig, unsigned char *pucChanged, unsigned int uiSize, unsigned short usOrig, unsigned short usChanged, unsigned short usFixed){
    
        PrintBuffer("Original Buffer",pucOrig,uiSize);
        PrintBuffer("Changed Buffer ",pucChanged,uiSize);
    
        printf("Orig    checksum: %04X\n",(unsigned int)usOrig);
        printf("Changed checksum: %04X\n",(unsigned int)usChanged);
        printf("Fixed   checksum: %04X\n",(unsigned int)usFixed);
    
    }
    
    int main(){
    
        srand((unsigned int)time(nullptr));
    
        unsigned char pucDataOrig[100];
        unsigned char pucDataChanged[100];
    
        bool bTestFailed = false;
    
        while (!bTestFailed){
    
            Randomize(pucDataOrig,sizeof(pucDataOrig));
    
            memcpy(pucDataChanged,pucDataOrig,sizeof(pucDataOrig));
    
            unsigned short usOrig = Calc(pucDataOrig,sizeof(pucDataOrig));
    
            unsigned int uiOld = 0,
                         uiNew = 0;
    
            Break(pucDataChanged,&uiOld,&uiNew);
    
            unsigned short usFixed = Fix(usOrig,uiOld,uiNew);
    
            unsigned short usChanged = Calc(pucDataChanged,sizeof(pucDataChanged));
    
            if (usChanged == usFixed){
    
                printf(".");
    
            }else{
    
                printf("\nTest case failed\n");
                PrintTestCase(pucDataOrig,pucDataChanged,sizeof(pucDataOrig),usOrig,usChanged,usFixed);
    
                bTestFailed = true;
    
            }
    
        }
    
        return 0;
    
    }
    

1 个答案:

答案 0 :(得分:0)

您必须减去旧的IP地址并将新的IP地址添加到udp校验和中,这是伪代码:

udp_hdr->dgram_cksum -= old_ipv4_addr & 0xffff;
udp_hdr->dgram_cksum -= old_ipv4_addr >> 16 & 0xffff;
udp_hdr->dgram_cksum += new_ipv4_addr & 0xffff;
udp_hdr->dgram_cksum += new_ipv4_addr >>16 & 0xffff;

这应该处理IP片段上的UDP校验和。