c - 我已经使用 ifconfig 将我的以太网和 Wifi 设置为混杂模式,但我仍然只收到来自和发往我的计算机的数据包
问题描述
我正在尝试使用原始套接字创建一个应用程序来监视网络流量,但我似乎只获取来自和发往我的计算机的数据包。我无法找出其中的问题,但是当我检查配置时我发现没有数据通过我的 enps20,但只通过 wlp5s0,但在我的代码中,我假设它是一个以太网协议。这是问题吗?如果是这样,我应该检查什么协议来检查 wlp5s0?
void error(int n,char* msg)
{
if(n==-1)
{
perror(msg);
exit(0);
}
}
void eth_hdr(struct ethhdr *eth)
{
printf("Ethernet Header : \n");
printf("Source MAC:%.2x.%.2x.%.2x.%.2x.%.2x%.2x\t",eth->h_source[0],eth->h_source[1],eth->h_source[2],eth->h_source[3],eth->h_source[4],eth->h_source[5]);
printf("Dest. MAC:%.2x.%.2x.%.2x.%.2x.%.2x%.2x\t",eth->h_dest[0],eth->h_dest[1],eth->h_dest[2],eth->h_dest[3],eth->h_dest[4],eth->h_dest[5]);
printf("Proto:%d\n",eth->h_proto);
}
void ip_hdr(struct iphdr *iph)
{
char address[INET6_ADDRSTRLEN];
struct sockaddr_in source,destination;
printf("IP Header : \n");
printf("%x\t",iph->version);
printf("%x\t",iph->protocol);
source.sin_addr.s_addr=iph->saddr;
destination.sin_addr.s_addr=iph->daddr;
inet_ntop(AF_INET,&source.sin_addr,address,INET_ADDRSTRLEN);
printf("Source Addr:%s\t",address);
inet_ntop(AF_INET,&destination.sin_addr,address,INET_ADDRSTRLEN);
printf("Destination Address:%s\n",address);
}
void udp_display(struct udphdr *udp)
{
struct sockaddr_in source,destination;
printf("UDP Header :\n");
printf("Source Port:%d\t",ntohs(udp->uh_sport));
printf("Destination Port:%d\t",ntohs(udp->uh_dport));
char address[INET6_ADDRSTRLEN];
source.sin_addr.s_addr=udp->source;
destination.sin_addr.s_addr=udp->dest;
inet_ntop(AF_INET,&source.sin_addr,address,INET_ADDRSTRLEN);
printf("Source Addr:%s\t",address);
inet_ntop(AF_INET,&destination.sin_addr,address,INET_ADDRSTRLEN);
printf("Destination Address:%s\n",address);
}
void tcp_display(struct tcphdr *tcp)
{
struct sockaddr_in source,destination;
printf("TCP Header :\n");
printf("Source Port:%d\t",ntohs(tcp->th_sport));
printf("Destination Port:%d\t",ntohs(tcp->th_dport));
char address[INET6_ADDRSTRLEN];
source.sin_addr.s_addr=tcp->source;
destination.sin_addr.s_addr=tcp->dest;
inet_ntop(AF_INET,&source.sin_addr,address,INET_ADDRSTRLEN);
printf("Source Addr:%s\t",address);
inet_ntop(AF_INET,&destination.sin_addr,address,INET_ADDRSTRLEN);
printf("Destination Address:%s\n",address);
}
int main()
{
int sockfd,n,on=1,iphdrlen;
struct ethhdr *eth;
struct iphdr *iph;
struct tcphdr *tcp;
struct udphdr *udp;
struct sockaddr addr,addr1;
struct sockaddr_ll address;
struct ifreq ifr;
socklen_t len;
unsigned char *buffer=(unsigned char*)malloc(65536);
unsigned char *data=(unsigned char*)malloc(65536);
memset(buffer,0,65536);
memset(&addr,0,sizeof(struct sockaddr));
memset(&addr1,0,sizeof(struct sockaddr));
memset(&address,0,sizeof(struct sockaddr_ll));
memset(&ifr,0,sizeof(struct ifreq));
sockfd=socket(AF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
error(sockfd,"Error in creating a socket");
char *opt="wlp5s0";
setsockopt(sockfd,SOL_SOCKET,SO_BINDTODEVICE,opt,len);
strncpy(ifr.ifr_name,opt,sizeof(ifr.ifr_name));
n=ioctl(sockfd,SIOGIFINDEX,&ifr);
error(n,"Trouble in getting the Index information");
address.sll_family=AF_PACKET;
address.sll_ifindex=ifr.ifr_ifindex;
address.sll_protocol=htons(ETH_P_ALL);
n=bind(sockfd,(struct sockaddr*)&address,sizeof(struct sockaddr_ll));
error(n,"There has been a problem in binding with that interface");
while (1)
{
n=recvfrom(sockfd,buffer,sizeof(buffer),0,&addr,&len);
error(n,"There has been a problem recieving the data");
eth=(struct ethhdr*)&buffer;
eth_hdr(eth);
iph=(struct iphdr*)(buffer+sizeof(struct ethhdr));
ip_hdr(iph);
iphdrlen=(iph->ihl)*4;
if(iph->protocol==6)
{
tcp=(struct tcphdr*)(buffer+sizeof(struct ethhdr)+iphdrlen);
tcp_display(tcp);
data=(buffer+sizeof(struct ethhdr)+iphdrlen+sizeof(struct udphdr));
}
else if(iph->protocol==17)
{
udp=(struct udphdr*)(buffer+sizeof(struct ethhdr)+iphdrlen);
udp_display(udp);
// data=(buffer+sizeof(struct ethhdr)+iphdrlen+sizeof(struct udphdr));
}
}
return 0;
}
解决方案
注意:我可以在问题中提供的程序中看到至少两个错误。
n=(int)recvfrom(sockfd,buffer,65536/*sizeof(buffer)*/,0,&addr,&len);
sizeof(buffer)
是指针的大小,而不是分配的数量。
eth=(struct ethhdr*)/*&*/buffer;
&
make将eth
变量buffer
本身视为存储;实际存储是变量指向的位置。
正如问题中提到的那样,对于所听的界面存在一种混淆,我在这里提供了一种选择界面的方法。
很久以前,我曾经在一个特定的网络接口上以混杂模式打开一个数据包套接字,如下所示:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <net/if.h>
#include <netpacket/packet.h>
const char * name="eth0";
//---- Create socket ----
int fd=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
if(fd==-1)
{ perror("socket"); return -1; }
//---- Find interface index ----
struct ifreq ifr;
memset(&ifr,0,sizeof(struct ifreq));
strncpy(ifr.ifr_name,name,sizeof(ifr.ifr_name));
if(ioctl(fd,SIOCGIFINDEX,&ifr)==-1)
{ perror("ioctl SIOCGIFINDEX"); close(fd); return -1; }
int index=ifr.ifr_ifindex;
//---- Bind socket to interface ----
struct sockaddr_ll addr;
memset(&addr,0,sizeof(struct sockaddr_ll));
addr.sll_family=AF_PACKET;
addr.sll_ifindex=index;
addr.sll_protocol=htons(ETH_P_ALL);
if(bind(fd,(struct sockaddr *)&addr,sizeof(struct sockaddr_ll))==-1)
{ perror("bind"); close(fd); return -1; }
//---- Determine MTU ----
memset(&ifr,0,sizeof(struct ifreq));
strncpy(ifr.ifr_name,name,sizeof(ifr.ifr_name));
if(ioctl(fd,SIOCGIFMTU,&ifr)==-1)
{ perror("ioctl SIOCGIFMTU"); close(fd); return -1; }
unsigned int mtu=ifr.ifr_mtu;
//---- Switch to promiscuous mode ----
struct packet_mreq mr;
memset(&mr,0,sizeof(struct packet_mreq));
mr.mr_ifindex=index;
mr.mr_type=PACKET_MR_PROMISC;
if(setsockopt(fd,SOL_PACKET,PACKET_ADD_MEMBERSHIP,
&mr,sizeof(struct packet_mreq))==-1)
{ perror("setsockopt PACKET_MR_PROMISC"); close(fd); return -1; }
对不起,有点长,但每个部分的评论应该是不言自明的。
ioctl()
有多种用途;这是有关 Linux 网络设备的文档:http:
//man7.org/linux/man-pages/man7/netdevice.7.html
将数据包套接字绑定到接口时,我们必须提供bind()
一个地址结构,告诉该套接字将绑定到哪个网络接口。
这个特定的结构类型是struct sockaddr_ll
(与我们struct sockaddr_in
用于 ipv4 套接字的方式相同)和相关字段 if sll_ifindex
(整数索引)。
该索引由操作系统在枚举网络设备时给出;我们通过之前的调用来了解它,该调用ioctl(SIOCGIFINDEX)
根据接口名称(ifr_name
的字段struct ifreq
)检索它。
推荐阅读
- c++ - 我可以只映射一个文件并修改它而不回写吗?
- python - 需要从另一个 txt 文件在现有 txt 文件中创建一个新列
- r - 如何根据多列值(重复值和字符串值)从 tibble 中删除行
- c# - WPF:仅更改 ListBox 中最后一项的 CornerRadius
- javascript - 根据单选按钮和复选框选择显示不同的文本
- c - scanf和gets有什么区别?
- websocket - 为什么我的 Socket.IO 服务器没有响应我的 Firecamp 和 C# 客户端?
- python - 如何在 Apache 服务器上托管 Dash 应用程序?
- c# - 如何使用 Powershell 在 Windows 10 上处理 LogonHours 属性?
- virtual-machine - 如何实现像 virtualbox 这样的虚拟化应用程序?