title: Update UDP checksum in fragmented packets
body: <p>I'm building a network appliance. I need to support NAT and IP packet fragmentation. When I change the source or destination address of a UDP packet, I have to correct the UDP checksum (and the IP checksum too, but that's trivial). When the packet is fragmented, I'd have to collect all the fragments to recalculate the checksum. I know the old address and the new address. I'd like to:</p>
  
  <ol>
  <li>Un-negate the checksum</li>
  <li>Subtract the old address</li>
  <li>Add the new address</li>
  <li>Re-reduce the sum and negate</li>
  </ol>
  
  <p>This process doesn't always work. Is there any way to update the checksum versus having to recalculate it from scratch?</p>
  
  <p>I've tried:</p>
  
  <pre><code>long CalcCheckSumAdd(unsigned char *pbHeader, int iSize, long lInitial){
  
      long lSum = lInitial;
  
      while (iSize &gt; 1){
  
          lSum += *((unsigned short*)pbHeader);
  
          pbHeader += 2;
  
          iSize -= 2;
  
      }
  
      if (iSize &gt; 0) lSum += *pbHeader;
  
      return lSum;
  
  }
  
  long CalcCheckSumSubract(unsigned char *pbHeader, int iSize, long lInitial){
  
      long lSum = lInitial;
  
      while (iSize &gt; 1){
  
          lSum -= *((unsigned short*)pbHeader);
  
          pbHeader += 2;
  
          iSize -= 2;
  
      }
  
      if (iSize &gt; 0) lSum -= *pbHeader;
  
      return lSum;
  
  }
  
  unsigned short CalcCheckSumFinish(long lSum){
  
      while (lSum &gt;&gt; 16){
  
          lSum = (lSum &amp; 0xFFFF) + (lSum &gt;&gt; 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*)&amp;ulOldAddress,sizeof(ulOldAddress),lSumFixed);
  
      lSumFixed = CalcCheckSumAdd((unsigned char*)&amp;ulNewAddress,sizeof(ulNewAddress),lSumFixed);
  
      return CalcCheckSumFinish(lSumFixed);
  
  }
  </code></pre>
  
  <p>Thanks!</p>
  
  <p><strong>EDIT: Added unit test code below</strong></p>
  
  <pre><code>#include &lt;time.h&gt;
  #include &lt;stdlib.h&gt;
  #include &lt;stdio.h&gt;
  #include &lt;string.h&gt;
  
  long CalcCheckSumAdd(unsigned char *pbHeader, int iSize, long lInitial){
  
      long lSum = lInitial;
  
      while (iSize &gt; 1){
  
          lSum += *((unsigned short*)pbHeader);
  
          pbHeader += 2;
  
          iSize -= 2;
  
      }
  
      if (iSize &gt; 0) lSum += *pbHeader;
  
      return lSum;
  
  }
  
  unsigned short CalcCheckSumFinish(long lSum){
  
      while (lSum &gt;&gt; 16){
  
          lSum = (lSum &amp; 0xFFFF) + (lSum &gt;&gt; 16);
  
      }
  
      return (unsigned short)(~lSum);
  
  }
  
  void Randomize(unsigned char *pucPacket, unsigned long ulSize){
  
      for (unsigned long ulByte = 0; ulByte &lt; 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 &amp; 0xffff;
      usOrig -= uiOld &gt;&gt; 16 &amp; 0xffff;
      usOrig += uiNew &amp; 0xffff;
      usOrig += uiNew &gt;&gt;16 &amp; 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 &lt; 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,&amp;uiOld,&amp;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;
  
  }
  </code></pre>