Getting Ethernet frame length on raw socket(nonblocking) - c

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

Related

Cannot capture the packet using libpcap

I am kind of new to use libpcap.
I am using this library to capture the packet and the code i wrote to the capture the packet is below.
The interface that I am tapping is always flooded with arp packet so there is always packet coming to the interface.But I cannot able to tap these packet. The interface is UP and running.
I got no error on pcap_open_live function.
The code is in C. And I am running this code on FreeBSD10 machine 32 bit.
void captutre_packet(char* ifname , int snaplen) {
char ebuf[PCAP_ERRBUF_SIZE];
int pflag = 0;/*promiscuous mode*/
snaplen = 100;
pcap_t* pcap = pcap_open_live(ifname, snaplen, !pflag , 0, ebuf);
if(pcap!=NULL) {
printf("pcap_open_live for %s \n" ,ifname );
}
int fd = pcap_get_selectable_fd(pcap);
pcap_setnonblock(pcap, 1, ebuf);
fd_set fds;
struct timeval tv;
FD_ZERO(&fds);
FD_SET(fd, &fds);
tv.tv_sec = 3;
tv.tv_usec = 0;
int retval = select(fd + 1, &fds, NULL, NULL, &tv);
if (retval == -1)
perror("select()");
else if (retval) {
printf("Data is available now.\n");
printf("calling pcap_dispatch \n");
pcap_dispatch(pcap , -1 , (pcap_handler) callback , NULL);
}
else
printf("No data within 3 seconds.\n");
}
void
callback(const char *unused, struct pcap_pkthdr *h, uint8_t *packet)
{
printf("got some packet \n");
}
I am always getting retval as 0 which is timeout.
I don't know what is happening under the hood I follow the tutorial and they also did exactly the same thing I do not know what i am missing.
I also want to understand how the packet from the ethernet layer once received get copied into this opened bpf socket/device (using pcap_open_live) and how the buffer is copied from kernel space to user space?
And for how long we can tap the packet till the kernel consume or reject the packet?
The pcap_open_live() call provided 0 as the packet buffer timeout value (the fourth argument). libpcap does not specify what a value of 0 means, because different packet capture mechanisms, on different operating systems, treat that value differently.
On systems using BPF, such as the BSDs and macOS, it means "wait until the packet buffer is completely full before providing the packets. If the packet buffer is large (it defaults to about 256K, on FreeBSD), and the packets are small (60 bytes for ARP packets), it may take a significant amount of time for the buffer to fill - longer than the timeout you're handing to select().
It's probably best to have a timeout value of between 100 milliseconds and 1 second, so pass an argument of somewhere between 100 and 1000, not 0.

Obtain packet timestamp through ioctl call on socket file descriptor

I'm working on a system running embedded linux. I'm trying to obtain the packet timestamp from a stream I'm receiving on a socket.
After creating the socket, I do the following:
if (fd != -1) {
int enabled = 1;
setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &enabled, sizeof(enabled);
}
After that I bind the socket, the socket is of type SOCK_STREAM. I successfully receive data on the socket, by calling the function recv(fd, buf, size, 0). Now in order to obtain the timestamp of the received data, I'm currently trying the following:
ret = recv(fd, buf, size, 0);
if (ret > 0) {
struct timeval tv_ioctl;
tv_ioctl.tv_sec = 0;
tv_ioctl.tv_usec = 0;
int error = ioctl(fd, SO_TIMESTAMP, &tv_ioctl);
printf("%ld.%ld - error = %d", (long int)tv_ioctl.tv_sec,
(long int)tv_ioctl.tv_usec, error);
}
The output of the printf statement is always the following:
0.0 error = -1
The error = -1 indicates that the ioctl call has failed. I've performed a test with getsockopt to check if the SO_TIMESTAMP option is set, getsockopt returns 0 for the option SO_TIMESTAMP so it seems correctly set. I'm a bit lost here, how can I further investigate why the ioctl call seems to be failing?
The ioctl to retrieve the most recent timestamp on the socket is SIOCGSTAMP; SO_TIMESTAMP is a socket option, not an ioctl. Your code should read:
int error = ioctl(fd, SIOCGSTAMP, &tv_ioctl);
^^^^^^^^^^
The alternate method to retrieve timestamps is to change recv to recvmmsg and extract the timestamp from the ancillary data. This is more efficient as it involves fewer system calls (Socket reading and timestamps); the ioctl is simpler, though.
Note that SIOCGSTAMP and SO_TIMESTAMP are mutually exclusive - if you're going to use SIOCGSTAMP you should disable SO_TIMESTAMP (with enabled = 0). This is because SO_TIMESTAMP directs the kernel to make the timestamp available via recvmmsg ancillary data instead of via SIOCGSTAMP.

libpcap drops some packets from specific IP

