Issue in pcap_set_buffer_size() - c

#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#define BUFFER_SIZE 65535
char errbuf[PCAP_ERRBUF_SIZE];
int main(int argc, char **argv)
{
int d;
pcap_if_t *alldevsp;
pcap_t *pkt_handle;
if((pcap_findalldevs(&alldevsp,errbuf))==-1)
{
printf("findalldevices: %s\n",errbuf);
exit(1);
}
printf("Availabel network devices are\n");
pcap_if_t *temp = alldevsp;
while((temp)!=NULL)
{
printf("%s: %s\n",(temp)->name,(temp)->description);
(temp)=(temp)->next;
}
pcap_freealldevs(alldevsp);
pkt_handle = pcap_create("wlan1",errbuf);
if(pkt_handle==NULL)
{
printf("create: %s\n",errbuf);
exit(1);
}
if((pcap_set_rfmon(pkt_handle, 1))!=0)
{
printf("Monitor mode could not be set\n");
exit(1);
}
if((pcap_set_buffer_size(pkt_handle, BUFFER_SIZE))!=0)
{
printf("ERROR\n");
exit(1);
}
if((d=(pcap_activate(pkt_handle)))!=0)
{
if(d==PCAP_ERROR_RFMON_NOTSUP)
printf("%d : PCAP_ERROR_RFMON_NOTSUP\n",d);
if(d==PCAP_WARNING)
printf("%d : PCAP_WARNING\n",d);
if(d==PCAP_ERROR)
printf("%d : PCAP_ERROR\n",d);
pcap_perror(pkt_handle,"Activate");
exit(1);
}
printf("d=%d\n",d);
while(1)
{
scanf("%d",&d);
if(d==-1)
break;
}
pcap_close(pkt_handle);
printf("Bye\n");
return 0;
}
When you run the above program using:
gcc -Wall -lpcap sample.c -o sample
I get the follwing error:
-1 : PCAP_ERROR
Activate: can't mmap rx ring: Invalid argument
However, if I comment out the section of code containing pcap_set_buffer_size() function call, the program works perfectly fine.
So, what is this problem with pcap_set_buffer_size()?
Why is it causing pcap_activate() to fail?

For a recent 64bit Linux:
Any buffer size equal or larger then 65616 should do.
For how the value is calculated please see the implementation of create_ring() in pcap-linux.c from the libpcap sources.
The default is 2*1024*1024 = 2097152.
The default buffer size on windows is 1000000.
Update:
The buffer size to be set by pcap_set_buffer_size() refers to the (ring-)buffer, which stores the already received packages. The optimal size depends on the use case and on the affordable system resources (non-pageable memory).
Please see the following statements on the receive buffer's size verbatim from man pcap:
Packets that arrive for a capture are stored in a buffer, so that they
do not have to be read by the application as soon as they arrive. On
some platforms, the
buffer's size can be set; a size that's too small could mean that, if too many packets are being captured and the
snapshot length doesn't limit the amount of
data that's buffered, packets could be dropped if the buffer fills up before the application can read packets from it, while
a size that's too large could use
more non-pageable operating system memory than is necessary to prevent packets from being dropped.
Update 1:
Anyway, the buffer's size should be least the snap length set for the handle in use, plus some bytes needed to properly align the buffer itself, otherwise activating the handle ends up as described in the original question.
One can retrieve the handle's current snap length using pcap_snapshot(). The default snap length is 65535 bytes.

Related

How to fix a segmentation fault for Ansi C Tcp client?

