restructuring and removing HEConfig

This commit is contained in:
brent s 2020-05-14 17:11:47 -04:00
parent 676aa8d5b6
commit 315af935ac
Signed by: bts
GPG Key ID: 8C004C2F93481F6B
5 changed files with 242 additions and 232 deletions

View File

@ -1,5 +1,6 @@
from . import args
from . import radvd
from . import tunnel
from . import config
from . import logger
from . import tunnelbroker

View File

@ -10,62 +10,9 @@ import requests.auth
from lxml import etree
from pyroute2 import IPRoute
##
from . import tunnel
from . import radvd


def xml2bool(xml_str):
if xml_str is None:
return(None)
xml_str = xml_str.lower()[0]
if xml_str in ('t', '1'):
return(True)
elif xml_str in ('f', '0'):
return(False)
else:
raise ValueError('Not a boolean value')


class IP(object):
type = None
version = None
_ip = ipaddress.ip_address
_net = ipaddress.ip_network
_net_ip = netaddr.IPAddress
_net_net = netaddr.IPNetwork

def __init__(self, ip, prefix, *args, **kwargs):
self.str = ip
self.prefix = int(prefix)
self.net_ip = self._net_ip(self.str)
self.net_net = self._net_net('{0}/{1}'.format(self.str, self.prefix))

def _ext_init(self):
self.ip = self._ip(self.str)
self.net = self._net('{0}/{1}'.format(self.str, self.prefix), strict = False)
return(None)


class IP4(IP):
type = 'IPv4'
version = 4
_ip = ipaddress.IPv4Address
_net = ipaddress.IPv4Network

def __init__(self, ip, prefix, *args, **kwargs):
super().__init__(ip, prefix, *args, **kwargs)
self._ext_init()


class IP6(IP):
type = 'IPv6'
version = 6
_ip = ipaddress.IPv6Address
_net = ipaddress.IPv6Network

def __init__(self, ip, prefix, *args, **kwargs):
super().__init__(ip, prefix, *args, **kwargs)
self._ext_init()
self.alloc_block = netaddr.SubnetSplitter(self.net_net)
from . import utils


class Credential(object):
@ -110,125 +57,9 @@ class Credential(object):
return(None)


class Assignment(object):
def __init__(self, assign_xml, radvd = False, dns = False):
self.xml = assign_xml
self.do_radvd = radvd
self.radvd_dns = dns
self.iface = None
self.iface_idx = None
self.iface_addrs = []
self.iface_blocks = []
self.alloc = None # This must be set externally to a mapped Allocation instance
self.alloc_id = None
self.prefix = None
self.alloc_block = None
self.parse()

def _alloc(self):
self.alloc_id = int(self.xml.attrib['alloc'].strip())
return(None)

def _iface(self):
_iface_txt = self.xml.attrib['iface'].strip()
self.iface = _iface_txt.strip()
ipr = IPRoute()
self.iface_idx = ipr.link_lookup(ifname = self.iface)[0]
ipr.close()
return(None)

def _prefix(self):
self.prefix = int(self.xml.attrib.get('prefix', 64).strip())
return(None)

def parse(self):
self._iface()
self._alloc()
self._prefix()
return(None)

def parse_alloc(self):
self.alloc_block = self.alloc.ip.alloc_block
self.iface_blocks = self.alloc_block.extract_subnet(self.prefix, count = 1)
for i in self.iface_blocks:
self.iface_addrs.append(IP6(str(next(i.iter_hosts())), 128))
return(None)


class Allocation(object):
def __init__(self, alloc_net):
_ip, _prefix = alloc_net.split('/')
self.id = int(_prefix.strip())
self.prefix = self.id
self.ip = IP6(_ip.strip(), self.prefix)


