Casting char array to struct pointer - c

I am trying to understand the following casting from this code
char out_packet_buffer[4500] ;
struct ip6_hdr *iphdr ;
iphdr = (struct ip6_hdr *) &out_packet_buffer[0];
Is my understanding correct that the member variables of the struct iphdr are stored in char array out_packet_buffer? Later in the code, out_packet_buffer is never used. Instead, iphdr is memcpyied to an uint8_t memory location (ether_frame). But iphdr is not uint8_t.
I'd appreciate any guidance for me to understand what is happening here.
Thanks

Is my understanding correct that the member variables of the struct iphdr are stored in char array out_packet_buffer?
Kind of. What happens in this casting is that we start "looking" at the memory chunk that starts from &out_packet_buffer[0] (or just out_packet_buffer) as a struct ip6_hdr instead of a char[].
Any later usage of iphdr is using the same memory, but splits it into struct ip6_hdr members instead of into char
As #Christian Gibbons said, I also think this violates strict aliasing which is UB

It looks like the code is preparing a packet for transmission over a network. The packet will consist of a header and a payload. The whole packet is, presumably, stored in out_packet_buffer. The ip6_header structure is the first few bytes of this, the data payload follows after. Using a structure for the header makes the code more readable but there’ll probably be a "structure order to network order" function just before it’s sent to a socket.
In any case, the data packet is just a sequence of bytes, so casting it to any 8-bit type is feasible

Related

How can I possibly send this struct over network?

I've got the following struct:
struct fetch_info_t {
u_int8_t grocery_type;
u_int8_t arg[1024];
} __attribute__((packed));
I'd like to send this over a socket to a server, to request data. I'd very much like to avoid any libraries, such as protobuf.
grocery_type can be any value between 1 and 255. Some grocery types, say type 128, must provide additional information. I'ts not enough to provide type 128, I'd also like to provide Cheeses as a string. Having that said, type 129 must provide a number, u_int32_t and not a string, unlike 128.
Basically I've allocated 1024 bytes for the additional information the system may require. The question is, how do I send it over a socket, or more specifically, populate arg with the right information non-system-dependant? I know htonl on the number could be used, but how do I actually set the buffer value to that?
I'd imagine that the info sending would actually eventually be casting the struct pointer to unsigned char array and send it like that over a socket. Let me please know if there's a better way.
You cannot assign directly the 32-bit value to the array
because the correct alignment is not guaranteed.
memcpy() will just replicate the bytes with not alignment problem.
u_int32_t the_value=htonl( ... );
struct fetch_info_t the_info;
the_info.grocery_type=129;
memcpy(the_info.arg, &the_value, sizeof(the_value));
Then, because your structure is packed, you can send it with
send(my_socket, &the_info,
sizeof(the_info.grocery_type)+sizeof(the_value), 0);
In case you need to send a string
char *the_text= ... ;
size_t the_size=strlen(the_text)+1;
struct fetch_info_t the_info;
the_info.grocery_type=128;
memcpy(the_info.arg, the_text, the_size);
send(my_socket, &the_info,
sizeof(the_info.grocery_type)+the_size, 0);
Note that the '\0' is transmitted here.

Regarding Pointer to structure in C

I am writing a program to implement the virtual software router. My C programming Skills are not that great. When I am going through my skeleton code, I came across this :
struct ip *ippacket = (struct ip*) (packet + sizeof(struct sr_ethernet_hdr));
Can anybody please explain what exactly it means ?
For a good understanding look up "pointer arithmetic."
If 'packet' is a pointer to a packet this is adding the size of a packet header, so the result 'ippacket' would be a pointer to the first byte that follows the packet header.
To answer for certain we would have to know how the variable packet was declared. I'll assume it is a pointer to a type of size 1, for example char* packet.
It appears that packet is expected to point to a block of memory that starts with a struct sr_ethernet_hdr followed by a struct ip, and it is desired that ippacket will point to the struct ip.
Pointer with name ippacket pointing at the adress of a struct ip is initialized to point to the address packet + sizeof(struct sr_ethernet_hdr) after casting that address to the correct type.
You should not be asking this sort of question on stackoverflow, it doesn't contribute to the community and is too basic to be helpful. Also you need to know this sort of thing already if you are writing a virtual router!