I'm trying to expand an example of a Tcp client developed using Ansi C, following the book "TCP/IP Sockets in C". The client connects to a Tcp Server providing strings of different lengths depending on the request provided by the client (I developed my own simple protocol). When the returned strings are short in length, everything works fine. When they're over a certain length (it happens for example with 4KB), the client crashes with a Segmentation Fault error.
The socket is handled using a wrapper to stream the i/o:
FILE *str = fdopen(sock, "r+"); // Wrap for stream I/O
And the transmission and reception are handled using fwrite() and fread().
This is the call that generates the error in my project (the caller):
uint8_t inbuf[MAX_WIRE_SIZE];
size_t respSize = GetNextMsg(str, inbuf, MAX_WIRE_SIZE); // Get the message
And this is the implementation of the GetNextMsg() function, that use to receive the data and unframe it:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <netinet/in.h>
#include "Practical.h"
/* Read 4-byte length and place in big-endian order.
* Then read the indicated number of bytes.
* If the input buffer is too small for the data, truncate to fit and
* return the negation of the *indicated* length. Thus a negative return
* other than -1 indicates that the message was truncated.
* (Ambiguity is possible only if the caller passes an empty buffer.)
* Input stream is always left empty.
*/
uint32_t GetNextMsg(FILE *in, uint8_t *buf, size_t bufSize)
{
uint32_t mSize = 0;
uint32_t extra = 0;
if (fread(&mSize, sizeof(uint32_t), 1, in) != 1)
return -1;
mSize = ntohl(mSize);
if (mSize > bufSize)
{
extra = mSize - bufSize;
mSize = bufSize; // Truncate
}
if (fread(buf, sizeof(uint8_t), mSize, in) != mSize)
{
fprintf(stderr, "Framing error: expected %d, read less\n", mSize);
return -1;
}
if (extra > 0)
{ // Message was truncated
uint32_t waste[BUFSIZE];
fread(waste, sizeof(uint8_t), extra, in); // Try to flush the channel
return -(mSize + extra); // Negation of indicated size
}
else
return mSize;
}
I suspect that this could be related to the fact that with Tcp, sender and receiver are handling data with a streaming behavior, therefore it's not granted that the receiver
gets all of the data at once, as the simple example from which I started probably assumed. In fact, with short strings everything works. With longer strings, it doesn't.
I've done a simplified debug inserting a printf as a first thing inside of the function, but when I have the crash this doesn't even get printed.
It seems like an issue with the FILE *str passed as an argument to the function, when
via the socket a message longer than usual is received.
The buffers are sized far bigger than the length of the message causing the issue (1MB vs 4KB).
I've even tried to increase the size of the socket buffer via the setsockopt:
int rcvBufferSize;
// Retrieve and print the default buffer size
int sockOptSize = sizeof(rcvBufferSize);
if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvBufferSize, (socklen_t*)&sockOptSize) < 0)
DieWithSystemMessage("getsockopt() failed");
printf("Initial Receive Buffer Size: %d\n", rcvBufferSize);
// Double the buffer size
rcvBufferSize *= 10;
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvBufferSize,
sizeof(rcvBufferSize)) < 0)
DieWithSystemMessage("setsockopt() failed");
but this didn't help.
Any ideas about the reason and how could I fix it?
This code:
{ // Message was truncated
uint32_t waste[BUFSIZE];
fread(waste, sizeof(uint8_t), extra, in); // Try to flush the channel
reads extra bytes into a buffer of size 4*BUFSIZE (4 because you intended to make the buffer unit8_t, but accidentally made it uint32_t instead).
If extra is larger than 4*BUFSIZE, then you will have a local buffer overflow and stack corruption, possibly resulting in a crash.
To do this correctly, something like this is needed:
int remaining = extra;
while (remaining > 0) {
char waste[BUFSIZE];
int to_read = min(BUFSIZE, remaining);
int got = fread(waste, 1, to_read, in);
if (got <= 0) break;
remaining -= got;
}

Unable to write the complete script onto a device on the serial port

The script file has over 6000 bytes which is copied into a buffer.The contents of the buffer are then written to the device connected to the serial port.However the write function only returns 4608 bytes whereas the buffer contains 6117 bytes.I'm unable to understand why this happens.
{
FILE *ptr;
long numbytes;
int i;
ptr=fopen("compass_script(1).4th","r");//Opening the script file
if(ptr==NULL)
return 1;
fseek(ptr,0,SEEK_END);
numbytes = ftell(ptr);//Number of bytes in the script
printf("number of bytes in the calibration script %ld\n",numbytes);
//Number of bytes in the script is 6117.
fseek(ptr,0,SEEK_SET);
char writebuffer[numbytes];//Creating a buffer to copy the file
if(writebuffer == NULL)
return 1;
int s=fread(writebuffer,sizeof(char),numbytes,ptr);
//Transferring contents into the buffer
perror("fread");
fclose(ptr);
fd = open("/dev/ttyUSB3",O_RDWR | O_NOCTTY | O_NONBLOCK);
//Opening serial port
speed_t baud=B115200;
struct termios serialset;//Setting a baud rate for communication
tcgetattr(fd,&serialset);
cfsetispeed(&serialset,baud);
cfsetospeed(&serialset,baud);
tcsetattr(fd,TCSANOW,&serialset);
long bytesw=0;
tcflush(fd,TCIFLUSH);
printf("\nnumbytes %ld",numbytes);
bytesw=write(fd,writebuffer,numbytes);
//Writing the script into the device connected to the serial port
printf("bytes written%ld\n",bytesw);//Only 4608 bytes are written
close (fd);
return 0;
}
Well, that's the specification. When you write to a file, your process normally is blocked until the whole data is written. And this means your process will run again only when all the data has been written to the disk buffers. This is not true for devices, as the device driver is the responsible of determining how much data is to be written in one pass. This means that, depending on the device driver, you'll get all data driven, only part of it, or even none at all. That simply depends on the device, and how the driver implements its control.
On the floor, device drivers normally have a limited amount of memory to fill buffers and are capable of a limited amount of data to be accepted. There are two policies here, the driver can block the process until more buffer space is available to process it, or it can return with a partial write only.
It's your program resposibility to accept a partial read and continue writing the rest of the buffer, or to pass back the problem to the client module and return only a partial write again. This approach is the most flexible one, and is the one implemented everywhere. Now you have a reason for your partial write, but the ball is on your roof, you have to decide what to do next.
Also, be careful, as you use long for the ftell() function call return value and int for the fwrite() function call... Although your amount of data is not huge and it's not probable that this values cannot be converted to long and int respectively, the return type of both calls is size_t and ssize_t resp. (like the speed_t type you use for the baudrate values) long can be 32bit and size_t a 64bit type.
The best thing you can do is to ensure the whole buffer is written by some code snippet like the next one:
char *p = buffer;
while (numbytes > 0) {
ssize_t n = write(fd, p, numbytes);
if (n < 0) {
perror("write");
/* driver signals some error */
return 1;
}
/* writing 0 bytes is weird, but possible, consider putting
* some code here to cope for that possibility. */
/* n >= 0 */
/* update pointer and numbytes */
p += n;
numbytes -= n;
}
/* if we get here, we have written all numbytes */

Debian I2C driver for OLED screen not working. [ssd1306]

I have some driver code that I am testing out for use with an SSD1306 driven OLED screen which is 128x32 (same as the OLED adafruit model). I need this to run in debian (I am using Linario-4.4.9)
I have followed the Debian guides on how to start creating a file handler for the device, this can be seen as follows below. The only thing in oled.h is the device adress (0x3C) and the proto types. I followed the initialization approach taken on the adafruit github (as I tried their code out first on an Ardunio to ensure the screen does in fact work). I believe I may be doing something wrong but I'm not entirely sure what I am doing wrong. I have also listed my init process below.
#include <errno.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <linux/i2c-dev.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include "oled.h"
int oled;
int lcd_driver_init(void)
{
///< Begin the init proc.
int dloc = open("/dev/i2c-1", O_RDWR);
if (dloc < 0 )
{
fprintf(stderr, "Error opening i2c device\n");
return -1;
}
if(ioctl(dloc, I2C_SLAVE, SCR_ADDR) < 0)
{
fprintf(stderr, "Error in ioctl. Errno :%i\n",errno);
return -2;
}
oled = dloc;
fprintf(stderr, "init success, device open and local\n");
return EXIT_SUCCESS;
}
int oled_command( uint8_t cmd)
{
char command[2]= {0};
command[1] = cmd;
int check = (write(oled, command, 2));
return check;
}
void oled_cmd_start()
{
int check = (write(oled, 0x00, sizeof(uint8_t)));
if(check<0)
fprintf(stderr, "Errno set:: %i\n", errno);
return;
}
void oled_data_start()
{
uint8_t _data_start_[1] ={ 0x40 };
int check = (write(oled, _data_start_, sizeof(uint8_t)));
if(check<0)
fprintf(stderr, "Errno set oled_data_start:: %i\n", errno);
return;
}
int oled_data (uint8_t xmit)
{
int check = (write(oled, &xmit, (sizeof(uint8_t))));
if(check<0)
fprintf(stderr, "Errno set oled_data:: %i\n", errno);
return check;
}
INIT PROCESS
void sendcommand(unsigned char payload)
{
oled_data(0x00); //Control Byte - Command
oled_data(payload); //payload
}
void lcd_init(void)
{
sendcommand(0xAE);//--Set Display off
sendcommand(0x00);//--set low column address
sendcommand(0x10);//--set high column address
sendcommand(0x81);//--set contrast control register
sendcommand(0x7f);
sendcommand(0xa1);//--set segment re-map 95 to 0
sendcommand(0xA6);//--set normal display
sendcommand(0xa8);//--set multiplex ratio(1 to 16)
sendcommand(0x1f);//--duty 1/32
sendcommand(0xd3);//--set display offset
sendcommand(0x00);//--not offset
sendcommand(0xd5);//--set display clock divide ratio/oscillator frequency
sendcommand(0xf0);//--set divide ratio
sendcommand(0xd9);//--set pre-charge period
sendcommand(0x22);
sendcommand(0xda);//--set com pins hardware configuration
sendcommand(0x02);//disable left/right remap and set for sequential
sendcommand(0xdb);//--set vcomh
sendcommand(0x49);//--0.83*vref
sendcommand(0x8d);//--set DC-DC enable
sendcommand(0x14);//
sendcommand(0xAF);//--turn on oled panel
sendcommand(0xA4);//--Entire Display ON
}
Following this, I send alternating 0xFF to try and make stripes on the screen. The only thing that shows up is random pixels. Nothing coherent.
I have connected a logic analyzer to sniff the I2C lines, and it appears that when I have the LA connected, the I2C lines no longer function and ERRNO returns an IO fault (#5).
There doesn't ever seem to be an issue opening up the device to get the file pointer however.
I do get ERRNO as timeout sometimes, but I have read that this is just an issue with I2C devices using the protocal as write expects a quicker response than I2C might give.
I am also compiling with -std=c99 -O0 to ensure all of the inline functions are there as well as ensuring that loop variables are available.
If anyone can point me in the right direction of can point out some flaw in my approach it would be much appreciated. Thank you.
EDIT
I've checked the device tree and the i2c device is correctly enabled. However none of the i2c_freq speeds seem to be enabled. Could this be causing the timeouts and the garbage data transfer?
I have connected a logic analyzer to sniff the I2C lines, and it appears that when I have the LA connected, the I2C lines no longer function and ERRNO returns an IO fault (#5).
logic analyzer is just a device for measurement. It converts the captured data into timing diagrams, decodes protocol which you have set. So it will not be responsible for any I2C read write error (until your grounding and h/w connections are correct).
For timeout issue either you can try by decreasing the i2c clock-frequency or ioctl I2C_TIMEOUT.
It turns out the SOM has an internal regulator for the I2C lines to be 1V8 where as the SSD1306 chip is running at 3V3 causing information to be mishandled. This behavior wasn't documented on the SOM.
Applying a level shifting chip to the design allowed for proper communication.
If someone has this same problem, check your schematics for voltage mismatch levels.

How do I write directly to a USB drive with over 4GB data?

I have a research project where I need to be able to fill a USB stick with a known pattern of data directly, no files or file system. My goal is to fill a drive from top to bottom with my pattern; I have written a program for writing to /dev/sd* but it is slow and does not work if the drive is over 4GB in size. The writing will stop at offset oxFFFFFFF or 2^32.
My code
#include <stdio.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <ctype.h>
int main(int argc, char **argv)
{
int fd;
uint64_t numbytes=0;
char response[4];
long int nblocks = 0; // number of blocks up to 4GB
int error;
size_t fill_length = 8;
char buf[512];
char fill[] = "ABCDEFGH";
printf("\n\n");
fd = open(argv[1], O_RDWR);
if(error = ioctl(fd, BLKGETSIZE64, &numbytes) != 0){
printf("Failed to read block device, ioctl returned: %i\n", error);
exit(0);
}
if (numbytes > 8589934592){ // Exit if more than 8 GB drive
printf("Drive is too large.l\n");
exit(0);
}
printf("Number of bytes: %lu, i.e. %.3f GiB\n", numbytes,
(double)numbytes / (1024 * 1024 * 1024));
nblocks = numbytes / 512;
printf("Number of blocks on device: %lu\n", nblocks);
strcpy(buf, fill); // fills with pattern, one time
for(int i =0; i < (512 - fill_length); i += fill_length){ // Fills up the rest of the buffer
strcat(buf, fill); // with the pattern to be repeated.
} // 512 is the default & smallest block size
printf("buf is:\n%s\n", buf);
printf("\n*** The device at %s will be completely overwritten ***\n", argv[1]);
printf("\nAre you sure you want to proceed? (Type:<Ctrl>-C to exit)\n");
// printf("\n nblocks: %lu", nblocks);
fgets(response, 3, stdin);
printf("writting to: %s\n", argv[1]);
for (int i = 0; i <= nblocks; i++)
{
write(fd, buf, 512);
}
printf("Closing...\n");
close(fd);
printf("Closed.\n");
return 0;
}
I realize my program isn't great and is dangerous as I could wipe out a HDD, but all I am looking for at this point is tips to make this work on drives over 4GB and hopefully make the process faster. It will have limited use by myself and possibly another.
A push in the right direction would be appreciated.
Use size_t for bytesize in memory, and off_t for file offsets on disk. For general integers, use intptr_t in your program. So your for loops should start with for (intptr_t i=0;
And don't write in small blocks of 512 bytes, but in something bigger (but a power of two), e.g. 16384 bytes.
If your program still does not work, use strace to find out the failing syscalls. And use perror in your program on failure of any syscall.
your code is 'effectively' writing to a hard disk that is larger than a 32bit int.
Therefore, I suggest paying attention to the built-in USB interface on the USB stick, where you can specify which logical block to write and so forth.
Note; USB sticks with built-in load levelling will NOT write sequentially on the media, irregardless of what you pass to it in the way of commands. However, most USB sticks can be told to format and so forth so you might be able to use that feature. Or you could use some utility for setting disk sectors to all (for instance) 0's Just be carefully to not overwrite the sector/block formatting information.
You can make off_t into a 64 bit value by placing this before any of the includes.
#define _FILE_OFFSET_BITS 64 // so off_t is 64 bit, see man fseeko
#include <stdio.h>
#include <unistd.h>
etc. ...
This also apparently also causes the read(), write(), fread(), fwrite(), etc. calls manage a 64-bit file offset internally. I have used it for a similar purpose when porting to Linux.
Ditto the advice not to use such a small buffer. Make it 64K (65,536 bytes) to greatly improve performance.

Reading and writing on I/O ports

I am trying to understand the following code:
#include<stdio.h>
#include<stdlib.h>
#include<sys/io.h>
#define baseport 0x378
int main()
{
int b;
if(ioperm(baseport,3,1))
{
perror("ioperm");
exit(1);
}
outb(0,baseport);
usleep(1000000);
printf("\n the status: %x,\n",inb(baseport));
if (ioperm(baseport,3,0)) {perror("ioperm"); exit(1);}
exit(0);
}
The output is 0xff, 255 in decimal, whether I write on Port 1 or Port 0 (using outb()). I cannot understand why it is 255 when I am writing 0 to it.
The result of doing inb(0x378) is hardware-dependent. Some chips return the value you have written previously with outb, and some other chips just return garbage. In any case, it is not the port to read bytes from a potentially connected device.
First you need to see the port's capabilities, input, output or both. If it can be configured as both, you have to set it to the respective mode and only then you can expect the right behavior.

Resources