Python scan for network IP addresses and macs - python

ip a output screenshotI am trying to create a script that scans a LAN and obtains the ip address and mac address of all the machines using Python. The script below does this, however it prints the list twice? How could this be achieved, or how could the script below be changed to print the list once(as a dictionary where the ip address is the key and the mac is the value)?
from __future__ import absolute_import, division, print_function
import logging
import scapy.config
import scapy.layers.l2
import scapy.route
import socket
import math
import errno
logging.basicConfig(format='%(asctime)s %(levelname)-5s %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=logging.DEBUG)
logger = logging.getLogger(__name__)
def long2net(arg):
if (arg <= 0 or arg >= 0xFFFFFFFF):
raise ValueError("illegal netmask value", hex(arg))
return 32 - int(round(math.log(0xFFFFFFFF - arg, 2)))
def to_CIDR_notation(bytes_network, bytes_netmask):
network = scapy.utils.ltoa(bytes_network)
netmask = long2net(bytes_netmask)
net = "%s/%s" % (network, netmask)
if netmask < 16:
logger.warn("%s is too big. skipping" % net)
return None
return net
def scan_and_print_neighbors(net, interface, timeout=1):
logger.info("arping %s on %s" % (net, interface))
try:
ans, unans = scapy.layers.l2.arping(net, iface=interface, timeout=timeout, verbose=True)
for s, r in ans.res:
line = r.sprintf("%Ether.src% %ARP.psrc%")
try:
hostname = socket.gethostbyaddr(r.psrc)
line += " " + hostname[0]
except socket.herror:
# failed to resolve
pass
logger.info(line)
except socket.error as e:
if e.errno == errno.EPERM: # Operation not permitted
logger.error("%s. Did you run as root?", e.strerror)
else:
raise
if __name__ == "__main__":
for network, netmask, _, interface, address in scapy.config.conf.route.routes:
# skip loopback network and default gw
if network == 0 or interface == 'lo' or address == '127.0.0.1' or address == '0.0.0.0':
continue
if netmask <= 0 or netmask == 0xFFFFFFFF:
continue
net = to_CIDR_notation(network, netmask)
if interface != scapy.config.conf.iface:
# see http://trac.secdev.org/scapy/ticket/537
logger.warn("skipping %s because scapy currently doesn't support arping on non-primary network interfaces", net)
continue
if net:
scan_and_print_neighbors(net, interface)

Have you tried setting verbose to False?
ans, unans = scapy.layers.l2.arping(net, iface=interface, timeout=timeout, verbose=False)
In addition to setting verbose=False in your scapy.layers.l2.arping(,
import the conf module:
from scapy.all import conf
And add conf.verb=0 just below your if __name__ == "__main__": line:
if __name__ == "__main__":
conf.verb=0
for network, netmask, _, interface, address in scapy.config.conf.route.routes:
# skip loopback network and default gw
if network == 0 or interface == 'lo' or address == '127.0.0.1' or address == '0.0.0.0':
continue
EDIT: I think your script is looping once for each "route" defined in your /proc/net/route that hasn't already been explicitly filtered out (i.e. the lines above your continue commands.) My guess is that if you where to execute route -n you'll probably find 2 routes in there that somehow have the same Network and Interface values but something else is differing like Netmask or Gateway.
Anyways, the hackish way to get pass this is to add a break after your call to scan_and_print_neighbors(net, interface) to exit the for-loop.
For example:
if __name__ == "__main__":
for network, netmask, _, interface, address in scapy.config.conf.route.routes:
# skip loopback network and default gw
if network == 0 or interface == 'lo' or address == '127.0.0.1' or address == '0.0.0.0':
continue
if netmask <= 0 or netmask == 0xFFFFFFFF:
continue
net = to_CIDR_notation(network, netmask)
if interface != scapy.config.conf.iface:
# see http://trac.secdev.org/scapy/ticket/537
logger.warn("skipping %s because scapy currently doesn't support arping on non-primary network interfaces", net)
continue
if net:
scan_and_print_neighbors(net, interface)
break

Related

Python DNS Server IP Address Query

I am trying to get the DNS Server IP Addresses using python. To do this in Windows command prompt, I would use
ipconfig -all
As shown below:
I want to do the exact same thing using a python script. Is there any way to extract these values?
I was successful in extracting the IP address of my device, but DNS Server IP is proving to be more challenging.
I recently had to get the IP addresses of the DNS servers that a set of cross platform hosts were using (linux, macOS, windows), this is how I ended up doing it and I hope it's helpful:
#!/usr/bin/env python
import platform
import socket
import subprocess
def is_valid_ipv4_address(address):
try:
socket.inet_pton(socket.AF_INET, address)
except AttributeError: # no inet_pton here, sorry
try:
socket.inet_aton(address)
except socket.error:
return False
return address.count('.') == 3
except socket.error: # not a valid address
return False
return True
def get_unix_dns_ips():
dns_ips = []
with open('/etc/resolv.conf') as fp:
for cnt, line in enumerate(fp):
columns = line.split()
if columns[0] == 'nameserver':
ip = columns[1:][0]
if is_valid_ipv4_address(ip):
dns_ips.append(ip)
return dns_ips
def get_windows_dns_ips():
output = subprocess.check_output(["ipconfig", "-all"])
ipconfig_all_list = output.split('\n')
dns_ips = []
for i in range(0, len(ipconfig_all_list)):
if "DNS Servers" in ipconfig_all_list[i]:
# get the first dns server ip
first_ip = ipconfig_all_list[i].split(":")[1].strip()
if not is_valid_ipv4_address(first_ip):
continue
dns_ips.append(first_ip)
# get all other dns server ips if they exist
k = i+1
while k < len(ipconfig_all_list) and ":" not in ipconfig_all_list[k]:
ip = ipconfig_all_list[k].strip()
if is_valid_ipv4_address(ip):
dns_ips.append(ip)
k += 1
# at this point we're done
break
return dns_ips
def main():
dns_ips = []
if platform.system() == 'Windows':
dns_ips = get_windows_dns_ips()
elif platform.system() == 'Darwin':
dns_ips = get_unix_dns_ips()
elif platform.system() == 'Linux':
dns_ips = get_unix_dns_ips()
else:
print("unsupported platform: {0}".format(platform.system()))
print(dns_ips)
return
if __name__ == "__main__":
main()
Resources I used to make this script:
https://stackoverflow.com/a/1325603
https://stackoverflow.com/a/4017219
Edit: If anyone has a better way of doing this please share :)
DNS Python (dnspython) might be helpful. You can get the DNS server address with:
import dns.resolver
dns_resolver = dns.resolver.Resolver()
dns_resolver.nameservers[0]

Get the data from scapy command to textfile?

I am trying to create an ARPscanner based on scapy. I found code from the Internet and I need to modify it to save the results to a .txt file. Can anyone help me to do that?
lena = int(raw_input("Enter Number : "))
print(lena)
logging.basicConfig(format='%(asctime)s %(levelname)-5s %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=logging.DEBUG)
logger = logging.getLogger(__name__)
def long2net(arg):
if (arg <= 0 or arg >= 0xFFFFFFFF):
raise ValueError("illegal netmask value", hex(arg))
return 32 - int(round(math.log(0xFFFFFFFF - arg, 2)))
def to_CIDR_notation(bytes_network, bytes_netmask):
network = scapy.utils.ltoa(bytes_network)
netmask = long2net(bytes_netmask)
net = "%s/%s" % (network, netmask)
if netmask < 16:
logger.warn("%s is too big. skipping" % net)
return None
return net
def scan_and_print_neighbors(net, interface, timeout=1):
logger.info("arping %s on %s" % (net, interface))
try:
ans, unans = scapy.layers.l2.arping(net, iface=interface, timeout=timeout, verbose=True)
for s, r in ans.res:
line = r.sprintf("%Ether.src% %ARP.psrc%")
try:
hostname = socket.gethostbyaddr(r.psrc)
line += " " + hostname[0]
except socket.herror:
# failed to resolve
pass
logger.info(line)
except socket.error as e:
if e.errno == errno.EPERM: # Operation not permitted
logger.error("%s. Did you run as root?", e.strerror)
else:
raise
if __name__ == "__main__":
if lena == 1:
for network, netmask, _, interface, address in scapy.config.conf.route.routes:
# skip loopback network and default gw
if network == 0 or interface == 'lo' or address == '127.0.0.1' or address == '0.0.0.0':
continue
if netmask <= 0 or netmask == 0xFFFFFFFF:
continue
net = to_CIDR_notation(network, netmask)
if interface != scapy.config.conf.iface:
# see http://trac.secdev.org/scapy/ticket/537
logger.warn("skipping %s because scapy currently doesn't support arping on non-primary network interfaces", net)
#continue
if net:
scan_and_print_neighbors(net, interface)
repr(network)
text_file = open("Output.txt", "w")
text_file.write(repr(network))
elif lena == 3 :
print("Bye Bye ")
I added this code to my script:
repr(network)
text_file = open("Output.txt", "w")
text_file.write(repr(network))
but it does not work, I just got a blank file.
solved :
if __name__ == "__main__":
import sys
sys.stdout = open('textout.txt', 'w')
... rest of your code here ...
sys.stdout.close()

Error when scanning subnet range, Python 2.7 port scanner

I'm currently working on this Python port scanner, I'm trying to implement a feature that will allow this port scanner to scan a local subnet.
Currently when the target IP ends in .0, it scans every IP in that subnet range, (.1 - .255) except when I run the program, returns 'cannot resolve , unknown host' for every single IP within the subnet range. The code I currently have is below:
# import modules used in port scanner
import optparse
from socket import *
from threading import *
import ipaddress
# connect-scan function, deals with connecting to the host / determining if ports are open / closed, takes arguments tgtHost, tgtPort
def connScan(tgtHost, tgtPort):
try:
connSkt = socket(AF_INET, SOCK_STREAM)
connSkt.connect((tgtHost, tgtPort))
connSkt.send('\r\n')
result = connSkt.recv(100)
# prints result if port is open
print '[+] ' + str(tgtPort) + '/tcp open'
except:
# prints result if port is closed
print '[-] ' + str(tgtPort) + '/tcp closed'
finally:
connSkt.close()
# port-scan function, takes arguments tgtHost, tgtPorts
def portScan(tgtHost, tgtPorts):
try:
# tries to get target IP address
tgtIP = gethostbyname(tgtHost)
except:
# if unsuccesful, prints out following result
print '[-] cannot resolve ' + unicode(tgtHost) + ': unknown host'
return
try:
# tries to get target address
tgtName = gethostbyaddr(tgtIP)
print '\n[+] scan results for: ' + tgtName[0]
except:
print '\n[+] scan results for: ' + tgtIP
# sets default time out to 1
setdefaulttimeout(1)
# for every port in tgtPorts
for tgtPort in tgtPorts:
# creates thread, target is connScan function, arguments are tgtHost, int(tgtPort)
t = Thread(target=connScan, args=(tgtHost, int(tgtPort)))
# starts the thread
t.start()
def main():
parser = optparse.OptionParser('usage %prog -t <target-host> -p <target-port(s)>')
parser.add_option('-t', dest='tgtHost', type='string', help='specify target host, for local subnet, use 192.168.1.0 (scans range 192.168.1.1 - 192.168.1.255')
parser.add_option('-p', dest='tgtPort', type='string', help='specify target port(s), seperated by a comma, seperate ranges with a -')
(options, args) = parser.parse_args()
if (options.tgtHost == None) | (options.tgtPort == None):
print parser.usage
exit(0)
else:
tgtHost = options.tgtHost
if tgtHost.endswith('.0'):
hosts = ipaddress.ip_network(unicode(tgtHost+'/24'))
else:
hosts = [tgtHost]
# allows ranges of ports to be used, when seperated by a -
if '-' in str(options.tgtPort):
tgtPorts = options.tgtPort.split('-')
tgtPorts = range(int(tgtPorts[0]),int(tgtPorts[1]))
else:
tgtPorts = str(options.tgtPort).split(',')
for tgtHost in hosts:
portScan(tgtHost, tgtPorts)
if __name__ == '__main__':
main()
I've been trying to find the solution for this, however have come up empty. Does anyone know whats wrong with the code?

Making a Fast Port Scanner

So I'm making a port scanner in python...
import socket
ip = "External IP"
s = socket.socket(2, 1) #socket.AF_INET, socket.SOCK_STREAM
def porttry(ip, port):
try:
s.connect((ip, port))
return True
except:
return None
for port in range(0, 10000):
value = porttry(ip, port)
if value == None:
print("Port not opened on %d" % port)
else:
print("Port opened on %d" % port)
break
raw_input()
But this is too slow, I want to somehow be able to some how close or break code after a period of time of not returning anything.
In addition to setting socket timeout, you can also apply multi-threading technique to turbo boost the process. It will be, at best, N times faster when you have N ports to scan.
# This script runs on Python 3
import socket, threading
def TCP_connect(ip, port_number, delay, output):
TCPsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
TCPsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
TCPsock.settimeout(delay)
try:
TCPsock.connect((ip, port_number))
output[port_number] = 'Listening'
except:
output[port_number] = ''
def scan_ports(host_ip, delay):
threads = [] # To run TCP_connect concurrently
output = {} # For printing purposes
# Spawning threads to scan ports
for i in range(10000):
t = threading.Thread(target=TCP_connect, args=(host_ip, i, delay, output))
threads.append(t)
# Starting threads
for i in range(10000):
threads[i].start()
# Locking the main thread until all threads complete
for i in range(10000):
threads[i].join()
# Printing listening ports from small to large
for i in range(10000):
if output[i] == 'Listening':
print(str(i) + ': ' + output[i])
def main():
host_ip = input("Enter host IP: ")
delay = int(input("How many seconds the socket is going to wait until timeout: "))
scan_ports(host_ip, delay)
if __name__ == "__main__":
main()
here is a quick and simple port scanner, it scans 100000 ports in 180 sec:
import threading
import socket
target = 'pythonprogramming.net'
#ip = socket.gethostbyname(target)
def portscan(port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(0.5)#
try:
con = s.connect((target,port))
print('Port :',port,"is open.")
con.close()
except:
pass
r = 1
for x in range(1,100):
t = threading.Thread(target=portscan,kwargs={'port':r})
r += 1
t.start()
Consider setting a timeout instead of a for loop by using socket.setdefaulttimeout(timeout).
This should be a bit faster.
#-*-coding:utf8;-*-
#qpy:3
#qpy:console
import socket
import os
# This is used to set a default timeout on socket
# objects.
DEFAULT_TIMEOUT = 0.5
# This is used for checking if a call to socket.connect_ex
# was successful.
SUCCESS = 0
def check_port(*host_port, timeout=DEFAULT_TIMEOUT):
''' Try to connect to a specified host on a specified port.
If the connection takes longer then the TIMEOUT we set we assume
the host is down. If the connection is a success we can safely assume
the host is up and listing on port x. If the connection fails for any
other reason we assume the host is down and the port is closed.'''
# Create and configure the socket.
sock = socket.socket()
sock.settimeout(timeout)
# the SO_REUSEADDR flag tells the kernel to reuse a local
# socket in TIME_WAIT state, without waiting for its natural
# timeout to expire.
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Like connect(address), but return an error indicator instead
# of raising an exception for errors returned by the C-level connect() 
# call (other problems, such as “host not found,” can still raise exceptions).
# The error indicator is 0 if the operation succeeded, otherwise the value of
# the errnovariable. This is useful to support, for example, asynchronous connects.
connected = sock.connect_ex(host_port) is SUCCESS
# Mark the socket closed.
# The underlying system resource (e.g. a file descriptor)
# is also closed when all file objects from makefile() are closed.
# Once that happens, all future operations on the socket object will fail.
# The remote end will receive no more data (after queued data is flushed).
sock.close()
# return True if port is open or False if port is closed.
return connected
con = check_port('www.google.com', 83)
print(con)
One can use threading.Thread and threading.Condition to synchronize port check and spawning new threads.
Script example usage:
python port_scan.py google.com 70 90
Checking 70 - 80
Checking 80 - 84
Checking 84 - 90
Found active port 80
Checking 90 - 91
Checking 91 - 94
All threads started ...
port_scan.py:
# import pdb
import socket, threading
from traceback import print_exc
class AllThreadsStarted(Exception): pass
class IPv4PortScanner(object):
def __init__(self, domain, timeout=2.0, port_range=(1024, 65535), threadcount=10):
self.domain = domain
self.timeout = timeout
self.port_range = port_range
self.threadcount = threadcount
self._lock = threading.Lock()
self._condition = threading.Condition(self._lock)
self._ports_active = []
self._ports_being_checked = []
self._next_port = self.port_range[0]
def check_port_(self, port):
"If connects then port is active"
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.settimeout(self.timeout)
try:
sock.connect((self.domain, port))
with self._lock:
self._ports_active.append(port)
print ("Found active port {}".format(port))
sock.close()
except socket.timeout, ex:
return
except:
print_exc()
# pdb.set_trace()
def check_port(self, port):
"updates self._ports_being_checked list on exit of this method"
try:
self.check_port_(port)
finally:
self._condition.acquire()
self._ports_being_checked.remove(port)
self._condition.notifyAll()
self._condition.release()
def start_another_thread(self):
if self._next_port > self.port_range[1]:
raise AllThreadsStarted()
port = self._next_port
self._next_port += 1
t = threading.Thread(target=self.check_port, args=(port,))
# update books
with self._lock:
self._ports_being_checked.append(port)
t.start()
def run(self):
try:
while True:
self._condition.acquire()
while len(self._ports_being_checked) >= self.threadcount:
# we wait for some threads to complete the task
self._condition.wait()
slots_available = self.threadcount - len(self._ports_being_checked)
self._condition.release()
print ("Checking {} - {}".format(self._next_port, self._next_port+slots_available))
for i in xrange(slots_available):
self.start_another_thread()
except AllThreadsStarted, ex:
print ("All threads started ...")
except:
print_exc()
if __name__ == "__main__":
import sys
domain = sys.argv[1]
port_s = int(sys.argv[2])
port_e = int(sys.argv[3])
scanner = IPv4PortScanner(domain=domain, port_range=(port_s, port_e))
scanner.run()
I think that this one snippet could help you : http://www.coderholic.com/python-port-scanner/
socket.setdefaulttimeout(0.5)
This will make the program faster!
socket.setdefualttimeout (time)
is used to keep trying to connect with port for perticular time...when you send request and there is timeout set for 2 seconds so it will try to connect with port for 2 seconds....if there will be no response from that port in 2 seconds....it will be count as a dead port
The following port scanner has a few constants defined at the top that you can modify as needed:
PURPOSE -- help message for the command line
PORTS -- range of ports you would like scanned
POOL_SIZE -- number of processes to scan with
TIMEOUT -- how long to wait for server connection
Feel free to adapt this according to your requirements. Maybe add some command line arguments?
#! /usr/bin/env python3
import argparse
import collections
import itertools
import multiprocessing
import operator
import socket
PURPOSE = 'Scan for open ports on a computer.'
PORTS = range(1 << 16)
POOL_SIZE = 1 << 8
TIMEOUT = 0.01
def main():
"""Get computer to scan, connect with process pool, and show open ports."""
parser = argparse.ArgumentParser(description=PURPOSE)
parser.add_argument('host', type=str, help='computer you want to scan')
host = parser.parse_args().host
with multiprocessing.Pool(POOL_SIZE, socket.setdefaulttimeout, [TIMEOUT]) \
as pool:
results = pool.imap_unordered(test, ((host, port) for port in PORTS))
servers = filter(operator.itemgetter(0), results)
numbers = map(operator.itemgetter(1), servers)
ordered = sorted(numbers)
print(f'Ports open on {host}:', *format_ports(ordered), sep='\n ')
field_names = 'family', 'socket_type', 'protocol', 'canon_name', 'address'
AddressInfo = collections.namedtuple('AddressInfo', field_names)
del field_names
def test(address):
"""Try connecting to the server and return whether or not it succeeded."""
host, port = address
for info in itertools.starmap(AddressInfo, socket.getaddrinfo(host, port)):
try:
probe = socket.socket(info.family, info.socket_type, info.protocol)
except OSError:
pass
else:
try:
probe.connect(info.address)
except OSError:
pass
else:
probe.shutdown(socket.SHUT_RDWR)
return True, port
finally:
probe.close()
return False, port
def format_ports(ports):
"""Convert port numbers into strings and show all associated services."""
if ports:
for port in ports:
try:
service = socket.getservbyport(port)
except OSError:
service = '?'
yield f'{port:<5} = {service}'
else:
yield 'None'
if __name__ == '__main__':
main()
I've just finished tinkering with Concurrent Futures on a port scanner and by God it's fast:
import concurrent.futures
import socket
def scan_port(domainip: str, port: int) -> tuple:
try:
# Use a faster socket implementation
s = socket.create_connection((domainip, port), timeout=0.5)
# Check if the connection was successful
if s:
return (port, "open")
else:
return (port, "closed")
except Exception as e:
print(f"Error scanning port {port}: {e}")
return (port, "error")
openports = {}
# Scan the ports in parallel using the faster scanning code
with concurrent.futures.ThreadPoolExecutor() as executor:
futures = [executor.submit(scan_port, domainip, port) for port in range(1, 1024)]
for future in concurrent.futures.as_completed(futures):
status = future.result()
if status[1] == "open":
openports[status[0]] = status[1]

How to find mtu value of network through code(in python)?

I have to write a code where I need to send data using udp protocol in python. I need to set the packet size to the MTU value of the network. Is there any way that I can decide the MTU value of the network writing some code in python?
This answer was taken from
http://books.google.co.il/books?id=9HGUc8AO2xQC&pg=PA31&lpg=PA31&dq#v=onepage&q&f=false
(page 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!'
There is a github-gist providing this functionality:
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()
Source: https://gist.github.com/nzjrs/8934855
The accepted answer did not work for me in Python 3.7. I get: OSError: [Errno 6] Device not configured
But, psutil now has this built in.
import psutil
print(psutil.net_if_stats())
Results in:
{
'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),
...
}
You can simply do a binary search over ping with DF (Don't Fragment) flag. Here is a working coding to find MTU through the above-mentioned technique. It gives you `minimum MTU of the full packet routing path AKA the max payload you can send.
Tested only on Windows (won't work on Linux/Mac as ping flags are different in different OS)
# 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))

Categories

Resources