2018-03-29 22:37:28 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
import logging
|
2018-04-05 14:36:01 +02:00
|
|
|
from redis import StrictRedis
|
2018-03-29 22:37:28 +02:00
|
|
|
|
|
|
|
import time
|
|
|
|
import pytricia
|
|
|
|
import ipaddress
|
2018-04-05 14:36:01 +02:00
|
|
|
from .libs.helpers import shutdown_requested, set_running, unset_running, get_socket_path
|
2018-03-29 22:37:28 +02:00
|
|
|
|
|
|
|
|
|
|
|
class RISPrefixLookup():
|
|
|
|
|
|
|
|
def __init__(self, loglevel: int=logging.DEBUG):
|
|
|
|
self.__init_logger(loglevel)
|
|
|
|
self.logger.info('Starting RIS Prefix fetcher')
|
2018-04-05 14:36:01 +02:00
|
|
|
self.prefix_db = StrictRedis(unix_socket_path=get_socket_path('prefixes'), db=0, decode_responses=True)
|
|
|
|
self.longest_prefix_matching = StrictRedis(unix_socket_path=get_socket_path('ris'), db=0, decode_responses=True)
|
2018-03-29 22:37:28 +02:00
|
|
|
self.tree_v4 = pytricia.PyTricia()
|
|
|
|
self.tree_v6 = pytricia.PyTricia(128)
|
2018-04-10 01:46:38 +02:00
|
|
|
self.force_init = True
|
|
|
|
self.current_v4 = None
|
|
|
|
self.current_v6 = None
|
2018-03-29 22:37:28 +02:00
|
|
|
|
|
|
|
def __init_logger(self, loglevel):
|
2018-04-10 00:20:59 +02:00
|
|
|
self.logger = logging.getLogger(f'{self.__class__.__name__}')
|
2018-03-29 22:37:28 +02:00
|
|
|
self.logger.setLevel(loglevel)
|
|
|
|
|
|
|
|
def cache_prefix(self, pipe, ip, prefix, asns):
|
|
|
|
pipe.hmset(ip, {'asn': asns, 'prefix': prefix})
|
|
|
|
pipe.expire(ip, 43200) # 12H
|
|
|
|
|
|
|
|
def init_tree(self):
|
|
|
|
for asn in self.prefix_db.smembers('asns'):
|
2018-04-10 00:20:59 +02:00
|
|
|
for prefix in self.prefix_db.smembers(f'{asn}|v4'):
|
2018-03-29 22:37:28 +02:00
|
|
|
self.tree_v4[prefix] = asn
|
2018-04-10 00:20:59 +02:00
|
|
|
for prefix in self.prefix_db.smembers(f'{asn}|v6'):
|
2018-03-29 22:37:28 +02:00
|
|
|
self.tree_v6[prefix] = asn
|
|
|
|
self.tree_v4['0.0.0.0/0'] = 0
|
2018-04-10 01:46:38 +02:00
|
|
|
self.tree_v6['::/0'] = 0
|
|
|
|
self.current_v4 = self.prefix_db.get('current|v4')
|
|
|
|
self.current_v6 = self.prefix_db.get('current|v6')
|
2018-03-29 22:37:28 +02:00
|
|
|
|
|
|
|
def run(self):
|
|
|
|
set_running(self.__class__.__name__)
|
|
|
|
while True:
|
|
|
|
if shutdown_requested():
|
|
|
|
break
|
|
|
|
if not self.prefix_db.get('ready'):
|
|
|
|
self.logger.debug('Prefix database not ready.')
|
|
|
|
time.sleep(5)
|
2018-04-10 01:46:38 +02:00
|
|
|
self.force_init = True
|
2018-03-29 22:37:28 +02:00
|
|
|
continue
|
2018-04-10 01:46:38 +02:00
|
|
|
if (self.force_init or
|
|
|
|
(self.current_v4 != self.prefix_db.get('current|v4')) or
|
|
|
|
(self.current_v6 != self.prefix_db.get('current|v6'))):
|
|
|
|
self.init_tree()
|
|
|
|
self.force_init = False
|
|
|
|
|
2018-03-29 22:37:28 +02:00
|
|
|
ips = self.longest_prefix_matching.spop('for_ris_lookup', 100)
|
|
|
|
if not ips: # TODO: add a check against something to stop the loop
|
|
|
|
self.logger.debug('Nothing to lookup')
|
|
|
|
break
|
|
|
|
pipe = self.longest_prefix_matching.pipeline(transaction=False)
|
|
|
|
for ip in ips:
|
|
|
|
if self.longest_prefix_matching.exists(ip):
|
2018-04-10 00:20:59 +02:00
|
|
|
self.logger.debug(f'Already cached: {ip}')
|
2018-03-29 22:37:28 +02:00
|
|
|
continue
|
|
|
|
ip = ipaddress.ip_address(ip)
|
|
|
|
if ip.version == 4:
|
|
|
|
prefix = self.tree_v4.get_key(ip)
|
|
|
|
asns = self.tree_v4.get(ip)
|
|
|
|
else:
|
|
|
|
prefix = self.tree_v6.get_key(ip)
|
|
|
|
asns = self.tree_v6.get(ip)
|
|
|
|
if not prefix:
|
2018-04-10 00:20:59 +02:00
|
|
|
self.logger.warning(f'The IP {ip} does not seem to be announced')
|
2018-03-29 22:37:28 +02:00
|
|
|
continue
|
|
|
|
self.cache_prefix(pipe, ip, prefix, asns)
|
|
|
|
pipe.execute()
|
|
|
|
unset_running(self.__class__.__name__)
|