diff --git a/build.xml b/build.xml index bed6c6081d4964a5d764284d52b4dcfe6ad22b7a..2a0094bbd873f6b032265a003773d390f18d8f24 100644 --- a/build.xml +++ b/build.xml @@ -44,8 +44,8 @@ <property name="floodlight-test-jar" location="${target}/floodlight-test.jar"/> <property name="thrift.dir" value="${basedir}/src/main/thrift"/> <property name="thrift.out.dir" value="lib/gen-java"/> - <property name="ant.build.javac.source" value="1.6"/> - <property name="ant.build.javac.target" value="1.6"/> + <property name="ant.build.javac.source" value="1.7"/> + <property name="ant.build.javac.target" value="1.7"/> <property name="findbugs.home" value="../build/findbugs-2.0.2"/> <property name="findbugs.results" value="findbugs-results" /> <property name="lib" location="lib"/> @@ -75,6 +75,11 @@ <include name="findbugs-annotations-2.0.1.jar" /> <include name="findbugs-jsr305-2.0.1.jar" /> <include name="derby-10.9.1.0.jar"/> + <include name="openflowj-0.3.5-SNAPSHOT.jar"/> + <include name="openflowj-0.3.5-SNAPSHOT-javadoc.jar"/> + <include name="hamcrest-core-1.3.jar"/> + <include name="hamcrest-integration-1.3.jar"/> + <include name="hamcrest-library-1.3.jar"/> </patternset> <path id="classpath"> diff --git a/lib/openflowj-0.3.5-SNAPSHOT-javadoc.jar b/lib/openflowj-0.3.5-SNAPSHOT-javadoc.jar new file mode 100644 index 0000000000000000000000000000000000000000..49c3f4551b6588514f1b18581e274c688fbf5bce Binary files /dev/null and b/lib/openflowj-0.3.5-SNAPSHOT-javadoc.jar differ diff --git a/lib/openflowj-0.3.5-SNAPSHOT-sources.jar b/lib/openflowj-0.3.5-SNAPSHOT-sources.jar new file mode 100644 index 0000000000000000000000000000000000000000..5786ced23c2b240d20195b3acef6e18a3e676db5 Binary files /dev/null and b/lib/openflowj-0.3.5-SNAPSHOT-sources.jar differ diff --git a/lib/openflowj-0.3.5-SNAPSHOT-tests.jar b/lib/openflowj-0.3.5-SNAPSHOT-tests.jar new file mode 100644 index 0000000000000000000000000000000000000000..67646a8c35818bb017599121b16d7aff19718d9a Binary files /dev/null and b/lib/openflowj-0.3.5-SNAPSHOT-tests.jar differ diff --git a/lib/openflowj-0.3.5-SNAPSHOT.jar b/lib/openflowj-0.3.5-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..dcb33c10e467f75f74ea96062893d7608d6ce71c Binary files /dev/null and b/lib/openflowj-0.3.5-SNAPSHOT.jar differ diff --git a/src/main/java/net/floodlightcontroller/core/web/ControllerSwitchesResource.java b/src/main/java/net/floodlightcontroller/core/web/ControllerSwitchesResource.java index c04eb5eef1dad04b7918e52c5df4673fdee29505..d8564f5703ec17b30cf53642e3225dd7f549e9ca 100644 --- a/src/main/java/net/floodlightcontroller/core/web/ControllerSwitchesResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/ControllerSwitchesResource.java @@ -101,7 +101,7 @@ public class ControllerSwitchesResource extends ServerResource { } public String getDpid() { - return sw.getStringId(); + return sw.getId().toString(); } public String getHarole() { diff --git a/src/main/java/net/floodlightcontroller/core/web/CounterResource.java b/src/main/java/net/floodlightcontroller/core/web/CounterResource.java index 14fa1b1e347cc9c2c8652e673c5448483477aae8..c08e5b3aa4026a580d9e99abc162e92aeb9d7589 100644 --- a/src/main/java/net/floodlightcontroller/core/web/CounterResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/CounterResource.java @@ -31,7 +31,7 @@ import org.restlet.resource.Get; public class CounterResource extends CounterResourceBase { @Get("json") public Map<String, Object> retrieve() { - String counterTitle = + /*TODO @Ryan String counterTitle = (String) getRequestAttributes().get("counterTitle"); Map<String, Object> model = new HashMap<String,Object>(); long dc; @@ -61,6 +61,6 @@ public class CounterResource extends CounterResourceBase { model.put(counterTitle, v.getDouble()); } } - return model; + return model;*/ return null; } } diff --git a/src/main/java/net/floodlightcontroller/core/web/SwitchCounterCategoriesResource.java b/src/main/java/net/floodlightcontroller/core/web/SwitchCounterCategoriesResource.java index 98c2a73753f0cb3fe84597eac0a64b29d61999a3..66ba78641b569aacb6a4dedae26de4c2bf2551a2 100644 --- a/src/main/java/net/floodlightcontroller/core/web/SwitchCounterCategoriesResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/SwitchCounterCategoriesResource.java @@ -61,7 +61,7 @@ public class SwitchCounterCategoriesResource extends CounterResourceBase { String switchID, String counterName, String layer) { - String fullCounterName = ""; + /*TODO @Ryan String fullCounterName = ""; NetworkLayer nl = NetworkLayer.L3; try { @@ -78,6 +78,6 @@ public class SwitchCounterCategoriesResource extends CounterResourceBase { List<String> categories = this.counterStore.getAllCategories(fullCounterName, nl); if (categories != null) { model.put(fullCounterName + "." + layer, categories); - } + }*/ } } diff --git a/src/main/java/net/floodlightcontroller/core/web/SwitchCounterResource.java b/src/main/java/net/floodlightcontroller/core/web/SwitchCounterResource.java index 61667daee959a7788753bc065613a3bf59cae462..8f33337d8f696028bba9c2b3e930556c8b0abb97 100644 --- a/src/main/java/net/floodlightcontroller/core/web/SwitchCounterResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/SwitchCounterResource.java @@ -34,7 +34,7 @@ import net.floodlightcontroller.core.internal.IOFSwitchService; public class SwitchCounterResource extends CounterResourceBase { @Get("json") public Map<String, Object> retrieve() { - IOFSwitchService switchService = + /*TODO @Ryan IOFSwitchService switchService = (IOFSwitchService)getContext().getAttributes(). get(IOFSwitchService.class.getCanonicalName()); HashMap<String,Object> model = new HashMap<String,Object>(); @@ -52,12 +52,12 @@ public class SwitchCounterResource extends CounterResourceBase { } else { getOneSwitchCounterJson(model, switchID, counterName); } - return model; + return model;*/ return null; } protected void getOneSwitchCounterJson(Map<String, Object> model, String switchID, String counterName) { - String fullCounterName = ""; + /*TODO @Ryan String fullCounterName = ""; try { counterName = URLDecoder.decode(counterName, "UTF-8"); @@ -73,7 +73,7 @@ public class SwitchCounterResource extends CounterResourceBase { sample.put(counter.getCounterDate().toString(), counter.getCounterValue().getLong()); model.put(switchID, sample); - } + } */ } } diff --git a/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java b/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java index 63ccc7178552ed994abdce4785f06b94e4c50790..45d9bc12fefcf6c10dd683cb1e4f6bdc066bd9a0 100644 --- a/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java @@ -47,7 +47,7 @@ public class SwitchRoleResource extends ServerResource { if (switchId.equalsIgnoreCase("all")) { HashMap<String,RoleInfo> model = new HashMap<String,RoleInfo>(); for (IOFSwitch sw: switchService.getAllSwitchMap().values()) { - switchId = sw.getStringId(); + switchId = sw.getId().toString(); //TODO @Ryan not sure what the changeDescription string should be here. roleInfo = new RoleInfo(HARole.ofOFRole(sw.getControllerRole()), "", null); model.put(switchId, roleInfo); diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowReconcileManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowReconcileManager.java index 0ad0c7b69aed40ee547fb380df0a0e6e2ed0b02a..578a15e60d15cc41294155e052082b107b9562b2 100644 --- a/src/main/java/net/floodlightcontroller/flowcache/FlowReconcileManager.java +++ b/src/main/java/net/floodlightcontroller/flowcache/FlowReconcileManager.java @@ -362,7 +362,7 @@ public class FlowReconcileManager implements IFloodlightModule, IFlowReconcileSe * @return */ protected int getCurrentCapacity() { - int minFlows = MIN_FLOW_RECONCILE_PER_SECOND * FLOW_RECONCILE_DELAY_MILLISEC / 1000; + /*int minFlows = MIN_FLOW_RECONCILE_PER_SECOND * FLOW_RECONCILE_DELAY_MILLISEC / 1000; List<DebugCounterResource> contCtrRsrcList = debugCounterService.getModuleCounterValues(Controller.class.getName()); for (DebugCounterResource dcr : contCtrRsrcList) { @@ -398,11 +398,11 @@ public class FlowReconcileManager implements IFloodlightModule, IFlowReconcileSe if (logger.isTraceEnabled()) { logger.trace("Capacity is {}", capacity); } - return capacity; + return capacity; */ return 0; } protected int getPktInRate(DebugCounterResource newCnt, Date currentTime) { - if (newCnt == null || + /*if (newCnt == null || newCnt.getCounterDate() == null || newCnt.getCounterValue() == null) { return 0; @@ -450,7 +450,7 @@ public class FlowReconcileManager implements IFloodlightModule, IFlowReconcileSe break; } - return (int)(diff/elapsedTimeInSecond); + return (int)(diff/elapsedTimeInSecond); */ return 0; } } diff --git a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java index b70a646f90896caf75169b19fa3d358e2155f061..f14134c5f17b651c9294b031667fdb13f109b9fd 100644 --- a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java +++ b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java @@ -24,33 +24,16 @@ import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.projectfloodlight.openflow.protocol.OFFlowMod; import org.projectfloodlight.openflow.protocol.match.Match; -import org.projectfloodlight.openflow.protocol.OFFactories; import org.projectfloodlight.openflow.protocol.OFMessage; import org.projectfloodlight.openflow.protocol.OFPacketIn; import org.projectfloodlight.openflow.protocol.OFPacketOut; import org.projectfloodlight.openflow.protocol.OFType; -import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.protocol.action.OFAction; -import org.projectfloodlight.openflow.protocol.action.OFActionSetDlSrc; -import org.projectfloodlight.openflow.protocol.action.OFActionSetDlDst; -import org.projectfloodlight.openflow.protocol.action.OFActionEnqueue; -import org.projectfloodlight.openflow.protocol.action.OFActionSetNwSrc; -import org.projectfloodlight.openflow.protocol.action.OFActionSetNwDst; -import org.projectfloodlight.openflow.protocol.action.OFActionSetNwTos; -import org.projectfloodlight.openflow.protocol.action.OFActionOutput; -import org.projectfloodlight.openflow.protocol.action.OFActionStripVlan; -import org.projectfloodlight.openflow.protocol.action.OFActionSetTpSrc; -import org.projectfloodlight.openflow.protocol.action.OFActionSetTpDst; -import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanVid; -import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanPcp; // PCP = Priority Code Point import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.IPv4Address; import org.projectfloodlight.openflow.types.IpProtocol; @@ -60,8 +43,6 @@ import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.TransportPort; import org.projectfloodlight.openflow.types.U16; import org.projectfloodlight.openflow.types.U64; -import org.projectfloodlight.openflow.types.VlanPcp; -import org.projectfloodlight.openflow.types.VlanVid; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -91,7 +72,9 @@ import net.floodlightcontroller.routing.Route; import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService; import net.floodlightcontroller.topology.ITopologyService; import net.floodlightcontroller.topology.NodePortTuple; -import net.floodlightcontroller.util.MatchString; +import net.floodlightcontroller.util.ActionUtils; +import net.floodlightcontroller.util.FlowModUtils; +import net.floodlightcontroller.util.MatchUtils; import net.floodlightcontroller.util.OFMessageDamper; /** @@ -143,10 +126,8 @@ public class LoadBalancer implements IFloodlightModule, new Comparator<SwitchPort>() { @Override public int compare(SwitchPort d1, SwitchPort d2) { - DatapathId d1ClusterId = - topologyService.getL2DomainId(d1.getSwitchDPID()); - DatapathId d2ClusterId = - topologyService.getL2DomainId(d2.getSwitchDPID()); + DatapathId d1ClusterId = topologyService.getL2DomainId(d1.getSwitchDPID()); + DatapathId d2ClusterId = topologyService.getL2DomainId(d2.getSwitchDPID()); return d1ClusterId.compareTo(d2ClusterId); } }; @@ -308,8 +289,7 @@ public class LoadBalancer implements IFloodlightModule, arpRequest.getSenderProtocolAddress())); // push ARP reply out - pushPacket(arpReply, sw, OFBufferId.NO_BUFFER, OFPort.ZERO, - pi.getInPort(), cntx, true); //TODO @Ryan is OFPort.ZERO == NONE (no port)? + pushPacket(arpReply, sw, OFBufferId.NO_BUFFER, OFPort.ZERO, pi.getInPort(), cntx, true); log.debug("proxy ARP reply pushed as {}", IPv4.fromIPv4Address(vips.get(vipId).address)); return; @@ -391,7 +371,7 @@ public class LoadBalancer implements IFloodlightModule, for (IDevice d : allDevices) { for (int j = 0; j < d.getIPv4Addresses().length; j++) { - if (srcDevice == null && client.ipAddress == d.getIPv4Addresses()[j]) + if (srcDevice == null && client.ipAddress.equals(d.getIPv4Addresses()[j])) srcDevice = d; if (dstDevice == null && member.address == d.getIPv4Addresses()[j].getInt()) { dstDevice = d; @@ -422,8 +402,8 @@ public class LoadBalancer implements IFloodlightModule, DatapathId dstIsland = topologyService.getL2DomainId(dstSwDpid); if ((dstIsland != null) && dstIsland.equals(srcIsland)) { on_same_island = true; - if ((sw.getId() == dstSwDpid) && - (pi.getInPort() == dstDap.getPort())) { + if ((sw.getId().equals(dstSwDpid)) && + (pi.getInPort().equals(dstDap.getPort()))) { on_same_if = true; } break; @@ -518,7 +498,7 @@ public class LoadBalancer implements IFloodlightModule, */ public void pushStaticVipRoute(boolean inBound, Route route, IPClient client, LBMember member, DatapathId pinSwitch) { List<NodePortTuple> path = route.getPath(); - if (path.size()>0) { + if (path.size() > 0) { for (int i = 0; i < path.size(); i+=2) { DatapathId sw = path.get(i).getNodeId(); @@ -529,21 +509,21 @@ public class LoadBalancer implements IFloodlightModule, OFFlowMod.Builder fmb = switchService.getSwitch(pinSwitch).getOFFactory().buildFlowModify(); - fmb.setIdleTimeout((short) 0); // infinite - fmb.setHardTimeout((short) 0); // infinite + fmb.setIdleTimeout(FlowModUtils.INFINITE_TIMEOUT); + fmb.setHardTimeout(FlowModUtils.INFINITE_TIMEOUT); fmb.setBufferId(OFBufferId.NO_BUFFER); - fmb.setOutPort(OFPort.ANY); //TODO @Ryan is this the same as OFPort.NONE in the old openflowj? + fmb.setOutPort(OFPort.ZERO); fmb.setCookie(U64.of(0)); fmb.setPriority(Short.MAX_VALUE); if (inBound) { entryName = "inbound-vip-"+ member.vipId+"-client-"+client.ipAddress+"-port-"+client.targetPort +"-srcswitch-"+path.get(0).getNodeId()+"-sw-"+sw; - matchString = "nw_src="+client.ipAddress.toString()+"," - + "nw_proto="+String.valueOf(client.nw_proto)+"," - + "tp_src="+client.srcPort.toString()+"," - + "dl_type="+LB_ETHER_TYPE+"," - + "in_port="+path.get(i).getPortId().toString(); + matchString = MatchUtils.STR_NW_SRC + "="+client.ipAddress.toString()+"," + + MatchUtils.STR_NW_PROTO + "="+String.valueOf(client.nw_proto)+"," + + MatchUtils.STR_TP_SRC + "="+client.srcPort.toString()+"," + + MatchUtils.STR_DL_TYPE + "="+LB_ETHER_TYPE+"," + + MatchUtils.STR_IN_PORT + "="+path.get(i).getPortId().toString(); if (sw == pinSwitch) { actionString = "set-dst-ip="+IPv4.fromIPv4Address(member.address)+"," @@ -556,29 +536,29 @@ public class LoadBalancer implements IFloodlightModule, } else { entryName = "outbound-vip-"+ member.vipId+"-client-"+client.ipAddress+"-port-"+client.targetPort +"-srcswitch-"+path.get(0).getNodeId()+"-sw-"+sw; - matchString = "nw_dst="+client.ipAddress.toString()+"," - + "nw_proto="+client.nw_proto.toString()+"," - + "tp_dst="+client.srcPort.toString()+"," - + "dl_type="+LB_ETHER_TYPE+"," - + "in_port="+path.get(i).getPortId().toString(); + matchString = MatchUtils.STR_NW_DST + "="+client.ipAddress.toString()+"," + + MatchUtils.STR_NW_PROTO + "="+client.nw_proto.toString()+"," + + MatchUtils.STR_TP_DST + "="+client.srcPort.toString()+"," + + MatchUtils.STR_DL_TYPE + "="+LB_ETHER_TYPE+"," + + MatchUtils.STR_IN_PORT + "="+path.get(i).getPortId().toString(); if (sw == pinSwitch) { - actionString = "set-src-ip="+IPv4.fromIPv4Address(vips.get(member.vipId).address)+"," - + "set-src-mac="+vips.get(member.vipId).proxyMac.toString()+"," - + "output="+path.get(i+1).getPortId(); + actionString = ActionUtils.STR_NW_SRC_SET + "="+IPv4.fromIPv4Address(vips.get(member.vipId).address)+"," + + ActionUtils.STR_DL_SRC_SET + "="+vips.get(member.vipId).proxyMac.toString()+"," + + ActionUtils.STR_OUTPUT + "="+path.get(i+1).getPortId(); } else { - actionString = "output="+path.get(i+1).getPortId(); + actionString = ActionUtils.STR_OUTPUT + "="+path.get(i+1).getPortId(); } } - parseActionString(fmb.build(), actionString, log); + ActionUtils.fromString(fmb, actionString, log); fmb.setPriority(U16.t(LB_PRIORITY)); Match match = null; try { - match = MatchString.fromString(matchString, switchService.getSwitch(sw).getOFFactory().getVersion()); + match = MatchUtils.fromString(matchString, switchService.getSwitch(sw).getOFFactory().getVersion()); } catch (IllegalArgumentException e) { log.debug("ignoring flow entry {} on switch {} with illegal OFMatch() key: " + matchString, entryName, swString); } @@ -646,7 +626,7 @@ public class LoadBalancer implements IFloodlightModule, @Override public LBPool createPool(LBPool pool) { - if (pool==null) + if (pool == null) pool = new LBPool(); pools.put(pool.id, pool); @@ -669,7 +649,7 @@ public class LoadBalancer implements IFloodlightModule, @Override public int removePool(String poolId) { LBPool pool; - if(pools!=null){ + if (pools != null) { pool = pools.get(poolId); if (pool.vipId != null) vips.get(pool.vipId).pools.remove(poolId); @@ -698,7 +678,7 @@ public class LoadBalancer implements IFloodlightModule, if(pools.containsKey(poolId)) { ArrayList<String> memberIds = pools.get(poolId).members; - for (int i=0; i<memberIds.size(); i++) + for (int i = 0; i<memberIds.size(); i++) result.add(members.get(memberIds.get(i))); } return result; @@ -833,476 +813,4 @@ public class LoadBalancer implements IFloodlightModule, floodlightProviderService.addOFMessageListener(OFType.PACKET_IN, this); restApiService.addRestletRoutable(new LoadBalancerWebRoutable()); } - - // Utilities borrowed from StaticFlowEntries - - private static class SubActionStruct { - OFAction action; - } - - /** - * Parses OFFlowMod actions from strings. - * @param flowMod The OFFlowMod to set the actions for - * @param actionstr The string containing all the actions - * @param log A logger to log for errors. - */ - public static void parseActionString(OFFlowMod flowMod, String actionstr, Logger log) { - List<OFAction> actions = new LinkedList<OFAction>(); - OFVersion version = flowMod.getVersion(); - if (actionstr != null) { - actionstr = actionstr.toLowerCase(); - for (String subaction : actionstr.split(",")) { - String action = subaction.split("[=:]")[0]; - SubActionStruct subaction_struct = null; - - if (action.equals("output")) { - subaction_struct = decode_output(subaction, version, log); - } - else if (action.equals("enqueue")) { - subaction_struct = decode_enqueue(subaction, version, log); - } - else if (action.equals("strip-vlan")) { - subaction_struct = decode_strip_vlan(subaction, version, log); - } - else if (action.equals("set-vlan-id")) { - subaction_struct = decode_set_vlan_id(subaction, version, log); - } - else if (action.equals("set-vlan-priority")) { - subaction_struct = decode_set_vlan_priority(subaction, version, log); - } - else if (action.equals("set-src-mac")) { - subaction_struct = decode_set_src_mac(subaction, version, log); - } - else if (action.equals("set-dst-mac")) { - subaction_struct = decode_set_dst_mac(subaction, version, log); - } - else if (action.equals("set-tos-bits")) { - subaction_struct = decode_set_tos_bits(subaction, version, log); - } - else if (action.equals("set-src-ip")) { - subaction_struct = decode_set_src_ip(subaction, version, log); - } - else if (action.equals("set-dst-ip")) { - subaction_struct = decode_set_dst_ip(subaction, version, log); - } - else if (action.equals("set-src-port")) { - subaction_struct = decode_set_src_port(subaction, version, log); - } - else if (action.equals("set-dst-port")) { - subaction_struct = decode_set_dst_port(subaction, version, log); - } - else { - log.error("Unexpected action '{}', '{}'", action, subaction); - } - - if (subaction_struct != null) { - actions.add(subaction_struct.action); - } - } - } - log.debug("action {}", actions); - - flowMod = flowMod.createBuilder().setActions(actions).build(); - } - - private static SubActionStruct decode_output(String subaction, OFVersion version, Logger log) { - SubActionStruct sa = null; - Matcher n; - - n = Pattern.compile("output=(?:((?:0x)?\\d+)|(all)|(controller)|(local)|(ingress-port)|(normal)|(flood))").matcher(subaction); - if (n.matches()) { - OFActionOutput.Builder ab = OFFactories.getFactory(version).actions().buildOutput(); - OFPort port = OFPort.ANY; //TODO @Ryan is ANY == NONE? - if (n.group(1) != null) { - try { - port = OFPort.of(get_short(n.group(1))); - } - catch (NumberFormatException e) { - log.debug("Invalid port in: '{}' (error ignored)", subaction); - return null; - } - } - else if (n.group(2) != null) - port = OFPort.ALL; - else if (n.group(3) != null) - port = OFPort.CONTROLLER; - else if (n.group(4) != null) - port = OFPort.LOCAL; - else if (n.group(5) != null) - port = OFPort.IN_PORT; - else if (n.group(6) != null) - port = OFPort.NORMAL; - else if (n.group(7) != null) - port = OFPort.FLOOD; - ab.setPort(port); - log.debug("action {}", ab.build()); - - sa = new SubActionStruct(); - sa.action = ab.build(); - } - else { - log.error("Invalid subaction: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_enqueue(String subaction, OFVersion version, Logger log) { - SubActionStruct sa = null; - Matcher n; - - n = Pattern.compile("enqueue=(?:((?:0x)?\\d+)\\:((?:0x)?\\d+))").matcher(subaction); - if (n.matches()) { - OFPort portnum = OFPort.of(0); - if (n.group(1) != null) { - try { - portnum = OFPort.of(get_short(n.group(1))); - } - catch (NumberFormatException e) { - log.debug("Invalid port-num in: '{}' (error ignored)", subaction); - return null; - } - } - - int queueid = 0; - if (n.group(2) != null) { - try { - queueid = get_int(n.group(2)); - } - catch (NumberFormatException e) { - log.debug("Invalid queue-id in: '{}' (error ignored)", subaction); - return null; - } - } - - OFActionEnqueue.Builder aeb = OFFactories.getFactory(version).actions().buildEnqueue(); - aeb.setPort(portnum); - aeb.setQueueId(queueid); - log.debug("action {}", aeb.build()); - - sa = new SubActionStruct(); - sa.action = aeb.build(); - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_strip_vlan(String subaction, OFVersion version, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("strip-vlan").matcher(subaction); - - if (n.matches()) { - OFActionStripVlan asvl = OFFactories.getFactory(version).actions().stripVlan(); - log.debug("action {}", asvl); - - sa = new SubActionStruct(); - sa.action = asvl; - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_vlan_id(String subaction, OFVersion version, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-vlan-id=((?:0x)?\\d+)").matcher(subaction); - - if (n.matches()) { - if (n.group(1) != null) { - try { - short vlanid = get_short(n.group(1)); - OFActionSetVlanVid.Builder avvid = OFFactories.getFactory(version).actions().buildSetVlanVid(); - avvid.setVlanVid(VlanVid.ofVlan(vlanid)); - log.debug(" action {}", avvid.build()); - - sa = new SubActionStruct(); - sa.action = avvid.build(); - } - catch (NumberFormatException e) { - log.debug("Invalid VLAN in: {} (error ignored)", subaction); - return null; - } - } - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_vlan_priority(String subaction, OFVersion version, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-vlan-priority=((?:0x)?\\d+)").matcher(subaction); - - if (n.matches()) { - if (n.group(1) != null) { - try { - byte prior = get_byte(n.group(1)); - OFActionSetVlanPcp.Builder avpcp = OFFactories.getFactory(version).actions().buildSetVlanPcp(); - avpcp.setVlanPcp(VlanPcp.of(prior)); - log.debug(" action {}", avpcp.build()); - - sa = new SubActionStruct(); - sa.action = avpcp.build(); - } - catch (NumberFormatException e) { - log.debug("Invalid VLAN priority in: {} (error ignored)", subaction); - return null; - } - } - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_src_mac(String subaction, OFVersion version, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-src-mac=(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(subaction); - - if (n.matches()) { - byte[] macaddr = get_mac_addr(n, subaction, log); - if (macaddr != null) { - OFActionSetDlSrc.Builder asdls = OFFactories.getFactory(version).actions().buildSetDlSrc(); - asdls.setDlAddr(MacAddress.of(macaddr)); - log.debug("action {}", asdls.build()); - - sa = new SubActionStruct(); - sa.action = asdls.build(); - } - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_dst_mac(String subaction, OFVersion version, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-dst-mac=(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(subaction); - - if (n.matches()) { - byte[] macaddr = get_mac_addr(n, subaction, log); - if (macaddr != null) { - OFActionSetDlDst.Builder asdld = OFFactories.getFactory(version).actions().buildSetDlDst(); - asdld.setDlAddr(MacAddress.of(macaddr)); - log.debug(" action {}", asdld.build()); - - sa = new SubActionStruct(); - sa.action = asdld.build(); - } - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_tos_bits(String subaction, OFVersion version, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-tos-bits=((?:0x)?\\d+)").matcher(subaction); - - if (n.matches()) { - if (n.group(1) != null) { - try { - byte tosbits = get_byte(n.group(1)); - OFActionSetNwTos.Builder snwtos = OFFactories.getFactory(version).actions().buildSetNwTos(); - snwtos.setNwTos(tosbits); - log.debug(" action {}", snwtos.build()); - - sa = new SubActionStruct(); - sa.action = snwtos.build(); - } - catch (NumberFormatException e) { - log.debug("Invalid dst-port in: {} (error ignored)", subaction); - return null; - } - } - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_src_ip(String subaction, OFVersion version, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-src-ip=(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(subaction); - - if (n.matches()) { - int ipaddr = get_ip_addr(n, subaction, log); - OFActionSetNwSrc.Builder snws = OFFactories.getFactory(version).actions().buildSetNwSrc(); - snws.setNwAddr(IPv4Address.of(ipaddr)); - log.debug(" action {}", snws.build()); - - sa = new SubActionStruct(); - sa.action = snws.build(); - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_dst_ip(String subaction, OFVersion version, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-dst-ip=(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(subaction); - - if (n.matches()) { - int ipaddr = get_ip_addr(n, subaction, log); - OFActionSetNwDst.Builder snwd = OFFactories.getFactory(version).actions().buildSetNwDst(); - snwd.setNwAddr(IPv4Address.of(ipaddr)); - log.debug("action {}", snwd.build()); - - sa = new SubActionStruct(); - sa.action = snwd.build(); - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_src_port(String subaction, OFVersion version, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-src-port=((?:0x)?\\d+)").matcher(subaction); - - if (n.matches()) { - if (n.group(1) != null) { - try { - TransportPort portnum = TransportPort.of(get_short(n.group(1))); - OFActionSetTpSrc.Builder stps = OFFactories.getFactory(version).actions().buildSetTpSrc(); - stps.setTpPort(portnum); - log.debug("action {}", stps.build()); - - sa = new SubActionStruct(); - sa.action = stps.build(); - } - catch (NumberFormatException e) { - log.debug("Invalid src-port in: {} (error ignored)", subaction); - return null; - } - } - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_dst_port(String subaction, OFVersion version, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-dst-port=((?:0x)?\\d+)").matcher(subaction); - - if (n.matches()) { - if (n.group(1) != null) { - try { - TransportPort portnum = TransportPort.of(get_short(n.group(1))); - OFActionSetTpDst.Builder stpd = OFFactories.getFactory(version).actions().buildSetTpDst(); - stpd.setTpPort(portnum); - log.debug("action {}", stpd); - - sa = new SubActionStruct(); - sa.action = stpd.build(); - } - catch (NumberFormatException e) { - log.debug("Invalid dst-port in: {} (error ignored)", subaction); - return null; - } - } - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static byte[] get_mac_addr(Matcher n, String subaction, Logger log) { - byte[] macaddr = new byte[6]; - - for (int i=0; i<6; i++) { - if (n.group(i+1) != null) { - try { - macaddr[i] = get_byte("0x" + n.group(i+1)); - } - catch (NumberFormatException e) { - log.debug("Invalid src-mac in: '{}' (error ignored)", subaction); - return null; - } - } - else { - log.debug("Invalid src-mac in: '{}' (null, error ignored)", subaction); - return null; - } - } - - return macaddr; - } - - private static int get_ip_addr(Matcher n, String subaction, Logger log) { - int ipaddr = 0; - - for (int i=0; i<4; i++) { - if (n.group(i+1) != null) { - try { - ipaddr = ipaddr<<8; - ipaddr = ipaddr | get_int(n.group(i+1)); - } - catch (NumberFormatException e) { - log.debug("Invalid src-ip in: '{}' (error ignored)", subaction); - return 0; - } - } - else { - log.debug("Invalid src-ip in: '{}' (null, error ignored)", subaction); - return 0; - } - } - - return ipaddr; - } - - // Parse int as decimal, hex (start with 0x or #) or octal (starts with 0) - private static int get_int(String str) { - return Integer.decode(str); - } - - // Parse short as decimal, hex (start with 0x or #) or octal (starts with 0) - private static short get_short(String str) { - return (short)(int)Integer.decode(str); - } - - // Parse byte as decimal, hex (start with 0x or #) or octal (starts with 0) - private static byte get_byte(String str) { - return Integer.decode(str).byteValue(); - } - - } diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java index b0dfdf5d51391356cacf1c79360200ee1389c3be..72d52bd49d05bd64f29d382519ad0f867cad698f 100644 --- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java +++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java @@ -18,15 +18,12 @@ package net.floodlightcontroller.staticflowentry; import java.io.IOException; import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; +import java.util.Iterator; import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import net.floodlightcontroller.core.annotations.LogMessageCategory; -import net.floodlightcontroller.core.annotations.LogMessageDoc; import net.floodlightcontroller.core.util.AppCookie; +import net.floodlightcontroller.util.ActionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,32 +33,12 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.MappingJsonFactory; -import org.projectfloodlight.openflow.protocol.OFFactories; import org.projectfloodlight.openflow.protocol.OFFlowMod; -import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.protocol.match.Match; import org.projectfloodlight.openflow.protocol.match.MatchField; -import org.projectfloodlight.openflow.types.IPv4Address; -import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFBufferId; import org.projectfloodlight.openflow.types.OFPort; -import org.projectfloodlight.openflow.types.TransportPort; import org.projectfloodlight.openflow.types.U64; -import org.projectfloodlight.openflow.types.VlanPcp; -import org.projectfloodlight.openflow.types.VlanVid; -import org.projectfloodlight.openflow.protocol.action.OFAction; -import org.projectfloodlight.openflow.protocol.action.OFActionSetDlDst; -import org.projectfloodlight.openflow.protocol.action.OFActionSetDlSrc; -import org.projectfloodlight.openflow.protocol.action.OFActionEnqueue; -import org.projectfloodlight.openflow.protocol.action.OFActionSetNwDst; -import org.projectfloodlight.openflow.protocol.action.OFActionSetNwSrc; -import org.projectfloodlight.openflow.protocol.action.OFActionSetNwTos; -import org.projectfloodlight.openflow.protocol.action.OFActionOutput; -import org.projectfloodlight.openflow.protocol.action.OFActionStripVlan; -import org.projectfloodlight.openflow.protocol.action.OFActionSetTpDst; -import org.projectfloodlight.openflow.protocol.action.OFActionSetTpSrc; -import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanVid; -import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanPcp; /** * Represents static flow entries to be maintained by the controller on the @@ -69,775 +46,364 @@ import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanPcp; */ @LogMessageCategory("Static Flow Pusher") public class StaticFlowEntries { - protected static Logger log = LoggerFactory.getLogger(StaticFlowEntries.class); - - private static class SubActionStruct { - OFAction action; - } - - /** - * This function generates a random hash for the bottom half of the cookie - * - * @param fm - * @param userCookie - * @param name - * @return A cookie that encodes the application ID and a hash - */ - public static U64 computeEntryCookie(OFFlowMod fm, int userCookie, String name) { - // flow-specific hash is next 20 bits LOOK! who knows if this - int prime = 211; - int flowHash = 2311; - for (int i=0; i < name.length(); i++) - flowHash = flowHash * prime + (int)name.charAt(i); - - return AppCookie.makeCookie(StaticFlowEntryPusher.STATIC_FLOW_APP_ID, flowHash); - } - - /** - * Sets defaults for an OFFlowMod - * @param fm The OFFlowMod to set defaults for - * @param entryName The name of the entry. Used to compute the cookie. - */ - public static OFFlowMod initDefaultFlowMod(OFFlowMod fm, String entryName) { - return fm.createBuilder().setIdleTimeout((short) 0) // infinite - .setHardTimeout((short) 0) // infinite - .setBufferId(OFBufferId.NO_BUFFER) - .setOutPort(OFPort.ANY) - .setCookie(computeEntryCookie(fm, 0, entryName)) - .setPriority(Integer.MAX_VALUE) - .build(); - } - - /** - * Gets the entry name of a flow mod - * @param fmJson The OFFlowMod in a JSON representation - * @return The name of the OFFlowMod, null if not found - * @throws IOException If there was an error parsing the JSON - */ - public static String getEntryNameFromJson(String fmJson) throws IOException{ - MappingJsonFactory f = new MappingJsonFactory(); - JsonParser jp; - - try { - jp = f.createJsonParser(fmJson); - } catch (JsonParseException e) { - throw new IOException(e); - } - - jp.nextToken(); - if (jp.getCurrentToken() != JsonToken.START_OBJECT) { - throw new IOException("Expected START_OBJECT"); - } - - while (jp.nextToken() != JsonToken.END_OBJECT) { - if (jp.getCurrentToken() != JsonToken.FIELD_NAME) { - throw new IOException("Expected FIELD_NAME"); - } - - String n = jp.getCurrentName(); - jp.nextToken(); - if (jp.getText().equals("")) - continue; - - if (n == "name") - return jp.getText(); - } - - return null; - } - - /** - * Parses an OFFlowMod (and it's inner OFMatch) to the storage entry format. - * @param fm The FlowMod to parse - * @param sw The switch the FlowMod is going to be installed on - * @param name The name of this static flow entry - * @return A Map representation of the storage entry - */ - public static Map<String, Object> flowModToStorageEntry(OFFlowMod fm, String sw, String name) { - Map<String, Object> entry = new HashMap<String, Object>(); - Match match = fm.getMatch(); - entry.put(StaticFlowEntryPusher.COLUMN_NAME, name); - entry.put(StaticFlowEntryPusher.COLUMN_SWITCH, sw); - entry.put(StaticFlowEntryPusher.COLUMN_ACTIVE, Boolean.toString(true)); - entry.put(StaticFlowEntryPusher.COLUMN_PRIORITY, Integer.toString(fm.getPriority())); - //entry.put(StaticFlowEntryPusher.COLUMN_WILDCARD, Integer.toString(match.getWildcards())); TODO @Ryan what to do about wildcards? - - if ((fm.getActions() != null) && (fm.getActions().size() > 0)) - entry.put(StaticFlowEntryPusher.COLUMN_ACTIONS, StaticFlowEntries.flowModActionsToString(fm.getActions())); - - if ((match.get(MatchField.IN_PORT) != null) && (match.get(MatchField.IN_PORT).getPortNumber() != 0)) - entry.put(StaticFlowEntryPusher.COLUMN_IN_PORT, Integer.toString(match.get(MatchField.IN_PORT).getPortNumber())); - - if ((match.get(MatchField.ETH_SRC) != null) && !match.get(MatchField.ETH_SRC).equals(MacAddress.of(0))) - entry.put(StaticFlowEntryPusher.COLUMN_DL_SRC, match.get(MatchField.ETH_SRC).toString()); - - if ((match.get(MatchField.ETH_DST) != null) && !match.get(MatchField.ETH_DST).equals(MacAddress.of(0))) - entry.put(StaticFlowEntryPusher.COLUMN_DL_DST, match.get(MatchField.ETH_DST).toString()); - - if ((match.get(MatchField.VLAN_VID) != null) && (match.get(MatchField.VLAN_VID).getVlan() != -1)) - entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN, match.get(MatchField.VLAN_VID).toString()); - - if ((match.get(MatchField.VLAN_PCP) != null) && (match.get(MatchField.VLAN_PCP).getValue() != 0)) - entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, Byte.toString(match.get(MatchField.VLAN_PCP).getValue())); - - if ((match.get(MatchField.ETH_TYPE) != null) && (match.get(MatchField.ETH_TYPE).getValue() != 0)) - entry.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, match.get(MatchField.ETH_TYPE).toString()); - - if ((match.get(MatchField.IP_ECN) != null) && (match.get(MatchField.IP_DSCP) != null) - && (match.get(MatchField.IP_ECN).getEcnValue() != 0) && (match.get(MatchField.IP_DSCP).getDscpValue() != 0)) - entry.put(StaticFlowEntryPusher.COLUMN_NW_TOS, // TOS = [DSCP bits 0-5] + [ECN bits 6-7] --> bitwise OR to get TOS byte - Byte.toString((byte) (match.get(MatchField.IP_ECN).getEcnValue() | match.get(MatchField.IP_DSCP).getDscpValue()))); - - if ((match.get(MatchField.IP_PROTO) != null) && (match.get(MatchField.IP_PROTO).getIpProtocolNumber() != 0)) - entry.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, Short.toString(match.get(MatchField.IP_PROTO).getIpProtocolNumber())); - - if ((match.get(MatchField.IPV4_SRC) != null) && (match.get(MatchField.IPV4_SRC).getInt() != 0)) - entry.put(StaticFlowEntryPusher.COLUMN_NW_SRC, match.get(MatchField.IPV4_SRC).toString()); - - if ((match.get(MatchField.IPV4_DST) != null) && (match.get(MatchField.IPV4_DST).getInt() != 0)) - entry.put(StaticFlowEntryPusher.COLUMN_NW_DST, match.get(MatchField.IPV4_DST).toString()); - - if ((match.get(MatchField.TCP_SRC) != null) && (match.get(MatchField.TCP_SRC).getPort() != 0)) - entry.put(StaticFlowEntryPusher.COLUMN_TP_SRC, match.get(MatchField.TCP_SRC).toString()); - else if ((match.get(MatchField.UDP_SRC) != null) && (match.get(MatchField.UDP_SRC).getPort() != 0)) - entry.put(StaticFlowEntryPusher.COLUMN_TP_SRC, match.get(MatchField.UDP_SRC).toString()); - else if ((match.get(MatchField.SCTP_SRC) != null) && (match.get(MatchField.SCTP_SRC).getPort() != 0)) - entry.put(StaticFlowEntryPusher.COLUMN_TP_SRC, match.get(MatchField.SCTP_SRC).toString()); - - if ((match.get(MatchField.TCP_DST) != null) && (match.get(MatchField.TCP_DST).getPort() != 0)) - entry.put(StaticFlowEntryPusher.COLUMN_TP_DST, match.get(MatchField.TCP_DST).toString()); - else if ((match.get(MatchField.UDP_DST) != null) && (match.get(MatchField.UDP_DST).getPort() != 0)) - entry.put(StaticFlowEntryPusher.COLUMN_TP_SRC, match.get(MatchField.UDP_DST).toString()); - else if ((match.get(MatchField.SCTP_DST) != null) && (match.get(MatchField.SCTP_DST).getPort() != 0)) - entry.put(StaticFlowEntryPusher.COLUMN_TP_SRC, match.get(MatchField.SCTP_DST).toString()); - - return entry; - } - - /** - * Returns a String representation of all the openflow actions. - * @param fmActions A list of OFActions to encode into one string - * @return A string of the actions encoded for our database - */ - @LogMessageDoc(level="ERROR", - message="Could not decode action {action}", - explanation="A static flow entry contained an invalid action", - recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) - private static String flowModActionsToString(List<OFAction> fmActions) { - StringBuilder sb = new StringBuilder(); - for (OFAction a : fmActions) { - if (sb.length() > 0) { - sb.append(','); - } - switch(a.getType()) { - case OUTPUT: - sb.append("output=" + ((OFActionOutput)a).getPort().toString()); - break; - case ENQUEUE: - long queue = ((OFActionEnqueue)a).getQueueId(); - OFPort port = ((OFActionEnqueue)a).getPort(); - sb.append("enqueue=" + Integer.toString(port.getPortNumber()) + ":0x" + String.format("%02x", queue)); - break; - case STRIP_VLAN: - sb.append("strip-vlan"); - break; - case SET_VLAN_VID: - sb.append("set-vlan-id=" + - ((OFActionSetVlanVid)a).getVlanVid().toString()); - break; - case SET_VLAN_PCP: - sb.append("set-vlan-priority=" + - Byte.toString(((OFActionSetVlanPcp)a).getVlanPcp().getValue())); - break; - case SET_DL_SRC: - sb.append("set-src-mac=" + - ((OFActionSetDlSrc)a).getDlAddr().toString()); - break; - case SET_DL_DST: - sb.append("set-dst-mac=" + - ((OFActionSetDlDst)a).getDlAddr().toString()); - break; - case SET_NW_TOS: - sb.append("set-tos-bits=" + - Short.toString(((OFActionSetNwTos)a).getNwTos())); - break; - case SET_NW_SRC: - sb.append("set-src-ip=" + - ((OFActionSetNwSrc)a).getNwAddr().toString()); - break; - case SET_NW_DST: - sb.append("set-dst-ip=" + - ((OFActionSetNwDst)a).getNwAddr().toString()); - break; - case SET_TP_SRC: - sb.append("set-src-port=" + - ((OFActionSetTpSrc)a).getTpPort().toString()); - break; - case SET_TP_DST: - sb.append("set-dst-port=" + - ((OFActionSetTpDst)a).getTpPort().toString()); - break; - default: - log.error("Could not decode action: {}", a); - break; - } - - } - return sb.toString(); - } - - /** - * Turns a JSON formatted Static Flow Pusher string into a storage entry - * Expects a string in JSON along the lines of: - * { - * "switch": "AA:BB:CC:DD:EE:FF:00:11", - * "name": "flow-mod-1", - * "cookie": "0", - * "priority": "32768", - * "ingress-port": "1", - * "actions": "output=2", - * } - * @param fmJson The JSON formatted static flow pusher entry - * @return The map of the storage entry - * @throws IOException If there was an error parsing the JSON - */ - public static Map<String, Object> jsonToStorageEntry(String fmJson) throws IOException { - Map<String, Object> entry = new HashMap<String, Object>(); - MappingJsonFactory f = new MappingJsonFactory(); - JsonParser jp; - - try { - jp = f.createJsonParser(fmJson); - } catch (JsonParseException e) { - throw new IOException(e); - } - - jp.nextToken(); - if (jp.getCurrentToken() != JsonToken.START_OBJECT) { - throw new IOException("Expected START_OBJECT"); - } - - while (jp.nextToken() != JsonToken.END_OBJECT) { - if (jp.getCurrentToken() != JsonToken.FIELD_NAME) { - throw new IOException("Expected FIELD_NAME"); - } - - String n = jp.getCurrentName(); - jp.nextToken(); - if (jp.getText().equals("")) - continue; - - if (n == "name") - entry.put(StaticFlowEntryPusher.COLUMN_NAME, jp.getText()); - else if (n == "switch") - entry.put(StaticFlowEntryPusher.COLUMN_SWITCH, jp.getText()); - else if (n == "actions") - entry.put(StaticFlowEntryPusher.COLUMN_ACTIONS, jp.getText()); - else if (n == "priority") - entry.put(StaticFlowEntryPusher.COLUMN_PRIORITY, jp.getText()); - else if (n == "active") - entry.put(StaticFlowEntryPusher.COLUMN_ACTIVE, jp.getText()); - else if (n == "wildcards") - entry.put(StaticFlowEntryPusher.COLUMN_WILDCARD, jp.getText()); - else if (n == "ingress-port") - entry.put(StaticFlowEntryPusher.COLUMN_IN_PORT, jp.getText()); - else if (n == "src-mac") - entry.put(StaticFlowEntryPusher.COLUMN_DL_SRC, jp.getText()); - else if (n == "dst-mac") - entry.put(StaticFlowEntryPusher.COLUMN_DL_DST, jp.getText()); - else if (n == "vlan-id") - entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN, jp.getText()); - else if (n == "vlan-priority") - entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, jp.getText()); - else if (n == "ether-type") - entry.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, jp.getText()); - else if (n == "tos-bits") - entry.put(StaticFlowEntryPusher.COLUMN_NW_TOS, jp.getText()); - else if (n == "protocol") - entry.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, jp.getText()); - else if (n == "src-ip") - entry.put(StaticFlowEntryPusher.COLUMN_NW_SRC, jp.getText()); - else if (n == "dst-ip") - entry.put(StaticFlowEntryPusher.COLUMN_NW_DST, jp.getText()); - else if (n == "src-port") - entry.put(StaticFlowEntryPusher.COLUMN_TP_SRC, jp.getText()); - else if (n == "dst-port") - entry.put(StaticFlowEntryPusher.COLUMN_TP_DST, jp.getText()); - } - - return entry; - } - - /** - * Parses OFFlowMod actions from strings. - * @param flowMod The OFFlowMod to set the actions for - * @param actionstr The string containing all the actions - * @param log A logger to log for errors. - */ - @LogMessageDoc(level="ERROR", - message="Unexpected action '{action}', '{subaction}'", - explanation="A static flow entry contained an invalid action", - recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) - public static OFFlowMod parseActionString(OFFlowMod flowMod, String actionstr, Logger log) { - List<OFAction> actions = new LinkedList<OFAction>(); - if (actionstr != null) { - actionstr = actionstr.toLowerCase(); - for (String subaction : actionstr.split(",")) { - String action = subaction.split("[=:]")[0]; - SubActionStruct subaction_struct = null; - - if (action.equals("output")) { - subaction_struct = StaticFlowEntries.decode_output(subaction, log); - } - else if (action.equals("enqueue")) { - subaction_struct = decode_enqueue(subaction, log); - } - else if (action.equals("strip-vlan")) { - subaction_struct = decode_strip_vlan(subaction, log); - } - else if (action.equals("set-vlan-id")) { - subaction_struct = decode_set_vlan_id(subaction, log); - } - else if (action.equals("set-vlan-priority")) { - subaction_struct = decode_set_vlan_priority(subaction, log); - } - else if (action.equals("set-src-mac")) { - subaction_struct = decode_set_src_mac(subaction, log); - } - else if (action.equals("set-dst-mac")) { - subaction_struct = decode_set_dst_mac(subaction, log); - } - else if (action.equals("set-tos-bits")) { - subaction_struct = decode_set_tos_bits(subaction, log); - } - else if (action.equals("set-src-ip")) { - subaction_struct = decode_set_src_ip(subaction, log); - } - else if (action.equals("set-dst-ip")) { - subaction_struct = decode_set_dst_ip(subaction, log); - } - else if (action.equals("set-src-port")) { - subaction_struct = decode_set_src_port(subaction, log); - } - else if (action.equals("set-dst-port")) { - subaction_struct = decode_set_dst_port(subaction, log); - } - else { - log.error("Unexpected action '{}', '{}'", action, subaction); - } - - if (subaction_struct != null) { - actions.add(subaction_struct.action); - } - } - } - log.debug("action {}", actions); - - return flowMod.createBuilder().setActions(actions).build(); - } - - @LogMessageDoc(level="ERROR", - message="Invalid subaction: '{subaction}'", - explanation="A static flow entry contained an invalid subaction", - recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) - private static SubActionStruct decode_output(String subaction, Logger log) { - SubActionStruct sa = null; - Matcher n; - - n = Pattern.compile("output=(?:((?:0x)?\\d+)|(all)|(controller)|(local)|(ingress-port)|(normal)|(flood))").matcher(subaction); - if (n.matches()) { - OFActionOutput.Builder ab = OFFactories.getFactory(OFVersion.OF_13).actions().buildOutput(); - OFPort port = OFPort.ANY; - if (n.group(1) != null) { - try { - port = OFPort.of(get_short(n.group(1))); - } - catch (NumberFormatException e) { - log.debug("Invalid port in: '{}' (error ignored)", subaction); - return null; - } - } - else if (n.group(2) != null) - port = OFPort.ALL; - else if (n.group(3) != null) - port = OFPort.CONTROLLER; - else if (n.group(4) != null) - port = OFPort.LOCAL; - else if (n.group(5) != null) - port = OFPort.IN_PORT; - else if (n.group(6) != null) - port = OFPort.NORMAL; - else if (n.group(7) != null) - port = OFPort.FLOOD; - ab.setPort(port); - log.debug("action {}", ab.build()); - - sa = new SubActionStruct(); - sa.action = ab.build(); - } - else { - log.error("Invalid subaction: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_enqueue(String subaction, Logger log) { - SubActionStruct sa = null; - Matcher n; - - n = Pattern.compile("enqueue=(?:((?:0x)?\\d+)\\:((?:0x)?\\d+))").matcher(subaction); - if (n.matches()) { - OFPort port = OFPort.of(0); - if (n.group(1) != null) { - try { - port = OFPort.of(get_short(n.group(1))); - } - catch (NumberFormatException e) { - log.debug("Invalid port-num in: '{}' (error ignored)", subaction); - return null; - } - } - - int queueid = 0; - if (n.group(2) != null) { - try { - queueid = get_int(n.group(2)); - } - catch (NumberFormatException e) { - log.debug("Invalid queue-id in: '{}' (error ignored)", subaction); - return null; - } - } - - OFActionEnqueue.Builder ab = OFFactories.getFactory(OFVersion.OF_13).actions().buildEnqueue(); - ab.setPort(port); - ab.setQueueId(queueid); - log.debug("action {}", ab.build()); - - sa = new SubActionStruct(); - sa.action = ab.build(); - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_strip_vlan(String subaction, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("strip-vlan").matcher(subaction); - - if (n.matches()) { - OFActionStripVlan a = OFFactories.getFactory(OFVersion.OF_13).actions().stripVlan(); - log.debug("action {}", a); - - sa = new SubActionStruct(); - sa.action = a; - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_vlan_id(String subaction, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-vlan-id=((?:0x)?\\d+)").matcher(subaction); - - if (n.matches()) { - if (n.group(1) != null) { - try { - VlanVid vlanid = VlanVid.ofVlan(get_short(n.group(1))); - OFActionSetVlanVid.Builder ab = OFFactories.getFactory(OFVersion.OF_13).actions().buildSetVlanVid(); - ab.setVlanVid(vlanid); - log.debug(" action {}", ab.build()); - - sa = new SubActionStruct(); - sa.action = ab.build(); - } - catch (NumberFormatException e) { - log.debug("Invalid VLAN in: {} (error ignored)", subaction); - return null; - } - } - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_vlan_priority(String subaction, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-vlan-priority=((?:0x)?\\d+)").matcher(subaction); - - if (n.matches()) { - if (n.group(1) != null) { - try { - VlanPcp prior = VlanPcp.of(get_byte(n.group(1))); - OFActionSetVlanPcp.Builder ab = OFFactories.getFactory(OFVersion.OF_13).actions().buildSetVlanPcp(); - ab.setVlanPcp(prior); - log.debug(" action {}", ab.build()); - - sa = new SubActionStruct(); - sa.action = ab.build(); - } - catch (NumberFormatException e) { - log.debug("Invalid VLAN priority in: {} (error ignored)", subaction); - return null; - } - } - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_src_mac(String subaction, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-src-mac=(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(subaction); - - if (n.matches()) { - MacAddress macaddr = MacAddress.of(get_mac_addr(n, subaction, log)); - if (macaddr != null) { - OFActionSetDlSrc.Builder ab = OFFactories.getFactory(OFVersion.OF_13).actions().buildSetDlSrc(); - ab.setDlAddr(macaddr); - log.debug("action {}", ab.build()); - - sa = new SubActionStruct(); - sa.action = ab.build(); - } - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_dst_mac(String subaction, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-dst-mac=(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(subaction); - - if (n.matches()) { - MacAddress macaddr = MacAddress.of(get_mac_addr(n, subaction, log)); - if (macaddr != null) { - OFActionSetDlDst.Builder ab = OFFactories.getFactory(OFVersion.OF_13).actions().buildSetDlDst(); - ab.setDlAddr(macaddr); - log.debug(" action {}", ab.build()); - - sa = new SubActionStruct(); - sa.action = ab.build(); - } - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_tos_bits(String subaction, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-tos-bits=((?:0x)?\\d+)").matcher(subaction); - - if (n.matches()) { - if (n.group(1) != null) { - try { - byte tosbits = get_byte(n.group(1)); - OFActionSetNwTos.Builder ab = OFFactories.getFactory(OFVersion.OF_13).actions().buildSetNwTos(); - ab.setNwTos(tosbits); - log.debug(" action {}", ab.build()); - - sa = new SubActionStruct(); - sa.action = ab.build(); - } - catch (NumberFormatException e) { - log.debug("Invalid dst-port in: {} (error ignored)", subaction); - return null; - } - } - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_src_ip(String subaction, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-src-ip=(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(subaction); - - if (n.matches()) { - IPv4Address ipaddr = IPv4Address.of(get_ip_addr(n, subaction, log)); - OFActionSetNwSrc.Builder ab = OFFactories.getFactory(OFVersion.OF_13).actions().buildSetNwSrc(); - ab.setNwAddr(ipaddr); - log.debug(" action {}", ab.build()); - - sa = new SubActionStruct(); - sa.action = ab.build(); - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_dst_ip(String subaction, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-dst-ip=(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(subaction); - - if (n.matches()) { - IPv4Address ipaddr = IPv4Address.of(get_ip_addr(n, subaction, log)); - OFActionSetNwDst.Builder ab = OFFactories.getFactory(OFVersion.OF_13).actions().buildSetNwDst(); - ab.setNwAddr(ipaddr); - log.debug("action {}", ab.build()); - - sa = new SubActionStruct(); - sa.action = ab.build(); - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_src_port(String subaction, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-src-port=((?:0x)?\\d+)").matcher(subaction); - - if (n.matches()) { - if (n.group(1) != null) { - try { - TransportPort portnum = TransportPort.of(get_short(n.group(1))); - OFActionSetTpSrc.Builder ab = OFFactories.getFactory(OFVersion.OF_13).actions().buildSetTpSrc(); - ab.setTpPort(portnum); - log.debug("action {}", ab.build()); - - sa = new SubActionStruct(); - sa.action = ab.build(); - } - catch (NumberFormatException e) { - log.debug("Invalid src-port in: {} (error ignored)", subaction); - return null; - } - } - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static SubActionStruct decode_set_dst_port(String subaction, Logger log) { - SubActionStruct sa = null; - Matcher n = Pattern.compile("set-dst-port=((?:0x)?\\d+)").matcher(subaction); - - if (n.matches()) { - if (n.group(1) != null) { - try { - TransportPort portnum = TransportPort.of(get_short(n.group(1))); - OFActionSetTpDst.Builder ab = OFFactories.getFactory(OFVersion.OF_13).actions().buildSetTpDst(); - ab.setTpPort(portnum); - log.debug("action {}", ab.build()); - - sa = new SubActionStruct(); - sa.action = ab.build(); - } - catch (NumberFormatException e) { - log.debug("Invalid dst-port in: {} (error ignored)", subaction); - return null; - } - } - } - else { - log.debug("Invalid action: '{}'", subaction); - return null; - } - - return sa; - } - - private static byte[] get_mac_addr(Matcher n, String subaction, Logger log) { - byte[] macaddr = new byte[6]; - - for (int i=0; i<6; i++) { - if (n.group(i+1) != null) { - try { - macaddr[i] = get_byte("0x" + n.group(i+1)); - } - catch (NumberFormatException e) { - log.debug("Invalid src-mac in: '{}' (error ignored)", subaction); - return null; - } - } - else { - log.debug("Invalid src-mac in: '{}' (null, error ignored)", subaction); - return null; - } - } - - return macaddr; - } - - private static int get_ip_addr(Matcher n, String subaction, Logger log) { - int ipaddr = 0; - - for (int i=0; i<4; i++) { - if (n.group(i+1) != null) { - try { - ipaddr = ipaddr<<8; - ipaddr = ipaddr | get_int(n.group(i+1)); - } - catch (NumberFormatException e) { - log.debug("Invalid src-ip in: '{}' (error ignored)", subaction); - return 0; - } - } - else { - log.debug("Invalid src-ip in: '{}' (null, error ignored)", subaction); - return 0; - } - } - - return ipaddr; - } - - // Parse int as decimal, hex (start with 0x or #) or octal (starts with 0) - private static int get_int(String str) { - return Integer.decode(str); - } - - // Parse short as decimal, hex (start with 0x or #) or octal (starts with 0) - private static short get_short(String str) { - return (short)(int)Integer.decode(str); - } - - // Parse byte as decimal, hex (start with 0x or #) or octal (starts with 0) - private static byte get_byte(String str) { - return Integer.decode(str).byteValue(); - } - + protected static Logger log = LoggerFactory.getLogger(StaticFlowEntries.class); + private static final int INFINITE_TIMEOUT = 0; + + /** + * This function generates a random hash for the bottom half of the cookie + * + * @param fm + * @param userCookie + * @param name + * @return A cookie that encodes the application ID and a hash + */ + public static U64 computeEntryCookie(int userCookie, String name) { + // flow-specific hash is next 20 bits LOOK! who knows if this + int prime = 211; + int flowHash = 2311; + for (int i=0; i < name.length(); i++) { + flowHash = flowHash * prime + (int)name.charAt(i); + } + + return AppCookie.makeCookie(StaticFlowEntryPusher.STATIC_FLOW_APP_ID, flowHash); + } + + /** + * Sets defaults for an OFFlowMod used in the StaticFlowEntryPusher + * @param fm The OFFlowMod to set defaults for + * @param entryName The name of the entry. Used to compute the cookie. + */ + public static void initDefaultFlowMod(OFFlowMod.Builder fmb, String entryName) { + fmb.setIdleTimeout(INFINITE_TIMEOUT) // not setting these would also work + .setHardTimeout(INFINITE_TIMEOUT) + .setBufferId(OFBufferId.NO_BUFFER) + .setOutPort(OFPort.ANY) + .setCookie(computeEntryCookie(0, entryName)) + .setPriority(Integer.MAX_VALUE); + return; + } + + /** + * Gets the entry name of a flow mod + * @param fmJson The OFFlowMod in a JSON representation + * @return The name of the OFFlowMod, null if not found + * @throws IOException If there was an error parsing the JSON + */ + public static String getEntryNameFromJson(String fmJson) throws IOException{ + MappingJsonFactory f = new MappingJsonFactory(); + JsonParser jp; + + try { + jp = f.createJsonParser(fmJson); + } catch (JsonParseException e) { + throw new IOException(e); + } + + jp.nextToken(); + if (jp.getCurrentToken() != JsonToken.START_OBJECT) { + throw new IOException("Expected START_OBJECT"); + } + + while (jp.nextToken() != JsonToken.END_OBJECT) { + if (jp.getCurrentToken() != JsonToken.FIELD_NAME) { + throw new IOException("Expected FIELD_NAME"); + } + + String n = jp.getCurrentName(); + jp.nextToken(); + if (jp.getText().equals("")) + continue; + + if (n == StaticFlowEntryPusher.COLUMN_NAME) + return jp.getText(); + } + return null; + } + + /** + * Parses an OFFlowMod (and it's inner Match) to the storage entry format. + * @param fm The FlowMod to parse + * @param sw The switch the FlowMod is going to be installed on + * @param name The name of this static flow entry + * @return A Map representation of the storage entry + */ + public static Map<String, Object> flowModToStorageEntry(OFFlowMod fm, String sw, String name) { + Map<String, Object> entry = new HashMap<String, Object>(); + entry.put(StaticFlowEntryPusher.COLUMN_NAME, name); + entry.put(StaticFlowEntryPusher.COLUMN_SWITCH, sw); + entry.put(StaticFlowEntryPusher.COLUMN_ACTIVE, Boolean.toString(true)); + entry.put(StaticFlowEntryPusher.COLUMN_PRIORITY, Integer.toString(fm.getPriority())); + + if ((fm.getActions() != null) && (fm.getActions().size() > 0)) { + entry.put(StaticFlowEntryPusher.COLUMN_ACTIONS, ActionUtils.actionsToString(fm.getActions(), log)); + } + + Match match = fm.getMatch(); + boolean setTOS = false; + Iterator<MatchField<?>> itr = match.getMatchFields().iterator(); // only get exact or masked fields (not fully wildcarded) + while(itr.hasNext()) { + @SuppressWarnings("rawtypes") // this is okay here + MatchField mf = itr.next(); + switch (mf.id) { + case IN_PORT: // iterates over only exact/masked fields. No need to check for null entries. + entry.put(StaticFlowEntryPusher.COLUMN_IN_PORT, Integer.toString((match.get(MatchField.IN_PORT)).getPortNumber())); + break; + case ETH_SRC: + entry.put(StaticFlowEntryPusher.COLUMN_DL_SRC, match.get(MatchField.ETH_SRC).toString()); + break; + case ETH_DST: + entry.put(StaticFlowEntryPusher.COLUMN_DL_DST, match.get(MatchField.ETH_DST).toString()); + break; + case VLAN_VID: + entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN, match.get(MatchField.VLAN_VID).getVlan()); + break; + case VLAN_PCP: + entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, Byte.toString(match.get(MatchField.VLAN_PCP).getValue())); + break; + case ETH_TYPE: + entry.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, match.get(MatchField.ETH_TYPE).getValue()); + break; + case IP_ECN: // TOS = [DSCP bits 0-5] + [ECN bits 6-7] --> bitwise OR to get TOS byte + if (setTOS) { //TODO @Ryan need to break TOS into ECN and DSCP columns + entry.put(StaticFlowEntryPusher.COLUMN_NW_TOS, + Byte.toString((byte) (match.get(MatchField.IP_ECN).getEcnValue() + | (Byte.parseByte(entry.get(StaticFlowEntryPusher.COLUMN_NW_TOS).toString()))))); + } else { + entry.put(StaticFlowEntryPusher.COLUMN_NW_TOS, Byte.toString((byte) (match.get(MatchField.IP_ECN).getEcnValue()))); + } + setTOS = true; + break; + case IP_DSCP: + if (setTOS) { + entry.put(StaticFlowEntryPusher.COLUMN_NW_TOS, + Byte.toString((byte) (match.get(MatchField.IP_DSCP).getDscpValue() + | (Byte.parseByte(entry.get(StaticFlowEntryPusher.COLUMN_NW_TOS).toString()))))); + } else { + entry.put(StaticFlowEntryPusher.COLUMN_NW_TOS, Byte.toString((byte) (match.get(MatchField.IP_ECN).getEcnValue()))); + } + setTOS = true; + break; + case IP_PROTO: + entry.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, Short.toString(match.get(MatchField.IP_PROTO).getIpProtocolNumber())); + break; + case IPV4_SRC: + entry.put(StaticFlowEntryPusher.COLUMN_NW_SRC, match.get(MatchField.IPV4_SRC).toString()); + break; + case IPV4_DST: + entry.put(StaticFlowEntryPusher.COLUMN_NW_DST, match.get(MatchField.IPV4_DST).toString()); + break; + case TCP_SRC: + entry.put(StaticFlowEntryPusher.COLUMN_TP_SRC, match.get(MatchField.TCP_SRC).getPort()); + break; + case UDP_SRC: + entry.put(StaticFlowEntryPusher.COLUMN_TP_SRC, match.get(MatchField.UDP_SRC).getPort()); + break; + case SCTP_SRC: + entry.put(StaticFlowEntryPusher.COLUMN_TP_SRC, match.get(MatchField.SCTP_SRC).getPort()); + break; + case TCP_DST: + entry.put(StaticFlowEntryPusher.COLUMN_TP_DST, match.get(MatchField.TCP_DST).getPort()); + break; + case UDP_DST: + entry.put(StaticFlowEntryPusher.COLUMN_TP_DST, match.get(MatchField.UDP_DST).getPort()); + break; + case SCTP_DST: + entry.put(StaticFlowEntryPusher.COLUMN_TP_DST, match.get(MatchField.SCTP_DST).getPort()); + break; + case ICMPV4_TYPE: + entry.put(StaticFlowEntryPusher.COLUMN_ICMP_TYPE, match.get(MatchField.ICMPV4_TYPE).getType()); + break; + case ICMPV4_CODE: + entry.put(StaticFlowEntryPusher.COLUMN_ICMP_CODE, match.get(MatchField.ICMPV4_CODE).getCode()); + break; + case ARP_OP: + entry.put(StaticFlowEntryPusher.COLUMN_ARP_OPCODE, match.get(MatchField.ARP_OP).getOpcode()); + break; + case ARP_SHA: + entry.put(StaticFlowEntryPusher.COLUMN_ARP_SHA, match.get(MatchField.ARP_SHA).toString()); + break; + case ARP_THA: + entry.put(StaticFlowEntryPusher.COLUMN_ARP_DHA, match.get(MatchField.ARP_THA).toString()); + break; + case ARP_SPA: + entry.put(StaticFlowEntryPusher.COLUMN_ARP_SPA, match.get(MatchField.ARP_SPA).toString()); + break; + case ARP_TPA: + entry.put(StaticFlowEntryPusher.COLUMN_ARP_DPA, match.get(MatchField.ARP_TPA).toString()); + break; + case MPLS_LABEL: + entry.put(StaticFlowEntryPusher.COLUMN_MPLS_LABEL, match.get(MatchField.MPLS_LABEL).getValue()); + break; + case MPLS_TC: + entry.put(StaticFlowEntryPusher.COLUMN_MPLS_TC, match.get(MatchField.MPLS_TC).getValue()); + break; + // case MPLS_BOS not implemented in loxi + case METADATA: + entry.put(StaticFlowEntryPusher.COLUMN_METADATA, match.get(MatchField.METADATA).getValue().getValue()); + break; + // case TUNNEL_ID not implemented in loxi + // case PBB_ISID not implemented in loxi + default: + log.error("Unhandled Match when parsing OFFlowMod: {}, {}", mf, mf.id); + break; + } // end switch-case + } // end while + return entry; + } + + /** + * Turns a JSON formatted Static Flow Pusher string into a storage entry + * Expects a string in JSON along the lines of: + * { + * "switch": "AA:BB:CC:DD:EE:FF:00:11", + * "name": "flow-mod-1", + * "cookie": "0", + * "priority": "32768", + * "ingress-port": "1", + * "actions": "output=2", + * } + * @param fmJson The JSON formatted static flow pusher entry + * @return The map of the storage entry + * @throws IOException If there was an error parsing the JSON + */ + public static Map<String, Object> jsonToStorageEntry(String fmJson) throws IOException { + Map<String, Object> entry = new HashMap<String, Object>(); + MappingJsonFactory f = new MappingJsonFactory(); + JsonParser jp; + + try { + jp = f.createJsonParser(fmJson); + } catch (JsonParseException e) { + throw new IOException(e); + } + + jp.nextToken(); + if (jp.getCurrentToken() != JsonToken.START_OBJECT) { + throw new IOException("Expected START_OBJECT"); + } + + while (jp.nextToken() != JsonToken.END_OBJECT) { + if (jp.getCurrentToken() != JsonToken.FIELD_NAME) { + throw new IOException("Expected FIELD_NAME"); + } + + String n = jp.getCurrentName(); + jp.nextToken(); + if (jp.getText().equals("")) { + continue; + } + + // Java 7 switch-case on strings automatically checks for (deep) string equality. + // IMHO, this makes things easier on the eyes than if, else if, else's + + // A simplification is to make the column names the same strings as those used to + // compose the JSON flow entry; keeps all names/keys centralized and reduces liklihood + // for future string errors. + switch (n) { + case StaticFlowEntryPusher.COLUMN_NAME: + entry.put(StaticFlowEntryPusher.COLUMN_NAME, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_SWITCH: + entry.put(StaticFlowEntryPusher.COLUMN_SWITCH, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_ACTIVE: + entry.put(StaticFlowEntryPusher.COLUMN_ACTIVE, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_IDLE_TIMEOUT: // store TO's, but conditionally push them + entry.put(StaticFlowEntryPusher.COLUMN_IDLE_TIMEOUT, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_HARD_TIMEOUT: + entry.put(StaticFlowEntryPusher.COLUMN_HARD_TIMEOUT, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_PRIORITY: + entry.put(StaticFlowEntryPusher.COLUMN_PRIORITY, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_COOKIE: // set manually, or computed from name + entry.put(StaticFlowEntryPusher.COLUMN_COOKIE, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_IN_PORT: + entry.put(StaticFlowEntryPusher.COLUMN_IN_PORT, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_DL_SRC: + entry.put(StaticFlowEntryPusher.COLUMN_DL_SRC, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_DL_DST: + entry.put(StaticFlowEntryPusher.COLUMN_DL_DST, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_DL_VLAN: + entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP: + entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_DL_TYPE: + entry.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_NW_TOS: + entry.put(StaticFlowEntryPusher.COLUMN_NW_TOS, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_NW_PROTO: + entry.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_NW_SRC: + entry.put(StaticFlowEntryPusher.COLUMN_NW_SRC, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_NW_DST: + entry.put(StaticFlowEntryPusher.COLUMN_NW_DST, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_TP_SRC: + entry.put(StaticFlowEntryPusher.COLUMN_TP_SRC, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_TP_DST: + entry.put(StaticFlowEntryPusher.COLUMN_TP_DST, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_ICMP_TYPE: + entry.put(StaticFlowEntryPusher.COLUMN_ICMP_TYPE, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_ICMP_CODE: + entry.put(StaticFlowEntryPusher.COLUMN_ICMP_CODE, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_ARP_OPCODE: + entry.put(StaticFlowEntryPusher.COLUMN_ARP_OPCODE, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_ARP_SHA: + entry.put(StaticFlowEntryPusher.COLUMN_ARP_SHA, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_ARP_DHA: + entry.put(StaticFlowEntryPusher.COLUMN_ARP_DHA, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_ARP_SPA: + entry.put(StaticFlowEntryPusher.COLUMN_ARP_SPA, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_ARP_DPA: + entry.put(StaticFlowEntryPusher.COLUMN_ARP_DPA, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_MPLS_LABEL: + entry.put(StaticFlowEntryPusher.COLUMN_MPLS_LABEL, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_MPLS_TC: + entry.put(StaticFlowEntryPusher.COLUMN_MPLS_TC, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_MPLS_BOS: // not supported as match in loxi right now + entry.put(StaticFlowEntryPusher.COLUMN_MPLS_BOS, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_METADATA: + entry.put(StaticFlowEntryPusher.COLUMN_METADATA, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_TUNNEL_ID: // not supported as match in loxi right now + entry.put(StaticFlowEntryPusher.COLUMN_TUNNEL_ID, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_PBB_ISID: // not supported as match in loxi right now + entry.put(StaticFlowEntryPusher.COLUMN_PBB_ISID, jp.getText()); + break; + case StaticFlowEntryPusher.COLUMN_ACTIONS: + entry.put(StaticFlowEntryPusher.COLUMN_ACTIONS, jp.getText()); + break; + default: + log.error("Could not decode field from JSON string: {}", n); + } + } + return entry; + } } diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java index 48e7dfd8ab8d4a36dac83c2503630711c48d6664..66300be1ed663116829131f8be8e81cfedb45f01 100644 --- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java +++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java @@ -50,8 +50,9 @@ import net.floodlightcontroller.storage.IResultSet; import net.floodlightcontroller.storage.IStorageSourceListener; import net.floodlightcontroller.storage.IStorageSourceService; import net.floodlightcontroller.storage.StorageException; +import net.floodlightcontroller.util.ActionUtils; import net.floodlightcontroller.util.FlowModUtils; -import net.floodlightcontroller.util.MatchString; +import net.floodlightcontroller.util.MatchUtils; import org.projectfloodlight.openflow.protocol.OFFactories; import org.projectfloodlight.openflow.protocol.OFFlowAdd; @@ -60,7 +61,6 @@ import org.projectfloodlight.openflow.protocol.OFFlowMod; import org.projectfloodlight.openflow.protocol.OFFlowRemoved; import org.projectfloodlight.openflow.protocol.OFFlowRemovedReason; import org.projectfloodlight.openflow.protocol.OFPortDesc; -import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.protocol.OFMessage; import org.projectfloodlight.openflow.protocol.OFType; import org.projectfloodlight.openflow.types.DatapathId; @@ -87,34 +87,66 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService, public static final String TABLE_NAME = "controller_staticflowtableentry"; public static final String COLUMN_NAME = "name"; - public static final String COLUMN_SWITCH = "switch_id"; + public static final String COLUMN_SWITCH = "switch"; public static final String COLUMN_ACTIVE = "active"; public static final String COLUMN_IDLE_TIMEOUT = "idle_timeout"; public static final String COLUMN_HARD_TIMEOUT = "hard_timeout"; public static final String COLUMN_PRIORITY = "priority"; public static final String COLUMN_COOKIE = "cookie"; - public static final String COLUMN_WILDCARD = "wildcards"; - public static final String COLUMN_IN_PORT = "in_port"; - public static final String COLUMN_DL_SRC = "dl_src"; - public static final String COLUMN_DL_DST = "dl_dst"; - public static final String COLUMN_DL_VLAN = "dl_vlan"; - public static final String COLUMN_DL_VLAN_PCP = "dl_vlan_pcp"; - public static final String COLUMN_DL_TYPE = "dl_type"; - public static final String COLUMN_NW_TOS = "nw_tos"; - public static final String COLUMN_NW_PROTO = "nw_proto"; - public static final String COLUMN_NW_SRC = "nw_src"; // includes CIDR-style - // netmask, e.g. - // "128.8.128.0/24" - public static final String COLUMN_NW_DST = "nw_dst"; - public static final String COLUMN_TP_DST = "tp_dst"; - public static final String COLUMN_TP_SRC = "tp_src"; + + // Common location for Match Strings. Still the same, but relocated. + public static final String COLUMN_IN_PORT = MatchUtils.STR_IN_PORT; + + public static final String COLUMN_DL_SRC = MatchUtils.STR_DL_SRC; + public static final String COLUMN_DL_DST = MatchUtils.STR_DL_DST; + public static final String COLUMN_DL_VLAN = MatchUtils.STR_DL_VLAN; + public static final String COLUMN_DL_VLAN_PCP = MatchUtils.STR_DL_VLAN_PCP; + public static final String COLUMN_DL_TYPE = MatchUtils.STR_DL_TYPE; + + public static final String COLUMN_NW_TOS = MatchUtils.STR_NW_TOS; + public static final String COLUMN_NW_PROTO = MatchUtils.STR_NW_PROTO; + public static final String COLUMN_NW_SRC = MatchUtils.STR_NW_SRC; // includes CIDR-style netmask, e.g. "128.8.128.0/24" + public static final String COLUMN_NW_DST = MatchUtils.STR_NW_DST; + + public static final String COLUMN_TP_SRC = MatchUtils.STR_TP_SRC; + public static final String COLUMN_TP_DST = MatchUtils.STR_TP_DST; + + /* newly added matches for OF1.3 port start here */ + public static final String COLUMN_ICMP_TYPE = MatchUtils.STR_ICMP_TYPE; + public static final String COLUMN_ICMP_CODE = MatchUtils.STR_ICMP_CODE; + + public static final String COLUMN_ARP_OPCODE = MatchUtils.STR_ARP_OPCODE; + public static final String COLUMN_ARP_SHA = MatchUtils.STR_ARP_SHA; + public static final String COLUMN_ARP_DHA = MatchUtils.STR_ARP_DHA; + public static final String COLUMN_ARP_SPA = MatchUtils.STR_ARP_SPA; + public static final String COLUMN_ARP_DPA = MatchUtils.STR_ARP_DPA; + + public static final String COLUMN_MPLS_LABEL = MatchUtils.STR_MPLS_LABEL; + public static final String COLUMN_MPLS_TC = MatchUtils.STR_MPLS_TC; + public static final String COLUMN_MPLS_BOS = MatchUtils.STR_MPLS_BOS; + + public static final String COLUMN_METADATA = MatchUtils.STR_METADATA; + public static final String COLUMN_TUNNEL_ID = MatchUtils.STR_TUNNEL_ID; + + public static final String COLUMN_PBB_ISID = MatchUtils.STR_PBB_ISID; + /* end newly added matches TODO @Ryan should look into full IPv6 support*/ + public static final String COLUMN_ACTIONS = "actions"; + public static String ColumnNames[] = { COLUMN_NAME, COLUMN_SWITCH, COLUMN_ACTIVE, COLUMN_IDLE_TIMEOUT, COLUMN_HARD_TIMEOUT, - COLUMN_PRIORITY, COLUMN_COOKIE, COLUMN_WILDCARD, COLUMN_IN_PORT, + COLUMN_PRIORITY, COLUMN_COOKIE, COLUMN_IN_PORT, COLUMN_DL_SRC, COLUMN_DL_DST, COLUMN_DL_VLAN, COLUMN_DL_VLAN_PCP, COLUMN_DL_TYPE, COLUMN_NW_TOS, COLUMN_NW_PROTO, COLUMN_NW_SRC, - COLUMN_NW_DST, COLUMN_TP_DST, COLUMN_TP_SRC, COLUMN_ACTIONS }; + COLUMN_NW_DST, COLUMN_TP_DST, COLUMN_TP_SRC, + /* newly added matches for OF1.3 port start here */ + COLUMN_ICMP_TYPE, COLUMN_ICMP_CODE, + COLUMN_ARP_OPCODE, COLUMN_ARP_SHA, COLUMN_ARP_DHA, + COLUMN_ARP_SPA, COLUMN_ARP_DPA, + COLUMN_MPLS_LABEL, COLUMN_MPLS_TC, COLUMN_MPLS_BOS, + COLUMN_METADATA, COLUMN_TUNNEL_ID, COLUMN_PBB_ISID, + /* end newly added matches */ + COLUMN_ACTIONS }; protected IFloodlightProviderService floodlightProviderService; @@ -140,7 +172,7 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService, OFFlowMod f1 = entriesFromStorage.get(dpid).get(o1); OFFlowMod f2 = entriesFromStorage.get(dpid).get(o2); if (f1 == null || f2 == null) // sort active=false flows by key - return o1.compareTo(o2); + return o1.compareTo(o2); return U16.of(f1.getPriority()).getValue() - U16.of(f2.getPriority()).getValue(); } }; @@ -240,8 +272,7 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService, try { Map<String, Object> row; // null1=no predicate, null2=no ordering - IResultSet resultSet = storageSourceService.executeQuery(TABLE_NAME, - ColumnNames, null, null); + IResultSet resultSet = storageSourceService.executeQuery(TABLE_NAME, ColumnNames, null, null); for (Iterator<IResultSet> it = resultSet.iterator(); it.hasNext();) { row = it.next().getRow(); parseRow(row, entries); @@ -268,14 +299,10 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService, String entryName = null; StringBuffer matchString = new StringBuffer(); - - OFFlowMod.Builder fmb = OFFactories.getFactory(OFVersion.OF_13).buildFlowModify(); - OFFlowMod fm = fmb.build(); + OFFlowMod.Builder fmb = null; if (!row.containsKey(COLUMN_SWITCH) || !row.containsKey(COLUMN_NAME)) { - log.debug( - "skipping entry with missing required 'switch' or 'name' entry: {}", - row); + log.debug("skipping entry with missing required 'switch' or 'name' entry: {}", row); return; } // most error checking done with ClassCastException @@ -283,9 +310,15 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService, // first, snag the required entries, for debugging info switchName = (String) row.get(COLUMN_SWITCH); entryName = (String) row.get(COLUMN_NAME); - if (!entries.containsKey(switchName)) + if (!entries.containsKey(switchName)) { entries.put(switchName, new HashMap<String, OFFlowMod>()); - fm = StaticFlowEntries.initDefaultFlowMod(fm, entryName); + } + + // get the correct builder for the OF version supported by the switch + // TODO @Ryan this should arguably be a FlowAdd, not a FlowModify + fmb = OFFactories.getFactory(switchService.getSwitch(DatapathId.of(switchName)).getOFFactory().getVersion()).buildFlowModify(); + + StaticFlowEntries.initDefaultFlowMod(fmb, entryName); for (String key : row.keySet()) { if (row.get(key) == null) @@ -293,40 +326,33 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService, if (key.equals(COLUMN_SWITCH) || key.equals(COLUMN_NAME) || key.equals("id")) continue; // already handled - // explicitly ignore timeouts and wildcards - if (key.equals(COLUMN_HARD_TIMEOUT) || key.equals(COLUMN_IDLE_TIMEOUT) || - key.equals(COLUMN_WILDCARD)) + // explicitly ignore timeouts and wildcards + if (key.equals(COLUMN_HARD_TIMEOUT) || key.equals(COLUMN_IDLE_TIMEOUT)) continue; if (key.equals(COLUMN_ACTIVE)) { if (!Boolean.valueOf((String) row.get(COLUMN_ACTIVE))) { - log.debug("skipping inactive entry {} for switch {}", - entryName, switchName); + log.debug("skipping inactive entry {} for switch {}", entryName, switchName); entries.get(switchName).put(entryName, null); // mark this an inactive return; } } else if (key.equals(COLUMN_ACTIONS)){ - fm = StaticFlowEntries.parseActionString(fm, (String) row.get(COLUMN_ACTIONS), log); + ActionUtils.fromString(fmb, (String) row.get(COLUMN_ACTIONS), log); } else if (key.equals(COLUMN_COOKIE)) { - fm = fm.createBuilder().setCookie( - StaticFlowEntries.computeEntryCookie(fm, - Integer.valueOf((String) row.get(COLUMN_COOKIE)), - entryName)).build(); + fmb.setCookie(StaticFlowEntries.computeEntryCookie(Integer.valueOf((String) row.get(COLUMN_COOKIE)), entryName)); } else if (key.equals(COLUMN_PRIORITY)) { - fm = fm.createBuilder().setPriority(U16.t(Integer.valueOf((String) row.get(COLUMN_PRIORITY)))).build(); - } else { // the rest of the keys are for OFMatch().fromString() - if (matchString.length() > 0) + fmb.setPriority(U16.t(Integer.valueOf((String) row.get(COLUMN_PRIORITY)))); + } else { // the rest of the keys are for Match().fromString() + if (matchString.length() > 0) { matchString.append(","); - matchString.append(key + "=" + row.get(key).toString()); + } + matchString.append(key + "=" + row.get(key).toString()); } } } catch (ClassCastException e) { if (entryName != null && switchName != null) { - log.warn( - "Skipping entry {} on switch {} with bad data : " - + e.getMessage(), entryName, switchName); + log.warn("Skipping entry {} on switch {} with bad data : " + e.getMessage(), entryName, switchName); } else { - log.warn("Skipping entry with bad data: {} :: {} ", - e.getMessage(), e.getStackTrace()); + log.warn("Skipping entry with bad data: {} :: {} ", e.getMessage(), e.getStackTrace()); } } @@ -334,15 +360,13 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService, try { //TODO @Ryan new fromString() method here. Should verify it especially - fm = fm.createBuilder().setMatch(MatchString.fromString(match, OFVersion.OF_13)).build(); + fmb.setMatch(MatchUtils.fromString(match, fmb.getVersion())); } catch (IllegalArgumentException e) { - log.debug( - "ignoring flow entry {} on switch {} with illegal OFMatch() key: " - + match, entryName, switchName); + log.debug("ignoring flow entry {} on switch {} with illegal OFMatch() key: " + match, entryName, switchName); return; } - entries.get(switchName).put(entryName, fm); + entries.get(switchName).put(entryName, fmb.build()); // add the FlowMod message to the table } @Override @@ -578,7 +602,7 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService, * never expire. */ if (AppCookie.extractApp(cookie) == STATIC_FLOW_APP_ID) { - if (OFFlowRemovedReason.DELETE.equals(msg.getReason())) //TODO @Ryan getReason() --> short vs. enum??? + if (OFFlowRemovedReason.DELETE.equals(msg.getReason())) log.error("Got a FlowRemove message for a infinite " + "timeout flow: {} from switch {}", msg, sw); // Stop the processing chain since we sent the delete. @@ -646,16 +670,11 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService, } @Override - public void init(FloodlightModuleContext context) - throws FloodlightModuleException { - floodlightProviderService = - context.getServiceImpl(IFloodlightProviderService.class); - switchService = - context.getServiceImpl(IOFSwitchService.class); - storageSourceService = - context.getServiceImpl(IStorageSourceService.class); - restApiService = - context.getServiceImpl(IRestApiService.class); + public void init(FloodlightModuleContext context) throws FloodlightModuleException { + floodlightProviderService = context.getServiceImpl(IFloodlightProviderService.class); + switchService = context.getServiceImpl(IOFSwitchService.class); + storageSourceService = context.getServiceImpl(IStorageSourceService.class); + restApiService = context.getServiceImpl(IRestApiService.class); haListener = new HAListenerDelegate(); } diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/web/ListStaticFlowEntriesResource.java b/src/main/java/net/floodlightcontroller/staticflowentry/web/ListStaticFlowEntriesResource.java index 37a39bc864bbaefbd46543ff1d2ce6ab1cac5b72..d9ec69c1eba4f379fb8d02a18ba42f24a29e6118 100644 --- a/src/main/java/net/floodlightcontroller/staticflowentry/web/ListStaticFlowEntriesResource.java +++ b/src/main/java/net/floodlightcontroller/staticflowentry/web/ListStaticFlowEntriesResource.java @@ -47,14 +47,12 @@ public class ListStaticFlowEntriesResource extends ServerResource { return sfpService.getFlows(); } else { try { - Map<String, Map<String, OFFlowMod>> retMap = - new HashMap<String, Map<String, OFFlowMod>>(); + Map<String, Map<String, OFFlowMod>> retMap = new HashMap<String, Map<String, OFFlowMod>>(); retMap.put(param, sfpService.getFlows(DatapathId.of(param))); return retMap; } catch (NumberFormatException e){ - setStatus(Status.CLIENT_ERROR_BAD_REQUEST, - ControllerSwitchesResource.DPID_ERROR); + setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ControllerSwitchesResource.DPID_ERROR); } } return null; diff --git a/src/main/java/net/floodlightcontroller/testmodule/TestModule.java b/src/main/java/net/floodlightcontroller/testmodule/TestModule.java index 7d55e68748e68e7de855bba2ed204d794d93b3ad..886a33750b3d0fb37b766c2b3eea2f1e59917405 100644 --- a/src/main/java/net/floodlightcontroller/testmodule/TestModule.java +++ b/src/main/java/net/floodlightcontroller/testmodule/TestModule.java @@ -3,28 +3,61 @@ package net.floodlightcontroller.testmodule; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Map; import org.projectfloodlight.openflow.protocol.OFFactories; +import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFFlowAdd; +import org.projectfloodlight.openflow.protocol.OFOxmClass; +import org.projectfloodlight.openflow.protocol.OFPortDesc; +import org.projectfloodlight.openflow.protocol.OFSetConfig; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.protocol.action.OFAction; import org.projectfloodlight.openflow.protocol.action.OFActionOutput; +import org.projectfloodlight.openflow.protocol.action.OFActionSetField; +import org.projectfloodlight.openflow.protocol.action.OFActionSetNwSrc; +import org.projectfloodlight.openflow.protocol.match.Match; +import org.projectfloodlight.openflow.protocol.match.MatchField; +import org.projectfloodlight.openflow.protocol.oxm.OFOxm; +import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthSrc; +import org.projectfloodlight.openflow.types.ArpOpcode; import org.projectfloodlight.openflow.types.DatapathId; +import org.projectfloodlight.openflow.types.EthType; +import org.projectfloodlight.openflow.types.ICMPv4Code; +import org.projectfloodlight.openflow.types.ICMPv4Type; +import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IpDscp; +import org.projectfloodlight.openflow.types.IpEcn; +import org.projectfloodlight.openflow.types.IpProtocol; +import org.projectfloodlight.openflow.types.MacAddress; +import org.projectfloodlight.openflow.types.OFBufferId; +import org.projectfloodlight.openflow.types.OFMetadata; import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.OFValueType; +import org.projectfloodlight.openflow.types.OFVlanVidMatch; +import org.projectfloodlight.openflow.types.TransportPort; +import org.projectfloodlight.openflow.types.U32; +import org.projectfloodlight.openflow.types.U8; +import org.projectfloodlight.openflow.types.VlanPcp; +import org.projectfloodlight.openflow.types.VlanVid; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.floodlightcontroller.core.IOFSwitchListener; +import net.floodlightcontroller.core.PortChangeType; +import net.floodlightcontroller.core.internal.IOFSwitchService; 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.staticflowentry.IStaticFlowEntryPusherService; -public class TestModule implements IFloodlightModule { +public class TestModule implements IFloodlightModule, IOFSwitchListener { private static IStaticFlowEntryPusherService sfps; - protected static Logger log; + private static IOFSwitchService switchService; + private static Logger log; @Override public Collection<Class<? extends IFloodlightService>> getModuleServices() { @@ -40,6 +73,7 @@ public class TestModule implements IFloodlightModule { public Collection<Class<? extends IFloodlightService>> getModuleDependencies() { Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>(); l.add(IStaticFlowEntryPusherService.class); + l.add(IOFSwitchService.class); return l; } @@ -47,6 +81,8 @@ public class TestModule implements IFloodlightModule { public void init(FloodlightModuleContext context) throws FloodlightModuleException { sfps = context.getServiceImpl(IStaticFlowEntryPusherService.class); + switchService = context.getServiceImpl(IOFSwitchService.class); + switchService.addOFSwitchListener(this); log = LoggerFactory.getLogger(TestModule.class); if (sfps == null) { log.error("Static Flow Pusher Service not found!"); @@ -56,29 +92,99 @@ public class TestModule implements IFloodlightModule { @Override public void startUp(FloodlightModuleContext context) throws FloodlightModuleException { - OFFlowAdd.Builder fmb = /*sw.getOFFactory()*/OFFactories.getFactory(OFVersion.OF_13).buildFlowAdd(); - //Match.Builder mb = OFFactories.getFactory(OFVersion.OF_13).buildMatch(); - - //fmb.setBufferId(OFBufferId.NO_BUFFER) - //fmb.setXid(pi.getXid()); - /*.setMatch(pi.getMatch())*/ - - // set actions - OFActionOutput.Builder actionBuilder = /*sw.getOFFactory()*/OFFactories.getFactory(OFVersion.OF_13).actions().buildOutput(); - actionBuilder.setPort(OFPort.ALL); - fmb.setActions(Collections.singletonList((OFAction) actionBuilder.build())); - fmb.setOutPort(OFPort.ALL); - // we aren't matching anything specifically, so all should be wildcarded by default - // in on any port, with any header attributes, send out all ports = Hub module - try { + + } + + @Override + public void switchAdded(DatapathId switchId) { + OFFactory factory = switchService.getSwitch(switchId).getOFFactory(); + OFFlowAdd.Builder fmb = factory.buildFlowAdd(); + List<OFAction> actions = new ArrayList<OFAction>(); + Match.Builder mb = factory.buildMatch(); + + /*try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); - } - sfps.addFlow("test-flow", fmb.build(), DatapathId.of(1)); // This should add the flow, regardless whether or not the switch is connected, I believe. If it connects in a second, it should be pushed. + }*/ + + //TODO @Ryan set a bunch of matches. Test for an OF1.0 and OF1.3 switch. See what happens if they are incorrectly applied. + /* L2 and ICMP TESTS mb.setExact(MatchField.ETH_TYPE, EthType.IPv4); + mb.setExact(MatchField.ETH_SRC, MacAddress.BROADCAST); + mb.setExact(MatchField.ETH_DST, MacAddress.BROADCAST); + mb.setExact(MatchField.IPV4_SRC, IPv4Address.of("127.1.1.1")); + mb.setExact(MatchField.IPV4_DST, IPv4Address.of("128.2.2.2")); + mb.setExact(MatchField.IP_PROTO, IpProtocol.ICMP); + mb.setExact(MatchField.ICMPV4_CODE, ICMPv4Code.of((short)1)); + mb.setExact(MatchField.ICMPV4_TYPE, ICMPv4Type.ECHO); */ + + /* ARP TESTS mb.setExact(MatchField.ETH_TYPE, EthType.ARP); + mb.setExact(MatchField.ARP_OP, ArpOpcode.REQUEST); + mb.setExact(MatchField.ARP_SHA, MacAddress.BROADCAST); + mb.setExact(MatchField.ARP_SPA, IPv4Address.of("130.127.39.241")); + mb.setExact(MatchField.ARP_THA, MacAddress.BROADCAST); + mb.setExact(MatchField.ARP_TPA, IPv4Address.of("130.127.39.241")); */ + + /* TP, IP OPT, VLAN TESTS */ mb.setExact(MatchField.ETH_TYPE, EthType.IPv4); + mb.setExact(MatchField.VLAN_PCP, VlanPcp.of((byte) 1)); // might as well test these now too + mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlan(512)); + mb.setExact(MatchField.MPLS_LABEL, U32.of(32)); + mb.setExact(MatchField.MPLS_TC, U8.of((short)64)); + mb.setExact(MatchField.IP_ECN, IpEcn.ECN_10); // and these + mb.setExact(MatchField.IP_DSCP, IpDscp.DSCP_16); + mb.setExact(MatchField.IP_PROTO, IpProtocol.UDP); // with tcp, udp, sctp + mb.setExact(MatchField.UDP_SRC, TransportPort.of(22)); + mb.setExact(MatchField.UDP_DST, TransportPort.of(80)); + + /* MPLS TESTS mb.setExact(MatchField.ETH_TYPE, EthType.MPLS_MULTICAST); + mb.setExact(MatchField.MPLS_LABEL, U32.of(18)); + mb.setExact(MatchField.MPLS_TC, U8.of((short)4));*/ + + /* METADATA TEST + mb.setExact(MatchField.METADATA, OFMetadata.ofRaw(1)); */ + + + //TODO @Ryan set a bunch of actions. "" "" """ """""" + OFActionOutput.Builder actionBuilder = factory.actions().buildOutput(); + actions.add(factory.actions().output(OFPort.of(1), Integer.MAX_VALUE)); + actions.add(factory.actions().setField(factory.oxms().ethSrc(MacAddress.BROADCAST))); + actions.add(factory.actions().setField(factory.oxms().ethDst(MacAddress.BROADCAST))); + actions.add(factory.actions().setField(factory.oxms().ipv4Src(IPv4Address.of("127.0.1.2")))); + actions.add(factory.actions().setField(factory.oxms().ipv4Src(IPv4Address.of("128.0.3.4")))); + + + fmb.setActions(actions); + fmb.setMatch(mb.build()); + + sfps.addFlow("test-flow", fmb.build(), switchId); //sfps.deleteFlow("test-flow"); + + } + + @Override + public void switchRemoved(DatapathId switchId) { + // TODO Auto-generated method stub + + } + + @Override + public void switchActivated(DatapathId switchId) { + // TODO Auto-generated method stub + + } + + @Override + public void switchPortChanged(DatapathId switchId, OFPortDesc port, + PortChangeType type) { + // TODO Auto-generated method stub + + } + @Override + public void switchChanged(DatapathId switchId) { + // TODO Auto-generated method stub + } } diff --git a/src/main/java/net/floodlightcontroller/util/ActionUtils.java b/src/main/java/net/floodlightcontroller/util/ActionUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..cd6962531b5fbf42f3fd148b9bce5b6118e4b8bb --- /dev/null +++ b/src/main/java/net/floodlightcontroller/util/ActionUtils.java @@ -0,0 +1,549 @@ +package net.floodlightcontroller.util; + +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import net.floodlightcontroller.core.annotations.LogMessageDoc; + +import org.projectfloodlight.openflow.protocol.OFFactories; +import org.projectfloodlight.openflow.protocol.OFFlowMod; +import org.projectfloodlight.openflow.protocol.OFVersion; +import org.projectfloodlight.openflow.protocol.action.OFAction; +import org.projectfloodlight.openflow.protocol.action.OFActionEnqueue; +import org.projectfloodlight.openflow.protocol.action.OFActionOutput; +import org.projectfloodlight.openflow.protocol.action.OFActionSetDlDst; +import org.projectfloodlight.openflow.protocol.action.OFActionSetDlSrc; +import org.projectfloodlight.openflow.protocol.action.OFActionSetNwDst; +import org.projectfloodlight.openflow.protocol.action.OFActionSetNwSrc; +import org.projectfloodlight.openflow.protocol.action.OFActionSetNwTos; +import org.projectfloodlight.openflow.protocol.action.OFActionSetTpDst; +import org.projectfloodlight.openflow.protocol.action.OFActionSetTpSrc; +import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanPcp; +import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanVid; +import org.projectfloodlight.openflow.protocol.action.OFActionStripVlan; +import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.MacAddress; +import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.TransportPort; +import org.projectfloodlight.openflow.types.VlanPcp; +import org.projectfloodlight.openflow.types.VlanVid; +import org.slf4j.Logger; + +/** + * OFAction helper functions. Use with any OpenFlowJ-Loxi Action. + * + * Includes string methods refactored from StaticFlowEntryPusher + * + * @author Ryan Izard <ryan.izard@bigswitch.com, rizard@g.clemson.edu> + */ +public class ActionUtils { + public static final String STR_OUTPUT = "output"; + public static final String STR_ENQUEUE = "enqueue"; + public static final String STR_VLAN_STRIP = "strip_vlan"; + public static final String STR_VLAN_POP = "pop_vlan"; + public static final String STR_VLAN_PUSH = "push_vlan"; + public static final String STR_VLAN_SET_PCP = "set_vlan_priority"; + public static final String STR_VLAN_SET_VID = "set_vlan_id"; + public static final String STR_QUEUE_SET = "set_queue"; + public static final String STR_DL_SRC_SET = "set_src_mac"; + public static final String STR_DL_DST_SET = "set_dst_mac"; + public static final String STR_NW_SRC_SET = "set_src_ip"; + public static final String STR_NW_DST_SET = "set_dst_ip"; + public static final String STR_NW_ECN_SET = "set_nw_ecn"; + public static final String STR_NW_TOS_SET = "set_tos_bits"; + public static final String STR_NW_TTL_SET = "set_ip_ttl"; + public static final String STR_NW_TTL_DEC = "dec_ip_ttl"; + public static final String STR_MPLS_LABEL_SET = "set_mpls_label"; + public static final String STR_MPLS_TC_SET = "set_mpls_tc"; + public static final String STR_MPLS_TTL_SET = "set_mpls_ttl"; + public static final String STR_MPLS_TTL_DEC = "dec_mpls_ttl"; + public static final String STR_MPLS_PUSH = "push_mpls"; + public static final String STR_MPLS_POP = "pop_mpls"; + public static final String STR_TP_SRC_SET = "set_src_port"; + public static final String STR_TP_DST_SET = "set_dst_port"; + public static final String STR_TTL_IN_COPY = "copy_ttl_in"; + public static final String STR_TTL_OUT_COPY = "copy_ttl_out"; + public static final String STR_PBB_PUSH = "push_pbb"; + public static final String STR_PBB_POP = "pop_pbb"; + public static final String STR_EXPERIMENTER = "experimenter"; + public static final String STR_GROUP = "group"; + public static final String STR_FIELD_SET = "set_field"; + + /** + * Returns a String representation of all the OpenFlow actions. + * @param actions A list of OFActions to encode into one string + * @return A dpctl-style string of the actions + */ + @LogMessageDoc(level="ERROR", + message="Could not decode action {action}", + explanation="A static flow entry contained an invalid action", + recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) + public static String actionsToString(List<OFAction> actions, Logger log) { + StringBuilder sb = new StringBuilder(); + for (OFAction a : actions) { + if (sb.length() > 0) { + sb.append(','); + } + switch(a.getType()) { + case OUTPUT: + sb.append(STR_OUTPUT + "=" + ((OFActionOutput)a).getPort().toString()); + break; + case ENQUEUE: + long queue = ((OFActionEnqueue)a).getQueueId(); + OFPort port = ((OFActionEnqueue)a).getPort(); + sb.append(STR_ENQUEUE + "=" + Integer.toString(port.getPortNumber()) + ":0x" + String.format("%02x", queue)); + break; + case STRIP_VLAN: + sb.append(STR_VLAN_STRIP); + break; + case SET_VLAN_VID: + sb.append(STR_VLAN_SET_VID + "=" + + ((OFActionSetVlanVid)a).getVlanVid().toString()); + break; + case SET_VLAN_PCP: + sb.append(STR_VLAN_SET_PCP + "=" + + Byte.toString(((OFActionSetVlanPcp)a).getVlanPcp().getValue())); + break; + case SET_DL_SRC: + sb.append(STR_DL_SRC_SET + "=" + + ((OFActionSetDlSrc)a).getDlAddr().toString()); + break; + case SET_DL_DST: + sb.append(STR_DL_DST_SET + "=" + + ((OFActionSetDlDst)a).getDlAddr().toString()); + break; + case SET_NW_TOS: + sb.append(STR_NW_TOS_SET + "=" + + Short.toString(((OFActionSetNwTos)a).getNwTos())); + break; + case SET_NW_SRC: + sb.append(STR_NW_SRC_SET + "=" + + ((OFActionSetNwSrc)a).getNwAddr().toString()); + break; + case SET_NW_DST: + sb.append(STR_NW_DST_SET + "=" + + ((OFActionSetNwDst)a).getNwAddr().toString()); + break; + case SET_TP_SRC: + sb.append(STR_TP_SRC_SET + "=" + + ((OFActionSetTpSrc)a).getTpPort().toString()); + break; + case SET_TP_DST: + sb.append(STR_TP_DST_SET + "=" + + ((OFActionSetTpDst)a).getTpPort().toString()); + break; + default: + log.error("Could not decode action: {}", a); + break; + } + + } + return sb.toString(); + } + + /** + * Parses OFFlowMod actions from strings. + * @param fmb The OFFlowMod.Builder to set the actions for + * @param bigString The string containing all the actions + * @param log A logger to log for errors. + */ + @LogMessageDoc(level="ERROR", + message="Unexpected action '{action}', '{subaction}'", + explanation="A static flow entry contained an invalid action", + recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) + public static void fromString(OFFlowMod.Builder fmb, String bigString, Logger log) { + List<OFAction> actions = new LinkedList<OFAction>(); + if (bigString != null) { + bigString = bigString.toLowerCase(); + for (String actionToDecode : bigString.split(",")) { + String action = actionToDecode.split("[=:]")[0]; + OFAction a = null; + + if (action.equals(STR_OUTPUT)) { + a = decode_output(actionToDecode, fmb.getVersion(), log); + } + else if (action.equals(STR_ENQUEUE)) { + a = decode_enqueue(actionToDecode, fmb.getVersion(), log); + } + else if (action.equals(STR_VLAN_STRIP)) { + a = decode_strip_vlan(actionToDecode, fmb.getVersion(), log); + } + else if (action.equals(STR_VLAN_SET_VID)) { + a = decode_set_vlan_id(actionToDecode, fmb.getVersion(), log); + } + else if (action.equals(STR_VLAN_SET_PCP)) { + a = decode_set_vlan_priority(actionToDecode, fmb.getVersion(), log); + } + else if (action.equals(STR_DL_SRC_SET)) { + a = decode_set_src_mac(actionToDecode, fmb.getVersion(), log); + } + else if (action.equals(STR_DL_DST_SET)) { + a = decode_set_dst_mac(actionToDecode, fmb.getVersion(), log); + } + else if (action.equals(STR_NW_TOS_SET)) { + a = decode_set_tos_bits(actionToDecode, fmb.getVersion(), log); + } + else if (action.equals(STR_NW_SRC_SET)) { + a = decode_set_src_ip(actionToDecode, fmb.getVersion(), log); + } + else if (action.equals(STR_NW_DST_SET)) { + a = decode_set_dst_ip(actionToDecode, fmb.getVersion(), log); + } + else if (action.equals(STR_TP_SRC_SET)) { + a = decode_set_src_port(actionToDecode, fmb.getVersion(), log); + } + else if (action.equals(STR_TP_DST_SET)) { + a = decode_set_dst_port(actionToDecode, fmb.getVersion(), log); + } + else { + log.error("Unexpected action '{}', '{}'", action, actionToDecode); + } + + if (a != null) { + actions.add(a); + } + } + } + log.debug("action {}", actions); + + fmb.setActions(actions); + return; + } + + @LogMessageDoc(level="ERROR", + message="Invalid subaction: '{subaction}'", + explanation="A static flow entry contained an invalid subaction", + recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG) + private static OFActionOutput decode_output(String actionToDecode, OFVersion version, Logger log) { + Matcher n = Pattern.compile(STR_OUTPUT + + "=(?:((?:0x)?\\d+)|(all)|(controller)|(local)|(ingress-port)|(normal)|(flood))").matcher(actionToDecode); + if (n.matches()) { + OFActionOutput.Builder ab = OFFactories.getFactory(version).actions().buildOutput(); + OFPort port = OFPort.ANY; + if (n.group(1) != null) { + try { + port = OFPort.of(get_short(n.group(1))); + } + catch (NumberFormatException e) { + log.debug("Invalid port in: '{}' (error ignored)", actionToDecode); + return null; + } + } + else if (n.group(2) != null) + port = OFPort.ALL; + else if (n.group(3) != null) + port = OFPort.CONTROLLER; + else if (n.group(4) != null) + port = OFPort.LOCAL; + else if (n.group(5) != null) + port = OFPort.IN_PORT; + else if (n.group(6) != null) + port = OFPort.NORMAL; + else if (n.group(7) != null) + port = OFPort.FLOOD; + ab.setPort(port); + log.debug("action {}", ab.build()); + return ab.build(); + } + else { + log.error("Invalid subaction: '{}'", actionToDecode); + return null; + } + } + + private static OFActionEnqueue decode_enqueue(String actionToDecode, OFVersion version, Logger log) { + Matcher n = Pattern.compile(STR_ENQUEUE + "=(?:((?:0x)?\\d+)\\:((?:0x)?\\d+))").matcher(actionToDecode); + if (n.matches()) { + OFPort port = OFPort.of(0); + if (n.group(1) != null) { + try { + port = OFPort.of(get_short(n.group(1))); + } + catch (NumberFormatException e) { + log.debug("Invalid port-num in: '{}' (error ignored)", actionToDecode); + return null; + } + } + + int queueid = 0; + if (n.group(2) != null) { + try { + queueid = get_int(n.group(2)); + } + catch (NumberFormatException e) { + log.debug("Invalid queue-id in: '{}' (error ignored)", actionToDecode); + return null; + } + } + OFActionEnqueue.Builder ab = OFFactories.getFactory(version).actions().buildEnqueue(); + ab.setPort(port); + ab.setQueueId(queueid); + log.debug("action {}", ab.build()); + return ab.build(); + } + else { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + } + + private static OFActionStripVlan decode_strip_vlan(String actionToDecode, OFVersion version, Logger log) { + Matcher n = Pattern.compile(STR_VLAN_STRIP).matcher(actionToDecode); + if (n.matches()) { + OFActionStripVlan a = OFFactories.getFactory(version).actions().stripVlan(); + log.debug("action {}", a); + return a; + } + else { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + } + + private static OFActionSetVlanVid decode_set_vlan_id(String actionToDecode, OFVersion version, Logger log) { + Matcher n = Pattern.compile(STR_VLAN_SET_VID + "=((?:0x)?\\d+)").matcher(actionToDecode); + if (n.matches()) { + if (n.group(1) != null) { + try { + VlanVid vlanid = VlanVid.ofVlan(get_short(n.group(1))); + OFActionSetVlanVid.Builder ab = OFFactories.getFactory(version).actions().buildSetVlanVid(); + ab.setVlanVid(vlanid); + log.debug("action {}", ab.build()); + return ab.build(); + } + catch (NumberFormatException e) { + log.debug("Invalid VLAN in: {} (error ignored)", actionToDecode); + return null; + } + } + } + else { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + return null; + } + + private static OFActionSetVlanPcp decode_set_vlan_priority(String actionToDecode, OFVersion version, Logger log) { + Matcher n = Pattern.compile(STR_VLAN_SET_PCP + "=((?:0x)?\\d+)").matcher(actionToDecode); + if (n.matches()) { + if (n.group(1) != null) { + try { + VlanPcp prior = VlanPcp.of(get_byte(n.group(1))); + OFActionSetVlanPcp.Builder ab = OFFactories.getFactory(OFVersion.OF_13).actions().buildSetVlanPcp(); + ab.setVlanPcp(prior); + log.debug("action {}", ab.build()); + return ab.build(); + } + catch (NumberFormatException e) { + log.debug("Invalid VLAN priority in: {} (error ignored)", actionToDecode); + return null; + } + } + } + else { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + return null; + } + + private static OFActionSetDlSrc decode_set_src_mac(String actionToDecode, OFVersion version, Logger log) { + Matcher n = Pattern.compile(STR_DL_SRC_SET + + "=(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(actionToDecode); + if (n.matches()) { + MacAddress macaddr = MacAddress.of(get_mac_addr(n, actionToDecode, log)); + if (macaddr != null) { + OFActionSetDlSrc.Builder ab = OFFactories.getFactory(version).actions().buildSetDlSrc(); + ab.setDlAddr(macaddr); + log.debug("action {}", ab.build()); + return ab.build(); + } + } + else { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + return null; + } + + private static OFActionSetDlDst decode_set_dst_mac(String actionToDecode, OFVersion version, Logger log) { + Matcher n = Pattern.compile(STR_DL_DST_SET + + "=(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(actionToDecode); + if (n.matches()) { + MacAddress macaddr = MacAddress.of(get_mac_addr(n, actionToDecode, log)); + if (macaddr != null) { + OFActionSetDlDst.Builder ab = OFFactories.getFactory(version).actions().buildSetDlDst(); + ab.setDlAddr(macaddr); + log.debug("action {}", ab.build()); + return ab.build(); + } + } + else { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + return null; + } + + private static OFActionSetNwTos decode_set_tos_bits(String actionToDecode, OFVersion version, Logger log) { + Matcher n = Pattern.compile(STR_NW_TOS_SET + "=((?:0x)?\\d+)").matcher(actionToDecode); + if (n.matches()) { + if (n.group(1) != null) { + try { + byte tosbits = get_byte(n.group(1)); + OFActionSetNwTos.Builder ab = OFFactories.getFactory(version).actions().buildSetNwTos(); + ab.setNwTos(tosbits); + log.debug("action {}", ab.build()); + return ab.build(); + } + catch (NumberFormatException e) { + log.debug("Invalid dst-port in: {} (error ignored)", actionToDecode); + return null; + } + } + } + else { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + return null; + } + + private static OFActionSetNwSrc decode_set_src_ip(String actionToDecode, OFVersion version, Logger log) { + Matcher n = Pattern.compile(STR_NW_SRC_SET + "=(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(actionToDecode); + if (n.matches()) { + IPv4Address ipaddr = IPv4Address.of(get_ip_addr(n, actionToDecode, log)); + OFActionSetNwSrc.Builder ab = OFFactories.getFactory(version).actions().buildSetNwSrc(); + ab.setNwAddr(ipaddr); + log.debug("action {}", ab.build()); + return ab.build(); + } + else { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + } + + private static OFActionSetNwDst decode_set_dst_ip(String actionToDecode, OFVersion version, Logger log) { + Matcher n = Pattern.compile(STR_NW_DST_SET + "=(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(actionToDecode); + if (n.matches()) { + IPv4Address ipaddr = IPv4Address.of(get_ip_addr(n, actionToDecode, log)); + OFActionSetNwDst.Builder ab = OFFactories.getFactory(version).actions().buildSetNwDst(); + ab.setNwAddr(ipaddr); + log.debug("action {}", ab.build()); + return ab.build(); + } + else { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + } + + private static OFActionSetTpSrc decode_set_src_port(String actionToDecode, OFVersion version, Logger log) { + Matcher n = Pattern.compile(STR_TP_SRC_SET + "=((?:0x)?\\d+)").matcher(actionToDecode); + if (n.matches()) { + if (n.group(1) != null) { + try { + TransportPort portnum = TransportPort.of(get_short(n.group(1))); + OFActionSetTpSrc.Builder ab = OFFactories.getFactory(version).actions().buildSetTpSrc(); + ab.setTpPort(portnum); + log.debug("action {}", ab.build()); + return ab.build(); + } + catch (NumberFormatException e) { + log.debug("Invalid src-port in: {} (error ignored)", actionToDecode); + return null; + } + } + } + else { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + return null; + } + + private static OFAction decode_set_dst_port(String actionToDecode, OFVersion version, Logger log) { + Matcher n = Pattern.compile(STR_TP_DST_SET + "=((?:0x)?\\d+)").matcher(actionToDecode); + if (n.matches()) { + if (n.group(1) != null) { + try { + TransportPort portnum = TransportPort.of(get_short(n.group(1))); + OFActionSetTpDst.Builder ab = OFFactories.getFactory(version).actions().buildSetTpDst(); + ab.setTpPort(portnum); + log.debug("action {}", ab.build()); + return ab.build(); + } + catch (NumberFormatException e) { + log.debug("Invalid dst-port in: {} (error ignored)", actionToDecode); + return null; + } + } + } + else { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + return null; + } + + private static byte[] get_mac_addr(Matcher n, String actionToDecode, Logger log) { + byte[] macaddr = new byte[6]; + for (int i=0; i<6; i++) { + if (n.group(i+1) != null) { + try { + macaddr[i] = get_byte("0x" + n.group(i+1)); + } + catch (NumberFormatException e) { + log.debug("Invalid src-mac in: '{}' (error ignored)", actionToDecode); + return null; + } + } + else { + log.debug("Invalid src-mac in: '{}' (null, error ignored)", actionToDecode); + return null; + } + } + return macaddr; + } + + private static int get_ip_addr(Matcher n, String actionToDecode, Logger log) { + int ipaddr = 0; + for (int i=0; i<4; i++) { + if (n.group(i+1) != null) { + try { + ipaddr = ipaddr<<8; + ipaddr = ipaddr | get_int(n.group(i+1)); + } + catch (NumberFormatException e) { + log.debug("Invalid src-ip in: '{}' (error ignored)", actionToDecode); + return 0; + } + } + else { + log.debug("Invalid src-ip in: '{}' (null, error ignored)", actionToDecode); + return 0; + } + } + return ipaddr; + } + + // Parse int as decimal, hex (start with 0x or #) or octal (starts with 0) + private static int get_int(String str) { + return Integer.decode(str); + } + + // Parse short as decimal, hex (start with 0x or #) or octal (starts with 0) + private static short get_short(String str) { + return (short)(int)Integer.decode(str); + } + + // Parse byte as decimal, hex (start with 0x or #) or octal (starts with 0) + private static byte get_byte(String str) { + return Integer.decode(str).byteValue(); + } + +} diff --git a/src/main/java/net/floodlightcontroller/util/FlowModUtils.java b/src/main/java/net/floodlightcontroller/util/FlowModUtils.java index bd17cafdd84ee94a930bda41a8341279e82299bf..94ddeeb6eccd79bba2b8d3ddbcee8a952e338aea 100644 --- a/src/main/java/net/floodlightcontroller/util/FlowModUtils.java +++ b/src/main/java/net/floodlightcontroller/util/FlowModUtils.java @@ -13,10 +13,24 @@ import org.projectfloodlight.openflow.protocol.OFVersion; * Convert an OFFlowMod to a specific OFFlowMod-OFFlowModCommand. * These function as setCommand(OFFlowModCommand) methods for an OFFlowMod. * Used initially in the static flow pusher, but will likely find merit elsewhere. + * + * Other useful FlowMod utility functions and constants are also included. * * @author Ryan Izard <ryan.izard@bigswitch.com, rizard@g.clemson.edu> */ public class FlowModUtils { + public static final int INFINITE_TIMEOUT = 0; + + public static final int PRIORITY_MAX = 32768; + public static final int PRIORITY_VERY_HIGH = 28672; + public static final int PRIORITY_HIGH = 24576; + public static final int PRIORITY_MED_HIGH = 20480; + public static final int PRIORITY_MED = 16384; + public static final int PRIORITY_MED_LOW = 12288; + public static final int PRIORITY_LOW = 8192; + public static final int PRIORITY_VERY_LOW = 4096; + public static final int PRIORITY_MIN = 0; + public static OFFlowAdd toFlowAdd(OFFlowMod fm) { OFVersion version = fm.getVersion(); OFFlowAdd.Builder b = OFFactories.getFactory(version).buildFlowAdd(); diff --git a/src/main/java/net/floodlightcontroller/util/MatchString.java b/src/main/java/net/floodlightcontroller/util/MatchString.java deleted file mode 100644 index fb71e3cd1fb3cf6c108f37c94fe750e471df221f..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/util/MatchString.java +++ /dev/null @@ -1,220 +0,0 @@ -package net.floodlightcontroller.util; - -import org.projectfloodlight.openflow.protocol.OFFactories; -import org.projectfloodlight.openflow.protocol.OFVersion; -import org.projectfloodlight.openflow.protocol.match.Match; -import org.projectfloodlight.openflow.protocol.match.MatchField; -import org.projectfloodlight.openflow.types.EthType; -import org.projectfloodlight.openflow.types.IPv4AddressWithMask; -import org.projectfloodlight.openflow.types.IpDscp; -import org.projectfloodlight.openflow.types.IpEcn; -import org.projectfloodlight.openflow.types.IpProtocol; -import org.projectfloodlight.openflow.types.MacAddress; -import org.projectfloodlight.openflow.types.OFPort; -import org.projectfloodlight.openflow.types.OFVlanVidMatch; -import org.projectfloodlight.openflow.types.TransportPort; -import org.projectfloodlight.openflow.types.U8; -import org.projectfloodlight.openflow.types.VlanPcp; - - -/** - * Represents an ofp_match structure - * - * @author David Erickson (daviderickson@cs.stanford.edu) - * @author Rob Sherwood (rob.sherwood@stanford.edu) - * - */ -public class MatchString { - /* List of Strings for marshalling and unmarshalling to human readable forms */ - final private static String STR_IN_PORT = "in_port"; - final private static String STR_DL_DST = "dl_dst"; - final private static String STR_DL_SRC = "dl_src"; - final private static String STR_DL_TYPE = "dl_type"; - final private static String STR_DL_VLAN = "dl_vlan"; - final private static String STR_DL_VLAN_PCP = "dl_vpcp"; - final private static String STR_NW_DST = "nw_dst"; - final private static String STR_NW_SRC = "nw_src"; - final private static String STR_NW_PROTO = "nw_proto"; - final private static String STR_NW_TOS = "nw_tos"; - final private static String STR_TP_DST = "tp_dst"; - final private static String STR_TP_SRC = "tp_src"; - - /** - * Output a dpctl-styled string, i.e., only list the elements that are not - * wildcarded - * - * A match-everything OFMatch outputs "OFMatch[]" - * - * @return - * "OFMatch[dl_src:00:20:01:11:22:33,nw_src:192.168.0.0/24,tp_dst:80]" - */ - /*public String toString(Match match) { - String str = ""; - - match - - // l1 - if ((wildcards & OFPFW_IN_PORT) == 0) - str += "," + STR_IN_PORT + "=" + U16.f(this.inputPort); - - // l2 - if ((wildcards & OFPFW_DL_DST) == 0) - str += "," + STR_DL_DST + "=" - + match.); - if ((wildcards & OFPFW_DL_SRC) == 0) - str += "," + STR_DL_SRC + "=" - + HexString.toHexString(this.dataLayerSource); - if ((wildcards & OFPFW_DL_TYPE) == 0) - str += "," + STR_DL_TYPE + "=0x" - + Integer.toHexString(U16.f(this.dataLayerType)); - if ((wildcards & OFPFW_DL_VLAN) == 0) - str += "," + STR_DL_VLAN + "=0x" - + Integer.toHexString(U16.f(this.dataLayerVirtualLan)); - if ((wildcards & OFPFW_DL_VLAN_PCP) == 0) - str += "," - + STR_DL_VLAN_PCP - + "=" - + Integer.toHexString(U8 - .f(this.dataLayerVirtualLanPriorityCodePoint)); - - // l3 - if (getNetworkDestinationMaskLen() > 0) - str += "," - + STR_NW_DST - + "=" - + cidrToString(networkDestination, - getNetworkDestinationMaskLen()); - if (getNetworkSourceMaskLen() > 0) - str += "," + STR_NW_SRC + "=" - + cidrToString(networkSource, getNetworkSourceMaskLen()); - if ((wildcards & OFPFW_NW_PROTO) == 0) - str += "," + STR_NW_PROTO + "=" + U8.f(this.networkProtocol); - if ((wildcards & OFPFW_NW_TOS) == 0) - str += "," + STR_NW_TOS + "=" + U8.f(this.networkTypeOfService); - - // l4 - if ((wildcards & OFPFW_TP_DST) == 0) - str += "," + STR_TP_DST + "=" + U16.f(this.transportDestination); - if ((wildcards & OFPFW_TP_SRC) == 0) - str += "," + STR_TP_SRC + "=" + U16.f(this.transportSource); - if ((str.length() > 0) && (str.charAt(0) == ',')) - str = str.substring(1); // trim the leading "," - // done - return "OFMatch[" + str + "]"; - }*/ - - /** - * Based on the method from OFMatch in openflowj 1.0. - * Set this Match's parameters based on a comma-separated key=value pair - * dpctl-style string, e.g., from the output of OFMatch.toString() <br> - * <p> - * Supported keys/values include <br> - * <p> - * <TABLE border=1> - * <TR> - * <TD>KEY(s) - * <TD>VALUE - * </TR> - * <TR> - * <TD>"in_port","input_port" - * <TD>integer - * </TR> - * <TR> - * <TD>"dl_src","eth_src", "dl_dst","eth_dst" - * <TD>hex-string - * </TR> - * <TR> - * <TD>"dl_type", "dl_vlan", "dl_vlan_pcp" - * <TD>integer - * </TR> - * <TR> - * <TD>"nw_src", "nw_dst", "ip_src", "ip_dst" - * <TD>CIDR-style netmask - * </TR> - * <TR> - * <TD>"tp_src","tp_dst" - * <TD>integer (max 64k) - * </TR> - * </TABLE> - * <p> - * The CIDR-style netmasks assume 32 netmask if none given, so: - * "128.8.128.118/32" is the same as "128.8.128.118" - * - * @param match - * a key=value comma separated string, e.g. - * "in_port=5,ip_dst=192.168.0.0/16,tp_src=80" - * @throws IllegalArgumentException - * on unexpected key or value - */ - - public static Match fromString(String match, OFVersion ofVersion) throws IllegalArgumentException { - if (match.equals("") || match.equalsIgnoreCase("any") - || match.equalsIgnoreCase("all") || match.equals("[]")) - match = "Match[]"; - String[] tokens = match.split("[\\[,\\]]"); - String[] values; - int initArg = 0; - if (tokens[0].equals("Match")) - initArg = 1; - - Match.Builder mb = OFFactories.getFactory(ofVersion).buildMatch(); - - int i; - for (i = initArg; i < tokens.length; i++) { - values = tokens[i].split("="); - if (values.length != 2) - throw new IllegalArgumentException("Token " + tokens[i] - + " does not have form 'key=value' parsing " + match); - values[0] = values[0].toLowerCase(); // try to make this case insens - if (values[0].equals(STR_IN_PORT) || values[0].equals("input_port")) { - mb.setExact(MatchField.IN_PORT, OFPort.of(Integer.valueOf(values[1]))); - } else if (values[0].equals(STR_DL_DST) || values[0].equals("eth_dst")) { - mb.setExact(MatchField.ETH_DST, MacAddress.of(values[1])); - } else if (values[0].equals(STR_DL_SRC) || values[0].equals("eth_src")) { - mb.setExact(MatchField.ETH_SRC, MacAddress.of(values[1])); - } else if (values[0].equals(STR_DL_TYPE) || values[0].equals("eth_type")) { - if (values[1].startsWith("0x")) { - mb.setExact(MatchField.ETH_TYPE, EthType.of(Integer.valueOf(values[1].replaceFirst("0x", ""), 16))); - } else { - mb.setExact(MatchField.ETH_TYPE, EthType.of(Integer.valueOf(values[1]))); - } - } else if (values[0].equals(STR_DL_VLAN)) { - if (values[1].contains("0x")) { - mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlan(Integer.valueOf(values[1].replaceFirst("0x", ""), 16))); - } else { - mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlan(Integer.valueOf(values[1]))); - } - } else if (values[0].equals(STR_DL_VLAN_PCP)) { - mb.setExact(MatchField.VLAN_PCP, VlanPcp.of(U8.t(Short.valueOf(values[1])))); - } else if (values[0].equals(STR_NW_DST) || values[0].equals("ip_dst")) { - mb.setMasked(MatchField.IPV4_DST, IPv4AddressWithMask.of(values[1])); - } else if (values[0].equals(STR_NW_SRC) || values[0].equals("ip_src")) { - mb.setMasked(MatchField.IPV4_SRC, IPv4AddressWithMask.of(values[1])); - } else if (values[0].equals(STR_NW_PROTO)) { - mb.setExact(MatchField.IP_PROTO, IpProtocol.of(Short.valueOf(values[1]))); - } else if (values[0].equals(STR_NW_TOS)) { - mb.setExact(MatchField.IP_ECN, IpEcn.of(U8.t(Short.valueOf(values[1])))); - mb.setExact(MatchField.IP_DSCP, IpDscp.of(U8.t(Short.valueOf(values[1])))); - } else if (values[0].equals(STR_TP_DST)) { - if (mb.get(MatchField.IP_PROTO).equals(IpProtocol.TCP)) { - mb.setExact(MatchField.TCP_DST, TransportPort.of(Integer.valueOf(values[1]))); - } else if (mb.get(MatchField.IP_PROTO).equals(IpProtocol.UDP)) { - mb.setExact(MatchField.UDP_DST, TransportPort.of(Integer.valueOf(values[1]))); - } else if (mb.get(MatchField.IP_PROTO).equals(IpProtocol.SCTP)) { - mb.setExact(MatchField.SCTP_DST, TransportPort.of(Integer.valueOf(values[1]))); - } - } else if (values[0].equals(STR_TP_SRC)) { - if (mb.get(MatchField.IP_PROTO).equals(IpProtocol.TCP)) { - mb.setExact(MatchField.TCP_SRC, TransportPort.of(Integer.valueOf(values[1]))); - } else if (mb.get(MatchField.IP_PROTO).equals(IpProtocol.UDP)) { - mb.setExact(MatchField.UDP_SRC, TransportPort.of(Integer.valueOf(values[1]))); - } else if (mb.get(MatchField.IP_PROTO).equals(IpProtocol.SCTP)) { - mb.setExact(MatchField.SCTP_SRC, TransportPort.of(Integer.valueOf(values[1]))); - } - } else { - throw new IllegalArgumentException("unknown token " + tokens[i] + " parsing " + match); - } - } - return mb.build(); - } -} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/util/MatchUtils.java b/src/main/java/net/floodlightcontroller/util/MatchUtils.java index 1d08b05d4174dde3002761801d74a331eebaa8d5..e10cf4a3fdf52b9466c9573f94ad06c49389f3e8 100644 --- a/src/main/java/net/floodlightcontroller/util/MatchUtils.java +++ b/src/main/java/net/floodlightcontroller/util/MatchUtils.java @@ -1,17 +1,82 @@ package net.floodlightcontroller.util; +import java.util.ArrayDeque; +import java.util.Arrays; import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; import org.projectfloodlight.openflow.protocol.OFFactories; +import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.protocol.match.Match; import org.projectfloodlight.openflow.protocol.match.MatchField; +import org.projectfloodlight.openflow.types.ArpOpcode; +import org.projectfloodlight.openflow.types.EthType; +import org.projectfloodlight.openflow.types.ICMPv4Code; +import org.projectfloodlight.openflow.types.ICMPv4Type; +import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv4AddressWithMask; +import org.projectfloodlight.openflow.types.IpDscp; +import org.projectfloodlight.openflow.types.IpEcn; +import org.projectfloodlight.openflow.types.IpProtocol; +import org.projectfloodlight.openflow.types.MacAddress; +import org.projectfloodlight.openflow.types.OFMetadata; +import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.OFVlanVidMatch; +import org.projectfloodlight.openflow.types.TransportPort; +import org.projectfloodlight.openflow.types.U32; +import org.projectfloodlight.openflow.types.U8; +import org.projectfloodlight.openflow.types.VlanPcp; /** * Match helper functions. Use with any OpenFlowJ-Loxi Match. + * + * Includes string methods adopted from OpenFlowJ for OpenFlow 1.0. * * @author Ryan Izard <ryan.izard@bigswitch.com, rizard@g.clemson.edu> + * @author David Erickson (daviderickson@cs.stanford.edu) + * @author Rob Sherwood (rob.sherwood@stanford.edu) */ public class MatchUtils { + /* List of Strings for marshalling and unmarshalling to human readable forms. + * Classes that convert from Match and String should reference these fields for a + * common string representation throughout the controller. The StaticFlowEntryPusher + * is one such example that references these strings. + */ + public static final String STR_IN_PORT = "ingress_port"; + + public static final String STR_DL_DST = "dl_dst"; + public static final String STR_DL_SRC = "dl_src"; + public static final String STR_DL_TYPE = "dl_type"; + public static final String STR_DL_VLAN = "dl_vlan"; + public static final String STR_DL_VLAN_PCP = "dl_vpcp"; + + public static final String STR_NW_DST = "nw_dst"; + public static final String STR_NW_SRC = "nw_src"; + public static final String STR_NW_PROTO = "nw_proto"; + public static final String STR_NW_TOS = "nw_tos"; + + public static final String STR_TP_DST = "tp_dst"; + public static final String STR_TP_SRC = "tp_src"; + + public static final String STR_ICMP_TYPE = "icmp_type"; + public static final String STR_ICMP_CODE = "icmp_code"; + + public static final String STR_ARP_OPCODE = "arp_opcode"; + public static final String STR_ARP_SHA = "arp_sha"; + public static final String STR_ARP_DHA = "arp_dha"; + public static final String STR_ARP_SPA = "arp_spa"; + public static final String STR_ARP_DPA = "arp_dpa"; + + public static final String STR_MPLS_LABEL = "mpls_label"; + public static final String STR_MPLS_TC = "mpls_tc"; + public static final String STR_MPLS_BOS = "mpls_bos"; + + public static final String STR_METADATA = "metadata"; + public static final String STR_TUNNEL_ID = "tunnel_id"; + + public static final String STR_PBB_ISID = "pbb_isid"; + /** * Create a point-to-point match for two devices at the IP layer. * Takes an existing match (e.g. from a PACKET_IN), and masks all @@ -98,7 +163,7 @@ public class MatchUtils { } return mb; } - + /** * Create a Match builder the same OF version as Match m. The returned builder * will not retain any MatchField information from Match m and will @@ -112,7 +177,7 @@ public class MatchUtils { public static Match.Builder createForgetfulBuilder(Match m) { return OFFactories.getFactory(m.getVersion()).buildMatch(); } - + /** * Create a duplicate Match object from Match m. * @@ -122,4 +187,250 @@ public class MatchUtils { public static Match createCopy(Match m) { return m.createBuilder().build(); // will use parent MatchFields to produce the new Match only if the builder is never modified } + + /** + * TODO @Ryan NOT IMPLEMENTED! Returns empty string right now. + * Output a dpctl-styled string, i.e., only list the elements that are not wildcarded. + * + * A match-everything Match outputs "Match[]" + * + * @return "Match[dl_src:00:20:01:11:22:33,nw_src:192.168.0.0/24,tp_dst:80]" + */ + public static String toString(Match match) { + /*String str = ""; + + match + + // l1 + if ((wildcards & OFPFW_IN_PORT) == 0) + str += "," + STR_IN_PORT + "=" + U16.f(this.inputPort); + + // l2 + if ((wildcards & OFPFW_DL_DST) == 0) + str += "," + STR_DL_DST + "=" + + match.); + if ((wildcards & OFPFW_DL_SRC) == 0) + str += "," + STR_DL_SRC + "=" + + HexString.toHexString(this.dataLayerSource); + if ((wildcards & OFPFW_DL_TYPE) == 0) + str += "," + STR_DL_TYPE + "=0x" + + Integer.toHexString(U16.f(this.dataLayerType)); + if ((wildcards & OFPFW_DL_VLAN) == 0) + str += "," + STR_DL_VLAN + "=0x" + + Integer.toHexString(U16.f(this.dataLayerVirtualLan)); + if ((wildcards & OFPFW_DL_VLAN_PCP) == 0) + str += "," + + STR_DL_VLAN_PCP + + "=" + + Integer.toHexString(U8 + .f(this.dataLayerVirtualLanPriorityCodePoint)); + + // l3 + if (getNetworkDestinationMaskLen() > 0) + str += "," + + STR_NW_DST + + "=" + + cidrToString(networkDestination, + getNetworkDestinationMaskLen()); + if (getNetworkSourceMaskLen() > 0) + str += "," + STR_NW_SRC + "=" + + cidrToString(networkSource, getNetworkSourceMaskLen()); + if ((wildcards & OFPFW_NW_PROTO) == 0) + str += "," + STR_NW_PROTO + "=" + U8.f(this.networkProtocol); + if ((wildcards & OFPFW_NW_TOS) == 0) + str += "," + STR_NW_TOS + "=" + U8.f(this.networkTypeOfService); + + // l4 + if ((wildcards & OFPFW_TP_DST) == 0) + str += "," + STR_TP_DST + "=" + U16.f(this.transportDestination); + if ((wildcards & OFPFW_TP_SRC) == 0) + str += "," + STR_TP_SRC + "=" + U16.f(this.transportSource); + if ((str.length() > 0) && (str.charAt(0) == ',')) + str = str.substring(1); // trim the leading "," + // done + return "OFMatch[" + str + "]"; */ + return ""; + } + + /** + * Based on the method from OFMatch in openflowj 1.0. + * Set this Match's parameters based on a comma-separated key=value pair + * dpctl-style string, e.g., from the output of OFMatch.toString() <br> + * <p> + * Supported keys/values include <br> + * <p> + * <TABLE border=1> + * <TR> + * <TD>KEY(s) + * <TD>VALUE + * </TR> + * <TR> + * <TD>"in_port","input_port" + * <TD>integer + * </TR> + * <TR> + * <TD>"dl_src", "dl_dst" + * <TD>hex-string + * </TR> + * <TR> + * <TD>"dl_type", "dl_vlan", "dl_vlan_pcp" + * <TD>integer + * </TR> + * <TR> + * <TD>"nw_src", "nw_dst" + * <TD>CIDR-style netmask + * </TR> + * <TR> + * <TD>"tp_src","tp_dst" + * <TD>integer (max 64k) + * </TR> + * </TABLE> + * <p> + * The CIDR-style netmasks assume 32 netmask if none given, so: + * "128.8.128.118/32" is the same as "128.8.128.118" + * + * @param match + * a key=value comma separated string, e.g. + * "in_port=5,nw_dst=192.168.0.0/16,tp_src=80" + * @throws IllegalArgumentException + * on unexpected key or value + */ + public static Match fromString(String match, OFVersion ofVersion) throws IllegalArgumentException { + if (match.equals("") || match.equalsIgnoreCase("any") || match.equalsIgnoreCase("all") || match.equals("[]")) { + match = "Match[]"; + } + + // Split into pairs of key=value + String[] tokens = match.split("[\\[,\\]]"); + int initArg = 0; + if (tokens[0].equals("Match")) { + initArg = 1; + } + + // Split up key=value pairs into [key, value], and insert into linked list + int i; + String[] tmp; + ArrayDeque<String[]> llValues = new ArrayDeque<String[]>(); + for (i = initArg; i < tokens.length; i++) { + tmp = tokens[i].split("="); + if (tmp.length != 2) { + throw new IllegalArgumentException("Token " + tokens[i] + " does not have form 'key=value' parsing " + match); + } + tmp[0] = tmp[0].toLowerCase(); // try to make key parsing case insensitive + llValues.add(tmp); // llValues contains [key, value] pairs. Create a queue of pairs to process. + } + + Match.Builder mb = OFFactories.getFactory(ofVersion).buildMatch(); + + while (!llValues.isEmpty()) { + IpProtocol ipProto; // used to prevent lots of match.get()'s for detecting transport protocol + String[] key_value = llValues.pollFirst(); // pop off the first element; this completely removes it from the queue. + switch (key_value[0]) { + case STR_IN_PORT: + mb.setExact(MatchField.IN_PORT, OFPort.of(Integer.valueOf(key_value[1]))); + break; + case STR_DL_DST: + mb.setExact(MatchField.ETH_DST, MacAddress.of(key_value[1])); + break; + case STR_DL_SRC: + mb.setExact(MatchField.ETH_SRC, MacAddress.of(key_value[1])); + break; + case STR_DL_TYPE: + if (key_value[1].startsWith("0x")) { + mb.setExact(MatchField.ETH_TYPE, EthType.of(Integer.valueOf(key_value[1].replaceFirst("0x", ""), 16))); + } else { + mb.setExact(MatchField.ETH_TYPE, EthType.of(Integer.valueOf(key_value[1]))); + } + break; + case STR_DL_VLAN: + if (key_value[1].contains("0x")) { + mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlan(Integer.valueOf(key_value[1].replaceFirst("0x", ""), 16))); + } else { + mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlan(Integer.valueOf(key_value[1]))); + } + break; + case STR_DL_VLAN_PCP: + mb.setExact(MatchField.VLAN_PCP, VlanPcp.of(U8.t(Short.valueOf(key_value[1])))); + break; + case STR_NW_DST: + mb.setMasked(MatchField.IPV4_DST, IPv4AddressWithMask.of(key_value[1])); + break; + case STR_NW_SRC: + mb.setMasked(MatchField.IPV4_SRC, IPv4AddressWithMask.of(key_value[1])); + break; + case STR_NW_PROTO: + mb.setExact(MatchField.IP_PROTO, IpProtocol.of(Short.valueOf(key_value[1]))); + break; + case STR_NW_TOS: + mb.setExact(MatchField.IP_ECN, IpEcn.of(U8.t(Short.valueOf(key_value[1])))); + mb.setExact(MatchField.IP_DSCP, IpDscp.of(U8.t(Short.valueOf(key_value[1])))); + break; + case STR_TP_DST: + // if we don't know the transport protocol yet, postpone parsing this [key, value] pair until we know. Put it at the back of the queue. + if ((ipProto = mb.get(MatchField.IP_PROTO)) == null) { + llValues.add(key_value); // place it back if we can't proceed yet + } else if (ipProto.equals(IpProtocol.TCP)) { + mb.setExact(MatchField.TCP_DST, TransportPort.of(Integer.valueOf(key_value[1]))); + } else if (ipProto.equals(IpProtocol.UDP)) { + mb.setExact(MatchField.UDP_DST, TransportPort.of(Integer.valueOf(key_value[1]))); + } else if (ipProto.equals(IpProtocol.SCTP)) { + mb.setExact(MatchField.SCTP_DST, TransportPort.of(Integer.valueOf(key_value[1]))); + } + break; + case STR_TP_SRC: + if ((ipProto = mb.get(MatchField.IP_PROTO)) == null) { + llValues.add(key_value); // place it back if we can't proceed yet + } else if (mb.get(MatchField.IP_PROTO).equals(IpProtocol.TCP)) { + mb.setExact(MatchField.TCP_SRC, TransportPort.of(Integer.valueOf(key_value[1]))); + } else if (mb.get(MatchField.IP_PROTO).equals(IpProtocol.UDP)) { + mb.setExact(MatchField.UDP_SRC, TransportPort.of(Integer.valueOf(key_value[1]))); + } else if (mb.get(MatchField.IP_PROTO).equals(IpProtocol.SCTP)) { + mb.setExact(MatchField.SCTP_SRC, TransportPort.of(Integer.valueOf(key_value[1]))); + } + break; + case STR_ICMP_TYPE: + mb.setExact(MatchField.ICMPV4_TYPE, ICMPv4Type.of(Short.parseShort(key_value[1]))); + break; + case STR_ICMP_CODE: + mb.setExact(MatchField.ICMPV4_CODE, ICMPv4Code.of(Short.parseShort(key_value[1]))); + break; + case STR_ARP_OPCODE: + mb.setExact(MatchField.ARP_OP, ArpOpcode.of(Integer.parseInt(key_value[1]))); + break; + case STR_ARP_SHA: + mb.setExact(MatchField.ARP_SHA, MacAddress.of(key_value[1])); + break; + case STR_ARP_DHA: + mb.setExact(MatchField.ARP_THA, MacAddress.of(key_value[1])); + break; + case STR_ARP_SPA: + mb.setExact(MatchField.ARP_SPA, IPv4Address.of(key_value[1])); + break; + case STR_ARP_DPA: + mb.setExact(MatchField.ARP_TPA, IPv4Address.of(key_value[1])); + break; + case STR_MPLS_LABEL: + mb.setExact(MatchField.MPLS_LABEL, U32.of(Long.parseLong(key_value[1]))); + break; + case STR_MPLS_TC: + mb.setExact(MatchField.MPLS_TC, U8.of(Short.parseShort(key_value[1]))); + break; + case STR_MPLS_BOS: + //no-op. Not implemented. + break; + case STR_METADATA: + mb.setExact(MatchField.METADATA, OFMetadata.ofRaw(Long.parseLong(key_value[1]))); + break; + case STR_TUNNEL_ID: + //no-op. Not implemented. + break; + case STR_PBB_ISID: + //no-op. Not implemented. + break; + default: + throw new IllegalArgumentException("unknown token " + key_value + " parsing " + match); + } + } + return mb.build(); + } }