7

我必须编写一个代码,我需要在 python 中使用 udp 协议发送数据。我需要将数据包大小设置为网络的 MTU 值。有什么方法可以决定网络的 MTU 值在 python 中编写一些代码吗?

4

4 回答 4

8

此答案取自 http://books.google.co.il/books?id=9HGUc8AO2xQC&pg=PA31&lpg=PA31&dq#v=onepage&q&f=false (第 31 页)

s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
hostName = #ip here
Port = 9999
s.connect((hostName, Port))
s.setsockopt(socket.IPPROTO_IP, IN.IP_MTU_DISCOVER, IN.IP_PMTUDISC_DO)
try:
    s.send('#' * 1473)
except socket.error:
    print 'The message did not make it'
    option = getattr(IN, 'IP_MTU', 14)
    print 'MTU:', s.getsockopt(socket.IPPROTO_IP, option)
else:
    print 'The big message was sent! Your network supports really big packets!'
于 2012-12-23T15:07:06.400 回答
1

有一个提供此功能的 github-gist:

import re
import socket
import struct
import logging
import subprocess
from fcntl import ioctl

SIOCGIFMTU = 0x8921
SIOCSIFMTU = 0x8922

log = logging.getLogger(__name__)

def get_mtu_for_address(ip):
    routeinfo = subprocess.check_output(['ip', 'route', 'get', ip])
    dev = re.search('.*dev (\w+) .*', routeinfo).groups()[0]
    mtuinfo = subprocess.check_output(['ip', 'link', 'show', dev])
    mtu = re.search('.*mtu ([0-9]+) .*', mtuinfo).groups()[0]
    return int(mtu)

class Iface:
    def __init__(self, ifname):
        self.ifname = ifname

    def get_mtu(self):
        '''Use socket ioctl call to get MTU size'''
        s = socket.socket(type=socket.SOCK_DGRAM)
        ifr = self.ifname + '\x00'*(32-len(self.ifname))
        try:
            ifs = ioctl(s, SIOCGIFMTU, ifr)
            mtu = struct.unpack('<H',ifs[16:18])[0]
        except Exception, s:
            log.critical('socket ioctl call failed: {0}'.format(s))
            raise

        log.debug('get_mtu: mtu of {0} = {1}'.format(self.ifname, mtu))
        self.mtu = mtu
        return mtu

    def set_mtu(self, mtu):
        '''Use socket ioctl call to set MTU size'''
        s = socket.socket(type=socket.SOCK_DGRAM)
        ifr = struct.pack('<16sH', self.ifname, mtu) + '\x00'*14
        try:
            ifs = ioctl(s, SIOCSIFMTU, ifr)
            self.mtu = struct.unpack('<H',ifs[16:18])[0]
        except Exception, s:
            log.critical('socket ioctl call failed: {0}'.format(s))
            raise

        log.debug('set_mtu: mtu of {0} = {1}'.format(self.ifname, self.mtu))

        return self.mtu


if __name__ == "__main__":
    import sys
    logging.basicConfig()

    mtu = None
    if len(sys.argv) > 2:
        dev,mtu = sys.argv[1:]
    elif len(sys.argv) > 1:
        dev = sys.argv[1]
    else:
        dev = 'eth0'

    iface = Iface(dev)
    if mtu is not None:
        iface.set_mtu(int(mtu))

    print dev,'mtu =',iface.get_mtu()

来源:https ://gist.github.com/nzjrs/8934855

于 2016-12-27T02:47:34.090 回答
0

接受的答案在 Python 3.7 中对我不起作用。我得到:OSError: [Errno 6] Device not configured

但是,psutil现在已经内置了。

import psutil
print(psutil.net_if_stats())

结果是:

{
  'lo0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=16384), 
  'en0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=1500), 
  ...
}
于 2021-04-14T22:28:41.560 回答
0

您可以简单地对带有DF (Don't Fragment)标志的 ping 进行二进制搜索。这是通过上述技术查找 MTU 的工作编码。它为您提供了完整数据包路由路径的最小 MTU,也就是您可以发送的最大有效负载。

仅在 Windows 上测试(不适用于 Linux/Mac,因为不同操作系统中的 ping 标志不同)


# tested on Windows 10 Home and python 3.6 [at Great Istanbul, Turkey]
import subprocess
from time import perf_counter


class FindMinMtu:
    """
        - Find Minimum "Maximum Transmission Unit" of a packet routing path via Binary Search

        - Suppose you want to find how much data you can send in each packet
          from London to Turkey?
        - Now we need to remember MTU and MSS (Max. Segment size) isn't not the same.
          MSS is the actual data (not headers) you can send. A typical formula for MSS is
          MSS = MTU - (IP header_size  + TCP/UDP/Any Transport Layer Protocol header_size)
          whereas MTU = Everything in packet - Ethernet headers
          MTU typical refers to Ethernet MTU, AKA how much payload can an ethernet cable push through next hop.
    """

    def __init__(self, url: str):
        self.url = url

        self._low_mtu = 500
        # typically ethernet cables can carry 1500 bytes (but Jumbo fiber can carry upto 9K bytes AFAIK)
        # so increase it as per your requirements
        self._high_mtu = 1500
        self._last_accepted = self._low_mtu

    @staticmethod
    def yield_console_output(command):
        p = subprocess.Popen(command,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
        return iter(p.stdout.readline, b'')

    def does_accept_mtu_size(self, size) -> bool:
        command = 'ping {domain_name} -t -f -l {size}'.format(domain_name=self.url,
                                                           size=size).split()
        for line in self.yield_console_output(command):
            line = line.decode(encoding='utf-8')
            if line.startswith('Packet') and 'DF' in line:
                return False
            elif line.startswith('Reply'):
                return True

    def find_min_mtu(self):
        while self._low_mtu <= self._high_mtu:
            if not (self.does_accept_mtu_size(self._low_mtu), self.does_accept_mtu_size(self._high_mtu)):
                return self._last_accepted
            else:
                middle = (self._high_mtu + self._low_mtu) // 2
                print("Low: {} High: {} Middle: {}".format(self._low_mtu, self._high_mtu, middle))
                if self.does_accept_mtu_size(middle):
                    self._last_accepted = middle
                    self._low_mtu = middle + 1
                else:
                    self._high_mtu = middle - 1
        return self._last_accepted


if __name__ == '__main__':
    start = perf_counter()
    # please provide protocol less domain name (without http://, https:// and also without www or any subdomain)
    # provide the naked url (without www/subdomain)
    f = FindMinMtu("libwired.com")
    print("\nMTU: {} bytes (Found in {} seconds)".format(f.find_min_mtu(), perf_counter() - start))
于 2021-08-13T16:02:32.067 回答