Source code for snare.net.addr
# coding: utf-8
"""
Network address functions
"""
import fcntl
import socket
import struct
import logging
logger = logging.getLogger(__name__)
# Dummy socket used for fcntl functions
_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
class AddrMeta(type):
@property
def maxvalue(cls):
return (0x1 << (cls.bytelen * 8)) - 1
[docs]class Addr(metaclass=AddrMeta):
"""Generic network address with support for various conversions and operators
:param addr: Address to create in string, int or bytes representation.
:type addr: str|int|bytes|Addr
"""
bytelen = 0
def __init__(self, addr):
self._str = None
self._int = None
self._bytes = None
if isinstance(addr, type(self)):
self._str = addr._str
self._bytes = addr._bytes
self._int = addr._int
elif isinstance(addr, str):
self._str = addr
elif isinstance(addr, int):
self._int = addr
elif isinstance(addr, bytes):
if len(addr) == self.bytelen:
self._bytes = addr
else:
self._str = addr.decode('utf-8')
else:
raise ValueError('Cannot create {!s} from {!s}'.format(type(self), type(addr)))
# Operations
def __and__(self, other):
return type(self)(int(self) & int(other))
def __or__(self, other):
return type(self)(int(self) | int(other))
def __xor__(self, other):
return type(self)(int(self) ^ int(other))
def __invert__(self):
return type(self)(int(self) ^ self.maxvalue)
def __hash__(self):
return hash(self.__class__) ^ int(self)
def __eq__(self, other):
return int(self) == int(other)
# Conversions
def __str__(self):
if self._str is None:
self._str = self.bytes_to_str(bytes(self))
return self._str
def __int__(self):
return int.from_bytes(bytes(self), byteorder='big')
def __bytes__(self):
if self._bytes is None:
if self._str is not None:
self._bytes = self.str_to_bytes(self._str)
elif self._int is not None:
self._bytes = self._int.to_bytes(self.bytelen, byteorder='big')
return self._bytes
def __repr__(self):
return '<{0}.{1} {2!s}>'.format(__name__, type(self).__name__, self)
[docs]class Ip(Addr):
"""IPv4 address with support for conversion to/from strings and byte operations"""
bytelen = 4
@staticmethod
def bytes_to_str(b):
return socket.inet_ntoa(b)
@staticmethod
def str_to_bytes(s):
return socket.inet_aton(s)
def slash(self):
x, i = int(self), 0
if x == 0:
return 0
while x & 0x1 == 0:
x >>= 1
i += 1
return 32 - i
[docs]class IpNet:
"""IPv4 address and network mask, representing a subnet."""
def __init__(self, ip, mask):
self.ip = Ip(ip)
self.mask = Ip(mask)
[docs] def network(self):
"""Calculate the network address from the IP and mask."""
return self.ip & self.mask
[docs] def broadcast(self):
"""Calculate the broadcast address from the IP and mask."""
return self.ip | ~self.mask
[docs] def contains(self, ip):
"""Determines if the given address is in this subnet
:param ip: IPv4 to check if contained in this subnet.
:type ip: Ip
"""
return (Ip(ip) & self.mask) == self.network()
def __contains__(self, ip):
return self.contains(ip)
[docs] def cidr(self):
"""Returns the CIDR representation of this subnet."""
return "{!s}/{:d}".format(self.ip, self.mask.slash())
def __str__(self):
return self.cidr()
def __repr__(self):
return '<{0}.{1} {2!s}>'.format(__name__, type(self).__name__, self)
[docs]class Mac(Addr):
"""Ethernet MAC address"""
bytelen = 6
@staticmethod
def bytes_to_str(b):
return ':'.join('%02x' % byte for byte in b)
@staticmethod
def str_to_bytes(s):
return bytes.fromhex(s.replace(':', ''))
def _ifctl(ifname, code):
if isinstance(ifname, str):
ifname = ifname.encode('utf-8')
return fcntl.ioctl(
_socket.fileno(),
code,
struct.pack('256s', ifname[:15])
)
[docs]def ifaddr(ifname):
"""Retrieves the IPv4 address for the given network interface name.
:param ifname: Interface name to retreive the IP address from.
:type ifname: str
:Example:
>>> ifaddr('lo')
<snare.net.addr.Ip 127.0.0.1>
"""
return Ip(_ifctl(ifname, 0x8915)[20:24]) # SIOCGIFADDR
[docs]def ifmask(ifname):
"""Retrieves the IPv4 network mask for the given network interface name.
:param ifname: Interface name to retreive the IP network mask from.
:type ifname: str
:Example:
>>> ifmask('lo')
<snare.net.addr.Ip 255.0.0.0>
"""
return Ip(_ifctl(ifname, 0x891b)[20:24]) # SIOCGIFNETMASK
[docs]def ifhwaddr(ifname):
"""Retrieves the Ethernet MAC address for the given network interface name.
:param ifname: Interface name to retreive the MAC address from.
:type ifname: str
:Example:
>>> ifhwaddr('lo')
<snare.net.addr.Mac 00:00:00:00:00:00>
"""
return Mac(_ifctl(ifname, 0x8927)[18:24]) # SIOCGIFHWADDR
[docs]def parsecidr(ipnet):
"""Parses and returns the given CIDR string as an IpNet subnet object.
:param ipnet: String representation of the subnet.
:type ipnet: str
:Example:
>>> parsecidr('172.16.224.70/20')
<snare.net.addr.IpNet 172.16.224.70/20>
"""
ipstr, maskstr = ipnet.split('/')
maskint = 0xffffffff ^ ((0x00000001 << (32-int(maskstr)))-1)
return IpNet(ipstr, maskint)
[docs]def ifcidr(ifname):
"""Retrieves the IPv4 address and network mask from the given inerface name.
:param ifname: Interface name to retreive the IP address and network mask from.
:type ifname: str
:Example:
>>> ifcidr('lo')
<snare.net.addr.IpNet 127.0.0.1/8>
"""
return IpNet(ifaddr(ifname), ifmask(ifname))