Security Basics mailing list archives

Verifying UDP Checksums


From: "Mister Dookie" <misterdookie () gmail com>
Date: Mon, 16 Jul 2007 14:54:59 -0400

Hello list,

I am trying to write some code that will allow me to test whether UDP
packets are being altered in transit on our network using UDP
checksums. Now I have put together from various sources a program that
allows me to test my capture abilities. However, no matter what I try
I cannot get the UDP checksum generation to work properly in Linux. I
was wondering if somebody who was familiar with packet capture and
alteration at the OS level in Linux could take a quick browse of my
code and see if they could figure out where I am going astray.

Regards,
John

Attached (and posted below) are the program to send the data and a
printout of the typical results I get.  The commands I use are:

   ./TestIp 100 127.0.0.1 9877

which generates packets and

   sudo ../build/TestIp 100 lo

which receives the packets and analyzes them.

The result I get of a typical run is:

Electric Fence 2.2.0 Copyright (C) 1987-1999 Bruce Perens <bruce () perens com>
Wait on sigint
Bound socket for sink to device lo index 1
Waiting for a packet
Got a packet
protocol 17
pseudoHeader 0x7ffe
ckSum 0xbf78a 0xf795
Expected Packet 0 Received Packet 0 Checksum 0xf795
  0   0   0   0   0   0   0   0
  0   0   0   0   8   0  69   0
  0 128   0   0  64   0  64  17
 60 107 127   0   0   1 127   0
  0   1 152 237  38 149   0 108
254 127   0   0   0   0   0   1
  2   3   4   5   6   7   8   9
 10  11  12  13  14  15  16  17
 18  19  20  21  22  23  24  25
 26  27  28  29  30  31  32  33
 34  35  36  37  38  39  40  41
 42  43  44  45  46  47  48  49
 50  51  52  53  54  55  56  57
 58  59  60  61  62  63  64  65
 66  67  68  69  70  71  72  73
 74  75  76  77  78  79  80  81
 82  83  84  85  86  87  88  89
 90  91  92  93  94  95
ethHeader 0x8 0 14
ipHeader 0xffff 14 20
udpHeader 0x26ebc 34 8
udpData 0x908d0 42 100
0x0000
0x7ffe PseudoHeader
0x0008 EthHeader
0x8006 PseudoHeader EthHeader
0xffff IpHeader
0x7ffe PseudoHeader IpHeader
0x0008 EthHeader IpHeader
0x8006 PseudoHeader EthHeader IpHeader
0x6ebe UdpHeader
0xeebc PseudoHeader UdpHeader
0x6ec6 EthHeader UdpHeader
0xeec4 PseudoHeader EthHeader UdpHeader
0x6ebe IpHeader UdpHeader
0xeebc PseudoHeader IpHeader UdpHeader
0x6ec6 EthHeader IpHeader UdpHeader
0xeec4 PseudoHeader EthHeader IpHeader UdpHeader
0x08d9 UdpData
0x88d7 PseudoHeader UdpData
0x08e1 EthHeader UdpData
0x88df PseudoHeader EthHeader UdpData
0x08d9 IpHeader UdpData
0x88d7 PseudoHeader IpHeader UdpData
0x08e1 EthHeader IpHeader UdpData
0x88df PseudoHeader EthHeader IpHeader UdpData
0x7797 UdpHeader UdpData
0xf795 PseudoHeader UdpHeader UdpData
0x779f EthHeader UdpHeader UdpData
0xf79d PseudoHeader EthHeader UdpHeader UdpData
0x7797 IpHeader UdpHeader UdpData
0xf795 PseudoHeader IpHeader UdpHeader UdpData
0x779f EthHeader IpHeader UdpHeader UdpData
0xf79d PseudoHeader EthHeader IpHeader UdpHeader UdpData
Return from wait on SigInt
packetCount 0

The source code (mostly not mine) is below:

