c - 我已经使用 ifconfig 将以太网和 Wifi 设置为混杂模式,但我仍然只收到来自和发送到我的计算机的数据包

标签 c sockets networking

我正在尝试使用原始套接字创建一个应用程序来监视网络流量,但我似乎只收到从我的计算机发送和发送到我的计算机的数据包。我无法找出其中的问题,但当我检查时我发现配置中没有数据通过我的 enps20,而只通过 wlp5s0,但在我的代码中,我假设它是以太网协议(protocol)。这是问题吗?如果是这样,我应该检查 wlp5s0 的协议(protocol)是什么?

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;

& 使 eth 将变量 buffer 本身视为存储;实际存储是变量指向的位置。

<小时/>

正如问题中提到的,对于所监听的接口(interface)存在一种困惑,我在这里提供了一种选择接口(interface)的方法。

很久以前,我曾经在一个特定的网络接口(interface)上以混杂模式打开一个数据包套接字,如下所示:

#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)到接口(interface)时,我们必须为 bind() 提供一个地址结构,告诉套接字将绑定(bind)到哪个网络接口(interface)。
这个特定的结构类型是 struct sockaddr_ll(与我们对 ipv4 套接字使用 struct sockaddr_in 的方式相同)和相关字段 if sll_ifindex(整数索引) ).
该索引由操作系统在枚举网络设备时给出;我们通过之前对 ioctl(SIOCGIFINDEX) 的调用来了解它,它根据接口(interface)名称(struct ifreqifr_name 字段)检索它>).

关于c - 我已经使用 ifconfig 将以太网和 Wifi 设置为混杂模式,但我仍然只收到来自和发送到我的计算机的数据包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57099758/

相关文章:

java - 该循环将读取输入流多少次?

Java套接字: Pushing requests to client through socket

c - 在 GTK 回调中发送数据

c - 使用 vmalloc 为内核模块分配大量内存

c - 如何在C中返回缓冲区地址

python - 测量 200 个并发用户的数据传输速率

web-services - 如何使 localhost(XAMPP) 从任何计算机访问?

c - 为什么使用 DDD 时没有出现设置断点挂起的选项?

c - 如何制作 "careless"套接字?

c++ - Unix IPC 套接字发送/接收同步