#!/usr/bin/env python ''' Created on Apr 2, 2013 Input (MRT ASCII text output from bgpdump utility) can either be directed in through stdin or may be specified with a file via -f option on the command line. @author: bressoud ''' # Required modules import sys import argparse # Globals args = {} # Dictionary of command-line options filled in by processOptions seentypes = {} # Track record types seen during processing AS2Prefix = {} # Place holder for a mapping from an AS number to a list of # prefixes originating from that AS unprocessedRecords = 0 # Aid in looking for gaps/unexpected record types during processing #----------------------------------------------------------------------------------------- # main(): Program entry point to process command line options and then process input # stream of records. #----------------------------------------------------------------------------------------- def main(): global args global seentypes # Initialization args = processOptions() # Put result into global for easy accessibility recordCount = 0 # Nested loop to process records. Outer loop executes once per record, while # inner loop executes over the lines within a record, and terminates when a # line with just a newline (or EOF) is encountered. line = args.file.readline() while line != '': recordCount += 1 record = [] # Aggregate the set of lines in a record into a list of strings while line != '' and line != '\n': record.append(line.strip()) line = args.file.readline() # Convert the list of strings into a Python dictionary of key:value pairs for # easier subsequent processing. processRecord(record) # prepare for next iteration line = args.file.readline() # Just some feedback of a record count and type info for the user print recordCount print seentypes #----------------------------------------------------------------------------------------- # processOptions(): Command line options and arguments are processed here and placed # in a dictionary returned to the caller # Feel free to add options for the parameters related to histogram processing or, if # desired, intermediate file (phase 1 to phase 2) info. #----------------------------------------------------------------------------------------- def processOptions(): parser = argparse.ArgumentParser() parser.add_argument("-f", "--file", action="store", type=file, default=sys.stdin, help="RIB/update file to process") result = parser.parse_args() return result #----------------------------------------------------------------------------------------- # processRecord(reclist): Entry point called once each record is complete. # reclist: list of strings for the lines making up the record (as produced by bgpdump) #----------------------------------------------------------------------------------------- def processRecord(reclist): # globals modified in the processing of a record global seentypes global AS2Prefix global unprocessedRecords # sanity checks assert(len(reclist) > 1) assert(reclist[0][0:4] == 'TIME') assert(reclist[1][0:4] == 'TYPE') # convert record to dictionary form for ease of further processing record = parseRecord(reclist) # processing for our global bookkeeping t = record['TYPE'] if t not in seentypes: seentypes[t] = 1 else: seentypes[t] += 1 # here is where we differentiate processing based on the type of record, with the # processing of RIB records pertinent to the phase 1 goal of associating prefixes # with their originating AS, and the processing of Update records pertinent to the # phase 2 goal of building the histogram for withdrawals on Egyptian prefixes. if t == 'TABLE_DUMP/INET' or t == 'TABLE_DUMP/INET6': processRIBEntry(record) elif t == 'BGP4MP/MESSAGE/Update': processUpdate(record) else: unprocessedRecords += 1 #----------------------------------------------------------------------------------------- # parseRecord(rlist): Convert a list of strings into a dictionary # rlist: list of strings for the lines making up the record (as produced by bgpdump) # returns the dictionary #----------------------------------------------------------------------------------------- def parseRecord(rlist): rec = {} # Most strings are easy -- KEY: VALUE String format # But some (like ANNOUNCE and WITHDRAW) are on a line by themselves and the # following lines have the associated addresses, and these are the one case # where the value is a list instead of a string. while len(rlist) > 0: line = rlist.pop(0) colonIndex = line.find(':') if colonIndex != -1: key = line[:colonIndex] value = line[colonIndex+2:] rec[key] = value elif line == 'ANNOUNCE' or line == 'WITHDRAW': key = line value = [] assert(len(rlist) > 0) while len(rlist) > 0: line = rlist.pop(0).strip() value.append(line) rec[key] = value else: key = line value = '' rec[key] = value return rec #----------------------------------------------------------------------------------------- # processRIBEntry(record): Entry point called once for each RIB table entry # record is the dictionary with the key-value pairs for the table entry #----------------------------------------------------------------------------------------- def processRIBEntry(record): pass #----------------------------------------------------------------------------------------- # processUpdate(record): Entry point called once for each update record # record is the dictionary with the key-value pairs for the table entry #----------------------------------------------------------------------------------------- def processUpdate(record): pass if __name__ == '__main__': main()