154 lines
5.5 KiB
Python
154 lines
5.5 KiB
Python
|
#!/usr/bin/env python2
|
||
|
__author__ = "Bradley Huffaker"
|
||
|
__email__ = "<bradley@caida.org>"
|
||
|
# This software is Copyright (C) 2022 The Regents of the University of
|
||
|
# California. All Rights Reserved. Permission to copy, modify, and
|
||
|
# distribute this software and its documentation for educational, research
|
||
|
# and non-profit purposes, without fee, and without a written agreement is
|
||
|
# hereby granted, provided that the above copyright notice, this paragraph
|
||
|
# and the following three paragraphs appear in all copies. Permission to
|
||
|
# make commercial use of this software may be obtained by contacting:
|
||
|
#
|
||
|
# Office of Innovation and Commercialization
|
||
|
# 9500 Gilman Drive, Mail Code 0910
|
||
|
# University of California
|
||
|
# La Jolla, CA 92093-0910
|
||
|
# (858) 534-5815
|
||
|
#
|
||
|
# invent@ucsd.edu
|
||
|
#
|
||
|
# This software program and documentation are copyrighted by The Regents of
|
||
|
# the University of California. The software program and documentation are
|
||
|
# supplied $B!H(Bas is$B!I(B, without any accompanying services from The Regents. The
|
||
|
# Regents does not warrant that the operation of the program will be
|
||
|
# uninterrupted or error-free. The end-user understands that the program
|
||
|
# was developed for research purposes and is advised not to rely
|
||
|
# exclusively on the program for any reason.
|
||
|
#
|
||
|
# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
|
||
|
# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
|
||
|
# INCLUDING LOST PR OFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
|
||
|
# DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF
|
||
|
# THE POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY
|
||
|
# DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
|
||
|
# SOFTWARE PROVIDED HEREUNDER IS ON AN $B!H(BAS IS$B!I(B BASIS, AND THE UNIVERSITY OF
|
||
|
# CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
|
||
|
# ENHANCEMENTS, OR MODIFICATIONS.
|
||
|
#
|
||
|
|
||
|
#import the low level _pybgpsteam library and other necessary libraries
|
||
|
from pybgpstream import BGPStream
|
||
|
from datetime import date
|
||
|
from datetime import timedelta
|
||
|
import argparse
|
||
|
|
||
|
import sys
|
||
|
import bz2
|
||
|
|
||
|
parser = argparse.ArgumentParser()
|
||
|
parser.add_argument("link_file", nargs=1, type=str)
|
||
|
parser.add_argument("-d", "--debug", type=int, default=-1)
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
def main():
|
||
|
sys.stderr.write("This is going to take some time\n")
|
||
|
|
||
|
# Handle debug option
|
||
|
debug_count = args.debug
|
||
|
|
||
|
peer_provider = download_links(args.link_file[0])
|
||
|
asn__cone = download_paths(peer_provider, debug_count)
|
||
|
|
||
|
print "# ASN followed by ASNs in it's customer cone"
|
||
|
print "# '1 23 4' means ASN 1's customer cone includes ASN 23 and ASN 4"
|
||
|
for asn,cone in sorted(asn__cone.items(), key=lambda a_c: len(a_c[1]),reverse=True):
|
||
|
print asn + " "+" ".join(sorted(cone,key=lambda a: int(a.lstrip('{').rstrip('}'))))
|
||
|
|
||
|
# Find the set of AS Relationships that are
|
||
|
# peer to peer or provider to customer.
|
||
|
def download_links(filename):
|
||
|
sys.stderr.write("loading relationships\n")
|
||
|
first = 1000
|
||
|
offset = 0
|
||
|
hasNextPage = True
|
||
|
|
||
|
peer_provider = set()
|
||
|
with bz2.BZ2File(filename, mode='r') as fin:
|
||
|
for line in fin:
|
||
|
# skip comments
|
||
|
if len(line) == 0 or line[0] == "#":
|
||
|
continue
|
||
|
asn0, asn1, rel = line.rstrip().split("|")
|
||
|
|
||
|
# peers work in both directions
|
||
|
if rel == 0:
|
||
|
peer_provider.add(asn1+" "+asn0)
|
||
|
peer_provider.add(asn0+" "+asn1)
|
||
|
|
||
|
# store the link from provider to customer
|
||
|
elif rel == -1:
|
||
|
peer_provider.add(asn1+" "+asn0)
|
||
|
else:
|
||
|
peer_provider.add(asn0+" "+asn1)
|
||
|
|
||
|
return peer_provider
|
||
|
|
||
|
# download the AS paths from BGPStream
|
||
|
# crop the path to the section after the
|
||
|
# first peer or provider link, then add
|
||
|
# all the remaining ASes to the preceeding
|
||
|
# ASes in the cropped path
|
||
|
def download_paths(peer_provider, debug_count):
|
||
|
# The set of ASes reachable through an AS's customers
|
||
|
asn__cone = {}
|
||
|
|
||
|
sys.stderr.write("downloading paths\n")
|
||
|
|
||
|
# Return a rib from yesterday's first second
|
||
|
from_time = date.today() - timedelta(days=1)
|
||
|
until_time = from_time + timedelta(seconds=1)
|
||
|
stream = BGPStream(
|
||
|
from_time=from_time.strftime("%Y-%m-%d %H:%M:%S"), until_time=until_time.strftime("%Y-%m-%d %H:%M:%S"),
|
||
|
record_type="ribs"
|
||
|
)
|
||
|
stream.add_rib_period_filter(86400) # This should limit BGPStream to download the full first BGP dump
|
||
|
|
||
|
# counter
|
||
|
count = 0
|
||
|
for elem in stream:
|
||
|
# Break when debug mode activated and count equals debug count
|
||
|
if (debug_count >= 0) and (count >= debug_count):
|
||
|
break
|
||
|
|
||
|
asns = elem.fields['as-path'].split(" ")
|
||
|
|
||
|
# Skip until the first peer-peer or provider->customer link
|
||
|
i = 0
|
||
|
while i+1 < len(asns):
|
||
|
link = asns[i]+" "+asns[i+1]
|
||
|
i += 1
|
||
|
if link in peer_provider:
|
||
|
break
|
||
|
|
||
|
# Since an AS only announces it's customer cone to it's peer or provider,
|
||
|
# the remaining ASes in the path are in the preciding ASes customer cone.
|
||
|
while i+1 < len(asns):
|
||
|
if asns[i] not in asn__cone:
|
||
|
asn__cone[asns[i]] = set()
|
||
|
cone = asn__cone[asns[i]]
|
||
|
j = i+1
|
||
|
while j < len(asns):
|
||
|
print (">",i,j,asns[i], asns[j])
|
||
|
cone.add(asns[j])
|
||
|
j += 1
|
||
|
i += 1
|
||
|
|
||
|
# Increment count
|
||
|
count += 1
|
||
|
|
||
|
return asn__cone
|
||
|
|
||
|
#run the main method
|
||
|
main()
|