Reading from a promiscuous network device - c

I want to write a real-time analysis tool for wireless traffic.
Does anyone know how to read from a promiscuous (or sniffing) device in C?
I know that you need to have root access to do it. I was wondering if anyone knows what functions are necessary to do this. Normal sockets don't seem to make sense here.

On Linux you use a PF_PACKET socket to read data from a raw device, such as an ethernet interface running in promiscuous mode:
s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))
This will send copies of every packet received up to your socket. It is quite likely that you don't really want every packet, though. The kernel can perform a first level of filtering using BPF, the Berkeley Packet Filter. BPF is essentially a stack-based virtual machine: it handles a small set of instructions such as:
ldh = load halfword (from packet)
jeq = jump if equal
ret = return with exit code
BPF's exit code tells the kernel whether to copy the packet to the socket or not. It is possible to write relatively small BPF programs directly, using setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, ). (WARNING: The kernel takes a struct sock_fprog, not a struct bpf_program, do not mix those up or your program will not work on some platforms).
For anything reasonably complex, you really want to use libpcap. BPF is limited in what it can do, in particular in the number of instructions it can execute per packet. libpcap will take care of splitting a complex filter up into two pieces, with the kernel performing a first level of filtering and the more-capable user-space code dropping the packets it didn't actually want to see.
libpcap also abstracts the kernel interface out of your application code. Linux and BSD use similar APIs, but Solaris requires DLPI and Windows uses something else.

