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:
- Find the device
- Prepare the packet
- 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.
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;
}
char** DevNames = GetDeviceList(); for(int i = 0;i<8;i++)
{
if(strlen(DevNames[i]) == 0){break;}
SendMessage(GetDlgItem(hWnd,DeviceList),CB_ADDSTRING,0,(LPARAM)DevNames[i]);
}
case WM_COMMAND:
{
if(HIWORD(wParam)==CBN_SELCHANGE) {
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);
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:
RawUDP VarName = RawUDP();
Or if you will use a linked list:
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
int SetDestinationMAC(char* DestFormattedString);int SetDestinationMAC(unsigned char* ByteArray); int SetSourceMAC(char* SrcFormattedString);
int SetSourceMAC(unsigned char* ByteArray);
void SetType(unsigned short Type);
void SetVersion(unsigned char Version); 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);
int SetUserData(unsigned char* UserData,unsigned short UserDataLength);
So if I wanted to make the destination port 12345
, I would just do:
VarName->SetDestinationPort(12345);
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.
VarName->ConstructPacket();
In summary, here is some code:
RawUDP* Packets = new RawUDP(); Packets->SetSourceIP("127.0.0.1"); MessageBox(NULL,Packets->GetSourceIP(),"Source IP",MB_OK);Packets->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.
DeviceInfo DI = GetDeviceInfo(ChosenDevice);
{
Packets->SetDestinationMAC(DI.GatewayPhysicalAddress);
}
Simple as that.
Finally to actually send the packet:
CurrentPacket->SendPacket(ChosenDevice);
So in conclusion, here's a review:
DeviceInfo DI = GetDeviceInfo(ChosenDevice);
RawUDP* Packets = new RawUDP();
Packets->SetDestinationMAC(DI.GatewayPhysicalAddress);
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
.
RawUDP.CalculateChecksum = false;
Related Articles
History
- 23rd December, 2008: Initial post