static const uint8_t inside function changes value - c

I am writing a small analysis tool using libpcap that sniffs traffic on an ethernet device and performs some sort of analysis on the received packets. In order to do so, I have the obvious libpcap loop:
void packet_loop(u_char *args, const struct pcap_pkthdr *header,
const u_char *packetdata) {
int size = (int)header->len;
//Before we map the buffer to the ethhdr struct,
//we check if the size fits
if (ETHER_HDR_LEN > size)
return;
const struct ethhdr *ethh = (const struct ethhdr *)(packetdata);
//If this protocol is IPv4 and the packet size is bigger than
//ETH hdr size
if (ETHERTYPE_IP == ntohs(ethh->h_proto)) {
//Before we map the buffer to the iph struct,
//we check if the size fits
if (ETHER_HDR_LEN + (int)sizeof(struct iphdr) > size)
return;
const struct iphdr *iph = (const struct iphdr*)
(packetdata + sizeof(struct ethhdr));
//If this protocol isn't UDP and the header length
//isn't 5 (20bytes)
if (IPPROTO_UDP != iph->protocol && 5 != iph->ihl)
return;
//eval_udp(packetdata, size);
const struct udphdr *udph = (const struct udphdr*)
(packetdata + sizeof(struct ethhdr) +
sizeof(struct iphdr));
if (DATA_SRCPORT == ntohs(udph->uh_sport) &&
DATA_DESTPORT == ntohs(udph->uh_dport)) {
analyse_data(packetdata);
}
}
}
that calls the follwoing code snipped on receival of a specific packet type. As you can see, I am using a static variable to keep track of the previous packet, in order to compare two.
void analyse_data(const uint8_t *packet)
{
if (!packet)
return;
static const uint8_t *basepacket;
//If there was no packet to base our analysis on, we will wait for one
if (!basepacket) {
basepacket = packet;
return;
}
const struct dataheader *basedh = (const struct dataheader *)
(__OFFSETSHERE__ + basepacket);
const struct dataheader *dh = (const struct dataheader *)
(__OFFSETSHERE__ + packet);
printf("%d -> %d\n", ntohs(basedh->sequenceid),
ntohs(dh->sequenceid));
basepacket = packet;
return;
}
struct dataheader is a regular struct, just like etthdr. I would expect a constant printout like:
0 -> 1
1 -> 2
2 -> 3
Unfortunately, I get a different printout, which is mostly right. But around every 20th-40th packet, I see the following behavior (example):
12->13
13->14
0->15
15->16
...
It is maybe interesting to note that this does NOT occcur, when I receive only packets of the specific type I look after (8-10 Mbit/s). Nevertheless, as soon as I use my tool in the "regular" network environment (around 100Mbit/s), I get this behavior. I checked my if statement, that filters the packet it works flawlessly (checking UDP source and destination ports). Wireshark also shows me that there is not a single packet on those ports that is not of that specific type.

