/*
* Copyright (c) 2013 Big Switch Networks, Inc.
*
* Licensed under the Eclipse Public License, Version 1.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package org.sdnplatform.netvirt.web;
import java.util.List;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFPacketOut;
import org.openflow.protocol.OFType;
import org.openflow.protocol.OFPacketIn.OFPacketInReason;
import org.openflow.util.HexString;
import org.restlet.data.Status;
import org.restlet.resource.Post;
import org.restlet.resource.ServerResource;
import org.sdnplatform.core.ListenerContext;
import org.sdnplatform.core.IControllerService;
import org.sdnplatform.core.IOFSwitch;
import org.sdnplatform.devicemanager.IDevice;
import org.sdnplatform.devicemanager.IDeviceService;
import org.sdnplatform.devicemanager.SwitchPort;
import org.sdnplatform.netvirt.core.NetVirtExplainPacket;
import org.sdnplatform.packet.Data;
import org.sdnplatform.packet.Ethernet;
import org.sdnplatform.packet.ICMP;
import org.sdnplatform.packet.IPv4;
import org.sdnplatform.packet.TCP;
import org.sdnplatform.packet.UDP;
import org.sdnplatform.routing.IRoutingDecision;
import org.sdnplatform.topology.NodePortTuple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Explain the policy decisions on a packet specified by the posted packet
* parameters
* @author readams
*/
public class ExplainResource extends ServerResource {
protected static Logger logger =
LoggerFactory.getLogger(ExplainResource.class);
byte[] ExplainPktSerialized;
/**
* The input packet parameters to be received via POST
* @author readams, subrata
*/
public static class PacketParameters {
// Layer 2 parameters
protected String destinationMACAddress = "00:00:00:00:00:00";
protected String sourceMACAddress = "00:00:00:00:00:00";
protected String priorityCode = "0";
protected String vlanID = Integer.toString(Ethernet.VLAN_UNTAGGED);
protected String etherType = Integer.toString(Ethernet.TYPE_IPv4);
// Layer 3 parameters
protected String sourceIpAddress = "0.0.0.0";
protected String destinationIpAddress = "0.0.0.0";
protected String ipv4Protocol = Integer.toString(IPv4.PROTOCOL_TCP);
// Layer 4 parameters
protected String ipL4SourcePort = "0";
protected String ipL4DestinationPort = "0";
// Switch and port from where the packet would have come
protected String srcSwitchDpid = "00:00:00:00:00:00:00:00";
protected String srcSwitchInPort = "0";
protected String dstSwitchDpid = "00:00:00:00:00:00:00:00";
protected String dstSwitchInPort = "0";
// setters and getters used by jackson.
public String getDestinationMACAddress() {
return destinationMACAddress;
}
public void setDestinationMACAddress(String destinationMACAddress) {
this.destinationMACAddress = destinationMACAddress;
}
public String getSourceMACAddress() {
return sourceMACAddress;
}
public void setSourceMACAddress(String sourceMACAddress) {
this.sourceMACAddress = sourceMACAddress;
}
public String getPriorityCode() {
return priorityCode;
}
public void setPriorityCode(String priorityCode) {
this.priorityCode = priorityCode;
}
public String getVlanID() {
return vlanID;
}
public void setVlanID(String vlanID) {
this.vlanID = vlanID;
}
public String getEtherType() {
return etherType;
}
public void setEtherType(String etherType) {
this.etherType = etherType;
}
public String getSourceIpAddress() {
return sourceIpAddress;
}
public void setSourceIpAddress(String sourceIpAddress) {
this.sourceIpAddress = sourceIpAddress;
}
public String getDestinationIpAddress() {
return destinationIpAddress;
}
public void setDestinationIpAddress(String destinationIpAddress) {
this.destinationIpAddress = destinationIpAddress;
}
public String getIpv4Protocol() {
return ipv4Protocol;
}
public void setIpv4Protocol(String ipv4Protocol) {
this.ipv4Protocol = ipv4Protocol;
}
public String getIpL4SourcePort() {
return ipL4SourcePort;
}
public void setIpL4SourcePort(String ipL4SourcePort) {
this.ipL4SourcePort = ipL4SourcePort;
}
public String getIpL4DestinationPort() {
return ipL4DestinationPort;
}
public void setIpL4DestinationPort(String ipL4DestinationPort) {
this.ipL4DestinationPort = ipL4DestinationPort;
}
public String getSrcSwitchDpid() {
return srcSwitchDpid;
}
public void setSrcSwitchDpid(String srcSwitchDpid) {
this.srcSwitchDpid = srcSwitchDpid;
}
public String getSrcSwitchInPort() {
return srcSwitchInPort;
}
public void setSrcSwitchInPort(String inPort) {
this.srcSwitchInPort = inPort;
}
public String getDstSwitchDpid() {
return dstSwitchDpid;
}
public void setDstSwitchDpid(String dstSwitchDpid) {
this.dstSwitchDpid = dstSwitchDpid;
}
public String getDstSwitchInPort() {
return dstSwitchInPort;
}
public void setDstSwitchInPort(String dstSwitchInPort) {
this.dstSwitchInPort = dstSwitchInPort;
}
@Override
public String toString() {
String vlanStr;
if (vlanID.equals("-1")) {
vlanStr = "Untagged";
} else {
vlanStr = vlanID;
}
return "[Dst. MAC Addr.="
+ destinationMACAddress + ", Src MAC Addr.="
+ sourceMACAddress + ", Priority=" + priorityCode
+ ", VLAN=" + vlanStr + ", Ether Type=" + etherType
+ ", Src IP Addr.=" + sourceIpAddress
+ ", Dst IP Addr.=" + destinationIpAddress
+ ", IP Protocol=" + ipv4Protocol + ", L4 Src Port="
+ ipL4SourcePort + ", L4 Dest Port="
+ ipL4DestinationPort + ", Src Switch DPID=" + srcSwitchDpid
+ ", Src Switch Input Port=" + srcSwitchInPort + "]";
}
}
/**
* The output model that contains the result of the explain
* @author readams
*/
public static class ExplainOutput {
public static class Acl {
protected String aclName;
protected String aclEntry;
protected String aclResult;
public String getAclName() {
return aclName;
}
public void setAclName(String aclName) {
this.aclName = aclName;
}
public String getAclEntry() {
return aclEntry;
}
public void setAclEntry(String aclEntry) {
this.aclEntry = aclEntry;
}
public String getAclResult() {
return aclResult;
}
public void setAclResult(String aclResult) {
this.aclResult = aclResult;
}
}
public static class ExpRoute {
public static class RouteEntry {
String dpid;
short inPort;
short outPort;
public RouteEntry(String dpid, short inPort, short outPort) {
this.dpid = dpid;
this.inPort = inPort;
this.outPort = outPort;
}
public String getDpid() {
return dpid;
}
public void setDpid(String dpid) {
this.dpid = dpid;
}
public short getInPort() {
return inPort;
}
public void setInPort(short inPort) {
this.inPort = inPort;
}
public short getOutPort() {
return outPort;
}
public void setOutPort(short outPort) {
this.outPort = outPort;
}
}
protected long clusterNum;
protected int numSwitches;
protected RouteEntry[] path;
public long getClusterNum() {
return clusterNum;
}
public void setClusterNum(long clusterNum) {
this.clusterNum = clusterNum;
}
public int getNumSwitches() {
return numSwitches;
}
public void setNumSwitches(int numSwitches) {
this.numSwitches = numSwitches;
}
public RouteEntry[] getPath() {
return path;
}
public void setPath(RouteEntry[] path) {
this.path = path;
}
}
public static class ExpVRouting {
private String srcIface;
private String dstIface;
private String action;
private String dropReason;
private String nextHopIp;
private String nextHopGatewayPool;
public String getSrcIface() {
return srcIface;
}
public void setSrcIface(String srcIface) {
this.srcIface = srcIface;
}
public String getDstIface() {
return dstIface;
}
public void setDstIface(String dstIface) {
this.dstIface = dstIface;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public String getDropReason() {
return dropReason;
}
public void setDropReason(String dropReason) {
this.dropReason = dropReason;
}
public String getNextHopIp() {
return nextHopIp;
}
public void setNextHopIp(String nextHopIp) {
this.nextHopIp = nextHopIp;
}
public String getNextHopGatewayPool() {
return nextHopGatewayPool;
}
public void setNextHopGatewayPool(String nextHopGatewayPool) {
this.nextHopGatewayPool = nextHopGatewayPool;
}
}
private PacketParameters ExplainPktParams;
private String Status;
private String Explanation;
/* Number of times virtual routing was performed */
private int numVRIterations;
private ExpVRouting[] expPktVRouting;
private String srcNetVirtName;
private String destNetVirtName;
private Acl inputAcl;
private Acl outputAcl;
private int numClusters;
private String routingAction;
private ExpRoute[] expPktRoute;
private String serviceName;
private String serviceNode;
public String getServiceName() {
return serviceName;
}
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
public String getServiceNode() {
return serviceNode;
}
public void setServiceNode(String serviceNode) {
this.serviceNode = serviceNode;
}
public PacketParameters getExplainPktParams() {
return ExplainPktParams;
}
public void setExplainPktParams(PacketParameters explainPktParams) {
ExplainPktParams = explainPktParams;
}
public String getSrcNetVirtName() {
return srcNetVirtName;
}
public void setSrcNetVirtName(String srcNetVirtName) {
this.srcNetVirtName = srcNetVirtName;
}
public String getDestNetVirtName() {
return destNetVirtName;
}
public void setDestNetVirtName(String destNetVirtName) {
this.destNetVirtName = destNetVirtName;
}
public Acl getInputAcl() {
return inputAcl;
}
public void setInputAcl(Acl inputAcl) {
this.inputAcl = inputAcl;
}
public Acl getOutputAcl() {
return outputAcl;
}
public void setOutputAcl(Acl outputAcl) {
this.outputAcl = outputAcl;
}
public int getNumClusters() {
return numClusters;
}
public void setNumClusters(int numClusters) {
this.numClusters = numClusters;
}
public ExpRoute[] getExpPktRoute() {
return expPktRoute;
}
public void setExpPktRoute(ExpRoute[] expPktRoute) {
this.expPktRoute = expPktRoute;
}
public String getRoutingAction() {
return routingAction;
}
public void setRoutingAction(String routingAction) {
this.routingAction = routingAction;
}
public String getStatus() {
return Status;
}
public void setStatus(String status) {
Status = status;
}
public String getExplanation() {
return Explanation;
}
public void setExplanation(String explanation) {
Explanation = explanation;
}
public ExplainOutput() {
inputAcl = new Acl();
outputAcl = new Acl();
}
public int getNumVRIterations() {
return numVRIterations;
}
public void setNumVRIterations(int numVRIterations) {
this.numVRIterations = numVRIterations;
}
public ExpVRouting[] getExpPktVRouting() {
return expPktVRouting;
}
public void setExpPktVRouting(ExpVRouting[] expPktVRouting) {
this.expPktVRouting = expPktVRouting;
}
}
/**
*
* @param param: Input parameters of packet fields
* @return: packet-in to be injected in the pkt processing chain
*/
private OFPacketIn CreateExplainPacket(PacketParameters param,
IControllerService controllerProvider) {
Ethernet ExplainPkt = new Ethernet();
ExplainPkt.setSourceMACAddress(param.sourceMACAddress);
ExplainPkt.setDestinationMACAddress(param.destinationMACAddress);
ExplainPkt.setEtherType(Short.parseShort(param.etherType));
ExplainPkt.setVlanID(Short.parseShort(param.vlanID));
ExplainPkt.setPriorityCode(Byte.parseByte(param.priorityCode));
switch (Short.parseShort(param.etherType)) {
case Ethernet.TYPE_IPv4:
IPv4 ipv4Pkt = new IPv4();
ExplainPkt.setPayload(ipv4Pkt);
ipv4Pkt.setProtocol(Byte.parseByte(param.ipv4Protocol));
ipv4Pkt.setSourceAddress(param.sourceIpAddress);
ipv4Pkt.setDestinationAddress(param.destinationIpAddress);
ipv4Pkt.setTtl((byte) 255);
switch (Byte.parseByte(param.ipv4Protocol)) {
case IPv4.PROTOCOL_TCP:
TCP tcpPkt = new TCP();
ipv4Pkt.setPayload(tcpPkt);
tcpPkt.setSourcePort(
Short.parseShort(param.ipL4SourcePort));
tcpPkt.setDestinationPort(
Short.parseShort(param.ipL4DestinationPort));
break;
case IPv4.PROTOCOL_UDP:
UDP udpPkt = new UDP();
ipv4Pkt.setPayload(udpPkt);
udpPkt.setSourcePort(
Short.parseShort(param.ipL4SourcePort));
udpPkt.setDestinationPort(
Short.parseShort(param.ipL4DestinationPort));
break;
case IPv4.PROTOCOL_ICMP:
ICMP icmpPkt = new ICMP();
ipv4Pkt.setPayload(icmpPkt);
icmpPkt.setIcmpType((byte) 8); // Type 8 = Echo
icmpPkt.setIcmpCode((byte) 0);
icmpPkt.setPayload(new Data(new byte[] {1, 2 , 3}));
break;
}
ExplainPktSerialized = ExplainPkt.serialize();
break;
case Ethernet.TYPE_ARP:
// TBD
// ARP arpPkt = new ARP();
// ExplainPkt.setPayload(arpPkt);
// Set the ARP pkt payload - TBD
// TBD
break;
}
OFPacketIn ExplainPacketIn =
(OFPacketIn) controllerProvider.getOFMessageFactory().
getMessage(OFType.PACKET_IN);
ExplainPacketIn.setInPort(Short.parseShort(param.srcSwitchInPort));
ExplainPacketIn.setBufferId(OFPacketOut.BUFFER_ID_NONE);
ExplainPacketIn.setReason(OFPacketInReason.NO_MATCH);
ExplainPacketIn.setPacketData(ExplainPktSerialized);
ExplainPacketIn.setTotalLength((short) ExplainPktSerialized.length); // TODO - shouodn't this be minimum plus the serialized?
// ExplainPacketIn.setLength((short)OFPacketIn.MINIMUM_LENGTH);
return ExplainPacketIn;
}
@Post("json")
public ExplainOutput handle(PacketParameters params) {
IControllerService controllerProvider =
(IControllerService)getContext().getAttributes().
get(IControllerService.class.getCanonicalName());
// Get the device manager handle
IDeviceService deviceManager =
(IDeviceService)getContext().getAttributes().
get(IDeviceService.class.getCanonicalName());
return handle(params, controllerProvider, deviceManager);
}
public ExplainOutput handle(PacketParameters params,
IControllerService controllerProvider,
IDeviceService deviceManager) {
ExplainOutput output = new ExplainOutput();
output.setExplainPktParams(params);
// Get latest, unblocked attachment point if source switch was not
// specified
if (HexString.toLong(params.srcSwitchDpid) == 0) {
long srcMac = HexString.toLong(params.sourceMACAddress);
Short vlan = (params.vlanID != null)
? Short.valueOf(params.vlanID)
: null;
Integer ip = (params.sourceIpAddress != null)
? IPv4.toIPv4Address(params.sourceIpAddress)
: null;
Long dpid = (params.srcSwitchDpid != null)
? HexString.toLong(params.srcSwitchDpid)
: null;
Integer port = (params.srcSwitchInPort != null)
? Integer.valueOf(params.srcSwitchInPort)
: null;
IDevice srcDev = deviceManager.findDevice(srcMac, vlan, ip, dpid, port);
if (srcDev == null) {
// Given Source device doesn't exist - return error
output.Status = "ERROR";
output.Explanation = "Source device not found";
setStatus(Status.SUCCESS_OK);
return output;
}
SwitchPort[] srcDaps = srcDev.getAttachmentPoints();
if (srcDaps == null || srcDaps.length == 0) {
// Given Source device's attachemnt point doesn't exist -
// return error
output.Status = "ERROR";
output.Explanation =
"No attachemnt point found for source device";
setStatus(Status.SUCCESS_OK);
return output;
}
params.srcSwitchInPort = Integer.toString(srcDaps[0].getPort());
params.srcSwitchDpid = Long.toString(srcDaps[0].getSwitchDPID());
}
// Check that the source switch is in the network
IOFSwitch ofSwitch = controllerProvider.getSwitches().get(
HexString.toLong(params.srcSwitchDpid));
if (ofSwitch == null) {
// Given Source Switch doesn't exist - return error
output.Status = "ERROR";
output.Explanation = "Source switch not found";
setStatus(Status.SUCCESS_OK);
return output;
}
// Create a new listener context
ListenerContext cntx = new ListenerContext();
// set appropriate context
NetVirtExplainPacket.ExplainStore.put(cntx,
NetVirtExplainPacket.KEY_EXPLAIN_PKT, NetVirtExplainPacket.VAL_EXPLAIN_PKT);
NetVirtExplainPacket.ExplainPktRoute epr = new NetVirtExplainPacket.ExplainPktRoute();
NetVirtExplainPacket.ExplainRouteStore.put(cntx,
NetVirtExplainPacket.KEY_EXPLAIN_PKT_ROUTE, epr);
NetVirtExplainPacket.ExplainPktVRouting vr =
new NetVirtExplainPacket.ExplainPktVRouting();
NetVirtExplainPacket.VRoutingStore.put(cntx,
NetVirtExplainPacket.KEY_EXPLAIN_PKT_VROUTING, vr);
// Create the packet
OFPacketIn ExplainPacketIn = CreateExplainPacket(params, controllerProvider);
// Inject the ExplainPacket into the processing chain
controllerProvider.injectOfMessage(ofSwitch, ExplainPacketIn, cntx);
// Prepare the output
String srcNetVirtName = NetVirtExplainPacket.ExplainStore.get(
cntx, NetVirtExplainPacket.KEY_EXPLAIN_PKT_SRC_NetVirt);
output.setSrcNetVirtName(srcNetVirtName);
String dstNetVirtName = NetVirtExplainPacket.ExplainStore.get(
cntx, NetVirtExplainPacket.KEY_EXPLAIN_PKT_DST_NetVirt);
output.setDestNetVirtName(dstNetVirtName);
String inAclName = NetVirtExplainPacket.ExplainStore.get(
cntx, NetVirtExplainPacket.KEY_EXPLAIN_PKT_INP_ACL_NAME);
output.inputAcl.setAclName(inAclName);
String outAclName = NetVirtExplainPacket.ExplainStore.get(
cntx, NetVirtExplainPacket.KEY_EXPLAIN_PKT_OUT_ACL_NAME);
output.outputAcl.setAclName(outAclName);
String inAclEntry = NetVirtExplainPacket.ExplainStore.get(
cntx, NetVirtExplainPacket.KEY_EXPLAIN_PKT_INP_ACL_ENTRY);
output.inputAcl.setAclEntry(inAclEntry);
String outAclEntry = NetVirtExplainPacket.ExplainStore.get(
cntx, NetVirtExplainPacket.KEY_EXPLAIN_PKT_OUT_ACL_ENTRY);
output.outputAcl.setAclEntry(outAclEntry);
String inAclResult = NetVirtExplainPacket.ExplainStore.get(
cntx, NetVirtExplainPacket.KEY_EXPLAIN_PKT_INP_ACL_RESULT);
output.inputAcl.setAclResult(inAclResult);
String outAclResult = NetVirtExplainPacket.ExplainStore.get(
cntx, NetVirtExplainPacket.KEY_EXPLAIN_PKT_OUT_ACL_RESULT);
output.outputAcl.setAclResult(outAclResult);
String serviceNameResult = NetVirtExplainPacket.ExplainStore.get(
cntx, NetVirtExplainPacket.KEY_EXPLAIN_PKT_SERVICE_NAME);
output.setServiceName(serviceNameResult);
String serviceNodeResult = NetVirtExplainPacket.ExplainStore.get(
cntx, NetVirtExplainPacket.KEY_EXPLAIN_PKT_SERVICE_NODE);
output.setServiceNode(serviceNodeResult);
IRoutingDecision vrd;
vrd = IRoutingDecision.rtStore.get(
cntx, IRoutingDecision.CONTEXT_DECISION);
if (vrd != null) {
output.setRoutingAction(vrd.getRoutingAction().toString());
} else {
String ExpPktAction = NetVirtExplainPacket.ExplainStore.get(cntx,
NetVirtExplainPacket.KEY_EXPLAIN_PKT_ACTION);
output.setRoutingAction(ExpPktAction);
}
NetVirtExplainPacket.ExplainPktVRouting vrOut =
NetVirtExplainPacket.VRoutingStore.
get(cntx, NetVirtExplainPacket.KEY_EXPLAIN_PKT_VROUTING);
if (vrOut != null) {
output.numVRIterations = vrOut.numIterations;
output.expPktVRouting =
new ExplainOutput.ExpVRouting[output.numVRIterations];
for (int i = 0; i < output.numVRIterations; i++) {
ExplainOutput.ExpVRouting evr = new ExplainOutput.ExpVRouting();
evr.srcIface = vrOut.arr.get(i).srcIface.getName();
if (vrOut.arr.get(i).dstIface != null)
evr.dstIface = vrOut.arr.get(i).dstIface.getName();
else
evr.dstIface = "";
evr.action = vrOut.arr.get(i).act.getAction().toString();
evr.nextHopIp = IPv4.fromIPv4Address(vrOut.arr.get(i).act.
getNextHopIp());
if (vrOut.arr.get(i).act.getNextHopGatewayPool() != null &&
vrOut.arr.get(i).act.getNextHopGatewayPoolRouter() != null) {
evr.nextHopGatewayPool = new StringBuilder()
.append(vrOut.arr.get(i).act.
getNextHopGatewayPoolRouter().getName())
.append("|")
.append(vrOut.arr.get(i).act.getNextHopGatewayPool())
.toString();
}
evr.dropReason = vrOut.arr.get(i).act.getReasonStr();
output.expPktVRouting[i] = evr;
}
}
NetVirtExplainPacket.ExplainPktRoute eprOut =
NetVirtExplainPacket.ExplainRouteStore.get(
cntx, NetVirtExplainPacket.KEY_EXPLAIN_PKT_ROUTE);
if (eprOut != null) {
output.numClusters = eprOut.numClusters;
// Check for errors
if (output.numClusters == 0) {
output.Status = "ERROR";
output.Explanation = "Route not found";
setStatus(Status.SUCCESS_OK);
return output;
}
output.expPktRoute = new ExplainOutput.ExpRoute[output.numClusters];
for (int idx=0; idx < eprOut.numClusters; idx++) {
int pathSize=0;
output.expPktRoute[idx] = new ExplainOutput.ExpRoute();
(output.expPktRoute[idx]).clusterNum = eprOut.oc.get(idx).clusterNumber;
//short srcPort = eprOut.oc[idx].srcDap.getSwitchPort().getPort();
//long srcDpid = eprOut.oc[idx].srcDap.getSwitchPort().getSw().getId();
//short dstPort = eprOut.oc[idx].dstDap.getSwitchPort().getPort();
//long dstDpid = eprOut.oc[idx].dstDap.getSwitchPort().getSw().getId();
// path size in terms of number of switches
pathSize = eprOut.oc.get(idx).route.getPath().size();
output.expPktRoute[idx].numSwitches = pathSize/2;
output.expPktRoute[idx].path =
new ExplainOutput.ExpRoute.RouteEntry[pathSize/2];
// Add the source switch to the route
if ((eprOut.oc.get(idx).srcDap != null) ||
(eprOut.oc.get(idx).dstDap != null)) {
List<NodePortTuple> nptList =
eprOut.oc.get(idx).route.getPath();
int pidx = 0;
for (int x = 0; x < pathSize; x=x+2) {
// x and x+1 will have the same switch
long dpid = nptList.get(x).getNodeId();
short inPort = nptList.get(x).getPortId();
short outPort = nptList.get(x+1).getPortId();
output.expPktRoute[idx].path[pidx] =
new ExplainOutput.ExpRoute.RouteEntry(
HexString.toHexString(dpid), inPort, outPort);
pidx++;
}
}
}
}
setStatus(Status.SUCCESS_OK);
output.Status = "SUCCESS";
output.Explanation = "OK";
return output;
}
}