Click here to Skip to main content
16,020,381 members
Articles / Programming Languages / C++
Article

RawUDP - A Class to Craft Custom UDP Packets

Rate me:
Please Sign up or sign in to vote.
4.60/5 (10 votes)
23 Dec 2008Ms-PL4 min read 36.8K   1.9K   34   4
An article showing how to use the RawUDP class to quickly create UDP packets and then how to send them with WinPCap

A Wireshark capture of packets generated by the sample application.

Introduction  

In this code, I will present a C++ class used to easily create and manipulate UDP packets from scratch. This can be useful for Windows users who can't send custom UDP packets using Winsock. (XP SP2 and later). In the sample application, WinPCap is used instead of Winsock to send the UDP packets.

Structure of RawUDP:

  • Header data is private
  • Accessor class functions are public and make for ease of extensibility
  • A pointer to the next RawUDP class in case a linked list is used
  • Headers separated into structures (EthernetIIHeader, IPv4Header, UDPHeader)

Background

Just about all of the inner workings of the class are described in my previous tutorial.

Before We Begin 

RawUDP requires WinPCap, Winsock, and the IP Helper API.

Getting WinPCap   

The WinPCap 4.02 libraries I use here can be downloaded here. If you want to run WinPCap applications, you will have to install the WinPcap binaries

Windows SDK

Some functions and structures used in this article are from Winsock2 and the Iphlp API. These are included in the Windows SDK, and can be downloaded for free here (1.33 GB).

Installing RawUDP Libraries

To use RawUDP, extract the RawUDP folder into your compiler's include folder.
(C:\Program Files\Microsoft Visual Studio 9.0\VC\Include on my Visual Studio 2008 installation).
Use #include "RawUDP/RawUDP.h" to include RawUDP in your project.

Using the Code  

Almost all of RawUDP's functionality is in the RawUDP class. With this class, you can change packet data, retrieve packet data, and send the packet. The class definition for RawUDP is found in RawUDP.h. The only functionality outside of RawUDP are some helper functions in NetworkStuff.h; these are: GetDeviceList() which returns a multidimensional array containing up to 8 installed devices supported by WinPCap, BytesTo16() which joins 2 chars into a short int and is used for checksumming, and GetDeviceInfo() which gets important information about a WinPCap supported device. So in order to send a custom UDP packet, you must:

  1. Find the device
  2. Prepare the packet
  3. Call SendPacket() with the device from #1 

Finding the Device

The device must be stored in the format of pcap_if_t* from WinPCap. Make sure you don't use a loopback device because these will not work. In the sample project, I used GetDeviceList() to get a list of devices for a dropdown list, but you can use your own method to get the device as long as in the end, you have a pcap_if_t* variable corresponding to the device you want to use. 

C++
char** GetDeviceList(void)
{
	char Error[PCAP_ERRBUF_SIZE];
	pcap_if_t* Devices;pcap_findalldevs_ex(PCAP_SRC_IF_STRING,NULL,&Devices,Error);

	char** Returned = new char* [8];
	for(int i = 0;i<8;i++){Returned[i] = new char[180];}
	for(int i = 0;i<8;i++){for(int x = 0;x<180;x++){Returned[i][x] = 0x00;}}

	int i = 0;
	for(pcap_if_t* CurrentDevice = Devices;CurrentDevice != NULL;
                CurrentDevice = CurrentDevice->next)
	{
		strcpy(Returned[i],CurrentDevice->description);
		i++;
	}
	return Returned;
}  
C++
char** DevNames = GetDeviceList(); // On WM_INITDIALOG (Before everything)
for(int i = 0;i<8;i++)
{
	if(strlen(DevNames[i]) == 0){break;}
	SendMessage(GetDlgItem(hWnd,DeviceList),CB_ADDSTRING,0,(LPARAM)DevNames[i]);
} // Fill device list
C++
case WM_COMMAND:
		{
		if(HIWORD(wParam)==CBN_SELCHANGE) //When devicelist selection changes
		{
		   char Error[PCAP_ERRBUF_SIZE];
		   char Desired[256];
		   GetDlgItemText(hWnd,DeviceList,Desired,256);

		   pcap_if_t* Devices = NULL;
		   pcap_findalldevs_ex(PCAP_SRC_IF_STRING,NULL,&Devices,Error);

		   //ChosenDevice is a global pcap_if_t*
                  for(pcap_if_t* CurrentDevice = Devices;CurrentDevice !=
				NULL;CurrentDevice = CurrentDevice->next)
		   {
		      if(strstr(CurrentDevice->description,Desired))
		      {
			ChosenDevice = CurrentDevice;
			break;
		      }
		   }
			if(ChosenDevice == NULL){MessageBox
				(NULL,"Problem setting device.",
				"Error",MB_ICONEXCLAMATION);break;}
			break;
		}

Preparing the Packet

First do this: 

C++
RawUDP VarName = RawUDP(); 

Or if you will use a linked list:

C++
RawUDP* VarName = new RawUDP(); 

RawUDP() initializes the class to an IPv4 UDP Packet set with almost everything to zero and no data. If you really want to, you can actually send this packet already just with RawUDP.SendPacket(), but that's no good, eh. So once you have the RawUDP object, you can proceed to changing packet data using the accessor functions. Remember that the accessor functions take parameters in little endian format (Don't use htons() or htonl() first, if you use inet_addr(), remember it converts the result to network byte order at the end). I can't post the whole class here because it's too big, but here's a list of the setter function prototypes