I once had to listen on raw ethernet frames and ended up creating a wrapper for this. By calling the function with the device name, ex eth0 I got a socket in return that was in promiscuous mode.
What you need to do is to create a raw socket and then put it into promiscuous mode. Here is how I did it.
int raw_init (const char *device)
{
struct ifreq ifr;
int raw_socket;
memset (&ifr, 0, sizeof (struct ifreq));
/* Open A Raw Socket */
if ((raw_socket = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 1)
{
printf ("ERROR: Could not open socket, Got #?\n");
exit (1);
}
/* Set the device to use */
strcpy (ifr.ifr_name, device);
/* Get the current flags that the device might have */
if (ioctl (raw_socket, SIOCGIFFLAGS, &ifr) == -1)
{
perror ("Error: Could not retrive the flags from the device.\n");
exit (1);
}
/* Set the old flags plus the IFF_PROMISC flag */
ifr.ifr_flags |= IFF_PROMISC;
if (ioctl (raw_socket, SIOCSIFFLAGS, &ifr) == -1)
{
perror ("Error: Could not set flag IFF_PROMISC");
exit (1);
}
printf ("Entering promiscuous mode\n");
/* Configure the device */
if (ioctl (raw_socket, SIOCGIFINDEX, &ifr) < 0)
{
perror ("Error: Error getting the device index.\n");
exit (1);
}
return raw_socket;
}
Then when you have your socket you can just use select to handle packets as they arrive.

You could use the pcap library (see http://www.tcpdump.org/pcap.htm) which is also used by tcpdump and Wireshark.

Why wouldn't you use something like WireShark?
It is open source, so at least you could learn a few things from it if you don't want to just use it.

WireShark on linux has the capability to capture the PLCP (physical layer convergence protocol) header information.

Related

How to connect my device to wifi with libnl?

I'm creating a C library that manage a lot of pheripherical of my embedded device. The S.O. used, is a linux dristro compiled with yocto. I'm trying to make some functions to connect my device to a wifi (well-know) router, with netlink (using the libnl commands). With the help of this community i've developed a function able to scan the routers in the area link here . Some of you know how to use the libnl command for connect my device to a router wifi?
I've developed the following code, that try to connect to an AP called "Validator_Test" (that have no password for authentication). The software return no error, but my device still remain disconneted from the ap.
static int iw_conn() {
struct nl_msg *msg = nlmsg_alloc();
int if_index = if_nametoindex("wlan0"); // Use this wireless interface for scanning.
// Open socket to kernel.
struct nl_sock *socket = nl_socket_alloc(); // Allocate new netlink socket in memory.
genl_connect(socket); // Create file descriptor and bind socket.
int driver_id = genl_ctrl_resolve(socket, "nl80211"); // Find the nl80211 driver ID.
genlmsg_put(msg, 0, 0, driver_id, 0, (NLM_F_REQUEST | NLM_F_ACK), NL80211_CMD_CONNECT, 0);
nla_put_u32(msg, NL80211_ATTR_IFINDEX, if_index); // Add message attribute, which interface to use.
nla_put(msg, NL80211_ATTR_SSID, strlen("Validator_Test"), "Validator_Test");
nla_put(msg, NL80211_ATTR_MAC, strlen("00:1e:42:21:e4:e9"), "00:1e:42:21:e4:e9");
int ret = nl_send_auto_complete(socket, msg); // Send the message.
printf("NL80211_CMD_CONNECT sent %d bytes to the kernel.\n", ret);
ret = nl_recvmsgs_default(socket); // Retrieve the kernel's answer. callback_dump() prints SSIDs to stdout.
nlmsg_free(msg);
if (ret < 0) {
printf("ERROR: nl_recvmsgs_default() returned %d (%s).\n", ret, nl_geterror(-ret));
return ret;
}
nla_put_failure:
return -ENOSPC;
}
It seems similar to this one:
How to use libnl and netlink socket for connect devices to AP programatically?
--
Thanks for the code.
Based on your code, I modified and did the test here; it works. The source code is at: https://github.com/neojou/nl80211/blob/master/test_connect/src/test_connect_nl80211.c
Some suggestions for this:
Make sure the test environment is correct
Before test the code, maybe you can try to use iw to do the test. iw is the open source tool, which uses netlink also. you can type "sudo iw wlan0 connect Validator_Test" and then use iwconfig to see if it is connected or not first. ( Suppose there is no security setting at the AP as you said )
there are two differences between your source code and mine
(1) don't need to set NL80211_ATTR_MAC
(2) ret = nl_recvmsgs_default(socket);
not sure if there is any judgement of the return value of your ap_conn(), but it seems better to return 0 in ap_conn(), when nl_recvmsgs_default() returns 0.

Getting Ethernet frame length on raw socket(nonblocking)

I am trying to send and receive raw ethernet frames to include a network device as a media access controller in a simulation environment.
Therefore it is important that the receiving of the packets works through nonblocking statements.
Now the sending of the raw ethernet frames works fine but there's one thing about the receive path that is confusing me:
How do I know where the one frame ends and the other frame begins.
What I fundamentally do is to open a raw socket:
device.socket = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
setting it up as non blocking:
flags = fcntl(s,F_GETFL,0);
assert(flags != -1);
fcntl(s, F_SETFL, flags | O_NONBLOCK);
and then call the recv() function cyclical to get the data from the socket:
length = recv(s, buffer, ETH_FRAME_LEN_MY, 0);
But as far as I know the recv() function only returns the amount of bytes, that is currently availible in the receive buffer and therefore I do not know if another frame starts or if I am still reading the "old" packet.
And because of the fact, that the length of the ethernet frame is not included in the header I can not do this on my own.
Thank you in advance!
If anyone runs into the same problem here's a possible solution:
You can use the libpcap library(in windows winpcap) to open the device as a capture device:
char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */
Pcap_t *handle; /* packet capture handle */
/* open capture device*/
/* max possible length, not in promiscous mode, no timeout!*/
handle = pcap_open_live(dev, 65536, 0, 0, errbuf);
if (handle == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
}
/* set capture device to non blocking*/
if(pcap_setnonblock(pcap_t *p, int nonblock, char *errbuf)){
fprintf("Could not set pcap interface in non blocking mode: %s \n", errbuf);
}
Now you can cyclic call the pcap_dispatch function to receive packet(s):
int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user);
You have to provide a callback function in which the data is handled.
See https://www.freebsd.org/cgi/man.cgi?query=pcap_dispatch&apropos=0&sektion=3&manpath=FreeBSD+11-current&format=html for further information.
You can send raw ethernet frames by using the inject function:
pcap_inject(pcap,frame,sizeof(frame));

How to properly put network interface into promiscuous mode on Linux

So how do you do this properly?
I know how to do it by creating socket, then setting IFF_PROMISC flag using ioctl (as explained in "howto check a network devices status in C?" and elsewhere), but this looks flawed at least in theory.
you read flags via ioctl
you update flags
someone else modified flags
you set updated flags via ioctl
Is there a better way or do I simply worry too much?
Later I found that one should add interface to PACKET_MR_PROMISC via setsockopt (which also does not have a race) like this:
void set_promisc(const char *ifname, bool enable)
{
struct packet_mreq mreq = {0};
int sfd;
int action;
if ((sfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) {
perror("unable to open socket");
return;
}
mreq.mr_ifindex = if_nametoindex(ifname);
mreq.mr_type = PACKET_MR_PROMISC;
if (mreq.mr_ifindex == 0) {
perror("unable to get interface index");
return;
}
if (enable)
action = PACKET_ADD_MEMBERSHIP;
else
action = PACKET_DROP_MEMBERSHIP;
if (setsockopt(sfd, SOL_PACKET, action, &mreq, sizeof(mreq)) != 0) {
perror("unable to enter promiscouous mode");
return;
}
close(sfd);
}
Unfortunately this has no effect whatsoever on interface, although it should, if I unserstand the doc correctly. Possibly broken since 2001 (tm)?
Comments in pcap source also complain about this.
PACKET_MR_PROMISC turns on promiscuous mode for the device. That will not be reflected in the status shown by ifconfig as it does not modify the state of the global IFF_PROMISC flag on the device. That does not mean it hasn't been done though. This is how the pcap library works now and the fact that wireshark (and a dozen other utilities) can open a device and see packets not addressed to the local system shows that it works.
There is an internal counter on each device that is incremented each time a process uses PACKET_MR_PROMISC, and decremented when that process goes away. That solves the race you originally described.
From the last link you provided:
> IFF_PROMISC is not set,
It's not supposed to be set.
The correct way to put into promiscuous mode the device to which a
PF_PACKET socket is to do a SOL_PACKET/PACKET_ADD_MEMBERSHIP
"setsockopt()" call with PACKET_MR_PROMISC as the argument (see the
"packet(7)" man page), and that's what libpcap is doing.
The old way of directly setting IFF_PROMISC had problems - to quote the
comment at the front of "pcap-linux.c":
[snipped]

How to create a non blocking socket without fcntl?

I'm working on a system that doesn't run unix / linux / windows or any familiar OS.
Yet, there are some libraries for sockets, one of them is socket.h.
The thing is that there is setsockopt function there, and some options, but there isn't fcntl.h library. I saw that this is the only way to get and set the socket's flags (O_NONBLOCK is what i seek).
Also, I saw that It is possible to use setsockopt with so_rcvtimeo and so_sndtimeo. I tried it and it is still blocking. The connect function was stuck when I was unplugging the ethernet cable right before the execution reached the connect line (in debug mode of course).
Is there a way to do it with the socket.h library only ?
Thanks,
Edit:
I'm using a system with Texas Instruments chip.
This is my current code, which doesn't work. "connect" returns -1 and I don't have errno to check what went wrong.
if (setsockopt(s, SOL_SOCKET, SO_BLOCKING, (char *)&isBlockingOption, sizeof(isBlockingOption)) < 0) {
/* closing the socket and exiting */
}
connect(s, &controlAddressStruct, sizeof(controlAddressStruct));
FD_ZERO(&conSocketSet);
FD_SET(s, &conSocketSet);
connectTimeout.tv_sec = 5;
connectTimeout.tv_usec = 0;
selectRet = fdselect( (int)s + 1, NULL, &conSocketSet, NULL, &connectTimeout);
if (selectRet == 1) {
socketLen = sizeof(so_error);
getsockopt(s, SOL_SOCKET, SO_ERROR, &so_error, &socketLen);
if (so_error == 0) {
return s;
}
}
return INVALID_SOCKET;
now, when the ethernet cable is plugged between the two systems, so_error = 0 and s is returned. Then when I read the FTP server reply, I get error so I exit the program.
But, if I removed the setsockopt for the SO_BLOCKING, everything is fine and the FTP server sends me the requested file.
If the send() and recv() interfaces are similar to the BSD sockets interface at all, the fourth parameter should correspond to flags. If so, you may be able to pass in MSG_DONTWAIT to the fourth parameter to effect non-blocking I/O.

What is the BSD (or portable) way to get ToS byte (like IP_RECVTOS from linux)?

What is the right (portable, stable) way to get the ToS byte of a received packet? I'm doing UDP with recvmsg() and on linux I can get the ToS if I setsockopt() IP_RECVTOS/IPV6_RECVTCLASS, but IP_RECVTOS doesn't seem to be available on my BSD systems. What is the right way to do this?
I primarily want this to work on the BSDs and Solaris.
Edit:
To clarify:
I currently use recvmsg() where I get the TTL and TOS in the msg_control field on Linux, but in order to get TTL and TOS I need to setsockopt()-enable IP_RECVTTL and IP_RECVTOS. And since Solaris and BSD (working with FreeBSD at the moment) don't have IP_RECVTOS from what I can see I don't get TOS when looping over the CMSG data.
I tried enabling IP_RECVOPTS and IP_RECVRETOPTS, but I still don't get any IP_TOS type CMSG.
Edit 2:
I want ToS to be able to verify (as much as possible) that it wasn't overwritten in transit. If for example a VoIP app all of a sudden notices that it's not getting EF tagged packets, then something is wrong and there should be an alarm. (and no, I'm not expecting EF to be respected or preserved over the public internet)
I want TTL basically just because I can. Hypothetically this could be used to trigger "something changed in the network between me and the other side" alerts, which can be useful to know if somethings stops working at the same time.
I was thinking if you can create two sockets.
One socket of type DGRAM used exclusively for sending
One Raw socket used exclusively for receiving.
Since you are using UDP, you can call a bind + recvFrom on the Raw Sock Fd and then manually unpack the IP header to determine the TOS or TTL.
When you want to send, use the DGRAM sockFd so you dont have to bother to actually create the UDP & IP packet yourself.
There may be issues like the kernel may pass the received buffer to both sockets or to the UDP socket instead of Raw socket or just to the Raw socket. If that is the case (or if it is implementation dependent) then we are back to square one. However, you can try calling bind on the Raw socket and see if it helps. I am aware this maybe a hack but searching on the net for a setsockopt for BSD returned nothing.
EDIT: I wrote a sample program
It kind of achieves the objective.
The code below creates two sockets (one raw & one udp). The udp socket is bound on the actual port I am expecting to receive data whereas the raw socket is bound on Port 0. I tested this on Linux and like I expected any data for port 2905 is received by both the sockets. I am however able to retrieve the TTL & TOS values. Dont downvote for the quality of the code. I am just experimenting whether it will work.
Further EDIT: Disabled the receive by UDP socket.
I have further enhanced the code to disable the receive by the UDP packet. Using setsockopt, I set the UDP's socket receive buffer to 0. This ensures the kernel does not pass the packet to the UDP socket. IMHO,You can now use the UDP socket exclusively for sending and the raw socket for reading. This should work for you in BSD and Solaris also.
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<arpa/inet.h>
#include<string.h>
#include "protHeaders.x"
#include "gen.h"
int main(void)
{
S32 rawSockFd;
S32 udpSockFd;
struct sockaddr_in rsin;
struct sockaddr_in usin;
S32 one = 1;
const S32* val = &one;
struct timeval tv;
fd_set rfds;
S32 maxFd;
S16 ret;
S8 rawBuffer[2048];
S8 udpBuffer[2048];
struct sockaddr udpFrom,rawFrom;
socklen_t rLen,uLen;
memset(rawBuffer,0,sizeof(rawBuffer));
memset(udpBuffer,0,sizeof(udpBuffer));
memset(udpFrom,0,sizeof(udpFrom));
memset(rawFrom,0,sizeof(rawFrom));
if ((rawSockFd = socket(PF_INET,SOCK_RAW,IPPROTO_UDP)) < 0)
{
perror("socket:create");
RETVALUE(RFAILED);
}
/* doing the IP_HDRINCL call */
if (setsockopt(rawSockFd,IPPROTO_IP,IP_HDRINCL,val,sizeof(one)) < 0)
{
perror("Server:setsockopt");
RETVALUE(RFAILED);
}
rsin.sin_family = AF_INET;
rsin.sin_addr.s_addr = htonl(INADDR_ANY);
rsin.sin_port = htons(0);
usin.sin_family = AF_INET;
usin.sin_addr.s_addr = htons(INADDR_ANY);
usin.sin_port = htons(2905);
if(bind(rawSockFd,(struct sockaddr *)&rsin, sizeof(rsin)) < 0 )
{
perror("Server: bind failed");
RETVALUE(RFAILED);
}
if ((udpSockFd = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0)
{
perror("socket:create");
RETVALUE(RFAILED);
}
if(bind(udpSockFd,(struct sockaddr *)&usin, sizeof(usin)) < 0 )
{
perror("Server: bind failed on udpsocket");
RETVALUE(RFAILED);
}
/*set upd socket receive buffer to 0 */
one = 0;
if (setsockopt(udpSockFd,SOL_SOCKET,SO_RCVBUF,(char *)&one,sizeof(one)) < 0)
{
perror("Server:setsockopt on udpsocket failed");
RETVALUE(RFAILED);
}
tv.tv_sec = 0;
tv.tv_usec = 0;
maxFd = (rawSockFd > udpSockFd)? rawSockFd:udpSockFd;
while(1)
{
FD_ZERO(&rfds);
FD_SET(rawSockFd,&rfds);
FD_SET(udpSockFd,&rfds);
ret = select(maxFd+1,&rfds,0,0,&tv);
if ( ret == -1)
{
perror("Select Failed");
RETVALUE(RFAILED);
}
if(FD_ISSET(rawSockFd,&rfds))
{
printf("Raw Socked Received Message\n");
if(recvfrom(rawSockFd,rawBuffer,sizeof(rawBuffer),0,&rawFrom,&rLen) == -1)
{
perror("Raw socket recvfrom failed");
RETVALUE(RFAILED);
}
/*print the tos */
printf("TOS:%x\n",*(rawBuffer+1));
printf("TTL:%x\n",*(rawBuffer+8));
}
if(FD_ISSET(udpSockFd,&rfds))
{
printf("UDP Socked Received Message\n");
if(recvfrom(udpSockFd,udpBuffer,sizeof(udpBuffer),0,&udpFrom,&uLen) == -1)
{
perror("Udp socket recvfrom failed");
RETVALUE(RFAILED);
}
printf("%s\n",udpBuffer);
}
}
RETVALUE(ROK);
}
The "proper" and standard solution is probably to use cmsg(3). You'll find a complete description in Stevens' "Unix network programming" book, a must-read.
Google Code Search found me this example of use.
My understanding is that firstly BSD does not support IP_RECVTOS like functionality and secondly BSD raw sockets do not support the reception of UDP nor TCP packets. However there are two other ways of doing this, firstly by using the /dev/bpf interface - either directly or via libpcap. Or secondly by using DIVERT sockets which allow for diversion of specified traffic flows to userland.
Has anyone actually tested the code above on a BSD box? (it may work on Solaris...)
On Linux this approach will work but as mentioned it is also possible (and more convenient) to use setsockopt() with IP_TOS on the outgoing socket to set the outgoing TOS byte and setsockopt() with IP_RECVTOS on the incoming socket and use recvmsg() to retrieve the TOS byte.
Unfortuneatly this sort of thing usually varies across different *ixs. On Solaris you want to use getsockopt with IP_TOS; I don't know about BSD.
See man 7 ip for details.

Resources