why my raw socket recv() seemed to get nothing?

158 Views Asked by At

Main Situation

I'm using raw socket trying to recv OSPF packet. Although I've successfully grabbed the packet through Wireshark, I could not receive anything through function recv(), where the thread just stuck.

the related thread function is as follows:

#define IPPROTO_OSPF (89)
// myconfigs::nic_name is "ens33", I'm sure it's correct (using it)

void* threadRecvPackets(void *intf) {
    Interface *interface = (Interface*) intf;
    int socket_fd;
    if ((socket_fd = socket(AF_INET, SOCK_RAW, IPPROTO_OSPF)) < 0) {
        perror("[Thread]RecvPacket: socket_fd init");
    }

    /* Bind sockets to certain Network Interface */
    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    strcpy(ifr.ifr_name, myconfigs::nic_name);
    if (setsockopt(socket_fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) {
        perror("[Thread]RecvPacket: setsockopt - bind to device");
    }

    /* Add to OSPF multicast */
    struct ip_mreq mreq;
    memset(&mreq, 0, sizeof(mreq));
    mreq.imr_interface.s_addr = htonl(interface->ip);
    mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.5");
    if (setsockopt(socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
        perror("[Thread]RecvPacket: setsockopt - multicast ip add membership");
    }

    /* Set Source Address - useless */
    // struct sockaddr_in src_addr;
    // memset(&src_addr, 0, sizeof(src_addr));
    // src_addr.sin_family = AF_INET;
    // src_addr.sin_addr.s_addr = htonl(INADDR_ANY);

#ifdef DEBUG
    printf("[Thread]RecvPacket init\n");
#endif

#define RECV_LEN 1024
    char* packet_rcv = (char*)malloc(RECV_LEN);
    while (true) {
        memset(packet_rcv, 0, RECV_LEN);

        #ifdef DEBUG
            printf("[Thread]RecvPacket: start to recv\n");
        #endif

        recv(socket_fd, packet_rcv, RECV_LEN, 0);  // stuck here!!
        // socklen_t src_addr_len = sizeof(src_addr);
        // recvfrom(socket_fd, packet_rcv, RECV_LEN, 0, (struct sockaddr*)&src_addr, &src_addr_len);
        
        in_addr_t src_ip = ntohl(*(uint32_t*)(packet_rcv + IPHDR_SRCIP));

        #ifdef DEBUG
            printf("[Thread]RecvPacket: recv one\n");
        #endif

        if (src_ip == interface->ip) {
            continue; // from self, finish packet process
        }

        /* ... dealing with the packet recvd, treated as 5 type of OSPF packet */

    }
}

the result is as follows (thread SendHelloPacket is using sendto() and is successful) result of the program

what's wrong with my usage? (I'm new hand of socket programming)

Other Details

  • environment: ubuntu 20.04 in VMWare
  • using ensp to simulate routers equipped with OSPF and send OSPF packets to my ubuntu in my virtual machine enter image description here
  • the packets catched by Wireshark in my ubuntu 20.04 is as follows: wireshark packets
  • the NIC information is as follows ("promisc" mode is of no use) enter image description here
1

There are 1 best solutions below

0
On BEST ANSWER

Oh, I think it's just because of my ignorance.

Reason May lie in: socket(AF_INET, SOCK_RAW, IPPROTO_OSPF). handling method in socket function like recv() isn't implemented. Only IPPROTO_TCP, IPPROTO_UDP, IPPROTO_ICMP, etc would work.

So I changed to use AF_PACKET(the same as PF_PACKET) with htons(ETH_P_IP), then without any other configuration I could receive all the Frames on data link layer. The rest things would be manually filter the receiving packets.

Here's the new function:

#include <netinet/if_ether.h> // should define more

void* threadRecvPackets(void *intf) {
    Interface *interface = (Interface*) intf;
    int socket_fd;
    if ((socket_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0) {
        perror("[Thread]RecvPacket: socket_fd init");
    }

#ifdef DEBUG
    printf("[Thread]RecvPacket init\n");
#endif

    struct iphdr *ip_header;
    struct in_addr src, dst;
#define RECV_LEN 1514
    char* frame_rcv = (char*)malloc(RECV_LEN);
    char* packet_rcv = frame_rcv + sizeof(struct ethhdr);
    while (true) {
        memset(frame_rcv, 0, RECV_LEN);
        int recv_size = recv(socket_fd, frame_rcv, RECV_LEN, 0);
        
        /* check IP Header : filter  */
        ip_header = (struct iphdr*)packet_rcv;
        // 1. not OSPF packet
        if (ip_header->protocol != 89) {
            continue;
        }
        // 2. src_ip or dst_ip don't fit
        in_addr_t src_ip = ntohl(*(uint32_t*)(packet_rcv + IPHDR_SRCIP));
        in_addr_t dst_ip = ntohl(*(uint32_t*)(packet_rcv + IPHDR_DSTIP));
        if ((dst_ip != interface->ip && dst_ip != ntohl(inet_addr("224.0.0.5"))) ||
            src_ip == interface->ip) {
            continue;
        }

        #ifdef DEBUG
            printf("[Thread]RecvPacket: recv one");
            src.s_addr = src_ip;
            dst.s_addr = dst_ip;
            printf(" src:%s, dst:%s\n", inet_ntoa(src), inet_ntoa(dst));
        #endif
        /* ... */
    }
}

But I'm also wondering if there's more proper way to do this. (filter the unwanted packet in somewhere of linux kernel not here)