I'm implementing packet collector, but I suffer from packet drops.
My binary can get most of packets from some specific IP region. (Ex. 100.101.1.1, 100.101.2.1). But to some specific IP region, I cannot get any packet. (Ex. 200.201.1.1, 200.201.2.1)
At that time, tcpdump can get packets from any IP regions.
My pcap code snippet from my implementation is followings:
struct bpf_program fp;
pcap_t *pcd;
char errbuf[PCAP_ERRBUF_SIZE];
bpf_u_int32 netp;
char port[16], dev[16];
......
pcd = pcap_open_live(dev, BUFSIZ, PROMISCUOUS, -1, errbuf);
pcap_compile(pcd, &fp, port, 0, netp);
pcap_setfilter(pcd, &fp);
while(1){
packet = pcap_next(pcd, &hdr);
}
Is there any idea for me?
Since you mentioned that you can get all the ip packets on the interface using tcpdump, I would consider the following line in your code is all right as long as you are using the same interface name for the parameter dev as you use for tcpdump.
pcap_open_live(dev, BUFSIZ, PROMISCUOUS, -1, errbuf);
The issue might be in the line,
pcap_compile(pcd, &fp, port, 0, netp);
In the above line, port variable is a filter string. Your packet collector will only collect the packets that passes this filter. If you are not using proper filter parameters in your port string to allow also the packets involving ip addresses 200.201.x.x, you will not capture them.

listening using Pcap with timeout

I want to write a small application using Libpcap in C on Linux.
Currently, it starts to sniff and wait for the packets. But that's not what I need actually. I want it to wait for N seconds and then stop listening.
How can I achieve that?
Here is my code:
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
printf("got packet\n);
}
int main()
{
int ret = 0;
char *dev = NULL; /* capture device name */
char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */
pcap_t *handle; /* packet capture handle */
char filter_exp[] = "udp dst port 1500"; /* filter expression */
struct bpf_program fp; /* compiled filter program (expression) */
bpf_u_int32 mask; /* subnet mask */
bpf_u_int32 net; /* ip */
int num_packets = 10; /* number of packets to capture */
/* get network number and mask associated with capture device */
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
fprintf(stderr, "Couldn't get netmask for device %s: %s\n",
dev, errbuf);
net = 0;
mask = 0;
}
/* print capture info */
printf("Device: %s\n", dev);
printf("Number of packets: %d\n", num_packets);
printf("Filter expression: %s\n", filter_exp);
/* open capture device */
handle = pcap_open_live(dev, SNAP_LEN, 1, 1000, errbuf);
if (handle == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
exit(EXIT_FAILURE);
}
/* compile the filter expression */
if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
fprintf(stderr, "Couldn't parse filter %s: %s\n",
filter_exp, pcap_geterr(handle));
exit(EXIT_FAILURE);
}
/* apply the compiled filter */
if (pcap_setfilter(handle, &fp) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n",
filter_exp, pcap_geterr(handle));
exit(EXIT_FAILURE);
}
/* now we can set our callback function */
pcap_loop(handle, num_packets, got_packet, NULL);
/* cleanup */
pcap_freecode(&fp);
pcap_close(handle);
}
You should call pcap_breakloop() when you want stop listening. So one way would be to:
setup an alarm to trigger in N seconds,
install a signal handler for SIGALRM signal,
call pcap_breakloop() inside the handler to make pcap_loop() return.
code:
void alarm_handler(int sig)
{
pcap_breakloop(handle);
}
int main()
{
...
alarm(N);
signal(SIGALRM, alarm_handler);
/* now we can set our callback function */
pcap_loop(handle, num_packets, got_packet, NULL);
/* cleanup */
pcap_freecode(&fp);
pcap_close(handle);
}
Note: As for using libpcap's read timeout to do this, it won't and can't work, man pcap explicitly warns against it:
The read timeout cannot be used to cause calls that read packets to return within a limited period of time [...] This means that the read timeout should NOT be used, for example, in an interactive application to allow the packet capture loop to poll for user input periodically, as there's no guarantee that a call reading packets will return after the timeout expires even if no packets have arrived.
If you look at this page from the tcpdump website, you can see the follwing:
Opening the device for sniffing
The task of creating a sniffing session is really quite simple. For this, we use pcap_open_live(). The prototype of this function (from the pcap man page) is as follows:
pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms,
char *ebuf)
The first argument is the device that we specified in the previous section. snaplen is an integer which defines the maximum number of bytes to be captured by pcap. promisc, when set to true, brings the interface into promiscuous mode (however, even if it is set to false, it is possible under specific cases for the interface to be in promiscuous mode, anyway). to_ms is the read time out in milliseconds (a value of 0 means no time out; on at least some platforms, this means that you may wait until a sufficient number of packets arrive before seeing any packets, so you should use a non-zero timeout). Lastly, ebuf is a string we can store any error messages within (as we did above with errbuf). The function returns our session handler.
If this does not work, let us know.
I have been studying and testing these code fragments for a while. Somewhere, I noticed the observation that when you exit pcap_loop, although you may have seen packets, the exit conditions suggest that you have seen none. I assume from this that all the processing of the packet must occur in the scope of the callback function. So if I want to reset quickly and be ready for another packet, I will need to spawn another process to work on each packet.
Old post but I was researching and came across this.
From the man page for pcap_loop() it specifically says, "It does not return when live read timeouts occur; instead, it attempts to read more packets."
But the man pages for pcap_dispatch() and pcap_next() both indicate they return on timeout.

Reading from a promiscuous network device

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.

Resources