Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to bind raw socket to interface

I'm struggling to bind raw socket to interface, my goal to implement simple packet sniffer. Already dedicated hours searching the web and went through references, part of them listed at bottom.

I'm able to open socket, no error on bind, but when stripping Ethernet header and observing IP header I see that catched loopback (127.0.0.1) and other, undesired, ethX ifaces traffic.

One of conclusions is setsockopt can NOT be used in my case, here is my code snippets :

struct sockaddr_ll sll;
int raw_sock;

raw_sock = socket( PF_PACKET , SOCK_RAW , htons(ETH_P_ALL)) ;
// also tried with AF_PACKET

bzero(&sll , sizeof(sll));

sll.sll_family = PF_PACKET; 
// also tried with AF_PACKET
sll.sll_ifindex =get_iface_index(raw_sock,"eth1");
// returns valid ifr.ifr_ifindex;
sll.sll_protocol = htons(ETH_P_ALL);
if((bind(raw_sock , (struct sockaddr *)&sll , sizeof(sll))) ==-1)
{
    perror("bind: ");
    exit(-1);
}

saddr_size = (sizeof sll);
data_size = recvfrom(raw_sock , buffer_ptr, 65536, 0 , &sll , (socklen_t*)&saddr_size);

Thanks in advance !

References :

  1. http://man7.org/linux/man-pages/man7/packet.7.html
  2. http://man7.org/linux/man-pages/man2/bind.2.html
  3. Raw socket with device bind using setsockopt() system is not working in Fedora core 6(2.6.18-1.2798.fc6)
  4. how to bind raw socket to specific interface

Edit-1 : Thanks a lot for dedicating your time for reply, I'm quite lost and frustrated from endless search for solution.

  1. I'm restricted to my own implementation, thus unable to use libcap nor others.
  2. Interface index returned from ioctl call SIOCGIFINDEX is 3 in my case and is identical to sll.sll_ifindex value. Assuming I can rely on "ip link show" - my eth1 index is indeed 3.

    int get_iface_index(int socket,char *iface_name){
    struct ifreq ifr;
    char ifname[IFNAMSIZ]="eth1"; 
    // Ugly hard coded, will be changed
    memset(&ifr, 0, sizeof(struct ifreq));
    strncpy((char *)ifr.ifr_name, ifname, IFNAMSIZ);
    if (ioctl(socket, SIOCGIFINDEX, &ifr) < 0){
            perror("ioctl: ");
            return -1;
    }
    return ifr.ifr_ifindex;
    // Always success here 2 for eth0, 3 for eth1
    }
    
like image 884
Vlad Avatar asked Oct 20 '22 14:10

Vlad


2 Answers

If you want to write a packet sniffer, I strongly suggest you use libpcap which is designed for the purpose. This will make sure the filtering (as to which packets you want) are done using Berkeley Packet Filter (BPF) before they even get to user land.

Your last link is about raw sockets, i.e. IPPROTO_RAW. These sockets bind using the setsockopt and SO_BINDTODEVICE. From the man page for raw:

A raw socket can be bound to a specific local address using the bind(2) call. If it isn't bound, all packets with the specified IP protocol are received. In addition, a RAW socket can be bound to a specific network device using SO_BINDTODEVICE; see socket(7).

An IPPROTO_RAW socket is send only. If you really want to receive all IP packets, use a packet(7) socket with the ETH_P_IP protocol. Note that packet sockets don't reassemble IP fragments, unlike raw sockets.

Thus your last link is not relevant, and you are correct to use the normal bind() call.

If you are determined not to use libpcap I suggest first you print out the value of sll.sll_ifindex. I'm betting it's zero (all interfaces). You have not shown us the source for get_iface_index, but I suspect the bug might be there.

like image 58
abligh Avatar answered Oct 28 '22 14:10

abligh


Another thing you should try is to compare strace(1) output from your code and that from the standard tcpdump(8) based on libpcap, though the latter might use the Linux PACKET_RX_RING extension.

This is how my own sniffer runs under strace(1). Seems like my code is essentially identical to yours.

socket(PF_PACKET, SOCK_RAW, 768)        = 3
ioctl(3, SIOCGIFINDEX, {ifr_name="eth1", ifr_index=3}) = 0
bind(3, {sa_family=AF_PACKET, proto=0000, if3, pkttype=PACKET_HOST, addr(0)={0, }, 20) = 0
recvfrom(3, "\377\377\377\377\377\377\0\17S\f\365\254\10\6\0\1\10\0\6\4\0\1\0\17S\f\365\254\n\312\233\2"..., 65535, 0, {sa_family=AF_PACKET, proto=0x806, if3, pkttype=PACKET_BROADCAST, addr(6)={1, 000f530cf5ac}, [18]) = 56
...
recvfrom(3, "\0\17S\f\365\254\254\26-o\244\325\10\6\0\1\10\0\6\4\0\2\254\26-o\244\325\n\312\233\5"..., 65535, 0, {sa_family=AF_PACKET, proto=0x806, if3, pkttype=PACKET_OUTGOING, addr(6)={1, ac162d6fa4d5}, [18]) = 42
...
recvfrom(3, "\254\26-o\244\325\0\17S\f\365\254\10\0E\0\0T\0\0@\0@\1\357\r\n\312\233\2\n\312"..., 65535, 0, {sa_family=AF_PACKET, proto=0x800, if3, pkttype=PACKET_HOST, addr(6)={1, 000f530cf5ac}, [18]) = 98
...
recvfrom(3, "\0\17S\f\365\254\254\26-o\244\325\10\0E\0\0Tq\235\0\0@\1\275p\n\312\233\5\n\312"..., 65535, 0, {sa_family=AF_PACKET, proto=0x800, if3, pkttype=PACKET_OUTGOING, addr(6)={1, ac162d6fa4d5}, [18]) = 98

Note the "pkttype" (sll.sll_pkttype) field in the strace(1) output. You might as well check

Environment

$ strace -V
strace -- version 4.5.20
$ uname -a
Linux kaidev01 3.2.0-57-generic #87-Ubuntu SMP Tue Nov 12 21:35:10 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
$ ip l show dev eth1                      
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
    link/ether ac:16:2d:6f:a4:d5 brd ff:ff:ff:ff:ff:ff

One minor issue with your code: you should define saddr_size as a socklen_t variable from the first place rather than resorting to pointer typecast which can be dangerous.

like image 34
nodakai Avatar answered Oct 28 '22 16:10

nodakai