-1

I was implementing the code for sending data through a raw socket. As part of that, I have to construct an IP packet. But I am facing the problem as explained below using a sample program.

#include <stdio.h>

struct iph {
    unsigned char ihl:4;
    unsigned char ver:4;
    unsigned char data;
    unsigned char data1;
};

int main()
{
    char buff[3] = {0};

    struct iph *ptr = (struct iph *)buff;
    ptr->ihl = 5;
    ptr->ver = 4;
    ptr->data = 0xAB;
    ptr->data1 = 0xCD;

    printf("%x %x %x\n", buff[0], buff[1], buff[2]);
}

Here the output I am expecting is 45 ab cd But I am getting 45 ffffffab ffffffcd. Can anyone explain how this is happening and share a solution to get the expected value?

shafeeq
  • 1,499
  • 1
  • 14
  • 30
  • Can you give me a solution? – shafeeq Dec 17 '20 at 10:04
  • I am asking the reason. Don't be funny @PaulOgilvie – shafeeq Dec 17 '20 at 10:05
  • `char buff` -> `unsigned char buff`. – Lundin Dec 17 '20 at 10:10
  • @shafeeq Paul Ogilvie is right. What you are doing here is undefined behavior. It's a violation of [strict aliasing](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule). It's also potentially undefined behavior because of alignment issues. You also can not rely on how bit-fields are located - ***at all***. So don't do these funny things. – Andrew Henle Dec 17 '20 at 10:11
  • You're _assuming_ that `iph` is 3 bytes. You should at least check with `sizeof`. You may also need to use something platform-specific to coerce the alignment you want, eg. `#pragma pack` or `__attribute__((packed))`. And you should use `memcpy` instead of just pointer-casting, in general – Useless Dec 17 '20 at 10:13
  • I had seen different examples like http://www.cs.binghamton.edu/~steflik/cs455/rawip.txt and in many other places. That is why I am asking. – shafeeq Dec 17 '20 at 11:22
  • @shafeeq There are a lot of programmers writing a lot of bad code, and those are just examples of both. That code violates strict aliasing also. And doing that is not safe - a lot of programmers think it's safe because the x86 architecture they've been limited to is ***very*** forgiving of such code. [But not always](https://stackoverflow.com/questions/46790550/c-undefined-behavior-strict-aliasing-rule-or-incorrect-alignment). For real-world examples, just Google "`SIGBUS` ARM" or "`SIGBUS` SPARC" and note how coders who venture outside of x86 get surprised that they wrote ***bad code***. – Andrew Henle Dec 17 '20 at 12:36

2 Answers2

3

char is signed on your machine and 0xab is a negative value for 8bit signed data types. So when it gets promoted, it gets promoted to the same negative value and that's 0xffffffab as an int.

Solution: use unsigned char

mch
  • 9,424
  • 2
  • 28
  • 42
1

You code has two issues, first buff array have to be 4 size not 3, second your buff type have to be unsigned char.

AriaN
  • 329
  • 1
  • 8
  • Well, it doesn't have to be size 4 because of automatic padding, but the `(struct iph *)buff` is a wildly dangerous cast none the less. `buff` is not necessarily aligned, but the struct must be. It is also a strict pointer aliasing violation. – Lundin Dec 17 '20 at 10:12
  • So the `ptr->data1` is not dangerous there? – AriaN Dec 17 '20 at 10:14
  • Generally, bit-fields are horribly standardized, but the compiler is required by the standard (C11 §6.7.2.1/11) to merge the two :4 bitfields into the same "storage unit", a char in this case. The code as whole is dangerous, but the struct is pretty certain to be 4 bytes large. – Lundin Dec 17 '20 at 10:25
  • What if we have enough space in `buff`? – shafeeq Dec 17 '20 at 11:24