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

时间:2019-07-18 17:21:46

标签: c sockets networking

我正在尝试使用原始套接字创建一个应用程序来监视网络流量,但我似乎只从计算机接收和发送正在处理的数据包。我无法找出其中的问题,但是当我检查时配置发现我没有数据通过我的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;
}

1 个答案:

答案 0 :(得分:0)

注意:我可以在问题提供的程序中看到至少两个错误。

n=(int)recvfrom(sockfd,buffer,65536/*sizeof(buffer)*/,0,&addr,&len);

sizeof(buffer)是指针的大小,而不是分配的数量。

eth=(struct ethhdr*)/*&*/buffer;

&使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(与ipv4套接字使用struct sockaddr_in的方式相同)和相关字段if sll_ifindex(整数索引)。
该索引由操作系统在枚举网络设备时给定;由于先前对ioctl(SIOCGIFINDEX)的调用会根据接口名称(ifr_name的{​​{1}}字段)对其进行检索,因此我们了解了它。