How to set socket option IP_DONTFRAG in python?

2.1k Views Asked by At

How can I set DONT_FRAGMENT flag in IP header using python sockets?

The following code

socket.setsockopt(socket.IPPROTO_IP, socket.IP_DONTFRAG, 1)

gives me this error:

AttributeError: 'module' object has no attribute 'IP_DONTFRAG'

Anyone has an idea?


def create_sender_session(self):
    logging.debug("Create Sender Session")
    if (self.send_ip_ver == 6 or self.resp_ip_ver == 6):
        self.sender_socket = socket.socket(
            socket.AF_INET6, socket.SOCK_DGRAM)
        self.sender_socket.setsockopt(
            socket.IPPROTO_IPV6, socket.IPV6_TCLASS, self.tos)
        self.sender_socket.setsockopt(
            socket.IPPROTO_IPV6, socket.IPV6_UNICAST_HOPS, self.ttl)
    else:
        self.sender_socket = socket.socket(
            socket.AF_INET, socket.SOCK_DGRAM)
        self.sender_socket.setsockopt(
            socket.SOL_IP, socket.IP_TTL, self.ttl)
        self.sender_socket.setsockopt(
            socket.IPPROTO_IP, socket.IP_TOS, self.tos)
    self.sender_socket.setsockopt(
        socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    self.sender_socket.bind((self.send_addr, self.send_port))
    logging.info('Sender started: {}:{}'.format(
        self.send_addr, self.send_port))
1

There are 1 best solutions below

1
On

It's harder to find documentation for some socket options than for others. *DONTFRAG seem to be in the latter category.
Some references:

  • [IBM]: setsockopt() - Set Socket Options

  • [FreeBSD]: IP(4) (emphasis is mine):

    IP_DONTFRAG may be used to set the Don't Fragment flag on IP packets. Currently this option is respected only on udp(4) and raw ip(4) sockets, unless the IP_HDRINCL option has been set. On tcp(4) sockets, the Don't Fragment flag is controlled by the Path MTU Discovery option. Sending a packet larger than the MTU size of the egress interface, determined by the destination address, returns an EMSGSIZE error.

(qaic-env) [cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q049051558]> ~/sopr.sh
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[064bit prompt]> uname -a
Linux cfati-5510-0 5.10.60.1-microsoft-standard-WSL2 #1 SMP Wed Aug 25 23:20:18 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
[064bit prompt]> head -n 2 /etc/os-release
NAME="Ubuntu"
VERSION="20.04.4 LTS (Focal Fossa)"
[064bit prompt]>
[064bit prompt]> python2 -c "import socket;print([e for e in dir(socket) if \"DONTFRAG\" in e])"
['IPV6_DONTFRAG']

As a note, Python 2 is no longer maintained (EndOfLife), although at the time the question was asked, it still was, so will switch to Python 3.

[064bit prompt]> python -c "import socket;print([e for e in dir(socket) if \"DONTFRAG\" in e])"
['IPV6_DONTFRAG']

[Python.Docs]: socket - Low-level networking interface doesn't mention them. However, only IPV6_DONTFRAG seems to exist.
And that isn't specific to Python.

[064bit prompt]> python -c "import socket, sys;print(\"{:s}\n{:s}\n{:d}\n\".format(sys.version, sys.platform, socket.IPV6_DONTFRAG))"
3.8.10 (default, Nov 26 2021, 20:14:08)
[GCC 9.3.0]
linux
62

[064bit prompt]> grep -r DONTFRAG /usr/include
/usr/include/linux/in6.h:#define IPV6_DONTFRAG          62
/usr/include/x86_64-linux-gnu/bits/in.h:#define IPV6_DONTFRAG           62

As IPV6_DONTFRAG name states, it's specific for IPv6 sockets (and it most likely won't work for IPv4):

[064bit prompt]> python
Python 3.8.10 (default, Nov 26 2021, 20:14:08)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> import socket
>>>
>>> s6d = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
>>> s6d.getsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG)
0
>>> s6d.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG, 1)
>>> s6d.getsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG)
1
>>> s6d.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG, 2)
>>> s6d.getsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG)
1
>>> s6d.close()
>>>
>>> s6s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
>>> s6s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG, 1)
>>> s6s.close()
>>>
>>> s4d = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s4d.getsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 95] Operation not supported
>>> s4d.getsockopt(socket.IPPROTO_IP, socket.IPV6_DONTFRAG)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 92] Protocol not available
>>>
>>> s4d.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG, 1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 92] Protocol not available
>>> s4d.setsockopt(socket.IPPROTO_IP, socket.IPV6_DONTFRAG, 1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 92] Protocol not available
>>> s4d.close()

So, for IPv6 sockets it didn't raise an exception (tests are required to establish whether under the hood it did what is supposed to).
For IPv4, the Path MTU Discovery option can be tried ([SO]: Path MTU Discovery using Socket option - IP_MTU & IP_MTU_DISCOVER).

Don't know how relevant it is, but it seems to be working on Win:

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q049051558]> "e:\Work\Dev\VEnvs\py_pc064_03.09_test0\Scripts\python.exe"
Python 3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> import socket
>>>
>>> s4d = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s4d.getsockopt(socket.IPPROTO_IP, socket.IPV6_DONTFRAG), s4d.getsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG)
(0, 0)
>>> s4d.setsockopt(socket.IPPROTO_IP, socket.IPV6_DONTFRAG, 1)
>>> s4d.getsockopt(socket.IPPROTO_IP, socket.IPV6_DONTFRAG), s4d.getsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG)
(1, 1)
>>> s4d.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG, 1)
>>> s4d.getsockopt(socket.IPPROTO_IP, socket.IPV6_DONTFRAG), s4d.getsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG)
(1, 1)
>>> s4d.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG, 0)
>>> s4d.getsockopt(socket.IPPROTO_IP, socket.IPV6_DONTFRAG), s4d.getsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG)
(0, 0)
>>> s4d.close()
>>>
>>> s4s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s4s.getsockopt(socket.IPPROTO_IP, socket.IPV6_DONTFRAG), s4s.getsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG)
(1, 1)
>>> s4s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG, 0)
>>> s4s.getsockopt(socket.IPPROTO_IP, socket.IPV6_DONTFRAG), s4s.getsockopt(socket.IPPROTO_IPV6, socket.IPV6_DONTFRAG)
(0, 0)
>>> s4s.close()