mirror of
https://github.com/Hopiu/linkchecker.git
synced 2026-03-18 15:00:28 +00:00
194 lines
6.9 KiB
Python
194 lines
6.9 KiB
Python
# -*- coding: iso-8859-1 -*-
|
|
"""from http://twistedmatrix.com/wiki/python/IfConfig
|
|
"""
|
|
import socket
|
|
import sys
|
|
import errno
|
|
import array
|
|
import struct
|
|
import subprocess
|
|
from _network import ifreq_size
|
|
from .. import log, LOG_CHECK
|
|
|
|
|
|
def pipecmd (cmd1, cmd2):
|
|
"""Return output of "cmd1 | cmd2"."""
|
|
p1 = subprocess.Popen(cmd1, stdout=subprocess.PIPE)
|
|
p2 = subprocess.Popen(cmd2, stdin=p1.stdout, stdout=subprocess.PIPE)
|
|
p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
|
|
return p2.communicate()[0]
|
|
|
|
|
|
def ifconfig_inet (iface):
|
|
"""Return parsed IPv4 info from ifconfig(8) binary."""
|
|
res = pipecmd(["ifconfig", iface], ["grep", "inet "])
|
|
info = {}
|
|
lastpart = None
|
|
for part in res.split():
|
|
# Linux systems have prefixes for each value
|
|
if part.startswith("addr:"):
|
|
info["address"] = part[5:]
|
|
elif part.startswith("Bcast:"):
|
|
info["broadcast"] = part[6:]
|
|
elif part.startswith("Mask:"):
|
|
info["netmask"] = part[5:]
|
|
elif lastpart == "inet":
|
|
info["address"] = part
|
|
elif lastpart in ("netmask", "broadcast"):
|
|
info[lastpart] = part
|
|
lastpart = part
|
|
return info
|
|
|
|
|
|
class IfConfig (object):
|
|
"""Access to socket interfaces"""
|
|
|
|
SIOCGIFNAME = 0x8910
|
|
SIOCGIFCONF = 0x8912
|
|
SIOCGIFFLAGS = 0x8913
|
|
SIOCGIFADDR = 0x8915
|
|
SIOCGIFBRDADDR = 0x8919
|
|
SIOCGIFNETMASK = 0x891b
|
|
SIOCGIFCOUNT = 0x8938
|
|
|
|
IFF_UP = 0x1 # Interface is up.
|
|
IFF_BROADCAST = 0x2 # Broadcast address valid.
|
|
IFF_DEBUG = 0x4 # Turn on debugging.
|
|
IFF_LOOPBACK = 0x8 # Is a loopback net.
|
|
IFF_POINTOPOINT = 0x10 # Interface is point-to-point link.
|
|
IFF_NOTRAILERS = 0x20 # Avoid use of trailers.
|
|
IFF_RUNNING = 0x40 # Resources allocated.
|
|
IFF_NOARP = 0x80 # No address resolution protocol.
|
|
IFF_PROMISC = 0x100 # Receive all packets.
|
|
IFF_ALLMULTI = 0x200 # Receive all multicast packets.
|
|
IFF_MASTER = 0x400 # Master of a load balancer.
|
|
IFF_SLAVE = 0x800 # Slave of a load balancer.
|
|
IFF_MULTICAST = 0x1000 # Supports multicast.
|
|
IFF_PORTSEL = 0x2000 # Can set media type.
|
|
IFF_AUTOMEDIA = 0x4000 # Auto media select active.
|
|
|
|
def __init__ (self):
|
|
"""Initialize a socket and determine ifreq structure size."""
|
|
# create a socket so we have a handle to query
|
|
self.sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
# Note that sizeof(struct ifreq) is not always 32
|
|
# (eg. on *BSD, x86_64 Linux) Thus the function call.
|
|
self.ifr_size = ifreq_size()
|
|
|
|
def _ioctl (self, func, args):
|
|
"""Call ioctl() with given parameters."""
|
|
import fcntl
|
|
return fcntl.ioctl(self.sockfd.fileno(), func, args)
|
|
|
|
def _getifreq (self, ifname):
|
|
"""Return ifreq buffer for given interface name."""
|
|
return struct.pack("%ds" % self.ifr_size, ifname)
|
|
|
|
def _getaddr (self, ifname, func):
|
|
"""Get interface address."""
|
|
try:
|
|
result = self._ioctl(func, self._getifreq(ifname))
|
|
except IOError as msg:
|
|
log.warn(LOG_CHECK,
|
|
"error getting addr for interface %r: %s", ifname, msg)
|
|
return None
|
|
return socket.inet_ntoa(result[20:24])
|
|
|
|
def getInterfaceList (self, flags=0):
|
|
"""Get all interface names in a list."""
|
|
if sys.platform == 'darwin':
|
|
command = ['ifconfig', '-l']
|
|
if flags & self.IFF_UP:
|
|
command.append('-u')
|
|
# replace with subprocess.check_output() for Python 2.7
|
|
res = subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0]
|
|
return res.split()
|
|
# initial 8kB buffer to hold interface data
|
|
bufsize = 8192
|
|
# 80kB buffer should be enough for most boxen
|
|
max_bufsize = bufsize * 10
|
|
while True:
|
|
buf = array.array('c', '\0' * bufsize)
|
|
ifreq = struct.pack("iP", buf.buffer_info()[1], buf.buffer_info()[0])
|
|
try:
|
|
result = self._ioctl(self.SIOCGIFCONF, ifreq)
|
|
break
|
|
except IOError as msg:
|
|
# in case of EINVAL the buffer size was too small
|
|
if msg[0] != errno.EINVAL or bufsize == max_bufsize:
|
|
raise
|
|
# increase buffer
|
|
bufsize += 8192
|
|
# loop over interface names
|
|
data = buf.tostring()
|
|
iflist = []
|
|
size, ptr = struct.unpack("iP", result)
|
|
i = 0
|
|
while i < size:
|
|
ifconf = data[i:i+self.ifr_size]
|
|
name = struct.unpack("16s%ds" % (self.ifr_size-16), ifconf)[0]
|
|
name = name.split('\0', 1)[0]
|
|
if name:
|
|
if flags and not (self.getFlags(name) & flags):
|
|
continue
|
|
iflist.append(name)
|
|
i += self.ifr_size
|
|
return iflist
|
|
|
|
def getFlags (self, ifname):
|
|
"""Get the flags for an interface"""
|
|
try:
|
|
result = self._ioctl(self.SIOCGIFFLAGS, self._getifreq(ifname))
|
|
except IOError as msg:
|
|
log.warn(LOG_CHECK,
|
|
"error getting flags for interface %r: %s", ifname, msg)
|
|
return 0
|
|
# extract the interface's flags from the return value
|
|
flags, = struct.unpack('H', result[16:18])
|
|
# return "UP" bit
|
|
return flags
|
|
|
|
def getAddr (self, ifname):
|
|
"""Get the inet addr for an interface.
|
|
@param ifname: interface name
|
|
@type ifname: string
|
|
"""
|
|
if sys.platform == 'darwin':
|
|
return ifconfig_inet(ifname).get('address')
|
|
return self._getaddr(ifname, self.SIOCGIFADDR)
|
|
|
|
def getMask (self, ifname):
|
|
"""Get the netmask for an interface.
|
|
@param ifname: interface name
|
|
@type ifname: string
|
|
"""
|
|
if sys.platform == 'darwin':
|
|
return ifconfig_inet(ifname).get('netmask')
|
|
return self._getaddr(ifname, self.SIOCGIFNETMASK)
|
|
|
|
def getBroadcast (self, ifname):
|
|
"""Get the broadcast addr for an interface.
|
|
@param ifname: interface name
|
|
@type ifname: string
|
|
"""
|
|
if sys.platform == 'darwin':
|
|
return ifconfig_inet(ifname).get('broadcast')
|
|
return self._getaddr(ifname, self.SIOCGIFBRDADDR)
|
|
|
|
def isUp (self, ifname):
|
|
"""Check whether interface is UP.
|
|
@param ifname: interface name
|
|
@type ifname: string
|
|
"""
|
|
return (self.getFlags(ifname) & self.IFF_UP) != 0
|
|
|
|
def isLoopback (self, ifname):
|
|
"""Check whether interface is a loopback device.
|
|
@param ifname: interface name
|
|
@type ifname: string
|
|
"""
|
|
# since not all systems have IFF_LOOPBACK as a flag defined,
|
|
# the ifname is tested first
|
|
if ifname.startswith('lo'):
|
|
return True
|
|
return (self.getFlags(ifname) & self.IFF_LOOPBACK) != 0
|