Programming raw udp sockets in C on Linux

Raw udp sockets

Raw udp sockets are used to send manually constructed udp packets. The udp header can be found in RFC 768 and has a very simple structure as shown below.

0      7 8     15 16    23 24    31
+--------+--------+--------+--------+
|     Source      |   Destination   |
|      Port       |      Port       |
+--------+--------+--------+--------+
|                 |                 |
|     Length      |    Checksum     |
+--------+--------+--------+--------+
|
|          data octets ...
+---------------- ...

	User Datagram Header Format

The length is the length of the udp header + data in bytes. So the udp header itself is 8 byte long and data can be upto 65536 byte long. The checksum is calculated the same way as the checksum of a tcp header, using a pseudo header.

The code example shown here is for Linux. Raw socket support is not fully available in the windows socket api (winsock). It is there but with a lot of restrictions.

Examples shown here would construct the IP header along with the udp socket. So its more like a raw IP packet that encapsulates UDP format data inside itself.

Code

A raw UDP socket can be simply created using the socket function

int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

In such a socket, the IP header shall be provided by the kernel. The application has to provide the UDP header + Data. If the IP_HDRINCL option is set on it, then the application would need to provide the IP header too.

Here is the full program that uses a raw udp socket to send out data.

/*
	Raw UDP sockets
*/
#include<stdio.h>	//for printf
#include<string.h> //memset
#include<sys/socket.h>	//for socket ofcourse
#include<stdlib.h> //for exit(0);
#include<errno.h> //For errno - the error number
#include<netinet/udp.h>	//Provides declarations for udp header
#include<netinet/ip.h>	//Provides declarations for ip header

/* 
	96 bit (12 bytes) pseudo header needed for udp header checksum calculation 
*/
struct pseudo_header
{
	u_int32_t source_address;
	u_int32_t dest_address;
	u_int8_t placeholder;
	u_int8_t protocol;
	u_int16_t udp_length;
};

/*
	Generic checksum calculation function
*/
unsigned short csum(unsigned short *ptr,int nbytes) 
{
	register long sum;
	unsigned short oddbyte;
	register short answer;

	sum=0;
	while(nbytes>1) {
		sum+=*ptr++;
		nbytes-=2;
	}
	if(nbytes==1) {
		oddbyte=0;
		*((u_char*)&oddbyte)=*(u_char*)ptr;
		sum+=oddbyte;
	}

	sum = (sum>>16)+(sum & 0xffff);
	sum = sum + (sum>>16);
	answer=(short)~sum;
	
	return(answer);
}

int main (void)
{
	//Create a raw socket of type IPPROTO
	int s = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
	
	if(s == -1)
	{
		//socket creation failed, may be because of non-root privileges
		perror("Failed to create raw socket");
		exit(1);
	}
	
	//Datagram to represent the packet
	char datagram[4096] , source_ip[32] , *data , *pseudogram;
	
	//zero out the packet buffer
	memset (datagram, 0, 4096);
	
	//IP header
	struct iphdr *iph = (struct iphdr *) datagram;
	
	//UDP header
	struct udphdr *udph = (struct udphdr *) (datagram + sizeof (struct ip));
	
	struct sockaddr_in sin;
	struct pseudo_header psh;
	
	//Data part
	data = datagram + sizeof(struct iphdr) + sizeof(struct udphdr);
	strcpy(data , "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
	
	//some address resolution
	strcpy(source_ip , "192.168.1.2");
	
	sin.sin_family = AF_INET;
	sin.sin_port = htons(80);
	sin.sin_addr.s_addr = inet_addr ("192.168.1.1");
	
	//Fill in the IP Header
	iph->ihl = 5;
	iph->version = 4;
	iph->tos = 0;
	iph->tot_len = sizeof (struct iphdr) + sizeof (struct udphdr) + strlen(data);
	iph->id = htonl (54321);	//Id of this packet
	iph->frag_off = 0;
	iph->ttl = 255;
	iph->protocol = IPPROTO_UDP;
	iph->check = 0;		//Set to 0 before calculating checksum
	iph->saddr = inet_addr ( source_ip );	//Spoof the source ip address
	iph->daddr = sin.sin_addr.s_addr;
	
	//Ip checksum
	iph->check = csum ((unsigned short *) datagram, iph->tot_len);
	
	//UDP header
	udph->source = htons (6666);
	udph->dest = htons (8622);
	udph->len = htons(8 + strlen(data));	//tcp header size
	udph->check = 0;	//leave checksum 0 now, filled later by pseudo header
	
	//Now the UDP checksum using the pseudo header
	psh.source_address = inet_addr( source_ip );
	psh.dest_address = sin.sin_addr.s_addr;
	psh.placeholder = 0;
	psh.protocol = IPPROTO_UDP;
	psh.udp_length = htons(sizeof(struct udphdr) + strlen(data) );
	
	int psize = sizeof(struct pseudo_header) + sizeof(struct udphdr) + strlen(data);
	pseudogram = malloc(psize);
	
	memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header));
	memcpy(pseudogram + sizeof(struct pseudo_header) , udph , sizeof(struct udphdr) + strlen(data));
	
	udph->check = csum( (unsigned short*) pseudogram , psize);
	
	//loop if you want to flood :)
	//while (1)
	{
		//Send the packet
		if (sendto (s, datagram, iph->tot_len ,	0, (struct sockaddr *) &sin, sizeof (sin)) < 0)
		{
			perror("sendto failed");
		}
		//Data send successfully
		else
		{
			printf ("Packet Send. Length : %d \n" , iph->tot_len);
		}
	}
	
	return 0;
}

//Complete 






Run the program with root user/privileges. On ubuntu use sudo.

If you only need to construct the UDP header then use IPPROTO_UDP as the protocol. Firewalls like firestarter can block such raw packets from transmitting, so disable them before testing the program.

A common application of raw udp sockets is udp based tracerouting where an application sends out udp packets with increasing ttl values in Ip header. We shall see an example of such a program in another article.

Last Updated On : 6th August 2016

Subscribe to get updates delivered to your inbox

6 Comments + Add Comment

  • Thanks for the code but I keep getting error on my UDP checksum calculation. How can I fix that? The IP checksum is correct though!

  • Also agree with Daniel, tcpdump was giving checksum errors until I changed csum to use sizeof (struct iphdr).

  • I agree with Daniel, if the data is different as I needed it to be, you must change the code to use this for IP header checksum: iph->check = csum ((unsigned short *) datagram, sizeof (struct iphdr));

  • you realy help me so much dude! thanks!

  • First of all, you should really change all the comments that say “TCP” to “UDP”. It looks like you’ve just copied some TCP code. Secondly, the IP header checksum is supposed to only sum the IP header itself, whereas in your code it sums the entire datagram. This may still work, but only because the rest of the datagram is set to 0. To do it correctly, you should only sum the length of the IP header.

    • the ip checksum is being calculated like this
      iph->check = csum ((unsigned short *) datagram, iph->tot_len);

      the second parameter is the ip header length which specifies how much of the whole datagram should be used to calculate the checksum, which is only the ip header in this case.

Leave a comment