Casting arrays and structs

Suppose I have some complex struct
struct icmphdr
{
u_int8_t type;
u_int8_t code;
u_int16_t checksum;
/* Parts of the packet below don’t have to appear */
union
{
struct
{
u_int16_t id;
u_int16_t sequence;
// Type of ICMP message
// Packet code
// Datagram checksum
} echo;
u_int32_t gateway;
struct
{
u_int16_t __unused;
u_int16_t mtu;
} frag;
} un;
};
and a
char buf[SIZE];//for some integer SIZE
what is the meaning and the interest of this cast ?
ip=(struct icmphdr*)buf; //ip was formerly defined as some struct iphdr *ip;
The likely scenario behind your code is this:
The programmer wanted to create a data protocol and represent the various contents as a struct, to ease programming and improve code readability.
The underlying API probably only allows data transmissions on byte basis. This means that the struct will have to be passed as a "chunk of bytes". Your particular code appears to be the receiver: it has a chunk of raw bytes and states that the data in those bytes corresponds to a struct.
Formally & theoretically, the C standard does not define what happens when you cast between pointers to different data types. In theory, anything can happen if you do. But in practice/the real world, such casts are well-defined as long as there some sort of guarantee about the structure of the data.
Here is where you can get problems. Many computers have alignment requirements, meaning that the compiler is free to insert so-called padding bytes anywhere inside your struct/union. These padding bytes may not necessarily be the same between two compilations, and they may certainly not be the same between two different systems.
So you have to either ensure that both the sender and the receiver have no padding enabled, or that they have the same padding. Otherwise you cannot use structs/unions, they will cause the program to crash and burn.
The quick & dirty way to ensure that struct padding isn't enabled, is to use a compiler option such as the non-standard #pragma pack 1, which is commonly supported by many compilers.
The professional, portable way is to add a compile-time assert to check that the size of the struct is indeed as intended. With C11, it would look like
static_assert(sizeof(struct icmphdr) ==
(sizeof(uint8_t) +
sizeof(uint8_t) + ... /* all individual members' types */ ),
"Error: padding detected");
If the compiler doesn't support static_assert, there are several ways to achieve something similar with various macros, or even a runtime assert().
That's pretty bad. Don't ever make a char buffer and cast it to a struct, because the alignment will be wrong (ie, the char buffer is going to have some random starting address because strings can start anywhere, but ints need/should have addresses multiples of four on most architectures).
The solution is not to do nasty casts like that. Make a proper union that will have the alignment of the most restrictive of its members, or use a special element to force the alignment you need if you have to (see the definition of sockaddr_storage in your /usr/include/sys/socket.h or similar).
Illustration
You create a buffer on the stack and read some data into it:
char buf[1024]; int nread = read(fd, &buf, sizeof(buf));
Now you pretend the buffer was the struct:
CHECK(nread >= sizeof(struct icmphdr));
struct icmphdr* hdr = (struct icmphdr*)buf;
hdr->u.gateway; // probable SIGSEGV on eg Itanium!
By reinterpreting the buffer as a struct, we bypassed the compiler's checks. If we're unlucky, &hdr->u.gateway won't be a multiple of four, and accessing it as an integer will barf on some platforms.
Illustration of solution
strut iphdr hdr; int nread = read(fd, &hdr, sizeof(hdr));
CHECK(nread == sizeof(hdr));
hdr.u.gateway; // OK
Let the compiler help you. Don't do grotty casts. When you make a buffer, tell the compiler what you're going to use the buffer for so it can put it in the correct place in memory for you.

