2

I found this piece of code online and it works as part of my project, but I'm not sure why. I don't want to just use it without understanding what it does.

type = (packet_data[12] << 8) | packet_data[13];

if I use it I get the proper type (0x0800 for IPv4) and can use it for comparison on printing out whether it's IPv4 or IPv6. If I don't use it and try something like:

if(packet_data[12] == 08 && packet_data[13] == 00)
    print out IPv4

it doesn't work (compiling errors).

Also if I just print out the values like

printf"%02X", packet_data[12];
printf"%02X", packet_data[13];

it prints out the proper value in the form 0800, but I need to print out that it's an IPv4 type. Which is why I need to comparison in the first place. Thanks for any piece of advice or explanation on what this does would be much appreciated. Thanks

Mike1982
  • 439
  • 10
  • 26
  • Always show *what errors you get* – Sami Kuhmonen Nov 25 '17 at 21:27
  • `08` is invalid as it is an octal prefix with 8 in it. `0x800` is hexadecimal – Jean-François Fabre Nov 25 '17 at 21:27
  • `if(packet_data[12] == 0x8 && packet_data[13] == 0)` would work – Jean-François Fabre Nov 25 '17 at 21:28
  • 1
    The integer literal `08` is invalid. Integer numbers starting with `0` are [*octal*](https://en.wikipedia.org/wiki/Octal) numbers, for which digits go from `0` to `7` (inclusive). [Any good beginners book](http://stackoverflow.com/questions/562303/the-definitive-c-book-guide-and-list) should have told you that. – Some programmer dude Nov 25 '17 at 21:30
  • @Someprogrammerdude : I am willing to bet that most _beginners_ books do not mention it at all, including those in the list you linked. I am not sure that makes them _not good_, but if you believe that, then the list is perhaps not a good suggestion. The one available for free in the beginners list of the highest scoring answer does not for example. Most occasions I have seen octal literals used in C code it was by accident and was the cause of bugs and errors such as this. The octal notation itself is hardly a subject for beginners, so I would not expect it to be covered. – Clifford Nov 25 '17 at 21:56

3 Answers3

1
if(packet_data[12] == 08 && packet_data[13] == 00)

the right literal operands are seen as octal base literals by the compiler.

Fortunately for you, 8 cannot represent an octal number and you're getting a compilation error.

You mean hexadecimal literals:

if (packet_data[12] == 0x8 && packet_data[13] == 0x0)

this line:

(packet_data[12] << 8) | packet_data[13]

recreates the big endian value (network convention) of the data located at offsets 12 & 13. Both are equivalent in your case, although the latter is more convenient to compare values as a whole.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • aaaah ok thank you very much, so the first line that I wasn't sure about type = (packet_data[12] << 8) | packet_data[13] does that for me? (converts the hexadecimal into an int). I looked up the << operator and it's bitwise operator, but not sure why that's used in the example I saw. Also why is there an | (or) symbol between them. Then it stores the results in type which I can compare as if(type == 0x0800) – Mike1982 Nov 25 '17 at 21:42
  • yes, exactly. it rebuilds the value from MSB & LSB (big endian) – Jean-François Fabre Nov 25 '17 at 21:46
  • @Mike1982 : No - it does not *"converts the hexadecimal into an int"* - there is no hexadecimal. It packs two 8 bit integers into a single 16 bit integer. To do that you move the most-significant byte to the upper 8 bits, and the least into the lower. The `|` is the bitwise-or operator. In this case you could also use the arithmetic `+` as it happens, but that might be misleading. – Clifford Nov 25 '17 at 22:03
0
type = (packet_data[12] << 8) | packet_data[13];

The << is bitwise shift left. It takes the binary representation of the variable and shifts its 1's to the left, 8 bits in this case.

'0x0800' looks like 100000000000 in binary. So in order for 0x0800 to be the type, it has to end up looking like that after | packet_data[13]. This last part is bitwise OR. It will write a 1 if either the left side or the right side have a 1 in that place and a 0 otherwise.

So after shifting the value in packet_data[12], the only way for it to be type 0x0800 (100000000000) is if packet_data[13] looks like 0x0800 or 0x0000:

type = (0x800)  <==>  ( 100000000000 | 100000000000 )
type = (0x800)  <==>  ( 100000000000 | 000000000000 )

Also, to get the 0x out from printf() you need a to add the %# format specifier. But to get 0x0800 you need to specify a .04 which means 4 characters including leading zeros. However this won't output the 0x if the type is 0. For that you'd need to hardcode the literal 0x into printf().

printf("%#02x\n", data);
printf("%#.04x\n", data);
printf("0x%.04x\n", data=0);

Output

0x800
0x0800
0x0000
Elfen Dew
  • 114
  • 1
  • 9
0

packet_data[12] << 8 takes the first Ethertype octet and shifts it 8 bits to the left to the upper 8 bits of a 16-bit word.

| packet_data[13] takes the second Ethertype octet and bitwise-ORs it to the previous 16-bit word.

You can then compare it to 0x0800 for IPv4 or 0x86DD for IPv6; see a more complete list on https://en.wikipedia.org/wiki/EtherType#Examples

As has already been pointed out 08 doesn't work since numerals starting with 0 represent octal numbers, and 8 doesn't exist in octal.

Zac67
  • 2,761
  • 1
  • 10
  • 21