package net.floodlightcontroller.nfv;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.packet.BasePacket;
import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.packet.IPv4;
import net.floodlightcontroller.restserver.IRestApiService;
import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService;
import org.openflow.protocol.OFMatch;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFPacketOut;
import org.openflow.protocol.OFType;
import org.openflow.protocol.action.OFAction;
import org.openflow.protocol.action.OFActionOutput;
import org.restlet.data.ChallengeScheme;
import org.restlet.data.MediaType;
import org.restlet.representation.Representation;
import org.restlet.resource.ClientResource;
import org.restlet.resource.ResourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Josep Batalle (josep.batalle@i2cat.net)
*
*/
public class NfvRouting implements NfvRoutingService, IOFMessageListener, IFloodlightModule {
protected IFloodlightProviderService floodlightProvider;
protected IStaticFlowEntryPusherService staticFlowEntryPusher;
protected static Logger logger;
protected String urlRouting = null;
protected String portRouting = null;
protected Boolean proactive = false;
protected String user = "admin";
protected String password = "123456";
protected IRestApiService restApi;
@Override
public String getName() {
return NfvRouting.class.getSimpleName();
}
@Override
public boolean isCallbackOrderingPrereq(OFType type, String name) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isCallbackOrderingPostreq(OFType type, String name) {
// TODO Auto-generated method stub
return false;
}
@Override
public net.floodlightcontroller.core.IListener.Command receive(
IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
BasePacket pkt = (BasePacket) IFloodlightProviderService.bcStore.get(
cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
// Instantiate two objects for OFMatch and OFPacketIn
OFPacketIn pin = (OFPacketIn) msg;
//logger.info("OF Version: "+msg.getDataAsString(sw, msg, cntx));
OFMatch match = new OFMatch();
match.loadFromPacket(pin.getPacketData(), pin.getInPort());
short receivedOutPort = 0;
switch (msg.getType()) {
case PACKET_IN:
if (match.getDataLayerType() == (short) 0x800 || match.getDataLayerType() == (short) 0x806) {
logger.info("Network Source: "+Integer.toString(match.getNetworkSource()));
logger.info(Integer.toString(match.getNetworkDestinationMaskLen()));
long initialTime = System.currentTimeMillis();
//logger.info(Short.valueOf(match.getDataLayerSource()));
logger.info("Packet IN detected..."+match.getDataLayerType());
if (match.getNetworkSource() != 0 && match.getNetworkDestination() != 0) {
// String url = "http://"+user+":"+password+"@" + urlRouting + ":" + portRouting + "/"
String url = "http://" + urlRouting + ":" + portRouting + "/"
+ "opennaas/vrf/routing/route/"
+ match.getNetworkSource() + "/"
+ match.getNetworkDestination() + "/"
+ sw.getStringId().toString() + "/"
+ match.getInputPort();
logger.info("OpenNaaS URL: " + url);
ClientResource service = new ClientResource(url);
service.setChallengeResponse(ChallengeScheme.HTTP_BASIC, user, password);
String response = "";
try {
Representation string = service.get(MediaType.TEXT_PLAIN);
response = string.getText();
logger.debug("Response: "+response);
receivedOutPort = Short.valueOf(response.split(":")[0]);
} catch (IOException e) {
logger.error("IOException "+e.getMessage());
e.printStackTrace();
} catch (ResourceException e) {
logger.error("ResourceException "+e.getMessage());
receivedOutPort = 0;
}catch (NullPointerException e) {
logger.error("NullPointerException "+e.getMessage());
receivedOutPort = 0;
}
if (receivedOutPort != 0) {
logger.debug("Source ip: " + IPv4.fromIPv4Address(match.getNetworkSource()));
logger.debug("Destination ip: " + IPv4.fromIPv4Address(match.getNetworkDestination()));
logger.debug("inputport: " + match.getInputPort());
logger.debug("mac: " + sw.getStringId());
configureFlows(match, sw, receivedOutPort);
}
}
}
/* IPv6 not supported yet
if (match.getDataLayerType() == (short) 0x86DD) {// IPv6
}
*/
//Send packet-out to switch
if (receivedOutPort != 0) {
OFPacketOut packetOutMessage = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
short packetOutLength = (short) OFPacketOut.MINIMUM_LENGTH; // starting
// length
// Set buffer_id, in_port, actions_len
packetOutMessage.setBufferId(pin.getBufferId());
packetOutMessage.setInPort(pin.getInPort());
packetOutMessage.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
packetOutLength += OFActionOutput.MINIMUM_LENGTH;
// set actions
List<OFAction> actions = new ArrayList<OFAction>(1);
actions.add(new OFActionOutput(Short.valueOf(receivedOutPort), (short) 0));
packetOutMessage.setActions(actions);
// set data - only if buffer_id == -1
if (pin.getBufferId() == OFPacketOut.BUFFER_ID_NONE) {
byte[] packetData = pin.getPacketData();
packetOutMessage.setPacketData(packetData);
packetOutLength += (short) packetData.length;
}
// finally, set the total length
packetOutMessage.setLength(packetOutLength);
try {
sw.write(packetOutMessage, null);
logger.info("write");
} catch (IOException e) {
logger.error("Failed to write {} to switch {}: {}",
new Object[] { packetOutMessage, sw, e });
}
}
break;
default:
break;
}
return Command.STOP;
}
private void configureFlows(OFMatch match, IOFSwitch sw, short receivedOutPort){
String name= "";
String dstIp = "";
short outP = 0;
String srcIp = "";
short inP = 0;
//output way
srcIp = IPv4.fromIPv4Address(match.getNetworkSource());
dstIp = IPv4.fromIPv4Address(match.getNetworkDestination());
name = createFlowName("2054", srcIp, dstIp, sw.getStringId().substring(sw.getStringId().length() - 2));
outP = receivedOutPort;
setJsonToSend(sw.getStringId(), name, "0x806", srcIp, dstIp, inP, outP);
name = createFlowName("2048", srcIp, dstIp, sw.getStringId().substring(sw.getStringId().length() - 2));
setJsonToSend(sw.getStringId(), name, "0x800", srcIp, dstIp, inP, outP);
srcIp = IPv4.fromIPv4Address(match.getNetworkDestination());
dstIp = IPv4.fromIPv4Address(match.getNetworkSource());
name = createFlowName("2054", srcIp, dstIp, sw.getStringId().substring(sw.getStringId().length() - 2));
outP = match.getInputPort();
setJsonToSend(sw.getStringId(), name, "0x806", srcIp, dstIp, inP, outP);
name = createFlowName("2048", srcIp, dstIp, sw.getStringId().substring(sw.getStringId().length() - 2));
setJsonToSend(sw.getStringId(), name, "0x800", srcIp, dstIp, inP, outP);
}
private String createFlowName(String type, String srcIp, String dstIp, String swId){
String name = "0-"+type+"-" + srcIp + "-" + dstIp + "-"+swId;
return name;
}
private String setJsonToSend(String mac, String name, String type, String SrcIp, String DstIp, short inP, short outP){
String json = "{\"switch\": \""+mac+"\", \"name\":\""+name+"\", \"ether-type\":\""+type+"\", \"dst-ip\":\""
+ DstIp+ "\" ,\"priority\":\"32767\",\"active\":\"true\", \"actions\":\"output="+outP+ "\"}";
if(!SrcIp.equals("")){
json = "{\"switch\": \""+mac+"\", \"name\":\""+name+"\", \"ether-type\":\""+type+"\", \"src-ip\":\""
+ SrcIp+ "\" , \"dst-ip\":\""+ DstIp+ "\" ,\"priority\":\"32767\",\"active\":\"true\", \"actions\":\"output="+outP+ "\"}";
}
staticFlowEntryPusher.addFlowFromJSON(name, json, mac);
return json;
}
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
l.add(NfvRoutingService.class);
return l;
}
@Override
public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
m.put(NfvRoutingService.class, this);
return m;
}
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
l.add(IFloodlightProviderService.class);
l.add(IStaticFlowEntryPusherService.class);
l.add(IRestApiService.class);
return l;
}
@Override
public void init(FloodlightModuleContext context) throws FloodlightModuleException {
floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
staticFlowEntryPusher = context.getServiceImpl(IStaticFlowEntryPusherService.class);
restApi = context.getServiceImpl(IRestApiService.class);
logger = LoggerFactory.getLogger(NfvRouting.class);
Map<String, String> configOptions = context.getConfigParams(this);
try {
String url = configOptions.get("url");
String port = configOptions.get("port");
String nfvType = configOptions.get("NFVType");
if (url != null) {
urlRouting = url;
}
if (port != null) {
portRouting = port;
}
if (proactive != null){
proactive = Boolean.valueOf(nfvType);
}
} catch (NumberFormatException e) {
logger.warn("Error parsing flow idle timeout, "
+ "using default of {} seconds", urlRouting);
}
}
@Override
public void startUp(FloodlightModuleContext context) {
floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
restApi.addRestletRoutable(new NfvRoutingWebRoutable());
}
@Override
public String getBuffer() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getUrlRouting() {
return urlRouting +":"+ portRouting;
}
@Override
public void setUrlRouting(String urlRouting, String portRouting) {
this.urlRouting = urlRouting;
this.portRouting = portRouting;
}
}