In this series of blog posts, I’d like to talk a little bit about socket programming in python, from the point of view of a network engineer.
In this post, I’ll talk about the IP Header “Total Length” in context of raw sockets.
Python provides a thin wrapper around the system calls to the
socket API. You can find the reference documentation here
Setting up a RAW socket for sending
There is a protocol we can used called
socket.SOCK_RAW. When we create a new socket with
socket.SOCK_RAW, we are telling the Kernel that we will pass along the correctly constructed packet.
raw_sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
socket.AF_INET is telling the Kernel that you want to send an *A*ddress *F*amily of INET.
socket.SOCK_RAW is now telling the Kernel that you want to construct the IP header yourself (A note on this in a moment).
socket.IPPROTO_RAW is now telling the Kernel that you want to construct your own transport protocol. The value put in here will be inserted into the IP Packet as the Protocol, unless you set
socket.IPPROTO_RAW, in which case you will set the Protocol number yourself.
A note on socket.SOCK_RAW
On Linux, when you set
socket.SOCK_RAW, this automatically sets a varliable in the Kernel called
socket.IP_HDRINCL to 1.
IP_HDRINCL is a variable that controls if the Kernel is expecting to get a full and complete IP Packet from you. 1 means “yes”, 0 means you wont construct the IP Packet yourself.
However on BSD,
IP_HDRINCL is ALWAYS 0 unless you explicitly set this.
So if you are sending a full IP Packet and Protocol Frame, and
IP_HDRINCL is 0, the Kernel will pad your IP Packet and Protocol Frame inside another IP Packet. This is obviously not good.
This means you should always, for the sake of clarity, set
raw_sock.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
In the IP Header there are two length fields - IP Header Length (
ip_ihl) and Total Length (
ip_ihl controls the length of the header, or another way of putting it as per RFC:
IHL: 4 bits Internet Header Length is the length of the internet header in 32 bit words, and thus points to the beginning of the data. Note that the minimum value for a correct header is 5.
ip_tot_len, however, covers the entire packet length:
Total Length: 16 bits Total Length is the length of the datagram, measured in octets, including internet header and data.
In Linux, if you set
ip_tot_len to 0, the Kernel will figure out the length for you automatically.
BSD, on the other hand, will never calculate this value for you. You MUST calculate this yourself, after constructing your Protocol packet, and whatever payload it has.
If you do not do this, you’ll see a very unhelpful message from the Kernel:
OSError: [Errno 22] Invalid argument
If you get
OSError: [Errno 22] Invalid argument on BSD, and you’re using RAW Sockets, make sure you’re setting