Home > Blockchain >  How to send an IPv6 address with the 'struct' library in python
How to send an IPv6 address with the 'struct' library in python

Time:12-09

I'm asking for your help because I've been stuck with the same problem for 3 days. If I have :

Value1 = 0, Value2 = 3.10 and IPv6 = '2001::1'

I would like to pack all 3 values with this command: package = struct.pack(*format*, value1, value2, IPv6)

My problem is: I don't know what format characters in C type I can use to pack the IPv6 and keep its 16 bytes. I know that I can use format = 'i f ?' with i for integer / f for float but I need to find with what to replace the '?' which is the format characters in C type for an IPv6 address to pack the three values.

Please, someone can help me?

CodePudding user response:

It is not that straightforward to use struct for this, as you'd have to know the proper byte values of the IPv6 address before adding then to a struct. '2001::1' is a textual representation that is nowhere close to giving you those values: you'd have to split the string on :, replace missing values with "0", then you 'd have a 4 16 bit number list to pack in the struct. and them, certainly there are corner cases and special syntax in the IPv6 string representation you'd have to account for.

Fortunately Python already handles that for you in the. ipaddress module of the stdlib.

Just import ipaddress, format the struct for the first part of your package and concatenate it with the "packed" attribute in the IPv6Address Python automatically genreates for you:

import struct
import ipaddress


Value1 = 0
Value2 = 3.10 
IPv6 = '2001::1'

payload = struct.pack("if", (Value1, Value2))   ipaddress.ip_address(IPv6).packed

However, I wonder if it will be productive to simply pack an int and a float along with an IP address in this way - whatever code will be reading this will be super coupled with the code you are writing to that. If you are simply storing it to a file to be read back by a Python program under your control just use pickle instead. If you intend to send these values to a non Python program over a network, a schemaless textual way of conveying them, like JSON, might be much simpler.

If you really want to store these, and only these, in a compact way in order to save space, and there are tens of thousands of them, and they will be read back by the same program: try numpy arrays. They will take care of the compact binary representation for each object type and can be read and written to binary files, and numpy will take care of record offset for you.

The only use case I could see for that is if you have a program not under your control in a low level protocol that would be expecting exactly this record format. Since you are speculating about how to create the payload, and trying to convey "3.10" as a floating point value, this does not seem to be the case. Talking about that, "3.10" or other numbers might not round trip well as a nicely formed 2-decimal digit value with structs like this, due to how floating points are represented internally. I suggest you review your goals and needs there, and not overcomplicate things.

To unpack back, the easier thing is to use struct to recover just the numeric values, and pass the remaining 16 bytes back to the ip_address factory function - it automatically creates an IPv6 object, which string representation is the human friendly "2001::1".

I type the "roundtrip" in an interactive prompt:

In [30]: import struct, ipaddress

In [31]: x = ipaddress.ip_address('2001::1')

In [32]: v1 = 2;v2 = 3.10

In [33]: payload = struct.pack(">if",v1, v2)   x.packed

In [34]: print(payload)
b'\x00\x00\x00\x02@Fff \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01'

In [35]: v3, v4, nx = struct.unpack(">if", aa[:-16])   (ipaddress.ip_address(aa[-16:]),)

In [36]: print(v3, v4, nx)
2 3.0999999046325684 2001::1

Two things to be noted: the added > prefix on the struct formatting string, to ensure byte order when encoding its contents, and that the "3.10" value, as described above, does not roundtrip as a "nice two decimal places" value. Use round(number, 2) when representing the number on the server side, if needed.

  • Related