csocketsnetwork-programmingethernetraw-ethernet

I can not see my networks raw packets in C


I have two virtual machines in vmware infrastructure (workstation) running Ubuntu 22.04, that are in the same network. With one program I am sending in one virtual machine raw packets, with a MAC that does not fit with any of the computers in the network (is a requirement). In the other virtual machine I can see the raw packets in Wireshark but my C program can not see it. I check the Iptables rules and there is no denied to raw packets. Here is the code I use to open the raw socket and read:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <arpa/inet.h>
#include <linux/filter.h>
#include <fcntl.h>

int main(int arg, char** argv[])
{
    int s, stat, cc;                         
    unsigned char buf[ETH_FRAME_LEN];                                          
    struct sockaddr_ll saddr;
    struct ifreq ifr;
    char *interface = "ens33";

    s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (s < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    strncpy(ifr.ifr_name, interface, IFNAMSIZ);
    if(ioctl(s, SIOGIFINDEX, &ifr))
    {
        perror("ioctl");
        close(s);
        exit(EXIT_FAILURE);
    }

    saddr.sll_family = AF_PACKET;
    saddr.sll_ifindex = ifr.ifr_ifindex;
    saddr.sll_protocol = htons(ETH_P_ALL);

    if (bind(s, (struct sockaddr *)&saddr, sizeof(saddr)))
    {
    perror("bind");
    exit(EXIT_FAILURE);
    }

    stat = fcntl (s, F_SETFL, O_NONBLOCK);
    if (stat < 0)
    perror ("bloqueante");

        while(1)
    {
        cc = read (s, buf, sizeof(buf));
        // Process the information
    }
}

I tried to use libpcap and works, but because of the infrastructure of the system I am not able to change the way the functions are used. I have one function to open the socket and another to read. This functions are used for other modules and it is not a possibility.

I change the MAC and the same problems. The virtual machines can see each other.


Solution

  • Set interface to promiscuous mode to receive packets not bound for it's MAC address.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <sys/ioctl.h>
    #include <net/if.h>
    #include <linux/if_packet.h>
    #include <net/ethernet.h>
    #include <arpa/inet.h>
    #include <linux/filter.h>
    #include <fcntl.h>
    
    int main(int arg, char* argv[])
    {
        int s, stat;
        size_t cc;
        unsigned char buf[ETH_FRAME_LEN];
        struct sockaddr_ll saddr = {};
        struct ifreq ifopts;    //for promiscuous mode 
        struct ifreq ifr = {};
        const char *interface = "ens33";
    
        s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
        if (s < 0) {
            perror("socket");
            exit(EXIT_FAILURE);
        }
        
        //Set Promiscuous Mode
        strncpy(ifopts.ifr_name, interface, IFNAMSIZ-1);
        ioctl(s, SIOCGIFFLAGS, &ifopts);
        ifopts.ifr_flags |= IFF_PROMISC;
        ioctl(s, SIOCSIFFLAGS, &ifopts);
    
        strncpy(ifr.ifr_name, interface, IFNAMSIZ-1);
        if(ioctl(s, SIOGIFINDEX, &ifr))
        {
            perror("ioctl");
            close(s);
            exit(EXIT_FAILURE);
        }
    
        saddr.sll_family = AF_PACKET;
        saddr.sll_ifindex = ifr.ifr_ifindex;
        saddr.sll_protocol = htons(ETH_P_ALL);
    
        if (bind(s, (struct sockaddr *)&saddr, sizeof(saddr)))
        {
            perror("bind");
            exit(EXIT_FAILURE);
        }
    
        stat = fcntl (s, F_SETFL, O_NONBLOCK);
        if (stat < 0)
            perror ("bloqueante");
    
        while(true)
        {
            cc = read (s, buf, sizeof(buf));
            // Process the information
        }
    }