// A simple routine that can act as either a source or sink of UDP packets.
// In the source mode it uses the standard UPD socket interface.
// In the sink mode it uses the raw socket interface so that packets
// are intercepted before the checksum is applied.  This allows the nature
// of errors to be observed.

#include "TetraCommUdp.h"
#include "TetraRealTimeMlock.h"

#include <vector>
#include <string>
#include <iomanip>

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/if_ether.h>
#include <netpacket/packet.h>
#include <signal.h>
#include <netinet/in.h>

using std::cout;
using std::cerr;

static int packetCount=0;

class GenData {
public:
 GenData(int size) : size(size), packetIndex(-1), buffer(size) {
   for (int Idx=4;Idx<size;Idx++) {
     buffer[Idx]=(static_cast<unsigned>(Idx-4))%256;
   }
 }
 int getSize() const { return size; }
 int getPacketIndex() const { return packetIndex; };
 char *getBufferAddress() { return &buffer[0]; }
 void nextPacket(void) {
   packetIndex++;
   int* to=reinterpret_cast<int *>(&buffer[0]);
   *to=packetIndex;
 }
private:
 int size;
 int packetIndex;
 vector<char> buffer;
};

class SourceData {
public:
 SourceData(const string& ipAddr, unsigned short port, int dataSize) :
   ipAddr(ipAddr), port(port), dataSize(dataSize) {}
 string ipAddr;
 unsigned short port;
 int dataSize;
} ;
        

void* UdpSource(void *pData) {
 TetraModem::TetraRealTimeMlock(128*1024);
 sigset_t sigSetInt;
 ::sigemptyset(&sigSetInt);
 ::sigaddset(&sigSetInt,SIGINT);
 ::pthread_sigmask(SIG_BLOCK,&sigSetInt,0);
 SourceData* pSourceData(reinterpret_cast<SourceData*>(pData));
 GenData genData(pSourceData->dataSize);
 TetraModem::TetraUdpSocket tus(pSourceData->ipAddr,pSourceData->port);
 while(1) {
   genData.nextPacket();
   tus.Send(genData.getBufferAddress(),genData.getSize());
   usleep(500);
   packetCount=genData.getPacketIndex();
 }
 return((void*)0);
}

class SinkData {
public:
 SinkData(const string& networkDevice, int dataSize) :
   networkDevice(networkDevice), dataSize(dataSize) {}
 string networkDevice;
 int dataSize;
} ;



