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

Ubuntu 16.04下AF_PACKET套接字捕获指定网卡数据包异常求助

问题描述

在Ubuntu 16.04系统中,我尝试捕获指定以太网接口的所有数据包:打开AF_PACKET套接字并将指定接口设置为混杂模式后,虽能捕获该接口的数据包,但同时也会接收到其他以太网接口(如eth0)的数据包。

我尝试调用setsockopt并使用PACKET_DROP_MEMBERSHIP来过滤eth0接口,但并未生效,仍会捕获到eth0的数据包。

相关代码如下:

// The following code opens socket and set the specified intf to PACKET_MR_PROMISC
// However, it also picks up packets from all other interfaces, for example, eth0

int fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

int interface_number = if_nametoindex(interface_name.c_str());
if (interface_number == -1) {
    syslog(LOG_ERR, "Can't get interface number for %s.\n", interface_name.c_str());
    return -1;
}
per_socket_data->ifindex = interface_number;
syslog(LOG_INFO, "intf %s, ifindex %d. \n", interface_name.c_str(), interface_number);

// Switch to PROMISC mode
struct packet_mreq sock_params;
memset(&sock_params, 0, sizeof(sock_params));
sock_params.mr_type = PACKET_MR_PROMISC;
sock_params.mr_ifindex = interface_number;
   
int set_promisc = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, (void *)&sock_params, sizeof(sock_params));


// The following code tries to prevent capturing packets from eth0. However, this does not work.
int eth0_interface_number = if_nametoindex(eth0.c_str());
if (eth0_interface_number == -1) {
    syslog(LOG_ERR, "Can't get interface number for %s.\n", eth0.c_str());
    return -1;
}
syslog(LOG_INFO, "intf %s, ifindex %d. \n", eth0.c_str(), eth0_interface_number);

memset(&sock_params, 0, sizeof(sock_params));
sock_params.mr_ifindex = eth0_interface_number;

int exclude_eth0 = setsockopt(fd, SOL_PACKET, PACKET_DROP_MEMBERSHIP, (void *)&sock_params, sizeof(sock_params));
解决方法

问题根源在于AF_PACKET套接字的默认行为:创建SOCK_RAW类型的AF_PACKET套接字时,默认会接收所有接口的数据包,PACKET_DROP_MEMBERSHIP仅用于移除通过PACKET_ADD_MEMBERSHIP加入的组(如多播组),无法直接过滤接口。

正确方案是将套接字绑定到目标接口,限制仅接收该接口的数据包,具体步骤如下:

  1. 创建套接字后,使用sockaddr_ll结构体绑定到指定接口
  2. 保持混杂模式设置以捕获该接口的所有数据包

修改后的代码示例:

int fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

int interface_number = if_nametoindex(interface_name.c_str());
if (interface_number == -1) {
    syslog(LOG_ERR, "Can't get interface number for %s.\n", interface_name.c_str());
    return -1;
}
per_socket_data->ifindex = interface_number;
syslog(LOG_INFO, "intf %s, ifindex %d. \n", interface_name.c_str(), interface_number);

// 绑定到指定接口
struct sockaddr_ll sll;
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_protocol = htons(ETH_P_ALL);
sll.sll_ifindex = interface_number;

if (bind(fd, (struct sockaddr *)&sll, sizeof(sll)) == -1) {
    syslog(LOG_ERR, "Failed to bind to interface %s: %s\n", interface_name.c_str(), strerror(errno));
    close(fd);
    return -1;
}

// 设置混杂模式
struct packet_mreq sock_params;
memset(&sock_params, 0, sizeof(sock_params));
sock_params.mr_type = PACKET_MR_PROMISC;
sock_params.mr_ifindex = interface_number;
   
int set_promisc = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, (void *)&sock_params, sizeof(sock_params));
if (set_promisc == -1) {
    syslog(LOG_ERR, "Failed to set promiscuous mode: %s\n", strerror(errno));
    close(fd);
    return -1;
}

关键说明:

  • sockaddr_ll是链路层专用的地址结构体,通过sll_ifindex指定目标接口索引,绑定后套接字只会接收该接口的数据包
  • 绑定操作与混杂模式设置的顺序不影响效果,但建议先绑定再设置混杂模式
  • 无需再使用PACKET_DROP_MEMBERSHIP,绑定操作已自动过滤其他接口的数据包

内容的提问来源于stack exchange,提问作者Charles Ju

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 13:14:59