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 args
from . import radvd from . import radvd
from . import tunnel
from . import config from . import config
from . import logger from . import logger
from . import tunnelbroker from . import tunnelbroker

View File

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

from . import utils

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)




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




class Assignment(object): class HETunnel(object):
def __init__(self, assign_xml, radvd = False, dns = False): def __init__(self, tun_xml):
self.xml = assign_xml pass
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 BaseConfig(object): class BaseConfig(object):
@ -333,7 +164,6 @@ class Config(BaseConfig):
with open(xml_path, 'rb') as fh: with open(xml_path, 'rb') as fh:
raw_xml = fh.read() raw_xml = fh.read()
super().__init__(raw_xml, *args, **kwargs) super().__init__(raw_xml, *args, **kwargs)
self.heconf = None
self.creds = {} self.creds = {}
self.tunnels = collections.OrderedDict() self.tunnels = collections.OrderedDict()
self.subparse() self.subparse()
@ -345,67 +175,62 @@ class Config(BaseConfig):
self.creds[cred.id] = cred self.creds[cred.id] = cred
return(None) return(None)


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

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


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




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

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

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

#
def _add_ns(self, raw_xml): # def _add_ns(self, raw_xml):
# https://mailman-mail5.webfaction.com/pipermail/lxml/20100323/013260.html # # https://mailman-mail5.webfaction.com/pipermail/lxml/20100323/013260.html
_xml = etree.fromstring(raw_xml) # _xml = etree.fromstring(raw_xml)
_nsmap = copy.deepcopy(_xml.nsmap) # _nsmap = copy.deepcopy(_xml.nsmap)
_nsmap.update(self.nsmap) # _nsmap.update(self.nsmap)
mod_xml = etree.Element(_xml.tag, {self.attr_qname: self.schema_loc}, nsmap = _nsmap) # mod_xml = etree.Element(_xml.tag, {self.attr_qname: self.schema_loc}, nsmap = _nsmap)
mod_xml[:] = _xml[:] # mod_xml[:] = _xml[:]
return(etree.tostring(mod_xml, # return(etree.tostring(mod_xml,
encoding = 'UTF-8', # encoding = 'UTF-8',
xml_declaration = True, # xml_declaration = True,
pretty_print = True, # pretty_print = True,
with_tail = True, # with_tail = True,
with_comments = True)) # with_comments = True))




class HETunnelConfig(BaseConfig): class HETunnelConfig(BaseConfig):
@ -416,8 +241,9 @@ class HETunnelConfig(BaseConfig):
attr_qname = etree.QName('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation') attr_qname = etree.QName('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')
schema_loc = 'https://tunnelbroker.net/tunnelInfo.php?tid {0}'.format(default_xsd) 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.xml = tun_xml
self.creds = creds
self.id = None self.id = None
self.description = None self.description = None
self.client = None # Client IPv6 self.client = None # Client IPv6
@ -432,13 +258,13 @@ class HETunnelConfig(BaseConfig):
for a in ('64', '48'): for a in ('64', '48'):
_alloc = self.xml.find('routed{0}'.format(a)) _alloc = self.xml.find('routed{0}'.format(a))
if _alloc is not None and _alloc.text.strip() != '': 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) return(None)


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


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


def _endpoint(self): 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) return(None)


def _id(self): def _id(self):
@ -458,7 +284,7 @@ class HETunnelConfig(BaseConfig):
def _my_ip(self): def _my_ip(self):
_ip = self.xml.find('clientv4').text _ip = self.xml.find('clientv4').text
if _ip is not None and _ip.strip() != '': 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) return(None)


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


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


def parse(self): def parse(self):

View File

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