libpcap controls the packet data it passes in to your packet_loop. Once packet_loop returns, you have no guarantee what the pointers for the packet data point to - libpcap could throw the packet away, or it could reuse the same space for a new packet.
This means if you want to compare 2 packets, you must make a copy of the 1. packet - you cannot save the pointer from one call to packet_loop and expect that pointer to be valid and point to the same packet in future calls to packet_loop. So your code could be changed to e.g.
void analyse_data(const uint8_t *packet, int size )
{
if (!packet)
return;
static const uint8_t basepacket[1024*64];
static int has_basepacket;
//If there was no packet to base our analysis on, we will wait for one
if (!has_basepacket){
if (size < sizeof basepacket) {
memcpy(basepacket, packet, size);
has_basepacket = 1;
}
return;
}
...
Also, make sure your verify the sizes everywhere. Just because the ethernet type says it is an IPv4 packet, doesn't mean you can trust it to contain a full IP packet. Just because the IP header says it is 20 bytes, doesn't mean you can trust it to contain a full IP packet, and so on for all the layers you attempt to decode.

Related

C creating a raw UDP packet

I am interested in creating a DNS (using UDP protocol to send it) response packet, however I found limited information how to create your own packet.
Most tutorials are like this https://opensourceforu.com/2015/03/a-guide-to-using-raw-sockets/
They use structs to fill in the fields and connect them into 1 sequence. But I am concerned that the compiler can pad the struct, making it "corrupted" (make the packet longer then it should be)
I fully know that there are struct attributes, that don't allow the compiler to pad structs, but I don't want to use them
Can anyone point me some resources on packet creation. I can use Libpcap and raw sockets
You do it like this:
// helper function to add uint32_t to a buffer
char *append_uint32(char *buf_position, uint32_t value) {
// network protocols usually use network byte order for numbers,
// htonl is POSIX function so you may have to make your own on other platform
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/htonl.html
value = htonl(value);
memcpy(buf_postion, &value, sizeof value);
return buf_position + sizeof value;
}
// example code using the function:
// generate packet with numbers 0...9 in network byte order
void func() {
char buf[sizeof(int32_t) * 10];
char *bptr = buf;
for(uint32_t i=0; i<10; ++i) {
bptr = append_uint32(bptr, i);
}
// do something with buf (use malloc instead of stack if you want return it!)
}

How to convert struct to char array in C?

I am trying to send an ethernet packet using RAW socket in C Linux. I have following struct definition in my code:
typedef struct vlink_header_s
{
uint8_t verCmd;
uint8_t reverseVerCmd;
}vlink_header_t;
typedef struct vlink_reg_rd_s
{
vlink_header_t header;
uint32_t address;
uint16_t length;
}vlink_reg_rd_t;
In main i created a struct:
vlink_reg_rd_t g_pkt;
g_pkt.header.verCmd = 0x10|VLINK_CMD_REG_RD;
g_pkt.header.reverseVerCmd = ~(g_pkt.header.verCmd);
g_pkt.address = 0x0007 .....
and message:
char sendbuf[1024];
struct ether_header *eh = (struct ether_header *) sendbuf;
how do I add all the info from the struct g_pkt to this sendbuf after ether_header so I can send a complete packet using:
sendto(sockfd, sendbuf, txLen, 0, (struct sockaddr*)&socket_address, sizeof(struct sockaddr_ll));
Everything else in my code is working, I tried other ways by adding info to sendbuf one by one and it works fine and my machine receive the packets on the other side too. I just want to make it more versatile because there are a bunch of commands and structs for each commands will work best. Thanks.
Try to use the memcpy function:
#include<iostream>
typedef struct vlink_header_s
{
uint8_t verCmd;
uint8_t reverseVerCmd;
}vlink_header_t;
typedef struct vlink_reg_rd_s
{
vlink_header_s header;
uint32_t address;
uint16_t length;
}vlink_reg_rd_t;
using namespace std;
int main()
{
vlink_reg_rd_t data;
//TODO: Set values into typedef data.
int size = sizeof(vlink_reg_rd_t); //get
char* buffer = new char[size];
memset(buffer, 0x00, size);
memcpy(buffer, &data, size); //Copy data from vlink_reg_rd_t to char*
//TODO: Send the buffer.
delete[] buffer; //free memory
return 0;
}
IMPORTANT: be aware of the order in which data types number are written into the buffer. Also it is necessary check the align the data into the structure to avoid extra bytes at the moment of use memcpy. Here you can check this topic:
for Microsoft:
https://msdn.microsoft.com/en-us/library/xh3e3fd0.aspx
https://msdn.microsoft.com/en-us/library/83ythb65.aspx
For Gcc:
https://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Type-Attributes.html
I have done this way with protocol buffer , you can take a look at : https://www.google.com.vn/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=protocol+buffer&* .
Edit : this way called serialize data , as you serialize your data into a proto file then compile it to the packet file that you use on both server and client .

IPC Socket latency fluctuation on Linux

I am working on a Client-Server Architecture on a single local machine with Linux 3.16 using AF_UNIX IPC Sockets. I have implemented a send_all and recv_all Functions to ensure sending/receiving all the wanted data and I use clock_gettime() with CLOCK_MONOTONIC to make time measurement just before the send-call on the client side and after the receive-call on the server side. Every packet I send is very small (11 Byte + the struct timespec). When I calc the delta of these two values after each transmission, I get fluctuating results by the factor ~15 while the shortest is around 150µs and the longest is above 2 milliseconds. While the <200µs would be fast enough for my needs, the >2ms are already very critical, so how can this varying latency be explained and is there a way to make the latency consistently as fast as possible for Sockets?
I am using SOCK_STREAM in the initialization and I thought that Nagle's Algorithm has to do something with the problem, but TCP_NODELAY is not settable via setsockopt() when using AF_UNIX (it seems to only work in AF_INET?). Also I'm thinking about using SOCK_DGRAM, but before I jump into that (for me) new area, I would like to know if I can even solve my problem with it.
My essential Functions for the Sockets are mostly inspired by Beej's Guide, but here some code anyway:
/* Packet Definition */
typedef struct Packet_ {
Byte opcode;
Byte from;
Byte to;
Byte payload[8];
timespec timestamp;
} Packet;
// Send
void send_all(SOCKET s, void* data, size_t len)
{
unsigned char* buffer_ptr = (unsigned char*)data;
unsigned int out_counter = 0;
while(out_counter < len)
{
out_counter += send(s, buffer_ptr, len - out_counter, 0);
}
}
// Receive
void recv_all(SOCKET s, void* buffer, size_t len)
{
unsigned char* buffer_ptr = (unsigned char*)buffer;
unsigned int in_counter = 0;
while(in_counter < len)
{
in_counter += recv(s, buffer_ptr + in_counter, len - in_counter, 0);
}
}
// Set Packet Header
void setPacketHeader(Packet* packet, Byte receivers, Byte opcode)
{
packet->from = this->get_ident();
packet->to = receivers;
packet->opcode = opcode;
}
// Set Packet Payload
void setPacketPayload(Packet* packet, void* payload, int pl_size)
{
clock_gettime(CLOCK_MONOTONIC, &(packet->timestamp));
memcpy(&(packet->payload), payload, pl_size);
}
Thanks for your time

Reading data from socket with readv

I'm having problems with a client-server communication made with writev()/readv().
I have two struct, header and data defined like this:
typedef struct {
int op;
int key;
} message_hdr_t;
typedef struct {
int len;
char *data;
} message_data_t;
The server does (in short):
message_hdr_t h = {1, 11};
message_data_t d;
d.len = 3;
strcpy(d.data, "msg");
struct iovec tosend[2];
tosend[0].iov_base = &h;
tosend[0].iov_len = sizeof(message_hdr_t);
tosend[1].iov_base = &d;
tosend[1].iov_len = sizeof(message_data_t);
writev(socket, tosend, 2);
close(socket);
The client (in short):
struct iovec received[2];
readv(socket, received, 2);
message_hdr_t header;
header.op = ((message_hdr_t *) received[0].iov_base)->op;
header.key = ((message_hdr_t *) received[0].iov_base)->key;
printf("Received op: %i, key: %i\n",header.op,header.key;
close(socket);
But the client gets a segfault because received[0].iov_base is NULL. Why?
The socket is correctly opened and the client is correctly connected to the server. It's an AF_UNIX socket.
First, in your server code, you are writing a pointer. This makes no sense. You don't want to transmit pointers over the wire. To transmit a string, you have to do something like this:
char* message = ...;
message_hdr_t h = {1, 11};
uint32_t message_length = strlen(message);
struct iovec tosend[3];
tosend[0].iov_base = &h;
tosend[0].iov_len = sizeof(message_hdr_t);
tosend[1].iov_base = &message_length;
tosend[1].iov_len = sizeof(message_length);
tosend[2].iov_base = message;
tosend[2].iov_len = message_length;
(You may want to move the string length to the message header and save one element of the vector, and make the protocol more readable).
Second, readv won't allocate memory for you, or divine out how many bytes you want to read. It's your job to correctly initialize iov_base and iov_len in the IO vector passed to readv. In order to read a dynamically-allocated variable-size string, you probably want to read twice. First, read a part of the message that contains the length of the string, then allocate the string, and read the rest of the message.

Mismatch in assigning IP address to a buffer and printing contents of buffer

I am implementing raw sockets in C. In the code below, I am parsing the IP header I received from sender.
a. I will send back the ack as well so storing IP address received in a buffer(ret_ip).
b. I don't have another computer so using lo (local loop back) as my interface.
//first two printf statements are printing the right address, 10.100.207.74
//daddr SENT = 32.112.247.9saddr SENT = 36.112.247.9
How can I get it correct?
I think this problem is due to memcpy whose first argument is pointing to unsigned char while second argument is pointing to _be32.
What I actually want to do in my program is : ret_ip's first 4 bytes should contain the destination address and next 4 the source address. Then I will create IP header and make dest addr=source addr and source-addr=dest-addr. and send ACK to sender.
char* ParseIPHeader(unsigned char *packet,int len)
{
struct ethhdr *ethernet_header;
struct iphdr *ip_header;
char *ret_ip;
ethernet_header=(struct ethhdr *)packet;
if(ntohs(ethernet_header->h_proto)==ETH_P_IP)
{
if(len>=(sizeof(struct ethhdr)+sizeof(struct iphdr)))
{
ip_header=(struct iphdr*)(packet+sizeof(struct ethhdr));
ret_ip=malloc(2*(sizeof(ip_header->daddr)));
printf("Dest IP address: %s\n",inet_ntoa(ip_header->daddr));
printf("Source IP address: %s\n",inet_ntoa(ip_header->saddr));
memcpy(ret_ip,&(ip_header->daddr),sizeof(ip_header->daddr));
memcpy(ret_ip+4,&(ip_header->saddr),4);
printf("daddr SENT = %s",inet_ntoa(ret_ip));
printf("saddr SENT = %s",inet_ntoa(ret_ip+4));
}
else
printf("IP packet does not have full header\n");
}
else
{
//not an IP packet
}
return ret_ip;
}
Thanks :)
first problem is your memory allocation
ret_ip=malloc(2*(ip_header->daddr));
it should be
ret_ip=malloc(2*(sizeof(ip_header->daddr)));
but why you are not using the ip_hdr struct again ? for example
struct iphdr *ret_ip = malloc(sizeof(iphdr));
ret_ip->daddr = ip_header->saddr;
ret_ip->saddr = ip_header->daddr;
i suggest this solution is much easier ;)

Resources