void* UdpSink(void *pData) {
 TetraModem::TetraRealTimeMlock(128*1024);
 sigset_t sigSetInt;
 ::sigemptyset(&sigSetInt);
 ::sigaddset(&sigSetInt,SIGINT);
 ::pthread_sigmask(SIG_BLOCK,&sigSetInt,0);
 SinkData* pSinkData(reinterpret_cast<SinkData*>(pData));
 GenData genData(pSinkData->dataSize);
 int UdpPrototype=(::getprotobyname("udp"))->p_proto;
 int socket=0;
 if((socket = ::socket(PF_PACKET, SOCK_RAW, UdpPrototype)) < 0) {
   cerr<<"Cannot create socket for sink: "
        <<strerror(errno)
        <<endl;
   return((void*)1);
 }
 // get the device interface
 struct ifreq ifReq;
 strncpy(&ifReq.ifr_name[0],pSinkData->networkDevice.c_str(),IFNAMSIZ);
 if (ioctl(socket,SIOCGIFINDEX,&ifReq)!=0) {
   cerr<<"Cannot find index for interface "<<ifReq.ifr_name<<": "
        <<strerror(errno)<<endl;
   return((void*)1);
 }
 // set up the address for the bind
 struct sockaddr_ll serverRecvAddr;
 memset(&serverRecvAddr, 0, sizeof(serverRecvAddr));
 serverRecvAddr.sll_family = (AF_PACKET);
 serverRecvAddr.sll_protocol=htons(ETH_P_IP);
 serverRecvAddr.sll_ifindex=ifReq.ifr_ifindex;
 // bind to the local address
 if(bind(socket,(struct sockaddr*)&serverRecvAddr,sizeof(serverRecvAddr))<0) {
   cerr<<"Cannot bind socket for sink to device "<<pSinkData->networkDevice
        <<" index "<<ifReq.ifr_ifindex
        <<": "
        <<strerror(errno)<<endl;
   return((void*)1);
 } else {
   cerr<<"Bound socket for sink to device "<<pSinkData->networkDevice
        <<" index "<<ifReq.ifr_ifindex<<endl;
 }
 unsigned ethHeaderSize=14; // ethernet header
 unsigned ipHeaderOffset=ethHeaderSize;
 unsigned ipHeaderSize=20;  // according to documents
 unsigned udpHeaderOffset=ipHeaderOffset+ipHeaderSize;
 unsigned short udpSourcePort;
 unsigned short udpDestinationPort;
 unsigned short udpLength;
 unsigned short udpChecksum;
 unsigned udpHeaderSize=sizeof(udpSourcePort)+
   sizeof(udpDestinationPort)+sizeof(udpLength)+sizeof(udpChecksum);
 unsigned udpDataOffset=udpHeaderOffset+udpHeaderSize;
 unsigned udpDataSize=genData.getSize();
 unsigned udpBufferSize=udpDataSize+udpHeaderSize;
 unsigned expectedRecvSize=udpBufferSize+ipHeaderSize+ethHeaderSize;
 unsigned bufferSize=2048;
 vector<char> rxBuf(bufferSize);  
 // A structure for the recvfrom call
 struct sockaddr_in recvFromAddr;
 memset(&recvFromAddr, 0, sizeof(recvFromAddr));
 sockaddr* pRecvAddr=reinterpret_cast<sockaddr *>(&recvFromAddr);
 socklen_t lenRecvFromAddr=sizeof(recvFromAddr);
 // Loop forever
 while(1) {
   genData.nextPacket();
   // Note that MSG_WAITALL creates an invalid parameter return.
   cerr<<"Waiting for a packet"<<endl;
   int bytesReceived = recvfrom(socket,&rxBuf[0],bufferSize,0,
                                 pRecvAddr,&lenRecvFromAddr);
   cerr<<"Got a packet"<<endl;
   if (bytesReceived<=0) {
     cerr<<"Receive error on packet "<<genData.getPacketIndex()
        <<": "<<strerror(errno)<<endl;
     return((void*)1);
   }
   if (bytesReceived!=(int)expectedRecvSize) {
     cerr<<"Packet "<<genData.getPacketIndex()
          <<" Expected "<<bufferSize<<" bytes "
          <<" got "<<bytesReceived<<" bytes"
          <<endl;
     if (bytesReceived<(int)expectedRecvSize) return((void*)1);
   }
   bool errFlag=false;
   // validate the checksum
   unsigned short* pBuf=reinterpret_cast<unsigned short*>
     (&rxBuf[udpHeaderOffset]);
   unsigned ckSum=0;
   // IP source and destination addressses are added in through
   // backing pBuf up by 8 above and boosting the loop limit below
   // by 8/2.
   // Protocol (2 bytes)
   unsigned short protocol=0xff&rxBuf[udpHeaderOffset-11];
   cerr<<"protocol "<<protocol<<endl;
   ckSum+=htons(protocol);
   // UDP length (2 bytes)
   unsigned short *pUdpLength=reinterpret_cast<unsigned short*>
     (&rxBuf[udpHeaderOffset+4]);
   ckSum+=pUdpLength[0];
   // source and dest Ip addressses
   unsigned short *pIpAddr=reinterpret_cast<unsigned short*>
     (&rxBuf[ipHeaderOffset+12]);
   for (int Idx=0;Idx<4;Idx++) {
     ckSum+=static_cast<unsigned>(pIpAddr[Idx]);
   }
   vector<unsigned> ckSumList(5);
   vector<string> nameList(5);
   ckSumList[0]=ckSum;
   nameList[0]="PseudoHeader";
   cerr<<"pseudoHeader "<<hex<<"0x"<<ckSum<<dec<<endl;
   for (unsigned idx=0;idx<(udpBufferSize)/2;idx++) {
     ckSum+=(unsigned)pBuf[idx];
   }
   cerr<<"ckSum "<<hex<<"0x"<<ckSum<<dec;
   ckSum=(ckSum>>16)+(ckSum&0xffff);
   cerr<<" "<<hex<<"0x"<<ckSum<<dec<<endl;
   if (ckSum!=0xffff) errFlag=true;
   int rxPacketIndex=(reinterpret_cast<int*>
                       (&rxBuf[udpDataOffset]))[0];
   if (genData.getPacketIndex()!=rxPacketIndex) errFlag=true;
   if (errFlag) {
     cerr<<"Expected Packet "<<genData.getPacketIndex()
          <<" Received Packet "<<rxPacketIndex
         <<" Checksum "<<hex<<"0x"<<ckSum<<dec<<" "<<endl;
     bool misMatchFound=false;
     int idx;
     for (idx=4;idx<genData.getSize();idx++) {
        if (rxBuf[idx+udpDataOffset]
            !=genData.getBufferAddress()[idx]) {
          misMatchFound=true;
          break;
        }
     }
     if (misMatchFound) {
        cerr<<"Mismatch at byte offset "<<idx
            <<" Expected "
            <<static_cast<unsigned>(genData.getBufferAddress()[idx])<<" "
            <<static_cast<unsigned>(genData.getBufferAddress()[idx+1])
            <<" Got "
            <<static_cast<unsigned>(rxBuf[idx+udpDataOffset])<<" "
            <<static_cast<unsigned>(rxBuf[idx+udpDataOffset+1])
            <<endl;
     }
     for (int dIdx=0;dIdx<bytesReceived;dIdx++) {
        cerr<<setw(4)<<(0xff&static_cast<unsigned>(rxBuf[dIdx]));
        if (dIdx%8==7) cerr<<endl;
     }
     if (bytesReceived%8!=0) cerr<<endl;

     // ethernet header
     pBuf=reinterpret_cast<unsigned short*>(&rxBuf[0]);
     ckSum=0;
     for (unsigned idx=0;idx<(ethHeaderSize)/2;idx++) {
        ckSum+=(unsigned)pBuf[idx];
     }
     cerr<<"ethHeader "<<hex<<"0x"<<ckSum<<dec
          <<" "<<0<<" "<<ethHeaderSize<<endl;
     ckSumList[1]=ckSum;
     nameList[1]="EthHeader";
     // IP header
     pBuf=reinterpret_cast<unsigned short*>(&rxBuf[ipHeaderOffset]);
     ckSum=0;
     for (unsigned idx=0;idx<(ipHeaderSize)/2;idx++) {
        ckSum+=(unsigned)pBuf[idx];
     }
     cerr<<"ipHeader "<<hex<<"0x"<<ckSum<<dec
          <<" "<<ipHeaderOffset<<" "<<ipHeaderSize<<endl;
     ckSumList[2]=ckSum;
     nameList[2]="IpHeader";
     // UDP header
     pBuf=reinterpret_cast<unsigned short*>(&rxBuf[udpHeaderOffset]);
     ckSum=0;
     for (unsigned idx=0;idx<(udpHeaderSize)/2;idx++) {
        ckSum+=(unsigned)pBuf[idx];
     }
     cerr<<"udpHeader "<<hex<<"0x"<<ckSum<<dec
          <<" "<<udpHeaderOffset<<" "<<udpHeaderSize<<endl;
     ckSumList[3]=ckSum;
     nameList[3]="UdpHeader";
     // UDP data
     pBuf=reinterpret_cast<unsigned short*>(&rxBuf[udpDataOffset]);
     ckSum=0;
     for (unsigned idx=0;idx<(udpDataSize)/2;idx++) {
        ckSum+=(unsigned)pBuf[idx];
     }
     cerr<<"udpData "<<hex<<"0x"<<ckSum<<dec
          <<" "<<udpDataOffset<<" "<<udpDataSize<<endl;
     ckSumList[4]=ckSum;
     nameList[4]="UdpData";
     for (unsigned bitMap=0;bitMap<(1<<5);bitMap++) {
        ostringstream labels;
        unsigned ckSumAcc=0;
        for (unsigned bitIdx=0;bitIdx<5;bitIdx++) {
          unsigned bitMask=(1<<bitIdx);
          if ((bitMap&bitMask)!=0) {
            ckSumAcc+=ckSumList[bitIdx];
            labels<<nameList[bitIdx]<<" ";
          }
        }
        unsigned ckSum16=((ckSumAcc>>16)+(ckSumAcc&0xffff));
        cerr<<"0x"<<setw(4)<<setfill('0')
            <<hex<<ckSum16<<dec<<setfill(' ')
            <<" "<<labels.str()<<endl;
     }

     return((void*)1);
   }
   packetCount=genData.getPacketIndex();
 }
 return((void*)0);
}

