首页 文章

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

提问于
浏览
2

我正在编写原始套接字客户端(成功发送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();
}

1 回答

  • 2

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

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

    从手册中提取的另一个重要说明是:

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

    手册也说:

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

    好的,假设你需要手头有IP / UPD Headers ,让我们去上班:-)

    首先,我们需要澄清一些观点:

    • 在上面的代码中,缺少某些 #include ... 标头 .

    • IPPROTO_RAW(如手册所述)不能用于接收所有协议 .

    • 为什么要定义socklen?例如,您可以在bind()中使用它 .

    • char *msg[SIZE] ???这是一个char指针数组!你只需要一个字符数组,如: char msg[SIZE] .

    • 请记住,您正在使用RAW套接字,并且从这些套接字接收的数据包附带 headers . 要打印消息,您需要在msg中执行偏移,该偏移对应于ip header和upd标头 . (在下面的代码中,请注意我已添加 #include <linux/ip.h>#include <linux/udp.h> 以获取标头的大小) .

    • 最后,做一个清理:在这种情况下只关闭()套接字:-)

    代码......


    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;
    }
    

    好的,现在编译程序:
    user@host:~$ gcc -o main main.c

    以root身份执行:
    root@host:~# ./main

    并在另一个终端发送带有nc的消息:
    -u指定UPD到nc
    user@host:~$ nc -u 127.0.0.1 9090

    这就对了!

相关问题