class Tunnel(object):
def __init__(self, tun_xml, he_tunnels):
self.xml = tun_xml
self.id = None
self.client = None
self.server = None
self.creds = None
self.creds_id = None
self.radvd = None
self.enable_radvd = None
self.radvd_dns = None
self.allocations = {} # This is a dict of {}[alloc.id] = Allocation obj
self.assignments = [] # This is a list of Assignment objs
self.heconf = he_tunnels
self.parse()

def _allocations(self):
self.allocations = self.heconf[self.id].allocations
return(None)

def _assignments(self):
_assigns_xml = self.xml.find('assignments')
self.enable_radvd = xml2bool(_assigns_xml.attrib.get('radvd', 'false'))
self.radvd_dns = xml2bool(_assigns_xml.attrib.get('radvdDns', 'false'))
for _assign_xml in _assigns_xml.findall('assign'):
assign = Assignment(_assign_xml, radvd = self.enable_radvd, dns = self.radvd_dns)
assign.alloc = self.allocations[assign.alloc_id]
assign.parse_alloc()
self.assignments.append(assign)
return(None)

def _client(self):
_client_xml = self.xml.find('client')
_ip_txt = _client_xml.text.strip()
_prefix_txt = _client_xml.attrib['prefix'].strip()
self.client = IP6(_ip_txt, _prefix_txt)
return(None)

def _creds(self):
self.creds_id = self.xml.attrib['creds'].strip()
return(None)

def _id(self):
self.id = int(self.xml.attrib['id'].strip())
return(None)

def _radvd(self):
self.radvd = radvd.RADVD()
self.radvd.conf.generate(self.assignments)
return(None)

def _server(self):
_server_xml = self.xml.find('server')
_ip_text = _server_xml.text.strip()
self.server = IP4(_ip_text, 32)
return(None)

def parse(self):
self._id()
self._creds()
self._client()
self._server()
self._allocations()
self._assignments()
self._radvd()
return(None)
class HETunnel(object):
def __init__(self, tun_xml):
pass


class BaseConfig(object):
@ -333,7 +164,6 @@ class Config(BaseConfig):
with open(xml_path, 'rb') as fh:
raw_xml = fh.read()
super().__init__(raw_xml, *args, **kwargs)
self.heconf = None
self.creds = {}
self.tunnels = collections.OrderedDict()
self.subparse()
@ -345,67 +175,62 @@ class Config(BaseConfig):
self.creds[cred.id] = cred
return(None)

def _heconf(self):
self.heconf = HEConfig(self.creds)
return(None)

def _tunnels(self):
tunnels_xml = self.xml.find('tunnels')
for tun_xml in tunnels_xml.findall('tunnel'):
tun_id = int(tun_xml.attrib['id'].strip())
tun = Tunnel(tun_xml, self.heconf.tunnels[tun_id])
tun.creds = self.creds.get(tun.creds_id)
tun_creds_id = tun_xml.attrib['creds']
tun = tunnel.Tunnel(tun_xml, self.creds[tun_creds_id])
self.tunnels[tun_id] = tun
return(None)

def subparse(self):
self._creds()
self._heconf()
self._tunnels()
return(None)


