无法接收原始套接字的数据包

Yom*_*shi 4 c sockets udp raw-sockets

我正在编写原始套接字客户端(成功发送UDP数据包)和服务器套接字,问题出在服务器部分。

我通过以下方式创建套接字:

int raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);

我也用 IPPROTO_RAW 尝试过,但得到相同的结果,我正在绑定它:

bind(raw_socket, (struct sockaddr*)&sockstr, sizeof(sockstr))

当尝试使用套接字接收一些数据包时,我收到的唯一有效负载是“E”(我认为这意味着“错误”),或者套接字继续侦听但阻塞并且没有任何反应。如何使用原始套接字接收 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();
}
Run Code Online (Sandbox Code Playgroud)

Ric*_*ali 7

正如原始套接字手册所说user@host:~$ man 7 raw

\n
\n

IPPROTO_RAW 协议意味着启用了 IP_HDRINCL,并且能够发送在传递的标头中指定的任何 IP 协议。 使用原始套接字不可能通过 IPPROTO_RAW 接收所有 IP 协议。

\n
\n

从手册中摘录的另一个重要注释是:

\n
\n

仅允许有效用户 ID 为 0 或具有 CAP_NET_RAW 功能的进程打开原始套接字。

\n
\n

手册也说:

\n
\n

从 Linux 2.2 开始,所有 IP 标头字段和选项都可以使用 IP 套接字选项进行设置。这意味着通常只有新协议或没有用户界面的协议(如 ICMP)才需要原始套接字。

\n
\n

好的,假设您需要掌握 IP/UDP 标头,那么让我们开始工作吧:-)

\n首先,我们需要明确一些要点:

\n
    \n
  • 在你上面的代码中一些#include ...缺少一些标头。
  • \n
  • IPPROTO_RAW(如手册所述)不能用于接收所有协议。
  • \n
  • 为什么定义 socklen?例如,您可以在bind()中使用它中使用它。
  • \n
  • char *msg[SIZE]???\xc2\xa0\xc2\xa0\xc2\xa0 这是一个 char 指针数组!您只需要一个字符数组,如下所示:char msg[SIZE]
  • \n
  • 请记住,您使用的是原始套接字,并且从这些套接字接收到的数据包带有headers要打印消息,您需要在msg中进行偏移量,该偏移量对应于ip标头加上udp标头。(在下面的代码中,请注意我已经添加#include <linux/ip.h>#include <linux/udp.h>来获取标题的大小)。
  • \n
  • 最后,进行清理:在本例中仅close()套接字:-)
  • \n
\n

代码 ...

\n
\n

主程序

\n
#include <arpa/inet.h>\n#include <netinet/in.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <unistd.h>\n\n#include <linux/ip.h> /* for ipv4 header */\n#include <linux/udp.h> /* for udp header */\n\n#define ADDR_TO_BIND "127.0.0.1"\n#define PORT_TO_BIND 9090\n\n#define MSG_SIZE 256\n#define HEADER_SIZE (sizeof(struct iphdr) + sizeof(struct udphdr))\n\nint main(void) {\n    int raw_socket;\n    struct sockaddr_in sockstr;\n    socklen_t socklen;\n\n    int retval = 0; /* the return value (give a look when an error happens)\n                     */\n\n    /* no pointer to array!\n     * >> It was like "a variable that contains an address -- and in this\n     *    address begins an array of chars"! */\n    /* now it is simple an array of chars :-)  */\n    char msg[MSG_SIZE];\n    ssize_t msglen; /* return value from recv() */\n\n    /* do not use IPPROTO_RAW to receive packets */\n    if ((raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) == -1) {\n        perror("socket");\n        return 1; /* here there is no clean up -- retval was not used */\n    }\n\n    sockstr.sin_family = AF_INET;\n    sockstr.sin_port = htons(PORT_TO_BIND);\n    sockstr.sin_addr.s_addr = inet_addr(ADDR_TO_BIND);\n    socklen = (socklen_t) sizeof(sockstr);\n\n    /* use socklen instead sizeof()  Why had you defined socklen? :-)  */\n    if (bind(raw_socket, (struct sockaddr*) &sockstr, socklen) == -1) {\n        perror("bind");\n        retval = 1; /* '1' means "Error" */\n        goto _go_close_socket;\n    }\n\n    memset(msg, 0, MSG_SIZE);\n\n    if ((msglen = recv(raw_socket, msg, MSG_SIZE, 0)) == -1) {\n        perror("recv");\n        retval = 1;\n        goto _go_close_socket;\n    }\n\n    if (msglen <= HEADER_SIZE) /* msg  can't be lesser than header! */\n        printf("No msg!\\n");\n    else {\n        msg[msglen - 1] = '\\0'; /* we need a null character at the end*/\n        printf("Your msg _plus_ headers's size is: %s\\n",\n               msg + HEADER_SIZE);\n    }\n\n_go_close_socket:\n    close(raw_socket);\n\n    return retval;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

好的,现在用以下命令编译程序:
\n user@host:~$ gcc -o main main.c

\n以 root 身份执行:
\n \n并在另一个终端中使用ncroot@host:~# ./main

发送一条消息:\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0 -u指定UDP 到nc \n \n就是这样!

user@host:~$ nc -u 127.0.0.1 9090

\n