C++
int SetDestinationMAC(char* DestFormattedString);//aa:bb:cc:dd:ee:ff
int SetDestinationMAC(unsigned char* ByteArray); //UCHAR Array[6] = {}
int SetSourceMAC(char* SrcFormattedString);
int SetSourceMAC(unsigned char* ByteArray);
void SetType(unsigned short Type);

void SetVersion(unsigned char Version); //No SetHeaderLen, only 20 byte implemented
void SetServices(unsigned char Services);
void SetIdentification(unsigned short Identification) ;
void SetFlagsAndFragmentOffset(unsigned char Flags,unsigned char FragmentOffset);
void SetTTL(unsigned char TTL);
void SetProtocol(unsigned char Protocol);
void SetIPChecksum(unsigned short Checksum);
void SetSourceIP(char* FormattedSourceIP);
void SetSourceIP(unsigned int ByteArray);
void SetDestinationIP(char* FormattedDestinationIP);
void SetDestinationIP(unsigned int ByteArray);

void SetSourcePort(unsigned short SourcePort);
void SetDestinationPort(unsigned short DestinationPort);
void SetUDPChecksum(unsigned short Checksum);
//No SetUDPHeaderLen because only 8 byte UDP headers implemented
//SetUserDataLength not implemented
int SetUserData(unsigned char* UserData,unsigned short UserDataLength);

So if I wanted to make the destination port 12345, I would just do:

C++
VarName->SetDestinationPort(12345);
//(Or use a period instead of -> if it's not a pointer)

After making all the changes to the packet, call ConstructPacket(). This function puts all of the information into the unsigned char* FinalPacket member of RawUDP and calculates the lengths and checksums.

C++
VarName->ConstructPacket(); 

In summary, here is some code:

C++
RawUDP* Packets = new RawUDP(); //Ideal for linked list
Packets->SetSourceIP("127.0.0.1"); // Setter
MessageBox(NULL,Packets->GetSourceIP(),"Source IP",MB_OK);//Getter function
Packets->ConstructPacket(); //Prepare the packet for sending
                            // Make all changes between the constructor
			// and ConstructPacket() 

Now the packet is ready to be sent =0.

Sending the Packet

Well, the packet is almost ready to be sent. You could send it without setting a destination MAC address, but it would get nowhere because no device on your network would pick it up. If the packet is to actually get somewhere, the destination MAC address must be set to the MAC of your network's default gateway. This way the default gateway will see the packet and send it from your network to the next device in the internet. In order to simplify this, I wrote the "GetDeviceInfo()" function. This function takes a pcap_if_t* as a parameter and returns its IP, MAC address, gateway's IP address, gateway's MAC address, and other useful info. The device's details are important because some ISPs and Modems don't allow packets with incorrect IP and MAC information out of the network, so it is necessary to allow the option for the user to use real details. The function and structure are described in more detail in my previous article, but back to the subject of setting the packet's destination MAC to that of the default gateway.   

C++
DeviceInfo DI = GetDeviceInfo(ChosenDevice);
{
     Packets->SetDestinationMAC(DI.GatewayPhysicalAddress);
}

Simple as that.

Finally to actually send the packet: 

C++
CurrentPacket->SendPacket(ChosenDevice);  

So in conclusion, here's a review:

C++
//Find the device you want by traversing the pcap_if_t* linked list
//from pcap_findalldevs_ex() and see which device has the same name or
//description as the one you want. Using the name would be better because
//some devices don't have a description. Here the device is stored in ChosenDevice

DeviceInfo DI = GetDeviceInfo(ChosenDevice);
RawUDP* Packets = new RawUDP();
Packets->SetDestinationMAC(DI.GatewayPhysicalAddress);
//Do all changes here
Packets->ConstructPacket();
Packets->SendPacket(ChosenDevice);

On Checksumming

The RawUDP class has a bool CalculateChecksums that determines if the IP and UDP checksums will be calculated when ConstructPacket() is called. If you want to implement your own checksumming or don't need it and want to save CPU time, you can set CalculateChecksum to false.

C++
RawUDP.CalculateChecksum = false; // No accessor for this )

Related Articles  

History   

  • 23rd December, 2008: Initial post

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
United States United States
Elec undergrad at Rice

Comments and Discussions

 
GeneralMy vote of 5 Pin
AHTOXA15-Mar-11 19:12
AHTOXA15-Mar-11 19:12 
Generalhelp Pin
aeham31730-Mar-09 3:51
aeham31730-Mar-09 3:51 
GeneralMy vote of 1 Pin
poltrone29-Dec-08 23:04
poltrone29-Dec-08 23:04 
beginner style code
GeneralRe: My vote of 1 Pin
Emmanuel Herrera30-Dec-08 7:37
Emmanuel Herrera30-Dec-08 7:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.