使用数据包套接字接收广播数据包

发布于 2024-08-28 11:41:58 字数 4825 浏览 7 评论 0原文

我尝试向网络发送 DHCP RENEW 数据包并接收响应。我广播该数据包,可以看到它已使用 Wireshark 成功发送。但我在接收响应时遇到困难。我使用数据包套接字来捕获数据包。我可以看到使用 Wireshark 对我的 RENEW 数据包有响应,但我的函数“packet_receive_renew”有时捕获数据包,但有时无法捕获数据包。我使用 FDSET 设置文件描述符,但代码中的“选择”无法意识到该文件描述符有新数据包,并且发生超时。我无法弄清楚为什么它有时捕获数据包有时不捕获。 有人有主意吗? 提前致谢。

这是接收函数。

int packet_receive_renew(struct client_info* info)
{
    int fd;
    struct sockaddr_ll sock, si_other;
    struct sockaddr_in si_me;
    fd_set rfds;
    struct timeval tv;
    time_t start, end;
    int bcast = 1;

    int ret = 0, try = 0;
    char buf[1500] = {'\0'};
    uint8_t tmp[BUFLEN] = {'\0'};
    struct dhcp_packet pkt;
    socklen_t slen = sizeof(si_other);
    struct dhcps* new_dhcps;

    memset((char *) &si_me, 0, sizeof(si_me));
    memset((char *) &si_other, 0, sizeof(si_other));
    memset(&pkt, 0, sizeof(struct dhcp_packet));

#define SERVER_AND_CLIENT_PORTS  ((67 << 16) + 68)

    static const struct sock_filter filter_instr[] = {
        /* check for udp */
        BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
        BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 4),     /* L5, L1, is UDP? */
        /* skip IP header */
        BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0),                     /* L5: */
        /* check udp source and destination ports */
        BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0),
        BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1), /* L3, L4 */
        /* returns */
        BPF_STMT(BPF_RET|BPF_K, 0x0fffffff ),                   /* L3: pass */
        BPF_STMT(BPF_RET|BPF_K, 0),                             /* L4: reject */
    };

    static const struct sock_fprog filter_prog = {
        .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
        /* casting const away: */
        .filter = (struct sock_filter *) filter_instr,
    };

    printf("opening raw socket on ifindex %d\n", info->interf.if_index);

    if (-1==(fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))))
    {
        perror("packet_receive_renew::socket");
        return -1;
    }

    printf("got raw socket fd %d\n", fd);

    /* Use only if standard ports are in use */
    /* Ignoring error (kernel may lack support for this) */
    if (-1==setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)))
        perror("packet_receive_renew::setsockopt");

    sock.sll_family = AF_PACKET;
    sock.sll_protocol = htons(ETH_P_IP);
    //sock.sll_pkttype = PACKET_BROADCAST;
    sock.sll_ifindex = info->interf.if_index;
    if (-1 == bind(fd, (struct sockaddr *) &sock, sizeof(sock))) {
        perror("packet_receive_renew::bind");
        close(fd);
        return -3;
    }

    if (-1 == setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast))) {
        perror("packet_receive_renew::setsockopt");
        close(fd);
        return -1;
    }

    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);
    tv.tv_sec = TIMEOUT;
    tv.tv_usec = 0;
    ret = time(&start);
    if (-1 == ret) {
        perror("packet_receive_renew::time");
        close(fd);
        return -1;
    }

    while(1) {
        ret = select(fd + 1, &rfds, NULL, NULL, &tv);
        time(&end);
        if (TOTAL_PENDING <= (end - start)) {
            fprintf(stderr, "End receiving\n");
            break;
        }
        if (-1 == ret)
        {
            perror("packet_receive_renew::select");
            close(fd);
            return -4;
        }
        else if (ret) {
            new_dhcps = (struct dhcps*)calloc(1, sizeof(struct dhcps));
            if (-1 == recvfrom(fd, buf, 1500, 0, (struct sockaddr*)&si_other, &slen)) {
                perror("packet_receive_renew::recvfrom");
                close(fd);
                return -4;
            }
            deref_packet((unsigned char*)buf, &pkt, info);
            if (-1!=(ret=get_option_val(pkt.options, DHO_DHCP_SERVER_IDENTIFIER, tmp))) {
                sprintf((char*)tmp, "%d.%d.%d.%d", tmp[0],tmp[1],tmp[2],tmp[3]);
                fprintf(stderr, "Received renew from %s\n", tmp);
            }
            else
            {
                fprintf(stderr, "Couldnt get DHO_DHCP_SERVER_IDENTIFIER%s\n", tmp);
                close(fd);
                return -5;
            }
            new_dhcps->dhcps_addr = strdup((char*)tmp);

            //add to list
            if (info->dhcps_list)
                info->dhcps_list->next = new_dhcps;
            else
                info->dhcps_list = new_dhcps;
            new_dhcps->next = NULL;
        }
        else {
            try++;
            tv.tv_sec = TOTAL_PENDING - try * TIMEOUT;
            tv.tv_usec = 0;
            fprintf(stderr, "Timeout occured\n");
        }
    }
    close(fd);
    printf("close fd:%d\n", fd);
    return 0;
}

