I'm writing raw socket client (which successfully sends UDP packets) and a server socket, The problem is in the server part.
Im creating a socket in the following way:
int raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
also I tried it with IPPROTO_RAW, but get the same result and I'm binding it:
bind(raw_socket, (struct sockaddr*)&sockstr, sizeof(sockstr))
when trying to receive some packets using the socket, the only payload I receive is "E" (I think that means "Error"), or the socket continues listening but blocks and nothing happens. How do I receive a UDP packet using a raw socket? My code:
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
int server(){
int raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (raw_socket== -1){
perror("Socket_creation_error\n");
return 1;
}
struct sockaddr_in sockstr;
sockstr.sin_addr.s_addr = inet_addr("127.0.0.1");
sockstr.sin_family = AF_INET;
sockstr.sin_port = htons(9090);
socklen_t s = (socklen_t)sizeof(sockstr);
if (bind(raw_socket, (struct sockaddr*)&sockstr, sizeof(sockstr))< 0){
perror("binding_err\n");
return 0;
}
char* msg[256];
memset(msg, 0, 256);
recv(raw_socket, msg, sizeof(msg), 0);
printf(msg);
return 0;
}
void main(){
server();
}
As raw sockets manual says user@host:~$ man 7 raw
:
A protocol of IPPROTO_RAW implies enabled IP_HDRINCL and is able to send any IP protocol that is specified in the passed header. Receiving of all IP protocols via IPPROTO_RAW is not possible using raw sockets.
Another important note also extracted from manual is:
Only processes with an effective user ID of 0 or the CAP_NET_RAW capability are allowed to open raw sockets.
And manual says too:
Starting with Linux 2.2, all IP header fields and options can be set using IP socket options. This means raw sockets are usually needed only for new protocols or protocols with no user interface (like ICMP).
Ok, assuming you need to have IP/UPD headers in hand, let's go to work :-)
First of all, we need to let some points clear:
#include ...
headers are missing.char *msg[SIZE]
??? It's an array of char pointers! You need only an array of chars, like this: char msg[SIZE]
.#include <linux/ip.h>
and #include <linux/udp.h>
to obtain the headers's size).main.c
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/ip.h> /* for ipv4 header */
#include <linux/udp.h> /* for upd header */
#define ADDR_TO_BIND "127.0.0.1"
#define PORT_TO_BIND 9090
#define MSG_SIZE 256
#define HEADER_SIZE (sizeof(struct iphdr) + sizeof(struct udphdr))
int main(void) {
int raw_socket;
struct sockaddr_in sockstr;
socklen_t socklen;
int retval = 0; /* the return value (give a look when an error happens)
*/
/* no pointer to array!
* >> It was like "a variable that contains an address -- and in this
* address begins an array of chars"! */
/* now it is simple an array of chars :-) */
char msg[MSG_SIZE];
ssize_t msglen; /* return value from recv() */
/* do not use IPPROTO_RAW to receive packets */
if ((raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) == -1) {
perror("socket");
return 1; /* here there is no clean up -- retval was not used */
}
sockstr.sin_family = AF_INET;
sockstr.sin_port = htons(PORT_TO_BIND);
sockstr.sin_addr.s_addr = inet_addr(ADDR_TO_BIND);
socklen = (socklen_t) sizeof(sockstr);
/* use socklen instead sizeof() Why had you defined socklen? :-) */
if (bind(raw_socket, (struct sockaddr*) &sockstr, socklen) == -1) {
perror("bind");
retval = 1; /* '1' means "Error" */
goto _go_close_socket;
}
memset(msg, 0, MSG_SIZE);
if ((msglen = recv(raw_socket, msg, MSG_SIZE, 0)) == -1) {
perror("recv");
retval = 1;
goto _go_close_socket;
}
if (msglen <= HEADER_SIZE) /* msg can't be lesser than header! */
printf("No msg!\n");
else {
msg[msglen - 1] = '\0'; /* we need a null character at the end*/
printf("Your msg _plus_ headers's size is: %s\n",
msg + HEADER_SIZE);
}
_go_close_socket:
close(raw_socket);
return retval;
}
Ok, now compile the program with:
user@host:~$ gcc -o main main.c
Execute it as root:
root@host:~# ./main
And in another terminal send a message with nc:
-u specifies UPD to nc
user@host:~$ nc -u 127.0.0.1 9090
That is it!