You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何绑定UDP套接字仅接收特定源IP(192.168.0.3)的数据包?

解决方法:仅接收指定源IP和端口的UDP数据包

你之前的误解在于,bind函数中设置的servaddr.sin_addr是本地网卡的监听地址,用来指定套接字监听哪个本地IP的端口,而不是过滤发送方的源地址。所以绑定对方IP或者设备名是无效的,因为bind不负责源地址过滤。以下是三种可行的实现方案:

方案1:接收后手动过滤(最简单直接)

每次通过recvfrom接收数据包时,获取发送方的地址信息,判断是否符合192.168.0.3:6516的条件,不符合则直接丢弃。这种方法逻辑清晰,容易调试,适合大多数场景。

示例代码:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// 假设gOptions已初始化,包含sockfd等字段
void handle_udp_packets() {
    struct sockaddr_in peer_addr;
    socklen_t peer_len = sizeof(peer_addr);
    char buf[1024];
    ssize_t recv_len;

    // 预定义目标源IP和端口
    const in_addr_t target_src_ip = inet_addr("192.168.0.3");
    const uint16_t target_src_port = htons(6516);

    while (1) {
        recv_len = recvfrom(gOptions.sockfd, buf, sizeof(buf), 0,
                            (struct sockaddr *)&peer_addr, &peer_len);
        if (recv_len < 0) {
            perror("recvfrom failed");
            continue;
        }

        // 校验源IP和端口
        if (peer_addr.sin_family == AF_INET &&
            peer_addr.sin_port == target_src_port &&
            peer_addr.sin_addr.s_addr == target_src_ip) {
            // 处理符合条件的数据包
            printf("Received valid packet from 192.168.0.3:6516, length: %zd\n", recv_len);
        } else {
            // 丢弃不符合条件的数据包
            continue;
        }
    }
}

方案2:BPF套接字过滤器(内核层面过滤)

通过setsockopt给套接字附加BPF(Berkeley Packet Filter)规则,让内核直接过滤掉不符合条件的数据包,减少用户态的无效处理开销。适合对性能要求较高的场景。

示例代码:

#include <linux/filter.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void attach_bpf_filter(int sockfd) {
    // BPF规则:仅允许源IP=192.168.0.3,源端口=6516的UDP包
    struct sock_filter filter_rules[] = {
        // 检查以太网帧类型是否为IP(0x0800)
        BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0800, 0, 5),

        // 检查IP协议是否为UDP(17)
        BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 3),

        // 检查UDP源端口是否为6516(网络字节序0x1984)
        BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 + 16), // IP头固定20字节,UDP源端口偏移16
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x1984, 0, 1),

        // 检查IP源地址是否为192.168.0.3(网络字节序0xc0a80003)
        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 20 + 12), // IP源地址偏移12
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xc0a80003, 0, 1),

        // 允许接收符合条件的数据包(返回最大长度)
        BPF_STMT(BPF_RET + BPF_K, 0xFFFF),
        // 丢弃不符合条件的数据包
        BPF_STMT(BPF_RET + BPF_K, 0)
    };

    struct sock_fprog filter_prog = {
        .len = sizeof(filter_rules) / sizeof(filter_rules[0]),
        .filter = filter_rules
    };

    if (setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)) < 0) {
        perror("Failed to attach BPF filter");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
}

// 使用方式:在bind之后调用attach_bpf_filter(gOptions.sockfd);

方案3:iptables系统层面过滤(全局生效)

如果希望整个系统仅接收来自192.168.0.3:6516、发往本地6516端口的UDP包,可以添加iptables规则。注意:该规则是全局的,会影响所有监听该端口的程序。

执行以下命令:

# 仅对eno3网卡生效,丢弃非192.168.0.3:6516发往本地6516的UDP包
iptables -A INPUT -i eno3 -p udp --dport 6516 ! -s 192.168.0.3 --sport 6516 -j DROP

如果需要永久保存规则,可根据你的Linux发行版执行对应的保存命令(如iptables-save > /etc/iptables/rules.v4)。


内容的提问来源于stack exchange,提问作者Stone Cooper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 11:05:15