Raw UDP sockets
Raw udp sockets are used to constructed udp packets with a application defined custom header. It is useful in security related network applications.
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.
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.
$ gcc raw_udp.c $ sudo ./a.out
Notes
To check the raw udp packets, use a packets sniffer like Wireshark. It should show all the packets generated by the above program.
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.
Hi madmat,
Sorry, I think you’re wrong:
You oversee, that a value >0xFFFF can be appear even after the first sum. So the addition of the bits >0xFFFF have to be done twice.
As an example, think about checksum of 0x3FFFF after addition of all bytes to be checksummed.
The first wrap gives you 3 + 0xFFFF = 0x10002, the second wrap gives you 1 + 2 = 0x0003 as final checksum.
This rule is valid for both, IP and UDP.
regards,
Stefan
in csum() I found a small error here:
sum = (sum>>16)+(sum & 0xffff);
sum = sum + (sum>>16); // this line is useless
I rebuild csum() a little:
uint16_t csum(void *buf, int cb)
{
uint16_t *ptr = (uint16_t *)buf;
//initialize the sum with the last byte in case of odd size, otherwise to zero
int32_t sum = (cb&1)?((uint8_t)buf)[cb-1]:0;
cb/=2;
while(cb–) sum+=*ptr++;
return (uint16_t)~((sum>>16)+(sum & 0xffff));
}
sould work the same way
For anyone got UDP checksum error, just swap the order of placeholder and protocol in the pseudo_header struct to match the endianness of your OS.
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.