class HEConfig(BaseConfig):
default_xsd = 'http://schema.xml.r00t2.io/projects/tunnelbroker.xsd'
nsmap = {None: 'https://tunelbroker.net/tunnelInfo.php',
'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
attr_qname = etree.QName('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')
schema_loc = 'https://tunnelbroker.net/tunnelInfo.php {0}'.format(default_xsd)

def __init__(self, creds, xml_url = 'https://tunnelbroker.net/tunnelInfo.php', *args, **kwargs):
# Creds are unique per tunnel... but we don't know the ID yet so we don't know which creds to use.
# TODO.
self.creds = creds
self.url = xml_url
req = requests.get(self.url,
auth = requests.auth.HTTPBasicAuth(self.creds.user, self.creds.password))
if not req.ok:
raise RuntimeError('Could not fetch remote tunnel information')
raw_xml = self._add_ns(req.content)
super().__init__(raw_xml, *args, **kwargs)
# In the format of: {tun_id: HETunnel()}
self.tunnels = collections.OrderedDict()
self.subparse()

def subparse(self):
for tun_xml in self.xml.findall('tunnel'):
tun = HETunnel(tun_xml)
self.tunnels[tun.id] = tun
return(None)

def _add_ns(self, raw_xml):
# https://mailman-mail5.webfaction.com/pipermail/lxml/20100323/013260.html
_xml = etree.fromstring(raw_xml)
_nsmap = copy.deepcopy(_xml.nsmap)
_nsmap.update(self.nsmap)
mod_xml = etree.Element(_xml.tag, {self.attr_qname: self.schema_loc}, nsmap = _nsmap)
mod_xml[:] = _xml[:]
return(etree.tostring(mod_xml,
encoding = 'UTF-8',
xml_declaration = True,
pretty_print = True,
with_tail = True,
with_comments = True))
# class HEConfig(BaseConfig):
# # This is unused. Kept mostly for reference.
#
# default_xsd = 'http://schema.xml.r00t2.io/projects/tunnelbroker.xsd'
# nsmap = {None: 'https://tunelbroker.net/tunnelInfo.php',
# 'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
# attr_qname = etree.QName('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')
# schema_loc = 'https://tunnelbroker.net/tunnelInfo.php {0}'.format(default_xsd)
#
# def __init__(self, creds, xml_url = 'https://tunnelbroker.net/tunnelInfo.php', *args, **kwargs):
# self.creds = creds
# self.url = xml_url
# req = requests.get(self.url,
# auth = requests.auth.HTTPBasicAuth(self.creds.user, self.creds.password))
# if not req.ok:
# raise RuntimeError('Could not fetch remote tunnel information')
# raw_xml = self._add_ns(req.content)
# super().__init__(raw_xml, *args, **kwargs)
# # In the format of: {tun_id: HETunnel()}
# self.tunnels = collections.OrderedDict()
# self.subparse()
#
# def subparse(self):
# for tun_xml in self.xml.findall('tunnel'):
# tun = HETunnel(tun_xml)
# self.tunnels[tun.id] = tun
# return(None)
#
# def _add_ns(self, raw_xml):
# # https://mailman-mail5.webfaction.com/pipermail/lxml/20100323/013260.html
# _xml = etree.fromstring(raw_xml)
# _nsmap = copy.deepcopy(_xml.nsmap)
# _nsmap.update(self.nsmap)
# mod_xml = etree.Element(_xml.tag, {self.attr_qname: self.schema_loc}, nsmap = _nsmap)
# mod_xml[:] = _xml[:]
# return(etree.tostring(mod_xml,
# encoding = 'UTF-8',
# xml_declaration = True,
# pretty_print = True,
# with_tail = True,
# with_comments = True))


class HETunnelConfig(BaseConfig):
@ -416,8 +241,9 @@ class HETunnelConfig(BaseConfig):
attr_qname = etree.QName('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')
schema_loc = 'https://tunnelbroker.net/tunnelInfo.php?tid {0}'.format(default_xsd)

def __init__(self, tun_xml):
def __init__(self, tun_xml, creds):
self.xml = tun_xml
self.creds = creds
self.id = None
self.description = None
self.client = None # Client IPv6
@ -432,13 +258,13 @@ class HETunnelConfig(BaseConfig):
for a in ('64', '48'):
_alloc = self.xml.find('routed{0}'.format(a))
if _alloc is not None and _alloc.text.strip() != '':
self.allocations[int(a)] = Allocation(_alloc.text.strip())
self.allocations[int(a)] = tunnel.Allocation(_alloc.text.strip())
return(None)

def _client(self):
_client = self.xml.find('clientv4').text
if _client is not None and _client.strip() != '':
self.client = IP4(_client.strip(), 32)
self.client = tunnel.IP4(_client.strip(), 32)
return(None)

def _desc(self):
@ -448,7 +274,7 @@ class HETunnelConfig(BaseConfig):
return(None)

def _endpoint(self):
self.endpoint = IP4(self.xml.find('serverv4').text.strip(), 32)
self.endpoint = tunnel.IP4(self.xml.find('serverv4').text.strip(), 32)
return(None)

def _id(self):
@ -458,7 +284,7 @@ class HETunnelConfig(BaseConfig):
def _my_ip(self):
_ip = self.xml.find('clientv4').text
if _ip is not None and _ip.strip() != '':
self.my_ip = IP4(_ip.strip(), 32)
self.my_ip = tunnel.IP4(_ip.strip(), 32)
return(None)

def _rdns(self):
@ -471,7 +297,7 @@ class HETunnelConfig(BaseConfig):
return(None)

def _server(self):
self.server = IP6(self.xml.find('serverv6'), 128)
self.server = tunnel.IP6(self.xml.find('serverv6'), 128)
return(None)

def parse(self):

View File

@ -12,17 +12,14 @@
<creds>
<!--
Credentials are kept separate from tunnel configuration because you can have multiple (up to 5) tunnels per user.
You can find the updateKey in the "Advanced" tab of your tunnel's configuration on your tunnelbroker.net panel.
-->
<cred id="ipv6user">
<user>ipv6user</user>
<password>someSecretPassword</password>
<updateKey>xXxXxXxXxXxXxXXX</updateKey>
</cred>
<cred id="anotheruser">
<user>someotheruser</user>
<password>anotherPassword</password>
<updateKey>0000000000000000</updateKey>
</cred>
</creds>
<tunnels>
@ -36,6 +33,10 @@
be "12345".
-->
<tunnel id="12345" creds="ipv6user">
<!--
You can find the updateKey in the "Advanced" tab of your tunnel's configuration on your tunnelbroker.net panel.
-->
<updateKey>xXxXxXxXxXxXxXXX</updateKey>
<!--
Where to assign your allocations. The default allocation prefix is a /64 (prefix="64"), since that's what SLAAC
recommends.
@ -68,6 +69,7 @@
And you can, of course, specify multiple tunnels.
-->
<tunnel id="54321" creds="anotheruser">
<updateKey>0000000000000000</updateKey>
<assignments>
<!--
Uses the default prefix of /64 from your standard /64 allocation from Hurricane Electric.

171
utils/he_ipv6/tunnel.py Normal file
View File

@ -0,0 +1,171 @@
import ipaddress
##
import netaddr
from pyroute2 import IPRoute
##
from . import utils
from . import radvd


class IP(object):
type = None
version = None
_ip = ipaddress.ip_address
_net = ipaddress.ip_network
_net_ip = netaddr.IPAddress
_net_net = netaddr.IPNetwork

def __init__(self, ip, prefix, *args, **kwargs):
self.str = ip
self.prefix = int(prefix)
self.net_ip = self._net_ip(self.str)
self.net_net = self._net_net('{0}/{1}'.format(self.str, self.prefix))

def _ext_init(self):
self.ip = self._ip(self.str)
self.net = self._net('{0}/{1}'.format(self.str, self.prefix), strict = False)
return(None)


class IP4(IP):
type = 'IPv4'
version = 4
_ip = ipaddress.IPv4Address
_net = ipaddress.IPv4Network

def __init__(self, ip, prefix, *args, **kwargs):
super().__init__(ip, prefix, *args, **kwargs)
self._ext_init()


class IP6(IP):
type = 'IPv6'
version = 6
_ip = ipaddress.IPv6Address
_net = ipaddress.IPv6Network

def __init__(self, ip, prefix, *args, **kwargs):
super().__init__(ip, prefix, *args, **kwargs)
self._ext_init()
self.alloc_block = netaddr.SubnetSplitter(self.net_net)


class Assignment(object):
def __init__(self, assign_xml, radvd = False, dns = False):
self.xml = assign_xml
self.do_radvd = radvd
self.radvd_dns = dns
self.iface = None
self.iface_idx = None
self.iface_addrs = []
self.iface_blocks = []
self.alloc = None # This must be set externally to a mapped Allocation instance
self.alloc_id = None
self.prefix = None
self.alloc_block = None
self.parse()

def _alloc(self):
self.alloc_id = int(self.xml.attrib['alloc'].strip())
return(None)

def _iface(self):
_iface_txt = self.xml.attrib['iface'].strip()
self.iface = _iface_txt.strip()
ipr = IPRoute()
self.iface_idx = ipr.link_lookup(ifname = self.iface)[0]
ipr.close()
return(None)

def _prefix(self):
self.prefix = int(self.xml.attrib.get('prefix', 64).strip())
return(None)

def parse(self):
self._iface()
self._alloc()
self._prefix()
return(None)

def parse_alloc(self):
self.alloc_block = self.alloc.ip.alloc_block
self.iface_blocks = self.alloc_block.extract_subnet(self.prefix, count = 1)
for i in self.iface_blocks:
self.iface_addrs.append(IP6(str(next(i.iter_hosts())), 128))
return(None)


class Allocation(object):
def __init__(self, alloc_net):
_ip, _prefix = alloc_net.split('/')
self.id = int(_prefix.strip())
self.prefix = self.id
self.ip = IP6(_ip.strip(), self.prefix)


class Tunnel(object):
def __init__(self, tun_xml, he_tunnels):
self.xml = tun_xml
self.id = None
self.client = None
self.server = None
self.creds = None
self.creds_id = None
self.radvd = None
self.enable_radvd = None
self.radvd_dns = None
self.allocations = {} # This is a dict of {}[alloc.id] = Allocation obj
self.assignments = [] # This is a list of Assignment objs
self.heconf = he_tunnels
self.parse()

def _allocations(self):
self.allocations = self.heconf[self.id].allocations
return(None)

def _assignments(self):
_assigns_xml = self.xml.find('assignments')
self.enable_radvd = utils.xml2bool(_assigns_xml.attrib.get('radvd', 'false'))
self.radvd_dns = utils.xml2bool(_assigns_xml.attrib.get('radvdDns', 'false'))
for _assign_xml in _assigns_xml.findall('assign'):
assign = Assignment(_assign_xml, radvd = self.enable_radvd, dns = self.radvd_dns)
assign.alloc = self.allocations[assign.alloc_id]
assign.parse_alloc()
self.assignments.append(assign)
return(None)

def _client(self):
_client_xml = self.xml.find('client')
_ip_txt = _client_xml.text.strip()
_prefix_txt = _client_xml.attrib['prefix'].strip()
self.client = IP6(_ip_txt, _prefix_txt)
return(None)

def _creds(self):
self.creds_id = self.xml.attrib['creds'].strip()
return(None)

def _id(self):
self.id = int(self.xml.attrib['id'].strip())
return(None)

def _radvd(self):
self.radvd = radvd.RADVD()
self.radvd.conf.generate(self.assignments)
return(None)

def _server(self):
_server_xml = self.xml.find('server')
_ip_text = _server_xml.text.strip()
self.server = IP4(_ip_text, 32)
return(None)

def parse(self):
self._id()
self._creds()
self._client()
self._server()
self._allocations()
self._assignments()
self._radvd()
return(None)

10
utils/he_ipv6/utils.py Normal file
View File

@ -0,0 +1,10 @@
def xml2bool(xml_str):
if xml_str is None:
return(None)
xml_str = xml_str.lower()[0]
if xml_str in ('t', '1'):
return(True)
elif xml_str in ('f', '0'):
return(False)
else:
raise ValueError('Not a boolean value')