无法将数据包接收到原始套接字
Can't receive packets to raw socket
我正在编写原始套接字客户端(成功发送 UDP 数据包)和服务器套接字,问题出在服务器部分。
我按以下方式创建套接字:
int raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
我也用 IPPROTO_RAW 尝试过,但得到相同的结果,我将其绑定:
bind(raw_socket, (struct sockaddr*)&sockstr, sizeof(sockstr))
当尝试使用套接字接收一些数据包时,我收到的唯一有效负载是 "E"(我认为这意味着 "Error"),或者套接字继续侦听但阻塞并且没有任何反应。
如何使用原始套接字接收 UDP 数据包?
我的代码:
#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();
}
正如原始套接字手册所说 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.
从手册中提取的另一个重要说明是:
Only processes with an effective user ID of 0 or the CAP_NET_RAW capability are allowed to open raw sockets.
手册也说:
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).
好的,假设您需要 IP/UPD header,我们开始工作吧:-)
首先,我们需要明确几点:
- 在您上面的代码中,缺少一些
#include ...
header。
- IPPROTO_RAW(如手册所述)不能用于接收所有协议。
- 为什么要定义 socklen?例如,您可以在 bind().
中使用它
char *msg[SIZE]
???这是一个字符指针数组!您只需要一个字符数组,如下所示:char msg[SIZE]
.
- 请记住,您使用的是 RAW 套接字,从这些套接字接收的数据包带有 headers。要打印您的消息,您需要在 msg 中进行偏移,对应于 ip header 加上 updheader。 (在下面的代码中,请注意我添加了
#include <linux/ip.h>
和 #include <linux/udp.h>
以获得 header 的大小)。
- 最后,进行清理:在这种情况下,仅 close() 套接字 :-)
代码...
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] = '[=10=]'; /* 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;
}
好的,现在编译程序:
user@host:~$ gcc -o main main.c
以 root 身份执行:
root@host:~# ./main
并在另一个终端发送消息 nc:
-u 指定 UPD 为 nc
user@host:~$ nc -u 127.0.0.1 9090
就是这样!
我正在编写原始套接字客户端(成功发送 UDP 数据包)和服务器套接字,问题出在服务器部分。
我按以下方式创建套接字:
int raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
我也用 IPPROTO_RAW 尝试过,但得到相同的结果,我将其绑定:
bind(raw_socket, (struct sockaddr*)&sockstr, sizeof(sockstr))
当尝试使用套接字接收一些数据包时,我收到的唯一有效负载是 "E"(我认为这意味着 "Error"),或者套接字继续侦听但阻塞并且没有任何反应。 如何使用原始套接字接收 UDP 数据包? 我的代码:
#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();
}
正如原始套接字手册所说 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.
从手册中提取的另一个重要说明是:
Only processes with an effective user ID of 0 or the CAP_NET_RAW capability are allowed to open raw sockets.
手册也说:
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).
好的,假设您需要 IP/UPD header,我们开始工作吧:-)
首先,我们需要明确几点:
- 在您上面的代码中,缺少一些
#include ...
header。 - IPPROTO_RAW(如手册所述)不能用于接收所有协议。
- 为什么要定义 socklen?例如,您可以在 bind(). 中使用它
char *msg[SIZE]
???这是一个字符指针数组!您只需要一个字符数组,如下所示:char msg[SIZE]
.- 请记住,您使用的是 RAW 套接字,从这些套接字接收的数据包带有 headers。要打印您的消息,您需要在 msg 中进行偏移,对应于 ip header 加上 updheader。 (在下面的代码中,请注意我添加了
#include <linux/ip.h>
和#include <linux/udp.h>
以获得 header 的大小)。 - 最后,进行清理:在这种情况下,仅 close() 套接字 :-)
代码...
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] = '[=10=]'; /* 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;
}
好的,现在编译程序:
user@host:~$ gcc -o main main.c
以 root 身份执行:
root@host:~# ./main
并在另一个终端发送消息 nc:
-u 指定 UPD 为 nc
user@host:~$ nc -u 127.0.0.1 9090
就是这样!