Search code examples
clinuxsocketslinux-kernelraw-sockets

Raw socket bound to device not receiving all packets


I have a raw socket set up bound to a device that is already in promiscuous mode:

int sock = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);
if(sock == -1)
{
   return -1;
}

struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_ifrn.ifrn_name, "eth0", IFNAMSIZ);
if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0)
{
    close(sock);
    return -2;
}

while(1) {
  packet_size = recvfrom(sock , buffer , 65536 , 0 , NULL, NULL);
  // packet processing...
}

And my issue is that I am only receiving packets on my socket with IP destination matching the IP of the device (eth0) I am bound to. How can I receive all the TCP packets that the device is receiving? I can see all the TCP packets on the device in Wireshark, but the only packets that I see in my raw socket are those addressed to the device IP.


Solution

  • The reason of receiving packets directed only to IP of your device is that you are using PF_INET raw socket. When PF_INET raw socket is used - skb faces different sanity checks when goes across the stack (see below).

    F.e. :

    int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
    {
        /*...*/
    
        /* When the interface is in promisc. mode, drop all the crap
         * that it receives, do not try to analyse it.
         */
        if (skb->pkt_type == PACKET_OTHERHOST)
            goto drop;
    

    So the call trace is something like: __netif_receive_skb_core()->ip_rcv()->...->ip_local_deliver()->...->raw_local_deliver()->raw_rcv()->...->tcp_rcv() (you can check trace through the trace-cmd).

    But tcpdump/Wireshark obtains packets around __netif_receive_skb_core(), i.e. before some sanity checks. Hence is discrepancy that confused you.

    Therefore if you want skb's to bypass a large part of Linux kernel network stack - you should use PF_PACKET raw sockets.

    Useful link