Struct pointer cast as uint8_t * throws an error

in my function I allocate memory for and fill a structure called messagePacket
struct messagePacket *packet = malloc(sizeof(struct messagePacket));
//fill
When I try to cast the pointer as a (uint8_t *), gcc throws a warning that says: large integer implicitly truncated to unsigned type
sendBuf(..., (uint8_t *)packet);
I've been able to do the following just fine, and I understand I can use this approach as a workaround. I'm here because I would rather learn from this than work around it.
uint8_t *buf = malloc(sizeof(struct messagePacket));
The size of struct messagePacket = 1209 B. My best guess is that the chunk of memory is super large that I gets stored in a high memory address, such as a 16 bye address? But that doesn't fit with the fact that I can malloc a uint8_t * of the same size.
I guess the compiler notices that your struct is larger than 8bit and using uint8_t you will only address the first byte of the structure.
Since this seems to be intended you could cast to (void *) and then to (uint8_t *).
But you should tell sendBuf the buffer size which is sizeof(struct messagePacket).
I think the warning is about some other argument. Please provide the full code for that line, the prototype for the sendBuf() function, and the full compiler warning for the line in question.
As a general idea, the sendBuf() function should probably use a const void * rather than a const uint8_t * for the data to send. See send() and friends.

Explanation of piece of code used in header files for socket programming

Can anybody explain me this piece of code?
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
here sin_zero is a char array but what is remaining part? It should be some integer. what this sign "-" means? Can anybody explain this to me?
Well, the - is called a "minus" :-) Seriously, everything between the square brackets is meant to calculate the size of sin_zero, which is a so-called padding. It's a member inside struct sockaddr_in, and it's just here to make sure that struct sockaddr_in is exactly of a certain size, most likely 16 bytes. The idea is to ensure that most(*) struct sockaddr variants are of the same size to avoid malloc problems.
Quoting a document I found on the subject:
The POSIX specification requires only three members in the structure: sin_family, sin_addr, and sin_port. It is acceptable for a POSIX-compliant implementation to define additional structure members, and this is normal for an Internet socket address structure. Almost all implementations add the sin_zero member so that all socket address structures are at least 16 bytes in size.
(*) In an earlier version, I wrote all. #MaximYegorushkin correctly noted that this is not true, for example struct sockaddr_un is bigger. If you want the maximum size that fits all types of addresses, use sizeof(struct sockaddr_storage). This struct exists only for this purpose (AFAIK).
sin_zero is a structure member that is used to pad out the structure to a certain minimum size. In this case, the amount of padding is calculated by starting with the desired size (sizeof (struct sockaddr)) and subtracting the space taken up by the other struct members from it.
So, to answer the question: The sign "-" here just means "subtraction".
I run into this question while learning C, too.
For other beginners (like me) it could be interesting to know that it seems to be kind of important clearing *sin_zero* in your programs (more on this topic can be found HERE).
On some systems/architectures it can cause problems and on others it can be fine not explicitly clearing *sin_zero*.
Example for clearing sin_zero:
struct sockaddr_in serversin;
memset(&serversin, 0, sizeof(serversin));
serversin.sin_family = ...;
serversin.sin_addr = ...;
serversin.sin_port = ...;
To prevent some future bugs I think it's best practice to explicitly clear it.
Sockaddr contains some things like in_addr and in_port_t, and it has room for some more. The sin_zero is the remaining size in sockaddr. It is the size of sockaddr which is not filled with something else.
Supposedly the sin_zero variable would be initialised to all null bytes, and set as the last field or at the end of the sockaddr struct. The purpose of this is to set the remaining bytes to null.
sin_zeor is a char array with the size calculated with the formula in the [] brackets
The '-' sign is actually a mathematical minus sign :-)
hth
Mario
It calculates the size of the array based on the size of some structs and a constant. The sign "-" means minus.

Resources