Skip to content

Instantly share code, notes, and snippets.

@DamianZaremba
Created August 2, 2011 22:29
Show Gist options
  • Save DamianZaremba/1121394 to your computer and use it in GitHub Desktop.
Save DamianZaremba/1121394 to your computer and use it in GitHub Desktop.
BGPServer written in python - doesn't actually do anything apart from process inotify events right now.
#!/usr/bin/env python3
import argparse
import os
import logging
import sys
import signal
import socket
import pyinotify
'''
BGPServer - this does the core work of threading out the other classes and
sitting around for inotify events.
'''
class BGPServer:
'''
Initializes the class and sets up some basic stuff
'''
def __init__(self, asn, asi, peers_file='peers', routes_file='routes',
logging_level=logging.WARNING):
self.asn = asn
self.asi = asi
self.peers_file = None
self.routes_file = None
logging.basicConfig()
self.logger = logging.getLogger('BGPServer')
self.logger.setLevel(logging_level)
if peers_file[0] != '/':
self.logger.debug('Turning relative peers_file path into absolute')
self.peers_file = os.path.realpath(peers_file)
if routes_file[0] != '/':
self.logger.debug('Turning relative routes_file path into absolute')
self.routes_file = os.path.realpath(routes_file)
if not self.peers_file:
self.logger.debug('Seeing peers_file to relative path')
self.peers_file = peers_file
if not self.routes_file:
self.logger.debug('Seeing routes_file to relative path')
self.routes_file = routes_file
if not self.asn or not self.asi:
self.logger.critical('No ASN or ASI specified')
self.logger.debug('Setting up watch manager')
self.wm = pyinotify.WatchManager()
self.notifier = pyinotify.Notifier(self.wm, FileEvent(BGPServer=self));
self.peers = {}
self.routes = {}
self.running = True
self.setup_signals()
'''
Sets up some signal handlers to ensure we exit as cleanly as possible.
'''
def setup_signals(self):
self.logger.debug('Registering signals')
signal.signal(signal.SIGTERM, self.shutdown)
signal.signal(signal.SIGINT, self.shutdown)
'''
Shuts down the server as cleanly as possible.
'''
def shutdown(self, *args, **kargs):
self.logger.debug('Starting shutdown')
self.running = False
try:
self.wm.rm_watch(self.routes_file)
self.wm.rm_watch(self.peers_file)
except:
self.logger.warning("Could not un-register watchers")
pass
try:
self.notifier.stop()
except:
self.logger.warning("Could not stop notifier cleanly")
pass
'''
Reloads the peers from file.
'''
def reload_peers(self):
self.logger.info("Reload peers running")
if os.path.isfile(self.peers_file):
fh = open(self.peers_file, 'r')
for line in fh.readlines():
line = line.strip()
if len(line) == 0: continue
if line[0] == "#": continue
parts = line.split()
if len(parts) == 2:
(address, password) = parts
else:
address = parts[0]
password = none
aparts = address.split('/')
address = aparts[0]
port = 179
if len(aparts) == 2:
try:
port = int(aparts[1])
except ValueError:
self.logger.critical("Invalid port specified for %s" %
address)
next
if self.valid_ip(address):
peer_id = "%s-%s" % (address, port)
self.logger.info("Adding %s to our peers" % peer_id)
self.peers[peer_id] = Peer(address, password)
else:
self.logger.warning("Skipping %s as the address looks dodgy." % peer_id)
fh.close()
'''
Checks if an ip looks valid.
'''
def valid_ip(self, ip):
try:
socket.inet_aton(ip)
except socket.error:
try:
socket.inet_pton(socket.AF_INET6, ip)
except socket.error:
return False
else:
return True
else:
return True
'''
Reloads the routes from file.
'''
def reload_routes(self):
self.logger.info("Reload routes running")
pass
'''
Drops the server into a processing loop until it receives a signal to quit.
'''
def run(self):
self.logger.debug('Starting run')
self.logger.debug('Triggering reloads')
self.reload_peers()
self.reload_routes()
self.logger.info('Adding watcher for routes file')
self.wm.add_watch(self.routes_file, pyinotify.IN_CLOSE_WRITE)
self.logger.info('Adding watcher for peers file')
self.wm.add_watch(self.peers_file, pyinotify.IN_CLOSE_WRITE)
self.logger.debug('Dropping into while loop')
while self.running == True:
self.logger.debug('Calling process_events()')
self.notifier.process_events()
self.logger.debug('Checking if we have any events')
if self.notifier.check_events():
self.logger.debug('Reading events')
self.notifier.read_events()
self.logger.debug('Processing events')
self.notifier.process_events()
'''
Peer - this class represents a single peer
'''
class Peer:
def __init__(self, address, password=None):
self.routes = {}
'''
FileEvent - this class gets called when one of the files changes
'''
class FileEvent(pyinotify.ProcessEvent):
def my_init(self, **kargs):
self.BGPServer = kargs.get('BGPServer')
def process_IN_CLOSE_WRITE(self, event):
if event.pathname == self.BGPServer.peers_file:
self.BGPServer.logger.debug("Triggering peers reload event")
self.BGPServer.reload_peers()
elif event.pathname == self.BGPServer.routes_file:
self.BGPServer.logger.debug("Triggering routes reload event")
self.BGPServer.reload_routes()
else:
self.BGPServer.logger.warn("Unknown path '%s'" % event.pathname)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
'-p', '--peers', dest='peers_file', action='store', default='peers',
type=str, help='Path to the peers file',
)
parser.add_argument(
'-r', '--routes', dest='routes_file', action='store', default='routes',
type=str, help='Path to the routes file',
)
parser.add_argument(
'-d', '--debug', dest='debug_level', action='store', default='warn',
type=str, help='Debug level (critical, warning, info, debug)',
)
parser.add_argument(
'-asn', '--as-number', dest='asn', action='store', type=str,
help='AS number',
)
parser.add_argument(
'-asi', '--as-identifier', dest='asi', action='store', type=str,
help='AS identifier (your IP address)',
)
args = parser.parse_args()
if args.debug_level.lower() == 'debug':
log_level = logging.DEBUG
elif args.debug_level.lower() in ('info', 'information'):
log_level = logging.INFO
elif args.debug_level.lower() in ('warn', 'warning'):
log_level = logging.WARNING
elif args.debug_level.lower() == 'critical':
log_level = logging.CRITICAL
else:
print("Invalid debug_level supplied")
sys.exit(2)
if not args.asi:
print("No ASI specified")
sys.exit(2)
if not args.asn:
print("No ASN specified")
sys.exit(2)
server = BGPServer(
peers_file = args.peers_file,
routes_file = args.routes_file,
logging_level = log_level,
asn = args.asn,
asi = args.asi
)
server.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment