package se.sics.asdistances;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
/**
* Analyzes AS topology information (BGP UPDATES from
* {@link http://archive.routeviews.org/} converted to ASCII) and calculates
* distances between transit ASes, which is stored together with additional AS
* topology information in an
* <code>ASDistances</code> object.
*
*
* The idea is to only calculate the distances between transit ASes, and for
* stub ASes maintain lists of their providers (which by definition is a transit
* AS). The distance to a stub AS is then the shortest distance to any of that
* stub ASes providers plus 1. This saves a lot of space and memory, and the
* time to lookup a stub AS' providers is very low.
*
*
* The only purpose of this class is to generate the
* <code>ASDistances</code> class. Otherwise it is never used.
*
* @author Niklas Wahlén <nwahlen@kth.se>
* @see ASDistances
*/
public class DistanceCalculator {
private static boolean ready = false;
private static final String DEFAULT_DATA_PATH = ASDistances.DEFAULT_DATA_PATH;
private static final String DEFAULT_RELATION_LOCATION = "ASrelation.txt";
private static final String DEFAULT_CONNPAIRS_LOCATION = "ASconnpairs.txt";
private static Map<Integer, int[]> stubASProviders;
private static Map<Integer, Integer> transitAStoInternalIndex;
private static Map<Integer, ArrayList<Integer>> edges;
private static byte[][] path;
private static DistanceCalculator instance;
private static int counter;
private DistanceCalculator() {
readASRelations();
readTransitASConnections();
calculateDistances();
PrefixHandler ph = PrefixHandler.getInstance();
ASDistances asd = new ASDistances();
asd.setIpPrefixToAS(ph.getPrefixToAS());
asd.setTransitASToInternalIndex(transitAStoInternalIndex);
asd.setTransitASDistances(path);
asd.setStubAProviders(stubASProviders);
try {
asd.serialize();
} catch (IOException ex) {
Logger.getLogger(DistanceCalculator.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Reads the AS relations file. All stub ASes are stored together with a
* list of their providers. All transit ASes are stored and mapped to an
* internal index. The internal index is necessary to minimize the size of
* the distance matrix; many AS numbers are not used and thus not present in
* the AS data.
*/
private static void readASRelations() {
InputStream is = null;
try {
is = new FileInputStream(DEFAULT_DATA_PATH + DEFAULT_RELATION_LOCATION);
stubASProviders = new HashMap<Integer, int[]>();
transitAStoInternalIndex = new HashMap<Integer, Integer>();
Scanner s = new Scanner(is);
int totalASes = 0;
int stubASProvidersCount = 0;
// Remove everything from the line except for the AS number
Pattern asnPattern = Pattern.compile("[^0-9]");
// Remove everything from the line except for the number of customers/siblings/peers
Pattern p = Pattern.compile("(^.*:#)|(::.*$)");
// Remove everything from the line except for the providers' AS numbers
Pattern p2 = Pattern.compile("(^.*::)|(:$)");
System.out.println("Reading AS relation information");
while (s.hasNext()) {
totalASes++;
int asn = Integer.valueOf(asnPattern.matcher(s.nextLine()).replaceAll(""));
String providerLine = s.nextLine();
int numberOfCustomers = Integer.valueOf(p.matcher(s.nextLine()).replaceAll(""));
int numberOfSiblings = Integer.valueOf(p.matcher(s.nextLine()).replaceAll(""));
int numberOfPeers = Integer.valueOf(p.matcher(s.nextLine()).replaceAll(""));
// Check if the current AS is a stub AS
if (numberOfCustomers + numberOfSiblings + numberOfPeers == 0) {
providerLine = p2.matcher(providerLine).replaceAll("");
String[] providers = providerLine.split(":");
int providersInt[] = new int[providers.length];
for (int i = 0; i < providers.length; i++) {
stubASProvidersCount++;
providersInt[i] = Integer.valueOf(providers[i]);
}
stubASProviders.put(asn, providersInt);
} else {
transitAStoInternalIndex.put(asn, transitAStoInternalIndex.size());
}
if (totalASes % 10000 == 0) {
System.out.println(totalASes);
}
}
System.out.println(totalASes);
System.out.println("- - - - - - - - - - - - - ");
System.out.println("Total ASes: " + totalASes);
System.out.println("Transit ASes: " + transitAStoInternalIndex.size());
System.out.println("Stub ASes: " + stubASProviders.size());
System.out.println("Ratio stub ASes: "
+ ((float) stubASProviders.size()) / ((float) totalASes));
System.out.println("Avg number of providers per stub AS: "
+ ((float) stubASProviders.values().size()) / ((float) stubASProviders.size()));
System.out.println();
} catch (FileNotFoundException ex) {
Logger.getLogger(DistanceCalculator.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
is.close();
} catch (IOException ex) {
Logger.getLogger(DistanceCalculator.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private int getTransitASInternalIndex(int transitAS) {
return transitAStoInternalIndex.get(transitAS);
}
/**
* Reads the AS connection pairs file and stores each AS as a key entry in a
* map, with an
* <code>ArrayList</code> of its neighbors as its value.
*/
private void readTransitASConnections() {
InputStream is = null;
try {
is = new FileInputStream(DEFAULT_DATA_PATH + DEFAULT_CONNPAIRS_LOCATION);
Scanner s = new Scanner(is);
edges = new HashMap<Integer, ArrayList<Integer>>();
System.out.println("Importing connections between transit ASes"
+ " (" + transitAStoInternalIndex.size() + ") .. ");
while (s.hasNext()) {
String strs[] = s.nextLine().split(" <-> ");
int fromAS = Integer.valueOf(strs[0].trim());
int toAS = Integer.valueOf(strs[1].trim());
// Check that the conncetion is between two transit ASes
// (otherwise it is ignored)
if (transitAStoInternalIndex.containsKey(fromAS) && transitAStoInternalIndex.containsKey(toAS)) {
// From AS
if (edges.containsKey(fromAS)) {
edges.get(fromAS).add(toAS);
} else {
ArrayList l = new ArrayList<Integer>();
l.add(toAS);
edges.put(fromAS, l);
}
// To AS
if (edges.containsKey(toAS)) {
edges.get(toAS).add(fromAS);
} else {
ArrayList l = new ArrayList<Integer>();
l.add(fromAS);
edges.put(toAS, l);
}
}
}
System.out.print("Import done, checking that all transit ASes are connected: ");
if (transitAStoInternalIndex.size() == edges.size()) {
System.out.println("OK.");
} else {
System.out.println("Failed.");
}
} catch (FileNotFoundException ex) {
Logger.getLogger(DistanceCalculator.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
is.close();
} catch (IOException ex) {
Logger.getLogger(DistanceCalculator.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
/**
* Calculates distances between transit ASes according to the Floyd-Warshall
* algorithm (an all-pairs shortest paths algorithm)
* {@link http://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm}
*/
private void calculateDistances() {
System.out.println();
System.out.println("Preparing to calculate AS distances .. ");
int n = edges.size();
path = new byte[n][n];
for (Integer i : edges.keySet()) {
int iInternal = getTransitASInternalIndex(i);
// Set the distance between all peers to the max value initially
Arrays.fill(path[iInternal], Byte.MAX_VALUE);
// The distance to ourselves is 0
path[iInternal][iInternal] = 0;
// The distance to any directly connected neighbor AS is 1
for (Integer j : edges.get(i)) {
path[iInternal][getTransitASInternalIndex(j)] = 1;
}
}
long starttime = System.currentTimeMillis();
System.out.println("Calculating distances between " + n + " transit ASes:");
// Floyd-Warshall algorithm, runs in
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
path[i][j] = (byte) Math.min(path[i][j], path[i][k] + path[k][j]);
}
}
increaseCounter();
}
long endtime = System.currentTimeMillis();
printRuntime(endtime - starttime);
}
private synchronized void increaseCounter() {
counter++;
if ((counter % 100) == 0) {
System.out.format("%d (%.2f%%)%n", counter, (((float) counter) / ((float) transitAStoInternalIndex.size())) * 100);
} else if (counter == transitAStoInternalIndex.size()) {
System.out.println(transitAStoInternalIndex.size() + " (100%)");
}
}
private void printRuntime(long runtime) {
long minutes = 0, seconds = 0;
if (runtime > 60000) {
minutes = runtime / 60000;
runtime -= minutes * 60000;
}
if (runtime > 1000) {
seconds = runtime / 1000;
}
long milliseconds = runtime - (seconds * 1000);
System.out.println("Runtime: " + minutes + ":" + seconds + "." + milliseconds);
}
/**
* When this class is initialized an
* <code>ASDistances</code> object will be created and stored to disk.
*
* @return the DistanceCalculator singleton
* @see ASDistances
*/
public static DistanceCalculator getInstance() {
if (instance == null) {
instance = new DistanceCalculator();
}
return instance;
}
/**
* Calls getInstance.
*
* @param args
* @see #getInstance()
*/
public static void main(String args[]) {
DistanceCalculator instance = getInstance();
}
}