I try to send DHCP RENEW packets to the network and receive the responses. I broadcast the packet and I can see that it's successfully sent using Wireshark. But I have difficulties receiving the responses.I use packet sockets to catch the packets. I can see that there are responses to my RENEW packet using Wireshark, but my function 'packet_receive_renew' sometimes catch the packets but sometimes it can not catch the packets. I set the file descriptor using FDSET but the 'select' in my code can not realize that there are new packets for that file descriptor and timeout occurs. I couldn't make it clear that why it sometimes catches the packets and sometimes doesn't.
Anybody have an idea?
Thanks in advance.

Here's the receive function.

int packet_receive_renew(struct client_info* info)
{
    int fd;
    struct sockaddr_ll sock, si_other;
    struct sockaddr_in si_me;
    fd_set rfds;
    struct timeval tv;
    time_t start, end;
    int bcast = 1;

    int ret = 0, try = 0;
    char buf[1500] = {'\0'};
    uint8_t tmp[BUFLEN] = {'\0'};
    struct dhcp_packet pkt;
    socklen_t slen = sizeof(si_other);
    struct dhcps* new_dhcps;

    memset((char *) &si_me, 0, sizeof(si_me));
    memset((char *) &si_other, 0, sizeof(si_other));
    memset(&pkt, 0, sizeof(struct dhcp_packet));

#define SERVER_AND_CLIENT_PORTS  ((67 << 16) + 68)

    static const struct sock_filter filter_instr[] = {
        /* check for udp */
        BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
        BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 4),     /* L5, L1, is UDP? */
        /* skip IP header */
        BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0),                     /* L5: */
        /* check udp source and destination ports */
        BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0),
        BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1), /* L3, L4 */
        /* returns */
        BPF_STMT(BPF_RET|BPF_K, 0x0fffffff ),                   /* L3: pass */
        BPF_STMT(BPF_RET|BPF_K, 0),                             /* L4: reject */
    };

    static const struct sock_fprog filter_prog = {
        .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
        /* casting const away: */
        .filter = (struct sock_filter *) filter_instr,
    };

    printf("opening raw socket on ifindex %d\n", info->interf.if_index);

    if (-1==(fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))))
    {
        perror("packet_receive_renew::socket");
        return -1;
    }

    printf("got raw socket fd %d\n", fd);

    /* Use only if standard ports are in use */
    /* Ignoring error (kernel may lack support for this) */
    if (-1==setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)))
        perror("packet_receive_renew::setsockopt");

    sock.sll_family = AF_PACKET;
    sock.sll_protocol = htons(ETH_P_IP);
    //sock.sll_pkttype = PACKET_BROADCAST;
    sock.sll_ifindex = info->interf.if_index;
    if (-1 == bind(fd, (struct sockaddr *) &sock, sizeof(sock))) {
        perror("packet_receive_renew::bind");
        close(fd);
        return -3;
    }

    if (-1 == setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast))) {
        perror("packet_receive_renew::setsockopt");
        close(fd);
        return -1;
    }

    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);
    tv.tv_sec = TIMEOUT;
    tv.tv_usec = 0;
    ret = time(&start);
    if (-1 == ret) {
        perror("packet_receive_renew::time");
        close(fd);
        return -1;
    }

    while(1) {
        ret = select(fd + 1, &rfds, NULL, NULL, &tv);
        time(&end);
        if (TOTAL_PENDING <= (end - start)) {
            fprintf(stderr, "End receiving\n");
            break;
        }
        if (-1 == ret)
        {
            perror("packet_receive_renew::select");
            close(fd);
            return -4;
        }
        else if (ret) {
            new_dhcps = (struct dhcps*)calloc(1, sizeof(struct dhcps));
            if (-1 == recvfrom(fd, buf, 1500, 0, (struct sockaddr*)&si_other, &slen)) {
                perror("packet_receive_renew::recvfrom");
                close(fd);
                return -4;
            }
            deref_packet((unsigned char*)buf, &pkt, info);
            if (-1!=(ret=get_option_val(pkt.options, DHO_DHCP_SERVER_IDENTIFIER, tmp))) {
                sprintf((char*)tmp, "%d.%d.%d.%d", tmp[0],tmp[1],tmp[2],tmp[3]);
                fprintf(stderr, "Received renew from %s\n", tmp);
            }
            else
            {
                fprintf(stderr, "Couldnt get DHO_DHCP_SERVER_IDENTIFIER%s\n", tmp);
                close(fd);
                return -5;
            }
            new_dhcps->dhcps_addr = strdup((char*)tmp);

            //add to list
            if (info->dhcps_list)
                info->dhcps_list->next = new_dhcps;
            else
                info->dhcps_list = new_dhcps;
            new_dhcps->next = NULL;
        }
        else {
            try++;
            tv.tv_sec = TOTAL_PENDING - try * TIMEOUT;
            tv.tv_usec = 0;
            fprintf(stderr, "Timeout occured\n");
        }
    }
    close(fd);
    printf("close fd:%d\n", fd);
    return 0;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

巾帼英雄 2024-09-04 11:41:58

我已经解决了这个问题。我认为这是一个时间问题。我打开监听套接字并在发送消息之前绑定它。因此,它可以毫无问题地捕获消息。
谢谢。

I've solved the problem. I think it was an timing issue. I opened the listening socket and bind it before sending the message. So, it can catch the message without problem.
Thanks.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文