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 :
Edit-1 : Thanks a lot for dedicating your time for reply, I'm quite lost and frustrated from endless search for solution.
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
}
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With