import os import stat import subprocess from .posix import PosixProcessProvider from .provider import FirewallProvider, RouteProvider, TunnelPrepProvider from .util import get_executable class ProcfsProvider(PosixProcessProvider): def pid2exe(self, pid): try: return os.readlink('/proc/%d/exe' % pid) except (OSError, IOError): return None def ppid_of(self, pid=None): if pid is None: return os.getppid() try: return int(next(open('/proc/%d/stat' % pid)).split()[3]) except (OSError, ValueError, IOError): return None class Iproute2Provider(RouteProvider): def __init__(self): self.iproute = get_executable('/sbin/ip') def _iproute(self, *args, **kwargs): cl = [self.iproute] cl.extend(str(v) for v in args if v is not None) for k, v in kwargs.items(): if v is not None: cl.extend((k, str(v))) if args[:2] == ('route', 'get'): output_start, keys = 1, ('via', 'dev', 'src', 'mtu') elif args[:2] == ('link', 'show'): output_start, keys = 3, ('state', 'mtu') else: output_start = None if output_start is not None: words = subprocess.check_output(cl, universal_newlines=True).split() if args[:2] == ('route', 'get') and words[0] in ('broadcast', 'multicast', 'local', 'unreachable'): output_start += 1 return {words[i]: words[i + 1] for i in range(output_start, len(words), 2) if words[i] in keys} else: subprocess.check_call(cl) def add_route(self, destination, *, via=None, dev=None, src=None, mtu=None): self._iproute('route', 'add', destination, via=via, dev=dev, src=src, mtu=mtu) def replace_route(self, destination, *, via=None, dev=None, src=None, mtu=None): self._iproute('route', 'replace', destination, via=via, dev=dev, src=src, mtu=mtu) def remove_route(self, destination): self._iproute('route', 'del', destination) def get_route(self, destination): r = self._iproute('route', 'get', destination) # Ignore localhost or incomplete routes if r.get('dev') == 'lo': del r['dev'] if 'dev' in r or 'via' in r: return r def flush_cache(self): self._iproute('route', 'flush', 'cache') self._iproute('-6', 'route', 'flush', 'cache') def get_link_info(self, device): return self._iproute('link', 'show', device) def set_link_info(self, device, state, mtu=None): self._iproute('link', 'set', state, dev=device, mtu=mtu) def add_address(self, device, address): flag = '-6' if address.version == 6 else '-4' self._iproute(flag, 'address', 'add', address, dev=device) class IptablesProvider(FirewallProvider): def __init__(self): self.iptables = get_executable('/sbin/iptables') def _iptables(self, *args): cl = [self.iptables] cl.extend(args) subprocess.check_call(cl) def configure_firewall(self, device): self._iptables('-A', 'INPUT', '-i', device, '-m', 'state', '--state', 'RELATED,ESTABLISHED', '-j', 'ACCEPT') self._iptables('-A', 'INPUT', '-i', device, '-j', 'DROP') def deconfigure_firewall(self, device): self._iptables('-D', 'INPUT', '-i', device, '-j', 'DROP') self._iptables('-D', 'INPUT', '-i', device, '-m', 'state', '--state', 'RELATED,ESTABLISHED', '-j', 'ACCEPT') class CheckTunDevProvider(TunnelPrepProvider): def create_tunnel(self): node = '/dev/net/tun' if not os.path.exists(node): os.makedirs(os.path.dirname(node), exist_ok=True) os.mknod(node, mode=0o640 | stat.S_IFCHR, device=os.makedev(10, 200)) def prepare_tunnel(self): if not os.access('/dev/net/tun', os.R_OK | os.W_OK): raise OSError("can't read and write /dev/net/tun")