c++network-programmingrecvraw-socketsospf

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


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


Solution

  • 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)