int
main(int argc, char** argv) {
 bool isSource=false;
 if (argc==4) {
   isSource=true;
 } else if (argc==3) {
   isSource=false;
 } else {
   cerr<<"Usage: <packetSize> {<ipAddress> <port>}|<networkDevice>"<<endl;
   exit(2);
 }
 // Need to set up for real time
 pthread_attr_t threadAttr;
 ::pthread_attr_init(&threadAttr);
 int schedPolicyRealTime=SCHED_RR;
 int schedPolicyOther=SCHED_OTHER;
 ::pthread_attr_init(&threadAttr);
 ::pthread_attr_setschedpolicy(&threadAttr,schedPolicyOther);
 ::sched_param schedParamRealTime;
 schedParamRealTime.sched_priority=
   ::sched_get_priority_max(schedPolicyRealTime);
 // Block sigint
 sigset_t sigSetInt;
 ::sigemptyset(&sigSetInt);
 ::sigaddset(&sigSetInt,SIGINT);
 ::pthread_sigmask(SIG_SETMASK,&sigSetInt,0);

 pthread_t threadId;
 int dataSize(atoi(argv[1]));
 if (isSource) {
   string ipAddress(argv[2]);
   short port=atoi(argv[3]);
   SourceData* pSourceData=new SourceData(ipAddress,port,dataSize);
   if (::pthread_create(&threadId,&threadAttr,
                         UdpSource,reinterpret_cast<void*>(pSourceData))!=0) {
     cerr<<"Error starting up Source thread: "<<strerror(errno)<<endl;
     exit(1);
   }
 } else {
   string networkDevice(argv[2]);
   SinkData* pSinkData=new SinkData(networkDevice,dataSize);
   if (::pthread_create(&threadId,&threadAttr,
                         UdpSink,reinterpret_cast<void*>(pSinkData))!=0) {
     cerr<<"Error starting up Sink thread: "<<strerror(errno)<<endl;
     exit(1);
   }
 }
 // Put it on the real time queue
 pthread_detach(threadId);
 schedParamRealTime.sched_priority=
   sched_get_priority_max(schedPolicyRealTime)-1;
 if (pthread_setschedparam
     (threadId,schedPolicyRealTime,&schedParamRealTime)) {
   int errnoCpy=errno;
   const int ErrBufLen(1024);
   char ErrBuf[ErrBufLen];
   cerr<<"ThreadStartup: Error doing setschedparam: "
     <<strerror_r(errnoCpy,ErrBuf,ErrBufLen)
     <<": Continuing without realtime";
 }
 cerr<<"Wait on sigint"<<endl;
 int sigReturn (0);
 while (sigReturn != SIGINT)
   {
     ::sigwait(&sigSetInt,&sigReturn);
   }
 cerr<<"Return from wait on SigInt"<<endl;
 cerr<<"packetCount "<<packetCount<<endl;
}

Attachment: TestIp.cpp
Description:

Attachment: results.txt
Description:


Current thread: