package org.opennaas.extensions.vrf.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.ws.rs.core.Response;
import javax.xml.bind.DatatypeConverter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonToken;
import org.codehaus.jackson.map.MappingJsonFactory;
import org.opennaas.extensions.openflowswitch.model.FloodlightOFAction;
import org.opennaas.extensions.openflowswitch.model.FloodlightOFFlow;
import org.opennaas.extensions.openflowswitch.model.FloodlightOFMatch;
import org.opennaas.extensions.openflowswitch.model.OFFlow;
import org.opennaas.extensions.sdnnetwork.model.NetworkConnection;
import org.opennaas.extensions.sdnnetwork.model.Port;
import org.opennaas.extensions.vrf.model.L2Forward;
import org.opennaas.extensions.vrf.model.VRFRoute;
/**
*
* @author Josep Batallé (josep.batalle@i2cat.net)
*/
public class Utils {
static Log log = LogFactory.getLog(Utils.class);
public static int detectIPVersion(String ip){
if (Utils.isIPv4Address(ip)){
return 4;
} else if (Utils.isIpAddress(ip) == 6 ) {
return 6;
} else {
return 0;
}
}
/**
* Accepts an IPv4 address and returns of string of the form xxx.xxx.xxx.xxx
* ie 192.168.0.1
*
* @param ipAddress
* @return
*/
public static String intIPv4toString(int ipAddress) {
StringBuilder sb = new StringBuilder();
int result;
for (int i = 0; i < 4; ++i) {
result = (ipAddress >> ((3 - i) * 8)) & 0xff;
sb.append(Integer.valueOf(result).toString());
if (i != 3) {
sb.append(".");
}
}
return sb.toString();
}
/**
* Accepts an IPv4 address of the form xxx.xxx.xxx.xxx, ie 192.168.0.1 and
* returns the corresponding 32 bit integer.
*
* @param ipAddress
* @return
*/
public static int StringIPv4toInt(String ipAddress) {
if (ipAddress == null) {
throw new IllegalArgumentException("Specified IPv4 address must"
+ "contain 4 sets of numerical digits separated by periods");
}
String[] octets = ipAddress.split("\\.");
if (octets.length != 4) {
throw new IllegalArgumentException("Specified IPv4 address must"
+ "contain 4 sets of numerical digits separated by periods");
}
int result = 0;
for (int i = 0; i < 4; ++i) {
int oct = Integer.valueOf(octets[i]);
if (oct > 255 || oct < 0) {
throw new IllegalArgumentException("Octet values in specified"
+ " IPv4 address must be 0 <= value <= 255");
}
result |= oct << ((3 - i) * 8);
}
return result;
}
/**
* Is an IPv4 address received from the controller. In integer format
*
* @param ipAddress
* @return
*/
public static boolean isIPv4Address(String ipAddress) {
return !ipAddress.contains(":");
}
public static String tryToCompressIPv6(String ipv6) {
if (ipv6.contains("::")) {
return ipv6;
} else {
return ipv6.replaceFirst(":0:", "::");
}
}
/**
* Check the IP version of the address
*
* @param ipAddress
* @return IP version of the address
*/
public static int isIpAddress(String ipAddress) {
Pattern VALID_IPV4_PATTERN = null;
Pattern VALID_IPV6_PATTERN = null;
String ipv4Pattern = "(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])";
final String ipv6Pattern = "([0-9a-f]{1,4}:){7}([0-9a-f]){1,4}";
final String IPV6_HEX4DECCOMPRESSED_REGEX = "\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z";
final String IPV6_6HEX4DEC_REGEX = "\\A((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z";
final String IPV6_HEXCOMPRESSED_REGEX = "\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\z";
final String IPV6_REGEX = "\\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\z";
try {
VALID_IPV4_PATTERN = Pattern.compile(ipv4Pattern, Pattern.CASE_INSENSITIVE);
VALID_IPV6_PATTERN = Pattern.compile(IPV6_HEXCOMPRESSED_REGEX, Pattern.CASE_INSENSITIVE);
} catch (PatternSyntaxException e) {
//logger.severe("Unable to compile pattern", e);
}
Matcher m1 = VALID_IPV4_PATTERN.matcher(ipAddress);
if (m1.matches()) {
return 4;
}
Matcher m2 = VALID_IPV6_PATTERN.matcher(ipAddress);
if (m2.matches()) {//Compressed
return 6;
}
//NO match. Try to find with other patterns
VALID_IPV6_PATTERN = Pattern.compile(IPV6_REGEX, Pattern.CASE_INSENSITIVE);
m2 = VALID_IPV6_PATTERN.matcher(ipAddress);
if (m2.matches()) {
return 6;
}
VALID_IPV6_PATTERN = Pattern.compile(ipv6Pattern, Pattern.CASE_INSENSITIVE);
m2 = VALID_IPV6_PATTERN.matcher(ipAddress);
if (m2.matches()) {
return 6;
}
VALID_IPV6_PATTERN = Pattern.compile(IPV6_HEX4DECCOMPRESSED_REGEX, Pattern.CASE_INSENSITIVE);
m2 = VALID_IPV6_PATTERN.matcher(ipAddress);
if (m2.matches()) {
return 6;
}
VALID_IPV6_PATTERN = Pattern.compile(IPV6_6HEX4DEC_REGEX, Pattern.CASE_INSENSITIVE);
m2 = VALID_IPV6_PATTERN.matcher(ipAddress);
if (m2.matches()) {
return 6;
}
return 0;
}
/**
* Usually addr1 is subnet address and addr2 is ip address. Function will
* return true, if addr2 is within addr1(subnet), or if addr1 and addr2 are
* equals
*
* @param addr1 Subnet address
* @param addr2 Subnet address or destination address
* @return
*/
public static boolean netMatch(String addr1, String addr2) {
TreeMap<String, Integer> subnet1 = extractIPandMask(addr1);
TreeMap<String, Integer> subnet2 = extractIPandMask(addr2);
if (subnet1.containsValue(0) && subnet2.containsValue(0)) {
if (subnet1.containsKey(subnet2.lastKey())) {
return true;
}
}
Inet4Address a1 = null;
Inet4Address a2 = null;
try {
a1 = (Inet4Address) InetAddress.getByName(subnet1.lastKey());
a2 = (Inet4Address) InetAddress.getByName(subnet2.lastKey());
} catch (UnknownHostException e) {
}
byte[] b = a1.getAddress();
int ipInt = ((b[0] & 0xFF) << 24)
| ((b[1] & 0xFF) << 16)
| ((b[2] & 0xFF) << 8)
| ((b[3] & 0xFF) << 0);
byte[] b1 = a2.getAddress();
int ipInt1 = ((b1[0] & 0xFF) << 24)
| ((b1[1] & 0xFF) << 16)
| ((b1[2] & 0xFF) << 8)
| ((b1[3] & 0xFF) << 0);
int mask = ~((1 << (32 - subnet1.get(subnet1.lastKey()))) - 1);
if ((ipInt & mask) == (ipInt1 & mask)) {
return true;
} else {
return false;
}
}
/**
* Extract the IP and the mask given an IP address
*
* @param addr in String format (e.g. 192.168.1.0/24 or 192.168.1.50)
* @return
*/
public static TreeMap<String, Integer> extractIPandMask(String addr) {
TreeMap<String, Integer> subnet = new TreeMap<String, Integer>();
String[] parts = addr.split("/");
String ip = parts[0];
int mask;
if (parts.length < 2) {
mask = 0;
} else {
mask = Integer.parseInt(parts[1]);
}
subnet.put(ip, mask);
return subnet;
}
/**
* The string is a subnet address, contains the mask
*
* @param addr
* @return The mask of the given ip subnet
*/
public static int isSubnetAddress(String addr) {
String[] parts = addr.split("/");
if (parts.length < 2) {
return 0;
} else {
return Integer.parseInt(parts[1]);
}
}
/**
* Mapping VRFRoute to Route used by SDN-OF module of OpenNaaS Layer3 to
* layer2
*
* @param route
* @return
*/
public static NetworkConnection VRFRouteToNetCon(VRFRoute route) {
NetworkConnection netCon = new NetworkConnection();
// netCon.setId();//internal-id, nom del flow que guarda el floodlight
netCon.setName(String.valueOf(route.getId()));//user friendly name
Port port = new Port();
port.setDeviceId(route.getSwitchInfo().getDPID());
port.setPortNumber(String.valueOf(route.getSwitchInfo().getInputPort()));
netCon.setSource(port);
port.setDeviceId(route.getSwitchInfo().getDPID());
port.setPortNumber(String.valueOf(route.getSwitchInfo().getOutputPort()));
netCon.setDestination(port);
return netCon;
}
/**
* Mapping VRFRoute to Floodlight Flow used by SDN-OF module of OpenNaaS
* Layer3 to layer2
*
* @param route
* @param etherType
* @return
*/
public static FloodlightOFFlow VRFRouteToFloodlightFlow(VRFRoute route, String etherType) {
FloodlightOFFlow flow = new FloodlightOFFlow();
FloodlightOFMatch match = new FloodlightOFMatch();
List<FloodlightOFAction> listActions = new ArrayList<FloodlightOFAction>();
FloodlightOFAction action = new FloodlightOFAction();
match.setSrcIp(route.getSourceAddress());
match.setDstIp(route.getDestinationAddress());
match.setEtherType(etherType);
// match.setIngressPort(String.valueOf(route.getSwitchInfo().getInputPort()));
action.setType(FloodlightOFAction.TYPE_OUTPUT);
action.setValue(String.valueOf(route.getSwitchInfo().getOutputPort()));
listActions.add(action);
flow.setActions(listActions);
flow.setActive(true);
flow.setMatch(match);
// flow.setName(String.valueOf(route.getId())+"-"+etherType+"-"+route.getSourceAddress()+"-"+route.getDestinationAddress());
flow.setName(createFlowName(String.valueOf(route.getId()), etherType, route.getSourceAddress(), route.getDestinationAddress(), route.getSwitchInfo().getDPID()));
flow.setSwitchId(route.getSwitchInfo().getDPID());
return flow;
}
public static String createFlowName(String id, String ethType, String source, String target, String dpid) {
// log.error("SETNAME FLOW: 0-" + ethType + "-" + source + "-" + target + "-" + dpid.substring(dpid.length() - 2));
// return id+"-"+ethType+"-"+source+"-"+target+"-" + dpid.substring(dpid.length() - 2);
source = source.replace("/", "-");
target = target.replace("/", "-");
dpid = dpid.substring(dpid.length() - 4).replace(":", "-");
return "0-" + ethType + "-" + source + "-" + target + "-" + dpid;
}
/**
* Copy InputStream to OutputStream (file).
*
* @param is
* @param os
* @throws IOException
*/
public static void copyStream(InputStream is, OutputStream os) throws IOException {
int BUFFER_SIZE = 16384;
byte[] buf = new byte[BUFFER_SIZE];
while (true) {
int len = is.read(buf);
if (len == -1) {
return;
}
os.write(buf, 0, len);
}
}
/**
* To convert the InputStream to String we use the Reader.read(char[]
* buffer) method. We iterate until the Reader return -1 which means there's
* no more data to read. We use the StringWriter class to produce the
* string.
*
* @param is
* @return The string that contains the value of the inputstream
* @throws IOException
*/
public static String convertStreamToString(InputStream is) throws IOException {
int BUFFER_SIZE = 1024;
if (is != null) {
Writer writer = new StringWriter();
char[] buffer = new char[BUFFER_SIZE];
try {
Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
} finally {
is.close();
}
return writer.toString();
} else {
return "";
}
}
/**
* Mapping VRFRoute to Floodlight Flow used by SDN-OF module of OpenNaaS
* Layer3 to layer2
*
* @param route
* @param etherType
* @return
*/
public static OFFlow VRFRouteToOFFlow(VRFRoute route, String etherType) {
OFFlow flow = new OFFlow();
FloodlightOFMatch match = new FloodlightOFMatch();
List<FloodlightOFAction> listActions = new ArrayList<FloodlightOFAction>();
FloodlightOFAction action = new FloodlightOFAction();
match.setSrcIp(route.getSourceAddress());
match.setDstIp(route.getDestinationAddress());
match.setEtherType(etherType);
match.setIngressPort(String.valueOf(route.getSwitchInfo().getInputPort()));
action.setType(FloodlightOFAction.TYPE_OUTPUT);
action.setValue(String.valueOf(route.getSwitchInfo().getOutputPort()));
listActions.add(action);
flow.setActions(listActions);
flow.setActive(true);
flow.setMatch(match);
// flow.setName(String.valueOf(route.getId())+"-"+etherType+"-"+route.getSourceAddress()+"-"+route.getDestinationAddress());
flow.setName(createFlowName(String.valueOf(route.getId()), etherType, route.getSourceAddress(), route.getDestinationAddress(), route.getSwitchInfo().getDPID()));
flow.setDPID(route.getSwitchInfo().getDPID());
return flow;
}
public static String intIPToString(String ip, int detectIPVersion) {
if(detectIPVersion == 4)
return Utils.intIPv4toString(Integer.parseInt(ip));
else if(detectIPVersion == 6){
return Utils.tryToCompressIPv6(ip);
}
return null;
}
public static String base64Encode(String stringToEncode) {
return DatatypeConverter.printBase64Binary(stringToEncode.getBytes());
}
/**
* Create a JSON string that contains the path.
* Format: [{ip:''},{dpid:'00:00'},{ip:''}]
* @param source
* @param switchDPID
* @param listOF
* @param target
* @return
*/
public static StringBuilder createJSONPath(String source, String switchDPID, List<OFFlow> listOF, String target) {
StringBuilder listFlows = new StringBuilder();
listFlows.append("[");
listFlows.append("{ip:'").append(source).append("'},");//source IP
if(switchDPID != null){
listFlows.append("{dpid:'").append(switchDPID).append("'},");//first switch id
}
for (int i = 0; i < listOF.size(); i++) {
if (i == 0) {
listFlows.append("{dpid:'");
listFlows.append(listOF.get(i).getDPID());
listFlows.append("'},");//others switch ids
}
for (int j = 0; j < i; j++) {
if (!listFlows.toString().contains(listOF.get(i).getDPID())) {
listFlows.append("{dpid:'");
listFlows.append(listOF.get(i).getDPID());
listFlows.append("'},");//others switch ids
}
}
}
listFlows.append("{ip:'").append(target).append("'}]");//final destination
return listFlows;
}
public static Response insertRoutesFromJSONFile(String content) {
String response = "Inserted";
List<VRFRoute> routes = new ArrayList<VRFRoute>();
try {
JsonFactory f = new MappingJsonFactory();
JsonParser jp = f.createJsonParser(content);
JsonToken current = jp.nextToken();
if (current != JsonToken.START_OBJECT) {
return Response.status(404).entity("Error: root should be object: quiting.").build();
}
while (jp.nextToken() != JsonToken.END_OBJECT) {
String fieldName = jp.getCurrentName();
current = jp.nextToken();// move from field name to field value
if (fieldName.equals("routeIPv4") || fieldName.equals("routeIPv6")) {
if (current == JsonToken.START_ARRAY) {
// For each of the records in the array
while (jp.nextToken() != JsonToken.END_ARRAY) {
// read the record into a tree model,
// this moves the parsing position to the end of it
JsonNode node = jp.readValueAsTree();
String field = jp.getCurrentName();
// And now we have random access to everything in the object
VRFRoute newRoute = new VRFRoute();
L2Forward newSwitch = new L2Forward();
newRoute.setSourceAddress(node.get("srcAddr").getValueAsText());
newRoute.setDestinationAddress(node.get("dstAddr").getValueAsText());
newSwitch.setInputPort(Integer.parseInt(node.get("swInfo").getPath("inPort").getValueAsText()));
newSwitch.setOutputPort(Integer.parseInt(node.get("swInfo").getPath("outPort").getValueAsText()));
newSwitch.setDPID(node.get("swInfo").getPath("dpid").getValueAsText());
newRoute.setSwitchInfo(newSwitch);
routes.add(newRoute);
}
} else {
response = "Error: records should be an array: skipping.";
jp.skipChildren();
}
} else {
response = "Unprocessed property: " + fieldName;
jp.skipChildren();
}
}
return Response.ok(routes).build();
} catch (IOException ex) {
Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, ex);
}
return Response.status(404).entity("Some error. Check the file. Possible error: " + response).build();
}
}