When you say you are "overlaying" your data-structure on the packet (at least that's what it seems you're saying), the value that appears in the four or eight bytes of your void*
pointer data-member (depending on your platform) is most likely a value inside either asctp_init
or scpt_data
chunk, not an actual pointer to that chunk of data. That is most likely why your pointer is not NULL, yet your seg-faulting when dereferencing the value in the pointer.
Secondly, overlaying a structure over serialized data without pragmas and/or compiler directives on the packing of the struture can be dangerous ... sometimes how you think the compiler may allocate/pad the structure is not how it actually ends up doing it, and then the structure does not match the actual format of the data-packet, and you'll end up with invalid values.
So I'm assuming right now you're trying to-do something like this by directly overlaying the first N bytes of your packet with the scpt_header
structure:
|scpt_header .........|void*|
|packet information ..|scpt_chunk.................|
That's not very portable, and can cause a lot of headaches especially when you insert network-byte-order issues into the situation. What you really want is something where you copy the contents of your SCPT packet into an internal data-structure, like a linked-list, that you can then properly manipulate.
One way could look like the following:
#include <arpa/inet.h>
unsigned char* packet_buffer = malloc(sizeof(PACKET_SIZE));
//... proceed to copy into the buffer however you are reading your packet
//now fill in your structure
unsigned char* temp = packet_buffer;
struct sctp_header header_data;
header_data.src_port = ntohs(*((uint16_t*)temp));
temp += sizeof(uint16_t);
header_data.dstport = ntohs(*((uint16_t*)temp));
temp += sizeof(uint16_t);
header_data.vtag = ntohl(*((uint32_t*)temp));
temp += sizeof(uint32_t);
//... keep going for all data members
//allocate memory for the first chunk (we'll assume you checked and it's a data chunk)
header_data.chunks = malloc(sizeof(sctp_data));
scpt_data* temp_chunk_ptr = header_data.chunks;
//copy the rest of the packet chunks into your linked-list data-structure
while (temp < (packet_buffer + PACKET_SIZE))
{
temp_chunk_ptr->tsn = ntohl(*((uint32_t*)temp));
temp += sizeof(uint32_t);
//... keep going for the rest of this data chunk
//allocate memory in your linked list for the next data-chunk
temp_chunk_ptr->data = malloc(sizeof(scpt_data));
temp_chunk_ptr = temp_chunk_ptr->data;
}
//free the packet buffer when you're done since you now have copied the data into a linked
//list data-structure in memory
free(packet_buffer);
This approach may seem cumbersome, but unfortunately if you're going to be dealing with endian and network-byte-order issues, you're not going to be able to simply overlay a data-structure on the packet data in a platform-portable manner, even if you declare it as a packed data-structure. Declaring a packed data-structure will align the bytes properly, but it won't correct endian issues which is what functions like ntohs
and ntohl
will do.
Also don't let your scpt_header
go out-of-scope without destroying the linked list it "owns" or else you're going to end up with a memory leak.
Update: If you still want to go the overlaying route, first make sure you are using a compiler directive to pack your struct so that there is no padding added. In gcc this would be
typdef struct sctp_header {
uint16_t srcport;
uint16_t dstport;
uint32_t vtag;
uint32_t chksum;
} __attribute__((packed)) sctp_header;
typedef struct sctp_data
{
uint8_t ctype;
uint8_t cflags;
uint16_t clength;
uint32_t tsn;
uint16_t stream_id;
uint16_t stream_seq;
uint32_t pp_id;
} __attribute__((packed)) sctp_data;
typedef struct sctp_init
{
uint8_t ctype;
uint8_t cflags;
uint16_t clength;
uint32_t initate_tag;
uint32_t a_rwnd;
uint16_t num_out_streams;
uint16_t num_in_streams;
uint32_t tsn;
} __attribute__((packed)) sctp_init;
but would be something else in other compilers. Also please note that I've changed your structures around a bit to better reflect how they are actually represented by the SCTP packet in-memory. Because the size of the two different packet types is different, we can't really do a union and overlay that in memory ... the union will technically be the size of the largest chunk-type member, and that's going to create problems when we try to create arrays, etc. I'm also getting rid of the pointers ... I see what you're trying to-do with them, but again, since you want to overlay these data-structures on the packet data, that's again going to cause problems since you're actually trying to "shift" the data around. The modifications to your original data-structures has been made to actually mirror how the data is laid out in memory without any fancy shifting or pointer casting. Since the type of each packet is represented by an unsigned char
, we can now do the following:
enum chunk_type { DATA = 0, INIT = 1 };
unsigned char* packet_buffer = malloc(sizeof(PACKET_SIZE));
//... copy the packet into your buffer
unsigned char* temp = packet_buffer;
sctp_header* header_ptr = temp;
temp += sizeof(sctp_header);
//... do something with your header
//now read the rest of the packets
while (temp < (packet_buffer + PACKET_SIZE))
{
switch(*temp)
{
case DATA:
sctp_data* data_ptr = temp;
//... do something with the data
temp += data_ptr->clength;
break;
case INIT:
sctp_init* init_ptr = temp;
// ... do something with your init type
temp += init_ptr->clength;
break;
default:
//do some error correction here
}
}
Keep in mind again that this method only corrects for alignment issues ... it does not correct for endianness, so beware as you read any values in a multi-byte data-type.