package se.sics.asdistances;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Reads prefix to AS mapping from file on instantiation (only used by
* <code>DistanceCalculator</code> when creating
* <code>ASDistances</code>) and provides static methods for manipulating and
* analyzing IP addresses.
*
* @author Niklas Wahlén <nwahlen@kth.se>
* @see DistanceCalculator
* @see ASDistances
*/
public class PrefixHandler {
private static PrefixHandler instance = null;
private Map<InternalPrefix, Integer> prefixToAS;
private final String DEFAULT_DATA_PATH = ASDistances.DEFAULT_DATA_PATH;
private final String DEFAULT_PREFIX_FILE = "routes.txt";
private PrefixHandler() {
InputStream is = null;
try {
is = new FileInputStream(DEFAULT_DATA_PATH + DEFAULT_PREFIX_FILE);
Scanner s = new Scanner(is);
prefixToAS = new HashMap<InternalPrefix, Integer>();
// Statistics
int privateASes = 0;
int reservedASes = 0;
int doubles = 0;
int multiple = 0;
int counter = 0;
System.out.println("Reading prefixes:");
while (s.hasNext()) {
// Read the first column, a routing prefix (formatted as a.b.c.d/x)
String prefix = s.next();
// Read the next column, an AS number
int asn = s.nextInt();
// Retrieve the prefix length from the routing prefix
byte prefixLength = Byte.valueOf(prefix.split("[/]")[1]);
int intPrefix = PrefixHandler.prefixToInteger(prefix);
Integer testASN = prefixToAS.get(new InternalPrefix(intPrefix, prefixLength));
// Check if the AS number is private or reserved (possible due to misconfigurations)
if (asn >= 64512 && asn <= 65534) {
privateASes++;
} else if (asn == 0 || (asn >= 59392 && asn <= 64511) || (asn >= 65535 && asn <= 131071)) {
reservedASes++;
} // Check if this exact prefix is already mapped to an AS number
// TODO: handle the case of multiple ASes mapped to the same prefix correctly
else if (testASN != null) {
if (testASN == asn) {
doubles++;
} else {
multiple++;
}
} // If the above cases check out ok it is safe to add this prefix-AS mapping
else {
prefixToAS.put(new InternalPrefix(intPrefix, prefixLength), asn);
}
counter++;
if (counter % 50000 == 0) {
System.out.println(counter);
}
}
System.out.println(counter);
System.out.println("- - - - - - - - - - - - - ");
System.out.println("Total prefixes saved: " + prefixToAS.size() + " (should be " + (counter - privateASes - reservedASes - doubles - multiple) + ")");
System.out.println("Doubles found: " + doubles);
System.out.println("Prefixes with multiple ASNs: " + multiple);
System.out.println("Prefixes to Private ASNs: " + privateASes);
System.out.println("Prefixes to Reserved ASNs: " + reservedASes);
s.close();
} catch (FileNotFoundException ex) {
Logger.getLogger(PrefixHandler.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
is.close();
} catch (IOException ex) {
Logger.getLogger(PrefixHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
/**
* Finds the AS which owns an IP address.
*
* @param ip
* @return The AS which owns the specified IP or -1 if no owner is found.
*/
public int getASFromIP(String ip) {
return getASFromIP(PrefixHandler.prefixToInteger(ip));
}
public int getASFromIP(int ip) {
for (byte prefixLength = 32; prefixLength > 0; prefixLength--) {
Integer asn = prefixToAS.get(new InternalPrefix(ip, prefixLength));
if (asn != null) {
return asn;
}
}
return -1;
}
/**
* Returns the mapping of routing prefixes and their corresponding AS
* number.
*
* @return prefix to AS mapping
*/
public Map<InternalPrefix, Integer> getPrefixToAS() {
return prefixToAS;
}
/**
* Finds the length of the longest shared prefix of two IP addresses.
*
* @param ip1
* @param ip2
* @return The shared prefix length in bits.
* @see #sharedPrefix(int, int)
*/
public static int sharedPrefix(String ip1, String ip2) {
return sharedPrefix(prefixToInteger(ip1), prefixToInteger(ip2));
}
/**
* Finds the length of the longest shared prefix of two IP addresses by
* calling
* <code>Integer.numberOfLeadingZeros(ip1 ^ ip2)</code>.
*
* @param ip1
* @param ip2
* @return The shared prefix length in bits.
*/
public static int sharedPrefix(int ip1, int ip2) {
return Integer.numberOfLeadingZeros(ip1 ^ ip2);
}
/**
* Converts an IP address (a.b.c.d) or a network prefix (a.b.c.d/x) to its
* integer value.
*
* @param prefix An IP address or a network prefix (if the prefix length is
* omitted the address will be treated as a prefix of length 32 bits).
* @return The integer value of the prefix.
*/
public static Integer prefixToInteger(String prefix) {
int intPrefix = 0;
String[] prefixPart = prefix.split("[/]");
if (prefixPart.length > 1) {
intPrefix = prefixToInteger(prefixPart[0], Integer.valueOf(prefixPart[1]));
} else {
intPrefix = prefixToInteger(prefixPart[0], 32);
}
return intPrefix;
}
/**
* Converts a network prefix to its integer value.
*
* @param prefix
* @param prefixLength
* @return The integer value of the prefix.
*/
public static Integer prefixToInteger(String prefix, int prefixLength) {
String[] prefixPart = prefix.split("[.]");
if (prefixPart.length < 4) {
throw new IllegalArgumentException("Not a valid IP prefix");
}
int intPrefix = ((Integer.parseInt(prefixPart[0]) << 24
| Integer.parseInt(prefixPart[1]) << 16
| Integer.parseInt(prefixPart[2]) << 8
| Integer.parseInt(prefixPart[3])) >>> (32 - prefixLength)) << (32 - prefixLength);
return intPrefix;
}
/**
* Converts an integer representation of an IP address to a String.
*
* @param ip
* @return The String representation of the IP address ("a.b.c.d").
*/
public static String prefixToString(int ip) {
return ((ip >>> 24) & 0xFF) + "."
+ ((ip >>> 16) & 0xFF) + "."
+ ((ip >>> 8) & 0xFF) + "."
+ (ip & 0xFF);
}
public static PrefixHandler getInstance() {
if (instance == null) {
instance = new PrefixHandler();
}
return instance;
}
}