How to Program raw UDP sockets in C on Linux
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.
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 //for printf #include //memset #include //for socket ofcourse #include //for exit(0); #include //For errno - the error number #include //Provides declarations for udp header #include //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.
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.
A Tech Enthusiast, Blogger, Linux Fan and a Software Developer. Writes about Computer hardware, Linux and Open Source software and coding in Python, Php and Javascript. He can be reached at [email protected] .
9 Comments
- Stefan September 14, 2021 at 8:08 pm 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
- Silver Moon Post author October 20, 2012 at 11:11 am 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.
Programming UDP sockets in C on Linux – Client and Server example
This article describes how to write a simple echo server and client using udp sockets in C on Linux/Unix platform.
UDP sockets or Datagram sockets are different from the TCP sockets in a number of ways.
The most important difference is that UDP sockets are not connection oriented. More technically speaking, a UDP server does not accept connections and a udp client does not connect to server.
The server will bind and then directly receive data and the client shall directly send the data.
Simple UDP Server
So lets first make a very simple ECHO server with UDP socket. The flow of the code would be
socket() -> bind() -> recvfrom() -> sendto()
/* Simple udp server */ #include //printf #include //memset #include //exit(0); #include #include #define BUFLEN 512 //Max length of buffer #define PORT 8888 //The port on which to listen for incoming data void die(char *s) < perror(s); exit(1); >int main(void) < struct sockaddr_in si_me, si_other; int s, i, slen = sizeof(si_other) , recv_len; char buf[BUFLEN]; //create a UDP socket if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) < die("socket"); >// zero out the structure memset((char *) &si_me, 0, sizeof(si_me)); si_me.sin_family = AF_INET; si_me.sin_port = htons(PORT); si_me.sin_addr.s_addr = htonl(INADDR_ANY); //bind socket to port if( bind(s , (struct sockaddr*)&si_me, sizeof(si_me) ) == -1) < die("bind"); >//keep listening for data while(1) < printf("Waiting for data. "); fflush(stdout); //try to receive some data, this is a blocking call if ((recv_len = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen)) == -1) < die("recvfrom()"); >//print details of the client/peer and the data received printf("Received packet from %s:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port)); printf("Data: %s\n" , buf); //now reply the client with the same data if (sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen) == -1) < die("sendto()"); >> close(s); return 0; >
Run the above code by doing a gcc server.c && ./a.out at the terminal. Then it will show waiting for data like this
$ gcc server.c && ./a.out Waiting for data.
Next step would be to connect to this server using a client. We shall be making a client program a little later but first for testing this code we can use netcat.
Test the server with netcat
Open another terminal and connect to this udp server using netcat and then send some data. The same data will be send back by the server. Over here we are using the ncat command from the nmap package.
$ ncat -vv localhost 8888 -u Ncat: Version 5.21 ( http://nmap.org/ncat ) Ncat: Connected to 127.0.0.1:8888. hello hello world world
Note : We had to use netcat because the ordinary telnet command does not support udp protocol. The -u option of netcat specifies udp protocol.
Check open port with netstat
The netstat command can be used to check if the udp port is open or not.
$ netstat -u -a Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State udp 0 0 localhost:11211 *:* udp 0 0 localhost:domain *:* udp 0 0 localhost:45286 localhost:8888 ESTABLISHED udp 0 0 *:33320 *:* udp 0 0 *:ipp *:* udp 0 0 *:8888 *:* udp 0 0 *:17500 *:* udp 0 0 *:mdns *:* udp 0 0 localhost:54747 localhost:54747 ESTABLISHED udp6 0 0 [::]:60439 [::]:* udp6 0 0 [::]:mdns [::]:*
Note the *:8888 entry of output. Thats our server program.
The entry that has localhost:8888 in «Foreign Address» column, indicates some client connected to it, which is netcat over here.
UDP Client
Now that we have tested our server with netcat, its time to make a client and use it instead of netcat.
The program flow is like
/* Simple udp client */ #include //printf #include //memset #include //exit(0); #include #include #define SERVER "127.0.0.1" #define BUFLEN 512 //Max length of buffer #define PORT 8888 //The port on which to send data void die(char *s) < perror(s); exit(1); >int main(void) < struct sockaddr_in si_other; int s, i, slen=sizeof(si_other); char buf[BUFLEN]; char message[BUFLEN]; if ( (s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) < die("socket"); >memset((char *) &si_other, 0, sizeof(si_other)); si_other.sin_family = AF_INET; si_other.sin_port = htons(PORT); if (inet_aton(SERVER , &si_other.sin_addr) == 0) < fprintf(stderr, "inet_aton() failed\n"); exit(1); >while(1) < printf("Enter message : "); gets(message); //send the message if (sendto(s, message, strlen(message) , 0 , (struct sockaddr *) &si_other, slen)==-1) < die("sendto()"); >//receive a reply and print it //clear the buffer by filling null, it might have previously received data memset(buf,'/* Simple udp client */ #include //printf #include //memset #include //exit(0); #include #include #define SERVER "127.0.0.1" #define BUFLEN 512 //Max length of buffer #define PORT 8888 //The port on which to send data void die(char *s) < perror(s); exit(1); >int main(void) < struct sockaddr_in si_other; int s, i, slen=sizeof(si_other); char buf[BUFLEN]; char message[BUFLEN]; if ( (s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) < die("socket"); >memset((char *) &si_other, 0, sizeof(si_other)); si_other.sin_family = AF_INET; si_other.sin_port = htons(PORT); if (inet_aton(SERVER , &si_other.sin_addr) == 0) < fprintf(stderr, "inet_aton() failed\n"); exit(1); >while(1) < printf("Enter message : "); gets(message); //send the message if (sendto(s, message, strlen(message) , 0 , (struct sockaddr *) &si_other, slen)==-1) < die("sendto()"); >//receive a reply and print it //clear the buffer by filling null, it might have previously received data memset(buf,'\0', BUFLEN); //try to receive some data, this is a blocking call if (recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen) == -1) < die("recvfrom()"); >puts(buf); > close(s); return 0; >', BUFLEN); //try to receive some data, this is a blocking call if (recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen) == -1) < die("recvfrom()"); >puts(buf); > close(s); return 0; >
Run the above program and it will ask for some message
$ gcc client.c -o client && ./client Enter message : happy happyWhatever message the client sends to server, the same comes back as it is and is echoed.
Conclusion
UDP sockets are used by protocols like DNS etc. The main idea behind using UDP is to transfer small amounts of data and where reliability is not a very important issue. UDP is also used in broadcasting/multicasting.
When a file transfer is being done or large amount of data is being transferred in parts the transfer has to be much more reliable for the task to complete. Then the TCP sockets are used.
A Tech Enthusiast, Blogger, Linux Fan and a Software Developer. Writes about Computer hardware, Linux and Open Source software and coding in Python, Php and Javascript. He can be reached at [email protected] .
16 Comments
- Joe June 7, 2021 at 3:03 pm Hello. You seem to have double pasted the second code sample inside itself. THanks for the tutorial
- Silver Moon Post author August 14, 2018 at 4:04 pm i haven’t done sockets for a long time. right now i can think of using multiple threads do things in parallel.
so the main thread could do its background work, and an extra thread could listen to the udp port for incoming messages.
or the other way round.
but i am not sure if that is the best approach. there might be better alternatives.
- Bryan Kelly March 15, 2018 at 4:04 am And ncat used option -vv which on my Ubuntu system means verbose. The captured text does not have the verbose output. My system had five lines of information for each line of typed in data.
Still, I am new to Linux and Ubuntu and this is an unexpected cool way to test the server app.
Thank you.