Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
#!/usr/bin/env python
import argparse
import csv
import ConfigParser
import datetime
import json
import re
import smtplib
import subprocess
import sys
from email.mime.text import MIMEText
from filecmp import cmp as fcmp
from shutil import copy2
from tabulate import tabulate
def matching_items(data, community):
result = {}
for (key, value) in data.items():
for subitem in value:
age = subitem["age"]
for attr in subitem["attrs"]:
for comm in community:
if attr["type"] == 2 and attr["as_paths"]:
origin_asn = attr["as_paths"][0]["asns"][-1]
if attr["type"] == 8 and comm in attr["communities"]:
result[key] = [age, origin_asn]
break
return result
def update_routes( comms, wd, ts ):
"""Query gobgp to obtain blackhole routes"""
myfile = wd+ts+'_routes.csv'
if fcmp(wd+'latest_routes.csv',wd+'current_routes.csv') is False:
copy2(wd+'latest_routes.csv',wd+'current_routes.csv')
with open(myfile, 'w') as f:
for family in ['ipv4', 'ipv6']:
route = subprocess.Popen(['gobgp','global','rib','-a',family,'-j'], stdout=subprocess.PIPE)
route = json.loads(route.stdout.read())
cidrs = matching_items(route, comms)
table = []
for (key, value) in cidrs.items():
table.append([key,datetime.datetime.fromtimestamp(value[0]).strftime('%Y-%m-%d'),'AS{}'.format(value[1])])
wr = csv.writer(f)
for row in table:
wr.writerow(row)
copy2(myfile,wd+'latest_routes.csv')
def diff_routes(wd):
"""diff the current routes with the last set of routes
and print tables of advertised and withdrawn routes"""
with open(wd+'latest_routes.csv','rb') as f:
reader = csv.reader(f)
latest = list(reader)
with open(wd+'current_routes.csv','rb') as f:
reader = csv.reader(f)
current = list(reader)
latest = sort_table(latest)
current = sort_table(current)
advertised = [x for x in latest if x not in current]
withdrawn = [x for x in current if x not in latest]
return advertised,withdrawn
def all_routes(wd):
"""print all current blackhole routes"""
with open(wd+'latest_routes.csv','rb') as f:
reader = csv.reader(f)
current = list(reader)
return sort_table(current)
def sort_table(data):
"""sort list and remove entries with no first value"""
table = []
for row in data:
table.append([row[0], row[1], row[2]])
table.sort(lambda x,y: cmp(x[0],y[0]))
table.sort(lambda x,y: cmp(x[2],y[2]))
return table
def commplain(comm):
"""Convert as:comm to plain decimal"""
left,right = re.split('\.|:', comm)
ret = int(left) * 65536 + int(right)
return ret
def mail(wd,mailfrom,rcptto,smtp_server,diff=False,all=False):
"""email blackhole routes to recipient"""
text = ''
if diff is True:
mail_adv, mail_with = diff_routes(wd)
adv = []
for row in mail_adv:
adv.append([row[0], row[1], row[2], 'http://bgp.he.net/{0}'.format(row[2])])
withdrawn = []
for row in mail_with:
withdrawn.append([row[0], row[1], row[2], 'http://bgp.he.net/{0}'.format(row[2])])
if adv != [] or mail_with != []:
text += '\nNewly advertised blackhole routes:\n\n{0}\n\nWithdrawn blackhole routes:\n\n{1}'.format(tabulate(adv,['IP prefix','Date Advertised','ASN','Link to ASN Info']), tabulate(withdrawn,['IP prefix','Date Advertised','ASN','Link to ASN Info']))
if all is True:
mail_all = all_routes(wd)
allroutes = []
for row in mail_all:
allroutes.append([row[0], row[1], row[2], 'http://bgp.he.net/{0}'.format(row[2])])
text += '\n\nAll current blackhole routes:\n\n{0}'.format(tabulate(allroutes,['IP Prefix','Date Advertised','ASN','Link to ASN Info']))
if text == '':
print 'No data to email. Exiting...'
return
msg = MIMEText(text)
msg['Subject'] = 'Blackhole routes advertised on the Internet2 network'
msg['From'] = mailfrom
msg['To'] = rcptto
s = smtplib.SMTP(smtp_server)
s.sendmail(mailfrom, [rcptto], msg.as_string())
s.quit()
def main(args):
"""Called when running rtbh-watch.py as a script"""
parser = argparse.ArgumentParser(description='Query the bird daemon for blackhole routes. Print output to screen or email results.', epilog='Example: python rtbh-watch.py -e -a')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-s','--screen', help='Print results to screen', action='store_const', const=True)
group.add_argument('-e','--email', help='Email results to email address', action='store_const', const=True)
group.add_argument('-u','--update', help='Update the local files from bird', action='store_const', const=True)
parser.add_argument('-c','--comm', help='communities to search for. Defaults to (11537,911),(11164,53666),(65535,666)', default=["11537:911","11164:53666","65535:666"])
parser.add_argument('-f','--file', help='config file', default='rtbh-watch.ini')
group2 = parser.add_argument_group(description='You need to pass one or both of the following flags')
group2.add_argument('-d','--diff', help='Print which routes have changed', action='store_const', const=True)
group2.add_argument('-a','--all-routes', help='Print all blackhole routes', action='store_const', const=True)
args = parser.parse_args()
config = ConfigParser.ConfigParser()
try:
config.readfp(open(args.file))
except:
print('Configuration file {0} must exist'.format(args.file))
return
wd = config.get('General','working_dir')
rcptto = config.get('Email','recipient')
mailfrom = config.get('Email','from')
smtp_server = config.get('Email','smtp_server')
ts = '{:%Y-%m-%d_%H-%M-%S}'.format(datetime.datetime.now())
comm = [commplain(c) for c in args.comm]
if args.update is True:
update_routes(comm, wd, ts)
if args.screen is True:
if args.diff is True:
advertised,withdrawn = diff_routes(wd)
print '\nNewly advertised blackhole routes:\n'
print tabulate(advertised,['IP Prefix','Date Advertised','ASN'])
print '\nWithdrawn blackhole routes:\n'
print tabulate(withdrawn,['IP Prefix','Date Advertised','ASN'])
if args.all_routes is True:
print '\nAll current blackhole routes:\n'
print tabulate(all_routes(wd),['IP Prefix','Date Advertised','ASN'])
if args.email is True:
mail(wd,mailfrom,rcptto,smtp_server,args.diff,args.all_routes)
if __name__ == '__main__':
main(sys.argv)